View Javadoc

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