blob: 3f9fac2fc877d37356bec1803e3c25790d719f9a [file] [log] [blame]
Raymonddee08492015-04-02 10:43:13 -07001/*
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
18package org.apache.commons.math.optimization.direct;
19
20import java.util.Arrays;
21import java.util.Comparator;
22
23import org.apache.commons.math.FunctionEvaluationException;
24import org.apache.commons.math.MathRuntimeException;
25import org.apache.commons.math.MaxEvaluationsExceededException;
26import org.apache.commons.math.MaxIterationsExceededException;
27import org.apache.commons.math.analysis.MultivariateRealFunction;
28import org.apache.commons.math.exception.util.LocalizedFormats;
29import org.apache.commons.math.optimization.GoalType;
30import org.apache.commons.math.optimization.MultivariateRealOptimizer;
31import org.apache.commons.math.optimization.OptimizationException;
32import org.apache.commons.math.optimization.RealConvergenceChecker;
33import org.apache.commons.math.optimization.RealPointValuePair;
34import org.apache.commons.math.optimization.SimpleScalarValueChecker;
35
36/**
37 * This class implements simplex-based direct search optimization
38 * algorithms.
39 *
40 * <p>Direct search methods only use objective function values, they don't
41 * need derivatives and don't either try to compute approximation of
42 * the derivatives. According to a 1996 paper by Margaret H. Wright
43 * (<a href="http://cm.bell-labs.com/cm/cs/doc/96/4-02.ps.gz">Direct
44 * Search Methods: Once Scorned, Now Respectable</a>), they are used
45 * when either the computation of the derivative is impossible (noisy
46 * functions, unpredictable discontinuities) or difficult (complexity,
47 * computation cost). In the first cases, rather than an optimum, a
48 * <em>not too bad</em> point is desired. In the latter cases, an
49 * optimum is desired but cannot be reasonably found. In all cases
50 * direct search methods can be useful.</p>
51 *
52 * <p>Simplex-based direct search methods are based on comparison of
53 * the objective function values at the vertices of a simplex (which is a
54 * set of n+1 points in dimension n) that is updated by the algorithms
55 * steps.<p>
56 *
57 * <p>The initial configuration of the simplex can be set using either
58 * {@link #setStartConfiguration(double[])} or {@link
59 * #setStartConfiguration(double[][])}. If neither method has been called
60 * before optimization is attempted, an explicit call to the first method
61 * with all steps set to +1 is triggered, thus building a default
62 * configuration from a unit hypercube. Each call to {@link
63 * #optimize(MultivariateRealFunction, GoalType, double[]) optimize} will reuse
64 * the current start configuration and move it such that its first vertex
65 * is at the provided start point of the optimization. If the {@code optimize}
66 * method is called to solve a different problem and the number of parameters
67 * change, the start configuration will be reset to a default one with the
68 * appropriate dimensions.</p>
69 *
70 * <p>If {@link #setConvergenceChecker(RealConvergenceChecker)} is not called,
71 * a default {@link SimpleScalarValueChecker} is used.</p>
72 *
73 * <p>Convergence is checked by providing the <em>worst</em> points of
74 * previous and current simplex to the convergence checker, not the best ones.</p>
75 *
76 * <p>This class is the base class performing the boilerplate simplex
77 * initialization and handling. The simplex update by itself is
78 * performed by the derived classes according to the implemented
79 * algorithms.</p>
80 *
81 * implements MultivariateRealOptimizer since 2.0
82 *
83 * @see MultivariateRealFunction
84 * @see NelderMead
85 * @see MultiDirectional
86 * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 févr. 2011) $
87 * @since 1.2
88 */
89public abstract class DirectSearchOptimizer implements MultivariateRealOptimizer {
90
91 /** Simplex. */
92 protected RealPointValuePair[] simplex;
93
94 /** Objective function. */
95 private MultivariateRealFunction f;
96
97 /** Convergence checker. */
98 private RealConvergenceChecker checker;
99
100 /** Maximal number of iterations allowed. */
101 private int maxIterations;
102
103 /** Number of iterations already performed. */
104 private int iterations;
105
106 /** Maximal number of evaluations allowed. */
107 private int maxEvaluations;
108
109 /** Number of evaluations already performed. */
110 private int evaluations;
111
112 /** Start simplex configuration. */
113 private double[][] startConfiguration;
114
115 /** Simple constructor.
116 */
117 protected DirectSearchOptimizer() {
118 setConvergenceChecker(new SimpleScalarValueChecker());
119 setMaxIterations(Integer.MAX_VALUE);
120 setMaxEvaluations(Integer.MAX_VALUE);
121 }
122
123 /** Set start configuration for simplex.
124 * <p>The start configuration for simplex is built from a box parallel to
125 * the canonical axes of the space. The simplex is the subset of vertices
126 * of a box parallel to the canonical axes. It is built as the path followed
127 * while traveling from one vertex of the box to the diagonally opposite
128 * vertex moving only along the box edges. The first vertex of the box will
129 * be located at the start point of the optimization.</p>
130 * <p>As an example, in dimension 3 a simplex has 4 vertices. Setting the
131 * steps to (1, 10, 2) and the start point to (1, 1, 1) would imply the
132 * start simplex would be: { (1, 1, 1), (2, 1, 1), (2, 11, 1), (2, 11, 3) }.
133 * The first vertex would be set to the start point at (1, 1, 1) and the
134 * last vertex would be set to the diagonally opposite vertex at (2, 11, 3).</p>
135 * @param steps steps along the canonical axes representing box edges,
136 * they may be negative but not null
137 * @exception IllegalArgumentException if one step is null
138 */
139 public void setStartConfiguration(final double[] steps)
140 throws IllegalArgumentException {
141 // only the relative position of the n final vertices with respect
142 // to the first one are stored
143 final int n = steps.length;
144 startConfiguration = new double[n][n];
145 for (int i = 0; i < n; ++i) {
146 final double[] vertexI = startConfiguration[i];
147 for (int j = 0; j < i + 1; ++j) {
148 if (steps[j] == 0.0) {
149 throw MathRuntimeException.createIllegalArgumentException(
150 LocalizedFormats.EQUAL_VERTICES_IN_SIMPLEX, j, j + 1);
151 }
152 System.arraycopy(steps, 0, vertexI, 0, j + 1);
153 }
154 }
155 }
156
157 /** Set start configuration for simplex.
158 * <p>The real initial simplex will be set up by moving the reference
159 * simplex such that its first point is located at the start point of the
160 * optimization.</p>
161 * @param referenceSimplex reference simplex
162 * @exception IllegalArgumentException if the reference simplex does not
163 * contain at least one point, or if there is a dimension mismatch
164 * in the reference simplex or if one of its vertices is duplicated
165 */
166 public void setStartConfiguration(final double[][] referenceSimplex)
167 throws IllegalArgumentException {
168
169 // only the relative position of the n final vertices with respect
170 // to the first one are stored
171 final int n = referenceSimplex.length - 1;
172 if (n < 0) {
173 throw MathRuntimeException.createIllegalArgumentException(
174 LocalizedFormats.SIMPLEX_NEED_ONE_POINT);
175 }
176 startConfiguration = new double[n][n];
177 final double[] ref0 = referenceSimplex[0];
178
179 // vertices loop
180 for (int i = 0; i < n + 1; ++i) {
181
182 final double[] refI = referenceSimplex[i];
183
184 // safety checks
185 if (refI.length != n) {
186 throw MathRuntimeException.createIllegalArgumentException(
187 LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, refI.length, n);
188 }
189 for (int j = 0; j < i; ++j) {
190 final double[] refJ = referenceSimplex[j];
191 boolean allEquals = true;
192 for (int k = 0; k < n; ++k) {
193 if (refI[k] != refJ[k]) {
194 allEquals = false;
195 break;
196 }
197 }
198 if (allEquals) {
199 throw MathRuntimeException.createIllegalArgumentException(
200 LocalizedFormats.EQUAL_VERTICES_IN_SIMPLEX, i, j);
201 }
202 }
203
204 // store vertex i position relative to vertex 0 position
205 if (i > 0) {
206 final double[] confI = startConfiguration[i - 1];
207 for (int k = 0; k < n; ++k) {
208 confI[k] = refI[k] - ref0[k];
209 }
210 }
211
212 }
213
214 }
215
216 /** {@inheritDoc} */
217 public void setMaxIterations(int maxIterations) {
218 this.maxIterations = maxIterations;
219 }
220
221 /** {@inheritDoc} */
222 public int getMaxIterations() {
223 return maxIterations;
224 }
225
226 /** {@inheritDoc} */
227 public void setMaxEvaluations(int maxEvaluations) {
228 this.maxEvaluations = maxEvaluations;
229 }
230
231 /** {@inheritDoc} */
232 public int getMaxEvaluations() {
233 return maxEvaluations;
234 }
235
236 /** {@inheritDoc} */
237 public int getIterations() {
238 return iterations;
239 }
240
241 /** {@inheritDoc} */
242 public int getEvaluations() {
243 return evaluations;
244 }
245
246 /** {@inheritDoc} */
247 public void setConvergenceChecker(RealConvergenceChecker convergenceChecker) {
248 this.checker = convergenceChecker;
249 }
250
251 /** {@inheritDoc} */
252 public RealConvergenceChecker getConvergenceChecker() {
253 return checker;
254 }
255
256 /** {@inheritDoc} */
257 public RealPointValuePair optimize(final MultivariateRealFunction function,
258 final GoalType goalType,
259 final double[] startPoint)
260 throws FunctionEvaluationException, OptimizationException, IllegalArgumentException {
261
262 if ((startConfiguration == null) ||
263 (startConfiguration.length != startPoint.length)) {
264 // no initial configuration has been set up for simplex
265 // build a default one from a unit hypercube
266 final double[] unit = new double[startPoint.length];
267 Arrays.fill(unit, 1.0);
268 setStartConfiguration(unit);
269 }
270
271 this.f = function;
272 final Comparator<RealPointValuePair> comparator =
273 new Comparator<RealPointValuePair>() {
274 public int compare(final RealPointValuePair o1,
275 final RealPointValuePair o2) {
276 final double v1 = o1.getValue();
277 final double v2 = o2.getValue();
278 return (goalType == GoalType.MINIMIZE) ?
279 Double.compare(v1, v2) : Double.compare(v2, v1);
280 }
281 };
282
283 // initialize search
284 iterations = 0;
285 evaluations = 0;
286 buildSimplex(startPoint);
287 evaluateSimplex(comparator);
288
289 RealPointValuePair[] previous = new RealPointValuePair[simplex.length];
290 while (true) {
291
292 if (iterations > 0) {
293 boolean converged = true;
294 for (int i = 0; i < simplex.length; ++i) {
295 converged &= checker.converged(iterations, previous[i], simplex[i]);
296 }
297 if (converged) {
298 // we have found an optimum
299 return simplex[0];
300 }
301 }
302
303 // we still need to search
304 System.arraycopy(simplex, 0, previous, 0, simplex.length);
305 iterateSimplex(comparator);
306
307 }
308
309 }
310
311 /** Increment the iterations counter by 1.
312 * @exception OptimizationException if the maximal number
313 * of iterations is exceeded
314 */
315 protected void incrementIterationsCounter()
316 throws OptimizationException {
317 if (++iterations > maxIterations) {
318 throw new OptimizationException(new MaxIterationsExceededException(maxIterations));
319 }
320 }
321
322 /** Compute the next simplex of the algorithm.
323 * @param comparator comparator to use to sort simplex vertices from best to worst
324 * @exception FunctionEvaluationException if the function cannot be evaluated at
325 * some point
326 * @exception OptimizationException if the algorithm fails to converge
327 * @exception IllegalArgumentException if the start point dimension is wrong
328 */
329 protected abstract void iterateSimplex(final Comparator<RealPointValuePair> comparator)
330 throws FunctionEvaluationException, OptimizationException, IllegalArgumentException;
331
332 /** Evaluate the objective function on one point.
333 * <p>A side effect of this method is to count the number of
334 * function evaluations</p>
335 * @param x point on which the objective function should be evaluated
336 * @return objective function value at the given point
337 * @exception FunctionEvaluationException if no value can be computed for the
338 * parameters or if the maximal number of evaluations is exceeded
339 * @exception IllegalArgumentException if the start point dimension is wrong
340 */
341 protected double evaluate(final double[] x)
342 throws FunctionEvaluationException, IllegalArgumentException {
343 if (++evaluations > maxEvaluations) {
344 throw new FunctionEvaluationException(new MaxEvaluationsExceededException(maxEvaluations), x);
345 }
346 return f.value(x);
347 }
348
349 /** Build an initial simplex.
350 * @param startPoint the start point for optimization
351 * @exception IllegalArgumentException if the start point does not match
352 * simplex dimension
353 */
354 private void buildSimplex(final double[] startPoint)
355 throws IllegalArgumentException {
356
357 final int n = startPoint.length;
358 if (n != startConfiguration.length) {
359 throw MathRuntimeException.createIllegalArgumentException(
360 LocalizedFormats.DIMENSIONS_MISMATCH_SIMPLE, n, startConfiguration.length);
361 }
362
363 // set first vertex
364 simplex = new RealPointValuePair[n + 1];
365 simplex[0] = new RealPointValuePair(startPoint, Double.NaN);
366
367 // set remaining vertices
368 for (int i = 0; i < n; ++i) {
369 final double[] confI = startConfiguration[i];
370 final double[] vertexI = new double[n];
371 for (int k = 0; k < n; ++k) {
372 vertexI[k] = startPoint[k] + confI[k];
373 }
374 simplex[i + 1] = new RealPointValuePair(vertexI, Double.NaN);
375 }
376
377 }
378
379 /** Evaluate all the non-evaluated points of the simplex.
380 * @param comparator comparator to use to sort simplex vertices from best to worst
381 * @exception FunctionEvaluationException if no value can be computed for the parameters
382 * @exception OptimizationException if the maximal number of evaluations is exceeded
383 */
384 protected void evaluateSimplex(final Comparator<RealPointValuePair> comparator)
385 throws FunctionEvaluationException, OptimizationException {
386
387 // evaluate the objective function at all non-evaluated simplex points
388 for (int i = 0; i < simplex.length; ++i) {
389 final RealPointValuePair vertex = simplex[i];
390 final double[] point = vertex.getPointRef();
391 if (Double.isNaN(vertex.getValue())) {
392 simplex[i] = new RealPointValuePair(point, evaluate(point), false);
393 }
394 }
395
396 // sort the simplex from best to worst
397 Arrays.sort(simplex, comparator);
398
399 }
400
401 /** Replace the worst point of the simplex by a new point.
402 * @param pointValuePair point to insert
403 * @param comparator comparator to use to sort simplex vertices from best to worst
404 */
405 protected void replaceWorstPoint(RealPointValuePair pointValuePair,
406 final Comparator<RealPointValuePair> comparator) {
407 int n = simplex.length - 1;
408 for (int i = 0; i < n; ++i) {
409 if (comparator.compare(simplex[i], pointValuePair) > 0) {
410 RealPointValuePair tmp = simplex[i];
411 simplex[i] = pointValuePair;
412 pointValuePair = tmp;
413 }
414 }
415 simplex[n] = pointValuePair;
416 }
417
418}