packages feed

g2-0.2.0.0: src/G2/Solver/Interface.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}

module G2.Solver.Interface
    ( subModel
    , subVar
    , subVarFuncCall
    , SMTConverter (..)
    , Solver (..)
    ) where

import G2.Execution.NormalForms
import G2.Language
import qualified G2.Language.ExprEnv as E
import G2.Solver.Converters
import G2.Solver.Solver

import Data.Function
import qualified Data.List as L
import Data.Maybe (mapMaybe, isJust, fromJust)
import qualified Data.HashMap.Lazy as HM
import qualified Data.Sequence as S

subModel :: State t -> Bindings -> ([Expr], Expr, Maybe FuncCall, S.Seq Expr)
subModel (State { expr_env = eenv
                , curr_expr = CurrExpr _ cexpr
                , assert_ids = ais
                , type_classes = tc
                , model = m
                , sym_gens = gens }) 
          (Bindings {input_names = inputNames}) = 
    let
        ais' = fmap (subVarFuncCall True m eenv tc) ais

        -- We do not inline all Lambdas, because higher order function arguments
        -- get preinserted into the model.
        -- See [Higher-Order Model] in G2.Execution.Reducers
        is = mapMaybe toVars inputNames
        gs = fmap fromJust . S.filter isJust $ fmap toVars gens
        
        sv = subVar False m eenv tc (is, cexpr, ais', gs)
    in
    untilEq (simplifyLams . pushCaseAppArgIn) sv
    where
        toVars n = case E.lookup n eenv of
                                Just e@(Lam _ _ _) -> Just . Var $ Id n (typeOf e)
                                Just e -> Just e
                                Nothing -> Nothing

        untilEq f x = let x' = f x in if x == x' then x' else untilEq f x'

subVarFuncCall :: Bool -> Model -> ExprEnv -> TypeClasses -> FuncCall -> FuncCall
subVarFuncCall inLam em eenv tc fc@(FuncCall {arguments = ars}) =
    subVar inLam em eenv tc $ fc {arguments = filter (not . isTC tc) ars}

subVar :: (ASTContainer m Expr) => Bool -> Model -> ExprEnv -> TypeClasses -> m -> m
subVar inLam em eenv tc = modifyContainedASTs (subVar' inLam em eenv tc [])

subVar' :: Bool -> Model -> ExprEnv -> TypeClasses -> [Id] -> Expr -> Expr
subVar' inLam em eenv tc is v@(Var i@(Id n _))
    | i `notElem` is
    , Just e <- HM.lookup n em =
        subVar' inLam em eenv tc (i:is) e
    | i `notElem` is
    , Just e <- E.lookup n eenv
    -- We want to inline a lambda only if inLam is true (we want to inline all lambdas),
    -- or if it's module is Nothing (and its name is likely uninteresting/unknown to the user)
    , (isExprValueForm eenv e && (notLam e || inLam || nameModule n == Nothing)) || isApp e || isVar e || isLitCase e =
        subVar' inLam em eenv tc (i:is) e
    | otherwise = v
subVar' inLam mdl eenv tc is cse@(Case e _ _ as) =
    case subVar' inLam mdl eenv tc is e of
        Lit l
            | Just (Alt _ ae) <- L.find (\(Alt (LitAlt l') _) -> l == l') as ->
                subVar' inLam mdl eenv tc is ae
        _ -> modifyChildren (subVar' inLam mdl eenv tc is) cse
subVar' inLam em eenv tc is e = modifyChildren (subVar' inLam em eenv tc is) e

isApp :: Expr -> Bool
isApp (App _ _) = True
isApp _ = False

notLam :: Expr -> Bool
notLam (Lam _ _ _) = False
notLam _ = True

isVar :: Expr -> Bool
isVar (Var _) = True
isVar _ = False

isLitCase :: Expr -> Bool
isLitCase (Case e _ _ _) = isPrimType (typeOf e)
isLitCase _ = False

isTC :: TypeClasses -> Expr -> Bool
isTC tc (Var (Id _ (TyCon n _))) = isTypeClassNamed n tc
isTC _ _ = False

-- | Rewrites a case statement returning a function type, and applied to a variable argument,
-- so that the variable argument is moved into each branch of the case statement.
-- This composes with `simplifyLams` to get better simplication for symbolic function concretizations.
pushCaseAppArgIn :: ASTContainer c Expr => c -> c
pushCaseAppArgIn = modifyASTs pushCaseAppArgIn'

pushCaseAppArgIn' :: Expr -> Expr
pushCaseAppArgIn' (App (Case scrut bind t as) v@(Var _)) =
    Case scrut bind t $ map (\(Alt am e) -> Alt am (App e v) ) as
pushCaseAppArgIn' e = e