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/env.h" |
30 |
|
#include "smt/smt_statistics_registry.h" |
31 |
|
#include "smt/solver_engine_scope.h" |
32 |
|
#include "theory/theory.h" |
33 |
|
#include "theory/theory_engine.h" |
34 |
|
|
35 |
|
namespace cvc5 { |
36 |
|
namespace prop { |
37 |
|
|
38 |
22653 |
CnfStream::CnfStream(SatSolver* satSolver, |
39 |
|
Registrar* registrar, |
40 |
|
context::Context* context, |
41 |
|
Env* env, |
42 |
|
ResourceManager* rm, |
43 |
|
FormulaLitPolicy flpol, |
44 |
22653 |
std::string name) |
45 |
|
: d_satSolver(satSolver), |
46 |
|
d_env(env), |
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 |
|
d_resourceManager(rm), |
56 |
22653 |
d_stats(name) |
57 |
|
{ |
58 |
22653 |
} |
59 |
|
|
60 |
9612516 |
bool CnfStream::assertClause(TNode node, SatClause& c) |
61 |
|
{ |
62 |
9612516 |
Trace("cnf") << "Inserting into stream " << c << " node = " << node << "\n"; |
63 |
|
|
64 |
9612516 |
ClauseId clauseId = d_satSolver->addClause(c, d_removable); |
65 |
|
|
66 |
9612516 |
return clauseId != ClauseIdUndef; |
67 |
|
} |
68 |
|
|
69 |
250405 |
bool CnfStream::assertClause(TNode node, SatLiteral a) |
70 |
|
{ |
71 |
500810 |
SatClause clause(1); |
72 |
250405 |
clause[0] = a; |
73 |
500810 |
return assertClause(node, clause); |
74 |
|
} |
75 |
|
|
76 |
4559673 |
bool CnfStream::assertClause(TNode node, SatLiteral a, SatLiteral b) |
77 |
|
{ |
78 |
9119346 |
SatClause clause(2); |
79 |
4559673 |
clause[0] = a; |
80 |
4559673 |
clause[1] = b; |
81 |
9119346 |
return assertClause(node, clause); |
82 |
|
} |
83 |
|
|
84 |
2990647 |
bool CnfStream::assertClause(TNode node, |
85 |
|
SatLiteral a, |
86 |
|
SatLiteral b, |
87 |
|
SatLiteral c) |
88 |
|
{ |
89 |
5981294 |
SatClause clause(3); |
90 |
2990647 |
clause[0] = a; |
91 |
2990647 |
clause[1] = b; |
92 |
2990647 |
clause[2] = c; |
93 |
5981294 |
return assertClause(node, clause); |
94 |
|
} |
95 |
|
|
96 |
75102036 |
bool CnfStream::hasLiteral(TNode n) const { |
97 |
75102036 |
NodeToLiteralMap::const_iterator find = d_nodeToLiteralMap.find(n); |
98 |
75102036 |
return find != d_nodeToLiteralMap.end(); |
99 |
|
} |
100 |
|
|
101 |
2877749 |
void CnfStream::ensureMappingForLiteral(TNode n) |
102 |
|
{ |
103 |
2877749 |
SatLiteral lit = getLiteral(n); |
104 |
2877749 |
if (!d_literalToNodeMap.contains(lit)) |
105 |
|
{ |
106 |
|
// Store backward-mappings |
107 |
262 |
d_literalToNodeMap.insert(lit, n); |
108 |
262 |
d_literalToNodeMap.insert(~lit, n.notNode()); |
109 |
|
} |
110 |
2877749 |
} |
111 |
|
|
112 |
2917329 |
void CnfStream::ensureLiteral(TNode n) |
113 |
|
{ |
114 |
2917329 |
AlwaysAssertArgument( |
115 |
|
hasLiteral(n) || n.getType().isBoolean(), |
116 |
|
n, |
117 |
|
"ProofCnfStream::ensureLiteral() requires a node of Boolean type.\n" |
118 |
|
"got node: %s\n" |
119 |
|
"its type: %s\n", |
120 |
|
n.toString().c_str(), |
121 |
|
n.getType().toString().c_str()); |
122 |
2917329 |
Trace("cnf") << "ensureLiteral(" << n << ")\n"; |
123 |
2982630 |
TimerStat::CodeTimer codeTimer(d_stats.d_cnfConversionTime, true); |
124 |
2917329 |
if (hasLiteral(n)) |
125 |
|
{ |
126 |
2852028 |
ensureMappingForLiteral(n); |
127 |
2852028 |
return; |
128 |
|
} |
129 |
|
// remove top level negation |
130 |
65301 |
n = n.getKind() == kind::NOT ? n[0] : n; |
131 |
65301 |
if (theory::Theory::theoryOf(n) == theory::THEORY_BOOL && !n.isVar()) |
132 |
|
{ |
133 |
|
// If we were called with something other than a theory atom (or |
134 |
|
// Boolean variable), we get a SatLiteral that is definitionally |
135 |
|
// equal to it. |
136 |
|
// These are not removable and have no proof ID |
137 |
44645 |
d_removable = false; |
138 |
|
|
139 |
44645 |
SatLiteral lit = toCNF(n, false); |
140 |
|
|
141 |
|
// Store backward-mappings |
142 |
|
// These may already exist |
143 |
44645 |
d_literalToNodeMap.insert_safe(lit, n); |
144 |
44645 |
d_literalToNodeMap.insert_safe(~lit, n.notNode()); |
145 |
|
} |
146 |
|
else |
147 |
|
{ |
148 |
|
// We have a theory atom or variable. |
149 |
20656 |
convertAtom(n); |
150 |
|
} |
151 |
|
} |
152 |
|
|
153 |
2794862 |
SatLiteral CnfStream::newLiteral(TNode node, bool isTheoryAtom, bool preRegister, bool canEliminate) { |
154 |
5589724 |
Trace("cnf") << d_name << "::newLiteral(" << node << ", " << isTheoryAtom |
155 |
2794862 |
<< ")\n" |
156 |
2794862 |
<< push; |
157 |
2794862 |
Assert(node.getKind() != kind::NOT); |
158 |
|
|
159 |
|
// if we are tracking formulas, everything is a theory atom |
160 |
2794862 |
if (!isTheoryAtom && d_flitPolicy == FormulaLitPolicy::TRACK_AND_NOTIFY) |
161 |
|
{ |
162 |
|
isTheoryAtom = true; |
163 |
|
d_notifyFormulas.insert(node); |
164 |
|
} |
165 |
|
|
166 |
|
// Get the literal for this node |
167 |
2794862 |
SatLiteral lit; |
168 |
2794862 |
if (!hasLiteral(node)) { |
169 |
2794860 |
Trace("cnf") << d_name << "::newLiteral: node already registered\n"; |
170 |
|
// If no literal, we'll make one |
171 |
2794860 |
if (node.getKind() == kind::CONST_BOOLEAN) { |
172 |
30898 |
Trace("cnf") << d_name << "::newLiteral: boolean const\n"; |
173 |
30898 |
if (node.getConst<bool>()) { |
174 |
15385 |
lit = SatLiteral(d_satSolver->trueVar()); |
175 |
|
} else { |
176 |
15513 |
lit = SatLiteral(d_satSolver->falseVar()); |
177 |
|
} |
178 |
|
} else { |
179 |
2763962 |
Trace("cnf") << d_name << "::newLiteral: new var\n"; |
180 |
2763962 |
lit = SatLiteral(d_satSolver->newVar(isTheoryAtom, preRegister, canEliminate)); |
181 |
|
} |
182 |
2794860 |
d_nodeToLiteralMap.insert(node, lit); |
183 |
2794860 |
d_nodeToLiteralMap.insert(node.notNode(), ~lit); |
184 |
|
} else { |
185 |
2 |
lit = getLiteral(node); |
186 |
|
} |
187 |
|
|
188 |
|
// If it's a theory literal, need to store it for back queries |
189 |
2794862 |
if (isTheoryAtom || d_flitPolicy == FormulaLitPolicy::TRACK) |
190 |
|
{ |
191 |
1509719 |
d_literalToNodeMap.insert_safe(lit, node); |
192 |
1509719 |
d_literalToNodeMap.insert_safe(~lit, node.notNode()); |
193 |
|
} |
194 |
|
|
195 |
|
// If a theory literal, we pre-register it |
196 |
2794862 |
if (preRegister) { |
197 |
|
// In case we are re-entered due to lemmas, save our state |
198 |
859219 |
bool backupRemovable = d_removable; |
199 |
859223 |
d_registrar->preRegister(node); |
200 |
859215 |
d_removable = backupRemovable; |
201 |
|
} |
202 |
|
// Here, you can have it |
203 |
2794858 |
Trace("cnf") << "newLiteral(" << node << ") => " << lit << "\n" << pop; |
204 |
2794858 |
return lit; |
205 |
|
} |
206 |
|
|
207 |
21780656 |
TNode CnfStream::getNode(const SatLiteral& literal) { |
208 |
21780656 |
Trace("cnf") << "getNode(" << literal << ")\n"; |
209 |
43561312 |
Trace("cnf") << "getNode(" << literal << ") => " |
210 |
21780656 |
<< d_literalToNodeMap[literal] << "\n"; |
211 |
21780656 |
return d_literalToNodeMap[literal]; |
212 |
|
} |
213 |
|
|
214 |
2590003 |
const CnfStream::NodeToLiteralMap& CnfStream::getTranslationCache() const |
215 |
|
{ |
216 |
2590003 |
return d_nodeToLiteralMap; |
217 |
|
} |
218 |
|
|
219 |
7394828 |
const CnfStream::LiteralToNodeMap& CnfStream::getNodeCache() const |
220 |
|
{ |
221 |
7394828 |
return d_literalToNodeMap; |
222 |
|
} |
223 |
|
|
224 |
24999 |
void CnfStream::getBooleanVariables(std::vector<TNode>& outputVariables) const { |
225 |
24999 |
context::CDList<TNode>::const_iterator it, it_end; |
226 |
83126 |
for (it = d_booleanVariables.begin(); it != d_booleanVariables.end(); ++ it) { |
227 |
58127 |
outputVariables.push_back(*it); |
228 |
|
} |
229 |
24999 |
} |
230 |
|
|
231 |
|
bool CnfStream::isNotifyFormula(TNode node) const |
232 |
|
{ |
233 |
|
return d_notifyFormulas.find(node) != d_notifyFormulas.end(); |
234 |
|
} |
235 |
|
|
236 |
899007 |
SatLiteral CnfStream::convertAtom(TNode node) |
237 |
|
{ |
238 |
899007 |
Trace("cnf") << "convertAtom(" << node << ")\n"; |
239 |
|
|
240 |
899007 |
Assert(!hasLiteral(node)) << "atom already mapped!"; |
241 |
|
|
242 |
899007 |
bool theoryLiteral = false; |
243 |
899007 |
bool canEliminate = true; |
244 |
899007 |
bool preRegister = false; |
245 |
|
|
246 |
|
// Is this a variable add it to the list |
247 |
899007 |
if (node.isVar() && node.getKind() != kind::BOOLEAN_TERM_VARIABLE) |
248 |
|
{ |
249 |
39788 |
d_booleanVariables.push_back(node); |
250 |
|
} |
251 |
|
else |
252 |
|
{ |
253 |
859219 |
theoryLiteral = true; |
254 |
859219 |
canEliminate = false; |
255 |
859219 |
preRegister = true; |
256 |
|
} |
257 |
|
|
258 |
|
// Make a new literal (variables are not considered theory literals) |
259 |
899011 |
SatLiteral lit = newLiteral(node, theoryLiteral, preRegister, canEliminate); |
260 |
|
// Return the resulting literal |
261 |
899003 |
return lit; |
262 |
|
} |
263 |
|
|
264 |
53338268 |
SatLiteral CnfStream::getLiteral(TNode node) { |
265 |
53338268 |
Assert(!node.isNull()) << "CnfStream: can't getLiteral() of null node"; |
266 |
|
|
267 |
106676536 |
Assert(d_nodeToLiteralMap.contains(node)) |
268 |
53338268 |
<< "Literal not in the CNF Cache: " << node << "\n"; |
269 |
|
|
270 |
53338268 |
SatLiteral literal = d_nodeToLiteralMap[node]; |
271 |
106676536 |
Trace("cnf") << "CnfStream::getLiteral(" << node << ") => " << literal |
272 |
53338268 |
<< "\n"; |
273 |
53338268 |
return literal; |
274 |
|
} |
275 |
|
|
276 |
277835 |
void CnfStream::handleXor(TNode xorNode) |
277 |
|
{ |
278 |
277835 |
Assert(!hasLiteral(xorNode)) << "Atom already mapped!"; |
279 |
277835 |
Assert(xorNode.getKind() == kind::XOR) << "Expecting an XOR expression!"; |
280 |
277835 |
Assert(xorNode.getNumChildren() == 2) << "Expecting exactly 2 children!"; |
281 |
277835 |
Assert(!d_removable) << "Removable clauses can not contain Boolean structure"; |
282 |
277835 |
Trace("cnf") << "CnfStream::handleXor(" << xorNode << ")\n"; |
283 |
|
|
284 |
277835 |
SatLiteral a = getLiteral(xorNode[0]); |
285 |
277835 |
SatLiteral b = getLiteral(xorNode[1]); |
286 |
|
|
287 |
277835 |
SatLiteral xorLit = newLiteral(xorNode); |
288 |
|
|
289 |
277835 |
assertClause(xorNode.negate(), a, b, ~xorLit); |
290 |
277835 |
assertClause(xorNode.negate(), ~a, ~b, ~xorLit); |
291 |
277835 |
assertClause(xorNode, a, ~b, xorLit); |
292 |
277835 |
assertClause(xorNode, ~a, b, xorLit); |
293 |
277835 |
} |
294 |
|
|
295 |
415044 |
void CnfStream::handleOr(TNode orNode) |
296 |
|
{ |
297 |
415044 |
Assert(!hasLiteral(orNode)) << "Atom already mapped!"; |
298 |
415044 |
Assert(orNode.getKind() == kind::OR) << "Expecting an OR expression!"; |
299 |
415044 |
Assert(orNode.getNumChildren() > 1) << "Expecting more then 1 child!"; |
300 |
415044 |
Assert(!d_removable) << "Removable clauses can not contain Boolean structure"; |
301 |
415044 |
Trace("cnf") << "CnfStream::handleOr(" << orNode << ")\n"; |
302 |
|
|
303 |
|
// Number of children |
304 |
415044 |
size_t numChildren = orNode.getNumChildren(); |
305 |
|
|
306 |
|
// Get the literal for this node |
307 |
415044 |
SatLiteral orLit = newLiteral(orNode); |
308 |
|
|
309 |
|
// Transform all the children first |
310 |
830088 |
SatClause clause(numChildren + 1); |
311 |
1448207 |
for (size_t i = 0; i < numChildren; ++i) |
312 |
|
{ |
313 |
1033163 |
clause[i] = getLiteral(orNode[i]); |
314 |
|
|
315 |
|
// lit <- (a_1 | a_2 | a_3 | ... | a_n) |
316 |
|
// lit | ~(a_1 | a_2 | a_3 | ... | a_n) |
317 |
|
// (lit | ~a_1) & (lit | ~a_2) & (lit & ~a_3) & ... & (lit & ~a_n) |
318 |
1033163 |
assertClause(orNode, orLit, ~clause[i]); |
319 |
|
} |
320 |
|
|
321 |
|
// lit -> (a_1 | a_2 | a_3 | ... | a_n) |
322 |
|
// ~lit | a_1 | a_2 | a_3 | ... | a_n |
323 |
415044 |
clause[numChildren] = ~orLit; |
324 |
|
// This needs to go last, as the clause might get modified by the SAT solver |
325 |
415044 |
assertClause(orNode.negate(), clause); |
326 |
415044 |
} |
327 |
|
|
328 |
700917 |
void CnfStream::handleAnd(TNode andNode) |
329 |
|
{ |
330 |
700917 |
Assert(!hasLiteral(andNode)) << "Atom already mapped!"; |
331 |
700917 |
Assert(andNode.getKind() == kind::AND) << "Expecting an AND expression!"; |
332 |
700917 |
Assert(andNode.getNumChildren() > 1) << "Expecting more than 1 child!"; |
333 |
700917 |
Assert(!d_removable) << "Removable clauses can not contain Boolean structure"; |
334 |
700917 |
Trace("cnf") << "handleAnd(" << andNode << ")\n"; |
335 |
|
|
336 |
|
// Number of children |
337 |
700917 |
size_t numChildren = andNode.getNumChildren(); |
338 |
|
|
339 |
|
// Get the literal for this node |
340 |
700917 |
SatLiteral andLit = newLiteral(andNode); |
341 |
|
|
342 |
|
// Transform all the children first (remembering the negation) |
343 |
1401834 |
SatClause clause(numChildren + 1); |
344 |
3941365 |
for (size_t i = 0; i < numChildren; ++i) |
345 |
|
{ |
346 |
3240448 |
clause[i] = ~getLiteral(andNode[i]); |
347 |
|
|
348 |
|
// lit -> (a_1 & a_2 & a_3 & ... & a_n) |
349 |
|
// ~lit | (a_1 & a_2 & a_3 & ... & a_n) |
350 |
|
// (~lit | a_1) & (~lit | a_2) & ... & (~lit | a_n) |
351 |
3240448 |
assertClause(andNode.negate(), ~andLit, ~clause[i]); |
352 |
|
} |
353 |
|
|
354 |
|
// lit <- (a_1 & a_2 & a_3 & ... a_n) |
355 |
|
// lit | ~(a_1 & a_2 & a_3 & ... & a_n) |
356 |
|
// lit | ~a_1 | ~a_2 | ~a_3 | ... | ~a_n |
357 |
700917 |
clause[numChildren] = andLit; |
358 |
|
// This needs to go last, as the clause might get modified by the SAT solver |
359 |
700917 |
assertClause(andNode, clause); |
360 |
700917 |
} |
361 |
|
|
362 |
8515 |
void CnfStream::handleImplies(TNode impliesNode) |
363 |
|
{ |
364 |
8515 |
Assert(!hasLiteral(impliesNode)) << "Atom already mapped!"; |
365 |
8515 |
Assert(impliesNode.getKind() == kind::IMPLIES) |
366 |
|
<< "Expecting an IMPLIES expression!"; |
367 |
8515 |
Assert(impliesNode.getNumChildren() == 2) << "Expecting exactly 2 children!"; |
368 |
8515 |
Assert(!d_removable) << "Removable clauses can not contain Boolean structure"; |
369 |
8515 |
Trace("cnf") << "handleImplies(" << impliesNode << ")\n"; |
370 |
|
|
371 |
|
// Convert the children to cnf |
372 |
8515 |
SatLiteral a = getLiteral(impliesNode[0]); |
373 |
8515 |
SatLiteral b = getLiteral(impliesNode[1]); |
374 |
|
|
375 |
8515 |
SatLiteral impliesLit = newLiteral(impliesNode); |
376 |
|
|
377 |
|
// lit -> (a->b) |
378 |
|
// ~lit | ~ a | b |
379 |
8515 |
assertClause(impliesNode.negate(), ~impliesLit, ~a, b); |
380 |
|
|
381 |
|
// (a->b) -> lit |
382 |
|
// ~(~a | b) | lit |
383 |
|
// (a | l) & (~b | l) |
384 |
8515 |
assertClause(impliesNode, a, impliesLit); |
385 |
8515 |
assertClause(impliesNode, ~b, impliesLit); |
386 |
8515 |
} |
387 |
|
|
388 |
272569 |
void CnfStream::handleIff(TNode iffNode) |
389 |
|
{ |
390 |
272569 |
Assert(!hasLiteral(iffNode)) << "Atom already mapped!"; |
391 |
272569 |
Assert(iffNode.getKind() == kind::EQUAL) << "Expecting an EQUAL expression!"; |
392 |
272569 |
Assert(iffNode.getNumChildren() == 2) << "Expecting exactly 2 children!"; |
393 |
272569 |
Assert(!d_removable) << "Removable clauses can not contain Boolean structure"; |
394 |
272569 |
Trace("cnf") << "handleIff(" << iffNode << ")\n"; |
395 |
|
|
396 |
|
// Convert the children to CNF |
397 |
272569 |
SatLiteral a = getLiteral(iffNode[0]); |
398 |
272569 |
SatLiteral b = getLiteral(iffNode[1]); |
399 |
|
|
400 |
|
// Get the now literal |
401 |
272569 |
SatLiteral iffLit = newLiteral(iffNode); |
402 |
|
|
403 |
|
// lit -> ((a-> b) & (b->a)) |
404 |
|
// ~lit | ((~a | b) & (~b | a)) |
405 |
|
// (~a | b | ~lit) & (~b | a | ~lit) |
406 |
272569 |
assertClause(iffNode.negate(), ~a, b, ~iffLit); |
407 |
272569 |
assertClause(iffNode.negate(), a, ~b, ~iffLit); |
408 |
|
|
409 |
|
// (a<->b) -> lit |
410 |
|
// ~((a & b) | (~a & ~b)) | lit |
411 |
|
// (~(a & b)) & (~(~a & ~b)) | lit |
412 |
|
// ((~a | ~b) & (a | b)) | lit |
413 |
|
// (~a | ~b | lit) & (a | b | lit) |
414 |
272569 |
assertClause(iffNode, ~a, ~b, iffLit); |
415 |
272569 |
assertClause(iffNode, a, b, iffLit); |
416 |
272569 |
} |
417 |
|
|
418 |
99665 |
void CnfStream::handleIte(TNode iteNode) |
419 |
|
{ |
420 |
99665 |
Assert(!hasLiteral(iteNode)) << "Atom already mapped!"; |
421 |
99665 |
Assert(iteNode.getKind() == kind::ITE); |
422 |
99665 |
Assert(iteNode.getNumChildren() == 3); |
423 |
99665 |
Assert(!d_removable) << "Removable clauses can not contain Boolean structure"; |
424 |
199330 |
Trace("cnf") << "handleIte(" << iteNode[0] << " " << iteNode[1] << " " |
425 |
99665 |
<< iteNode[2] << ")\n"; |
426 |
|
|
427 |
99665 |
SatLiteral condLit = getLiteral(iteNode[0]); |
428 |
99665 |
SatLiteral thenLit = getLiteral(iteNode[1]); |
429 |
99665 |
SatLiteral elseLit = getLiteral(iteNode[2]); |
430 |
|
|
431 |
99665 |
SatLiteral iteLit = newLiteral(iteNode); |
432 |
|
|
433 |
|
// If ITE is true then one of the branches is true and the condition |
434 |
|
// implies which one |
435 |
|
// lit -> (ite b t e) |
436 |
|
// lit -> (t | e) & (b -> t) & (!b -> e) |
437 |
|
// lit -> (t | e) & (!b | t) & (b | e) |
438 |
|
// (!lit | t | e) & (!lit | !b | t) & (!lit | b | e) |
439 |
99665 |
assertClause(iteNode.negate(), ~iteLit, thenLit, elseLit); |
440 |
99665 |
assertClause(iteNode.negate(), ~iteLit, ~condLit, thenLit); |
441 |
99665 |
assertClause(iteNode.negate(), ~iteLit, condLit, elseLit); |
442 |
|
|
443 |
|
// If ITE is false then one of the branches is false and the condition |
444 |
|
// implies which one |
445 |
|
// !lit -> !(ite b t e) |
446 |
|
// !lit -> (!t | !e) & (b -> !t) & (!b -> !e) |
447 |
|
// !lit -> (!t | !e) & (!b | !t) & (b | !e) |
448 |
|
// (lit | !t | !e) & (lit | !b | !t) & (lit | b | !e) |
449 |
99665 |
assertClause(iteNode, iteLit, ~thenLit, ~elseLit); |
450 |
99665 |
assertClause(iteNode, iteLit, ~condLit, ~thenLit); |
451 |
99665 |
assertClause(iteNode, iteLit, condLit, ~elseLit); |
452 |
99665 |
} |
453 |
|
|
454 |
2756248 |
SatLiteral CnfStream::toCNF(TNode node, bool negated) |
455 |
|
{ |
456 |
5512496 |
Trace("cnf") << "toCNF(" << node |
457 |
2756248 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
458 |
|
|
459 |
5512496 |
TNode cur; |
460 |
2756248 |
SatLiteral nodeLit; |
461 |
5512496 |
std::vector<TNode> visit; |
462 |
5512496 |
std::unordered_map<TNode, bool> cache; |
463 |
|
|
464 |
2756248 |
visit.push_back(node); |
465 |
25615576 |
while (!visit.empty()) |
466 |
|
{ |
467 |
11429668 |
cur = visit.back(); |
468 |
11429668 |
Assert(cur.getType().isBoolean()); |
469 |
|
|
470 |
17569514 |
if (hasLiteral(cur)) |
471 |
|
{ |
472 |
6139846 |
visit.pop_back(); |
473 |
6139846 |
continue; |
474 |
|
} |
475 |
|
|
476 |
5289822 |
const auto& it = cache.find(cur); |
477 |
5289822 |
if (it == cache.end()) |
478 |
|
{ |
479 |
2757597 |
cache.emplace(cur, false); |
480 |
2757597 |
Kind k = cur.getKind(); |
481 |
|
// Only traverse Boolean nodes |
482 |
5289829 |
if (k == kind::NOT || k == kind::XOR || k == kind::ITE |
483 |
2154732 |
|| k == kind::IMPLIES || k == kind::OR || k == kind::AND |
484 |
3787846 |
|| (k == kind::EQUAL && cur[0].getType().isBoolean())) |
485 |
|
{ |
486 |
|
// Preserve the order of the recursive version |
487 |
7915740 |
for (size_t i = 0, size = cur.getNumChildren(); i < size; ++i) |
488 |
|
{ |
489 |
5915823 |
visit.push_back(cur[size - 1 - i]); |
490 |
|
} |
491 |
|
} |
492 |
2757597 |
continue; |
493 |
|
} |
494 |
2532225 |
else if (!it->second) |
495 |
|
{ |
496 |
2532225 |
it->second = true; |
497 |
2532225 |
Kind k = cur.getKind(); |
498 |
2532225 |
switch (k) |
499 |
|
{ |
500 |
|
case kind::NOT: Assert(hasLiteral(cur[0])); break; |
501 |
277835 |
case kind::XOR: handleXor(cur); break; |
502 |
99665 |
case kind::ITE: handleIte(cur); break; |
503 |
8515 |
case kind::IMPLIES: handleImplies(cur); break; |
504 |
415044 |
case kind::OR: handleOr(cur); break; |
505 |
700917 |
case kind::AND: handleAnd(cur); break; |
506 |
1030249 |
default: |
507 |
1030249 |
if (k == kind::EQUAL && cur[0].getType().isBoolean()) |
508 |
|
{ |
509 |
272569 |
handleIff(cur); |
510 |
|
} |
511 |
|
else |
512 |
|
{ |
513 |
757684 |
convertAtom(cur); |
514 |
|
} |
515 |
1030245 |
break; |
516 |
|
} |
517 |
|
} |
518 |
2532221 |
visit.pop_back(); |
519 |
|
} |
520 |
|
|
521 |
2756244 |
nodeLit = getLiteral(node); |
522 |
5512488 |
Trace("cnf") << "toCNF(): resulting literal: " |
523 |
2756244 |
<< (!negated ? nodeLit : ~nodeLit) << "\n"; |
524 |
5512488 |
return negated ? ~nodeLit : nodeLit; |
525 |
|
} |
526 |
|
|
527 |
159105 |
void CnfStream::convertAndAssertAnd(TNode node, bool negated) |
528 |
|
{ |
529 |
159105 |
Assert(node.getKind() == kind::AND); |
530 |
318210 |
Trace("cnf") << "CnfStream::convertAndAssertAnd(" << node |
531 |
159105 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
532 |
159105 |
if (!negated) { |
533 |
|
// If the node is a conjunction, we handle each conjunct separately |
534 |
151257 |
for(TNode::const_iterator conjunct = node.begin(), node_end = node.end(); |
535 |
151257 |
conjunct != node_end; ++conjunct ) { |
536 |
133331 |
convertAndAssert(*conjunct, false); |
537 |
|
} |
538 |
|
} else { |
539 |
|
// If the node is a disjunction, we construct a clause and assert it |
540 |
141177 |
int nChildren = node.getNumChildren(); |
541 |
282354 |
SatClause clause(nChildren); |
542 |
141177 |
TNode::const_iterator disjunct = node.begin(); |
543 |
1689299 |
for(int i = 0; i < nChildren; ++ disjunct, ++ i) { |
544 |
1548122 |
Assert(disjunct != node.end()); |
545 |
1548122 |
clause[i] = toCNF(*disjunct, true); |
546 |
|
} |
547 |
141177 |
Assert(disjunct == node.end()); |
548 |
141177 |
assertClause(node.negate(), clause); |
549 |
|
} |
550 |
159104 |
} |
551 |
|
|
552 |
234719 |
void CnfStream::convertAndAssertOr(TNode node, bool negated) |
553 |
|
{ |
554 |
234719 |
Assert(node.getKind() == kind::OR); |
555 |
469438 |
Trace("cnf") << "CnfStream::convertAndAssertOr(" << node |
556 |
234719 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
557 |
234719 |
if (!negated) { |
558 |
|
// If the node is a disjunction, we construct a clause and assert it |
559 |
234528 |
int nChildren = node.getNumChildren(); |
560 |
469056 |
SatClause clause(nChildren); |
561 |
234528 |
TNode::const_iterator disjunct = node.begin(); |
562 |
968097 |
for(int i = 0; i < nChildren; ++ disjunct, ++ i) { |
563 |
733569 |
Assert(disjunct != node.end()); |
564 |
733569 |
clause[i] = toCNF(*disjunct, false); |
565 |
|
} |
566 |
234528 |
Assert(disjunct == node.end()); |
567 |
234528 |
assertClause(node, clause); |
568 |
|
} else { |
569 |
|
// If the node is a conjunction, we handle each conjunct separately |
570 |
1248 |
for(TNode::const_iterator conjunct = node.begin(), node_end = node.end(); |
571 |
1248 |
conjunct != node_end; ++conjunct ) { |
572 |
1057 |
convertAndAssert(*conjunct, true); |
573 |
|
} |
574 |
|
} |
575 |
234719 |
} |
576 |
|
|
577 |
53 |
void CnfStream::convertAndAssertXor(TNode node, bool negated) |
578 |
|
{ |
579 |
53 |
Assert(node.getKind() == kind::XOR); |
580 |
106 |
Trace("cnf") << "CnfStream::convertAndAssertXor(" << node |
581 |
53 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
582 |
53 |
if (!negated) { |
583 |
|
// p XOR q |
584 |
51 |
SatLiteral p = toCNF(node[0], false); |
585 |
51 |
SatLiteral q = toCNF(node[1], false); |
586 |
|
// Construct the clauses (p => !q) and (!q => p) |
587 |
102 |
SatClause clause1(2); |
588 |
51 |
clause1[0] = ~p; |
589 |
51 |
clause1[1] = ~q; |
590 |
51 |
assertClause(node, clause1); |
591 |
102 |
SatClause clause2(2); |
592 |
51 |
clause2[0] = p; |
593 |
51 |
clause2[1] = q; |
594 |
51 |
assertClause(node, clause2); |
595 |
|
} else { |
596 |
|
// !(p XOR q) is the same as p <=> q |
597 |
2 |
SatLiteral p = toCNF(node[0], false); |
598 |
2 |
SatLiteral q = toCNF(node[1], false); |
599 |
|
// Construct the clauses (p => q) and (q => p) |
600 |
4 |
SatClause clause1(2); |
601 |
2 |
clause1[0] = ~p; |
602 |
2 |
clause1[1] = q; |
603 |
2 |
assertClause(node.negate(), clause1); |
604 |
4 |
SatClause clause2(2); |
605 |
2 |
clause2[0] = p; |
606 |
2 |
clause2[1] = ~q; |
607 |
2 |
assertClause(node.negate(), clause2); |
608 |
|
} |
609 |
53 |
} |
610 |
|
|
611 |
17049 |
void CnfStream::convertAndAssertIff(TNode node, bool negated) |
612 |
|
{ |
613 |
17049 |
Assert(node.getKind() == kind::EQUAL); |
614 |
34098 |
Trace("cnf") << "CnfStream::convertAndAssertIff(" << node |
615 |
17049 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
616 |
17049 |
if (!negated) { |
617 |
|
// p <=> q |
618 |
16512 |
SatLiteral p = toCNF(node[0], false); |
619 |
16512 |
SatLiteral q = toCNF(node[1], false); |
620 |
|
// Construct the clauses (p => q) and (q => p) |
621 |
33024 |
SatClause clause1(2); |
622 |
16512 |
clause1[0] = ~p; |
623 |
16512 |
clause1[1] = q; |
624 |
16512 |
assertClause(node, clause1); |
625 |
33024 |
SatClause clause2(2); |
626 |
16512 |
clause2[0] = p; |
627 |
16512 |
clause2[1] = ~q; |
628 |
16512 |
assertClause(node, clause2); |
629 |
|
} else { |
630 |
|
// !(p <=> q) is the same as p XOR q |
631 |
537 |
SatLiteral p = toCNF(node[0], false); |
632 |
537 |
SatLiteral q = toCNF(node[1], false); |
633 |
|
// Construct the clauses (p => !q) and (!q => p) |
634 |
1074 |
SatClause clause1(2); |
635 |
537 |
clause1[0] = ~p; |
636 |
537 |
clause1[1] = ~q; |
637 |
537 |
assertClause(node.negate(), clause1); |
638 |
1074 |
SatClause clause2(2); |
639 |
537 |
clause2[0] = p; |
640 |
537 |
clause2[1] = q; |
641 |
537 |
assertClause(node.negate(), clause2); |
642 |
|
} |
643 |
17049 |
} |
644 |
|
|
645 |
72736 |
void CnfStream::convertAndAssertImplies(TNode node, bool negated) |
646 |
|
{ |
647 |
72736 |
Assert(node.getKind() == kind::IMPLIES); |
648 |
145472 |
Trace("cnf") << "CnfStream::convertAndAssertImplies(" << node |
649 |
72736 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
650 |
72736 |
if (!negated) { |
651 |
|
// p => q |
652 |
72496 |
SatLiteral p = toCNF(node[0], false); |
653 |
72496 |
SatLiteral q = toCNF(node[1], false); |
654 |
|
// Construct the clause ~p || q |
655 |
144992 |
SatClause clause(2); |
656 |
72496 |
clause[0] = ~p; |
657 |
72496 |
clause[1] = q; |
658 |
72496 |
assertClause(node, clause); |
659 |
|
} else {// Construct the |
660 |
|
// !(p => q) is the same as (p && ~q) |
661 |
240 |
convertAndAssert(node[0], false); |
662 |
240 |
convertAndAssert(node[1], true); |
663 |
|
} |
664 |
72736 |
} |
665 |
|
|
666 |
18879 |
void CnfStream::convertAndAssertIte(TNode node, bool negated) |
667 |
|
{ |
668 |
18879 |
Assert(node.getKind() == kind::ITE); |
669 |
37758 |
Trace("cnf") << "CnfStream::convertAndAssertIte(" << node |
670 |
18879 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
671 |
|
// ITE(p, q, r) |
672 |
18879 |
SatLiteral p = toCNF(node[0], false); |
673 |
18879 |
SatLiteral q = toCNF(node[1], negated); |
674 |
18879 |
SatLiteral r = toCNF(node[2], negated); |
675 |
|
// Construct the clauses: |
676 |
|
// (p => q) and (!p => r) |
677 |
|
// |
678 |
|
// Note that below q and r can be used directly because whether they are |
679 |
|
// negated has been push to the literal definitions above |
680 |
37758 |
Node nnode = node; |
681 |
18879 |
if( negated ){ |
682 |
73 |
nnode = node.negate(); |
683 |
|
} |
684 |
37758 |
SatClause clause1(2); |
685 |
18879 |
clause1[0] = ~p; |
686 |
18879 |
clause1[1] = q; |
687 |
18879 |
assertClause(nnode, clause1); |
688 |
37758 |
SatClause clause2(2); |
689 |
18879 |
clause2[0] = p; |
690 |
18879 |
clause2[1] = r; |
691 |
18879 |
assertClause(nnode, clause2); |
692 |
18879 |
} |
693 |
|
|
694 |
|
// At the top level we must ensure that all clauses that are asserted are |
695 |
|
// not unit, except for the direct assertions. This allows us to remove the |
696 |
|
// clauses later when they are not needed anymore (lemmas for example). |
697 |
561753 |
void CnfStream::convertAndAssert(TNode node, |
698 |
|
bool removable, |
699 |
|
bool negated, |
700 |
|
bool input) |
701 |
|
{ |
702 |
1123506 |
Trace("cnf") << "convertAndAssert(" << node |
703 |
1123506 |
<< ", negated = " << (negated ? "true" : "false") |
704 |
561753 |
<< ", removable = " << (removable ? "true" : "false") << ")\n"; |
705 |
561753 |
d_removable = removable; |
706 |
1123506 |
TimerStat::CodeTimer codeTimer(d_stats.d_cnfConversionTime, true); |
707 |
561757 |
convertAndAssert(node, negated); |
708 |
561749 |
} |
709 |
|
|
710 |
767584 |
void CnfStream::convertAndAssert(TNode node, bool negated) |
711 |
|
{ |
712 |
1535168 |
Trace("cnf") << "convertAndAssert(" << node |
713 |
767584 |
<< ", negated = " << (negated ? "true" : "false") << ")\n"; |
714 |
|
|
715 |
767584 |
d_resourceManager->spendResource(Resource::CnfStep); |
716 |
|
|
717 |
767584 |
switch(node.getKind()) { |
718 |
159106 |
case kind::AND: convertAndAssertAnd(node, negated); break; |
719 |
234719 |
case kind::OR: convertAndAssertOr(node, negated); break; |
720 |
53 |
case kind::XOR: convertAndAssertXor(node, negated); break; |
721 |
72736 |
case kind::IMPLIES: convertAndAssertImplies(node, negated); break; |
722 |
18879 |
case kind::ITE: convertAndAssertIte(node, negated); break; |
723 |
70966 |
case kind::NOT: convertAndAssert(node[0], !negated); break; |
724 |
99459 |
case kind::EQUAL: |
725 |
99459 |
if (node[0].getType().isBoolean()) |
726 |
|
{ |
727 |
17049 |
convertAndAssertIff(node, negated); |
728 |
17049 |
break; |
729 |
|
} |
730 |
|
CVC5_FALLTHROUGH; |
731 |
|
default: |
732 |
|
{ |
733 |
388158 |
Node nnode = node; |
734 |
194079 |
if (negated) |
735 |
|
{ |
736 |
64729 |
nnode = node.negate(); |
737 |
|
} |
738 |
|
// Atoms |
739 |
388158 |
assertClause(nnode, toCNF(node, negated)); |
740 |
|
} |
741 |
194075 |
break; |
742 |
|
} |
743 |
767577 |
} |
744 |
|
|
745 |
22653 |
CnfStream::Statistics::Statistics(const std::string& name) |
746 |
22653 |
: d_cnfConversionTime(smtStatisticsRegistry().registerTimer( |
747 |
22653 |
name + "::CnfStream::cnfConversionTime")) |
748 |
|
{ |
749 |
22653 |
} |
750 |
|
|
751 |
|
} // namespace prop |
752 |
31137 |
} // namespace cvc5 |