statistics 0.16.2.1 → 0.16.3.0
raw patch · 18 files changed
+377/−258 lines, 18 filesdep +doctestdep +tasty-benchdep +tasty-papidep ~basedep ~mwc-randomdep ~parallelsetup-changed
Dependencies added: doctest, tasty-bench, tasty-papi
Dependency ranges changed: base, mwc-random, parallel, tasty, vector
Files
- Setup.lhs +0/−3
- Statistics/Correlation.hs +32/−4
- Statistics/Function.hs +2/−2
- Statistics/Regression.hs +45/−7
- Statistics/Sample.hs +69/−20
- Statistics/Test/Internal.hs +9/−8
- Statistics/Test/StudentT.hs +1/−1
- Statistics/Types.hs +6/−5
- bench-papi/Bench.hs +14/−0
- bench-time/Bench.hs +14/−0
- benchmark/bench.hs +23/−19
- changelog.md +22/−0
- statistics.cabal +71/−18
- tests/Tests/Correlation.hs +7/−7
- tests/Tests/ExactDistribution.hs +57/−66
- tests/Tests/Math/Tables.hs +0/−47
- tests/Tests/Math/gen.py +0/−51
- tests/doctest.hs +5/−0
− Setup.lhs
@@ -1,3 +0,0 @@-#!/usr/bin/env runhaskell-> import Distribution.Simple-> main = defaultMain
Statistics/Correlation.hs view
@@ -6,9 +6,11 @@ module Statistics.Correlation ( -- * Pearson correlation pearson+ , pearson2 , pearsonMatByRow -- * Spearman correlation , spearman+ , spearman2 , spearmanMatByRow ) where @@ -25,11 +27,18 @@ -- | Pearson correlation for sample of pairs. Exactly same as -- 'Statistics.Sample.correlation'-pearson :: (G.Vector v (Double, Double), G.Vector v Double)+pearson :: (G.Vector v (Double, Double)) => v (Double, Double) -> Double pearson = correlation {-# INLINE pearson #-} +-- | Pearson correlation for sample of pairs. Exactly same as+-- 'Statistics.Sample.correlation'+pearson2 :: (G.Vector v Double)+ => v Double -> v Double -> Double+pearson2 = correlation2+{-# INLINE pearson2 #-}+ -- | Compute pairwise Pearson correlation between rows of a matrix pearsonMatByRow :: Matrix -> Matrix pearsonMatByRow m@@ -43,15 +52,13 @@ -- Spearman ---------------------------------------------------------------- --- | compute Spearman correlation between two samples+-- | Compute Spearman correlation between two samples spearman :: ( Ord a , Ord b , G.Vector v a , G.Vector v b , G.Vector v (a, b) , G.Vector v Int- , G.Vector v Double- , G.Vector v (Double, Double) , G.Vector v (Int, a) , G.Vector v (Int, b) )@@ -63,6 +70,27 @@ where (x, y) = G.unzip xy {-# INLINE spearman #-}++-- | Compute Spearman correlation between two samples. Samples must+-- have same length.+spearman2 :: ( Ord a+ , Ord b+ , G.Vector v a+ , G.Vector v b+ , G.Vector v Int+ , G.Vector v (Int, a)+ , G.Vector v (Int, b)+ )+ => v a+ -> v b+ -> Double+spearman2 xs ys+ | nx /= ny = error "Statistics.Correlation.spearman2: samples must have same length"+ | otherwise = pearson $ G.zip (rankUnsorted xs) (rankUnsorted ys)+ where+ nx = G.length xs+ ny = G.length ys+{-# INLINE spearman2 #-} -- | compute pairwise Spearman correlation between rows of a matrix spearmanMatByRow :: Matrix -> Matrix
Statistics/Function.hs view
@@ -76,8 +76,8 @@ {-# INLINE indices #-} -- | Zip a vector with its indices.-indexed :: (G.Vector v e, G.Vector v Int, G.Vector v (Int,e)) => v e -> v (Int,e)-indexed a = G.zip (indices a) a+indexed :: (G.Vector v e, G.Vector v (Int,e)) => v e -> v (Int,e)+indexed xs = G.imap (,) xs {-# INLINE indexed #-} data MM = MM {-# UNPACK #-} !Double {-# UNPACK #-} !Double
Statistics/Regression.hs view
@@ -41,8 +41,15 @@ -- element than the list of predictors; the last element is the -- /y/-intercept value. ----- * /R²/, the coefficient of determination (see 'rSquare' for+-- * /R²/, the coefficient of determination (see 'rSquare' for -- details).+--+-- >>> import qualified Data.Vector.Unboxed as VU+-- >>> :{+-- olsRegress [ VU.fromList [0,1,2,3]+-- ] (VU.fromList [1000, 1001, 1002, 1003])+-- :}+-- ([1.0000000000000218,999.9999999999999],1.0) olsRegress :: [Vector] -- ^ Non-empty list of predictor vectors. Must all have -- the same length. These will become the columns of@@ -65,7 +72,30 @@ lss@(n:ls) = map G.length preds olsRegress _ _ = error "no predictors given" --- | Compute the ordinary least-squares solution to /A x = b/.+-- | Compute the ordinary least-squares solution to overdetermined+-- linear system \(Ax = b\). In other words it finds+--+-- \[ \operatorname{argmin}|Ax-b|^2 \].+--+-- All columns of \(A\) must be linearly independent. It's not+-- checked function will return nonsensical result if resulting+-- linear system is poorly conditioned.+--+-- >>> import qualified Data.Vector.Unboxed as VU+-- >>> :{+-- ols (fromColumns [ VU.fromList [0,1,2,3]+-- , VU.fromList [1,1,1,1]+-- ]) (VU.fromList [1000, 1001, 1002, 1003])+-- :}+-- [1.0000000000000218,999.9999999999999]+--+-- >>> :{+-- ols (fromColumns [ VU.fromList [0,1,2,3]+-- , VU.fromList [4,2,1,1]+-- , VU.fromList [1,1,1,1]+-- ]) (VU.fromList [1000, 1001, 1002, 1003])+-- :}+-- [1.0000000000005393,4.2290644612446807e-13,999.9999999999983] ols :: Matrix -- ^ /A/ has at least as many rows as columns. -> Vector -- ^ /b/ has the same length as columns in /A/. -> Vector@@ -92,7 +122,7 @@ where n = rows r l = U.length b --- | Compute /R²/, the coefficient of determination that+-- | Compute /R²/, the coefficient of determination that -- indicates goodness-of-fit of a regression. -- -- This value will be 1 if the predictors fit perfectly, dropping to 0@@ -101,11 +131,19 @@ -> Vector -- ^ Responders. -> Vector -- ^ Regression coefficients. -> Double-rSquare pred resp coeff = 1 - r / t+rSquare pred resp coeff+ -- Data has zero variance. If fit is perfect we set R² to 1 else to+ -- 0. This is not perfect heuristic. Fit residuals may be nonzero+ -- due to rounding.+ | t == 0 = if r == 0 then 1 else 0+ -- If fit residuals are worse than average we simply set R² to 0+ | r2 >= 0 && r2 <= 1 = r2+ | otherwise = 0 where- r = sum $ flip U.imap resp $ \i x -> square (x - p i)- t = sum $ flip U.map resp $ \x -> square (x - mean resp)- p i = sum . flip U.imap coeff $ \j -> (* unsafeIndex pred i j)+ r2 = 1 - r / t+ r = sum $ flip U.imap resp $ \i x -> square (x - p i)+ t = sum $ flip U.map resp $ \x -> square (x - mean resp)+ p i = sum $ flip U.imap coeff $ \j x -> x * unsafeIndex pred i j -- | Bootstrap a regression function. Returns both the results of the -- regression and the requested confidence interval values.
Statistics/Sample.hs view
@@ -20,6 +20,7 @@ , range -- * Statistics of location+ , expectation , mean , welfordMean , meanWeighted@@ -54,17 +55,20 @@ -- * Joint distributions , covariance , correlation+ , covariance2+ , correlation2 , pair -- * References -- $references ) where -import Statistics.Function (minMax)+import Statistics.Function (minMax,square) import Statistics.Sample.Internal (robustSumVar, sum) import Statistics.Types.Internal (Sample,WeightedSample) import qualified Data.Vector as V import qualified Data.Vector.Generic as G import qualified Data.Vector.Unboxed as U+import Numeric.Sum (kbn, Summation(zero,add)) -- Operator ^ will be overridden import Prelude hiding ((^), sum)@@ -76,9 +80,17 @@ where (lo , hi) = minMax s {-# INLINE range #-} +-- | /O(n)/ Compute expectation of function over for sample. This is+-- simply @mean . map f@ but won't create intermediate vector.+expectation :: (G.Vector v a) => (a -> Double) -> v a -> Double+expectation f xs = kbn (G.foldl' (\s -> add s . f) zero xs)+ / fromIntegral (G.length xs)+{-# INLINE expectation #-}+ -- | /O(n)/ Arithmetic mean. This uses Kahan-Babuška-Neumaier -- summation, so is more accurate than 'welfordMean' unless the input--- values are very large.+-- values are very large. This function is not subject to stream+-- fusion. mean :: (G.Vector v Double) => v Double -> Double mean xs = sum xs / fromIntegral (G.length xs) {-# SPECIALIZE mean :: U.Vector Double -> Double #-}@@ -122,7 +134,7 @@ -- | /O(n)/ Geometric mean of a sample containing no negative values. geometricMean :: (G.Vector v Double) => v Double -> Double-geometricMean = exp . mean . G.map log+geometricMean = exp . expectation log {-# INLINE geometricMean #-} -- | Compute the /k/th central moment of a sample. The central moment@@ -138,7 +150,7 @@ | a < 0 = error "Statistics.Sample.centralMoment: negative input" | a == 0 = 1 | a == 1 = 0- | otherwise = sum (G.map go xs) / fromIntegral (G.length xs)+ | otherwise = expectation go xs where go x = (x-m) ^ a m = mean xs@@ -354,40 +366,77 @@ -- | Covariance of sample of pairs. For empty sample it's set to -- zero-covariance :: (G.Vector v (Double,Double), G.Vector v Double)+covariance :: (G.Vector v (Double,Double)) => v (Double,Double) -> Double covariance xy | n == 0 = 0- | otherwise = mean $ G.zipWith (*)- (G.map (\x -> x - muX) xs)- (G.map (\y -> y - muY) ys)+ | otherwise = expectation (\(x,y) -> (x - muX)*(y - muY)) xy where- n = G.length xy- (xs,ys) = G.unzip xy- muX = mean xs- muY = mean ys+ n = G.length xy+ muX = expectation fst xy+ muY = expectation snd xy {-# SPECIALIZE covariance :: U.Vector (Double,Double) -> Double #-} {-# SPECIALIZE covariance :: V.Vector (Double,Double) -> Double #-} -- | Correlation coefficient for sample of pairs. Also known as -- Pearson's correlation. For empty sample it's set to zero.-correlation :: (G.Vector v (Double,Double), G.Vector v Double)+correlation :: (G.Vector v (Double,Double)) => v (Double,Double) -> Double correlation xy | n == 0 = 0 | otherwise = cov / sqrt (varX * varY) where- n = G.length xy- (xs,ys) = G.unzip xy- (muX,varX) = meanVariance xs- (muY,varY) = meanVariance ys- cov = mean $ G.zipWith (*)- (G.map (\x -> x - muX) xs)- (G.map (\y -> y - muY) ys)+ n = G.length xy+ muX = expectation (\(x,_) -> x) xy+ muY = expectation (\(_,y) -> y) xy+ varX = expectation (\(x,_) -> square (x - muX)) xy+ varY = expectation (\(_,y) -> square (y - muY)) xy+ cov = expectation (\(x,y) -> (x - muX)*(y - muY)) xy {-# SPECIALIZE correlation :: U.Vector (Double,Double) -> Double #-} {-# SPECIALIZE correlation :: V.Vector (Double,Double) -> Double #-}+++-- | Covariance of two samples. Both vectors must be of the same+-- length. If both are empty it's set to zero+covariance2 :: (G.Vector v Double)+ => v Double+ -> v Double+ -> Double+covariance2 xs ys+ | nx /= ny = error $ "Statistics.Sample.covariance2: both samples must have same length"+ | nx == 0 = 0+ | otherwise = sum (G.zipWith (\x y -> (x - muX)*(y - muY)) xs ys)+ / fromIntegral nx+ where+ nx = G.length xs+ ny = G.length ys+ muX = mean xs+ muY = mean ys+{-# SPECIALIZE covariance2 :: U.Vector Double -> U.Vector Double -> Double #-}+{-# SPECIALIZE covariance2 :: V.Vector Double -> V.Vector Double -> Double #-}++-- | Correlation coefficient for two samples. Both vector must have+-- same length Also known as Pearson's correlation. For empty sample+-- it's set to zero.+correlation2 :: (G.Vector v Double)+ => v Double+ -> v Double+ -> Double+correlation2 xs ys+ | nx /= ny = error $ "Statistics.Sample.correlation2: both samples must have same length"+ | nx == 0 = 0+ | otherwise = cov / sqrt (varX * varY)+ where+ nx = G.length xs+ ny = G.length ys+ (muX,varX) = meanVariance xs+ (muY,varY) = meanVariance ys+ cov = sum (G.zipWith (\x y -> (x - muX)*(y - muY)) xs ys)+ / fromIntegral nx+{-# SPECIALIZE correlation2 :: U.Vector Double -> U.Vector Double -> Double #-}+{-# SPECIALIZE correlation2 :: V.Vector Double -> V.Vector Double -> Double #-} -- | Pair two samples. It's like 'G.zip' but requires that both
Statistics/Test/Internal.hs view
@@ -8,6 +8,7 @@ import Data.Ord import Data.Vector.Generic ((!)) import qualified Data.Vector.Generic as G+import qualified Data.Vector.Unboxed as U import qualified Data.Vector.Generic.Mutable as M import Statistics.Function @@ -27,15 +28,16 @@ -- In case of ties average of ranks of equal elements is assigned -- to each ----- >>> rank (==) (fromList [10,20,30::Int])--- > fromList [1.0,2.0,3.0]+-- >>> import qualified Data.Vector.Unboxed as VU+-- >>> rank (==) (VU.fromList [10,20,30::Int])+-- [1.0,2.0,3.0] ----- >>> rank (==) (fromList [10,10,10,30::Int])--- > fromList [2.0,2.0,2.0,4.0]-rank :: (G.Vector v a, G.Vector v Double)+-- >>> rank (==) (VU.fromList [10,10,10,30::Int])+-- [2.0,2.0,2.0,4.0]+rank :: (G.Vector v a) => (a -> a -> Bool) -- ^ Equivalence relation -> v a -- ^ Vector to rank- -> v Double+ -> U.Vector Double rank eq vec = G.unfoldr go (Rank 0 (-1) 1 vec) where go (Rank 0 _ r v)@@ -58,11 +60,10 @@ rankUnsorted :: ( Ord a , G.Vector v a , G.Vector v Int- , G.Vector v Double , G.Vector v (Int, a) ) => v a- -> v Double+ -> U.Vector Double rankUnsorted xs = G.create $ do -- Put ranks into their original positions -- NOTE: backpermute will do wrong thing
Statistics/Test/StudentT.hs view
@@ -71,7 +71,7 @@ -- | Paired two-sample t-test. Two samples are paired in a -- within-subject design. Returns @Nothing@ if sample size is not -- sufficient.-pairedTTest :: forall v. (G.Vector v (Double, Double), G.Vector v Double)+pairedTTest :: forall v. (G.Vector v (Double, Double)) => PositionTest -- ^ one- or two-tailed test -> v (Double, Double) -- ^ paired samples -> Maybe (Test StudentT)
Statistics/Types.hs view
@@ -94,7 +94,7 @@ -- second from @1 - CL@ or significance level. -- -- >>> cl95--- mkCLFromSignificance 0.05+-- mkCLFromSignificance 5.0e-2 -- -- Prior to 0.14 confidence levels were passed to function as plain -- @Doubles@. Use 'mkCL' to convert them to @CL@.@@ -134,7 +134,7 @@ -- exception if parameter is out of [0,1] range -- -- >>> mkCL 0.95 -- same as cl95--- mkCLFromSignificance 0.05+-- mkCLFromSignificance 5.0000000000000044e-2 mkCL :: (Ord a, Num a) => a -> CL a mkCL = fromMaybe (error "Statistics.Types.mkCL: probability is out if [0,1] range")@@ -144,7 +144,7 @@ -- parameter is out of [0,1] range -- -- >>> mkCLE 0.95 -- same as cl95--- Just (mkCLFromSignificance 0.05)+-- Just (mkCLFromSignificance 5.0000000000000044e-2) mkCLE :: (Ord a, Num a) => a -> Maybe (CL a) mkCLE p | p >= 0 && p <= 1 = Just $ CL (1 - p)@@ -155,7 +155,7 @@ -- throw exception if parameter is out of [0,1] range -- -- >>> mkCLFromSignificance 0.05 -- same as cl95--- mkCLFromSignificance 0.05+-- mkCLFromSignificance 5.0e-2 mkCLFromSignificance :: (Ord a, Num a) => a -> CL a mkCLFromSignificance = fromMaybe (error errMkCL) . mkCLFromSignificanceE @@ -163,7 +163,7 @@ -- parameter is out of [0,1] range -- -- >>> mkCLFromSignificanceE 0.05 -- same as cl95--- Just (mkCLFromSignificance 0.05)+-- Just (mkCLFromSignificance 5.0e-2) mkCLFromSignificanceE :: (Ord a, Num a) => a -> Maybe (CL a) mkCLFromSignificanceE p | p >= 0 && p <= 1 = Just $ CL p@@ -299,6 +299,7 @@ -- > , confIntUDX = 6 -- > , confIntCL = cl95 -- > }+-- > } -- -- Prior to statistics 0.14 @Estimate@ data type used following definition: --
+ bench-papi/Bench.hs view
@@ -0,0 +1,14 @@+-- |+-- Here we reexport definitions of tasty-bench+module Bench+ ( whnf+ , nf+ , nfIO+ , whnfIO+ , bench+ , bgroup+ , defaultMain+ , benchIngredients+ ) where++import Test.Tasty.PAPI
+ bench-time/Bench.hs view
@@ -0,0 +1,14 @@+-- |+-- Here we reexport definitions of tasty-bench+module Bench+ ( whnf+ , nf+ , nfIO+ , whnfIO+ , bench+ , bgroup+ , defaultMain+ , benchIngredients+ ) where++import Test.Tasty.Bench
benchmark/bench.hs view
@@ -1,24 +1,26 @@-import Control.Monad.ST (runST)-import Criterion.Main import Data.Complex import Statistics.Sample import Statistics.Transform-import Statistics.Correlation.Pearson+import Statistics.Correlation import System.Random.MWC-import qualified Data.Vector.Unboxed as U+import qualified Data.Vector.Unboxed as VU+import qualified Data.Vector.Unboxed.Mutable as MVU +import Bench + -- Test sample-sample :: U.Vector Double-sample = runST $ flip uniformVector 10000 =<< create+sample :: VU.Vector Double+sample = VU.create $ do g <- create+ MVU.replicateM 10000 (uniform g) -- Weighted test sample-sampleW :: U.Vector (Double,Double)-sampleW = U.zip sample (U.reverse sample)+sampleW :: VU.Vector (Double,Double)+sampleW = VU.zip sample (VU.reverse sample) -- Complex vector for FFT tests-sampleC :: U.Vector (Complex Double)-sampleC = U.zipWith (:+) sample (U.reverse sample)+sampleC :: VU.Vector (Complex Double)+sampleC = VU.zipWith (:+) sample (VU.reverse sample) -- Simple benchmark for functions from Statistics.Sample@@ -37,9 +39,11 @@ , bench "varianceUnbiased" $ nf (\x -> varianceUnbiased x) sample , bench "varianceWeighted" $ nf (\x -> varianceWeighted x) sampleW -- Correlation- , bench "pearson" $ nf (\x -> pearson (U.reverse sample) x) sample- , bench "pearson'" $ nf (\x -> pearson' (U.reverse sample) x) sample- , bench "pearsonFast" $ nf (\x -> pearsonFast (U.reverse sample) x) sample+ , bench "pearson" $ nf pearson sampleW+ , bench "covariance" $ nf covariance sampleW+ , bench "correlation" $ nf correlation sampleW+ , bench "covariance2" $ nf (covariance2 sample) sample+ , bench "correlation2" $ nf (correlation2 sample) sample -- Other , bench "stdDev" $ nf (\x -> stdDev x) sample , bench "skewness" $ nf (\x -> skewness x) sample@@ -52,17 +56,17 @@ ] , bgroup "FFT" [ bgroup "fft"- [ bench (show n) $ whnf fft (U.take n sampleC) | n <- fftSizes ]+ [ bench (show n) $ whnf fft (VU.take n sampleC) | n <- fftSizes ] , bgroup "ifft"- [ bench (show n) $ whnf ifft (U.take n sampleC) | n <- fftSizes ]+ [ bench (show n) $ whnf ifft (VU.take n sampleC) | n <- fftSizes ] , bgroup "dct"- [ bench (show n) $ whnf dct (U.take n sample) | n <- fftSizes ]+ [ bench (show n) $ whnf dct (VU.take n sample) | n <- fftSizes ] , bgroup "dct_"- [ bench (show n) $ whnf dct_ (U.take n sampleC) | n <- fftSizes ]+ [ bench (show n) $ whnf dct_ (VU.take n sampleC) | n <- fftSizes ] , bgroup "idct"- [ bench (show n) $ whnf idct (U.take n sample) | n <- fftSizes ]+ [ bench (show n) $ whnf idct (VU.take n sample) | n <- fftSizes ] , bgroup "idct_"- [ bench (show n) $ whnf idct_ (U.take n sampleC) | n <- fftSizes ]+ [ bench (show n) $ whnf idct_ (VU.take n sampleC) | n <- fftSizes ] ] ]
changelog.md view
@@ -1,3 +1,25 @@+## Changes in 0.16.3.0++ * `S.Sample.correlation`, `S.Sample.covariance`,+ `S.Correlation.pearson` do not allocate temporary arrays.++ * Variants of correlation which take two vectors as input are added:+ `S.Sample.correlation2`, `S.Sample.covariance2`, `S.Correlation.pearson2`,+ `S.Correlation.spearman2`.++ * Contexts for `S.Function.indexed`, `S.Correlation.spearman`, `S.pairedTTest`,+ `S.Sample.correlation`, `S.Sample.covariance`, reduced.++ * Computation of `rSquare` in linear regression has special case for case when+ data variation is 0.++ * Doctests added.++ * Benchmarks using `tasty-bench` and `tasty-papi` added.++ * Spurious test failures fixed.++ ## Changes in 0.16.2.1 * Unnecessary constraint dropped from `tStatisticsPaired`.
statistics.cabal view
@@ -1,5 +1,8 @@+cabal-version: 3.0+build-type: Simple+ name: statistics-version: 0.16.2.1+version: 0.16.3.0 synopsis: A library of statistical types, data, and functions description: This library provides a number of common functions and types useful@@ -22,7 +25,7 @@ * Common statistical tests for significant differences between samples. -license: BSD2+license: BSD-2-Clause license-file: LICENSE homepage: https://github.com/haskell/statistics bug-reports: https://github.com/haskell/statistics/issues@@ -30,32 +33,39 @@ maintainer: Alexey Khudaykov <alexey.skladnoy@gmail.com> copyright: 2009-2014 Bryan O'Sullivan category: Math, Statistics-build-type: Simple-cabal-version: >= 1.10+ extra-source-files: README.markdown- benchmark/bench.hs changelog.md examples/kde/KDE.hs examples/kde/data/faithful.csv examples/kde/kde.html examples/kde/kde.tpl- tests/Tests/Math/Tables.hs- tests/Tests/Math/gen.py tests/utils/Makefile tests/utils/fftw.c tested-with:- GHC ==8.4.4- GHC ==8.6.5- GHC ==8.8.4- GHC ==8.10.7- GHC ==9.0.2- GHC ==9.2.8- GHC ==9.4.6- GHC ==9.6.2+ GHC ==8.4.4+ || ==8.6.5+ || ==8.8.4+ || ==8.10.7+ || ==9.0.2+ || ==9.2.8+ || ==9.4.8+ || ==9.6.6+ || ==9.8.4+ || ==9.10.1 +source-repository head+ type: git+ location: https://github.com/haskell/statistics +flag BenchPAPI+ Description: Enable building of benchmarks which use instruction counters.+ It requires libpapi and only works on Linux so it's protected by flag+ Default: False+ Manual: True+ library default-language: Haskell2010 exposed-modules:@@ -176,6 +186,49 @@ , vector , vector-algorithms -source-repository head- type: git- location: https://github.com/haskell/statistics+test-suite statistics-doctests+ default-language: Haskell2010+ type: exitcode-stdio-1.0+ hs-source-dirs: tests+ main-is: doctest.hs+ if impl(ghcjs) || impl(ghc < 8.0)+ Buildable: False+ -- Linker on macos prints warnings to console which confuses doctests.+ -- We simply disable doctests on ma for older GHC+ -- > warning: -single_module is obsolete+ if os(darwin) && impl(ghc < 9.6)+ buildable: False+ build-depends:+ base -any+ , statistics -any+ , doctest >=0.15 && <0.24++-- We want to be able to build benchmarks using both tasty-bench and tasty-papi.+-- They have similar API so we just create two shim modules which reexport+-- definitions from corresponding library and pick one in cabal file.+common bench-stanza+ ghc-options: -Wall+ default-language: Haskell2010+ build-depends: base < 5+ , vector >= 0.12.3+ , statistics+ , mwc-random+ , tasty >=1.3.1++benchmark statistics-bench+ import: bench-stanza+ type: exitcode-stdio-1.0+ hs-source-dirs: benchmark bench-time+ main-is: bench.hs+ Other-modules: Bench+ build-depends: tasty-bench >= 0.3++benchmark statistics-bench-papi+ import: bench-stanza+ type: exitcode-stdio-1.0+ if impl(ghcjs) || !flag(BenchPAPI)+ buildable: False+ hs-source-dirs: benchmark bench-papi+ main-is: bench.hs+ Other-modules: Bench+ build-depends: tasty-papi >= 0.1.2
tests/Tests/Correlation.hs view
@@ -102,11 +102,11 @@ , not (isNaN c3) , not (isNaN c4) ]- ==> ( counterexample (show sample0)- $ counterexample (show sample1)- $ counterexample (show sample2)- $ counterexample (show sample3)- $ counterexample (show sample4)+ ==> ( counterexample ("S0 = " ++ show sample0)+ $ counterexample ("S1 = " ++ show sample1)+ $ counterexample ("S2 = " ++ show sample2)+ $ counterexample ("S3 = " ++ show sample3)+ $ counterexample ("S4 = " ++ show sample4) $ counterexample (show (c1,c2,c3,c4)) $ and [ c1 == c2 , c1 == c3@@ -117,8 +117,8 @@ -- We need to stretch sample into [-10 .. 10] range to avoid -- problems with under/overflows etc. stretch xs- | a == b = xs- | otherwise = [ (x - a - 10) * 20 / (a - b) | x <- xs ]+ | a == b = xs+ | otherwise = [ ((x - a)/(b - a) - 0.5) * 20 | x <- xs ] where a = minimum xs b = maximum xs
tests/Tests/ExactDistribution.hs view
@@ -1,8 +1,9 @@-{-# LANGUAGE BangPatterns,- FlexibleInstances,- FlexibleContexts,- ScopedTypeVariables- #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-} -- | -- Module : Tests.ExactDistribution -- Copyright : (c) 2022 Lorenz Minder@@ -64,8 +65,6 @@ , ExactHypergeomDistr(..) -- * Linking to production distributions- , ProductionProbFuncs(..)- , productionProbFuncs , ProductionLinkage -- * Individual test routines@@ -88,6 +87,7 @@ import Test.Tasty.QuickCheck (testProperty) import Test.QuickCheck as QC import Numeric.MathFunctions.Comparison (relativeError)+import Numeric.MathFunctions.Constants (m_tiny) import Statistics.Distribution import Statistics.Distribution.Binomial@@ -295,92 +295,84 @@ -- ---------------------------------------------------------------- --- | Distribution evaluation functions.------ This is used to store a-data ProductionProbFuncs = ProductionProbFuncs {- prodProb :: Int -> Double- , prodCumulative :: Double -> Double- , prodComplCumulative :: Double -> Double- }--productionProbFuncs :: (DiscreteDistr a) => a -> ProductionProbFuncs-productionProbFuncs d = ProductionProbFuncs {- prodProb = probability d- , prodCumulative = cumulative d- , prodComplCumulative = complCumulative d- }--class (ExactDiscreteDistr a) => ProductionLinkage a where- productionLinkage :: a -> ProductionProbFuncs+class (ExactDiscreteDistr a, DiscreteDistr (ProdDistrib a)+ ) => ProductionLinkage a where+ type ProdDistrib a+ toProd :: a -> ProdDistrib a instance ProductionLinkage ExactBinomialDistr where- productionLinkage (ExactBD n p) =- let d = binomial (fromIntegral n) (fromRational p)- in productionProbFuncs d+ type ProdDistrib ExactBinomialDistr = BinomialDistribution+ toProd (ExactBD n p) = binomial (fromIntegral n) (fromRational p) instance ProductionLinkage ExactDiscreteUniformDistr where- productionLinkage (ExactDU lower upper) =- let d = discreteUniformAB (fromIntegral lower) (fromIntegral upper)- in productionProbFuncs d+ type ProdDistrib ExactDiscreteUniformDistr = DiscreteUniform+ toProd (ExactDU lower upper) = discreteUniformAB (fromIntegral lower) (fromIntegral upper) instance ProductionLinkage ExactGeometricDistr where- productionLinkage (ExactGeom p) =- let d = geometric $ fromRational p- in productionProbFuncs d+ type ProdDistrib ExactGeometricDistr = GeometricDistribution+ toProd (ExactGeom p) = geometric $ fromRational p instance ProductionLinkage ExactHypergeomDistr where- productionLinkage (ExactHG nK nN n) =- let d = hypergeometric (fromIntegral nK) (fromIntegral nN) (fromIntegral n)- in productionProbFuncs d+ type ProdDistrib ExactHypergeomDistr = HypergeometricDistribution+ toProd (ExactHG nK nN n) =+ hypergeometric (fromIntegral nK) (fromIntegral nN) (fromIntegral n) + ---------------------------------------------------------------- -- Tests ---------------------------------------------------------------- +-- Compare that probabilities agree. If they are denormalized just+-- return True. You can't say much about precision+probabilityAgree :: Double -> Double -> Double -> Bool+probabilityAgree tol pe pa+ | pa < 0 = False+ | pe < 0 = False+ | pe < m_tiny = True+ | otherwise = relativeError pe pa < tol+ -- Check production probability mass function accuracy. -- -- Inputs: tolerance (max relative error) and test case-pmfMatch :: (Show a, ProductionLinkage a) => Double -> TestCase a -> Bool-pmfMatch tol (TestCase dExact k) =- let dProd = productionLinkage dExact- pe = fromRational $ exactProb dExact k- pa = prodProb dProd k'- k' = fromIntegral k- in relativeError pe pa < tol+pmfMatch :: (Show a, ProductionLinkage a) => Double -> TestCase a -> Property+pmfMatch tol (TestCase dExact k)+ = counterexample ("Exact = " ++ show pe)+ $ counterexample ("Approx = " ++ show pa)+ $ probabilityAgree tol pe pa+ where+ pe = fromRational $ exactProb dExact k+ pa = probability (toProd dExact) (fromIntegral k) -- Check production cumulative probability function accuracy. -- -- Inputs: tolerance (max relative error) and test case. cdfMatch :: (Show a, ProductionLinkage a) => Double -> TestCase a -> Bool-cdfMatch tol (TestCase dExact k) =- let dProd = productionLinkage dExact- pe = fromRational $ exactCumulative dExact k- pa = prodCumulative dProd k'- k' = fromIntegral k- in relativeError pe pa < tol+cdfMatch tol (TestCase dExact k)+ = probabilityAgree tol pe pa+ where+ pe = fromRational $ exactCumulative dExact k+ pa = cumulative (toProd dExact) (fromIntegral k) -- Check production complement cumulative function accuracy. -- -- Inputs: tolerance (max relative error) and test case. complCdfMatch :: (Show a, ProductionLinkage a) => Double -> TestCase a -> Bool-complCdfMatch tol (TestCase dExact k) =- let dProd = productionLinkage dExact- pe = fromRational $ 1 - exactCumulative dExact k- pa = prodComplCumulative dProd k'- k' = fromIntegral k- in relativeError pe pa < tol+complCdfMatch tol (TestCase dExact k)+ = probabilityAgree tol pe pa+ where+ pe = fromRational $ 1 - exactCumulative dExact k+ pa = complCumulative (toProd dExact) (fromIntegral k) -- Phantom type to encode an exact distribution. data Tag a = Tag -distTests :: (Show a, ProductionLinkage a, Arbitrary (TestCase a)) =>+distTests :: forall a. (Show a, ProductionLinkage a, Arbitrary (TestCase a)) => Tag a -> String -> Double -> TestTree distTests (Tag :: Tag a) name tol =- testGroup ("Exact tests for " ++ name) [- testProperty "PMF match" $ ((pmfMatch tol) :: TestCase a -> Bool)- , testProperty "CDF match" $ ((cdfMatch tol) :: TestCase a -> Bool)- , testProperty "1 - CDF match" $ ((complCdfMatch tol) :: TestCase a -> Bool)+ testGroup ("Exact tests for " ++ name)+ [ testProperty "PMF match" $ pmfMatch @a tol+ , testProperty "CDF match" $ cdfMatch @a tol+ , testProperty "1 - CDF match" $ complCdfMatch @a tol ] @@ -388,9 +380,8 @@ exactDistributionTests :: TestTree exactDistributionTests = testGroup "Test distributions against exact"- [- distTests (Tag :: Tag ExactBinomialDistr) "Binomial" 1.0e-12- , distTests (Tag :: Tag ExactDiscreteUniformDistr) "DiscreteUniform" 1.0e-12- , distTests (Tag :: Tag ExactGeometricDistr) "Geometric" 1.0e-13- , distTests (Tag :: Tag ExactHypergeomDistr) "Hypergeometric" 1.0e-12+ [ distTests (Tag @ExactBinomialDistr) "Binomial" 1.0e-12+ , distTests (Tag @ExactDiscreteUniformDistr) "DiscreteUniform" 1.0e-12+ , distTests (Tag @ExactGeometricDistr) "Geometric" 1.0e-13+ , distTests (Tag @ExactHypergeomDistr) "Hypergeometric" 1.0e-12 ]
− tests/Tests/Math/Tables.hs
@@ -1,47 +0,0 @@-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
@@ -1,51 +0,0 @@-#!/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/doctest.hs view
@@ -0,0 +1,5 @@+import Test.DocTest (doctest)++main :: IO ()+main = doctest ["-XHaskell2010", "Statistics"]+