This is an automated email from the ASF dual-hosted git repository. erans pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-math.git
commit 3ba0221c267e299525d385c496840c2b92eeac9c Author: Gilles Sadowski <gillese...@gmail.com> AuthorDate: Fri Aug 20 12:51:09 2021 +0200 Simplify data format for "SimplexOptimizerTest" (randomized) input. This commit also makes the following changes (unit tests): * Add/remove/rename/rewrite test functions. --- .../optim/nonlinear/scalar/TestFunction.java | 67 ++++++++++++---------- .../scalar/noderiv/BOBYQAOptimizerTest.java | 16 +----- .../scalar/noderiv/CMAESOptimizerTest.java | 21 +------ .../scalar/noderiv/SimplexOptimizerTest.java | 61 +++++++++++--------- 4 files changed, 76 insertions(+), 89 deletions(-) diff --git a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/TestFunction.java b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/TestFunction.java index 0292bd8..f28bf11 100644 --- a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/TestFunction.java +++ b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/TestFunction.java @@ -19,7 +19,6 @@ package org.apache.commons.math4.legacy.optim.nonlinear.scalar; import java.util.function.Function; import java.util.function.DoubleUnaryOperator; import org.apache.commons.math4.legacy.analysis.MultivariateFunction; -import org.apache.commons.math4.legacy.core.jdkmath.AccurateMath; /** * Generators of {@link MultivariateFunction multivariate scalar functions}. @@ -71,20 +70,26 @@ public enum TestFunction { }; }), TWO_AXES(dim -> { + final int halfDim = dim / 2; return x -> { double f = 0; - for (int i = 0; i < dim; i++) { - f += (i < dim / 2 ? 1e6 : 1) * x[i] * x[i]; + for (int i = 0; i < halfDim; i++) { + f += 1e6 * x[i] * x[i]; + } + for (int i = halfDim; i < dim; i++) { + f += x[i] * x[i]; } return f; }; }), ELLI(dim -> { - final double last = dim - 1; + final double M = Math.pow(1e3, 1d / (dim - 1)); return x -> { + double factor = 1; double f = 0; for (int i = 0; i < dim; i++) { - f += Math.pow(1e3, i / last) * x[i] * x[i]; + f += factor * x[i] * x[i]; + factor *= M; } return f; }; @@ -96,22 +101,15 @@ public enum TestFunction { }; }), // https://www.sfu.ca/~ssurjano/sumpow.html - DIFF_POW(dim -> { + SUM_POW(dim -> { return x -> { double f = 0; for (int i = 0; i < dim; i++) { - f += AccurateMath.pow(Math.abs(x[i]), i + 2); + f += Math.pow(Math.abs(x[i]), i + 2); } return f; }; }), - SS_DIFF_POW(dim -> { - final MultivariateFunction diffPow = DIFF_POW.withDimension(dim); - return x -> { - double f = Math.pow(diffPow.value(x), 0.25); - return f; - }; - }), // https://www.sfu.ca/~ssurjano/ackley.html ACKLEY(dim -> { final double A = 20; @@ -134,33 +132,26 @@ public enum TestFunction { // https://www.sfu.ca/~ssurjano/rastr.html RASTRIGIN(dim -> { final double A = 10; + final double twopi = 2 * Math.PI; return x -> { double sum = 0; for (int i = 0; i < dim; i++) { final double xi = x[i]; - sum += xi * xi - A * Math.cos(2 * Math.PI * xi); + sum += xi * xi - A * Math.cos(twopi * xi); } return A * dim + sum; }; }), - // https://www.sfu.ca/~ssurjano/powell.html - POWELL(dim -> { - final int last = dim / 4; + // http://benchmarkfcns.xyz/benchmarkfcns/salomonfcn.html + SALOMON(dim -> { return x -> { - double f = 0; - for (int i = 0; i < last; i++) { - final int fourI = 4 * i; - final double x4i = x[fourI]; - final double x4iP1 = x[fourI + 1]; - final double x4iP2 = x[fourI + 2]; - final double x4iP3 = x[fourI + 3]; - final double a = x4i + 10 * x4iP1; - final double b = x4iP2 - x4iP3; - final double c = x4iP1 - 2 * x4iP2; - final double d = x4i - x4iP3; - f += a * a + 5 * b * b + c * c * c * c + 10 * d * d * d * d; + double sum = 0; + for (int i = 0; i < dim; i++) { + final double xi = x[i]; + sum += xi * xi; } - return f; + final double sqrtSum = Math.sqrt(sum); + return 1 - Math.cos(2 * Math.PI * sqrtSum) + 0.1 * sqrtSum; }; }), ROSENBROCK(dim -> { @@ -177,6 +168,20 @@ public enum TestFunction { return f; }; }), + // http://benchmarkfcns.xyz/benchmarkfcns/happycatfcn.html + HAPPY_CAT(dim -> { + final double alpha = 0.125; + return x -> { + double sum = 0; + double sumSq = 0; + for (int i = 0; i < dim; i++) { + final double xi = x[i]; + sum += xi; + sumSq += xi * xi; + } + return Math.pow(sumSq - dim, 2 * alpha) + (0.5 * sumSq + sum) / dim + 0.5; + }; + }), PARABOLA(dim -> { return x -> { double f = 0; diff --git a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/noderiv/BOBYQAOptimizerTest.java b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/noderiv/BOBYQAOptimizerTest.java index bd6cd3a..45e4b29 100644 --- a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/noderiv/BOBYQAOptimizerTest.java +++ b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/noderiv/BOBYQAOptimizerTest.java @@ -177,30 +177,18 @@ public class BOBYQAOptimizerTest { } @Test - public void testDiffPow() { + public void testSumPow() { final int dim = DIM / 2; double[] startPoint = OptimTestUtils.point(dim, 1.0); double[][] boundaries = null; PointValuePair expected = new PointValuePair(OptimTestUtils.point(dim, 0.0), 0.0); - doTest(TestFunction.DIFF_POW.withDimension(dim), startPoint, boundaries, + doTest(TestFunction.SUM_POW.withDimension(dim), startPoint, boundaries, GoalType.MINIMIZE, 1e-8, 1e-1, 21000, expected); } @Test - public void testSsDiffPow() { - final int dim = DIM / 2; - double[] startPoint = OptimTestUtils.point(dim, 1.0); - double[][] boundaries = null; - PointValuePair expected = - new PointValuePair(OptimTestUtils.point(dim, 0.0), 0.0); - doTest(TestFunction.SS_DIFF_POW.withDimension(dim), startPoint, boundaries, - GoalType.MINIMIZE, - 1e-2, 1.3e-1, 50000, expected); - } - - @Test public void testAckley() { double[] startPoint = OptimTestUtils.point(DIM,0.1); double[][] boundaries = null; diff --git a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/noderiv/CMAESOptimizerTest.java b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/noderiv/CMAESOptimizerTest.java index bf9bdb4..9724e27 100644 --- a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/noderiv/CMAESOptimizerTest.java +++ b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/noderiv/CMAESOptimizerTest.java @@ -299,36 +299,21 @@ public class CMAESOptimizerTest { } @Test - public void testDiffPow() { + public void testSumPow() { double[] startPoint = OptimTestUtils.point(DIM,1.0); double[] insigma = OptimTestUtils.point(DIM,0.1); double[][] boundaries = null; PointValuePair expected = new PointValuePair(OptimTestUtils.point(DIM,0.0),0.0); - doTest(TestFunction.DIFF_POW.withDimension(DIM), startPoint, insigma, boundaries, + doTest(TestFunction.SUM_POW.withDimension(DIM), startPoint, insigma, boundaries, GoalType.MINIMIZE, 10, true, 0, 1e-13, 1e-8, 1e-1, 100000, expected); - doTest(TestFunction.DIFF_POW.withDimension(DIM), startPoint, insigma, boundaries, + doTest(TestFunction.SUM_POW.withDimension(DIM), startPoint, insigma, boundaries, GoalType.MINIMIZE, 10, false, 0, 1e-13, 1e-8, 2e-1, 100000, expected); } @Test - public void testSsDiffPow() { - double[] startPoint = OptimTestUtils.point(DIM,1.0); - double[] insigma = OptimTestUtils.point(DIM,0.1); - double[][] boundaries = null; - PointValuePair expected = - new PointValuePair(OptimTestUtils.point(DIM,0.0),0.0); - doTest(TestFunction.SS_DIFF_POW.withDimension(DIM), startPoint, insigma, boundaries, - GoalType.MINIMIZE, 10, true, 0, 1e-13, - 1e-4, 1e-1, 200000, expected); - doTest(TestFunction.SS_DIFF_POW.withDimension(DIM), startPoint, insigma, boundaries, - GoalType.MINIMIZE, 10, false, 0, 1e-13, - 1e-4, 1e-1, 200000, expected); - } - - @Test public void testAckley() { double[] startPoint = OptimTestUtils.point(DIM,1.0); double[] insigma = OptimTestUtils.point(DIM,1.0); diff --git a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/noderiv/SimplexOptimizerTest.java b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/noderiv/SimplexOptimizerTest.java index 23cc75f..a8fc3ff 100644 --- a/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/noderiv/SimplexOptimizerTest.java +++ b/commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/optim/nonlinear/scalar/noderiv/SimplexOptimizerTest.java @@ -33,7 +33,10 @@ import org.junit.jupiter.params.aggregator.ArgumentsAccessor; import org.junit.jupiter.params.aggregator.ArgumentsAggregationException; import org.junit.jupiter.params.aggregator.AggregateWith; import org.junit.jupiter.params.provider.CsvFileSource; +import org.apache.commons.rng.UniformRandomProvider; import org.apache.commons.rng.simple.RandomSource; +import org.apache.commons.rng.sampling.distribution.ContinuousUniformSampler; +import org.apache.commons.rng.sampling.UnitSphereSampler; import org.apache.commons.math4.legacy.core.MathArrays; import org.apache.commons.math4.legacy.exception.MathUnsupportedOperationException; import org.apache.commons.math4.legacy.exception.TooManyEvaluationsException; @@ -89,7 +92,7 @@ public class SimplexOptimizerTest { @ParameterizedTest @CsvFileSource(resources = NELDER_MEAD_INPUT_FILE) void testFunctionWithNelderMead(@AggregateWith(TaskAggregator.class) Task task) { - // task.checkAlongLine(1000, true); + // task.checkAlongLine(1000); task.run(new NelderMeadTransform()); } @@ -114,7 +117,7 @@ public class SimplexOptimizerTest { /** Default convergence criterion. */ private static final double CONVERGENCE_CHECK = 1e-9; /** Default cooling factor. */ - private static final double SA_COOL_FACTOR = 0.5; + private static final double SA_COOL_FACTOR = 0.7; /** Default acceptance probability at beginning of SA. */ private static final double SA_START_PROB = 0.9; /** Default acceptance probability at end of SA. */ @@ -131,8 +134,6 @@ public class SimplexOptimizerTest { private final int functionEvaluations; /** Side length of initial simplex. */ private final double simplexSideLength; - /** Range of random noise. */ - private final double jitter; /** Whether to perform simulated annealing. */ private final boolean withSA; /** File prefix (for saving debugging info). */ @@ -148,7 +149,6 @@ public class SimplexOptimizerTest { * {@code optimum}. * @param functionEvaluations Allowed number of function evaluations. * @param simplexSideLength Side length of initial simplex. - * @param jitter Size of random jitter. * @param withSA Whether to perform simulated annealing. * @param tracePrefix Prefix of the file where to save simplex * transformations during the optimization. @@ -162,7 +162,6 @@ public class SimplexOptimizerTest { double pointTolerance, int functionEvaluations, double simplexSideLength, - double jitter, boolean withSA, String tracePrefix, int[] traceIndices) { @@ -172,7 +171,6 @@ public class SimplexOptimizerTest { this.pointTolerance = pointTolerance; this.functionEvaluations = functionEvaluations; this.simplexSideLength = simplexSideLength; - this.jitter = jitter; this.withSA = withSA; this.tracePrefix = tracePrefix; this.traceIndices = traceIndices; @@ -219,16 +217,12 @@ public class SimplexOptimizerTest { optim.addObserver(createCallback(factory)); } - final Simplex initialSimplex = - Simplex.alongAxes(OptimTestUtils.point(dim, - simplexSideLength, - jitter)); - final double[] startPoint = OptimTestUtils.point(start, jitter); + final Simplex initialSimplex = Simplex.equalSidesAlongAxes(dim, simplexSideLength); final PointValuePair result = optim.optimize(new MaxEval(maxEval), new ObjectiveFunction(function), GoalType.MINIMIZE, - new InitialGuess(startPoint), + new InitialGuess(start), initialSimplex, factory, sa, @@ -258,6 +252,7 @@ public class SimplexOptimizerTest { final String sep = "__"; final String name = tracePrefix + sanitizeBasename(function + sep + + Arrays.toString(start) + sep + factory + sep); // Create file; write first data block (optimum) and columns header. @@ -274,15 +269,17 @@ public class SimplexOptimizerTest { out.println(); out.println("#"); - out.print("# <1: evaluations> <2: objective>"); + out.print("# <1: evaluations> <2: f(x)> <3: |f(x) - f(optimum)|>"); for (int i = 0; i < start.length; i++) { - out.print(" <" + (i + 3) + ": coordinate " + i + ">"); + out.print(" <" + (i + 4) + ": x[" + i + "]>"); } out.println(); } catch (IOException e) { Assertions.fail(e.getMessage()); } + final double fAtOptimum = function.value(optimum); + // Return callback function. return (simplex, isInit, numEval) -> { try (PrintWriter out = new PrintWriter(Files.newBufferedWriter(Paths.get(name), @@ -300,7 +297,8 @@ public class SimplexOptimizerTest { for (int index : traceIndices) { final PointValuePair p = points.get(index); out.print(numEval + fieldSep + - p.getValue() + fieldSep); + p.getValue() + fieldSep + + Math.abs(p.getValue() - fAtOptimum) + fieldSep); final double[] coord = p.getPoint(); for (int i = 0; i < coord.length; i++) { @@ -321,12 +319,10 @@ public class SimplexOptimizerTest { * {@link #start} is reached at the {@link #optimum}. * * @param numPoints Number of points at which to evaluate the function. - * @param plot Whether to generate a file (for visual debugging). */ - public void checkAlongLine(int numPoints, - boolean plot) { - if (plot) { - final String name = createPlotBasename(function, start, optimum); + public void checkAlongLine(int numPoints) { + if (tracePrefix != null) { + final String name = tracePrefix + createPlotBasename(function, start, optimum); try (PrintWriter out = new PrintWriter(Files.newBufferedWriter(Paths.get(name)))) { checkAlongLine(numPoints, out); } catch (IOException e) { @@ -448,14 +444,29 @@ public class SimplexOptimizerTest { final TestFunction funcGen = a.get(index++, TestFunction.class); final int dim = a.getInteger(index++); - final double[] start = toArrayOfDoubles(a.getString(index++), dim); final double[] optimum = toArrayOfDoubles(a.getString(index++), dim); + final double minRadius = a.getDouble(index++); + final double maxRadius = a.getDouble(index++); + if (minRadius < 0 || + maxRadius < 0 || + minRadius >= maxRadius) { + throw new ArgumentsAggregationException("radii"); + } final double pointTol = a.getDouble(index++); final int funcEval = a.getInteger(index++); - final double sideLength = a.getDouble(index++); - final double jitter = a.getDouble(index++); final boolean withSA = a.getBoolean(index++); + // Generate a start point within a spherical shell around the optimum. + final UniformRandomProvider rng = OptimTestUtils.rng(); + final double radius = ContinuousUniformSampler.of(rng, minRadius, maxRadius).sample(); + final double[] start = UnitSphereSampler.of(rng, dim).sample(); + for (int i = 0; i < dim; i++) { + start[i] *= radius; + start[i] += optimum[i]; + } + // Simplex side. + final double sideLength = 0.5 * (maxRadius - minRadius); + if (index == a.size()) { // No more arguments. return new Task(funcGen.withDimension(dim), @@ -464,7 +475,6 @@ public class SimplexOptimizerTest { pointTol, funcEval, sideLength, - jitter, withSA, null, null); @@ -481,7 +491,6 @@ public class SimplexOptimizerTest { pointTol, funcEval, sideLength, - jitter, withSA, tracePrefix, spxIndices);