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