-
Notifications
You must be signed in to change notification settings - Fork 4
/
SAT.hs
224 lines (185 loc) · 6.23 KB
/
SAT.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
{-|
Module : SAT
Description : Basic SAT operations
This module provides basic functions for working with Solver objects. A simple
example of typical use is:
@
main :: IO ()
main = do s <- newSolver
x <- newLit s
y <- newLit s
addClause s [neg x, neg y]
addClause s [x, y]
b <- solve s []
if b then
do putStrLn \"Found model!\"
a <- modelValue s x
b <- modelValue s y
putStrLn (\"x=\" ++ show a ++ \", y=\" ++ show b)
else
do putStrLn \"No model found.\"
deleteSolver s
@
-}
module SAT(
-- * The Solver object
Solver
, newSolver
, deleteSolver
, withNewSolver
, numAssigns
, numClauses
, numLearnts
, numVars
, numFreeVars
, numConflicts
-- * Literals
, Lit
, newLit
, false, true
, bool
, neg
, pos
-- * Clauses
, addClause
-- * Solving
, solve
, modelValue
, modelValueMaybe
, conflict
-- * Implied constants
, valueMaybe
)
where
import qualified MiniSat as M
import Data.IORef
import Data.Maybe( fromMaybe )
------------------------------------------------------------------------------
-- The Solver object
-- | The type of a Solver object
data Solver = Solver M.Solver (IORef (Maybe Lit))
-- | Create a Solver object.
newSolver :: IO Solver
newSolver =
do s <- M.newSolver
ref <- newIORef Nothing
return (Solver s ref)
-- | Delete a Solver object. Use only once!
deleteSolver :: Solver -> IO ()
deleteSolver (Solver s _) =
do M.deleteSolver s
-- | Create a Solver object, and delete when done.
withNewSolver :: (Solver -> IO a) -> IO a
withNewSolver h =
M.withNewSolver $ \s ->
do ref <- newIORef Nothing
h (Solver s ref)
-- | The current number of assigned literals.
numAssigns :: Solver -> IO Int
numAssigns (Solver m _) = M.minisat_num_assigns m
-- | The current number of original clauses.
numClauses :: Solver -> IO Int
numClauses (Solver m _) = M.minisat_num_clauses m
-- | The current number of learnt clauses.
numLearnts :: Solver -> IO Int
numLearnts (Solver m _) = M.minisat_num_learnts m
-- | The current number of variables.
numVars :: Solver -> IO Int
numVars (Solver m _) = M.minisat_num_vars m
numFreeVars :: Solver -> IO Int
numFreeVars (Solver m _) = M.minisat_num_freeVars m
numConflicts :: Solver -> IO Int
numConflicts (Solver m _) = M.minisat_num_conflicts m
------------------------------------------------------------------------------
-- Literals
-- | The type of a literal
data Lit = Bool Bool | Lit M.Lit
deriving ( Eq, Ord )
instance Show Lit where
show (Bool b) = show b
show (Lit x) = show x
-- | Create a fresh literal in a given Solver.
newLit :: Solver -> IO Lit
newLit (Solver s _) = Lit `fmap` M.newLit s
-- | Constant literal.
true, false :: Lit
true = Bool True
false = Bool False
-- | Create a constant literal based on a Bool.
bool :: Bool -> Lit
bool = Bool
-- | Negate a literal.
neg :: Lit -> Lit
neg (Bool b) = Bool (not b)
neg (Lit x) = Lit (M.neg x)
-- | Return the sign of a literal. The sign flips when a literal is negated.
pos :: Lit -> Bool
pos x = x < neg x
------------------------------------------------------------------------------
-- Clauses
-- | Add a clause in a given Solver. (The argument list is thus /disjunctive/.)
addClause :: Solver -> [Lit] -> IO ()
addClause (Solver s _) xs
| true `elem` xs = do return ()
| otherwise = do M.addClause s [ x | Lit x <- xs ]; return ()
------------------------------------------------------------------------------
-- Solving
-- | Try to find a model of all clauses in the given Solver, under the
-- assumptions of the given arguments. (The argument list is thus /conjunctive/.)
-- Returns True if a model was found, False if no model was found.
solve :: Solver -> [Lit] -> IO Bool
solve (Solver s ref) xs
| false `elem` xs =
do writeIORef ref (Just true)
return False
| otherwise =
do writeIORef ref Nothing
M.solve s [ x | Lit x <- xs ]
-- | If the last call to 'solve' returned False: Return the conflict clause
-- that was the reason for the fact that no model was found under the
-- specified assumptions. The conflict clause only contains literals that
-- are negations of the assumptions given to 'solve'. The conflict
-- clause is always logically implied by the current set of clauses.
--
-- For example, if the returned clause is empty, there is a contradiction even
-- without any assumptions.
--
-- This function can be used to implement so-called \'unsatisfiable cores\'.
--
-- There are no guarantees about minimality of the returned clause.
-- (/Only use when 'solve' has previously returned False!/)
conflict :: Solver -> IO [Lit]
conflict (Solver s ref) =
do mx <- readIORef ref
case mx of
Nothing -> do xs <- M.conflict s
return (map Lit xs)
Just x -> do return [x]
------------------------------------------------------------------------------
-- | If the last call to 'solve' returned True, return the value of
-- the specified literal in the found model.
-- (/Only use when 'solve' has previously returned True!/)
modelValue :: Solver -> Lit -> IO Bool
modelValue s x =
do mb <- modelValueMaybe s x
return (fromMaybe (not (pos x)) mb)
-- | If the last call to 'solve' returned True, return the value of
-- the specified literal in the found model, or Nothing if there is a model
-- regardless of the value of this literal.
-- There are no guarantees about when Nothing is returned.
-- (/Only use when 'solve' has previously returned True!/)
modelValueMaybe :: Solver -> Lit -> IO (Maybe Bool)
modelValueMaybe _ (Bool b) =
do return (Just b)
modelValueMaybe (Solver s _) (Lit x) =
do M.modelValue s x
------------------------------------------------------------------------------
-- Implied constants
-- | Check whether or not a given literal has received a top-level value
-- in the given Solver. This can happen when the literal is implied to be
-- False or True by the current set of clauses. There are no guarantees about
-- when this actually happens.
valueMaybe :: Solver -> Lit -> IO (Maybe Bool)
valueMaybe _ (Bool b) = return (Just b)
valueMaybe (Solver s _) (Lit x) = M.value s x
------------------------------------------------------------------------------