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         *
108         * @param optData Optimization data. The following data will be looked for:
109         * <ul>
110         *  <li>{@link MaxEval}</li>
111         *  <li>{@link MaxIter}</li>
112         * </ul>
113         * @return a point/value pair that satifies the convergence criteria.
114         * @throws TooManyEvaluationsException if the maximal number of
115         * evaluations is exceeded.
116         * @throws TooManyIterationsException if the maximal number of
117         * iterations is exceeded.
118         */
119        public PAIR optimize(OptimizationData... optData)
120            throws TooManyEvaluationsException,
121                   TooManyIterationsException {
122            // Retrieve settings.
123            parseOptimizationData(optData);
124            // Reset counters.
125            evaluations.resetCount();
126            iterations.resetCount();
127            // Perform optimization.
128            return doOptimize();
129        }
130    
131        /**
132         * Performs the bulk of the optimization algorithm.
133         *
134         * @return the point/value pair giving the optimal value of the
135         * objective function.
136         */
137        protected abstract PAIR doOptimize();
138    
139        /**
140         * Increment the evaluation count.
141         *
142         * @throws TooManyEvaluationsException if the allowed evaluations
143         * have been exhausted.
144         */
145        protected void incrementEvaluationCount()
146            throws TooManyEvaluationsException {
147            evaluations.incrementCount();
148        }
149    
150        /**
151         * Increment the iteration count.
152         *
153         * @throws TooManyIterationsException if the allowed iterations
154         * have been exhausted.
155         */
156        protected void incrementIterationCount()
157            throws TooManyIterationsException {
158            iterations.incrementCount();
159        }
160    
161        /**
162         * Scans the list of (required and optional) optimization data that
163         * characterize the problem.
164         *
165         * @param optData Optimization data.
166         * The following data will be looked for:
167         * <ul>
168         *  <li>{@link MaxEval}</li>
169         *  <li>{@link MaxIter}</li>
170         * </ul>
171         */
172        private void parseOptimizationData(OptimizationData... optData) {
173            // The existing values (as set by the previous call) are reused if
174            // not provided in the argument list.
175            for (OptimizationData data : optData) {
176                if (data instanceof MaxEval) {
177                    evaluations.setMaximalCount(((MaxEval) data).getMaxEval());
178                    continue;
179                }
180                if (data instanceof MaxIter) {
181                    iterations.setMaximalCount(((MaxIter) data).getMaxIter());
182                    continue;
183                }
184            }
185        }
186    
187        /**
188         * Defines the action to perform when reaching the maximum number
189         * of evaluations.
190         */
191        private static class MaxEvalCallback
192            implements  Incrementor.MaxCountExceededCallback {
193            /**
194             * {@inheritDoc}
195             * @throws TooManyEvaluationsException.
196             */
197            public void trigger(int max) {
198                throw new TooManyEvaluationsException(max);
199            }
200        }
201    
202        /**
203         * Defines the action to perform when reaching the maximum number
204         * of evaluations.
205         */
206        private static class MaxIterCallback
207            implements Incrementor.MaxCountExceededCallback {
208            /**
209             * {@inheritDoc}
210             * @throws TooManyIterationsException.
211             */
212            public void trigger(int max) {
213                throw new TooManyIterationsException(max);
214            }
215        }
216    }