Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
LaguerreSolver |
|
| 5.625;5.625 |
1 | /* |
|
2 | * Copyright 2005 The Apache Software Foundation. |
|
3 | * |
|
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
|
5 | * you may not use this file except in compliance with the License. |
|
6 | * You may obtain a copy of the License at |
|
7 | * |
|
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
9 | * |
|
10 | * Unless required by applicable law or agreed to in writing, software |
|
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
13 | * See the License for the specific language governing permissions and |
|
14 | * limitations under the License. |
|
15 | */ |
|
16 | package org.apache.commons.math.analysis; |
|
17 | ||
18 | import org.apache.commons.math.ConvergenceException; |
|
19 | import org.apache.commons.math.FunctionEvaluationException; |
|
20 | import org.apache.commons.math.complex.*; |
|
21 | ||
22 | /** |
|
23 | * Implements the <a href="http://mathworld.wolfram.com/LaguerresMethod.html"> |
|
24 | * Laguerre's Method</a> for root finding of real coefficient polynomials. |
|
25 | * For reference, see <b>A First Course in Numerical Analysis</b>, |
|
26 | * ISBN 048641454X, chapter 8. |
|
27 | * <p> |
|
28 | * Laguerre's method is global in the sense that it can start with any initial |
|
29 | * approximation and be able to solve all roots from that point. |
|
30 | * |
|
31 | * @version $Revision$ $Date$ |
|
32 | */ |
|
33 | public class LaguerreSolver extends UnivariateRealSolverImpl { |
|
34 | ||
35 | /** serializable version identifier */ |
|
36 | static final long serialVersionUID = 5287689975005870178L; |
|
37 | ||
38 | /** polynomial function to solve */ |
|
39 | private PolynomialFunction p; |
|
40 | ||
41 | /** |
|
42 | * Construct a solver for the given function. |
|
43 | * |
|
44 | * @param f function to solve |
|
45 | * @throws IllegalArgumentException if function is not polynomial |
|
46 | */ |
|
47 | public LaguerreSolver(UnivariateRealFunction f) throws |
|
48 | IllegalArgumentException { |
|
49 | ||
50 | 12 | super(f, 100, 1E-6); |
51 | 12 | if (f instanceof PolynomialFunction) { |
52 | 10 | p = (PolynomialFunction)f; |
53 | } else { |
|
54 | 2 | throw new IllegalArgumentException("Function is not polynomial."); |
55 | } |
|
56 | 10 | } |
57 | ||
58 | /** |
|
59 | * Returns a copy of the polynomial function. |
|
60 | * |
|
61 | * @return a fresh copy of the polynomial function |
|
62 | */ |
|
63 | public PolynomialFunction getPolynomialFunction() { |
|
64 | 0 | return new PolynomialFunction(p.getCoefficients()); |
65 | } |
|
66 | ||
67 | /** |
|
68 | * Find a real root in the given interval with initial value. |
|
69 | * <p> |
|
70 | * Requires bracketing condition. |
|
71 | * |
|
72 | * @param min the lower bound for the interval |
|
73 | * @param max the upper bound for the interval |
|
74 | * @param initial the start value to use |
|
75 | * @return the point at which the function value is zero |
|
76 | * @throws ConvergenceException if the maximum iteration count is exceeded |
|
77 | * or the solver detects convergence problems otherwise |
|
78 | * @throws FunctionEvaluationException if an error occurs evaluating the |
|
79 | * function |
|
80 | * @throws IllegalArgumentException if any parameters are invalid |
|
81 | */ |
|
82 | public double solve(double min, double max, double initial) throws |
|
83 | ConvergenceException, FunctionEvaluationException { |
|
84 | ||
85 | // check for zeros before verifying bracketing |
|
86 | 0 | if (p.value(min) == 0.0) { return min; } |
87 | 0 | if (p.value(max) == 0.0) { return max; } |
88 | 0 | if (p.value(initial) == 0.0) { return initial; } |
89 | ||
90 | 0 | verifyBracketing(min, max, p); |
91 | 0 | verifySequence(min, initial, max); |
92 | 0 | if (isBracketing(min, initial, p)) { |
93 | 0 | return solve(min, initial); |
94 | } else { |
|
95 | 0 | return solve(initial, max); |
96 | } |
|
97 | } |
|
98 | ||
99 | /** |
|
100 | * Find a real root in the given interval. |
|
101 | * <p> |
|
102 | * Despite the bracketing condition, the root returned by solve(Complex[], |
|
103 | * Complex) may not be a real zero inside [min, max]. For example, |
|
104 | * p(x) = x^3 + 1, min = -2, max = 2, initial = 0. We can either try |
|
105 | * another initial value, or, as we did here, call solveAll() to obtain |
|
106 | * all roots and pick up the one that we're looking for. |
|
107 | * |
|
108 | * @param min the lower bound for the interval |
|
109 | * @param max the upper bound for the interval |
|
110 | * @return the point at which the function value is zero |
|
111 | * @throws ConvergenceException if the maximum iteration count is exceeded |
|
112 | * or the solver detects convergence problems otherwise |
|
113 | * @throws FunctionEvaluationException if an error occurs evaluating the |
|
114 | * function |
|
115 | * @throws IllegalArgumentException if any parameters are invalid |
|
116 | */ |
|
117 | public double solve(double min, double max) throws ConvergenceException, |
|
118 | FunctionEvaluationException { |
|
119 | ||
120 | Complex c[], root[], initial, z; |
|
121 | ||
122 | // check for zeros before verifying bracketing |
|
123 | 16 | if (p.value(min) == 0.0) { return min; } |
124 | 16 | if (p.value(max) == 0.0) { return max; } |
125 | 16 | verifyBracketing(min, max, p); |
126 | ||
127 | 12 | double coefficients[] = p.getCoefficients(); |
128 | 12 | c = new Complex[coefficients.length]; |
129 | 64 | for (int i = 0; i < coefficients.length; i++) { |
130 | 52 | c[i] = new Complex(coefficients[i], 0.0); |
131 | } |
|
132 | 12 | initial = new Complex(0.5 * (min + max), 0.0); |
133 | 12 | z = solve(c, initial); |
134 | 12 | if (isRootOK(min, max, z)) { |
135 | 10 | setResult(z.getReal(), iterationCount); |
136 | 10 | return result; |
137 | } |
|
138 | ||
139 | // solve all roots and select the one we're seeking |
|
140 | 2 | root = solveAll(c, initial); |
141 | 10 | for (int i = 0; i < root.length; i++) { |
142 | 10 | if (isRootOK(min, max, root[i])) { |
143 | 2 | setResult(root[i].getReal(), iterationCount); |
144 | 2 | return result; |
145 | } |
|
146 | } |
|
147 | ||
148 | // should never happen |
|
149 | 0 | throw new ConvergenceException("Convergence failed."); |
150 | } |
|
151 | ||
152 | /** |
|
153 | * Returns true iff the given complex root is actually a real zero |
|
154 | * in the given interval, within the solver tolerance level. |
|
155 | * |
|
156 | * @param min the lower bound for the interval |
|
157 | * @param max the upper bound for the interval |
|
158 | * @param z the complex root |
|
159 | * @return true iff z is the sought-after real zero |
|
160 | */ |
|
161 | protected boolean isRootOK(double min, double max, Complex z) { |
|
162 | 22 | double tolerance = Math.max(relativeAccuracy * z.abs(), absoluteAccuracy); |
163 | 22 | return (isSequence(min, z.getReal(), max)) && |
164 | (Math.abs(z.getImaginary()) <= tolerance || |
|
165 | z.abs() <= functionValueAccuracy); |
|
166 | } |
|
167 | ||
168 | /** |
|
169 | * Find all complex roots for the polynomial with the given coefficients, |
|
170 | * starting from the given initial value. |
|
171 | * |
|
172 | * @param coefficients the polynomial coefficients array |
|
173 | * @param initial the start value to use |
|
174 | * @return the point at which the function value is zero |
|
175 | * @throws ConvergenceException if the maximum iteration count is exceeded |
|
176 | * or the solver detects convergence problems otherwise |
|
177 | * @throws FunctionEvaluationException if an error occurs evaluating the |
|
178 | * function |
|
179 | * @throws IllegalArgumentException if any parameters are invalid |
|
180 | */ |
|
181 | public Complex[] solveAll(double coefficients[], double initial) throws |
|
182 | ConvergenceException, FunctionEvaluationException { |
|
183 | ||
184 | 2 | Complex c[] = new Complex[coefficients.length]; |
185 | 2 | Complex z = new Complex(initial, 0.0); |
186 | 14 | for (int i = 0; i < c.length; i++) { |
187 | 12 | c[i] = new Complex(coefficients[i], 0.0); |
188 | } |
|
189 | 2 | return solveAll(c, z); |
190 | } |
|
191 | ||
192 | /** |
|
193 | * Find all complex roots for the polynomial with the given coefficients, |
|
194 | * starting from the given initial value. |
|
195 | * |
|
196 | * @param coefficients the polynomial coefficients array |
|
197 | * @param initial the start value to use |
|
198 | * @return the point at which the function value is zero |
|
199 | * @throws ConvergenceException if the maximum iteration count is exceeded |
|
200 | * or the solver detects convergence problems otherwise |
|
201 | * @throws FunctionEvaluationException if an error occurs evaluating the |
|
202 | * function |
|
203 | * @throws IllegalArgumentException if any parameters are invalid |
|
204 | */ |
|
205 | public Complex[] solveAll(Complex coefficients[], Complex initial) throws |
|
206 | ConvergenceException, FunctionEvaluationException { |
|
207 | ||
208 | 4 | int i, j, n, iterationCount = 0; |
209 | Complex root[], c[], subarray[], oldc, newc; |
|
210 | ||
211 | 4 | n = coefficients.length - 1; |
212 | 4 | if (n < 1) { |
213 | 0 | throw new IllegalArgumentException |
214 | ("Polynomial degree must be positive: degree=" + n); |
|
215 | } |
|
216 | 4 | c = new Complex[n+1]; // coefficients for deflated polynomial |
217 | 28 | for (i = 0; i <= n; i++) { |
218 | 24 | c[i] = coefficients[i]; |
219 | } |
|
220 | ||
221 | // solve individual root successively |
|
222 | 4 | root = new Complex[n]; |
223 | 24 | for (i = 0; i < n; i++) { |
224 | 20 | subarray = new Complex[n-i+1]; |
225 | 20 | System.arraycopy(c, 0, subarray, 0, subarray.length); |
226 | 20 | root[i] = solve(subarray, initial); |
227 | // polynomial deflation using synthetic division |
|
228 | 20 | newc = c[n-i]; |
229 | 80 | for (j = n-i-1; j >= 0; j--) { |
230 | 60 | oldc = c[j]; |
231 | 60 | c[j] = newc; |
232 | 60 | newc = oldc.add(newc.multiply(root[i])); |
233 | } |
|
234 | 20 | iterationCount += this.iterationCount; |
235 | } |
|
236 | ||
237 | 4 | resultComputed = true; |
238 | 4 | this.iterationCount = iterationCount; |
239 | 4 | return root; |
240 | } |
|
241 | ||
242 | /** |
|
243 | * Find a complex root for the polynomial with the given coefficients, |
|
244 | * starting from the given initial value. |
|
245 | * |
|
246 | * @param coefficients the polynomial coefficients array |
|
247 | * @param initial the start value to use |
|
248 | * @return the point at which the function value is zero |
|
249 | * @throws ConvergenceException if the maximum iteration count is exceeded |
|
250 | * or the solver detects convergence problems otherwise |
|
251 | * @throws FunctionEvaluationException if an error occurs evaluating the |
|
252 | * function |
|
253 | * @throws IllegalArgumentException if any parameters are invalid |
|
254 | */ |
|
255 | public Complex solve(Complex coefficients[], Complex initial) throws |
|
256 | ConvergenceException, FunctionEvaluationException { |
|
257 | ||
258 | 32 | Complex z = initial, oldz, pv, dv, d2v, G, G2, H, delta, denominator; |
259 | ||
260 | 32 | int n = coefficients.length - 1; |
261 | 32 | if (n < 1) { |
262 | 0 | throw new IllegalArgumentException |
263 | ("Polynomial degree must be positive: degree=" + n); |
|
264 | } |
|
265 | 32 | Complex N = new Complex((double)n, 0.0); |
266 | 32 | Complex N1 = new Complex((double)(n-1), 0.0); |
267 | ||
268 | 32 | int i = 1; |
269 | 32 | oldz = new Complex(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); |
270 | 112 | while (i <= maximalIterationCount) { |
271 | // Compute pv (polynomial value), dv (derivative value), and |
|
272 | // d2v (second derivative value) simultaneously. |
|
273 | 112 | pv = coefficients[n]; |
274 | 112 | dv = d2v = new Complex(0.0, 0.0); |
275 | 508 | for (int j = n-1; j >= 0; j--) { |
276 | 396 | d2v = dv.add(z.multiply(d2v)); |
277 | 396 | dv = pv.add(z.multiply(dv)); |
278 | 396 | pv = coefficients[j].add(z.multiply(pv)); |
279 | } |
|
280 | 112 | d2v = d2v.multiply(new Complex(2.0, 0.0)); |
281 | ||
282 | // check for convergence |
|
283 | 112 | double tolerance = Math.max(relativeAccuracy * z.abs(), |
284 | absoluteAccuracy); |
|
285 | 112 | if ((z.subtract(oldz)).abs() <= tolerance) { |
286 | 14 | resultComputed = true; |
287 | 14 | iterationCount = i; |
288 | 14 | return z; |
289 | } |
|
290 | 98 | if (pv.abs() <= functionValueAccuracy) { |
291 | 18 | resultComputed = true; |
292 | 18 | iterationCount = i; |
293 | 18 | return z; |
294 | } |
|
295 | ||
296 | // now pv != 0, calculate the new approximation |
|
297 | 80 | G = dv.divide(pv); |
298 | 80 | G2 = G.multiply(G); |
299 | 80 | H = G2.subtract(d2v.divide(pv)); |
300 | 80 | delta = N1.multiply((N.multiply(H)).subtract(G2)); |
301 | // choose a denominator larger in magnitude |
|
302 | 80 | Complex dplus = G.add(ComplexUtils.sqrt(delta)); |
303 | 80 | Complex dminus = G.subtract(ComplexUtils.sqrt(delta)); |
304 | 80 | denominator = dplus.abs() > dminus.abs() ? dplus : dminus; |
305 | // Perturb z if denominator is zero, for instance, |
|
306 | // p(x) = x^3 + 1, z = 0. |
|
307 | 80 | if (denominator.equals(new Complex(0.0, 0.0))) { |
308 | 2 | z = z.add(new Complex(absoluteAccuracy, absoluteAccuracy)); |
309 | 2 | oldz = new Complex(Double.POSITIVE_INFINITY, |
310 | Double.POSITIVE_INFINITY); |
|
311 | } else { |
|
312 | 78 | oldz = z; |
313 | 78 | z = z.subtract(N.divide(denominator)); |
314 | } |
|
315 | 80 | i++; |
316 | } |
|
317 | 0 | throw new ConvergenceException("Maximum number of iterations exceeded."); |
318 | } |
|
319 | } |