    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     */
    017    package org.apache.commons.math3.optim;
    018
    019    import org.apache.commons.math3.util.Incrementor;
    020    import org.apache.commons.math3.exception.TooManyEvaluationsException;
    021    import org.apache.commons.math3.exception.TooManyIterationsException;
    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     * and storing the convergence checker.
    028     * <em>It is not a "user" class.</em>
    029     *
    030     * @param <PAIR> Type of the point/value pair returned by the optimization
    031     * algorithm.
    032     *
    033     * @version $Id$
    034     * @since 3.1
    035     */
    036    public abstract class BaseOptimizer<PAIR> {
    037        /** Evaluations counter. */
    038        protected final Incrementor evaluations;
    039        /** Iterations counter. */
    040        protected final Incrementor iterations;
    041        /** Convergence checker. */
    042        private ConvergenceChecker<PAIR> checker;
    043
    044        /**
    045         * @param checker Convergence checker.
    046         */
    047        protected BaseOptimizer(ConvergenceChecker<PAIR> checker) {
    048            this.checker = checker;
    049
    050            evaluations = new Incrementor(0, new MaxEvalCallback());
    051            iterations = new Incrementor(0, new MaxIterCallback());
    052        }
    053
    054        /**
    055         * Gets the maximal number of function evaluations.
    056         *
    057         * @return the maximal number of function evaluations.
    058         */
    059        public int getMaxEvaluations() {
    060            return evaluations.getMaximalCount();
    061        }
    062
    063        /**
    064         * Gets the number of evaluations of the objective function.
    065         * The number of evaluations corresponds to the last call to the
    066         * {@code optimize} method. It is 0 if the method has not been
    067         * called yet.
    068         *
    069         * @return the number of evaluations of the objective function.
    070         */
    071        public int getEvaluations() {
    072            return evaluations.getCount();
    073        }
    074
    075        /**
    076         * Gets the maximal number of iterations.
    077         *
    078         * @return the maximal number of iterations.
    079         */
    080        public int getMaxIterations() {
    081            return iterations.getMaximalCount();
    082        }
    083
    084        /**
    085         * Gets the number of iterations performed by the algorithm.
    086         * The number iterations corresponds to the last call to the
    087         * {@code optimize} method. It is 0 if the method has not been
    088         * called yet.
    089         *
    090         * @return the number of evaluations of the objective function.
    091         */
    092        public int getIterations() {
    093            return iterations.getCount();
    094        }
    095
    096        /**
    097         * Gets the convergence checker.
    098         *
    099         * @return the object used to check for convergence.
    100         */
    101        public ConvergenceChecker<PAIR> getConvergenceChecker() {
    102            return checker;
    103        }
    104
    105        /**
    106         * Stores data and performs the optimization.
    107         * <br/>
    108         * The list of parameters is open-ended so that sub-classes can extend it
    109         * with arguments specific to their concrete implementations.
    110         * <br/>
    111         * When the method is called multiple times, instance data is overwritten
    112         * only when actually present in the list of arguments: when not specified,
    113         * data set in a previous call is retained (and thus is optional in
    114         * subsequent calls).
    115         *
    116         * @param optData Optimization data. The following data will be looked for:
    117         * <ul>
    118         *  <li>{@link MaxEval}</li>
    119         *  <li>{@link MaxIter}</li>
    120         * </ul>
    121         * @return a point/value pair that satifies the convergence criteria.
    122         * @throws TooManyEvaluationsException if the maximal number of
    123         * evaluations is exceeded.
    124         * @throws TooManyIterationsException if the maximal number of
    125         * iterations is exceeded.
    126         */
    127        public PAIR optimize(OptimizationData... optData)
    128            throws TooManyEvaluationsException,
    129                   TooManyIterationsException {
    130            // Retrieve settings.
    131            parseOptimizationData(optData);
    132            // Reset counters.
    133            evaluations.resetCount();
    134            iterations.resetCount();
    135            // Perform optimization.
    136            return doOptimize();
    137        }
    138
    139        /**
    140         * Performs the bulk of the optimization algorithm.
    141         *
    142         * @return the point/value pair giving the optimal value of the
    143         * objective function.
    144         */
    145        protected abstract PAIR doOptimize();
    146
    147        /**
    148         * Increment the evaluation count.
    149         *
    150         * @throws TooManyEvaluationsException if the allowed evaluations
    151         * have been exhausted.
    152         */
    153        protected void incrementEvaluationCount()
    154            throws TooManyEvaluationsException {
    155            evaluations.incrementCount();
    156        }
    157
    158        /**
    159         * Increment the iteration count.
    160         *
    161         * @throws TooManyIterationsException if the allowed iterations
    162         * have been exhausted.
    163         */
    164        protected void incrementIterationCount()
    165            throws TooManyIterationsException {
    166            iterations.incrementCount();
    167        }
    168
    169        /**
    170         * Scans the list of (required and optional) optimization data that
    171         * characterize the problem.
    172         *
    173         * @param optData Optimization data.
    174         * The following data will be looked for:
    175         * <ul>
    176         *  <li>{@link MaxEval}</li>
    177         *  <li>{@link MaxIter}</li>
    178         * </ul>
    179         */
    180        private void parseOptimizationData(OptimizationData... optData) {
    181            // The existing values (as set by the previous call) are reused if
    182            // not provided in the argument list.
    183            for (OptimizationData data : optData) {
    184                if (data instanceof MaxEval) {
    185                    evaluations.setMaximalCount(((MaxEval) data).getMaxEval());
    186                    continue;
    187                }
    188                if (data instanceof MaxIter) {
    189                    iterations.setMaximalCount(((MaxIter) data).getMaxIter());
    190                    continue;
    191                }
    192            }
    193        }
    194
    195        /**
    196         * Defines the action to perform when reaching the maximum number
    197         * of evaluations.
    198         */
    199        private static class MaxEvalCallback
    200            implements  Incrementor.MaxCountExceededCallback {
    201            /**
    202             * {@inheritDoc}
    203             * @throws TooManyEvaluationsException.
    204             */
    205            public void trigger(int max) {
    206                throw new TooManyEvaluationsException(max);
    207            }
    208        }
    209
    210        /**
    211         * Defines the action to perform when reaching the maximum number
    212         * of evaluations.
    213         */
    214        private static class MaxIterCallback
    215            implements Incrementor.MaxCountExceededCallback {
    216            /**
    217             * {@inheritDoc}
    218             * @throws TooManyIterationsException.
    219             */
    220            public void trigger(int max) {
    221                throw new TooManyIterationsException(max);
    222            }
    223        }
    224    }