A Solution of Project Two : Bairstow's Method

Below is a possible solution to the project.

1. Study and implementation of Bairstow's method

Using the deconv command in MATLAB for the synthetic division, an implementation for the method is given in the following two M-files:
function [q,f] = bairstow1(p,eps,N)
%
%  Computes one quadratic factor of the polynomial p.
%  On entry :
%    p        coefficient vector of polynomial, size(p,2) > 2;
%    eps      accuracy requirement on the coefficients of result;
%    N        maximal number of iterations.
%  On return :
%    q        coefficient vector of the quotient: p = q*f
%    f        coefficient vector of a quadratic factor.
%
n = size(p,2);
r = rand;
s = rand;
for i = 1:N
   d = [1 -r -s];                % divisor
   [q1,r1] = deconv(p,d);
   b0 = r1(n) + r*r1(n-1);
   b1 = r1(n-1);
   p2 = [q1 b1 b0];
   [q2,r2] = deconv(p2,d);
   c1 = r2(n-1);                 % define linear system
   c2 = q2(n-2);
   c3 = q2(n-3);
   a = [c2 c3; c1 c2];
   b = [-b1; -b0];
   drs = a\b;
   r = r + drs(1);
   s = s + drs(2);
   if (norm(drs) < eps)
      break;
   end;
end;
q = q1;
f = d;
function r = bairstow(p,eps,N)
%
%  Computes all roots of the polynomial p with Bairstow's method.
%  On entry :
%    p        coefficient vector of a polynomial;
%    eps      accuracy requirement on the quadratic factors;
%    N        maximal number of iterations to compute one factor.
%  On return :
%    r        approximations for all the roots of p.
%
n = size(p,2);
q = p;
r = zeros(1,n-1);
for i = 1:floor(n/2)
   if (size(q,2) <= 3)
      f = q;
      q = 0;
   else
      [q,f] = bairstow1(q,eps,N);
   end
   if size(f,2) == 2
      r(2*i-1) = -f(2)/f(1);
   else
      disc = f(2)^2 - 4*f(1)*f(3);
      r(2*i-1) = (-f(2) - sqrt(disc))/(2*f(1));
      r(2*i)   = (-f(2) + sqrt(disc))/(2*f(1));
   end;
end;
r = r';

2. Numerical experiments on random and special polynomials

1. Random Polynomials.

We generate a random polynomial of degree five and compute the roots with several different values of eps. Each time we list the error:

+------------------------------------------------------------+
|   eps             absolute  errors on the roots            |
+============================================================+
| 1.0e-4     -1.591901586106559e-07 + 1.304010855962545e-07i |
|            -1.591901586106559e-07 - 1.304010855962545e-07i |
|             2.924182164698585e-05                          |
|            -1.446172066482676e-05 + 4.286444749590501e-06i |
|            -1.446172066482676e-05 - 4.286444749590501e-06i |
+------------------------------------------------------------+
| 1.0e-8     -5.760947274779937e-13 + 5.523359547510154e-13i |
|            -5.760947274779937e-13 - 5.523359547510154e-13i |
|             5.702327499079729e-12                          |
|            -2.274957999759408e-12 - 8.339551271774326e-12i |
|            -2.274957999759408e-12 + 8.339551271774326e-12i |
+------------------------------------------------------------+
| 1.0e-12    -1.110223024625157e-16 + 1.110223024625157e-16i |
|            -1.110223024625157e-16 - 1.110223024625157e-16i |
|             1.665334536937735e-15                          |
|            -6.106226635438361e-16 + 3.330669073875470e-16i |
|            -6.106226635438361e-16 - 3.330669073875470e-16i |
+------------------------------------------------------------+
| 1.0e-16    -1.110223024625157e-16 + 2.220446049250313e-16i |
|            -1.110223024625157e-16 - 2.220446049250313e-16i |
|             5.551115123125783e-16                          |
|            -8.326672684688674e-17 + 1.110223024625157e-16i |
|            -8.326672684688674e-17 - 1.110223024625157e-16i |
+------------------------------------------------------------+

The propagation of the errors is noticeable with eps = 1.0e-4 and less pronounced with eps = 1.0e-8. For the details we refer to the output of the MATLAB session:

>> format long e
>> p = randn(1,6)

p =

  Columns 1 through 3 

     1.189164201652103e+00    -3.763327659331765e-02     3.272923614086541e-01

  Columns 4 through 6 

     1.746391428209245e-01    -1.867085776814394e-01     7.257905482933027e-01

>> r = roots(p)

r =

     -9.156617707168502e-01                         
     -1.877222373959926e-01 + 9.256377302559300e-01i
     -1.877222373959926e-01 - 9.256377302559300e-01i
      6.613765374439257e-01 + 5.565965671504062e-01i
      6.613765374439257e-01 - 5.565965671504062e-01i

>> rb1 = bairstow(p,1.0e-4,100)

rb1 =

      6.613766966340843e-01 + 5.565966975514918e-01i
      6.613766966340843e-01 - 5.565966975514918e-01i
     -1.877077756753278e-01 + 9.256420167006796e-01i
     -1.877077756753278e-01 - 9.256420167006796e-01i
     -9.156910125384972e-01                         

>> err = sort(r) - sort(rb1)

err =

     -1.591901586106559e-07 + 1.304010855962545e-07i
     -1.591901586106559e-07 - 1.304010855962545e-07i
      2.924182164698585e-05                         
     -1.446172066482676e-05 + 4.286444749590501e-06i
     -1.446172066482676e-05 - 4.286444749590501e-06i

>> sort(rb1)

ans =

      6.613766966340843e-01 - 5.565966975514918e-01i
      6.613766966340843e-01 + 5.565966975514918e-01i
     -9.156910125384972e-01                         
     -1.877077756753278e-01 - 9.256420167006796e-01i
     -1.877077756753278e-01 + 9.256420167006796e-01i

>> % we see the largest errors at the roots that were computed later
>> rb2 = bairstow(p,1.0e-8,100)

rb2 =

      6.613765374445018e-01 + 5.565965671509585e-01i
      6.613765374445018e-01 - 5.565965671509585e-01i
     -1.877222373937176e-01 + 9.256377302475904e-01i
     -1.877222373937176e-01 - 9.256377302475904e-01i
     -9.156617707225525e-01                         

>> err = sort(r) - sort(rb2)

err =

     -5.760947274779937e-13 + 5.523359547510154e-13i
     -5.760947274779937e-13 - 5.523359547510154e-13i
      5.702327499079729e-12                         
     -2.274957999759408e-12 - 8.339551271774326e-12i
     -2.274957999759408e-12 + 8.339551271774326e-12i

>> sort(rb2)

ans =

      6.613765374445018e-01 - 5.565965671509585e-01i
      6.613765374445018e-01 + 5.565965671509585e-01i
     -9.156617707225525e-01                         
     -1.877222373937176e-01 - 9.256377302475904e-01i
     -1.877222373937176e-01 + 9.256377302475904e-01i

>> % there is still a slight difference between the accuracy of the roots
>> rb3 = bairstow(p,1.0e-12,100)

rb3 =

      6.613765374439258e-01 + 5.565965671504063e-01i
      6.613765374439258e-01 - 5.565965671504063e-01i
     -1.877222373959920e-01 + 9.256377302559303e-01i
     -1.877222373959920e-01 - 9.256377302559303e-01i
     -9.156617707168518e-01                         

>> err = sort(r) - sort(rb3)

err =

     -1.110223024625157e-16 + 1.110223024625157e-16i
     -1.110223024625157e-16 - 1.110223024625157e-16i
      1.665334536937735e-15                         
     -6.106226635438361e-16 + 3.330669073875470e-16i
     -6.106226635438361e-16 - 3.330669073875470e-16i

>> % here we see no difference anymore in the errors
>> % due to the order of computation
>> rb4 = bairstow(p,1.0e-16,100)

rb4 =

      6.613765374439258e-01 + 5.565965671504064e-01i
      6.613765374439258e-01 - 5.565965671504064e-01i
     -1.877222373959925e-01 + 9.256377302559301e-01i
     -1.877222373959925e-01 - 9.256377302559301e-01i
     -9.156617707168507e-01                         

>> err = sort(r) - sort(rb4)

err =

     -1.110223024625157e-16 + 2.220446049250313e-16i
     -1.110223024625157e-16 - 2.220446049250313e-16i
      5.551115123125783e-16                         
     -8.326672684688674e-17 + 1.110223024625157e-16i
     -8.326672684688674e-17 - 1.110223024625157e-16i

>> % same conclusion as before

2. Multiple Roots.

  1. The geometric convergence rate

    The code for Newton's was modified so that it computes the ratio of two consecutive errors which reveals (m-1)/m = 0.8, which is then used to estimate the multiplicity. First we show the output of the MATLAB session:

    >> p = poly(ones(1,5))
    
    p =
    
         1    -5    10   -10     5    -1
    
    >> [q,r] = newton1(p,0.9,eps,20)
      |dx|     |f(x)|   |dx/previous_dx|     m
    2.00e-02  3.28e-06      2.00e-02      1.02e+00
    1.60e-02  1.07e-06      8.00e-01      5.00e+00
    1.28e-02  3.52e-07      8.00e-01      5.00e+00
    1.02e-02  1.15e-07      8.00e-01      5.00e+00
    8.19e-03  3.78e-08      8.00e-01      5.00e+00
    6.55e-03  1.24e-08      8.00e-01      5.00e+00
    5.24e-03  4.06e-09      8.00e-01      5.00e+00
    4.19e-03  1.33e-09      8.00e-01      5.00e+00
    3.36e-03  4.36e-10      8.00e-01      5.00e+00
    2.68e-03  1.43e-10      8.00e-01      5.00e+00
    2.15e-03  4.68e-11      8.00e-01      5.00e+00
    1.72e-03  1.53e-11      8.00e-01      5.00e+00
    1.37e-03  5.02e-12      8.00e-01      5.00e+00
    1.10e-03  1.65e-12      8.00e-01      5.00e+00
    8.79e-04  5.40e-13      8.00e-01      5.00e+00
    7.05e-04  1.77e-13      8.01e-01      5.03e+00
    5.64e-04  5.82e-14      8.00e-01      5.00e+00
    4.54e-04  1.85e-14      8.05e-01      5.13e+00
    3.56e-04  6.00e-15      7.85e-01      4.65e+00
    2.79e-04  1.22e-15      7.83e-01      4.61e+00
    failed requirements after 20 steps
    
    q =
    
        1.0000   -4.0012    6.0035   -4.0035    1.0012
    
    
    r =
    
        0.9988
    
    

    The file newton1.m :

    function [q,x] = newton(p,x,eps,N)
    %
    %  Applies Newton's method using synthetic division
    %  on the polynomial p, starting at x.
    %  On return is the quotient and a new approximation for the root.
    %  The iteration stops until the accuracy eps is reached.
    %  No more than N steps are performed.
    %  This method displays estimates for the multiplicity m.
    %
    %  Examples :
    %     p = [1 0 -1]; x = 0.9;
    %     [q,x] = newton(p,x,eps,4)
    %     p = [1 0 1]; x = 0.1 + 0.9*i;
    %     [q,x] = newton(p,x,eps,4)
    %
    n = size(p,2);
    d = [1 -x];
    [q,r] = deconv(p,d);
    prev_dx = 1;
    fprintf('  |dx|     |f(x)|   |dx/previous_dx|     m \n');
    for i = 1:N
       [q1,r1] = deconv(q,d);
       dx = r(n)/r1(n-1);
       x = x - dx;
       d(2) = -x;
       [q,r] = deconv(p,d);
       dx = abs(dx);
       m = prev_dx/(prev_dx - dx);
       fprintf('%8.2e  %8.2e  %12.2e  %12.2e\n', dx, abs(r(n)), dx/prev_dx, m);
       if (abs(dx) < eps) | (abs(r(n)) < eps)
          fprintf('succeeded after %d steps\n', i);
          return;
       end
       prev_dx = dx;
    end
    fprintf('failed requirements after %d steps\n', N);
    
  2. Restoring quadratic convergence

    If we use the multiplicity information in Newton's method, there is only one step:

    >> p = poly(ones(1,5))
    
    p =
    
         1    -5    10   -10     5    -1
    
    >> [q,r] = newton2(p,0.9,5,eps,4)
      real(x)               imag(x)                 dx        f(x)
    9.999999999993783e-01 0.000000000000000e+00  -2.00e-02  0.00e+00
    succeeded after 1 steps
    
    q =
    
        1.0000   -4.0000    6.0000   -4.0000    1.0000
    
    
    r =
    
        1.0000
    

    The file newton2.m :

    function [q,x] = newton(p,x,m,eps,N)
    %
    %  Applies Newton's method using synthetic division
    %  on the polynomial p, starting at x, with m as multiplicity.
    %  On return is the quotient and a new approximation for the root.
    %  The iteration stops until the accuracy eps is reached.
    %  No more than N steps are performed.
    %
    n = size(p,2);
    d = [1 -x];
    [q,r] = deconv(p,d);
    fprintf('  real(x)               imag(x)                 dx        f(x) \n');
    for i = 1:N
       [q1,r1] = deconv(q,d);
       dx = r(n)/r1(n-1);
       x = x - m*dx;
       d(2) = -x;
       [q,r] = deconv(p,d);
       fprintf('%.15e %.15e  %.2e  %.2e\n', real(x), imag(x), dx, r(n));
       if (abs(dx) < eps) | (abs(r(n)) < eps)
          fprintf('succeeded after %d steps\n', i);
          return;
       end
    end
    fprintf('failed requirements after %d steps\n', N);
    

3. A famous polynomial.

The polynomial (x-1)*(x-2)* ... *(x-20) was used by Wilkinson to test the routines he was developing to find the roots. The bad behaviour of his routines on this polynomial are due to the nature of the problem.

We see that Bairstow's method fails terribly on the Wilkinson polynomial. This polynomial is very sensitive to changes in the input coefficients. The deflation process add errors to the coefficients in the quotient. What makes the implementation fail completely though is the bad scaling of the coefficients in the linear system we solve. The coefficients of the Wilkinson polynomial vary enormously in magnitude which leads to badly conditioned linear systems.

>> w = diag(wilkinson(41));
>> r = w(1:20,:)'

r =

  Columns 1 through 13 

    20    19    18    17    16    15    14    13    12    11    10     9     8

  Columns 14 through 20 

     7     6     5     4     3     2     1

>> c = poly(r)

c =

   1.0e+19 *

  Columns 1 through 7 

    0.0000   -0.0000    0.0000   -0.0000    0.0000   -0.0000    0.0000

  Columns 8 through 14 

   -0.0000    0.0000   -0.0000    0.0001   -0.0010    0.0063   -0.0311

  Columns 15 through 21 

    0.1207   -0.3600    0.8038   -1.2871    1.3804   -0.8753    0.2433

>> roots(c)

ans =

   20.0003
   18.9970
   18.0118
   16.9695
   16.0509
   14.9319
   14.0684
   12.9472
   12.0345
   10.9836
   10.0063
    8.9983
    8.0003
    7.0000
    6.0000
    5.0000
    4.0000
    3.0000
    2.0000
    1.0000

>> rb = bairstow(c,eps,100)
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 1.253588e-16.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 3.139568e-17.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 7.862779e-18.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 1.969186e-18.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 4.931684e-19.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 1.235108e-19.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 3.093241e-20.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 7.746814e-21.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 1.940136e-21.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 4.858939e-22.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 1.216888e-22.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 3.047613e-23.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 7.632540e-24.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 1.911518e-24.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 4.787265e-25.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 1.198938e-25.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 3.002658e-26.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 7.519953e-27.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 1.883321e-27.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 4.716648e-28.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 1.181252e-28.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 2.958366e-29.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 7.409026e-30.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 1.855540e-30.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 4.647073e-31.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 1.163828e-31.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 2.914727e-32.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 7.299735e-33.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 1.828169e-33.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 4.578524e-34.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18
Warning: Matrix is close to singular or badly scaled.
         Results may be inaccurate. RCOND = 1.146660e-34.
> In /home/6/jan/Courses/MCS471/Project2/bairstow1.m at line 26
  In /home/6/jan/Courses/MCS471/Project2/bairstow.m at line 18

rb =

   NaN
   NaN
   NaN
   NaN
   NaN
   NaN
   NaN
   NaN
   NaN
   NaN
   NaN
   NaN
   NaN
   NaN
   NaN
   NaN
   NaN
   NaN
   NaN
   NaN

Here we show the sensitivity of the roots of the Wilkinson polynomial:

>> w = diag(wilkinson(41));
>> r = w(1:20,:)';
>> c = poly(r);
>> c(2) = c(2) + 1.0e-8;
>> roots(c)

ans =

  19.8694 + 0.4832i
  19.8694 - 0.4832i
  17.8768 + 1.5260i
  17.8768 - 1.5260i
  15.4027 + 1.7511i
  15.4027 - 1.7511i
  13.1282 + 1.2783i
  13.1282 - 1.2783i
  11.2499 + 0.4671i
  11.2499 - 0.4671i
   9.9403          
   9.0060          
   8.0000          
   6.9999          
   6.0000          
   5.0000          
   4.0000          
   3.0000          
   2.0000          
   1.0000          

A change of 10^(-8) to one of the coefficients gives rise to complex roots.