Home | History | Annotate | Download | only in util
      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.util;
     18 
     19 import org.apache.commons.math.ConvergenceException;
     20 import org.apache.commons.math.MathException;
     21 import org.apache.commons.math.MaxIterationsExceededException;
     22 import org.apache.commons.math.exception.util.LocalizedFormats;
     23 
     24 /**
     25  * Provides a generic means to evaluate continued fractions.  Subclasses simply
     26  * provided the a and b coefficients to evaluate the continued fraction.
     27  *
     28  * <p>
     29  * References:
     30  * <ul>
     31  * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">
     32  * Continued Fraction</a></li>
     33  * </ul>
     34  * </p>
     35  *
     36  * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 aot 2010) $
     37  */
     38 public abstract class ContinuedFraction {
     39 
     40     /** Maximum allowed numerical error. */
     41     private static final double DEFAULT_EPSILON = 10e-9;
     42 
     43     /**
     44      * Default constructor.
     45      */
     46     protected ContinuedFraction() {
     47         super();
     48     }
     49 
     50     /**
     51      * Access the n-th a coefficient of the continued fraction.  Since a can be
     52      * a function of the evaluation point, x, that is passed in as well.
     53      * @param n the coefficient index to retrieve.
     54      * @param x the evaluation point.
     55      * @return the n-th a coefficient.
     56      */
     57     protected abstract double getA(int n, double x);
     58 
     59     /**
     60      * Access the n-th b coefficient of the continued fraction.  Since b can be
     61      * a function of the evaluation point, x, that is passed in as well.
     62      * @param n the coefficient index to retrieve.
     63      * @param x the evaluation point.
     64      * @return the n-th b coefficient.
     65      */
     66     protected abstract double getB(int n, double x);
     67 
     68     /**
     69      * Evaluates the continued fraction at the value x.
     70      * @param x the evaluation point.
     71      * @return the value of the continued fraction evaluated at x.
     72      * @throws MathException if the algorithm fails to converge.
     73      */
     74     public double evaluate(double x) throws MathException {
     75         return evaluate(x, DEFAULT_EPSILON, Integer.MAX_VALUE);
     76     }
     77 
     78     /**
     79      * Evaluates the continued fraction at the value x.
     80      * @param x the evaluation point.
     81      * @param epsilon maximum error allowed.
     82      * @return the value of the continued fraction evaluated at x.
     83      * @throws MathException if the algorithm fails to converge.
     84      */
     85     public double evaluate(double x, double epsilon) throws MathException {
     86         return evaluate(x, epsilon, Integer.MAX_VALUE);
     87     }
     88 
     89     /**
     90      * Evaluates the continued fraction at the value x.
     91      * @param x the evaluation point.
     92      * @param maxIterations maximum number of convergents
     93      * @return the value of the continued fraction evaluated at x.
     94      * @throws MathException if the algorithm fails to converge.
     95      */
     96     public double evaluate(double x, int maxIterations) throws MathException {
     97         return evaluate(x, DEFAULT_EPSILON, maxIterations);
     98     }
     99 
    100     /**
    101      * <p>
    102      * Evaluates the continued fraction at the value x.
    103      * </p>
    104      *
    105      * <p>
    106      * The implementation of this method is based on equations 14-17 of:
    107      * <ul>
    108      * <li>
    109      *   Eric W. Weisstein. "Continued Fraction." From MathWorld--A Wolfram Web
    110      *   Resource. <a target="_blank"
    111      *   href="http://mathworld.wolfram.com/ContinuedFraction.html">
    112      *   http://mathworld.wolfram.com/ContinuedFraction.html</a>
    113      * </li>
    114      * </ul>
    115      * The recurrence relationship defined in those equations can result in
    116      * very large intermediate results which can result in numerical overflow.
    117      * As a means to combat these overflow conditions, the intermediate results
    118      * are scaled whenever they threaten to become numerically unstable.</p>
    119      *
    120      * @param x the evaluation point.
    121      * @param epsilon maximum error allowed.
    122      * @param maxIterations maximum number of convergents
    123      * @return the value of the continued fraction evaluated at x.
    124      * @throws MathException if the algorithm fails to converge.
    125      */
    126     public double evaluate(double x, double epsilon, int maxIterations)
    127         throws MathException
    128     {
    129         double p0 = 1.0;
    130         double p1 = getA(0, x);
    131         double q0 = 0.0;
    132         double q1 = 1.0;
    133         double c = p1 / q1;
    134         int n = 0;
    135         double relativeError = Double.MAX_VALUE;
    136         while (n < maxIterations && relativeError > epsilon) {
    137             ++n;
    138             double a = getA(n, x);
    139             double b = getB(n, x);
    140             double p2 = a * p1 + b * p0;
    141             double q2 = a * q1 + b * q0;
    142             boolean infinite = false;
    143             if (Double.isInfinite(p2) || Double.isInfinite(q2)) {
    144                 /*
    145                  * Need to scale. Try successive powers of the larger of a or b
    146                  * up to 5th power. Throw ConvergenceException if one or both
    147                  * of p2, q2 still overflow.
    148                  */
    149                 double scaleFactor = 1d;
    150                 double lastScaleFactor = 1d;
    151                 final int maxPower = 5;
    152                 final double scale = FastMath.max(a,b);
    153                 if (scale <= 0) {  // Can't scale
    154                     throw new ConvergenceException(
    155                             LocalizedFormats.CONTINUED_FRACTION_INFINITY_DIVERGENCE,
    156                              x);
    157                 }
    158                 infinite = true;
    159                 for (int i = 0; i < maxPower; i++) {
    160                     lastScaleFactor = scaleFactor;
    161                     scaleFactor *= scale;
    162                     if (a != 0.0 && a > b) {
    163                         p2 = p1 / lastScaleFactor + (b / scaleFactor * p0);
    164                         q2 = q1 / lastScaleFactor + (b / scaleFactor * q0);
    165                     } else if (b != 0) {
    166                         p2 = (a / scaleFactor * p1) + p0 / lastScaleFactor;
    167                         q2 = (a / scaleFactor * q1) + q0 / lastScaleFactor;
    168                     }
    169                     infinite = Double.isInfinite(p2) || Double.isInfinite(q2);
    170                     if (!infinite) {
    171                         break;
    172                     }
    173                 }
    174             }
    175 
    176             if (infinite) {
    177                // Scaling failed
    178                throw new ConvergenceException(
    179                  LocalizedFormats.CONTINUED_FRACTION_INFINITY_DIVERGENCE,
    180                   x);
    181             }
    182 
    183             double r = p2 / q2;
    184 
    185             if (Double.isNaN(r)) {
    186                 throw new ConvergenceException(
    187                   LocalizedFormats.CONTINUED_FRACTION_NAN_DIVERGENCE,
    188                   x);
    189             }
    190             relativeError = FastMath.abs(r / c - 1.0);
    191 
    192             // prepare for next iteration
    193             c = p2 / q2;
    194             p0 = p1;
    195             p1 = p2;
    196             q0 = q1;
    197             q1 = q2;
    198         }
    199 
    200         if (n >= maxIterations) {
    201             throw new MaxIterationsExceededException(maxIterations,
    202                 LocalizedFormats.NON_CONVERGENT_CONTINUED_FRACTION,
    203                 x);
    204         }
    205 
    206         return c;
    207     }
    208 }
    209