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