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.univariate; 018 019import org.apache.commons.math4.legacy.analysis.UnivariateFunction; 020import org.apache.commons.math4.legacy.exception.MaxCountExceededException; 021import org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException; 022import org.apache.commons.math4.legacy.exception.TooManyEvaluationsException; 023import org.apache.commons.math4.legacy.optim.nonlinear.scalar.GoalType; 024import org.apache.commons.math4.core.jdkmath.JdkMath; 025import org.apache.commons.math4.legacy.core.IntegerSequence.Incrementor; 026 027/** 028 * Provide an interval that brackets a local optimum of a function. 029 * This code is based on a Python implementation (from <em>SciPy</em>, 030 * module {@code optimize.py} v0.5). 031 * 032 * @since 2.2 033 */ 034public class BracketFinder { 035 /** Tolerance to avoid division by zero. */ 036 private static final double EPS_MIN = 1e-21; 037 /** 038 * Golden section. 039 */ 040 private static final double GOLD = 1.618034; 041 /** 042 * Factor for expanding the interval. 043 */ 044 private final double growLimit; 045 /** 046 * Number of allowed function evaluations. 047 */ 048 private final int maxEvaluations; 049 /** 050 * Number of function evaluations performed in the last search. 051 */ 052 private int evaluations; 053 /** 054 * Lower bound of the bracket. 055 */ 056 private double lo; 057 /** 058 * Higher bound of the bracket. 059 */ 060 private double hi; 061 /** 062 * Point inside the bracket. 063 */ 064 private double mid; 065 /** 066 * Function value at {@link #lo}. 067 */ 068 private double fLo; 069 /** 070 * Function value at {@link #hi}. 071 */ 072 private double fHi; 073 /** 074 * Function value at {@link #mid}. 075 */ 076 private double fMid; 077 078 /** 079 * Constructor with default values {@code 100, 500} (see the 080 * {@link #BracketFinder(double,int) other constructor}). 081 */ 082 public BracketFinder() { 083 this(100, 500); 084 } 085 086 /** 087 * Create a bracketing interval finder. 088 * 089 * @param growLimit Expanding factor. 090 * @param maxEvaluations Maximum number of evaluations allowed for finding 091 * a bracketing interval. 092 */ 093 public BracketFinder(double growLimit, 094 int maxEvaluations) { 095 if (growLimit <= 0) { 096 throw new NotStrictlyPositiveException(growLimit); 097 } 098 if (maxEvaluations <= 0) { 099 throw new NotStrictlyPositiveException(maxEvaluations); 100 } 101 102 this.growLimit = growLimit; 103 this.maxEvaluations = maxEvaluations; 104 } 105 106 /** 107 * Search new points that bracket a local optimum of the function. 108 * 109 * @param func Function whose optimum should be bracketed. 110 * @param goal {@link GoalType Goal type}. 111 * @param xA Initial point. 112 * @param xB Initial point. 113 * @throws TooManyEvaluationsException if the maximum number of evaluations 114 * is exceeded. 115 */ 116 public void search(UnivariateFunction func, 117 GoalType goal, 118 double xA, 119 double xB) { 120 final FunctionEvaluator eval = new FunctionEvaluator(func); 121 final boolean isMinim = goal == GoalType.MINIMIZE; 122 123 double fA = eval.value(xA); 124 double fB = eval.value(xB); 125 if (isMinim ? 126 fA < fB : 127 fA > fB) { 128 129 double tmp = xA; 130 xA = xB; 131 xB = tmp; 132 133 tmp = fA; 134 fA = fB; 135 fB = tmp; 136 } 137 138 double xC = xB + GOLD * (xB - xA); 139 double fC = eval.value(xC); 140 141 while (isMinim ? fC < fB : fC > fB) { 142 double tmp1 = (xB - xA) * (fB - fC); 143 double tmp2 = (xB - xC) * (fB - fA); 144 145 double val = tmp2 - tmp1; 146 double denom = JdkMath.abs(val) < EPS_MIN ? 2 * EPS_MIN : 2 * val; 147 148 double w = xB - ((xB - xC) * tmp2 - (xB - xA) * tmp1) / denom; 149 double wLim = xB + growLimit * (xC - xB); 150 151 double fW; 152 if ((w - xC) * (xB - w) > 0) { 153 fW = eval.value(w); 154 if (isMinim ? 155 fW < fC : 156 fW > fC) { 157 xA = xB; 158 xB = w; 159 fA = fB; 160 fB = fW; 161 break; 162 } else if (isMinim ? 163 fW > fB : 164 fW < fB) { 165 xC = w; 166 fC = fW; 167 break; 168 } 169 w = xC + GOLD * (xC - xB); 170 fW = eval.value(w); 171 } else if ((w - wLim) * (wLim - xC) >= 0) { 172 w = wLim; 173 fW = eval.value(w); 174 } else if ((w - wLim) * (xC - w) > 0) { 175 fW = eval.value(w); 176 if (isMinim ? 177 fW < fC : 178 fW > fC) { 179 xB = xC; 180 xC = w; 181 w = xC + GOLD * (xC - xB); 182 fB = fC; 183 fC =fW; 184 fW = eval.value(w); 185 } 186 } else { 187 w = xC + GOLD * (xC - xB); 188 fW = eval.value(w); 189 } 190 191 xA = xB; 192 fA = fB; 193 xB = xC; 194 fB = fC; 195 xC = w; 196 fC = fW; 197 } 198 199 lo = xA; 200 fLo = fA; 201 mid = xB; 202 fMid = fB; 203 hi = xC; 204 fHi = fC; 205 206 if (lo > hi) { 207 double tmp = lo; 208 lo = hi; 209 hi = tmp; 210 211 tmp = fLo; 212 fLo = fHi; 213 fHi = tmp; 214 } 215 } 216 217 /** 218 * @return the number of evaluations. 219 */ 220 public int getMaxEvaluations() { 221 return maxEvaluations; 222 } 223 224 /** 225 * @return the number of evaluations. 226 */ 227 public int getEvaluations() { 228 return evaluations; 229 } 230 231 /** 232 * @return the lower bound of the bracket. 233 * @see #getFLo() 234 */ 235 public double getLo() { 236 return lo; 237 } 238 239 /** 240 * Get function value at {@link #getLo()}. 241 * @return function value at {@link #getLo()} 242 */ 243 public double getFLo() { 244 return fLo; 245 } 246 247 /** 248 * @return the higher bound of the bracket. 249 * @see #getFHi() 250 */ 251 public double getHi() { 252 return hi; 253 } 254 255 /** 256 * Get function value at {@link #getHi()}. 257 * @return function value at {@link #getHi()} 258 */ 259 public double getFHi() { 260 return fHi; 261 } 262 263 /** 264 * @return a point in the middle of the bracket. 265 * @see #getFMid() 266 */ 267 public double getMid() { 268 return mid; 269 } 270 271 /** 272 * Get function value at {@link #getMid()}. 273 * @return function value at {@link #getMid()} 274 */ 275 public double getFMid() { 276 return fMid; 277 } 278 279 /** 280 * Utility for incrementing a counter at each function evaluation. 281 */ 282 private final class FunctionEvaluator { 283 /** Function. */ 284 private final UnivariateFunction func; 285 /** Counter. */ 286 private final Incrementor inc; 287 288 /** 289 * @param func Function. 290 */ 291 FunctionEvaluator(UnivariateFunction func) { 292 this.func = func; 293 inc = Incrementor.create().withMaximalCount(maxEvaluations); 294 evaluations = 0; 295 } 296 297 /** 298 * @param x Argument. 299 * @return {@code f(x)} 300 * @throws TooManyEvaluationsException if the maximal number of evaluations is 301 * exceeded. 302 */ 303 double value(double x) { 304 try { 305 inc.increment(); 306 evaluations = inc.getCount(); 307 } catch (MaxCountExceededException e) { 308 throw new TooManyEvaluationsException(e.getMax()); 309 } 310 311 return func.value(x); 312 } 313 } 314}