-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathSmt.hs
364 lines (300 loc) · 14.7 KB
/
Smt.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
-- L4 to SMT interface using the SimpleSMT library
module Smt(proveAssertionSMT, proveExpr, constrProofTarget) where
import L4.KeyValueMap
( ValueKVM(MapVM, IntVM, IdVM),
selectOneOfInstr,
getAssocOfPathValue )
import L4.Syntax
import L4.SyntaxManipulation (
spine,
ruleToFormula,
conjsExpr,
notExpr, etaExpand, decomposeFun)
import L4.Typing (isBooleanTp, isIntegerTp, isFloatTp, superClassesOfClassDecl)
import RuleTransfo
( isNamedRule )
import SimpleSMT qualified as SMT
import Control.Monad ( when, foldM )
import L4.PrintProg (renameExpr, printARName )
import Data.Maybe (fromMaybe)
import Model (displayableModel, printDisplayableModel)
-------------------------------------------------------------
-- Extensions to SExpr construction currently not in SimpleSMT
-------------------------------------------------------------
declareSort :: SMT.Solver -> String -> Int -> IO SMT.SExpr
declareSort proc srt ar =
do SMT.ackCommand proc $ SMT.fun "declare-sort" [ SMT.Atom srt, SMT.Atom (show ar) ]
return (SMT.const srt)
-- defineSort is here more restrictive than the SMT define-sort,
-- and is only used for aliasing sort symbols according to function sortAlias
defineSort :: SMT.Solver -> ClassName -> IO SMT.SExpr
defineSort proc cn =
do SMT.ackCommand proc $ SMT.fun "define-sort" [ SMT.Atom (stringOfClassName cn), SMT.List [], sortAlias cn ]
return (SMT.const (stringOfClassName cn))
getModel :: SMT.Solver -> IO SMT.SExpr
getModel proc =
SMT.command proc $ SMT.List [ SMT.Atom "get-model" ]
-------------------------------------------------------------
-- Conversion functions from L4 Expr to SExpr
-------------------------------------------------------------
-- mapping class names to sorts
type SMTSortEnv = [(ClassName, SMT.SExpr)]
-- mapping variable names to their sorts
type SMTFunEnv = [(VarName, SMT.SExpr)]
data SMTEnv = SMTEnv { sortEnv :: SMTSortEnv,
funEnv :: SMTFunEnv }
quantifToSMT :: Quantif -> String
quantifToSMT All = "forall"
quantifToSMT Ex = "exists"
quantif :: Quantif -> SMT.SExpr -> SMT.SExpr -> SMT.SExpr
quantif q vds e = SMT.fun (quantifToSMT q) [vds, e]
-- local variable reference in a quantified expression
localVarRef :: VarName -> SMT.SExpr
localVarRef = SMT.Atom
-- TODO: distinction between ideal mathematical numbers (tInt, tReal)
-- vs their implementation (integer words, floats)
tpToSort :: Show t => SMTSortEnv -> Tp t-> SMT.SExpr
tpToSort se t
| isBooleanTp t = SMT.tBool
| isIntegerTp t = SMT.tInt
| isFloatTp t = SMT.tReal
| otherwise = case t of
ClassT _ cn -> Data.Maybe.fromMaybe (error $ "internal error in tpToSort: Type not found: " ++ show cn) (lookup cn se)
_ -> error $ "in tpToSort: " ++ show t ++ " not supported"
tpToRank :: Show t => SMTSortEnv -> Tp t -> ([SMT.SExpr], SMT.SExpr)
tpToRank se f@FunT{} =
let (args, res) = spine [] f
in (map (tpToSort se) args, tpToSort se res)
tpToRank se t = ([], tpToSort se t)
-- local variable declaration in a quantification.
-- TODO: only first-order quantification, no functional types
-- (has to be checked in advance)
varTypeToSExprTD :: Show t => SMTSortEnv -> VarName -> Tp t -> SMT.SExpr
varTypeToSExprTD se vn t = SMT.List [SMT.List [SMT.Atom vn, snd (tpToRank se t)]]
-- SMT variable / function declaration
varDeclToFun :: Show t => SMT.Solver -> SMTSortEnv -> VarDecl t -> IO (VarName, SMT.SExpr)
varDeclToFun s se (VarDecl _ vn vt) =
let (argTs, resT) = tpToRank se vt
in do
sf <- SMT.declareFun s vn argTs resT
return (vn, sf)
predefinedToFun :: SMTFunEnv
predefinedToFun = [("distinct", SMT.Atom "distinct")]
varDeclsToFunEnv :: Show t => SMT.Solver -> SMTSortEnv -> [VarDecl t] -> IO SMTFunEnv
varDeclsToFunEnv s se vds =
let predefEnv = predefinedToFun
in do
funDeclEnv <- mapM (varDeclToFun s se) vds
return (predefEnv ++ funDeclEnv)
varDefnToFun :: SMT.Solver -> SMTEnv -> VarDefn (Tp()) -> IO SMTEnv --(VarName, SMT.SExpr)
varDefnToFun s env (VarDefn _ vn vt e) =
let (_, resT) = tpToRank (sortEnv env) vt
(vds, bd) = decomposeFun (etaExpand (renameExpr [] e))
params = map (\vd -> (nameOfVarDecl vd, tpToSort (sortEnv env) (tpOfVarDecl vd))) vds
bdSE = exprToSExpr env bd
in do
sf <- SMT.defineFun s vn params resT bdSE
return env{funEnv = funEnv env ++ [(vn, sf)]}
varDefnsToSMTEnv :: SMT.Solver -> SMTEnv -> [VarDefn (Tp())] -> IO SMTEnv
varDefnsToSMTEnv s = foldM (varDefnToFun s)
topLevelUserClassDecl :: ClassDecl t -> Bool
topLevelUserClassDecl cd = (length (superClassesOfClassDecl cd) >= 2) && superClassesOfClassDecl cd!!1 == ClassC
-- Only the following class declarations are meant to be translated to SMT sorts:
-- - Some special system defined classes (not subclasses of Class)
-- - Top-level user defined classes (direct subclasses of Class)
declarableSort :: ClassDecl t -> Bool
declarableSort cd =
topLevelUserClassDecl cd ||
nameOfClassDecl cd == StateC
-- For each definable sort, a sort alias has to be introduced in sortAlias
definableSort :: ClassDecl t -> Bool
definableSort cd =
nameOfClassDecl cd == TimeC
sortAlias :: ClassName -> SMT.SExpr
sortAlias TimeC = SMT.Atom "Real"
sortAlias cn = error ("sort alias for " ++ show cn ++ " (internal error)")
classDeclToSortDecl :: SMT.Solver -> ClassDecl t -> IO (ClassName, SMT.SExpr)
classDeclToSortDecl s (ClassDecl _ cn _) =
do
se <- declareSort s (stringOfClassName cn) 0
return (cn, se)
classDeclToSortDefn :: SMT.Solver -> ClassDecl t -> IO (ClassName, SMT.SExpr)
classDeclToSortDefn s (ClassDecl _ cn _) =
do
se <- defineSort s cn
return (cn, se)
classDeclsToSortEnv :: SMT.Solver -> [ClassDecl t] -> IO SMTSortEnv
classDeclsToSortEnv s cds = do
sdecls <- mapM (classDeclToSortDecl s) (filter declarableSort cds)
sdefns <- mapM (classDeclToSortDefn s) (filter definableSort cds)
return (sdecls ++ sdefns)
valToSExpr :: Val -> SMT.SExpr
valToSExpr (BoolV b) = SMT.bool b
valToSExpr (IntV i) = SMT.int i
valToSExpr (FloatV f) = SMT.real (toRational f)
valToSExpr _ = error "valToSExpr: not implemented"
-- TODO: For this to work, names (also of bound variables) have to be unique
varToSExpr :: SMTFunEnv -> Var t -> SMT.SExpr
varToSExpr env (GlobalVar qvn) =
let vn = nameOfQVarName qvn
in Data.Maybe.fromMaybe
(error $ "internal error in varToSExpr: Var not found: " ++ show vn)
(lookup vn env)
varToSExpr _env (LocalVar qvn _) = let vn = nameOfQVarName qvn in localVarRef vn
transUArithOp :: UArithOp -> SMT.SExpr -> SMT.SExpr
transUArithOp UAminus = SMT.neg
transUBoolOp :: UBoolOp -> SMT.SExpr -> SMT.SExpr
transUBoolOp UBnot = SMT.not
transUnaOp :: UnaOp -> SMT.SExpr -> SMT.SExpr
transUnaOp (UArith ua) = transUArithOp ua
transUnaOp (UBool ub) = transUBoolOp ub
transUnaOp (UTemporal _) = error "transUnaOp: temporal operators should be resolved elsewhere"
transBArithOp :: BArithOp -> SMT.SExpr -> SMT.SExpr -> SMT.SExpr
transBArithOp BAadd = SMT.add
transBArithOp BAsub = SMT.sub
transBArithOp BAmul = SMT.mul
transBArithOp BAdiv = SMT.div
transBArithOp BAmod = SMT.mod
transBComparOp :: BComparOp -> SMT.SExpr -> SMT.SExpr -> SMT.SExpr
transBComparOp BCeq = SMT.eq
transBComparOp BClt = SMT.lt
transBComparOp BClte = SMT.leq
transBComparOp BCgt = SMT.gt
transBComparOp BCgte = SMT.geq
transBComparOp BCne = \e1 e2 -> SMT.not (SMT.eq e1 e2)
transBBoolOp :: BBoolOp -> SMT.SExpr -> SMT.SExpr -> SMT.SExpr
transBBoolOp BBimpl = SMT.implies
transBBoolOp BBor = SMT.or
transBBoolOp BBand = SMT.and
transBinOp :: BinOp -> SMT.SExpr -> SMT.SExpr -> SMT.SExpr
transBinOp (BArith ba) = transBArithOp ba
transBinOp (BCompar bc) = transBComparOp bc
transBinOp (BBool bb) = transBBoolOp bb
sExprApply :: SMT.SExpr -> SMT.SExpr -> SMT.SExpr
sExprApply f a = case f of
SMT.Atom _ -> SMT.List [f, a]
SMT.List es -> SMT.List (es ++ [a])
-- Important: Consistent references in Expr are maintained via de Bruijn indices, which do not exist in SExpr
-- Therefore, calls of exprToSExpr should be preceded by a call to renameExpr
exprToSExpr :: Show t => SMTEnv -> Expr t -> SMT.SExpr
exprToSExpr _ (ValE _ v) = valToSExpr v
exprToSExpr env (VarE _ v) = varToSExpr (funEnv env) v
exprToSExpr env (UnaOpE _ u e) = transUnaOp u (exprToSExpr env e)
exprToSExpr env (BinOpE _ b e1 e2) = transBinOp b (exprToSExpr env e1) (exprToSExpr env e2)
exprToSExpr env (IfThenElseE _ c e1 e2) = SMT.ite (exprToSExpr env c) (exprToSExpr env e1) (exprToSExpr env e2)
exprToSExpr env (QuantifE _ q v e) =
quantif q (varTypeToSExprTD (sortEnv env) (nameOfVarDecl v) (tpOfVarDecl v)) (exprToSExpr env e)
exprToSExpr env (AppE _ f a) = sExprApply (exprToSExpr env f) (exprToSExpr env a)
exprToSExpr _ e = error ("exprToSExpr: term " ++ show e ++ " not translatable")
-- TODO: still incomplete
-------------------------------------------------------------
-- Launching the solver and retrieving results
-------------------------------------------------------------
-- When no explicit log level is provided, the default is set to 1
-- (silent mode not showing interaction with SMT solver)
selectLogLevel :: Maybe ValueKVM -> Int
selectLogLevel config =
let defaultLogLevel = 1
in case getAssocOfPathValue ["loglevel"] (fromMaybe (IntVM 0) config) of
Nothing -> defaultLogLevel
Just (IntVM n) -> fromIntegral n
Just _ -> defaultLogLevel
createSolver :: Maybe ValueKVM -> Maybe SMT.Logger -> IO SMT.Solver
createSolver config lg =
let defaultConfig = ("z3", ["-in"])
(solverName, solverParams) = case config of
Nothing -> defaultConfig
Just vkvm -> case getAssocOfPathValue ["solver"] vkvm of
Just (IdVM "cvc4") -> ("cvc4", ["--lang=smt2"])
Just (IdVM "mathsat") -> ("mathsat", [])
_ -> defaultConfig
in SMT.newSolver solverName solverParams lg
-- Tried with the following provers:
-- alt-ergo gets stuck in interaction (apparently only reads from file)
-- Boolector (impossible to get compiled)
-- cvc4 does not work with quantifiers, simple boolean or arithmetic queries supported
-- mathsat terminates with an error (quantifiers not supported), simple boolean or arithmetic queries supported
-- yices does not support logics like LIA
selectLogic :: SMT.Solver -> Maybe ValueKVM -> IO ()
selectLogic s config =
let defaultConfig = "LIA"
logicName = case config of
Nothing -> defaultConfig
Just vkvm -> case getAssocOfPathValue ["logic"] vkvm of
Just (IdVM l) -> l
_ -> defaultConfig
in SMT.setLogic s logicName
-- Prove an expression e
-- For checkSat == True, check for satisfiability, otherwise for unsatisfiability
-- The expression e is assumed to be closed (no unbound local variables),
-- but may contain references to global declared or defined variables as in vardecls / vardefns
proveExpr :: Maybe ValueKVM -> Bool -> [ClassDecl (Tp())] -> [VarDecl (Tp())] -> [VarDefn (Tp())] -> Expr (Tp()) ->IO ()
proveExpr config checkSat cdecls vardecls vardefns e = do
l <- SMT.newLogger (selectLogLevel config)
s <- createSolver config (Just l)
selectLogic s config
sEnv <- classDeclsToSortEnv s cdecls
fEnv <- varDeclsToFunEnv s sEnv vardecls
varDefnEnv <- varDefnsToSMTEnv s (SMTEnv sEnv fEnv) vardefns
SMT.assert s (exprToSExpr varDefnEnv (renameExpr [] e))
checkRes <- SMT.check s
when (checkRes == SMT.Sat) $ do
if checkSat
then putStrLn "Formula satisfiable, found model."
else putStrLn "Formula not valid, found countermodel."
mdl <- getModel s
-- pPrint mdl
putStrLn (printDisplayableModel (displayableModel mdl))
when (checkRes == SMT.Unsat) $ do
if checkSat
then putStrLn "Formula unsatisfiable."
else putStrLn "Formula valid."
when (checkRes == SMT.Unknown) $ do
putStrLn "Solver produced unknown output."
putStrLn ""
-- TODO: to be defined in detail
defaultRuleSet :: Program t -> [Rule t]
defaultRuleSet = rulesOfProgram
-- TODO: rule specs are here supposed to be comma separated lists of rule names inclosed in { .. }
-- It should also be possible to specify transformations to the rules
rulesOfRuleSpec :: Program t -> ValueKVM -> [Rule t]
rulesOfRuleSpec p (MapVM kvm) =
let nameRuleAssoc = map (\r -> (fromMaybe "" (nameOfRule r), r)) (filter isNamedRule (rulesOfProgram p))
in map (\(k, _v) -> fromMaybe (error ("rule name " ++ k ++ " unknown in rule set")) (lookup k nameRuleAssoc)) kvm
rulesOfRuleSpec _p instr =
error ("rule specification " ++ show instr ++ " should be a list (in { .. }) of rule names and transformations")
-- add rules of rs2 to rs1, not adding rules already existing in rs1 as determined by name
addToRuleSet :: [Rule t] -> [Rule t] -> [Rule t]
addToRuleSet rs1 rs2 = rs1 ++ [r2 | r2 <- rs2 , Prelude.not (any (\r1 -> nameOfRule r1 == nameOfRule r2) rs1) ]
-- delete from rs1 the rules in rs2 as determined by name
delFromRuleSet :: [Rule t] -> [Rule t] -> [Rule t]
delFromRuleSet rs1 rs2 = [r1 | r1 <- rs1 , Prelude.not (any (\r2 -> nameOfRule r1 == nameOfRule r2) rs2) ]
composeApplicableRuleSet :: Program t -> Maybe ValueKVM -> Maybe ValueKVM -> Maybe ValueKVM -> [Rule t]
composeApplicableRuleSet p mbadd mbdel mbonly =
case mbonly of
Just onlyRls -> addToRuleSet [] (rulesOfRuleSpec p onlyRls)
Nothing -> addToRuleSet
(delFromRuleSet (defaultRuleSet p)
(rulesOfRuleSpec p (fromMaybe (MapVM []) mbdel)))
(rulesOfRuleSpec p (fromMaybe (MapVM []) mbadd))
selectApplicableRules :: Program t -> ValueKVM -> [Rule t]
selectApplicableRules p instr =
case getAssocOfPathValue ["rules"] instr of
Nothing -> defaultRuleSet p
Just rulespec -> composeApplicableRuleSet p
(getAssocOfPathValue ["add"] rulespec)
(getAssocOfPathValue ["del"] rulespec)
(getAssocOfPathValue ["only"] rulespec)
proveAssertionSMT :: Program (Tp ()) -> ValueKVM -> Assertion (Tp ()) -> IO ()
proveAssertionSMT prg instr asrt = do
putStrLn ("Launching SMT solver on " ++ printARName (nameOfAssertion asrt))
let proveConsistency = selectOneOfInstr ["consistent", "valid"] instr == "consistent"
let applicableRules = selectApplicableRules prg instr
let proofTarget = constrProofTarget proveConsistency (map ruleToFormula applicableRules) (exprOfAssertion asrt)
let config = getAssocOfPathValue ["config"] instr
proveExpr config proveConsistency (classDeclsOfProgram prg) (varDeclsOfProgram prg) (varDefnsOfProgram prg) proofTarget
constrProofTarget :: Bool -> [Expr (Tp())] -> Expr (Tp ()) -> Expr (Tp ())
constrProofTarget sat preconds concl =
if sat
then conjsExpr (concl : preconds)
else conjsExpr (notExpr concl : preconds)