SciBaseTypes-0.0.0.1: Statistics/Probability.hs
-- | Probability-related types.
--
-- TODO instances for serialization and further stuff
-- TODO vector instances
module Statistics.Probability where
import Control.Lens
import Numeric.Log
import Data.Vector.Unboxed.Deriving
import Data.Vector.Unboxed (Unbox)
import Algebra.Structure.SemiRing
import Numeric.LogDomain
import Numeric.Limits
data IsNormalized = Normalized | NotNormalized
-- * Probability in linear space
-- | @Prob@ wraps a @Double@ that encodes probabilities. If @Prob@ is tagged as
-- @Normalized@, the contained values are in the range @[0,...,1]@, otherwise
-- they are in the range @[0,...,∞]@.
newtype Prob (n ∷ IsNormalized) x = Prob { getProb ∷ x }
deriving (Eq,Ord,Show,Read)
derivingUnbox "Prob"
[t| forall n x. Unbox x ⇒ Prob n x → x |] [| getProb |] [| Prob |]
deriving instance (Enum x) ⇒ Enum (Prob n x)
deriving instance (Num x) ⇒ Num (Prob n x)
deriving instance (Fractional x) ⇒ Fractional (Prob n x)
deriving instance (Floating x) ⇒ Floating (Prob n x)
deriving instance (Real x) ⇒ Real (Prob n x)
deriving instance (RealFrac x) ⇒ RealFrac (Prob n x)
deriving instance (RealFloat x) ⇒ RealFloat (Prob n x)
instance (Num r) ⇒ SemiRing (Prob n r) where
srplus = (+)
srmul = (*)
srzero = 0
srone = 1
-- | Turns a value into a normalized probability. @error@ if the value is not
-- in the range @[0,...,1]@.
prob ∷ (Ord x, Num x, Show x) ⇒ x → Prob Normalized x
prob x
| x >= 0 && x <= 1 = Prob x
| otherwise = error $ show x ++ " not in range of [0,...,1]"
{-# Inline prob #-}
-- | Simple wrapper around @Prob@ that fixes non-normalization.
prob' ∷ (Ord x, Num x, Show x) ⇒ x → Prob NotNormalized x
prob' = Prob
{-# Inline prob' #-}
-- * Probability in log space. A number of operations internally cast to @Log@
-- from @log-domain@, but the values themselves are *not* newtype-wrapped @Log
-- x@ values. This is because we want to be *explicit* that these are
-- log-probabilities.
--
-- @Log@ numbers in cases like @fromIntegral 1 :: Log Double@ are treated as
-- not being in the log-domain, hence @fromIntegral performs a @log@
-- operations.
newtype LogProb (n ∷ IsNormalized) x = LogProb { getLogProb ∷ x }
deriving (Eq,Ord,Show)
derivingUnbox "LogProb"
[t| forall n x. Unbox x ⇒ LogProb n x → x |] [| getLogProb |] [| LogProb |]
instance (Precise x, RealFloat x) ⇒ Num (LogProb n x) where
(+) = withLog2 (+)
(*) = withLog2 (*)
abs = withLog1 abs
signum = withLog1 signum
fromInteger = LogProb . fromInteger
negate = withLog1 negate
(-) = withLog2 (-)
instance (Num d, Fractional d) ⇒ NumericLimits (LogProb n d) where
minFinite = LogProb 0
maxFinite = LogProb (1/0)
withLog1 ∷ (Log x → Log y) → LogProb n x → LogProb n y
withLog1 op (LogProb x) = LogProb . ln $ op (Exp x)
{-# Inline withLog1 #-}
withLog2 ∷ (Log x → Log y → Log z) → LogProb n x → LogProb n y → LogProb n z
withLog2 op (LogProb x) (LogProb y) = LogProb . ln $ op (Exp x) (Exp y)
{-# Inline withLog2 #-}
-- * Conversion between probability in linear and log-space.
-- | Turn probability into log-probability.
p2lp ∷ (Floating x) ⇒ Prob n x → LogProb n x
p2lp (Prob x) = LogProb $ log x
{-# Inline p2lp #-}
-- | Turn log-probability into probability.
lp2p ∷ (Floating x) ⇒ LogProb n x → Prob n x
lp2p (LogProb x) = Prob $ exp x
{-# Inline lp2p #-}
-- | An isomorphism between @Prob@ and @LogProb@.
aslp ∷ (Floating x) ⇒ Iso' (Prob n x) (LogProb n x)
aslp = iso p2lp lp2p
{-# Inline aslp #-}