packages feed

combinat-0.2.9.0: test/Tests/Series.hs

-- | Tests for power series
--

{-# LANGUAGE CPP, GeneralizedNewtypeDeriving, DataKinds, KindSignatures #-}
module Tests.Series where

--------------------------------------------------------------------------------

import Math.Combinat.Numbers.Series

import Test.Framework
import Test.Framework.Providers.QuickCheck2
import Test.QuickCheck
import System.Random

import Data.List
import Data.Ratio

import GHC.TypeLits
import Data.Proxy

import Math.Combinat.Sign
import Math.Combinat.Numbers
import Math.Combinat.Partitions.Integer
import Math.Combinat.Helper

--------------------------------------------------------------------------------
-- * code used only for tests

-- | Expansion of @1 / (1-x^k)@. Included for completeness only; 
-- it equals to @coinSeries [k]@, and for example
-- for @k=4@ it is simply
-- 
-- > [1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0...]
--
pseries1 :: Int -> [Integer]
pseries1 k1 = convolveWithPSeries1 k1 unitSeries 

-- | The expansion of @1 / (1-x^k_1-x^k_2)@
pseries2 :: Int -> Int -> [Integer]
pseries2 k1 k2 = convolveWithPSeries2 k1 k2 unitSeries 

-- | The expansion of @1 / (1-x^k_1-x^k_2-x^k_3)@
pseries3 :: Int -> Int -> Int -> [Integer]
pseries3 k1 k2 k3 = convolveWithPSeries3 k1 k2 k3 unitSeries

--------------------------------------------------------------------------------

-- | Convolve with (the expansion of) @1 / (1-x^k1)@
convolveWithPSeries1 :: Int -> [Integer] -> [Integer]
convolveWithPSeries1 k1 series1 = xs where
  series = series1 ++ repeat 0 
  xs = zipWith (+) series ( replicate k1 0 ++ xs )

-- | Convolve with (the expansion of) @1 / (1-x^k1-x^k2)@
convolveWithPSeries2 :: Int -> Int -> [Integer] -> [Integer]
convolveWithPSeries2 k1 k2 series1 = xs where
  series = series1 ++ repeat 0 
  xs = zipWith3 (\x y z -> x + y + z)
    series
    ( replicate k1 0 ++ xs )
    ( replicate k2 0 ++ xs )
    
-- | Convolve with (the expansion of) @1 / (1-x^k_1-x^k_2-x^k_3)@
convolveWithPSeries3 :: Int -> Int -> Int -> [Integer] -> [Integer]
convolveWithPSeries3 k1 k2 k3 series1 = xs where
  series = series1 ++ repeat 0 
  xs = zipWith4 (\x y z w -> x + y + z + w)
    series
    ( replicate k1 0 ++ xs )
    ( replicate k2 0 ++ xs )
    ( replicate k3 0 ++ xs )

--------------------------------------------------------------------------------

-- | @1 / (1 - a*x^k)@. 
-- For example, for @a=3@ and @k=2@ it is just
-- 
-- > [1,0,3,0,9,0,27,0,81,0,243,0,729,0,2187,0,6561,0,19683,0...]
--
pseries1' :: Num a => (a,Int) -> [a]
pseries1' ak1 = convolveWithPSeries1' ak1 unitSeries

-- | @1 / (1 - a_1*x^k_1 - a_2*x^k_2)@
pseries2' :: Num a => (a,Int) -> (a,Int) -> [a]
pseries2' ak1 ak2 = convolveWithPSeries2' ak1 ak2 unitSeries

-- | @1 / (1 - a_1*x^k_1 - a_2*x^k_2 - a_3*x^k_3)@
pseries3' :: Num a => (a,Int) -> (a,Int) -> (a,Int) -> [a]
pseries3' ak1 ak2 ak3 = convolveWithPSeries3' ak1 ak2 ak3 unitSeries

--------------------------------------------------------------------------------

-- | Convolve with @1 / (1 - a*x^k)@. 
convolveWithPSeries1' :: Num a => (a,Int) -> [a] -> [a]
convolveWithPSeries1' (a1,k1) series1 = xs where
  series = series1 ++ repeat 0 
  xs = zipWith (+)
    series
    ( replicate k1 0 ++ map (*a1) xs )

-- | Convolve with @1 / (1 - a_1*x^k_1 - a_2*x^k_2)@
convolveWithPSeries2' :: Num a => (a,Int) -> (a,Int) -> [a] -> [a]
convolveWithPSeries2' (a1,k1) (a2,k2) series1 = xs where
  series = series1 ++ repeat 0 
  xs = zipWith3 (\x y z -> x + y + z)
    series
    ( replicate k1 0 ++ map (*a1) xs )
    ( replicate k2 0 ++ map (*a2) xs )
    
-- | Convolve with @1 / (1 - a_1*x^k_1 - a_2*x^k_2 - a_3*x^k_3)@
convolveWithPSeries3' :: Num a => (a,Int) -> (a,Int) -> (a,Int) -> [a] -> [a]
convolveWithPSeries3' (a1,k1) (a2,k2) (a3,k3) series1 = xs where
  series = series1 ++ repeat 0 
  xs = zipWith4 (\x y z w -> x + y + z + w)
    series
    ( replicate k1 0 ++ map (*a1) xs )
    ( replicate k2 0 ++ map (*a2) xs )
    ( replicate k3 0 ++ map (*a3) xs )

--------------------------------------------------------------------------------
-- * Types and instances

{-
swap :: (a,b) -> (b,a)
swap (x,y) = (y,x)
-}

-- compare the first N elements of the infinite lists
(=..=) :: (Eq a, Num a) => Int -> [a] -> [a] -> Bool
(=..=) n xs1 ys1 = take n xs == take n ys where
  xs = xs1 ++ repeat 0
  ys = ys1 ++ repeat 0

infix 4 =..=

-- compare the first 100 elements of the infinite lists
(=!=) :: (Eq a, Num a) => [a] -> [a] -> Bool
(=!=) xs1 ys1 = (take m xs == take m ys) where 
  m = 100
  xs = xs1 ++ repeat 0
  ys = ys1 ++ repeat 0

infix 4 =!=

-- compare the first 500 elements of the infinite lists
(=!!=) :: (Eq a, Num a) => [a] -> [a] -> Bool
(=!!=) xs1 ys1 = (take m xs == take m ys) where 
  m = 500
  xs = xs1 ++ repeat 0
  ys = ys1 ++ repeat 0

infix 4 =!!=

newtype XNat   = XNat   { fromXNat   :: Int      } deriving (Eq,Ord,Show,Num,Random)

newtype Rat    = Rat    { fromRat    :: Rational } deriving (Eq,Ord,Show,Num,Fractional)
newtype NZRat  = NZRat  { fromNZRat  :: Rational } deriving (Eq,Ord,Show,Num,Fractional)

-- type parameter is for controlling the size (length), because some tests are too slow
newtype Ser  (n :: Nat) = Ser  { fromSer'  :: [Integer] } deriving (Eq,Ord,Show)
newtype SerR (n :: Nat) = SerR { fromSerR' :: [Rational] } deriving (Eq,Ord,Show)

newtype Exp  = Exp  { fromExp  ::  Int  } deriving (Eq,Ord,Show,Num,Random)
newtype Exps = Exps { fromExps :: [Int] } deriving (Eq,Ord,Show)
newtype CoeffExp  = CoeffExp  { fromCoeffExp  ::  (Integer,Int)  } deriving (Eq,Ord,Show)
newtype CoeffExps = CoeffExps { fromCoeffExps :: [(Integer,Int)] } deriving (Eq,Ord,Show)

----------------------------------------

serProxy :: f (n :: Nat) -> Proxy n
serProxy _ = Proxy

seriesSize :: KnownNat (n :: Nat) => f (n :: Nat) -> Int
seriesSize ser = fromInteger $ natVal (serProxy ser) where 

----------------------------------------

fromSer  = fromSer500
fromSerR = fromSerR500

fromSer25 :: Ser 25 -> [Integer]
fromSer25 = fromSer'

fromSer100 :: Ser 100 -> [Integer]
fromSer100 = fromSer'

fromSer500 :: Ser 500 -> [Integer]
fromSer500 = fromSer'

----------------------------------------

fromSerR25 :: SerR 25 -> [Rational]
fromSerR25 = fromSerR'

fromSerR50 :: SerR 50 -> [Rational]
fromSerR50 = fromSerR'

fromSerR100 :: SerR 100 -> [Rational]
fromSerR100 = fromSerR'

fromSerR500 :: SerR 500 -> [Rational]
fromSerR500 = fromSerR'

----------------------------------------

{-
minSerSize = 0   :: Int
maxSerSize = 500 :: Int
-}

minSerValue = -10000 :: Int
maxSerValue =  10000 :: Int

rndList :: (RandomGen g, Random a) => Int -> (a, a) -> g -> ([a], g)
rndList n minmax g = swap $ mapAccumL f g [1..n] where
  f g _ = swap $ randomR minmax g 

instance Random Rat where
  random g = (Rat (fromIntegral x % fromIntegral y), g'') where
    (x,g' ) = randomR (-100,100::Int) g
    (y,g'') = randomR (   1, 25::Int) g'        -- hackety hack hack
  randomR _ g = random g

instance Random NZRat where
  random g = let (Rat q , g') = random g
             in  if q /= 0 then (NZRat q, g') else random g'            
  randomR _ g = random g

instance Arbitrary XNat where
  arbitrary = choose (XNat 0 , XNat 750)

instance Arbitrary Exp where
  arbitrary = choose (Exp 1 , Exp 32)

instance Arbitrary CoeffExp where
  arbitrary = do
    coeff <- choose (minSerValue, maxSerValue) :: Gen Int
    exp   <- arbitrary :: Gen Exp
    return $ CoeffExp (fromIntegral coeff, fromExp exp)
   
instance KnownNat (n :: Nat) => Random (Ser n) where
  random g = (series, g2) where
    maxSerSize = seriesSize series
    series     = Ser (map fi list) 
    (size,g1) = randomR (0,maxSerSize) g
    (list,g2) = rndList size (minSerValue,maxSerValue) g1
    fi :: Int -> Integer
    fi = fromIntegral 
  randomR _ = random

instance KnownNat (n :: Nat) => Random (SerR n) where
  random g = (series, g2) where
    maxSerSize = seriesSize series
    series    = SerR (map fromRat list) 
    (size,g1) = randomR (0,maxSerSize) g
    (list,g2) = rndList size (fromIntegral minSerValue, fromIntegral maxSerValue) g1
  randomR _ = random

instance Random Exps where
  random g = (Exps list, g2) where
    (size,g1) = randomR (0,10) g
    (list,g2) = rndList size (1,32) g1
  randomR _ = random

instance Random CoeffExps where
  random g = (CoeffExps (zip (map fromIntegral list2) list1), g3) where
    (size,g1) = randomR (0,10) g
    (list1,g2) = rndList size (1,32) g1
    (list2,g3) = rndList size (minSerValue,maxSerValue) g2
  randomR _ = random

instance Arbitrary Rat where
  arbitrary = choose undefined

instance Arbitrary NZRat where
  arbitrary = choose undefined
  
instance KnownNat n => Arbitrary (Ser n) where
  arbitrary = choose undefined

instance KnownNat n => Arbitrary (SerR n) where
  arbitrary = choose undefined

instance Arbitrary Exps where
  arbitrary = choose undefined

instance Arbitrary CoeffExps where
  arbitrary = choose undefined

--------------------------------------------------------------------------------
-- * test group

testgroup_PowerSeries :: Test
testgroup_PowerSeries = testGroup "Power series"
  [ 
    testProperty "mulSeries  == mulSeriesNaive"   prop_mulSeries_vs_naive
  , testProperty "divSeries  == mulWithRecip"     prop_divSeries_vs_mult_with_recip
  , testProperty "recip xs   == 1 / xs"           prop_recipSeries_vs_one_over
  , testProperty "compose    == composeNaive"     prop_compose_vs_naive
  , testProperty "substitute == substituteNaive"  prop_substitute_vs_naive
  , testProperty "inversion  == inversionNaive"   prop_inversion_vs_naive

  , testProperty "lagrange inversion works /1"       prop_lagrange_inversion1
  , testProperty "lagrange inversion works /2"       prop_lagrange_inversion2
  , testProperty "naive lagrange inversion works /1"       prop_lagrange_inversion_naive1
  , testProperty "naive lagrange inversion works /2"       prop_lagrange_inversion_naive2
  , testProperty "integral naive lagrange inversion works /1"       prop_lagrange_inversion_int_naive1
  , testProperty "integral naive lagrange inversion works /2"       prop_lagrange_inversion_int_naive2

  , testProperty "diff . int == id"            prop_diff_integrate
  , testProperty "tail (int . diff) == tail"   prop_integrate_diff
  , testProperty "sin vs sin2"                 prop_sin_vs_sin2
  , testProperty "cos vs cos2"                 prop_cos_vs_cos2

  , testProperty "convPSeries1 vs generic"     prop_conv1_vs_gen
  , testProperty "convPSeries2 vs generic"     prop_conv2_vs_gen
  , testProperty "convPSeries3 vs generic"     prop_conv3_vs_gen
  , testProperty "convPSeries1' vs generic"    prop_conv1_vs_gen'
  , testProperty "convPSeries2' vs generic"    prop_conv2_vs_gen'
  , testProperty "convPSeries3' vs generic"    prop_conv3_vs_gen'
  , testProperty "convolve_pseries"            prop_convolve_pseries 
  , testProperty "convolve_pseries'"           prop_convolve_pseries' 
  , testProperty "coinSeries vs pseries"       prop_coin_vs_pseries
  , testProperty "coinSeries vs pseries'"      prop_coin_vs_pseries'

    -- these are very slow, because random is slow
  , testProperty "leftIdentity"    prop_leftIdentity
  , testProperty "rightIdentity"   prop_rightIdentity
  , testProperty "commutativity"   prop_commutativity
  , testProperty "associativity"   prop_associativity
  ]

--------------------------------------------------------------------------------
-- * properties

prop_mulSeries_vs_naive ser1 ser2 = (mulSeries xs ys =!= mulSeriesNaive xs ys) where
  xs = fromSer ser1
  ys = fromSer ser2

prop_divSeries_vs_mult_with_recip (NZRat q) ser1 ser2 = (=..=) 60 (divSeries xs ys) (mulSeries xs (reciprocalSeries ys)) where
  xs =     fromSerR100 ser1
  ys = q : fromSerR100 ser2

prop_recipSeries_vs_one_over (NZRat q) ser = (reciprocalSeries xs =!= divSeries unitSeries xs) where
  xs = q : fromSerR100 ser

prop_compose_vs_naive ser1 ser2 = (=..=) 25 (composeSeries xs ys) (composeSeriesNaive xs ys) where
  xs =     fromSer25 ser1
  ys = 0 : fromSer25 ser2

prop_substitute_vs_naive ser1 ser2 = (=..=) 25 (substitute xs ys) (substituteNaive xs ys) where
  xs = 0 : fromSer25 ser1
  ys =     fromSer25 ser2

prop_inversion_vs_naive (NZRat q) ser = (=..=) 25 (lagrangeInversion xs) (lagrangeInversionNaive xs) where
  xs = 0 : q : fromSerR25 ser

prop_lagrange_inversion1 (NZRat q) ser = (=..=) 35 (substitute f (lagrangeInversion f)) (0 : 1 : repeat 0) where f = 0 : q : fromSerR50 ser
prop_lagrange_inversion2 (NZRat q) ser = (=..=) 35 (substitute (lagrangeInversion f) f) (0 : 1 : repeat 0) where f = 0 : q : fromSerR50 ser

prop_lagrange_inversion_naive1 (NZRat q) ser = (=..=) 20 (substituteNaive f (lagrangeInversionNaive f)) (0 : 1 : repeat 0) where f = 0 : q : fromSerR25 ser
prop_lagrange_inversion_naive2 (NZRat q) ser = (=..=) 20 (substituteNaive (lagrangeInversionNaive f) f) (0 : 1 : repeat 0) where f = 0 : q : fromSerR25 ser

prop_lagrange_inversion_int_naive1 ser = (=..=) 20 (substituteNaive f (integralLagrangeInversionNaive f)) (0 : 1 : repeat 0) where f = 0 : 1 : fromSer25 ser
prop_lagrange_inversion_int_naive2 ser = (=..=) 20 (substituteNaive (integralLagrangeInversionNaive f) f) (0 : 1 : repeat 0) where f = 0 : 1 : fromSer25 ser

--------------------------------------------------------------------------------

prop_diff_integrate ser = (xs =!= differentiateSeries (integrateSeries xs)) where
  xs = fromSerR ser

prop_integrate_diff ser = (0 : tail xs =!= integrateSeries (differentiateSeries xs)) where
  xs = fromSerR ser

prop_cos_vs_cos2 = (cosSeries =!= (cosSeries2 :: [Rational])) 
prop_sin_vs_sin2 = (sinSeries =!= (sinSeries2 :: [Rational])) 

--------------------------------------------------------------------------------
     
prop_leftIdentity ser = ( xs =!= unitSeries `convolve` xs ) where 
  xs = fromSer100 ser 

prop_rightIdentity ser = ( unitSeries `convolve` xs =!= xs ) where 
  xs = fromSer100 ser 

prop_commutativity ser1 ser2 = ( xs `convolve` ys =!= ys `convolve` xs ) where 
  xs = fromSer100 ser1
  ys = fromSer100 ser2

prop_associativity ser1 ser2 ser3 = ( one =!= two ) where
  one = (xs `convolve` ys) `convolve` zs
  two = xs `convolve` (ys `convolve` zs)
  xs = fromSer100 ser1
  ys = fromSer100 ser2
  zs = fromSer100 ser3

--------------------------------------------------------------------------------
  
prop_conv1_vs_gen exp1 ser = ( one =!= two ) where
  one = convolveWithPSeries1 k1 xs 
  two = convolveWithPSeries [k1] xs
  k1 = fromExp exp1
  xs = fromSer ser  

prop_conv2_vs_gen exp1 exp2 ser = (one =!= two) where
  one = convolveWithPSeries2 k1 k2 xs 
  two = convolveWithPSeries [k2,k1] xs
  k1 = fromExp exp1
  k2 = fromExp exp2
  xs = fromSer ser  

prop_conv3_vs_gen exp1 exp2 exp3 ser = (one =!= two) where
  one = convolveWithPSeries3 k1 k2 k3 xs 
  two = convolveWithPSeries [k2,k3,k1] xs
  k1 = fromExp exp1
  k2 = fromExp exp2
  k3 = fromExp exp3
  xs = fromSer ser  

prop_conv1_vs_gen' exp1 ser = ( one =!= two ) where
  one = convolveWithPSeries1' ak1 xs 
  two = convolveWithPSeries' [ak1] xs
  ak1 = fromCoeffExp exp1
  xs = fromSer ser  

prop_conv2_vs_gen' exp1 exp2 ser = (one =!= two) where
  one = convolveWithPSeries2' ak1 ak2 xs 
  two = convolveWithPSeries' [ak2,ak1] xs
  ak1 = fromCoeffExp exp1
  ak2 = fromCoeffExp exp2
  xs = fromSer ser  

prop_conv3_vs_gen' exp1 exp2 exp3 ser = (one =!= two) where
  one = convolveWithPSeries3' ak1 ak2 ak3 xs 
  two = convolveWithPSeries' [ak2,ak3,ak1] xs
  ak1 = fromCoeffExp exp1
  ak2 = fromCoeffExp exp2
  ak3 = fromCoeffExp exp3
  xs = fromSer ser  

prop_convolve_pseries exps1 ser = (one =!= two) where
  one = convolveWithPSeries ks1 xs 
  two = xs `convolve` pseries ks1 
  ks1 = fromExps exps1
  xs = fromSer ser  

prop_convolve_pseries' cexps1 ser = (one =!= two) where
  one = convolveWithPSeries' aks1 xs 
  two = xs `convolve` pseries' aks1 
  aks1 = fromCoeffExps cexps1
  xs = fromSer ser  

prop_coin_vs_pseries exps1 = (one =!= two) where
  one = coinSeries ks1 
  two = convolveMany (map pseries1 ks1)
  ks1 = fromExps exps1

prop_coin_vs_pseries' cexps1 = (one =!= two) where
  one = coinSeries' aks1 
  two = convolveMany (map pseries1' aks1)
  aks1 = fromCoeffExps cexps1
    
--------------------------------------------------------------------------------