UnivariateSolverUtils.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.  *      https://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. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.analysis.solvers;

  22. import org.hipparchus.CalculusFieldElement;
  23. import org.hipparchus.analysis.CalculusFieldUnivariateFunction;
  24. import org.hipparchus.analysis.UnivariateFunction;
  25. import org.hipparchus.exception.LocalizedCoreFormats;
  26. import org.hipparchus.exception.MathIllegalArgumentException;
  27. import org.hipparchus.exception.NullArgumentException;
  28. import org.hipparchus.util.FastMath;
  29. import org.hipparchus.util.MathArrays;
  30. import org.hipparchus.util.MathUtils;

  31. /**
  32.  * Utility routines for {@link UnivariateSolver} objects.
  33.  *
  34.  */
  35. public class UnivariateSolverUtils {
  36.     /**
  37.      * Class contains only static methods.
  38.      */
  39.     private UnivariateSolverUtils() {}

  40.     /**
  41.      * Convenience method to find a zero of a univariate real function.  A default
  42.      * solver is used.
  43.      *
  44.      * @param function Function.
  45.      * @param x0 Lower bound for the interval.
  46.      * @param x1 Upper bound for the interval.
  47.      * @return a value where the function is zero.
  48.      * @throws MathIllegalArgumentException if the function has the same sign at the
  49.      * endpoints.
  50.      * @throws NullArgumentException if {@code function} is {@code null}.
  51.      */
  52.     public static double solve(UnivariateFunction function, double x0, double x1)
  53.         throws MathIllegalArgumentException, NullArgumentException {
  54.         MathUtils.checkNotNull(function, LocalizedCoreFormats.FUNCTION);
  55.         final UnivariateSolver solver = new BrentSolver();
  56.         return solver.solve(Integer.MAX_VALUE, function, x0, x1);
  57.     }

  58.     /**
  59.      * Convenience method to find a zero of a univariate real function.  A default
  60.      * solver is used.
  61.      *
  62.      * @param function Function.
  63.      * @param x0 Lower bound for the interval.
  64.      * @param x1 Upper bound for the interval.
  65.      * @param absoluteAccuracy Accuracy to be used by the solver.
  66.      * @return a value where the function is zero.
  67.      * @throws MathIllegalArgumentException if the function has the same sign at the
  68.      * endpoints.
  69.      * @throws NullArgumentException if {@code function} is {@code null}.
  70.      */
  71.     public static double solve(UnivariateFunction function,
  72.                                double x0, double x1,
  73.                                double absoluteAccuracy)
  74.         throws MathIllegalArgumentException, NullArgumentException {
  75.         MathUtils.checkNotNull(function, LocalizedCoreFormats.FUNCTION);
  76.         final UnivariateSolver solver = new BrentSolver(absoluteAccuracy);
  77.         return solver.solve(Integer.MAX_VALUE, function, x0, x1);
  78.     }

  79.     /**
  80.      * Force a root found by a non-bracketing solver to lie on a specified side,
  81.      * as if the solver were a bracketing one.
  82.      *
  83.      * @param maxEval maximal number of new evaluations of the function
  84.      * (evaluations already done for finding the root should have already been subtracted
  85.      * from this number)
  86.      * @param f function to solve
  87.      * @param bracketing bracketing solver to use for shifting the root
  88.      * @param baseRoot original root found by a previous non-bracketing solver
  89.      * @param min minimal bound of the search interval
  90.      * @param max maximal bound of the search interval
  91.      * @param allowedSolution the kind of solutions that the root-finding algorithm may
  92.      * accept as solutions.
  93.      * @return a root approximation, on the specified side of the exact root
  94.      * @throws MathIllegalArgumentException if the function has the same sign at the
  95.      * endpoints.
  96.      */
  97.     public static double forceSide(final int maxEval, final UnivariateFunction f,
  98.                                    final BracketedUnivariateSolver<UnivariateFunction> bracketing,
  99.                                    final double baseRoot, final double min, final double max,
  100.                                    final AllowedSolution allowedSolution)
  101.         throws MathIllegalArgumentException {

  102.         if (allowedSolution == AllowedSolution.ANY_SIDE) {
  103.             // no further bracketing required
  104.             return baseRoot;
  105.         }

  106.         // find a very small interval bracketing the root
  107.         final double step = FastMath.max(bracketing.getAbsoluteAccuracy(),
  108.                                          FastMath.abs(baseRoot * bracketing.getRelativeAccuracy()));
  109.         double xLo        = FastMath.max(min, baseRoot - step);
  110.         double fLo        = f.value(xLo);
  111.         double xHi        = FastMath.min(max, baseRoot + step);
  112.         double fHi        = f.value(xHi);
  113.         int remainingEval = maxEval - 2;
  114.         while (remainingEval > 0) {

  115.             if ((fLo >= 0 && fHi <= 0) || (fLo <= 0 && fHi >= 0)) {
  116.                 // compute the root on the selected side
  117.                 return bracketing.solve(remainingEval, f, xLo, xHi, baseRoot, allowedSolution);
  118.             }

  119.             // try increasing the interval
  120.             boolean changeLo = false;
  121.             boolean changeHi = false;
  122.             if (fLo < fHi) {
  123.                 // increasing function
  124.                 if (fLo >= 0) {
  125.                     changeLo = true;
  126.                 } else {
  127.                     changeHi = true;
  128.                 }
  129.             } else if (fLo > fHi) {
  130.                 // decreasing function
  131.                 if (fLo <= 0) {
  132.                     changeLo = true;
  133.                 } else {
  134.                     changeHi = true;
  135.                 }
  136.             } else {
  137.                 // unknown variation
  138.                 changeLo = true;
  139.                 changeHi = true;
  140.             }

  141.             // update the lower bound
  142.             if (changeLo) {
  143.                 xLo = FastMath.max(min, xLo - step);
  144.                 fLo  = f.value(xLo);
  145.                 remainingEval--;
  146.             }

  147.             // update the higher bound
  148.             if (changeHi) {
  149.                 xHi = FastMath.min(max, xHi + step);
  150.                 fHi  = f.value(xHi);
  151.                 remainingEval--;
  152.             }

  153.         }

  154.         throw new MathIllegalArgumentException(LocalizedCoreFormats.FAILED_BRACKETING,
  155.                                                xLo, xHi, fLo, fHi,
  156.                                                maxEval - remainingEval, maxEval, baseRoot,
  157.                                                min, max);

  158.     }

  159.     /**
  160.      * This method simply calls {@link #bracket(UnivariateFunction, double, double, double,
  161.      * double, double, int) bracket(function, initial, lowerBound, upperBound, q, r, maximumIterations)}
  162.      * with {@code q} and {@code r} set to 1.0 and {@code maximumIterations} set to {@code Integer.MAX_VALUE}.
  163.      * <p>
  164.      * <strong>Note: </strong> this method can take {@code Integer.MAX_VALUE}
  165.      * iterations to throw a {@code MathIllegalStateException.}  Unless you are
  166.      * confident that there is a root between {@code lowerBound} and
  167.      * {@code upperBound} near {@code initial}, it is better to use
  168.      * {@link #bracket(UnivariateFunction, double, double, double, double,double, int)
  169.      * bracket(function, initial, lowerBound, upperBound, q, r, maximumIterations)},
  170.      * explicitly specifying the maximum number of iterations.</p>
  171.      *
  172.      * @param function Function.
  173.      * @param initial Initial midpoint of interval being expanded to
  174.      * bracket a root.
  175.      * @param lowerBound Lower bound (a is never lower than this value)
  176.      * @param upperBound Upper bound (b never is greater than this
  177.      * value).
  178.      * @return a two-element array holding a and b.
  179.      * @throws MathIllegalArgumentException if a root cannot be bracketed.
  180.      * @throws MathIllegalArgumentException if {@code maximumIterations <= 0}.
  181.      * @throws NullArgumentException if {@code function} is {@code null}.
  182.      */
  183.     public static double[] bracket(UnivariateFunction function,
  184.                                    double initial,
  185.                                    double lowerBound, double upperBound)
  186.         throws MathIllegalArgumentException, NullArgumentException {
  187.         return bracket(function, initial, lowerBound, upperBound, 1.0, 1.0, Integer.MAX_VALUE);
  188.     }

  189.      /**
  190.      * This method simply calls {@link #bracket(UnivariateFunction, double, double, double,
  191.      * double, double, int) bracket(function, initial, lowerBound, upperBound, q, r, maximumIterations)}
  192.      * with {@code q} and {@code r} set to 1.0.
  193.      * @param function Function.
  194.      * @param initial Initial midpoint of interval being expanded to
  195.      * bracket a root.
  196.      * @param lowerBound Lower bound (a is never lower than this value).
  197.      * @param upperBound Upper bound (b never is greater than this
  198.      * value).
  199.      * @param maximumIterations Maximum number of iterations to perform
  200.      * @return a two element array holding a and b.
  201.      * @throws MathIllegalArgumentException if the algorithm fails to find a and b
  202.      * satisfying the desired conditions.
  203.      * @throws MathIllegalArgumentException if {@code maximumIterations <= 0}.
  204.      * @throws NullArgumentException if {@code function} is {@code null}.
  205.      */
  206.     public static double[] bracket(UnivariateFunction function,
  207.                                    double initial,
  208.                                    double lowerBound, double upperBound,
  209.                                    int maximumIterations)
  210.         throws MathIllegalArgumentException, NullArgumentException {
  211.         return bracket(function, initial, lowerBound, upperBound, 1.0, 1.0, maximumIterations);
  212.     }

  213.     /**
  214.      * This method attempts to find two values a and b satisfying <ul>
  215.      * <li> {@code lowerBound <= a < initial < b <= upperBound} </li>
  216.      * <li> {@code f(a) * f(b) <= 0} </li>
  217.      * </ul>
  218.      * If {@code f} is continuous on {@code [a,b]}, this means that {@code a}
  219.      * and {@code b} bracket a root of {@code f}.
  220.      * <p>
  221.      * The algorithm checks the sign of \( f(l_k) \) and \( f(u_k) \) for increasing
  222.      * values of k, where \( l_k = max(lower, initial - \delta_k) \),
  223.      * \( u_k = min(upper, initial + \delta_k) \), using recurrence
  224.      * \( \delta_{k+1} = r \delta_k + q, \delta_0 = 0\) and starting search with \( k=1 \).
  225.      * The algorithm stops when one of the following happens: <ul>
  226.      * <li> at least one positive and one negative value have been found --  success!</li>
  227.      * <li> both endpoints have reached their respective limits -- MathIllegalArgumentException </li>
  228.      * <li> {@code maximumIterations} iterations elapse -- MathIllegalArgumentException </li></ul>
  229.      * <p>
  230.      * If different signs are found at first iteration ({@code k=1}), then the returned
  231.      * interval will be \( [a, b] = [l_1, u_1] \). If different signs are found at a later
  232.      * iteration {@code k>1}, then the returned interval will be either
  233.      * \( [a, b] = [l_{k+1}, l_{k}] \) or \( [a, b] = [u_{k}, u_{k+1}] \). A root solver called
  234.      * with these parameters will therefore start with the smallest bracketing interval known
  235.      * at this step.
  236.      * </p>
  237.      * <p>
  238.      * Interval expansion rate is tuned by changing the recurrence parameters {@code r} and
  239.      * {@code q}. When the multiplicative factor {@code r} is set to 1, the sequence is a
  240.      * simple arithmetic sequence with linear increase. When the multiplicative factor {@code r}
  241.      * is larger than 1, the sequence has an asymptotically exponential rate. Note than the
  242.      * additive parameter {@code q} should never be set to zero, otherwise the interval would
  243.      * degenerate to the single initial point for all values of {@code k}.
  244.      * </p>
  245.      * <p>
  246.      * As a rule of thumb, when the location of the root is expected to be approximately known
  247.      * within some error margin, {@code r} should be set to 1 and {@code q} should be set to the
  248.      * order of magnitude of the error margin. When the location of the root is really a wild guess,
  249.      * then {@code r} should be set to a value larger than 1 (typically 2 to double the interval
  250.      * length at each iteration) and {@code q} should be set according to half the initial
  251.      * search interval length.
  252.      * </p>
  253.      * <p>
  254.      * As an example, if we consider the trivial function {@code f(x) = 1 - x} and use
  255.      * {@code initial = 4}, {@code r = 1}, {@code q = 2}, the algorithm will compute
  256.      * {@code f(4-2) = f(2) = -1} and {@code f(4+2) = f(6) = -5} for {@code k = 1}, then
  257.      * {@code f(4-4) = f(0) = +1} and {@code f(4+4) = f(8) = -7} for {@code k = 2}. Then it will
  258.      * return the interval {@code [0, 2]} as the smallest one known to be bracketing the root.
  259.      * As shown by this example, the initial value (here {@code 4}) may lie outside of the returned
  260.      * bracketing interval.
  261.      * </p>
  262.      * @param function function to check
  263.      * @param initial Initial midpoint of interval being expanded to
  264.      * bracket a root.
  265.      * @param lowerBound Lower bound (a is never lower than this value).
  266.      * @param upperBound Upper bound (b never is greater than this
  267.      * value).
  268.      * @param q additive offset used to compute bounds sequence (must be strictly positive)
  269.      * @param r multiplicative factor used to compute bounds sequence
  270.      * @param maximumIterations Maximum number of iterations to perform
  271.      * @return a two element array holding the bracketing values.
  272.      * @exception MathIllegalArgumentException if function cannot be bracketed in the search interval
  273.      */
  274.     public static double[] bracket(final UnivariateFunction function, final double initial,
  275.                                    final double lowerBound, final double upperBound,
  276.                                    final double q, final double r, final int maximumIterations)
  277.         throws MathIllegalArgumentException {

  278.         MathUtils.checkNotNull(function, LocalizedCoreFormats.FUNCTION);

  279.         if (q <= 0)  {
  280.             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL_BOUND_EXCLUDED,
  281.                                                    q, 0);
  282.         }
  283.         if (maximumIterations <= 0)  {
  284.             throw new MathIllegalArgumentException(LocalizedCoreFormats.INVALID_MAX_ITERATIONS, maximumIterations);
  285.         }
  286.         verifySequence(lowerBound, initial, upperBound);

  287.         // initialize the recurrence
  288.         double a     = initial;
  289.         double b     = initial;
  290.         double fa    = Double.NaN;
  291.         double fb    = Double.NaN;
  292.         double delta = 0;

  293.         for (int numIterations = 0;
  294.              (numIterations < maximumIterations) && (a > lowerBound || b < upperBound);
  295.              ++numIterations) {

  296.             final double previousA  = a;
  297.             final double previousFa = fa;
  298.             final double previousB  = b;
  299.             final double previousFb = fb;

  300.             delta = r * delta + q;
  301.             a     = FastMath.max(initial - delta, lowerBound);
  302.             b     = FastMath.min(initial + delta, upperBound);
  303.             fa    = function.value(a);
  304.             fb    = function.value(b);

  305.             if (numIterations == 0) {
  306.                 // at first iteration, we don't have a previous interval
  307.                 // we simply compare both sides of the initial interval
  308.                 if (fa * fb <= 0) {
  309.                     // the first interval already brackets a root
  310.                     return new double[] { a, b };
  311.                 }
  312.             } else {
  313.                 // we have a previous interval with constant sign and expand it,
  314.                 // we expect sign changes to occur at boundaries
  315.                 if (fa * previousFa <= 0) {
  316.                     // sign change detected at near lower bound
  317.                     return new double[] { a, previousA };
  318.                 } else if (fb * previousFb <= 0) {
  319.                     // sign change detected at near upper bound
  320.                     return new double[] { previousB, b };
  321.                 }
  322.             }

  323.         }

  324.         // no bracketing found
  325.         throw new MathIllegalArgumentException(LocalizedCoreFormats.NOT_BRACKETING_INTERVAL,
  326.                                                a, b, fa, fb);

  327.     }

  328.     /**
  329.      * This method simply calls {@link #bracket(CalculusFieldUnivariateFunction,
  330.      * CalculusFieldElement, CalculusFieldElement, CalculusFieldElement, CalculusFieldElement,
  331.      * CalculusFieldElement, int) bracket(function, initial, lowerBound, upperBound, q, r, maximumIterations)}
  332.      * with {@code q} and {@code r} set to 1.0 and {@code maximumIterations} set to {@code Integer.MAX_VALUE}.
  333.      * <p>
  334.      * <strong>Note: </strong> this method can take {@code Integer.MAX_VALUE}
  335.      * iterations to throw a {@code MathIllegalStateException.}  Unless you are
  336.      * confident that there is a root between {@code lowerBound} and
  337.      * {@code upperBound} near {@code initial}, it is better to use
  338.      * {@link #bracket(UnivariateFunction, double, double, double, double,double, int)
  339.      * bracket(function, initial, lowerBound, upperBound, q, r, maximumIterations)},
  340.      * explicitly specifying the maximum number of iterations.</p>
  341.      *
  342.      * @param function Function.
  343.      * @param initial Initial midpoint of interval being expanded to
  344.      * bracket a root.
  345.      * @param lowerBound Lower bound (a is never lower than this value)
  346.      * @param upperBound Upper bound (b never is greater than this
  347.      * value).
  348.      * @param <T> type of the field elements
  349.      * @return a two-element array holding a and b.
  350.      * @throws MathIllegalArgumentException if a root cannot be bracketed.
  351.      * @throws MathIllegalArgumentException if {@code maximumIterations <= 0}.
  352.      * @throws NullArgumentException if {@code function} is {@code null}.
  353.      * @since 1.2
  354.      */
  355.     public static <T extends CalculusFieldElement<T>> T[] bracket(CalculusFieldUnivariateFunction<T> function,
  356.                                                               T initial,
  357.                                                               T lowerBound, T upperBound)
  358.         throws MathIllegalArgumentException, NullArgumentException {
  359.         return bracket(function, initial, lowerBound, upperBound,
  360.                        initial.getField().getOne(), initial.getField().getOne(),
  361.                        Integer.MAX_VALUE);
  362.     }

  363.      /**
  364.      * This method simply calls {@link #bracket(CalculusFieldUnivariateFunction,
  365.      * CalculusFieldElement, CalculusFieldElement, CalculusFieldElement, CalculusFieldElement,
  366.      * CalculusFieldElement, int) bracket(function, initial, lowerBound, upperBound, q, r, maximumIterations)}
  367.      * with {@code q} and {@code r} set to 1.0.
  368.      * @param function Function.
  369.      * @param initial Initial midpoint of interval being expanded to
  370.      * bracket a root.
  371.      * @param lowerBound Lower bound (a is never lower than this value).
  372.      * @param upperBound Upper bound (b never is greater than this
  373.      * value).
  374.      * @param maximumIterations Maximum number of iterations to perform
  375.      * @param <T> type of the field elements
  376.      * @return a two element array holding a and b.
  377.      * @throws MathIllegalArgumentException if the algorithm fails to find a and b
  378.      * satisfying the desired conditions.
  379.      * @throws MathIllegalArgumentException if {@code maximumIterations <= 0}.
  380.      * @throws NullArgumentException if {@code function} is {@code null}.
  381.      * @since 1.2
  382.      */
  383.     public static <T extends CalculusFieldElement<T>> T[] bracket(CalculusFieldUnivariateFunction<T> function,
  384.                                                               T initial,
  385.                                                               T lowerBound, T upperBound,
  386.                                                               int maximumIterations)
  387.         throws MathIllegalArgumentException, NullArgumentException {
  388.         return bracket(function, initial, lowerBound, upperBound,
  389.                        initial.getField().getOne(), initial.getField().getOne(),
  390.                        maximumIterations);
  391.     }

  392.     /**
  393.      * This method attempts to find two values a and b satisfying <ul>
  394.      * <li> {@code lowerBound <= a < initial < b <= upperBound} </li>
  395.      * <li> {@code f(a) * f(b) <= 0} </li>
  396.      * </ul>
  397.      * If {@code f} is continuous on {@code [a,b]}, this means that {@code a}
  398.      * and {@code b} bracket a root of {@code f}.
  399.      * <p>
  400.      * The algorithm checks the sign of \( f(l_k) \) and \( f(u_k) \) for increasing
  401.      * values of k, where \( l_k = max(lower, initial - \delta_k) \),
  402.      * \( u_k = min(upper, initial + \delta_k) \), using recurrence
  403.      * \( \delta_{k+1} = r \delta_k + q, \delta_0 = 0\) and starting search with \( k=1 \).
  404.      * The algorithm stops when one of the following happens: <ul>
  405.      * <li> at least one positive and one negative value have been found --  success!</li>
  406.      * <li> both endpoints have reached their respective limits -- MathIllegalArgumentException </li>
  407.      * <li> {@code maximumIterations} iterations elapse -- MathIllegalArgumentException </li></ul>
  408.      * <p>
  409.      * If different signs are found at first iteration ({@code k=1}), then the returned
  410.      * interval will be \( [a, b] = [l_1, u_1] \). If different signs are found at a later
  411.      * iteration {@code k>1}, then the returned interval will be either
  412.      * \( [a, b] = [l_{k+1}, l_{k}] \) or \( [a, b] = [u_{k}, u_{k+1}] \). A root solver called
  413.      * with these parameters will therefore start with the smallest bracketing interval known
  414.      * at this step.
  415.      * </p>
  416.      * <p>
  417.      * Interval expansion rate is tuned by changing the recurrence parameters {@code r} and
  418.      * {@code q}. When the multiplicative factor {@code r} is set to 1, the sequence is a
  419.      * simple arithmetic sequence with linear increase. When the multiplicative factor {@code r}
  420.      * is larger than 1, the sequence has an asymptotically exponential rate. Note than the
  421.      * additive parameter {@code q} should never be set to zero, otherwise the interval would
  422.      * degenerate to the single initial point for all values of {@code k}.
  423.      * </p>
  424.      * <p>
  425.      * As a rule of thumb, when the location of the root is expected to be approximately known
  426.      * within some error margin, {@code r} should be set to 1 and {@code q} should be set to the
  427.      * order of magnitude of the error margin. When the location of the root is really a wild guess,
  428.      * then {@code r} should be set to a value larger than 1 (typically 2 to double the interval
  429.      * length at each iteration) and {@code q} should be set according to half the initial
  430.      * search interval length.
  431.      * </p>
  432.      * <p>
  433.      * As an example, if we consider the trivial function {@code f(x) = 1 - x} and use
  434.      * {@code initial = 4}, {@code r = 1}, {@code q = 2}, the algorithm will compute
  435.      * {@code f(4-2) = f(2) = -1} and {@code f(4+2) = f(6) = -5} for {@code k = 1}, then
  436.      * {@code f(4-4) = f(0) = +1} and {@code f(4+4) = f(8) = -7} for {@code k = 2}. Then it will
  437.      * return the interval {@code [0, 2]} as the smallest one known to be bracketing the root.
  438.      * As shown by this example, the initial value (here {@code 4}) may lie outside of the returned
  439.      * bracketing interval.
  440.      * </p>
  441.      * @param function function to check
  442.      * @param initial Initial midpoint of interval being expanded to
  443.      * bracket a root.
  444.      * @param lowerBound Lower bound (a is never lower than this value).
  445.      * @param upperBound Upper bound (b never is greater than this
  446.      * value).
  447.      * @param q additive offset used to compute bounds sequence (must be strictly positive)
  448.      * @param r multiplicative factor used to compute bounds sequence
  449.      * @param maximumIterations Maximum number of iterations to perform
  450.      * @param <T> type of the field elements
  451.      * @return a two element array holding the bracketing values.
  452.      * @exception MathIllegalArgumentException if function cannot be bracketed in the search interval
  453.      * @since 1.2
  454.      */
  455.     public static <T extends CalculusFieldElement<T>> T[] bracket(final CalculusFieldUnivariateFunction<T> function,
  456.                                                               final T initial,
  457.                                                               final T lowerBound, final T upperBound,
  458.                                                               final T q, final T r,
  459.                                                               final int maximumIterations)
  460.         throws MathIllegalArgumentException {

  461.         MathUtils.checkNotNull(function, LocalizedCoreFormats.FUNCTION);

  462.         if (q.getReal() <= 0)  {
  463.             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL_BOUND_EXCLUDED,
  464.                                                    q, 0);
  465.         }
  466.         if (maximumIterations <= 0)  {
  467.             throw new MathIllegalArgumentException(LocalizedCoreFormats.INVALID_MAX_ITERATIONS, maximumIterations);
  468.         }
  469.         verifySequence(lowerBound.getReal(), initial.getReal(), upperBound.getReal());

  470.         // initialize the recurrence
  471.         T a     = initial;
  472.         T b     = initial;
  473.         T fa    = null;
  474.         T fb    = null;
  475.         T delta = initial.getField().getZero();

  476.         for (int numIterations = 0;
  477.              (numIterations < maximumIterations) &&
  478.              (a.getReal() > lowerBound.getReal() || b.getReal() < upperBound.getReal());
  479.              ++numIterations) {

  480.             final T previousA  = a;
  481.             final T previousFa = fa;
  482.             final T previousB  = b;
  483.             final T previousFb = fb;

  484.             delta = r.multiply(delta).add(q);
  485.             a     = max(initial.subtract(delta), lowerBound);
  486.             b     = min(initial.add(delta), upperBound);
  487.             fa    = function.value(a);
  488.             fb    = function.value(b);

  489.             if (numIterations == 0) {
  490.                 // at first iteration, we don't have a previous interval
  491.                 // we simply compare both sides of the initial interval
  492.                 if (fa.multiply(fb).getReal() <= 0) {
  493.                     // the first interval already brackets a root
  494.                     final T[] interval = MathArrays.buildArray(initial.getField(), 2);
  495.                     interval[0] = a;
  496.                     interval[1] = b;
  497.                     return interval;
  498.                 }
  499.             } else {
  500.                 // we have a previous interval with constant sign and expand it,
  501.                 // we expect sign changes to occur at boundaries
  502.                 if (fa.multiply(previousFa).getReal() <= 0) {
  503.                     // sign change detected at near lower bound
  504.                     final T[] interval = MathArrays.buildArray(initial.getField(), 2);
  505.                     interval[0] = a;
  506.                     interval[1] = previousA;
  507.                     return interval;
  508.                 } else if (fb.multiply(previousFb).getReal() <= 0) {
  509.                     // sign change detected at near upper bound
  510.                     final T[] interval = MathArrays.buildArray(initial.getField(), 2);
  511.                     interval[0] = previousB;
  512.                     interval[1] = b;
  513.                     return interval;
  514.                 }
  515.             }

  516.         }

  517.         // no bracketing found
  518.         throw new MathIllegalArgumentException(LocalizedCoreFormats.NOT_BRACKETING_INTERVAL,
  519.                                                a.getReal(), b.getReal(), fa.getReal(), fb.getReal());

  520.     }

  521.     /** Compute the maximum of two values
  522.      * @param a first value
  523.      * @param b second value
  524.      * @param <T> type of the field elements
  525.      * @return b if a is lesser or equal to b, a otherwise
  526.      * @since 1.2
  527.      */
  528.     private static <T extends CalculusFieldElement<T>> T max(final T a, final T b) {
  529.         return (a.subtract(b).getReal() <= 0) ? b : a;
  530.     }

  531.     /** Compute the minimum of two values
  532.      * @param a first value
  533.      * @param b second value
  534.      * @param <T> type of the field elements
  535.      * @return a if a is lesser or equal to b, b otherwise
  536.      * @since 1.2
  537.      */
  538.     private static <T extends CalculusFieldElement<T>> T min(final T a, final T b) {
  539.         return (a.subtract(b).getReal() <= 0) ? a : b;
  540.     }

  541.     /**
  542.      * Compute the midpoint of two values.
  543.      *
  544.      * @param a first value.
  545.      * @param b second value.
  546.      * @return the midpoint.
  547.      */
  548.     public static double midpoint(double a, double b) {
  549.         return (a + b) * 0.5;
  550.     }

  551.     /**
  552.      * Check whether the interval bounds bracket a root. That is, if the
  553.      * values at the endpoints are not equal to zero, then the function takes
  554.      * opposite signs at the endpoints.
  555.      *
  556.      * @param function Function.
  557.      * @param lower Lower endpoint.
  558.      * @param upper Upper endpoint.
  559.      * @return {@code true} if the function values have opposite signs at the
  560.      * given points.
  561.      * @throws NullArgumentException if {@code function} is {@code null}.
  562.      */
  563.     public static boolean isBracketing(UnivariateFunction function,
  564.                                        final double lower,
  565.                                        final double upper)
  566.         throws NullArgumentException {
  567.         MathUtils.checkNotNull(function, LocalizedCoreFormats.FUNCTION);
  568.         final double fLo = function.value(lower);
  569.         final double fHi = function.value(upper);
  570.         return (fLo >= 0 && fHi <= 0) || (fLo <= 0 && fHi >= 0);
  571.     }

  572.     /**
  573.      * Check whether the arguments form a (strictly) increasing sequence.
  574.      *
  575.      * @param start First number.
  576.      * @param mid Second number.
  577.      * @param end Third number.
  578.      * @return {@code true} if the arguments form an increasing sequence.
  579.      */
  580.     public static boolean isSequence(final double start,
  581.                                      final double mid,
  582.                                      final double end) {
  583.         return (start < mid) && (mid < end);
  584.     }

  585.     /**
  586.      * Check that the endpoints specify an interval.
  587.      *
  588.      * @param lower Lower endpoint.
  589.      * @param upper Upper endpoint.
  590.      * @throws MathIllegalArgumentException if {@code lower >= upper}.
  591.      */
  592.     public static void verifyInterval(final double lower,
  593.                                       final double upper)
  594.         throws MathIllegalArgumentException {
  595.         if (lower >= upper) {
  596.             throw new MathIllegalArgumentException(LocalizedCoreFormats.ENDPOINTS_NOT_AN_INTERVAL,
  597.                                                 lower, upper, false);
  598.         }
  599.     }

  600.     /**
  601.      * Check that {@code lower < initial < upper}.
  602.      *
  603.      * @param lower Lower endpoint.
  604.      * @param initial Initial value.
  605.      * @param upper Upper endpoint.
  606.      * @throws MathIllegalArgumentException if {@code lower >= initial} or
  607.      * {@code initial >= upper}.
  608.      */
  609.     public static void verifySequence(final double lower,
  610.                                       final double initial,
  611.                                       final double upper)
  612.         throws MathIllegalArgumentException {
  613.         verifyInterval(lower, initial);
  614.         verifyInterval(initial, upper);
  615.     }

  616.     /**
  617.      * Check that the endpoints specify an interval and the end points
  618.      * bracket a root.
  619.      *
  620.      * @param function Function.
  621.      * @param lower Lower endpoint.
  622.      * @param upper Upper endpoint.
  623.      * @throws MathIllegalArgumentException if the function has the same sign at the
  624.      * endpoints.
  625.      * @throws NullArgumentException if {@code function} is {@code null}.
  626.      */
  627.     public static void verifyBracketing(UnivariateFunction function,
  628.                                         final double lower,
  629.                                         final double upper)
  630.         throws MathIllegalArgumentException, NullArgumentException {
  631.         MathUtils.checkNotNull(function, LocalizedCoreFormats.FUNCTION);
  632.         verifyInterval(lower, upper);
  633.         if (!isBracketing(function, lower, upper)) {
  634.             throw new MathIllegalArgumentException(LocalizedCoreFormats.NOT_BRACKETING_INTERVAL,
  635.                                                    lower, upper,
  636.                                                    function.value(lower), function.value(upper));
  637.         }
  638.     }
  639. }