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.  *      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.exception.LocalizedCoreFormats;
  23. import org.hipparchus.exception.MathIllegalArgumentException;
  24. import org.hipparchus.exception.MathIllegalStateException;
  25. import org.hipparchus.util.FastMath;

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

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

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

  77.     /**
  78.      * {@inheritDoc}
  79.      */
  80.     @Override
  81.     protected double doSolve()
  82.         throws MathIllegalArgumentException, MathIllegalStateException {
  83.         final double min = getMin();
  84.         final double max = getMax();

  85.         verifyInterval(min, max);

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

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

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

  102.         if(y0 * y1 > 0) {
  103.             throw new MathIllegalArgumentException(LocalizedCoreFormats.NOT_BRACKETING_INTERVAL,
  104.                                                    x0, x1, y0, y1);
  105.         }

  106.         double x2 = 0.5 * (x0 + x1);
  107.         double y2 = computeObjectiveValue(x2);

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

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

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