crf-chain2-tiers-0.3.0: src/Data/CRF/Chain2/Tiers/Model.hs
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE BangPatterns #-}
module Data.CRF.Chain2.Tiers.Model
(
-- * Model
Model (..)
, mkModel
, fromSet
, fromMap
, toMap
-- * Potential
, phi
, index
) where
import Control.Applicative ((<$>), (<*>))
import Control.Monad (guard)
import Data.Binary (Binary, get, put)
import Data.Int (Int32)
import qualified Data.Set as S
import qualified Data.Map as M
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as U
import Data.Vector.Binary ()
import qualified Data.Number.LogFloat as L
import Data.CRF.Chain2.Tiers.Dataset.Internal
import Data.CRF.Chain2.Tiers.Feature
import qualified Data.CRF.Chain2.Tiers.Array as A
-- | Dummy feature index.
dummy :: FeatIx
dummy = FeatIx (-1)
{-# INLINE dummy #-}
----------------------------------------------------
-- (Feature -> FeatIx) map
----------------------------------------------------
-- | Transition map restricted to a particular tagging layer.
-- Transitions of x form.
type T1Map = A.Array Lb FeatIx
-- | Transition map restricted to a particular tagging layer.
-- Transitions of (x, y) form.
type T2Map = A.Array (Lb, Lb) FeatIx
-- | Transition map restricted to a particular tagging layer.
-- Transitions of (x, y, z) form.
type T3Map = A.Array (Lb, Lb, Lb) FeatIx
mkT3Map :: [(Feat, FeatIx)] -> T3Map
mkT3Map xs =
let ys = [((x, y, z), ix) | (TFeat3 x y z _, ix) <- xs]
in A.mkArray dummy ys
mkT2Map :: [(Feat, FeatIx)] -> T2Map
mkT2Map xs =
let ys = [((x, y), ix) | (TFeat2 x y _, ix) <- xs]
in A.mkArray dummy ys
mkT1Map :: [(Feat, FeatIx)] -> T1Map
mkT1Map xs =
let ys = [(x, ix) | (TFeat1 x _, ix) <- xs]
in A.mkArray dummy ys
unT3Map :: Int -> T3Map -> [(Feat, FeatIx)]
unT3Map k t3 =
[ (TFeat3 x y z k, ix)
| ((x, y, z), ix) <- A.unArray t3
, ix /= dummy ]
unT2Map :: Int -> T2Map -> [(Feat, FeatIx)]
unT2Map k t2 =
[ (TFeat2 x y k, ix)
| ((x, y), ix) <- A.unArray t2
, ix /= dummy ]
unT1Map :: Int -> T1Map -> [(Feat, FeatIx)]
unT1Map k t1 =
[ (TFeat1 x k, ix)
| (x, ix) <- A.unArray t1
, ix /= dummy ]
-- | Observation map restricted to a particular tagging layer.
data OMap = OMap {
-- | Where memory ranges related to
-- individual observations begin?
oBeg :: U.Vector Int32
-- | Labels related to individual observations.
, oLb :: U.Vector Lb
-- | Feature indices related to individual (Ob, Lb) pairs.
, oIx :: U.Vector FeatIx }
instance Binary OMap where
put OMap{..} = put oBeg >> put oLb >> put oIx
get = OMap <$> get <*> get <*> get
mkOMap :: [(Feat, FeatIx)] -> OMap
mkOMap xs = OMap
{ oBeg = U.fromList $ scanl (+) 0
[ fromIntegral (M.size lbMap)
| ob <- map mkOb [0 .. maxOb]
, let lbMap = maybe M.empty id $ M.lookup ob ftMap ]
, oLb = U.fromList . concat $
[ M.keys lbMap
| lbMap <- M.elems ftMap ]
, oIx = U.fromList . concat $
[ M.elems lbMap
| lbMap <- M.elems ftMap ] }
where
-- A feature map of type Map Ob (Map Lb FeatIx).
ftMap = fmap M.fromList $ M.fromListWith (++)
[ (ob, [(x, ix)])
| (OFeat ob x _, ix) <- xs ]
-- Max observation
maxOb = unOb . fst $ M.findMax ftMap
-- | Deconstruct observation feature map given the layer identifier.
unOMap :: Int -> OMap -> [(Feat, FeatIx)]
unOMap k OMap{..} =
[ (OFeat o x k, i)
| (o, (p, q)) <- zip
(map mkOb [0..])
(pairs . map fromIntegral $ U.toList oBeg)
, (x, i) <- zip
(U.toList $ U.slice p (q - p) oLb)
(U.toList $ U.slice p (q - p) oIx) ]
where
pairs xs = zip xs (tail xs)
-- | Feature map restricted to a particular layer.
data LayerMap = LayerMap
{ t1Map :: !T1Map
, t2Map :: !T2Map
, t3Map :: !T3Map
, obMap :: !OMap }
instance Binary LayerMap where
put LayerMap{..} = put t1Map >> put t2Map >> put t3Map >> put obMap
get = LayerMap <$> get <*> get <*> get <*> get
-- | Deconstruct the layer map given the layer identifier.
unLayerMap :: Int -> LayerMap -> [(Feat, FeatIx)]
unLayerMap k LayerMap{..} = concat
[ unT1Map k t1Map
, unT2Map k t2Map
, unT3Map k t3Map
, unOMap k obMap ]
-- | Feature map is a vectro of layer maps.
type FeatMap = V.Vector LayerMap
-- | Get index of a feature.
featIndex :: Feat -> FeatMap -> Maybe FeatIx
featIndex (TFeat3 x y z k) v = do
m <- t3Map <$> (v V.!? k)
ix <- m A.!? (x, y, z)
guard (ix /= dummy)
return ix
featIndex (TFeat2 x y k) v = do
m <- t2Map <$> (v V.!? k)
ix <- m A.!? (x, y)
guard (ix /= dummy)
return ix
featIndex (TFeat1 x k) v = do
m <- t1Map <$> (v V.!? k)
ix <- m A.!? x
guard (ix /= dummy)
return ix
featIndex (OFeat ob x k) v = do
OMap{..} <- obMap <$> (v V.!? k)
p <- fromIntegral <$> oBeg U.!? (unOb ob)
q <- fromIntegral <$> oBeg U.!? (unOb ob + 1)
i <- U.findIndex (==x) (U.slice p (q - p) oLb)
ix <- oIx U.!? (p + i)
-- guard (ix /= dummy)
return ix
-- | Make feature map from a *set* of (feature, index) pairs.
mkFeatMap :: [(Feat, FeatIx)] -> FeatMap
mkFeatMap xs = V.fromList
-- TODO: We can first divide features between individual layers.
[ mkLayerMap $ filter (inLayer k . fst) xs
| k <- [0 .. maxLayerNum] ]
where
-- Make layer map.
mkLayerMap = LayerMap
<$> mkT1Map
<*> mkT2Map
<*> mkT3Map
<*> mkOMap
-- Number of layers (TODO: could be taken as mkFeatMap argument).
maxLayerNum = maximum $ map (ln.fst) xs
-- Check if feature is in a given layer.
inLayer k x | ln x == k = True
| otherwise = False
-- | Deconstruct the feature map.
unFeatMap :: FeatMap -> [(Feat, FeatIx)]
unFeatMap fm = concat
[ unLayerMap i layer
| (i, layer) <- zip [0..] (V.toList fm) ]
----------------------------------------------------
-- Internal model
----------------------------------------------------
-- | Internal model data.
data Model = Model
{ values :: U.Vector Double
, featMap :: FeatMap }
instance Binary Model where
put Model{..} = put values >> put featMap
get = Model <$> get <*> get
-- | Construct model from a feature set.
-- All values will be set to 1 in log domain.
fromSet :: S.Set Feat -> Model
fromSet ftSet = Model
{ values = U.replicate (S.size ftSet) 0.0
, featMap =
let featIxs = map FeatIx [0..]
featLst = S.toList ftSet
in mkFeatMap (zip featLst featIxs) }
-- | Construct model from a dataset given a feature selection function.
mkModel :: FeatSel -> [(Xs, Ys)] -> Model
mkModel ftSel = fromSet . S.fromList . concatMap (uncurry ftSel)
-- | Construct model from a (feature -> value) map.
fromMap :: M.Map Feat L.LogFloat -> Model
fromMap ftMap = Model
{ values = U.fromList . map L.logFromLogFloat $ M.elems ftMap
, featMap =
let featIxs = map FeatIx [0..]
featLst = M.keys ftMap
in mkFeatMap (zip featLst featIxs) }
-- | Convert model to a (feature -> value) map.
toMap :: Model -> M.Map Feat L.LogFloat
toMap Model{..} = M.fromList
[ (ft, L.logToLogFloat (values U.! unFeatIx ix))
| (ft, ix) <- unFeatMap featMap ]
----------------------------------------------------
-- Potential
----------------------------------------------------
-- | Potential assigned to the feature -- exponential of the
-- corresonding parameter.
phi :: Model -> Feat -> L.LogFloat
phi Model{..} ft = case featIndex ft featMap of
Just ix -> L.logToLogFloat (values U.! unFeatIx ix)
Nothing -> L.logToLogFloat (0 :: Double)
{-# INLINE phi #-}
-- | Index of a feature.
index :: Model -> Feat -> Maybe FeatIx
index Model{..} ft = featIndex ft featMap
{-# INLINE index #-}