module UHC.Light.Compiler.CHR.Solve
( CHRStore, emptyCHRStore
, chrStoreFromElems, chrStoreUnion, chrStoreUnions, chrStoreSingletonElem
, chrStoreToList, chrStoreElems
, ppCHRStore, ppCHRStore'
, SolveStep (..), SolveTrace
, ppSolveTrace
, SolveState, emptySolveState
, solveStateResetDone
, chrSolveStateDoneConstraints, chrSolveStateTrace
, chrSolve, chrSolve'
, chrSolve'' )
where
import UHC.Light.Compiler.CHR
import UHC.Light.Compiler.CHR.Constraint
import UHC.Light.Compiler.CHR.Key
import UHC.Light.Compiler.Substitutable
import UHC.Light.Compiler.VarLookup
import UHC.Light.Compiler.Base.Common
import UHC.Light.Compiler.Base.TreeTrie as TreeTrie
import qualified Data.Set as Set
import qualified Data.Map as Map
import Data.List as List
import Data.Maybe
import UHC.Util.Pretty as Pretty
import UHC.Util.Utils
import Data.Typeable (Typeable,Typeable1)
import Data.Generics (Data)
import UHC.Util.Serialize
import Control.Monad
type CHRTrie k v = TreeTrie.TreeTrie k v
type CHRTrieKey = TreeTrie.TreeTrieKey Key
type CHRLookupHow = TreeTrieLookup
chrLookupHowExact = TTL_Exact
chrLookupHowWildAtTrie = TTL_WildInTrie
chrLookupHowWildAtKey = TTL_WildInKey
emptyCHRTrie = TreeTrie.empty
ppCHRTrieKey :: CHRTrieKey -> PP_Doc
ppCHRTrieKey = ppTreeTrieKey
chrTrieFromListByKeyWith :: (v -> v -> v) -> [(CHRTrieKey,v)] -> CHRTrie Key v
chrTrieFromListByKeyWith = TreeTrie.fromListByKeyWith
chrTrieToListByKey :: CHRTrie Key v -> [(CHRTrieKey,v)]
chrTrieToListByKey = TreeTrie.toListByKey
chrTrieUnionWith :: (v -> v -> v) -> CHRTrie Key v -> CHRTrie Key v -> CHRTrie Key v
chrTrieUnionWith = TreeTrie.unionWith
chrTrieUnion :: CHRTrie Key v -> CHRTrie Key v -> CHRTrie Key v
chrTrieUnion = TreeTrie.union
chrTrieElems :: CHRTrie Key v -> [v]
chrTrieElems = TreeTrie.elems
chrTrieDeleteListByKey :: [CHRTrieKey] -> CHRTrie Key v -> CHRTrie Key v
chrTrieDeleteListByKey = TreeTrie.deleteListByKey
chrTrieFromListPartialExactWith :: (v -> v -> v) -> [(CHRTrieKey,v)] -> CHRTrie Key v
chrTrieFromListPartialExactWith = TreeTrie.fromListByKeyWith
chrTrieLookup' :: (CHRTrieKey -> v -> v') -> CHRLookupHow -> CHRTrieKey -> CHRTrie Key v -> ([v'],Maybe v')
chrTrieLookup' = TreeTrie.lookupPartialByKey'
chrTrieLookup :: CHRLookupHow -> CHRTrieKey -> CHRTrie Key v -> ([v],Maybe v)
chrTrieLookup = TreeTrie.lookupPartialByKey
chrToKey :: TTKeyable x => x -> CHRTrieKey
chrToKey = ttkFixate . toTTKey
chrToWorkKey :: TTKeyable x => x -> CHRTrieKey
chrToWorkKey = ttkFixate . toTTKey' (defaultTTKeyableOpts {ttkoptsVarsAsWild = False})
type CHRKey = CHRTrieKey
type UsedByKey = (CHRKey,Int)
data StoredCHR p i g s
= StoredCHR
{ storedChr :: !(CHR (Constraint p i) g s)
, storedKeyedInx :: !Int
, storedKeys :: ![Maybe CHRKey]
, storedIdent :: !UsedByKey
}
deriving (Typeable, Data)
storedSimpSz :: StoredCHR p i g s -> Int
storedSimpSz = chrSimpSz . storedChr
newtype CHRStore pred info guard subst
= CHRStore
{ chrstoreTrie :: CHRTrie Key [StoredCHR pred info guard subst]
}
deriving (Typeable, Data)
mkCHRStore trie = CHRStore trie
emptyCHRStore :: CHRStore pred info guard subst
emptyCHRStore = mkCHRStore emptyCHRTrie
cmbStoredCHRs :: [StoredCHR p i g s] -> [StoredCHR p i g s] -> [StoredCHR p i g s]
cmbStoredCHRs s1 s2
= map (\s@(StoredCHR {storedIdent=(k,nr)}) -> s {storedIdent = (k,nr+l)}) s1 ++ s2
where l = length s2
instance Show (StoredCHR p i g s) where
show _ = "StoredCHR"
ppStoredCHR :: (PP p, PP i, PP g) => StoredCHR p i g s -> PP_Doc
ppStoredCHR c@(StoredCHR {storedIdent=(idKey,idSeqNr)})
= storedChr c
>-< indent 2
(ppParensCommas
[ pp $ storedKeyedInx c
, pp $ storedSimpSz c
, "keys" >#< (ppBracketsCommas $ map (maybe (pp "?") ppCHRTrieKey) $ storedKeys c)
, "ident" >#< ppParensCommas [ppCHRTrieKey idKey,pp idSeqNr]
])
instance (PP p, PP i, PP g) => PP (StoredCHR p i g s) where
pp = ppStoredCHR
chrStoreFromElems :: (TTKeyable p) => [CHR (Constraint p i) g s] -> CHRStore p i g s
chrStoreFromElems chrs
= mkCHRStore
$ chrTrieFromListByKeyWith cmbStoredCHRs
[ (k,[StoredCHR chr i ks' (concat ks,0)])
| chr <- chrs
, let cs = chrHead chr
simpSz = chrSimpSz chr
ks = map chrToKey cs
, (c,k,i) <- zip3 cs ks [0..]
, let (ks1,(_:ks2)) = splitAt i ks
ks' = map Just ks1 ++ [Nothing] ++ map Just ks2
]
chrStoreSingletonElem :: (TTKeyable p) => CHR (Constraint p i) g s -> CHRStore p i g s
chrStoreSingletonElem x = chrStoreFromElems [x]
chrStoreUnion :: CHRStore p i g s -> CHRStore p i g s -> CHRStore p i g s
chrStoreUnion cs1 cs2 = mkCHRStore $ chrTrieUnionWith cmbStoredCHRs (chrstoreTrie cs1) (chrstoreTrie cs2)
chrStoreUnions :: [CHRStore p i g s] -> CHRStore p i g s
chrStoreUnions [] = emptyCHRStore
chrStoreUnions [s] = s
chrStoreUnions ss = foldr1 chrStoreUnion ss
chrStoreToList :: CHRStore p i g s -> [(CHRKey,[CHR (Constraint p i) g s])]
chrStoreToList cs
= [ (k,chrs)
| (k,e) <- chrTrieToListByKey $ chrstoreTrie cs
, let chrs = [chr | (StoredCHR {storedChr = chr, storedKeyedInx = 0}) <- e]
, not $ Prelude.null chrs
]
chrStoreElems :: CHRStore p i g s -> [CHR (Constraint p i) g s]
chrStoreElems = concatMap snd . chrStoreToList
ppCHRStore :: (PP p,PP g,PP i) => CHRStore p i g s -> PP_Doc
ppCHRStore = ppCurlysCommasBlock . map (\(k,v) -> ppCHRTrieKey k >-< indent 2 (":" >#< ppBracketsCommasBlock v)) . chrStoreToList
ppCHRStore' :: (PP p,PP g,PP i) => CHRStore p i g s -> PP_Doc
ppCHRStore' = ppCurlysCommasBlock . map (\(k,v) -> ppCHRTrieKey k >-< indent 2 (":" >#< ppBracketsCommasBlock v)) . chrTrieToListByKey . chrstoreTrie
type WorkTime = Int
initWorkTime :: WorkTime
initWorkTime = 0
type WorkKey = CHRKey
type WorkUsedInMap = Map.Map (Set.Set CHRKey) (Set.Set UsedByKey)
type WorkTrie p i = CHRTrie Key (Work p i)
data Work p i
= Work
{ workKey :: WorkKey
, workCnstr :: !(Constraint p i)
, workTime :: WorkTime
}
data WorkList p i
= WorkList
{ wlTrie :: !(WorkTrie p i)
, wlDoneSet :: !(Set.Set WorkKey)
, wlQueue :: !(AssocL WorkKey (Work p i))
, wlScanned :: !(AssocL WorkKey (Work p i))
, wlUsedIn :: !WorkUsedInMap
}
emptyWorkList = WorkList emptyCHRTrie Set.empty [] [] Map.empty
wlUsedInUnion :: WorkUsedInMap -> WorkUsedInMap -> WorkUsedInMap
wlUsedInUnion = Map.unionWith Set.union
instance Show (Work p i) where
show _ = "SolveWork"
instance (PP p,PP i) => PP (Work p i) where
pp w = pp $ workCnstr w
ppUsedByKey :: UsedByKey -> PP_Doc
ppUsedByKey (k,i) = ppCHRTrieKey k >|< "/" >|< i
mkWorkList :: (TTKeyable p) => WorkTime -> [Constraint p i] -> WorkList p i
mkWorkList wtm = flip (wlInsert wtm) emptyWorkList
wlToList :: WorkList p i -> [Constraint p i]
wlToList wl = map workCnstr $ chrTrieElems $ wlTrie wl
wlCnstrToIns :: (TTKeyable p) => WorkList p i -> [Constraint p i] -> AssocL WorkKey (Constraint p i)
wlCnstrToIns wl@(WorkList {wlDoneSet = ds}) inscs
= [(chrToWorkKey c,c) | c <- inscs, let k = chrToKey c, not (k `Set.member` ds)]
wlDeleteByKeyAndInsert' :: WorkTime -> [WorkKey] -> AssocL WorkKey (Constraint p i) -> WorkList p i -> WorkList p i
wlDeleteByKeyAndInsert' wtm delkeys inskeycs wl@(WorkList {wlQueue = wlq, wlTrie = wlt, wlDoneSet = ds})
= wl { wlQueue = Map.toList inswork ++ [ w | w@(k,_) <- wlq, not (k `elem` delkeys) ]
, wlTrie = instrie `chrTrieUnion` chrTrieDeleteListByKey delkeys wlt
, wlDoneSet = Map.keysSet inswork `Set.union` ds
}
where inswork = Map.fromList [ (k,Work k c wtm) | (k,c) <- inskeycs ]
instrie = chrTrieFromListPartialExactWith const $ Map.toList inswork
wlDeleteByKeyAndInsert :: (TTKeyable p) => WorkTime -> [WorkKey] -> [Constraint p i] -> WorkList p i -> WorkList p i
wlDeleteByKeyAndInsert wtm delkeys inscs wl
= wlDeleteByKeyAndInsert' wtm delkeys (wlCnstrToIns wl inscs) wl
wlInsert :: (TTKeyable p) => WorkTime -> [Constraint p i] -> WorkList p i -> WorkList p i
wlInsert wtm = wlDeleteByKeyAndInsert wtm []
data SolveStep p i g s
= SolveStep
{ stepChr :: CHR (Constraint p i) g s
, stepSubst :: s
, stepNewTodo :: [Constraint p i]
, stepNewDone :: [Constraint p i]
}
| SolveStats
{ stepStats :: Map.Map String PP_Doc
}
| SolveDbg
{ stepPP :: PP_Doc
}
type SolveTrace p i g s = [SolveStep p i g s]
instance Show (SolveStep p i g s) where
show _ = "SolveStep"
instance (PP p, PP i, PP g) => PP (SolveStep p i g s) where
pp (SolveStep step _ todo done) = "STEP" >#< (step >-< "new todo:" >#< ppBracketsCommas todo >-< "new done:" >#< ppBracketsCommas done)
pp (SolveStats stats ) = "STATS" >#< (ppAssocLV (Map.toList stats))
pp (SolveDbg p ) = "DBG" >#< p
ppSolveTrace :: (PP s, PP p, PP i, PP g) => SolveTrace p i g s -> PP_Doc
ppSolveTrace tr = ppBracketsCommasBlock [ pp st | st <- tr ]
type SolveCount a b = Map.Map a (Map.Map b Int)
scntUnion :: (Ord a,Ord b) => SolveCount a b -> SolveCount a b -> SolveCount a b
scntUnion = Map.unionWith (Map.unionWith (+))
scntInc :: (Ord a,Ord b) => a -> b -> SolveCount a b -> SolveCount a b
scntInc a b c1
= Map.singleton a (Map.singleton b 1) `scntUnion` c1
type SolveMatchCache p i g s = Map.Map WorkKey [((StoredCHR p i g s,([WorkKey],[Work p i])),s)]
type LastQueryW = Map.Map WorkKey WorkTime
type LastQuery = Map.Map CHRKey LastQueryW
emptyLastQuery :: LastQuery
emptyLastQuery = Map.empty
lqSingleton :: CHRKey -> Set.Set WorkKey -> WorkTime -> LastQuery
lqSingleton ck wks wtm = Map.singleton ck $ Map.fromList [ (w,wtm) | w <- Set.toList wks ]
lqUnion :: LastQuery -> LastQuery -> LastQuery
lqUnion = Map.unionWith Map.union
lqLookupC :: CHRKey -> LastQuery -> LastQueryW
lqLookupC = Map.findWithDefault Map.empty
lqLookupW :: WorkKey -> LastQueryW -> WorkTime
lqLookupW = Map.findWithDefault initWorkTime
data SolveState p i g s
= SolveState
{ stWorkList :: !(WorkList p i)
, stDoneCnstrSet :: !(Set.Set (Constraint p i))
, stTrace :: SolveTrace p i g s
, stCountCnstr :: SolveCount WorkKey String
, stMatchCache :: !(SolveMatchCache p i g s)
, stHistoryCount :: WorkTime
, stLastQuery :: LastQuery
}
stDoneCnstrs :: SolveState p i g s -> [Constraint p i]
stDoneCnstrs = Set.toList . stDoneCnstrSet
emptySolveState :: SolveState p i g s
emptySolveState = SolveState emptyWorkList Set.empty [] Map.empty Map.empty initWorkTime emptyLastQuery
solveStateResetDone :: SolveState p i g s -> SolveState p i g s
solveStateResetDone s = s {stDoneCnstrSet = Set.empty}
chrSolveStateDoneConstraints :: SolveState p i g s -> [Constraint p i]
chrSolveStateDoneConstraints = stDoneCnstrs
chrSolveStateTrace :: SolveState p i g s -> SolveTrace p i g s
chrSolveStateTrace = stTrace
chrSolve
:: ( Ord i, Ord p
, CHRMatchable env p s, CHRCheckable env g s
, VarLookupCmb s s
, VarUpdatable s s, VarUpdatable g s, VarUpdatable i s, VarUpdatable p s
, CHREmptySubstitution s
, Ord (Constraint p i)
)
=> env
-> CHRStore p i g s
-> [Constraint p i]
-> [Constraint p i]
chrSolve env chrStore cnstrs
= work ++ done
where (work,done,_) = chrSolve' env chrStore cnstrs
chrSolve'
:: ( Ord i, Ord p
, CHRMatchable env p s, CHRCheckable env g s
, VarLookupCmb s s
, VarUpdatable s s, VarUpdatable g s, VarUpdatable i s, VarUpdatable p s
, CHREmptySubstitution s
, Ord (Constraint p i)
)
=> env
-> CHRStore p i g s
-> [Constraint p i]
-> ([Constraint p i],[Constraint p i],SolveTrace p i g s)
chrSolve' env chrStore cnstrs
= (wlToList (stWorkList finalState), stDoneCnstrs finalState, stTrace finalState)
where finalState = chrSolve'' env chrStore cnstrs emptySolveState
chrSolve''
:: forall env p i g s .
( Ord i, Ord p
, CHRMatchable env p s, CHRCheckable env g s
, VarLookupCmb s s
, VarUpdatable s s, VarUpdatable g s, VarUpdatable i s, VarUpdatable p s
, CHREmptySubstitution s
, Ord (Constraint p i)
)
=> env
-> CHRStore p i g s
-> [Constraint p i]
-> SolveState p i g s
-> SolveState p i g s
chrSolve'' env chrStore cnstrs prevState
= postState {stMatchCache = Map.empty}
where postState
= st
where st = iter $ initState prevState
iter st@(SolveState {stWorkList = wl@(WorkList {wlQueue = (workHd@(workHdKey,_) : workTl)})})
= case matches of
(_:_)
-> expandMatch stmatch matches
where
expandMatch st@(SolveState {stWorkList = wl, stHistoryCount = histCount})
( ( ( schr@(StoredCHR {storedIdent = chrId, storedChr = chr@(CHR {chrBody = b, chrSimpSz = simpSz})})
, (keys,works)
)
, subst
) : tlMatch
)
= expandMatch st' tlMatchY
where (tlMatchY,tlMatchN) = partition (\(r@(_,(ks,_)),_) -> not (any (`elem` keysSimp) ks || slvIsUsedByPropPart (wlUsedIn wl') r)) tlMatch
(keysSimp,keysProp) = splitAt simpSz keys
usedIn = Map.singleton (Set.fromList keysProp) (Set.singleton chrId)
(bTodo,bDone) = splitDone $ map (varUpd subst) b
bTodo' = wlCnstrToIns wl bTodo
wl' = wlDeleteByKeyAndInsert' histCount keysSimp bTodo'
$ wl { wlUsedIn = usedIn `wlUsedInUnion` wlUsedIn wl
, wlScanned = []
, wlQueue = wlQueue wl ++ wlScanned wl
}
st' = st { stWorkList = wl'
, stDoneCnstrSet = Set.unions [Set.fromList bDone, Set.fromList $ map workCnstr $ take simpSz works, stDoneCnstrSet st]
, stMatchCache = if List.null bTodo' then stMatchCache st else Map.empty
, stHistoryCount = histCount + 1
}
expandMatch st _
= iter st
_ -> iter st'
where wl' = wl { wlScanned = workHd : wlScanned wl, wlQueue = workTl }
st' = stmatch { stWorkList = wl', stTrace = SolveDbg (ppdbg) : stTrace stmatch }
where (matches,lastQuery,ppdbg,stats) = workMatches st
stmatch =
(st { stCountCnstr = scntInc workHdKey "workMatched" $ stCountCnstr st
, stMatchCache = Map.insert workHdKey [] (stMatchCache st)
, stLastQuery = lastQuery
})
iter st
= st
mkStats stats new = stats `Map.union` Map.fromList (assocLMapKey showPP new)
addStats _ _ st = st
workMatches st@(SolveState {stWorkList = WorkList {wlQueue = (workHd@(workHdKey,Work {workTime = workHdTm}) : _), wlTrie = wlTrie, wlUsedIn = wlUsedIn}, stHistoryCount = histCount, stLastQuery = lastQuery})
| isJust mbInCache = ( fromJust mbInCache
, lastQuery
, Pretty.empty, mkStats Map.empty [("cache sz",pp (Map.size (stMatchCache st)))]
)
| otherwise = ( r5
, foldr lqUnion lastQuery [ lqSingleton ck wks histCount | (_,(_,(ck,wks))) <- r23 ]
, Pretty.empty
, Map.empty
)
where
mbInCache = Map.lookup workHdKey (stMatchCache st)
r2 :: [StoredCHR p i g s]
r2 = concat
$ TreeTrie.lookupResultToList
$ chrTrieLookup chrLookupHowWildAtTrie workHdKey
$ chrstoreTrie chrStore
r23 :: [( StoredCHR p i g s
, ( [( [(CHRKey, Work p i)]
, [(CHRKey, Work p i)]
)]
, (CHRKey, Set.Set CHRKey)
) )]
r23 = map (\c -> (c, slvCandidate workHdKey lastQuery wlTrie c)) r2
r3, r4
:: [( StoredCHR p i g s
, ( [CHRKey]
, [Work p i]
) )]
r3 = concatMap (\(c,cands) -> zip (repeat c) (map unzip $ slvCombine cands)) $ r23
r4 = filter (not . slvIsUsedByPropPart wlUsedIn) r3
r5 :: [( ( StoredCHR p i g s
, ( [CHRKey]
, [Work p i]
) )
, s
)]
r5 = mapMaybe (\r@(chr,kw@(_,works)) -> fmap (\s -> (r,s)) $ slvMatch env chr (map workCnstr works)) r4
initState st = st { stWorkList = wlInsert (stHistoryCount st) wlnew $ stWorkList st, stDoneCnstrSet = Set.unions [Set.fromList done, stDoneCnstrSet st] }
where (wlnew,done) = splitDone cnstrs
splitDone = partition cnstrRequiresSolve
slvCandidate
:: CHRKey
-> LastQuery
-> WorkTrie p i
-> StoredCHR p i g s
-> ( [( [(CHRKey, Work p i)]
, [(CHRKey, Work p i)]
)]
, (CHRKey, Set.Set CHRKey)
)
slvCandidate workHdKey lastQuery wlTrie (StoredCHR {storedIdent = (ck,_), storedKeys = ks, storedChr = chr})
= ( map (maybe (lkup chrLookupHowExact workHdKey) (lkup chrLookupHowWildAtKey)) ks
, ( ck
, Set.fromList $ map (maybe workHdKey id) ks
) )
where lkup how k = partition (\(_,w) -> workTime w < lastQueryTm) $ map (\w -> (workKey w,w)) $ TreeTrie.lookupResultToList $ chrTrieLookup how k wlTrie
where lastQueryTm = lqLookupW k $ lqLookupC ck lastQuery
slvCombine :: Eq k => ([([Assoc k v], [Assoc k v])], t) -> [AssocL k v]
slvCombine ([],_) = []
slvCombine ((lh:lt),_)
= concatMap combineToDistinguishedElts l2
where l2 = g2 [] lh lt
where g2 ll l [] = [mk ll l []]
g2 ll l lr@(lrh:lrt) = mk ll l lr : g2 (ll ++ [l]) lrh lrt
mk ll (bef,aft) lr = map fst ll ++ [aft] ++ map cmb lr
where cmb (a,b) = a++b
slvIsUsedByPropPart
:: Ord k
=> Map.Map (Set.Set k) (Set.Set UsedByKey)
-> (StoredCHR p i g s, ([k], t))
-> Bool
slvIsUsedByPropPart wlUsedIn (chr,(keys,_))
= fnd $ drop (storedSimpSz chr) keys
where fnd k = maybe False (storedIdent chr `Set.member`) $ Map.lookup (Set.fromList k) wlUsedIn
slvMatch
:: ( CHREmptySubstitution s
, CHRMatchable env p s
, CHRCheckable env g s
, VarLookupCmb s s
)
=> env -> StoredCHR p i g s -> [Constraint p i] -> Maybe s
slvMatch env chr cnstrs
= foldl cmb (Just chrEmptySubst) $ matches chr cnstrs ++ checks chr
where matches (StoredCHR {storedChr = CHR {chrHead = hc}}) cnstrs
= zipWith mt hc cnstrs
where mt cFr cTo subst = chrMatchTo env subst cFr cTo
checks (StoredCHR {storedChr = CHR {chrGuard = gd}})
= map chk gd
where chk g subst = chrCheck env subst g
cmb (Just s) next = fmap (|+> s) $ next s
cmb _ _ = Nothing
instance (Serialize p, Serialize i, Serialize g, Serialize s) => Serialize (CHRStore p i g s) where
sput (CHRStore a) = sput a
sget = liftM CHRStore sget
instance (Serialize p, Serialize i, Serialize g, Serialize s) => Serialize (StoredCHR p i g s) where
sput (StoredCHR a b c d) = sput a >> sput b >> sput c >> sput d
sget = liftM4 StoredCHR sget sget sget sget