module Language.Fixpoint.Solver.Instantiate (
instantiateAxioms,
instantiateFInfo,
) where
import Language.Fixpoint.Types
import Language.Fixpoint.Types.Config as FC
import qualified Language.Fixpoint.Smt.Theories as FT
import Language.Fixpoint.Types.Visitor (eapps, kvars, mapMExpr)
import Language.Fixpoint.Misc (mapFst)
import Language.Fixpoint.Smt.Interface ( smtPop, smtPush
, smtDecls, smtAssert
, checkValid', Context(..) )
import Language.Fixpoint.Defunctionalize (defuncAny, makeLamArg)
import Language.Fixpoint.SortCheck (elaborate)
import Control.Monad.State
import qualified Data.Text as T
import qualified Data.HashMap.Strict as M
import qualified Data.List as L
import Data.Maybe (catMaybes, fromMaybe)
import Data.Char (isUpper)
import Data.Foldable (foldlM)
(~>) :: (Expr, String) -> Expr -> EvalST Expr
(_e,_str) ~> e' = do
modify (\st -> st{evId = evId st + 1})
return (η e')
instantiateFInfo :: Config -> Context -> FInfo c -> IO (FInfo c)
instantiateFInfo cfg ctx fi = do
cm' <- sequence $ M.mapWithKey instantiateOne (cm fi)
return $ fi { cm = cm' }
where instantiateOne = instantiateAxioms cfg ctx (bs fi) (gLits fi) (ae fi)
instantiateAxioms :: Config -> Context -> BindEnv -> SEnv Sort -> AxiomEnv
-> Integer -> SubC c
-> IO (SubC c)
instantiateAxioms _ _ _ _ aenv sid sub
| not (M.lookupDefault False sid (aenvExpand aenv))
= return sub
instantiateAxioms cfg ctx bds fenv aenv sid sub
= flip strengthenLhs sub . pAnd . (is0 ++) .
(if arithmeticAxioms cfg then (is ++) else id) <$>
if rewriteAxioms cfg then evalEqs else return []
where
is0 = eqBody <$> L.filter (null . eqArgs) eqs
is = instances maxNumber aenv initOccurences
evalEqs =
map (uncurry (PAtom Eq)) .
filter (uncurry (/=)) <$>
evaluate cfg ctx ((vv Nothing, slhs sub):binds) fenv aenv initExpressions
initExpressions = expr (slhs sub) : expr (srhs sub) : (expr <$> binds)
binds = envCs bds (senv sub)
initOccurences = concatMap (makeInitOccurences as eqs) initExpressions
eqs = aenvEqs aenv
fuelNumber = M.lookupDefault 0 sid (aenvFuel aenv)
as = (,fuelNumber) . eqName <$> filter (not . null . eqArgs) eqs
maxNumber = (length (aenvSyms aenv) * length initOccurences) ^ fuelNumber
data Knowledge
= KN { knSels :: ![(Expr, Expr)]
, knEqs :: ![(Expr, Expr)]
, knSims :: ![Rewrite]
, knAms :: ![Equation]
, knContext :: IO Context
, knPreds :: !([(Symbol, Sort)] -> Expr -> Context -> IO Bool)
, knLams :: [(Symbol, Sort)]
}
emptyKnowledge :: IO Context -> Knowledge
emptyKnowledge cxt = KN [] [] [] [] cxt (\_ _ _ -> return False) []
lookupKnowledge :: Knowledge -> Expr -> Maybe Expr
lookupKnowledge γ e
| Just e' <- L.lookup e (knEqs γ)
= Just e'
| Just e' <- L.lookup e (knSels γ)
= Just e'
| otherwise
= Nothing
isValid :: Knowledge -> Expr -> IO Bool
isValid γ b = knPreds γ (knLams γ) b =<< knContext γ
makeKnowledge :: Config -> Context -> AxiomEnv -> SEnv Sort
-> [(Symbol, SortedReft)]
-> ([(Expr, Expr)], Knowledge)
makeKnowledge cfg ctx aenv fenv es = (simpleEqs,) $ (emptyKnowledge context)
{ knSels = sels
, knEqs = eqs
, knSims = aenvSimpl aenv
, knAms = aenvEqs aenv
, knPreds = \bs e c -> askSMT c bs e
}
where
(xv, sv) = (vv Nothing, sr_sort $ snd $ head es)
fbinds = toListSEnv fenv ++ [(x, s) | (x, RR s _) <- es]
senv = fromListSEnv fbinds
context :: IO Context
context = do
smtPop ctx
smtPush ctx
smtDecls ctx $ L.nub [(x, toSMT [] s) | (x, s) <- fbinds
, not (M.member x FT.theorySymbols)]
smtAssert ctx (pAnd ([toSMT [] (PAtom Eq e1 e2) | (e1, e2) <- simpleEqs]
++ filter (null.kvars) ((toSMT [] . expr) <$> es)
))
return ctx
atms = splitPAnd =<< (expr <$> filter isProof es)
simpleEqs = makeSimplifications (aenvSimpl aenv) =<<
L.nub (catMaybes [getDCEquality e1 e2 | PAtom Eq e1 e2 <- atms])
sels = (go . expr) =<< es
go e = let es = splitPAnd e
su = mkSubst [(x, EVar y) | PAtom Eq (EVar x) (EVar y) <- es ]
sels = [(EApp (EVar s) x, e) | PAtom Eq (EApp (EVar s) x) e <- es
, isSelector s ]
in L.nub (sels ++ subst su sels)
eqs = [(EVar x, ex) | Equ a _ bd <- filter (null . eqArgs) $ aenvEqs aenv
, PAtom Eq (EVar x) ex <- splitPAnd bd
, x == a
]
toSMT xs = defuncAny cfg (insertSEnv xv sv senv) .
elaborate "symbolic evaluation"
(foldl (\env (x,s) -> insertSEnv x s (deleteSEnv x env))
(insertSEnv xv sv senv)
xs)
askSMT :: Context -> [(Symbol, Sort)] -> Expr -> IO Bool
askSMT cxt xss e
| isTautoPred e = return True
| isContraPred e = return False
| null (kvars e) = do
smtPush cxt
b <- checkValid' cxt [] PTrue (toSMT xss e)
smtPop cxt
return b
| otherwise = return False
isSelector :: Symbol -> Bool
isSelector = L.isPrefixOf "select" . symbolString
isProof (_, RR s _) = showpp s == "Tuple"
makeSimplifications :: [Rewrite] -> (Symbol, [Expr], Expr) -> [(Expr, Expr)]
makeSimplifications sis (dc, es, e)
= go =<< sis
where
go (SMeasure f dc' xs bd)
| dc == dc', length xs == length es
= [(EApp (EVar f) e, subst (mkSubst $ zip xs es) bd)]
go _
= []
getDCEquality :: Expr -> Expr -> Maybe (Symbol, [Expr], Expr)
getDCEquality e1 e2
| Just dc1 <- f1
, Just dc2 <- f2
= if dc1 == dc2
then Nothing
else error ("isDCEquality on" ++ showpp e1 ++ "\n" ++ showpp e2)
| Just dc1 <- f1
= Just (dc1, es1, e2)
| Just dc2 <- f2
= Just (dc2, es2, e1)
| otherwise
= Nothing
where
(f1, es1) = mapFst getDC $ splitEApp e1
(f2, es2) = mapFst getDC $ splitEApp e2
getDC (EVar x)
= if isUpper $ head $ symbolString $ dropModuleNames x
then Just x
else Nothing
getDC _
= Nothing
dropModuleNames = mungeNames (symbol . last) "."
mungeNames _ _ "" = ""
mungeNames f d s'@(symbolText -> s)
| s' == tupConName = tupConName
| otherwise = f $ T.splitOn d $ stripParens s
stripParens t = fromMaybe t ((T.stripPrefix "(" >=> T.stripSuffix ")") t)
splitPAnd :: Expr -> [Expr]
splitPAnd (PAnd es) = concatMap splitPAnd es
splitPAnd e = [e]
assertSelectors :: Knowledge -> Expr -> EvalST ()
assertSelectors γ e = do
EvalEnv _ _ evaenv <- get
let sims = aenvSimpl evaenv
_ <- foldlM (\_ s -> mapMExpr (go s) e) e sims
return ()
where
go :: Rewrite -> Expr -> EvalST Expr
go (SMeasure f dc xs bd) e@(EApp _ _)
| (EVar dc', es) <- splitEApp e
, dc == dc', length xs == length es
= addSMTEquality γ (EApp (EVar f) e) (subst (mkSubst $ zip xs es) bd)
>> return e
go _ e
= return e
addSMTEquality :: Knowledge -> Expr -> Expr -> EvalST (IO ())
addSMTEquality γ e1 e2 =
return $ do ctx <- knContext γ
smtAssert ctx (PAtom Eq (makeLam γ e1) (makeLam γ e2))
data EvalEnv = EvalEnv { evId :: Int
, evSequence :: [(Expr,Expr)]
, _evAEnv :: AxiomEnv
}
type EvalST a = StateT EvalEnv IO a
evaluate :: Config -> Context -> [(Symbol, SortedReft)] -> SEnv Sort -> AxiomEnv
-> [Expr]
-> IO [(Expr, Expr)]
evaluate cfg ctx facts fenv aenv einit
= (eqs ++) <$>
(fmap join . sequence)
(evalOne <$> L.nub (grepTopApps =<< einit))
where
(eqs, γ) = makeKnowledge cfg ctx aenv fenv facts
initEvalSt = EvalEnv 0 [] aenv
evalOne :: Expr -> IO [(Expr, Expr)]
evalOne e = do
(e', st) <- runStateT (eval γ e) initEvalSt
if e' == e then return [] else return ((e, e'):evSequence st)
grepTopApps :: Expr -> [Expr]
grepTopApps (PAnd es) = concatMap grepTopApps es
grepTopApps (PAtom _ e1 e2) = grepTopApps e1 ++ grepTopApps e2
grepTopApps e@(EApp _ _) = [e]
grepTopApps _ = []
makeLam :: Knowledge -> Expr -> Expr
makeLam γ e = foldl (flip ELam) e (knLams γ)
eval :: Knowledge -> Expr -> EvalST Expr
eval γ e | Just e' <- lookupKnowledge γ e
= (e, "Knowledge") ~> e'
eval γ (ELam (x,s) e)
= do let x' = makeLamArg s (1+ length (knLams γ))
e' <- eval γ{knLams = (x',s):knLams γ} (subst1 e (x, EVar x'))
return $ ELam (x,s) $ subst1 e' (x', EVar x)
eval γ e@(EIte b e1 e2)
= do b' <- eval γ b
evalIte γ e b' e1 e2
eval γ e@(EApp _ _)
= evalArgs γ e >>= evalApp γ e
eval γ (PAtom r e1 e2)
= PAtom r <$> eval γ e1 <*> eval γ e2
eval γ (ENeg e)
= ENeg <$> eval γ e
eval γ (EBin o e1 e2)
= EBin o <$> eval γ e1 <*> eval γ e2
eval γ (ETApp e t)
= flip ETApp t <$> eval γ e
eval γ (ETAbs e s)
= flip ETAbs s <$> eval γ e
eval γ (PNot e)
= PNot <$> eval γ e
eval γ (PImp e1 e2)
= PImp <$> eval γ e1 <*> eval γ e2
eval γ (PIff e1 e2)
= PIff <$> eval γ e1 <*> eval γ e2
eval _ e = return e
evalArgs :: Knowledge -> Expr -> EvalST (Expr, [Expr])
evalArgs γ = go []
where
go acc (EApp f e)
= do f' <- eval γ f
e' <- eval γ e
go (e':acc) f'
go acc e
= (,acc) <$> eval γ e
evalApp :: Knowledge -> Expr -> (Expr, [Expr]) -> EvalST Expr
evalApp γ e (EVar f, [ex])
| (EVar dc, es) <- splitEApp ex
, Just simp <- L.find (\simp -> (smName simp == f) && (smDC simp == dc))
(knSims γ)
, length (smArgs simp) == length es
= do e' <- eval γ $ η $ substPopIf (zip (smArgs simp) es) (smBody simp)
(e, "Rewrite -" ++ showpp f) ~> e'
evalApp γ _ (EVar f, es)
| Just eq <- L.find ((==f) . eqName) (knAms γ)
, Just bd <- getEqBody eq
, length (eqArgs eq) == length es
, f `notElem` syms bd
= eval γ $ η $ substPopIf (zip (eqArgs eq) es) bd
evalApp γ _e (EVar f, es)
| Just eq <- L.find ((==f) . eqName) (knAms γ)
, Just bd <- getEqBody eq
, length (eqArgs eq) == length es
= evalRecApplication γ (eApps (EVar f) es) $
subst (mkSubst $ zip (eqArgs eq) es) bd
evalApp _ _ (f, es)
= return $ eApps f es
substPopIf :: [(Symbol, Expr)] -> Expr -> Expr
substPopIf xes e = η $ foldl go e xes
where
go e (x, EIte b e1 e2) = EIte b (subst1 e (x, e1)) (subst1 e (x, e2))
go e (x, ex) = subst1 e (x, ex)
evalRecApplication :: Knowledge -> Expr -> Expr -> EvalST Expr
evalRecApplication γ e (EIte b e1 e2)
= do b' <- eval γ b
b'' <- liftIO (isValid γ b')
if b''
then addApplicationEq γ e e1 >>
assertSelectors γ e1 >>
eval γ e1 >>=
((e, "App") ~>)
else do b''' <- liftIO (isValid γ (PNot b'))
if b'''
then addApplicationEq γ e e2 >>
assertSelectors γ e2 >>
eval γ e2 >>=
((e, "App") ~>)
else return e
evalRecApplication _ _ e
= return e
addApplicationEq :: Knowledge -> Expr -> Expr -> EvalST ()
addApplicationEq γ e1 e2 =
modify (\st -> st{evSequence = (makeLam γ e1, makeLam γ e2):evSequence st})
evalIte :: Knowledge -> Expr -> Expr -> Expr -> Expr -> EvalST Expr
evalIte γ e b e1 e2 = join $
evalIte' γ e b e1 e2 <$>
liftIO (isValid γ b) <*>
liftIO (isValid γ (PNot b))
evalIte' :: Knowledge -> Expr -> Expr -> Expr -> Expr -> Bool -> Bool
-> EvalST Expr
evalIte' γ e _ e1 _ b _
| b
= do e' <- eval γ e1
(e, "If-True of:" ++ showpp b) ~> e'
evalIte' γ e _ _ e2 _ b'
| b'
= do e' <- eval γ e2
(e, "If-False") ~> e'
evalIte' γ _ b e1 e2 _ _
= do e1' <- eval γ e1
e2' <- eval γ e2
return $ EIte b e1' e2'
η :: Expr -> Expr
η = snd . go
where
go (EIte b t f)
| isTautoPred t && isFalse f
= (True, b)
go (EIte b e1 e2)
= let (fb, b') = go b
(f1, e1') = go e1
(f2, e2') = go e2
in
(fb || f1 || f2, EIte b' e1' e2')
go (EApp (EIte b f1 f2) e)
= (True, EIte b (snd $ go $ EApp f1 e) (snd $ go $ EApp f2 e))
go (EApp f (EIte b e1 e2))
= (True, EIte b (snd $ go $ EApp f e1) (snd $ go $ EApp f e2))
go (EApp e1 e2)
= let (f1, e1') = go e1
(f2, e2') = go e2
in
if f1 || f2
then go $ EApp e1' e2'
else (False, EApp e1' e2')
go e = (False, e)
type Fuel = Int
type FuelMap = [(Symbol, Fuel)]
goodFuelMap :: FuelMap -> Bool
goodFuelMap = any ((>0) . snd)
hasFuel :: FuelMap -> Symbol -> Bool
hasFuel fm x = maybe True (\x -> 0 < x) (L.lookup x fm)
makeFuelMap :: (Fuel -> Fuel) -> FuelMap -> Symbol -> FuelMap
makeFuelMap f ((x, fx):fs) y
| x == y = (x, f fx) : fs
| otherwise = (x, fx) : makeFuelMap f fs y
makeFuelMap _ _ _ = error "makeFuelMap"
data Occurence = Occ {_ofun :: Symbol, _oargs :: [Expr], ofuel :: FuelMap}
deriving (Show)
instances :: Int -> AxiomEnv -> [Occurence] -> [Expr]
instances maxIs aenv !occs
= instancesLoop aenv maxIs eqs occs
where
eqs = filter (not . null . eqArgs) (aenvEqs aenv)
instancesLoop :: AxiomEnv -> Int -> [Equation] -> [Occurence] -> [Expr]
instancesLoop _ _ eqs = go 0 []
where
go :: Int -> [Expr] -> [Occurence] -> [Expr]
go !i acc occs
= let is = concatMap (unfold eqs) occs
newIs = findNewEqs is acc
newOccs = concatMap (grepOccurences eqs) newIs
in
if null newIs
then acc
else go (i + length newIs) ((fst <$> newIs) ++ acc) newOccs
findNewEqs :: [(Expr, FuelMap)] -> [Expr] -> [(Expr, FuelMap)]
findNewEqs [] _ = []
findNewEqs ((e, f):xss) es
| e `elem` es = findNewEqs xss es
| otherwise = (e,f):findNewEqs xss es
makeInitOccurences :: [(Symbol, Fuel)] -> [Equation] -> Expr -> [Occurence]
makeInitOccurences xs eqs e
= [Occ x es xs | (EVar x, es) <- splitEApp <$> eapps e
, Equ x' xs' _ <- eqs, x == x'
, length xs' == length es]
grepOccurences :: [Equation] -> (Expr, FuelMap) -> [Occurence]
grepOccurences eqs (e, fs)
= filter (goodFuelMap . ofuel)
[Occ x es fs | (EVar x, es) <- splitEApp <$> eapps e
, Equ x' xs' _ <- eqs, x == x'
, length xs' == length es]
unfold :: [Equation] -> Occurence -> [(Expr, FuelMap)]
unfold eqs (Occ x es fs)
= catMaybes [if hasFuel fs x
then Just (subst (mkSubst $ zip xs' es) e
, makeFuelMap (\x -> x1) fs x)
else Nothing
| Equ x' xs' e <- eqs
, x == x'
, length xs' == length es]
instance Expression (Symbol, SortedReft) where
expr (x, RR _ (Reft (v, r))) = subst1 (expr r) (v, EVar x)