View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  
23  package org.hipparchus.optim.nonlinear.scalar.noderiv;
24  
25  import org.hipparchus.analysis.MultivariateFunction;
26  import org.hipparchus.exception.MathRuntimeException;
27  import org.hipparchus.optim.InitialGuess;
28  import org.hipparchus.optim.MaxEval;
29  import org.hipparchus.optim.PointValuePair;
30  import org.hipparchus.optim.SimpleBounds;
31  import org.hipparchus.optim.SimpleValueChecker;
32  import org.hipparchus.optim.nonlinear.scalar.GoalType;
33  import org.hipparchus.optim.nonlinear.scalar.ObjectiveFunction;
34  import org.hipparchus.util.FastMath;
35  import org.junit.jupiter.api.Test;
36  
37  import static org.junit.jupiter.api.Assertions.assertEquals;
38  import static org.junit.jupiter.api.Assertions.assertThrows;
39  import static org.junit.jupiter.api.Assertions.assertTrue;
40  
41  class SimplexOptimizerMultiDirectionalTest {
42      @Test
43      void testBoundsUnsupported() {
44          assertThrows(MathRuntimeException.class, () -> {
45              SimplexOptimizer optimizer = new SimplexOptimizer(1e-10, 1e-30);
46              final FourExtrema fourExtrema = new FourExtrema();
47  
48              optimizer.optimize(new MaxEval(100),
49                  new ObjectiveFunction(fourExtrema),
50                  GoalType.MINIMIZE,
51                  new InitialGuess(new double[]{-3, 0}),
52                  new NelderMeadSimplex(new double[]{0.2, 0.2}),
53                  new SimpleBounds(new double[]{-5, -1},
54                      new double[]{5, 1}));
55          });
56      }
57  
58      @Test
59      void testMinimize1() {
60          SimplexOptimizer optimizer = new SimplexOptimizer(1e-11, 1e-30);
61          final FourExtrema fourExtrema = new FourExtrema();
62  
63          final PointValuePair optimum
64              = optimizer.optimize(new MaxEval(200),
65                                   new ObjectiveFunction(fourExtrema),
66                                   GoalType.MINIMIZE,
67                                   new InitialGuess(new double[] { -3, 0 }),
68                                   new MultiDirectionalSimplex(new double[] { 0.2, 0.2 }));
69          assertEquals(fourExtrema.xM, optimum.getPoint()[0], 4e-6);
70          assertEquals(fourExtrema.yP, optimum.getPoint()[1], 3e-6);
71          assertEquals(fourExtrema.valueXmYp, optimum.getValue(), 8e-13);
72          assertTrue(optimizer.getEvaluations() > 120);
73          assertTrue(optimizer.getEvaluations() < 150);
74  
75          // Check that the number of iterations is updated (MATH-949).
76          assertTrue(optimizer.getIterations() > 0);
77      }
78  
79      @Test
80      void testMinimize2() {
81          SimplexOptimizer optimizer = new SimplexOptimizer(1e-11, 1e-30);
82          final FourExtrema fourExtrema = new FourExtrema();
83  
84          final PointValuePair optimum
85              = optimizer.optimize(new MaxEval(200),
86                                   new ObjectiveFunction(fourExtrema),
87                                   GoalType.MINIMIZE,
88                                   new InitialGuess(new double[] { 1, 0 }),
89                                   new MultiDirectionalSimplex(new double[] { 0.2, 0.2 }));
90          assertEquals(fourExtrema.xP, optimum.getPoint()[0], 2e-8);
91          assertEquals(fourExtrema.yM, optimum.getPoint()[1], 3e-6);
92          assertEquals(fourExtrema.valueXpYm, optimum.getValue(), 2e-12);
93          assertTrue(optimizer.getEvaluations() > 120);
94          assertTrue(optimizer.getEvaluations() < 150);
95  
96          // Check that the number of iterations is updated (MATH-949).
97          assertTrue(optimizer.getIterations() > 0);
98      }
99  
100     @Test
101     void testMaximize1() {
102         SimplexOptimizer optimizer = new SimplexOptimizer(1e-11, 1e-30);
103         final FourExtrema fourExtrema = new FourExtrema();
104 
105         final PointValuePair optimum
106             = optimizer.optimize(new MaxEval(200),
107                                  new ObjectiveFunction(fourExtrema),
108                                  GoalType.MAXIMIZE,
109                                  new InitialGuess(new double[] { -3.0, 0.0 }),
110                                  new MultiDirectionalSimplex(new double[] { 0.2, 0.2 }));
111         assertEquals(fourExtrema.xM, optimum.getPoint()[0], 7e-7);
112         assertEquals(fourExtrema.yM, optimum.getPoint()[1], 3e-7);
113         assertEquals(fourExtrema.valueXmYm, optimum.getValue(), 2e-14);
114         assertTrue(optimizer.getEvaluations() > 120);
115         assertTrue(optimizer.getEvaluations() < 150);
116 
117         // Check that the number of iterations is updated (MATH-949).
118         assertTrue(optimizer.getIterations() > 0);
119     }
120 
121     @Test
122     void testMaximize2() {
123         SimplexOptimizer optimizer = new SimplexOptimizer(new SimpleValueChecker(1e-15, 1e-30));
124         final FourExtrema fourExtrema = new FourExtrema();
125 
126         final PointValuePair optimum
127             = optimizer.optimize(new MaxEval(200),
128                                  new ObjectiveFunction(fourExtrema),
129                                  GoalType.MAXIMIZE,
130                                  new InitialGuess(new double[] { 1, 0 }),
131                                  new MultiDirectionalSimplex(new double[] { 0.2, 0.2 }));
132         assertEquals(fourExtrema.xP, optimum.getPoint()[0], 2e-8);
133         assertEquals(fourExtrema.yP, optimum.getPoint()[1], 3e-6);
134         assertEquals(fourExtrema.valueXpYp, optimum.getValue(), 2e-12);
135         assertTrue(optimizer.getEvaluations() > 180);
136         assertTrue(optimizer.getEvaluations() < 220);
137 
138         // Check that the number of iterations is updated (MATH-949).
139         assertTrue(optimizer.getIterations() > 0);
140     }
141 
142     @Test
143     void testRosenbrock() {
144         MultivariateFunction rosenbrock
145             = new MultivariateFunction() {
146                     public double value(double[] x) {
147                         ++count;
148                         double a = x[1] - x[0] * x[0];
149                         double b = 1.0 - x[0];
150                         return 100 * a * a + b * b;
151                     }
152                 };
153 
154         count = 0;
155         SimplexOptimizer optimizer = new SimplexOptimizer(-1, 1e-3);
156         PointValuePair optimum
157            = optimizer.optimize(new MaxEval(100),
158                                 new ObjectiveFunction(rosenbrock),
159                                 GoalType.MINIMIZE,
160                                 new InitialGuess(new double[] { -1.2, 1 }),
161                                 new MultiDirectionalSimplex(new double[][] {
162                                         { -1.2,  1.0 },
163                                         { 0.9, 1.2 },
164                                         {  3.5, -2.3 } }));
165 
166         assertEquals(count, optimizer.getEvaluations());
167         assertTrue(optimizer.getEvaluations() > 50);
168         assertTrue(optimizer.getEvaluations() < 100);
169         assertTrue(optimum.getValue() > 1e-2);
170     }
171 
172     @Test
173     void testPowell() {
174         MultivariateFunction powell
175             = new MultivariateFunction() {
176                     public double value(double[] x) {
177                         ++count;
178                         double a = x[0] + 10 * x[1];
179                         double b = x[2] - x[3];
180                         double c = x[1] - 2 * x[2];
181                         double d = x[0] - x[3];
182                         return a * a + 5 * b * b + c * c * c * c + 10 * d * d * d * d;
183                     }
184                 };
185 
186         count = 0;
187         SimplexOptimizer optimizer = new SimplexOptimizer(-1, 1e-3);
188         PointValuePair optimum
189             = optimizer.optimize(new MaxEval(1000),
190                                  new ObjectiveFunction(powell),
191                                  GoalType.MINIMIZE,
192                                  new InitialGuess(new double[] { 3, -1, 0, 1 }),
193                                  new MultiDirectionalSimplex(4));
194         assertEquals(count, optimizer.getEvaluations());
195         assertTrue(optimizer.getEvaluations() > 800);
196         assertTrue(optimizer.getEvaluations() < 900);
197         assertTrue(optimum.getValue() > 1e-2);
198     }
199 
200     @Test
201     void testMath283() {
202         // fails because MultiDirectional.iterateSimplex is looping forever
203         // the while(true) should be replaced with a convergence check
204         SimplexOptimizer optimizer = new SimplexOptimizer(1e-14, 1e-14);
205         final Gaussian2D function = new Gaussian2D(0, 0, 1);
206         PointValuePair estimate = optimizer.optimize(new MaxEval(1000),
207                                                      new ObjectiveFunction(function),
208                                                      GoalType.MAXIMIZE,
209                                                      new InitialGuess(function.getMaximumPosition()),
210                                                      new MultiDirectionalSimplex(2));
211         final double EPSILON = 1e-5;
212         final double expectedMaximum = function.getMaximum();
213         final double actualMaximum = estimate.getValue();
214         assertEquals(expectedMaximum, actualMaximum, EPSILON);
215 
216         final double[] expectedPosition = function.getMaximumPosition();
217         final double[] actualPosition = estimate.getPoint();
218         assertEquals(expectedPosition[0], actualPosition[0], EPSILON );
219         assertEquals(expectedPosition[1], actualPosition[1], EPSILON );
220     }
221 
222     private static class FourExtrema implements MultivariateFunction {
223         // The following function has 4 local extrema.
224         final double xM = -3.841947088256863675365;
225         final double yM = -1.391745200270734924416;
226         final double xP =  0.2286682237349059125691;
227         final double yP = -yM;
228         final double valueXmYm = 0.2373295333134216789769; // Local maximum.
229         final double valueXmYp = -valueXmYm; // Local minimum.
230         final double valueXpYm = -0.7290400707055187115322; // Global minimum.
231         final double valueXpYp = -valueXpYm; // Global maximum.
232 
233         public double value(double[] variables) {
234             final double x = variables[0];
235             final double y = variables[1];
236             return (x == 0 || y == 0) ? 0 :
237                 FastMath.atan(x) * FastMath.atan(x + 2) * FastMath.atan(y) * FastMath.atan(y) / (x * y);
238         }
239     }
240 
241     private static class Gaussian2D implements MultivariateFunction {
242         private final double[] maximumPosition;
243         private final double std;
244 
245         public Gaussian2D(double xOpt, double yOpt, double std) {
246             maximumPosition = new double[] { xOpt, yOpt };
247             this.std = std;
248         }
249 
250         public double getMaximum() {
251             return value(maximumPosition);
252         }
253 
254         public double[] getMaximumPosition() {
255             return maximumPosition.clone();
256         }
257 
258         public double value(double[] point) {
259             final double x = point[0], y = point[1];
260             final double twoS2 = 2.0 * std * std;
261             return 1.0 / (twoS2 * FastMath.PI) * FastMath.exp(-(x * x + y * y) / twoS2);
262         }
263     }
264 
265     private int count;
266 }