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  package org.hipparchus.optim.nonlinear.scalar.noderiv;
23  
24  import org.hipparchus.analysis.MultivariateFunction;
25  import org.hipparchus.analysis.SumSincFunction;
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.nonlinear.scalar.GoalType;
32  import org.hipparchus.optim.nonlinear.scalar.ObjectiveFunction;
33  import org.hipparchus.util.FastMath;
34  import org.junit.jupiter.api.Test;
35  
36  import static org.junit.jupiter.api.Assertions.assertEquals;
37  import static org.junit.jupiter.api.Assertions.assertThrows;
38  import static org.junit.jupiter.api.Assertions.assertTrue;
39  
40  /**
41   * Test for {@link PowellOptimizer}.
42   */
43  class PowellOptimizerTest {
44      @Test
45      void testBoundsUnsupported() {
46          assertThrows(MathRuntimeException.class, () -> {
47              final MultivariateFunction func = new SumSincFunction(-1);
48              final PowellOptimizer optim = new PowellOptimizer(1e-8, 1e-5,
49                  1e-4, 1e-4);
50  
51              optim.optimize(new MaxEval(100),
52                  new ObjectiveFunction(func),
53                  GoalType.MINIMIZE,
54                  new InitialGuess(new double[]{-3, 0}),
55                  new SimpleBounds(new double[]{-5, -1},
56                      new double[]{5, 1}));
57          });
58      }
59  
60      @Test
61      void testSumSinc() {
62          final MultivariateFunction func = new SumSincFunction(-1);
63  
64          int dim = 2;
65          final double[] minPoint = new double[dim];
66          for (int i = 0; i < dim; i++) {
67              minPoint[i] = 0;
68          }
69  
70          double[] init = new double[dim];
71  
72          // Initial is minimum.
73          for (int i = 0; i < dim; i++) {
74              init[i] = minPoint[i];
75          }
76          doTest(func, minPoint, init, GoalType.MINIMIZE, 1e-9, 1e-9);
77  
78          // Initial is far from minimum.
79          for (int i = 0; i < dim; i++) {
80              init[i] = minPoint[i] + 3;
81          }
82          doTest(func, minPoint, init, GoalType.MINIMIZE, 1e-9, 1e-5);
83          // More stringent line search tolerance enhances the precision
84          // of the result.
85          doTest(func, minPoint, init, GoalType.MINIMIZE, 1e-9, 1e-9, 1e-7);
86      }
87  
88      @Test
89      void testQuadratic() {
90          final MultivariateFunction func = new MultivariateFunction() {
91                  public double value(double[] x) {
92                      final double a = x[0] - 1;
93                      final double b = x[1] - 1;
94                      return a * a + b * b + 1;
95                  }
96              };
97  
98          int dim = 2;
99          final double[] minPoint = new double[dim];
100         for (int i = 0; i < dim; i++) {
101             minPoint[i] = 1;
102         }
103 
104         double[] init = new double[dim];
105 
106         // Initial is minimum.
107         for (int i = 0; i < dim; i++) {
108             init[i] = minPoint[i];
109         }
110         doTest(func, minPoint, init, GoalType.MINIMIZE, 1e-9, 1e-8);
111 
112         // Initial is far from minimum.
113         for (int i = 0; i < dim; i++) {
114             init[i] = minPoint[i] - 20;
115         }
116         doTest(func, minPoint, init, GoalType.MINIMIZE, 1e-9, 1e-8);
117     }
118 
119     @Test
120     void testMaximizeQuadratic() {
121         final MultivariateFunction func = new MultivariateFunction() {
122                 public double value(double[] x) {
123                     final double a = x[0] - 1;
124                     final double b = x[1] - 1;
125                     return -a * a - b * b + 1;
126                 }
127             };
128 
129         int dim = 2;
130         final double[] maxPoint = new double[dim];
131         for (int i = 0; i < dim; i++) {
132             maxPoint[i] = 1;
133         }
134 
135         double[] init = new double[dim];
136 
137         // Initial is minimum.
138         for (int i = 0; i < dim; i++) {
139             init[i] = maxPoint[i];
140         }
141         doTest(func, maxPoint, init,  GoalType.MAXIMIZE, 1e-9, 1e-8);
142 
143         // Initial is far from minimum.
144         for (int i = 0; i < dim; i++) {
145             init[i] = maxPoint[i] - 20;
146         }
147         doTest(func, maxPoint, init, GoalType.MAXIMIZE, 1e-9, 1e-8);
148     }
149 
150     /**
151      * Ensure that we do not increase the number of function evaluations when
152      * the function values are scaled up.
153      * Note that the tolerances parameters passed to the constructor must
154      * still hold sensible values because they are used to set the line search
155      * tolerances.
156      */
157     @Test
158     void testRelativeToleranceOnScaledValues() {
159         final MultivariateFunction func = new MultivariateFunction() {
160                 public double value(double[] x) {
161                     final double a = x[0] - 1;
162                     final double b = x[1] - 1;
163                     return a * a * FastMath.sqrt(FastMath.abs(a)) + b * b + 1;
164                 }
165             };
166 
167         int dim = 2;
168         final double[] minPoint = new double[dim];
169         for (int i = 0; i < dim; i++) {
170             minPoint[i] = 1;
171         }
172 
173         double[] init = new double[dim];
174         // Initial is far from minimum.
175         for (int i = 0; i < dim; i++) {
176             init[i] = minPoint[i] - 20;
177         }
178 
179         final double relTol = 1e-10;
180 
181         final int maxEval = 1000;
182         // Very small absolute tolerance to rely solely on the relative
183         // tolerance as a stopping criterion
184         final PowellOptimizer optim = new PowellOptimizer(relTol, 1e-100);
185 
186         final PointValuePair funcResult = optim.optimize(new MaxEval(maxEval),
187                                                          new ObjectiveFunction(func),
188                                                          GoalType.MINIMIZE,
189                                                          new InitialGuess(init));
190         final double funcValue = func.value(funcResult.getPoint());
191         final int funcEvaluations = optim.getEvaluations();
192 
193         final double scale = 1e10;
194         final MultivariateFunction funcScaled = new MultivariateFunction() {
195                 public double value(double[] x) {
196                     return scale * func.value(x);
197                 }
198             };
199 
200         final PointValuePair funcScaledResult = optim.optimize(new MaxEval(maxEval),
201                                                                new ObjectiveFunction(funcScaled),
202                                                                GoalType.MINIMIZE,
203                                                                new InitialGuess(init));
204         final double funcScaledValue = funcScaled.value(funcScaledResult.getPoint());
205         final int funcScaledEvaluations = optim.getEvaluations();
206 
207         // Check that both minima provide the same objective funciton values,
208         // within the relative function tolerance.
209         assertEquals(1, funcScaledValue / (scale * funcValue), relTol);
210 
211         // Check that the numbers of evaluations are the same.
212         assertEquals(funcEvaluations, funcScaledEvaluations);
213     }
214 
215     /**
216      * @param func Function to optimize.
217      * @param optimum Expected optimum.
218      * @param init Starting point.
219      * @param goal Minimization or maximization.
220      * @param fTol Tolerance (relative error on the objective function) for
221      * "Powell" algorithm.
222      * @param pointTol Tolerance for checking that the optimum is correct.
223      */
224     private void doTest(MultivariateFunction func,
225                         double[] optimum,
226                         double[] init,
227                         GoalType goal,
228                         double fTol,
229                         double pointTol) {
230         final PowellOptimizer optim = new PowellOptimizer(fTol, FastMath.ulp(1d));
231 
232         final PointValuePair result = optim.optimize(new MaxEval(1000),
233                                                      new ObjectiveFunction(func),
234                                                      goal,
235                                                      new InitialGuess(init));
236         final double[] point = result.getPoint();
237 
238         for (int i = 0, dim = optimum.length; i < dim; i++) {
239             assertEquals(optimum[i], point[i], pointTol, "found[" + i + "]=" + point[i] + " value=" + result.getValue());
240         }
241     }
242 
243     /**
244      * @param func Function to optimize.
245      * @param optimum Expected optimum.
246      * @param init Starting point.
247      * @param goal Minimization or maximization.
248      * @param fTol Tolerance (relative error on the objective function) for
249      * "Powell" algorithm.
250      * @param fLineTol Tolerance (relative error on the objective function)
251      * for the internal line search algorithm.
252      * @param pointTol Tolerance for checking that the optimum is correct.
253      */
254     private void doTest(MultivariateFunction func,
255                         double[] optimum,
256                         double[] init,
257                         GoalType goal,
258                         double fTol,
259                         double fLineTol,
260                         double pointTol) {
261         final PowellOptimizer optim = new PowellOptimizer(fTol, FastMath.ulp(1d),
262                                                           fLineTol, FastMath.ulp(1d));
263 
264         final PointValuePair result = optim.optimize(new MaxEval(1000),
265                                                      new ObjectiveFunction(func),
266                                                      goal,
267                                                      new InitialGuess(init));
268         final double[] point = result.getPoint();
269 
270         for (int i = 0, dim = optimum.length; i < dim; i++) {
271             assertEquals(optimum[i], point[i], pointTol, "found[" + i + "]=" + point[i] + " value=" + result.getValue());
272         }
273 
274         assertTrue(optim.getIterations() > 0);
275     }
276 }