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 java.util.Arrays;
25  import java.util.Random;
26  
27  import org.hipparchus.analysis.MultivariateFunction;
28  import org.hipparchus.exception.MathIllegalArgumentException;
29  import org.hipparchus.exception.MathIllegalStateException;
30  import org.hipparchus.optim.InitialGuess;
31  import org.hipparchus.optim.MaxEval;
32  import org.hipparchus.optim.PointValuePair;
33  import org.hipparchus.optim.SimpleBounds;
34  import org.hipparchus.optim.nonlinear.scalar.GoalType;
35  import org.hipparchus.optim.nonlinear.scalar.ObjectiveFunction;
36  import org.hipparchus.util.FastMath;
37  import org.junit.Assert;
38  import org.junit.Test;
39  
40  /**
41   * Test for {@link BOBYQAOptimizer}.
42   */
43  public class BOBYQAOptimizerTest {
44  
45      static final int DIM = 13;
46  
47      @Test(expected=MathIllegalArgumentException.class)
48      public void testInitOutOfBounds() {
49          double[] startPoint = point(DIM, 3);
50          double[][] boundaries = boundaries(DIM, -1, 2);
51          doTest(new Rosen(), startPoint, boundaries,
52                  GoalType.MINIMIZE,
53                  1e-13, 1e-6, 2000, null);
54      }
55  
56      @Test(expected=MathIllegalArgumentException.class)
57      public void testBoundariesDimensionMismatch() {
58          double[] startPoint = point(DIM, 0.5);
59          double[][] boundaries = boundaries(DIM + 1, -1, 2);
60          doTest(new Rosen(), startPoint, boundaries,
61                 GoalType.MINIMIZE,
62                 1e-13, 1e-6, 2000, null);
63      }
64  
65      @Test(expected=MathIllegalArgumentException.class)
66      public void testProblemDimensionTooSmall() {
67          double[] startPoint = point(1, 0.5);
68          doTest(new Rosen(), startPoint, null,
69                 GoalType.MINIMIZE,
70                 1e-13, 1e-6, 2000, null);
71      }
72  
73      @Test(expected=MathIllegalStateException.class)
74      public void testMaxEvaluations() {
75          final int lowMaxEval = 2;
76          double[] startPoint = point(DIM, 0.1);
77          double[][] boundaries = null;
78          doTest(new Rosen(), startPoint, boundaries,
79                 GoalType.MINIMIZE,
80                 1e-13, 1e-6, lowMaxEval, null);
81       }
82  
83      @Test
84      public void testRosen() {
85          double[] startPoint = point(DIM,0.1);
86          double[][] boundaries = null;
87          PointValuePair expected = new PointValuePair(point(DIM,1.0),0.0);
88          doTest(new Rosen(), startPoint, boundaries,
89                  GoalType.MINIMIZE,
90                  1e-13, 1e-6, 2000, expected);
91       }
92  
93      @Test
94      public void testMaximize() {
95          double[] startPoint = point(DIM,1.0);
96          double[][] boundaries = null;
97          PointValuePair expected = new PointValuePair(point(DIM,0.0),1.0);
98          doTest(new MinusElli(), startPoint, boundaries,
99                  GoalType.MAXIMIZE,
100                 2e-10, 5e-6, 1000, expected);
101         boundaries = boundaries(DIM,-0.3,0.3);
102         startPoint = point(DIM,0.1);
103         doTest(new MinusElli(), startPoint, boundaries,
104                 GoalType.MAXIMIZE,
105                 2e-10, 5e-6, 1000, expected);
106     }
107 
108     @Test
109     public void testEllipse() {
110         double[] startPoint = point(DIM,1.0);
111         double[][] boundaries = null;
112         PointValuePair expected =
113             new PointValuePair(point(DIM,0.0),0.0);
114         doTest(new Elli(), startPoint, boundaries,
115                 GoalType.MINIMIZE,
116                 1e-13, 1e-6, 1000, expected);
117      }
118 
119     @Test
120     public void testElliRotated() {
121         double[] startPoint = point(DIM,1.0);
122         double[][] boundaries = null;
123         PointValuePair expected =
124             new PointValuePair(point(DIM,0.0),0.0);
125         doTest(new ElliRotated(), startPoint, boundaries,
126                 GoalType.MINIMIZE,
127                 1e-12, 1e-6, 10000, expected);
128     }
129 
130     @Test
131     public void testCigar() {
132         double[] startPoint = point(DIM,1.0);
133         double[][] boundaries = null;
134         PointValuePair expected =
135             new PointValuePair(point(DIM,0.0),0.0);
136         doTest(new Cigar(), startPoint, boundaries,
137                 GoalType.MINIMIZE,
138                 1e-13, 1e-6, 100, expected);
139     }
140 
141     @Test
142     public void testTwoAxes() {
143         double[] startPoint = point(DIM,1.0);
144         double[][] boundaries = null;
145         PointValuePair expected =
146             new PointValuePair(point(DIM,0.0),0.0);
147         doTest(new TwoAxes(), startPoint, boundaries,
148                 GoalType.MINIMIZE, 2*
149                 1e-13, 1e-6, 100, expected);
150      }
151 
152     @Test
153     public void testCigTab() {
154         double[] startPoint = point(DIM,1.0);
155         double[][] boundaries = null;
156         PointValuePair expected =
157             new PointValuePair(point(DIM,0.0),0.0);
158         doTest(new CigTab(), startPoint, boundaries,
159                 GoalType.MINIMIZE,
160                 1e-13, 5e-5, 100, expected);
161      }
162 
163     @Test
164     public void testSphere() {
165         double[] startPoint = point(DIM,1.0);
166         double[][] boundaries = null;
167         PointValuePair expected =
168             new PointValuePair(point(DIM,0.0),0.0);
169         doTest(new Sphere(), startPoint, boundaries,
170                 GoalType.MINIMIZE,
171                 1e-13, 1e-6, 100, expected);
172     }
173 
174     @Test
175     public void testTablet() {
176         double[] startPoint = point(DIM,1.0);
177         double[][] boundaries = null;
178         PointValuePair expected =
179             new PointValuePair(point(DIM,0.0),0.0);
180         doTest(new Tablet(), startPoint, boundaries,
181                 GoalType.MINIMIZE,
182                 1e-13, 1e-6, 100, expected);
183     }
184 
185     @Test
186     public void testDiffPow() {
187         double[] startPoint = point(DIM/2,1.0);
188         double[][] boundaries = null;
189         PointValuePair expected =
190             new PointValuePair(point(DIM/2,0.0),0.0);
191         doTest(new DiffPow(), startPoint, boundaries,
192                 GoalType.MINIMIZE,
193                 1e-8, 1e-1, 21000, expected);
194     }
195 
196     @Test
197     public void testSsDiffPow() {
198         double[] startPoint = point(DIM/2,1.0);
199         double[][] boundaries = null;
200         PointValuePair expected =
201             new PointValuePair(point(DIM/2,0.0),0.0);
202         doTest(new SsDiffPow(), startPoint, boundaries,
203                 GoalType.MINIMIZE,
204                 1e-2, 1.3e-1, 50000, expected);
205     }
206 
207     @Test
208     public void testAckley() {
209         double[] startPoint = point(DIM,0.1);
210         double[][] boundaries = null;
211         PointValuePair expected =
212             new PointValuePair(point(DIM,0.0),0.0);
213         doTest(new Ackley(), startPoint, boundaries,
214                 GoalType.MINIMIZE,
215                 1e-7, 1e-5, 1000, expected);
216     }
217 
218     @Test
219     public void testRastrigin() {
220         double[] startPoint = point(DIM,1.0);
221 
222         double[][] boundaries = null;
223         PointValuePair expected =
224             new PointValuePair(point(DIM,0.0),0.0);
225         doTest(new Rastrigin(), startPoint, boundaries,
226                 GoalType.MINIMIZE,
227                 1e-13, 1e-6, 1000, expected);
228     }
229 
230     @Test
231     public void testConstrainedRosen() {
232         double[] startPoint = point(DIM,0.1);
233 
234         double[][] boundaries = boundaries(DIM,-1,2);
235         PointValuePair expected =
236             new PointValuePair(point(DIM,1.0),0.0);
237         doTest(new Rosen(), startPoint, boundaries,
238                 GoalType.MINIMIZE,
239                 1e-13, 1e-6, 2000, expected);
240     }
241 
242     /**
243      * @param func Function to optimize.
244      * @param startPoint Starting point.
245      * @param boundaries Upper / lower point limit.
246      * @param goal Minimization or maximization.
247      * @param fTol Tolerance relative error on the objective function.
248      * @param pointTol Tolerance for checking that the optimum is correct.
249      * @param maxEvaluations Maximum number of evaluations.
250      * @param expected Expected point / value.
251      */
252     private void doTest(MultivariateFunction func,
253                         double[] startPoint,
254                         double[][] boundaries,
255                         GoalType goal,
256                         double fTol,
257                         double pointTol,
258                         int maxEvaluations,
259                         PointValuePair expected) {
260         doTest(func,
261                startPoint,
262                boundaries,
263                goal,
264                fTol,
265                pointTol,
266                maxEvaluations,
267                0,
268                expected,
269                "");
270     }
271 
272     /**
273      * @param func Function to optimize.
274      * @param startPoint Starting point.
275      * @param boundaries Upper / lower point limit.
276      * @param goal Minimization or maximization.
277      * @param fTol Tolerance relative error on the objective function.
278      * @param pointTol Tolerance for checking that the optimum is correct.
279      * @param maxEvaluations Maximum number of evaluations.
280      * @param additionalInterpolationPoints Number of interpolation to used
281      * in addition to the default (2 * dim + 1).
282      * @param expected Expected point / value.
283      */
284     private void doTest(MultivariateFunction func,
285                         double[] startPoint,
286                         double[][] boundaries,
287                         GoalType goal,
288                         double fTol,
289                         double pointTol,
290                         int maxEvaluations,
291                         int additionalInterpolationPoints,
292                         PointValuePair expected,
293                         String assertMsg) {
294 
295 //         System.out.println(func.getClass().getName() + " BEGIN"); // XXX
296 
297         int dim = startPoint.length;
298         final int numIterpolationPoints = 2 * dim + 1 + additionalInterpolationPoints;
299         BOBYQAOptimizer optim = new BOBYQAOptimizer(numIterpolationPoints);
300         PointValuePair result = boundaries == null ?
301             optim.optimize(new MaxEval(maxEvaluations),
302                            new ObjectiveFunction(func),
303                            goal,
304                            SimpleBounds.unbounded(dim),
305                            new InitialGuess(startPoint)) :
306             optim.optimize(new MaxEval(maxEvaluations),
307                            new ObjectiveFunction(func),
308                            goal,
309                            new InitialGuess(startPoint),
310                            new SimpleBounds(boundaries[0],
311                                             boundaries[1]));
312 //        System.out.println(func.getClass().getName() + " = "
313 //              + optim.getEvaluations() + " f(");
314 //        for (double x: result.getPoint())  System.out.print(x + " ");
315 //        System.out.println(") = " +  result.getValue());
316         Assert.assertEquals(assertMsg, expected.getValue(), result.getValue(), fTol);
317         for (int i = 0; i < dim; i++) {
318             Assert.assertEquals(expected.getPoint()[i],
319                                 result.getPoint()[i], pointTol);
320         }
321 
322 //         System.out.println(func.getClass().getName() + " END"); // XXX
323     }
324 
325     private static double[] point(int n, double value) {
326         double[] ds = new double[n];
327         Arrays.fill(ds, value);
328         return ds;
329     }
330 
331     private static double[][] boundaries(int dim,
332             double lower, double upper) {
333         double[][] boundaries = new double[2][dim];
334         for (int i = 0; i < dim; i++)
335             boundaries[0][i] = lower;
336         for (int i = 0; i < dim; i++)
337             boundaries[1][i] = upper;
338         return boundaries;
339     }
340 
341     private static class Sphere implements MultivariateFunction {
342 
343         public double value(double[] x) {
344             double f = 0;
345             for (int i = 0; i < x.length; ++i)
346                 f += x[i] * x[i];
347             return f;
348         }
349     }
350 
351     private static class Cigar implements MultivariateFunction {
352         private double factor;
353 
354         Cigar() {
355             this(1e3);
356         }
357 
358         Cigar(double axisratio) {
359             factor = axisratio * axisratio;
360         }
361 
362         public double value(double[] x) {
363             double f = x[0] * x[0];
364             for (int i = 1; i < x.length; ++i)
365                 f += factor * x[i] * x[i];
366             return f;
367         }
368     }
369 
370     private static class Tablet implements MultivariateFunction {
371         private double factor;
372 
373         Tablet() {
374             this(1e3);
375         }
376 
377         Tablet(double axisratio) {
378             factor = axisratio * axisratio;
379         }
380 
381         public double value(double[] x) {
382             double f = factor * x[0] * x[0];
383             for (int i = 1; i < x.length; ++i)
384                 f += x[i] * x[i];
385             return f;
386         }
387     }
388 
389     private static class CigTab implements MultivariateFunction {
390         private double factor;
391 
392         CigTab() {
393             this(1e4);
394         }
395 
396         CigTab(double axisratio) {
397             factor = axisratio;
398         }
399 
400         public double value(double[] x) {
401             int end = x.length - 1;
402             double f = x[0] * x[0] / factor + factor * x[end] * x[end];
403             for (int i = 1; i < end; ++i)
404                 f += x[i] * x[i];
405             return f;
406         }
407     }
408 
409     private static class TwoAxes implements MultivariateFunction {
410 
411         private double factor;
412 
413         TwoAxes() {
414             this(1e6);
415         }
416 
417         TwoAxes(double axisratio) {
418             factor = axisratio * axisratio;
419         }
420 
421         public double value(double[] x) {
422             double f = 0;
423             for (int i = 0; i < x.length; ++i)
424                 f += (i < x.length / 2 ? factor : 1) * x[i] * x[i];
425             return f;
426         }
427     }
428 
429     private static class ElliRotated implements MultivariateFunction {
430         private Basis B = new Basis();
431         private double factor;
432 
433         ElliRotated() {
434             this(1e3);
435         }
436 
437         ElliRotated(double axisratio) {
438             factor = axisratio * axisratio;
439         }
440 
441         public double value(double[] x) {
442             double f = 0;
443             x = B.Rotate(x);
444             for (int i = 0; i < x.length; ++i)
445                 f += FastMath.pow(factor, i / (x.length - 1.)) * x[i] * x[i];
446             return f;
447         }
448     }
449 
450     private static class Elli implements MultivariateFunction {
451 
452         private double factor;
453 
454         Elli() {
455             this(1e3);
456         }
457 
458         Elli(double axisratio) {
459             factor = axisratio * axisratio;
460         }
461 
462         public double value(double[] x) {
463             double f = 0;
464             for (int i = 0; i < x.length; ++i)
465                 f += FastMath.pow(factor, i / (x.length - 1.)) * x[i] * x[i];
466             return f;
467         }
468     }
469 
470     private static class MinusElli implements MultivariateFunction {
471         private final Elli elli = new Elli();
472         public double value(double[] x) {
473             return 1.0 - elli.value(x);
474         }
475     }
476 
477     private static class DiffPow implements MultivariateFunction {
478 //        private int fcount = 0;
479         public double value(double[] x) {
480             double f = 0;
481             for (int i = 0; i < x.length; ++i)
482                 f += FastMath.pow(FastMath.abs(x[i]), 2. + 10 * (double) i
483                         / (x.length - 1.));
484 //            System.out.print("" + (fcount++) + ") ");
485 //            for (int i = 0; i < x.length; i++)
486 //                System.out.print(x[i] +  " ");
487 //            System.out.println(" = " + f);
488             return f;
489         }
490     }
491 
492     private static class SsDiffPow implements MultivariateFunction {
493 
494         public double value(double[] x) {
495             double f = FastMath.pow(new DiffPow().value(x), 0.25);
496             return f;
497         }
498     }
499 
500     private static class Rosen implements MultivariateFunction {
501 
502         public double value(double[] x) {
503             double f = 0;
504             for (int i = 0; i < x.length - 1; ++i)
505                 f += 1e2 * (x[i] * x[i] - x[i + 1]) * (x[i] * x[i] - x[i + 1])
506                 + (x[i] - 1.) * (x[i] - 1.);
507             return f;
508         }
509     }
510 
511     private static class Ackley implements MultivariateFunction {
512         private double axisratio;
513 
514         Ackley(double axra) {
515             axisratio = axra;
516         }
517 
518         public Ackley() {
519             this(1);
520         }
521 
522         public double value(double[] x) {
523             double f = 0;
524             double res2 = 0;
525             double fac = 0;
526             for (int i = 0; i < x.length; ++i) {
527                 fac = FastMath.pow(axisratio, (i - 1.) / (x.length - 1.));
528                 f += fac * fac * x[i] * x[i];
529                 res2 += FastMath.cos(2. * FastMath.PI * fac * x[i]);
530             }
531             f = (20. - 20. * FastMath.exp(-0.2 * FastMath.sqrt(f / x.length))
532                     + FastMath.exp(1.) - FastMath.exp(res2 / x.length));
533             return f;
534         }
535     }
536 
537     private static class Rastrigin implements MultivariateFunction {
538 
539         private double axisratio;
540         private double amplitude;
541 
542         Rastrigin() {
543             this(1, 10);
544         }
545 
546         Rastrigin(double axisratio, double amplitude) {
547             this.axisratio = axisratio;
548             this.amplitude = amplitude;
549         }
550 
551         public double value(double[] x) {
552             double f = 0;
553             double fac;
554             for (int i = 0; i < x.length; ++i) {
555                 fac = FastMath.pow(axisratio, (i - 1.) / (x.length - 1.));
556                 if (i == 0 && x[i] < 0)
557                     fac *= 1.;
558                 f += fac * fac * x[i] * x[i] + amplitude
559                 * (1. - FastMath.cos(2. * FastMath.PI * fac * x[i]));
560             }
561             return f;
562         }
563     }
564 
565     private static class Basis {
566         double[][] basis;
567         Random rand = new Random(2); // use not always the same basis
568 
569         double[] Rotate(double[] x) {
570             GenBasis(x.length);
571             double[] y = new double[x.length];
572             for (int i = 0; i < x.length; ++i) {
573                 y[i] = 0;
574                 for (int j = 0; j < x.length; ++j)
575                     y[i] += basis[i][j] * x[j];
576             }
577             return y;
578         }
579 
580         void GenBasis(int DIM) {
581             if (basis != null ? basis.length == DIM : false)
582                 return;
583 
584             double sp;
585             int i, j, k;
586 
587             /* generate orthogonal basis */
588             basis = new double[DIM][DIM];
589             for (i = 0; i < DIM; ++i) {
590                 /* sample components gaussian */
591                 for (j = 0; j < DIM; ++j)
592                     basis[i][j] = rand.nextGaussian();
593                 /* substract projection of previous vectors */
594                 for (j = i - 1; j >= 0; --j) {
595                     for (sp = 0., k = 0; k < DIM; ++k)
596                         sp += basis[i][k] * basis[j][k]; /* scalar product */
597                     for (k = 0; k < DIM; ++k)
598                         basis[i][k] -= sp * basis[j][k]; /* substract */
599                 }
600                 /* normalize */
601                 for (sp = 0., k = 0; k < DIM; ++k)
602                     sp += basis[i][k] * basis[i][k]; /* squared norm */
603                 for (k = 0; k < DIM; ++k)
604                     basis[i][k] /= FastMath.sqrt(sp);
605             }
606         }
607     }
608 }