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
|
module XFD.SMTLib
( solveSMT
) where
import XFD.SMTLib.Pretty
import XFD.SMTLib.Types
import XFD.SMTLib.Build
import XFD.SMTLib.RDParser
import XFD.SMTLib.FromFD
import XFD.FD
import XFD.Solver
import IO
import IOExts
import List (last, init)
hGetContentsUntil :: Handle -> String -> IO String
hGetContentsUntil h s = do line <- hGetLine h
let l = unstring line
if l == s
then return []
else do ls <- hGetContentsUntil h s
return (l ++ "\n" ++ ls)
where
unstring :: String -> String
unstring s' = case (s', head s', last s') of
(_:tr@(_:_), '"', '"') -> init tr
_ -> s'
hPutStrFlush :: Handle -> String -> IO ()
hPutStrFlush h s = hPutStr h s >> hFlush h
outputDelimiter :: String
outputDelimiter = "===STOP-READING==="
hGetDelimited :: Handle -> IO String
hGetDelimited h = hGetContentsUntil h outputDelimiter
delimSMT :: SMT -> String
delimSMT cmds = showSMT (cmds =>> echo outputDelimiter)
solveSMT :: SolverImpl
solveSMT _ _ [] _ = return [[]]
solveSMT cfg options vars@(_:_) constr = do
let pre = setOptions (smtOptions cfg) =>> setLogic (smtLogic cfg)
cmds = declare (allFDVars constr) =>> assert ( constr )
smt = pre =>> cmds
exec = unwords $ (executable cfg):(flags cfg)
(debug, numSol, (persist, filename), mini, maxi) = getSolverOptions options
when (debug > 0) $ putStrLn $ "debugging level " ++ (show debug)
when (debug > 0) $ do
putStrLn $ "using solver " ++ (executable cfg)
putStrLn $ " called with options " ++ unwords (flags cfg)
putStrLn $ " with logic " ++ (smtLogic cfg)
when persist $ do
putStrLn $ "saving program in file '" ++ filename ++ "'"
writeFile filename $ showSMT smt
when (debug > 1) $ putStr "starting solver... "
(inH, outH, _) <- execCmd exec
when (debug > 1) $ putStrLn "done"
when (debug > 1) $ putStr "writing program to stdin... "
hPutStrFlush inH $ delimSMT $ smt
when (debug > 1) $ putStrLn "done"
checkForError outH debug
when (debug > 1) $ putStrLn "searching solutions"
solutions <- getSolutions inH outH vars numSol debug 1 mini maxi
when (debug > 1) $ putStrLn $ "found " ++ (show (length solutions)) ++ " solutions"
hPutStrFlush inH $ showSMT exit
hClose inH
hClose outH
return solutions
checkForError :: Handle -> Int -> IO ()
checkForError outH debug = do
when (debug > 1) $ putStr "checking for errors raised by solver... "
response <- hGetDelimited outH
if (response /= "")
then do
case parse response of
Left e -> error e
Right p -> case p of
CmdGenResponse (Error msg) -> do
when (debug > 1) $ putStrLn ("got error message")
error msg
_ -> error "unexpected response"
else do
when (debug > 1) $ putStrLn "all clear"
putSolution :: Int -> [Int] -> IO ()
putSolution n s = putStrLn $ "Solution #" ++ (show n) ++ ": " ++ (show s)
getSolutions :: Handle -> Handle -> [FDExpr] -> Int -> Int
-> Int -> (Bool, FDExpr) -> (Bool, FDExpr) -> IO [[Int]]
getSolutions inH outH fds n debug nSol (mini, miv) (maxi, mav)
| n == 0 = return []
| otherwise = do
hPutStrFlush inH $ delimSMT checkSat
when (debug > 1) $ putStr "waiting for checksat response... "
response <- hGetDelimited outH
when (debug > 1) $ putStrLn "received"
case parse response of
Left e -> putStr e >> return []
Right p -> case p of
CmdGenResponse (Error msg) -> do
putStrLn $ "error from solver: " ++ msg
return []
CmdCheckSatResponse Sat -> do
when mini $ optimize inH outH debug miv (<#)
when maxi $ optimize inH outH debug mav (>#)
hPutStrFlush inH $ delimSMT (getValue fds)
when (debug > 1) $ putStr "waiting for value response... "
values <- hGetDelimited outH
when (debug > 1) $ putStrLn "received"
let vs = case parse values of
Left e -> error e
Right vr -> convertValueResponse vr
sol = extractValues vs
when (debug > 1) $ putStr "writing excluded solution... "
hPutStrFlush inH $ showSMT (excludeSolution vs)
when (debug > 1) $ putStrLn "done"
when (debug > 0) $ putSolution nSol sol
liftIO (sol:) $ getSolutions inH outH fds (n-1) debug (nSol+1) (False, _) (False, _)
_ -> return []
optimize :: Handle -> Handle -> Int -> FDExpr -> (FDExpr -> FDExpr -> FDConstr) -> IO ()
optimize inH outH debug ov relop = do
when (debug > 1) $ putStrLn $ "optimizing value for variable "
++ getFDVarName optv
hPutStrFlush inH $ delimSMT (getValue [optv])
when (debug > 1) $ putStr "waiting for value response... "
values <- hGetDelimited outH
when (debug > 1) $ putStrLn "received"
let vs = case parse values of
Left e -> error e
Right vr -> convertValueResponse vr
sol = head $ extractValues vs
optval <- optimize' sol
when (debug > 1) $ putStrLn $ "found optimal value to be " ++ show optval
hPutStrFlush inH $ showSMT (assert (optv =# fd optval))
hPutStrFlush inH $ delimSMT checkSat
when (debug > 1) $ putStr "waiting for checksat response... "
response <- hGetDelimited outH
when (debug > 1) $ putStrLn "received"
case parse response of
Left e -> error e
Right p -> case p of
CmdGenResponse (Error msg) -> error $ "error from solver: " ++ msg
CmdCheckSatResponse Sat -> return ()
_ -> error "error occured"
where
optv = case ov of
FDVar _ _ _ _ -> ov
_ -> error "optimize: not an FDVar"
optimize' :: Int -> IO Int
optimize' val = do
hPutStrFlush inH $ showSMT $ push 1
hPutStrFlush inH $ showSMT (assert (optv `relop` fd val))
hPutStrFlush inH $ delimSMT checkSat
when (debug > 1) $ putStr "waiting for checksat response... "
response <- hGetDelimited outH
when (debug > 1) $ putStrLn "received"
case parse response of
Left e -> error e
Right p -> case p of
CmdGenResponse (Error msg) -> error $ "error from solver: " ++ msg
CmdCheckSatResponse Sat -> do
hPutStrFlush inH $ delimSMT (getValue [optv])
when (debug > 1) $ putStr "waiting for value response... "
values <- hGetDelimited outH
when (debug > 1) $ putStrLn "received"
let vs = case parse values of
Left e -> error e
Right vr -> convertValueResponse vr
sol = head $ extractValues vs
hPutStrFlush inH $ showSMT $ pop 1
optimize' sol
_ -> do
hPutStrFlush inH $ showSMT $ pop 1
return val
convertValueResponse :: CmdResponse -> [FDExpr]
convertValueResponse rsp = case rsp of
CmdGetValueResponse vr -> map extractVP vr
_ -> []
where
(ValuationPair n v) = toFD n v
toFD n v = case n of
TermQualIdentifier (QIdentifier (ISymbol s)) -> FDVar s _ _ (value v False)
TermSpecConstant (SpecConstantNumeral _) -> fd (value v False)
TermQualIdentifierT (QIdentifier (ISymbol "-")) [i] -> fd (value i True)
_ -> error "not a variable name or integer"
value t neg = case t of
TermSpecConstant (SpecConstantNumeral v) -> if neg then (-v) else v
TermQualIdentifierT (QIdentifier (ISymbol "-")) [v] -> value v True
_ -> error "not a value"
extractValues :: [FDExpr] -> [Int]
= map getFDVal
excludeSolution :: [FDExpr] -> SMT
excludeSolution vs = assert (orC $ map exclude (filterVars vs))
where
exclude x = case x of
FDVar _ _ _ v -> x /=# fd v
_ -> error "solutin of non FDVars cannot be excluded"
|