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}