| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.math4.optim.univariate; |
| |
| import org.apache.commons.math4.analysis.UnivariateFunction; |
| import org.apache.commons.math4.exception.MaxCountExceededException; |
| import org.apache.commons.math4.exception.NotStrictlyPositiveException; |
| import org.apache.commons.math4.exception.TooManyEvaluationsException; |
| import org.apache.commons.math4.optim.nonlinear.scalar.GoalType; |
| import org.apache.commons.math4.util.FastMath; |
| import org.apache.commons.math4.util.IntegerSequence.Incrementor; |
| |
| /** |
| * Provide an interval that brackets a local optimum of a function. |
| * This code is based on a Python implementation (from <em>SciPy</em>, |
| * module {@code optimize.py} v0.5). |
| * |
| * @since 2.2 |
| */ |
| public class BracketFinder { |
| /** Tolerance to avoid division by zero. */ |
| private static final double EPS_MIN = 1e-21; |
| /** |
| * Golden section. |
| */ |
| private static final double GOLD = 1.618034; |
| /** |
| * Factor for expanding the interval. |
| */ |
| private final double growLimit; |
| /** |
| * Number of allowed function evaluations. |
| */ |
| private final int maxEvaluations; |
| /** |
| * Number of function evaluations performed in the last search. |
| */ |
| private int evaluations; |
| /** |
| * Lower bound of the bracket. |
| */ |
| private double lo; |
| /** |
| * Higher bound of the bracket. |
| */ |
| private double hi; |
| /** |
| * Point inside the bracket. |
| */ |
| private double mid; |
| /** |
| * Function value at {@link #lo}. |
| */ |
| private double fLo; |
| /** |
| * Function value at {@link #hi}. |
| */ |
| private double fHi; |
| /** |
| * Function value at {@link #mid}. |
| */ |
| private double fMid; |
| |
| /** |
| * Constructor with default values {@code 100, 500} (see the |
| * {@link #BracketFinder(double,int) other constructor}). |
| */ |
| public BracketFinder() { |
| this(100, 500); |
| } |
| |
| /** |
| * Create a bracketing interval finder. |
| * |
| * @param growLimit Expanding factor. |
| * @param maxEvaluations Maximum number of evaluations allowed for finding |
| * a bracketing interval. |
| */ |
| public BracketFinder(double growLimit, |
| int maxEvaluations) { |
| if (growLimit <= 0) { |
| throw new NotStrictlyPositiveException(growLimit); |
| } |
| if (maxEvaluations <= 0) { |
| throw new NotStrictlyPositiveException(maxEvaluations); |
| } |
| |
| this.growLimit = growLimit; |
| this.maxEvaluations = maxEvaluations; |
| } |
| |
| /** |
| * Search new points that bracket a local optimum of the function. |
| * |
| * @param func Function whose optimum should be bracketed. |
| * @param goal {@link GoalType Goal type}. |
| * @param xA Initial point. |
| * @param xB Initial point. |
| * @throws TooManyEvaluationsException if the maximum number of evaluations |
| * is exceeded. |
| */ |
| public void search(UnivariateFunction func, |
| GoalType goal, |
| double xA, |
| double xB) { |
| final FunctionEvaluator eval = new FunctionEvaluator(func); |
| final boolean isMinim = goal == GoalType.MINIMIZE; |
| |
| double fA = eval.value(xA); |
| double fB = eval.value(xB); |
| if (isMinim ? |
| fA < fB : |
| fA > fB) { |
| |
| double tmp = xA; |
| xA = xB; |
| xB = tmp; |
| |
| tmp = fA; |
| fA = fB; |
| fB = tmp; |
| } |
| |
| double xC = xB + GOLD * (xB - xA); |
| double fC = eval.value(xC); |
| |
| while (isMinim ? fC < fB : fC > fB) { |
| double tmp1 = (xB - xA) * (fB - fC); |
| double tmp2 = (xB - xC) * (fB - fA); |
| |
| double val = tmp2 - tmp1; |
| double denom = FastMath.abs(val) < EPS_MIN ? 2 * EPS_MIN : 2 * val; |
| |
| double w = xB - ((xB - xC) * tmp2 - (xB - xA) * tmp1) / denom; |
| double wLim = xB + growLimit * (xC - xB); |
| |
| double fW; |
| if ((w - xC) * (xB - w) > 0) { |
| fW = eval.value(w); |
| if (isMinim ? |
| fW < fC : |
| fW > fC) { |
| xA = xB; |
| xB = w; |
| fA = fB; |
| fB = fW; |
| break; |
| } else if (isMinim ? |
| fW > fB : |
| fW < fB) { |
| xC = w; |
| fC = fW; |
| break; |
| } |
| w = xC + GOLD * (xC - xB); |
| fW = eval.value(w); |
| } else if ((w - wLim) * (wLim - xC) >= 0) { |
| w = wLim; |
| fW = eval.value(w); |
| } else if ((w - wLim) * (xC - w) > 0) { |
| fW = eval.value(w); |
| if (isMinim ? |
| fW < fC : |
| fW > fC) { |
| xB = xC; |
| xC = w; |
| w = xC + GOLD * (xC - xB); |
| fB = fC; |
| fC =fW; |
| fW = eval.value(w); |
| } |
| } else { |
| w = xC + GOLD * (xC - xB); |
| fW = eval.value(w); |
| } |
| |
| xA = xB; |
| fA = fB; |
| xB = xC; |
| fB = fC; |
| xC = w; |
| fC = fW; |
| } |
| |
| lo = xA; |
| fLo = fA; |
| mid = xB; |
| fMid = fB; |
| hi = xC; |
| fHi = fC; |
| |
| if (lo > hi) { |
| double tmp = lo; |
| lo = hi; |
| hi = tmp; |
| |
| tmp = fLo; |
| fLo = fHi; |
| fHi = tmp; |
| } |
| } |
| |
| /** |
| * @return the number of evaluations. |
| */ |
| public int getMaxEvaluations() { |
| return maxEvaluations; |
| } |
| |
| /** |
| * @return the number of evaluations. |
| */ |
| public int getEvaluations() { |
| return evaluations; |
| } |
| |
| /** |
| * @return the lower bound of the bracket. |
| * @see #getFLo() |
| */ |
| public double getLo() { |
| return lo; |
| } |
| |
| /** |
| * Get function value at {@link #getLo()}. |
| * @return function value at {@link #getLo()} |
| */ |
| public double getFLo() { |
| return fLo; |
| } |
| |
| /** |
| * @return the higher bound of the bracket. |
| * @see #getFHi() |
| */ |
| public double getHi() { |
| return hi; |
| } |
| |
| /** |
| * Get function value at {@link #getHi()}. |
| * @return function value at {@link #getHi()} |
| */ |
| public double getFHi() { |
| return fHi; |
| } |
| |
| /** |
| * @return a point in the middle of the bracket. |
| * @see #getFMid() |
| */ |
| public double getMid() { |
| return mid; |
| } |
| |
| /** |
| * Get function value at {@link #getMid()}. |
| * @return function value at {@link #getMid()} |
| */ |
| public double getFMid() { |
| return fMid; |
| } |
| |
| /** |
| * Utility for incrementing a counter at each function evaluation. |
| */ |
| private class FunctionEvaluator { |
| /** Function. */ |
| private final UnivariateFunction func; |
| /** Counter. */ |
| private final Incrementor inc; |
| |
| /** |
| * @param func Function. |
| */ |
| FunctionEvaluator(UnivariateFunction func) { |
| this.func = func; |
| inc = Incrementor.create().withMaximalCount(maxEvaluations); |
| evaluations = 0; |
| } |
| |
| /** |
| * @param x Argument. |
| * @return {@code f(x)} |
| * @throws TooManyEvaluationsException if the maximal number of evaluations is |
| * exceeded. |
| */ |
| double value(double x) { |
| try { |
| inc.increment(); |
| evaluations = inc.getCount(); |
| } catch (MaxCountExceededException e) { |
| throw new TooManyEvaluationsException(e.getMax()); |
| } |
| |
| return func.value(x); |
| } |
| } |
| } |