/FileExtraction/Java_unseen/crisis-economics_CRISIS/CRISIS/test/eu/crisis_economics/abm/markets/clearing/heterogeneous/LineSearchTest.java
/*
 * This file is part of CRISIS, an economics simulator.
 * 
 * Copyright (C) 2015 John Kieran Phillips
 *
 * CRISIS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * CRISIS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with CRISIS.  If not, see <http://www.gnu.org/licenses/>.
 */
package eu.crisis_economics.abm.markets.clearing.heterogeneous;

import java.util.Random;

import org.apache.commons.math3.analysis.MultivariateFunction;
import org.junit.Assert;
import org.junit.Test;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;

import eu.crisis_economics.abm.algorithms.optimization.BrentLineSearch;
import eu.crisis_economics.abm.algorithms.optimization.BrentLineSearch.LineSearchResult;

public class LineSearchTest {
   
   @BeforeMethod
   public void setUp() {
      System.out.println("Testing Brent line search minimizer..");
   }
   
   @Test
   /**
     * Test the Brent line search algorithm by minimizing a one-dimensional
     * cosine function. The starting point of the minimization is the origin,
     * and the maximum distance to travel in the positive x direction is 2*Pi.
     * The expected solution is therefor the local minimum at Pi.
     */
   public void testOneDimensionalCosMinimization() {
      final MultivariateFunction meritFunction = new MultivariateFunction() {
         @Override
         public double value(final double[] x) {
            return Math.cos(x[0]);
         }
      };
      final double[] lineDirection = new double[] { 1.0 };
      double distanceToTravel = Math.PI * 2.;
      final LineSearchResult solution = BrentLineSearch.doLineSearch(
         meritFunction, new double[] { 0.0 }, lineDirection, distanceToTravel);
      Assert.assertTrue(Math.abs(solution.getEvaluationAtSolution() + 1.) < 1.e-10);
      Assert.assertEquals(solution.getSolutionPoint()[0], Math.PI, 1.e-10);
   }
   
   @Test
   /**
     * Test the Brent line search algorithm by minimizing a multidimensional
     * quadratic scalar function. This test generates many random quadratic 
     * fields with random global minima. In each case, the starting point
     * of the minimization is random and the line search direction points 
     * toward the global minimum.
     */
   public void batchTestMultidimensionalQuadraticLineSearch() {
      final Random dice = new Random();
      for(int i = 0; i< 100; ++i) {     // 100 random quadratic minimization tests
         final int numDimensions = dice.nextInt(20) + 2;
         final double[]
            globalMinimum = new double[numDimensions],
            startingPoint = new double[numDimensions];
         for(int j = 0; j< globalMinimum.length; ++j) {
            globalMinimum[j] = dice.nextDouble() * 10. - 5.;
            startingPoint[j] = dice.nextDouble() * 10. - 5.;
         }
         testMultiDimensionalQuadraticMinimization(
            numDimensions,
            startingPoint,
            globalMinimum
            );
      }
   }
   
   private void testMultiDimensionalQuadraticMinimization(
      final int numberOfDimensions,
      final double[] startingPoint,
      final double[] locationOfGlobalMinimum
      ) {
      final MultivariateFunction meritFunction = new MultivariateFunction() {
         @Override
         public double value(final double[] x) {
            double result = 0.;
            for(int i = 0; i< numberOfDimensions; ++i)
               result += (x[i] - locationOfGlobalMinimum[i]) * (x[i] - locationOfGlobalMinimum[i]);
            return result;
         }
      };
      final double[] lineDirection = new double[startingPoint.length];
      double distanceToTravel = 0.;
      for(int i = 0; i< lineDirection.length; ++i) {
         lineDirection[i] = locationOfGlobalMinimum[i] - startingPoint[i];
         distanceToTravel += lineDirection[i] * lineDirection[i];
      }
      distanceToTravel = Math.sqrt(distanceToTravel);
      final LineSearchResult solution = BrentLineSearch.doLineSearch(
         meritFunction, startingPoint, lineDirection, distanceToTravel + 1.);
      Assert.assertTrue(Math.abs(solution.getEvaluationAtSolution()) < 1.e-10);
      for(int i = 0; i< startingPoint.length; ++i)
         Assert.assertEquals(solution.getSolutionPoint()[i], locationOfGlobalMinimum[i], 1.e-10);
   }
   
   @Test
   /**
     * Test the Brent line search algorithm by minimizing a negative
     * multidimensional quadratic scalar function, with maximum at the
     * origin. This test generated many random boundary boxes (limits
     * inside of which to perform line searches) and, in each case, 
     * extremizes the quadratic function along a random direction starting
     * at the origin. It is expected that the solution lies on the domain
     * bounds in each case.
     */
   public void batchTestBoundedDecreasingMultidimensionalSearch() {
      final Random dice = new Random();
      for(int i = 0; i< 100; ++i) {     // 100 random tests
         final int numDimensions = dice.nextInt(20) + 2;
         final double[]
            lowerDomainBounds = new double[numDimensions],
            upperDomainBounds = new double[numDimensions];
         for(int j = 0; j< numDimensions; ++j) {
            lowerDomainBounds[j] = -dice.nextDouble();
            upperDomainBounds[j] = +dice.nextDouble();
         }
         testOptimizationWithDomainBounds(
            numDimensions,
            lowerDomainBounds,
            upperDomainBounds
            );
      }
   }
   
   private void testOptimizationWithDomainBounds(
      final int numberOfDimensions,
      final double[] lowerDomainBounds,
      final double[] upperDomainBounds
      ) {
      final MultivariateFunction meritFunction = new MultivariateFunction() {
         @Override
         public double value(final double[] x) {
            double result = 0.;
            for(int i = 0; i< numberOfDimensions; ++i)
               result += -x[i] * x[i];
            return result;
         }
      };
      Random dice = new Random();
      double[]
         startingPoint = new double[numberOfDimensions],
         vectorDirection = new double[numberOfDimensions];
      for(int i = 0; i< numberOfDimensions; ++i)
         vectorDirection[i] = dice.nextDouble() - .5;
      LineSearchResult solution = BrentLineSearch.doLineSearch(
         meritFunction,
         startingPoint,
         vectorDirection,
         upperDomainBounds,
         lowerDomainBounds
         );
      boolean
         solutionIsOnDomainBoundary = false;
      for(int i = 0; i< numberOfDimensions; ++i) {
         final double
            solutionPoint = solution.getSolutionPoint()[i];
         System.out.printf(
            "Coordinate %4d: distance from lower bound: %16.10g, from upper bound: %16.10g\n",
            i, solutionPoint - lowerDomainBounds[i], upperDomainBounds[i] - solutionPoint);
         if(Math.abs(solutionPoint - upperDomainBounds[i]) < 1.e-8 ||
            Math.abs(solutionPoint - lowerDomainBounds[i]) < 1.e-8)
            solutionIsOnDomainBoundary = true;
      }
      Assert.assertTrue(solutionIsOnDomainBoundary);
   }
   
   @AfterMethod
   public void tearDown() {
      System.out.println("Brent line search minimizer tests pass.");
   }
   
   /**
     * Manual entry point.
     */
   static public void main(String[] args) {
      final LineSearchTest test = new LineSearchTest();
      test.setUp();
      test.testOneDimensionalCosMinimization();
      test.batchTestMultidimensionalQuadraticLineSearch();
      test.batchTestBoundedDecreasingMultidimensionalSearch();
      test.tearDown();
   }
}
