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.analysis.MultivariateVectorFunction;
27  import org.hipparchus.exception.MathIllegalStateException;
28  import org.hipparchus.exception.MathRuntimeException;
29  import org.hipparchus.linear.Array2DRowRealMatrix;
30  import org.hipparchus.linear.RealMatrix;
31  import org.hipparchus.optim.InitialGuess;
32  import org.hipparchus.optim.MaxEval;
33  import org.hipparchus.optim.PointValuePair;
34  import org.hipparchus.optim.SimpleBounds;
35  import org.hipparchus.optim.nonlinear.scalar.GoalType;
36  import org.hipparchus.optim.nonlinear.scalar.LeastSquaresConverter;
37  import org.hipparchus.optim.nonlinear.scalar.ObjectiveFunction;
38  import org.hipparchus.util.FastMath;
39  import org.junit.jupiter.api.Test;
40  
41  import static org.junit.jupiter.api.Assertions.assertEquals;
42  import static org.junit.jupiter.api.Assertions.assertThrows;
43  import static org.junit.jupiter.api.Assertions.assertTrue;
44  
45  class SimplexOptimizerNelderMeadTest {
46      @Test
47      void testBoundsUnsupported() {
48          assertThrows(MathRuntimeException.class, () -> {
49              SimplexOptimizer optimizer = new SimplexOptimizer(1e-10, 1e-30);
50              final FourExtrema fourExtrema = new FourExtrema();
51  
52              optimizer.optimize(new MaxEval(100),
53                  new ObjectiveFunction(fourExtrema),
54                  GoalType.MINIMIZE,
55                  new InitialGuess(new double[]{-3, 0}),
56                  new NelderMeadSimplex(new double[]{0.2, 0.2}),
57                  new SimpleBounds(new double[]{-5, -1},
58                      new double[]{5, 1}));
59          });
60      }
61  
62      @Test
63      void testMinimize1() {
64          SimplexOptimizer optimizer = new SimplexOptimizer(1e-10, 1e-30);
65          final FourExtrema fourExtrema = new FourExtrema();
66  
67          final PointValuePair optimum
68              = optimizer.optimize(new MaxEval(100),
69                                   new ObjectiveFunction(fourExtrema),
70                                   GoalType.MINIMIZE,
71                                   new InitialGuess(new double[] { -3, 0 }),
72                                   new NelderMeadSimplex(new double[] { 0.2, 0.2 }));
73          assertEquals(fourExtrema.xM, optimum.getPoint()[0], 2e-7);
74          assertEquals(fourExtrema.yP, optimum.getPoint()[1], 2e-5);
75          assertEquals(fourExtrema.valueXmYp, optimum.getValue(), 6e-12);
76          assertTrue(optimizer.getEvaluations() > 60);
77          assertTrue(optimizer.getEvaluations() < 90);
78  
79          // Check that the number of iterations is updated (MATH-949).
80          assertTrue(optimizer.getIterations() > 0);
81      }
82  
83      @Test
84      void testMinimize2() {
85          SimplexOptimizer optimizer = new SimplexOptimizer(1e-10, 1e-30);
86          final FourExtrema fourExtrema = new FourExtrema();
87  
88          final PointValuePair optimum
89              = optimizer.optimize(new MaxEval(100),
90                                   new ObjectiveFunction(fourExtrema),
91                                   GoalType.MINIMIZE,
92                                   new InitialGuess(new double[] { 1, 0 }),
93                                   new NelderMeadSimplex(new double[] { 0.2, 0.2 }));
94          assertEquals(fourExtrema.xP, optimum.getPoint()[0], 5e-6);
95          assertEquals(fourExtrema.yM, optimum.getPoint()[1], 6e-6);
96          assertEquals(fourExtrema.valueXpYm, optimum.getValue(), 1e-11);
97          assertTrue(optimizer.getEvaluations() > 60);
98          assertTrue(optimizer.getEvaluations() < 90);
99  
100         // Check that the number of iterations is updated (MATH-949).
101         assertTrue(optimizer.getIterations() > 0);
102     }
103 
104     @Test
105     void testMaximize1() {
106         SimplexOptimizer optimizer = new SimplexOptimizer(1e-10, 1e-30);
107         final FourExtrema fourExtrema = new FourExtrema();
108 
109         final PointValuePair optimum
110             = optimizer.optimize(new MaxEval(100),
111                                  new ObjectiveFunction(fourExtrema),
112                                  GoalType.MAXIMIZE,
113                                  new InitialGuess(new double[] { -3, 0 }),
114                                  new NelderMeadSimplex(new double[] { 0.2, 0.2 }));
115         assertEquals(fourExtrema.xM, optimum.getPoint()[0], 1e-5);
116         assertEquals(fourExtrema.yM, optimum.getPoint()[1], 3e-6);
117         assertEquals(fourExtrema.valueXmYm, optimum.getValue(), 3e-12);
118         assertTrue(optimizer.getEvaluations() > 60);
119         assertTrue(optimizer.getEvaluations() < 90);
120 
121         // Check that the number of iterations is updated (MATH-949).
122         assertTrue(optimizer.getIterations() > 0);
123     }
124 
125     @Test
126     void testMaximize2() {
127         SimplexOptimizer optimizer = new SimplexOptimizer(1e-10, 1e-30);
128         final FourExtrema fourExtrema = new FourExtrema();
129 
130         final PointValuePair optimum
131             = optimizer.optimize(new MaxEval(100),
132                                  new ObjectiveFunction(fourExtrema),
133                                  GoalType.MAXIMIZE,
134                                  new InitialGuess(new double[] { 1, 0 }),
135                                  new NelderMeadSimplex(new double[] { 0.2, 0.2 }));
136         assertEquals(fourExtrema.xP, optimum.getPoint()[0], 4e-6);
137         assertEquals(fourExtrema.yP, optimum.getPoint()[1], 5e-6);
138         assertEquals(fourExtrema.valueXpYp, optimum.getValue(), 7e-12);
139         assertTrue(optimizer.getEvaluations() > 60);
140         assertTrue(optimizer.getEvaluations() < 90);
141 
142         // Check that the number of iterations is updated (MATH-949).
143         assertTrue(optimizer.getIterations() > 0);
144     }
145 
146     @Test
147     void testRosenbrock() {
148 
149         Rosenbrock rosenbrock = new Rosenbrock();
150         SimplexOptimizer optimizer = new SimplexOptimizer(-1, 1e-3);
151         PointValuePair optimum
152         = optimizer.optimize(new MaxEval(100),
153                              new ObjectiveFunction(rosenbrock),
154                              GoalType.MINIMIZE,
155                              new InitialGuess(new double[] { -1.2, 1 }),
156                                 new NelderMeadSimplex(new double[][] {
157                                         { -1.2,  1 },
158                                         { 0.9, 1.2 },
159                                         {  3.5, -2.3 } }));
160 
161         assertEquals(rosenbrock.getCount(), optimizer.getEvaluations());
162         assertTrue(optimizer.getEvaluations() > 40);
163         assertTrue(optimizer.getEvaluations() < 50);
164         assertTrue(optimum.getValue() < 8e-4);
165     }
166 
167     @Test
168     void testPowell() {
169         Powell powell = new Powell();
170         SimplexOptimizer optimizer = new SimplexOptimizer(-1, 1e-3);
171         PointValuePair optimum =
172             optimizer.optimize(new MaxEval(200),
173                                new ObjectiveFunction(powell),
174                                GoalType.MINIMIZE,
175                                new InitialGuess(new double[] { 3, -1, 0, 1 }),
176                                new NelderMeadSimplex(4));
177         assertEquals(powell.getCount(), optimizer.getEvaluations());
178         assertTrue(optimizer.getEvaluations() > 110);
179         assertTrue(optimizer.getEvaluations() < 130);
180         assertTrue(optimum.getValue() < 2e-3);
181     }
182 
183     @Test
184     void testLeastSquares1() {
185         final RealMatrix factors
186             = new Array2DRowRealMatrix(new double[][] {
187                     { 1, 0 },
188                     { 0, 1 }
189                 }, false);
190         LeastSquaresConverter ls = new LeastSquaresConverter(new MultivariateVectorFunction() {
191                 public double[] value(double[] variables) {
192                     return factors.operate(variables);
193                 }
194             }, new double[] { 2.0, -3.0 });
195         SimplexOptimizer optimizer = new SimplexOptimizer(-1, 1e-6);
196         PointValuePair optimum =
197             optimizer.optimize(new MaxEval(200),
198                                new ObjectiveFunction(ls),
199                                GoalType.MINIMIZE,
200                                new InitialGuess(new double[] { 10, 10 }),
201                                new NelderMeadSimplex(2));
202         assertEquals( 2, optimum.getPointRef()[0], 3e-5);
203         assertEquals(-3, optimum.getPointRef()[1], 4e-4);
204         assertTrue(optimizer.getEvaluations() > 60);
205         assertTrue(optimizer.getEvaluations() < 80);
206         assertTrue(optimum.getValue() < 1.0e-6);
207     }
208 
209     @Test
210     void testLeastSquares2() {
211         final RealMatrix factors
212             = new Array2DRowRealMatrix(new double[][] {
213                     { 1, 0 },
214                     { 0, 1 }
215                 }, false);
216         LeastSquaresConverter ls = new LeastSquaresConverter(new MultivariateVectorFunction() {
217                 public double[] value(double[] variables) {
218                     return factors.operate(variables);
219                 }
220             }, new double[] { 2, -3 }, new double[] { 10, 0.1 });
221         SimplexOptimizer optimizer = new SimplexOptimizer(-1, 1e-6);
222         PointValuePair optimum =
223             optimizer.optimize(new MaxEval(200),
224                                new ObjectiveFunction(ls),
225                                GoalType.MINIMIZE,
226                                new InitialGuess(new double[] { 10, 10 }),
227                                new NelderMeadSimplex(2));
228         assertEquals( 2, optimum.getPointRef()[0], 5e-5);
229         assertEquals(-3, optimum.getPointRef()[1], 8e-4);
230         assertTrue(optimizer.getEvaluations() > 60);
231         assertTrue(optimizer.getEvaluations() < 80);
232         assertTrue(optimum.getValue() < 1e-6);
233     }
234 
235     @Test
236     void testLeastSquares3() {
237         final RealMatrix factors =
238             new Array2DRowRealMatrix(new double[][] {
239                     { 1, 0 },
240                     { 0, 1 }
241                 }, false);
242         LeastSquaresConverter ls = new LeastSquaresConverter(new MultivariateVectorFunction() {
243                 public double[] value(double[] variables) {
244                     return factors.operate(variables);
245                 }
246             }, new double[] { 2, -3 }, new Array2DRowRealMatrix(new double [][] {
247                     { 1, 1.2 }, { 1.2, 2 }
248                 }));
249         SimplexOptimizer optimizer = new SimplexOptimizer(-1, 1e-6);
250         PointValuePair optimum
251             = optimizer.optimize(new MaxEval(200),
252                                  new ObjectiveFunction(ls),
253                                  GoalType.MINIMIZE,
254                                  new InitialGuess(new double[] { 10, 10 }),
255                                  new NelderMeadSimplex(2));
256         assertEquals( 2, optimum.getPointRef()[0], 2e-3);
257         assertEquals(-3, optimum.getPointRef()[1], 8e-4);
258         assertTrue(optimizer.getEvaluations() > 60);
259         assertTrue(optimizer.getEvaluations() < 80);
260         assertTrue(optimum.getValue() < 1e-6);
261     }
262 
263     @Test
264     void testMaxIterations() {
265         assertThrows(MathIllegalStateException.class, () -> {
266             Powell powell = new Powell();
267             SimplexOptimizer optimizer = new SimplexOptimizer(-1, 1e-3);
268             optimizer.optimize(new MaxEval(20),
269                 new ObjectiveFunction(powell),
270                 GoalType.MINIMIZE,
271                 new InitialGuess(new double[]{3, -1, 0, 1}),
272                 new NelderMeadSimplex(4));
273         });
274     }
275 
276     private static class FourExtrema implements MultivariateFunction {
277         // The following function has 4 local extrema.
278         final double xM = -3.841947088256863675365;
279         final double yM = -1.391745200270734924416;
280         final double xP =  0.2286682237349059125691;
281         final double yP = -yM;
282         final double valueXmYm = 0.2373295333134216789769; // Local maximum.
283         final double valueXmYp = -valueXmYm; // Local minimum.
284         final double valueXpYm = -0.7290400707055187115322; // Global minimum.
285         final double valueXpYp = -valueXpYm; // Global maximum.
286 
287         public double value(double[] variables) {
288             final double x = variables[0];
289             final double y = variables[1];
290             return (x == 0 || y == 0) ? 0 :
291                 FastMath.atan(x) * FastMath.atan(x + 2) * FastMath.atan(y) * FastMath.atan(y) / (x * y);
292         }
293     }
294 
295     private static class Rosenbrock implements MultivariateFunction {
296         private int count;
297 
298         public Rosenbrock() {
299             count = 0;
300         }
301 
302         public double value(double[] x) {
303             ++count;
304             double a = x[1] - x[0] * x[0];
305             double b = 1.0 - x[0];
306             return 100 * a * a + b * b;
307         }
308 
309         public int getCount() {
310             return count;
311         }
312     }
313 
314     private static class Powell implements MultivariateFunction {
315         private int count;
316 
317         public Powell() {
318             count = 0;
319         }
320 
321         public double value(double[] x) {
322             ++count;
323             double a = x[0] + 10 * x[1];
324             double b = x[2] - x[3];
325             double c = x[1] - 2 * x[2];
326             double d = x[0] - x[3];
327             return a * a + 5 * b * b + c * c * c * c + 10 * d * d * d * d;
328         }
329 
330         public int getCount() {
331             return count;
332         }
333     }
334 }