File Coverage

blib/lib/Math/Polynomial/Solve.pm
Criterion Covered Total %
statement 485 508 95.4
branch 154 192 80.2
condition 18 27 66.6
subroutine 36 37 97.3
pod 25 26 96.1
total 718 790 90.8


line stmt bran cond sub pod time code
1             package Math::Polynomial::Solve;
2              
3             require 5.010001;
4 17     17   907412 use strict;
  17         175  
  17         469  
5 17     17   79 use warnings;
  17         28  
  17         360  
6 17     17   5584 use utf8;
  17         202  
  17         70  
7 17     17   477 use Carp;
  17         29  
  17         786  
8              
9 17     17   2171 use Math::Complex;
  17         54549  
  17         2289  
10 17     17   4640 use Math::Utils qw(:polynomial :utility);
  17         33214  
  17         2850  
11 17     17   112 use Exporter;
  17         27  
  17         3829  
12              
13             our $VERSION = '2.81';
14             our @ISA = qw(Exporter);
15              
16             #
17             # Three # for "I am here" messages.
18             # Four # for variable dumps.
19             # Five # for a dump of the companion matrix.
20             # Six # for sturm structs (sign chain, etc).
21             #
22             #use Smart::Comments q(######);
23              
24             #
25             # Export only on request.
26             #
27             our %EXPORT_TAGS = (
28             'classical' => [ qw(
29             linear_roots
30             quadratic_roots
31             cubic_roots
32             quartic_roots
33             ) ],
34             'numeric' => [ qw(poly_roots
35             poly_option
36             build_companion
37             balance_matrix
38             hqr_eigen_hessenberg
39             ) ],
40             'sturm' => [ qw(
41             poly_real_root_count
42             poly_sturm_chain
43             sturm_real_root_range_count
44             sturm_bisection
45             sturm_bisection_roots
46             sturm_sign_count
47             sturm_sign_chain
48             sturm_sign_minus_inf
49             sturm_sign_plus_inf
50             ) ],
51             'utility' => [ qw(
52             epsilon
53             laguerre
54             newtonraphson
55             poly_iteration
56             poly_nonzero_term_count
57             poly_tolerance
58             ) ],
59             );
60              
61             our @EXPORT_OK = (
62             @{ $EXPORT_TAGS{'classical'} },
63             @{ $EXPORT_TAGS{'numeric'} },
64             @{ $EXPORT_TAGS{'sturm'} },
65             @{ $EXPORT_TAGS{'utility'} } );
66              
67             our @EXPORT = qw( ascending_order );
68              
69             #
70             # Add an :all tag automatically.
71             #
72             $EXPORT_TAGS{all} = [@EXPORT_OK, @EXPORT];
73              
74             #
75             # Options to set or unset to force poly_roots() to use different
76             # methods of solving.
77             #
78             # hessenberg (default 1): set to 1 to force poly_roots() to use the QR
79             # Hessenberg method regardless of the degree of the polynomial. Set to zero
80             # to force poly_roots() uses one of the specialized routines (linerar_roots(),
81             # quadratic_roots(), etc) if the degree of the polynomial is less than five.
82             #
83             # root_function (default 0): set to 1 to force poly_roots() to use
84             # Math::Complex's root(-c/a, n) function if the polynomial is of the form
85             # ax**n + c.
86             #
87             # varsubst (default 0): try to reduce the degree of the polynomial through
88             # variable substitution before solving.
89             #
90             my %option = (
91             hessenberg => 1,
92             root_function => 0,
93             varsubst => 0,
94             );
95              
96             #
97             # Iteration limits. The Hessenberg matrix method and the Laguerre method run
98             # continuously until they converge upon an answer. The iteration limits are
99             # there to prevent the loops from running forever if they fail to converge.
100             #
101             my %iteration = (
102             hessenberg => 60,
103             newtonraphson => 60,
104             laguerre => 60,
105             sturm_bisection => 100,
106             );
107              
108             #
109             # Some values here are placeholders only, and will get
110             # replaced in the BEGIN block.
111             #
112             my %tolerance = (
113             newtonraphson => 1e-14,
114             laguerre => 1e-14,
115             );
116              
117             #
118             # Set up the epsilon variable, the value that is, in the floating-point
119             # math of the computer, the smallest value a variable can have before
120             # it is indistinguishable from zero when adding it to one.
121             #
122             my $epsilon;
123              
124             BEGIN
125             {
126 17     17   63 $epsilon = 0.125;
127 17         45 my $epsilon2 = $epsilon/2.0;
128              
129 17         70 while (1.0 + $epsilon2 > 1.0)
130             {
131 833         921 $epsilon = $epsilon2;
132 833         1218 $epsilon2 /= 2.0;
133             }
134              
135 17         57 $tolerance{laguerre} = 2 * $epsilon;
136 17         57563 $tolerance{newtonraphson} = 2 * $epsilon;
137             }
138              
139             #
140             # Flag to determine whether calling order is
141             # ($an_1, $an_2, $an_3, ...) or if it is
142             # ($a0, $a1, $a2, $a3, ...)
143             #
144             my $ascending_flag = 0; # default 0, in a later version it will be 1.
145              
146             #
147             # See the END block.
148             #
149             my $ascending_order_called = 0;
150             my %called_by;
151              
152             =pod
153              
154             =encoding UTF-8
155              
156             =head1 NAME
157              
158             Math::Polynomial::Solve - Find the roots of polynomial equations.
159              
160             =head1 SYNOPSIS
161              
162             use Math::Complex; # The roots may be complex numbers.
163             use Math::Polynomial::Solve qw(poly_roots);
164              
165             my @x = poly_roots(@coefficients);
166              
167             or
168              
169             use Math::Complex; # The roots may be complex numbers.
170             use Math::Polynomial::Solve qw(:numeric); # See the EXPORT section
171              
172             #
173             # Find roots using the matrix method.
174             #
175             my @x = poly_roots(@coefficients);
176              
177             #
178             # Alternatively, use the classical methods instead of the matrix
179             # method if the polynomial degree is less than five.
180             #
181             poly_option(hessenberg => 0);
182             @x = poly_roots(@coefficients);
183              
184             or
185              
186             use Math::Complex; # The roots may be complex numbers.
187             use Math::Polynomial::Solve qw(:classical); # See the EXPORT section
188              
189             #
190             # Find the polynomial roots using the classical methods.
191             #
192              
193             # Find the roots of ax + b
194             my @x1 = linear_roots($a, $b);
195              
196             # Find the roots of ax**2 + bx +c
197             my @x2 = quadratic_roots($a, $b, $c);
198              
199             # Find the roots of ax**3 + bx**2 +cx + d
200             my @x3 = cubic_roots($a, $b, $c, $d);
201              
202             # Find the roots of ax**4 + bx**3 +cx**2 + dx + e
203             my @x4 = quartic_roots($a, $b, $c, $d, $e);
204              
205             or
206              
207             use Math::Complex; # The roots may be complex numbers.
208             use Math::Polynomial;
209             use Math::Polynomial::Solve qw(:classical ascending_order);
210              
211             ascending_order(1); # Change default coefficient order for M::P::S.
212              
213             #
214             # Form 8*x**3 - 6*x - 1
215             #
216             my $p1 = Math::Polynomial->new(-1, -6, 0, 8);
217              
218             #
219             # Use Math::Polynomial's coefficient order.
220             # If ascending_order() had not been called,
221             # the statement would be:
222             #
223             # my @roots = poly_roots(reverse $p1->coefficients);
224             #
225             my @roots = poly_roots($p1->coefficients);
226              
227             or
228              
229             use Math::Polynomial::Solve qw(:sturm);
230              
231             #
232             # Find the number of unique real roots of the polynomial.
233             #
234             my $no_of_unique_roots = poly_real_root_count(@coefficients);
235              
236             =head1 DESCRIPTION
237              
238             This package supplies a set of functions that find the roots of
239             polynomials, along with some utility functions.
240              
241             Roots will be either real or of type L.
242              
243             Functions making use of the Sturm sequence are also available, letting you
244             find the number of real roots present in a range of X values.
245              
246             In addition to the root-finding functions, the internal functions have
247             also been exported for your use.
248              
249             =cut
250              
251             #
252             # $asending = ascending_order();
253             # $oldorder = ascending_order($neworder);
254             #
255             #
256             sub ascending_order
257             {
258 14     14 1 1236 my $ascend = $ascending_flag;
259              
260 14 50       66 if (scalar @_ > 0)
261             {
262 14 50       53 $ascending_flag = ($_[0] == 0)? 0: 1;
263 14         27 $ascending_order_called = 1;
264             }
265              
266 14         30 return $ascend;
267             }
268              
269             #
270             # ($new_coefficients_ref, $varsubst) = poly_analysis(@coefficients);
271             #
272             # If the polynomial has evenly spaced gaps of zero coefficients, reduce
273             # the polynomial through variable substitution.
274             #
275             # For example, a degree-6 polynomial like 9x**6 + 128x**3 + 7
276             # can be reduced to a polynomial 9y**2 + 128y + 7, where y = x**3.
277             #
278             # After solving a quadratic instead of a sextic, the actual roots of
279             # the original equation are found by taking the cube roots of each
280             # root of the quadratic.
281             #
282             # Not exported. Coefficients are always in ascending order.
283             sub poly_analysis
284             {
285 44     44 0 84 my(@coefficients) = @_;
286 44         57 my @czp;
287 44         58 my $m = 1;
288              
289             #
290             # Is the count of coefficients a multiple of any of the primes?
291             #
292             # Realistically I don't expect any gaps that can't be handled by
293             # the first three prime numbers, but it's not much of a waste of
294             # space to check the first dozen.
295             #
296 44         185 @czp = grep(($#coefficients % $_) == 0,
297             (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37)
298             );
299              
300             #
301             # Any coefficients zero at the non-N degrees? (1==T,0==F).
302             #
303             #### @czp
304             #
305 44 100       84 if (@czp)
306             {
307 30         58 for my $j (1..$#coefficients - 1)
308             {
309 106 100       187 if (abs($coefficients[$j]) > $epsilon)
310             {
311 20         44 @czp = grep(($j % $_) == 0, @czp);
312             }
313             }
314              
315             #
316             # The remaining list of primes represent the gap size
317             # between non-zero coefficients.
318             #
319 30         65 map(($m *= $_), @czp);
320              
321             #### Substitution degree: $m
322             }
323              
324             #
325             # If there's a sequence of zero-filled gaps in the coefficients,
326             # reduce the polynomial by degree $m and check again for the
327             # next round of factors (e.g., X**8 + X**4 + 1 needs two rounds
328             # to get to a factor of 4).
329             #
330 44 100       85 if ($m > 1)
331             {
332 22         38 my @alt_coefs;
333 22         85 push @alt_coefs, $coefficients[$_*$m] for (0..$#coefficients/$m);
334 22         55 my($cf, $m1) = poly_analysis(@alt_coefs);
335 22         46 @coefficients = @$cf;
336 22         42 $m *= $m1;
337             }
338              
339 44         100 return \@coefficients, $m;
340             }
341              
342             =head1 EXPORT
343              
344             There is an B tag that exports everything.
345              
346             Currently there is one default export, L.
347              
348             If you want to have more fine-grained control you may
349             individually name the functions in an export list, or
350             use one four export tags:
351              
352             L,
353             L,
354             L,
355             L,
356              
357             =head2 EXPORTED BY DEFAULT
358              
359             =head3 ascending_order()
360              
361             Changes the default order of the coefficents to the functions.
362              
363             When Math::Polynomial::Solve was originally written, it followed the
364             calling convention of L, using the highest degree
365             coefficient, followed by the next highest degree coefficient, and so
366             on in descending order.
367              
368             Later Math::Polynomial was re-written, and the order of the coefficients were
369             put in ascending order, e.g.:
370              
371             use Math::Polynomial;
372              
373             #
374             # Create the polynomial 8*x**3 - 6*x - 1.
375             #
376             $fpx = Math::Polynomial->new(-1, -6, 0, 8);
377              
378             If you use Math::Polynomial with this module, it will probably be
379             more convenient to change the default parameter list of
380             Math::Polynomial::Solve's functions, using the ascending_order() function:
381              
382             use Math::Polynomial;
383             use Math::Polynomial::Solve qw(:all);
384              
385             ascending_order(1);
386              
387             my $fp4 = Math::Polynomial->interpolate([1 .. 4], [14, 19, 25, 32]);
388              
389             #
390             # Find roots of $fp4.
391             #
392             my @fp4_roots = quartic_roots($fp4->coefficients);
393              
394             or
395              
396             my @fp4_roots = poly_roots($fp4->coefficients);
397              
398             If C had not been called, the previous line of code
399             would have been written instead as
400              
401             my @fp4_roots = poly_roots(reverse $fp4->coefficients);
402              
403             The function is a way to help with the change in the API when version 3.00 of
404             this module is released. At that point coefficients will be in ascending
405             order by default, and you will need to use C to use the
406             old (current) style.
407              
408             =head2 Numeric Functions
409              
410             These are the functions that calculate the polynomial's roots through numeric
411             algorithms. They are all exported under the tag "numeric".
412              
413             =head3 poly_roots()
414              
415             Returns the roots of a polynomial equation, regardless of degree.
416             Unlike the other root-finding functions, it will check for coefficients
417             of zero for the highest power, and 'step down' the degree of the
418             polynomial to the appropriate case. Additionally, it will check for
419             coefficients of zero for the lowest power terms, and add zeros to its
420             root list before calling one of the root-finding functions.
421              
422             By default, C will use the Hessenberg matrix method for solving
423             polynomials. This can be changed by calling L.
424              
425             The method of poly_roots() is almost equivalent to
426              
427             @x = hqr_eigen_hessenberg(
428             balance_matrix(build_companion(@coefficients))
429             );
430              
431             except this wouldn't check for leading and trailing zero coefficients, and it
432             ignores the settings of C.
433              
434             =cut
435              
436             sub poly_roots
437             {
438 135 50   135 1 355655 my(@coefficients) = ($ascending_flag == 0)? reverse @_: @_;
439 135         249 my(@x, @zero_x);
440 135         189 my $subst_degree = 1;
441              
442             #
443             #### @coefficients
444             #
445             # Check for zero coefficients in the higher-powered terms.
446             #
447 135   33     753 pop @coefficients while (scalar @coefficients and
448             abs($coefficients[$#coefficients]) < $epsilon);
449              
450 135 50       339 if (@coefficients == 0)
451             {
452 0         0 carp "All coefficients are zero\n";
453 0         0 return (0);
454             }
455              
456             #
457             # How about zero coefficients in the low terms?
458             #
459 135   66     552 while (scalar @coefficients and
460             abs($coefficients[0]) < $epsilon)
461             {
462 12         16 push @zero_x, 0;
463             shift @coefficients
464 12         33 }
465              
466             #
467             # If the polynomial is of the form c + ax**n, and if the
468             # root_function option is set, use the Math::Complex::root()
469             # function to return the roots.
470             #
471             ### %option
472             #
473 135 100 100     415 if ($option{root_function} and
474             poly_nonzero_term_count(@coefficients) == 2)
475             {
476 15         89 return @zero_x,
477             root(-$coefficients[0]/$coefficients[$#coefficients],
478             $#coefficients);
479             }
480              
481             #
482             # Next do some analysis of the coefficients.
483             # See if we can reduce the size of the polynomial by
484             # doing some variable substitution.
485             #
486 120 100 66     319 if ($option{varsubst} and $#coefficients > 1)
487             {
488 22         34 my $cf;
489 22         59 ($cf, $subst_degree) = poly_analysis(@coefficients);
490 22 50       70 @coefficients = @$cf if ($subst_degree > 1);
491             }
492              
493             #
494             # If the remaining polynomial is a quintic or higher, or
495             # if $option{hessenberg} is set, continue with the matrix
496             # calculation.
497             #
498             #### @coefficients
499             #### $subst_degree
500             #
501             #
502             # With the coefficents in ascending order,
503             # pretend it was always that way for the next
504             # function calls.
505             #
506 120         174 my $temp_ascending_flag = $ascending_flag;
507 120         209 $ascending_flag = 1;
508              
509 120 100 66     431 if ($option{hessenberg} or $#coefficients > 4)
    100          
    100          
    100          
    50          
510             {
511             #
512             # QR iterations from the matrix.
513             #
514 87         218 @x = hqr_eigen_hessenberg(
515             balance_matrix(build_companion(@coefficients))
516             );
517             }
518             elsif ($#coefficients == 4)
519             {
520 15         35 @x = quartic_roots(@coefficients);
521             }
522             elsif ($#coefficients == 3)
523             {
524 4         10 @x = cubic_roots(@coefficients);
525             }
526             elsif ($#coefficients == 2)
527             {
528 5         18 @x = quadratic_roots(@coefficients);
529             }
530             elsif ($#coefficients == 1)
531             {
532 9         37 @x = linear_roots(@coefficients);
533             }
534              
535 120         5071 $ascending_flag = $temp_ascending_flag;
536              
537 120 100       304 @x = map(root($_, $subst_degree), @x) if ($subst_degree > 1);
538              
539 120         7612 return @zero_x, @x;
540             }
541              
542              
543             =head3 poly_option()
544              
545             Set options that affect the behavior of the C function. All
546             options are set to either 1 ("on") or 0 ("off"). See also L
547             and L.
548              
549             Options may be set and saved:
550              
551             #
552             # Set a few options...
553             #
554             poly_option(hessenberg => 0, root_function => 1);
555              
556             #
557             # Get all of the current options and their values.
558             #
559             my %all_options = poly_option();
560              
561             #
562             # Set some options but save the former option values
563             # for later.
564             #
565             my %changed_options = poly_option(hessenberg => 1, varsubst => 1);
566              
567             The current options available are:
568              
569             =over 4
570              
571             =item 'hessenberg'
572              
573             Use the QR Hessenberg matrix method to solve the polynomial. By default, this
574             is set to 1. If set to 0, C uses one of the L
575             root-finding functions listed below, I the degree of the polynomial is four
576             or less.
577              
578             =item 'root_function'
579              
580             Use the L function from Math::Complex if the
581             polynomial is of the form C. This will take precedence over the other
582             solving methods.
583              
584             =item 'varsubst'
585              
586             Reduce polynomials to a lower degree through variable substitution, if possible.
587              
588             For example, with C set to one and the polynomial to solve being
589             C<9x**6 + 128x**3 + 21>, C will reduce the polynomial to
590             C<9y**2 + 128y + 21> (where C),
591             and solve the quadratic (either classically or numerically, depending
592             on the hessenberg option). Taking the cube root of each quadratic root
593             completes the operation.
594              
595             This has the benefit of having a simpler matrix to solve, or if the
596             C option is set to zero, has the effect of being able to use one of
597             the classical methods on a polynomial of high degree. In the above example, the
598             order-six polynomial gets solved with the quadratic_roots() function if the
599             hessenberg option is zero.
600              
601             Currently the variable substitution is fairly simple and will only look
602             for gaps of zeros in the coefficients that are multiples of the prime numbers
603             less than or equal to 37 (2, 3, 5, et cetera).
604              
605             =back
606              
607             =cut
608              
609             sub poly_option
610             {
611 22     22 1 35273 my %opts = @_;
612 22         34 my %old_opts;
613              
614 22 100       80 return %option if (scalar @_ == 0);
615              
616 15         46 for my $okey (keys %opts)
617             {
618             #
619             # If this is a real option, save its old value, then set it.
620             #
621 15 50       39 if (exists $option{$okey})
622             {
623 15         27 $old_opts{$okey} = $option{$okey};
624 15 100       46 $option{$okey} = ($opts{$okey})? 1: 0;
625             }
626             else
627             {
628 0         0 carp "poly_option(): unknown key $okey.";
629             }
630             }
631              
632 15         42 return %old_opts;
633             }
634              
635             =head3 build_companion
636              
637             Creates the initial companion matrix of the polynomial. Returns an array
638             of arrays (the internal representation of a matrix). This may be used as
639             an argument to the L contructor:
640              
641             my @cm = build_companion(@coef);
642              
643             my $m = Math::Matrix->new(@cm);
644             $m->print();
645              
646             The Wikipedia article at L has
647             more information on the subject.
648              
649             =cut
650              
651             #
652             # Perl code to find roots of a polynomial translated by Nick Ing-Simmons
653             # from FORTRAN code by Hiroshi Murakami.
654             #
655             # From the netlib archive: http://netlib.bell-labs.com/netlib/search.html
656             # In particular http://netlib.bell-labs.com/netlib/opt/companion.tgz
657             #
658             sub build_companion
659             {
660 87 50   87 1 239 my @coefficients = ($ascending_flag == 0)? reverse @_: @_;
661 87         162 my $n = $#coefficients - 1;
662 87         125 my @h;
663              
664             #
665             ### build_companion called with: @coefficients
666             #
667             # First step: Divide by the leading coefficient and negate.
668             #
669 87         145 my $cn = - (pop @coefficients);
670 87         311 map($_ /= $cn, @coefficients);
671              
672             #
673             # Next: set up the diagonal matrix.
674             #
675 87         209 for my $i (0 .. $n)
676             {
677 351         628 $h[$i][$n] = shift @coefficients;
678 351         897 map($h[$i][$_] = 0.0, 0 .. $n - 1);
679             }
680              
681 87         249 map($h[$_][$_ - 1] = 1.0, 1 .. $n);
682              
683 87         242 return @h;
684             }
685              
686             =head3 balance_matrix
687              
688             Balances the matrix (makes the rows and columns have similar norms) created
689             by build_companion() by applying a matrix transformation with a diagonal
690             matrix of powers of two.
691              
692             This is used to help prevent any rounding errors that occur if the elements
693             of the matrix differ greatly in magnitude.
694              
695             =cut
696              
697             # BASE is the base of the floating point representation on the machine.
698             # It is 16 for base 16 float : for example, IBM system 360/370.
699             # It is 2 for base 2 float : for example, IEEE float.
700             sub BASE () { 2 }
701             sub BASESQR () { BASE * BASE }
702              
703             #
704             # @matrix = balance_matrix(@cm);
705             #
706             # Balance the companion matrix created by build_companion().
707             #
708             # Return an array of arrays representing the N by N matrix.
709             #
710             # In the :numeric export set.
711             #
712             sub balance_matrix
713             {
714 87     87 1 184 my @h = @_;
715 87         141 my $n = $#h;
716              
717             #
718             ### Balancing the unsymmetric matrix A.
719             #
720             ##### @h
721             #
722             # Perl code translated by Nick Ing-Simmons from FORTRAN code
723             # by Hiroshi Murakami.
724             #
725             # The Fortran code is based on the Algol code "balance" from paper:
726             # "Balancing a Matrix for Calculation of Eigenvalues and Eigenvectors"
727             # by B. N. Parlett and C. Reinsch, Numer. Math. 13, 293-304(1969).
728             #
729             # Note: The only non-zero elements of the companion matrix are touched.
730             #
731 87         128 my $noconv = 1;
732 87         179 while ($noconv)
733             {
734 156         207 $noconv = 0;
735 156         274 for my $i (0 .. $n)
736             {
737             #
738             # Touch only non-zero elements of companion.
739             #
740 681         784 my $c;
741 681 100       983 if ($i != $n)
742             {
743 525         783 $c = abs($h[$i + 1][$i]);
744             }
745             else
746             {
747 156         195 $c = 0.0;
748 156         290 for my $j (0 .. $n - 1)
749             {
750 525         782 $c += abs($h[$j][$n]);
751             }
752             }
753              
754 681         828 my $r;
755 681 100       1121 if ($i == 0)
    100          
756             {
757 156         530 $r = abs($h[0][$n]);
758             }
759             elsif ($i != $n)
760             {
761 378         621 $r = abs($h[$i][$i - 1]) + abs($h[$i][$n]);
762             }
763             else
764             {
765 147         220 $r = abs($h[$i][$i - 1]);
766             }
767              
768 681 100 66     1728 next if ($c == 0.0 || $r == 0.0);
769              
770 672         905 my $g = $r / BASE;
771 672         771 my $f = 1.0;
772 672         863 my $s = $c + $r;
773 672         1133 while ( $c < $g )
774             {
775 205         257 $f = $f * BASE;
776 205         325 $c = $c * BASESQR;
777             }
778              
779 672         794 $g = $r * BASE;
780 672         1070 while ($c >= $g)
781             {
782 113         152 $f = $f / BASE;
783 113         191 $c = $c / BASESQR;
784             }
785              
786 672 100       1377 if (($c + $r) < 0.95 * $s * $f)
787             {
788 173         226 $g = 1.0 / $f;
789 173         213 $noconv = 1;
790              
791             #C Generic code.
792             #C do $j=1,$n
793             #C $h($i,$j)=$h($i,$j)*$g
794             #C enddo
795             #C do $j=1,$n
796             #C $h($j,$i)=$h($j,$i)*$f
797             #C enddo
798             #C begin specific code. Touch only non-zero elements of companion.
799 173 100       299 if ($i == 0)
800             {
801 41         61 $h[0][$n] *= $g;
802             }
803             else
804             {
805 132         202 $h[$i][$i - 1] *= $g;
806 132         181 $h[$i][$n] *= $g;
807             }
808 173 100       262 if ($i != $n)
809             {
810 145         268 $h[$i + 1][$i] *= $f;
811             }
812             else
813             {
814 28         56 for my $j (0 .. $n)
815             {
816 142         210 $h[$j][$i] *= $f;
817             }
818             }
819             }
820             } # for $i
821             } # while $noconv
822              
823             #
824             ### Returning balanced matrix.
825             ##### @h
826             #
827 87         242 return @h;
828             }
829              
830              
831             =head3 hqr_eigen_hessenberg
832              
833             Returns the roots of the polynomial equation by solving the matrix created by
834             C and C. See L.
835              
836             =cut
837              
838             sub hqr_eigen_hessenberg
839             {
840 87     87 1 178 my @h = @_;
841 87         125 my $n = $#h;
842              
843             #
844             ### hqr_eigen_hessenberg()
845             #
846             # Eigenvalue Computation by the Householder QR method for the
847             # Real Hessenberg matrix.
848             #
849             # Perl code translated by Nick Ing-Simmons from FORTRAN code
850             # by Hiroshi Murakami.
851             #
852             # The Fortran code is based on the Algol code "hqr" from the paper:
853             # "The QR Algorithm for Real Hessenberg Matrices"
854             # by R. S. Martin, G. Peters and J. H. Wilkinson,
855             # Numer. Math. 14, 219-231(1970).
856             #
857 87         131 my($p, $q, $r);
858 87         118 my $t = 0.0;
859              
860 87         109 my @roots;
861              
862             ROOT:
863 87         166 while ($n >= 0)
864             {
865 207         289 my $its = 0;
866 207         277 my $na = $n - 1;
867              
868 207         430 while ($its < $iteration{hessenberg})
869             {
870 1298         1705 my($w, $x, $y);
871              
872             #
873             # Look for single small sub-diagonal element;
874             #
875 1298         1574 my $l = 0;
876 1298         2160 for my $d (reverse 1 .. $n)
877             {
878 4215 100       9031 if (abs( $h[$d][ $d - 1 ] ) <= $epsilon *
879             (abs( $h[ $d - 1 ][ $d - 1 ] ) +
880             abs( $h[$d][$d] ) ) )
881             {
882 157         220 $l = $d;
883 157         222 last;
884             }
885             }
886              
887 1298         1927 $x = $h[$n][$n];
888              
889 1298 100       2048 if ($l == $n)
890             {
891             #
892             # One (real) root found.
893             #
894 63         87 $n--;
895 63         113 push @roots, $x + $t;
896 63         165 next ROOT;
897             }
898              
899 1235         1492 $y = $h[$na][$na];
900 1235         1590 $w = $h[$n][$na] * $h[$na][$n];
901              
902 1235 100       1860 if ($l == $na)
903             {
904 144         211 $p = ( $y - $x ) / 2;
905 144         191 $q = $p * $p + $w;
906 144         358 $y = sqrt( abs($q) );
907 144         909 $x += $t;
908              
909 144 100       264 if ($q > 0.0)
910             {
911             #
912             # Real pair.
913             #
914 17 100       50 $y = -$y if ( $p < 0.0 );
915 17         26 $y += $p;
916 17         33 push @roots, $x - $w / $y;
917 17         29 push @roots, $x + $y;
918             }
919             else
920             {
921             #
922             # Complex or twin pair.
923             #
924 127         307 push @roots, $x + $p - $y * i;
925 127         32947 push @roots, $x + $p + $y * i;
926             }
927              
928 144         27470 $n -= 2;
929 144         464 next ROOT;
930             }
931              
932 1091 50       1758 croak "Too many iterations ($its) at n=$n\n" if ($its >= $iteration{hessenberg});
933              
934 1091 100 100     2954 if ($its && $its % 10 == 0)
935             {
936             #
937             # Form exceptional shift.
938             #
939             ### Exceptional shift at: $its
940             #
941              
942 53         83 $t += $x;
943 53         89 for my $i (0 .. $n)
944             {
945 249         355 $h[$i][$i] -= $x;
946             }
947              
948 53         131 my $s = abs($h[$n][$na]) + abs($h[$na][$n - 2]);
949 53         69 $y = 0.75 * $s;
950 53         70 $x = $y;
951 53         80 $w = -0.4375 * $s * $s;
952             }
953              
954 1091         1331 $its++;
955              
956             #
957             ### Look for two consecutive small
958             ### sub-diagonal elements.
959             #
960 1091         1385 my $m = $l; # Set in case we fall through the loop.
961 1091         1757 for my $d (reverse $l .. $n - 2)
962             {
963 2809         3588 my $z = $h[$d][$d];
964 2809         3636 my $s = $y - $z;
965 2809         3246 $r = $x - $z;
966 2809         4436 $p = ($r * $s - $w) / $h[$d + 1][$d] + $h[$d][$d + 1];
967 2809         3849 $q = $h[$d + 1][$d + 1] - $z - $r - $s;
968 2809         3494 $r = $h[$d + 2][$d + 1];
969              
970 2809         3829 $s = abs($p) + abs($q) + abs($r);
971 2809         3280 $p /= $s;
972 2809         3087 $q /= $s;
973 2809         3096 $r /= $s;
974              
975             #
976             # The sub-diagonal check doesn't get made for
977             # the last iteration of the loop, and the only
978             # reason we have the loop continue up to this
979             # point is to set $p, $q, and $r.
980             #
981 2809 100       4327 last if ($d == $l);
982              
983 1726 100       4620 if (abs($h[$d][$d - 1]) * (abs($q) + abs($r)) <=
984             $epsilon * abs($p) * (
985             abs($h[$d - 1][$d - 1]) +
986             abs($z) +
987             abs($h[$d + 1][$d + 1])
988             ))
989             {
990 8         23 $m = $d;
991 8         14 last;
992             }
993             }
994              
995             #
996             #### $n
997             #### $l
998             #### $m
999             #
1000 1091         1839 for my $i (($m + 2) .. $n)
1001             {
1002 2809         3924 $h[$i][$i - 2] = 0.0;
1003             }
1004 1091         1526 for my $i (($m + 3) .. $n)
1005             {
1006 1718         2372 $h[$i][$i - 3] = 0.0;
1007             }
1008              
1009             #
1010             # Double QR step involving rows $l to $n and
1011             # columns $m to $n.
1012             #
1013 1091         1460 for my $k ($m .. $na)
1014             {
1015 3900         4478 my $z;
1016 3900         5107 my $notlast = ($k != $na);
1017 3900 100       5908 if ($k != $m)
1018             {
1019 2809         3758 $p = $h[$k][$k - 1];
1020 2809         3854 $q = $h[$k + 1][$k - 1];
1021 2809 100       4371 $r = ($notlast)? $h[$k + 2][$k - 1]: 0.0;
1022              
1023 2809         4247 $x = abs($p) + abs($q) + abs($r);
1024 2809 50       4293 next if ( $x == 0.0 );
1025              
1026 2809         3359 $p /= $x;
1027 2809         3099 $q /= $x;
1028 2809         3243 $r /= $x;
1029             }
1030              
1031 3900         8419 my $s = sqrt($p * $p + $q * $q + $r * $r);
1032 3900 100       23642 $s = -$s if ($p < 0.0);
1033              
1034 3900 100       6326 if ($k != $m)
    100          
1035             {
1036 2809         4157 $h[$k][$k - 1] = -$s * $x;
1037             }
1038             elsif ($l != $m)
1039             {
1040 8         17 $h[$k][$k - 1] *= -1;
1041             }
1042              
1043 3900         4596 $p += $s;
1044 3900         4713 $x = $p / $s;
1045 3900         4540 $y = $q / $s;
1046 3900         4526 $z = $r / $s;
1047 3900         4436 $q /= $p;
1048 3900         4297 $r /= $p;
1049              
1050             #
1051             # Row modification.
1052             #
1053 3900         5719 for my $j ($k .. $n)
1054             {
1055 14074         18695 $p = $h[$k][$j] + $q * $h[$k + 1][$j];
1056              
1057 14074 100       19825 if ($notlast)
1058             {
1059 11892         14917 $p += $r * $h[ $k + 2 ][$j];
1060 11892         15148 $h[ $k + 2 ][$j] -= $p * $z;
1061             }
1062              
1063 14074         17445 $h[ $k + 1 ][$j] -= $p * $y;
1064 14074         18856 $h[$k][$j] -= $p * $x;
1065             }
1066              
1067 3900         4913 my $j = $k + 3;
1068 3900 100       6018 $j = $n if ($j > $n);
1069              
1070             #
1071             # Column modification.
1072             #
1073 3900         5625 for my $i ($l .. $j)
1074             {
1075 18629         24660 $p = $x * $h[$i][$k] +
1076             $y * $h[$i][$k + 1];
1077              
1078 18629 100       25987 if ($notlast)
1079             {
1080 13624         16583 $p += $z * $h[$i][$k + 2];
1081 13624         17001 $h[$i][$k + 2] -= $p * $r;
1082             }
1083              
1084 18629         22603 $h[$i][$k + 1] -= $p * $q;
1085 18629         25631 $h[$i][$k] -= $p;
1086             }
1087             } # for $k
1088             } # while $its
1089             } # while $n
1090 87         292 return @roots;
1091             }
1092              
1093              
1094             =head2 Classical Functions
1095              
1096             These are the functions that solve polynomials via the classical methods.
1097             Quartic, cubic, quadratic, and even linear equations may be solved with
1098             these functions. They are all exported under the tag "classical".
1099              
1100             L will use these functions I the hessenberg option
1101             is set to 0, I the degree of the polynomial is four or less.
1102              
1103             The leading coefficient C<$a> must always be non-zero for the classical
1104             functions.
1105              
1106             =head3 linear_roots()
1107              
1108             Here for completeness's sake more than anything else. Returns the
1109             solution for
1110              
1111             ax + b = 0
1112              
1113             by returning C<-b/a>. This may be in either a scalar or an array context.
1114              
1115             =cut
1116              
1117             sub linear_roots
1118             {
1119 9 50   9 1 29 my($b, $a) = ($ascending_flag == 0)? reverse @_: @_;
1120              
1121 9 50       26 if (abs($a) < $epsilon)
1122             {
1123 0         0 carp "The coefficient of the highest power must not be zero!\n";
1124 0         0 return ();
1125             }
1126              
1127 9 50       33 return wantarray? (-$b/$a): -$b/$a;
1128             }
1129              
1130              
1131             =head3 quadratic_roots()
1132              
1133             Gives the roots of the quadratic equation
1134              
1135             ax**2 + bx + c = 0
1136              
1137             using the well-known quadratic formula. Returns a two-element list.
1138              
1139             =cut
1140              
1141             sub quadratic_roots
1142             {
1143 40 50   40 1 4158 my($c, $b, $a) = ($ascending_flag == 0)? reverse @_: @_;
1144              
1145 40 50       103 if (abs($a) < $epsilon)
1146             {
1147 0         0 carp "The coefficient of the highest power must not be zero!\n";
1148 0         0 return ();
1149             }
1150              
1151 40 100       97 return (0, -$b/$a) if (abs($c) < $epsilon);
1152              
1153 38         163 my $dis_sqrt = sqrt($b*$b - $a * 4 * $c);
1154              
1155 38 100       1686 $dis_sqrt = -$dis_sqrt if ($b < $epsilon); # Avoid catastrophic cancellation.
1156              
1157 38         802 my $xt = ($b + $dis_sqrt)/-2;
1158              
1159 38         2686 return ($xt/$a, $c/$xt);
1160             }
1161              
1162              
1163             =head3 cubic_roots()
1164              
1165             Gives the roots of the cubic equation
1166              
1167             ax**3 + bx**2 + cx + d = 0
1168              
1169             by the method described by R. W. D. Nickalls (see the L
1170             section below). Returns a three-element list. The first element will
1171             always be real. The next two values will either be both real or both
1172             complex numbers.
1173              
1174             =cut
1175              
1176             sub cubic_roots
1177             {
1178 23 50   23 1 11201 my($d, $c, $b, $a) = ($ascending_flag == 0)? reverse @_: @_;
1179 23         45 my @x;
1180              
1181 23 50       68 if (abs($a) < $epsilon)
1182             {
1183 0         0 carp "The coefficient of the highest power must not be zero!\n";
1184 0         0 return @x;
1185             }
1186              
1187             #
1188             # We're calling exported functions that also check
1189             # the $ascending_flag. To avoid reversing the reversed,
1190             # temporarily set the flag to zero and reset before returning.
1191             #
1192 23         42 my $temp_ascending_flag = $ascending_flag;
1193 23         38 $ascending_flag = 1;
1194              
1195 23 100       61 if (abs($d) < $epsilon)
1196             {
1197 2         9 @x = quadratic_roots($c, $b, $a);
1198 2         3 $ascending_flag = $temp_ascending_flag;
1199 2         6 return (0, @x);
1200             }
1201              
1202 21         56 my $xN = -$b/3/$a;
1203 21         53 my $yN = $d + $xN * ($c + $xN * ($b + $a * $xN));
1204              
1205 21         42 my $two_a = 2 * $a;
1206 21         57 my $delta_sq = ($b * $b - 3 * $a * $c)/(9 * $a * $a);
1207 21         68 my $h_sq = 4/9 * ($b * $b - 3 * $a * $c) * $delta_sq**2;
1208 21         44 my $dis = $yN * $yN - $h_sq;
1209 21         35 my $twothirds_pi = (2 * pi)/3;
1210              
1211             #
1212             ### cubic_roots() calculations...
1213             #### $two_a
1214             #### $delta_sq
1215             #### $h_sq
1216             #### $dis
1217             #
1218 21 100       71 if ($dis > $epsilon)
    100          
1219             {
1220             #
1221             ### Cubic branch 1, $dis is greater than 0...
1222             #
1223             # One real root, two complex roots.
1224             #
1225 10         38 my $dis_sqrt = sqrt($dis);
1226 10         96 my $r_p = $yN - $dis_sqrt;
1227 10         22 my $r_q = $yN + $dis_sqrt;
1228 10         35 my $p = cbrt( abs($r_p)/$two_a );
1229 10         100 my $q = cbrt( abs($r_q)/$two_a );
1230              
1231 10 100       78 $p = -$p if ($r_p > 0);
1232 10 100       34 $q = -$q if ($r_q > 0);
1233              
1234 10         25 $x[0] = $xN + $p + $q;
1235 10         30 $x[1] = $xN + $p * exp($twothirds_pi * i)
1236             + $q * exp(-$twothirds_pi * i);
1237 10         6579 $x[2] = ~$x[1];
1238             }
1239             elsif ($dis < -$epsilon)
1240             {
1241             #
1242             ### Cubic branch 2, $dis is less than 0...
1243             #
1244             # Three distinct real roots.
1245             #
1246 7         31 my $theta = acos(-$yN/sqrt($h_sq))/3;
1247 7         139 my $delta = sqrt($b * $b - 3 * $a * $c)/(3 * $a);
1248 7         70 my $two_d = 2 * $delta;
1249              
1250 7         39 @x = ($xN + $two_d * cos($theta),
1251             $xN + $two_d * cos($twothirds_pi - $theta),
1252             $xN + $two_d * cos($twothirds_pi + $theta));
1253             }
1254             else
1255             {
1256             #
1257             ### Cubic branch 3, $dis equals 0, within epsilon...
1258             #
1259             # abs($dis) <= $epsilon (effectively zero).
1260             #
1261             # Three real roots (two or three equal).
1262             #
1263 4         17 my $delta = cbrt($yN/$two_a);
1264              
1265 4         48 @x = ($xN + $delta, $xN + $delta, $xN - 2 * $delta);
1266             }
1267              
1268 21         454 $ascending_flag = $temp_ascending_flag;
1269 21         87 return @x;
1270             }
1271              
1272             =head3 quartic_roots()
1273              
1274             Gives the roots of the quartic equation
1275              
1276             ax**4 + bx**3 + cx**2 + dx + e = 0
1277              
1278             using Ferrari's method (see the L section below). Returns
1279             a four-element list. The first two elements will be either
1280             both real or both complex. The next two elements will also be alike in
1281             type.
1282              
1283             =cut
1284              
1285             sub quartic_roots
1286             {
1287 22 50   22 1 8841 my($e, $d, $c, $b, $a) = ($ascending_flag == 0)? reverse @_: @_;
1288 22         35 my @x = ();
1289              
1290 22 50       50 if (abs($a) < $epsilon)
1291             {
1292 0         0 carp "Coefficient of highest power must not be zero!\n";
1293 0         0 return @x;
1294             }
1295              
1296             #
1297             # We're calling exported functions that also check
1298             # the $ascending_flag. To avoid reversing the reversed,
1299             # temporarily set the flag to one and reset before returning.
1300             #
1301 22         31 my $temp_ascending_flag = $ascending_flag;
1302 22         29 $ascending_flag = 1;
1303              
1304 22 50       46 if (abs($e) < $epsilon)
1305             {
1306 0         0 @x = cubic_roots($d, $c, $b, $a);
1307 0         0 $ascending_flag = $temp_ascending_flag;
1308 0         0 return (0, @x);
1309             }
1310              
1311             #
1312             # First step: Divide by the leading coefficient.
1313             #
1314 22         35 $b /= $a;
1315 22         27 $c /= $a;
1316 22         28 $d /= $a;
1317 22         31 $e /= $a;
1318              
1319             #
1320             # Second step: simplify the equation to the
1321             # "resolvent cubic" y**4 + fy**2 + gy + h.
1322             #
1323             # (This is done by setting x = y - b/4).
1324             #
1325 22         33 my $b4 = $b/4;
1326              
1327             #
1328             # The f, g, and h values are:
1329             #
1330 22         48 my $f = $c -
1331             6 * $b4 * $b4;
1332 22         47 my $g = $d +
1333             2 * $b4 * (-$c + 4 * $b4 * $b4);
1334 22         44 my $h = $e +
1335             $b4 * (-$d + $b4 * ($c - 3 * $b4 * $b4));
1336              
1337             #
1338             ### quartic_roots calculations
1339             #### $b4
1340             #### $f
1341             #### $g
1342             #### $h
1343             #
1344 22 100       76 if (abs($h) < $epsilon)
    100          
1345             {
1346             #
1347             ### Quartic branch 1, $h equals 0, within epsilon...
1348             #
1349             # Special case: h == 0. We have a cubic times y.
1350             #
1351 2         6 @x = (0, cubic_roots($g, $f, 0, 1));
1352             }
1353             elsif (abs($g * $g) < $epsilon)
1354             {
1355             #
1356             ### Quartic branch 2, $g equals 0, within epsilon...
1357             #
1358             # Another special case: g == 0. We have a quadratic
1359             # with y-squared.
1360             #
1361             # (We check $g**2 because that's what the constant
1362             # value actually is in Ferrari's method, and it is
1363             # possible for $g to be outside of epsilon while
1364             # $g**2 is inside, i.e., "zero").
1365             #
1366 16         28 my($p, $q) = quadratic_roots($h, $f, 1);
1367 16         1118 $p = sqrt($p);
1368 16         813 $q = sqrt($q);
1369 16         864 @x = ($p, -$p, $q, -$q);
1370             }
1371             else
1372             {
1373             #
1374             ### Quartic branch 3, Ferrari's method...
1375             #
1376             # Special cases don't apply, so continue on with Ferrari's
1377             # method. This involves setting up the resolvent cubic
1378             # as the product of two quadratics.
1379             #
1380             # After setting up conditions that guarantee that the
1381             # coefficients come out right (including the zero value
1382             # for the third-power term), we wind up with a 6th
1383             # degree polynomial with, fortunately, only even-powered
1384             # terms. In other words, a cubic with z = y**2.
1385             #
1386             # Take a root of that equation, and get the
1387             # quadratics from it.
1388             #
1389 4         5 my $z;
1390 4         14 ($z, undef, undef) = cubic_roots(-$g*$g, $f*$f - 4*$h, 2*$f, 1);
1391              
1392             #### $z
1393              
1394 4         14 my $alpha = sqrt($z);
1395 4         89 my $rho = $g/$alpha;
1396 4         68 my $beta = ($f + $z - $rho)/2;
1397 4         169 my $gamma = ($f + $z + $rho)/2;
1398              
1399 4         162 @x = quadratic_roots($beta, $alpha, 1);
1400 4         238 push @x, quadratic_roots($gamma, -$alpha, 1);
1401             }
1402              
1403 22         1298 $ascending_flag = $temp_ascending_flag;
1404 22         57 return ($x[0] - $b4, $x[1] - $b4, $x[2] - $b4, $x[3] - $b4);
1405             }
1406              
1407             =head2 Sturm Functions
1408              
1409             These are the functions that create and make use of the Sturm sequence.
1410             They are all exported under the tag "sturm".
1411              
1412             =head3 poly_real_root_count()
1413              
1414             Return the number of I, I roots of the polynomial.
1415              
1416             $unique_roots = poly_real_root_count(@coefficients);
1417              
1418             For example, the equation C<(x + 3)**3> forms the polynomial
1419             C, but since all three of its roots are identical,
1420             C will return 1.
1421              
1422             Likewise, C will return 0 because the two roots
1423             of C are both complex.
1424              
1425             This function is the all-in-one function to use instead of
1426              
1427             my @chain = poly_sturm_chain(@coefficients);
1428              
1429             return sturm_sign_count(sturm_sign_minus_inf(\@chain)) -
1430             sturm_sign_count(sturm_sign_plus_inf(\@chain));
1431              
1432             if you don't intend to use the Sturm chain for anything else.
1433              
1434             =cut
1435              
1436             sub poly_real_root_count
1437             {
1438 17     17 1 4048 my @coefficients = @_;
1439              
1440 17         32 my @chain = poly_sturm_chain(@coefficients);
1441              
1442 17         36 return sturm_sign_count(sturm_sign_minus_inf(\@chain)) -
1443             sturm_sign_count(sturm_sign_plus_inf(\@chain));
1444             }
1445              
1446             =head3 sturm_real_root_range_count()
1447              
1448             Return the number of I, I roots of the polynomial between two X values.
1449              
1450             my($x0, $x1) = (0, 1000);
1451              
1452             my @chain = poly_sturm_chain(@coefficients);
1453             $root_count = sturm_real_root_range_count(\@chain, $x0, $x1);
1454              
1455             This is equivalent to:
1456              
1457             my($x0, $x1) = (0, 1000);
1458              
1459             my @chain = poly_sturm_chain(@coefficients);
1460             my @signs = sturm_sign_chain(\@chain, [$x0, $x1]);
1461             $root_count = sturm_sign_count(@{$signs[0]}) - sturm_sign_count(@{$signs[1]});
1462              
1463             =cut
1464              
1465             sub sturm_real_root_range_count
1466             {
1467 156     156 1 249 my($chain_ref, $x0, $x1) = @_;
1468              
1469 156         285 my @signs = sturm_sign_chain($chain_ref, [$x0, $x1]);
1470              
1471 156         219 my $count0 = sturm_sign_count(@{$signs[0]});
  156         241  
1472 156         178 my $count1 = sturm_sign_count(@{$signs[1]});
  156         218  
1473              
1474             #
1475             ###### (from, to): join(", ", ($x0, $x1))
1476             ###### sign count from: $count0
1477             ###### sign count to: $count1
1478             #
1479 156         302 return $count0 - $count1;
1480             }
1481              
1482              
1483             =head3 sturm_bisection()
1484              
1485             Finds the boundaries around the roots of a polynomial function,
1486             using the root count method of Sturm.
1487              
1488             @boundaries = sturm_bisection(\@chain, $from, $to);
1489              
1490             The elements of @boundaries will be a list of two-element arrays, each
1491             one bracketing a root.
1492              
1493             It will not bracket complex roots.
1494              
1495             This allows you to use a different root-finding function than laguerre(),
1496             which is the default function used by sturm_bisection_roots().
1497              
1498             =cut
1499              
1500             sub sturm_bisection
1501             {
1502 21     21 1 31 my($chain_ref, $from, $to) = @_;
1503 21         24 my(@coefficients) = @{${$chain_ref}[0]};
  21         54  
  21         42  
1504 21         26 my @boundaries;
1505              
1506             #
1507             #### @coefficients
1508             #
1509             #
1510             # If we have a linear equation, just solve the thing. We're not
1511             # going to find a useful second derivative, after all. (Which
1512             # would raise the question of why we're here without a useful
1513             # Sturm chain, but never mind...)
1514             #
1515 21 50       35 if ($#coefficients == 1)
1516             {
1517 0         0 my $root = linear_roots(@coefficients);
1518              
1519             #
1520             # But make sure the root is within the
1521             # asked-for range.
1522             #
1523 0 0 0     0 return () if ($root < $from or $root > $to);
1524 0         0 return ([$root, $root]);
1525             }
1526              
1527             #
1528             # Do Sturm bisection here.
1529             #
1530 21         34 my $range_count = sturm_real_root_range_count($chain_ref, $from, $to);
1531              
1532             #
1533             # If we're down to one root in this range, use Laguerre's method
1534             # to hunt it down.
1535             #
1536 21 50       35 return () if ($range_count == 0);
1537 21 100       44 return ([$from, $to]) if ($range_count == 1);
1538              
1539             #
1540             # More than one root in this range, so subdivide
1541             # until each root has its own range.
1542             #
1543 8         10 my $its = 0;
1544              
1545             ROOT:
1546 8         12 for (;;)
1547             {
1548 59         89 my $mid = ($to + $from)/2.0;
1549 59         81 my $frommid_count = sturm_real_root_range_count($chain_ref, $from, $mid);
1550 59         87 my $midto_count = sturm_real_root_range_count($chain_ref, $mid, $to);
1551              
1552             #
1553             #### $its
1554             #### $from
1555             #### $mid
1556             #### $to
1557             #### $frommid_count
1558             #### $midto_count
1559             #
1560              
1561             #
1562             # Bisect again if we only narrowed down to a range
1563             # containing all the roots.
1564             #
1565 59 100       98 if ($frommid_count == 0)
    100          
1566             {
1567 39         47 $from = $mid;
1568             }
1569             elsif ($midto_count == 0)
1570             {
1571 12         15 $to = $mid;
1572             }
1573             else
1574             {
1575             #
1576             # We've divided the roots between two ranges. Do it
1577             # again until each range has a single root in it.
1578             #
1579 8         19 push @boundaries, sturm_bisection($chain_ref, $from, $mid);
1580 8         14 push @boundaries, sturm_bisection($chain_ref, $mid, $to);
1581 8         12 last ROOT;
1582             }
1583 51 50       85 croak "Too many iterations ($its) at mid=$mid\n" if ($its >= $iteration{sturm_bisection});
1584 51         60 $its++;
1585             }
1586 8         14 return @boundaries;
1587             }
1588              
1589              
1590             =head3 sturm_bisection_roots()
1591              
1592             Return the I roots counted by L.
1593             Uses L to bracket the roots of the polynomial,
1594             then uses L to close in on each root.
1595              
1596             my($from, $to) = (-1000, 0);
1597             my @chain = poly_sturm_chain(@coefficients);
1598             my @roots = sturm_bisection_roots(\@chain, $from, $to);
1599              
1600             As it is using the Sturm functions, it will find only the real roots.
1601              
1602             =cut
1603              
1604             sub sturm_bisection_roots
1605             {
1606 5     5 1 17 my($chain_ref, $from, $to) = @_;
1607 5         6 my $cref0 = ${$chain_ref}[0];
  5         6  
1608 5         11 my @boundaries = sturm_bisection($chain_ref, $from, $to);
1609 5         8 my @roots;
1610              
1611 5         6 my $temp_ascending_flag = $ascending_flag;
1612 5         6 $ascending_flag = 1;
1613              
1614             #
1615             #### sturm_bisection() returns: @boundaries
1616             #
1617 5         6 for my $bracket (@boundaries)
1618             {
1619 13         22 my ($left, $right) = @$bracket;
1620 13         23 push @roots, laguerre($cref0, ($left + $right)/2.0);
1621             }
1622              
1623 5         8 $ascending_flag = $temp_ascending_flag;
1624              
1625 5         16 return @roots;
1626             }
1627              
1628              
1629             =head3 poly_sturm_chain()
1630              
1631             Returns the chain of Sturm functions used to evaluate the number of roots of a
1632             polynomial in a range of X values. The chain is a list of coefficient
1633             references, the coefficients being stored in ascending order.
1634              
1635             If you feed in a sequence of X values to the Sturm functions, you can tell where
1636             the (real, not complex) roots of the polynomial are by counting the number of
1637             times the Y values change sign.
1638              
1639             See L above for an example of its use.
1640              
1641             =cut
1642              
1643             sub poly_sturm_chain
1644             {
1645 55     55 1 7546 my @coefficients = @_;
1646 55         82 my $degree = $#coefficients;
1647 55         115 my(@chain, @remd);
1648 55         0 my($f1, $f2);
1649              
1650 55 100       106 @coefficients = reverse @coefficients unless ($ascending_flag);
1651              
1652 55         91 $f1 = [@coefficients];
1653 55         132 $f2 = pl_derivative(\@coefficients);
1654              
1655 55         580 push @chain, $f1;
1656              
1657             #
1658             # Start the first links of the chain.
1659             #
1660             SKIPIT: {
1661 55 100       66 last SKIPIT if ($degree < 1); #return @chain if ($degree < 1);
  55         105  
1662              
1663 52         64 push @chain, $f2;
1664              
1665 52 100       87 last SKIPIT if ($degree < 2); #return @chain if ($degree < 2);
1666              
1667             #
1668             ###### poly_sturm_chain chain before do loop:
1669             ###### @chain
1670             #
1671             do
1672 49         61 {
1673 77         152 my ($q, $r) = pl_div($f1, $f2);
1674              
1675             #
1676             # Remove any leading zeros in the remainder.
1677             #
1678 77         2081 @remd = @{$r};
  77         130  
1679 77   100     336 pop @remd while (@remd and abs($remd[$#remd]) < $epsilon);
1680              
1681 77         104 $f1 = $f2;
1682 77 100       147 $f2 = (@remd)? [map {$_ * -1} @remd]: [0];
  103         188  
1683 77         217 push @chain, $f2;
1684             }
1685             while ($#remd > 0);
1686             }
1687              
1688             #
1689             ###### poly_sturm_chain:
1690             ###### @chain
1691             #
1692 55         131 return @chain;
1693             }
1694              
1695             =head3 sturm_sign_count()
1696              
1697             Counts and returns the number of sign changes in a sequence of signs,
1698             such as those returned by the L
1699              
1700             See L and L for
1701             examples of its use.
1702              
1703             =cut
1704              
1705             sub sturm_sign_count
1706             {
1707 346     346 1 464 my @sign_seq = @_;
1708 346         381 my $scnt = 0;
1709              
1710 346         394 my $s1 = shift @sign_seq;
1711 346         450 for my $s2 (@sign_seq)
1712             {
1713 1060 100       1494 $scnt++ if ($s1 != $s2);
1714 1060         1234 $s1 = $s2;
1715             }
1716              
1717 346         820 return $scnt;
1718             }
1719              
1720              
1721             =head3 Sturm Sign Sequence Functions
1722              
1723             =head4 sturm_sign_chain()
1724              
1725             =head4 sturm_sign_minus_inf()
1726              
1727             =head4 sturm_sign_plus_inf()
1728              
1729             These functions return the array of signs that are used by the functions
1730             L and L to find
1731             the number of real roots in a polynomial.
1732              
1733             In normal use you will probably never need to use them, unless you want
1734             to examine the internals of the Sturm functions:
1735              
1736             #
1737             # Examine the sign changes that occur at each endpoint of
1738             # the x range.
1739             #
1740             my(@coefficients) = (1, 4, 7, 23);
1741             my(@xvals) = (-12, 12);
1742              
1743             my @chain = poly_sturm_chain( @coefficients);
1744             my @signs = sturm_sign_chain(\@chain, \@xvals); # An array of arrays.
1745              
1746             print "\nPolynomial: [", join(", ", @coefficients), "]\n";
1747              
1748             for my $j (0..$#signs)
1749             {
1750             my @s = @{$signs[$j]};
1751             print $xval[$j], "\n",
1752             "\t", join(", ", @s), "], sign count = ",
1753             sturm_sign_count(@s), "\n\n";
1754             }
1755              
1756             Similar examinations can be made at plus and minus infinity:
1757              
1758             #
1759             # Examine the sign changes that occur between plus and minus
1760             # infinity.
1761             #
1762             my @coefficients = (1, 4, 7, 23);
1763              
1764             my @chain = poly_sturm_chain( @coefficients);
1765             my @smi = sturm_sign_minus_inf(\@chain);
1766             my @spi = sturm_sign_plus_inf(\@chain);
1767              
1768             print "\nPolynomial: [", join(", ", @coefficients), "]\n";
1769              
1770             print "Minus Inf:\n",
1771             "\t", join(", ", @smi), "], sign count = ",
1772             sturm_sign_count(@smi), "\n\n";
1773              
1774             print "Plus Inf:\n",
1775             "\t", join(", ", @spi), "], sign count = ",
1776             sturm_sign_count(@spi), "\n\n";
1777              
1778             =cut
1779              
1780             #
1781             # @signs = sturm_minus_inf(\@chain);
1782             #
1783             # Return an array of signs from the chain at minus infinity.
1784             #
1785             # In the :sturm export set.
1786             #
1787             sub sturm_sign_minus_inf
1788             {
1789 17     17 1 26 my($chain_ref) = @_;
1790 17         22 my @signs;
1791              
1792 17         27 for my $c (@$chain_ref)
1793             {
1794 56         178 my @coefficients = @$c;
1795 56 100       102 push @signs, sign($coefficients[$#coefficients]) *
1796             ((($#coefficients & 1) == 1)? -1: 1);
1797             }
1798              
1799 17         73 return @signs;
1800             }
1801              
1802             #
1803             # @signs = sturm_plus_inf(\@chain);
1804             #
1805             # Return an array of signs from the chain at infinity.
1806             #
1807             # In the :sturm export set.
1808             #
1809             sub sturm_sign_plus_inf
1810             {
1811 17     17 1 23 my($chain_ref) = @_;
1812 17         21 my @signs;
1813              
1814 17         23 for my $c (@$chain_ref)
1815             {
1816 56         231 my @coefficients = @$c;
1817 56         93 push @signs, sign($coefficients[$#coefficients]);
1818             }
1819              
1820 17         82 return @signs;
1821             }
1822              
1823             #
1824             # @sign_chains = sturm_sign_chain(\@chain, \@xvals);
1825             #
1826             # Return an array of signs for each x-value passed in each function in
1827             # the Sturm chain.
1828             #
1829             # In the :sturm export set.
1830             #
1831             sub sturm_sign_chain
1832             {
1833 156     156 1 197 my($chain_ref, $xvals_ref) = @_;
1834 156         197 my $fn_count = $#$chain_ref;
1835 156         192 my $x_count = $#$xvals_ref;
1836 156         207 my @sign_chain;
1837              
1838 156         341 push @sign_chain, [] for (0..$x_count);
1839              
1840 156         214 for my $p_ref (@$chain_ref)
1841             {
1842 647         1050 my @ysigns = sign(pl_evaluate($p_ref, $xvals_ref));
1843              
1844             #
1845             # We just retrieved the signs of a single function across
1846             # our x-vals. We want it the other way around; signs listed
1847             # by x-val across functions.
1848             #
1849             # (list of lists)
1850             # |
1851             # v
1852             # f0 f1 f2 f3 f4 ...
1853             # x0 - - + - + (list 0)
1854             #
1855             # x1 + - - + + (list 1)
1856             #
1857             # x2 + - + + + (list 2)
1858             #
1859             # ...
1860             #
1861 647         12900 for my $j (0..$x_count)
1862             {
1863 1294         1412 push @{$sign_chain[$j]}, shift @ysigns;
  1294         1978  
1864             }
1865             }
1866              
1867             #
1868             ###### sturm_sign_chain() returns
1869             ###### @sign_chain: @sign_chain
1870             #
1871 156         240 return @sign_chain;
1872             }
1873              
1874              
1875             =head2 Utility Functions
1876              
1877             These are internal functions used by the other functions listed above
1878             that may also be useful to the user, or which affect the behavior of
1879             other functions. They are all exported under the tag "utility".
1880              
1881             =head3 epsilon()
1882              
1883             Returns the machine epsilon value that was calculated when this module was
1884             loaded.
1885              
1886             The value may be changed, although this in general is not recommended.
1887              
1888             my $old_epsilon = epsilon($new_epsilon);
1889              
1890             The previous value of epsilon may be saved to be restored later.
1891              
1892             The Wikipedia article at L has
1893             more information on the subject.
1894              
1895             =cut
1896              
1897             sub epsilon
1898             {
1899 0     0 1 0 my $eps = $epsilon;
1900 0 0       0 $epsilon = $_[0] if (scalar @_ > 0);
1901 0         0 return $eps;
1902             }
1903              
1904             =head3 laguerre()
1905              
1906             A numerical method for finding a root of an equation, especially made
1907             for polynomials.
1908              
1909             @roots = laguerre(\@coefficients, \@xvalues);
1910             push @roots, laguerre(\@coefficients, $another_xvalue);
1911              
1912             For each x value the function will attempt to find a root closest to it.
1913             The function will return real roots only.
1914              
1915             This is the function used by L after using
1916             L to narrow its search to a range containing a single
1917             root.
1918              
1919             =cut
1920              
1921             sub laguerre
1922             {
1923 17     17   144 no Math::Complex;
  17         34  
  17         4296  
1924 16     16 1 1668 my($p_ref, $xval_ref) = @_;
1925 16         22 my $n = $#$p_ref;
1926 16         22 my @xvalues;
1927             my @roots;
1928              
1929 16 50       28 $p_ref = [reverse @$p_ref] unless ($ascending_flag);
1930              
1931             #
1932             # Allow some flexibility in sending the x-values.
1933             #
1934 16 100       35 if (ref $xval_ref eq "ARRAY")
1935             {
1936 3         4 @xvalues = @$xval_ref;
1937             }
1938             else
1939             {
1940             #
1941             # It could happen. Someone might type \$x instead of $x.
1942             #
1943 13 50       22 @xvalues = ((ref $xval_ref eq "SCALAR")? $$xval_ref: $xval_ref);
1944             }
1945              
1946 16         25 for my $x (@xvalues)
1947             {
1948             #
1949             #### laguerre looking near: $x
1950             #### Coefficient: @$p_ref
1951             #### Degree: $n
1952             #
1953 21         25 my $its = 0;
1954              
1955             ROOT:
1956 21         26 for (;;)
1957             {
1958             #
1959             # Get the values of the function and its first and
1960             # second derivatives at X.
1961             #
1962 95         151 my($y, $dy, $d2y) = pl_dxevaluate($p_ref, $x);
1963              
1964 95 100       11420 if (abs($y) <= $tolerance{laguerre})
1965             {
1966 20         62 push @roots, $x;
1967 20         41 last ROOT;
1968             }
1969              
1970             #
1971             #### At Iteration: $its
1972             #### x: $x
1973             #### f(x): $y
1974             #### f'(x): $dy
1975             #### f''(x): $d2y
1976             #
1977 75         211 my $g = $dy/$y;
1978 75         514 my $h = $g * $g - $d2y/$y;
1979 75         1207 my $f = sqrt(($n - 1) * ($n * $h - $g*$g));
1980 75 100       2441 $f = - $f if (abs($g - $f) > abs($g + $f));
1981              
1982             #
1983             #### g: $g
1984             #### h: $h
1985             #### f: $f
1986             #
1987             # Divide by the largest value of $g plus
1988             # $f, bearing in mind that $f is the result
1989             # of a square root function and may be positive
1990             # or negative.
1991             #
1992             # Use the abs() function to determine size
1993             # since $g or $f may be complex numbers.
1994             #
1995 75         1818 my $dx = $n/($g + $f);
1996              
1997 75         1012 $x -= $dx;
1998 75 100       416 if (abs($dx) <= $tolerance{laguerre})
1999             {
2000 1         2 push @roots, $x;
2001 1         3 last ROOT;
2002             }
2003              
2004 74 50       249 croak "Too many iterations ($its) at dx=$dx\n" if ($its >= $iteration{laguerre});
2005 74         117 $its++;
2006             }
2007              
2008             ### root found at iteration $its
2009             #### $x
2010             }
2011              
2012 16         34 return @roots;
2013             }
2014              
2015              
2016             =head3 newtonraphson()
2017              
2018             Like L, a numerical method for finding a root of an equation.
2019              
2020             @roots = newtonraphson(\@coefficients, \@xvalues);
2021             push @roots, newtonraphson(\@coefficients, $another_xvalue);
2022              
2023             For each x value the function will attempt to find a root closest to it.
2024             The function will return real roots only.
2025              
2026             This function is provided as an alternative to laguerre(). It is not
2027             used internally by any other functions.
2028              
2029             =cut
2030              
2031             sub newtonraphson
2032             {
2033 17     17   111 no Math::Complex;
  17         33  
  17         8845  
2034 2     2 1 874 my($p_ref, $xval_ref) = @_;
2035 2         4 my $n = $#$p_ref;
2036 2         3 my @xvalues;
2037             my @roots;
2038              
2039 2 50       5 $p_ref = [reverse @$p_ref] unless ($ascending_flag);
2040              
2041             #
2042             # Allow some flexibility in sending the x-values.
2043             #
2044 2 50       5 if (ref $xval_ref eq "ARRAY")
2045             {
2046 2         4 @xvalues = @$xval_ref;
2047             }
2048             else
2049             {
2050             #
2051             # It could happen. Someone might type \$x instead of $x.
2052             #
2053 0 0       0 @xvalues = ((ref $xval_ref eq "SCALAR")? $$xval_ref: $xval_ref);
2054             }
2055              
2056             #
2057             ### newtonraphson()
2058             #### @xvalues
2059             #
2060 2         5 for my $x (@xvalues)
2061             {
2062 6         7 my $its = 0;
2063              
2064             ROOT:
2065 6         7 for (;;)
2066             {
2067             #
2068             # Get the values of the function and its
2069             # first derivative at X.
2070             #
2071 40         64 my($y, $dy, undef) = pl_dxevaluate($p_ref, $x);
2072 40         719 my $dx = $y/$dy;
2073 40         49 $x -= $dx;
2074              
2075 40 100       69 if (abs($dx) <= $tolerance{newtonraphson})
2076             {
2077 6         8 push @roots, $x;
2078 6         11 last ROOT;
2079             }
2080              
2081             #
2082             #### At Iteration: $its
2083             #### x: $x
2084             #### f(x): $y
2085             #### f'(x): $dy
2086             #
2087 34 50       47 croak "Too many iterations ($its) at dx=$dx\n" if ($its >= $iteration{newtonraphson});
2088 34         40 $its++;
2089             }
2090              
2091             ### root found at iteration $its
2092             #### $x
2093             }
2094              
2095 2         8 return @roots;
2096             }
2097              
2098             =head3 poly_iteration()
2099              
2100             Sets the limit to the number of iterations that a solving method may go
2101             through before giving up trying to find a root. Each method of root-finding
2102             used by L, L, and L
2103             has its own iteration limit, which may be found, like L,
2104             simply by looking at the return value of poly_iteration().
2105              
2106             #
2107             # Get all of the current iteration limits.
2108             #
2109             my %its_limits = poly_iteration();
2110              
2111             #
2112             # Double the limit for the hessenberg method, but set the limit
2113             # for Laguerre's method to 20.
2114             #
2115             my %old_limits = poly_iteration(hessenberg => $its_limits{hessenberg} * 2,
2116             laguerre => 20);
2117              
2118             #
2119             # Reset the limits with the former values, but save the values we had
2120             # for later.
2121             #
2122             my %hl_limits = poly_iteration(%old_limits);
2123              
2124             There are iteration limit values for:
2125              
2126             =over 4
2127              
2128             =item 'hessenberg'
2129              
2130             The numeric method used by poly_roots(), if the hessenberg option is set.
2131             Its default value is 60.
2132              
2133             =item 'laguerre'
2134              
2135             The numeric method used by L. Laguerre's method is used within
2136             sturm_bisection_roots() once it has narrowed its search in on an individual
2137             root, and of course laguerre() may be called independently. Its default value
2138             is 60.
2139              
2140             =item 'newtonraphson'
2141              
2142             The numeric method used by newtonraphson(). The Newton-Raphson method is offered
2143             as an alternative to Laguerre's method. Its default value is 60.
2144              
2145             =item 'sturm_bisection'
2146              
2147             The bisection method used to find roots within a range. Its default value
2148             is 100.
2149              
2150             =back
2151              
2152             =cut
2153              
2154             sub poly_iteration
2155             {
2156 13     13 1 1512 my %limits = @_;
2157 13         17 my %old_limits;
2158              
2159 13 100       43 return %iteration if (scalar @_ == 0);
2160              
2161 6         15 for my $k (keys %limits)
2162             {
2163             #
2164             # If this is a real iteration limit, save its old
2165             # value, then set it.
2166             #
2167 6 50       12 if (exists $iteration{$k})
2168             {
2169 6         9 my $val = abs(int($limits{$k}));
2170            
2171 6 50       11 carp "poly_iteration(): Unreasonably small value for $k => $val\n" if ($val < 10);
2172              
2173 6         10 $old_limits{$k} = $iteration{$k};
2174 6         9 $iteration{$k} = $val;
2175             }
2176             else
2177             {
2178 0         0 croak "poly_iteration(): unknown key $k.";
2179             }
2180             }
2181              
2182 6         15 return %old_limits;
2183             }
2184              
2185             =head3 poly_tolerance()
2186              
2187             Set the degree of accuracy needed for comparisons to be equal or roots
2188             to be found. Amongst the root finding functions this currently only
2189             affects laguerre() and newtonraphson(), as the Hessenberg matrix method
2190             determines how close it needs to get using a complicated formula based
2191             on L.
2192              
2193             #
2194             # Print the tolerances.
2195             #
2196             my %tolerances = poly_tolerance();
2197             print "Default tolerances:\n";
2198             for my $k (keys %tolerances)
2199             {
2200             print "$k => ", $tolerances{$k}, "\n";
2201             }
2202              
2203             #
2204             # Quadruple the tolerance for Laguerre's method.
2205             #
2206             poly_tolerance(laguerre => 4 * $tolerances{laguerre});
2207              
2208             Tolerances may be set for:
2209              
2210             =over 4
2211              
2212             =item 'laguerre'
2213              
2214             The numeric method used by laguerre(). Laguerre's method is used within
2215             sturm_bisection_roots() once an individual root has been found within a
2216             range, and of course it may be called independently.
2217              
2218             =item 'newtonraphson'
2219              
2220             The numeric method used by newtonraphson(). Newton-Raphson is, like
2221             Laguerre's method, a method for finding a root near the starting X value.
2222              
2223             =back
2224              
2225             =cut
2226              
2227             sub poly_tolerance
2228             {
2229 5     5 1 662 my %tols = @_;
2230 5         6 my %old_tols;
2231              
2232 5 100       17 return %tolerance if (scalar @_ == 0);
2233              
2234 2         5 for my $k (keys %tols)
2235             {
2236             #
2237             # If this is a real tolerance limit, save its old
2238             # value, then set it.
2239             #
2240 2 50       5 if (exists $tolerance{$k})
2241             {
2242 2         5 my $val = abs($tols{$k});
2243              
2244 2         2 $old_tols{$k} = $tolerance{$k};
2245 2         5 $tolerance{$k} = $val;
2246             }
2247             else
2248             {
2249 0         0 croak "poly_tolerance(): unknown key $k.";
2250             }
2251             }
2252              
2253 2         4 return %old_tols;
2254             }
2255              
2256             =head3 poly_nonzero_term_count()
2257              
2258             Returns a simple count of the number of coefficients that aren't zero.
2259              
2260             =cut
2261              
2262             sub poly_nonzero_term_count
2263             {
2264 39     39 1 115068 my(@coefficients) = @_;
2265 39         70 my $nzc = 0;
2266              
2267 39         108 for my $j (0..$#coefficients)
2268             {
2269 227 100       459 $nzc++ if (abs($coefficients[$j]) > $epsilon);
2270             }
2271 39         98 return $nzc;
2272             }
2273              
2274             END {
2275 17     17   34772 unless (1) #unless ($ascending_order_called)
2276             {
2277             my $end_list = join("\n", keys %called_by);
2278             warn "Coefficient order is in a default state, which will change by version 3.00.\n\n",
2279             "Please use ascending_order(0) at the beginning of your code to make\n",
2280             "sure your function parameters will be in the correct order when the\n",
2281             "default order changes.\n\n",
2282             "See the README file and the Math::Polynomial::Solve documentation for\n",
2283             "more information.\n",
2284             "File(s): $end_list\n";
2285             }
2286             }
2287              
2288             1;
2289             __END__