BracketFinder.java

  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.univariate;

  18. import org.apache.commons.math4.legacy.analysis.UnivariateFunction;
  19. import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
  20. import org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException;
  21. import org.apache.commons.math4.legacy.exception.TooManyEvaluationsException;
  22. import org.apache.commons.math4.legacy.optim.nonlinear.scalar.GoalType;
  23. import org.apache.commons.math4.core.jdkmath.JdkMath;
  24. import org.apache.commons.math4.legacy.core.IntegerSequence.Incrementor;

  25. /**
  26.  * Provide an interval that brackets a local optimum of a function.
  27.  * This code is based on a Python implementation (from <em>SciPy</em>,
  28.  * module {@code optimize.py} v0.5).
  29.  *
  30.  * @since 2.2
  31.  */
  32. public class BracketFinder {
  33.     /** Tolerance to avoid division by zero. */
  34.     private static final double EPS_MIN = 1e-21;
  35.     /**
  36.      * Golden section.
  37.      */
  38.     private static final double GOLD = 1.618034;
  39.     /**
  40.      * Factor for expanding the interval.
  41.      */
  42.     private final double growLimit;
  43.     /**
  44.      * Number of allowed function evaluations.
  45.      */
  46.     private final int maxEvaluations;
  47.     /**
  48.      * Number of function evaluations performed in the last search.
  49.      */
  50.     private int evaluations;
  51.     /**
  52.      * Lower bound of the bracket.
  53.      */
  54.     private double lo;
  55.     /**
  56.      * Higher bound of the bracket.
  57.      */
  58.     private double hi;
  59.     /**
  60.      * Point inside the bracket.
  61.      */
  62.     private double mid;
  63.     /**
  64.      * Function value at {@link #lo}.
  65.      */
  66.     private double fLo;
  67.     /**
  68.      * Function value at {@link #hi}.
  69.      */
  70.     private double fHi;
  71.     /**
  72.      * Function value at {@link #mid}.
  73.      */
  74.     private double fMid;

  75.     /**
  76.      * Constructor with default values {@code 100, 500} (see the
  77.      * {@link #BracketFinder(double,int) other constructor}).
  78.      */
  79.     public BracketFinder() {
  80.         this(100, 500);
  81.     }

  82.     /**
  83.      * Create a bracketing interval finder.
  84.      *
  85.      * @param growLimit Expanding factor.
  86.      * @param maxEvaluations Maximum number of evaluations allowed for finding
  87.      * a bracketing interval.
  88.      */
  89.     public BracketFinder(double growLimit,
  90.                          int maxEvaluations) {
  91.         if (growLimit <= 0) {
  92.             throw new NotStrictlyPositiveException(growLimit);
  93.         }
  94.         if (maxEvaluations <= 0) {
  95.             throw new NotStrictlyPositiveException(maxEvaluations);
  96.         }

  97.         this.growLimit = growLimit;
  98.         this.maxEvaluations = maxEvaluations;
  99.     }

  100.     /**
  101.      * Search new points that bracket a local optimum of the function.
  102.      *
  103.      * @param func Function whose optimum should be bracketed.
  104.      * @param goal {@link GoalType Goal type}.
  105.      * @param xA Initial point.
  106.      * @param xB Initial point.
  107.      * @throws TooManyEvaluationsException if the maximum number of evaluations
  108.      * is exceeded.
  109.      */
  110.     public void search(UnivariateFunction func,
  111.                        GoalType goal,
  112.                        double xA,
  113.                        double xB) {
  114.         final FunctionEvaluator eval = new FunctionEvaluator(func);
  115.         final boolean isMinim = goal == GoalType.MINIMIZE;

  116.         double fA = eval.value(xA);
  117.         double fB = eval.value(xB);
  118.         if (isMinim ?
  119.             fA < fB :
  120.             fA > fB) {

  121.             double tmp = xA;
  122.             xA = xB;
  123.             xB = tmp;

  124.             tmp = fA;
  125.             fA = fB;
  126.             fB = tmp;
  127.         }

  128.         double xC = xB + GOLD * (xB - xA);
  129.         double fC = eval.value(xC);

  130.         while (isMinim ? fC < fB : fC > fB) {
  131.             double tmp1 = (xB - xA) * (fB - fC);
  132.             double tmp2 = (xB - xC) * (fB - fA);

  133.             double val = tmp2 - tmp1;
  134.             double denom = JdkMath.abs(val) < EPS_MIN ? 2 * EPS_MIN : 2 * val;

  135.             double w = xB - ((xB - xC) * tmp2 - (xB - xA) * tmp1) / denom;
  136.             double wLim = xB + growLimit * (xC - xB);

  137.             double fW;
  138.             if ((w - xC) * (xB - w) > 0) {
  139.                 fW = eval.value(w);
  140.                 if (isMinim ?
  141.                     fW < fC :
  142.                     fW > fC) {
  143.                     xA = xB;
  144.                     xB = w;
  145.                     fA = fB;
  146.                     fB = fW;
  147.                     break;
  148.                 } else if (isMinim ?
  149.                            fW > fB :
  150.                            fW < fB) {
  151.                     xC = w;
  152.                     fC = fW;
  153.                     break;
  154.                 }
  155.                 w = xC + GOLD * (xC - xB);
  156.                 fW = eval.value(w);
  157.             } else if ((w - wLim) * (wLim - xC) >= 0) {
  158.                 w = wLim;
  159.                 fW = eval.value(w);
  160.             } else if ((w - wLim) * (xC - w) > 0) {
  161.                 fW = eval.value(w);
  162.                 if (isMinim ?
  163.                     fW < fC :
  164.                     fW > fC) {
  165.                     xB = xC;
  166.                     xC = w;
  167.                     w = xC + GOLD * (xC - xB);
  168.                     fB = fC;
  169.                     fC =fW;
  170.                     fW = eval.value(w);
  171.                 }
  172.             } else {
  173.                 w = xC + GOLD * (xC - xB);
  174.                 fW = eval.value(w);
  175.             }

  176.             xA = xB;
  177.             fA = fB;
  178.             xB = xC;
  179.             fB = fC;
  180.             xC = w;
  181.             fC = fW;
  182.         }

  183.         lo = xA;
  184.         fLo = fA;
  185.         mid = xB;
  186.         fMid = fB;
  187.         hi = xC;
  188.         fHi = fC;

  189.         if (lo > hi) {
  190.             double tmp = lo;
  191.             lo = hi;
  192.             hi = tmp;

  193.             tmp = fLo;
  194.             fLo = fHi;
  195.             fHi = tmp;
  196.         }
  197.     }

  198.     /**
  199.      * @return the number of evaluations.
  200.      */
  201.     public int getMaxEvaluations() {
  202.         return maxEvaluations;
  203.     }

  204.     /**
  205.      * @return the number of evaluations.
  206.      */
  207.     public int getEvaluations() {
  208.         return evaluations;
  209.     }

  210.     /**
  211.      * @return the lower bound of the bracket.
  212.      * @see #getFLo()
  213.      */
  214.     public double getLo() {
  215.         return lo;
  216.     }

  217.     /**
  218.      * Get function value at {@link #getLo()}.
  219.      * @return function value at {@link #getLo()}
  220.      */
  221.     public double getFLo() {
  222.         return fLo;
  223.     }

  224.     /**
  225.      * @return the higher bound of the bracket.
  226.      * @see #getFHi()
  227.      */
  228.     public double getHi() {
  229.         return hi;
  230.     }

  231.     /**
  232.      * Get function value at {@link #getHi()}.
  233.      * @return function value at {@link #getHi()}
  234.      */
  235.     public double getFHi() {
  236.         return fHi;
  237.     }

  238.     /**
  239.      * @return a point in the middle of the bracket.
  240.      * @see #getFMid()
  241.      */
  242.     public double getMid() {
  243.         return mid;
  244.     }

  245.     /**
  246.      * Get function value at {@link #getMid()}.
  247.      * @return function value at {@link #getMid()}
  248.      */
  249.     public double getFMid() {
  250.         return fMid;
  251.     }

  252.     /**
  253.      * Utility for incrementing a counter at each function evaluation.
  254.      */
  255.     private final class FunctionEvaluator {
  256.         /** Function. */
  257.         private final UnivariateFunction func;
  258.         /** Counter. */
  259.         private final Incrementor inc;

  260.         /**
  261.          * @param func Function.
  262.          */
  263.         FunctionEvaluator(UnivariateFunction func) {
  264.             this.func = func;
  265.             inc = Incrementor.create().withMaximalCount(maxEvaluations);
  266.             evaluations = 0;
  267.         }

  268.         /**
  269.          * @param x Argument.
  270.          * @return {@code f(x)}
  271.          * @throws TooManyEvaluationsException if the maximal number of evaluations is
  272.          * exceeded.
  273.          */
  274.         double value(double x) {
  275.             try {
  276.                 inc.increment();
  277.                 evaluations = inc.getCount();
  278.             } catch (MaxCountExceededException e) {
  279.                 throw new TooManyEvaluationsException(e.getMax());
  280.             }

  281.             return func.value(x);
  282.         }
  283.     }
  284. }