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 }