dtmc-0.2.0.0: src/Dtmc/Distribution/Map/Internal.hs
{- |
Module : Dtmc.Distribution.Map.Internal
Description : Unsafe carrier for map-backed distributions.
The public smart constructor validates and canonicalises the simplex
invariant. Internal callers may construct values only when their operation
preserves that invariant up to floating-point error.
-}
module Dtmc.Distribution.Map.Internal (
DistributionMap (DistributionMap),
unDistributionMap,
denseWeights,
) where
import Data.Map.Strict (
Map,
)
import Data.Map.Strict qualified as Map
import Dtmc.Distribution (
Distribution (..),
)
{- | A finite-support probability distribution backed by a strict map. The
internal constructor performs no validation.
-}
newtype DistributionMap state
= -- | Wrap an unchecked state-to-weight map.
DistributionMap (Map state Double)
type role DistributionMap nominal
deriving instance (Eq state) => Eq (DistributionMap state)
deriving instance (Show state) => Show (DistributionMap state)
{- | Return the canonical state-to-weight map without copying or validation.
Complexity: @O(1)@ time and @O(1)@ space.
-}
unDistributionMap :: DistributionMap state -> Map state Double
unDistributionMap (DistributionMap weights) = weights
{- | Return the weights of a map-backed distribution over a supplied ascending
state list, inserting exact zeros for absent states. Stored states absent from
the supplied list are ignored; lawful 'Dtmc.State.FiniteState' enumerations
contain every value of their state type.
Complexity: @O(n + s)@ time for @n@ requested states and stored support size
@s@, with @O(s)@ temporary space and @O(n)@ result space.
-}
denseWeights :: (Ord state) => [state] -> DistributionMap state -> [Double]
denseWeights states = align states . Map.toAscList . unDistributionMap
where
align [] _ = []
align remaining [] = replicate (length remaining) 0
align allStates@(state : rest) allWeights@((storedState, weight) : weights) =
case compare storedState state of
LT -> align allStates weights
EQ -> weight : align rest weights
GT -> 0 : align rest allWeights
instance Distribution (DistributionMap state) where
type DistributionState (DistributionMap state) = state
probabilityAt distribution state =
Map.findWithDefault 0 state (unDistributionMap distribution)
distributionWeights = Map.toAscList . unDistributionMap