BrentOptimizer.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.math3.optimization.univariate;

  18. import org.apache.commons.math3.util.Precision;
  19. import org.apache.commons.math3.util.FastMath;
  20. import org.apache.commons.math3.exception.NumberIsTooSmallException;
  21. import org.apache.commons.math3.exception.NotStrictlyPositiveException;
  22. import org.apache.commons.math3.optimization.ConvergenceChecker;
  23. import org.apache.commons.math3.optimization.GoalType;

  24. /**
  25.  * For a function defined on some interval {@code (lo, hi)}, this class
  26.  * finds an approximation {@code x} to the point at which the function
  27.  * attains its minimum.
  28.  * It implements Richard Brent's algorithm (from his book "Algorithms for
  29.  * Minimization without Derivatives", p. 79) for finding minima of real
  30.  * univariate functions.
  31.  * <br/>
  32.  * This code is an adaptation, partly based on the Python code from SciPy
  33.  * (module "optimize.py" v0.5); the original algorithm is also modified
  34.  * <ul>
  35.  *  <li>to use an initial guess provided by the user,</li>
  36.  *  <li>to ensure that the best point encountered is the one returned.</li>
  37.  * </ul>
  38.  *
  39.  * @deprecated As of 3.1 (to be removed in 4.0).
  40.  * @since 2.0
  41.  */
  42. @Deprecated
  43. public class BrentOptimizer extends BaseAbstractUnivariateOptimizer {
  44.     /**
  45.      * Golden section.
  46.      */
  47.     private static final double GOLDEN_SECTION = 0.5 * (3 - FastMath.sqrt(5));
  48.     /**
  49.      * Minimum relative tolerance.
  50.      */
  51.     private static final double MIN_RELATIVE_TOLERANCE = 2 * FastMath.ulp(1d);
  52.     /**
  53.      * Relative threshold.
  54.      */
  55.     private final double relativeThreshold;
  56.     /**
  57.      * Absolute threshold.
  58.      */
  59.     private final double absoluteThreshold;

  60.     /**
  61.      * The arguments are used implement the original stopping criterion
  62.      * of Brent's algorithm.
  63.      * {@code abs} and {@code rel} define a tolerance
  64.      * {@code tol = rel |x| + abs}. {@code rel} should be no smaller than
  65.      * <em>2 macheps</em> and preferably not much less than <em>sqrt(macheps)</em>,
  66.      * where <em>macheps</em> is the relative machine precision. {@code abs} must
  67.      * be positive.
  68.      *
  69.      * @param rel Relative threshold.
  70.      * @param abs Absolute threshold.
  71.      * @param checker Additional, user-defined, convergence checking
  72.      * procedure.
  73.      * @throws NotStrictlyPositiveException if {@code abs <= 0}.
  74.      * @throws NumberIsTooSmallException if {@code rel < 2 * Math.ulp(1d)}.
  75.      */
  76.     public BrentOptimizer(double rel,
  77.                           double abs,
  78.                           ConvergenceChecker<UnivariatePointValuePair> checker) {
  79.         super(checker);

  80.         if (rel < MIN_RELATIVE_TOLERANCE) {
  81.             throw new NumberIsTooSmallException(rel, MIN_RELATIVE_TOLERANCE, true);
  82.         }
  83.         if (abs <= 0) {
  84.             throw new NotStrictlyPositiveException(abs);
  85.         }

  86.         relativeThreshold = rel;
  87.         absoluteThreshold = abs;
  88.     }

  89.     /**
  90.      * The arguments are used for implementing the original stopping criterion
  91.      * of Brent's algorithm.
  92.      * {@code abs} and {@code rel} define a tolerance
  93.      * {@code tol = rel |x| + abs}. {@code rel} should be no smaller than
  94.      * <em>2 macheps</em> and preferably not much less than <em>sqrt(macheps)</em>,
  95.      * where <em>macheps</em> is the relative machine precision. {@code abs} must
  96.      * be positive.
  97.      *
  98.      * @param rel Relative threshold.
  99.      * @param abs Absolute threshold.
  100.      * @throws NotStrictlyPositiveException if {@code abs <= 0}.
  101.      * @throws NumberIsTooSmallException if {@code rel < 2 * Math.ulp(1d)}.
  102.      */
  103.     public BrentOptimizer(double rel,
  104.                           double abs) {
  105.         this(rel, abs, null);
  106.     }

  107.     /** {@inheritDoc} */
  108.     @Override
  109.     protected UnivariatePointValuePair doOptimize() {
  110.         final boolean isMinim = getGoalType() == GoalType.MINIMIZE;
  111.         final double lo = getMin();
  112.         final double mid = getStartValue();
  113.         final double hi = getMax();

  114.         // Optional additional convergence criteria.
  115.         final ConvergenceChecker<UnivariatePointValuePair> checker
  116.             = getConvergenceChecker();

  117.         double a;
  118.         double b;
  119.         if (lo < hi) {
  120.             a = lo;
  121.             b = hi;
  122.         } else {
  123.             a = hi;
  124.             b = lo;
  125.         }

  126.         double x = mid;
  127.         double v = x;
  128.         double w = x;
  129.         double d = 0;
  130.         double e = 0;
  131.         double fx = computeObjectiveValue(x);
  132.         if (!isMinim) {
  133.             fx = -fx;
  134.         }
  135.         double fv = fx;
  136.         double fw = fx;

  137.         UnivariatePointValuePair previous = null;
  138.         UnivariatePointValuePair current
  139.             = new UnivariatePointValuePair(x, isMinim ? fx : -fx);
  140.         // Best point encountered so far (which is the initial guess).
  141.         UnivariatePointValuePair best = current;

  142.         int iter = 0;
  143.         while (true) {
  144.             final double m = 0.5 * (a + b);
  145.             final double tol1 = relativeThreshold * FastMath.abs(x) + absoluteThreshold;
  146.             final double tol2 = 2 * tol1;

  147.             // Default stopping criterion.
  148.             final boolean stop = FastMath.abs(x - m) <= tol2 - 0.5 * (b - a);
  149.             if (!stop) {
  150.                 double p = 0;
  151.                 double q = 0;
  152.                 double r = 0;
  153.                 double u = 0;

  154.                 if (FastMath.abs(e) > tol1) { // Fit parabola.
  155.                     r = (x - w) * (fx - fv);
  156.                     q = (x - v) * (fx - fw);
  157.                     p = (x - v) * q - (x - w) * r;
  158.                     q = 2 * (q - r);

  159.                     if (q > 0) {
  160.                         p = -p;
  161.                     } else {
  162.                         q = -q;
  163.                     }

  164.                     r = e;
  165.                     e = d;

  166.                     if (p > q * (a - x) &&
  167.                         p < q * (b - x) &&
  168.                         FastMath.abs(p) < FastMath.abs(0.5 * q * r)) {
  169.                         // Parabolic interpolation step.
  170.                         d = p / q;
  171.                         u = x + d;

  172.                         // f must not be evaluated too close to a or b.
  173.                         if (u - a < tol2 || b - u < tol2) {
  174.                             if (x <= m) {
  175.                                 d = tol1;
  176.                             } else {
  177.                                 d = -tol1;
  178.                             }
  179.                         }
  180.                     } else {
  181.                         // Golden section step.
  182.                         if (x < m) {
  183.                             e = b - x;
  184.                         } else {
  185.                             e = a - x;
  186.                         }
  187.                         d = GOLDEN_SECTION * e;
  188.                     }
  189.                 } else {
  190.                     // Golden section step.
  191.                     if (x < m) {
  192.                         e = b - x;
  193.                     } else {
  194.                         e = a - x;
  195.                     }
  196.                     d = GOLDEN_SECTION * e;
  197.                 }

  198.                 // Update by at least "tol1".
  199.                 if (FastMath.abs(d) < tol1) {
  200.                     if (d >= 0) {
  201.                         u = x + tol1;
  202.                     } else {
  203.                         u = x - tol1;
  204.                     }
  205.                 } else {
  206.                     u = x + d;
  207.                 }

  208.                 double fu = computeObjectiveValue(u);
  209.                 if (!isMinim) {
  210.                     fu = -fu;
  211.                 }

  212.                 // User-defined convergence checker.
  213.                 previous = current;
  214.                 current = new UnivariatePointValuePair(u, isMinim ? fu : -fu);
  215.                 best = best(best,
  216.                             best(previous,
  217.                                  current,
  218.                                  isMinim),
  219.                             isMinim);

  220.                 if (checker != null && checker.converged(iter, previous, current)) {
  221.                     return best;
  222.                 }

  223.                 // Update a, b, v, w and x.
  224.                 if (fu <= fx) {
  225.                     if (u < x) {
  226.                         b = x;
  227.                     } else {
  228.                         a = x;
  229.                     }
  230.                     v = w;
  231.                     fv = fw;
  232.                     w = x;
  233.                     fw = fx;
  234.                     x = u;
  235.                     fx = fu;
  236.                 } else {
  237.                     if (u < x) {
  238.                         a = u;
  239.                     } else {
  240.                         b = u;
  241.                     }
  242.                     if (fu <= fw ||
  243.                         Precision.equals(w, x)) {
  244.                         v = w;
  245.                         fv = fw;
  246.                         w = u;
  247.                         fw = fu;
  248.                     } else if (fu <= fv ||
  249.                                Precision.equals(v, x) ||
  250.                                Precision.equals(v, w)) {
  251.                         v = u;
  252.                         fv = fu;
  253.                     }
  254.                 }
  255.             } else { // Default termination (Brent's criterion).
  256.                 return best(best,
  257.                             best(previous,
  258.                                  current,
  259.                                  isMinim),
  260.                             isMinim);
  261.             }
  262.             ++iter;
  263.         }
  264.     }

  265.     /**
  266.      * Selects the best of two points.
  267.      *
  268.      * @param a Point and value.
  269.      * @param b Point and value.
  270.      * @param isMinim {@code true} if the selected point must be the one with
  271.      * the lowest value.
  272.      * @return the best point, or {@code null} if {@code a} and {@code b} are
  273.      * both {@code null}. When {@code a} and {@code b} have the same function
  274.      * value, {@code a} is returned.
  275.      */
  276.     private UnivariatePointValuePair best(UnivariatePointValuePair a,
  277.                                           UnivariatePointValuePair b,
  278.                                           boolean isMinim) {
  279.         if (a == null) {
  280.             return b;
  281.         }
  282.         if (b == null) {
  283.             return a;
  284.         }

  285.         if (isMinim) {
  286.             return a.getValue() <= b.getValue() ? a : b;
  287.         } else {
  288.             return a.getValue() >= b.getValue() ? a : b;
  289.         }
  290.     }
  291. }