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;
18  
19  import java.util.function.Function;
20  import java.util.function.DoubleUnaryOperator;
21  import org.apache.commons.math4.legacy.analysis.MultivariateFunction;
22  
23  /**
24   * Generators of {@link MultivariateFunction multivariate scalar functions}.
25   * The functions are intended for testing optimizer implementations.
26   * <p>
27   * Note: The {@link #withDimension(int) function generators} take the space
28   * dimension (i.e. the length of the array argument passed to the generated
29   * function) as argument; it is thus assumed that the test functions can be
30   * generalized to any dimension.
31   */
32  public enum TestFunction {
33      // https://www.sfu.ca/~ssurjano/spheref.html
34      SPHERE(dim -> {
35              return x -> {
36                  double f = 0;
37                  for (int i = 0; i < dim; i++) {
38                      f += x[i] * x[i];
39                  }
40                  return f;
41              };
42          }),
43      CIGAR(dim -> {
44              return x -> {
45                  double f = x[0] * x[0];
46                  for (int i = 1; i < dim; i++) {
47                      f += 1e3 * x[i] * x[i];
48                  }
49                  return f;
50              };
51          }),
52      TABLET(dim -> {
53              return x -> {
54                  double f = 1e3 * x[0] * x[0];
55                  for (int i = 1; i < dim; i++) {
56                      f += x[i] * x[i];
57                  }
58                  return f;
59              };
60          }),
61      CIG_TAB(dim -> {
62              final double factor = 1e4;
63              final int last = dim - 1;
64              return x -> {
65                  double f = x[0] * x[0] / factor + factor * x[last] * x[last];
66                  for (int i = 1; i < last; i++) {
67                      f += x[i] * x[i];
68                  }
69                  return f;
70              };
71          }),
72      TWO_AXES(dim -> {
73              final int halfDim = dim / 2;
74              return x -> {
75                  double f = 0;
76                  for (int i = 0; i < halfDim; i++) {
77                      f += 1e6 * x[i] * x[i];
78                  }
79                  for (int i = halfDim; i < dim; i++) {
80                      f += x[i] * x[i];
81                  }
82                  return f;
83              };
84          }),
85      ELLI(dim -> {
86              final double M = Math.pow(1e3, 1d / (dim - 1));
87              return x -> {
88                  double factor = 1;
89                  double f = 0;
90                  for (int i = 0; i < dim; i++) {
91                      f += factor * x[i] * x[i];
92                      factor *= M;
93                  }
94                  return f;
95              };
96          }),
97      MINUS_ELLI(dim -> {
98              final MultivariateFunction elli = ELLI.withDimension(dim);
99              return x -> {
100                 return 1 - elli.value(x);
101             };
102         }),
103     // https://www.sfu.ca/~ssurjano/sumpow.html
104     SUM_POW(dim -> {
105             return x -> {
106                 double f = 0;
107                 for (int i = 0; i < dim; i++) {
108                     f += Math.pow(Math.abs(x[i]), i + 2);
109                 }
110                 return f;
111             };
112         }),
113     // https://www.sfu.ca/~ssurjano/ackley.html
114     ACKLEY(dim -> {
115             final double A = 20;
116             final double B = 0.2;
117             final double C = 2 * Math.PI;
118             return x -> {
119                 double acc1 = 0;
120                 double acc2 = 0;
121                 for (int i = 0; i < dim; i++) {
122                     final double v = x[i];
123                     acc1 += v * v;
124                     acc2 += Math.cos(C * v);
125                 }
126                 acc1 = -B * Math.sqrt(acc1 / dim);
127                 acc2 /= dim;
128 
129                 return -A * Math.exp(acc1) - Math.exp(acc2) + A + Math.E;
130             };
131         }),
132     // https://www.sfu.ca/~ssurjano/rastr.html
133     RASTRIGIN(dim -> {
134             final double A = 10;
135             final double twopi = 2 * Math.PI;
136             return x -> {
137                 double sum = 0;
138                 for (int i = 0; i < dim; i++) {
139                     final double xi = x[i];
140                     sum += xi * xi - A * Math.cos(twopi * xi);
141                 }
142                 return A * dim + sum;
143             };
144         }),
145     // http://benchmarkfcns.xyz/benchmarkfcns/salomonfcn.html
146     SALOMON(dim -> {
147             return x -> {
148                 double sum = 0;
149                 for (int i = 0; i < dim; i++) {
150                     final double xi = x[i];
151                     sum += xi * xi;
152                 }
153                 final double sqrtSum = Math.sqrt(sum);
154                 return 1 - Math.cos(2 * Math.PI * sqrtSum) + 0.1 * sqrtSum;
155             };
156         }),
157     // https://scholarship.rice.edu/handle/1911/16304
158     ROSENBROCK(dim -> {
159             if ((dim & 1) != 0) {
160                 throw new IllegalArgumentException("Must be a multiple of 2 (was: " + dim + ")");
161             }
162             final int last = dim / 2;
163             return x -> {
164                 double f = 0;
165                 for (int i = 1; i <= last; i++) {
166                     final int twoI = 2 * i;
167                     final int i0 = twoI - 1;
168                     final int i1 = twoI;
169                     final double x2iM1 = x[i0 - 1];
170                     final double x2i = x[i1 - 1];
171                     final double t2iM1 = x2i - x2iM1 * x2iM1;
172                     final double t2i = 1 - x2iM1;
173                     f += 100 * t2iM1 * t2iM1 + t2i * t2i;
174                 }
175                 return f;
176             };
177         }),
178     // http://benchmarkfcns.xyz/benchmarkfcns/happycatfcn.html
179     HAPPY_CAT(dim -> {
180             final double alpha = 0.125;
181             return x -> {
182                 double sum = 0;
183                 double sumSq = 0;
184                 for (int i = 0; i < dim; i++) {
185                     final double xi = x[i];
186                     sum += xi;
187                     sumSq += xi * xi;
188                 }
189                 return Math.pow(sumSq - dim, 2 * alpha) + (0.5 * sumSq + sum) / dim + 0.5;
190             };
191         }),
192     PARABOLA(dim -> {
193             return x -> {
194                 double f = 0;
195                 for (int i = 0; i < dim; i++) {
196                     final double xi = x[i];
197                     f += xi * xi;
198                 }
199                 return f;
200             };
201         }),
202     // https://www.sfu.ca/~ssurjano/griewank.html
203     GRIEWANK(dim -> {
204             final double A = 4000;
205             return x -> {
206                 double sum = 0;
207                 double prod = 1;
208                 for (int i = 0; i < dim; i++) {
209                     final double xi = x[i];
210                     sum += xi * xi;
211                     prod *= Math.cos(xi / Math.sqrt(i + 1));
212                 }
213                 return sum / A - prod + 1;
214             };
215         }),
216     // https://www.sfu.ca/~ssurjano/levy.html
217     LEVY(dim -> {
218             final int last = dim - 1;
219             final DoubleUnaryOperator w = x -> 1 + 0.25 * (x - 1);
220             return x -> {
221                 final double a0 = Math.sin(Math.PI * w.applyAsDouble(x[0]));
222                 double sum = a0 * a0;
223                 for (int i = 0; i < last; i++) {
224                     final double wi = w.applyAsDouble(x[i]);
225                     final double wiM1 = wi - 1;
226                     final double ai = Math.sin(Math.PI * wi + 1);
227                     sum += wiM1 * wiM1 * (1 + 10 * ai * ai);
228                 }
229                 final double wl = w.applyAsDouble(x[last]);
230                 final double wlM1 = wl - 1;
231                 final double al = Math.sin(2 * Math.PI * wl);
232                 return sum + wlM1 * wlM1 * (1 + al * al);
233             };
234         }),
235     // https://www.sfu.ca/~ssurjano/schwef.html
236     SCHWEFEL(dim -> {
237             final double A = 418.9829;
238             return x -> {
239                 double sum = 0;
240                 for (int i = 0; i < dim; i++) {
241                     final double xi = x[i];
242                     sum += xi * Math.sin(Math.sqrt(Math.abs(xi)));
243                 }
244                 return A * dim - sum;
245             };
246         }),
247     // https://www.sfu.ca/~ssurjano/zakharov.html
248     ZAKHAROV(dim -> {
249             final double A = 0.5;
250             return x -> {
251                 double sum1 = 0;
252                 double sum2 = 0;
253                 for (int i = 0; i < dim; i++) {
254                     final double xi = x[i];
255                     sum1 += xi * xi;
256                     sum2 += A * (i + 1) * xi;
257                 }
258                 final double sum22 = sum2 * sum2;
259                 return sum1 + sum22 + sum22 * sum22;
260             };
261         }),
262     // https://www.sfu.ca/~ssurjano/permdb.html
263     PERM(dim -> {
264             final double BETA = 10;
265             return x -> {
266                 double sum1 = 0;
267                 for (int i = 0; i < dim; i++) {
268                     final double iP1 = i + 1;
269                     double sum2 = 0;
270                     for (int j = 0; j < dim; j++) {
271                         final double jP1 = j + 1;
272                         final double a = Math.pow(jP1, iP1) + BETA;
273                         final double b = Math.pow(x[j] / jP1, iP1) - 1;
274                         sum2 += a * b;
275                     }
276                     sum1 += sum2 * sum2;
277                 }
278                 return sum1;
279             };
280         }),
281     // https://www.sfu.ca/~ssurjano/stybtang.html
282     STYBLINSKI_TANG(dim -> {
283             final double A = 0.5;
284             final double B = 16;
285             final double C = 5;
286             return x -> {
287                 double sum = 0;
288                 for (int i = 0; i < dim; i++) {
289                     final double xi = x[i];
290                     final double xi2 = xi * xi;
291                     final double xi4 = xi2 * xi2;
292                     sum += xi4 - B * xi2 + C * xi;
293                 }
294                 return A * sum;
295             };
296         }),
297     // https://scholarship.rice.edu/handle/1911/16304
298     POWELL(dim -> {
299             if (dim % 4 != 0) {
300                 throw new IllegalArgumentException("Must be a multiple of 4 (was: " + dim + ")");
301             }
302             final int last = dim / 4;
303             return x -> {
304                 double sum = 0;
305                 for (int i = 1; i <= last; i++) {
306                     final int fourI = 4 * i;
307                     final int i0 = fourI - 3;
308                     final int i1 = fourI - 2;
309                     final int i2 = fourI - 1;
310                     final int i3 = fourI;
311                     final double x4iM3 = x[i0 - 1];
312                     final double x4iM2 = x[i1 - 1];
313                     final double x4iM1 = x[i2 - 1];
314                     final double x4i = x[i3 - 1];
315                     final double t4iM3 = x4iM3 + 10 * x4iM2;
316                     final double t4iM2 = x4iM1 - x4i;
317                     final double t4iM1 = x4iM2 - 2 * x4iM1;
318                     final double sqT4iM1 = t4iM1 * t4iM1;
319                     final double t4i = x4iM3 - x4i;
320                     final double sqT4i = t4i * t4i;
321                     sum += t4iM3 * t4iM3 + 5 * t4iM2 * t4iM2 + sqT4iM1 * sqT4iM1 + 10 * sqT4i * sqT4i;
322                 }
323                 return sum;
324             };
325         });
326 
327     /** Template for variable dimension. */
328     private final Function<Integer, MultivariateFunction> generator;
329 
330     /**
331      * @param gen Template for variable dimension.
332      */
333     TestFunction(Function<Integer, MultivariateFunction> gen) {
334         generator = gen;
335     }
336 
337     /**
338      * @param dim Dimension.
339      * @return the function for the given dimension.
340      */
341     public MultivariateFunction withDimension(final int dim) {
342         return new MultivariateFunction() {
343             /** Delegate. */
344             private final MultivariateFunction f = generator.apply(dim);
345 
346             @Override
347             public double value(double[] x) {
348                 if (x.length != dim) {
349                     throw new IllegalArgumentException("Dimension mismatch: " + x.length +
350                                                        "(expected: " + dim + ")");
351                 }
352                 return f.value(x);
353             };
354 
355             @Override
356             public String toString() {
357                 final StringBuilder sb = new StringBuilder();
358                 sb.append("[")
359                     .append(TestFunction.this.toString())
360                     .append(" dim=")
361                     .append(dim)
362                     .append("]");
363                 return sb.toString();
364             }
365         };
366     }
367 }