HMarkov-1.0.0.3: src/Data/Markov/HMarkov/Helpers.hs
{-# LANGUAGE TemplateHaskell #-}
{-|
Module : Data.Markov.HMarkov.Helpers
Description : Helpers for Data.Markov.HMarkov
Copyright : (c) Sam Raker, 2016
License : BSD3
Maintainer : sam.raker@gmail.com
Stability : experimental
Portability : POSIX
-}
module Data.Markov.HMarkov.Helpers
(
-- * Data structures
CountMarkovMap(..)
, MarkovMap(..)
-- * Helper functions
, vApply
, vidx
, ded
, makeSlices
, nrmlz
, sumP
, pix
, getNext
, buildMap
, initMap
, updateMarkov
, toMarkovMap
) where
import Control.Lens
import Data.Maybe
import Data.Vector as V
-- | Map of counts
data CountMarkovMap a
= CMarkovMap (V.Vector a) (V.Vector (V.Vector Double)) -- ^ Map of counts
-- | Map of frequencies
data MarkovMap a = MarkovMap {
-- | Index of elements
_idx :: V.Vector a,
-- | Map of frequencies
_mMap :: V.Vector (V.Vector Double) }
deriving (Show)
makeLenses ''MarkovMap
-- | Apply a function to the first two elements in a vector and a third thing
vApply :: (a -> a -> b -> c) -> b -> V.Vector a -> c
vApply f x v = f (v V.! 0) (v V.! 1) x
-- | Get the index of an element in a vector
-- WARNING: will throw an error if `x` is not in `v`
vidx :: (Eq a) => a -> V.Vector a -> Int
vidx x v = fromJust $ V.elemIndex x v
-- | Deduplicate a vector
ded :: (Eq a) => V.Vector a -> V.Vector a
ded = V.foldl f V.empty where
f accm x = if V.elem x accm then accm else V.snoc accm x
-- | Initialize a CountMarkovMap from a vector
initMap :: (Eq a) => V.Vector a -> CountMarkovMap a
initMap xs = let d = ded xs
l = V.length xs in
CMarkovMap d (V.replicate l (V.replicate l 0))
-- | Update a CountMarkovMap
-- `a` and `b` should be sequential elements
updateMarkov :: (Eq a) => a -> a -> CountMarkovMap a -> CountMarkovMap a
updateMarkov a b (CMarkovMap i m) = CMarkovMap i $ over (ix (vidx a i) . ix (vidx b i)) (+ 1) m
-- | Make 2-element (vector) slices of a vector
makeSlices :: V.Vector a -> V.Vector (V.Vector a)
makeSlices xs = V.map (\i -> V.slice i 2 xs) $ V.enumFromN 0 (V.length xs - 1)
-- | Normalize a vector of doubles by dividing each element by the sum of the vector
nrmlz :: V.Vector Double -> V.Vector Double
nrmlz v = V.map (/ V.sum v) v
-- | Progressively sum elements in a vector of doubles, skipping over 0s
sumP :: V.Vector Double -> V.Vector Double
sumP v = fst $ V.foldl f (V.empty, 0.0) v where
f (accm, n) a = if a > 0 then (V.snoc accm (n + a), n + a) else (V.snoc accm 0, n)
-- | Convert a CountMarkovMap to a MarkovMap by normalizing and summing its elements
toMarkovMap :: CountMarkovMap a -> MarkovMap a
toMarkovMap (CMarkovMap ci cm) = MarkovMap ci $ V.map (sumP . nrmlz) cm
-- | Get the index of the first element in a vector of doubles that's less than or equal to
-- a value
pix :: Double -> V.Vector Double -> Int
pix x = V.ifoldr f 0 where
f i p a = if x <= p then i else a
-- | Generate the 'next' element from a MarkovMap
getNext :: (Eq a) => a -> Double -> MarkovMap a -> a
getNext t x m = (m ^. idx) V.! (pix x ((m ^. mMap) V.! (vidx t $ m ^. idx)))
-- | Build a MarkovMap from a vector of elements
buildMap :: (Eq a) => V.Vector a -> MarkovMap a
buildMap xs = toMarkovMap $ V.foldl (vApply updateMarkov) (initMap xs) (makeSlices xs)