GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/booleans/theory_bool_rewriter.cpp Lines: 223 231 96.5 %
Date: 2021-09-10 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
12130253
RewriteResponse TheoryBoolRewriter::postRewrite(TNode node) {
32
12130253
  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
716971
RewriteResponse flattenNode(TNode n, TNode trivialNode, TNode skipNode)
49
{
50
  typedef std::unordered_set<TNode> node_set;
51
52
1433942
  node_set visited;
53
716971
  visited.insert(skipNode);
54
55
1433942
  std::vector<TNode> toProcess;
56
716971
  toProcess.push_back(n);
57
58
716971
  Kind k = n.getKind();
59
  typedef std::vector<TNode> ChildList;
60
1433942
  ChildList childList;   //TNode should be fine, since 'n' is still there
61
62
2082837
  for (unsigned i = 0; i < toProcess.size(); ++ i) {
63
2746776
    TNode current = toProcess[i];
64
8338165
    for(unsigned j = 0, j_end = current.getNumChildren(); j < j_end; ++ j) {
65
12754193
      TNode child = current[j];
66
6972299
      if(visited.find(child) != visited.end()) {
67
1175361
        continue;
68
5796938
      } else if(child == trivialNode) {
69
15044
        return RewriteResponse(REWRITE_DONE, trivialNode);
70
      } else {
71
5781894
        visited.insert(child);
72
5781894
        if(child.getKind() == k)
73
664773
          toProcess.push_back(child);
74
        else
75
5117121
          childList.push_back(child);
76
      }
77
    }
78
  }
79
701927
  if (childList.size() == 0) return RewriteResponse(REWRITE_DONE, skipNode);
80
610941
  if (childList.size() == 1) return RewriteResponse(REWRITE_AGAIN, childList[0]);
81
82
  /* Trickery to stay under number of children possible in a node */
83
465515
  NodeManager* nodeManager = NodeManager::currentNM();
84
465515
  if (childList.size() < expr::NodeValue::MAX_CHILDREN)
85
  {
86
931030
    Node retNode = nodeManager->mkNode(k, childList);
87
465515
    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
5023128
inline int equalityParity(TNode a, TNode b){
112
5023128
  if(a == b){
113
15417
    return 1;
114
5007711
  }else if(a.getKind() == kind::NOT && a[0] == b){
115
136
    return 2;
116
5007575
  }else if(b.getKind() == kind::NOT && b[0] == a){
117
3455
    return 3;
118
  }else{
119
5004120
    return 0;
120
  }
121
}
122
123
144626
inline Node makeNegation(TNode n){
124
144626
  bool even = false;
125
158952
  while(n.getKind() == kind::NOT){
126
7163
    n = n[0];
127
7163
    even = !even;
128
  }
129
144626
  if(even){
130
7015
    return n;
131
  } else {
132
137611
    if(n.isConst()){
133
7121
      return NodeManager::currentNM()->mkConst(!n.getConst<bool>());
134
    }else{
135
130490
      return n.notNode();
136
    }
137
  }
138
}
139
140
19075620
RewriteResponse TheoryBoolRewriter::preRewrite(TNode n) {
141
19075620
  NodeManager* nodeManager = NodeManager::currentNM();
142
38151240
  Node tt = nodeManager->mkConst(true);
143
38151240
  Node ff = nodeManager->mkConst(false);
144
145
19075620
  switch(n.getKind()) {
146
3908775
  case kind::NOT: {
147
3908775
    if (n[0] == tt) return RewriteResponse(REWRITE_DONE, ff);
148
3827809
    if (n[0] == ff) return RewriteResponse(REWRITE_DONE, tt);
149
3740863
    if (n[0].getKind() == kind::NOT) return RewriteResponse(REWRITE_AGAIN, n[0][0]);
150
3566167
    break;
151
  }
152
4665824
  case kind::OR: {
153
4665824
    bool done = true;
154
4665824
    TNode::iterator i = n.begin(), iend = n.end();
155
32528954
    for(; i != iend; ++i) {
156
13992566
      if (*i == tt) return RewriteResponse(REWRITE_DONE, tt);
157
13931565
      if (*i == ff) done = false;
158
13931565
      if ((*i).getKind() == kind::OR) done = false;
159
    }
160
4604823
    if (!done) {
161
291254
      return flattenNode(n, /* trivialNode = */ tt, /* skipNode = */ ff);
162
    }
163
    // x v ... v x --> x
164
    unsigned ind, size;
165
4321628
    for (ind = 0, size = n.getNumChildren(); ind < size - 1; ++ind)
166
    {
167
4318264
      if (n[ind] != n[ind+1])
168
      {
169
4310205
        break;
170
      }
171
    }
172
4313569
    if (ind == size - 1)
173
    {
174
3364
      return RewriteResponse(REWRITE_AGAIN, n[0]);
175
    }
176
4310205
    break;
177
  }
178
4617652
  case kind::AND: {
179
4617652
    bool done = true;
180
4617652
    TNode::iterator i = n.begin(), iend = n.end();
181
57438298
    for(; i != iend; ++i) {
182
26621559
      if (*i == ff) return RewriteResponse(REWRITE_DONE, ff);
183
26410323
      if (*i == tt) done = false;
184
26410323
      if ((*i).getKind() == kind::AND) done = false;
185
    }
186
4406416
    if (!done) {
187
851434
      RewriteResponse ret = flattenNode(n, /* trivialNode = */ ff, /* skipNode = */ tt);
188
425717
      Debug("bool-flatten") << n << ": " << ret.d_node << std::endl;
189
425717
      return ret;
190
    }
191
    // x ^ ... ^ x --> x
192
    unsigned ind, size;
193
4004526
    for (ind = 0, size = n.getNumChildren(); ind < size - 1; ++ind)
194
    {
195
3999739
      if (n[ind] != n[ind+1])
196
      {
197
3975912
        break;
198
      }
199
    }
200
3980699
    if (ind == size - 1)
201
    {
202
4787
      return RewriteResponse(REWRITE_AGAIN, n[0]);
203
    }
204
3975912
    break;
205
  }
206
528082
  case kind::IMPLIES: {
207
528082
    if (n[0] == ff || n[1] == tt) return RewriteResponse(REWRITE_DONE, tt);
208
525457
    if (n[0] == tt && n[1] == ff) return RewriteResponse(REWRITE_DONE, ff);
209
525353
    if (n[0] == tt) return RewriteResponse(REWRITE_AGAIN, n[1]);
210
513972
    if (n[1] == ff) return RewriteResponse(REWRITE_AGAIN, makeNegation(n[0]));
211
504208
    break;
212
  }
213
1586828
  case kind::EQUAL: {
214
    // rewrite simple cases of IFF
215
1586828
    if(n[0] == tt) {
216
      // IFF true x
217
13596
      return RewriteResponse(REWRITE_AGAIN, n[1]);
218
1573232
    } else if(n[1] == tt) {
219
      // IFF x true
220
29408
      return RewriteResponse(REWRITE_AGAIN, n[0]);
221
1543824
    } else if(n[0] == ff) {
222
      // IFF false x
223
43046
      return RewriteResponse(REWRITE_AGAIN, makeNegation(n[1]));
224
1500778
    } else if(n[1] == ff) {
225
      // IFF x false
226
66271
      return RewriteResponse(REWRITE_AGAIN, makeNegation(n[0]));
227
1434507
    } else if(n[0] == n[1]) {
228
      // IFF x x
229
12921
      return RewriteResponse(REWRITE_DONE, tt);
230
1421586
    } else if(n[0].getKind() == kind::NOT && n[0][0] == n[1]) {
231
      // IFF (NOT x) x
232
311
      return RewriteResponse(REWRITE_DONE, ff);
233
1421275
    } else if(n[1].getKind() == kind::NOT && n[1][0] == n[0]) {
234
      // IFF x (NOT x)
235
601
      return RewriteResponse(REWRITE_DONE, ff);
236
1420674
    } 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
41356
      TNode t,c;
255
20686
      if (n[0][0].isConst()) {
256
956
        c = n[0][0];
257
956
        t = n[0][1];
258
      }
259
19730
      else if (n[0][1].isConst()) {
260
1796
        c = n[0][1];
261
1796
        t = n[0][0];
262
      }
263
20686
      bool matchesForm = false;
264
20686
      bool constantsEqual = false;
265
20686
      if (!c.isNull()) {
266
2752
        if (n[1][0] == t && n[1][1].isConst()) {
267
8
          matchesForm = true;
268
8
          constantsEqual = (n[1][1] == c);
269
        }
270
2744
        else if (n[1][1] == t && n[1][0].isConst()) {
271
8
          matchesForm = true;
272
8
          constantsEqual = (n[1][0] == c);
273
        }
274
      }
275
20686
      if(matchesForm){
276
16
        if(constantsEqual){
277
1
          return RewriteResponse(REWRITE_DONE, tt);
278
        }else{
279
30
          Cardinality kappa = t.getType().getCardinality();
280
30
          Cardinality two(2l);
281
15
          if(kappa.knownLessThanOrEqual(two)){
282
2
            return RewriteResponse(REWRITE_DONE, ff);
283
          }else{
284
26
            Node neitherEquality = (makeNegation(n[0])).andNode(makeNegation(n[1]));
285
13
            return RewriteResponse(REWRITE_AGAIN, neitherEquality);
286
          }
287
        }
288
      }
289
    }
290
    // sort
291
1420658
    if (n[0].getId() > n[1].getId())
292
    {
293
      return RewriteResponse(REWRITE_AGAIN,
294
187264
                             nodeManager->mkNode(kind::EQUAL, n[1], n[0]));
295
    }
296
1233394
    return RewriteResponse(REWRITE_DONE, n);
297
  }
298
1210295
  case kind::XOR: {
299
    // rewrite simple cases of XOR
300
1210295
    if(n[0] == tt) {
301
      // XOR true x
302
6412
      return RewriteResponse(REWRITE_AGAIN, makeNegation(n[1]));
303
1203883
    } else if(n[1] == tt) {
304
      // XOR x true
305
15454
      return RewriteResponse(REWRITE_AGAIN, makeNegation(n[0]));
306
1188429
    } else if(n[0] == ff) {
307
      // XOR false x
308
37999
      return RewriteResponse(REWRITE_AGAIN, n[1]);
309
1150430
    } else if(n[1] == ff) {
310
      // XOR x false
311
114634
      return RewriteResponse(REWRITE_AGAIN, n[0]);
312
1035796
    } else if(n[0] == n[1]) {
313
      // XOR x x
314
937
      return RewriteResponse(REWRITE_DONE, ff);
315
1034859
    } else if(n[0].getKind() == kind::NOT && n[0][0] == n[1]) {
316
      // XOR (NOT x) x
317
29
      return RewriteResponse(REWRITE_DONE, tt);
318
1034830
    } else if(n[1].getKind() == kind::NOT && n[1][0] == n[0]) {
319
      // XOR x (NOT x)
320
196
      return RewriteResponse(REWRITE_DONE, tt);
321
    }
322
1034634
    break;
323
  }
324
1868563
  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
1868563
    if (n[0].isConst()) {
329
244168
      if (n[0] == tt) {
330
        // ITE true x y
331
32406
        Debug("bool-ite") << "TheoryBoolRewriter::preRewrite_ITE: n[0] ==tt "
332
16203
                          << n << ": " << n[1] << std::endl;
333
16203
        return RewriteResponse(REWRITE_AGAIN, n[1]);
334
      } else {
335
227965
        Assert(n[0] == ff);
336
        // ITE false x y
337
455930
        Debug("bool-ite") << "TheoryBoolRewriter::preRewrite_ITE: n[0] ==ff "
338
227965
                          << n << ": " << n[1] << std::endl;
339
227965
        return RewriteResponse(REWRITE_AGAIN, n[2]);
340
      }
341
1624395
    } else if (n[1].isConst()) {
342
364096
      if (n[1] == tt && n[2] == ff) {
343
9336
        Debug("bool-ite")
344
4668
            << "TheoryBoolRewriter::preRewrite_ITE: n[1] ==tt && n[2] == ff "
345
4668
            << n << ": " << n[0] << std::endl;
346
4668
        return RewriteResponse(REWRITE_AGAIN, n[0]);
347
      }
348
359428
      else if (n[1] == ff && n[2] == tt) {
349
7306
        Debug("bool-ite")
350
3653
            << "TheoryBoolRewriter::preRewrite_ITE: n[1] ==ff && n[2] == tt "
351
3653
            << n << ": " << n[0].notNode() << std::endl;
352
3653
        return RewriteResponse(REWRITE_AGAIN, makeNegation(n[0]));
353
      }
354
    }
355
356
1616074
    if (n[0].getKind() == kind::NOT)
357
    {
358
      // ite(not(c), x, y) ---> ite(c, y, x)
359
      return RewriteResponse(
360
117537
          REWRITE_AGAIN, nodeManager->mkNode(kind::ITE, n[0][0], n[2], n[1]));
361
    }
362
363
    int parityTmp;
364
1498537
    if ((parityTmp = equalityParity(n[1], n[2])) != 0) {
365
23890
      Node resp = (parityTmp == 1) ? (Node)n[1] : n[0].eqNode(n[1]);
366
23890
      Debug("bool-ite")
367
11945
          << "TheoryBoolRewriter::preRewrite_ITE:  equalityParity n[1], n[2] "
368
11945
          << parityTmp << " " << n << ": " << resp << std::endl;
369
11945
      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
1486592
    } 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
5814
      Node resp = n[0].iteNode( (parityTmp == 1) ? tt : ff, n[2]);
379
5814
      Debug("bool-ite")
380
2907
          << "TheoryBoolRewriter::preRewrite_ITE: equalityParity n[0], n[1] "
381
2907
          << parityTmp << " " << n << ": " << resp << std::endl;
382
2907
      return RewriteResponse(REWRITE_AGAIN, resp);
383
1483685
    } 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
756
      Node resp = n[0].iteNode(n[1], (parityTmp == 1) ? ff : tt);
387
756
      Debug("bool-ite")
388
378
          << "TheoryBoolRewriter::preRewrite_ITE: equalityParity n[0], n[2] "
389
378
          << parityTmp << " " << n << ": " << resp << std::endl;
390
378
      return RewriteResponse(REWRITE_AGAIN, resp);
391
4352167
    } else if(n[1].getKind() == kind::ITE &&
392
2868860
              (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
3624
      Node resp = n[0].iteNode((parityTmp == 1) ? n[1][1] : n[1][2], n[2]);
397
3624
      Debug("bool-ite")
398
1812
          << "TheoryBoolRewriter::preRewrite: equalityParity n[0], n[1][0] "
399
1812
          << parityTmp << " " << n << ": " << resp << std::endl;
400
1812
      return RewriteResponse(REWRITE_AGAIN, resp);
401
4780006
    } else if(n[2].getKind() == kind::ITE &&
402
3298511
              (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
3932
      Node resp = n[0].iteNode(n[1], (parityTmp == 1) ? n[2][2] : n[2][1]);
407
3932
      Debug("bool-ite")
408
1966
          << "TheoryBoolRewriter::preRewrite: equalityParity n[0], n[2][0] "
409
1966
          << parityTmp << " " << n << ": " << resp << std::endl;
410
1966
      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
1479529
    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
93484
          n[1] == tt ? n[0].orNode(n[2]) : (n[0].negate()).andNode(n[2]);
423
93484
      Debug("bool-ite") << "TheoryBoolRewriter::preRewrite_ITE: n[1] const "
424
46742
                        << n << ": " << resp << std::endl;
425
46742
      return RewriteResponse(REWRITE_AGAIN, resp);
426
    }
427
1432787
    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
109792
          n[2] == tt ? (n[0].negate()).orNode(n[1]) : n[0].andNode(n[1]);
433
109792
      Debug("bool-ite") << "TheoryBoolRewriter::preRewrite_ITE: n[2] const "
434
54896
                        << n << ": " << resp << std::endl;
435
54896
      return RewriteResponse(REWRITE_AGAIN, resp);
436
    }
437
438
1377891
    break;
439
  }
440
689601
  default:
441
689601
    return RewriteResponse(REWRITE_DONE, n);
442
  }
443
14769017
  return RewriteResponse(REWRITE_DONE, n);
444
}
445
446
}  // namespace booleans
447
}  // namespace theory
448
29502
}  // namespace cvc5