packages feed

imp-ppl-0.1.0.0: src/Imp/Semiring.hs

-- | The 'Semiring' class and the four WMC semirings.
module Imp.Semiring
  ( Semiring(..)
  , ProbS(..)
  , DualS(..)
  , IntervalS(..)
  , PolyS(..)
  , sumS
  ) where

import Data.Bits ((.|.))
import qualified Data.Map.Strict as Map
import qualified Data.Vector as V

-- | The algebra WMC is parameterised over.
class Semiring a where
  zero :: a
  one  :: a
  (.+.) :: a -> a -> a
  (.*.) :: a -> a -> a

infixl 6 .+.
infixl 7 .*.

-- | Sum a list in the semiring.
sumS :: Semiring s => [s] -> s
sumS = foldl' (.+.) zero

-- | Probability semiring for precise WMC.
newtype ProbS = ProbS { unProb :: Double }
  deriving newtype (Show, Eq)

instance Semiring ProbS where
  zero = ProbS 0
  one  = ProbS 1
  ProbS a .+. ProbS b = ProbS (a + b)
  ProbS a .*. ProbS b = ProbS (a * b)

-- | Dual number semiring for forward-mode AD through WMC.
data DualS = DualS !Double !(V.Vector Double)
  deriving stock (Show, Eq)

instance Semiring DualS where
  zero = DualS 0 V.empty
  one  = DualS 1 V.empty
  DualS a da .+. DualS b db = DualS (a + b) (vplus da db)
  DualS a da .*. DualS b db =
    DualS (a * b) (vplus (vscale a db) (vscale b da))

vplus :: V.Vector Double -> V.Vector Double -> V.Vector Double
vplus a b
  | V.null a  = b
  | V.null b  = a
  | otherwise = V.zipWith (+) a b

vscale :: Double -> V.Vector Double -> V.Vector Double
vscale s v = V.map (* s) v

-- | Interval semiring for one-pass, sound WMC.
data IntervalS = IntervalS !Double !Double
  deriving stock (Show, Eq)

instance Semiring IntervalS where
  zero = IntervalS 0 0
  one  = IntervalS 1 1
  IntervalS a b .+. IntervalS c d = IntervalS (a + c) (b + d)
  IntervalS a b .*. IntervalS c d = IntervalS (a * c) (b * d)

-- | Polynomial semiring for symbolic WMC.
newtype PolyS = PolyS { unPoly :: Map.Map Integer Double }
  deriving stock (Show, Eq)

instance Semiring PolyS where
  zero = PolyS Map.empty
  one  = PolyS (Map.singleton 0 1)
  PolyS a .+. PolyS b = PolyS $ Map.filter (/= 0) $ Map.unionWith (+) a b
  PolyS a .*. PolyS b = PolyS $ Map.filter (/= 0) $ Map.fromListWith (+)
    [ (ma .|. mb, ca * cb)
    | (ma, ca) <- Map.toList a
    , (mb, cb) <- Map.toList b
    ]