statistics 0.10.0.0 → 0.10.0.1
raw patch · 11 files changed
+892/−11 lines, 11 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ Statistics.Transform: dct_ :: Vector CD -> Vector Double
+ Statistics.Transform: idct_ :: Vector CD -> Vector Double
- Statistics.Transform: dct :: Vector CD -> Vector Double
+ Statistics.Transform: dct :: Vector Double -> Vector Double
- Statistics.Transform: idct :: Vector CD -> Vector Double
+ Statistics.Transform: idct :: Vector Double -> Vector Double
Files
- Statistics/Distribution/FDistribution.hs +1/−1
- Statistics/Sample/KernelDensity.hs +3/−3
- Statistics/Transform.hs +20/−6
- statistics.cabal +9/−1
- tests/Tests/Distribution.hs +276/−0
- tests/Tests/Helpers.hs +96/−0
- tests/Tests/Math.hs +159/−0
- tests/Tests/Math/Tables.hs +47/−0
- tests/Tests/Math/gen.py +51/−0
- tests/Tests/NonparametricTest.hs +148/−0
- tests/Tests/Transform.hs +82/−0
Statistics/Distribution/FDistribution.hs view
@@ -25,7 +25,7 @@ -- | Student-T distribution data FDistribution = F { fDistributionNDF1 :: {-# UNPACK #-} !Double , fDistributionNDF2 :: {-# UNPACK #-} !Double- , pdfFactor :: {-# UNPACK #-} !Double+ , _pdfFactor :: {-# UNPACK #-} !Double } deriving (Eq,Show,Read,Typeable)
Statistics/Sample/KernelDensity.hs view
@@ -31,7 +31,7 @@ import Statistics.Function (minMax, nextHighestPowerOfTwo) import Statistics.Math.RootFinding (fromRoot, ridders) import Statistics.Sample.Histogram (histogram_)-import Statistics.Transform (dct, idct)+import Statistics.Transform (dct_, idct_) import qualified Data.Vector.Generic as G import qualified Data.Vector.Unboxed as U @@ -78,12 +78,12 @@ where mesh = G.generate ni $ \z -> min + (d * fromIntegral z) where d = r / (n-1)- density = G.map (/r) . idct $ G.zipWith f a (G.enumFromTo 0 (n-1))+ density = G.map (/r) . idct_ $ G.zipWith f a (G.enumFromTo 0 (n-1)) where f b z = b * exp (sqr z * sqr pi * t_star * (-0.5)) :+ 0 !n = fromIntegral ni !ni = nextHighestPowerOfTwo n0 !r = max - min- a = dct . G.map (/ G.sum h) $ h+ a = dct_ . G.map (/ G.sum h) $ h where h = G.map (/ (len :+ 0)) $ histogram_ ni min max xs !len = fromIntegral (G.length xs) !t_star = fromRoot (0.28 * len ** (-0.4)) . ridders 1e-14 (0,0.1) $ \x ->
Statistics/Transform.hs view
@@ -17,9 +17,14 @@ module Statistics.Transform (+ -- * Type synonyms CD+ -- * Discrete cosine transform , dct+ , dct_ , idct+ , idct_+ -- * Fast Fourier transform , fft , ifft ) where@@ -35,9 +40,13 @@ type CD = Complex Double --- | Discrete cosine transform.-dct :: U.Vector CD -> U.Vector Double-dct xs = G.map realPart $ G.zipWith (*) weights (fft interleaved)+-- | Discrete cosine transform (DCT-II).+dct :: U.Vector Double -> U.Vector Double+dct = dct_ . G.map (:+0)++-- | Discrete cosine transform, with complex coefficients (DCT-II).+dct_ :: U.Vector CD -> U.Vector Double+dct_ xs = G.map realPart $ G.zipWith (*) weights (fft interleaved) where interleaved = G.backpermute xs $ G.enumFromThenTo 0 2 (len-2) G.++ G.enumFromThenTo (len-1) (len-3) 1@@ -46,9 +55,14 @@ where n = fi len len = G.length xs --- | Inverse discrete cosine transform.-idct :: U.Vector CD -> U.Vector Double-idct xs = G.generate len interleave+-- | Inverse discrete cosine transform (DCT-III).+idct :: U.Vector Double -> U.Vector Double+idct = idct_ . G.map (:+0)++-- | Inverse discrete cosine transform, with complex coefficients+-- (DCT-III).+idct_ :: U.Vector CD -> U.Vector Double+idct_ xs = G.generate len interleave where interleave z | even z = vals `G.unsafeIndex` halve z | otherwise = vals `G.unsafeIndex` (len - halve z - 1)
statistics.cabal view
@@ -1,5 +1,5 @@ name: statistics-version: 0.10.0.0+version: 0.10.0.1 synopsis: A library of statistical types, data, and functions description: This library provides a number of common functions and types useful@@ -111,6 +111,7 @@ examples/kde/data/faithful.csv examples/kde/kde.html examples/kde/kde.tpl+ tests/Tests/Math/gen.py library exposed-modules:@@ -175,6 +176,13 @@ type: exitcode-stdio-1.0 hs-source-dirs: tests main-is: tests.hs+ other-modules:+ Tests.Distribution+ Tests.Helpers+ Tests.Math+ Tests.Math.Tables+ Tests.NonparametricTest+ Tests.Transform ghc-options: -Wall -threaded -rtsopts
+ tests/Tests/Distribution.hs view
@@ -0,0 +1,276 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE ScopedTypeVariables #-}+-- Required for Param+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverlappingInstances #-}+module Tests.Distribution (+ distributionTests+ ) where++import Control.Applicative+import Control.Exception++import Data.List (find)+import Data.Typeable (Typeable)++import qualified Numeric.IEEE as IEEE++import Test.Framework (Test,testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)+import Test.QuickCheck as QC+import Test.QuickCheck.Monadic as QC+import Text.Printf++import Statistics.Distribution+import Statistics.Distribution.Binomial+import Statistics.Distribution.ChiSquared+import Statistics.Distribution.CauchyLorentz+import Statistics.Distribution.Exponential+import Statistics.Distribution.FDistribution+import Statistics.Distribution.Gamma+import Statistics.Distribution.Geometric+import Statistics.Distribution.Hypergeometric+import Statistics.Distribution.Normal+import Statistics.Distribution.Poisson+import Statistics.Distribution.StudentT+import Statistics.Distribution.Uniform++import Prelude hiding (catch)++import Tests.Helpers+++-- | Tests for all distributions+distributionTests :: Test+distributionTests = testGroup "Tests for all distributions"+ [ contDistrTests (T :: T CauchyDistribution )+ , contDistrTests (T :: T ChiSquared )+ , contDistrTests (T :: T ExponentialDistribution )+ , contDistrTests (T :: T GammaDistribution )+ , contDistrTests (T :: T NormalDistribution )+ , contDistrTests (T :: T UniformDistribution )+ , contDistrTests (T :: T StudentT )+ , contDistrTests (T :: T FDistribution )++ , discreteDistrTests (T :: T BinomialDistribution )+ , discreteDistrTests (T :: T GeometricDistribution )+ , discreteDistrTests (T :: T HypergeometricDistribution )+ , discreteDistrTests (T :: T PoissonDistribution )++ , unitTests+ ]++----------------------------------------------------------------+-- Tests+----------------------------------------------------------------++-- Tests for continous distribution+contDistrTests :: (Param d, ContDistr d, QC.Arbitrary d, Typeable d, Show d) => T d -> Test+contDistrTests t = testGroup ("Tests for: " ++ typeName t) $+ cdfTests t +++ [ testProperty "PDF sanity" $ pdfSanityCheck t+ , testProperty "Quantile is CDF inverse" $ quantileIsInvCDF t+ , testProperty "quantile fails p<0||p>1" $ quantileShouldFail t+ ]++-- Tests for discrete distribution+discreteDistrTests :: (Param d, DiscreteDistr d, QC.Arbitrary d, Typeable d, Show d) => T d -> Test+discreteDistrTests t = testGroup ("Tests for: " ++ typeName t) $+ cdfTests t +++ [ testProperty "Prob. sanity" $ probSanityCheck t+ , testProperty "CDF is sum of prob." $ discreteCDFcorrect t+ ]++-- Tests for distributions which have CDF+cdfTests :: (Param d, Distribution d, QC.Arbitrary d, Show d) => T d -> [Test]+cdfTests t =+ [ testProperty "C.D.F. sanity" $ cdfSanityCheck t+ , testProperty "CDF limit at +∞" $ cdfLimitAtPosInfinity t+ , testProperty "CDF limit at -∞" $ cdfLimitAtNegInfinity t+ , testProperty "CDF is nondecreasing" $ cdfIsNondecreasing t+ , testProperty "1-CDF is correct" $ cdfComplementIsCorrect t+ ]+----------------------------------------------------------------++-- CDF is in [0,1] range+cdfSanityCheck :: (Distribution d) => T d -> d -> Double -> Bool+cdfSanityCheck _ d x = c >= 0 && c <= 1 + where c = cumulative d x++-- CDF never decreases+cdfIsNondecreasing :: (Distribution d) => T d -> d -> Double -> Double -> Bool+cdfIsNondecreasing _ d = monotonicallyIncreasesIEEE $ cumulative d++-- CDF limit at +∞ is 1+cdfLimitAtPosInfinity :: (Param d, Distribution d) => T d -> d -> Property+cdfLimitAtPosInfinity _ d =+ okForInfLimit d ==> printTestCase ("Last elements: " ++ show (drop 990 probs))+ $ Just 1.0 == (find (>=1) probs)+ where+ probs = take 1000 $ map (cumulative d) $ iterate (*1.4) 1++-- CDF limit at -∞ is 0+cdfLimitAtNegInfinity :: (Param d, Distribution d) => T d -> d -> Property+cdfLimitAtNegInfinity _ d =+ okForInfLimit d ==> printTestCase ("Last elements: " ++ show (drop 990 probs))+ $ case find (< IEEE.epsilon) probs of+ Nothing -> False+ Just p -> p >= 0+ where+ probs = take 1000 $ map (cumulative d) $ iterate (*1.4) (-1)++-- CDF's complement is implemented correctly+cdfComplementIsCorrect :: (Distribution d) => T d -> d -> Double -> Bool+cdfComplementIsCorrect _ d x = (eq 1e-14) 1 (cumulative d x + complCumulative d x)+++-- PDF is positive+pdfSanityCheck :: (ContDistr d) => T d -> d -> Double -> Bool+pdfSanityCheck _ d x = p >= 0+ where p = density d x++-- Quantile is inverse of CDF+quantileIsInvCDF :: (Param d, ContDistr d) => T d -> d -> Double -> Property+quantileIsInvCDF _ d p =+ p > 0 && p < 1 ==> ( printTestCase (printf "Quantile = %g" q )+ $ printTestCase (printf "Probability = %g" p )+ $ printTestCase (printf "Probability' = %g" p')+ $ printTestCase (printf "Error = %e" (abs $ p - p'))+ $ abs (p - p') < invQuantilePrec d+ )+ where+ q = quantile d p+ p' = cumulative d q++-- Test that quantile fails if p<0 or p>1+quantileShouldFail :: (ContDistr d) => T d -> d -> Double -> Property+quantileShouldFail _ d p =+ p < 0 || p > 1 ==> QC.monadicIO $ do r <- QC.run $ catch+ (do { return $! quantile d p; return False })+ (\(e :: SomeException) -> return True)+ QC.assert r+++-- Probability is in [0,1] range+probSanityCheck :: (DiscreteDistr d) => T d -> d -> Int -> Bool+probSanityCheck _ d x = p >= 0 && p <= 1 + where p = probability d x++-- Check that discrete CDF is correct+discreteCDFcorrect :: (DiscreteDistr d) => T d -> d -> Int -> Int -> Property+discreteCDFcorrect _ d a b = + abs (a - b) < 100 ==> abs (p1 - p2) < 3e-10+ -- Avoid too large differeneces. Otherwise there is to much to sum+ --+ -- Absolute difference is used guard againist precision loss when+ -- close values of CDF are subtracted+ where+ n = min a b+ m = max a b+ p1 = cumulative d (fromIntegral m + 0.5) - cumulative d (fromIntegral n - 0.5)+ p2 = sum $ map (probability d) [n .. m]+++ +----------------------------------------------------------------+-- Arbitrary instances for ditributions+----------------------------------------------------------------++instance QC.Arbitrary BinomialDistribution where+ arbitrary = binomial <$> QC.choose (1,100) <*> QC.choose (0,1)+instance QC.Arbitrary ExponentialDistribution where+ arbitrary = exponential <$> QC.choose (0,100)+instance QC.Arbitrary GammaDistribution where+ arbitrary = gammaDistr <$> QC.choose (0.1,10) <*> QC.choose (0.1,10)+instance QC.Arbitrary GeometricDistribution where+ arbitrary = geometric <$> QC.choose (0,1)+instance QC.Arbitrary HypergeometricDistribution where+ arbitrary = do l <- QC.choose (1,20)+ m <- QC.choose (0,l)+ k <- QC.choose (1,l)+ return $ hypergeometric m l k+instance QC.Arbitrary NormalDistribution where+ arbitrary = normalDistr <$> QC.choose (-100,100) <*> QC.choose (1e-3, 1e3)+instance QC.Arbitrary PoissonDistribution where+ arbitrary = poisson <$> QC.choose (0,1)+instance QC.Arbitrary ChiSquared where+ arbitrary = chiSquared <$> QC.choose (1,100)+instance QC.Arbitrary UniformDistribution where+ arbitrary = do a <- QC.arbitrary+ b <- QC.arbitrary `suchThat` (/= a)+ return $ uniformDistr a b+instance QC.Arbitrary CauchyDistribution where+ arbitrary = cauchyDistribution+ <$> arbitrary+ <*> ((abs <$> arbitrary) `suchThat` (> 0))+instance QC.Arbitrary StudentT where+ arbitrary = studentT <$> ((abs <$> arbitrary) `suchThat` (>0))+instance QC.Arbitrary FDistribution where+ arbitrary = fDistribution + <$> ((abs <$> arbitrary) `suchThat` (>0))+ <*> ((abs <$> arbitrary) `suchThat` (>0))++++-- Parameters for distribution testing. Some distribution require+-- relaxing parameters a bit+class Param a where+ -- Precision for quantileIsInvCDF+ invQuantilePrec :: a -> Double+ invQuantilePrec _ = 1e-14+ -- Distribution is OK for testing limits+ okForInfLimit :: a -> Bool+ okForInfLimit _ = True+++instance Param a++instance Param StudentT where+ invQuantilePrec _ = 1e-13+ okForInfLimit d = studentTndf d > 0.75++instance Param FDistribution where+ invQuantilePrec _ = 1e-12++++----------------------------------------------------------------+-- Unit tests+----------------------------------------------------------------++unitTests :: Test+unitTests = testGroup "Unit tests"+ [ testAssertion "density (gammaDistr 150 1/150) 1 == 4.883311" $+ 4.883311418525483 =~ (density (gammaDistr 150 (1/150)) 1)+ -- Student-T+ , testStudentPDF 0.3 1.34 0.0648215 -- PDF+ , testStudentPDF 1 0.42 0.27058+ , testStudentPDF 4.4 0.33 0.352994+ , testStudentCDF 0.3 3.34 0.757146 -- CDF+ , testStudentCDF 1 0.42 0.626569+ , testStudentCDF 4.4 0.33 0.621739+ -- F-distribution+ , testFdistrPDF 1 3 3 (1/(6 * pi)) -- PDF+ , testFdistrPDF 2 2 1.2 0.206612+ , testFdistrPDF 10 12 8 0.000385613179281892790166+ , testFdistrCDF 1 3 3 0.81830988618379067153 -- CDF+ , testFdistrCDF 2 2 1.2 0.545455+ , testFdistrCDF 10 12 8 0.99935509863451408041+ ]+ where+ -- Student-T+ testStudentPDF ndf x exact+ = testAssertion (printf "density (studentT %f) %f ≈ %f" ndf x exact)+ $ eq 1e-5 exact (density (studentT ndf) x)+ testStudentCDF ndf x exact+ = testAssertion (printf "cumulative (studentT %f) %f ≈ %f" ndf x exact)+ $ eq 1e-5 exact (cumulative (studentT ndf) x)+ -- F-distribution+ testFdistrPDF n m x exact+ = testAssertion (printf "density (fDistribution %i %i) %f ≈ %f [got %f]" n m x exact d)+ $ eq 1e-5 exact d+ where d = density (fDistribution n m) x+ testFdistrCDF n m x exact+ = testAssertion (printf "cumulative (fDistribution %i %i) %f ≈ %f [got %f]" n m x exact d)+ $ eq 1e-5 exact d+ where d = cumulative (fDistribution n m) x
+ tests/Tests/Helpers.hs view
@@ -0,0 +1,96 @@+-- | Helpers for testing+module Tests.Helpers (+ -- * helpers+ T(..)+ , typeName+ , eq+ , eqC+ , (=~)+ -- * Generic QC tests+ , monotonicallyIncreases+ , monotonicallyIncreasesIEEE+ -- * HUnit helpers+ , testAssertion+ ) where++import Data.Complex+import Data.Typeable++import qualified Numeric.IEEE as IEEE++import qualified Test.HUnit as HU+import Test.Framework+import Test.Framework.Providers.HUnit++import Statistics.Constants++++----------------------------------------------------------------+-- Helpers+----------------------------------------------------------------++-- | Phantom typed value used to select right instance in QC tests+data T a = T++-- | String representation of type name+typeName :: Typeable a => T a -> String+typeName = show . typeOf . typeParam+ where+ typeParam :: T a -> a+ typeParam _ = undefined++-- | Approximate equality for 'Double'. Doesn't work well for numbers+-- which are almost zero.+eq :: Double -- ^ Relative error+ -> Double -> Double -> Bool+eq eps a b + | a == 0 && b == 0 = True+ | otherwise = abs (a - b) <= eps * max (abs a) (abs b)++-- | Approximate equality for 'Complex Double'+eqC :: Double -- ^ Relative error+ -> Complex Double+ -> Complex Double+ -> Bool+eqC eps a@(ar :+ ai) b@(br :+ bi)+ | a == 0 && b == 0 = True+ | otherwise = abs (ar - br) <= eps * d+ && abs (ai - bi) <= eps * d+ where+ d = max (realPart $ abs a) (realPart $ abs b)+++-- | Approximately equal up to 1 ulp+(=~) :: Double -> Double -> Bool+(=~) = eq m_epsilon+++----------------------------------------------------------------+-- Generic QC+----------------------------------------------------------------++-- Check that function is nondecreasing+monotonicallyIncreases :: (Ord a, Ord b) => (a -> b) -> a -> a -> Bool+monotonicallyIncreases f x1 x2 = f (min x1 x2) <= f (max x1 x2)++-- Check that function is nondecreasing taking rounding errors into+-- account.+--+-- In fact funstion is allowed to decrease less than one ulp in order+-- to guard againist problems with excess precision. On x86 FPU works+-- with 80-bit numbers but doubles are 64-bit so rounding happens+-- whenever values are moved from registers to memory+monotonicallyIncreasesIEEE :: (Ord a, IEEE.IEEE b) => (a -> b) -> a -> a -> Bool+monotonicallyIncreasesIEEE f x1 x2 =+ y1 <= y2 || (y1 - y2) < y2 * IEEE.epsilon+ where+ y1 = f (min x1 x2)+ y2 = f (max x1 x2)++----------------------------------------------------------------+-- HUnit helpers+----------------------------------------------------------------++testAssertion :: String -> Bool -> Test+testAssertion str cont = testCase str $ HU.assertBool str cont
+ tests/Tests/Math.hs view
@@ -0,0 +1,159 @@+{-# LANGUAGE ViewPatterns #-}+-- | Tests for Statistics.Math+module Tests.Math (+ mathTests+ ) where++import Data.Vector.Unboxed (fromList)+import qualified Data.Vector as V+import Data.Vector ((!))++import Test.QuickCheck hiding (choose)+import Test.Framework+import Test.Framework.Providers.QuickCheck2++import Tests.Helpers+import Tests.Math.Tables+import Statistics.Math+++mathTests :: Test+mathTests = testGroup "S.Math"+ [ testProperty "Γ(x+1) = x·Γ(x) logGamma" $ gammaReccurence logGamma 3e-8+ , testProperty "Γ(x+1) = x·Γ(x) logGammaL" $ gammaReccurence logGammaL 2e-13+ , testProperty "γ(1,x) = 1 - exp(-x)" $ incompleteGammaAt1Check+ , testProperty "γ - increases" $+ \s x y -> s > 0 && x > 0 && y > 0 ==> monotonicallyIncreases (incompleteGamma s) x y+ , testProperty "invIncompleteGamma = γ^-1" $ invIGammaIsInverse+ , testProperty "invIncompleteBeta = B^-1" $ invIBetaIsInverse+ , chebyshevTests+ -- Unit tests+ , testAssertion "Factorial is expected to be precise at 1e-15 level"+ $ and [ eq 1e-15 (factorial (fromIntegral n))+ (fromIntegral (factorial' n))+ |n <- [0..170]]+ , testAssertion "Log factorial is expected to be precise at 1e-15 level"+ $ and [ eq 1e-15 (logFactorial (fromIntegral n))+ (log $ fromIntegral $ factorial' n)+ | n <- [2..170]]+ , testAssertion "logGamma is expected to be precise at 1e-9 level [integer points]"+ $ and [ eq 1e-9 (logGamma (fromIntegral n))+ (logFactorial (n-1))+ | n <- [3..10000]]+ , testAssertion "logGamma is expected to be precise at 1e-9 level [fractional points]"+ $ and [ eq 1e-9 (logGamma x) lg | (x,lg) <- tableLogGamma ]+ , testAssertion "logGammaL is expected to be precise at 1e-15 level"+ $ and [ eq 1e-15 (logGammaL (fromIntegral n))+ (logFactorial (n-1))+ | n <- [3..10000]]+ , testAssertion "logGammaL is expected to be precise at 1e-9 level [fractional points]"+ $ and [ eq 1e-10 (logGammaL x) lg | (x,lg) <- tableLogGamma ]+ , testAssertion "logBeta is expected to be precise at 1e-6 level"+ $ and [ eq 1e-6 (logBeta p q)+ (logGammaL p + logGammaL q - logGammaL (p+q))+ | p <- [0.1,0.2 .. 0.9] ++ [2 .. 20]+ , q <- [0.1,0.2 .. 0.9] ++ [2 .. 20]]+ -- FIXME: Why 1e-8? Is it due to poor precision of logBeta?+ , testAssertion "incompleteBeta is expected to be precise at 1e-8 level"+ $ and [ eq 1e-8 (incompleteBeta p q x) ib | (p,q,x,ib) <- tableIncompleteBeta ]+ , testAssertion "choose is expected to precise at 1e-12 level"+ $ and [ eq 1e-12 (choose (fromIntegral n) (fromIntegral k)) (fromIntegral $ choose' n k)+ | n <- [0..300], k <- [0..n]]+ ]++----------------------------------------------------------------+-- QC tests+----------------------------------------------------------------++-- Γ(x+1) = x·Γ(x)+gammaReccurence :: (Double -> Double) -> Double -> Double -> Property+gammaReccurence logG ε x =+ (x > 0 && x < 100) ==> (abs (g2 - g1 - log x) < ε)+ where+ g1 = logG x+ g2 = logG (x+1)+++-- γ(1,x) = 1 - exp(-x)+-- Since Γ(1) = 1 normalization doesn't make any difference+incompleteGammaAt1Check :: Double -> Property+incompleteGammaAt1Check x =+ x > 0 ==> (incompleteGamma 1 x + exp(-x)) ≈ 1+ where+ (≈) = eq 1e-13++-- invIncompleteGamma is inverse of incompleteGamma+invIGammaIsInverse :: Double -> Double -> Property+invIGammaIsInverse (abs -> a) (abs . snd . properFraction -> p) =+ a > 0 && p > 0 && p < 1 ==> ( printTestCase ("x = " ++ show x )+ $ printTestCase ("p' = " ++ show p')+ $ printTestCase ("Δp = " ++ show (p - p'))+ $ abs (p - p') <= 1e-12+ )+ where+ x = invIncompleteGamma a p+ p' = incompleteGamma a x++-- invIncompleteBeta is inverse of incompleteBeta+invIBetaIsInverse :: Double -> Double -> Double -> Property+invIBetaIsInverse (abs -> p) (abs -> q) (abs . snd . properFraction -> x) =+ p > 0 && q > 0 ==> ( printTestCase ("p = " ++ show p )+ $ printTestCase ("q = " ++ show q )+ $ printTestCase ("x = " ++ show x )+ $ printTestCase ("x' = " ++ show x')+ $ printTestCase ("a = " ++ show a) + $ printTestCase ("err = " ++ (show $ abs $ (x - x') / x))+ $ abs (x - x') <= 1e-12+ )+ where+ x' = incompleteBeta p q a+ a = invIncompleteBeta p q x+ +-- Test that Chebyshev polynomial of low order are evaluated correctly+chebyshevTests :: Test+chebyshevTests = testGroup "Chebyshev polynomials"+ [ testProperty "Chebyshev 0" $ \a0 (Ch x) ->+ (ch0 x * a0) ≈ (chebyshev x $ fromList [a0])+ , testProperty "Chebyshev 1" $ \a0 a1 (Ch x) ->+ (a0*ch0 x + a1*ch1 x) ≈ (chebyshev x $ fromList [a0,a1])+ , testProperty "Chebyshev 2" $ \a0 a1 a2 (Ch x) ->+ (a0*ch0 x + a1*ch1 x + a2*ch2 x) ≈ (chebyshev x $ fromList [a0,a1,a2])+ , testProperty "Chebyshev 3" $ \a0 a1 a2 a3 (Ch x) ->+ (a0*ch0 x + a1*ch1 x + a2*ch2 x + a3*ch3 x) ≈ (chebyshev x $ fromList [a0,a1,a2,a3])+ , testProperty "Chebyshev 4" $ \a0 a1 a2 a3 a4 (Ch x) ->+ (a0*ch0 x + a1*ch1 x + a2*ch2 x + a3*ch3 x + a4*ch4 x) ≈ (chebyshev x $ fromList [a0,a1,a2,a3,a4])+ ]+ where (≈) = eq 1e-12++-- Chebyshev polynomials of low order+ch0,ch1,ch2,ch3,ch4 :: Double -> Double+ch0 _ = 1+ch1 x = x+ch2 x = 2*x^2 - 1+ch3 x = 4*x^3 - 3*x+ch4 x = 8*x^4 - 8*x^2 + 1++newtype Ch = Ch Double+ deriving Show+instance Arbitrary Ch where+ arbitrary = do x <- arbitrary+ return $ Ch $ 2 * (snd . properFraction) x - 1++++----------------------------------------------------------------+-- Unit tests+----------------------------------------------------------------++-- Lookup table for fact factorial calculation. It has fixed size+-- which is bad but it's OK for this particular case+factorial_table :: V.Vector Integer+factorial_table = V.generate 2000 (\n -> product [1..fromIntegral n])++-- Exact implementation of factorial+factorial' :: Integer -> Integer+factorial' n = factorial_table ! fromIntegral n++-- Exact albeit slow implementation of choose+choose' :: Integer -> Integer -> Integer+choose' n k = factorial' n `div` (factorial' k * factorial' (n-k))
+ tests/Tests/Math/Tables.hs view
@@ -0,0 +1,47 @@+module Tests.Math.Tables where++tableLogGamma :: [(Double,Double)]+tableLogGamma =+ [(0.000001250000000, 13.592366285131769033)+ , (0.000068200000000, 9.5930266308318756785)+ , (0.000246000000000, 8.3100370767447966358)+ , (0.000880000000000, 7.03508133735248542)+ , (0.003120000000000, 5.768129358365567505)+ , (0.026700000000000, 3.6082588918892977148)+ , (0.077700000000000, 2.5148371858768232556)+ , (0.234000000000000, 1.3579557559432759994)+ , (0.860000000000000, 0.098146578027685615897)+ , (1.340000000000000, -0.11404757557207759189)+ , (1.890000000000000, -0.0425116422978701336)+ , (2.450000000000000, 0.25014296569217625565)+ , (3.650000000000000, 1.3701041997380685178)+ , (4.560000000000000, 2.5375143317949580002)+ , (6.660000000000000, 5.9515377269550207018)+ , (8.250000000000000, 9.0331869196051233217)+ , (11.300000000000001, 15.814180681373947834)+ , (25.600000000000001, 56.711261598328121636)+ , (50.399999999999999, 146.12815158702164808)+ , (123.299999999999997, 468.85500075897556371)+ , (487.399999999999977, 2526.9846647543727158)+ , (853.399999999999977, 4903.9359135978220365)+ , (2923.300000000000182, 20402.93198938705973)+ , (8764.299999999999272, 70798.268343590112636)+ , (12630.000000000000000, 106641.77264982508495)+ , (34500.000000000000000, 325976.34838781820145)+ , (82340.000000000000000, 849629.79603036714252)+ , (234800.000000000000000, 2668846.4390507959761)+ , (834300.000000000000000, 10540830.912557534873)+ , (1230000.000000000000000, 16017699.322315014899)+ ]+tableIncompleteBeta :: [(Double,Double,Double,Double)]+tableIncompleteBeta =+ [(2.000000000000000, 3.000000000000000, 0.030000000000000, 0.0051864299999999996862)+ , (2.000000000000000, 3.000000000000000, 0.230000000000000, 0.22845923000000001313)+ , (2.000000000000000, 3.000000000000000, 0.760000000000000, 0.95465728000000005249)+ , (4.000000000000000, 2.300000000000000, 0.890000000000000, 0.93829812158347802864)+ , (1.000000000000000, 1.000000000000000, 0.550000000000000, 0.55000000000000004441)+ , (0.300000000000000, 12.199999999999999, 0.110000000000000, 0.95063000053947077639)+ , (13.100000000000000, 9.800000000000001, 0.120000000000000, 1.3483109941962659385e-07)+ , (13.100000000000000, 9.800000000000001, 0.420000000000000, 0.071321857831804780226)+ , (13.100000000000000, 9.800000000000001, 0.920000000000000, 0.99999578339197081611)+ ]
+ tests/Tests/Math/gen.py view
@@ -0,0 +1,51 @@+#!/usr/bin/python+"""+"""++from mpmath import *++def printListLiteral(lines) :+ print " [" + "\n , ".join(lines) + "\n ]"++################################################################+# Generate header+print "module Tests.Math.Tables where"+print++################################################################+## Generate table for logGamma+print "tableLogGamma :: [(Double,Double)]"+print "tableLogGamma ="++gammaArg = [ 1.25e-6, 6.82e-5, 2.46e-4, 8.8e-4, 3.12e-3, 2.67e-2,+ 7.77e-2, 0.234, 0.86, 1.34, 1.89, 2.45,+ 3.65, 4.56, 6.66, 8.25, 11.3, 25.6,+ 50.4, 123.3, 487.4, 853.4, 2923.3, 8764.3,+ 1.263e4, 3.45e4, 8.234e4, 2.348e5, 8.343e5, 1.23e6,+ ]+printListLiteral(+ [ '(%.15f, %.20g)' % (x, log(gamma(x))) for x in gammaArg ]+ )+++################################################################+## Generate table for incompleteBeta++print "tableIncompleteBeta :: [(Double,Double,Double,Double)]"+print "tableIncompleteBeta ="++incompleteBetaArg = [+ (2, 3, 0.03),+ (2, 3, 0.23),+ (2, 3, 0.76),+ (4, 2.3, 0.89),+ (1, 1, 0.55),+ (0.3, 12.2, 0.11),+ (13.1, 9.8, 0.12),+ (13.1, 9.8, 0.42),+ (13.1, 9.8, 0.92),+ ]+printListLiteral(+ [ '(%.15f, %.15f, %.15f, %.20g)' % (p,q,x, betainc(p,q,0,x, regularized=True))+ for (p,q,x) in incompleteBetaArg+ ])
+ tests/Tests/NonparametricTest.hs view
@@ -0,0 +1,148 @@+-- Tests for Statistics.Test.NonParametric+module Tests.NonparametricTest (+ nonparametricTests+ ) where+++import qualified Data.Vector.Unboxed as U+import Test.HUnit (Test(..),assertEqual,assertBool)+import qualified Test.Framework as TF+import Test.Framework.Providers.HUnit++import Statistics.Test.MannWhitneyU+import Statistics.Test.WilcoxonT+++++nonparametricTests :: TF.Test+nonparametricTests = TF.testGroup "Nonparametric tests"+ $ hUnitTestToTests =<< concat [ mannWhitneyTests+ , wilcoxonSumTests+ , wilcoxonPairTests+ ]+++----------------------------------------------------------------++mannWhitneyTests :: [Test]+mannWhitneyTests = zipWith test [(0::Int)..] testData +++ [TestCase $ assertEqual "Mann-Whitney U Critical Values, m=1"+ (replicate (20*3) Nothing)+ [mannWhitneyUCriticalValue (1,x) p | x <- [1..20], p <- [0.005,0.01,0.025]]+ ,TestCase $ assertEqual "Mann-Whitney U Critical Values, m=2, p=0.025"+ (replicate 7 Nothing ++ map Just [0,0,0,0,1,1,1,1,1,2,2,2,2])+ [mannWhitneyUCriticalValue (2,x) 0.025 | x <- [1..20]]+ ,TestCase $ assertEqual "Mann-Whitney U Critical Values, m=6, p=0.05"+ (replicate 1 Nothing ++ map Just [0, 2,3,5,7,8,10,12,14,16,17,19,21,23,25,26,28,30,32])+ [mannWhitneyUCriticalValue (6,x) 0.05 | x <- [1..20]]+ ,TestCase $ assertEqual "Mann-Whitney U Critical Values, m=20, p=0.025"+ (replicate 1 Nothing ++ map Just [2,8,14,20,27,34,41,48,55,62,69,76,83,90,98,105,112,119,127])+ [mannWhitneyUCriticalValue (20,x) 0.025 | x <- [1..20]]+ ]+ where+ test n (a, b, c, d)+ = TestCase $ do assertEqual ("Mann-Whitney U " ++ show n) c us+ assertEqual ("Mann-Whitney U Sig " ++ show n)+ d $ mannWhitneyUSignificant TwoTailed (length a, length b) 0.05 us+ where+ us = mannWhitneyU (U.fromList a) (U.fromList b)++ -- List of (Sample A, Sample B, (Positive Rank, Negative Rank))+ testData :: [([Double], [Double], (Double, Double), Maybe TestResult)]+ testData = [ ( [3,4,2,6,2,5]+ , [9,7,5,10,6,8]+ , (2, 34)+ , Just Significant+ )+ , ( [540,480,600,590,605]+ , [760,890,1105,595,940]+ , (2, 23)+ , Just Significant+ )+ , ( [19,22,16,29,24]+ , [20,11,17,12]+ , (17, 3)+ , Just NotSignificant+ )+ , ( [126,148,85,61, 179,93, 45,189,85,93]+ , [194,128,69,135,171,149,89,248,79,137]+ , (35,65)+ , Just NotSignificant+ )+ , ( [1..30]+ , [1..30]+ , (450,450)+ , Just NotSignificant+ )+ , ( [1 .. 30]+ , [11.5 .. 40 ]+ , (190.0,710.0)+ , Just Significant+ )+ ]++wilcoxonSumTests :: [Test]+wilcoxonSumTests = zipWith test [(0::Int)..] testData+ where+ test n (a, b, c) = TestCase $ assertEqual ("Wilcoxon Sum " ++ show n) c (wilcoxonRankSums (U.fromList a) (U.fromList b))++ -- List of (Sample A, Sample B, (Positive Rank, Negative Rank))+ testData :: [([Double], [Double], (Double, Double))]+ testData = [ ( [8.50,9.48,8.65,8.16,8.83,7.76,8.63]+ , [8.27,8.20,8.25,8.14,9.00,8.10,7.20,8.32,7.70]+ , (75, 61)+ )+ , ( [0.45,0.50,0.61,0.63,0.75,0.85,0.93]+ , [0.44,0.45,0.52,0.53,0.56,0.58,0.58,0.65,0.79]+ , (71.5, 64.5)+ )+ ]++wilcoxonPairTests :: [Test]+wilcoxonPairTests = zipWith test [(0::Int)..] testData +++ -- Taken from the Mitic paper:+ [ TestCase $ assertBool "Sig 16, 35" (to4dp 0.0467 $ wilcoxonMatchedPairSignificance 16 35)+ , TestCase $ assertBool "Sig 16, 36" (to4dp 0.0523 $ wilcoxonMatchedPairSignificance 16 36)+ , TestCase $ assertEqual "Wilcoxon critical values, p=0.05"+ (replicate 4 Nothing ++ map Just [0,2,3,5,8,10,13,17,21,25,30,35,41,47,53,60,67,75,83,91,100,110,119])+ [wilcoxonMatchedPairCriticalValue x 0.05 | x <- [1..27]]+ , TestCase $ assertEqual "Wilcoxon critical values, p=0.025"+ (replicate 5 Nothing ++ map Just [0,2,3,5,8,10,13,17,21,25,29,34,40,46,52,58,65,73,81,89,98,107])+ [wilcoxonMatchedPairCriticalValue x 0.025 | x <- [1..27]]+ , TestCase $ assertEqual "Wilcoxon critical values, p=0.01"+ (replicate 6 Nothing ++ map Just [0,1,3,5,7,9,12,15,19,23,27,32,37,43,49,55,62,69,76,84,92])+ [wilcoxonMatchedPairCriticalValue x 0.01 | x <- [1..27]]+ , TestCase $ assertEqual "Wilcoxon critical values, p=0.005"+ (replicate 7 Nothing ++ map Just [0,1,3,5,7,9,12,15,19,23,27,32,37,42,48,54,61,68,75,83])+ [wilcoxonMatchedPairCriticalValue x 0.005 | x <- [1..27]]+ ]+ where+ test n (a, b, c) = TestCase $ assertEqual ("Wilcoxon Paired " ++ show n) c (wilcoxonMatchedPairSignedRank (U.fromList a) (U.fromList b))++ -- List of (Sample A, Sample B, (Positive Rank, Negative Rank))+ testData :: [([Double], [Double], (Double, Double))]+ testData = [ ([1..10], [1..10], (0, 0 ))+ , ([1..5], [6..10], (0, 5*(-3)))+ -- Worked example from the Internet:+ , ( [125,115,130,140,140,115,140,125,140,135]+ , [110,122,125,120,140,124,123,137,135,145]+ , ( sum $ filter (> 0) [7,-3,1.5,9,0,-4,8,-6,1.5,-5]+ , sum $ filter (< 0) [7,-3,1.5,9,0,-4,8,-6,1.5,-5]+ )+ )+ -- Worked examples from books/papers:+ , ( [2.4,1.9,2.3,1.9,2.4,2.5]+ , [2.0,2.1,2.0,2.0,1.8,2.0]+ , (18, -3)+ )+ , ( [130,170,125,170,130,130,145,160]+ , [120,163,120,135,143,136,144,120]+ , (27, -9)+ )+ , ( [540,580,600,680,430,740,600,690,605,520]+ , [760,710,1105,880,500,990,1050,640,595,520]+ , (3, -42)+ )+ ]+ to4dp tgt x = x >= tgt - 0.00005 && x < tgt + 0.00005
+ tests/Tests/Transform.hs view
@@ -0,0 +1,82 @@+module Tests.Transform+ (+ tests+ ) where++import Data.Bits ((.&.), shiftL)+import Data.Complex (Complex((:+)))+import Data.Functor ((<$>))+import Statistics.Function (within)+import Statistics.Transform++import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)+import Test.QuickCheck (Positive(..),Property,choose,vectorOf,+ arbitrary,printTestCase)+import qualified Data.Vector.Generic as G+import qualified Data.Vector.Unboxed as U++import Tests.Helpers++++tests :: Test+tests = testGroup "fft" [+ testProperty "t_impulse" t_impulse+ , testProperty "t_impulse_offset" t_impulse_offset+ , testProperty "ifft . fft = id" (t_fftInverse $ ifft . fft)+ , testProperty "fft . ifft = id" (t_fftInverse $ fft . ifft)+ ]++-- A single real-valued impulse at the beginning of an otherwise zero+-- vector should be replicated in every real component of the result,+-- and all the imaginary components should be zero.+t_impulse :: Double -> Positive Int -> Bool+t_impulse k (Positive m) = G.all (c_near i) (fft v)+ where v = i `G.cons` G.replicate (n-1) 0+ i = k :+ 0+ n = 1 `shiftL` (m .&. 6)++-- If a real-valued impulse is offset from the beginning of an+-- otherwise zero vector, the sum-of-squares of each component of the+-- result should equal the square of the impulse.+t_impulse_offset :: Double -> Positive Int -> Positive Int -> Bool+t_impulse_offset k (Positive x) (Positive m) = G.all ok (fft v)+ where v = G.concat [G.replicate xn 0, G.singleton i, G.replicate (n-xn-1) 0]+ ok (re :+ im) = within ulps (re*re + im*im) (k*k)+ i = k :+ 0+ xn = x `rem` n+ n = 1 `shiftL` (m .&. 6)++-- Test that (ifft . fft ≈ id)+--+-- Approximate equality here is tricky. Smaller values of vector tend+-- to have large relative error. Thus we should test that vectors as+-- whole are approximate equal.+t_fftInverse :: (U.Vector CD -> U.Vector CD) -> Property+t_fftInverse roundtrip = do+ n <- (2^) <$> choose (0,9::Int) -- Size of vector+ x <- G.fromList <$> vectorOf n arbitrary -- Vector to transform+ let x' = roundtrip x+ id $ printTestCase "Original vector"+ $ printTestCase (show x)+ $ printTestCase "Transformed one"+ $ printTestCase (show x)+ $ printTestCase (show n)+ $ vectorNorm (U.zipWith (-) x x') <= 1e-15 * vectorNorm x+++----------------------------------------------------------------++-- With an error tolerance of 8 ULPs, a million QuickCheck tests are+-- likely to all succeed. With a tolerance of 7, we fail around the+-- half million mark.+ulps :: Int+ulps = 8++c_near :: CD -> CD -> Bool+c_near (a :+ b) (c :+ d) = within ulps a c && within ulps b d++-- Norm of vector+vectorNorm :: U.Vector CD -> Double+vectorNorm = sqrt . U.sum . U.map (\(x :+ y) -> x*x + y*y)