From 0912bac5201d98af64a832766146797a9d75bb6f Mon Sep 17 00:00:00 2001 From: Gilles Sadowski Date: Fri, 19 Oct 2012 14:20:16 +0000 Subject: [PATCH] MATH-868 MATH-879 "lambda" and "inputSigma" should be passed to "optimize" as arguments of type "OptimizationData". Deprecated constructors. Updated unit tests. git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1400108 13f79535-47bb-0310-9956-ffa450edef68 --- .../optimization/direct/CMAESOptimizer.java | 77 +++++++++++++++++-- .../direct/CMAESOptimizerTest.java | 21 ++--- 2 files changed, 81 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/apache/commons/math3/optimization/direct/CMAESOptimizer.java b/src/main/java/org/apache/commons/math3/optimization/direct/CMAESOptimizer.java index 608df0eb9..41497074c 100644 --- a/src/main/java/org/apache/commons/math3/optimization/direct/CMAESOptimizer.java +++ b/src/main/java/org/apache/commons/math3/optimization/direct/CMAESOptimizer.java @@ -24,6 +24,7 @@ import java.util.List; import org.apache.commons.math3.analysis.MultivariateFunction; import org.apache.commons.math3.exception.DimensionMismatchException; import org.apache.commons.math3.exception.NotPositiveException; +import org.apache.commons.math3.exception.NotStrictlyPositiveException; import org.apache.commons.math3.exception.OutOfRangeException; import org.apache.commons.math3.exception.TooManyEvaluationsException; import org.apache.commons.math3.linear.Array2DRowRealMatrix; @@ -225,6 +226,10 @@ public class CMAESOptimizer /** * Default constructor, uses default parameters + * + * @deprecated As of version 3.1: Parameter {@code lambda} must be + * passed with the call to {@link #optimize(int,MultivariateFunction,GoalType,OptimizationData[]) + * optimize} (whereas in the current code it is set to an undocumented value). */ public CMAESOptimizer() { this(0); @@ -232,9 +237,12 @@ public class CMAESOptimizer /** * @param lambda Population size. + * @deprecated As of version 3.1: Parameter {@code lambda} must be + * passed with the call to {@link #optimize(int,MultivariateFunction,GoalType,OptimizationData[]) + * optimize} (whereas in the current code it is set to an undocumented value).. */ public CMAESOptimizer(int lambda) { - this(lambda, DEFAULT_MAXITERATIONS, DEFAULT_STOPFITNESS, + this(lambda, null, DEFAULT_MAXITERATIONS, DEFAULT_STOPFITNESS, DEFAULT_ISACTIVECMA, DEFAULT_DIAGONALONLY, DEFAULT_CHECKFEASABLECOUNT, DEFAULT_RANDOMGENERATOR, false, null); @@ -244,7 +252,7 @@ public class CMAESOptimizer * @param lambda Population size. * @param inputSigma Initial standard deviations to sample new points * around the initial guess. - * @deprecated As of version 3.1: Parameter {@code inputSigma} must be + * @deprecated As of version 3.1: Parameters {@code lambda} and {@code inputSigma} must be * passed with the call to {@link #optimize(int,MultivariateFunction,GoalType,OptimizationData[]) * optimize}. */ @@ -296,7 +304,7 @@ public class CMAESOptimizer * @param random Random generator. * @param generateStatistics Whether statistic data is collected. * @param checker Convergence checker. - * @deprecated As of version 3.1: Parameter {@code inputSigma} must be + * @deprecated As of version 3.1: Parameters {@code lambda} and {@code inputSigma} must be * passed with the call to {@link #optimize(int,MultivariateFunction,GoalType,OptimizationData[]) * optimize}. */ @@ -319,7 +327,6 @@ public class CMAESOptimizer } /** - * @param lambda Population size. * @param maxIterations Maximal number of iterations. * @param stopFitness Whether to stop if objective function value is smaller than * {@code stopFitness}. @@ -331,9 +338,10 @@ public class CMAESOptimizer * @param random Random generator. * @param generateStatistics Whether statistic data is collected. * @param checker Convergence checker. + * + * @since 3.1 */ - public CMAESOptimizer(int lambda, - int maxIterations, + public CMAESOptimizer(int maxIterations, double stopFitness, boolean isActiveCMA, int diagonalOnly, @@ -342,7 +350,6 @@ public class CMAESOptimizer boolean generateStatistics, ConvergenceChecker checker) { super(checker); - this.lambda = lambda; this.maxIterations = maxIterations; this.stopFitness = stopFitness; this.isActiveCMA = isActiveCMA; @@ -397,8 +404,17 @@ public class CMAESOptimizer /** * @param s Sigma values. + * @throws NotPositiveException if any of the array entries is smaller + * than zero. */ - public Sigma(double[] s) { + public Sigma(double[] s) + throws NotPositiveException { + for (int i = 0; i < s.length; i++) { + if (s[i] < 0) { + throw new NotPositiveException(s[i]); + } + } + sigma = s.clone(); } @@ -410,6 +426,40 @@ public class CMAESOptimizer } } + /** + * Population size. + * The number of offspring is the primary strategy parameter. + * In the absence of better clues, a good default could be an + * integer close to {@code 4 + 3 ln(n)}, where {@code n} is the + * number of optimized parameters. + * Increasing the population size improves global search properties + * at the expense of speed (which in general decreases at most + * linearly with increasing population size). + */ + public static class PopulationSize implements OptimizationData { + /** Population size. */ + private final int lambda; + + /** + * @param size Population size. + * @throws NotStrictlyPositiveException if {@code size <= 0}. + */ + public PopulationSize(int size) + throws NotStrictlyPositiveException { + if (size <= 0) { + throw new NotStrictlyPositiveException(size); + } + lambda = size; + } + + /** + * @return the population size. + */ + public int getPopulationSize() { + return lambda; + } + } + /** * Optimize an objective function. * @@ -420,6 +470,7 @@ public class CMAESOptimizer * * @return the point/value pair giving the optimal value for objective * function. @@ -593,6 +644,7 @@ public class CMAESOptimizer * @param optData Optimization data. The following data will be looked for: * */ private void parseOptimizationData(OptimizationData... optData) { @@ -603,6 +655,10 @@ public class CMAESOptimizer inputSigma = ((Sigma) data).getSigma(); continue; } + if (data instanceof PopulationSize) { + lambda = ((PopulationSize) data).getPopulationSize(); + continue; + } } } @@ -620,6 +676,7 @@ public class CMAESOptimizer } for (int i = 0; i < init.length; i++) { if (inputSigma[i] < 0) { + // XXX Remove this block in 4.0 (check performed in "Sigma" class). throw new NotPositiveException(inputSigma[i]); } if (inputSigma[i] > uB[i] - lB[i]) { @@ -636,11 +693,15 @@ public class CMAESOptimizer */ private void initializeCMA(double[] guess) { if (lambda <= 0) { + // XXX Line below to replace the current one in 4.0 (MATH-879). + // throw new NotStrictlyPositiveException(lambda); lambda = 4 + (int) (3 * Math.log(dimension)); } // initialize sigma final double[][] sigmaArray = new double[guess.length][1]; for (int i = 0; i < guess.length; i++) { + // XXX Line below to replace the current one in 4.0 (MATH-868). + // sigmaArray[i][0] = inputSigma[i]; sigmaArray[i][0] = inputSigma == null ? 0.3 : inputSigma[i]; } final RealMatrix insigma = new Array2DRowRealMatrix(sigmaArray, false); diff --git a/src/test/java/org/apache/commons/math3/optimization/direct/CMAESOptimizerTest.java b/src/test/java/org/apache/commons/math3/optimization/direct/CMAESOptimizerTest.java index 6a71338f2..544456bd8 100644 --- a/src/test/java/org/apache/commons/math3/optimization/direct/CMAESOptimizerTest.java +++ b/src/test/java/org/apache/commons/math3/optimization/direct/CMAESOptimizerTest.java @@ -52,7 +52,7 @@ public class CMAESOptimizerTest { @Test(expected = NumberIsTooLargeException.class) public void testInitOutofbounds1() { double[] startPoint = point(DIM,3); - double[] insigma = null; + double[] insigma = point(DIM, 0.3); double[][] boundaries = boundaries(DIM,-1,2); PointValuePair expected = new PointValuePair(point(DIM,1.0),0.0); @@ -63,7 +63,7 @@ public class CMAESOptimizerTest { @Test(expected = NumberIsTooSmallException.class) public void testInitOutofbounds2() { double[] startPoint = point(DIM, -2); - double[] insigma = null; + double[] insigma = point(DIM, 0.3); double[][] boundaries = boundaries(DIM,-1,2); PointValuePair expected = new PointValuePair(point(DIM,1.0),0.0); @@ -75,7 +75,7 @@ public class CMAESOptimizerTest { @Test(expected = DimensionMismatchException.class) public void testBoundariesDimensionMismatch() { double[] startPoint = point(DIM,0.5); - double[] insigma = null; + double[] insigma = point(DIM, 0.3); double[][] boundaries = boundaries(DIM+1,-1,2); PointValuePair expected = new PointValuePair(point(DIM,1.0),0.0); @@ -111,7 +111,7 @@ public class CMAESOptimizerTest { @Test(expected = DimensionMismatchException.class) public void testInputSigmaDimensionMismatch() { double[] startPoint = point(DIM,0.5); - double[] insigma = point(DIM+1,-0.5); + double[] insigma = point(DIM + 1, 0.5); double[][] boundaries = null; PointValuePair expected = new PointValuePair(point(DIM,1.0),0.0); @@ -459,17 +459,20 @@ public class CMAESOptimizerTest { PointValuePair expected) { int dim = startPoint.length; // test diagonalOnly = 0 - slow but normally fewer feval# - CMAESOptimizer optim = new CMAESOptimizer(lambda, inSigma, 30000, - stopValue, isActive, diagonalOnly, - 0, new MersenneTwister(), false); + CMAESOptimizer optim = new CMAESOptimizer(30000, stopValue, isActive, diagonalOnly, + 0, new MersenneTwister(), false, null); final double[] lB = boundaries == null ? null : boundaries[0]; final double[] uB = boundaries == null ? null : boundaries[1]; PointValuePair result = boundaries == null ? optim.optimize(maxEvaluations, func, goal, - new InitialGuess(startPoint)) : + new InitialGuess(startPoint), + new CMAESOptimizer.Sigma(inSigma), + new CMAESOptimizer.PopulationSize(lambda)) : optim.optimize(maxEvaluations, func, goal, new InitialGuess(startPoint), - new SimpleBounds(lB, uB)); + new SimpleBounds(lB, uB), + new CMAESOptimizer.Sigma(inSigma), + new CMAESOptimizer.PopulationSize(lambda)); // System.out.println("sol=" + Arrays.toString(result.getPoint())); Assert.assertEquals(expected.getValue(), result.getValue(), fTol); for (int i = 0; i < dim; i++) {