···11{-# LANGUAGE DataKinds #-}
22{-# LANGUAGE TypeOperators #-}
33{-# LANGUAGE RankNTypes #-}
44-{-# LANGUAGE TypeApplications #-}
44+---------------------------------
55+-- |
66+-- Module : CHMM
77+-- Copyright : (C) Kaushik Chakraborty, 2019
88+-- License : Apache v2 (see the file LICENSE)
99+-- Maintainer : Kaushik Chakraborty <git@kaushikc.org>
1010+-- Stability : experimental
1111+--
1212+---------------------------------
513614module CHMM where
71588-import Prelude(undefined)
916import Papa
1010-1117import GHC.TypeNats
1218import Data.Proxy
1319import Linear.V
···1723-----------------------
18241925trajectory :: forall n t a . (KnownNat n, KnownNat t) => Proxy t -> V n a -> V (n ^ t) (V n a)
2020-trajectory p = V . Vec.fromList . fmap (V . Vec.fromList) . variateRep t . toList
2626+trajectory p = V . Vec.fromList . fmap (V . Vec.fromList) . variateRep t' . toList
2127 where
2222- t = fromIntegral $ natVal p
2828+ t' = fromIntegral $ natVal p
2929+
+193-83
src/hmm.hs
···33{-# LANGUAGE ScopedTypeVariables #-}
44{-# LANGUAGE TemplateHaskell #-}
55{-# LANGUAGE TypeOperators #-}
66+---------------------------------
77+-- |
88+-- Module : HMM
99+-- Copyright : (C) Kaushik Chakraborty, 2019
1010+-- License : Apache v2 (see the file LICENSE)
1111+-- Maintainer : Kaushik Chakraborty <git@kaushikc.org>
1212+-- Stability : experimental
1313+--
1414+---------------------------------
1515+616module HMM where
71788--- import Prelude (undefined)
99--- import System.IO (print)
1818+import Prelude (undefined)
1919+import System.IO (print)
1020import Text.Printf (printf)
11211222import Papa
···2535import qualified Numeric.LinearAlgebra as HMat
2636-----------------------------------------------------------------------------------
27372828--- | Type synonyms
3838+-- * Type synonyms
2939type R = Double
3040type TransitionModel (s :: Nat) a = V s (V s a)
3141type SensorDiagonal (s :: Nat) a = V s (V s a)
···3343type Message (s :: Nat) a = V s (V 1 a)
3444type Distribution (s :: Nat) a = V s a
35453636--- | Utility Functions
4646+-- * HMM
4747+4848+-- | Hidden Markov Models
4949+data HMM (s :: Nat) (t :: Nat) a b = HMM {
5050+ -- | prior distribution which is used as initial forward message
5151+ _prior :: Message s a,
5252+ -- | transition model with @s@ states as @sxs@ matrix
5353+ _tModel :: TransitionModel s a,
5454+ -- | evidence value vector, each index maps to the corresponding evidence values
5555+ _sDist :: V t b,
5656+ -- | sensor model with @t@ evidence values having @sxs@ diagonal matrix capturing their ditributions for each state @s@
5757+ _sModel :: SensorModel t s a
5858+ } deriving (Show)
5959+6060+-- |
6161+-- === __HMM lenses__
6262+--
6363+makeLenses ''HMM
6464+6565+-- ** Smart constructors
6666+mkHMM_ :: (KnownNat s, KnownNat t) => Maybe (Message s R) -- ^ Prior distribution on the initial state, @P(X0)@. If nothing then considered @(0.5,0.5)@
6767+ -> TransitionModel s R -- ^ Transition Model as @sxs@ matrix, @s@ being the number of states
6868+ -> V t b -- ^ evidence values vector map
6969+ -> V t (V s R) -- ^ a @txs@ matrix for each evidence value @t@, a vector capturing conditional probabilities for each state @s@
7070+ -> HMM s t R b
7171+mkHMM_ mp xs evs = let p = fromMaybe (V $ Vec.replicate (dim xs) 0.5) mp
7272+ in
7373+ HMM p xs evs . (scaled <$>)
7474+7575+-- | A default HMM where both transition model states and evidence variables have boolean support
7676+mkHMM :: KnownNat s => TransitionModel s R -> V 2 (V s R) -> HMM s 2 R Bool
7777+mkHMM ts = mkHMM_ Nothing ts (V $ Vec.fromList [True, False])
7878+7979+-- | A default HMM with one state variable having boolean support
8080+mkHMM1 :: V2 (V2 R) -> V2 (V2 R) -> HMM 2 2 R Bool
8181+mkHMM1 ts ss = let f = (_V #) . over mapped toV
8282+ in
8383+ uncurry mkHMM $ over both f (ts , ss)
8484+8585+-- | create a @sxs@ diagonal matrix with corresponding posterior probabilities from the sensor model of the @HMM@ for an input sensor value
8686+sensorDiagonal :: (KnownNat s, KnownNat t, Eq b)
8787+ => HMM s t a b
8888+ -> b -- ^ sensor value
8989+ -> Maybe (SensorDiagonal s a)
9090+sensorDiagonal hmm e = findIndexOf (sDist.folded) (== e) hmm >>= (\i -> hmm ^? sModel . ix i)
9191+9292+9393+-- * Utility Functions
37943895-- | Multiply each number by a constant such that the sum is 1.0
3996normalise :: (Fractional a, Foldable f, Functor f) => f a -> f a
···44101 in
45102 (/ s) <$> xs
461034747-toHM :: (KnownNat s, KnownNat t) => V s (V t R) -> HM.L s t
4848-toHM = HM.matrix . foldMap (Vec.toList . toVector)
4949-5050-fromHM :: (KnownNat s, KnownNat t) => HM.L s t -> V s (V t R)
5151-fromHM m = V $ V <$> Vec.fromList (fmap Vec.fromList $ HMat.toLists $ HM.extract m)
5252-104104+-- | inverse a square matrix
53105inverse :: forall s. (KnownNat s) => V s (V s R) -> V s (V s R)
54106inverse = fromHM . HM.inv . toHM
55107108108+-- | reverse contents of a 'Linear.V' vector
56109reverseV :: forall s a. (KnownNat s) => V s a -> V s a
57110reverseV = V . Vec.fromList . Papa.reverse . toList
5811159112extract1 :: (KnownNat s) => V s (V 1 a) -> V s a
60113extract1 = V . foldMap toVector
61114115115+-- | Pointwise product of 2 @mxn@ 'V' vectors
62116infix 8 !**!
63117(!**!) :: (KnownNat m, KnownNat n) => V m (V n R) -> V m (V n R) -> V m (V n R)
64118as !**! bs = fromHM $ toHM as * toHM bs
65119120120+-- | an unit vector having @s@ columns
66121unitColumn :: forall s. KnownNat s => Message s R
67122unitColumn = V $ Vec.replicate (fromIntegral $ natVal (Proxy :: Proxy s)) (toV $ V1 1.0)
68123124124+-- ** Converting to/from __HMatrix__ matrices
691257070--- | Hidden Markov Models
126126+-- | take a @sxt@ 'Linear.V.V' matrix and give corresponding 'Numeric.LinearAlgebra.Static.L' version
127127+toHM :: (KnownNat s, KnownNat t) => V s (V t R) -> HM.L s t
128128+toHM = HM.matrix . foldMap (Vec.toList . toVector)
711297272-data HMM (s :: Nat) (t :: Nat) a b = HMM {
7373- -- prior distribution which is used as initial forward message
7474- _prior :: Message s a,
7575- -- transition model with `s` states as `sxs` matrix
7676- _tModel :: TransitionModel s a,
7777- -- evidence value vector, each index maps to the corresponding evidence values
7878- _sDist :: V t b,
7979- -- sensor model with `t` evidence values having `sxs` diagonal matrix capturing their ditributions for each state `s`
8080- _sModel :: SensorModel t s a
8181- } deriving (Show)
8282-makeLenses ''HMM
130130+-- | take a @sxt@ 'Numeric.LinearAlgebra.Static.L' matrix and give corresponding 'Linear.V.V' version
131131+fromHM :: (KnownNat s, KnownNat t) => HM.L s t -> V s (V t R)
132132+fromHM m = V $ V <$> Vec.fromList (fmap Vec.fromList $ HMat.toLists $ HM.extract m)
83133841348585--- | Smart HMM constructors
135135+-- * Inference Algorithms
861368787--- | Make HMM
8888--- `mp :: Maybe (Message s a)` -- Prior distribution on the initial state, `P(X0)`. If nothing then considered `(0.5,0.5)`
8989--- `xs :: TransitionModel s a` -- Transition Model as `sxs` matrix, `s` being the number of states
9090--- `evs :: V t b` - evidence values vector map
9191--- `es :: V t (V s a)` -- a `txs` matrix for each evidence value `t`, a vector capturing conditional probabilities for each state `s`
9292-mkHMM_ :: (KnownNat s, KnownNat t) => Maybe (Message s R) -> TransitionModel s R -> V t b -> V t (V s R) -> HMM s t R b
9393-mkHMM_ mp xs evs = let p = fromMaybe (V $ Vec.replicate (dim xs) 0.5) mp
9494- in
9595- HMM p xs evs . (scaled <$>)
9696-9797--- | A default HMM where both transition model states and evidence variables have boolean support
9898-mkHMM :: KnownNat s => TransitionModel s R -> V 2 (V s R) -> HMM s 2 R Bool
9999-mkHMM ts = mkHMM_ Nothing ts (V $ Vec.fromList [True, False])
100100-101101--- | A default HMM with one state variable having boolean support
102102-mkHMM1 :: V2 (V2 R) -> V2 (V2 R) -> HMM 2 2 R Bool
103103-mkHMM1 ts ss = let f = (_V #) . over mapped toV
104104- in
105105- uncurry mkHMM $ over both f (ts , ss)
106106-107107-sensorDiagonal :: (KnownNat s, KnownNat t, Eq b) => HMM s t a b -> b -> Maybe (SensorDiagonal s a)
108108-sensorDiagonal hmm e = findIndexOf (sDist.folded) (== e) hmm >>= (\i -> hmm ^? sModel . ix i)
137137+-- ** Forward-Backward
109138110139-- | Filtering message propagated forward
111140forward :: (KnownNat s) => HMM s t R b -> SensorDiagonal s R -> Message s R -> Message s R
···115144backward :: (KnownNat s) => HMM s t R b -> SensorDiagonal s R -> Message s R -> Message s R
116145backward hmm ok1 bk2 = (hmm ^. tModel) !*! ok1 !*! bk2
117146147147+-- | The forward–backward algorithm for smoothing: computing posterior prob- abilities of a sequence of states given a sequence of observations
148148+forwardBackward :: forall s t u b . (KnownNat t, KnownNat s, KnownNat u, Eq b)
149149+ => HMM s u R b -- ^ HMM model as a way to implement
150150+ -> V t b -- ^ list of evidences for each time step
151151+ -> V t (Distribution s R)
152152+forwardBackward hmm evs = let
153153+ -- reifying the number of evidences
154154+ tNat = dim evs
155155+ -- forward messages from time t .. 0
156156+ fv :: V (t + 1) (Message s R)
157157+ fv = V $ Vec.fromList $ foldl' (\m@(x:_) e -> forward hmm (sensorDiagonal hmm e ^?! _Just) x : m) [hmm ^. prior] evs
158158+ -- getting rid of the prior message from the end of the list
159159+ -- so now forward messages vector is from time t .. 1
160160+ fv_0 :: V t (Message s R)
161161+ fv_0 = V $ Vec.fromList $ fv ^.. taking tNat traversed
118162119119--- | Fixed Lag Smoothing
163163+ -- backward messages from time 1 .. t
164164+ bs :: V t (Message s R)
165165+ bs = V $ Vec.fromList $ foldl' (\m@(x:_) e -> backward hmm (sensorDiagonal hmm e ^?! _Just) x : m) [unitColumn] $ reverseV evs
166166+ -- reversing the backward messages
167167+ -- so now the backward messages vector is from time t .. 1
168168+ revBs :: V t (Message s R)
169169+ revBs = reverseV bs
170170+ in
171171+ -- smoothing probabilities in reverse order of the list of evidences
172172+ -- i.e. starting from time t .. 1
173173+ liftA2 (\f b' -> extract1 $ normalise $ f !**! b') fv_0 revBs
174174+120175176176+-- ** Fixed Lag Smoothing
177177+178178+-- *** Data Types
121179-- | Persistent State
122180data Persistent (s :: Nat) a b d= Persistent {
123123- _t :: d,
124124- _f_msg :: Message s a,
125125- _b :: V s (V s a),
126126- _e_td_t :: Vec.Vector b
181181+ _t :: d, -- ^ current time
182182+ _f_msg :: Message s a, -- ^ the forward message @P(Xt|e1:t)@
183183+ _b :: V s (V s a), -- ^ the @d@-step backward transformation matrix
184184+ _e_td_t :: Vec.Vector b -- ^ double-ended list of evidence from @t − d@ to @t@
127185 } deriving (Show)
186186+187187+-- |
188188+-- === __Persistent lenses__
189189+--
128190makeLenses ''Persistent
129191130130--- | State to start with
192192+-- *** Smart constructor
193193+-- | Initial persistent state where
194194+--
195195+-- * t = 1
196196+--
197197+-- * f_msg = hmm.prior
198198+--
199199+-- * b = identity matrix
200200+--
201201+-- * e_td_t = empty vector
131202persistentInit :: forall s b d. (KnownNat s, Integral d) => Message s R -> Persistent s R b d
132203persistentInit p = Persistent { _t = 1, _f_msg = p, _b = identity, _e_td_t = Vec.empty}
133204134134--- | Online Algorithm for smoothing with a fixed time lag of `d` steps
135135--- hmm -- HMM model
136136--- d -- length of lag
137137--- e -- evidence at time t
138138-fixedLagSmoothing :: forall s u b d. (KnownNat s, KnownNat u, Eq b, Integral d) => HMM s u R b -> d -> b -> MaybeT (State (Persistent s R b d)) (Distribution s R)
205205+-- *** Algo
206206+-- | Online Algorithm for smoothing with a fixed time lag of @d@ steps
207207+fixedLagSmoothing :: forall s u b d. (KnownNat s, KnownNat u, Eq b, Integral d)
208208+ => HMM s u R b -- ^ HMM model
209209+ -> d -- ^ length of lag
210210+ -> b -- ^ evidence at time @t@
211211+ -> MaybeT (State (Persistent s R b d)) (Distribution s R)
139212fixedLagSmoothing hmm d e = do
140213 e_td_t %= flip Vec.snoc e
141214···159232 t += 1
160233 mzero
161234235235+-- ** [/WIP/] Cost based HMM(cHMM)
236236+-- Inference algo from cost-HMM paper
162237163163--- | The forward–backward algorithm for smoothing: computing posterior prob- abilities of a sequence of states given a sequence of observations
164164--- hmm - HMM model as a way to implement
165165--- evs - list of evidences for each time step
166166-forwardBackward :: forall s t u b . (KnownNat t, KnownNat s, KnownNat u, Eq b) => HMM s u R b -> V t b -> V t (Distribution s R)
167167-forwardBackward hmm evs = let
168168- -- reifying the number of evidences
169169- tNat = dim evs
170170- -- forward messages from time t .. 0
171171- fv :: V (t + 1) (Message s R)
172172- fv = V $ Vec.fromList $ foldl' (\m@(x:_) e -> forward hmm (sensorDiagonal hmm e ^?! _Just) x : m) [hmm ^. prior] evs
173173- -- getting rid of the prior message from the end of the list
174174- -- so now forward messages vector is from time t .. 1
175175- fv_0 :: V t (Message s R)
176176- fv_0 = V $ Vec.fromList $ fv ^.. taking tNat traversed
238238+cHMMInfer :: forall s u t b i. (KnownNat s, KnownNat u, KnownNat t, Eq b, Ord b, Integral i, Show i, Show b)
239239+ => HMM s u R b -- ^ HMM model
240240+ -> V t i -- ^ state trajectory for time @1 .. t@; represented as @0@-based index of state
241241+ -> V t b -- ^ evidence/observation trajectory for time @1 .. t@
242242+ -> IO R
243243+cHMMInfer hmm xs evs = do
244244+ let r = (\x y z -> (x * y) / z) <$> y0 <*> y1 <*> y2 $ zs
245245+ print zs
246246+ print $ y0 zs
247247+ print $ y1 zs
248248+ return r
249249+ where
250250+ y0 :: Vec.Vector (i, b) -> R
251251+ y0 = foldl' (\m (s,o') ->
252252+ m *
253253+ sensorDiagonal hmm o' ^?! _Just . ix (fromIntegral s) . ix (fromIntegral s))
254254+ 1
177255178178- -- backward messages from time 1 .. t
179179- bs :: V t (Message s R)
180180- bs = V $ Vec.fromList $ foldl' (\m@(x:_) e -> backward hmm (sensorDiagonal hmm e ^?! _Just) x : m) [unitColumn] $ reverseV evs
181181- -- reversing the backward messages
182182- -- so now the backward messages vector is from time t .. 1
183183- revBs :: V t (Message s R)
184184- revBs = reverseV bs
185185- in
186186- -- smoothing probabilities in reverse order of the list of evidences
187187- -- i.e. starting from time t .. 1
188188- liftA2 (\f b' -> extract1 $ normalise $ f !**! b') fv_0 revBs
256256+ y1 :: Vec.Vector (i, b) -> R
257257+ y1 vs = ifoldl (\idx m (s,_) ->
258258+ m *
259259+ bool
260260+ (hmm ^?! tModel . ix (vs ^?! ix (idx - 1) . _1 . to fromIntegral) . ix (fromIntegral s))
261261+ (((Linear.transpose $ hmm ^. prior) !*! hmm ^. tModel) ^?! ix 0 . ix (fromIntegral s))
262262+ (idx == 0))
263263+ 1 vs
264264+265265+ y2 :: KnownNat s => Vec.Vector (i, b) -> R
266266+ y2 vs = sum . extract1 $
267267+ ifoldl (\idx m (_,o') ->
268268+ bool
269269+ ((sensorDiagonal hmm o' ^?! _Just) !*! Linear.transpose (hmm ^. tModel) !*! m)
270270+ ((sensorDiagonal hmm o' ^?! _Just) !*! Linear.transpose (hmm ^. tModel) !*! hmm ^. prior)
271271+ (idx == 0)
272272+ )
273273+ (unitColumn :: Message s R)
274274+ vs
275275+276276+ zs :: Vec.Vector (i, b)
277277+ zs = Vec.zip (toVector xs) (toVector evs)
278278+279279+-- * Sample HMM Models
189280190190--- | execution
191281umbrellaHMM :: HMM 2 2 R Bool
192282umbrellaHMM = mkHMM1 (V2 (V2 0.7 0.3) (V2 0.3 0.7)) (V2 (V2 0.9 0.2) (V2 0.1 0.8))
283283+284284+swarmRobotsHMM :: HMM 4 4 R Int
285285+swarmRobotsHMM = mkHMM_
286286+ (Just $ V $ Vec.replicate 4 (toV $ V1 0.25))
287287+ (V $ Vec.fromList
288288+ [
289289+ (V $ Vec.fromList [0.7393859 , 0.2233470 , 0.0330670 , 0.0042001]),
290290+ (V $ Vec.fromList [0.0928202 , 0.7172515 , 0.1719556 , 0.0179727]),
291291+ (V $ Vec.fromList [0.0139341 , 0.1755173 , 0.6936519 , 0.1168966]),
292292+ (V $ Vec.fromList [0.0047083 , 0.0403520 , 0.2561893 , 0.6987504])
293293+ ])
294294+ (V $ Vec.fromList [1..4])
295295+ (Linear.transpose $ (V $ Vec.fromList
296296+ [
297297+ (V $ Vec.fromList [1.00000, 0.00000, 0.00000, 0.00000]),
298298+ (V $ Vec.fromList [0.10000, 0.90000, 0.00000, 0.00000]),
299299+ (V $ Vec.fromList [0.01000, 0.18000, 0.81000, 0.00000]),
300300+ (V $ Vec.fromList [0.00100, 0.02700, 0.24300, 0.72900])
301301+ ]))
193302303303+-- * Main
194304runFLSAlgo :: [Bool] -> Integer -> [Maybe (Distribution 2 String)]
195305runFLSAlgo bs d = let hmm = mkHMM1 (V2 (V2 0.7 0.3) (V2 0.3 0.7)) (V2 (V2 0.9 0.2) (V2 0.1 0.8))
196306 initState = persistentInit (V $ Vec.fromList [V $ Vec.singleton 0.5, V $ Vec.singleton 0.5]) :: Persistent 2 R Bool Integer
···202312203313204314runFBAlgo :: [Bool] -> [Distribution 2 R]
205205-runFBAlgo bs = reifyVectorNat (Vec.fromList bs) (toList . forwardBackward umbrellaHMM)
315315+runFBAlgo bs = reifyVectorNat (Vec.fromList bs) (toList . forwardBackward umbrellaHMM)