packages feed

numeric-quest-0.1: Roots.hs

module Roots where                
import Data.Complex
import Data.List(genericLength)

roots :: RealFloat a => a -> Int -> [Complex a] -> [Complex a]
roots eps count as =
      --
      -- List of complex roots of a polynomial
      -- a0 + a1*x + a2*x^2...
      -- represented by the list as=[a0,a1,a2...]
      -- where
      --     eps is a desired accuracy
      --     count is a maximum count of iterations allowed
      -- Require: list 'as' must have at least two elements
      --     and the last element must not be zero 
      roots' eps count as []
      where
          roots' eps count as xs 
              | length as <= 2  = x:xs
              | otherwise       = 
                  roots' eps count (deflate x bs [last as]) (x:xs)
              where
                  x  = laguerre eps count as 0
                  bs = drop 1 (reverse (drop 1 as))
                  deflate z bs cs
                      | bs == []   = cs
                      | otherwise  = 
                          deflate z (tail bs) (((head bs)+z*(head cs)):cs)


laguerre :: RealFloat a => a -> Int -> [Complex a] -> Complex a -> Complex a       
laguerre eps count as x
      --
      -- One of the roots of the polynomial 'as',
      -- where
      --    eps is a desired accuracy
      --    count is a maximum count of iterations allowed
      --    x is initial guess of the root
      -- This method is due to Laguerre.
      --
      | count <= 0               = x
      | magnitude (x - x') < eps = x'
      | otherwise                = laguerre eps (count - 1) as x'
      where
          x'     = laguerre2 eps as as' as'' x
          as'    = polynomial_derivative as
          as''   = polynomial_derivative as' 
          laguerre2 eps as as' as'' x
              -- One iteration step
              | magnitude b < eps           = x
              | magnitude gp < magnitude gm = 
                  if gm == 0 then x - 1 else x - n/gm
              | otherwise                   = 
                  if gp == 0 then x - 1 else x - n/gp
              where
                  gp    = g + delta
                  gm    = g - delta
                  g     = d/b
                  delta = sqrt ((n-1)*(n*h - g2))
                  h     = g2 - f/b
                  b     = polynomial_value as x
                  d     = polynomial_value as' x
                  f     = polynomial_value as'' x
                  g2    = g^2
                  n     = genericLength as

polynomial_value :: Num a => [a] -> a -> a
polynomial_value as x =
      --
      -- Value of polynomial a0 + a1 x  + a2 x^2 ...
      -- evaluated for 'x',
      -- where 'as' is a list [a0,a1,a2...]
      --
      foldr (u x) 0 as
      where
          u x a b = a + b*x

polynomial_derivative :: Num a => [a] -> [a]
polynomial_derivative as
      --
      -- List of coefficients for derivative of polynomial
      -- a0 + a1 x + a2 x^2 ...
      --
      | as == []  = []
      | otherwise = deriv 1 (drop 1 as) []
      where
          deriv n bs cs
             | bs == []   = reverse2 cs
             | otherwise  = deriv (n+1) (tail bs) ((n*(head bs)):cs) 
          reverse2 cs
              | cs == []  = []
              | otherwise = reverse cs



-----------------------------------------------------------------------------
--
-- Copyright:
--
--      (C) 1998 Numeric Quest Inc., All rights reserved
--
-- Email:
--
--      jans@numeric-quest.com
--
-- License:
--
--      GNU General Public License, GPL
-- 
-----------------------------------------------------------------------------