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