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;
18
19 import org.apache.commons.math4.legacy.exception.TooManyEvaluationsException;
20 import org.apache.commons.math4.legacy.exception.TooManyIterationsException;
21 import org.apache.commons.math4.legacy.core.IntegerSequence;
22
23 /**
24 * Base class for implementing optimizers.
25 * It contains the boiler-plate code for counting the number of evaluations
26 * of the objective function and the number of iterations of the algorithm.
27 * It also stores a {@link ConvergenceChecker convergence checker}, as well
28 * as {@link Tolerance default tolerances} (how the checker and tolerances
29 * are used is determined by subclasses).
30 * <em>It is not a "user" class.</em>
31 *
32 * @param <PAIR> Type of the point/value pair returned by the optimization
33 * algorithm.
34 *
35 * @since 3.1
36 */
37 public abstract class BaseOptimizer<PAIR> {
38 /** Callback to use for the evaluation counter. */
39 private static final MaxEvalCallback MAX_EVAL_CALLBACK = new MaxEvalCallback();
40 /** Callback to use for the iteration counter. */
41 private static final MaxIterCallback MAX_ITER_CALLBACK = new MaxIterCallback();
42
43 /** Convergence checker. */
44 private ConvergenceChecker<PAIR> checker;
45 /** Maximum number of evaluations. */
46 private int maxEvaluations;
47 /** Maximum number of iterations. */
48 private int maxIterations;
49 /** Evaluations counter. */
50 private IntegerSequence.Incrementor evaluations;
51 /** Iterations counter. */
52 private IntegerSequence.Incrementor iterations;
53 /** Relative tolerance. */
54 private double relativeTolerance = 1e-6;
55 /** Absolute tolerance. */
56 private double absoluteTolerance = 1e-6;
57
58 /**
59 * @param checker Convergence checker.
60 */
61 protected BaseOptimizer(ConvergenceChecker<PAIR> checker) {
62 this(checker, 0, Integer.MAX_VALUE);
63 }
64
65 /**
66 * @param checker Convergence checker.
67 * @param maxEval Maximum number of objective function evaluations.
68 * @param maxIter Maximum number of algorithm iterations.
69 */
70 protected BaseOptimizer(ConvergenceChecker<PAIR> checker,
71 int maxEval,
72 int maxIter) {
73 this.checker = checker;
74 this.maxEvaluations = maxEval;
75 this.maxIterations = maxIter;
76 }
77
78 /** @return the relative tolerance. */
79 protected double getRelativeTolerance() {
80 return relativeTolerance;
81 }
82
83 /** @return the absolute tolerance. */
84 protected double getAbsoluteTolerance() {
85 return absoluteTolerance;
86 }
87
88 /**
89 * Gets the maximal number of function evaluations.
90 *
91 * @return the maximal number of function evaluations.
92 */
93 public int getMaxEvaluations() {
94 return evaluations.getMaximalCount();
95 }
96
97 /**
98 * Gets the number of evaluations of the objective function.
99 * The number of evaluations corresponds to the last call to the
100 * {@code optimize} method. It is 0 if the method has not been
101 * called yet.
102 *
103 * @return the number of evaluations of the objective function.
104 */
105 public int getEvaluations() {
106 return evaluations.getCount();
107 }
108
109 /**
110 * Gets the maximal number of iterations.
111 *
112 * @return the maximal number of iterations.
113 */
114 public int getMaxIterations() {
115 return iterations.getMaximalCount();
116 }
117
118 /**
119 * Gets the number of iterations performed by the algorithm.
120 * The number iterations corresponds to the last call to the
121 * {@code optimize} method. It is 0 if the method has not been
122 * called yet.
123 *
124 * @return the number of evaluations of the objective function.
125 */
126 public int getIterations() {
127 return iterations.getCount();
128 }
129
130 /**
131 * Gets the convergence checker.
132 *
133 * @return the object used to check for convergence.
134 */
135 public ConvergenceChecker<PAIR> getConvergenceChecker() {
136 return checker;
137 }
138
139 /**
140 * Stores data and performs the optimization.
141 * <p>
142 * The list of parameters is open-ended so that sub-classes can extend it
143 * with arguments specific to their concrete implementations.
144 * <p>
145 * When the method is called multiple times, instance data is overwritten
146 * only when actually present in the list of arguments: when not specified,
147 * data set in a previous call is retained (and thus is optional in
148 * subsequent calls).
149 * <p>
150 * Important note: Subclasses <em>must</em> override
151 * {@link #parseOptimizationData(OptimizationData[])} if they need to register
152 * their own options; but then, they <em>must</em> also call
153 * {@code super.parseOptimizationData(optData)} within that method.
154 *
155 * @param optData Optimization data.
156 * This method will register the following data:
157 * <ul>
158 * <li>{@link MaxEval}</li>
159 * <li>{@link MaxIter}</li>
160 * <li>{@link ConvergenceChecker}</li>
161 * <li>{@link Tolerance}</li>
162 * </ul>
163 * @return a point/value pair that satisfies the convergence criteria.
164 * @throws TooManyEvaluationsException if the maximal number of
165 * evaluations is exceeded.
166 * @throws TooManyIterationsException if the maximal number of
167 * iterations is exceeded.
168 */
169 public PAIR optimize(OptimizationData... optData) {
170 // Parse options.
171 parseOptimizationData(optData);
172 // Reset counters.
173 resetCounters();
174 // Perform optimization.
175 return doOptimize();
176 }
177
178 /**
179 * Performs the optimization.
180 *
181 * @return a point/value pair that satisfies the convergence criteria.
182 * @throws TooManyEvaluationsException if the maximal number of
183 * evaluations is exceeded.
184 * @throws TooManyIterationsException if the maximal number of
185 * iterations is exceeded.
186 */
187 public PAIR optimize() {
188 // Reset counters.
189 resetCounters();
190 // Perform optimization.
191 return doOptimize();
192 }
193
194 /**
195 * Performs the bulk of the optimization algorithm.
196 *
197 * @return the point/value pair giving the optimal value of the
198 * objective function.
199 */
200 protected abstract PAIR doOptimize();
201
202 /**
203 * Increment the evaluation count.
204 *
205 * @throws TooManyEvaluationsException if the allowed evaluations
206 * have been exhausted.
207 */
208 protected void incrementEvaluationCount() {
209 evaluations.increment();
210 }
211
212 /**
213 * Increment the iteration count.
214 *
215 * @throws TooManyIterationsException if the allowed iterations
216 * have been exhausted.
217 */
218 protected void incrementIterationCount() {
219 iterations.increment();
220 }
221
222 /**
223 * Scans the list of (required and optional) optimization data that
224 * characterize the problem.
225 *
226 * @param optData Optimization data.
227 * The following data will be looked for:
228 * <ul>
229 * <li>{@link MaxEval}</li>
230 * <li>{@link MaxIter}</li>
231 * <li>{@link ConvergenceChecker}</li>
232 * </ul>
233 */
234 protected void parseOptimizationData(OptimizationData... optData) {
235 // The existing values (as set by the previous call) are reused if
236 // not provided in the argument list.
237 for (OptimizationData data : optData) {
238 if (data instanceof MaxEval) {
239 maxEvaluations = ((MaxEval) data).getMaxEval();
240 continue;
241 }
242 if (data instanceof MaxIter) {
243 maxIterations = ((MaxIter) data).getMaxIter();
244 continue;
245 }
246 if (data instanceof ConvergenceChecker) {
247 checker = (ConvergenceChecker<PAIR>) data;
248 continue;
249 }
250 if (data instanceof Tolerance) {
251 final Tolerance tol = (Tolerance) data;
252 relativeTolerance = tol.getRelativeTolerance();
253 absoluteTolerance = tol.getAbsoluteTolerance();
254 continue;
255 }
256 }
257 }
258
259 /** Reset counters. */
260 private void resetCounters() {
261 evaluations = IntegerSequence.Incrementor.create()
262 .withMaximalCount(maxEvaluations)
263 .withCallback(MAX_EVAL_CALLBACK);
264 iterations = IntegerSequence.Incrementor.create()
265 .withMaximalCount(maxIterations)
266 .withCallback(MAX_ITER_CALLBACK);
267 }
268
269 /**
270 * Defines the action to perform when reaching the maximum number
271 * of evaluations.
272 */
273 private static final class MaxEvalCallback
274 implements IntegerSequence.Incrementor.MaxCountExceededCallback {
275 /**
276 * {@inheritDoc}
277 * @throws TooManyEvaluationsException
278 */
279 @Override
280 public void trigger(int max) {
281 throw new TooManyEvaluationsException(max);
282 }
283 }
284
285 /**
286 * Defines the action to perform when reaching the maximum number
287 * of evaluations.
288 */
289 private static final class MaxIterCallback
290 implements IntegerSequence.Incrementor.MaxCountExceededCallback {
291 /**
292 * {@inheritDoc}
293 * @throws TooManyIterationsException
294 */
295 @Override
296 public void trigger(int max) {
297 throw new TooManyIterationsException(max);
298 }
299 }
300 }