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 }