001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.math4.legacy.optim;
018
019import org.apache.commons.math4.legacy.exception.TooManyEvaluationsException;
020import org.apache.commons.math4.legacy.exception.TooManyIterationsException;
021import org.apache.commons.math4.legacy.core.IntegerSequence;
022
023/**
024 * Base class for implementing optimizers.
025 * It contains the boiler-plate code for counting the number of evaluations
026 * of the objective function and the number of iterations of the algorithm.
027 * It also stores a {@link ConvergenceChecker convergence checker}, as well
028 * as {@link Tolerance default tolerances} (how the checker and tolerances
029 * are used is determined by subclasses).
030 * <em>It is not a "user" class.</em>
031 *
032 * @param <PAIR> Type of the point/value pair returned by the optimization
033 * algorithm.
034 *
035 * @since 3.1
036 */
037public abstract class BaseOptimizer<PAIR> {
038    /** Callback to use for the evaluation counter. */
039    private static final MaxEvalCallback MAX_EVAL_CALLBACK = new MaxEvalCallback();
040    /** Callback to use for the iteration counter. */
041    private static final MaxIterCallback MAX_ITER_CALLBACK = new MaxIterCallback();
042
043    /** Convergence checker. */
044    private ConvergenceChecker<PAIR> checker;
045    /** Maximum number of evaluations. */
046    private int maxEvaluations;
047    /** Maximum number of iterations. */
048    private int maxIterations;
049    /** Evaluations counter. */
050    private IntegerSequence.Incrementor evaluations;
051    /** Iterations counter. */
052    private IntegerSequence.Incrementor iterations;
053    /** Relative tolerance. */
054    private double relativeTolerance = 1e-6;
055    /** Absolute tolerance. */
056    private double absoluteTolerance = 1e-6;
057
058    /**
059     * @param checker Convergence checker.
060     */
061    protected BaseOptimizer(ConvergenceChecker<PAIR> checker) {
062        this(checker, 0, Integer.MAX_VALUE);
063    }
064
065    /**
066     * @param checker Convergence checker.
067     * @param maxEval Maximum number of objective function evaluations.
068     * @param maxIter Maximum number of algorithm iterations.
069     */
070    protected BaseOptimizer(ConvergenceChecker<PAIR> checker,
071                            int maxEval,
072                            int maxIter) {
073        this.checker = checker;
074        this.maxEvaluations = maxEval;
075        this.maxIterations = maxIter;
076    }
077
078    /** @return the relative tolerance. */
079    protected double getRelativeTolerance() {
080        return relativeTolerance;
081    }
082
083    /** @return the absolute tolerance. */
084    protected double getAbsoluteTolerance() {
085        return absoluteTolerance;
086    }
087
088    /**
089     * Gets the maximal number of function evaluations.
090     *
091     * @return the maximal number of function evaluations.
092     */
093    public int getMaxEvaluations() {
094        return evaluations.getMaximalCount();
095    }
096
097    /**
098     * Gets the number of evaluations of the objective function.
099     * 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}