1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Dejan Jovanovic, Haniel Barbosa, Liana Hadarean |
4 |
|
* |
5 |
|
* This file is part of the cvc5 project. |
6 |
|
* |
7 |
|
* Copyright (c) 2009-2021 by the authors listed in the file AUTHORS |
8 |
|
* in the top-level source directory and their institutional affiliations. |
9 |
|
* All rights reserved. See the file COPYING in the top-level source |
10 |
|
* directory for licensing information. |
11 |
|
* **************************************************************************** |
12 |
|
* |
13 |
|
* A CNF converter that takes in asserts and has the side effect of given an |
14 |
|
* equisatisfiable stream of assertions to PropEngine. |
15 |
|
*/ |
16 |
|
#include "prop/cnf_stream.h" |
17 |
|
|
18 |
|
#include <queue> |
19 |
|
|
20 |
|
#include "base/check.h" |
21 |
|
#include "base/output.h" |
22 |
|
#include "expr/node.h" |
23 |
|
#include "options/bv_options.h" |
24 |
|
#include "proof/clause_id.h" |
25 |
|
#include "prop/minisat/minisat.h" |
26 |
|
#include "prop/prop_engine.h" |
27 |
|
#include "prop/theory_proxy.h" |
28 |
|
#include "smt/dump.h" |
29 |
|
#include "smt/smt_engine.h" |
30 |
|
#include "printer/printer.h" |
31 |
|
#include "smt/smt_engine_scope.h" |
32 |
|
#include "theory/theory.h" |
33 |
|
#include "theory/theory_engine.h" |
34 |
|
|
35 |
|
namespace cvc5 { |
36 |
|
namespace prop { |
37 |
|
|
38 |
17943 |
CnfStream::CnfStream(SatSolver* satSolver, |
39 |
|
Registrar* registrar, |
40 |
|
context::Context* context, |
41 |
|
OutputManager* outMgr, |
42 |
|
ResourceManager* rm, |
43 |
|
FormulaLitPolicy flpol, |
44 |
17943 |
std::string name) |
45 |
|
: d_satSolver(satSolver), |
46 |
|
d_outMgr(outMgr), |
47 |
|
d_booleanVariables(context), |
48 |
|
d_notifyFormulas(context), |
49 |
|
d_nodeToLiteralMap(context), |
50 |
|
d_literalToNodeMap(context), |
51 |
|
d_flitPolicy(flpol), |
52 |
|
d_registrar(registrar), |
53 |
|
d_name(name), |
54 |
|
d_removable(false), |
55 |
17943 |
d_resourceManager(rm) |
56 |
|
{ |
57 |
17943 |
} |
58 |
|
|
59 |
9889312 |
bool CnfStream::assertClause(TNode node, SatClause& c) |
60 |
|
{ |
61 |
9889312 |
Trace("cnf") << "Inserting into stream " << c << " node = " << node << "\n"; |
62 |
9889312 |
if (Dump.isOn("clauses") && d_outMgr != nullptr) |
63 |
|
{ |
64 |
|
const Printer& printer = d_outMgr->getPrinter(); |
65 |
|
std::ostream& out = d_outMgr->getDumpOut(); |
66 |
|
if (c.size() == 1) |
67 |
|
{ |
68 |
|
printer.toStreamCmdAssert(out, getNode(c[0])); |
69 |
|
} |
70 |
|
else |
71 |
|
{ |
72 |
|
Assert(c.size() > 1); |
73 |
|
NodeBuilder b(kind::OR); |
74 |
|
for (unsigned i = 0; i < c.size(); ++i) |
75 |
|
{ |
76 |
|
b << getNode(c[i]); |
77 |
|
} |
78 |
|
Node n = b; |
79 |
|
printer.toStreamCmdAssert(out, n); |
80 |
|
} |
81 |
|
} |
82 |
|
|
83 |
9889312 |
ClauseId clauseId = d_satSolver->addClause(c, d_removable); |
84 |
|
|
85 |
9889312 |
return clauseId != ClauseIdUndef; |
86 |
|
} |
87 |
|
|
88 |
199660 |
bool CnfStream::assertClause(TNode node, SatLiteral a) |
89 |
|
{ |
90 |
399320 |
SatClause clause(1); |
91 |
199660 |
clause[0] = a; |
92 |
399320 |
return assertClause(node, clause); |
93 |
|
} |
94 |
|
|
95 |
4398834 |
bool CnfStream::assertClause(TNode node, SatLiteral a, SatLiteral b) |
96 |
|
{ |
97 |
8797668 |
SatClause clause(2); |
98 |
4398834 |
clause[0] = a; |
99 |
4398834 |
clause[1] = b; |
100 |
8797668 |
return assertClause(node, clause); |
101 |
|
} |
102 |
|
|
103 |
3202389 |
bool CnfStream::assertClause(TNode node, |
104 |
|
SatLiteral a, |
105 |
|
SatLiteral b, |
106 |
|
SatLiteral c) |
107 |
|
{ |
108 |
6404778 |
SatClause clause(3); |
109 |
3202389 |
clause[0] = a; |
110 |
3202389 |
clause[1] = b; |
111 |
3202389 |
clause[2] = c; |
112 |
6404778 |
return assertClause(node, clause); |
113 |
|
} |
114 |
|
|
115 |
84852370 |
bool CnfStream::hasLiteral(TNode n) const { |
116 |
84852370 |
NodeToLiteralMap::const_iterator find = d_nodeToLiteralMap.find(n); |
117 |
84852370 |
return find != d_nodeToLiteralMap.end(); |
118 |
|
} |
119 |
|
|
120 |
148815 |
void CnfStream::ensureMappingForLiteral(TNode n) |
121 |
|
{ |
122 |
148815 |
SatLiteral lit = getLiteral(n); |
123 |
148815 |
if (!d_literalToNodeMap.contains(lit)) |
124 |
|
{ |
125 |
|
// Store backward-mappings |
126 |
|
d_literalToNodeMap.insert(lit, n); |
127 |
|
d_literalToNodeMap.insert(~lit, n.notNode()); |
128 |
|
} |
129 |
148815 |
} |
130 |
|
|
131 |
234798 |
void CnfStream::ensureLiteral(TNode n) |
132 |
|
{ |
133 |
234798 |
AlwaysAssertArgument( |
134 |
|
hasLiteral(n) || n.getType().isBoolean(), |
135 |
|
n, |
136 |
|
"ProofCnfStream::ensureLiteral() requires a node of Boolean type.\n" |
137 |
|
"got node: %s\n" |
138 |
|
"its type: %s\n", |
139 |
|
n.toString().c_str(), |
140 |
|
n.getType().toString().c_str()); |
141 |
234798 |
Trace("cnf") << "ensureLiteral(" << n << ")\n"; |
142 |
234798 |
if (hasLiteral(n)) |
143 |
|
{ |
144 |
130498 |
ensureMappingForLiteral(n); |
145 |
130498 |
return; |
146 |
|
} |
147 |
|
// remove top level negation |
148 |
104300 |
n = n.getKind() == kind::NOT ? n[0] : n; |
149 |
104300 |
if (theory::Theory::theoryOf(n) == theory::THEORY_BOOL && !n.isVar()) |
150 |
|
{ |
151 |
|
// If we were called with something other than a theory atom (or |
152 |
|
// Boolean variable), we get a SatLiteral that is definitionally |
153 |
|
// equal to it. |
154 |
|
// These are not removable and have no proof ID |
155 |
14059 |
d_removable = false; |
156 |
|
|
157 |
14059 |
SatLiteral lit = toCNF(n, false); |
158 |
|
|
159 |
|
// Store backward-mappings |
160 |
|
// These may already exist |
161 |
14059 |
d_literalToNodeMap.insert_safe(lit, n); |
162 |
14059 |
d_literalToNodeMap.insert_safe(~lit, n.notNode()); |
163 |
|
} |
164 |
|
else |
165 |
|
{ |
166 |
|
// We have a theory atom or variable. |
167 |
90241 |
convertAtom(n); |
168 |
|
} |
169 |
|
} |
170 |
|
|
171 |
2837155 |
SatLiteral CnfStream::newLiteral(TNode node, bool isTheoryAtom, bool preRegister, bool canEliminate) { |
172 |
5674310 |
Trace("cnf") << d_name << "::newLiteral(" << node << ", " << isTheoryAtom |
173 |
2837155 |
<< ")\n" |
174 |
2837155 |
<< push; |
175 |
2837155 |
Assert(node.getKind() != kind::NOT); |
176 |
|
|
177 |
|
// if we are tracking formulas, everything is a theory atom |
178 |
2837155 |
if (!isTheoryAtom && d_flitPolicy == FormulaLitPolicy::TRACK_AND_NOTIFY) |
179 |
|
{ |
180 |
|
isTheoryAtom = true; |
181 |
|
d_notifyFormulas.insert(node); |
182 |
|
} |
183 |
|
|
184 |
|
// Get the literal for this node |
185 |
2837155 |
SatLiteral lit; |
186 |
2837155 |
if (!hasLiteral(node)) { |
187 |
2837146 |
Trace("cnf") << d_name << "::newLiteral: node already registered\n"; |
188 |
|
// If no literal, we'll make one |
189 |
2837146 |
if (node.getKind() == kind::CONST_BOOLEAN) { |
190 |
18326 |
Trace("cnf") << d_name << "::newLiteral: boolean const\n"; |
191 |
18326 |
if (node.getConst<bool>()) { |
192 |
9113 |
lit = SatLiteral(d_satSolver->trueVar()); |
193 |
|
} else { |
194 |
9213 |
lit = SatLiteral(d_satSolver->falseVar()); |
195 |
|
} |
196 |
|
} else { |
197 |
2818820 |
Trace("cnf") << d_name << "::newLiteral: new var\n"; |
198 |
2818820 |
lit = SatLiteral(d_satSolver->newVar(isTheoryAtom, preRegister, canEliminate)); |
199 |
|
} |
200 |
2837146 |
d_nodeToLiteralMap.insert(node, lit); |
201 |
2837146 |
d_nodeToLiteralMap.insert(node.notNode(), ~lit); |
202 |
|
} else { |
203 |
9 |
lit = getLiteral(node); |
204 |
|
} |
205 |
|
|
206 |
|
// If it's a theory literal, need to store it for back queries |
207 |
4880245 |
if (isTheoryAtom || d_flitPolicy == FormulaLitPolicy::TRACK |
208 |
4549289 |
|| (Dump.isOn("clauses"))) |
209 |
|
{ |
210 |
1125021 |
d_literalToNodeMap.insert_safe(lit, node); |
211 |
1125021 |
d_literalToNodeMap.insert_safe(~lit, node.notNode()); |
212 |
|
} |
213 |
|
|
214 |
|
// If a theory literal, we pre-register it |
215 |
2837155 |
if (preRegister) { |
216 |
|
// In case we are re-entered due to lemmas, save our state |
217 |
794065 |
bool backupRemovable = d_removable; |
218 |
794069 |
d_registrar->preRegister(node); |
219 |
794061 |
d_removable = backupRemovable; |
220 |
|
} |
221 |
|
// Here, you can have it |
222 |
2837151 |
Trace("cnf") << "newLiteral(" << node << ") => " << lit << "\n" << pop; |
223 |
2837151 |
return lit; |
224 |
|
} |
225 |
|
|
226 |
15855554 |
TNode CnfStream::getNode(const SatLiteral& literal) { |
227 |
15855554 |
Trace("cnf") << "getNode(" << literal << ")\n"; |
228 |
31711108 |
Trace("cnf") << "getNode(" << literal << ") => " |
229 |
15855554 |
<< d_literalToNodeMap[literal] << "\n"; |
230 |
15855554 |
return d_literalToNodeMap[literal]; |
231 |
|
} |
232 |
|
|
233 |
4990438 |
const CnfStream::NodeToLiteralMap& CnfStream::getTranslationCache() const |
234 |
|
{ |
235 |
4990438 |
return d_nodeToLiteralMap; |
236 |
|
} |
237 |
|
|
238 |
14287717 |
const CnfStream::LiteralToNodeMap& CnfStream::getNodeCache() const |
239 |
|
{ |
240 |
14287717 |
return d_literalToNodeMap; |
241 |
|
} |
242 |
|
|
243 |
15156 |
void CnfStream::getBooleanVariables(std::vector<TNode>& outputVariables) const { |
244 |
15156 |
context::CDList<TNode>::const_iterator it, it_end; |
245 |
73409 |
for (it = d_booleanVariables.begin(); it != d_booleanVariables.end(); ++ it) { |
246 |
58253 |
outputVariables.push_back(*it); |
247 |
|
} |
248 |
15156 |
} |
249 |
|
|
250 |
|
bool CnfStream::isNotifyFormula(TNode node) const |
251 |
|
{ |
252 |
|
return d_notifyFormulas.find(node) != d_notifyFormulas.end(); |
253 |
|
} |
254 |
|
|
255 |
874719 |
SatLiteral CnfStream::convertAtom(TNode node) |
256 |
|
{ |
257 |
874719 |
Trace("cnf") << "convertAtom(" << node << ")\n"; |
258 |
|
|
259 |
874719 |
Assert(!hasLiteral(node)) << "atom already mapped!"; |
260 |
|
|
261 |
874719 |
bool theoryLiteral = false; |
262 |
874719 |
bool canEliminate = true; |
263 |
874719 |
bool preRegister = false; |
264 |
|
|
265 |
|
// Is this a variable add it to the list |
266 |
874719 |
if (node.isVar() && node.getKind() != kind::BOOLEAN_TERM_VARIABLE) |
267 |
|
{ |
268 |
80654 |
d_booleanVariables.push_back(node); |
269 |
|
} |
270 |
|
else |
271 |
|
{ |
272 |
794065 |
theoryLiteral = true; |
273 |
794065 |
canEliminate = false; |
274 |
794065 |
preRegister = true; |
275 |
|
} |
276 |
|
|
277 |
|
// Make a new literal (variables are not considered theory literals) |
278 |
874723 |
SatLiteral lit = newLiteral(node, theoryLiteral, preRegister, canEliminate); |
279 |
|
// Return the resulting literal |
280 |
874715 |
return lit; |
281 |
|
} |
282 |
|
|
283 |
63070892 |
SatLiteral CnfStream::getLiteral(TNode node) { |
284 |
63070892 |
Assert(!node.isNull()) << "CnfStream: can't getLiteral() of null node"; |
285 |
|
|
286 |
126141784 |
Assert(d_nodeToLiteralMap.contains(node)) |
287 |
63070892 |
<< "Literal not in the CNF Cache: " << node << "\n"; |
288 |
|
|
289 |
63070892 |
SatLiteral literal = d_nodeToLiteralMap[node]; |
290 |
126141784 |
Trace("cnf") << "CnfStream::getLiteral(" << node << ") => " << literal |
291 |
63070892 |
<< "\n"; |
292 |
63070892 |
return literal; |
293 |
|
} |
294 |
|
|
295 |
290540 |
SatLiteral CnfStream::handleXor(TNode xorNode) |
296 |
|
{ |
297 |
290540 |
Assert(!hasLiteral(xorNode)) << "Atom already mapped!"; |
298 |
290540 |
Assert(xorNode.getKind() == kind::XOR) << "Expecting an XOR expression!"; |
299 |
290540 |
Assert(xorNode.getNumChildren() == 2) << "Expecting exactly 2 children!"; |
300 |
290540 |
Assert(!d_removable) << "Removable clauses can not contain Boolean structure"; |
301 |
290540 |
Trace("cnf") << "CnfStream::handleXor(" << xorNode << ")\n"; |
302 |
|
|
303 |
290540 |
SatLiteral a = toCNF(xorNode[0]); |
304 |
290540 |
SatLiteral b = toCNF(xorNode[1]); |
305 |
|
|
306 |
290540 |
SatLiteral xorLit = newLiteral(xorNode); |
307 |
|
|
308 |
290540 |
assertClause(xorNode.negate(), a, b, ~xorLit); |
309 |
290540 |
assertClause(xorNode.negate(), ~a, ~b, ~xorLit); |
310 |
290540 |
assertClause(xorNode, a, ~b, xorLit); |
311 |
290540 |
assertClause(xorNode, ~a, b, xorLit); |
312 |
|
|
313 |
290540 |
return xorLit; |
314 |
|
} |
315 |
|
|
316 |
417034 |
SatLiteral CnfStream::handleOr(TNode orNode) |
317 |
|
{ |
318 |
417034 |
Assert(!hasLiteral(orNode)) << "Atom already mapped!"; |
319 |
417034 |
Assert(orNode.getKind() == kind::OR) << "Expecting an OR expression!"; |
320 |
417034 |
Assert(orNode.getNumChildren() > 1) << "Expecting more then 1 child!"; |
321 |
417034 |
Assert(!d_removable) << "Removable clauses can not contain Boolean structure"; |
322 |
417034 |
Trace("cnf") << "CnfStream::handleOr(" << orNode << ")\n"; |
323 |
|
|
324 |
|
// Number of children |
325 |
417034 |
unsigned n_children = orNode.getNumChildren(); |
326 |
|
|
327 |
|
// Transform all the children first |
328 |
417034 |
TNode::const_iterator node_it = orNode.begin(); |
329 |
417034 |
TNode::const_iterator node_it_end = orNode.end(); |
330 |
834068 |
SatClause clause(n_children + 1); |
331 |
1418308 |
for(int i = 0; node_it != node_it_end; ++node_it, ++i) { |
332 |
1001274 |
clause[i] = toCNF(*node_it); |
333 |
|
} |
334 |
|
|
335 |
|
// Get the literal for this node |
336 |
417034 |
SatLiteral orLit = newLiteral(orNode); |
337 |
|
|
338 |
|
// lit <- (a_1 | a_2 | a_3 | ... | a_n) |
339 |
|
// lit | ~(a_1 | a_2 | a_3 | ... | a_n) |
340 |
|
// (lit | ~a_1) & (lit | ~a_2) & (lit & ~a_3) & ... & (lit & ~a_n) |
341 |
1418308 |
for(unsigned i = 0; i < n_children; ++i) { |
342 |
1001274 |
assertClause(orNode, orLit, ~clause[i]); |
343 |
|
} |
344 |
|
|
345 |
|
// lit -> (a_1 | a_2 | a_3 | ... | a_n) |
346 |
|
// ~lit | a_1 | a_2 | a_3 | ... | a_n |
347 |
417034 |
clause[n_children] = ~orLit; |
348 |
|
// This needs to go last, as the clause might get modified by the SAT solver |
349 |
417034 |
assertClause(orNode.negate(), clause); |
350 |
|
|
351 |
|
// Return the literal |
352 |
834068 |
return orLit; |
353 |
|
} |
354 |
|
|
355 |
746793 |
SatLiteral CnfStream::handleAnd(TNode andNode) |
356 |
|
{ |
357 |
746793 |
Assert(!hasLiteral(andNode)) << "Atom already mapped!"; |
358 |
746793 |
Assert(andNode.getKind() == kind::AND) << "Expecting an AND expression!"; |
359 |
746793 |
Assert(andNode.getNumChildren() > 1) << "Expecting more than 1 child!"; |
360 |
746793 |
Assert(!d_removable) << "Removable clauses can not contain Boolean structure"; |
361 |
746793 |
Trace("cnf") << "handleAnd(" << andNode << ")\n"; |
362 |
|
|
363 |
|
// Number of children |
364 |
746793 |
unsigned n_children = andNode.getNumChildren(); |
365 |
|
|
366 |
|
// Transform all the children first (remembering the negation) |
367 |
746793 |
TNode::const_iterator node_it = andNode.begin(); |
368 |
746793 |
TNode::const_iterator node_it_end = andNode.end(); |
369 |
1493586 |
SatClause clause(n_children + 1); |
370 |
3927650 |
for(int i = 0; node_it != node_it_end; ++node_it, ++i) { |
371 |
3180857 |
clause[i] = ~toCNF(*node_it); |
372 |
|
} |
373 |
|
|
374 |
|
// Get the literal for this node |
375 |
746793 |
SatLiteral andLit = newLiteral(andNode); |
376 |
|
|
377 |
|
// lit -> (a_1 & a_2 & a_3 & ... & a_n) |
378 |
|
// ~lit | (a_1 & a_2 & a_3 & ... & a_n) |
379 |
|
// (~lit | a_1) & (~lit | a_2) & ... & (~lit | a_n) |
380 |
3927650 |
for(unsigned i = 0; i < n_children; ++i) { |
381 |
3180857 |
assertClause(andNode.negate(), ~andLit, ~clause[i]); |
382 |
|
} |
383 |
|
|
384 |
|
// lit <- (a_1 & a_2 & a_3 & ... a_n) |
385 |
|
// lit | ~(a_1 & a_2 & a_3 & ... & a_n) |
386 |
|
// lit | ~a_1 | ~a_2 | ~a_3 | ... | ~a_n |
387 |
746793 |
clause[n_children] = andLit; |
388 |
|
// This needs to go last, as the clause might get modified by the SAT solver |
389 |
746793 |
assertClause(andNode, clause); |
390 |
|
|
391 |
1493586 |
return andLit; |
392 |
|
} |
393 |
|
|
394 |
6822 |
SatLiteral CnfStream::handleImplies(TNode impliesNode) |
395 |
|
{ |
396 |
6822 |
Assert(!hasLiteral(impliesNode)) << "Atom already mapped!"; |
397 |
6822 |
Assert(impliesNode.getKind() == kind::IMPLIES) |
398 |
|
<< "Expecting an IMPLIES expression!"; |
399 |
6822 |
Assert(impliesNode.getNumChildren() == 2) << "Expecting exactly 2 children!"; |
400 |
6822 |
Assert(!d_removable) << "Removable clauses can not contain Boolean structure"; |
401 |
6822 |
Trace("cnf") << "handleImplies(" << impliesNode << ")\n"; |
402 |
|
|
403 |
|
// Convert the children to cnf |
404 |
6822 |
SatLiteral a = toCNF(impliesNode[0]); |
405 |
6822 |
SatLiteral b = toCNF(impliesNode[1]); |
406 |
|
|
407 |
6822 |
SatLiteral impliesLit = newLiteral(impliesNode); |
408 |
|
|
409 |
|
// lit -> (a->b) |
410 |
|
// ~lit | ~ a | b |
411 |
6822 |
assertClause(impliesNode.negate(), ~impliesLit, ~a, b); |
412 |
|
|
413 |
|
// (a->b) -> lit |
414 |
|
// ~(~a | b) | lit |
415 |
|
// (a | l) & (~b | l) |
416 |
6822 |
assertClause(impliesNode, a, impliesLit); |
417 |
6822 |
assertClause(impliesNode, ~b, impliesLit); |
418 |
|
|
419 |
6822 |
return impliesLit; |
420 |
|
} |
421 |
|
|
422 |
371598 |
SatLiteral CnfStream::handleIff(TNode iffNode) |
423 |
|
{ |
424 |
371598 |
Assert(!hasLiteral(iffNode)) << "Atom already mapped!"; |
425 |
371598 |
Assert(iffNode.getKind() == kind::EQUAL) << "Expecting an EQUAL expression!"; |
426 |
371598 |
Assert(iffNode.getNumChildren() == 2) << "Expecting exactly 2 children!"; |
427 |
371598 |
Assert(!d_removable) << "Removable clauses can not contain Boolean structure"; |
428 |
371598 |
Trace("cnf") << "handleIff(" << iffNode << ")\n"; |
429 |
|
|
430 |
|
// Convert the children to CNF |
431 |
371598 |
SatLiteral a = toCNF(iffNode[0]); |
432 |
371598 |
SatLiteral b = toCNF(iffNode[1]); |
433 |
|
|
434 |
|
// Get the now literal |
435 |
371598 |
SatLiteral iffLit = newLiteral(iffNode); |
436 |
|
|
437 |
|
// lit -> ((a-> b) & (b->a)) |
438 |
|
// ~lit | ((~a | b) & (~b | a)) |
439 |
|
// (~a | b | ~lit) & (~b | a | ~lit) |
440 |
371598 |
assertClause(iffNode.negate(), ~a, b, ~iffLit); |
441 |
371598 |
assertClause(iffNode.negate(), a, ~b, ~iffLit); |
442 |
|
|
443 |
|
// (a<->b) -> lit |
444 |
|
// ~((a & b) | (~a & ~b)) | lit |
445 |
|
// (~(a & b)) & (~(~a & ~b)) | lit |
446 |
|
// ((~a | ~b) & (a | b)) | lit |
447 |
|
// (~a | ~b | lit) & (a | b | lit) |
448 |
371598 |
assertClause(iffNode, ~a, ~b, iffLit); |
449 |
371598 |
assertClause(iffNode, a, b, iffLit); |
450 |
|
|
451 |
371598 |
return iffLit; |
452 |
|
} |
453 |
|
|
454 |
85666 |
SatLiteral CnfStream::handleIte(TNode iteNode) |
455 |
|
{ |
456 |
85666 |
Assert(!hasLiteral(iteNode)) << "Atom already mapped!"; |
457 |
85666 |
Assert(iteNode.getKind() == kind::ITE); |
458 |
85666 |
Assert(iteNode.getNumChildren() == 3); |
459 |
85666 |
Assert(!d_removable) << "Removable clauses can not contain Boolean structure"; |
460 |
171332 |
Trace("cnf") << "handleIte(" << iteNode[0] << " " << iteNode[1] << " " |
461 |
85666 |
<< iteNode[2] << ")\n"; |
462 |
|
|
463 |
85666 |
SatLiteral condLit = toCNF(iteNode[0]); |
464 |
85666 |
SatLiteral thenLit = toCNF(iteNode[1]); |
465 |
85666 |
SatLiteral elseLit = toCNF(iteNode[2]); |
466 |
|
|
467 |
85666 |
SatLiteral iteLit = newLiteral(iteNode); |
468 |
|
|
469 |
|
// If ITE is true then one of the branches is true and the condition |
470 |
|
// implies which one |
471 |
|
// lit -> (ite b t e) |
472 |
|
// lit -> (t | e) & (b -> t) & (!b -> e) |
473 |
|
// lit -> (t | e) & (!b | t) & (b | e) |
474 |
|
// (!lit | t | e) & (!lit | !b | t) & (!lit | b | e) |
475 |
85666 |
assertClause(iteNode.negate(), ~iteLit, thenLit, elseLit); |
476 |
85666 |
assertClause(iteNode.negate(), ~iteLit, ~condLit, thenLit); |
477 |
85666 |
assertClause(iteNode.negate(), ~iteLit, condLit, elseLit); |
478 |
|
|
479 |
|
// If ITE is false then one of the branches is false and the condition |
480 |
|
// implies which one |
481 |
|
// !lit -> !(ite b t e) |
482 |
|
// !lit -> (!t | !e) & (b -> !t) & (!b -> !e) |
483 |
|
// !lit -> (!t | !e) & (!b | !t) & (b | !e) |
484 |
|
// (lit | !t | !e) & (lit | !b | !t) & (lit | b | !e) |
485 |
85666 |
assertClause(iteNode, iteLit, ~thenLit, ~elseLit); |
486 |
85666 |
assertClause(iteNode, iteLit, ~condLit, ~thenLit); |
487 |
85666 |
assertClause(iteNode, iteLit, condLit, ~elseLit); |
488 |
|
|
489 |
85666 |
return iteLit; |
490 |
|
} |
491 |
|
|
492 |
9031326 |
SatLiteral CnfStream::toCNF(TNode node, bool negated) |
493 |
|
{ |
494 |
18062652 |
Trace("cnf") << "toCNF(" << node |
495 |
9031326 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
496 |
9031326 |
SatLiteral nodeLit; |
497 |
18062652 |
Node negatedNode = node.notNode(); |
498 |
|
|
499 |
|
// If the non-negated node has already been translated, get the translation |
500 |
9031326 |
if(hasLiteral(node)) { |
501 |
6231110 |
Trace("cnf") << "toCNF(): already translated\n"; |
502 |
6231110 |
nodeLit = getLiteral(node); |
503 |
|
// Return the (maybe negated) literal |
504 |
6231110 |
return !negated ? nodeLit : ~nodeLit; |
505 |
|
} |
506 |
|
// Handle each Boolean operator case |
507 |
2800216 |
switch (node.getKind()) |
508 |
|
{ |
509 |
190142 |
case kind::NOT: nodeLit = ~toCNF(node[0]); break; |
510 |
290540 |
case kind::XOR: nodeLit = handleXor(node); break; |
511 |
85666 |
case kind::ITE: nodeLit = handleIte(node); break; |
512 |
6822 |
case kind::IMPLIES: nodeLit = handleImplies(node); break; |
513 |
417034 |
case kind::OR: nodeLit = handleOr(node); break; |
514 |
746793 |
case kind::AND: nodeLit = handleAnd(node); break; |
515 |
593567 |
case kind::EQUAL: |
516 |
1187134 |
nodeLit = |
517 |
1780701 |
node[0].getType().isBoolean() ? handleIff(node) : convertAtom(node); |
518 |
593567 |
break; |
519 |
469652 |
default: |
520 |
|
{ |
521 |
469656 |
nodeLit = convertAtom(node); |
522 |
|
} |
523 |
469648 |
break; |
524 |
|
} |
525 |
|
// Return the (maybe negated) literal |
526 |
5600424 |
Trace("cnf") << "toCNF(): resulting literal: " |
527 |
2800212 |
<< (!negated ? nodeLit : ~nodeLit) << "\n"; |
528 |
2800212 |
return !negated ? nodeLit : ~nodeLit; |
529 |
|
} |
530 |
|
|
531 |
105788 |
void CnfStream::convertAndAssertAnd(TNode node, bool negated) |
532 |
|
{ |
533 |
105788 |
Assert(node.getKind() == kind::AND); |
534 |
211576 |
Trace("cnf") << "CnfStream::convertAndAssertAnd(" << node |
535 |
105788 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
536 |
105788 |
if (!negated) { |
537 |
|
// If the node is a conjunction, we handle each conjunct separately |
538 |
363814 |
for(TNode::const_iterator conjunct = node.begin(), node_end = node.end(); |
539 |
363814 |
conjunct != node_end; ++conjunct ) { |
540 |
350929 |
convertAndAssert(*conjunct, false); |
541 |
|
} |
542 |
|
} else { |
543 |
|
// If the node is a disjunction, we construct a clause and assert it |
544 |
92901 |
int nChildren = node.getNumChildren(); |
545 |
185802 |
SatClause clause(nChildren); |
546 |
92901 |
TNode::const_iterator disjunct = node.begin(); |
547 |
1350583 |
for(int i = 0; i < nChildren; ++ disjunct, ++ i) { |
548 |
1257682 |
Assert(disjunct != node.end()); |
549 |
1257682 |
clause[i] = toCNF(*disjunct, true); |
550 |
|
} |
551 |
92901 |
Assert(disjunct == node.end()); |
552 |
92901 |
assertClause(node.negate(), clause); |
553 |
|
} |
554 |
105787 |
} |
555 |
|
|
556 |
440627 |
void CnfStream::convertAndAssertOr(TNode node, bool negated) |
557 |
|
{ |
558 |
440627 |
Assert(node.getKind() == kind::OR); |
559 |
881254 |
Trace("cnf") << "CnfStream::convertAndAssertOr(" << node |
560 |
440627 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
561 |
440627 |
if (!negated) { |
562 |
|
// If the node is a disjunction, we construct a clause and assert it |
563 |
440326 |
int nChildren = node.getNumChildren(); |
564 |
880652 |
SatClause clause(nChildren); |
565 |
440326 |
TNode::const_iterator disjunct = node.begin(); |
566 |
1749820 |
for(int i = 0; i < nChildren; ++ disjunct, ++ i) { |
567 |
1309494 |
Assert(disjunct != node.end()); |
568 |
1309494 |
clause[i] = toCNF(*disjunct, false); |
569 |
|
} |
570 |
440326 |
Assert(disjunct == node.end()); |
571 |
440326 |
assertClause(node, clause); |
572 |
|
} else { |
573 |
|
// If the node is a conjunction, we handle each conjunct separately |
574 |
1933 |
for(TNode::const_iterator conjunct = node.begin(), node_end = node.end(); |
575 |
1933 |
conjunct != node_end; ++conjunct ) { |
576 |
1632 |
convertAndAssert(*conjunct, true); |
577 |
|
} |
578 |
|
} |
579 |
440627 |
} |
580 |
|
|
581 |
53 |
void CnfStream::convertAndAssertXor(TNode node, bool negated) |
582 |
|
{ |
583 |
53 |
Assert(node.getKind() == kind::XOR); |
584 |
106 |
Trace("cnf") << "CnfStream::convertAndAssertXor(" << node |
585 |
53 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
586 |
53 |
if (!negated) { |
587 |
|
// p XOR q |
588 |
53 |
SatLiteral p = toCNF(node[0], false); |
589 |
53 |
SatLiteral q = toCNF(node[1], false); |
590 |
|
// Construct the clauses (p => !q) and (!q => p) |
591 |
106 |
SatClause clause1(2); |
592 |
53 |
clause1[0] = ~p; |
593 |
53 |
clause1[1] = ~q; |
594 |
53 |
assertClause(node, clause1); |
595 |
106 |
SatClause clause2(2); |
596 |
53 |
clause2[0] = p; |
597 |
53 |
clause2[1] = q; |
598 |
53 |
assertClause(node, clause2); |
599 |
|
} else { |
600 |
|
// !(p XOR q) is the same as p <=> q |
601 |
|
SatLiteral p = toCNF(node[0], false); |
602 |
|
SatLiteral q = toCNF(node[1], false); |
603 |
|
// Construct the clauses (p => q) and (q => p) |
604 |
|
SatClause clause1(2); |
605 |
|
clause1[0] = ~p; |
606 |
|
clause1[1] = q; |
607 |
|
assertClause(node.negate(), clause1); |
608 |
|
SatClause clause2(2); |
609 |
|
clause2[0] = p; |
610 |
|
clause2[1] = ~q; |
611 |
|
assertClause(node.negate(), clause2); |
612 |
|
} |
613 |
53 |
} |
614 |
|
|
615 |
80533 |
void CnfStream::convertAndAssertIff(TNode node, bool negated) |
616 |
|
{ |
617 |
80533 |
Assert(node.getKind() == kind::EQUAL); |
618 |
161066 |
Trace("cnf") << "CnfStream::convertAndAssertIff(" << node |
619 |
80533 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
620 |
80533 |
if (!negated) { |
621 |
|
// p <=> q |
622 |
80002 |
SatLiteral p = toCNF(node[0], false); |
623 |
80002 |
SatLiteral q = toCNF(node[1], false); |
624 |
|
// Construct the clauses (p => q) and (q => p) |
625 |
160004 |
SatClause clause1(2); |
626 |
80002 |
clause1[0] = ~p; |
627 |
80002 |
clause1[1] = q; |
628 |
80002 |
assertClause(node, clause1); |
629 |
160004 |
SatClause clause2(2); |
630 |
80002 |
clause2[0] = p; |
631 |
80002 |
clause2[1] = ~q; |
632 |
80002 |
assertClause(node, clause2); |
633 |
|
} else { |
634 |
|
// !(p <=> q) is the same as p XOR q |
635 |
531 |
SatLiteral p = toCNF(node[0], false); |
636 |
531 |
SatLiteral q = toCNF(node[1], false); |
637 |
|
// Construct the clauses (p => !q) and (!q => p) |
638 |
1062 |
SatClause clause1(2); |
639 |
531 |
clause1[0] = ~p; |
640 |
531 |
clause1[1] = ~q; |
641 |
531 |
assertClause(node.negate(), clause1); |
642 |
1062 |
SatClause clause2(2); |
643 |
531 |
clause2[0] = p; |
644 |
531 |
clause2[1] = q; |
645 |
531 |
assertClause(node.negate(), clause2); |
646 |
|
} |
647 |
80533 |
} |
648 |
|
|
649 |
58725 |
void CnfStream::convertAndAssertImplies(TNode node, bool negated) |
650 |
|
{ |
651 |
58725 |
Assert(node.getKind() == kind::IMPLIES); |
652 |
117450 |
Trace("cnf") << "CnfStream::convertAndAssertImplies(" << node |
653 |
58725 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
654 |
58725 |
if (!negated) { |
655 |
|
// p => q |
656 |
58488 |
SatLiteral p = toCNF(node[0], false); |
657 |
58488 |
SatLiteral q = toCNF(node[1], false); |
658 |
|
// Construct the clause ~p || q |
659 |
116976 |
SatClause clause(2); |
660 |
58488 |
clause[0] = ~p; |
661 |
58488 |
clause[1] = q; |
662 |
58488 |
assertClause(node, clause); |
663 |
|
} else {// Construct the |
664 |
|
// !(p => q) is the same as (p && ~q) |
665 |
237 |
convertAndAssert(node[0], false); |
666 |
237 |
convertAndAssert(node[1], true); |
667 |
|
} |
668 |
58725 |
} |
669 |
|
|
670 |
17430 |
void CnfStream::convertAndAssertIte(TNode node, bool negated) |
671 |
|
{ |
672 |
17430 |
Assert(node.getKind() == kind::ITE); |
673 |
34860 |
Trace("cnf") << "CnfStream::convertAndAssertIte(" << node |
674 |
17430 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
675 |
|
// ITE(p, q, r) |
676 |
17430 |
SatLiteral p = toCNF(node[0], false); |
677 |
17430 |
SatLiteral q = toCNF(node[1], negated); |
678 |
17430 |
SatLiteral r = toCNF(node[2], negated); |
679 |
|
// Construct the clauses: |
680 |
|
// (p => q) and (!p => r) |
681 |
|
// |
682 |
|
// Note that below q and r can be used directly because whether they are |
683 |
|
// negated has been push to the literal definitions above |
684 |
34860 |
Node nnode = node; |
685 |
17430 |
if( negated ){ |
686 |
69 |
nnode = node.negate(); |
687 |
|
} |
688 |
34860 |
SatClause clause1(2); |
689 |
17430 |
clause1[0] = ~p; |
690 |
17430 |
clause1[1] = q; |
691 |
17430 |
assertClause(nnode, clause1); |
692 |
34860 |
SatClause clause2(2); |
693 |
17430 |
clause2[0] = p; |
694 |
17430 |
clause2[1] = r; |
695 |
17430 |
assertClause(nnode, clause2); |
696 |
17430 |
} |
697 |
|
|
698 |
|
// At the top level we must ensure that all clauses that are asserted are |
699 |
|
// not unit, except for the direct assertions. This allows us to remove the |
700 |
|
// clauses later when they are not needed anymore (lemmas for example). |
701 |
502584 |
void CnfStream::convertAndAssert(TNode node, |
702 |
|
bool removable, |
703 |
|
bool negated, |
704 |
|
bool input) |
705 |
|
{ |
706 |
1005168 |
Trace("cnf") << "convertAndAssert(" << node |
707 |
1005168 |
<< ", negated = " << (negated ? "true" : "false") |
708 |
502584 |
<< ", removable = " << (removable ? "true" : "false") << ")\n"; |
709 |
502584 |
d_removable = removable; |
710 |
502588 |
convertAndAssert(node, negated); |
711 |
502580 |
} |
712 |
|
|
713 |
906187 |
void CnfStream::convertAndAssert(TNode node, bool negated) |
714 |
|
{ |
715 |
1812374 |
Trace("cnf") << "convertAndAssert(" << node |
716 |
906187 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
717 |
|
|
718 |
906187 |
d_resourceManager->spendResource(Resource::CnfStep); |
719 |
|
|
720 |
906187 |
switch(node.getKind()) { |
721 |
105789 |
case kind::AND: convertAndAssertAnd(node, negated); break; |
722 |
440627 |
case kind::OR: convertAndAssertOr(node, negated); break; |
723 |
53 |
case kind::XOR: convertAndAssertXor(node, negated); break; |
724 |
58725 |
case kind::IMPLIES: convertAndAssertImplies(node, negated); break; |
725 |
17430 |
case kind::ITE: convertAndAssertIte(node, negated); break; |
726 |
50571 |
case kind::NOT: convertAndAssert(node[0], !negated); break; |
727 |
146404 |
case kind::EQUAL: |
728 |
146404 |
if (node[0].getType().isBoolean()) |
729 |
|
{ |
730 |
80533 |
convertAndAssertIff(node, negated); |
731 |
80533 |
break; |
732 |
|
} |
733 |
|
CVC5_FALLTHROUGH; |
734 |
|
default: |
735 |
|
{ |
736 |
304924 |
Node nnode = node; |
737 |
152462 |
if (negated) |
738 |
|
{ |
739 |
46663 |
nnode = node.negate(); |
740 |
|
} |
741 |
|
// Atoms |
742 |
304924 |
assertClause(nnode, toCNF(node, negated)); |
743 |
|
} |
744 |
152458 |
break; |
745 |
|
} |
746 |
906180 |
} |
747 |
|
|
748 |
|
} // namespace prop |
749 |
27735 |
} // namespace cvc5 |