packages feed

dtmc-0.2.0.0: src/Dtmc/Distribution/Vector/Internal.hs

{- |
Module      : Dtmc.Distribution.Vector.Internal
Description : Unsafe carrier for dense distribution vectors.

The public smart constructor validates and canonicalises the simplex
invariant. Internal callers may use the constructor only when their operation
preserves that invariant up to floating-point error.
-}
module Dtmc.Distribution.Vector.Internal (
    DistributionVector (DistributionVector),
    unDistributionVector,
) where

import Dtmc.Distribution (
    Distribution (..),
 )
import Dtmc.State (
    FiniteState,
    finiteStates,
 )
import Dtmc.State.Internal (
    stateIndexInt,
 )
import Numeric.LinearAlgebra qualified as LA

{- | A state distribution vector whose coordinates follow the canonical order
of its finite state type. The internal constructor performs no validation.
-}
newtype DistributionVector state
    = -- | Wrap an unchecked probability vector.
      DistributionVector (LA.Vector Double)

-- Nominal role prevents coercion between distinct state types, including
-- state types with the same cardinality.
type role DistributionVector nominal

deriving instance Show (DistributionVector state)

{- | Return the stored probability vector unchanged. This performs no copy,
validation, clamping, or renormalisation.

Complexity: @O(1)@ time and @O(1)@ space.
-}
unDistributionVector ::
    DistributionVector state ->
    LA.Vector Double
unDistributionVector (DistributionVector vector) = vector

instance (FiniteState state) => Distribution (DistributionVector state) where
    type DistributionState (DistributionVector state) = state

    probabilityAt distribution state =
        unDistributionVector distribution `LA.atIndex` stateIndexInt state

    distributionWeights distribution =
        [ (state, weight)
        | (state, weight) <- zip finiteStates weights
        , weight /= 0
        ]
      where
        weights = LA.toList (unDistributionVector distribution)