1 /********************* */
2 /*! \file symmetry_breaker.cpp
4 ** Top contributors (to current version):
5 ** Morgan Deters, Mathias Preiner, Liana Hadarean
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2020 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
12 ** \brief Implementation of algorithm suggested by Deharbe, Fontaine,
13 ** Merz, and Paleo, "Exploiting symmetry in SMT problems," CADE 2011
15 ** Implementation of algorithm suggested by Deharbe, Fontaine, Merz,
16 ** and Paleo, "Exploiting symmetry in SMT problems," CADE 2011.
21 ** \f$ P := guess\_permutations(\phi) \f$
22 ** foreach \f$ {c_0, ..., c_n} \in P \f$ do
23 ** if \f$ invariant\_by\_permutations(\phi, {c_0, ..., c_n}) \f$ then
24 ** T := \f$ select\_terms(\phi, {c_0, ..., c_n}) \f$
25 ** cts := \f$ \emptyset \f$
26 ** while T != \f$ \empty \wedge |cts| <= n \f$ do
27 ** \f$ t := select\_most\_promising\_term(T, \phi) \f$
28 ** \f$ T := T \setminus {t} \f$
29 ** cts := cts \f$ \cup used\_in(t, {c_0, ..., c_n}) \f$
30 ** let \f$ c \in {c_0, ..., c_n} \setminus cts \f$
31 ** cts := cts \f$ \cup {c} \f$
32 ** if cts != \f$ {c_0, ..., c_n} \f$ then
33 ** \f$ \phi := \phi \wedge ( \vee_{c_i \in cts} t = c_i ) \f$
38 ** return \f$ \phi \f$
42 #include "theory/uf/symmetry_breaker.h"
43 #include "theory/rewriter.h"
44 #include "util/hash.h"
55 using namespace ::CVC4::context
;
57 SymmetryBreaker::Template::Template() :
63 TNode
SymmetryBreaker::Template::find(TNode n
) {
64 unordered_map
<TNode
, TNode
, TNodeHashFunction
>::iterator i
= d_reps
.find(n
);
65 if(i
== d_reps
.end()) {
68 return d_reps
[n
] = find((*i
).second
);
72 bool SymmetryBreaker::Template::matchRecursive(TNode t
, TNode n
) {
73 IndentedScope
scope(Debug("ufsymm:match"));
75 Debug("ufsymm:match") << "UFSYMM matching " << t
<< endl
76 << "UFSYMM to " << n
<< endl
;
78 if(t
.getKind() != n
.getKind() || t
.getNumChildren() != n
.getNumChildren()) {
79 Debug("ufsymm:match") << "UFSYMM BAD MATCH on kind, #children" << endl
;
83 if(t
.getNumChildren() == 0) {
86 Debug("ufsymm:match") << "UFSYMM we have constants, failing match" << endl
;
89 Assert(t
.isVar() && n
.isVar());
92 Debug("ufsymm:match") << "UFSYMM variable match " << t
<< " , " << n
<< endl
;
93 Debug("ufsymm:match") << "UFSYMM sets: " << t
<< " =>";
94 if(d_sets
.find(t
) != d_sets
.end()) {
95 for(set
<TNode
>::iterator i
= d_sets
[t
].begin(); i
!= d_sets
[t
].end(); ++i
) {
96 Debug("ufsymm:match") << " " << *i
;
99 Debug("ufsymm:match") << endl
;
101 Debug("ufsymm:match") << "UFSYMM sets: " << n
<< " =>";
102 if(d_sets
.find(n
) != d_sets
.end()) {
103 for(set
<TNode
>::iterator i
= d_sets
[n
].begin(); i
!= d_sets
[n
].end(); ++i
) {
104 Debug("ufsymm:match") << " " << *i
;
107 Debug("ufsymm:match") << endl
;
109 if(d_sets
.find(t
) == d_sets
.end()) {
110 Debug("ufsymm:match") << "UFSYMM inserting " << t
<< " in with " << n
<< endl
;
114 if(d_sets
.find(n
) != d_sets
.end()) {
115 Debug("ufsymm:match") << "UFSYMM merging " << n
<< " and " << t
<< " in with " << n
<< endl
;
116 d_sets
[n
].insert(d_sets
[t
].begin(), d_sets
[t
].end());
121 Debug("ufsymm:match") << "UFSYMM inserting " << n
<< " in with " << t
<< endl
;
130 if(t
.getMetaKind() == kind::metakind::PARAMETERIZED
) {
131 if(t
.getOperator() != n
.getOperator()) {
132 Debug("ufsymm:match") << "UFSYMM BAD MATCH on operators: " << t
.getOperator() << " != " << n
.getOperator() << endl
;
136 TNode::iterator ti
= t
.begin();
137 TNode::iterator ni
= n
.begin();
138 while(ti
!= t
.end()) {
139 if(*ti
!= *ni
) { // nothing to do if equal
140 if(!matchRecursive(*ti
, *ni
)) {
141 Debug("ufsymm:match") << "UFSYMM BAD MATCH, withdrawing.." << endl
;
152 bool SymmetryBreaker::Template::match(TNode n
) {
153 // try to "match" n and d_template
154 if(d_template
.isNull()) {
155 Debug("ufsymm") << "UFSYMM setting template " << n
<< endl
;
159 return matchRecursive(d_template
, n
);
163 void SymmetryBreaker::Template::reset() {
164 d_template
= Node::null();
169 SymmetryBreaker::SymmetryBreaker(context::Context
* context
,
171 ContextNotifyObj(context
),
172 d_assertionsToRerun(context
),
173 d_rerunningAssertions(false),
179 d_normalizationCache(),
191 SBGuard(bool& b
) : d_ref(b
), d_old(b
) {}
192 ~SBGuard() { Debug("uf") << "reset to " << d_old
<< std::endl
; d_ref
= d_old
; }
193 };/* class SBGuard */
195 void SymmetryBreaker::rerunAssertionsIfNecessary() {
196 if(d_rerunningAssertions
|| !d_phi
.empty() || d_assertionsToRerun
.empty()) {
200 SBGuard
g(d_rerunningAssertions
);
201 d_rerunningAssertions
= true;
203 Debug("ufsymm") << "UFSYMM: rerunning assertions..." << std::endl
;
204 for(CDList
<Node
>::const_iterator i
= d_assertionsToRerun
.begin();
205 i
!= d_assertionsToRerun
.end();
209 Debug("ufsymm") << "UFSYMM: DONE rerunning assertions..." << std::endl
;
212 Node
SymmetryBreaker::norm(TNode phi
) {
213 Node n
= Rewriter::rewrite(phi
);
214 return normInternal(n
, 0);
217 Node
SymmetryBreaker::normInternal(TNode n
, size_t level
) {
218 Node
& result
= d_normalizationCache
[n
];
219 if(!result
.isNull()) {
223 switch(Kind k
= n
.getKind()) {
225 case kind::DISTINCT
: {
226 // commutative N-ary operator handling
227 vector
<TNode
> kids(n
.begin(), n
.end());
228 sort(kids
.begin(), kids
.end());
229 return result
= NodeManager::currentNM()->mkNode(k
, kids
);
233 // commutative+associative N-ary operator handling
235 kids
.reserve(n
.getNumChildren());
238 Debug("ufsymm:norm") << "UFSYMM processing " << n
<< endl
;
240 TNode m
= work
.front();
242 for(TNode::iterator i
= m
.begin(); i
!= m
.end(); ++i
) {
243 if((*i
).getKind() == k
) {
246 if( (*i
).getKind() == kind::OR
) {
247 kids
.push_back(normInternal(*i
, level
));
248 } else if((*i
).getKind() == kind::EQUAL
) {
249 kids
.push_back(normInternal(*i
, level
));
250 if((*i
)[0].isVar() ||
252 d_termEqs
[(*i
)[0]].insert((*i
)[1]);
253 d_termEqs
[(*i
)[1]].insert((*i
)[0]);
255 d_termEqsOnly
[(*i
)[0]].insert((*i
)[1]);
256 d_termEqsOnly
[(*i
)[1]].insert((*i
)[0]);
257 Debug("ufsymm:eq") << "UFSYMM " << (*i
)[0] << " <==> " << (*i
)[1] << endl
;
265 } while(!work
.empty());
266 Debug("ufsymm:norm") << "UFSYMM got " << kids
.size() << " kids for the " << k
<< "-kinded Node" << endl
;
267 sort(kids
.begin(), kids
.end());
268 return result
= NodeManager::currentNM()->mkNode(k
, kids
);
272 // commutative+associative N-ary operator handling
274 kids
.reserve(n
.getNumChildren());
277 Debug("ufsymm:norm") << "UFSYMM processing " << n
<< endl
;
278 TNode matchingTerm
= TNode::null();
279 vector
<TNode
> matchingTermEquals
;
280 bool first
= true, matchedVar
= false;
282 TNode m
= work
.front();
284 for(TNode::iterator i
= m
.begin(); i
!= m
.end(); ++i
) {
285 if((*i
).getKind() == k
) {
288 if( (*i
).getKind() == kind::AND
) {
290 matchingTerm
= TNode::null();
291 kids
.push_back(normInternal(*i
, level
+ 1));
292 } else if((*i
).getKind() == kind::EQUAL
) {
293 kids
.push_back(normInternal(*i
, level
+ 1));
294 if((*i
)[0].isVar() ||
296 d_termEqs
[(*i
)[0]].insert((*i
)[1]);
297 d_termEqs
[(*i
)[1]].insert((*i
)[0]);
301 } else if(!matchingTerm
.isNull()) {
303 if(matchingTerm
== (*i
)[0]) {
304 matchingTermEquals
.push_back((*i
)[1]);
305 } else if(matchingTerm
== (*i
)[1]) {
306 matchingTermEquals
.push_back((*i
)[0]);
308 matchingTerm
= TNode::null();
310 } else if((*i
)[0] == matchingTerm
[0]) {
311 matchingTermEquals
.push_back(matchingTerm
[1]);
312 matchingTermEquals
.push_back((*i
)[1]);
313 matchingTerm
= matchingTerm
[0];
315 } else if((*i
)[1] == matchingTerm
[0]) {
316 matchingTermEquals
.push_back(matchingTerm
[1]);
317 matchingTermEquals
.push_back((*i
)[0]);
318 matchingTerm
= matchingTerm
[0];
320 } else if((*i
)[0] == matchingTerm
[1]) {
321 matchingTermEquals
.push_back(matchingTerm
[0]);
322 matchingTermEquals
.push_back((*i
)[1]);
323 matchingTerm
= matchingTerm
[1];
325 } else if((*i
)[1] == matchingTerm
[1]) {
326 matchingTermEquals
.push_back(matchingTerm
[0]);
327 matchingTermEquals
.push_back((*i
)[0]);
328 matchingTerm
= matchingTerm
[1];
331 matchingTerm
= TNode::null();
336 matchingTerm
= TNode::null();
341 matchingTerm
= TNode::null();
346 } while(!work
.empty());
347 if(!matchingTerm
.isNull()) {
348 if(Debug
.isOn("ufsymm:eq")) {
349 Debug("ufsymm:eq") << "UFSYMM here we can conclude that " << matchingTerm
<< " is one of {";
350 for(vector
<TNode
>::const_iterator i
= matchingTermEquals
.begin(); i
!= matchingTermEquals
.end(); ++i
) {
351 Debug("ufsymm:eq") << " " << *i
;
353 Debug("ufsymm:eq") << " }" << endl
;
355 d_termEqsOnly
[matchingTerm
].insert(matchingTermEquals
.begin(), matchingTermEquals
.end());
357 Debug("ufsymm:norm") << "UFSYMM got " << kids
.size() << " kids for the " << k
<< "-kinded Node" << endl
;
358 sort(kids
.begin(), kids
.end());
359 return result
= NodeManager::currentNM()->mkNode(k
, kids
);
365 d_termEqs
[n
[0]].insert(n
[1]);
366 d_termEqs
[n
[1]].insert(n
[0]);
368 d_termEqsOnly
[n
[0]].insert(n
[1]);
369 d_termEqsOnly
[n
[1]].insert(n
[0]);
370 Debug("ufsymm:eq") << "UFSYMM " << n
[0] << " <==> " << n
[1] << endl
;
375 // commutative binary operator handling
376 return n
[1] < n
[0] ? NodeManager::currentNM()->mkNode(k
, n
[1], n
[0]) : Node(n
);
379 // Normally T-rewriting is enough; only special cases (like
380 // Boolean-layer stuff) has to go above.
385 void SymmetryBreaker::assertFormula(TNode phi
) {
386 rerunAssertionsIfNecessary();
387 if(!d_rerunningAssertions
) {
388 d_assertionsToRerun
.push_back(phi
);
390 // use d_phi, put into d_permutations
391 Debug("ufsymm") << "UFSYMM assertFormula(): phi is " << phi
<< endl
;
392 d_phi
.push_back(phi
);
393 if(phi
.getKind() == kind::OR
) {
395 Node::iterator i
= phi
.begin();
397 while(i
!= phi
.end()) {
402 unordered_map
<TNode
, set
<TNode
>, TNodeHashFunction
>& ps
= t
.partitions();
405 Debug("ufsymm") << "UFSYMM partition*: " << kv
.first
;
406 set
<TNode
>& p
= kv
.second
;
407 for(set
<TNode
>::iterator j
= p
.begin();
410 Debug("ufsymm") << " " << *j
;
412 Debug("ufsymm") << endl
;
414 Permutations::iterator pi
= d_permutations
.find(p
);
415 if(pi
== d_permutations
.end()) {
416 d_permutations
.insert(p
);
420 if(!d_template
.match(phi
)) {
421 // we hit a bad match, extract the partitions and reset the template
422 unordered_map
<TNode
, set
<TNode
>, TNodeHashFunction
>& ps
= d_template
.partitions();
423 Debug("ufsymm") << "UFSYMM hit a bad match---have " << ps
.size() << " partitions:" << endl
;
424 for(unordered_map
<TNode
, set
<TNode
>, TNodeHashFunction
>::iterator i
= ps
.begin();
427 Debug("ufsymm") << "UFSYMM partition: " << (*i
).first
;
428 set
<TNode
>& p
= (*i
).second
;
429 if(Debug
.isOn("ufsymm")) {
430 for(set
<TNode
>::iterator j
= p
.begin();
433 Debug("ufsymm") << " " << *j
;
436 Debug("ufsymm") << endl
;
437 p
.insert((*i
).first
);
438 d_permutations
.insert(p
);
441 bool good CVC4_UNUSED
= d_template
.match(phi
);
446 void SymmetryBreaker::clear() {
449 d_permutations
.clear();
452 d_normalizationCache
.clear();
454 d_termEqsOnly
.clear();
457 void SymmetryBreaker::apply(std::vector
<Node
>& newClauses
) {
458 rerunAssertionsIfNecessary();
460 Debug("ufsymm") << "UFSYMM =====================================================" << endl
461 << "UFSYMM have " << d_permutations
.size() << " permutation sets" << endl
;
462 if(!d_permutations
.empty()) {
463 { TimerStat::CodeTimer
codeTimer(d_stats
.d_initNormalizationTimer
);
466 for(vector
<Node
>::iterator i
= d_phi
.begin(); i
!= d_phi
.end(); ++i
) {
470 Debug("ufsymm:norm") << "UFSYMM init-norm-rewrite " << n
<< endl
471 << "UFSYMM to " << *i
<< endl
;
475 for (const Permutation
& p
: d_permutations
)
477 ++(d_stats
.d_permutationSetsConsidered
);
478 Debug("ufsymm") << "UFSYMM looking at permutation: " << p
<< endl
;
479 size_t n
= p
.size() - 1;
480 if(invariantByPermutations(p
)) {
481 ++(d_stats
.d_permutationSetsInvariant
);
484 while(!d_terms
.empty() && cts
.size() <= n
) {
485 Debug("ufsymm") << "UFSYMM ==== top of loop, d_terms.size() == " << d_terms
.size() << " , cts.size() == " << cts
.size() << " , n == " << n
<< endl
;
486 Terms::iterator ti
= selectMostPromisingTerm(d_terms
);
488 Debug("ufsymm") << "UFSYMM promising term is " << t
<< endl
;
490 insertUsedIn(t
, p
, cts
);
491 if(Debug
.isOn("ufsymm")) {
493 Debug("ufsymm") << "UFSYMM cts is empty" << endl
;
495 for(set
<Node
>::iterator ctsi
= cts
.begin(); ctsi
!= cts
.end(); ++ctsi
) {
496 Debug("ufsymm") << "UFSYMM cts: " << *ctsi
<< endl
;
501 Debug("ufsymm") << "UFSYMM looking for c \\in " << p
<< " \\ cts" << endl
;
502 set
<TNode
>::const_iterator i
;
503 for(i
= p
.begin(); i
!= p
.end(); ++i
) {
504 if(cts
.find(*i
) == cts
.end()) {
507 Debug("ufsymm") << "UFSYMM found first: " << c
<< endl
;
509 Debug("ufsymm") << "UFSYMM found second: " << *i
<< endl
;
515 Debug("ufsymm") << "UFSYMM can't find a c, restart outer loop" << endl
;
518 Debug("ufsymm") << "UFSYMM inserting into cts: " << c
<< endl
;
520 // This tests cts != p: if "i == p.end()", we got all the way
521 // through p without seeing two elements not in cts (on the
522 // second one, we break from the above loop). We know we
523 // found at least one (and subsequently added it to cts). So
525 Debug("ufsymm") << "UFSYMM p == " << p
<< endl
;
526 if(i
!= p
.end() || p
.size() != cts
.size()) {
527 Debug("ufsymm") << "UFSYMM cts != p" << endl
;
528 NodeBuilder
<> disj(kind::OR
);
529 NodeManager
* nm
= NodeManager::currentNM();
530 for (const Node
& nn
: cts
)
534 disj
<< nm
->mkNode(kind::EQUAL
, t
, nn
);
538 if(disj
.getNumChildren() > 1) {
540 ++(d_stats
.d_clauses
);
546 if(Debug
.isOn("ufsymm")) {
547 Debug("ufsymm") << "UFSYMM symmetry-breaking clause: " << d
<< endl
;
549 Debug("ufsymm:clauses") << "UFSYMM symmetry-breaking clause: " << d
<< endl
;
551 newClauses
.push_back(d
);
553 Debug("ufsymm") << "UFSYMM cts == p" << endl
;
555 Debug("ufsymm") << "UFSYMM ==== end of loop, d_terms.size() == " << d_terms
.size() << " , cts.size() == " << cts
.size() << " , n == " << n
<< endl
;
564 void SymmetryBreaker::guessPermutations() {
565 // use d_phi, put into d_permutations
566 Debug("ufsymm") << "UFSYMM guessPermutations()" << endl
;
569 bool SymmetryBreaker::invariantByPermutations(const Permutation
& p
) {
570 TimerStat::CodeTimer
codeTimer(d_stats
.d_invariantByPermutationsTimer
);
573 Debug("ufsymm") << "UFSYMM invariantByPermutations()? " << p
<< endl
;
575 Assert(p
.size() > 1);
577 // check that the types match
578 Permutation::iterator permIt
= p
.begin();
579 TypeNode type
= (*permIt
++).getType();
581 if(type
!= (*permIt
++).getType()) {
582 Debug("ufsymm") << "UFSYMM types don't match, aborting.." << endl
;
585 } while(permIt
!= p
.end());
590 Permutation::iterator i
= p
.begin();
597 for (const Node
& nn
: d_phi
)
600 nn
.substitute(subs
.begin(), subs
.end(), repls
.begin(), repls
.end());
602 if (nn
!= n
&& d_phiSet
.find(n
) == d_phiSet
.end())
605 << "UFSYMM P_swap is NOT an inv perm op for " << p
<< endl
606 << "UFSYMM because this node: " << nn
<< endl
607 << "UFSYMM rewrite-norms to : " << n
<< endl
608 << "UFSYMM which is not in our set of normalized assertions" << endl
;
611 else if (Debug
.isOn("ufsymm:p"))
615 Debug("ufsymm:p") << "UFSYMM P_swap passes trivially: " << nn
<< endl
;
619 Debug("ufsymm:p") << "UFSYMM P_swap passes: " << nn
<< endl
620 << "UFSYMM rewrites: " << s
<< endl
621 << "UFSYMM norms: " << n
<< endl
;
625 Debug("ufsymm") << "UFSYMM P_swap is an inv perm op for " << p
<< endl
;
627 // check P_circ, unless size == 2 in which case P_circ == P_swap
641 repls
.push_back(*p
.begin());
642 Assert(subs
.size() == repls
.size());
643 for (const Node
& nn
: d_phi
)
646 nn
.substitute(subs
.begin(), subs
.end(), repls
.begin(), repls
.end());
648 if (nn
!= n
&& d_phiSet
.find(n
) == d_phiSet
.end())
651 << "UFSYMM P_circ is NOT an inv perm op for " << p
<< endl
652 << "UFSYMM because this node: " << nn
<< endl
653 << "UFSYMM rewrite-norms to : " << n
<< endl
654 << "UFSYMM which is not in our set of normalized assertions"
658 else if (Debug
.isOn("ufsymm:p"))
662 Debug("ufsymm:p") << "UFSYMM P_circ passes trivially: " << nn
<< endl
;
666 Debug("ufsymm:p") << "UFSYMM P_circ passes: " << nn
<< endl
667 << "UFSYMM rewrites: " << s
<< endl
668 << "UFSYMM norms: " << n
<< endl
;
672 Debug("ufsymm") << "UFSYMM P_circ is an inv perm op for " << p
<< endl
;
674 Debug("ufsymm") << "UFSYMM no need to check P_circ, since P_circ == P_swap for perm sets of size 2" << endl
;
680 // debug-assertion-only function
681 template <class T1
, class T2
>
682 static bool isSubset(const T1
& s
, const T2
& t
) {
683 if(s
.size() > t
.size()) {
684 //Debug("ufsymm") << "DEBUG ASSERTION FAIL: s not a subset of t "
685 // << "because size(s) > size(t)" << endl;
688 for(typename
T1::const_iterator si
= s
.begin(); si
!= s
.end(); ++si
) {
689 if(t
.find(*si
) == t
.end()) {
690 //Debug("ufsymm") << "DEBUG ASSERTION FAIL: s not a subset of t "
691 // << "because s element \"" << *si << "\" not in t" << endl;
696 // At this point, didn't find any elements from s not in t, so
697 // conclude that s \subseteq t
701 void SymmetryBreaker::selectTerms(const Permutation
& p
) {
702 TimerStat::CodeTimer
codeTimer(d_stats
.d_selectTermsTimer
);
704 // use d_phi, put into d_terms
705 Debug("ufsymm") << "UFSYMM selectTerms(): " << p
<< endl
;
708 for(Permutation::iterator i
= p
.begin(); i
!= p
.end(); ++i
) {
709 const TermEq
& teq
= d_termEqs
[*i
];
710 for(TermEq::const_iterator j
= teq
.begin(); j
!= teq
.end(); ++j
) {
711 Debug("ufsymm") << "selectTerms: insert in terms " << *j
<< std::endl
;
713 terms
.insert(teq
.begin(), teq
.end());
715 for(set
<Node
>::iterator i
= terms
.begin(); i
!= terms
.end(); ++i
) {
716 if(d_termEqsOnly
.find(*i
) != d_termEqsOnly
.end()) {
717 const TermEq
& teq
= d_termEqsOnly
[*i
];
718 if(isSubset(teq
, p
)) {
719 Debug("ufsymm") << "selectTerms: teq = {";
720 for(TermEq::const_iterator j
= teq
.begin(); j
!= teq
.end(); ++j
) {
721 Debug("ufsymm") << " " << *j
<< std::endl
;
723 Debug("ufsymm") << " } is subset of p " << p
<< std::endl
;
724 d_terms
.insert(d_terms
.end(), *i
);
726 if(Debug
.isOn("ufsymm")) {
727 Debug("ufsymm") << "UFSYMM selectTerms() threw away candidate: " << *i
<< endl
;
728 Debug("ufsymm:eq") << "UFSYMM selectTerms() #teq == " << teq
.size() << " #p == " << p
.size() << endl
;
730 for(j
= teq
.begin(); j
!= teq
.end(); ++j
) {
731 Debug("ufsymm:eq") << "UFSYMM -- teq " << *j
<< " in " << p
<< " ?" << endl
;
732 if(p
.find(*j
) == p
.end()) {
733 Debug("ufsymm") << "UFSYMM -- because its teq " << *j
734 << " isn't in " << p
<< endl
;
737 Debug("ufsymm:eq") << "UFSYMM -- yep" << endl
;
740 Assert(j
!= teq
.end())
741 << "failed to find a difference between p and teq ?!";
745 Debug("ufsymm") << "selectTerms: don't have data for " << *i
<< " so can't conclude anything" << endl
;
748 if(Debug
.isOn("ufsymm")) {
749 for(list
<Term
>::iterator i
= d_terms
.begin(); i
!= d_terms
.end(); ++i
) {
750 Debug("ufsymm") << "UFSYMM selectTerms() returning: " << *i
<< endl
;
755 SymmetryBreaker::Statistics::Statistics(std::string name
)
756 : d_clauses(name
+ "theory::uf::symmetry_breaker::clauses", 0)
757 , d_units(name
+ "theory::uf::symmetry_breaker::units", 0)
758 , d_permutationSetsConsidered(name
+ "theory::uf::symmetry_breaker::permutationSetsConsidered", 0)
759 , d_permutationSetsInvariant(name
+ "theory::uf::symmetry_breaker::permutationSetsInvariant", 0)
760 , d_invariantByPermutationsTimer(name
+ "theory::uf::symmetry_breaker::timers::invariantByPermutations")
761 , d_selectTermsTimer(name
+ "theory::uf::symmetry_breaker::timers::selectTerms")
762 , d_initNormalizationTimer(name
+ "theory::uf::symmetry_breaker::timers::initNormalization")
764 smtStatisticsRegistry()->registerStat(&d_clauses
);
765 smtStatisticsRegistry()->registerStat(&d_units
);
766 smtStatisticsRegistry()->registerStat(&d_permutationSetsConsidered
);
767 smtStatisticsRegistry()->registerStat(&d_permutationSetsInvariant
);
768 smtStatisticsRegistry()->registerStat(&d_invariantByPermutationsTimer
);
769 smtStatisticsRegistry()->registerStat(&d_selectTermsTimer
);
770 smtStatisticsRegistry()->registerStat(&d_initNormalizationTimer
);
773 SymmetryBreaker::Statistics::~Statistics()
775 smtStatisticsRegistry()->unregisterStat(&d_clauses
);
776 smtStatisticsRegistry()->unregisterStat(&d_units
);
777 smtStatisticsRegistry()->unregisterStat(&d_permutationSetsConsidered
);
778 smtStatisticsRegistry()->unregisterStat(&d_permutationSetsInvariant
);
779 smtStatisticsRegistry()->unregisterStat(&d_invariantByPermutationsTimer
);
780 smtStatisticsRegistry()->unregisterStat(&d_selectTermsTimer
);
781 smtStatisticsRegistry()->unregisterStat(&d_initNormalizationTimer
);
784 SymmetryBreaker::Terms::iterator
785 SymmetryBreaker::selectMostPromisingTerm(Terms
& terms
) {
787 Debug("ufsymm") << "UFSYMM selectMostPromisingTerm()" << endl
;
788 return terms
.begin();
791 void SymmetryBreaker::insertUsedIn(Term term
, const Permutation
& p
, set
<Node
>& cts
) {
792 // insert terms from p used in term into cts
793 //Debug("ufsymm") << "UFSYMM usedIn(): " << term << " , " << p << endl;
794 if (p
.find(term
) != p
.end()) {
797 for(TNode::iterator i
= term
.begin(); i
!= term
.end(); ++i
) {
798 insertUsedIn(*i
, p
, cts
);
803 }/* CVC4::theory::uf namespace */
804 }/* CVC4::theory namespace */
806 std::ostream
& operator<<(std::ostream
& out
, const theory::uf::SymmetryBreaker::Permutation
& p
) {
808 set
<TNode
>::const_iterator i
= p
.begin();
809 while(i
!= p
.end()) {
819 }/* CVC4 namespace */