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