001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.math3.util; 018 019import org.apache.commons.math3.exception.ConvergenceException; 020import org.apache.commons.math3.exception.MaxCountExceededException; 021import org.apache.commons.math3.exception.util.LocalizedFormats; 022 023/** 024 * Provides a generic means to evaluate continued fractions. Subclasses simply 025 * provided the a and b coefficients to evaluate the continued fraction. 026 * 027 * <p> 028 * References: 029 * <ul> 030 * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html"> 031 * Continued Fraction</a></li> 032 * </ul> 033 * </p> 034 * 035 */ 036public abstract class ContinuedFraction { 037 /** Maximum allowed numerical error. */ 038 private static final double DEFAULT_EPSILON = 10e-9; 039 040 /** 041 * Default constructor. 042 */ 043 protected ContinuedFraction() { 044 super(); 045 } 046 047 /** 048 * Access the n-th a coefficient of the continued fraction. Since a can be 049 * a function of the evaluation point, x, that is passed in as well. 050 * @param n the coefficient index to retrieve. 051 * @param x the evaluation point. 052 * @return the n-th a coefficient. 053 */ 054 protected abstract double getA(int n, double x); 055 056 /** 057 * Access the n-th b coefficient of the continued fraction. Since b can be 058 * a function of the evaluation point, x, that is passed in as well. 059 * @param n the coefficient index to retrieve. 060 * @param x the evaluation point. 061 * @return the n-th b coefficient. 062 */ 063 protected abstract double getB(int n, double x); 064 065 /** 066 * Evaluates the continued fraction at the value x. 067 * @param x the evaluation point. 068 * @return the value of the continued fraction evaluated at x. 069 * @throws ConvergenceException if the algorithm fails to converge. 070 */ 071 public double evaluate(double x) throws ConvergenceException { 072 return evaluate(x, DEFAULT_EPSILON, Integer.MAX_VALUE); 073 } 074 075 /** 076 * Evaluates the continued fraction at the value x. 077 * @param x the evaluation point. 078 * @param epsilon maximum error allowed. 079 * @return the value of the continued fraction evaluated at x. 080 * @throws ConvergenceException if the algorithm fails to converge. 081 */ 082 public double evaluate(double x, double epsilon) throws ConvergenceException { 083 return evaluate(x, epsilon, Integer.MAX_VALUE); 084 } 085 086 /** 087 * Evaluates the continued fraction at the value x. 088 * @param x the evaluation point. 089 * @param maxIterations maximum number of convergents 090 * @return the value of the continued fraction evaluated at x. 091 * @throws ConvergenceException if the algorithm fails to converge. 092 * @throws MaxCountExceededException if maximal number of iterations is reached 093 */ 094 public double evaluate(double x, int maxIterations) 095 throws ConvergenceException, MaxCountExceededException { 096 return evaluate(x, DEFAULT_EPSILON, maxIterations); 097 } 098 099 /** 100 * Evaluates the continued fraction at the value x. 101 * <p> 102 * The implementation of this method is based on the modified Lentz algorithm as described 103 * on page 18 ff. in: 104 * <ul> 105 * <li> 106 * I. J. Thompson, A. R. Barnett. "Coulomb and Bessel Functions of Complex Arguments and Order." 107 * <a target="_blank" href="http://www.fresco.org.uk/papers/Thompson-JCP64p490.pdf"> 108 * http://www.fresco.org.uk/papers/Thompson-JCP64p490.pdf</a> 109 * </li> 110 * </ul> 111 * <b>Note:</b> the implementation uses the terms a<sub>i</sub> and b<sub>i</sub> as defined in 112 * <a href="http://mathworld.wolfram.com/ContinuedFraction.html">Continued Fraction @ MathWorld</a>. 113 * </p> 114 * 115 * @param x the evaluation point. 116 * @param epsilon maximum error allowed. 117 * @param maxIterations maximum number of convergents 118 * @return the value of the continued fraction evaluated at x. 119 * @throws ConvergenceException if the algorithm fails to converge. 120 * @throws MaxCountExceededException if maximal number of iterations is reached 121 */ 122 public double evaluate(double x, double epsilon, int maxIterations) 123 throws ConvergenceException, MaxCountExceededException { 124 final double small = 1e-50; 125 double hPrev = getA(0, x); 126 127 // use the value of small as epsilon criteria for zero checks 128 if (Precision.equals(hPrev, 0.0, small)) { 129 hPrev = small; 130 } 131 132 int n = 1; 133 double dPrev = 0.0; 134 double cPrev = hPrev; 135 double hN = hPrev; 136 137 while (n < maxIterations) { 138 final double a = getA(n, x); 139 final double b = getB(n, x); 140 141 double dN = a + b * dPrev; 142 if (Precision.equals(dN, 0.0, small)) { 143 dN = small; 144 } 145 double cN = a + b / cPrev; 146 if (Precision.equals(cN, 0.0, small)) { 147 cN = small; 148 } 149 150 dN = 1 / dN; 151 final double deltaN = cN * dN; 152 hN = hPrev * deltaN; 153 154 if (Double.isInfinite(hN)) { 155 throw new ConvergenceException(LocalizedFormats.CONTINUED_FRACTION_INFINITY_DIVERGENCE, 156 x); 157 } 158 if (Double.isNaN(hN)) { 159 throw new ConvergenceException(LocalizedFormats.CONTINUED_FRACTION_NAN_DIVERGENCE, 160 x); 161 } 162 163 if (FastMath.abs(deltaN - 1.0) < epsilon) { 164 break; 165 } 166 167 dPrev = dN; 168 cPrev = cN; 169 hPrev = hN; 170 n++; 171 } 172 173 if (n >= maxIterations) { 174 throw new MaxCountExceededException(LocalizedFormats.NON_CONVERGENT_CONTINUED_FRACTION, 175 maxIterations, x); 176 } 177 178 return hN; 179 } 180 181}