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;
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 }