dtmc-0.2.0.0: src/Dtmc/Distribution/Vector.hs
{- |
Module : Dtmc.Distribution.Vector
Description : Dense probability vectors over finite state types.
t'DistributionVector' stores a probability law over a 'FiniteState' type in an
hmatrix vector. Coordinates follow its canonical state order, so
'fromList' and 'toList' are a positional pair: both speak the same list of
weights, one coordinate per state. 'fromList' checks and canonicalises the
simplex invariant with the @1e-9@ tolerance documented by
'Dtmc.Simplex.SimplexError'.
To build a vector from /labelled/ weights, where duplicates should combine
and missing states should default to zero, use
'Dtmc.Distribution.Map.fromList' and read the coordinates off the result:
> Vector.fromList [probabilityAt m s | s <- finiteStates]
-}
module Dtmc.Distribution.Vector (
DistributionVector,
DistributionVectorError (..),
fromList,
toList,
) where
import Data.Bifunctor (
bimap,
)
import Dtmc.Distribution.Vector.Internal (
DistributionVector (DistributionVector),
unDistributionVector,
)
import Dtmc.Simplex (
SimplexError,
)
import Dtmc.Simplex.Internal (
canonicaliseSimplexEntries,
)
import Dtmc.State (
FiniteState,
)
import Dtmc.State.Internal (
stateCardinalityInt,
)
import Numeric.LinearAlgebra qualified as LA
{- | Why a list of weights was rejected as a state distribution.
-}
data DistributionVectorError
= -- | The state cardinality and the supplied number of weights.
WrongLength Int Int
| -- | The weights failed simplex validation. The coordinate index the
-- 'SimplexError' carries is zero-based in canonical state order.
InWeights SimplexError
deriving (Eq, Show)
{- | Construct a dense state distribution from one weight per state, in
canonical state order. The list must have exactly as many entries as the
state type has inhabitants; tolerated coordinate error is clamped to @[0, 1]@
and the repaired weights are normalised before storage. This is the exact
inverse of 'toList' up to that repair.
For a state type of cardinality zero the only accepted input is @[]@, which
is rejected as @Left (InWeights (SumOffBy 0))@: the empty simplex has no
points.
Complexity: @O(n)@ time and @O(n)@ temporary and result space for state
cardinality @n@.
-}
fromList ::
forall state.
(FiniteState state) =>
[Double] ->
Either DistributionVectorError (DistributionVector state)
fromList weights
| supplied /= dimension = Left (WrongLength dimension supplied)
| otherwise =
bimap InWeights (DistributionVector . LA.fromList) canonicalised
where
dimension = stateCardinalityInt @state
supplied = length weights
canonicalised = canonicaliseSimplexEntries weights
{- | Return every stored coordinate in canonical state order, including exact
zeros. This is a representation-neutral copy of the dense vector.
Complexity: @O(n)@ time and @O(n)@ temporary and result space for state
cardinality @n@.
-}
toList :: DistributionVector state -> [Double]
toList = LA.toList . unDistributionVector