1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Gereon Kremer, Morgan Deters, Dejan Jovanovic |
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 non-clausal circuit propagator for Boolean simplification. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "theory/booleans/circuit_propagator.h" |
17 |
|
|
18 |
|
#include <algorithm> |
19 |
|
#include <stack> |
20 |
|
#include <vector> |
21 |
|
|
22 |
|
#include "expr/node_algorithm.h" |
23 |
|
#include "expr/proof_node.h" |
24 |
|
#include "expr/proof_node_manager.h" |
25 |
|
#include "theory/booleans/proof_circuit_propagator.h" |
26 |
|
#include "theory/eager_proof_generator.h" |
27 |
|
#include "theory/theory.h" |
28 |
|
#include "util/hash.h" |
29 |
|
#include "util/utility.h" |
30 |
|
|
31 |
|
using namespace std; |
32 |
|
|
33 |
|
namespace cvc5 { |
34 |
|
namespace theory { |
35 |
|
namespace booleans { |
36 |
|
|
37 |
9586 |
CircuitPropagator::CircuitPropagator(bool enableForward, bool enableBackward) |
38 |
|
: d_context(), |
39 |
|
d_propagationQueue(), |
40 |
|
d_propagationQueueClearer(&d_context, d_propagationQueue), |
41 |
19172 |
d_conflict(&d_context, TrustNode()), |
42 |
|
d_learnedLiterals(), |
43 |
|
d_learnedLiteralClearer(&d_context, d_learnedLiterals), |
44 |
|
d_backEdges(), |
45 |
|
d_backEdgesClearer(&d_context, d_backEdges), |
46 |
|
d_seen(&d_context), |
47 |
|
d_state(&d_context), |
48 |
|
d_forwardPropagation(enableForward), |
49 |
|
d_backwardPropagation(enableBackward), |
50 |
|
d_needsFinish(false), |
51 |
|
d_pnm(nullptr), |
52 |
|
d_epg(nullptr), |
53 |
|
d_proofInternal(nullptr), |
54 |
28758 |
d_proofExternal(nullptr) |
55 |
|
{ |
56 |
9586 |
} |
57 |
|
|
58 |
10275 |
void CircuitPropagator::finish() |
59 |
|
{ |
60 |
10275 |
Trace("circuit-prop") << "FINISH" << std::endl; |
61 |
10275 |
d_context.pop(); |
62 |
10275 |
} |
63 |
|
|
64 |
575724 |
void CircuitPropagator::assertTrue(TNode assertion) |
65 |
|
{ |
66 |
575724 |
Trace("circuit-prop") << "TRUE: " << assertion << std::endl; |
67 |
575724 |
if (assertion.getKind() == kind::CONST_BOOLEAN && !assertion.getConst<bool>()) |
68 |
|
{ |
69 |
660 |
makeConflict(assertion); |
70 |
|
} |
71 |
575064 |
else if (assertion.getKind() == kind::AND) |
72 |
|
{ |
73 |
6310 |
ProofCircuitPropagatorBackward prover{d_pnm, assertion, true}; |
74 |
3155 |
if (isProofEnabled()) |
75 |
|
{ |
76 |
807 |
addProof(assertion, prover.assume(assertion)); |
77 |
|
} |
78 |
498504 |
for (auto it = assertion.begin(); it != assertion.end(); ++it) |
79 |
|
{ |
80 |
495349 |
addProof(*it, prover.andTrue(it)); |
81 |
495349 |
assertTrue(*it); |
82 |
|
} |
83 |
|
} |
84 |
|
else |
85 |
|
{ |
86 |
|
// Analyze the assertion for back-edges and all that |
87 |
571909 |
computeBackEdges(assertion); |
88 |
|
// Assign the given assertion to true |
89 |
571909 |
if (isProofEnabled()) |
90 |
|
{ |
91 |
46977 |
assignAndEnqueue(assertion, true, d_pnm->mkAssume(assertion)); |
92 |
|
} |
93 |
|
else |
94 |
|
{ |
95 |
524932 |
assignAndEnqueue(assertion, true, nullptr); |
96 |
|
} |
97 |
|
} |
98 |
575724 |
} |
99 |
|
|
100 |
1099163 |
void CircuitPropagator::assignAndEnqueue(TNode n, |
101 |
|
bool value, |
102 |
|
std::shared_ptr<ProofNode> proof) |
103 |
|
{ |
104 |
2198326 |
Trace("circuit-prop") << "CircuitPropagator::assign(" << n << ", " |
105 |
1099163 |
<< (value ? "true" : "false") << ")" << std::endl; |
106 |
|
|
107 |
1099163 |
if (n.getKind() == kind::CONST_BOOLEAN) |
108 |
|
{ |
109 |
|
// Assigning a constant to the opposite value is dumb |
110 |
11236 |
if (value != n.getConst<bool>()) |
111 |
|
{ |
112 |
|
makeConflict(n); |
113 |
|
return; |
114 |
|
} |
115 |
|
} |
116 |
|
|
117 |
1099163 |
if (isProofEnabled()) |
118 |
|
{ |
119 |
96606 |
if (proof == nullptr) |
120 |
|
{ |
121 |
|
Warning() << "CircuitPropagator: Proof is missing for " << n << std::endl; |
122 |
|
Assert(false); |
123 |
|
} |
124 |
|
else |
125 |
|
{ |
126 |
96606 |
Assert(!proof->getResult().isNull()); |
127 |
193212 |
Node expected = value ? Node(n) : n.negate(); |
128 |
96606 |
if (proof->getResult() != expected) |
129 |
|
{ |
130 |
|
Warning() << "CircuitPropagator: Incorrect proof: " << expected |
131 |
|
<< " vs. " << proof->getResult() << std::endl |
132 |
|
<< *proof << std::endl; |
133 |
|
} |
134 |
96606 |
addProof(expected, std::move(proof)); |
135 |
|
} |
136 |
|
} |
137 |
|
|
138 |
|
// Get the current assignment |
139 |
1099163 |
AssignmentStatus state = d_state[n]; |
140 |
|
|
141 |
1099163 |
if (state != UNASSIGNED) |
142 |
|
{ |
143 |
|
// If the node is already assigned we might have a conflict |
144 |
408445 |
if (value != (state == ASSIGNED_TO_TRUE)) |
145 |
|
{ |
146 |
271 |
makeConflict(n); |
147 |
|
} |
148 |
|
} |
149 |
|
else |
150 |
|
{ |
151 |
|
// If unassigned, mark it as assigned |
152 |
690718 |
d_state[n] = value ? ASSIGNED_TO_TRUE : ASSIGNED_TO_FALSE; |
153 |
|
// Add for further propagation |
154 |
690718 |
d_propagationQueue.push_back(n); |
155 |
|
} |
156 |
|
} |
157 |
|
|
158 |
931 |
void CircuitPropagator::makeConflict(Node n) |
159 |
|
{ |
160 |
1839 |
auto bfalse = NodeManager::currentNM()->mkConst(false); |
161 |
931 |
ProofGenerator* g = nullptr; |
162 |
931 |
if (isProofEnabled()) |
163 |
|
{ |
164 |
344 |
if (d_epg->hasProofFor(bfalse)) |
165 |
|
{ |
166 |
23 |
return; |
167 |
|
} |
168 |
321 |
ProofCircuitPropagator pcp(d_pnm); |
169 |
321 |
if (n == bfalse) |
170 |
|
{ |
171 |
218 |
d_epg->setProofFor(bfalse, pcp.assume(bfalse)); |
172 |
|
} |
173 |
|
else |
174 |
|
{ |
175 |
206 |
d_epg->setProofFor(bfalse, |
176 |
206 |
pcp.conflict(pcp.assume(n), pcp.assume(n.negate()))); |
177 |
|
} |
178 |
321 |
g = d_proofInternal.get(); |
179 |
642 |
Trace("circuit-prop") << "Added conflict " << *d_epg->getProofFor(bfalse) |
180 |
321 |
<< std::endl; |
181 |
642 |
Trace("circuit-prop") << "\texpanded " << *g->getProofFor(bfalse) |
182 |
321 |
<< std::endl; |
183 |
|
} |
184 |
908 |
d_conflict = TrustNode::mkTrustLemma(bfalse, g); |
185 |
|
} |
186 |
|
|
187 |
571909 |
void CircuitPropagator::computeBackEdges(TNode node) |
188 |
|
{ |
189 |
1143818 |
Debug("circuit-prop") << "CircuitPropagator::computeBackEdges(" << node << ")" |
190 |
571909 |
<< endl; |
191 |
|
|
192 |
|
// Vector of nodes to visit |
193 |
1143818 |
vector<TNode> toVisit; |
194 |
|
|
195 |
|
// Start with the top node |
196 |
571909 |
if (d_seen.find(node) == d_seen.end()) |
197 |
|
{ |
198 |
558005 |
toVisit.push_back(node); |
199 |
558005 |
d_seen.insert(node); |
200 |
|
} |
201 |
|
|
202 |
|
// Initialize the back-edges for the root, so we don't have a special case |
203 |
571909 |
d_backEdges[node]; |
204 |
|
|
205 |
|
// Go through the visit list |
206 |
1598187 |
for (unsigned i = 0; i < toVisit.size(); ++i) |
207 |
|
{ |
208 |
|
// Node we need to visit |
209 |
2052556 |
TNode current = toVisit[i]; |
210 |
2052556 |
Debug("circuit-prop") |
211 |
1026278 |
<< "CircuitPropagator::computeBackEdges(): processing " << current |
212 |
1026278 |
<< endl; |
213 |
1026278 |
Assert(d_seen.find(current) != d_seen.end()); |
214 |
|
|
215 |
|
// If this not an atom visit all the children and compute the back edges |
216 |
1026278 |
if (Theory::theoryOf(current) == THEORY_BOOL) |
217 |
|
{ |
218 |
2707156 |
for (unsigned child = 0, child_end = current.getNumChildren(); |
219 |
2707156 |
child < child_end; |
220 |
|
++child) |
221 |
|
{ |
222 |
3656160 |
TNode childNode = current[child]; |
223 |
|
// Add the back edge |
224 |
1828080 |
d_backEdges[childNode].push_back(current); |
225 |
|
// Add to the queue if not seen yet |
226 |
1828080 |
if (d_seen.find(childNode) == d_seen.end()) |
227 |
|
{ |
228 |
468273 |
toVisit.push_back(childNode); |
229 |
468273 |
d_seen.insert(childNode); |
230 |
|
} |
231 |
|
} |
232 |
|
} |
233 |
|
} |
234 |
571909 |
} |
235 |
|
|
236 |
564174 |
void CircuitPropagator::propagateBackward(TNode parent, bool parentAssignment) |
237 |
|
{ |
238 |
1128348 |
Debug("circuit-prop") << "CircuitPropagator::propagateBackward(" << parent |
239 |
564174 |
<< ", " << parentAssignment << ")" << endl; |
240 |
1128348 |
ProofCircuitPropagatorBackward prover{d_pnm, parent, parentAssignment}; |
241 |
|
|
242 |
|
// backward rules |
243 |
564174 |
switch (parent.getKind()) |
244 |
|
{ |
245 |
5789 |
case kind::AND: |
246 |
5789 |
if (parentAssignment) |
247 |
|
{ |
248 |
|
// AND = TRUE: forall children c, assign(c = TRUE) |
249 |
20914 |
for (TNode::iterator i = parent.begin(), i_end = parent.end(); |
250 |
20914 |
i != i_end; |
251 |
|
++i) |
252 |
|
{ |
253 |
19962 |
assignAndEnqueue(*i, true, prover.andTrue(i)); |
254 |
|
} |
255 |
|
} |
256 |
|
else |
257 |
|
{ |
258 |
|
// AND = FALSE: if all children BUT ONE == TRUE, assign(c = FALSE) |
259 |
|
TNode::iterator holdout = |
260 |
10257 |
find_if_unique(parent.begin(), parent.end(), [this](TNode x) { |
261 |
20514 |
return !isAssignedTo(x, true); |
262 |
25351 |
}); |
263 |
4837 |
if (holdout != parent.end()) |
264 |
|
{ |
265 |
400 |
assignAndEnqueue(*holdout, false, prover.andFalse(parent, holdout)); |
266 |
|
} |
267 |
|
} |
268 |
5789 |
break; |
269 |
483705 |
case kind::OR: |
270 |
483705 |
if (parentAssignment) |
271 |
|
{ |
272 |
|
// OR = TRUE: if all children BUT ONE == FALSE, assign(c = TRUE) |
273 |
|
TNode::iterator holdout = |
274 |
966101 |
find_if_unique(parent.begin(), parent.end(), [this](TNode x) { |
275 |
1932202 |
return !isAssignedTo(x, false); |
276 |
2414801 |
}); |
277 |
482599 |
if (holdout != parent.end()) |
278 |
|
{ |
279 |
510 |
assignAndEnqueue(*holdout, true, prover.orTrue(parent, holdout)); |
280 |
|
} |
281 |
|
} |
282 |
|
else |
283 |
|
{ |
284 |
|
// OR = FALSE: forall children c, assign(c = FALSE) |
285 |
5402 |
for (TNode::iterator i = parent.begin(), i_end = parent.end(); |
286 |
5402 |
i != i_end; |
287 |
|
++i) |
288 |
|
{ |
289 |
4296 |
assignAndEnqueue(*i, false, prover.orFalse(i)); |
290 |
|
} |
291 |
|
} |
292 |
483705 |
break; |
293 |
69056 |
case kind::NOT: |
294 |
|
// NOT = b: assign(c = !b) |
295 |
69056 |
assignAndEnqueue( |
296 |
138112 |
parent[0], !parentAssignment, prover.Not(!parentAssignment, parent)); |
297 |
69056 |
break; |
298 |
329 |
case kind::ITE: |
299 |
329 |
if (isAssignedTo(parent[0], true)) |
300 |
|
{ |
301 |
|
// ITE c x y = v: if c is assigned and TRUE, assign(x = v) |
302 |
11 |
assignAndEnqueue(parent[1], parentAssignment, prover.iteC(true)); |
303 |
|
} |
304 |
318 |
else if (isAssignedTo(parent[0], false)) |
305 |
|
{ |
306 |
|
// ITE c x y = v: if c is assigned and FALSE, assign(y = v) |
307 |
3 |
assignAndEnqueue(parent[2], parentAssignment, prover.iteC(false)); |
308 |
|
} |
309 |
315 |
else if (isAssigned(parent[1]) && isAssigned(parent[2])) |
310 |
|
{ |
311 |
|
if (getAssignment(parent[1]) == parentAssignment |
312 |
|
&& getAssignment(parent[2]) != parentAssignment) |
313 |
|
{ |
314 |
|
// ITE c x y = v: if c is unassigned, x and y are assigned, x==v and |
315 |
|
// y!=v, assign(c = TRUE) |
316 |
|
assignAndEnqueue(parent[0], true, prover.iteIsCase(1)); |
317 |
|
} |
318 |
|
else if (getAssignment(parent[1]) != parentAssignment |
319 |
|
&& getAssignment(parent[2]) == parentAssignment) |
320 |
|
{ |
321 |
|
// ITE c x y = v: if c is unassigned, x and y are assigned, x!=v and |
322 |
|
// y==v, assign(c = FALSE) |
323 |
|
assignAndEnqueue(parent[0], false, prover.iteIsCase(0)); |
324 |
|
} |
325 |
|
} |
326 |
329 |
break; |
327 |
2350 |
case kind::EQUAL: |
328 |
2350 |
Assert(parent[0].getType().isBoolean()); |
329 |
2350 |
if (parentAssignment) |
330 |
|
{ |
331 |
|
// IFF x y = TRUE: if x [resp y] is assigned, assign(y = x.assignment |
332 |
|
// [resp x = y.assignment]) |
333 |
1772 |
if (isAssigned(parent[0])) |
334 |
|
{ |
335 |
668 |
assignAndEnqueue(parent[1], |
336 |
1336 |
getAssignment(parent[0]), |
337 |
1336 |
prover.eqYFromX(getAssignment(parent[0]), parent)); |
338 |
|
} |
339 |
1104 |
else if (isAssigned(parent[1])) |
340 |
|
{ |
341 |
40 |
assignAndEnqueue(parent[0], |
342 |
80 |
getAssignment(parent[1]), |
343 |
80 |
prover.eqXFromY(getAssignment(parent[1]), parent)); |
344 |
|
} |
345 |
|
} |
346 |
|
else |
347 |
|
{ |
348 |
|
// IFF x y = FALSE: if x [resp y] is assigned, assign(y = !x.assignment |
349 |
|
// [resp x = !y.assignment]) |
350 |
578 |
if (isAssigned(parent[0])) |
351 |
|
{ |
352 |
5 |
assignAndEnqueue(parent[1], |
353 |
10 |
!getAssignment(parent[0]), |
354 |
10 |
prover.neqYFromX(getAssignment(parent[0]), parent)); |
355 |
|
} |
356 |
573 |
else if (isAssigned(parent[1])) |
357 |
|
{ |
358 |
3 |
assignAndEnqueue(parent[0], |
359 |
6 |
!getAssignment(parent[1]), |
360 |
6 |
prover.neqXFromY(getAssignment(parent[1]), parent)); |
361 |
|
} |
362 |
|
} |
363 |
2350 |
break; |
364 |
2829 |
case kind::IMPLIES: |
365 |
2829 |
if (parentAssignment) |
366 |
|
{ |
367 |
2196 |
if (isAssignedTo(parent[0], true)) |
368 |
|
{ |
369 |
|
// IMPLIES x y = TRUE, and x == TRUE: assign(y = TRUE) |
370 |
594 |
assignAndEnqueue(parent[1], true, prover.impliesYFromX(parent)); |
371 |
|
} |
372 |
2196 |
if (isAssignedTo(parent[1], false)) |
373 |
|
{ |
374 |
|
// IMPLIES x y = TRUE, and y == FALSE: assign(x = FALSE) |
375 |
50 |
assignAndEnqueue(parent[0], false, prover.impliesXFromY(parent)); |
376 |
|
} |
377 |
|
} |
378 |
|
else |
379 |
|
{ |
380 |
|
// IMPLIES x y = FALSE: assign(x = TRUE) and assign(y = FALSE) |
381 |
633 |
assignAndEnqueue(parent[0], true, prover.impliesNegX()); |
382 |
633 |
assignAndEnqueue(parent[1], false, prover.impliesNegY()); |
383 |
|
} |
384 |
2829 |
break; |
385 |
116 |
case kind::XOR: |
386 |
116 |
if (parentAssignment) |
387 |
|
{ |
388 |
105 |
if (isAssigned(parent[0])) |
389 |
|
{ |
390 |
|
// XOR x y = TRUE, and x assigned, assign(y = !assignment(x)) |
391 |
|
assignAndEnqueue( |
392 |
|
parent[1], |
393 |
|
!getAssignment(parent[0]), |
394 |
|
prover.xorYFromX( |
395 |
|
!parentAssignment, getAssignment(parent[0]), parent)); |
396 |
|
} |
397 |
105 |
else if (isAssigned(parent[1])) |
398 |
|
{ |
399 |
|
// XOR x y = TRUE, and y assigned, assign(x = !assignment(y)) |
400 |
6 |
assignAndEnqueue( |
401 |
|
parent[0], |
402 |
12 |
!getAssignment(parent[1]), |
403 |
30 |
prover.xorXFromY( |
404 |
18 |
!parentAssignment, getAssignment(parent[1]), parent)); |
405 |
|
} |
406 |
|
} |
407 |
|
else |
408 |
|
{ |
409 |
11 |
if (isAssigned(parent[0])) |
410 |
|
{ |
411 |
|
// XOR x y = FALSE, and x assigned, assign(y = assignment(x)) |
412 |
5 |
assignAndEnqueue( |
413 |
|
parent[1], |
414 |
10 |
getAssignment(parent[0]), |
415 |
25 |
prover.xorYFromX( |
416 |
15 |
!parentAssignment, getAssignment(parent[0]), parent)); |
417 |
|
} |
418 |
6 |
else if (isAssigned(parent[1])) |
419 |
|
{ |
420 |
|
// XOR x y = FALSE, and y assigned, assign(x = assignment(y)) |
421 |
3 |
assignAndEnqueue( |
422 |
|
parent[0], |
423 |
6 |
getAssignment(parent[1]), |
424 |
15 |
prover.xorXFromY( |
425 |
9 |
!parentAssignment, getAssignment(parent[1]), parent)); |
426 |
|
} |
427 |
|
} |
428 |
116 |
break; |
429 |
|
default: Unhandled(); |
430 |
|
} |
431 |
564174 |
} |
432 |
|
|
433 |
687358 |
void CircuitPropagator::propagateForward(TNode child, bool childAssignment) |
434 |
|
{ |
435 |
|
// The assignment we have |
436 |
1374716 |
Debug("circuit-prop") << "CircuitPropagator::propagateForward(" << child |
437 |
687358 |
<< ", " << childAssignment << ")" << endl; |
438 |
|
|
439 |
|
// Get the back any nodes where this is child |
440 |
687358 |
const vector<Node>& parents = d_backEdges.find(child)->second; |
441 |
|
|
442 |
|
// Go through the parents and see if there is anything to propagate |
443 |
687358 |
vector<Node>::const_iterator parent_it = parents.begin(); |
444 |
687358 |
vector<Node>::const_iterator parent_it_end = parents.end(); |
445 |
1743796 |
for (; parent_it != parent_it_end && d_conflict.get().isNull(); ++parent_it) |
446 |
|
{ |
447 |
|
// The current parent of the child |
448 |
1056438 |
TNode parent = *parent_it; |
449 |
528219 |
Debug("circuit-prop") << "Parent: " << parent << endl; |
450 |
528219 |
Assert(expr::hasSubterm(parent, child)); |
451 |
|
|
452 |
1056438 |
ProofCircuitPropagatorForward prover{d_pnm, child, childAssignment, parent}; |
453 |
|
|
454 |
|
// Forward rules |
455 |
528219 |
switch (parent.getKind()) |
456 |
|
{ |
457 |
29715 |
case kind::AND: |
458 |
29715 |
if (childAssignment) |
459 |
|
{ |
460 |
25566 |
TNode::iterator holdout; |
461 |
19413517 |
holdout = find_if(parent.begin(), parent.end(), [this](TNode x) { |
462 |
38775902 |
return !isAssignedTo(x, true); |
463 |
38801468 |
}); |
464 |
|
|
465 |
25566 |
if (holdout == parent.end()) |
466 |
|
{ // all children are assigned TRUE |
467 |
|
// AND ...(x=TRUE)...: if all children now assigned to TRUE, |
468 |
|
// assign(AND = TRUE) |
469 |
17808 |
assignAndEnqueue(parent, true, prover.andAllTrue()); |
470 |
|
} |
471 |
7758 |
else if (isAssignedTo(parent, false)) |
472 |
|
{ // the AND is FALSE |
473 |
|
// is the holdout unique ? |
474 |
|
TNode::iterator other = |
475 |
1269 |
find_if(holdout + 1, parent.end(), [this](TNode x) { |
476 |
2538 |
return !isAssignedTo(x, true); |
477 |
3665 |
}); |
478 |
1127 |
if (other == parent.end()) |
479 |
|
{ // the holdout is unique |
480 |
|
// AND ...(x=TRUE)...: if all children BUT ONE now assigned to |
481 |
|
// TRUE, and AND == FALSE, assign(last_holdout = FALSE) |
482 |
410 |
assignAndEnqueue( |
483 |
820 |
*holdout, false, prover.andFalse(parent, holdout)); |
484 |
|
} |
485 |
|
} |
486 |
|
} |
487 |
|
else |
488 |
|
{ |
489 |
|
// AND ...(x=FALSE)...: assign(AND = FALSE) |
490 |
4149 |
assignAndEnqueue(parent, false, prover.andOneFalse()); |
491 |
|
} |
492 |
29715 |
break; |
493 |
422161 |
case kind::OR: |
494 |
422161 |
if (childAssignment) |
495 |
|
{ |
496 |
|
// OR ...(x=TRUE)...: assign(OR = TRUE) |
497 |
243394 |
assignAndEnqueue(parent, true, prover.orOneTrue()); |
498 |
|
} |
499 |
|
else |
500 |
|
{ |
501 |
178767 |
TNode::iterator holdout; |
502 |
937228 |
holdout = find_if(parent.begin(), parent.end(), [this](TNode x) { |
503 |
1516922 |
return !isAssignedTo(x, false); |
504 |
1695689 |
}); |
505 |
178767 |
if (holdout == parent.end()) |
506 |
|
{ // all children are assigned FALSE |
507 |
|
// OR ...(x=FALSE)...: if all children now assigned to FALSE, |
508 |
|
// assign(OR = FALSE) |
509 |
4223 |
assignAndEnqueue(parent, false, prover.orFalse()); |
510 |
|
} |
511 |
174544 |
else if (isAssignedTo(parent, true)) |
512 |
|
{ // the OR is TRUE |
513 |
|
// is the holdout unique ? |
514 |
|
TNode::iterator other = |
515 |
207604 |
find_if(holdout + 1, parent.end(), [this](TNode x) { |
516 |
415208 |
return !isAssignedTo(x, false); |
517 |
586209 |
}); |
518 |
171001 |
if (other == parent.end()) |
519 |
|
{ // the holdout is unique |
520 |
|
// OR ...(x=FALSE)...: if all children BUT ONE now assigned to |
521 |
|
// FALSE, and OR == TRUE, assign(last_holdout = TRUE) |
522 |
87427 |
assignAndEnqueue(*holdout, true, prover.orTrue(parent, holdout)); |
523 |
|
} |
524 |
|
} |
525 |
|
} |
526 |
422161 |
break; |
527 |
|
|
528 |
68994 |
case kind::NOT: |
529 |
|
// NOT (x=b): assign(NOT = !b) |
530 |
68994 |
assignAndEnqueue( |
531 |
137988 |
parent, !childAssignment, prover.Not(childAssignment, parent)); |
532 |
68994 |
break; |
533 |
|
|
534 |
66 |
case kind::ITE: |
535 |
66 |
if (child == parent[0]) |
536 |
|
{ |
537 |
32 |
if (childAssignment) |
538 |
|
{ |
539 |
23 |
if (isAssigned(parent[1])) |
540 |
|
{ |
541 |
|
// ITE (c=TRUE) x y: if x is assigned, assign(ITE = x.assignment) |
542 |
3 |
assignAndEnqueue(parent, |
543 |
6 |
getAssignment(parent[1]), |
544 |
6 |
prover.iteEvalThen(getAssignment(parent[1]))); |
545 |
|
} |
546 |
|
} |
547 |
|
else |
548 |
|
{ |
549 |
9 |
if (isAssigned(parent[2])) |
550 |
|
{ |
551 |
|
// ITE (c=FALSE) x y: if y is assigned, assign(ITE = y.assignment) |
552 |
5 |
assignAndEnqueue(parent, |
553 |
10 |
getAssignment(parent[2]), |
554 |
10 |
prover.iteEvalElse(getAssignment(parent[2]))); |
555 |
|
} |
556 |
|
} |
557 |
|
} |
558 |
66 |
if (child == parent[1]) |
559 |
|
{ |
560 |
24 |
if (isAssignedTo(parent[0], true)) |
561 |
|
{ |
562 |
|
// ITE c (x=v) y: if c is assigned and TRUE, assign(ITE = v) |
563 |
14 |
assignAndEnqueue( |
564 |
28 |
parent, childAssignment, prover.iteEvalThen(childAssignment)); |
565 |
|
} |
566 |
|
} |
567 |
66 |
if (child == parent[2]) |
568 |
|
{ |
569 |
10 |
Assert(child == parent[2]); |
570 |
10 |
if (isAssignedTo(parent[0], false)) |
571 |
|
{ |
572 |
|
// ITE c x (y=v): if c is assigned and FALSE, assign(ITE = v) |
573 |
6 |
assignAndEnqueue( |
574 |
12 |
parent, childAssignment, prover.iteEvalElse(childAssignment)); |
575 |
|
} |
576 |
|
} |
577 |
66 |
break; |
578 |
1927 |
case kind::EQUAL: |
579 |
1927 |
Assert(parent[0].getType().isBoolean()); |
580 |
1927 |
if (isAssigned(parent[0]) && isAssigned(parent[1])) |
581 |
|
{ |
582 |
|
// IFF x y: if x and y is assigned, assign(IFF = (x.assignment <=> |
583 |
|
// y.assignment)) |
584 |
1587 |
assignAndEnqueue(parent, |
585 |
3174 |
getAssignment(parent[0]) == getAssignment(parent[1]), |
586 |
3174 |
prover.eqEval(getAssignment(parent[0]), |
587 |
3174 |
getAssignment(parent[1]))); |
588 |
|
} |
589 |
|
else |
590 |
|
{ |
591 |
340 |
if (isAssigned(parent)) |
592 |
|
{ |
593 |
176 |
if (child == parent[0]) |
594 |
|
{ |
595 |
100 |
if (getAssignment(parent)) |
596 |
|
{ |
597 |
|
// IFF (x = b) y: if IFF is assigned to TRUE, assign(y = b) |
598 |
98 |
assignAndEnqueue(parent[1], |
599 |
|
childAssignment, |
600 |
196 |
prover.eqYFromX(childAssignment, parent)); |
601 |
|
} |
602 |
|
else |
603 |
|
{ |
604 |
|
// IFF (x = b) y: if IFF is assigned to FALSE, assign(y = !b) |
605 |
2 |
assignAndEnqueue(parent[1], |
606 |
2 |
!childAssignment, |
607 |
4 |
prover.neqYFromX(childAssignment, parent)); |
608 |
|
} |
609 |
|
} |
610 |
|
else |
611 |
|
{ |
612 |
76 |
Assert(child == parent[1]); |
613 |
76 |
if (getAssignment(parent)) |
614 |
|
{ |
615 |
|
// IFF x y = b: if IFF is assigned to TRUE, assign(x = b) |
616 |
76 |
assignAndEnqueue(parent[0], |
617 |
|
childAssignment, |
618 |
152 |
prover.eqXFromY(childAssignment, parent)); |
619 |
|
} |
620 |
|
else |
621 |
|
{ |
622 |
|
// IFF x y = b y: if IFF is assigned to FALSE, assign(x = !b) |
623 |
|
assignAndEnqueue(parent[0], |
624 |
|
!childAssignment, |
625 |
|
prover.neqXFromY(childAssignment, parent)); |
626 |
|
} |
627 |
|
} |
628 |
|
} |
629 |
|
} |
630 |
1927 |
break; |
631 |
5247 |
case kind::IMPLIES: |
632 |
5247 |
if (isAssigned(parent[0]) && isAssigned(parent[1])) |
633 |
|
{ |
634 |
|
// IMPLIES (x=v1) (y=v2): assign(IMPLIES = (!v1 || v2)) |
635 |
2022 |
assignAndEnqueue( |
636 |
|
parent, |
637 |
4044 |
!getAssignment(parent[0]) || getAssignment(parent[1]), |
638 |
4044 |
prover.impliesEval(getAssignment(parent[0]), |
639 |
4044 |
getAssignment(parent[1]))); |
640 |
|
} |
641 |
|
else |
642 |
|
{ |
643 |
11727 |
if (child == parent[0] && childAssignment |
644 |
11493 |
&& isAssignedTo(parent, true)) |
645 |
|
{ |
646 |
|
// IMPLIES (x=TRUE) y [with IMPLIES == TRUE]: assign(y = TRUE) |
647 |
80 |
assignAndEnqueue(parent[1], true, prover.impliesYFromX(parent)); |
648 |
|
} |
649 |
10848 |
if (child == parent[1] && !childAssignment |
650 |
9682 |
&& isAssignedTo(parent, true)) |
651 |
|
{ |
652 |
|
// IMPLIES x (y=FALSE) [with IMPLIES == TRUE]: assign(x = FALSE) |
653 |
6 |
assignAndEnqueue(parent[0], false, prover.impliesXFromY(parent)); |
654 |
|
} |
655 |
|
// Note that IMPLIES == FALSE doesn't need any cases here |
656 |
|
// because if that assignment has been done, we've already |
657 |
|
// propagated all the children (in back-propagation). |
658 |
|
} |
659 |
5247 |
break; |
660 |
109 |
case kind::XOR: |
661 |
109 |
if (isAssigned(parent)) |
662 |
|
{ |
663 |
35 |
if (child == parent[0]) |
664 |
|
{ |
665 |
|
// XOR (x=v) y [with XOR assigned], assign(y = (v ^ XOR) |
666 |
20 |
assignAndEnqueue( |
667 |
|
parent[1], |
668 |
40 |
childAssignment != getAssignment(parent), |
669 |
80 |
prover.xorYFromX( |
670 |
40 |
!getAssignment(parent), childAssignment, parent)); |
671 |
|
} |
672 |
|
else |
673 |
|
{ |
674 |
15 |
Assert(child == parent[1]); |
675 |
|
// XOR x (y=v) [with XOR assigned], assign(x = (v ^ XOR)) |
676 |
15 |
assignAndEnqueue( |
677 |
|
parent[0], |
678 |
30 |
childAssignment != getAssignment(parent), |
679 |
60 |
prover.xorXFromY( |
680 |
30 |
!getAssignment(parent), childAssignment, parent)); |
681 |
|
} |
682 |
|
} |
683 |
109 |
if (isAssigned(parent[0]) && isAssigned(parent[1])) |
684 |
|
{ |
685 |
37 |
assignAndEnqueue(parent, |
686 |
74 |
getAssignment(parent[0]) != getAssignment(parent[1]), |
687 |
74 |
prover.xorEval(getAssignment(parent[0]), |
688 |
74 |
getAssignment(parent[1]))); |
689 |
|
} |
690 |
109 |
break; |
691 |
|
default: Unhandled(); |
692 |
|
} |
693 |
|
} |
694 |
687358 |
} |
695 |
|
|
696 |
10275 |
TrustNode CircuitPropagator::propagate() |
697 |
|
{ |
698 |
10275 |
Debug("circuit-prop") << "CircuitPropagator::propagate()" << std::endl; |
699 |
|
|
700 |
697633 |
for (unsigned i = 0; |
701 |
697633 |
i < d_propagationQueue.size() && d_conflict.get().isNull(); |
702 |
|
++i) |
703 |
|
{ |
704 |
|
// The current node we are propagating |
705 |
1374716 |
TNode current = d_propagationQueue[i]; |
706 |
1374716 |
Debug("circuit-prop") << "CircuitPropagator::propagate(): processing " |
707 |
687358 |
<< current << std::endl; |
708 |
687358 |
bool assignment = getAssignment(current); |
709 |
1374716 |
Debug("circuit-prop") << "CircuitPropagator::propagate(): assigned to " |
710 |
687358 |
<< (assignment ? "true" : "false") << std::endl; |
711 |
|
|
712 |
|
// Is this an atom |
713 |
2671626 |
bool atom = Theory::theoryOf(current) != THEORY_BOOL || current.isVar() |
714 |
2749576 |
|| (current.getKind() == kind::EQUAL |
715 |
689852 |
&& (current[0].isVar() && current[1].isVar())); |
716 |
|
|
717 |
|
// If an atom, add to the list for simplification |
718 |
687358 |
if (atom |
719 |
802340 |
|| (current.getKind() == kind::EQUAL |
720 |
689708 |
&& (current[0].isVar() || current[1].isVar()))) |
721 |
|
{ |
722 |
229964 |
Debug("circuit-prop") |
723 |
114982 |
<< "CircuitPropagator::propagate(): adding to learned: " |
724 |
114982 |
<< (assignment ? (Node)current : current.notNode()) << std::endl; |
725 |
229964 |
Node lit = assignment ? Node(current) : current.notNode(); |
726 |
|
|
727 |
114982 |
if (isProofEnabled()) |
728 |
|
{ |
729 |
29259 |
if (d_epg->hasProofFor(lit)) |
730 |
|
{ |
731 |
|
// if we have a parent proof generator that provides proofs of the |
732 |
|
// inputs to this class, we must use the lazy proof chain |
733 |
29259 |
ProofGenerator* pg = d_proofInternal.get(); |
734 |
29259 |
if (d_proofExternal != nullptr) |
735 |
|
{ |
736 |
29259 |
d_proofExternal->addLazyStep(lit, pg); |
737 |
29259 |
pg = d_proofExternal.get(); |
738 |
|
} |
739 |
58518 |
TrustNode tlit = TrustNode::mkTrustLemma(lit, pg); |
740 |
29259 |
d_learnedLiterals.push_back(tlit); |
741 |
|
} |
742 |
|
else |
743 |
|
{ |
744 |
|
Warning() << "CircuitPropagator: Proof is missing for " << lit |
745 |
|
<< std::endl; |
746 |
|
TrustNode tlit = TrustNode::mkTrustLemma(lit, nullptr); |
747 |
|
d_learnedLiterals.push_back(tlit); |
748 |
|
} |
749 |
|
} |
750 |
|
else |
751 |
|
{ |
752 |
171446 |
TrustNode tlit = TrustNode::mkTrustLemma(lit, nullptr); |
753 |
85723 |
d_learnedLiterals.push_back(tlit); |
754 |
|
} |
755 |
114982 |
Trace("circuit-prop") << "Added proof for " << lit << std::endl; |
756 |
|
} |
757 |
|
|
758 |
|
// Propagate this value to the children (if not an atom or a constant) |
759 |
687358 |
if (d_backwardPropagation && !atom && !current.isConst()) |
760 |
|
{ |
761 |
564174 |
propagateBackward(current, assignment); |
762 |
|
} |
763 |
|
// Propagate this value to the parents |
764 |
687358 |
if (d_forwardPropagation) |
765 |
|
{ |
766 |
687358 |
propagateForward(current, assignment); |
767 |
|
} |
768 |
|
} |
769 |
|
|
770 |
|
// No conflict |
771 |
10275 |
return d_conflict; |
772 |
|
} |
773 |
|
|
774 |
3117 |
void CircuitPropagator::setProof(ProofNodeManager* pnm, |
775 |
|
context::Context* ctx, |
776 |
|
ProofGenerator* defParent) |
777 |
|
{ |
778 |
3117 |
d_pnm = pnm; |
779 |
3117 |
d_epg.reset(new EagerProofGenerator(pnm, ctx)); |
780 |
6234 |
d_proofInternal.reset( |
781 |
3117 |
new LazyCDProofChain(pnm, true, ctx, d_epg.get(), true)); |
782 |
3117 |
if (defParent != nullptr) |
783 |
|
{ |
784 |
|
// If we provide a parent proof generator (defParent), we want the ASSUME |
785 |
|
// leafs of proofs provided by this class to call the getProofFor method on |
786 |
|
// the parent. To do this, we use a LazyCDProofChain. |
787 |
6234 |
d_proofExternal.reset( |
788 |
3117 |
new LazyCDProofChain(pnm, true, ctx, defParent, false)); |
789 |
|
} |
790 |
3117 |
} |
791 |
|
|
792 |
2382902 |
bool CircuitPropagator::isProofEnabled() const |
793 |
|
{ |
794 |
2382902 |
return d_proofInternal != nullptr; |
795 |
|
} |
796 |
|
|
797 |
592762 |
void CircuitPropagator::addProof(TNode f, std::shared_ptr<ProofNode> pf) |
798 |
|
{ |
799 |
592762 |
if (isProofEnabled()) |
800 |
|
{ |
801 |
127027 |
if (!d_epg->hasProofFor(f)) |
802 |
|
{ |
803 |
113626 |
Trace("circuit-prop") << "Adding proof for " << f << std::endl |
804 |
56813 |
<< "\t" << *pf << std::endl; |
805 |
56813 |
d_epg->setProofFor(f, std::move(pf)); |
806 |
|
} |
807 |
|
else |
808 |
|
{ |
809 |
140428 |
auto prf = d_epg->getProofFor(f); |
810 |
140428 |
Trace("circuit-prop") << "Ignoring proof, we already have" << std::endl |
811 |
70214 |
<< "\t" << *prf << std::endl; |
812 |
|
} |
813 |
|
} |
814 |
592762 |
} |
815 |
|
|
816 |
|
} // namespace booleans |
817 |
|
} // namespace theory |
818 |
27735 |
} // namespace cvc5 |