GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/uf/symmetry_breaker.cpp Lines: 409 485 84.3 %
Date: 2021-11-07 Branches: 943 2172 43.4 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Morgan Deters, Mathias Preiner, Liana Hadarean
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
 * Implementation of algorithm suggested by Deharbe, Fontaine, Merz,
14
 * and Paleo, "Exploiting symmetry in SMT problems," CADE 2011.
15
 *
16
 * From the paper:
17
 *
18
 * <pre>
19
 *   \f$ P := guess\_permutations(\phi) \f$
20
 *   foreach \f$ {c_0, ..., c_n} \in P \f$ do
21
 *     if \f$ invariant\_by\_permutations(\phi, {c_0, ..., c_n}) \f$ then
22
 *       T := \f$ select\_terms(\phi, {c_0, ..., c_n}) \f$
23
 *       cts := \f$ \emptyset \f$
24
 *       while T != \f$ \empty \wedge |cts| <= n \f$ do
25
 *         \f$ t := select\_most\_promising\_term(T, \phi) \f$
26
 *         \f$ T := T \setminus {t} \f$
27
 *         cts := cts \f$ \cup used\_in(t, {c_0, ..., c_n}) \f$
28
 *         let \f$ c \in {c_0, ..., c_n} \setminus cts \f$
29
 *         cts := cts \f$ \cup {c} \f$
30
 *         if cts != \f$ {c_0, ..., c_n} \f$ then
31
 *           \f$ \phi := \phi \wedge ( \vee_{c_i \in cts} t = c_i ) \f$
32
 *         end
33
 *       end
34
 *     end
35
 *   end
36
 *   return \f$ \phi \f$
37
 * </pre>
38
 */
39
40
#include "theory/uf/symmetry_breaker.h"
41
#include "theory/rewriter.h"
42
#include "util/hash.h"
43
44
#include <iterator>
45
#include <queue>
46
47
using namespace std;
48
49
namespace cvc5 {
50
namespace theory {
51
namespace uf {
52
53
using namespace ::cvc5::context;
54
55
44548
SymmetryBreaker::Template::Template() :
56
  d_template(),
57
  d_sets(),
58
44548
  d_reps() {
59
44548
}
60
61
109401
TNode SymmetryBreaker::Template::find(TNode n) {
62
109401
  unordered_map<TNode, TNode>::iterator i = d_reps.find(n);
63
109401
  if(i == d_reps.end()) {
64
81984
    return n;
65
  } else {
66
27417
    return d_reps[n] = find((*i).second);
67
  }
68
}
69
70
138854
bool SymmetryBreaker::Template::matchRecursive(TNode t, TNode n) {
71
277708
  IndentedScope scope(Debug("ufsymm:match"));
72
73
277708
  Debug("ufsymm:match") << "UFSYMM matching " << t << endl
74
138854
                        << "UFSYMM       to " << n << endl;
75
76
138854
  if(t.getKind() != n.getKind() || t.getNumChildren() != n.getNumChildren()) {
77
42840
    Debug("ufsymm:match") << "UFSYMM BAD MATCH on kind, #children" << endl;
78
42840
    return false;
79
  }
80
81
96014
  if(t.getNumChildren() == 0) {
82
40996
    if (!t.isVar())
83
    {
84
4
      Debug("ufsymm:match") << "UFSYMM non-variables, failing match" << endl;
85
4
      return false;
86
    }
87
40992
    t = find(t);
88
40992
    n = find(n);
89
40992
    Debug("ufsymm:match") << "UFSYMM variable match " << t << " , " << n << endl;
90
40992
    Debug("ufsymm:match") << "UFSYMM sets: " << t << " =>";
91
40992
    if(d_sets.find(t) != d_sets.end()) {
92
90356
      for(set<TNode>::iterator i = d_sets[t].begin(); i != d_sets[t].end(); ++i) {
93
70092
        Debug("ufsymm:match") << " " << *i;
94
      }
95
    }
96
40992
    Debug("ufsymm:match") << endl;
97
40992
    if(t != n) {
98
29117
      Debug("ufsymm:match") << "UFSYMM sets: " << n << " =>";
99
29117
      if(d_sets.find(n) != d_sets.end()) {
100
173
        for(set<TNode>::iterator i = d_sets[n].begin(); i != d_sets[n].end(); ++i) {
101
114
          Debug("ufsymm:match") << " " << *i;
102
        }
103
      }
104
29117
      Debug("ufsymm:match") << endl;
105
106
29117
      if(d_sets.find(t) == d_sets.end()) {
107
20728
        Debug("ufsymm:match") << "UFSYMM inserting " << t << " in with " << n << endl;
108
20728
        d_reps[t] = n;
109
20728
        d_sets[n].insert(t);
110
      } else {
111
8389
        if(d_sets.find(n) != d_sets.end()) {
112
31
          Debug("ufsymm:match") << "UFSYMM merging " << n << " and " << t << " in with " << n << endl;
113
31
          d_sets[n].insert(d_sets[t].begin(), d_sets[t].end());
114
31
          d_sets[n].insert(t);
115
31
          d_reps[t] = n;
116
31
          d_sets.erase(t);
117
        } else {
118
8358
          Debug("ufsymm:match") << "UFSYMM inserting " << n << " in with " << t << endl;
119
8358
          d_sets[t].insert(n);
120
8358
          d_reps[n] = t;
121
        }
122
      }
123
    }
124
40992
    return true;
125
  }
126
127
55018
  if(t.getMetaKind() == kind::metakind::PARAMETERIZED) {
128
8434
    if(t.getOperator() != n.getOperator()) {
129
56
      Debug("ufsymm:match") << "UFSYMM BAD MATCH on operators: " << t.getOperator() << " != " << n.getOperator() << endl;
130
56
      return false;
131
    }
132
  }
133
54962
  TNode::iterator ti = t.begin();
134
54962
  TNode::iterator ni = n.begin();
135
185362
  while(ti != t.end()) {
136
83192
    if(*ti != *ni) { // nothing to do if equal
137
70913
      if(!matchRecursive(*ti, *ni)) {
138
17992
        Debug("ufsymm:match") << "UFSYMM BAD MATCH, withdrawing.." << endl;
139
17992
        return false;
140
      }
141
    }
142
65200
    ++ti;
143
65200
    ++ni;
144
  }
145
146
36970
  return true;
147
}
148
149
119788
bool SymmetryBreaker::Template::match(TNode n) {
150
  // try to "match" n and d_template
151
119788
  if(d_template.isNull()) {
152
51847
    Debug("ufsymm") << "UFSYMM setting template " << n << endl;
153
51847
    d_template = n;
154
51847
    return true;
155
  } else {
156
67941
    return matchRecursive(d_template, n);
157
  }
158
}
159
160
42843
void SymmetryBreaker::Template::reset() {
161
42843
  d_template = Node::null();
162
42843
  d_sets.clear();
163
42843
  d_reps.clear();
164
42843
}
165
166
15273
SymmetryBreaker::SymmetryBreaker(Env& env, std::string name)
167
    : EnvObj(env),
168
15273
      ContextNotifyObj(userContext()),
169
15273
      d_assertionsToRerun(userContext()),
170
      d_rerunningAssertions(false),
171
      d_phi(),
172
      d_phiSet(),
173
      d_permutations(),
174
      d_terms(),
175
      d_template(),
176
      d_normalizationCache(),
177
      d_termEqs(),
178
      d_termEqsOnly(),
179
      d_name(name),
180
45819
      d_stats(d_name + "theory::uf::symmetry_breaker::")
181
{
182
15273
}
183
184
class SBGuard {
185
  bool& d_ref;
186
  bool d_old;
187
public:
188
  SBGuard(bool& b) : d_ref(b), d_old(b) {}
189
  ~SBGuard() { Debug("uf") << "reset to " << d_old << std::endl; d_ref = d_old; }
190
};/* class SBGuard */
191
192
31368
void SymmetryBreaker::rerunAssertionsIfNecessary() {
193
31368
  if(d_rerunningAssertions || !d_phi.empty() || d_assertionsToRerun.empty()) {
194
31368
    return;
195
  }
196
197
  SBGuard g(d_rerunningAssertions);
198
  d_rerunningAssertions = true;
199
200
  Debug("ufsymm") << "UFSYMM: rerunning assertions..." << std::endl;
201
  for(CDList<Node>::const_iterator i = d_assertionsToRerun.begin();
202
      i != d_assertionsToRerun.end();
203
      ++i) {
204
    assertFormula(*i);
205
  }
206
  Debug("ufsymm") << "UFSYMM: DONE rerunning assertions..." << std::endl;
207
}
208
209
10596603
Node SymmetryBreaker::norm(TNode phi) {
210
21193206
  Node n = rewrite(phi);
211
21193206
  return normInternal(n, 0);
212
}
213
214
10606063
Node SymmetryBreaker::normInternal(TNode n, size_t level) {
215
10606063
  Node& result = d_normalizationCache[n];
216
10606063
  if(!result.isNull()) {
217
10525806
    return result;
218
  }
219
220
80257
  switch(Kind k = n.getKind()) {
221
222
  case kind::DISTINCT: {
223
    // commutative N-ary operator handling
224
    vector<TNode> kids(n.begin(), n.end());
225
    sort(kids.begin(), kids.end());
226
    return result = NodeManager::currentNM()->mkNode(k, kids);
227
  }
228
229
184
  case kind::AND: {
230
    // commutative+associative N-ary operator handling
231
368
    vector<Node> kids;
232
184
    kids.reserve(n.getNumChildren());
233
368
    queue<TNode> work;
234
184
    work.push(n);
235
184
    Debug("ufsymm:norm") << "UFSYMM processing " << n << endl;
236
    do {
237
368
      TNode m = work.front();
238
184
      work.pop();
239
552
      for(TNode::iterator i = m.begin(); i != m.end(); ++i) {
240
368
        if((*i).getKind() == k) {
241
          work.push(*i);
242
        } else {
243
368
          if( (*i).getKind() == kind::OR ) {
244
            kids.push_back(normInternal(*i, level));
245
368
          } else if((*i).getKind() == kind::EQUAL) {
246
368
            kids.push_back(normInternal(*i, level));
247
736
            if((*i)[0].isVar() ||
248
368
               (*i)[1].isVar()) {
249
368
              d_termEqs[(*i)[0]].insert((*i)[1]);
250
368
              d_termEqs[(*i)[1]].insert((*i)[0]);
251
368
              if(level == 0) {
252
                d_termEqsOnly[(*i)[0]].insert((*i)[1]);
253
                d_termEqsOnly[(*i)[1]].insert((*i)[0]);
254
                Debug("ufsymm:eq") << "UFSYMM " << (*i)[0] << " <==> " << (*i)[1] << endl;
255
              }
256
            }
257
          } else {
258
            kids.push_back(*i);
259
          }
260
        }
261
      }
262
184
    } while(!work.empty());
263
184
    Debug("ufsymm:norm") << "UFSYMM got " << kids.size() << " kids for the " << k << "-kinded Node" << endl;
264
184
    sort(kids.begin(), kids.end());
265
184
    return result = NodeManager::currentNM()->mkNode(k, kids);
266
  }
267
268
62327
  case kind::OR: {
269
    // commutative+associative N-ary operator handling
270
124654
    vector<Node> kids;
271
62327
    kids.reserve(n.getNumChildren());
272
124654
    queue<TNode> work;
273
62327
    work.push(n);
274
62327
    Debug("ufsymm:norm") << "UFSYMM processing " << n << endl;
275
124654
    TNode matchingTerm = TNode::null();
276
124654
    vector<TNode> matchingTermEquals;
277
62327
    bool first = true, matchedVar = false;
278
    do {
279
124654
      TNode m = work.front();
280
62327
      work.pop();
281
227204
      for(TNode::iterator i = m.begin(); i != m.end(); ++i) {
282
164877
        if((*i).getKind() == k) {
283
          work.push(*i);
284
        } else {
285
164877
          if( (*i).getKind() == kind::AND ) {
286
336
            first = false;
287
336
            matchingTerm = TNode::null();
288
336
            kids.push_back(normInternal(*i, level + 1));
289
164541
          } else if((*i).getKind() == kind::EQUAL) {
290
8756
            kids.push_back(normInternal(*i, level + 1));
291
17712
            if((*i)[0].isVar() ||
292
8956
               (*i)[1].isVar()) {
293
8656
              d_termEqs[(*i)[0]].insert((*i)[1]);
294
8656
              d_termEqs[(*i)[1]].insert((*i)[0]);
295
8656
              if(level == 0) {
296
8656
                if(first) {
297
1730
                  matchingTerm = *i;
298
6926
                } else if(!matchingTerm.isNull()) {
299
6808
                  if(matchedVar) {
300
5080
                    if(matchingTerm == (*i)[0]) {
301
2096
                      matchingTermEquals.push_back((*i)[1]);
302
2984
                    } else if(matchingTerm == (*i)[1]) {
303
2982
                      matchingTermEquals.push_back((*i)[0]);
304
                    } else {
305
2
                      matchingTerm = TNode::null();
306
                    }
307
1728
                  } else if((*i)[0] == matchingTerm[0]) {
308
626
                    matchingTermEquals.push_back(matchingTerm[1]);
309
626
                    matchingTermEquals.push_back((*i)[1]);
310
626
                    matchingTerm = matchingTerm[0];
311
626
                    matchedVar = true;
312
1102
                  } else if((*i)[1] == matchingTerm[0]) {
313
                    matchingTermEquals.push_back(matchingTerm[1]);
314
                    matchingTermEquals.push_back((*i)[0]);
315
                    matchingTerm = matchingTerm[0];
316
                    matchedVar = true;
317
1102
                  } else if((*i)[0] == matchingTerm[1]) {
318
4
                    matchingTermEquals.push_back(matchingTerm[0]);
319
4
                    matchingTermEquals.push_back((*i)[1]);
320
4
                    matchingTerm = matchingTerm[1];
321
4
                    matchedVar = true;
322
1098
                  } else if((*i)[1] == matchingTerm[1]) {
323
1068
                    matchingTermEquals.push_back(matchingTerm[0]);
324
1068
                    matchingTermEquals.push_back((*i)[0]);
325
1068
                    matchingTerm = matchingTerm[1];
326
1068
                    matchedVar = true;
327
                  } else {
328
30
                    matchingTerm = TNode::null();
329
                  }
330
                }
331
              }
332
            } else {
333
100
              matchingTerm = TNode::null();
334
            }
335
8756
            first = false;
336
          } else {
337
155785
            first = false;
338
155785
            matchingTerm = TNode::null();
339
155785
            kids.push_back(*i);
340
          }
341
        }
342
      }
343
62327
    } while(!work.empty());
344
62327
    if(!matchingTerm.isNull()) {
345
1692
      if(Debug.isOn("ufsymm:eq")) {
346
        Debug("ufsymm:eq") << "UFSYMM here we can conclude that " << matchingTerm << " is one of {";
347
        for(vector<TNode>::const_iterator i = matchingTermEquals.begin(); i != matchingTermEquals.end(); ++i) {
348
          Debug("ufsymm:eq") << " " << *i;
349
        }
350
        Debug("ufsymm:eq") << " }" << endl;
351
      }
352
1692
      d_termEqsOnly[matchingTerm].insert(matchingTermEquals.begin(), matchingTermEquals.end());
353
    }
354
62327
    Debug("ufsymm:norm") << "UFSYMM got " << kids.size() << " kids for the " << k << "-kinded Node" << endl;
355
62327
    sort(kids.begin(), kids.end());
356
62327
    return result = NodeManager::currentNM()->mkNode(k, kids);
357
  }
358
359
13382
  case kind::EQUAL:
360
34316
    if(n[0].isVar() ||
361
20934
       n[1].isVar()) {
362
9606
      d_termEqs[n[0]].insert(n[1]);
363
9606
      d_termEqs[n[1]].insert(n[0]);
364
9606
      if(level == 0) {
365
582
        d_termEqsOnly[n[0]].insert(n[1]);
366
582
        d_termEqsOnly[n[1]].insert(n[0]);
367
582
        Debug("ufsymm:eq") << "UFSYMM " << n[0] << " <==> " << n[1] << endl;
368
      }
369
    }
370
    CVC5_FALLTHROUGH;
371
  case kind::XOR:
372
    // commutative binary operator handling
373
13382
    return n[1] < n[0] ? NodeManager::currentNM()->mkNode(k, n[1], n[0]) : Node(n);
374
375
4364
  default:
376
    // Normally T-rewriting is enough; only special cases (like
377
    // Boolean-layer stuff) has to go above.
378
4364
    return n;
379
  }
380
}
381
382
31222
void SymmetryBreaker::assertFormula(TNode phi) {
383
31222
  rerunAssertionsIfNecessary();
384
31222
  if(!d_rerunningAssertions) {
385
31222
    d_assertionsToRerun.push_back(phi);
386
  }
387
  // use d_phi, put into d_permutations
388
31222
  Debug("ufsymm") << "UFSYMM assertFormula(): phi is " << phi << endl;
389
31222
  d_phi.push_back(phi);
390
31222
  if(phi.getKind() == kind::OR) {
391
58550
    Template t;
392
29275
    Node::iterator i = phi.begin();
393
29275
    t.match(*i++);
394
62057
    while(i != phi.end()) {
395
36860
      if(!t.match(*i++)) {
396
20469
        break;
397
      }
398
    }
399
29275
    unordered_map<TNode, set<TNode>>& ps = t.partitions();
400
41433
    for (auto& kv : ps)
401
    {
402
12158
      Debug("ufsymm") << "UFSYMM partition*: " << kv.first;
403
12158
      set<TNode>& p = kv.second;
404
28469
      for(set<TNode>::iterator j = p.begin();
405
28469
          j != p.end();
406
          ++j) {
407
16311
        Debug("ufsymm") << " " << *j;
408
      }
409
12158
      Debug("ufsymm") << endl;
410
12158
      p.insert(kv.first);
411
12158
      Permutations::iterator pi = d_permutations.find(p);
412
12158
      if(pi == d_permutations.end()) {
413
10475
        d_permutations.insert(p);
414
      }
415
    }
416
  }
417
31222
  if(!d_template.match(phi)) {
418
    // we hit a bad match, extract the partitions and reset the template
419
22431
    unordered_map<TNode, set<TNode>>& ps = d_template.partitions();
420
22431
    Debug("ufsymm") << "UFSYMM hit a bad match---have " << ps.size() << " partitions:" << endl;
421
30914
    for (unordered_map<TNode, set<TNode>>::iterator i = ps.begin();
422
30914
         i != ps.end();
423
         ++i)
424
    {
425
8483
      Debug("ufsymm") << "UFSYMM partition: " << (*i).first;
426
8483
      set<TNode>& p = (*i).second;
427
8483
      if(Debug.isOn("ufsymm")) {
428
        for(set<TNode>::iterator j = p.begin();
429
            j != p.end();
430
            ++j) {
431
          Debug("ufsymm") << " " << *j;
432
        }
433
      }
434
8483
      Debug("ufsymm") << endl;
435
8483
      p.insert((*i).first);
436
8483
      d_permutations.insert(p);
437
    }
438
22431
    d_template.reset();
439
22431
    bool good CVC5_UNUSED = d_template.match(phi);
440
22431
    Assert(good);
441
  }
442
31222
}
443
444
20412
void SymmetryBreaker::clear() {
445
20412
  d_phi.clear();
446
20412
  d_phiSet.clear();
447
20412
  d_permutations.clear();
448
20412
  d_terms.clear();
449
20412
  d_template.reset();
450
20412
  d_normalizationCache.clear();
451
20412
  d_termEqs.clear();
452
20412
  d_termEqsOnly.clear();
453
20412
}
454
455
146
void SymmetryBreaker::apply(std::vector<Node>& newClauses) {
456
146
  rerunAssertionsIfNecessary();
457
146
  guessPermutations();
458
292
  Debug("ufsymm") << "UFSYMM =====================================================" << endl
459
146
                  << "UFSYMM have " << d_permutations.size() << " permutation sets" << endl;
460
146
  if(!d_permutations.empty()) {
461
108
    { TimerStat::CodeTimer codeTimer(d_stats.d_initNormalizationTimer);
462
      // normalize d_phi
463
464
30377
      for(vector<Node>::iterator i = d_phi.begin(); i != d_phi.end(); ++i) {
465
60646
        Node n = *i;
466
30323
        *i = norm(n);
467
30323
        d_phiSet.insert(*i);
468
60646
        Debug("ufsymm:norm") << "UFSYMM init-norm-rewrite " << n << endl
469
30323
                             << "UFSYMM                to " << *i << endl;
470
      }
471
    }
472
473
17174
    for (const Permutation& p : d_permutations)
474
    {
475
17120
      ++(d_stats.d_permutationSetsConsidered);
476
17120
      Debug("ufsymm") << "UFSYMM looking at permutation: " << p << endl;
477
17120
      size_t n = p.size() - 1;
478
17120
      if(invariantByPermutations(p)) {
479
114
        ++(d_stats.d_permutationSetsInvariant);
480
114
        selectTerms(p);
481
228
        set<Node> cts;
482
174
        while(!d_terms.empty() && cts.size() <= n) {
483
30
          Debug("ufsymm") << "UFSYMM ==== top of loop, d_terms.size() == " << d_terms.size() << " , cts.size() == " << cts.size() << " , n == " << n << endl;
484
30
          Terms::iterator ti = selectMostPromisingTerm(d_terms);
485
60
          Node t = *ti;
486
30
          Debug("ufsymm") << "UFSYMM promising term is " << t << endl;
487
30
          d_terms.erase(ti);
488
30
          insertUsedIn(t, p, cts);
489
30
          if(Debug.isOn("ufsymm")) {
490
            if(cts.empty()) {
491
              Debug("ufsymm") << "UFSYMM cts is empty" << endl;
492
            } else {
493
              for(set<Node>::iterator ctsi = cts.begin(); ctsi != cts.end(); ++ctsi) {
494
                Debug("ufsymm") << "UFSYMM cts: " << *ctsi << endl;
495
              }
496
            }
497
          }
498
60
          TNode c;
499
30
          Debug("ufsymm") << "UFSYMM looking for c \\in " << p << " \\ cts" << endl;
500
30
          set<TNode>::const_iterator i;
501
102
          for(i = p.begin(); i != p.end(); ++i) {
502
92
            if(cts.find(*i) == cts.end()) {
503
50
              if(c.isNull()) {
504
30
                c = *i;
505
30
                Debug("ufsymm") << "UFSYMM found first: " << c << endl;
506
              } else {
507
20
                Debug("ufsymm") << "UFSYMM found second: " << *i << endl;
508
20
                break;
509
              }
510
            }
511
          }
512
30
          if(c.isNull()) {
513
            Debug("ufsymm") << "UFSYMM can't find a c, restart outer loop" << endl;
514
            break;
515
          }
516
30
          Debug("ufsymm") << "UFSYMM inserting into cts: " << c << endl;
517
30
          cts.insert(c);
518
          // This tests cts != p: if "i == p.end()", we got all the way
519
          // through p without seeing two elements not in cts (on the
520
          // second one, we break from the above loop).  We know we
521
          // found at least one (and subsequently added it to cts).  So
522
          // now cts == p.
523
30
          Debug("ufsymm") << "UFSYMM p == " << p << endl;
524
30
          if(i != p.end() || p.size() != cts.size()) {
525
20
            Debug("ufsymm") << "UFSYMM cts != p" << endl;
526
40
            NodeBuilder disj(kind::OR);
527
20
            NodeManager* nm = NodeManager::currentNM();
528
60
            for (const Node& nn : cts)
529
            {
530
40
              if (t != nn)
531
              {
532
40
                disj << nm->mkNode(kind::EQUAL, t, nn);
533
              }
534
            }
535
40
            Node d;
536
20
            if(disj.getNumChildren() > 1) {
537
12
              d = disj;
538
12
              ++(d_stats.d_clauses);
539
            } else {
540
8
              d = disj[0];
541
8
              disj.clear();
542
8
              ++(d_stats.d_units);
543
            }
544
20
            if(Debug.isOn("ufsymm")) {
545
              Debug("ufsymm") << "UFSYMM symmetry-breaking clause: " << d << endl;
546
            } else {
547
20
              Debug("ufsymm:clauses") << "UFSYMM symmetry-breaking clause: " << d << endl;
548
            }
549
20
            newClauses.push_back(d);
550
          } else {
551
10
            Debug("ufsymm") << "UFSYMM cts == p" << endl;
552
          }
553
30
          Debug("ufsymm") << "UFSYMM ==== end of loop, d_terms.size() == " << d_terms.size() << " , cts.size() == " << cts.size() << " , n == " << n << endl;
554
        }
555
      }
556
    }
557
  }
558
559
146
  clear();
560
146
}
561
562
146
void SymmetryBreaker::guessPermutations() {
563
  // use d_phi, put into d_permutations
564
146
  Debug("ufsymm") << "UFSYMM guessPermutations()" << endl;
565
146
}
566
567
17120
bool SymmetryBreaker::invariantByPermutations(const Permutation& p) {
568
34240
  TimerStat::CodeTimer codeTimer(d_stats.d_invariantByPermutationsTimer);
569
570
  // use d_phi
571
17120
  Debug("ufsymm") << "UFSYMM invariantByPermutations()? " << p << endl;
572
573
17120
  Assert(p.size() > 1);
574
575
  // check that the types match
576
17120
  Permutation::iterator permIt = p.begin();
577
34240
  TypeNode type = (*permIt++).getType();
578
5304
  do {
579
22424
    if(type != (*permIt++).getType()) {
580
      Debug("ufsymm") << "UFSYMM types don't match, aborting.." << endl;
581
      return false;
582
    }
583
22424
  } while(permIt != p.end());
584
585
  // check P_swap
586
34240
  vector<Node> subs;
587
34240
  vector<Node> repls;
588
17120
  Permutation::iterator i = p.begin();
589
34240
  TNode p0 = *i++;
590
34240
  TNode p1 = *i;
591
17120
  subs.push_back(p0);
592
17120
  subs.push_back(p1);
593
17120
  repls.push_back(p1);
594
17120
  repls.push_back(p0);
595
10565332
  for (const Node& nn : d_phi)
596
  {
597
    Node s =
598
21113426
        nn.substitute(subs.begin(), subs.end(), repls.begin(), repls.end());
599
21113426
    Node n = norm(s);
600
10565214
    if (nn != n && d_phiSet.find(n) == d_phiSet.end())
601
    {
602
34004
      Debug("ufsymm")
603
17002
          << "UFSYMM P_swap is NOT an inv perm op for " << p << endl
604
17002
          << "UFSYMM because this node: " << nn << endl
605
17002
          << "UFSYMM rewrite-norms to : " << n << endl
606
17002
          << "UFSYMM which is not in our set of normalized assertions" << endl;
607
17002
      return false;
608
    }
609
10548212
    else if (Debug.isOn("ufsymm:p"))
610
    {
611
      if (nn == s)
612
      {
613
        Debug("ufsymm:p") << "UFSYMM P_swap passes trivially: " << nn << endl;
614
      }
615
      else
616
      {
617
        Debug("ufsymm:p") << "UFSYMM P_swap passes: " << nn << endl
618
                          << "UFSYMM      rewrites: " << s << endl
619
                          << "UFSYMM         norms: " << n << endl;
620
      }
621
    }
622
  }
623
118
  Debug("ufsymm") << "UFSYMM P_swap is an inv perm op for " << p << endl;
624
625
  // check P_circ, unless size == 2 in which case P_circ == P_swap
626
118
  if(p.size() > 2) {
627
14
    subs.clear();
628
14
    repls.clear();
629
14
    bool first = true;
630
82
    for (TNode nn : p)
631
    {
632
68
      subs.push_back(nn);
633
68
      if(!first) {
634
54
        repls.push_back(nn);
635
      } else {
636
14
        first = false;
637
      }
638
    }
639
14
    repls.push_back(*p.begin());
640
14
    Assert(subs.size() == repls.size());
641
1076
    for (const Node& nn : d_phi)
642
    {
643
      Node s =
644
2128
          nn.substitute(subs.begin(), subs.end(), repls.begin(), repls.end());
645
2128
      Node n = norm(s);
646
1066
      if (nn != n && d_phiSet.find(n) == d_phiSet.end())
647
      {
648
8
        Debug("ufsymm")
649
4
            << "UFSYMM P_circ is NOT an inv perm op for " << p << endl
650
4
            << "UFSYMM because this node: " << nn << endl
651
4
            << "UFSYMM rewrite-norms to : " << n << endl
652
4
            << "UFSYMM which is not in our set of normalized assertions"
653
4
            << endl;
654
4
        return false;
655
      }
656
1062
      else if (Debug.isOn("ufsymm:p"))
657
      {
658
        if (nn == s)
659
        {
660
          Debug("ufsymm:p") << "UFSYMM P_circ passes trivially: " << nn << endl;
661
        }
662
        else
663
        {
664
          Debug("ufsymm:p") << "UFSYMM P_circ passes: " << nn << endl
665
                            << "UFSYMM      rewrites: " << s << endl
666
                            << "UFSYMM         norms: " << n << endl;
667
        }
668
      }
669
    }
670
10
    Debug("ufsymm") << "UFSYMM P_circ is an inv perm op for " << p << endl;
671
  } else {
672
104
    Debug("ufsymm") << "UFSYMM no need to check P_circ, since P_circ == P_swap for perm sets of size 2" << endl;
673
  }
674
675
114
  return true;
676
}
677
678
// debug-assertion-only function
679
template <class T1, class T2>
680
852
static bool isSubset(const T1& s, const T2& t) {
681
852
  if(s.size() > t.size()) {
682
    //Debug("ufsymm") << "DEBUG ASSERTION FAIL: s not a subset of t "
683
    //                << "because size(s) > size(t)" << endl;
684
560
    return false;
685
  }
686
1050
  for(typename T1::const_iterator si = s.begin(); si != s.end(); ++si) {
687
758
    if(t.find(*si) == t.end()) {
688
      //Debug("ufsymm") << "DEBUG ASSERTION FAIL: s not a subset of t "
689
      //                << "because s element \"" << *si << "\" not in t" << endl;
690
      return false;
691
    }
692
  }
693
694
  // At this point, didn't find any elements from s not in t, so
695
  // conclude that s \subseteq t
696
292
  return true;
697
}
698
699
114
void SymmetryBreaker::selectTerms(const Permutation& p) {
700
228
  TimerStat::CodeTimer codeTimer(d_stats.d_selectTermsTimer);
701
702
  // use d_phi, put into d_terms
703
114
  Debug("ufsymm") << "UFSYMM selectTerms(): " << p << endl;
704
114
  d_terms.clear();
705
228
  set<Node> terms;
706
358
  for(Permutation::iterator i = p.begin(); i != p.end(); ++i) {
707
244
    const TermEq& teq = d_termEqs[*i];
708
2442
    for(TermEq::const_iterator j = teq.begin(); j != teq.end(); ++j) {
709
2198
      Debug("ufsymm") << "selectTerms: insert in terms " << *j << std::endl;
710
    }
711
244
    terms.insert(teq.begin(), teq.end());
712
  }
713
1124
  for(set<Node>::iterator i = terms.begin(); i != terms.end(); ++i) {
714
1010
    if(d_termEqsOnly.find(*i) != d_termEqsOnly.end()) {
715
852
      const TermEq& teq = d_termEqsOnly[*i];
716
852
      if(isSubset(teq, p)) {
717
292
        Debug("ufsymm") << "selectTerms: teq = {";
718
1050
        for(TermEq::const_iterator j = teq.begin(); j != teq.end(); ++j) {
719
758
          Debug("ufsymm") << " " << *j << std::endl;
720
        }
721
292
        Debug("ufsymm") << " } is subset of p " << p << std::endl;
722
292
        d_terms.insert(d_terms.end(), *i);
723
      } else {
724
560
        if(Debug.isOn("ufsymm")) {
725
          Debug("ufsymm") << "UFSYMM selectTerms() threw away candidate: " << *i << endl;
726
          Debug("ufsymm:eq") << "UFSYMM selectTerms() #teq == " << teq.size() << " #p == " << p.size() << endl;
727
          TermEq::iterator j;
728
          for(j = teq.begin(); j != teq.end(); ++j) {
729
            Debug("ufsymm:eq") << "UFSYMM              -- teq " << *j << " in " << p << " ?" << endl;
730
            if(p.find(*j) == p.end()) {
731
              Debug("ufsymm") << "UFSYMM              -- because its teq " << *j
732
                              << " isn't in " << p << endl;
733
              break;
734
            } else {
735
              Debug("ufsymm:eq") << "UFSYMM              -- yep" << endl;
736
            }
737
          }
738
          Assert(j != teq.end())
739
              << "failed to find a difference between p and teq ?!";
740
        }
741
      }
742
    } else {
743
158
      Debug("ufsymm") << "selectTerms: don't have data for " << *i << " so can't conclude anything" << endl;
744
    }
745
  }
746
114
  if(Debug.isOn("ufsymm")) {
747
    for(list<Term>::iterator i = d_terms.begin(); i != d_terms.end(); ++i) {
748
      Debug("ufsymm") << "UFSYMM selectTerms() returning: " << *i << endl;
749
    }
750
  }
751
114
}
752
753
15273
SymmetryBreaker::Statistics::Statistics(const std::string& name)
754
30546
    : d_clauses(smtStatisticsRegistry().registerInt(name + "clauses")),
755
30546
      d_units(smtStatisticsRegistry().registerInt(name + "units")),
756
15273
      d_permutationSetsConsidered(smtStatisticsRegistry().registerInt(
757
30546
          name + "permutationSetsConsidered")),
758
15273
      d_permutationSetsInvariant(smtStatisticsRegistry().registerInt(
759
30546
          name + "permutationSetsInvariant")),
760
15273
      d_invariantByPermutationsTimer(smtStatisticsRegistry().registerTimer(
761
30546
          name + "timers::invariantByPermutations")),
762
      d_selectTermsTimer(
763
30546
          smtStatisticsRegistry().registerTimer(name + "timers::selectTerms")),
764
15273
      d_initNormalizationTimer(smtStatisticsRegistry().registerTimer(
765
106911
          name + "timers::initNormalization"))
766
{
767
15273
}
768
769
SymmetryBreaker::Terms::iterator
770
30
SymmetryBreaker::selectMostPromisingTerm(Terms& terms) {
771
  // use d_phi
772
30
  Debug("ufsymm") << "UFSYMM selectMostPromisingTerm()" << endl;
773
30
  return terms.begin();
774
}
775
776
46
void SymmetryBreaker::insertUsedIn(Term term, const Permutation& p, set<Node>& cts) {
777
  // insert terms from p used in term into cts
778
  //Debug("ufsymm") << "UFSYMM usedIn(): " << term << " , " << p << endl;
779
46
  if (p.find(term) != p.end()) {
780
16
    cts.insert(term);
781
  } else {
782
46
    for(TNode::iterator i = term.begin(); i != term.end(); ++i) {
783
16
      insertUsedIn(*i, p, cts);
784
    }
785
  }
786
46
}
787
788
}  // namespace uf
789
}  // namespace theory
790
791
std::ostream& operator<<(std::ostream& out, const theory::uf::SymmetryBreaker::Permutation& p) {
792
  out << "{";
793
  set<TNode>::const_iterator i = p.begin();
794
  while(i != p.end()) {
795
    out << *i;
796
    if(++i != p.end()) {
797
      out << ",";
798
    }
799
  }
800
  out << "}";
801
  return out;
802
}
803
804
31137
}  // namespace cvc5