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 "printer/printer.h" |
25 |
|
#include "proof/clause_id.h" |
26 |
|
#include "prop/minisat/minisat.h" |
27 |
|
#include "prop/prop_engine.h" |
28 |
|
#include "prop/theory_proxy.h" |
29 |
|
#include "smt/dump.h" |
30 |
|
#include "smt/env.h" |
31 |
|
#include "smt/smt_engine_scope.h" |
32 |
|
#include "smt/smt_statistics_registry.h" |
33 |
|
#include "theory/theory.h" |
34 |
|
#include "theory/theory_engine.h" |
35 |
|
|
36 |
|
namespace cvc5 { |
37 |
|
namespace prop { |
38 |
|
|
39 |
12480 |
CnfStream::CnfStream(SatSolver* satSolver, |
40 |
|
Registrar* registrar, |
41 |
|
context::Context* context, |
42 |
|
Env* env, |
43 |
|
ResourceManager* rm, |
44 |
|
FormulaLitPolicy flpol, |
45 |
12480 |
std::string name) |
46 |
|
: d_satSolver(satSolver), |
47 |
|
d_env(env), |
48 |
|
d_booleanVariables(context), |
49 |
|
d_notifyFormulas(context), |
50 |
|
d_nodeToLiteralMap(context), |
51 |
|
d_literalToNodeMap(context), |
52 |
|
d_flitPolicy(flpol), |
53 |
|
d_registrar(registrar), |
54 |
|
d_name(name), |
55 |
|
d_removable(false), |
56 |
|
d_resourceManager(rm), |
57 |
12480 |
d_stats(name) |
58 |
|
{ |
59 |
12480 |
} |
60 |
|
|
61 |
6116903 |
bool CnfStream::assertClause(TNode node, SatClause& c) |
62 |
|
{ |
63 |
6116903 |
Trace("cnf") << "Inserting into stream " << c << " node = " << node << "\n"; |
64 |
6116903 |
if (Dump.isOn("clauses") && d_env != nullptr) |
65 |
|
{ |
66 |
|
const Printer& printer = d_env->getPrinter(); |
67 |
|
std::ostream& out = d_env->getDumpOut(); |
68 |
|
if (c.size() == 1) |
69 |
|
{ |
70 |
|
printer.toStreamCmdAssert(out, getNode(c[0])); |
71 |
|
} |
72 |
|
else |
73 |
|
{ |
74 |
|
Assert(c.size() > 1); |
75 |
|
NodeBuilder b(kind::OR); |
76 |
|
for (unsigned i = 0; i < c.size(); ++i) |
77 |
|
{ |
78 |
|
b << getNode(c[i]); |
79 |
|
} |
80 |
|
Node n = b; |
81 |
|
printer.toStreamCmdAssert(out, n); |
82 |
|
} |
83 |
|
} |
84 |
|
|
85 |
6116903 |
ClauseId clauseId = d_satSolver->addClause(c, d_removable); |
86 |
|
|
87 |
6116903 |
return clauseId != ClauseIdUndef; |
88 |
|
} |
89 |
|
|
90 |
144565 |
bool CnfStream::assertClause(TNode node, SatLiteral a) |
91 |
|
{ |
92 |
289130 |
SatClause clause(1); |
93 |
144565 |
clause[0] = a; |
94 |
289130 |
return assertClause(node, clause); |
95 |
|
} |
96 |
|
|
97 |
2714472 |
bool CnfStream::assertClause(TNode node, SatLiteral a, SatLiteral b) |
98 |
|
{ |
99 |
5428944 |
SatClause clause(2); |
100 |
2714472 |
clause[0] = a; |
101 |
2714472 |
clause[1] = b; |
102 |
5428944 |
return assertClause(node, clause); |
103 |
|
} |
104 |
|
|
105 |
1979889 |
bool CnfStream::assertClause(TNode node, |
106 |
|
SatLiteral a, |
107 |
|
SatLiteral b, |
108 |
|
SatLiteral c) |
109 |
|
{ |
110 |
3959778 |
SatClause clause(3); |
111 |
1979889 |
clause[0] = a; |
112 |
1979889 |
clause[1] = b; |
113 |
1979889 |
clause[2] = c; |
114 |
3959778 |
return assertClause(node, clause); |
115 |
|
} |
116 |
|
|
117 |
43524125 |
bool CnfStream::hasLiteral(TNode n) const { |
118 |
43524125 |
NodeToLiteralMap::const_iterator find = d_nodeToLiteralMap.find(n); |
119 |
43524125 |
return find != d_nodeToLiteralMap.end(); |
120 |
|
} |
121 |
|
|
122 |
2587149 |
void CnfStream::ensureMappingForLiteral(TNode n) |
123 |
|
{ |
124 |
2587149 |
SatLiteral lit = getLiteral(n); |
125 |
2587149 |
if (!d_literalToNodeMap.contains(lit)) |
126 |
|
{ |
127 |
|
// Store backward-mappings |
128 |
223 |
d_literalToNodeMap.insert(lit, n); |
129 |
223 |
d_literalToNodeMap.insert(~lit, n.notNode()); |
130 |
|
} |
131 |
2587149 |
} |
132 |
|
|
133 |
2617635 |
void CnfStream::ensureLiteral(TNode n) |
134 |
|
{ |
135 |
2617635 |
AlwaysAssertArgument( |
136 |
|
hasLiteral(n) || n.getType().isBoolean(), |
137 |
|
n, |
138 |
|
"ProofCnfStream::ensureLiteral() requires a node of Boolean type.\n" |
139 |
|
"got node: %s\n" |
140 |
|
"its type: %s\n", |
141 |
|
n.toString().c_str(), |
142 |
|
n.getType().toString().c_str()); |
143 |
2617635 |
Trace("cnf") << "ensureLiteral(" << n << ")\n"; |
144 |
2650635 |
TimerStat::CodeTimer codeTimer(d_stats.d_cnfConversionTime, true); |
145 |
2617635 |
if (hasLiteral(n)) |
146 |
|
{ |
147 |
2584635 |
ensureMappingForLiteral(n); |
148 |
2584635 |
return; |
149 |
|
} |
150 |
|
// remove top level negation |
151 |
33000 |
n = n.getKind() == kind::NOT ? n[0] : n; |
152 |
33000 |
if (theory::Theory::theoryOf(n) == theory::THEORY_BOOL && !n.isVar()) |
153 |
|
{ |
154 |
|
// If we were called with something other than a theory atom (or |
155 |
|
// Boolean variable), we get a SatLiteral that is definitionally |
156 |
|
// equal to it. |
157 |
|
// These are not removable and have no proof ID |
158 |
25814 |
d_removable = false; |
159 |
|
|
160 |
25814 |
SatLiteral lit = toCNF(n, false); |
161 |
|
|
162 |
|
// Store backward-mappings |
163 |
|
// These may already exist |
164 |
25814 |
d_literalToNodeMap.insert_safe(lit, n); |
165 |
25814 |
d_literalToNodeMap.insert_safe(~lit, n.notNode()); |
166 |
|
} |
167 |
|
else |
168 |
|
{ |
169 |
|
// We have a theory atom or variable. |
170 |
7186 |
convertAtom(n); |
171 |
|
} |
172 |
|
} |
173 |
|
|
174 |
1692113 |
SatLiteral CnfStream::newLiteral(TNode node, bool isTheoryAtom, bool preRegister, bool canEliminate) { |
175 |
3384226 |
Trace("cnf") << d_name << "::newLiteral(" << node << ", " << isTheoryAtom |
176 |
1692113 |
<< ")\n" |
177 |
1692113 |
<< push; |
178 |
1692113 |
Assert(node.getKind() != kind::NOT); |
179 |
|
|
180 |
|
// if we are tracking formulas, everything is a theory atom |
181 |
1692113 |
if (!isTheoryAtom && d_flitPolicy == FormulaLitPolicy::TRACK_AND_NOTIFY) |
182 |
|
{ |
183 |
|
isTheoryAtom = true; |
184 |
|
d_notifyFormulas.insert(node); |
185 |
|
} |
186 |
|
|
187 |
|
// Get the literal for this node |
188 |
1692113 |
SatLiteral lit; |
189 |
1692113 |
if (!hasLiteral(node)) { |
190 |
1692113 |
Trace("cnf") << d_name << "::newLiteral: node already registered\n"; |
191 |
|
// If no literal, we'll make one |
192 |
1692113 |
if (node.getKind() == kind::CONST_BOOLEAN) { |
193 |
12868 |
Trace("cnf") << d_name << "::newLiteral: boolean const\n"; |
194 |
12868 |
if (node.getConst<bool>()) { |
195 |
6380 |
lit = SatLiteral(d_satSolver->trueVar()); |
196 |
|
} else { |
197 |
6488 |
lit = SatLiteral(d_satSolver->falseVar()); |
198 |
|
} |
199 |
|
} else { |
200 |
1679245 |
Trace("cnf") << d_name << "::newLiteral: new var\n"; |
201 |
1679245 |
lit = SatLiteral(d_satSolver->newVar(isTheoryAtom, preRegister, canEliminate)); |
202 |
|
} |
203 |
1692113 |
d_nodeToLiteralMap.insert(node, lit); |
204 |
1692113 |
d_nodeToLiteralMap.insert(node.notNode(), ~lit); |
205 |
|
} else { |
206 |
|
lit = getLiteral(node); |
207 |
|
} |
208 |
|
|
209 |
|
// If it's a theory literal, need to store it for back queries |
210 |
2945428 |
if (isTheoryAtom || d_flitPolicy == FormulaLitPolicy::TRACK |
211 |
2764603 |
|| (Dump.isOn("clauses"))) |
212 |
|
{ |
213 |
619623 |
d_literalToNodeMap.insert_safe(lit, node); |
214 |
619623 |
d_literalToNodeMap.insert_safe(~lit, node.notNode()); |
215 |
|
} |
216 |
|
|
217 |
|
// If a theory literal, we pre-register it |
218 |
1692113 |
if (preRegister) { |
219 |
|
// In case we are re-entered due to lemmas, save our state |
220 |
438798 |
bool backupRemovable = d_removable; |
221 |
438802 |
d_registrar->preRegister(node); |
222 |
438794 |
d_removable = backupRemovable; |
223 |
|
} |
224 |
|
// Here, you can have it |
225 |
1692109 |
Trace("cnf") << "newLiteral(" << node << ") => " << lit << "\n" << pop; |
226 |
1692109 |
return lit; |
227 |
|
} |
228 |
|
|
229 |
9204126 |
TNode CnfStream::getNode(const SatLiteral& literal) { |
230 |
9204126 |
Trace("cnf") << "getNode(" << literal << ")\n"; |
231 |
18408252 |
Trace("cnf") << "getNode(" << literal << ") => " |
232 |
9204126 |
<< d_literalToNodeMap[literal] << "\n"; |
233 |
9204126 |
return d_literalToNodeMap[literal]; |
234 |
|
} |
235 |
|
|
236 |
14297 |
const CnfStream::NodeToLiteralMap& CnfStream::getTranslationCache() const |
237 |
|
{ |
238 |
14297 |
return d_nodeToLiteralMap; |
239 |
|
} |
240 |
|
|
241 |
22256 |
const CnfStream::LiteralToNodeMap& CnfStream::getNodeCache() const |
242 |
|
{ |
243 |
22256 |
return d_literalToNodeMap; |
244 |
|
} |
245 |
|
|
246 |
12873 |
void CnfStream::getBooleanVariables(std::vector<TNode>& outputVariables) const { |
247 |
12873 |
context::CDList<TNode>::const_iterator it, it_end; |
248 |
70437 |
for (it = d_booleanVariables.begin(); it != d_booleanVariables.end(); ++ it) { |
249 |
57564 |
outputVariables.push_back(*it); |
250 |
|
} |
251 |
12873 |
} |
252 |
|
|
253 |
|
bool CnfStream::isNotifyFormula(TNode node) const |
254 |
|
{ |
255 |
|
return d_notifyFormulas.find(node) != d_notifyFormulas.end(); |
256 |
|
} |
257 |
|
|
258 |
503557 |
SatLiteral CnfStream::convertAtom(TNode node) |
259 |
|
{ |
260 |
503557 |
Trace("cnf") << "convertAtom(" << node << ")\n"; |
261 |
|
|
262 |
503557 |
Assert(!hasLiteral(node)) << "atom already mapped!"; |
263 |
|
|
264 |
503557 |
bool theoryLiteral = false; |
265 |
503557 |
bool canEliminate = true; |
266 |
503557 |
bool preRegister = false; |
267 |
|
|
268 |
|
// Is this a variable add it to the list |
269 |
503557 |
if (node.isVar() && node.getKind() != kind::BOOLEAN_TERM_VARIABLE) |
270 |
|
{ |
271 |
64759 |
d_booleanVariables.push_back(node); |
272 |
|
} |
273 |
|
else |
274 |
|
{ |
275 |
438798 |
theoryLiteral = true; |
276 |
438798 |
canEliminate = false; |
277 |
438798 |
preRegister = true; |
278 |
|
} |
279 |
|
|
280 |
|
// Make a new literal (variables are not considered theory literals) |
281 |
503561 |
SatLiteral lit = newLiteral(node, theoryLiteral, preRegister, canEliminate); |
282 |
|
// Return the resulting literal |
283 |
503553 |
return lit; |
284 |
|
} |
285 |
|
|
286 |
32527910 |
SatLiteral CnfStream::getLiteral(TNode node) { |
287 |
32527910 |
Assert(!node.isNull()) << "CnfStream: can't getLiteral() of null node"; |
288 |
|
|
289 |
65055820 |
Assert(d_nodeToLiteralMap.contains(node)) |
290 |
32527910 |
<< "Literal not in the CNF Cache: " << node << "\n"; |
291 |
|
|
292 |
32527910 |
SatLiteral literal = d_nodeToLiteralMap[node]; |
293 |
65055820 |
Trace("cnf") << "CnfStream::getLiteral(" << node << ") => " << literal |
294 |
32527910 |
<< "\n"; |
295 |
32527910 |
return literal; |
296 |
|
} |
297 |
|
|
298 |
191125 |
void CnfStream::handleXor(TNode xorNode) |
299 |
|
{ |
300 |
191125 |
Assert(!hasLiteral(xorNode)) << "Atom already mapped!"; |
301 |
191125 |
Assert(xorNode.getKind() == kind::XOR) << "Expecting an XOR expression!"; |
302 |
191125 |
Assert(xorNode.getNumChildren() == 2) << "Expecting exactly 2 children!"; |
303 |
191125 |
Assert(!d_removable) << "Removable clauses can not contain Boolean structure"; |
304 |
191125 |
Trace("cnf") << "CnfStream::handleXor(" << xorNode << ")\n"; |
305 |
|
|
306 |
191125 |
SatLiteral a = getLiteral(xorNode[0]); |
307 |
191125 |
SatLiteral b = getLiteral(xorNode[1]); |
308 |
|
|
309 |
191125 |
SatLiteral xorLit = newLiteral(xorNode); |
310 |
|
|
311 |
191125 |
assertClause(xorNode.negate(), a, b, ~xorLit); |
312 |
191125 |
assertClause(xorNode.negate(), ~a, ~b, ~xorLit); |
313 |
191125 |
assertClause(xorNode, a, ~b, xorLit); |
314 |
191125 |
assertClause(xorNode, ~a, b, xorLit); |
315 |
191125 |
} |
316 |
|
|
317 |
248163 |
void CnfStream::handleOr(TNode orNode) |
318 |
|
{ |
319 |
248163 |
Assert(!hasLiteral(orNode)) << "Atom already mapped!"; |
320 |
248163 |
Assert(orNode.getKind() == kind::OR) << "Expecting an OR expression!"; |
321 |
248163 |
Assert(orNode.getNumChildren() > 1) << "Expecting more then 1 child!"; |
322 |
248163 |
Assert(!d_removable) << "Removable clauses can not contain Boolean structure"; |
323 |
248163 |
Trace("cnf") << "CnfStream::handleOr(" << orNode << ")\n"; |
324 |
|
|
325 |
|
// Number of children |
326 |
248163 |
size_t numChildren = orNode.getNumChildren(); |
327 |
|
|
328 |
|
// Get the literal for this node |
329 |
248163 |
SatLiteral orLit = newLiteral(orNode); |
330 |
|
|
331 |
|
// Transform all the children first |
332 |
496326 |
SatClause clause(numChildren + 1); |
333 |
818628 |
for (size_t i = 0; i < numChildren; ++i) |
334 |
|
{ |
335 |
570465 |
clause[i] = getLiteral(orNode[i]); |
336 |
|
|
337 |
|
// lit <- (a_1 | a_2 | a_3 | ... | a_n) |
338 |
|
// lit | ~(a_1 | a_2 | a_3 | ... | a_n) |
339 |
|
// (lit | ~a_1) & (lit | ~a_2) & (lit & ~a_3) & ... & (lit & ~a_n) |
340 |
570465 |
assertClause(orNode, orLit, ~clause[i]); |
341 |
|
} |
342 |
|
|
343 |
|
// lit -> (a_1 | a_2 | a_3 | ... | a_n) |
344 |
|
// ~lit | a_1 | a_2 | a_3 | ... | a_n |
345 |
248163 |
clause[numChildren] = ~orLit; |
346 |
|
// This needs to go last, as the clause might get modified by the SAT solver |
347 |
248163 |
assertClause(orNode.negate(), clause); |
348 |
248163 |
} |
349 |
|
|
350 |
472339 |
void CnfStream::handleAnd(TNode andNode) |
351 |
|
{ |
352 |
472339 |
Assert(!hasLiteral(andNode)) << "Atom already mapped!"; |
353 |
472339 |
Assert(andNode.getKind() == kind::AND) << "Expecting an AND expression!"; |
354 |
472339 |
Assert(andNode.getNumChildren() > 1) << "Expecting more than 1 child!"; |
355 |
472339 |
Assert(!d_removable) << "Removable clauses can not contain Boolean structure"; |
356 |
472339 |
Trace("cnf") << "handleAnd(" << andNode << ")\n"; |
357 |
|
|
358 |
|
// Number of children |
359 |
472339 |
size_t numChildren = andNode.getNumChildren(); |
360 |
|
|
361 |
|
// Get the literal for this node |
362 |
472339 |
SatLiteral andLit = newLiteral(andNode); |
363 |
|
|
364 |
|
// Transform all the children first (remembering the negation) |
365 |
944678 |
SatClause clause(numChildren + 1); |
366 |
2594868 |
for (size_t i = 0; i < numChildren; ++i) |
367 |
|
{ |
368 |
2122529 |
clause[i] = ~getLiteral(andNode[i]); |
369 |
|
|
370 |
|
// lit -> (a_1 & a_2 & a_3 & ... & a_n) |
371 |
|
// ~lit | (a_1 & a_2 & a_3 & ... & a_n) |
372 |
|
// (~lit | a_1) & (~lit | a_2) & ... & (~lit | a_n) |
373 |
2122529 |
assertClause(andNode.negate(), ~andLit, ~clause[i]); |
374 |
|
} |
375 |
|
|
376 |
|
// lit <- (a_1 & a_2 & a_3 & ... a_n) |
377 |
|
// lit | ~(a_1 & a_2 & a_3 & ... & a_n) |
378 |
|
// lit | ~a_1 | ~a_2 | ~a_3 | ... | ~a_n |
379 |
472339 |
clause[numChildren] = andLit; |
380 |
|
// This needs to go last, as the clause might get modified by the SAT solver |
381 |
472339 |
assertClause(andNode, clause); |
382 |
472339 |
} |
383 |
|
|
384 |
5033 |
void CnfStream::handleImplies(TNode impliesNode) |
385 |
|
{ |
386 |
5033 |
Assert(!hasLiteral(impliesNode)) << "Atom already mapped!"; |
387 |
5033 |
Assert(impliesNode.getKind() == kind::IMPLIES) |
388 |
|
<< "Expecting an IMPLIES expression!"; |
389 |
5033 |
Assert(impliesNode.getNumChildren() == 2) << "Expecting exactly 2 children!"; |
390 |
5033 |
Assert(!d_removable) << "Removable clauses can not contain Boolean structure"; |
391 |
5033 |
Trace("cnf") << "handleImplies(" << impliesNode << ")\n"; |
392 |
|
|
393 |
|
// Convert the children to cnf |
394 |
5033 |
SatLiteral a = getLiteral(impliesNode[0]); |
395 |
5033 |
SatLiteral b = getLiteral(impliesNode[1]); |
396 |
|
|
397 |
5033 |
SatLiteral impliesLit = newLiteral(impliesNode); |
398 |
|
|
399 |
|
// lit -> (a->b) |
400 |
|
// ~lit | ~ a | b |
401 |
5033 |
assertClause(impliesNode.negate(), ~impliesLit, ~a, b); |
402 |
|
|
403 |
|
// (a->b) -> lit |
404 |
|
// ~(~a | b) | lit |
405 |
|
// (a | l) & (~b | l) |
406 |
5033 |
assertClause(impliesNode, a, impliesLit); |
407 |
5033 |
assertClause(impliesNode, ~b, impliesLit); |
408 |
5033 |
} |
409 |
|
|
410 |
205925 |
void CnfStream::handleIff(TNode iffNode) |
411 |
|
{ |
412 |
205925 |
Assert(!hasLiteral(iffNode)) << "Atom already mapped!"; |
413 |
205925 |
Assert(iffNode.getKind() == kind::EQUAL) << "Expecting an EQUAL expression!"; |
414 |
205925 |
Assert(iffNode.getNumChildren() == 2) << "Expecting exactly 2 children!"; |
415 |
205925 |
Assert(!d_removable) << "Removable clauses can not contain Boolean structure"; |
416 |
205925 |
Trace("cnf") << "handleIff(" << iffNode << ")\n"; |
417 |
|
|
418 |
|
// Convert the children to CNF |
419 |
205925 |
SatLiteral a = getLiteral(iffNode[0]); |
420 |
205925 |
SatLiteral b = getLiteral(iffNode[1]); |
421 |
|
|
422 |
|
// Get the now literal |
423 |
205925 |
SatLiteral iffLit = newLiteral(iffNode); |
424 |
|
|
425 |
|
// lit -> ((a-> b) & (b->a)) |
426 |
|
// ~lit | ((~a | b) & (~b | a)) |
427 |
|
// (~a | b | ~lit) & (~b | a | ~lit) |
428 |
205925 |
assertClause(iffNode.negate(), ~a, b, ~iffLit); |
429 |
205925 |
assertClause(iffNode.negate(), a, ~b, ~iffLit); |
430 |
|
|
431 |
|
// (a<->b) -> lit |
432 |
|
// ~((a & b) | (~a & ~b)) | lit |
433 |
|
// (~(a & b)) & (~(~a & ~b)) | lit |
434 |
|
// ((~a | ~b) & (a | b)) | lit |
435 |
|
// (~a | ~b | lit) & (a | b | lit) |
436 |
205925 |
assertClause(iffNode, ~a, ~b, iffLit); |
437 |
205925 |
assertClause(iffNode, a, b, iffLit); |
438 |
205925 |
} |
439 |
|
|
440 |
64440 |
void CnfStream::handleIte(TNode iteNode) |
441 |
|
{ |
442 |
64440 |
Assert(!hasLiteral(iteNode)) << "Atom already mapped!"; |
443 |
64440 |
Assert(iteNode.getKind() == kind::ITE); |
444 |
64440 |
Assert(iteNode.getNumChildren() == 3); |
445 |
64440 |
Assert(!d_removable) << "Removable clauses can not contain Boolean structure"; |
446 |
128880 |
Trace("cnf") << "handleIte(" << iteNode[0] << " " << iteNode[1] << " " |
447 |
64440 |
<< iteNode[2] << ")\n"; |
448 |
|
|
449 |
64440 |
SatLiteral condLit = getLiteral(iteNode[0]); |
450 |
64440 |
SatLiteral thenLit = getLiteral(iteNode[1]); |
451 |
64440 |
SatLiteral elseLit = getLiteral(iteNode[2]); |
452 |
|
|
453 |
64440 |
SatLiteral iteLit = newLiteral(iteNode); |
454 |
|
|
455 |
|
// If ITE is true then one of the branches is true and the condition |
456 |
|
// implies which one |
457 |
|
// lit -> (ite b t e) |
458 |
|
// lit -> (t | e) & (b -> t) & (!b -> e) |
459 |
|
// lit -> (t | e) & (!b | t) & (b | e) |
460 |
|
// (!lit | t | e) & (!lit | !b | t) & (!lit | b | e) |
461 |
64440 |
assertClause(iteNode.negate(), ~iteLit, thenLit, elseLit); |
462 |
64440 |
assertClause(iteNode.negate(), ~iteLit, ~condLit, thenLit); |
463 |
64440 |
assertClause(iteNode.negate(), ~iteLit, condLit, elseLit); |
464 |
|
|
465 |
|
// If ITE is false then one of the branches is false and the condition |
466 |
|
// implies which one |
467 |
|
// !lit -> !(ite b t e) |
468 |
|
// !lit -> (!t | !e) & (b -> !t) & (!b -> !e) |
469 |
|
// !lit -> (!t | !e) & (!b | !t) & (b | !e) |
470 |
|
// (lit | !t | !e) & (lit | !b | !t) & (lit | b | !e) |
471 |
64440 |
assertClause(iteNode, iteLit, ~thenLit, ~elseLit); |
472 |
64440 |
assertClause(iteNode, iteLit, ~condLit, ~thenLit); |
473 |
64440 |
assertClause(iteNode, iteLit, condLit, ~elseLit); |
474 |
64440 |
} |
475 |
|
|
476 |
2555449 |
SatLiteral CnfStream::toCNF(TNode node, bool negated) |
477 |
|
{ |
478 |
5110898 |
Trace("cnf") << "toCNF(" << node |
479 |
2555449 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
480 |
|
|
481 |
5110898 |
TNode cur; |
482 |
2555449 |
SatLiteral nodeLit; |
483 |
5110898 |
std::vector<TNode> visit; |
484 |
5110898 |
std::unordered_map<TNode, bool> cache; |
485 |
|
|
486 |
2555449 |
visit.push_back(node); |
487 |
18955547 |
while (!visit.empty()) |
488 |
|
{ |
489 |
8200053 |
cur = visit.back(); |
490 |
8200053 |
Assert(cur.getType().isBoolean()); |
491 |
|
|
492 |
12903427 |
if (hasLiteral(cur)) |
493 |
|
{ |
494 |
4703374 |
visit.pop_back(); |
495 |
4703374 |
continue; |
496 |
|
} |
497 |
|
|
498 |
3496679 |
const auto& it = cache.find(cur); |
499 |
3496679 |
if (it == cache.end()) |
500 |
|
{ |
501 |
1816933 |
cache.emplace(cur, false); |
502 |
1816933 |
Kind k = cur.getKind(); |
503 |
|
// Only traverse Boolean nodes |
504 |
3496683 |
if (k == kind::NOT || k == kind::XOR || k == kind::ITE |
505 |
1424185 |
|| k == kind::IMPLIES || k == kind::OR || k == kind::AND |
506 |
2515579 |
|| (k == kind::EQUAL && cur[0].getType().isBoolean())) |
507 |
|
{ |
508 |
|
// Preserve the order of the recursive version |
509 |
5151883 |
for (size_t i = 0, size = cur.getNumChildren(); i < size; ++i) |
510 |
|
{ |
511 |
3827671 |
visit.push_back(cur[size - 1 - i]); |
512 |
|
} |
513 |
|
} |
514 |
1816933 |
continue; |
515 |
|
} |
516 |
1679746 |
else if (!it->second) |
517 |
|
{ |
518 |
1679746 |
it->second = true; |
519 |
1679746 |
Kind k = cur.getKind(); |
520 |
1679746 |
switch (k) |
521 |
|
{ |
522 |
|
case kind::NOT: Assert(hasLiteral(cur[0])); break; |
523 |
191125 |
case kind::XOR: handleXor(cur); break; |
524 |
64440 |
case kind::ITE: handleIte(cur); break; |
525 |
5033 |
case kind::IMPLIES: handleImplies(cur); break; |
526 |
248163 |
case kind::OR: handleOr(cur); break; |
527 |
472339 |
case kind::AND: handleAnd(cur); break; |
528 |
698646 |
default: |
529 |
698646 |
if (k == kind::EQUAL && cur[0].getType().isBoolean()) |
530 |
|
{ |
531 |
205925 |
handleIff(cur); |
532 |
|
} |
533 |
|
else |
534 |
|
{ |
535 |
492725 |
convertAtom(cur); |
536 |
|
} |
537 |
698642 |
break; |
538 |
|
} |
539 |
|
} |
540 |
1679742 |
visit.pop_back(); |
541 |
|
} |
542 |
|
|
543 |
2555445 |
nodeLit = getLiteral(node); |
544 |
5110890 |
Trace("cnf") << "toCNF(): resulting literal: " |
545 |
2555445 |
<< (!negated ? nodeLit : ~nodeLit) << "\n"; |
546 |
5110890 |
return negated ? ~nodeLit : nodeLit; |
547 |
|
} |
548 |
|
|
549 |
98208 |
void CnfStream::convertAndAssertAnd(TNode node, bool negated) |
550 |
|
{ |
551 |
98208 |
Assert(node.getKind() == kind::AND); |
552 |
196416 |
Trace("cnf") << "CnfStream::convertAndAssertAnd(" << node |
553 |
98208 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
554 |
98208 |
if (!negated) { |
555 |
|
// If the node is a conjunction, we handle each conjunct separately |
556 |
364111 |
for(TNode::const_iterator conjunct = node.begin(), node_end = node.end(); |
557 |
364111 |
conjunct != node_end; ++conjunct ) { |
558 |
350964 |
convertAndAssert(*conjunct, false); |
559 |
|
} |
560 |
|
} else { |
561 |
|
// If the node is a disjunction, we construct a clause and assert it |
562 |
85059 |
int nChildren = node.getNumChildren(); |
563 |
170118 |
SatClause clause(nChildren); |
564 |
85059 |
TNode::const_iterator disjunct = node.begin(); |
565 |
1209643 |
for(int i = 0; i < nChildren; ++ disjunct, ++ i) { |
566 |
1124584 |
Assert(disjunct != node.end()); |
567 |
1124584 |
clause[i] = toCNF(*disjunct, true); |
568 |
|
} |
569 |
85059 |
Assert(disjunct == node.end()); |
570 |
85059 |
assertClause(node.negate(), clause); |
571 |
|
} |
572 |
98207 |
} |
573 |
|
|
574 |
385952 |
void CnfStream::convertAndAssertOr(TNode node, bool negated) |
575 |
|
{ |
576 |
385952 |
Assert(node.getKind() == kind::OR); |
577 |
771904 |
Trace("cnf") << "CnfStream::convertAndAssertOr(" << node |
578 |
385952 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
579 |
385952 |
if (!negated) { |
580 |
|
// If the node is a disjunction, we construct a clause and assert it |
581 |
385771 |
int nChildren = node.getNumChildren(); |
582 |
771542 |
SatClause clause(nChildren); |
583 |
385771 |
TNode::const_iterator disjunct = node.begin(); |
584 |
1509426 |
for(int i = 0; i < nChildren; ++ disjunct, ++ i) { |
585 |
1123655 |
Assert(disjunct != node.end()); |
586 |
1123655 |
clause[i] = toCNF(*disjunct, false); |
587 |
|
} |
588 |
385771 |
Assert(disjunct == node.end()); |
589 |
385771 |
assertClause(node, clause); |
590 |
|
} else { |
591 |
|
// If the node is a conjunction, we handle each conjunct separately |
592 |
1183 |
for(TNode::const_iterator conjunct = node.begin(), node_end = node.end(); |
593 |
1183 |
conjunct != node_end; ++conjunct ) { |
594 |
1002 |
convertAndAssert(*conjunct, true); |
595 |
|
} |
596 |
|
} |
597 |
385952 |
} |
598 |
|
|
599 |
49 |
void CnfStream::convertAndAssertXor(TNode node, bool negated) |
600 |
|
{ |
601 |
49 |
Assert(node.getKind() == kind::XOR); |
602 |
98 |
Trace("cnf") << "CnfStream::convertAndAssertXor(" << node |
603 |
49 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
604 |
49 |
if (!negated) { |
605 |
|
// p XOR q |
606 |
49 |
SatLiteral p = toCNF(node[0], false); |
607 |
49 |
SatLiteral q = toCNF(node[1], false); |
608 |
|
// Construct the clauses (p => !q) and (!q => p) |
609 |
98 |
SatClause clause1(2); |
610 |
49 |
clause1[0] = ~p; |
611 |
49 |
clause1[1] = ~q; |
612 |
49 |
assertClause(node, clause1); |
613 |
98 |
SatClause clause2(2); |
614 |
49 |
clause2[0] = p; |
615 |
49 |
clause2[1] = q; |
616 |
49 |
assertClause(node, clause2); |
617 |
|
} else { |
618 |
|
// !(p XOR q) is the same as p <=> q |
619 |
|
SatLiteral p = toCNF(node[0], false); |
620 |
|
SatLiteral q = toCNF(node[1], false); |
621 |
|
// Construct the clauses (p => q) and (q => p) |
622 |
|
SatClause clause1(2); |
623 |
|
clause1[0] = ~p; |
624 |
|
clause1[1] = q; |
625 |
|
assertClause(node.negate(), clause1); |
626 |
|
SatClause clause2(2); |
627 |
|
clause2[0] = p; |
628 |
|
clause2[1] = ~q; |
629 |
|
assertClause(node.negate(), clause2); |
630 |
|
} |
631 |
49 |
} |
632 |
|
|
633 |
5280 |
void CnfStream::convertAndAssertIff(TNode node, bool negated) |
634 |
|
{ |
635 |
5280 |
Assert(node.getKind() == kind::EQUAL); |
636 |
10560 |
Trace("cnf") << "CnfStream::convertAndAssertIff(" << node |
637 |
5280 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
638 |
5280 |
if (!negated) { |
639 |
|
// p <=> q |
640 |
4758 |
SatLiteral p = toCNF(node[0], false); |
641 |
4758 |
SatLiteral q = toCNF(node[1], false); |
642 |
|
// Construct the clauses (p => q) and (q => p) |
643 |
9516 |
SatClause clause1(2); |
644 |
4758 |
clause1[0] = ~p; |
645 |
4758 |
clause1[1] = q; |
646 |
4758 |
assertClause(node, clause1); |
647 |
9516 |
SatClause clause2(2); |
648 |
4758 |
clause2[0] = p; |
649 |
4758 |
clause2[1] = ~q; |
650 |
4758 |
assertClause(node, clause2); |
651 |
|
} else { |
652 |
|
// !(p <=> q) is the same as p XOR q |
653 |
522 |
SatLiteral p = toCNF(node[0], false); |
654 |
522 |
SatLiteral q = toCNF(node[1], false); |
655 |
|
// Construct the clauses (p => !q) and (!q => p) |
656 |
1044 |
SatClause clause1(2); |
657 |
522 |
clause1[0] = ~p; |
658 |
522 |
clause1[1] = ~q; |
659 |
522 |
assertClause(node.negate(), clause1); |
660 |
1044 |
SatClause clause2(2); |
661 |
522 |
clause2[0] = p; |
662 |
522 |
clause2[1] = q; |
663 |
522 |
assertClause(node.negate(), clause2); |
664 |
|
} |
665 |
5280 |
} |
666 |
|
|
667 |
41811 |
void CnfStream::convertAndAssertImplies(TNode node, bool negated) |
668 |
|
{ |
669 |
41811 |
Assert(node.getKind() == kind::IMPLIES); |
670 |
83622 |
Trace("cnf") << "CnfStream::convertAndAssertImplies(" << node |
671 |
41811 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
672 |
41811 |
if (!negated) { |
673 |
|
// p => q |
674 |
41572 |
SatLiteral p = toCNF(node[0], false); |
675 |
41572 |
SatLiteral q = toCNF(node[1], false); |
676 |
|
// Construct the clause ~p || q |
677 |
83144 |
SatClause clause(2); |
678 |
41572 |
clause[0] = ~p; |
679 |
41572 |
clause[1] = q; |
680 |
41572 |
assertClause(node, clause); |
681 |
|
} else {// Construct the |
682 |
|
// !(p => q) is the same as (p && ~q) |
683 |
239 |
convertAndAssert(node[0], false); |
684 |
239 |
convertAndAssert(node[1], true); |
685 |
|
} |
686 |
41811 |
} |
687 |
|
|
688 |
14651 |
void CnfStream::convertAndAssertIte(TNode node, bool negated) |
689 |
|
{ |
690 |
14651 |
Assert(node.getKind() == kind::ITE); |
691 |
29302 |
Trace("cnf") << "CnfStream::convertAndAssertIte(" << node |
692 |
14651 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
693 |
|
// ITE(p, q, r) |
694 |
14651 |
SatLiteral p = toCNF(node[0], false); |
695 |
14651 |
SatLiteral q = toCNF(node[1], negated); |
696 |
14651 |
SatLiteral r = toCNF(node[2], negated); |
697 |
|
// Construct the clauses: |
698 |
|
// (p => q) and (!p => r) |
699 |
|
// |
700 |
|
// Note that below q and r can be used directly because whether they are |
701 |
|
// negated has been push to the literal definitions above |
702 |
29302 |
Node nnode = node; |
703 |
14651 |
if( negated ){ |
704 |
71 |
nnode = node.negate(); |
705 |
|
} |
706 |
29302 |
SatClause clause1(2); |
707 |
14651 |
clause1[0] = ~p; |
708 |
14651 |
clause1[1] = q; |
709 |
14651 |
assertClause(nnode, clause1); |
710 |
29302 |
SatClause clause2(2); |
711 |
14651 |
clause2[0] = p; |
712 |
14651 |
clause2[1] = r; |
713 |
14651 |
assertClause(nnode, clause2); |
714 |
14651 |
} |
715 |
|
|
716 |
|
// At the top level we must ensure that all clauses that are asserted are |
717 |
|
// not unit, except for the direct assertions. This allows us to remove the |
718 |
|
// clauses later when they are not needed anymore (lemmas for example). |
719 |
337149 |
void CnfStream::convertAndAssert(TNode node, |
720 |
|
bool removable, |
721 |
|
bool negated, |
722 |
|
bool input) |
723 |
|
{ |
724 |
674298 |
Trace("cnf") << "convertAndAssert(" << node |
725 |
674298 |
<< ", negated = " << (negated ? "true" : "false") |
726 |
337149 |
<< ", removable = " << (removable ? "true" : "false") << ")\n"; |
727 |
337149 |
d_removable = removable; |
728 |
674298 |
TimerStat::CodeTimer codeTimer(d_stats.d_cnfConversionTime, true); |
729 |
337153 |
convertAndAssert(node, negated); |
730 |
337145 |
} |
731 |
|
|
732 |
737414 |
void CnfStream::convertAndAssert(TNode node, bool negated) |
733 |
|
{ |
734 |
1474828 |
Trace("cnf") << "convertAndAssert(" << node |
735 |
737414 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
736 |
|
|
737 |
737414 |
d_resourceManager->spendResource(Resource::CnfStep); |
738 |
|
|
739 |
737414 |
switch(node.getKind()) { |
740 |
98209 |
case kind::AND: convertAndAssertAnd(node, negated); break; |
741 |
385952 |
case kind::OR: convertAndAssertOr(node, negated); break; |
742 |
49 |
case kind::XOR: convertAndAssertXor(node, negated); break; |
743 |
41811 |
case kind::IMPLIES: convertAndAssertImplies(node, negated); break; |
744 |
14651 |
case kind::ITE: convertAndAssertIte(node, negated); break; |
745 |
47824 |
case kind::NOT: convertAndAssert(node[0], !negated); break; |
746 |
68711 |
case kind::EQUAL: |
747 |
68711 |
if (node[0].getType().isBoolean()) |
748 |
|
{ |
749 |
5280 |
convertAndAssertIff(node, negated); |
750 |
5280 |
break; |
751 |
|
} |
752 |
|
CVC5_FALLTHROUGH; |
753 |
|
default: |
754 |
|
{ |
755 |
287282 |
Node nnode = node; |
756 |
143641 |
if (negated) |
757 |
|
{ |
758 |
44212 |
nnode = node.negate(); |
759 |
|
} |
760 |
|
// Atoms |
761 |
287282 |
assertClause(nnode, toCNF(node, negated)); |
762 |
|
} |
763 |
143637 |
break; |
764 |
|
} |
765 |
737407 |
} |
766 |
|
|
767 |
12480 |
CnfStream::Statistics::Statistics(const std::string& name) |
768 |
12480 |
: d_cnfConversionTime(smtStatisticsRegistry().registerTimer( |
769 |
12480 |
name + "::CnfStream::cnfConversionTime")) |
770 |
|
{ |
771 |
12480 |
} |
772 |
|
|
773 |
|
} // namespace prop |
774 |
22755 |
} // namespace cvc5 |