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 java.io.Serializable;
20
21 import org.apache.commons.math.ConvergenceException;
22 import org.apache.commons.math.MathException;
23 import org.apache.commons.math.MaxIterationsExceededException;
24
25 /**
26 * Provides a generic means to evaluate continued fractions. Subclasses simply
27 * provided the a and b coefficients to evaluate the continued fraction.
28 *
29 * <p>
30 * References:
31 * <ul>
32 * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">
33 * Continued Fraction</a></li>
34 * </ul>
35 * </p>
36 *
37 * @version $Revision: 617953 $ $Date: 2008-02-02 22:54:00 -0700 (Sat, 02 Feb 2008) $
38 */
39 public abstract class ContinuedFraction implements Serializable {
40
41 /** Serialization UID */
42 private static final long serialVersionUID = 1768555336266158242L;
43
44 /** Maximum allowed numerical error. */
45 private static final double DEFAULT_EPSILON = 10e-9;
46
47 /**
48 * Default constructor.
49 */
50 protected ContinuedFraction() {
51 super();
52 }
53
54 /**
55 * Access the n-th a coefficient of the continued fraction. Since a can be
56 * a function of the evaluation point, x, that is passed in as well.
57 * @param n the coefficient index to retrieve.
58 * @param x the evaluation point.
59 * @return the n-th a coefficient.
60 */
61 protected abstract double getA(int n, double x);
62
63 /**
64 * Access the n-th b coefficient of the continued fraction. Since b can be
65 * a function of the evaluation point, x, that is passed in as well.
66 * @param n the coefficient index to retrieve.
67 * @param x the evaluation point.
68 * @return the n-th b coefficient.
69 */
70 protected abstract double getB(int n, double x);
71
72 /**
73 * Evaluates the continued fraction at the value x.
74 * @param x the evaluation point.
75 * @return the value of the continued fraction evaluated at x.
76 * @throws MathException if the algorithm fails to converge.
77 */
78 public double evaluate(double x) throws MathException {
79 return evaluate(x, DEFAULT_EPSILON, Integer.MAX_VALUE);
80 }
81
82 /**
83 * Evaluates the continued fraction at the value x.
84 * @param x the evaluation point.
85 * @param epsilon maximum error allowed.
86 * @return the value of the continued fraction evaluated at x.
87 * @throws MathException if the algorithm fails to converge.
88 */
89 public double evaluate(double x, double epsilon) throws MathException {
90 return evaluate(x, epsilon, Integer.MAX_VALUE);
91 }
92
93 /**
94 * Evaluates the continued fraction at the value x.
95 * @param x the evaluation point.
96 * @param maxIterations maximum number of convergents
97 * @return the value of the continued fraction evaluated at x.
98 * @throws MathException if the algorithm fails to converge.
99 */
100 public double evaluate(double x, int maxIterations) throws MathException {
101 return evaluate(x, DEFAULT_EPSILON, maxIterations);
102 }
103
104 /**
105 * <p>
106 * Evaluates the continued fraction at the value x.
107 * </p>
108 *
109 * <p>
110 * The implementation of this method is based on equations 14-17 of:
111 * <ul>
112 * <li>
113 * Eric W. Weisstein. "Continued Fraction." From MathWorld--A Wolfram Web
114 * Resource. <a target="_blank"
115 * href="http://mathworld.wolfram.com/ContinuedFraction.html">
116 * http://mathworld.wolfram.com/ContinuedFraction.html</a>
117 * </li>
118 * </ul>
119 * The recurrence relationship defined in those equations can result in
120 * very large intermediate results which can result in numerical overflow.
121 * As a means to combat these overflow conditions, the intermediate results
122 * are scaled whenever they threaten to become numerically unstable.</p>
123 *
124 * @param x the evaluation point.
125 * @param epsilon maximum error allowed.
126 * @param maxIterations maximum number of convergents
127 * @return the value of the continued fraction evaluated at x.
128 * @throws MathException if the algorithm fails to converge.
129 */
130 public double evaluate(double x, double epsilon, int maxIterations)
131 throws MathException
132 {
133 double p0 = 1.0;
134 double p1 = getA(0, x);
135 double q0 = 0.0;
136 double q1 = 1.0;
137 double c = p1 / q1;
138 int n = 0;
139 double relativeError = Double.MAX_VALUE;
140 while (n < maxIterations && relativeError > epsilon) {
141 ++n;
142 double a = getA(n, x);
143 double b = getB(n, x);
144 double p2 = a * p1 + b * p0;
145 double q2 = a * q1 + b * q0;
146 if (Double.isInfinite(p2) || Double.isInfinite(q2)) {
147 // need to scale
148 if (a != 0.0) {
149 p2 = p1 + (b / a * p0);
150 q2 = q1 + (b / a * q0);
151 } else if (b != 0) {
152 p2 = (a / b * p1) + p0;
153 q2 = (a / b * q1) + q0;
154 } else {
155 // can not scale an convergent is unbounded.
156 throw new ConvergenceException(
157 "Continued fraction convergents diverged to +/- infinity for value {0}",
158 new Object[] { new Double(x) });
159 }
160 }
161 double r = p2 / q2;
162 relativeError = Math.abs(r / c - 1.0);
163
164 // prepare for next iteration
165 c = p2 / q2;
166 p0 = p1;
167 p1 = p2;
168 q0 = q1;
169 q1 = q2;
170 }
171
172 if (n >= maxIterations) {
173 throw new MaxIterationsExceededException(maxIterations,
174 "Continued fraction convergents failed to converge for value {0}",
175 new Object[] { new Double(x) });
176 }
177
178 return c;
179 }
180 }