GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/booleans/theory_bool_rewriter.cpp Lines: 223 231 96.5 %
Date: 2021-09-29 Branches: 884 1595 55.4 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Tim King, Dejan Jovanovic, Haniel Barbosa
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
 * [[ Add one-line brief description here ]]
14
 *
15
 * [[ Add lengthier description here ]]
16
 * \todo document this file
17
 */
18
19
#include "theory/booleans/theory_bool_rewriter.h"
20
21
#include <algorithm>
22
#include <unordered_set>
23
24
#include "expr/node_value.h"
25
#include "util/cardinality.h"
26
27
namespace cvc5 {
28
namespace theory {
29
namespace booleans {
30
31
8227573
RewriteResponse TheoryBoolRewriter::postRewrite(TNode node) {
32
8227573
  return preRewrite(node);
33
}
34
35
/**
36
 * flattenNode looks for children of same kind, and if found merges
37
 * them into the parent.
38
 *
39
 * It simultaneously handles a couple of other optimizations:
40
 * - trivialNode - if found during exploration, return that node itself
41
 *    (like in case of OR, if "true" is found, makes sense to replace
42
 *     whole formula with "true")
43
 * - skipNode - as name suggests, skip them
44
 *    (like in case of OR, you may want to skip any "false" nodes found)
45
 *
46
 * Use a null node if you want to ignore any of the optimizations.
47
 */
48
558130
RewriteResponse flattenNode(TNode n, TNode trivialNode, TNode skipNode)
49
{
50
  typedef std::unordered_set<TNode> node_set;
51
52
1116260
  node_set visited;
53
558130
  visited.insert(skipNode);
54
55
1116260
  std::vector<TNode> toProcess;
56
558130
  toProcess.push_back(n);
57
58
558130
  Kind k = n.getKind();
59
  typedef std::vector<TNode> ChildList;
60
1116260
  ChildList childList;   //TNode should be fine, since 'n' is still there
61
62
1556392
  for (unsigned i = 0; i < toProcess.size(); ++ i) {
63
2010101
    TNode current = toProcess[i];
64
6665955
    for(unsigned j = 0, j_end = current.getNumChildren(); j < j_end; ++ j) {
65
10261559
      TNode child = current[j];
66
5667693
      if(visited.find(child) != visited.end()) {
67
1060250
        continue;
68
4607443
      } else if(child == trivialNode) {
69
13577
        return RewriteResponse(REWRITE_DONE, trivialNode);
70
      } else {
71
4593866
        visited.insert(child);
72
4593866
        if(child.getKind() == k)
73
454630
          toProcess.push_back(child);
74
        else
75
4139236
          childList.push_back(child);
76
      }
77
    }
78
  }
79
544553
  if (childList.size() == 0) return RewriteResponse(REWRITE_DONE, skipNode);
80
464704
  if (childList.size() == 1) return RewriteResponse(REWRITE_AGAIN, childList[0]);
81
82
  /* Trickery to stay under number of children possible in a node */
83
348190
  NodeManager* nodeManager = NodeManager::currentNM();
84
348190
  if (childList.size() < expr::NodeValue::MAX_CHILDREN)
85
  {
86
696380
    Node retNode = nodeManager->mkNode(k, childList);
87
348190
    return RewriteResponse(REWRITE_DONE, retNode);
88
  }
89
  else
90
  {
91
    Assert(childList.size()
92
           < static_cast<size_t>(expr::NodeValue::MAX_CHILDREN)
93
                 * static_cast<size_t>(expr::NodeValue::MAX_CHILDREN));
94
    NodeBuilder nb(k);
95
    ChildList::iterator cur = childList.begin(), next, en = childList.end();
96
    while (cur != en)
97
    {
98
      next = min(cur + expr::NodeValue::MAX_CHILDREN, en);
99
      nb << (nodeManager->mkNode(k, ChildList(cur, next)));
100
      cur = next;
101
    }
102
    return RewriteResponse(REWRITE_DONE, nb.constructNode());
103
  }
104
}
105
106
// Equality parity returns
107
// * 0 if no relation between a and b is found
108
// * 1 if a == b
109
// * 2 if a == not(b)
110
// * 3 or b == not(a)
111
4016838
inline int equalityParity(TNode a, TNode b){
112
4016838
  if(a == b){
113
11792
    return 1;
114
4005046
  }else if(a.getKind() == kind::NOT && a[0] == b){
115
80
    return 2;
116
4004966
  }else if(b.getKind() == kind::NOT && b[0] == a){
117
3077
    return 3;
118
  }else{
119
4001889
    return 0;
120
  }
121
}
122
123
105455
inline Node makeNegation(TNode n){
124
105455
  bool even = false;
125
115787
  while(n.getKind() == kind::NOT){
126
5166
    n = n[0];
127
5166
    even = !even;
128
  }
129
105455
  if(even){
130
5076
    return n;
131
  } else {
132
100379
    if(n.isConst()){
133
6748
      return NodeManager::currentNM()->mkConst(!n.getConst<bool>());
134
    }else{
135
93631
      return n.notNode();
136
    }
137
  }
138
}
139
140
13011351
RewriteResponse TheoryBoolRewriter::preRewrite(TNode n) {
141
13011351
  NodeManager* nodeManager = NodeManager::currentNM();
142
26022702
  Node tt = nodeManager->mkConst(true);
143
26022702
  Node ff = nodeManager->mkConst(false);
144
145
13011351
  switch(n.getKind()) {
146
2224846
  case kind::NOT: {
147
2224846
    if (n[0] == tt) return RewriteResponse(REWRITE_DONE, ff);
148
2199557
    if (n[0] == ff) return RewriteResponse(REWRITE_DONE, tt);
149
2157728
    if (n[0].getKind() == kind::NOT) return RewriteResponse(REWRITE_AGAIN, n[0][0]);
150
2075110
    break;
151
  }
152
3381248
  case kind::OR: {
153
3381248
    bool done = true;
154
3381248
    TNode::iterator i = n.begin(), iend = n.end();
155
22322218
    for(; i != iend; ++i) {
156
9526942
      if (*i == tt) return RewriteResponse(REWRITE_DONE, tt);
157
9470485
      if (*i == ff) done = false;
158
9470485
      if ((*i).getKind() == kind::OR) done = false;
159
    }
160
3324791
    if (!done) {
161
226709
      return flattenNode(n, /* trivialNode = */ tt, /* skipNode = */ ff);
162
    }
163
    // x v ... v x --> x
164
    unsigned ind, size;
165
3101905
    for (ind = 0, size = n.getNumChildren(); ind < size - 1; ++ind)
166
    {
167
3100312
      if (n[ind] != n[ind+1])
168
      {
169
3096489
        break;
170
      }
171
    }
172
3098082
    if (ind == size - 1)
173
    {
174
1593
      return RewriteResponse(REWRITE_AGAIN, n[0]);
175
    }
176
3096489
    break;
177
  }
178
3131692
  case kind::AND: {
179
3131692
    bool done = true;
180
3131692
    TNode::iterator i = n.begin(), iend = n.end();
181
41227182
    for(; i != iend; ++i) {
182
19229520
      if (*i == ff) return RewriteResponse(REWRITE_DONE, ff);
183
19047745
      if (*i == tt) done = false;
184
19047745
      if ((*i).getKind() == kind::AND) done = false;
185
    }
186
2949917
    if (!done) {
187
662842
      RewriteResponse ret = flattenNode(n, /* trivialNode = */ ff, /* skipNode = */ tt);
188
331421
      Debug("bool-flatten") << n << ": " << ret.d_node << std::endl;
189
331421
      return ret;
190
    }
191
    // x ^ ... ^ x --> x
192
    unsigned ind, size;
193
2637918
    for (ind = 0, size = n.getNumChildren(); ind < size - 1; ++ind)
194
    {
195
2635329
      if (n[ind] != n[ind+1])
196
      {
197
2615907
        break;
198
      }
199
    }
200
2618496
    if (ind == size - 1)
201
    {
202
2589
      return RewriteResponse(REWRITE_AGAIN, n[0]);
203
    }
204
2615907
    break;
205
  }
206
271864
  case kind::IMPLIES: {
207
271864
    if (n[0] == ff || n[1] == tt) return RewriteResponse(REWRITE_DONE, tt);
208
269984
    if (n[0] == tt && n[1] == ff) return RewriteResponse(REWRITE_DONE, ff);
209
269904
    if (n[0] == tt) return RewriteResponse(REWRITE_AGAIN, n[1]);
210
262109
    if (n[1] == ff) return RewriteResponse(REWRITE_AGAIN, makeNegation(n[0]));
211
261505
    break;
212
  }
213
1104940
  case kind::EQUAL: {
214
    // rewrite simple cases of IFF
215
1104940
    if(n[0] == tt) {
216
      // IFF true x
217
10744
      return RewriteResponse(REWRITE_AGAIN, n[1]);
218
1094196
    } else if(n[1] == tt) {
219
      // IFF x true
220
22025
      return RewriteResponse(REWRITE_AGAIN, n[0]);
221
1072171
    } else if(n[0] == ff) {
222
      // IFF false x
223
35176
      return RewriteResponse(REWRITE_AGAIN, makeNegation(n[1]));
224
1036995
    } else if(n[1] == ff) {
225
      // IFF x false
226
47981
      return RewriteResponse(REWRITE_AGAIN, makeNegation(n[0]));
227
989014
    } else if(n[0] == n[1]) {
228
      // IFF x x
229
2294
      return RewriteResponse(REWRITE_DONE, tt);
230
986720
    } else if(n[0].getKind() == kind::NOT && n[0][0] == n[1]) {
231
      // IFF (NOT x) x
232
208
      return RewriteResponse(REWRITE_DONE, ff);
233
986512
    } else if(n[1].getKind() == kind::NOT && n[1][0] == n[0]) {
234
      // IFF x (NOT x)
235
257
      return RewriteResponse(REWRITE_DONE, ff);
236
986255
    } else if(n[0].getKind() == kind::EQUAL && n[1].getKind() == kind::EQUAL) {
237
      // a : (= i x)
238
      // i : (= j k)
239
      // x : (= y z)
240
241
      // assume wlog k, z are constants and j is the same symbol as y
242
      // (iff (= j k) (= j z))
243
      // if k = z
244
      //  then (iff (= j k) (= j k)) => true
245
      // else
246
      //  (iff (= j k) (= j z)) <=> b
247
      //  b : (and (not (= j k)) (not (= j z)))
248
      //  (= j k) (= j z) | a b
249
      //  f       f       | t t
250
      //  f       t       | f f
251
      //  t       f       | f f
252
      //  t       t       | * f
253
      // * j cannot equal both k and z in a theory model
254
17587
      TNode t,c;
255
8798
      if (n[0][0].isConst()) {
256
771
        c = n[0][0];
257
771
        t = n[0][1];
258
      }
259
8027
      else if (n[0][1].isConst()) {
260
961
        c = n[0][1];
261
961
        t = n[0][0];
262
      }
263
8798
      bool matchesForm = false;
264
8798
      bool constantsEqual = false;
265
8798
      if (!c.isNull()) {
266
1732
        if (n[1][0] == t && n[1][1].isConst()) {
267
5
          matchesForm = true;
268
5
          constantsEqual = (n[1][1] == c);
269
        }
270
1727
        else if (n[1][1] == t && n[1][0].isConst()) {
271
4
          matchesForm = true;
272
4
          constantsEqual = (n[1][0] == c);
273
        }
274
      }
275
8798
      if(matchesForm){
276
9
        if(constantsEqual){
277
1
          return RewriteResponse(REWRITE_DONE, tt);
278
        }else{
279
16
          Cardinality kappa = t.getType().getCardinality();
280
16
          Cardinality two(2l);
281
8
          if(kappa.knownLessThanOrEqual(two)){
282
2
            return RewriteResponse(REWRITE_DONE, ff);
283
          }else{
284
12
            Node neitherEquality = (makeNegation(n[0])).andNode(makeNegation(n[1]));
285
6
            return RewriteResponse(REWRITE_AGAIN, neitherEquality);
286
          }
287
        }
288
      }
289
    }
290
    // sort
291
986246
    if (n[0].getId() > n[1].getId())
292
    {
293
      return RewriteResponse(REWRITE_AGAIN,
294
122342
                             nodeManager->mkNode(kind::EQUAL, n[1], n[0]));
295
    }
296
863904
    return RewriteResponse(REWRITE_DONE, n);
297
  }
298
878600
  case kind::XOR: {
299
    // rewrite simple cases of XOR
300
878600
    if(n[0] == tt) {
301
      // XOR true x
302
5405
      return RewriteResponse(REWRITE_AGAIN, makeNegation(n[1]));
303
873195
    } else if(n[1] == tt) {
304
      // XOR x true
305
13477
      return RewriteResponse(REWRITE_AGAIN, makeNegation(n[0]));
306
859718
    } else if(n[0] == ff) {
307
      // XOR false x
308
34345
      return RewriteResponse(REWRITE_AGAIN, n[1]);
309
825373
    } else if(n[1] == ff) {
310
      // XOR x false
311
99083
      return RewriteResponse(REWRITE_AGAIN, n[0]);
312
726290
    } else if(n[0] == n[1]) {
313
      // XOR x x
314
712
      return RewriteResponse(REWRITE_DONE, ff);
315
725578
    } else if(n[0].getKind() == kind::NOT && n[0][0] == n[1]) {
316
      // XOR (NOT x) x
317
21
      return RewriteResponse(REWRITE_DONE, tt);
318
725557
    } else if(n[1].getKind() == kind::NOT && n[1][0] == n[0]) {
319
      // XOR x (NOT x)
320
148
      return RewriteResponse(REWRITE_DONE, tt);
321
    }
322
725409
    break;
323
  }
324
1518034
  case kind::ITE: {
325
    // non-Boolean-valued ITEs should have been removed in place of
326
    // a variable
327
    // rewrite simple cases of ITE
328
1518034
    if (n[0].isConst()) {
329
235513
      if (n[0] == tt) {
330
        // ITE true x y
331
25582
        Debug("bool-ite") << "TheoryBoolRewriter::preRewrite_ITE: n[0] ==tt "
332
12791
                          << n << ": " << n[1] << std::endl;
333
12791
        return RewriteResponse(REWRITE_AGAIN, n[1]);
334
      } else {
335
222722
        Assert(n[0] == ff);
336
        // ITE false x y
337
445444
        Debug("bool-ite") << "TheoryBoolRewriter::preRewrite_ITE: n[0] ==ff "
338
222722
                          << n << ": " << n[1] << std::endl;
339
222722
        return RewriteResponse(REWRITE_AGAIN, n[2]);
340
      }
341
1282521
    } else if (n[1].isConst()) {
342
302883
      if (n[1] == tt && n[2] == ff) {
343
7932
        Debug("bool-ite")
344
3966
            << "TheoryBoolRewriter::preRewrite_ITE: n[1] ==tt && n[2] == ff "
345
3966
            << n << ": " << n[0] << std::endl;
346
3966
        return RewriteResponse(REWRITE_AGAIN, n[0]);
347
      }
348
298917
      else if (n[1] == ff && n[2] == tt) {
349
5600
        Debug("bool-ite")
350
2800
            << "TheoryBoolRewriter::preRewrite_ITE: n[1] ==ff && n[2] == tt "
351
2800
            << n << ": " << n[0].notNode() << std::endl;
352
2800
        return RewriteResponse(REWRITE_AGAIN, makeNegation(n[0]));
353
      }
354
    }
355
356
1275755
    if (n[0].getKind() == kind::NOT)
357
    {
358
      // ite(not(c), x, y) ---> ite(c, y, x)
359
      return RewriteResponse(
360
101333
          REWRITE_AGAIN, nodeManager->mkNode(kind::ITE, n[0][0], n[2], n[1]));
361
    }
362
363
    int parityTmp;
364
1174422
    if ((parityTmp = equalityParity(n[1], n[2])) != 0) {
365
18396
      Node resp = (parityTmp == 1) ? (Node)n[1] : n[0].eqNode(n[1]);
366
18396
      Debug("bool-ite")
367
9198
          << "TheoryBoolRewriter::preRewrite_ITE:  equalityParity n[1], n[2] "
368
9198
          << parityTmp << " " << n << ": " << resp << std::endl;
369
9198
      return RewriteResponse(REWRITE_AGAIN, resp);
370
    // Curiously, this rewrite affects several benchmarks dramatically, including copy_array and some simple_startup - disable for now
371
    // } else if (n[0].getKind() == kind::NOT) {
372
    //   return RewriteResponse(REWRITE_AGAIN, n[0][0].iteNode(n[2], n[1]));
373
1165224
    } else if(!n[1].isConst() && (parityTmp = equalityParity(n[0], n[1])) != 0){
374
      // (parityTmp == 1) if n[0] == n[1]
375
      // otherwise, n[0] == not(n[1]) or not(n[0]) == n[1]
376
377
      // if n[1] is constant this can loop, this is possible in prewrite
378
5662
      Node resp = n[0].iteNode( (parityTmp == 1) ? tt : ff, n[2]);
379
5662
      Debug("bool-ite")
380
2831
          << "TheoryBoolRewriter::preRewrite_ITE: equalityParity n[0], n[1] "
381
2831
          << parityTmp << " " << n << ": " << resp << std::endl;
382
2831
      return RewriteResponse(REWRITE_AGAIN, resp);
383
1162393
    } else if(!n[2].isConst() && (parityTmp = equalityParity(n[0], n[2])) != 0){
384
      // (parityTmp == 1) if n[0] == n[2]
385
      // otherwise, n[0] == not(n[2]) or not(n[0]) == n[2]
386
644
      Node resp = n[0].iteNode(n[1], (parityTmp == 1) ? ff : tt);
387
644
      Debug("bool-ite")
388
322
          << "TheoryBoolRewriter::preRewrite_ITE: equalityParity n[0], n[2] "
389
322
          << parityTmp << " " << n << ": " << resp << std::endl;
390
322
      return RewriteResponse(REWRITE_AGAIN, resp);
391
3509895
    } else if(n[1].getKind() == kind::ITE &&
392
2347824
              (parityTmp = equalityParity(n[0], n[1][0])) != 0){
393
      // (parityTmp == 1) then n : (ite c (ite c x y) z)
394
      // (parityTmp > 1)  then n : (ite c (ite (not c) x y) z) or
395
      // n: (ite (not c) (ite c x y) z)
396
2530
      Node resp = n[0].iteNode((parityTmp == 1) ? n[1][1] : n[1][2], n[2]);
397
2530
      Debug("bool-ite")
398
1265
          << "TheoryBoolRewriter::preRewrite: equalityParity n[0], n[1][0] "
399
1265
          << parityTmp << " " << n << ": " << resp << std::endl;
400
1265
      return RewriteResponse(REWRITE_AGAIN, resp);
401
3920789
    } else if(n[2].getKind() == kind::ITE &&
402
2759983
              (parityTmp = equalityParity(n[0], n[2][0])) != 0){
403
      // (parityTmp == 1) then n : (ite c x (ite c y z))
404
      // (parityTmp > 1)  then n : (ite c x (ite (not c) y z)) or
405
      // n: (ite (not c) x (ite c y z))
406
2666
      Node resp = n[0].iteNode(n[1], (parityTmp == 1) ? n[2][2] : n[2][1]);
407
2666
      Debug("bool-ite")
408
1333
          << "TheoryBoolRewriter::preRewrite: equalityParity n[0], n[2][0] "
409
1333
          << parityTmp << " " << n << ": " << resp << std::endl;
410
1333
      return RewriteResponse(REWRITE_AGAIN, resp);
411
    }
412
413
    // Rewrites for ITEs with a constant branch. These rewrites are applied
414
    // after the parity rewrites above because they may simplify ITEs such as
415
    // `(ite c c true)` to `(ite c true true)`. As a result, we avoid
416
    // introducing an unnecessary conjunction/disjunction here.
417
1159473
    if (n[1].isConst() && (n[1] == tt || n[1] == ff))
418
    {
419
      // ITE C true y --> C v y
420
      // ITE C false y --> ~C ^ y
421
      Node resp =
422
86854
          n[1] == tt ? n[0].orNode(n[2]) : (n[0].negate()).andNode(n[2]);
423
86854
      Debug("bool-ite") << "TheoryBoolRewriter::preRewrite_ITE: n[1] const "
424
43427
                        << n << ": " << resp << std::endl;
425
43427
      return RewriteResponse(REWRITE_AGAIN, resp);
426
    }
427
1116046
    else if (n[2].isConst() && (n[2] == tt || n[2] == ff))
428
    {
429
      // ITE C x true --> ~C v x
430
      // ITE C x false --> C ^ x
431
      Node resp =
432
93686
          n[2] == tt ? (n[0].negate()).orNode(n[1]) : n[0].andNode(n[1]);
433
93686
      Debug("bool-ite") << "TheoryBoolRewriter::preRewrite_ITE: n[2] const "
434
46843
                        << n << ": " << resp << std::endl;
435
46843
      return RewriteResponse(REWRITE_AGAIN, resp);
436
    }
437
438
1069203
    break;
439
  }
440
500127
  default:
441
500127
    return RewriteResponse(REWRITE_DONE, n);
442
  }
443
9843623
  return RewriteResponse(REWRITE_DONE, n);
444
}
445
446
}  // namespace booleans
447
}  // namespace theory
448
22746
}  // namespace cvc5