packages feed

statistics 0.10.1.0 → 0.10.2.0

raw patch · 27 files changed

+1117/−66 lines, 27 filesdep ~math-functionsPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: math-functions

API changes (from Hackage documentation)

+ Statistics.Distribution: genContinous :: (ContDistr d, PrimMonad m) => d -> Gen (PrimState m) -> m Double
+ Statistics.Distribution.Beta: bdAlpha :: BetaDistribution -> Double
+ Statistics.Distribution.Beta: bdBeta :: BetaDistribution -> Double
+ Statistics.Distribution.Beta: betaDistr :: Double -> Double -> BetaDistribution
+ Statistics.Distribution.Beta: data BetaDistribution
+ Statistics.Distribution.Beta: improperBetaDistr :: Double -> Double -> BetaDistribution
+ Statistics.Distribution.Beta: instance ContDistr BetaDistribution
+ Statistics.Distribution.Beta: instance ContGen BetaDistribution
+ Statistics.Distribution.Beta: instance Distribution BetaDistribution
+ Statistics.Distribution.Beta: instance Eq BetaDistribution
+ Statistics.Distribution.Beta: instance MaybeMean BetaDistribution
+ Statistics.Distribution.Beta: instance MaybeVariance BetaDistribution
+ Statistics.Distribution.Beta: instance Mean BetaDistribution
+ Statistics.Distribution.Beta: instance Read BetaDistribution
+ Statistics.Distribution.Beta: instance Show BetaDistribution
+ Statistics.Distribution.Beta: instance Typeable BetaDistribution
+ Statistics.Distribution.Beta: instance Variance BetaDistribution
+ Statistics.Distribution.CauchyLorentz: instance ContGen CauchyDistribution
+ Statistics.Distribution.ChiSquared: instance ContGen ChiSquared
+ Statistics.Distribution.Exponential: instance ContGen ExponentialDistribution
+ Statistics.Distribution.FDistribution: instance ContGen FDistribution
+ Statistics.Distribution.Gamma: improperGammaDistr :: Double -> Double -> GammaDistribution
+ Statistics.Distribution.Gamma: instance ContGen GammaDistribution
+ Statistics.Distribution.StudentT: instance ContGen StudentT
+ Statistics.Distribution.Uniform: uniformA :: UniformDistribution -> Double
+ Statistics.Distribution.Uniform: uniformB :: UniformDistribution -> Double
- Statistics.Distribution: class Distribution d
+ Statistics.Distribution: class Distribution d where complCumulative d x = 1 - cumulative d x
- Statistics.Distribution: class MaybeMean d => MaybeVariance d
+ Statistics.Distribution: class MaybeMean d => MaybeVariance d where maybeVariance d = (*) <$> x <*> x where x = maybeStdDev d maybeStdDev = fmap sqrt . maybeVariance
- Statistics.Distribution: class (Mean d, MaybeVariance d) => Variance d
+ Statistics.Distribution: class (Mean d, MaybeVariance d) => Variance d where variance d = x * x where x = stdDev d stdDev = sqrt . variance

Files

README.markdown view
@@ -14,21 +14,6 @@ libraries and applications that use this library using a high level of optimisation. -Suggested GHC options:--    -O -funbox-strict-fields--To illustrate, here are the times (in seconds) to generate and sum 250-million random Word32 values, on a laptop with a 2.4GHz Core2 Duo-P8600 processor, running Fedora 11 and GHC 6.10.3:--    no flags   200+-    -O           1.249-    -O -fvia-C   0.991--As the numbers above suggest, compiling without optimisation will-yield unacceptable performance.-  # Get involved! 
Statistics/Distribution.hs view
@@ -24,6 +24,7 @@       -- ** Random number generation     , ContGen(..)     , DiscreteGen(..)+    , genContinous       -- * Helper functions     , findRoot     , sumProbabilities@@ -123,7 +124,13 @@ class (DiscreteDistr d, ContGen d) => DiscreteGen d where   genDiscreteVar :: PrimMonad m => d -> Gen (PrimState m) -> m Int -+-- | Generate variates from continous distribution using inverse+--   transform rule.+genContinous :: (ContDistr d, PrimMonad m) => d -> Gen (PrimState m) -> m Double+genContinous d gen = do+  x <- uniform gen+  return $! quantile d x+{-# INLINE genContinous #-}  data P = P {-# UNPACK #-} !Double {-# UNPACK #-} !Double 
+ Statistics/Distribution/Beta.hs view
@@ -0,0 +1,98 @@+{-# LANGUAGE DeriveDataTypeable #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Statistics.Distribution.Beta+-- Copyright   :  (C) 2012 Edward Kmett,+-- License     :  BSD-style (see the file LICENSE)+--+-- Maintainer  :  Edward Kmett <ekmett@gmail.com>+-- Stability   :  provisional+-- Portability :  DeriveDataTypeable+--+----------------------------------------------------------------------------+module Statistics.Distribution.Beta+  ( BetaDistribution+    -- * Constructor+  , betaDistr+  , improperBetaDistr+    -- * Accessors+  , bdAlpha+  , bdBeta+  ) where++import Numeric.SpecFunctions           (incompleteBeta, invIncompleteBeta, logBeta)+import Numeric.MathFunctions.Constants (m_NaN)+import qualified Statistics.Distribution as D+import Data.Typeable++-- | The beta distribution+data BetaDistribution = BD+ { bdAlpha :: {-# UNPACK #-} !Double+   -- ^ Alpha shape parameter+ , bdBeta  :: {-# UNPACK #-} !Double+   -- ^ Beta shape parameter+ } deriving (Eq,Read,Show,Typeable)++-- | Create beta distribution. Both shape parameters must be positive.+betaDistr :: Double             -- ^ Shape parameter alpha+          -> Double             -- ^ Shape parameter beta+          -> BetaDistribution+betaDistr a b+  | a > 0 && b > 0 = improperBetaDistr a b+  | otherwise      =+      error $  "Statistics.Distribution.Beta.betaDistr: "+            ++ "shape parameters must be positive. Got a = "+            ++ show a+            ++ " b = "+            ++ show b+{-# INLINE betaDistr #-}++-- | Create beta distribution. This construtor doesn't check parameters.+improperBetaDistr :: Double             -- ^ Shape parameter alpha+                  -> Double             -- ^ Shape parameter beta+                  -> BetaDistribution+improperBetaDistr = BD+{-# INLINE improperBetaDistr #-}++instance D.Distribution BetaDistribution where+  cumulative (BD a b) x+    | x <= 0    = 0+    | x >= 1    = 1+    | otherwise = incompleteBeta a b x+  {-# INLINE cumulative #-}++instance D.Mean BetaDistribution where+  mean (BD a b) = a / (a + b)+  {-# INLINE mean #-}++instance D.MaybeMean BetaDistribution where+  maybeMean = Just . D.mean+  {-# INLINE maybeMean #-}++instance D.Variance BetaDistribution where+  variance (BD a b) = a*b / (apb*apb*(apb+1))+    where apb = a + b+  {-# INLINE variance #-}++instance D.MaybeVariance BetaDistribution where+  maybeVariance = Just . D.variance+  {-# INLINE maybeVariance #-}++instance D.ContDistr BetaDistribution where+  density (BD a b) x+   | a <= 0 || b <= 0 = m_NaN+   | x <= 0 = 0+   | x >= 1 = 0+   | otherwise = exp $ (a-1)*log x + (b-1)*log (1-x) - logBeta a b+  {-# INLINE density #-}++  quantile (BD a b) p+    | p == 0         = 0+    | p == 1         = 1+    | p > 0 && p < 1 = invIncompleteBeta a b p+    | otherwise      =+        error $ "Statistics.Distribution.Gamma.quantile: p must be in [0,1] range. Got: "++show p+  {-# INLINE quantile #-}++instance D.ContGen BetaDistribution where+  genContVar = D.genContinous
Statistics/Distribution/Binomial.hs view
@@ -85,13 +85,13 @@ {-# INLINE variance #-}  -- | Construct binomial distribution. Number of trials must be---   positive and probability must be in [0,1] range+--   non-negative and probability must be in [0,1] range binomial :: Int                 -- ^ Number of trials.          -> Double              -- ^ Probability.          -> BinomialDistribution binomial n p -  | n <= 0         = -    error $ msg ++ "number of trials must be positive. Got " ++ show n+  | n < 0          =+    error $ msg ++ "number of trials must be non-negative. Got " ++ show n   | p < 0 || p > 1 =      error $ msg++"probability must be in [0,1] range. Got " ++ show p   | otherwise      = BD n p
Statistics/Distribution/CauchyLorentz.hs view
@@ -63,3 +63,6 @@     | p == 1         =  1 / 0     | otherwise      =       error $ "Statistics.Distribution.CauchyLorentz..quantile: p must be in [0,1] range. Got: "++show p++instance D.ContGen CauchyDistribution where+  genContVar = D.genContinous
Statistics/Distribution/ChiSquared.hs view
@@ -21,7 +21,8 @@ import Data.Typeable         (Typeable) import Numeric.SpecFunctions (incompleteGamma,invIncompleteGamma,logGamma) -import qualified Statistics.Distribution as D+import qualified Statistics.Distribution         as D+import qualified System.Random.MWC.Distributions as MWC   -- | Chi-squared distribution@@ -63,6 +64,10 @@ instance D.MaybeVariance ChiSquared where     maybeStdDev   = Just . D.stdDev     maybeVariance = Just . D.variance++instance D.ContGen ChiSquared where+    genContVar (ChiSquared n) = MWC.chiSquare n+  cumulative :: ChiSquared -> Double -> Double cumulative chi x
Statistics/Distribution/Exponential.hs view
@@ -24,8 +24,9 @@     ) where  import Data.Typeable (Typeable)-import qualified Statistics.Distribution as D-import qualified Statistics.Sample as S+import qualified Statistics.Distribution         as D+import qualified Statistics.Sample               as S+import qualified System.Random.MWC.Distributions as MWC import Statistics.Types (Sample)  newtype ExponentialDistribution = ED {@@ -54,6 +55,9 @@ instance D.MaybeVariance ExponentialDistribution where     maybeStdDev   = Just . D.stdDev     maybeVariance = Just . D.variance++instance D.ContGen ExponentialDistribution where+  genContVar = MWC.exponential . edLambda  cumulative :: ExponentialDistribution -> Double -> Double cumulative (ED l) x | x <= 0    = 0
Statistics/Distribution/FDistribution.hs view
@@ -22,7 +22,7 @@   --- | Student-T distribution+-- | F distribution data FDistribution = F { fDistributionNDF1 :: {-# UNPACK #-} !Double                        , fDistributionNDF2 :: {-# UNPACK #-} !Double                        , _pdfFactor        :: {-# UNPACK #-} !Double@@ -74,6 +74,9 @@   maybeStdDev (F n m _)      | m > 4     = Just $ 2 * sqr m * (m + n - 2) / (n * sqr (m - 2) * (m - 4))     | otherwise = Nothing++instance D.ContGen FDistribution where+  genContVar = D.genContinous  sqr :: Double -> Double sqr x = x * x
Statistics/Distribution/Gamma.hs view
@@ -19,6 +19,7 @@       GammaDistribution     -- * Constructors     , gammaDistr+    , improperGammaDistr     -- * Accessors     , gdShape     , gdScale@@ -27,8 +28,9 @@ import Data.Typeable (Typeable) import Numeric.MathFunctions.Constants (m_pos_inf, m_NaN) import Numeric.SpecFunctions           (incompleteGamma, invIncompleteGamma)-import Statistics.Distribution.Poisson.Internal as Poisson-import qualified Statistics.Distribution        as D+import Statistics.Distribution.Poisson.Internal  as Poisson+import qualified Statistics.Distribution         as D+import qualified System.Random.MWC.Distributions as MWC  -- | The gamma distribution. data GammaDistribution = GD {@@ -44,10 +46,18 @@ gammaDistr k theta   | k     <= 0 = error $ msg ++ "shape must be positive. Got " ++ show k   | theta <= 0 = error $ msg ++ "scale must be positive. Got " ++ show theta-  | otherwise  = GD k theta+  | otherwise  = improperGammaDistr k theta     where msg = "Statistics.Distribution.Gamma.gammaDistr: " {-# INLINE gammaDistr #-} +-- | Create gamma distribution. This constructor do not check whether+--   parameters are valid+improperGammaDistr :: Double            -- ^ Shape parameter. /k/+                   -> Double            -- ^ Scale parameter, &#977;.+                   -> GammaDistribution+improperGammaDistr = GD+{-# INLINE improperGammaDistr #-}+ instance D.Distribution GammaDistribution where     cumulative = cumulative @@ -70,6 +80,8 @@     maybeStdDev   = Just . D.stdDev     maybeVariance = Just . D.variance +instance D.ContGen GammaDistribution where+    genContVar (GD a l) = MWC.gamma a l   density :: GammaDistribution -> Double -> Double
Statistics/Distribution/Geometric.hs view
@@ -59,10 +59,9 @@ geometric :: Double                -- ^ Success rate           -> GeometricDistribution geometric x-  | x < 0 || x > 1 = +  | x >= 0 && x <= 1 = GD x+  | otherwise        =     error $ "Statistics.Distribution.Geometric.geometric: probability must be in [0,1] range. Got " ++ show x-  | otherwise      =-    GD x {-# INLINE geometric #-}  probability :: GeometricDistribution -> Int -> Double
Statistics/Distribution/Normal.hs view
@@ -57,8 +57,8 @@     stdDev = stdDev  instance D.ContGen NormalDistribution where-    genContVar d gen = do x <- MWC.standard gen-                          return $! stdDev d * (x - mean d)+    genContVar d = MWC.normal (mean d) (stdDev d)+    {-# INLINE genContVar #-}  -- | Standard normal distribution with mean equal to 0 and variance equal to 1 standard :: NormalDistribution
Statistics/Distribution/Poisson.hs view
@@ -63,9 +63,9 @@ -- | Create Poisson distribution. poisson :: Double -> PoissonDistribution poisson l-  | l <= 0    = error $ "Statistics.Distribution.Poisson.poisson:\-                        \ lambda must be positive. Got " ++ show l-  | otherwise = PD l+  | l >=  0   = PD l+  | otherwise = error $ "Statistics.Distribution.Poisson.poisson:\+                        \ lambda must be non-negative. Got " ++ show l {-# INLINE poisson #-}  -- $references
Statistics/Distribution/StudentT.hs view
@@ -69,3 +69,6 @@ instance D.MaybeVariance StudentT where   maybeStdDev (StudentT ndf) | ndf > 2   = Just $ ndf / (ndf - 2)                              | otherwise = Nothing++instance D.ContGen StudentT where+  genContVar = D.genContinous
Statistics/Distribution/Uniform.hs view
@@ -9,19 +9,26 @@ -- Portability : portable -- -- Variate distributed uniformly in the interval.-module Statistics.Distribution.Uniform (-    UniformDistribution-  , uniformDistr-  ) where+module Statistics.Distribution.Uniform+    (+      UniformDistribution+    -- * Constructors+    , uniformDistr+    -- ** Accessors+    , uniformA+    , uniformB+    ) where  import Data.Typeable (Typeable) import qualified Statistics.Distribution as D import qualified System.Random.MWC       as MWC  --- | Uniform distribution-data UniformDistribution = UniformDistribution {-# UNPACK #-} !Double {-# UNPACK #-} !Double-                           deriving (Eq,Show,Read,Typeable)+-- | Uniform distribution from A to B+data UniformDistribution = UniformDistribution {+      uniformA :: {-# UNPACK #-} !Double -- ^ Low boundary of distribution+    , uniformB :: {-# UNPACK #-} !Double -- ^ Upper boundary of distribution+    } deriving (Eq, Read, Show, Typeable)  -- | Create uniform distribution. uniformDistr :: Double -> Double -> UniformDistribution
Statistics/Sample/Histogram.hs view
@@ -19,6 +19,7 @@     , range     ) where +import Numeric.MathFunctions.Constants (m_epsilon) import Statistics.Function (minMax) import qualified Data.Vector.Generic as G import qualified Data.Vector.Generic.Mutable as GM@@ -72,7 +73,7 @@          GM.write bins b . (+1) =<< GM.read bins b          go (i+1)        len = G.length xs-       d = (hi - lo) / fromIntegral numBins+       d = ((hi - lo) * (1 + realToFrac m_epsilon)) / fromIntegral numBins {-# INLINE histogram_ #-}  -- | /O(n)/ Compute decent defaults for the lower and upper bounds of
Statistics/Sample/KernelDensity.hs view
@@ -52,7 +52,8 @@ kde n0 xs = kde_ n0 (lo - range / 10) (hi + range / 10) xs   where     (lo,hi) = minMax xs-    range = hi - lo+    range   | U.length xs <= 1 = 1       -- Unreasonable guess+            | otherwise        = hi - lo  -- | Gaussian kernel density estimator for one-dimensional data, using -- the method of Botev et al.@@ -72,19 +73,20 @@      -- ^ Upper bound (@max@) of the mesh range.      -> U.Vector Double -> (U.Vector Double, U.Vector Double) kde_ n0 min max xs+  | U.null xs = error "Statistics.KernelDensity.kde: empty sample"   | n0 < 1    = error "Statistics.KernelDensity.kde: invalid number of points"   | otherwise = (mesh, density)   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 (/(2 * 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))-    !n = fromIntegral ni+    !n  = fromIntegral ni     !ni = nextHighestPowerOfTwo n0-    !r = max - min-    a = dct . G.map (/ G.sum h) $ h-      where h = G.map (/ len) $ histogram_ ni min max xs-    !len = fromIntegral (G.length xs)+    !r  = max - min+    a   = dct . G.map (/ G.sum h) $ h+        where h = G.map (/ len) $ 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 ->               x - (len * (2 * sqrt pi) * go 6 (f 7 x)) ** (-0.4)       where@@ -94,10 +96,9 @@                 iv = G.map sqr $ G.enumFromTo 1 (n-1)         go s !h | s == 1    = h                 | otherwise = go (s-1) (f s time)-          where time = (2 * const * k0 / len / h) ** (2 / (3 + 2 * s))+          where time  = (2 * const * k0 / len / h) ** (2 / (3 + 2 * s))                 const = (1 + 0.5 ** (s+0.5)) / 3-                k0 = U.product (G.enumFromThenTo 1 3 (2*s-1)) / m_sqrt_2_pi-    _bandwidth = sqrt t_star * r+                k0    = U.product (G.enumFromThenTo 1 3 (2*s-1)) / m_sqrt_2_pi     sqr x = x * x  -- $references
Statistics/Test/WilcoxonT.hs view
@@ -74,7 +74,7 @@ -- all the coefficients by r down the list. -- -- This list will be processed lazily from the head.-coefficients :: Int -> [Int]+coefficients :: Int -> [Integer] coefficients 1 = [1, 1] -- 1 + x coefficients r = let coeffs = coefficients (r-1)                      (firstR, rest) = splitAt r coeffs@@ -86,7 +86,10 @@  -- This list will be processed lazily from the head. summedCoefficients :: Int -> [Double]-summedCoefficients = map fromIntegral . scanl1 (+) . coefficients+summedCoefficients n+  | n < 1     = error "Statistics.Test.WilcoxonT.summedCoefficients: nonpositive sample size"+  | n > 1023  = error "Statistics.Test.WilcoxonT.summedCoefficients: sample is too large (see bug #18)"+  | otherwise = map fromIntegral $ scanl1 (+) $ coefficients n  -- | Tests whether a given result from a Wilcoxon signed-rank matched-pairs test -- is significant at the given level.
Statistics/Transform.hs view
@@ -43,37 +43,50 @@  -- | Discrete cosine transform (DCT-II). dct :: U.Vector Double -> U.Vector Double-dct = dct_ . G.map (:+0)+dct = dctWorker . G.map (:+0) --- | Discrete cosine transform, with complex coefficients (DCT-II).+-- | Discrete cosine transform (DCT-II). Only real part of vector is+--   transformed, imaginary part is ignored. dct_ :: U.Vector CD -> U.Vector Double-dct_ xs = G.map realPart $ G.zipWith (*) weights (fft interleaved)+dct_ = dctWorker . G.map (\(i :+ _) -> i :+ 0)++dctWorker :: U.Vector CD -> U.Vector Double+dctWorker 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-    weights = G.cons 1 . G.generate (len-1) $ \x ->+    weights = G.cons 2 . G.generate (len-1) $ \x ->               2 * exp ((0:+(-1))*fi (x+1)*pi/(2*n))       where n = fi len     len = G.length xs ++ -- | Inverse discrete cosine transform (DCT-III). It's inverse of -- 'dct' only up to scale parameter: -- -- > (idct . dct) x = (* lenngth x) idct :: U.Vector Double -> U.Vector Double-idct = idct_ . G.map (:+0)+idct = idctWorker . G.map (:+0) --- | Inverse discrete cosine transform, with complex coefficients--- (DCT-III).+-- | Inverse discrete cosine transform (DCT-III). Only real part of vector is+--   transformed, imaginary part is ignored. idct_ :: U.Vector CD -> U.Vector Double-idct_ xs = G.generate len interleave+idct_ = idctWorker . G.map (\(i :+ _) -> i :+ 0)++idctWorker :: U.Vector CD -> U.Vector Double+idctWorker xs = G.generate len interleave   where     interleave z | even z    = vals `G.unsafeIndex` halve z                  | otherwise = vals `G.unsafeIndex` (len - halve z - 1)     vals = G.map realPart . ifft $ G.zipWith (*) weights xs-    weights = G.generate len $ \x -> n * exp ((0:+1)*fi x*pi/(2*n))+    weights +      = G.cons n+      $ G.generate (len - 1) $ \x -> 2 * n * exp ((0:+1) * fi (x+1) * pi/(2*n))       where n = fi len     len = G.length xs+  -- | Inverse fast Fourier transform. ifft :: U.Vector CD -> U.Vector CD
statistics.cabal view
@@ -1,5 +1,5 @@ name:           statistics-version:        0.10.1.0+version:        0.10.2.0 synopsis:       A library of statistical types, data, and functions description:   This library provides a number of common functions and types useful@@ -22,6 +22,26 @@   * Common statistical tests for significant differences between     samples.   .+  Changes in 0.10.2.0+  .+  * Bugs in DCT and IDCT are fixed.+  .+  * Accesors for uniform distribution are added.+  .+  * 'ContGen' instances for all continous distribtuions are added.+  .+  * Beta distribution is added.+  .+  * Constructor for improper gamma distribtuion is added.+  .+  * Binomial distribution allows zero trials.+  .+  * Poisson distribution now accept zero parameter.+  .+  * Integer overflow in caculation of Wilcoxon-T test is fixed.+  .+  * Bug in 'ContGen' instance for normal distribution is fixed.+  .   Changes in 0.10.1.0   .   * Kolmogorov-Smirnov nonparametric test added.@@ -135,6 +155,7 @@     Statistics.Autocorrelation     Statistics.Constants     Statistics.Distribution+    Statistics.Distribution.Beta     Statistics.Distribution.Binomial     Statistics.Distribution.CauchyLorentz     Statistics.Distribution.ChiSquared@@ -199,10 +220,11 @@   other-modules:     Tests.Distribution     Tests.Helpers-    Tests.Math-    Tests.Math.Tables+    Tests.Function     Tests.NonparametricTest+    Tests.NonparametricTest.Table     Tests.Transform+    Tests.KDE    ghc-options:     -Wall -threaded -rtsopts@@ -215,6 +237,7 @@     test-framework,     test-framework-quickcheck2,     test-framework-hunit,+    math-functions,     statistics,     primitive,     vector,
+ tests/Tests/Distribution.hs view
@@ -0,0 +1,284 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE ScopedTypeVariables  #-}+-- Required for Param+{-# LANGUAGE FlexibleInstances    #-}+{-# LANGUAGE OverlappingInstances #-}+{-# LANGUAGE ViewPatterns #-}+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.Beta+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 BetaDistribution        )+  , 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 (snd . properFraction -> 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+  = printTestCase (printf "CDF = %g" p1)+  $ printTestCase (printf "Sum = %g" p2)+  $ printTestCase (printf "Δ   = %g" (abs (p1 - p2)))+  $ 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  = n + (abs (a - b) `mod` 100)+    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 BetaDistribution where+  arbitrary = betaDistr <$> QC.choose (1e-3,10) <*> QC.choose (1e-3,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/Function.hs view
@@ -0,0 +1,26 @@+module Tests.Function ( tests ) where++import qualified Data.Vector.Unboxed as U+import           Data.Vector.Unboxed   ((!))++import Test.QuickCheck+import Test.Framework+import Test.Framework.Providers.QuickCheck2++import Statistics.Function++++tests :: Test+tests = testGroup "S.Function"+  [ testProperty "Sort is sort" p_sort+  ]+++p_sort :: [Double] -> Property+p_sort xs =+  not (null xs) ==> U.all (uncurry (<=)) (U.zip v $ U.tail v)+    where+      v = sort $ U.fromList xs++      
+ tests/Tests/Helpers.hs view
@@ -0,0 +1,100 @@+-- | Helpers for testing+module Tests.Helpers (+    -- * helpers+    T(..)+  , typeName+  , eq+  , eqC+  , (=~)+    -- * Generic QC tests+  , monotonicallyIncreases+  , monotonicallyIncreasesIEEE+    -- * HUnit helpers+  , testAssertion+  , testEquality+  ) 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 Numeric.MathFunctions.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++testEquality :: (Show a, Eq a) => String -> a -> a -> Test+testEquality msg a b = testCase msg $ HU.assertEqual msg a b
+ tests/Tests/KDE.hs view
@@ -0,0 +1,45 @@+-- | Tests for Kernel density estimates.+module Tests.KDE ( +  tests +  )where++import Data.Vector.Unboxed ((!))+import qualified Data.Vector.Unboxed as U++import Test.Framework                       (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)+import Test.QuickCheck                      +import Text.Printf++import Statistics.Sample.KernelDensity++++tests :: Test+tests = testGroup "KDE"+  [ testProperty "integral(PDF) == 1" t_densityIsPDF+  ]++t_densityIsPDF :: [Double] -> Property+t_densityIsPDF vec +  = not (null vec) ==> test+  where+    (xs,ys)  = kde 4096 (U.fromList vec)+    step     = (xs ! 1) - (xs ! 0)+    integral = integratePDF step ys+    --+    test = printTestCase (printf "Integral %f" integral)+         $ abs (1 - integral) <= 1e-3++          ++integratePDF :: Double -> U.Vector Double -> Double+integratePDF step vec +  = step * U.sum (U.zipWith (*) vec weights)+  where+    n       = U.length vec+    weights = U.generate n go+      where+        go i | i == 0    = 0.5+             | i == n-1  = 0.5+             | otherwise = 1
+ tests/Tests/NonparametricTest.hs view
@@ -0,0 +1,233 @@+-- Tests for Statistics.Test.NonParametric+module Tests.NonparametricTest (+  nonparametricTests+  ) where+++import qualified Data.Vector.Unboxed as U+import Test.HUnit                     (assertEqual)+import Test.Framework+import Test.Framework.Providers.HUnit++import Statistics.Test.MannWhitneyU+import Statistics.Test.WilcoxonT++import Tests.Helpers+import Tests.NonparametricTest.Table++import Statistics.Test.KolmogorovSmirnov+import Statistics.Distribution.Normal    (standard)++++nonparametricTests :: Test+nonparametricTests = testGroup "Nonparametric tests"+                   $ concat [ mannWhitneyTests+                            , wilcoxonSumTests+                            , wilcoxonPairTests+                            , kolmogorovSmirnovDTest+                            ]++----------------------------------------------------------------++mannWhitneyTests :: [Test]+mannWhitneyTests = zipWith test [(0::Int)..] testData +++  [ testEquality "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]]+  , testEquality "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]]+  , testEquality "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]]+  , testEquality "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 "Mann-Whitney" $ do+          assertEqual ("Mann-Whitney U "     ++ show n) c us+          assertEqual ("Mann-Whitney U Sig " ++ show n) d ss+      where+        us = mannWhitneyU (U.fromList a) (U.fromList b)+        ss = mannWhitneyUSignificant TwoTailed (length a, length b) 0.05 us+    -- 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 "Wilcoxon Sum"+                     $ 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:+  [ testAssertion "Sig 16, 35" (to4dp 0.0467 $ wilcoxonMatchedPairSignificance 16 35)+  , testAssertion "Sig 16, 36" (to4dp 0.0523 $ wilcoxonMatchedPairSignificance 16 36)+  , testEquality   "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]]+  , testEquality "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]]+  , testEquality "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]]+  , testEquality "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) = testEquality ("Wilcoxon Paired " ++ show n) c res+      where res = (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++++----------------------------------------------------------------+-- K-S test+----------------------------------------------------------------+++kolmogorovSmirnovDTest :: [Test]+kolmogorovSmirnovDTest =+  [ testAssertion "K-S D statistics" $+    and [ eq 1e-6 (kolmogorovSmirnovD standard (toU sample)) reference+        | (reference,sample) <- tableKSD+        ]+  , testAssertion "K-S 2-sample statistics" $+    and [ eq 1e-6 (kolmogorovSmirnov2D (toU xs) (toU ys)) reference+        | (reference,xs,ys) <- tableKS2D+        ]+  , testAssertion "K-S probability" $+    and [ eq 1e-14 (kolmogorovSmirnovProbability n d) p+        | (d,n,p) <- testData+        ]+  ]+  where+    toU = U.fromList+    -- Test data for the calculation of cumulative probability +    -- P(D[n] < d).+    -- +    -- Test data is:+    --    (D[n], n, p)+    -- Table is generated using sample program from paper+    testData :: [(Double,Int,Double)]+    testData = +      [ (0.09           ,    3, 0                   )+      , (0.2            ,    3, 0.00177777777777778 )+      , (0.301          ,    3, 0.116357025777778   )+      , (0.392          ,    3, 0.383127210666667   )+      , (0.5003         ,    3, 0.667366306558667   )+      , (0.604          ,    3, 0.861569877333333   )+      , (0.699          ,    3, 0.945458198         )+      , (0.802          ,    3, 0.984475216         )+      , (0.9            ,    3, 0.998               )+      , (0.09           ,    5, 0                   )+      , (0.2            ,    5, 0.0384              )+      , (0.301          ,    5, 0.33993786080016    )+      , (0.392          ,    5, 0.66931908083712    )+      , (0.5003         ,    5, 0.888397260183794   )+      , (0.604          ,    5, 0.971609957879808   )+      , (0.699          ,    5, 0.994331075994008   )+      , (0.802          ,    5, 0.999391366368064   )+      , (0.9            ,    5, 0.99998             )+      , (0.09           ,    8, 3.37615237575e-06   )+      , (0.2            ,    8, 0.151622071801758   )+      , (0.301          ,    8, 0.613891042670582   )+      , (0.392          ,    8, 0.871491561427005   )+      , (0.5003         ,    8, 0.977534089199071   )+      , (0.604          ,    8, 0.997473116268255   )+      , (0.699          ,    8, 0.999806082005123   )+      , (0.802          ,    8, 0.999995133786947   )+      , (0.9            ,    8, 0.99999998          )+      , (0.09           ,   10, 3.89639433093119e-05)+      , (0.2            ,   10, 0.25128096          )+      , (0.301          ,   10, 0.732913126355935   )+      , (0.392          ,   10, 0.932185254518767   )+      , (0.5003         ,   10, 0.992276179340446   )+      , (0.604          ,   10, 0.999495533516769   )+      , (0.699          ,   10, 0.999979691783985   )+      , (0.802          ,   10, 0.999999801409237   )+      , (0.09           ,   20, 0.00794502217168886 )+      , (0.2            ,   20, 0.647279826376584   )+      , (0.301          ,   20, 0.958017466965765   )+      , (0.392          ,   20, 0.997206424843499   )+      , (0.5003         ,   20, 0.999962641414228   )+      , (0.09           ,   30, 0.0498147538075168  )+      , (0.2            ,   30, 0.842030838984526   )+      , (0.301          ,   30, 0.993403560017612   )+      , (0.392          ,   30, 0.99988478803318    )+      , (0.09           ,  100, 0.629367974413669   )+      ]
+ tests/Tests/NonparametricTest/Table.hs view
@@ -0,0 +1,36 @@+module Tests.NonparametricTest.Table where++-- Table for Kolmogorov-Smirnov statistics for standard normal+-- distribution. Generated using R.+--+-- First element of tuple is D second is sample for which it was+-- calculated. +tableKSD :: [(Double,[Double])]+tableKSD = +  [ (0.2012078,[1.360645,-0.3151904,-1.245443,0.1741977,-0.1421206,-1.798246,1.171594,-1.335844,-5.050093e-2,1.030063,-1.849005,0.6491455,-0.7028004])+  , (0.2569956,[0.3884734,-1.227821,-0.4166262,0.429118,-0.9280124,0.8025867,-0.6703089,-0.2124872,0.1224496,0.1087734,-4.285284e-2,-1.039936,-0.7071956])+  , (0.1960356,[-1.814745,-0.6327167,0.7082493,0.6264716,1.02061,-0.4094635,0.821026,-0.4255047,-0.4820728,-0.2239833,0.648517,1.114283,0.3610216])+  , (0.2095746,[0.187011,0.1805498,0.4448389,0.6065506,0.2308673,0.5292549,-1.489902,-1.455191,0.5449396,-0.1436403,-0.7977073,-0.2693545,0.8260888,-1.474473,-2.158696e-2,-0.1455387])+  , (0.1922603,[0.5772317,-1.255561,1.605823,0.4923361,0.2470848,1.176101,-0.3767689,-0.6896885,0.4509345,-0.5048447,0.9436534,1.025599,0.2998393,-3.415219e-2,1.264315,-1.44433,-1.646449e-2])+  , (0.2173401,[1.812807,-0.8687497,-0.5710508,1.003647,1.142621,0.6546577,-6.083323e-3,1.628574e-2,1.067499,-1.953143,-0.6060077,1.90859,-0.7480553,0.6715162,-0.928759,1.862,1.604621,-0.2171044,-0.1835918])+  , (0.2510541,[-0.4769572,1.062319,0.9952284,1.198086,1.015589,-0.4154523,-0.6711762,1.202902,0.2217098,5.381759e-2,0.6679715,0.2551287,-0.1371492])+  , (0.1996022,[1.158607,-0.7354863,1.526559,-0.7855418,-2.82999,-0.6045106,-0.1830228,0.3306812,-0.819657,-1.223715,0.2536423,-0.4155781,1.447042])+  , (0.2284761,[1.239965,0.8187093,0.5199788,1.172072,0.748259,1.869376e-2,0.1625921,-1.712065,0.7043582,-1.702702,-0.4792806,-0.1023351,0.1187189])+  , (0.2337866,[0.9417261,-0.1024297,-0.7354359,1.099991,0.801984,-0.3745397,-1.749564,1.795771,1.099963,-0.605557,-2.035897,1.893603,-0.3468928,-0.2593938,2.100988,0.9665698,0.8757091,0.7696328,0.8730729,-0.3990352,2.04361,-0.4617864,-0.155021,2.15774,0.2687795,-0.9853512,-0.3264898,1.260026,4.267695,-0.5571145,0.6307067,-0.1691405,-1.730686])+  , (0.3389167,[2.025542,-1.542641,-1.090238,3.99027,9.949129e-2,-0.8974433,-2.508418,6.390346,-2.675515,1.154459,1.688072,2.220727,-0.4743102])+  , (0.4920231,[0.5192906,-3.260813,-1.245185,1.693084,3.561318,4.058924,2.27063,0.9446943,4.794159,-3.423733,0.8240817,0.644059,0.900175,1.932513,1.024586,2.82823,2.072192,-0.353231,-0.4319673,1.505952,1.0199,4.555054,2.364929,5.531467,3.279415,3.19821,2.726925,1.680027,-0.9041334,-0.8246765,-1.343979,8.454955,1.354581])+  , (0.6727408,[-6.705672,-3.193988,-4.612611,-3.207994,-5.070172,-6.141169,-0.397149,-4.093359,-1.204801,-3.986585,-2.724662,0.9868107,-6.295266,-5.95839,-6.35114,-1.679555,-2.635889,-4.050329,1.557428,-2.548465,-0.9073924,-1.502018,-4.535688,-4.158818,-8.833434,-5.944697,-1.569672,-4.70399,-7.832059,-4.093708,-8.393417,-2.085432,-7.06495,-0.4230419,-3.046822,-3.23895,-0.9265873,-9.227822,3.293713,-5.593577,-5.942398,-4.358421,2.660044,-4.301572,-1.258879,0.1499903,3.572833,-3.19844,0.8652432,-0.3025793,-1.576673,-7.666265,-6.751463,-1.398944,-2.690656,-1.429654,7.508364e-2,0.7998344,-3.562074,-1.021431,1.342968,2.110244,-7.561497,-2.372083,-3.649193,-5.7723,-1.068083,0.7537809,-4.569546,-1.198005,-5.638384,-1.227226,-1.195852,-1.118175,-9.130527,0.9675821,-2.497391,0.5988562,-1.965783,-4.25741,-6.547006,-1.459294,-2.380556,-3.977307,-7.809006,-4.276819,-4.028746,-9.055546,-3.599239,-1.470512,-8.253329,-1.351687,-4.269324,-6.140353,-6.30808,-1.834091,-3.135146,-9.391791,3.117815,-5.554733,-2.556769,-3.287376,-2.064013,-5.741995,-5.047918,-4.808841,-1.488526,-0.2351115,-5.760833,-2.722929,-7.012353,2.281171,-3.890514,-1.516824,-1.41011,-1.828457,-5.561244,-3.472142,-10.16919,-0.4369042,-5.698953,-4.587462,-4.897086])+  ]++-- Table for 2-sample Kolmogorov-Smirnov statistics. Generated using R+--+-- First element is D, second and third are samples+tableKS2D :: [(Double,[Double],[Double])]+tableKS2D =+  [ (0.2820513,[-0.4212928,2.146532,0.7585263,-0.5086105,-0.7725486,6.235548e-2,-0.1849861,0.861972,-0.1958534,-3.379697e-2,-1.316854,0.6701269],[0.4957582,0.4241167,0.9822869,0.4504248,-0.1749617,1.178098,-1.117222,-0.859273,0.3073736,0.4344583,-0.4761338,-1.332374,1.487291])+  , (0.2820513,[-0.712252,0.7990333,-0.7968473,1.443609,1.163096,-1.349071,-0.1553941,-2.003104,-0.3400618,-0.7019282,0.183293,-0.2352167],[-0.4622455,-0.8132221,0.1161614,-1.472115e-2,1.001454,-6.557789e-2,-0.2531216,-1.032432,0.4105478,1.749614,0.9722899,5.850942e-2,-0.3352746])+  , (0.2564103,[0.3509882,-0.2982833,1.314731,1.264223,-0.8156374,0.3734029,-3.288915e-2,0.6766016,0.9786335,0.1079949,-0.4211722,1.58656],[0.8024675,7.464538e-2,0.2739861,-2.334255e-2,0.5611802,0.6683374,0.4358206,0.349843,1.207834,1.402578,-0.4049183,0.4286042,1.665129])+  , (0.1833333,[1.376196,9.926384e-2,2.199292,-2.04993,0.5585353,-0.4812132,0.1041527,2.084774,0.71194,-1.398245,-4.458574e-2,1.484945,-1.473182,1.020076,-0.7019646,0.2182066,-1.702963,-0.3522622,-0.8129267,-0.6338972],[-1.020371,0.3323861,1.513288,0.1958708,-1.0723,5.323446e-2,-0.9993713,-0.7046356,-0.6781067,-0.4471603,1.512042,-0.2650665,-4.765228e-2,-1.501205,1.228664,0.5332935,-0.2960315,-0.1509683])+  , (0.5666667,[0.7145305,0.1255674,2.001531,0.1419216],[2.113474,-0.3352839,-0.4962429,-1.386079,0.6404667,-0.7145304,0.1084008,-0.9821421,-2.270472,-1.003846,-0.5644588,2.699695,-1.296494,-0.1538839,1.319094,-1.127544,0.3568889,0.2004726,-1.313291,0.3581084,0.3313498,0.9336278,0.9850203,-1.309506,1.170459,-0.7517466,-1.771269,0.7156381,-1.129691,0.877729])+  , (0.5,[0.6950626,0.1643805,-0.3102472,0.4810762,0.1844602,1.338836,-0.8083386,-0.5482141,0.9532421,-0.2644837],[7.527945,-1.95654,1.513725,-1.318431,2.453895,0.2078194,0.7371092,2.834245,-2.134794,3.938259])+  ] 
+ tests/Tests/Transform.hs view
@@ -0,0 +1,145 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts  #-}+{-# LANGUAGE ViewPatterns      #-}+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,Arbitrary(..),Gen,choose,vectorOf,+                                             printTestCase, quickCheck)++import Text.Printf++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)+        , testProperty "idct . dct = id [up to scale]"+            (t_fftInverse (\v -> U.map (/ (2 * fromIntegral (U.length v))) $ idct $ dct v))+        , testProperty "dct . idct = id [up to scale]"+            (t_fftInverse (\v -> U.map (/ (2 * fromIntegral (U.length v))) $ idct $ dct v))+          -- Exact small size DCT+          -- 2+        , testDCT [1,0] $ map (*2) [1, cos (pi/4)   ]+        , testDCT [0,1] $ map (*2) [1, cos (3*pi/4) ]+          -- 4+        , testDCT [1,0,0,0] $ map (*2) [1, cos(  pi/8), cos( 2*pi/8), cos( 3*pi/8)]+        , testDCT [0,1,0,0] $ map (*2) [1, cos(3*pi/8), cos( 6*pi/8), cos( 9*pi/8)]+        , testDCT [0,0,1,0] $ map (*2) [1, cos(5*pi/8), cos(10*pi/8), cos(15*pi/8)]+        , testDCT [0,0,0,1] $ map (*2) [1, cos(7*pi/8), cos(14*pi/8), cos(21*pi/8)]+          -- Exact small size IDCT+          -- 2+        , testIDCT [1,0]            [1,         1          ]+        , testIDCT [0,1] $ map (*2) [cos(pi/4), cos(3*pi/4)]+          -- 4+        , testIDCT [1,0,0,0]            [1,            1,            1,            1            ]+        , testIDCT [0,1,0,0] $ map (*2) [cos(   pi/8), cos( 3*pi/8), cos( 5*pi/8), cos( 7*pi/8) ]+        , testIDCT [0,0,1,0] $ map (*2) [cos( 2*pi/8), cos( 6*pi/8), cos(10*pi/8), cos(14*pi/8) ]+        , testIDCT [0,0,0,1] $ map (*2) [cos( 3*pi/8), cos( 9*pi/8), cos(15*pi/8), cos(21*pi/8) ]+        ]++-- 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 :: (HasNorm (U.Vector a), U.Unbox a, Num a, Show a, Arbitrary a)+             => (U.Vector a -> U.Vector a) -> Property+t_fftInverse roundtrip = do+  x <- genFftVector+  let n  = G.length x+      x' = roundtrip x+      d  = G.zipWith (-) x x'+      nd = vectorNorm d+      nx = vectorNorm x+  id $ printTestCase "Original vector"+     $ printTestCase (show x )+     $ printTestCase "Transformed one"+     $ printTestCase (show x')+     $ printTestCase (printf "Length = %i" n)+     $ printTestCase (printf "|x - x'| / |x| = %.6g" (nd / nx))+     $ nd <= 3e-14 * nx++-- Test discrete cosine transform+testDCT :: [Double] -> [Double] -> Test+testDCT (U.fromList -> vec) (U.fromList -> res)+  = testAssertion ("DCT test for " ++ show vec)+  $ vecEqual 3e-14 (dct vec) res++-- Test inverse discrete cosine transform+testIDCT :: [Double] -> [Double] -> Test+testIDCT (U.fromList -> vec) (U.fromList -> res)+  = testAssertion ("IDCT test for " ++ show vec)+  $ vecEqual 3e-14 (idct vec) res++++----------------------------------------------------------------++-- 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++-- Arbitrary vector for FFT od DCT+genFftVector :: (U.Unbox a, Arbitrary a) => Gen (U.Vector a)+genFftVector = do+  n <- (2^)  <$> choose (1,9::Int)    -- Size of vector+  G.fromList <$> vectorOf n arbitrary -- Vector to transform++-- Ad-hoc type class for calculation of vector norm+class HasNorm a where+  vectorNorm :: a -> Double++instance HasNorm (U.Vector Double) where+  vectorNorm = sqrt . U.sum . U.map (\x -> x*x)++instance HasNorm (U.Vector CD) where+  vectorNorm = sqrt . U.sum . U.map (\(x :+ y) -> x*x + y*y)++-- Approximate equality for vectors+vecEqual :: Double -> U.Vector Double -> U.Vector Double -> Bool+vecEqual ε v u+  = vectorNorm (U.zipWith (-) v u) < ε * vectorNorm v
+ tests/tests.hs view
@@ -0,0 +1,15 @@+import Test.Framework       (defaultMain)++import Tests.Distribution+import Tests.NonparametricTest+import qualified Tests.Transform+import qualified Tests.Function+import qualified Tests.KDE++main :: IO ()+main = defaultMain [ distributionTests +                   , nonparametricTests+                   , Tests.Transform.tests+                   , Tests.Function.tests+                   , Tests.KDE.tests+                   ]