packages feed

inch-0.1.0: src/Language/Inch/Solver.lhs

> {-# LANGUAGE GADTs, TypeOperators, FlexibleContexts, PatternGuards,
>              RankNTypes #-}

> module Language.Inch.Solver where

> import Control.Applicative hiding (Alternative)
> import Control.Monad.Writer hiding (All)
> import Data.List
> import Data.Map (Map)
> import qualified Data.Map as Map
> import Data.Maybe

> import qualified Data.Integer.Presburger as P
> import Data.Integer.Presburger (Formula (TRUE, FALSE, (:=:), (:<:), (:<=:), (:>:), (:>=:), (:\/:), (:/\:), (:=>:)), (.*))

> import Language.Inch.BwdFwd
> import Language.Inch.Kind 
> import Language.Inch.Type
> import Language.Inch.TyNum
> import Language.Inch.Context
> import Language.Inch.Unify
> import Language.Inch.Kit
> import Language.Inch.Error
> import Language.Inch.Check


> unifySolveConstraints :: Contextual ()
> unifySolveConstraints = do
>     (g, ns) <- runWriter . collectEqualities <$> getContext
>     putContext g
>     mapM_ (uncurry unify) ns
>     return ()
>   where
>     collectEqualities :: Context -> Writer [(Type KNum, Type KNum)] Context
>     collectEqualities B0 = return B0
>     collectEqualities (g :< Layer l True)  = return $ g :< Layer l True
>     collectEqualities (g :< Layer l False) = (:< Layer l False) <$> collectEqualities g
>     collectEqualities (g :< Constraint Wanted (TyComp EL `TyApp` m `TyApp` n)) = tell [(m, n)]
>         >> collectEqualities g
>     collectEqualities (g :< e) = (:< e) <$> collectEqualities g


> trySolveConstraints :: Contextual ([Type KConstraint], [Type KConstraint])
> trySolveConstraints = do
>     g <- getContext
>     let (g', vs, hs, ps) = collect g [] [] []
>     putContext g'
>     qs <- simplifyConstraints vs hs ps
>     return (hs, qs)
>   where
>     collect :: Context -> [Ex (Var ())] -> [Type KConstraint] -> [Type KConstraint] ->
>                    (Context, [Ex (Var ())], [Type KConstraint], [Type KConstraint])
>     collect B0 vs hs ps = (B0, vs, hs, ps)
>     collect (g :< Constraint Wanted p)  vs hs ps = collect g vs hs (p:ps)
>     collect (g :< Constraint Given h)   vs hs ps =
>         collect g vs (h:hs) ps <:< Constraint Given h
>     collect (g :< A e@(a := Some d)) vs hs ps =
>         collect g vs (map (replaceTy a d) hs) (map (replaceTy a d) ps) <:< A e
>     collect (g :< A e@(a := _)) vs hs ps | a <? (hs, ps) =
>         collect g (Ex a:vs) hs ps <:< A e
>     collect (g :< Layer l True)   vs hs ps = (g :< Layer l True, vs', hs', ps')
>         where (vs', hs', ps') = collectHyps g vs hs ps
>     collect (g :< Layer l False)  vs hs ps = collect g vs hs ps <:< Layer l False
>     collect (g :< e) vs hs ps = collect g vs hs ps <:< e
>
>     collectHyps ::  Context -> [Ex (Var ())] -> [Type KConstraint] -> [Type KConstraint] ->
>                         ([Ex (Var ())], [Type KConstraint], [Type KConstraint])
>     collectHyps B0 vs hs ps = (vs, hs, ps)
>     collectHyps (g :< Constraint Given h) vs hs ps = collectHyps g vs (h:hs) ps
>     collectHyps (g :< A (a := Some d)) vs hs ps =
>         collectHyps g vs (map (replaceTy a d) hs) (map (replaceTy a d) ps)
>     collectHyps (g :< A (a := _)) vs hs ps | a <? (hs, ps) =
>         collectHyps g (Ex a:vs) hs ps
>     collectHyps (g :< _) vs hs ps = collectHyps g vs hs ps

>     (g, a, b, c) <:< e = (g :< e, a, b, c)

> solveConstraints :: Contextual ()
> solveConstraints = do
>     (hs, qs) <- trySolveConstraints
>     case qs of
>         []  -> return ()
>         _   -> traceContext "halp" >> errCannotDeduce hs qs

> solveOrSuspend :: Contextual ()
> solveOrSuspend = want . snd =<< trySolveConstraints
>   where
>     want :: [Type KConstraint] -> Contextual ()
>     want [] = return ()
>     want (p:ps)
>         | nonsense p  = errImpossible p
>         | otherwise   = modifyContext (:< Constraint Wanted p)
>                                 >> want ps
>
>     nonsense :: Type KConstraint -> Bool
>     nonsense t = maybe False not $ 
>                  trivialPred . normalisePred =<< constraintToPred t


> simplifyConstraints :: [Ex (Var ())] -> [Type KConstraint] ->
>                            [Type KConstraint] -> Contextual [Type KConstraint]
> simplifyConstraints vs hs ps = do
>     hs' <- mapM expandTySyns hs
>     ps' <- mapM expandTySyns ps
>     simplifyClassConstraints hs' $ filter (not . checkPred hs') (nub ps')
>   where
>     -- Compute the transitive dependency closure of the variables that occur in p.
>     -- We have to keep iterating until we reach a fixed point. This
>     -- will produce the minimum set of variables and hypotheses on
>     -- which the solution of p can depend.
>     iterDeps :: ([Ex (Var ())], [Type KConstraint]) ->
>                     ([Ex (Var ())], [Type KConstraint]) ->
>                         ([Ex (Var ())], [Type KConstraint]) ->
>                             ([Ex (Var ())], [Type KConstraint])
>     iterDeps old             ([], [])         _                = old
>     iterDeps (oldVs, oldHs)  (newVs, newHs)  (poolVs, poolHs)  =
>         iterDeps (oldVs ++ newVs, oldHs ++ newHs) (newVs', newHs') (poolVs', poolHs')
>       where
>         (newVs', poolVs') = partition (\ (Ex v) -> v <? newHs) poolVs
>         (newHs', poolHs') = partition (newVs <<?) poolHs
>
>     checkPred :: [Type KConstraint] -> Type KConstraint -> Bool
>     checkPred chs p = p' `elem` phs' || case constraintToPred p' of
>                      Just p''  -> P.check . toFormula xs'' phs'' . normalisePred $ p''
>                      Nothing   -> False
>       where
>         (pvs, pool)  = partition (\ (Ex v) -> v <? p) vs
>         (xs, phs)    = iterDeps ([], []) (pvs, []) (pool, chs)
>         (xs', phs', p')   = elimEquations xs phs p 
>         phs'' = map normalisePred . catMaybes . map constraintToPred $ phs'
>         xs'' = catMaybes $ map (\ (Ex v) -> fixNum v) xs'

>     elimEquations :: [Ex (Var ())] -> [Type KConstraint] -> Type KConstraint ->
>                          ([Ex (Var ())], [Type KConstraint], Type KConstraint)
>     elimEquations xs ys q = help [] ys q
>       where
>         help :: [Type KConstraint] -> [Type KConstraint] -> Type KConstraint ->
>                     ([Ex (Var ())], [Type KConstraint], Type KConstraint)
>         help ohs []      p = (xs, ohs, p)
>         help ohs (h@(TyComp EL `TyApp` m `TyApp` n):rs) p = 
>             case solveForAny (normaliseNum (n - m)) of
>                 Nothing      -> help (h:ohs) rs p
>                 Just (a, t)  -> help [] (map (replaceTy a t') (rs ++ ohs)) (replaceTy a t' p)
>                     where t' = reifyNum t
>         help ohs (h:rs) p = help (h:ohs) rs p


> toFormula :: [Var () KNum] -> [NormalPredicate] -> NormalPredicate -> P.Formula
> toFormula xs ys px = 

<  trace (unlines ["toFormula", "[" ++ intercalate "," (map fogSysVar vs) ++ "]","[" ++ intercalate "," (map (renderMe . fogSysPred . reifyPred) hs) ++ "]","(" ++ renderMe (fogSysPred $ reifyPred p) ++ ")"]) $

>   case trivialPred px of
>     Just True   -> TRUE
>     Just False  -> FALSE
>     Nothing -- | null ys && isSimple p  -> FALSE
>             | px `elem` ys            -> TRUE
>     Nothing     -> let r = convert xs []
>                    in {- trace ("result: " ++ show r) -} r
>                   
>   where
>     convert :: [Var () KNum] -> [(Var () KNum, P.Term)] -> P.Formula
>     convert []      axs = gogo axs ys Map.empty $ \ hs' mts' ->
>                              predToFormula axs px mts' $ \ p' _ ->
>                                  hs' :=>: p'
>     convert (v:vs)  axs = P.Forall (\ t -> convert vs ((v, t) : axs))
                
>     gogo :: [(Var () KNum, P.Term)] -> [NormalPredicate] -> Map Monomial P.Term ->
>                 (P.Formula -> Map Monomial P.Term -> P.Formula) -> P.Formula
>     gogo _   []      mts f = f TRUE mts
>     gogo axs (h:hs)  mts f = predToFormula axs h mts $ \ h' mts' ->
>                                  gogo axs hs mts' (\ x -> f (h' :/\: x))

>     predToFormula :: [(Var () KNum, P.Term)] -> NormalPredicate ->
>                          Map Monomial P.Term ->
>                          (P.Formula -> Map Monomial P.Term -> P.Formula) -> P.Formula
>     predToFormula axs (P c m n) mts f  = linearise axs m mts $ \ m' mts' ->
>                                                linearise axs n mts' $ \ n' mts'' ->
>                                                  f (compToFormula c m' n') mts''
>     predToFormula axs (p :=> q) mts f  = predToFormula axs p mts $ 
>         \ p' mts' -> predToFormula axs q mts' $ \ q' mts'' -> f (p' :=>: q') mts''

>     linearise ::  [(Var () KNum, P.Term)] -> NormalNum ->
>                     Map Monomial P.Term ->
>                     (P.Term -> Map Monomial P.Term -> P.Formula) -> P.Formula
>     linearise axs zs ms f = help 0 (Map.toList (elimNN zs)) ms
>       where
>         help :: P.Term -> [(Monomial, Integer)] ->
>                     Map Monomial P.Term -> P.Formula
>         help t []            mts = f t mts
>         help t ((fs, k):ks)  mts = case getLinearMono fs of
>             Just (Left ())           -> help (t + fromInteger k) ks mts
>             Just (Right (VarFac a))  -> help (t + k .* fromJust (lookup a axs)) ks mts
>             Just (Right (UnFac o `AppFac` m)) | Just lo <- linUnOp o ->
>                 linearise axs m mts $ \ m' mts' ->
>                     P.Exists $ \ y ->
>                         lo m' y :/\: help (t + k .* y) ks mts'
>             Just (Right (BinFac o `AppFac` m `AppFac` n)) | Just lo <- linBinOp o ->
>                  linearise axs m mts $ \ m' mts' ->
>                      linearise axs n mts' $ \ n' mts'' ->
>                          P.Exists $ \ y ->
>                              lo m' n' y :/\: help (t + k .* y) ks mts''        
>             _ -> case Map.lookup fs mts of
>                 Just n   -> help (t + k .* n) ks mts    
>                 Nothing  -> P.Forall (\ y -> help (t + k .* y) ks (Map.insert fs y mts))

>     linUnOp :: UnOp -> Maybe (P.Term -> P.Term -> P.Formula)
>     linUnOp Abs = Just $ \ m y -> ((m :=: y) :/\: (m :>=: 0))
>                                       :\/: ((m :=: -y) :/\: (m :<: 0))
>     linUnOp Signum = Just $ \ m y -> ((y :=: 1) :/\: (m :>: 0))
>                                       :\/: ((y :=: -1) :/\: (m :<: 0))
>                                       :\/: ((y :=: 0) :/\: (m :=: 0))

>     linBinOp :: BinOp -> Maybe (P.Term -> P.Term -> P.Term -> P.Formula)
>     linBinOp Max = Just $ \ m n y -> ((m :=: y) :/\: (m :>=: n))
>                                       :\/: ((n :=: y) :/\: (n :>=: m))
>     linBinOp Min = Just $ \ m n y -> ((m :=: y) :/\: (m :<=: n))
>                                       :\/: ((n :=: y) :/\: (n :<=: m))
>     linBinOp _ = Nothing

>     compToFormula :: Comparator -> P.Term -> P.Term -> P.Formula
>     compToFormula EL  = (:=:)
>     compToFormula LE  = (:<=:)
>     compToFormula LS  = (:<:)
>     compToFormula GE  = (:>=:)
>     compToFormula GR  = (:>:)



> simplifyClassConstraints :: [Type KConstraint] -> [Type KConstraint] ->
>                                 Contextual [Type KConstraint]
> simplifyClassConstraints _  []     = return []
> simplifyClassConstraints hs (q:qs) = case splitConstraint q of
>     Nothing      -> (q :) <$> simplifyClassConstraints hs qs
>     Just (c, _) -> do
>         is <- lookupInstances c
>         let hs' = hs ++ is
>         (simp, hard) <- if q `elem` hs' then return ([], [])
>                                         else simplify (hs ++ is) q
>         (simp ++) <$> simplifyClassConstraints (simp ++ hs) (hard ++ qs)
>   where
>     splitConstraint :: Type k -> Maybe (ClassName, [Ex (Ty ())])
>     splitConstraint (TyCon c _)    = Just (c, [])
>     splitConstraint (f `TyApp` s)  = do  (c, as) <- splitConstraint f
>                                          Just (c, as ++ [Ex s])
>                                       
>     splitConstraint _              = Nothing
>
>     simplify :: [Type KConstraint] -> Type KConstraint ->
>                     Contextual ([Type KConstraint], [Type KConstraint])
>     simplify []     p = return ([p], [])
>     simplify (h:xs) p = do
>         ms <- matcher h p []
>         case ms of
>             Just (cs, _)  -> return ([], cs)
>             Nothing       -> simplify xs p
>
>     matcher :: Type k -> Type k -> [Ex (Var ())] -> 
>                    Contextual (Maybe ([Type KConstraint], Subst))
>     matcher (Qual g h) p vs = (\ mp -> (\ (cs, ss) -> (applySubst ss g:cs, ss)) <$> mp) <$> matcher h p vs
>     matcher (TyVar a) p vs | a `hetElem` vs = return (Just ([], [VT a p]))
>     matcher (Bind All x k t) p vs = do
>         v   <- freshVar SysVar x k
>         ms  <- matcher (unbindTy v t) p (Ex v : vs)
>         return $ (\ (cs, ss) -> (cs, filter (vtVarIs v) ss)) <$> ms
>     matcher (TyApp f s) (TyApp f' s') vs = hetEq (getTyKind f) (getTyKind f') (do
>         ms <- matcher f f' vs
>         case ms of
>             Nothing        -> return Nothing
>             Just (cs, ss)  -> do
>                 ms' <- matcher (applySubst ss s) s' vs
>                 case ms' of
>                     Nothing -> return Nothing
>                     Just (cs', ss') -> return $ Just (cs ++ cs', ss ++ ss')
>       ) (return Nothing)
>     matcher s t _  | s == t     = return (Just ([], []))
>                    | otherwise  = return Nothing

> type Subst = [VarType]

> data VarType where
>   VT :: Var () k -> Type k -> VarType

> vtVarIs :: Var () k -> VarType -> Bool
> vtVarIs a (VT v _) = a =?= v

> lookupSubst :: Subst -> Var () k -> Maybe (Type k)
> lookupSubst [] _ = Nothing
> lookupSubst (VT v t : s) a = hetEq a v (Just t) (lookupSubst s a)

> applySubst :: Subst -> Type k -> Type k
> applySubst s = substTy f
>   where
>     f :: Var () l -> Type l
>     f v = maybe (TyVar v) id (lookupSubst s v)