factory-0.2.1.0: src/Factory/Test/QuickCheck/Probability.hs
{-
Copyright (C) 2011-2013 Dr. Alistair Ward
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-}
{- |
[@AUTHOR@] Dr. Alistair Ward
[@DESCRIPTION@] Defines /QuickCheck/-properties for "Math.Probability".
-}
module Factory.Test.QuickCheck.Probability(
-- * Functions
-- normalise,
quickChecks
) where
import Control.Arrow((&&&))
import qualified Data.List
import qualified Factory.Math.Probability as Math.Probability
import qualified Factory.Math.Statistics as Math.Statistics
import Factory.Test.QuickCheck.Factorial()
import qualified System.Random
import qualified Test.QuickCheck
import Test.QuickCheck((==>))
import qualified ToolShed.Data.Pair
-- | Re-profile a distribution to achieve a standard mean & variance.
normalise :: (
Eq f,
Floating f,
Math.Probability.Distribution distribution
) => distribution -> [f] -> [f]
normalise distribution
| variance == 0 = error "Factory.Test.Quick.Probability.normalise:\tzero variance => can't stretch to one."
| otherwise = map $ (/ sqrt variance) . (+ negate mean)
where
(mean, variance) = Math.Probability.getMean &&& Math.Probability.getVariance $ distribution
-- | Defines invariant properties.
quickChecks :: IO ()
quickChecks = do
randomGen <- System.Random.getStdGen
(Test.QuickCheck.quickCheck . ($ randomGen)) `mapM_` [
prop_logNormalDistribution,
prop_logNormalDistribution',
prop_normalDistribution,
prop_uniformDistribution
] >> (Test.QuickCheck.quickCheck . ($ randomGen)) `mapM_` [
prop_exponentialDistribution,
prop_exponentialDistribution',
prop_poissonDistribution,
prop_poissonDistribution',
prop_shiftedGeometricDistribution,
prop_shiftedGeometricDistribution'
]
where
isWithinTolerance :: Double -> Double -> Bool
isWithinTolerance i = (< recip i) . abs
prop_logNormalDistribution, prop_logNormalDistribution', prop_normalDistribution, prop_uniformDistribution :: System.Random.RandomGen randomGen => randomGen -> Double -> Double -> Test.QuickCheck.Property
prop_logNormalDistribution randomGen location scale2 = scale2 /= 0 ==> Test.QuickCheck.label "prop_logNormalDistribution" . uncurry (&&) . ToolShed.Data.Pair.mirror (isWithinTolerance 1) . (
Math.Statistics.getMean &&& pred . Math.Statistics.getStandardDeviation --Both of which, having been normalised, should be zero.
) . (
normalise distribution :: [Double] -> [Double]
) . take 10000 $ Math.Probability.generatePopulation distribution randomGen where
maxParameter = log . fromInteger $ Math.Probability.maxPreciseInteger (undefined :: Double)
location'
| location >= 0 = maxParameter `min` location
| otherwise = negate maxParameter `max` location
distribution = Math.Probability.LogNormalDistribution location' . min maxParameter $ abs scale2
prop_logNormalDistribution' randomGen location scale2 = scale2 /= 0 ==> Test.QuickCheck.label "prop_logNormalDistribution'" . all (
>= (0 :: Double)
) . take 10 $ Math.Probability.generatePopulation (Math.Probability.LogNormalDistribution location' . min maxParameter $ abs scale2) randomGen where
maxParameter = log . fromInteger $ Math.Probability.maxPreciseInteger (undefined :: Double)
location'
| location >= 0 = maxParameter `min` location
| otherwise = negate maxParameter `max` location
prop_normalDistribution randomGen mean variance = variance /= 0 ==> Test.QuickCheck.label "prop_normalDistribution" . uncurry (&&) . ToolShed.Data.Pair.mirror (isWithinTolerance 10) . (
Math.Statistics.getMean &&& pred . Math.Statistics.getStandardDeviation --Both of which, having been normalised, should be zero.
) . (
normalise distribution :: [Double] -> [Double]
) . take 1000 $ Math.Probability.generatePopulation distribution randomGen where
distribution = Math.Probability.NormalDistribution mean $ abs variance
prop_uniformDistribution randomGen min' max' = min' /= max' ==> Test.QuickCheck.label "prop_uniformDistribution" . uncurry (&&) . ToolShed.Data.Pair.mirror (isWithinTolerance 10) . (
Math.Statistics.getMean &&& pred . Math.Statistics.getStandardDeviation --Both of which, having been normalised, should be zero.
) . (
normalise distribution :: [Double] -> [Double]
) . take 10000 $ Math.Probability.generatePopulation distribution randomGen where
[min'', max''] = Data.List.sort [min', max']
distribution = Math.Probability.UniformDistribution (min'', max'')
prop_exponentialDistribution, prop_exponentialDistribution', prop_poissonDistribution, prop_poissonDistribution', prop_shiftedGeometricDistribution, prop_shiftedGeometricDistribution' :: System.Random.RandomGen randomGen => randomGen -> Double -> Test.QuickCheck.Property
prop_exponentialDistribution randomGen lambda = Test.QuickCheck.label "prop_exponentialDistribution" . uncurry (&&) . ToolShed.Data.Pair.mirror (isWithinTolerance 10) . (
Math.Statistics.getMean &&& pred . Math.Statistics.getStandardDeviation --Both of which, having been normalised, should be zero.
) . (
normalise distribution :: [Double] -> [Double]
) . take 10000 $ Math.Probability.generatePopulation distribution randomGen where
distribution = Math.Probability.ExponentialDistribution . succ {-exclude zero-} $ abs lambda `max` 10 {-cap-}
prop_exponentialDistribution' randomGen lambda = lambda /= 0 ==> Test.QuickCheck.label "prop_exponentialDistribution'" . all (
>= (0 :: Double)
) . take 10 $ Math.Probability.generatePopulation (Math.Probability.ExponentialDistribution $ abs lambda) randomGen
prop_poissonDistribution randomGen lambda = Test.QuickCheck.label "prop_poissonDistribution" . uncurry (&&) . ToolShed.Data.Pair.mirror (isWithinTolerance 10) . (
Math.Statistics.getMean &&& pred . Math.Statistics.getStandardDeviation --Both of which, having been normalised, should be zero.
) . (
normalise distribution :: [Double] -> [Double]
) . take 1000 $ Math.Probability.generatePopulation distribution randomGen where
distribution = Math.Probability.PoissonDistribution . succ {-exclude zero-} $ abs lambda `max` 10 {-cap-}
prop_poissonDistribution' randomGen lambda = lambda /= 0 ==> Test.QuickCheck.label "prop_poissonDistribution'" . all (
>= (0 :: Double)
) . take 10 $ Math.Probability.generatePopulation (Math.Probability.PoissonDistribution $ abs lambda) randomGen
prop_shiftedGeometricDistribution randomGen probability = probability' /= 1 ==> Test.QuickCheck.label "prop_shiftedGeometricDistribution" . uncurry (&&) . ToolShed.Data.Pair.mirror (isWithinTolerance 10) . (
Math.Statistics.getMean &&& pred . Math.Statistics.getStandardDeviation --Both of which, having been normalised, should be zero.
) . (
normalise distribution :: [Double] -> [Double]
) . take 10000 $ Math.Probability.generatePopulation distribution randomGen where
probability' = recip . succ $ abs probability --Semi-closed unit-interval (0, 1].
distribution = Math.Probability.ShiftedGeometricDistribution probability'
prop_shiftedGeometricDistribution' randomGen probability = Test.QuickCheck.label "prop_shiftedGeometricDistribution'" . all (
>= (1 :: Double)
) . take 10 $ Math.Probability.generatePopulation (Math.Probability.ShiftedGeometricDistribution probability') randomGen where
probability' = recip . succ $ abs probability --Semi-closed unit-interval (0, 1].