{-# 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)