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