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
This commit is contained in:
Gilles Sadowski 2012-10-19 14:20:16 +00:00
parent ea138c2da2
commit 0912bac520
2 changed files with 81 additions and 17 deletions

View File

@ -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<PointValuePair> 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
* <ul>
* <li>{@link org.apache.commons.math3.optimization.InitialGuess InitialGuess}</li>
* <li>{@link Sigma}</li>
* <li>{@link PopulationSize}</li>
* </ul>
* @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:
* <ul>
* <li>{@link Sigma}</li>
* <li>{@link PopulationSize}</li>
* </ul>
*/
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);

View File

@ -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++) {