|
| 1 | +module SMT.Prove (makeProvable, Error(..), provableIsOriginal) where |
| 2 | +-- Packages |
| 3 | +import Data.SBV |
| 4 | +import Data.Text.Internal ( Text ) |
| 5 | +import qualified Data.Functor as F |
| 6 | +import qualified Data.Map as Map |
| 7 | +import qualified Data.Set as Set |
| 8 | +import Prelude hiding ( Ordering(..) ) |
| 9 | +import Control.Monad.State.Lazy |
| 10 | +import Control.Monad.Except |
| 11 | +import Control.Monad.Reader |
| 12 | + |
| 13 | +-- GCL |
| 14 | +import Pretty |
| 15 | +import GCL.Common |
| 16 | +import GCL.Predicate |
| 17 | +import GCL.Predicate.Util |
| 18 | +import Syntax.Abstract |
| 19 | +import Syntax.Common |
| 20 | +import Debug.Trace |
| 21 | + |
| 22 | +makeProvable :: PO -> [Expr] -> Symbolic SBool |
| 23 | +makeProvable po props = let e1 = toExpr $ poPre po |
| 24 | + e2 = toExpr $ poPost po |
| 25 | + in makeImplication e1 e2 props |
| 26 | + |
| 27 | +makeImplication :: Expr -> Expr -> [Expr] -> Symbolic SBool |
| 28 | +makeImplication e1 e2 props = do |
| 29 | + -- inspect the expressions (DEBUG) |
| 30 | + lift (print $ pretty e1) |
| 31 | + lift (print $ pretty e2) |
| 32 | + |
| 33 | + -- capture free variables |
| 34 | + let propVarSet = Set.unions $ map fv props |
| 35 | + varList = Set.toList $ Set.map nameToText (fv e1 <> fv e2 <> propVarSet) |
| 36 | + len = length varList |
| 37 | + |
| 38 | + -- create symbolic bindings |
| 39 | + environment <- generateEnvironment varList len |
| 40 | + |
| 41 | + -- create constraints from global properties |
| 42 | + mapM_ (\prop -> constrain' $ runTranslator (toSBool prop) environment) props |
| 43 | + constrain' $ runTranslator (toSBool e1) environment |
| 44 | + |
| 45 | + |
| 46 | + -- return the evaluated expression to prove |
| 47 | +-- return $ runReader p1 environment |
| 48 | +-- .=> runReader p2 environment |
| 49 | + let maybeConclusion = runTranslator (toSBool e2) environment |
| 50 | + case maybeConclusion of |
| 51 | + (Left e) -> do |
| 52 | + lift $ print e |
| 53 | + return $ literal False |
| 54 | + (Right conclusion) -> do |
| 55 | + return conclusion |
| 56 | + |
| 57 | + |
| 58 | + where constrain' :: Either Error SBool -> SymbolicT IO () |
| 59 | + constrain' (Left _) = return () |
| 60 | + constrain' (Right b) = constrain b |
| 61 | + |
| 62 | +type Name2Num = Map.Map Text SInteger |
| 63 | +type Name2Bol = Map.Map Text SBool |
| 64 | +type Name2Chr = Map.Map Text SChar |
| 65 | + |
| 66 | +-- Error "function name" "first parameter" |
| 67 | +data Error = |
| 68 | + NotDefinedError Text | |
| 69 | + PONotFoundError | |
| 70 | + Z3NotFoundError |
| 71 | + deriving (Show) |
| 72 | + |
| 73 | + |
| 74 | +type Environment = (Name2Num, Name2Bol, Name2Chr) |
| 75 | +type Translator = ExceptT Error (Reader Environment) |
| 76 | +runTranslator :: Translator a -> Environment -> Either Error a |
| 77 | +runTranslator x = runReader (runExceptT x) |
| 78 | + |
| 79 | +provableIsOriginal :: PO -> [Expr] -> Bool |
| 80 | +provableIsOriginal po props = |
| 81 | + let e1 = toExpr $ poPre po |
| 82 | + e2 = toExpr $ poPost po |
| 83 | + in foldr (\e b -> checkSBool e && b) (checkSBool e1 && checkSBool e2) props |
| 84 | + |
| 85 | +generateEnvironment :: [Text] -> Int -> SymbolicT IO Environment |
| 86 | +generateEnvironment varList len = do |
| 87 | + intVars <- mkFreeVars len |
| 88 | + bolVars <- mkFreeVars len |
| 89 | + chrVars <- mkFreeVars len |
| 90 | + let intMap = Map.fromList $ zip varList intVars |
| 91 | + bolMap = Map.fromList $ zip varList bolVars |
| 92 | + chrMap = Map.fromList $ zip varList chrVars |
| 93 | + -- inspect the bindings (DEBUG) |
| 94 | + lift (traceIO $ show bolMap) |
| 95 | + lift (traceIO $ show intMap) |
| 96 | + return (bolMap, intMap, chrMap) |
| 97 | + |
| 98 | +bindNum :: Name -> Translator SInteger |
| 99 | +bindNum varName = do |
| 100 | + let nameText = nameToText varName |
| 101 | + (numMap, _, _) <- ask |
| 102 | + return (numMap Map.! nameText) |
| 103 | + |
| 104 | +bindBol :: Name -> Translator SBool |
| 105 | +bindBol varName = do |
| 106 | + let nameText = nameToText varName |
| 107 | + (_, bolMap, _) <- ask |
| 108 | + return (bolMap Map.! nameText) |
| 109 | + |
| 110 | +-- takes an expression that evaluates to an integer |
| 111 | +-- and convert it to a function of the environment |
| 112 | +toSInteger :: Expr -> Translator SInteger |
| 113 | +toSInteger (Lit (Num i) _) = return $ literal (fromIntegral i :: Integer) |
| 114 | +toSInteger (Var varName _) = bindNum varName |
| 115 | +toSInteger (Const varName _) = bindNum varName |
| 116 | +toSInteger (App (Op (ArithOp op)) e _) = |
| 117 | + handleUnaryArithNumOp op e |
| 118 | +toSInteger (App (App (Op (ArithOp op)) e1 _) e2 _) = |
| 119 | + handleBinaryArithNumOp op e1 e2 |
| 120 | +toSInteger e = throwError (NotDefinedError $ toText e <> toText "toSInteger") |
| 121 | + |
| 122 | +-- takes an expression that evaluates to a boolean value |
| 123 | +-- and convert it to a function of the environment |
| 124 | +toSBool :: Expr -> Translator SBool |
| 125 | +toSBool (Lit (Bol b) _) = return $ literal b |
| 126 | +toSBool (Var varName _) = bindBol varName |
| 127 | +toSBool (Const varName _) = bindBol varName |
| 128 | +toSBool (App (App (Op (ChainOp (EQProp l))) e1 _) e2 _) = |
| 129 | + handleChainOp (EQProp l) e1 e2 |
| 130 | +toSBool (App (App (Op (ChainOp (EQPropU l))) e1 _) e2 _) = |
| 131 | + handleChainOp (EQProp l) e1 e2 |
| 132 | +toSBool (App (App (Op (ChainOp op)) e1 _) e2 _) = |
| 133 | + handleChainOp op e1 e2 |
| 134 | +toSBool (App (Op (ArithOp op)) e _) = |
| 135 | + handleUnaryArithBolOp op e |
| 136 | +toSBool (App (App (Op (ArithOp op)) e1 _) e2 _) = |
| 137 | + handleBinaryArithBolOp op e1 e2 |
| 138 | +toSBool _ = throwError (NotDefinedError $ toText "toSBool") |
| 139 | + |
| 140 | +handleChainOp :: ChainOp -> Expr -> Expr -> Translator SBool |
| 141 | +handleChainOp (EQProp _) e1 e2 = do |
| 142 | + v1 <- toSBool e1 |
| 143 | + v2 <- toSBool e2 |
| 144 | + case e1 of |
| 145 | + App (App (Op (ChainOp _)) _ _) e12 _ -> do |
| 146 | + b1 <- toSBool e1 |
| 147 | + v12 <- toSBool e12 |
| 148 | + let b2 = v12 .== v2 |
| 149 | + return $ b1 .&& b2 |
| 150 | + _ -> return $ v1 .== v2 |
| 151 | + |
| 152 | +handleChainOp op e1 e2 = do |
| 153 | + v2 <- toSInteger e2 |
| 154 | + case e1 of |
| 155 | + App (App (Op (ChainOp op')) _ _) e12 _ -> do |
| 156 | + b1 <- toSBool e1 |
| 157 | + v12 <- toSInteger e12 |
| 158 | + b2 <- applyChainOp op' v12 v2 |
| 159 | + return $ b1 .&& b2 |
| 160 | + _ -> do |
| 161 | + v1 <- toSInteger e1 |
| 162 | + applyChainOp op v1 v2 |
| 163 | + where applyChainOp :: ChainOp -> SInteger -> SInteger -> Translator SBool |
| 164 | + applyChainOp (EQ _) i1 i2 = return $ i1 .== i2 |
| 165 | + applyChainOp (NEQ _) i1 i2 = return $ i1 ./= i2 |
| 166 | + applyChainOp (NEQU _) i1 i2 = return $ i1 ./= i2 |
| 167 | + applyChainOp (LTE _) i1 i2 = return $ i1 .<= i2 |
| 168 | + applyChainOp (LTEU _) i1 i2 = return $ i1 .<= i2 |
| 169 | + applyChainOp (GTE _) i1 i2 = return $ i1 .>= i2 |
| 170 | + applyChainOp (GTEU _) i1 i2 = return $ i1 .>= i2 |
| 171 | + applyChainOp (LT _) i1 i2 = return $ i1 .< i2 |
| 172 | + applyChainOp (GT _) i1 i2 = return $ i1 .> i2 |
| 173 | + applyChainOp (EQProp _) _ _ = throwError (NotDefinedError $ toText "EQProp with int") |
| 174 | + applyChainOp (EQPropU _) _ _ = throwError (NotDefinedError $ toText "EQProp with int") |
| 175 | + |
| 176 | +handleUnaryArithNumOp :: ArithOp -> Expr -> Translator SInteger |
| 177 | +handleUnaryArithNumOp (NegNum _) e = toSInteger e F.<&> negate |
| 178 | +handleUnaryArithNumOp _ _ = throwError (NotDefinedError $ toText "handleUnaryArithNumOp undefined") |
| 179 | + |
| 180 | +handleUnaryArithBolOp :: ArithOp -> Expr -> Translator SBool |
| 181 | +handleUnaryArithBolOp (Neg _) e = toSBool e F.<&> sNot |
| 182 | +handleUnaryArithBolOp (NegU _) e = toSBool e F.<&> sNot |
| 183 | +handleUnaryArithBolOp _ _ = throwError (NotDefinedError $ toText "handleUnaryArithBolOp") |
| 184 | + |
| 185 | +handleBinaryArithNumOp :: ArithOp -> Expr -> Expr -> Translator SInteger |
| 186 | +handleBinaryArithNumOp (Add _) e1 e2 = applyBinaryArithNumOp (+) e1 e2 |
| 187 | +handleBinaryArithNumOp (Sub _) e1 e2 = applyBinaryArithNumOp (-) e1 e2 |
| 188 | +handleBinaryArithNumOp (Mul _) e1 e2 = applyBinaryArithNumOp (*) e1 e2 |
| 189 | +handleBinaryArithNumOp (Div _) e1 e2 = applyBinaryArithNumOp sDiv e1 e2 |
| 190 | +handleBinaryArithNumOp (Mod _) e1 e2 = applyBinaryArithNumOp sMod e1 e2 |
| 191 | +handleBinaryArithNumOp (Max _) e1 e2 = applyBinaryArithNumOp smax e1 e2 |
| 192 | +handleBinaryArithNumOp (Min _) e1 e2 = applyBinaryArithNumOp smin e1 e2 |
| 193 | +--handleBinaryArithNumOp (Exp _) e1 e2 = applyBinaryArithNumOp (.^) e1 e2 |
| 194 | +handleBinaryArithNumOp _ _ _ = throwError (NotDefinedError $ toText "handleBinaryArithNumOp") |
| 195 | + |
| 196 | +handleBinaryArithBolOp :: ArithOp -> Expr -> Expr -> Translator SBool |
| 197 | +handleBinaryArithBolOp (Implies _) e1 e2 = applyBinaryArithBolOp (.=>) e1 e2 |
| 198 | +handleBinaryArithBolOp (ImpliesU _) e1 e2 = applyBinaryArithBolOp (.=>) e1 e2 |
| 199 | +handleBinaryArithBolOp (Conj _) e1 e2 = applyBinaryArithBolOp (.&&) e1 e2 |
| 200 | +handleBinaryArithBolOp (ConjU _) e1 e2 = applyBinaryArithBolOp (.&&) e1 e2 |
| 201 | +handleBinaryArithBolOp (Disj _) e1 e2 = applyBinaryArithBolOp (.||) e1 e2 |
| 202 | +handleBinaryArithBolOp (DisjU _) e1 e2 = applyBinaryArithBolOp (.||) e1 e2 |
| 203 | +handleBinaryArithBolOp _ _ _ = throwError (NotDefinedError $ toText "handleBinaryArithBolOp") |
| 204 | + |
| 205 | +applyBinaryArithNumOp :: (SInteger -> SInteger -> SInteger) -> Expr -> Expr -> Translator SInteger |
| 206 | +applyBinaryArithNumOp op e1 e2 = do |
| 207 | + f1 <- toSInteger e1 |
| 208 | + f2 <- toSInteger e2 |
| 209 | + return $ f1 `op` f2 |
| 210 | + |
| 211 | +applyBinaryArithBolOp :: (SBool -> SBool -> SBool) -> Expr -> Expr -> Translator SBool |
| 212 | +applyBinaryArithBolOp op e1 e2 = do |
| 213 | + f1 <- toSBool e1 |
| 214 | + f2 <- toSBool e2 |
| 215 | + return $ f1 `op` f2 |
| 216 | + |
| 217 | + |
| 218 | +-- takes an expression that evaluates to an integer |
| 219 | +-- and convert it to a function of the environment |
| 220 | +checkSInteger :: Expr -> Bool |
| 221 | +checkSInteger (Lit (Num _) _) = True |
| 222 | +checkSInteger (Var _ _) = True |
| 223 | +checkSInteger (Const _ _) = True |
| 224 | +checkSInteger (App (Op (ArithOp op)) e _) = |
| 225 | + checkHandleUnaryArithNumOp op e |
| 226 | +checkSInteger (App (App (Op (ArithOp op)) e1 _) e2 _) = |
| 227 | + checkHandleBinaryArithNumOp op e1 e2 |
| 228 | +checkSInteger _ = False |
| 229 | + |
| 230 | +-- takes an expression that evaluates to a boolean value |
| 231 | +-- and convert it to a function of the environment |
| 232 | +checkSBool :: Expr -> Bool |
| 233 | +checkSBool (Lit (Bol _) _) = True |
| 234 | +checkSBool (Var _ _) = True |
| 235 | +checkSBool (Const _ _) = True |
| 236 | +checkSBool (App (App (Op (ChainOp (EQProp l))) e1 _) e2 _) = |
| 237 | + checkHandleChainOp (EQProp l) e1 e2 |
| 238 | +checkSBool (App (App (Op (ChainOp (EQPropU l))) e1 _) e2 _) = |
| 239 | + checkHandleChainOp (EQProp l) e1 e2 |
| 240 | +checkSBool (App (App (Op (ChainOp op)) e1 _) e2 _) = |
| 241 | + checkHandleChainOp op e1 e2 |
| 242 | +checkSBool (App (Op (ArithOp op)) e _) = |
| 243 | + checkHandleUnaryArithBolOp op e |
| 244 | +checkSBool (App (App (Op (ArithOp op)) e1 _) e2 _) = |
| 245 | + checkHandleBinaryArithBolOp op e1 e2 |
| 246 | +checkSBool _ = False |
| 247 | + |
| 248 | +checkHandleChainOp :: ChainOp -> Expr -> Expr -> Bool |
| 249 | +checkHandleChainOp (EQProp _) e1 e2 = |
| 250 | + case e1 of |
| 251 | + App (App (Op (ChainOp _)) _ _) e12 _ -> |
| 252 | + checkSBool e12 && checkSBool e2 |
| 253 | + _ -> checkSBool e1 && checkSBool e2 |
| 254 | + |
| 255 | +checkHandleChainOp op e1 e2 = do |
| 256 | + let b2 = checkSInteger e2 |
| 257 | + case e1 of |
| 258 | + App (App (Op (ChainOp op')) _ _) e12 _ -> |
| 259 | + let b1 = checkSBool e1 |
| 260 | + b12 = checkSInteger e12 |
| 261 | + b = checkApplyChainOp op' |
| 262 | + in b1 && b2 && b12 && b |
| 263 | + _ -> |
| 264 | + let b1 = checkSInteger e1 |
| 265 | + in checkApplyChainOp op && b1 && b2 |
| 266 | + where checkApplyChainOp :: ChainOp -> Bool |
| 267 | + checkApplyChainOp (EQ _) = True |
| 268 | + checkApplyChainOp (NEQ _) = True |
| 269 | + checkApplyChainOp (NEQU _) = True |
| 270 | + checkApplyChainOp (LTE _) = True |
| 271 | + checkApplyChainOp (LTEU _) = True |
| 272 | + checkApplyChainOp (GTE _) = True |
| 273 | + checkApplyChainOp (GTEU _) = True |
| 274 | + checkApplyChainOp (LT _) = True |
| 275 | + checkApplyChainOp (GT _) = True |
| 276 | + checkApplyChainOp (EQProp _) = False |
| 277 | + checkApplyChainOp (EQPropU _) = False |
| 278 | + |
| 279 | +checkHandleUnaryArithNumOp :: ArithOp -> Expr -> Bool |
| 280 | +checkHandleUnaryArithNumOp (NegNum _) _ = True |
| 281 | +checkHandleUnaryArithNumOp _ _ = False |
| 282 | + |
| 283 | +checkHandleUnaryArithBolOp :: ArithOp -> Expr -> Bool |
| 284 | +checkHandleUnaryArithBolOp (Neg _) e = checkSBool e |
| 285 | +checkHandleUnaryArithBolOp (NegU _) e = checkSBool e |
| 286 | +checkHandleUnaryArithBolOp _ _ = False |
| 287 | + |
| 288 | +checkHandleBinaryArithNumOp :: ArithOp -> Expr -> Expr -> Bool |
| 289 | +checkHandleBinaryArithNumOp (Add _) e1 e2 = checkApplyBinaryArithNumOp e1 e2 |
| 290 | +checkHandleBinaryArithNumOp (Sub _) e1 e2 = checkApplyBinaryArithNumOp e1 e2 |
| 291 | +checkHandleBinaryArithNumOp (Mul _) e1 e2 = checkApplyBinaryArithNumOp e1 e2 |
| 292 | +checkHandleBinaryArithNumOp (Div _) e1 e2 = checkApplyBinaryArithNumOp e1 e2 |
| 293 | +checkHandleBinaryArithNumOp (Mod _) e1 e2 = checkApplyBinaryArithNumOp e1 e2 |
| 294 | +checkHandleBinaryArithNumOp (Max _) e1 e2 = checkApplyBinaryArithNumOp e1 e2 |
| 295 | +checkHandleBinaryArithNumOp (Min _) e1 e2 = checkApplyBinaryArithNumOp e1 e2 |
| 296 | +--checkHandleBinaryArithNumOp (Exp _) e1 e2 = applyBinaryArithNumOp (.^) e1 e2 |
| 297 | +checkHandleBinaryArithNumOp _ _ _ = False |
| 298 | + |
| 299 | +checkHandleBinaryArithBolOp :: ArithOp -> Expr -> Expr -> Bool |
| 300 | +checkHandleBinaryArithBolOp (Implies _) e1 e2 = checkApplyBinaryArithBolOp e1 e2 |
| 301 | +checkHandleBinaryArithBolOp (ImpliesU _) e1 e2 = checkApplyBinaryArithBolOp e1 e2 |
| 302 | +checkHandleBinaryArithBolOp (Conj _) e1 e2 = checkApplyBinaryArithBolOp e1 e2 |
| 303 | +checkHandleBinaryArithBolOp (ConjU _) e1 e2 = checkApplyBinaryArithBolOp e1 e2 |
| 304 | +checkHandleBinaryArithBolOp (Disj _) e1 e2 = checkApplyBinaryArithBolOp e1 e2 |
| 305 | +checkHandleBinaryArithBolOp (DisjU _) e1 e2 = checkApplyBinaryArithBolOp e1 e2 |
| 306 | +checkHandleBinaryArithBolOp _ _ _ = False |
| 307 | + |
| 308 | +checkApplyBinaryArithNumOp :: Expr -> Expr -> Bool |
| 309 | +checkApplyBinaryArithNumOp e1 e2 = checkSInteger e1 && checkSInteger e2 |
| 310 | + |
| 311 | +checkApplyBinaryArithBolOp :: Expr -> Expr -> Bool |
| 312 | +checkApplyBinaryArithBolOp e1 e2 = checkSBool e1 && checkSBool e2 |
0 commit comments