MullerSolver2.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.analysis.solvers;

  18. import org.apache.commons.math4.legacy.exception.NoBracketingException;
  19. import org.apache.commons.math4.legacy.exception.NumberIsTooLargeException;
  20. import org.apache.commons.math4.legacy.exception.TooManyEvaluationsException;
  21. import org.apache.commons.math4.core.jdkmath.JdkMath;

  22. /**
  23.  * This class implements the <a href="http://mathworld.wolfram.com/MullersMethod.html">
  24.  * Muller's Method</a> for root finding of real univariate functions. For
  25.  * reference, see <b>Elementary Numerical Analysis</b>, ISBN 0070124477,
  26.  * chapter 3.
  27.  * <p>
  28.  * Muller's method applies to both real and complex functions, but here we
  29.  * restrict ourselves to real functions.
  30.  * This class differs from {@link MullerSolver} in the way it avoids complex
  31.  * operations.</p><p>
  32.  * Except for the initial [min, max], it does not require bracketing
  33.  * condition, e.g. f(x0), f(x1), f(x2) can have the same sign. If a complex
  34.  * number arises in the computation, we simply use its modulus as a real
  35.  * approximation.</p>
  36.  * <p>
  37.  * Because the interval may not be bracketing, the bisection alternative is
  38.  * not applicable here. However in practice our treatment usually works
  39.  * well, especially near real zeroes where the imaginary part of the complex
  40.  * approximation is often negligible.</p>
  41.  * <p>
  42.  * The formulas here do not use divided differences directly.</p>
  43.  *
  44.  * @since 1.2
  45.  * @see MullerSolver
  46.  */
  47. public class MullerSolver2 extends AbstractUnivariateSolver {

  48.     /** Default absolute accuracy. */
  49.     private static final double DEFAULT_ABSOLUTE_ACCURACY = 1e-6;

  50.     /**
  51.      * Construct a solver with default accuracy (1e-6).
  52.      */
  53.     public MullerSolver2() {
  54.         this(DEFAULT_ABSOLUTE_ACCURACY);
  55.     }
  56.     /**
  57.      * Construct a solver.
  58.      *
  59.      * @param absoluteAccuracy Absolute accuracy.
  60.      */
  61.     public MullerSolver2(double absoluteAccuracy) {
  62.         super(absoluteAccuracy);
  63.     }
  64.     /**
  65.      * Construct a solver.
  66.      *
  67.      * @param relativeAccuracy Relative accuracy.
  68.      * @param absoluteAccuracy Absolute accuracy.
  69.      */
  70.     public MullerSolver2(double relativeAccuracy,
  71.                         double absoluteAccuracy) {
  72.         super(relativeAccuracy, absoluteAccuracy);
  73.     }

  74.     /**
  75.      * {@inheritDoc}
  76.      */
  77.     @Override
  78.     protected double doSolve()
  79.         throws TooManyEvaluationsException,
  80.                NumberIsTooLargeException,
  81.                NoBracketingException {
  82.         final double min = getMin();
  83.         final double max = getMax();

  84.         verifyInterval(min, max);

  85.         final double relativeAccuracy = getRelativeAccuracy();
  86.         final double absoluteAccuracy = getAbsoluteAccuracy();
  87.         final double functionValueAccuracy = getFunctionValueAccuracy();

  88.         // x2 is the last root approximation
  89.         // x is the new approximation and new x2 for next round
  90.         // x0 < x1 < x2 does not hold here

  91.         double x0 = min;
  92.         double y0 = computeObjectiveValue(x0);
  93.         if (JdkMath.abs(y0) < functionValueAccuracy) {
  94.             return x0;
  95.         }
  96.         double x1 = max;
  97.         double y1 = computeObjectiveValue(x1);
  98.         if (JdkMath.abs(y1) < functionValueAccuracy) {
  99.             return x1;
  100.         }

  101.         if(y0 * y1 > 0) {
  102.             throw new NoBracketingException(x0, x1, y0, y1);
  103.         }

  104.         double x2 = 0.5 * (x0 + x1);
  105.         double y2 = computeObjectiveValue(x2);

  106.         double oldx = Double.POSITIVE_INFINITY;
  107.         while (true) {
  108.             // quadratic interpolation through x0, x1, x2
  109.             final double q = (x2 - x1) / (x1 - x0);
  110.             final double a = q * (y2 - (1 + q) * y1 + q * y0);
  111.             final double b = (2 * q + 1) * y2 - (1 + q) * (1 + q) * y1 + q * q * y0;
  112.             final double c = (1 + q) * y2;
  113.             final double delta = b * b - 4 * a * c;
  114.             double x;
  115.             final double denominator;
  116.             if (delta >= 0.0) {
  117.                 // choose a denominator larger in magnitude
  118.                 double dplus = b + JdkMath.sqrt(delta);
  119.                 double dminus = b - JdkMath.sqrt(delta);
  120.                 denominator = JdkMath.abs(dplus) > JdkMath.abs(dminus) ? dplus : dminus;
  121.             } else {
  122.                 // take the modulus of (B +/- JdkMath.sqrt(delta))
  123.                 denominator = JdkMath.sqrt(b * b - delta);
  124.             }
  125.             if (denominator != 0) {
  126.                 x = x2 - 2.0 * c * (x2 - x1) / denominator;
  127.                 // perturb x if it exactly coincides with x1 or x2
  128.                 // the equality tests here are intentional
  129.                 while (x == x1 || x == x2) {
  130.                     x += absoluteAccuracy;
  131.                 }
  132.             } else {
  133.                 // extremely rare case, get a random number to skip it
  134.                 x = min + JdkMath.random() * (max - min);
  135.                 oldx = Double.POSITIVE_INFINITY;
  136.             }
  137.             final double y = computeObjectiveValue(x);

  138.             // check for convergence
  139.             final double tolerance = JdkMath.max(relativeAccuracy * JdkMath.abs(x), absoluteAccuracy);
  140.             if (JdkMath.abs(x - oldx) <= tolerance ||
  141.                 JdkMath.abs(y) <= functionValueAccuracy) {
  142.                 return x;
  143.             }

  144.             // prepare the next iteration
  145.             x0 = x1;
  146.             y0 = y1;
  147.             x1 = x2;
  148.             y1 = y2;
  149.             x2 = x;
  150.             y2 = y;
  151.             oldx = x;
  152.         }
  153.     }
  154. }