You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by er...@apache.org on 2021/08/22 00:33:25 UTC

[commons-math] 01/13: Simplify data format for "SimplexOptimizerTest" (randomized) input.

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 <gi...@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);