1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Clark Barrett, Andres Noetzli, Andrew Reynolds |
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 |
|
* Simplifications based on unconstrained variables. |
14 |
|
* |
15 |
|
* This module implements a preprocessing phase which replaces certain |
16 |
|
* "unconstrained" expressions by variables. Based on Roberto |
17 |
|
* Bruttomesso's PhD thesis. |
18 |
|
*/ |
19 |
|
|
20 |
|
#include "preprocessing/passes/unconstrained_simplifier.h" |
21 |
|
|
22 |
|
#include "expr/dtype.h" |
23 |
|
#include "expr/skolem_manager.h" |
24 |
|
#include "preprocessing/assertion_pipeline.h" |
25 |
|
#include "preprocessing/preprocessing_pass_context.h" |
26 |
|
#include "smt/logic_exception.h" |
27 |
|
#include "smt/smt_statistics_registry.h" |
28 |
|
#include "theory/logic_info.h" |
29 |
|
#include "theory/rewriter.h" |
30 |
|
|
31 |
|
namespace cvc5 { |
32 |
|
namespace preprocessing { |
33 |
|
namespace passes { |
34 |
|
|
35 |
|
using namespace std; |
36 |
|
using namespace cvc5::theory; |
37 |
|
|
38 |
8954 |
UnconstrainedSimplifier::UnconstrainedSimplifier( |
39 |
8954 |
PreprocessingPassContext* preprocContext) |
40 |
|
: PreprocessingPass(preprocContext, "unconstrained-simplifier"), |
41 |
8954 |
d_numUnconstrainedElim(smtStatisticsRegistry().registerInt( |
42 |
17908 |
"preprocessor::number of unconstrained elims")), |
43 |
8954 |
d_context(preprocContext->getDecisionContext()), |
44 |
35816 |
d_substitutions(preprocContext->getDecisionContext()) |
45 |
|
{ |
46 |
8954 |
} |
47 |
|
|
48 |
448793 |
struct unc_preprocess_stack_element |
49 |
|
{ |
50 |
|
TNode node; |
51 |
|
TNode parent; |
52 |
1242 |
unc_preprocess_stack_element(TNode n) : node(n) {} |
53 |
129245 |
unc_preprocess_stack_element(TNode n, TNode p) : node(n), parent(p) {} |
54 |
|
}; /* struct unc_preprocess_stack_element */ |
55 |
|
|
56 |
1242 |
void UnconstrainedSimplifier::visitAll(TNode assertion) |
57 |
|
{ |
58 |
|
// Do a topological sort of the subexpressions and substitute them |
59 |
2484 |
vector<unc_preprocess_stack_element> toVisit; |
60 |
1242 |
toVisit.push_back(assertion); |
61 |
|
|
62 |
262216 |
while (!toVisit.empty()) |
63 |
|
{ |
64 |
|
// The current node we are processing |
65 |
172461 |
TNode current = toVisit.back().node; |
66 |
172461 |
TNode parent = toVisit.back().parent; |
67 |
130487 |
toVisit.pop_back(); |
68 |
|
|
69 |
130487 |
TNodeCountMap::iterator find = d_visited.find(current); |
70 |
130487 |
if (find != d_visited.end()) |
71 |
|
{ |
72 |
88513 |
if (find->second == 1) |
73 |
|
{ |
74 |
11613 |
d_visitedOnce.erase(current); |
75 |
11613 |
if (current.isVar()) |
76 |
|
{ |
77 |
3170 |
d_unconstrained.erase(current); |
78 |
|
} |
79 |
|
else |
80 |
|
{ |
81 |
|
// Also erase the children from the visited-once set when we visit a |
82 |
|
// node a second time, otherwise variables in this node are not |
83 |
|
// erased from the set of unconstrained variables. |
84 |
20223 |
for (TNode childNode : current) |
85 |
|
{ |
86 |
11780 |
toVisit.push_back(unc_preprocess_stack_element(childNode, current)); |
87 |
|
} |
88 |
|
} |
89 |
|
} |
90 |
88513 |
++find->second; |
91 |
88513 |
continue; |
92 |
|
} |
93 |
|
|
94 |
41974 |
d_visited[current] = 1; |
95 |
41974 |
d_visitedOnce[current] = parent; |
96 |
|
|
97 |
41974 |
if (current.getNumChildren() == 0) |
98 |
|
{ |
99 |
14962 |
if (current.getKind() == kind::VARIABLE |
100 |
7481 |
|| current.getKind() == kind::SKOLEM) |
101 |
|
{ |
102 |
4386 |
d_unconstrained.insert(current); |
103 |
|
} |
104 |
|
} |
105 |
34493 |
else if (current.isClosure()) |
106 |
|
{ |
107 |
|
// Throw an exception. This should never happen in practice unless the |
108 |
|
// user specifically enabled unconstrained simplification in an illegal |
109 |
|
// logic. |
110 |
|
throw LogicException( |
111 |
|
"Cannot use unconstrained simplification in this logic, due to " |
112 |
|
"(possibly internally introduced) quantified formula."); |
113 |
|
} |
114 |
|
else |
115 |
|
{ |
116 |
151958 |
for (TNode childNode : current) |
117 |
|
{ |
118 |
117465 |
toVisit.push_back(unc_preprocess_stack_element(childNode, current)); |
119 |
|
} |
120 |
|
} |
121 |
|
} |
122 |
1242 |
} |
123 |
|
|
124 |
265 |
Node UnconstrainedSimplifier::newUnconstrainedVar(TypeNode t, TNode var) |
125 |
|
{ |
126 |
265 |
SkolemManager* sm = NodeManager::currentNM()->getSkolemManager(); |
127 |
|
Node n = sm->mkDummySkolem( |
128 |
|
"unconstrained", |
129 |
|
t, |
130 |
|
"a new var introduced because of unconstrained variable " |
131 |
265 |
+ var.toString()); |
132 |
265 |
return n; |
133 |
|
} |
134 |
|
|
135 |
167 |
void UnconstrainedSimplifier::processUnconstrained() |
136 |
|
{ |
137 |
167 |
NodeManager* nm = NodeManager::currentNM(); |
138 |
|
|
139 |
334 |
vector<TNode> workList(d_unconstrained.begin(), d_unconstrained.end()); |
140 |
334 |
Node currentSub; |
141 |
334 |
TNode parent; |
142 |
|
bool swap; |
143 |
|
bool isSigned; |
144 |
|
bool strict; |
145 |
334 |
vector<TNode> delayQueueLeft; |
146 |
334 |
vector<Node> delayQueueRight; |
147 |
|
|
148 |
334 |
TNode current = workList.back(); |
149 |
167 |
workList.pop_back(); |
150 |
|
for (;;) |
151 |
|
{ |
152 |
1661 |
Assert(d_visitedOnce.find(current) != d_visitedOnce.end()); |
153 |
1661 |
parent = d_visitedOnce[current]; |
154 |
1661 |
if (!parent.isNull()) |
155 |
|
{ |
156 |
1587 |
swap = isSigned = strict = false; |
157 |
1587 |
bool checkParent = false; |
158 |
1587 |
switch (parent.getKind()) |
159 |
|
{ |
160 |
|
// If-then-else operator - any two unconstrained children makes the |
161 |
|
// parent unconstrained |
162 |
159 |
case kind::ITE: |
163 |
|
{ |
164 |
159 |
Assert(parent[0] == current || parent[1] == current |
165 |
|
|| parent[2] == current); |
166 |
|
bool uCond = |
167 |
477 |
parent[0] == current |
168 |
477 |
|| d_unconstrained.find(parent[0]) != d_unconstrained.end(); |
169 |
|
bool uThen = |
170 |
477 |
parent[1] == current |
171 |
477 |
|| d_unconstrained.find(parent[1]) != d_unconstrained.end(); |
172 |
|
bool uElse = |
173 |
477 |
parent[2] == current |
174 |
477 |
|| d_unconstrained.find(parent[2]) != d_unconstrained.end(); |
175 |
159 |
if ((uCond && uThen) || (uCond && uElse) || (uThen && uElse)) |
176 |
|
{ |
177 |
249 |
if (d_unconstrained.find(parent) == d_unconstrained.end() |
178 |
166 |
&& !d_substitutions.hasSubstitution(parent)) |
179 |
|
{ |
180 |
57 |
++d_numUnconstrainedElim; |
181 |
57 |
if (uThen) |
182 |
|
{ |
183 |
40 |
if (parent[1] != current) |
184 |
|
{ |
185 |
30 |
if (parent[1].isVar()) |
186 |
|
{ |
187 |
30 |
currentSub = parent[1]; |
188 |
|
} |
189 |
|
else |
190 |
|
{ |
191 |
|
Assert(d_substitutions.hasSubstitution(parent[1])); |
192 |
|
currentSub = d_substitutions.apply(parent[1]); |
193 |
|
} |
194 |
|
} |
195 |
10 |
else if (currentSub.isNull()) |
196 |
|
{ |
197 |
8 |
currentSub = current; |
198 |
|
} |
199 |
|
} |
200 |
17 |
else if (parent[2] != current) |
201 |
|
{ |
202 |
9 |
if (parent[2].isVar()) |
203 |
|
{ |
204 |
9 |
currentSub = parent[2]; |
205 |
|
} |
206 |
|
else |
207 |
|
{ |
208 |
|
Assert(d_substitutions.hasSubstitution(parent[2])); |
209 |
|
currentSub = d_substitutions.apply(parent[2]); |
210 |
|
} |
211 |
|
} |
212 |
8 |
else if (currentSub.isNull()) |
213 |
|
{ |
214 |
8 |
currentSub = current; |
215 |
|
} |
216 |
57 |
current = parent; |
217 |
|
} |
218 |
|
else |
219 |
|
{ |
220 |
26 |
currentSub = Node(); |
221 |
|
} |
222 |
|
} |
223 |
76 |
else if (uCond) |
224 |
|
{ |
225 |
50 |
Cardinality card = parent.getType().getCardinality(); |
226 |
75 |
if (card.isFinite() && !card.isLargeFinite() |
227 |
66 |
&& card.getFiniteCardinality() == 2) |
228 |
|
{ |
229 |
|
// Special case: condition is unconstrained, then and else are |
230 |
|
// different, and total cardinality of the type is 2, then the |
231 |
|
// result is unconstrained |
232 |
8 |
Node test = Rewriter::rewrite(parent[1].eqNode(parent[2])); |
233 |
4 |
if (test == nm->mkConst<bool>(false)) |
234 |
|
{ |
235 |
4 |
++d_numUnconstrainedElim; |
236 |
4 |
if (currentSub.isNull()) |
237 |
|
{ |
238 |
|
currentSub = current; |
239 |
|
} |
240 |
4 |
currentSub = newUnconstrainedVar(parent.getType(), currentSub); |
241 |
4 |
current = parent; |
242 |
|
} |
243 |
|
} |
244 |
|
} |
245 |
159 |
break; |
246 |
|
} |
247 |
|
|
248 |
|
// Comparisons that return a different type - assuming domains are |
249 |
|
// larger than 1, any unconstrained child makes parent unconstrained as |
250 |
|
// well |
251 |
110 |
case kind::EQUAL: |
252 |
110 |
if (parent[0].getType() != parent[1].getType()) |
253 |
|
{ |
254 |
2 |
TNode other = (parent[0] == current) ? parent[1] : parent[0]; |
255 |
2 |
if (current.getType().isSubtypeOf(other.getType())) |
256 |
|
{ |
257 |
2 |
break; |
258 |
|
} |
259 |
|
} |
260 |
108 |
if (parent[0].getType().getCardinality().isOne()) |
261 |
|
{ |
262 |
|
break; |
263 |
|
} |
264 |
108 |
if (parent[0].getType().isDatatype()) |
265 |
|
{ |
266 |
|
TypeNode tn = parent[0].getType(); |
267 |
|
const DType& dt = tn.getDType(); |
268 |
|
if (dt.isRecursiveSingleton(tn)) |
269 |
|
{ |
270 |
|
// domain size may be 1 |
271 |
|
break; |
272 |
|
} |
273 |
|
} |
274 |
108 |
if (parent[0].getType().isBoolean()) |
275 |
|
{ |
276 |
2 |
checkParent = true; |
277 |
2 |
break; |
278 |
|
} |
279 |
|
CVC5_FALLTHROUGH; |
280 |
|
case kind::BITVECTOR_COMP: |
281 |
|
case kind::LT: |
282 |
|
case kind::LEQ: |
283 |
|
case kind::GT: |
284 |
|
case kind::GEQ: |
285 |
|
{ |
286 |
236 |
if (d_unconstrained.find(parent) == d_unconstrained.end() |
287 |
236 |
&& !d_substitutions.hasSubstitution(parent)) |
288 |
|
{ |
289 |
118 |
++d_numUnconstrainedElim; |
290 |
118 |
Assert(parent[0] != parent[1] |
291 |
|
&& (parent[0] == current || parent[1] == current)); |
292 |
118 |
if (currentSub.isNull()) |
293 |
|
{ |
294 |
27 |
currentSub = current; |
295 |
|
} |
296 |
118 |
currentSub = newUnconstrainedVar(parent.getType(), currentSub); |
297 |
118 |
current = parent; |
298 |
|
} |
299 |
|
else |
300 |
|
{ |
301 |
|
currentSub = Node(); |
302 |
|
} |
303 |
118 |
break; |
304 |
|
} |
305 |
|
|
306 |
|
// Unary operators that propagate unconstrainedness |
307 |
86 |
case kind::NOT: |
308 |
|
case kind::BITVECTOR_NOT: |
309 |
|
case kind::BITVECTOR_NEG: |
310 |
|
case kind::UMINUS: |
311 |
86 |
++d_numUnconstrainedElim; |
312 |
86 |
Assert(parent[0] == current); |
313 |
86 |
if (currentSub.isNull()) |
314 |
|
{ |
315 |
4 |
currentSub = current; |
316 |
|
} |
317 |
86 |
current = parent; |
318 |
86 |
break; |
319 |
|
|
320 |
|
// Unary operators that propagate unconstrainedness and return a |
321 |
|
// different type |
322 |
7 |
case kind::BITVECTOR_EXTRACT: |
323 |
7 |
++d_numUnconstrainedElim; |
324 |
7 |
Assert(parent[0] == current); |
325 |
7 |
if (currentSub.isNull()) |
326 |
|
{ |
327 |
5 |
currentSub = current; |
328 |
|
} |
329 |
7 |
currentSub = newUnconstrainedVar(parent.getType(), currentSub); |
330 |
7 |
current = parent; |
331 |
7 |
break; |
332 |
|
|
333 |
|
// Operators returning same type requiring all children to be |
334 |
|
// unconstrained |
335 |
66 |
case kind::AND: |
336 |
|
case kind::OR: |
337 |
|
case kind::IMPLIES: |
338 |
|
case kind::BITVECTOR_AND: |
339 |
|
case kind::BITVECTOR_OR: |
340 |
|
case kind::BITVECTOR_NAND: |
341 |
|
case kind::BITVECTOR_NOR: |
342 |
|
{ |
343 |
66 |
bool allUnconstrained = true; |
344 |
162 |
for (TNode child : parent) |
345 |
|
{ |
346 |
124 |
if (d_unconstrained.find(child) == d_unconstrained.end()) |
347 |
|
{ |
348 |
28 |
allUnconstrained = false; |
349 |
28 |
break; |
350 |
|
} |
351 |
|
} |
352 |
66 |
if (allUnconstrained) |
353 |
|
{ |
354 |
38 |
checkParent = true; |
355 |
|
} |
356 |
|
} |
357 |
66 |
break; |
358 |
|
|
359 |
|
// Require all children to be unconstrained and different |
360 |
54 |
case kind::BITVECTOR_SHL: |
361 |
|
case kind::BITVECTOR_LSHR: |
362 |
|
case kind::BITVECTOR_ASHR: |
363 |
|
case kind::BITVECTOR_UDIV: |
364 |
|
case kind::BITVECTOR_UREM: |
365 |
|
case kind::BITVECTOR_SDIV: |
366 |
|
case kind::BITVECTOR_SREM: |
367 |
|
case kind::BITVECTOR_SMOD: |
368 |
|
{ |
369 |
54 |
bool allUnconstrained = true; |
370 |
54 |
bool allDifferent = true; |
371 |
138 |
for (TNode::iterator child_it = parent.begin(); |
372 |
138 |
child_it != parent.end(); |
373 |
|
++child_it) |
374 |
|
{ |
375 |
96 |
if (d_unconstrained.find(*child_it) == d_unconstrained.end()) |
376 |
|
{ |
377 |
12 |
allUnconstrained = false; |
378 |
12 |
break; |
379 |
|
} |
380 |
126 |
for (TNode::iterator child_it2 = child_it + 1; |
381 |
126 |
child_it2 != parent.end(); |
382 |
|
++child_it2) |
383 |
|
{ |
384 |
42 |
if (*child_it == *child_it2) |
385 |
|
{ |
386 |
|
allDifferent = false; |
387 |
|
break; |
388 |
|
} |
389 |
|
} |
390 |
|
} |
391 |
54 |
if (allUnconstrained && allDifferent) |
392 |
|
{ |
393 |
42 |
checkParent = true; |
394 |
|
} |
395 |
54 |
break; |
396 |
|
} |
397 |
|
|
398 |
|
// Requires all children to be unconstrained and different, and returns |
399 |
|
// a different type |
400 |
22 |
case kind::BITVECTOR_CONCAT: |
401 |
|
{ |
402 |
22 |
bool allUnconstrained = true; |
403 |
22 |
bool allDifferent = true; |
404 |
35 |
for (TNode::iterator child_it = parent.begin(); |
405 |
35 |
child_it != parent.end(); |
406 |
|
++child_it) |
407 |
|
{ |
408 |
33 |
if (d_unconstrained.find(*child_it) == d_unconstrained.end()) |
409 |
|
{ |
410 |
20 |
allUnconstrained = false; |
411 |
20 |
break; |
412 |
|
} |
413 |
24 |
for (TNode::iterator child_it2 = child_it + 1; |
414 |
24 |
child_it2 != parent.end(); |
415 |
|
++child_it2) |
416 |
|
{ |
417 |
11 |
if (*child_it == *child_it2) |
418 |
|
{ |
419 |
|
allDifferent = false; |
420 |
|
break; |
421 |
|
} |
422 |
|
} |
423 |
|
} |
424 |
22 |
if (allUnconstrained && allDifferent) |
425 |
|
{ |
426 |
4 |
if (d_unconstrained.find(parent) == d_unconstrained.end() |
427 |
4 |
&& !d_substitutions.hasSubstitution(parent)) |
428 |
|
{ |
429 |
2 |
++d_numUnconstrainedElim; |
430 |
2 |
if (currentSub.isNull()) |
431 |
|
{ |
432 |
|
currentSub = current; |
433 |
|
} |
434 |
2 |
currentSub = newUnconstrainedVar(parent.getType(), currentSub); |
435 |
2 |
current = parent; |
436 |
|
} |
437 |
|
else |
438 |
|
{ |
439 |
|
currentSub = Node(); |
440 |
|
} |
441 |
|
} |
442 |
|
} |
443 |
22 |
break; |
444 |
|
|
445 |
|
// N-ary operators returning same type requiring at least one child to |
446 |
|
// be unconstrained |
447 |
4 |
case kind::PLUS: |
448 |
|
case kind::MINUS: |
449 |
4 |
if (current.getType().isInteger() && !parent.getType().isInteger()) |
450 |
|
{ |
451 |
|
break; |
452 |
|
} |
453 |
|
CVC5_FALLTHROUGH; |
454 |
|
case kind::XOR: |
455 |
|
case kind::BITVECTOR_XOR: |
456 |
|
case kind::BITVECTOR_XNOR: |
457 |
|
case kind::BITVECTOR_ADD: |
458 |
14 |
case kind::BITVECTOR_SUB: checkParent = true; break; |
459 |
|
|
460 |
|
// Multiplication/division: must be non-integer and other operand must |
461 |
|
// be non-zero |
462 |
8 |
case kind::MULT: |
463 |
|
case kind::DIVISION: |
464 |
|
{ |
465 |
8 |
Assert(parent.getNumChildren() == 2); |
466 |
16 |
TNode other; |
467 |
8 |
if (parent[0] == current) |
468 |
|
{ |
469 |
|
other = parent[1]; |
470 |
|
} |
471 |
|
else |
472 |
|
{ |
473 |
8 |
Assert(parent[1] == current); |
474 |
8 |
other = parent[0]; |
475 |
|
} |
476 |
8 |
if (d_unconstrained.find(other) != d_unconstrained.end()) |
477 |
|
{ |
478 |
|
if (d_unconstrained.find(parent) == d_unconstrained.end() |
479 |
|
&& !d_substitutions.hasSubstitution(parent)) |
480 |
|
{ |
481 |
|
if (current.getType().isInteger() && other.getType().isInteger()) |
482 |
|
{ |
483 |
|
Assert(parent.getKind() == kind::DIVISION |
484 |
|
|| parent.getType().isInteger()); |
485 |
|
if (parent.getKind() == kind::DIVISION) |
486 |
|
{ |
487 |
|
break; |
488 |
|
} |
489 |
|
} |
490 |
|
++d_numUnconstrainedElim; |
491 |
|
if (currentSub.isNull()) |
492 |
|
{ |
493 |
|
currentSub = current; |
494 |
|
} |
495 |
|
current = parent; |
496 |
|
} |
497 |
|
else |
498 |
|
{ |
499 |
|
currentSub = Node(); |
500 |
|
} |
501 |
|
} |
502 |
|
else |
503 |
|
{ |
504 |
|
// if only the denominator of a division is unconstrained, can't |
505 |
|
// set it to 0 so the result is not unconstrained |
506 |
8 |
if (parent.getKind() == kind::DIVISION && current == parent[1]) |
507 |
|
{ |
508 |
|
break; |
509 |
|
} |
510 |
|
// if we are an integer, the only way we are unconstrained is if |
511 |
|
// we are a MULT by -1 |
512 |
8 |
if (current.getType().isInteger()) |
513 |
|
{ |
514 |
|
// div/mult by 1 should have been simplified |
515 |
4 |
Assert(other != nm->mkConst<Rational>(1)); |
516 |
|
// div by -1 should have been simplified |
517 |
4 |
if (other != nm->mkConst<Rational>(-1)) |
518 |
|
{ |
519 |
2 |
break; |
520 |
|
} |
521 |
|
else |
522 |
|
{ |
523 |
2 |
Assert(parent.getKind() == kind::MULT); |
524 |
2 |
Assert(parent.getType().isInteger()); |
525 |
|
} |
526 |
|
} |
527 |
|
else |
528 |
|
{ |
529 |
|
// TODO(#2377): could build ITE here |
530 |
8 |
Node test = other.eqNode(nm->mkConst<Rational>(0)); |
531 |
4 |
if (Rewriter::rewrite(test) != nm->mkConst<bool>(false)) |
532 |
|
{ |
533 |
|
break; |
534 |
|
} |
535 |
|
} |
536 |
6 |
++d_numUnconstrainedElim; |
537 |
6 |
if (currentSub.isNull()) |
538 |
|
{ |
539 |
6 |
currentSub = current; |
540 |
|
} |
541 |
6 |
current = parent; |
542 |
|
} |
543 |
6 |
break; |
544 |
|
} |
545 |
|
|
546 |
|
// Bitvector MULT - current must only appear once in the children: |
547 |
|
// all other children must be unconstrained or odd |
548 |
870 |
case kind::BITVECTOR_MULT: |
549 |
|
{ |
550 |
870 |
bool found = false; |
551 |
870 |
bool done = false; |
552 |
|
|
553 |
876 |
for (TNode child : parent) |
554 |
|
{ |
555 |
2286 |
if (child == current) |
556 |
|
{ |
557 |
708 |
if (found) |
558 |
|
{ |
559 |
|
done = true; |
560 |
|
break; |
561 |
|
} |
562 |
708 |
found = true; |
563 |
708 |
continue; |
564 |
|
} |
565 |
870 |
else if (d_unconstrained.find(child) == d_unconstrained.end()) |
566 |
|
{ |
567 |
|
Node extractOp = |
568 |
868 |
nm->mkConst<BitVectorExtract>(BitVectorExtract(0, 0)); |
569 |
868 |
vector<Node> children; |
570 |
866 |
children.push_back(child); |
571 |
868 |
Node test = nm->mkNode(extractOp, children); |
572 |
868 |
BitVector one(1, unsigned(1)); |
573 |
866 |
test = test.eqNode(nm->mkConst<BitVector>(one)); |
574 |
866 |
if (Rewriter::rewrite(test) != nm->mkConst<bool>(true)) |
575 |
|
{ |
576 |
864 |
done = true; |
577 |
864 |
break; |
578 |
|
} |
579 |
|
} |
580 |
|
} |
581 |
870 |
if (done) |
582 |
|
{ |
583 |
864 |
break; |
584 |
|
} |
585 |
6 |
checkParent = true; |
586 |
6 |
break; |
587 |
|
} |
588 |
|
|
589 |
|
// Uninterpreted function - if domain is infinite, no quantifiers are |
590 |
|
// used, and any child is unconstrained, result is unconstrained |
591 |
62 |
case kind::APPLY_UF: |
592 |
124 |
if (d_preprocContext->getLogicInfo().isQuantified() |
593 |
124 |
|| !current.getType().getCardinality().isInfinite()) |
594 |
|
{ |
595 |
|
break; |
596 |
|
} |
597 |
124 |
if (d_unconstrained.find(parent) == d_unconstrained.end() |
598 |
124 |
&& !d_substitutions.hasSubstitution(parent)) |
599 |
|
{ |
600 |
62 |
++d_numUnconstrainedElim; |
601 |
62 |
if (currentSub.isNull()) |
602 |
|
{ |
603 |
|
currentSub = current; |
604 |
|
} |
605 |
|
// always introduce a new variable; it is unsound to try to reuse |
606 |
|
// currentSub as the variable, see issue #4469. |
607 |
62 |
currentSub = newUnconstrainedVar(parent.getType(), currentSub); |
608 |
62 |
current = parent; |
609 |
|
} |
610 |
|
else |
611 |
|
{ |
612 |
|
currentSub = Node(); |
613 |
|
} |
614 |
62 |
break; |
615 |
|
|
616 |
|
// Array select - if array is unconstrained, so is result |
617 |
24 |
case kind::SELECT: |
618 |
24 |
if (parent[0] == current) |
619 |
|
{ |
620 |
20 |
++d_numUnconstrainedElim; |
621 |
20 |
Assert(current.getType().isArray()); |
622 |
20 |
if (currentSub.isNull()) |
623 |
|
{ |
624 |
20 |
currentSub = current; |
625 |
|
} |
626 |
60 |
currentSub = newUnconstrainedVar( |
627 |
40 |
current.getType().getArrayConstituentType(), currentSub); |
628 |
20 |
current = parent; |
629 |
|
} |
630 |
24 |
break; |
631 |
|
|
632 |
|
// Array store - if both store and value are unconstrained, so is |
633 |
|
// resulting store |
634 |
12 |
case kind::STORE: |
635 |
36 |
if (((parent[0] == current |
636 |
24 |
&& d_unconstrained.find(parent[2]) != d_unconstrained.end()) |
637 |
60 |
|| (parent[2] == current |
638 |
12 |
&& d_unconstrained.find(parent[0]) |
639 |
12 |
!= d_unconstrained.end()))) |
640 |
|
{ |
641 |
|
if (d_unconstrained.find(parent) == d_unconstrained.end() |
642 |
|
&& !d_substitutions.hasSubstitution(parent)) |
643 |
|
{ |
644 |
|
++d_numUnconstrainedElim; |
645 |
|
if (parent[0] != current) |
646 |
|
{ |
647 |
|
if (parent[0].isVar()) |
648 |
|
{ |
649 |
|
currentSub = parent[0]; |
650 |
|
} |
651 |
|
else |
652 |
|
{ |
653 |
|
Assert(d_substitutions.hasSubstitution(parent[0])); |
654 |
|
currentSub = d_substitutions.apply(parent[0]); |
655 |
|
} |
656 |
|
} |
657 |
|
else if (currentSub.isNull()) |
658 |
|
{ |
659 |
|
currentSub = current; |
660 |
|
} |
661 |
|
current = parent; |
662 |
|
} |
663 |
|
else |
664 |
|
{ |
665 |
|
currentSub = Node(); |
666 |
|
} |
667 |
|
} |
668 |
12 |
break; |
669 |
|
|
670 |
|
// Bit-vector comparisons: replace with new Boolean variable, but have |
671 |
|
// to also conjoin with a side condition as there is always one case |
672 |
|
// when the comparison is forced to be false |
673 |
60 |
case kind::BITVECTOR_ULT: |
674 |
|
case kind::BITVECTOR_UGE: |
675 |
|
case kind::BITVECTOR_UGT: |
676 |
|
case kind::BITVECTOR_ULE: |
677 |
|
case kind::BITVECTOR_SLT: |
678 |
|
case kind::BITVECTOR_SGE: |
679 |
|
case kind::BITVECTOR_SGT: |
680 |
|
case kind::BITVECTOR_SLE: |
681 |
|
{ |
682 |
|
// Tuples over (signed, swap, strict). |
683 |
60 |
switch (parent.getKind()) |
684 |
|
{ |
685 |
|
case kind::BITVECTOR_UGE: break; |
686 |
30 |
case kind::BITVECTOR_ULT: strict = true; break; |
687 |
|
case kind::BITVECTOR_ULE: swap = true; break; |
688 |
|
case kind::BITVECTOR_UGT: |
689 |
|
swap = true; |
690 |
|
strict = true; |
691 |
|
break; |
692 |
|
case kind::BITVECTOR_SGE: isSigned = true; break; |
693 |
30 |
case kind::BITVECTOR_SLT: |
694 |
30 |
isSigned = true; |
695 |
30 |
strict = true; |
696 |
30 |
break; |
697 |
|
case kind::BITVECTOR_SLE: |
698 |
|
isSigned = true; |
699 |
|
swap = true; |
700 |
|
break; |
701 |
|
case kind::BITVECTOR_SGT: |
702 |
|
isSigned = true; |
703 |
|
swap = true; |
704 |
|
strict = true; |
705 |
|
break; |
706 |
|
default: Unreachable(); |
707 |
|
} |
708 |
120 |
TNode other; |
709 |
60 |
bool left = false; |
710 |
60 |
if (parent[0] == current) |
711 |
|
{ |
712 |
29 |
other = parent[1]; |
713 |
29 |
left = true; |
714 |
|
} |
715 |
|
else |
716 |
|
{ |
717 |
31 |
Assert(parent[1] == current); |
718 |
31 |
other = parent[0]; |
719 |
|
} |
720 |
60 |
if (d_unconstrained.find(other) != d_unconstrained.end()) |
721 |
|
{ |
722 |
34 |
if (d_unconstrained.find(parent) == d_unconstrained.end() |
723 |
34 |
&& !d_substitutions.hasSubstitution(parent)) |
724 |
|
{ |
725 |
9 |
++d_numUnconstrainedElim; |
726 |
9 |
if (currentSub.isNull()) |
727 |
|
{ |
728 |
8 |
currentSub = current; |
729 |
|
} |
730 |
9 |
currentSub = newUnconstrainedVar(parent.getType(), currentSub); |
731 |
9 |
current = parent; |
732 |
|
} |
733 |
|
else |
734 |
|
{ |
735 |
8 |
currentSub = Node(); |
736 |
|
} |
737 |
|
} |
738 |
|
else |
739 |
|
{ |
740 |
43 |
unsigned size = current.getType().getBitVectorSize(); |
741 |
|
BitVector bv = |
742 |
64 |
isSigned ? BitVector(size, Integer(1).multiplyByPow2(size - 1)) |
743 |
103 |
: BitVector(size, unsigned(0)); |
744 |
43 |
if (swap == left) |
745 |
|
{ |
746 |
23 |
bv = ~bv; |
747 |
|
} |
748 |
43 |
if (currentSub.isNull()) |
749 |
|
{ |
750 |
35 |
currentSub = current; |
751 |
|
} |
752 |
43 |
currentSub = newUnconstrainedVar(parent.getType(), currentSub); |
753 |
43 |
current = parent; |
754 |
|
Node test = |
755 |
82 |
Rewriter::rewrite(other.eqNode(nm->mkConst<BitVector>(bv))); |
756 |
43 |
if (test == nm->mkConst<bool>(false)) |
757 |
|
{ |
758 |
4 |
break; |
759 |
|
} |
760 |
39 |
currentSub = strict ? currentSub.andNode(test.notNode()) |
761 |
|
: currentSub.orNode(test); |
762 |
|
// Delay adding this substitution - see comment at end of function |
763 |
39 |
delayQueueLeft.push_back(current); |
764 |
39 |
delayQueueRight.push_back(currentSub); |
765 |
39 |
currentSub = Node(); |
766 |
39 |
parent = TNode(); |
767 |
|
} |
768 |
56 |
break; |
769 |
|
} |
770 |
|
|
771 |
|
// Do nothing |
772 |
21 |
case kind::BITVECTOR_SIGN_EXTEND: |
773 |
|
case kind::BITVECTOR_ZERO_EXTEND: |
774 |
|
case kind::BITVECTOR_REPEAT: |
775 |
|
case kind::BITVECTOR_ROTATE_LEFT: |
776 |
|
case kind::BITVECTOR_ROTATE_RIGHT: |
777 |
|
|
778 |
21 |
default: break; |
779 |
|
} |
780 |
1587 |
if (checkParent) |
781 |
|
{ |
782 |
|
// run for various cases from above |
783 |
204 |
if (d_unconstrained.find(parent) == d_unconstrained.end() |
784 |
204 |
&& !d_substitutions.hasSubstitution(parent)) |
785 |
|
{ |
786 |
70 |
++d_numUnconstrainedElim; |
787 |
70 |
if (currentSub.isNull()) |
788 |
|
{ |
789 |
30 |
currentSub = current; |
790 |
|
} |
791 |
70 |
current = parent; |
792 |
|
} |
793 |
|
else |
794 |
|
{ |
795 |
32 |
currentSub = Node(); |
796 |
|
} |
797 |
|
} |
798 |
2032 |
if (current == parent && d_visited[parent] == 1) |
799 |
|
{ |
800 |
445 |
d_unconstrained.insert(parent); |
801 |
445 |
continue; |
802 |
|
} |
803 |
|
} |
804 |
1216 |
if (!currentSub.isNull()) |
805 |
|
{ |
806 |
224 |
Trace("unc-simp") |
807 |
112 |
<< "UnconstrainedSimplifier::processUnconstrained: introduce " |
808 |
112 |
<< currentSub << " for " << current << ", parent " << parent |
809 |
112 |
<< std::endl; |
810 |
112 |
Assert(currentSub.isVar()); |
811 |
112 |
d_substitutions.addSubstitution(current, currentSub, false); |
812 |
|
} |
813 |
1216 |
if (workList.empty()) |
814 |
|
{ |
815 |
167 |
break; |
816 |
|
} |
817 |
1049 |
current = workList.back(); |
818 |
1049 |
currentSub = Node(); |
819 |
1049 |
workList.pop_back(); |
820 |
1494 |
} |
821 |
334 |
TNode left; |
822 |
334 |
Node right; |
823 |
|
// All substitutions except those arising from bitvector comparisons are |
824 |
|
// substitutions t -> x where x is a variable. This allows us to build the |
825 |
|
// substitution very quickly (never invalidating the substitution cache). |
826 |
|
// Bitvector comparisons are more complicated and may require |
827 |
|
// back-substitution and cache-invalidation. So we do these last. |
828 |
245 |
while (!delayQueueLeft.empty()) |
829 |
|
{ |
830 |
39 |
left = delayQueueLeft.back(); |
831 |
39 |
if (!d_substitutions.hasSubstitution(left)) |
832 |
|
{ |
833 |
38 |
right = d_substitutions.apply(delayQueueRight.back()); |
834 |
38 |
d_substitutions.addSubstitution(delayQueueLeft.back(), right); |
835 |
|
} |
836 |
39 |
delayQueueLeft.pop_back(); |
837 |
39 |
delayQueueRight.pop_back(); |
838 |
|
} |
839 |
167 |
} |
840 |
|
|
841 |
515 |
PreprocessingPassResult UnconstrainedSimplifier::applyInternal( |
842 |
|
AssertionPipeline* assertionsToPreprocess) |
843 |
|
{ |
844 |
515 |
d_preprocContext->spendResource(Resource::PreprocessStep); |
845 |
|
|
846 |
515 |
const std::vector<Node>& assertions = assertionsToPreprocess->ref(); |
847 |
|
|
848 |
515 |
d_context->push(); |
849 |
|
|
850 |
1757 |
for (const Node& assertion : assertions) |
851 |
|
{ |
852 |
1242 |
visitAll(assertion); |
853 |
|
} |
854 |
|
|
855 |
515 |
if (!d_unconstrained.empty()) |
856 |
|
{ |
857 |
167 |
processUnconstrained(); |
858 |
|
// d_substitutions.print(CVC5Message.getStream()); |
859 |
575 |
for (size_t i = 0, asize = assertions.size(); i < asize; ++i) |
860 |
|
{ |
861 |
816 |
Node a = assertions[i]; |
862 |
816 |
Node as = Rewriter::rewrite(d_substitutions.apply(a)); |
863 |
|
// replace the assertion |
864 |
408 |
assertionsToPreprocess->replace(i, as); |
865 |
|
} |
866 |
|
} |
867 |
|
|
868 |
|
// to clear substitutions map |
869 |
515 |
d_context->pop(); |
870 |
|
|
871 |
515 |
d_visited.clear(); |
872 |
515 |
d_visitedOnce.clear(); |
873 |
515 |
d_unconstrained.clear(); |
874 |
|
|
875 |
515 |
return PreprocessingPassResult::NO_CONFLICT; |
876 |
|
} |
877 |
|
|
878 |
|
|
879 |
|
} // namespace passes |
880 |
|
} // namespace preprocessing |
881 |
27735 |
} // namespace cvc5 |