Blame doc/poly.rst

Packit 67cb25
.. index::
Packit 67cb25
   single: polynomials, roots of
Packit 67cb25
Packit 67cb25
***********
Packit 67cb25
Polynomials
Packit 67cb25
***********
Packit 67cb25
Packit 67cb25
.. include:: include.rst
Packit 67cb25
Packit 67cb25
This chapter describes functions for evaluating and solving polynomials.
Packit 67cb25
There are routines for finding real and complex roots of quadratic and
Packit 67cb25
cubic equations using analytic methods.  An iterative polynomial solver
Packit 67cb25
is also available for finding the roots of general polynomials with real
Packit 67cb25
coefficients (of any order).  The functions are declared in the header
Packit 67cb25
file :file:`gsl_poly.h`.
Packit 67cb25
Packit 67cb25
.. index::
Packit 67cb25
   single: polynomial evaluation
Packit 67cb25
   single: evaluation of polynomials
Packit 67cb25
Packit 67cb25
Polynomial Evaluation
Packit 67cb25
=====================
Packit 67cb25
Packit 67cb25
The functions described here evaluate the polynomial 
Packit 67cb25
Packit 67cb25
.. only:: not texinfo
Packit 67cb25
Packit 67cb25
   .. math::
Packit 67cb25
Packit 67cb25
      P(x) = c[0] + c[1] x + c[2] x^2 + \dots + c[len-1] x^{len-1}
Packit 67cb25
Packit 67cb25
.. only:: texinfo
Packit 67cb25
Packit 67cb25
   P(x) = c[0] + c[1] x + c[2] x^2 + ... + c[len-1] x^{len-1}
Packit 67cb25
   
Packit 67cb25
using Horner's method for stability. |inlinefns|
Packit 67cb25
Packit 67cb25
.. function:: double gsl_poly_eval (const double c[], const int len, const double x)
Packit 67cb25
Packit 67cb25
   This function evaluates a polynomial with real coefficients for the real variable :data:`x`.
Packit 67cb25
Packit 67cb25
.. function:: gsl_complex gsl_poly_complex_eval (const double c[], const int len, const gsl_complex z)
Packit 67cb25
Packit 67cb25
   This function evaluates a polynomial with real coefficients for the complex variable :data:`z`.
Packit 67cb25
Packit 67cb25
.. function:: gsl_complex gsl_complex_poly_complex_eval (const gsl_complex c[], const int len, const gsl_complex z)
Packit 67cb25
Packit 67cb25
   This function evaluates a polynomial with complex coefficients for the complex variable :data:`z`.
Packit 67cb25
Packit 67cb25
.. function:: int gsl_poly_eval_derivs (const double c[], const size_t lenc, const double x, double res[], const size_t lenres)
Packit 67cb25
Packit 67cb25
   This function evaluates a polynomial and its derivatives storing the
Packit 67cb25
   results in the array :data:`res` of size :data:`lenres`.  The output array
Packit 67cb25
   contains the values of :math:`d^k P(x)/d x^k` for the specified value of
Packit 67cb25
   :data:`x` starting with :math:`k = 0`.
Packit 67cb25
Packit 67cb25
.. index::
Packit 67cb25
   single: divided differences, polynomials
Packit 67cb25
   single: evaluation of polynomials, in divided difference form
Packit 67cb25
Packit 67cb25
Divided Difference Representation of Polynomials
Packit 67cb25
================================================
Packit 67cb25
Packit 67cb25
The functions described here manipulate polynomials stored in Newton's
Packit 67cb25
divided-difference representation.  The use of divided-differences is
Packit 67cb25
described in Abramowitz & Stegun sections 25.1.4 and 25.2.26, and
Packit 67cb25
Burden and Faires, chapter 3, and discussed briefly below.
Packit 67cb25
Packit 67cb25
Given a function :math:`f(x)`, an :math:`n`\ th degree interpolating polynomial :math:`P_{n}(x)`
Packit 67cb25
can be constructed which agrees with :math:`f` at :math:`n+1` distinct points
Packit 67cb25
:math:`x_0,x_1,...,x_{n}`. This polynomial can be written in a
Packit 67cb25
form known as Newton's divided-difference representation
Packit 67cb25
Packit 67cb25
.. only:: not texinfo
Packit 67cb25
Packit 67cb25
   .. math::
Packit 67cb25
Packit 67cb25
      P_{n}(x) = f(x_0) + \sum_{k=1}^n [x_0,x_1,...,x_k] (x-x_0)(x-x_1) \cdots (x-x_{k-1})
Packit 67cb25
Packit 67cb25
.. only:: texinfo
Packit 67cb25
Packit 67cb25
   P_{n}(x) = f(x_0) + \sum_{k=1}^n [x_0,x_1,...,x_k] (x-x_0)(x-x_1) ... (x-x_{k-1})
Packit 67cb25
Packit 67cb25
where the divided differences :math:`[x_0,x_1,...,x_k]` are defined in section 25.1.4 of
Packit 67cb25
Abramowitz and Stegun. Additionally, it is possible to construct an interpolating
Packit 67cb25
polynomial of degree :math:`2n+1` which also matches the first derivatives of :math:`f`
Packit 67cb25
at the points :math:`x_0,x_1,...,x_n`. This is called the Hermite interpolating
Packit 67cb25
polynomial and is defined as
Packit 67cb25
Packit 67cb25
.. only:: not texinfo
Packit 67cb25
Packit 67cb25
   .. math::
Packit 67cb25
Packit 67cb25
      H_{2n+1}(x) = f(z_0) + \sum_{k=1}^{2n+1} [z_0,z_1,...,z_k] (x-z_0)(x-z_1) \cdots (x-z_{k-1})
Packit 67cb25
Packit 67cb25
.. only:: texinfo
Packit 67cb25
Packit 67cb25
   H_{2n+1}(x) = f(z_0) + \sum_{k=1}^{2n+1} [z_0,z_1,...,z_k] (x-z_0)(x-z_1) ... (x-z_{k-1})
Packit 67cb25
Packit 67cb25
where the elements of :math:`z = \{x_0,x_0,x_1,x_1,...,x_n,x_n\}` are defined by
Packit 67cb25
:math:`z_{2k} = z_{2k+1} = x_k`. The divided-differences :math:`[z_0,z_1,...,z_k]`
Packit 67cb25
are discussed in Burden and Faires, section 3.4.
Packit 67cb25
Packit 67cb25
.. function:: int gsl_poly_dd_init (double dd[], const double xa[], const double ya[], size_t size)
Packit 67cb25
Packit 67cb25
   This function computes a divided-difference representation of the
Packit 67cb25
   interpolating polynomial for the points :math:`(x, y)` stored in
Packit 67cb25
   the arrays :data:`xa` and :data:`ya` of length :data:`size`.  On output the
Packit 67cb25
   divided-differences of (:data:`xa`, :data:`ya`) are stored in the array
Packit 67cb25
   :data:`dd`, also of length :data:`size`. Using the notation above,
Packit 67cb25
   :math:`dd[k] = [x_0,x_1,...,x_k]`.
Packit 67cb25
Packit 67cb25
.. function:: double gsl_poly_dd_eval (const double dd[], const double xa[], const size_t size, const double x)
Packit 67cb25
Packit 67cb25
   This function evaluates the polynomial stored in divided-difference form
Packit 67cb25
   in the arrays :data:`dd` and :data:`xa` of length :data:`size` at the point
Packit 67cb25
   :data:`x`. |inlinefn|
Packit 67cb25
Packit 67cb25
.. function:: int gsl_poly_dd_taylor (double c[], double xp, const double dd[], const double xa[], size_t size, double w[])
Packit 67cb25
Packit 67cb25
   This function converts the divided-difference representation of a
Packit 67cb25
   polynomial to a Taylor expansion.  The divided-difference representation
Packit 67cb25
   is supplied in the arrays :data:`dd` and :data:`xa` of length :data:`size`.
Packit 67cb25
   On output the Taylor coefficients of the polynomial expanded about the
Packit 67cb25
   point :data:`xp` are stored in the array :data:`c` also of length
Packit 67cb25
   :data:`size`.  A workspace of length :data:`size` must be provided in the
Packit 67cb25
   array :data:`w`.
Packit 67cb25
Packit 67cb25
.. function:: int gsl_poly_dd_hermite_init (double dd[], double za[], const double xa[], const double ya[], const double dya[], const size_t size)
Packit 67cb25
Packit 67cb25
   This function computes a divided-difference representation of the
Packit 67cb25
   interpolating Hermite polynomial for the points :math:`(x,y)` stored in
Packit 67cb25
   the arrays :data:`xa` and :data:`ya` of length :data:`size`. Hermite interpolation
Packit 67cb25
   constructs polynomials which also match first derivatives :math:`dy/dx` which are
Packit 67cb25
   provided in the array :data:`dya` also of length :data:`size`. The first derivatives can be
Packit 67cb25
   incorported into the usual divided-difference algorithm by forming a new
Packit 67cb25
   dataset :math:`z = \{x_0,x_0,x_1,x_1,...\}`, which is stored in the array
Packit 67cb25
   :data:`za` of length 2*\ :data:`size` on output. On output the
Packit 67cb25
   divided-differences of the Hermite representation are stored in the array
Packit 67cb25
   :data:`dd`, also of length 2*\ :data:`size`. Using the notation above,
Packit 67cb25
   :math:`dd[k] = [z_0,z_1,...,z_k]`. The resulting Hermite polynomial
Packit 67cb25
   can be evaluated by calling :func:`gsl_poly_dd_eval` and using
Packit 67cb25
   :data:`za` for the input argument :data:`xa`.
Packit 67cb25
Packit 67cb25
.. index::
Packit 67cb25
   single: quadratic equation, solving
Packit 67cb25
Packit 67cb25
Quadratic Equations
Packit 67cb25
===================
Packit 67cb25
Packit 67cb25
.. function:: int gsl_poly_solve_quadratic (double a, double b, double c, double * x0, double * x1)
Packit 67cb25
Packit 67cb25
   This function finds the real roots of the quadratic equation,
Packit 67cb25
Packit 67cb25
   .. math::
Packit 67cb25
Packit 67cb25
      a x^2 + b x + c = 0
Packit 67cb25
Packit 67cb25
   The number of real roots (either zero, one or two) is returned, and
Packit 67cb25
   their locations are stored in :data:`x0` and :data:`x1`.  If no real roots
Packit 67cb25
   are found then :data:`x0` and :data:`x1` are not modified.  If one real root
Packit 67cb25
   is found (i.e. if :math:`a=0`) then it is stored in :data:`x0`.  When two
Packit 67cb25
   real roots are found they are stored in :data:`x0` and :data:`x1` in
Packit 67cb25
   ascending order.  The case of coincident roots is not considered
Packit 67cb25
   special.  For example :math:`(x-1)^2=0` will have two roots, which happen
Packit 67cb25
   to have exactly equal values.
Packit 67cb25
Packit 67cb25
   The number of roots found depends on the sign of the discriminant
Packit 67cb25
   :math:`b^2 - 4 a c`.  This will be subject to rounding and cancellation
Packit 67cb25
   errors when computed in double precision, and will also be subject to
Packit 67cb25
   errors if the coefficients of the polynomial are inexact.  These errors
Packit 67cb25
   may cause a discrete change in the number of roots.  However, for
Packit 67cb25
   polynomials with small integer coefficients the discriminant can always
Packit 67cb25
   be computed exactly.
Packit 67cb25
Packit 67cb25
.. function:: int gsl_poly_complex_solve_quadratic (double a, double b, double c, gsl_complex * z0, gsl_complex * z1)
Packit 67cb25
Packit 67cb25
   This function finds the complex roots of the quadratic equation,
Packit 67cb25
Packit 67cb25
   .. math::
Packit 67cb25
Packit 67cb25
      a z^2 + b z + c = 0
Packit 67cb25
Packit 67cb25
   The number of complex roots is returned (either one or two) and the
Packit 67cb25
   locations of the roots are stored in :data:`z0` and :data:`z1`.  The roots
Packit 67cb25
   are returned in ascending order, sorted first by their real components
Packit 67cb25
   and then by their imaginary components.  If only one real root is found
Packit 67cb25
   (i.e. if :math:`a=0`) then it is stored in :data:`z0`.
Packit 67cb25
Packit 67cb25
.. index::
Packit 67cb25
   single: cubic equation, solving
Packit 67cb25
Packit 67cb25
Cubic Equations
Packit 67cb25
===============
Packit 67cb25
Packit 67cb25
.. function:: int gsl_poly_solve_cubic (double a, double b, double c, double * x0, double * x1, double * x2)
Packit 67cb25
Packit 67cb25
   This function finds the real roots of the cubic equation,
Packit 67cb25
Packit 67cb25
   .. math::
Packit 67cb25
Packit 67cb25
      x^3 + a x^2 + b x + c = 0
Packit 67cb25
Packit 67cb25
   with a leading coefficient of unity.  The number of real roots (either
Packit 67cb25
   one or three) is returned, and their locations are stored in :data:`x0`,
Packit 67cb25
   :data:`x1` and :data:`x2`.  If one real root is found then only :data:`x0`
Packit 67cb25
   is modified.  When three real roots are found they are stored in
Packit 67cb25
   :data:`x0`, :data:`x1` and :data:`x2` in ascending order.  The case of
Packit 67cb25
   coincident roots is not considered special.  For example, the equation
Packit 67cb25
   :math:`(x-1)^3=0` will have three roots with exactly equal values.  As
Packit 67cb25
   in the quadratic case, finite precision may cause equal or
Packit 67cb25
   closely-spaced real roots to move off the real axis into the complex
Packit 67cb25
   plane, leading to a discrete change in the number of real roots.
Packit 67cb25
Packit 67cb25
.. function:: int gsl_poly_complex_solve_cubic (double a, double b, double c, gsl_complex * z0, gsl_complex * z1, gsl_complex * z2)
Packit 67cb25
Packit 67cb25
   This function finds the complex roots of the cubic equation,
Packit 67cb25
Packit 67cb25
   .. math::
Packit 67cb25
Packit 67cb25
      z^3 + a z^2 + b z + c = 0
Packit 67cb25
Packit 67cb25
   The number of complex roots is returned (always three) and the locations
Packit 67cb25
   of the roots are stored in :data:`z0`, :data:`z1` and :data:`z2`.  The roots
Packit 67cb25
   are returned in ascending order, sorted first by their real components
Packit 67cb25
   and then by their imaginary components.
Packit 67cb25
Packit 67cb25
.. index::
Packit 67cb25
   single: general polynomial equations, solving
Packit 67cb25
Packit 67cb25
General Polynomial Equations
Packit 67cb25
============================
Packit 67cb25
Packit 67cb25
The roots of polynomial equations cannot be found analytically beyond
Packit 67cb25
the special cases of the quadratic, cubic and quartic equation.  The
Packit 67cb25
algorithm described in this section uses an iterative method to find the
Packit 67cb25
approximate locations of roots of higher order polynomials.
Packit 67cb25
Packit 67cb25
.. type:: gsl_poly_complex_workspace
Packit 67cb25
Packit 67cb25
   This workspace contains parameters used for finding roots of general polynomials
Packit 67cb25
Packit 67cb25
.. function:: gsl_poly_complex_workspace * gsl_poly_complex_workspace_alloc (size_t n)
Packit 67cb25
Packit 67cb25
   This function allocates space for a :type:`gsl_poly_complex_workspace`
Packit 67cb25
   struct and a workspace suitable for solving a polynomial with :data:`n`
Packit 67cb25
   coefficients using the routine :func:`gsl_poly_complex_solve`.
Packit 67cb25
Packit 67cb25
   The function returns a pointer to the newly allocated
Packit 67cb25
   :type:`gsl_poly_complex_workspace` if no errors were detected, and a null
Packit 67cb25
   pointer in the case of error.
Packit 67cb25
Packit 67cb25
.. function:: void gsl_poly_complex_workspace_free (gsl_poly_complex_workspace * w)
Packit 67cb25
Packit 67cb25
   This function frees all the memory associated with the workspace
Packit 67cb25
   :data:`w`.
Packit 67cb25
Packit 67cb25
.. function:: int gsl_poly_complex_solve (const double * a, size_t n, gsl_poly_complex_workspace * w, gsl_complex_packed_ptr z)
Packit 67cb25
Packit 67cb25
   This function computes the roots of the general polynomial 
Packit 67cb25
Packit 67cb25
   .. only:: not texinfo
Packit 67cb25
Packit 67cb25
      .. math::
Packit 67cb25
Packit 67cb25
         P(x) = a_0 + a_1 x + a_2 x^2 + \cdots + a_{n-1} x^{n-1}
Packit 67cb25
Packit 67cb25
   .. only:: texinfo
Packit 67cb25
Packit 67cb25
      P(x) = a_0 + a_1 x + a_2 x^2 + ... + a_{n-1} x^{n-1}
Packit 67cb25
      
Packit 67cb25
   using balanced-QR reduction of the companion matrix.  The parameter :data:`n`
Packit 67cb25
   specifies the length of the coefficient array.  The coefficient of the
Packit 67cb25
   highest order term must be non-zero.  The function requires a workspace
Packit 67cb25
   :data:`w` of the appropriate size.  The :math:`n-1` roots are returned in
Packit 67cb25
   the packed complex array :data:`z` of length :math:`2(n-1)`, alternating
Packit 67cb25
   real and imaginary parts.
Packit 67cb25
Packit 67cb25
   The function returns :data:`GSL_SUCCESS` if all the roots are found. If
Packit 67cb25
   the QR reduction does not converge, the error handler is invoked with
Packit 67cb25
   an error code of :data:`GSL_EFAILED`.  Note that due to finite precision,
Packit 67cb25
   roots of higher multiplicity are returned as a cluster of simple roots
Packit 67cb25
   with reduced accuracy.  The solution of polynomials with higher-order
Packit 67cb25
   roots requires specialized algorithms that take the multiplicity
Packit 67cb25
   structure into account (see e.g. Z. Zeng, Algorithm 835, ACM
Packit 67cb25
   Transactions on Mathematical Software, Volume 30, Issue 2 (2004), pp
Packit 67cb25
   218--236).
Packit 67cb25
Packit 67cb25
Examples
Packit 67cb25
========
Packit 67cb25
Packit 67cb25
To demonstrate the use of the general polynomial solver we will take the
Packit 67cb25
polynomial :math:`P(x) = x^5 - 1` which has these roots:
Packit 67cb25
Packit 67cb25
.. only:: not texinfo
Packit 67cb25
Packit 67cb25
   .. math::
Packit 67cb25
Packit 67cb25
      1, e^{2\pi i / 5}, e^{4\pi i / 5}, e^{6\pi i / 5}, e^{8\pi i / 5}
Packit 67cb25
Packit 67cb25
.. only:: texinfo
Packit 67cb25
Packit 67cb25
   1, e^{2*pi i / 5}, e^{4*pi i / 5}, e^{6*pi i / 5}, e^{8*pi i / 5}
Packit 67cb25
Packit 67cb25
The following program will find these roots.
Packit 67cb25
Packit 67cb25
.. include:: examples/polyroots.c
Packit 67cb25
   :code:
Packit 67cb25
Packit 67cb25
The output of the program is
Packit 67cb25
Packit 67cb25
.. include:: examples/polyroots.txt
Packit 67cb25
   :code:
Packit 67cb25
Packit 67cb25
which agrees with the analytic result, :math:`z_n = \exp(2 \pi n i/5)`.
Packit 67cb25
Packit 67cb25
References and Further Reading
Packit 67cb25
==============================
Packit 67cb25
Packit 67cb25
The balanced-QR method and its error analysis are described in the
Packit 67cb25
following papers,
Packit 67cb25
Packit 67cb25
* R.S. Martin, G. Peters and J.H. Wilkinson, "The QR Algorithm for Real
Packit 67cb25
  Hessenberg Matrices", Numerische Mathematik, 14 (1970), 219--231.
Packit 67cb25
Packit 67cb25
* B.N. Parlett and C. Reinsch, "Balancing a Matrix for Calculation of
Packit 67cb25
  Eigenvalues and Eigenvectors", Numerische Mathematik, 13 (1969),
Packit 67cb25
  293--304.
Packit 67cb25
Packit 67cb25
* A. Edelman and H. Murakami, "Polynomial roots from companion matrix
Packit 67cb25
  eigenvalues", Mathematics of Computation, Vol.: 64, No.: 210
Packit 67cb25
  (1995), 763--776.
Packit 67cb25
Packit 67cb25
The formulas for divided differences are given in the following texts,
Packit 67cb25
Packit 67cb25
* Abramowitz and Stegun, Handbook of Mathematical Functions,
Packit 67cb25
  Sections 25.1.4 and 25.2.26.
Packit 67cb25
Packit 67cb25
* R. L. Burden and J. D. Faires, Numerical Analysis, 9th edition,
Packit 67cb25
  ISBN 0-538-73351-9, 2011.