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.univariate;
23  
24  
25  import org.hipparchus.UnitTestUtils;
26  import org.hipparchus.analysis.FunctionUtils;
27  import org.hipparchus.analysis.QuinticFunction;
28  import org.hipparchus.analysis.UnivariateFunction;
29  import org.hipparchus.analysis.function.Sin;
30  import org.hipparchus.analysis.function.StepFunction;
31  import org.hipparchus.exception.LocalizedCoreFormats;
32  import org.hipparchus.exception.MathIllegalArgumentException;
33  import org.hipparchus.exception.MathIllegalStateException;
34  import org.hipparchus.optim.ConvergenceChecker;
35  import org.hipparchus.optim.MaxEval;
36  import org.hipparchus.optim.nonlinear.scalar.GoalType;
37  import org.hipparchus.util.FastMath;
38  import org.junit.Assert;
39  import org.junit.Test;
40  
41  /**
42   */
43  public final class BrentOptimizerTest {
44  
45      @Test
46      public void testSinMin() {
47          UnivariateFunction f = new Sin();
48          UnivariateOptimizer optimizer = new BrentOptimizer(1e-10, 1e-14);
49          Assert.assertEquals(3 * FastMath.PI / 2, optimizer.optimize(new MaxEval(200),
50                                                                      new UnivariateObjectiveFunction(f),
51                                                                      GoalType.MINIMIZE,
52                                                                      new SearchInterval(4, 5)).getPoint(), 1e-8);
53          Assert.assertTrue(optimizer.getEvaluations() <= 50);
54          Assert.assertEquals(200, optimizer.getMaxEvaluations());
55          Assert.assertEquals(3 * FastMath.PI / 2, optimizer.optimize(new MaxEval(200),
56                                                                      new UnivariateObjectiveFunction(f),
57                                                                      GoalType.MINIMIZE,
58                                                                      new SearchInterval(1, 5)).getPoint(), 1e-8);
59          Assert.assertTrue(optimizer.getEvaluations() <= 100);
60          Assert.assertTrue(optimizer.getEvaluations() >= 15);
61          try {
62              optimizer.optimize(new MaxEval(10),
63                                 new UnivariateObjectiveFunction(f),
64                                 GoalType.MINIMIZE,
65                                 new SearchInterval(4, 5));
66              Assert.fail("an exception should have been thrown");
67          } catch (MathIllegalStateException fee) {
68              // expected
69          }
70      }
71  
72      @Test
73      public void testSinMinWithValueChecker() {
74          final UnivariateFunction f = new Sin();
75          final ConvergenceChecker<UnivariatePointValuePair> checker = new SimpleUnivariateValueChecker(1e-5, 1e-14);
76          // The default stopping criterion of Brent's algorithm should not
77          // pass, but the search will stop at the given relative tolerance
78          // for the function value.
79          final UnivariateOptimizer optimizer = new BrentOptimizer(1e-10, 1e-14, checker);
80          final UnivariatePointValuePair result = optimizer.optimize(new MaxEval(200),
81                                                                     new UnivariateObjectiveFunction(f),
82                                                                     GoalType.MINIMIZE,
83                                                                     new SearchInterval(4, 5));
84          Assert.assertEquals(3 * FastMath.PI / 2, result.getPoint(), 1e-3);
85      }
86  
87      @Test
88      public void testBoundaries() {
89          final double lower = -1.0;
90          final double upper = +1.0;
91          UnivariateFunction f = new UnivariateFunction() {
92              @Override
93              public double value(double x) {
94                  if (x < lower) {
95                      throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL,
96                                                             x, lower);
97                  } else if (x > upper) {
98                      throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_LARGE,
99                                                             x, upper);
100                 } else {
101                     return x;
102                 }
103             }
104         };
105         UnivariateOptimizer optimizer = new BrentOptimizer(1e-10, 1e-14);
106         Assert.assertEquals(lower,
107                             optimizer.optimize(new MaxEval(100),
108                                                new UnivariateObjectiveFunction(f),
109                                                GoalType.MINIMIZE,
110                                                new SearchInterval(lower, upper)).getPoint(),
111                             1.0e-8);
112         Assert.assertEquals(upper,
113                             optimizer.optimize(new MaxEval(100),
114                                                new UnivariateObjectiveFunction(f),
115                                                GoalType.MAXIMIZE,
116                                                new SearchInterval(lower, upper)).getPoint(),
117                             1.0e-8);
118     }
119 
120     @Test
121     public void testQuinticMin() {
122         // The function has local minima at -0.27195613 and 0.82221643.
123         UnivariateFunction f = new QuinticFunction();
124         UnivariateOptimizer optimizer = new BrentOptimizer(1e-10, 1e-14);
125         Assert.assertEquals(-0.27195613, optimizer.optimize(new MaxEval(200),
126                                                             new UnivariateObjectiveFunction(f),
127                                                             GoalType.MINIMIZE,
128                                                             new SearchInterval(-0.3, -0.2)).getPoint(), 1.0e-8);
129         Assert.assertEquals( 0.82221643, optimizer.optimize(new MaxEval(200),
130                                                             new UnivariateObjectiveFunction(f),
131                                                             GoalType.MINIMIZE,
132                                                             new SearchInterval(0.3,  0.9)).getPoint(), 1.0e-8);
133         Assert.assertTrue(optimizer.getEvaluations() <= 50);
134 
135         // search in a large interval
136         Assert.assertEquals(-0.27195613, optimizer.optimize(new MaxEval(200),
137                                                             new UnivariateObjectiveFunction(f),
138                                                             GoalType.MINIMIZE,
139                                                             new SearchInterval(-1.0, 0.2)).getPoint(), 1.0e-8);
140         Assert.assertTrue(optimizer.getEvaluations() <= 50);
141     }
142 
143     @Test
144     public void testQuinticMinStatistics() {
145         // The function has local minima at -0.27195613 and 0.82221643.
146         UnivariateFunction f = new QuinticFunction();
147         UnivariateOptimizer optimizer = new BrentOptimizer(1e-11, 1e-14);
148 
149         final UnitTestUtils.SimpleStatistics[] stat = new UnitTestUtils.SimpleStatistics[2];
150         for (int i = 0; i < stat.length; i++) {
151             stat[i] = new UnitTestUtils.SimpleStatistics();
152         }
153 
154         final double min = -0.75;
155         final double max = 0.25;
156         final int nSamples = 200;
157         final double delta = (max - min) / nSamples;
158         for (int i = 0; i < nSamples; i++) {
159             final double start = min + i * delta;
160             stat[0].addValue(optimizer.optimize(new MaxEval(40),
161                                                 new UnivariateObjectiveFunction(f),
162                                                 GoalType.MINIMIZE,
163                                                 new SearchInterval(min, max, start)).getPoint());
164             stat[1].addValue(optimizer.getEvaluations());
165         }
166 
167         final double meanOptValue = stat[0].getMean();
168         final double medianEval = stat[1].getMedian();
169         Assert.assertTrue(meanOptValue > -0.2719561281);
170         Assert.assertTrue(meanOptValue < -0.2719561280);
171         Assert.assertEquals(23, (int) medianEval);
172 
173         // MATH-1121: Ensure that the iteration counter is incremented.
174         Assert.assertTrue(optimizer.getIterations() > 0);
175     }
176 
177     @Test
178     public void testQuinticMax() {
179         // The quintic function has zeros at 0, +-0.5 and +-1.
180         // The function has a local maximum at 0.27195613.
181         UnivariateFunction f = new QuinticFunction();
182         UnivariateOptimizer optimizer = new BrentOptimizer(1e-12, 1e-14);
183         Assert.assertEquals(0.27195613, optimizer.optimize(new MaxEval(100),
184                                                            new UnivariateObjectiveFunction(f),
185                                                            GoalType.MAXIMIZE,
186                                                            new SearchInterval(0.2, 0.3)).getPoint(), 1e-8);
187         try {
188             optimizer.optimize(new MaxEval(5),
189                                new UnivariateObjectiveFunction(f),
190                                GoalType.MAXIMIZE,
191                                new SearchInterval(0.2, 0.3));
192             Assert.fail("an exception should have been thrown");
193         } catch (MathIllegalStateException miee) {
194             // expected
195         }
196     }
197 
198     @Test
199     public void testMinEndpoints() {
200         UnivariateFunction f = new Sin();
201         UnivariateOptimizer optimizer = new BrentOptimizer(1e-8, 1e-14);
202 
203         // endpoint is minimum
204         double result = optimizer.optimize(new MaxEval(50),
205                                            new UnivariateObjectiveFunction(f),
206                                            GoalType.MINIMIZE,
207                                            new SearchInterval(3 * FastMath.PI / 2, 5)).getPoint();
208         Assert.assertEquals(3 * FastMath.PI / 2, result, 1e-6);
209 
210         result = optimizer.optimize(new MaxEval(50),
211                                     new UnivariateObjectiveFunction(f),
212                                     GoalType.MINIMIZE,
213                                     new SearchInterval(4, 3 * FastMath.PI / 2)).getPoint();
214         Assert.assertEquals(3 * FastMath.PI / 2, result, 1e-6);
215     }
216 
217     @Test
218     public void testMath832() {
219         final UnivariateFunction f = new UnivariateFunction() {
220                 @Override
221                 public double value(double x) {
222                     final double sqrtX = FastMath.sqrt(x);
223                     final double a = 1e2 * sqrtX;
224                     final double b = 1e6 / x;
225                     final double c = 1e4 / sqrtX;
226 
227                     return a + b + c;
228                 }
229             };
230 
231         UnivariateOptimizer optimizer = new BrentOptimizer(1e-10, 1e-8);
232         final double result = optimizer.optimize(new MaxEval(1483),
233                                                  new UnivariateObjectiveFunction(f),
234                                                  GoalType.MINIMIZE,
235                                                  new SearchInterval(Double.MIN_VALUE,
236                                                                     Double.MAX_VALUE)).getPoint();
237 
238         Assert.assertEquals(804.9355825, result, 1e-6);
239     }
240 
241     /**
242      * Contrived example showing that prior to the resolution of MATH-855
243      * (second revision), the algorithm would not return the best point if
244      * it happened to be the initial guess.
245      */
246     @Test
247     public void testKeepInitIfBest() {
248         final double minSin = 3 * FastMath.PI / 2;
249         final double offset = 1e-8;
250         final double delta = 1e-7;
251         final UnivariateFunction f1 = new Sin();
252         final UnivariateFunction f2 = new StepFunction(new double[] { minSin, minSin + offset, minSin + 2 * offset},
253                                                        new double[] { 0, -1, 0 });
254         final UnivariateFunction f = FunctionUtils.add(f1, f2);
255         // A slightly less stringent tolerance would make the test pass
256         // even with the previous implementation.
257         final double relTol = 1e-8;
258         final UnivariateOptimizer optimizer = new BrentOptimizer(relTol, 1e-100);
259         final double init = minSin + 1.5 * offset;
260         final UnivariatePointValuePair result
261             = optimizer.optimize(new MaxEval(200),
262                                  new UnivariateObjectiveFunction(f),
263                                  GoalType.MINIMIZE,
264                                  new SearchInterval(minSin - 6.789 * delta,
265                                                     minSin + 9.876 * delta,
266                                                     init));
267 
268         final double sol = result.getPoint();
269         final double expected = init;
270 
271 //         System.out.println("numEval=" + numEval);
272 //         System.out.println("min=" + init + " f=" + f.value(init));
273 //         System.out.println("sol=" + sol + " f=" + f.value(sol));
274 //         System.out.println("exp=" + expected + " f=" + f.value(expected));
275 
276         Assert.assertTrue("Best point not reported", f.value(sol) <= f.value(expected));
277     }
278 
279     /**
280      * Contrived example showing that prior to the resolution of MATH-855,
281      * the algorithm, by always returning the last evaluated point, would
282      * sometimes not report the best point it had found.
283      */
284     @Test
285     public void testMath855() {
286         final double minSin = 3 * FastMath.PI / 2;
287         final double offset = 1e-8;
288         final double delta = 1e-7;
289         final UnivariateFunction f1 = new Sin();
290         final UnivariateFunction f2 = new StepFunction(new double[] { minSin, minSin + offset, minSin + 5 * offset },
291                                                        new double[] { 0, -1, 0 });
292         final UnivariateFunction f = FunctionUtils.add(f1, f2);
293         final UnivariateOptimizer optimizer = new BrentOptimizer(1e-8, 1e-100);
294         final UnivariatePointValuePair result
295             = optimizer.optimize(new MaxEval(200),
296                                  new UnivariateObjectiveFunction(f),
297                                  GoalType.MINIMIZE,
298                                  new SearchInterval(minSin - 6.789 * delta,
299                                                     minSin + 9.876 * delta));
300 
301         final double sol = result.getPoint();
302         final double expected = 4.712389027602411;
303 
304         // System.out.println("min=" + (minSin + offset) + " f=" + f.value(minSin + offset));
305         // System.out.println("sol=" + sol + " f=" + f.value(sol));
306         // System.out.println("exp=" + expected + " f=" + f.value(expected));
307 
308         Assert.assertTrue("Best point not reported", f.value(sol) <= f.value(expected));
309     }
310 }