{-# LANGUAGE Rank2Types #-} module LOAG.AOAG where import LOAG.Common import LOAG.Graphs import LOAG.Rep import AbstractSyntax import CommonTypes import Control.Arrow ((&&&), (***)) import Control.Monad (forM, forM_, MonadPlus(..), when, unless) import Control.Monad.ST import Control.Monad.Error (ErrorT(..)) import Control.Monad.State (MonadState(..)) import Data.Maybe (fromMaybe, catMaybes, fromJust, isNothing) import Data.List (elemIndex, foldl', delete, (\\), insert, nub) import Data.STRef import Data.Tuple (swap) import qualified Data.Set as S import qualified Data.IntSet as IS import qualified Data.IntMap as IM import qualified Data.Map as M import Data.Array.MArray import qualified Data.Array as A import Data.Array.ST import ErrorMessages as Err import Pretty import UU.Scanner.Position data Settings = Settings { -- current minimum ads size prune_val :: Int -- whether to minimize the number of fake dependencies -- could be very costly , min_ads :: Bool } default_settings = Settings 999 False type AOAG s a = ST s a -- | Catch a type 3 cycle-error made by a given constructor -- | two alternatives are given to proceed type ADS = [Edge] type AOAGRes = Either Error LOAGRes -- | Calculate a total order if the semantics given -- originate from a linearly-ordered AG type2error,limiterror,aoagerror :: Error type2error = Err.CustomError False noPos $ text "Type 2 cycle" limiterror = Err.CustomError False noPos $ text "Limit reached" aoagerror = Err.CustomError False noPos $ text "Not an LOAG/AOAG" schedule :: LOAGRep -> Grammar -> Ag -> [Edge] -> AOAGRes schedule sem gram@(Grammar _ _ _ _ dats _ _ _ _ _ _ _ _ _) ag@(Ag bounds_s bounds_p de nts) ads = runST $ aoag default_settings ads where -- get the maps from semantics and translate them to functions nmp = (nmp_LOAGRep_LOAGRep sem) ofld = (ofld_LOAGRep_LOAGRep sem) genA = gen_LOAGRep_LOAGRep sem inss = inss_LOAGRep_LOAGRep sem -- select candidates, using the edge that caused the cycle -- from the list of intra-thread dependencies -- (intra-visit dependencies without edges in ids) candidates :: Edge -> Cycle -> [Edge] -> [Edge] candidates _ c = foldr (\(f,t) acc -> if f `IS.member` c &&t `IS.member` c then (t,f):acc else acc) [] -- | Move occurrence to its corresponding attribute gen :: Vertex -> Vertex gen v = genA A.! v genEdge :: Edge -> Edge genEdge (f,t) = (gen f, gen t) -- | Decide for a given production edge whether the vertices -- belong to the same field siblings :: Edge -> Bool siblings (f, t) = ofld A.! f == ofld A.! t -- | Given an nonterminal-edge, instantiate it -- assumes that the occurrences of fields are added in the same order instEdge :: Edge -> [Edge] instEdge (f, t) = zip (inss A.! f) (inss A.! t) aoag :: Settings -> [Edge] -> AOAG s AOAGRes aoag cfg init_ads = run where run :: AOAG s AOAGRes run = induced ads >>= detect detect (Left err) = return $ Left err detect (Right (dp,idp,ids@(idsf,idst))) = do -- Attribute -> TimeSlot schedA <- mapArray (const Nothing) idsf -- map TimeSlot -> [Attribute] schedS <- newSTRef $ foldr (\(Nonterminal nt _ _ _ _) -> M.insert (getName nt) (IM.singleton 1 [])) M.empty dats fr_ids <- freeze_graph ids threads <- completing fr_ids (schedA, schedS) nts let (ivd, comp) = fetchEdges fr_ids threads nts eRoC <- m_edp dp init_ads ivd comp (schedA, schedS) case eRoC of Left res -> return $ Right res Right (e,c,T3 cs) -> find_ads dp idp ids (schedA, schedS) e c cs find_ads :: Graph s -> Graph s -> Graph s -> SchedRef s -> Edge -> Cycle -> [Edge] -> AOAG s AOAGRes find_ads dp idp ids sched e cycle comp = do pruner <- newSTRef 999 explore dp idp ids sched init_ads pruner e cycle comp explore :: Graph s -> Graph s -> Graph s -> SchedRef s -> [Edge] -> STRef s Int -> Edge -> Cycle -> [Edge] -> AOAG s AOAGRes explore dp idp ids sched@(schedA, schedS) ads pruner e c comp = explore' dp idp ids sched ads (candidates e c comp) pruner where explore' :: Graph s -> Graph s -> Graph s -> SchedRef s -> [Edge] -> [Edge] -> STRef s Int -> AOAG s AOAGRes explore' _ _ _ _ _ [] _ = return $ Left aoagerror explore' dp idp ids sched@(schedA,schedS) ads (fd:cs) pruner = do p_val <- readSTRef pruner if length ads >= p_val -1 then return $ Left limiterror else do idpf_clone <- mapArray id (fst idp) idpt_clone <- mapArray id (snd idp) let idp_c = (idpf_clone, idpt_clone) idsf_clone <- mapArray id (fst ids) idst_clone <- mapArray id (snd ids) let ids_c = (idsf_clone, idst_clone) schedA_c <- mapArray id schedA schedS_v <- readSTRef schedS schedS_c <- newSTRef schedS_v let sched_c = (schedA_c, schedS_c) let runM = reschedule dp idp ids sched (fd:ads) fd pruner let backtrack = explore' dp idp_c ids_c sched_c ads cs pruner maoag <- runM case maoag of Left _ -> backtrack Right (tdp1,inf1,ads1) -> if LOAG.AOAG.min_ads cfg then do writeSTRef pruner (length ads1) maoag' <- backtrack case maoag' of Right (tdp2,inf2,ads2) -> return $ Right (tdp2,inf2,ads2) otherwise -> return $ Right (tdp1,inf1,ads1) else return $ Right (tdp1,inf1,ads1) -- step 1, 2 and 3 induced :: [Edge] -> AOAG s (Either Error (Graph s, Graph s, Graph s)) induced ads = do dpf <- newArray bounds_p IS.empty dpt <- newArray bounds_p IS.empty idpf <- newArray bounds_p IS.empty idpt <- newArray bounds_p IS.empty idsf <- newArray bounds_s IS.empty idst <- newArray bounds_s IS.empty let ids = (idsf,idst) let idp = (idpf,idpt) let dp = (dpf ,dpt) inducing dp idp ids (de ++ ads) inducing :: Graph s -> Graph s -> Graph s -> [Edge] -> AOAG s (Either Error (Graph s, Graph s, Graph s)) inducing dp idp ids es = do res <- adds (addD dp idp ids) [] es case res of Left _ -> return $ Left $ type2error Right _ -> return $ Right (dp, idp, ids) addD :: Graph s -> Graph s -> Graph s -> Edge -> AOAG s (Either Error [Edge]) addD dp' idp' ids' e = do resd <- e `insErt` dp' resdp <- e `inserT` idp' case resdp of Right es -> adds (addN idp' ids') [] (e:es) Left c -> return $ Left $ type2error addI :: Graph s -> Graph s -> Edge -> AOAG s (Either Error [Edge]) addI idp' ids' e = do exists <- member e idp' if not exists then do res <- e `inserT` idp' case res of Right es -> adds (addN idp' ids') [] es Left c -> return $ Left $ type2error else return $ Right [] adds f acc [] = return $ Right acc adds f acc (e:es) = do mes <- f e case mes of Left err -> return $ Left err Right news -> adds f (acc++news) es addN :: Graph s -> Graph s -> Edge -> AOAG s (Either Error [Edge]) addN idp' ids' e = do if (siblings e) then do let s_edge = genEdge e exists <- member s_edge ids' if not exists then do _ <- inserT s_edge ids' let es = instEdge s_edge addedEx <- adds (addI idp' ids') [] es case addedEx of Right news -> return $ Right (s_edge : news) Left err -> return $ Left err else return $ Right [] else return $ Right [] -- step 6, 7 m_edp :: Graph s -> [Edge] -> [Edge] -> [Edge] -> SchedRef s -> AOAG s (Either LOAGRes (Edge,Cycle,CType)) m_edp (dpf, dpt) ads ivd comp sched = do edpf <- mapArray id dpf edpt <- mapArray id dpt mc <- addEDs (edpf,edpt) (concatMap instEdge ivd) case mc of Just (e, c) -> return $ Right (e,c,T3 $ concatMap instEdge comp) Nothing -> do tdp <- freeze edpt infs <- readSTRef (snd sched) return $ Left (Just tdp,infs,ads) reschedule :: Graph s -> Graph s -> Graph s -> SchedRef s -> [Edge] -> Edge -> STRef s Int -> AOAG s AOAGRes reschedule dp idp ids sched@(_,threadRef) ads e pruner = do extra <- addN idp ids e case extra of Left err -> return $ Left err Right extra -> do forM_ extra $ swap_ivd ids sched fr_ids <- freeze_graph ids threads <- readSTRef threadRef let (ivd, comp) = fetchEdges fr_ids threads nts eRoC <- m_edp dp ads ivd comp sched case eRoC of Left res -> return $ Right res Right (e,c,(T3 cs)) -> explore dp idp ids sched ads pruner e c cs where swap_ivd :: Graph s -> SchedRef s -> Edge -> AOAG s () swap_ivd ids@(idsf, idst) sr@(schedA, schedS) (f,t) = do --the edge should point from higher to lower timeslot assigned <- freeze schedA let oldf = maybe (error "unassigned f") id $ assigned A.! f oldt = maybe (error "unassigned t") id $ assigned A.! t dirf = snd $ alab $ nmp M.! f dirt = snd $ alab $ nmp M.! t newf | oldf < oldt = oldt + (if dirf /= dirt then 1 else 0) | otherwise = oldf nt = show $ typeOf $ nmp M.! f -- the edge was pointing in wrong direction so we moved -- the attribute to a new interaction, now some of its -- predecessors/ancestors might need to be moved too unless (oldf == newf) $ do writeArray schedA f (Just newf) modifySTRef schedS (M.adjust (IM.update (Just . delete f) oldf) nt) modifySTRef schedS (M.adjust(IM.alter(Just. maybe [f] (insert f))newf)nt) predsf <- readArray idst f succsf <- readArray idsf f let rest = (map (flip (,) f) $ IS.toList predsf) ++ (map ((,) f) $ IS.toList succsf) in mapM_ (swap_ivd ids sr) rest