directed-cubical-0.1.1.0: Math/Topology/CubeCmplx/DPTrace.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE BangPatterns #-}
-- | Module : Math.Topology.CubeCmplx.DPTrace
-- Copyright : 2014 Michael Misamore
-- License : BSD-style
-- Maintainer : m.misamore@gmail.com
-- Stability : experimental
-- Portability : portable
--
-- Model directed process traces in a directed cubical framework.
--
module Math.Topology.CubeCmplx.DPTrace (
-- * Resource requests
ResId, ReqType(..), ResReq,
-- * Process Traces
PID, PTrace, pTrace, pTraces,
-- * Modeling contention problems
ptsAmbReg, ptsForbRegs, ptsCmplx
) where
import Data.Function (on)
import Data.List (sort, groupBy, sortBy, transpose, (\\))
import Data.Ord (comparing)
import Data.Maybe (fromJust, catMaybes)
import Control.Arrow ((***))
import Control.Monad (guard)
import qualified Data.IntMap.Strict as M
(IntMap, empty, null, filter, lookup, insert, adjust)
import Math.Topology.CubeCmplx.DirCubeCmplx
-- Resource Requests --
-- | Type for resource IDs.
type ResId = Int
-- | Classical acquire/release notation.
data ReqType = P | V deriving (Show, Eq)
-- | Resource request consists of acquiring/releasing a given resource.
type ResReq = (ReqType, ResId)
-- | Valid list of resource requests.
data ResReqs = ResReqs { reqs :: ![ResReq], parity :: !(M.IntMap Bool) }
instance Eq ResReqs where r1 == r2 = (reqs r1) == (reqs r2)
instance Show ResReqs where show rs = show (reqs rs)
-- | Construct empty list of resource requests.
reqEmpty :: ResReqs
reqEmpty = ResReqs [] M.empty
-- | Given a resource request, add to list of resource requests
-- if permissible.
(<+>) :: ResReq -> ResReqs -> Maybe ResReqs
r@(t,i) <+> rs = case M.lookup i pty of
Nothing -> if t == V
then Just $ ResReqs (r : reqs rs)
(M.insert i True pty)
else Nothing
Just False -> if t == V then addElt else Nothing
Just True -> if t == P then addElt else Nothing
where pty = parity rs
addElt = Just $ ResReqs (r : reqs rs) (M.adjust not i pty)
-- | Fold up list of resource requests, validating pair counts.
reqList :: [ResReq] -> Maybe ResReqs
reqList rs = do rss <- bld rs
guard (M.null . M.filter (== True) $ parity rss)
return rss
where bld rs = foldr step (Just reqEmpty) rs
step r (Just rs) = r <+> rs
step r _ = Nothing
-- Process Traces --
-- | Type for process IDs.
type PID = Int
-- | Process trace is Process ID together with valid list of requests.
data PTrace = PTrace { ptId :: !PID, ptReqs :: !ResReqs } deriving (Show,Eq)
-- | Attempt to build valid process trace from ID and list of requests.
pTrace :: PID -> [ResReq] -> Maybe PTrace
pTrace id rs = do rss <- reqList rs
return $ PTrace id rss
-- | Attempt to build list of process traces with default process IDs
-- from list of lists of requests.
pTraces :: [[ResReq]] -> Maybe [PTrace]
pTraces = sequence . map (uncurry pTrace) . zip [1..]
-- | Given a process trace, output list of associated resource request points
-- ordered by time.
ptPts :: PTrace -> [ReqPt]
ptPts pt = zipWith3 reqPt (repeat $ ptId pt) rs [1..]
where rs = reqs . ptReqs $ pt
-- | Given a process trace, output list of lists of associated resource
-- request points grouped by resource id.
ptPtGrps :: PTrace -> [[ReqPt]]
ptPtGrps pt = groupBy ((==) `on` rpResId) .
sortBy (comparing rpResId) $ ptPts pt
-- Resource request points --
-- | Resource request points labeled by requesting process and time.
data ReqPt = ReqPt { pid :: !PID, res :: !ResReq, t :: !T } deriving (Eq)
-- | Pretty print resource request points.
instance Show ReqPt where show p = show (pid p, res p, t p)
-- | Ordered by time.
instance Ord ReqPt where p1 <= p2 = (t p1) <= (t p2)
-- | Construct a resource request point from a triple.
reqPt :: PID -> ResReq -> T -> ReqPt
reqPt p r t = ReqPt p r t
-- | Fetch resource id for a resource request point.
rpResId :: ReqPt -> ResId
rpResId = snd . res
-- Resource Traces --
-- | Resource trace: process, resource, and list of consumption intervals.
data ResTrace = ResTrace { rtPid :: PID, rtResId :: ResId, ivls :: [[T]] }
instance Show ResTrace where show rt = show (rtPid rt, rtResId rt, ivls rt)
-- | Given a process trace, output associated list of resource traces
-- for that process.
ptResTraces :: PTrace -> [ResTrace]
ptResTraces pt = zipWith3 ResTrace (repeat (ptId pt)) (map rid idgrps)
(map pairUp idgrps)
where idgrps = ptPtGrps pt
sg g = sortBy (comparing t) g
ts g = map t (sg g)
pairUp g = pairUp' (ts g) where
pairUp' (a:b:rs) = [[a,b]] ++ pairUp' rs
pairUp' [] = []
rid g = rpResId $ head g
-- | Given a list of process traces, output associated flattened list of
-- all associated resource traces.
ptsResTraces :: [PTrace] -> [ResTrace]
ptsResTraces = concatMap ptResTraces
-- | Group list of resource traces by resource id.
rtGrpRes :: [ResTrace] -> [[ResTrace]]
rtGrpRes rts = groupBy ((==) `on` rtResId) . sortBy (comparing rtResId) $ rts
-- Resource Competitions --
-- | Competition for a given resource.
type ResComp = [(PID, [[T]])]
-- | Given a list of process traces, get associated list of resource
-- competitions. In each, two or more processes compete for the same
-- resource.
resComps :: [PTrace] -> [ResComp]
resComps pts = map ts . filter ((>1).length) $ rts
where rts = rtGrpRes . ptsResTraces $ pts
ts g = zip (map rtPid g) (map ivls g)
-- | Given a resource competition and an ambient dimension n, determine the
-- maximal vertex spans it could generate in an ambient complex of dim n.
rcVertSpans :: Int -> ResComp -> [VertSpan]
rcVertSpans n rc = zipWith vsCoordsUnsafe
(map (map head) $ sequence mins)
(map (map last) $ sequence maxes)
where pids = map fst rc
opids = [1..n] \\ pids
minOs = zip opids (repeat [[0]])
maxOs = zip opids (repeat [[maxBound :: T]])
mins = map snd . sortBy (comparing fst) $ rc ++ minOs
maxes = map snd . sortBy (comparing fst) $ rc ++ maxOs
-- Modeling Contention Problems --
-- | Given a list of process traces, determine a minimal vertex span suitable
-- for modeling a resource contention problem.
ptsAmbReg :: [PTrace] -> VertSpan
ptsAmbReg pts = uncurry vsCoordsUnsafe . unzip $ map bds pts
where coords = map t . ptPts
bds = ((+(-1)) *** (+1)) .
foldr (\k (m,n) -> (min k m, max k n))
(maxBound :: T, minBound :: T) . coords
-- | Given a list of process traces, calculate associated list of
-- "forbidden regions" which represent resource contention. These regions
-- may overlap to form more complex regions.
ptsForbRegs :: [PTrace] -> [VertSpan]
ptsForbRegs pts = concatMap (rcVertSpans n) . resComps $ pts
where n = vsDim $ ptsAmbReg pts
-- | Given a list of process traces, represent the associated resource
-- contention problem by a finite directed cubical complex. The ordering
-- of the coordinates is the same as the ordering of the processes in
-- the list.
ptsCmplx :: [PTrace] -> CubeCmplx
ptsCmplx pts = cmplxVertOp cx (vertexUnsafe $ replicate (vsDim reg) 1) vAdd
where reg = ptsAmbReg pts
cx = foldr (flip cmplxDelVsInt) (vsCmplx reg) (ptsForbRegs pts)