Home | History | Annotate | Download | only in univariate
      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.math.optimization.univariate;
     18 
     19 import org.apache.commons.math.FunctionEvaluationException;
     20 import org.apache.commons.math.MaxIterationsExceededException;
     21 import org.apache.commons.math.exception.NotStrictlyPositiveException;
     22 import org.apache.commons.math.optimization.GoalType;
     23 import org.apache.commons.math.util.FastMath;
     24 
     25 /**
     26  * Implements Richard Brent's algorithm (from his book "Algorithms for
     27  * Minimization without Derivatives", p. 79) for finding minima of real
     28  * univariate functions. This implementation is an adaptation partly
     29  * based on the Python code from SciPy (module "optimize.py" v0.5).
     30  *
     31  * @version $Revision: 1070725 $ $Date: 2011-02-15 02:31:12 +0100 (mar. 15 fvr. 2011) $
     32  * @since 2.0
     33  */
     34 public class BrentOptimizer extends AbstractUnivariateRealOptimizer {
     35     /**
     36      * Golden section.
     37      */
     38     private static final double GOLDEN_SECTION = 0.5 * (3 - FastMath.sqrt(5));
     39 
     40     /**
     41      * Construct a solver.
     42      */
     43     public BrentOptimizer() {
     44         setMaxEvaluations(1000);
     45         setMaximalIterationCount(100);
     46         setAbsoluteAccuracy(1e-11);
     47         setRelativeAccuracy(1e-9);
     48     }
     49 
     50     /** {@inheritDoc} */
     51     @Override
     52     protected double doOptimize()
     53         throws MaxIterationsExceededException, FunctionEvaluationException {
     54         return localMin(getGoalType() == GoalType.MINIMIZE,
     55                         getMin(), getStartValue(), getMax(),
     56                         getRelativeAccuracy(), getAbsoluteAccuracy());
     57     }
     58 
     59     /**
     60      * Find the minimum of the function within the interval {@code (lo, hi)}.
     61      *
     62      * If the function is defined on the interval {@code (lo, hi)}, then
     63      * this method finds an approximation {@code x} to the point at which
     64      * the function attains its minimum.<br/>
     65      * {@code t} and {@code eps} define a tolerance {@code tol = eps |x| + t}
     66      * and the function is never evaluated at two points closer together than
     67      * {@code tol}. {@code eps} should be no smaller than <em>2 macheps</em> and
     68      * preferable not much less than <em>sqrt(macheps)</em>, where
     69      * <em>macheps</em> is the relative machine precision. {@code t} should be
     70      * positive.
     71      * @param isMinim {@code true} when minimizing the function.
     72      * @param lo Lower bound of the interval.
     73      * @param mid Point inside the interval {@code [lo, hi]}.
     74      * @param hi Higher bound of the interval.
     75      * @param eps Relative accuracy.
     76      * @param t Absolute accuracy.
     77      * @return the optimum point.
     78      * @throws MaxIterationsExceededException if the maximum iteration count
     79      * is exceeded.
     80      * @throws FunctionEvaluationException if an error occurs evaluating the function.
     81      */
     82     private double localMin(boolean isMinim,
     83                             double lo, double mid, double hi,
     84                             double eps, double t)
     85         throws MaxIterationsExceededException, FunctionEvaluationException {
     86         if (eps <= 0) {
     87             throw new NotStrictlyPositiveException(eps);
     88         }
     89         if (t <= 0) {
     90             throw new NotStrictlyPositiveException(t);
     91         }
     92         double a;
     93         double b;
     94         if (lo < hi) {
     95             a = lo;
     96             b = hi;
     97         } else {
     98             a = hi;
     99             b = lo;
    100         }
    101 
    102         double x = mid;
    103         double v = x;
    104         double w = x;
    105         double d = 0;
    106         double e = 0;
    107         double fx = computeObjectiveValue(x);
    108         if (!isMinim) {
    109             fx = -fx;
    110         }
    111         double fv = fx;
    112         double fw = fx;
    113 
    114         while (true) {
    115             double m = 0.5 * (a + b);
    116             final double tol1 = eps * FastMath.abs(x) + t;
    117             final double tol2 = 2 * tol1;
    118 
    119             // Check stopping criterion.
    120             if (FastMath.abs(x - m) > tol2 - 0.5 * (b - a)) {
    121                 double p = 0;
    122                 double q = 0;
    123                 double r = 0;
    124                 double u = 0;
    125 
    126                 if (FastMath.abs(e) > tol1) { // Fit parabola.
    127                     r = (x - w) * (fx - fv);
    128                     q = (x - v) * (fx - fw);
    129                     p = (x - v) * q - (x - w) * r;
    130                     q = 2 * (q - r);
    131 
    132                     if (q > 0) {
    133                         p = -p;
    134                     } else {
    135                         q = -q;
    136                     }
    137 
    138                     r = e;
    139                     e = d;
    140 
    141                     if (p > q * (a - x) &&
    142                         p < q * (b - x) &&
    143                         FastMath.abs(p) < FastMath.abs(0.5 * q * r)) {
    144                         // Parabolic interpolation step.
    145                         d = p / q;
    146                         u = x + d;
    147 
    148                         // f must not be evaluated too close to a or b.
    149                         if (u - a < tol2 || b - u < tol2) {
    150                             if (x <= m) {
    151                                 d = tol1;
    152                             } else {
    153                                 d = -tol1;
    154                             }
    155                         }
    156                     } else {
    157                         // Golden section step.
    158                         if (x < m) {
    159                             e = b - x;
    160                         } else {
    161                             e = a - x;
    162                         }
    163                         d = GOLDEN_SECTION * e;
    164                     }
    165                 } else {
    166                     // Golden section step.
    167                     if (x < m) {
    168                         e = b - x;
    169                     } else {
    170                         e = a - x;
    171                     }
    172                     d = GOLDEN_SECTION * e;
    173                 }
    174 
    175                 // Update by at least "tol1".
    176                 if (FastMath.abs(d) < tol1) {
    177                     if (d >= 0) {
    178                         u = x + tol1;
    179                     } else {
    180                         u = x - tol1;
    181                     }
    182                 } else {
    183                     u = x + d;
    184                 }
    185 
    186                 double fu = computeObjectiveValue(u);
    187                 if (!isMinim) {
    188                     fu = -fu;
    189                 }
    190 
    191                 // Update a, b, v, w and x.
    192                 if (fu <= fx) {
    193                     if (u < x) {
    194                         b = x;
    195                     } else {
    196                         a = x;
    197                     }
    198                     v = w;
    199                     fv = fw;
    200                     w = x;
    201                     fw = fx;
    202                     x = u;
    203                     fx = fu;
    204                 } else {
    205                     if (u < x) {
    206                         a = u;
    207                     } else {
    208                         b = u;
    209                     }
    210                     if (fu <= fw || w == x) {
    211                         v = w;
    212                         fv = fw;
    213                         w = u;
    214                         fw = fu;
    215                     } else if (fu <= fv || v == x || v == w) {
    216                         v = u;
    217                         fv = fu;
    218                     }
    219                 }
    220             } else { // termination
    221                 setFunctionValue(isMinim ? fx : -fx);
    222                 return x;
    223             }
    224             incrementIterationsCounter();
    225         }
    226     }
    227 }
    228