GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/booleans/theory_bool_rewriter.cpp Lines: 221 229 96.5 %
Date: 2021-05-22 Branches: 866 1565 55.3 %

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 <algorithm>
20
#include <unordered_set>
21
22
#include "expr/node_value.h"
23
#include "theory/booleans/theory_bool_rewriter.h"
24
25
namespace cvc5 {
26
namespace theory {
27
namespace booleans {
28
29
12081682
RewriteResponse TheoryBoolRewriter::postRewrite(TNode node) {
30
12081682
  return preRewrite(node);
31
}
32
33
/**
34
 * flattenNode looks for children of same kind, and if found merges
35
 * them into the parent.
36
 *
37
 * It simultaneously handles a couple of other optimizations:
38
 * - trivialNode - if found during exploration, return that node itself
39
 *    (like in case of OR, if "true" is found, makes sense to replace
40
 *     whole formula with "true")
41
 * - skipNode - as name suggests, skip them
42
 *    (like in case of OR, you may want to skip any "false" nodes found)
43
 *
44
 * Use a null node if you want to ignore any of the optimizations.
45
 */
46
775125
RewriteResponse flattenNode(TNode n, TNode trivialNode, TNode skipNode)
47
{
48
  typedef std::unordered_set<TNode> node_set;
49
50
1550250
  node_set visited;
51
775125
  visited.insert(skipNode);
52
53
1550250
  std::vector<TNode> toProcess;
54
775125
  toProcess.push_back(n);
55
56
775125
  Kind k = n.getKind();
57
  typedef std::vector<TNode> ChildList;
58
1550250
  ChildList childList;   //TNode should be fine, since 'n' is still there
59
60
2283621
  for (unsigned i = 0; i < toProcess.size(); ++ i) {
61
3033213
    TNode current = toProcess[i];
62
9894665
    for(unsigned j = 0, j_end = current.getNumChildren(); j < j_end; ++ j) {
63
15709943
      TNode child = current[j];
64
8386169
      if(visited.find(child) != visited.end()) {
65
1046174
        continue;
66
7339995
      } else if(child == trivialNode) {
67
16221
        return RewriteResponse(REWRITE_DONE, trivialNode);
68
      } else {
69
7323774
        visited.insert(child);
70
7323774
        if(child.getKind() == k)
71
750518
          toProcess.push_back(child);
72
        else
73
6573256
          childList.push_back(child);
74
      }
75
    }
76
  }
77
758904
  if (childList.size() == 0) return RewriteResponse(REWRITE_DONE, skipNode);
78
675663
  if (childList.size() == 1) return RewriteResponse(REWRITE_AGAIN, childList[0]);
79
80
  /* Trickery to stay under number of children possible in a node */
81
521286
  NodeManager* nodeManager = NodeManager::currentNM();
82
521286
  if (childList.size() < expr::NodeValue::MAX_CHILDREN)
83
  {
84
1042572
    Node retNode = nodeManager->mkNode(k, childList);
85
521286
    return RewriteResponse(REWRITE_DONE, retNode);
86
  }
87
  else
88
  {
89
    Assert(childList.size()
90
           < static_cast<size_t>(expr::NodeValue::MAX_CHILDREN)
91
                 * static_cast<size_t>(expr::NodeValue::MAX_CHILDREN));
92
    NodeBuilder nb(k);
93
    ChildList::iterator cur = childList.begin(), next, en = childList.end();
94
    while (cur != en)
95
    {
96
      next = min(cur + expr::NodeValue::MAX_CHILDREN, en);
97
      nb << (nodeManager->mkNode(k, ChildList(cur, next)));
98
      cur = next;
99
    }
100
    return RewriteResponse(REWRITE_DONE, nb.constructNode());
101
  }
102
}
103
104
// Equality parity returns
105
// * 0 if no relation between a and b is found
106
// * 1 if a == b
107
// * 2 if a == not(b)
108
// * 3 or b == not(a)
109
4691043
inline int equalityParity(TNode a, TNode b){
110
4691043
  if(a == b){
111
10420
    return 1;
112
4680623
  }else if(a.getKind() == kind::NOT && a[0] == b){
113
144
    return 2;
114
4680479
  }else if(b.getKind() == kind::NOT && b[0] == a){
115
1044
    return 3;
116
  }else{
117
4679435
    return 0;
118
  }
119
}
120
121
155185
inline Node makeNegation(TNode n){
122
155185
  bool even = false;
123
178799
  while(n.getKind() == kind::NOT){
124
11807
    n = n[0];
125
11807
    even = !even;
126
  }
127
155185
  if(even){
128
11355
    return n;
129
  } else {
130
143830
    if(n.isConst()){
131
2488
      return NodeManager::currentNM()->mkConst(!n.getConst<bool>());
132
    }else{
133
141342
      return n.notNode();
134
    }
135
  }
136
}
137
138
18798270
RewriteResponse TheoryBoolRewriter::preRewrite(TNode n) {
139
18798270
  NodeManager* nodeManager = NodeManager::currentNM();
140
37596540
  Node tt = nodeManager->mkConst(true);
141
37596540
  Node ff = nodeManager->mkConst(false);
142
143
18798270
  switch(n.getKind()) {
144
3661822
  case kind::NOT: {
145
3661822
    if (n[0] == tt) return RewriteResponse(REWRITE_DONE, ff);
146
3588953
    if (n[0] == ff) return RewriteResponse(REWRITE_DONE, tt);
147
3502194
    if (n[0].getKind() == kind::NOT) return RewriteResponse(REWRITE_AGAIN, n[0][0]);
148
3355315
    break;
149
  }
150
4572775
  case kind::OR: {
151
4572775
    bool done = true;
152
4572775
    TNode::iterator i = n.begin(), iend = n.end();
153
31213581
    for(; i != iend; ++i) {
154
13377765
      if (*i == tt) return RewriteResponse(REWRITE_DONE, tt);
155
13320403
      if (*i == ff) done = false;
156
13320403
      if ((*i).getKind() == kind::OR) done = false;
157
    }
158
4515413
    if (!done) {
159
296902
      return flattenNode(n, /* trivialNode = */ tt, /* skipNode = */ ff);
160
    }
161
    // x v ... v x --> x
162
    unsigned ind, size;
163
4227055
    for (ind = 0, size = n.getNumChildren(); ind < size - 1; ++ind)
164
    {
165
4223175
      if (n[ind] != n[ind+1])
166
      {
167
4214631
        break;
168
      }
169
    }
170
4218511
    if (ind == size - 1)
171
    {
172
3880
      return RewriteResponse(REWRITE_AGAIN, n[0]);
173
    }
174
4214631
    break;
175
  }
176
4663952
  case kind::AND: {
177
4663952
    bool done = true;
178
4663952
    TNode::iterator i = n.begin(), iend = n.end();
179
58814020
    for(; i != iend; ++i) {
180
27271704
      if (*i == ff) return RewriteResponse(REWRITE_DONE, ff);
181
27075034
      if (*i == tt) done = false;
182
27075034
      if ((*i).getKind() == kind::AND) done = false;
183
    }
184
4467282
    if (!done) {
185
956446
      RewriteResponse ret = flattenNode(n, /* trivialNode = */ ff, /* skipNode = */ tt);
186
478223
      Debug("bool-flatten") << n << ": " << ret.d_node << std::endl;
187
478223
      return ret;
188
    }
189
    // x ^ ... ^ x --> x
190
    unsigned ind, size;
191
4000681
    for (ind = 0, size = n.getNumChildren(); ind < size - 1; ++ind)
192
    {
193
3995378
      if (n[ind] != n[ind+1])
194
      {
195
3983756
        break;
196
      }
197
    }
198
3989059
    if (ind == size - 1)
199
    {
200
5303
      return RewriteResponse(REWRITE_AGAIN, n[0]);
201
    }
202
3983756
    break;
203
  }
204
517908
  case kind::IMPLIES: {
205
517908
    if (n[0] == ff || n[1] == tt) return RewriteResponse(REWRITE_DONE, tt);
206
515029
    if (n[0] == tt && n[1] == ff) return RewriteResponse(REWRITE_DONE, ff);
207
514921
    if (n[0] == tt) return RewriteResponse(REWRITE_AGAIN, n[1]);
208
505005
    if (n[1] == ff) return RewriteResponse(REWRITE_AGAIN, makeNegation(n[0]));
209
484475
    break;
210
  }
211
1601957
  case kind::EQUAL: {
212
    // rewrite simple cases of IFF
213
1601957
    if(n[0] == tt) {
214
      // IFF true x
215
10944
      return RewriteResponse(REWRITE_AGAIN, n[1]);
216
1591013
    } else if(n[1] == tt) {
217
      // IFF x true
218
29510
      return RewriteResponse(REWRITE_AGAIN, n[0]);
219
1561503
    } else if(n[0] == ff) {
220
      // IFF false x
221
36598
      return RewriteResponse(REWRITE_AGAIN, makeNegation(n[1]));
222
1524905
    } else if(n[1] == ff) {
223
      // IFF x false
224
75192
      return RewriteResponse(REWRITE_AGAIN, makeNegation(n[0]));
225
1449713
    } else if(n[0] == n[1]) {
226
      // IFF x x
227
27465
      return RewriteResponse(REWRITE_DONE, tt);
228
1422248
    } else if(n[0].getKind() == kind::NOT && n[0][0] == n[1]) {
229
      // IFF (NOT x) x
230
779
      return RewriteResponse(REWRITE_DONE, ff);
231
1421469
    } else if(n[1].getKind() == kind::NOT && n[1][0] == n[0]) {
232
      // IFF x (NOT x)
233
156
      return RewriteResponse(REWRITE_DONE, ff);
234
1421313
    } else if(n[0].getKind() == kind::EQUAL && n[1].getKind() == kind::EQUAL) {
235
      // a : (= i x)
236
      // i : (= j k)
237
      // x : (= y z)
238
239
      // assume wlog k, z are constants and j is the same symbol as y
240
      // (iff (= j k) (= j z))
241
      // if k = z
242
      //  then (iff (= j k) (= j k)) => true
243
      // else
244
      //  (iff (= j k) (= j z)) <=> b
245
      //  b : (and (not (= j k)) (not (= j z)))
246
      //  (= j k) (= j z) | a b
247
      //  f       f       | t t
248
      //  f       t       | f f
249
      //  t       f       | f f
250
      //  t       t       | * f
251
      // * j cannot equal both k and z in a theory model
252
27820
      TNode t,c;
253
13918
      if (n[0][0].isConst()) {
254
810
        c = n[0][0];
255
810
        t = n[0][1];
256
      }
257
13108
      else if (n[0][1].isConst()) {
258
2313
        c = n[0][1];
259
2313
        t = n[0][0];
260
      }
261
13918
      bool matchesForm = false;
262
13918
      bool constantsEqual = false;
263
13918
      if (!c.isNull()) {
264
3123
        if (n[1][0] == t && n[1][1].isConst()) {
265
8
          matchesForm = true;
266
8
          constantsEqual = (n[1][1] == c);
267
        }
268
3115
        else if (n[1][1] == t && n[1][0].isConst()) {
269
8
          matchesForm = true;
270
8
          constantsEqual = (n[1][0] == c);
271
        }
272
      }
273
13918
      if(matchesForm){
274
16
        if(constantsEqual){
275
1
          return RewriteResponse(REWRITE_DONE, tt);
276
        }else{
277
30
          Cardinality kappa = t.getType().getCardinality();
278
30
          Cardinality two(2l);
279
15
          if(kappa.knownLessThanOrEqual(two)){
280
2
            return RewriteResponse(REWRITE_DONE, ff);
281
          }else{
282
26
            Node neitherEquality = (makeNegation(n[0])).andNode(makeNegation(n[1]));
283
13
            return RewriteResponse(REWRITE_AGAIN, neitherEquality);
284
          }
285
        }
286
      }
287
    }
288
1421297
    break;
289
  }
290
1254701
  case kind::XOR: {
291
    // rewrite simple cases of XOR
292
1254701
    if(n[0] == tt) {
293
      // XOR true x
294
4237
      return RewriteResponse(REWRITE_AGAIN, makeNegation(n[1]));
295
1250464
    } else if(n[1] == tt) {
296
      // XOR x true
297
14623
      return RewriteResponse(REWRITE_AGAIN, makeNegation(n[0]));
298
1235841
    } else if(n[0] == ff) {
299
      // XOR false x
300
35159
      return RewriteResponse(REWRITE_AGAIN, n[1]);
301
1200682
    } else if(n[1] == ff) {
302
      // XOR x false
303
114833
      return RewriteResponse(REWRITE_AGAIN, n[0]);
304
1085849
    } else if(n[0] == n[1]) {
305
      // XOR x x
306
1277
      return RewriteResponse(REWRITE_DONE, ff);
307
1084572
    } else if(n[0].getKind() == kind::NOT && n[0][0] == n[1]) {
308
      // XOR (NOT x) x
309
36
      return RewriteResponse(REWRITE_DONE, tt);
310
1084536
    } else if(n[1].getKind() == kind::NOT && n[1][0] == n[0]) {
311
      // XOR x (NOT x)
312
151
      return RewriteResponse(REWRITE_DONE, tt);
313
    }
314
1084385
    break;
315
  }
316
1876355
  case kind::ITE: {
317
    // non-Boolean-valued ITEs should have been removed in place of
318
    // a variable
319
    // rewrite simple cases of ITE
320
1876355
    if (n[0].isConst()) {
321
245144
      if (n[0] == tt) {
322
        // ITE true x y
323
28576
        Debug("bool-ite") << "TheoryBoolRewriter::preRewrite_ITE: n[0] ==tt "
324
14288
                          << n << ": " << n[1] << std::endl;
325
14288
        return RewriteResponse(REWRITE_AGAIN, n[1]);
326
      } else {
327
230856
        Assert(n[0] == ff);
328
        // ITE false x y
329
461712
        Debug("bool-ite") << "TheoryBoolRewriter::preRewrite_ITE: n[0] ==ff "
330
230856
                          << n << ": " << n[1] << std::endl;
331
230856
        return RewriteResponse(REWRITE_AGAIN, n[2]);
332
      }
333
1631211
    } else if (n[1].isConst()) {
334
425262
      if (n[1] == tt && n[2] == ff) {
335
9594
        Debug("bool-ite")
336
4797
            << "TheoryBoolRewriter::preRewrite_ITE: n[1] ==tt && n[2] == ff "
337
4797
            << n << ": " << n[0] << std::endl;
338
4797
        return RewriteResponse(REWRITE_AGAIN, n[0]);
339
      }
340
420465
      else if (n[1] == ff && n[2] == tt) {
341
7958
        Debug("bool-ite")
342
3979
            << "TheoryBoolRewriter::preRewrite_ITE: n[1] ==ff && n[2] == tt "
343
3979
            << n << ": " << n[0].notNode() << std::endl;
344
3979
        return RewriteResponse(REWRITE_AGAIN, makeNegation(n[0]));
345
      }
346
416486
      if (n[1] == tt || n[1] == ff)
347
      {
348
        // ITE C true y --> C v y
349
        // ITE C false y --> ~C ^ y
350
        Node resp =
351
228648
            n[1] == tt ? n[0].orNode(n[2]) : (n[0].negate()).andNode(n[2]);
352
228648
        Debug("bool-ite") << "TheoryBoolRewriter::preRewrite_ITE: n[1] const "
353
114324
                          << n << ": " << resp << std::endl;
354
114324
        return RewriteResponse(REWRITE_AGAIN, resp);
355
      }
356
    }
357
358
1508111
    if (n[0].getKind() == kind::NOT)
359
    {
360
      // ite(not(c), x, y) ---> ite(c, y, x)
361
      return RewriteResponse(
362
81679
          REWRITE_AGAIN, nodeManager->mkNode(kind::ITE, n[0][0], n[2], n[1]));
363
    }
364
365
1426432
    if (n[2].isConst() && (n[2] == tt || n[2] == ff))
366
    {
367
      // ITE C x true --> ~C v x
368
      // ITE C x false --> C ^ x
369
      Node resp =
370
52066
          n[2] == tt ? (n[0].negate()).orNode(n[1]) : n[0].andNode(n[1]);
371
52066
      Debug("bool-ite") << "TheoryBoolRewriter::preRewrite_ITE: n[2] const "
372
26033
                        << n << ": " << resp << std::endl;
373
26033
      return RewriteResponse(REWRITE_AGAIN, resp);
374
    }
375
376
    int parityTmp;
377
1400399
    if ((parityTmp = equalityParity(n[1], n[2])) != 0) {
378
13226
      Node resp = (parityTmp == 1) ? (Node)n[1] : n[0].eqNode(n[1]);
379
13226
      Debug("bool-ite")
380
6613
          << "TheoryBoolRewriter::preRewrite_ITE:  equalityParity n[1], n[2] "
381
6613
          << parityTmp << " " << n << ": " << resp << std::endl;
382
6613
      return RewriteResponse(REWRITE_AGAIN, resp);
383
    // Curiously, this rewrite affects several benchmarks dramatically, including copy_array and some simple_startup - disable for now
384
    // } else if (n[0].getKind() == kind::NOT) {
385
    //   return RewriteResponse(REWRITE_AGAIN, n[0][0].iteNode(n[2], n[1]));
386
1393786
    } else if(!n[1].isConst() && (parityTmp = equalityParity(n[0], n[1])) != 0){
387
      // (parityTmp == 1) if n[0] == n[1]
388
      // otherwise, n[0] == not(n[1]) or not(n[0]) == n[1]
389
390
      // if n[1] is constant this can loop, this is possible in prewrite
391
550
      Node resp = n[0].iteNode( (parityTmp == 1) ? tt : ff, n[2]);
392
550
      Debug("bool-ite")
393
275
          << "TheoryBoolRewriter::preRewrite_ITE: equalityParity n[0], n[1] "
394
275
          << parityTmp << " " << n << ": " << resp << std::endl;
395
275
      return RewriteResponse(REWRITE_AGAIN, resp);
396
1393511
    } else if(!n[2].isConst() && (parityTmp = equalityParity(n[0], n[2])) != 0){
397
      // (parityTmp == 1) if n[0] == n[2]
398
      // otherwise, n[0] == not(n[2]) or not(n[0]) == n[2]
399
696
      Node resp = n[0].iteNode(n[1], (parityTmp == 1) ? ff : tt);
400
696
      Debug("bool-ite")
401
348
          << "TheoryBoolRewriter::preRewrite_ITE: equalityParity n[0], n[2] "
402
348
          << parityTmp << " " << n << ": " << resp << std::endl;
403
348
      return RewriteResponse(REWRITE_AGAIN, resp);
404
3965995
    } else if(n[1].getKind() == kind::ITE &&
405
2572832
              (parityTmp = equalityParity(n[0], n[1][0])) != 0){
406
      // (parityTmp == 1) then n : (ite c (ite c x y) z)
407
      // (parityTmp > 1)  then n : (ite c (ite (not c) x y) z) or
408
      // n: (ite (not c) (ite c x y) z)
409
5256
      Node resp = n[0].iteNode((parityTmp == 1) ? n[1][1] : n[1][2], n[2]);
410
5256
      Debug("bool-ite")
411
2628
          << "TheoryBoolRewriter::preRewrite: equalityParity n[0], n[1][0] "
412
2628
          << parityTmp << " " << n << ": " << resp << std::endl;
413
2628
      return RewriteResponse(REWRITE_AGAIN, resp);
414
4491361
    } else if(n[2].getKind() == kind::ITE &&
415
3100826
              (parityTmp = equalityParity(n[0], n[2][0])) != 0){
416
      // (parityTmp == 1) then n : (ite c x (ite c y z))
417
      // (parityTmp > 1)  then n : (ite c x (ite (not c) y z)) or
418
      // n: (ite (not c) x (ite c y z))
419
3488
      Node resp = n[0].iteNode(n[1], (parityTmp == 1) ? n[2][2] : n[2][1]);
420
3488
      Debug("bool-ite")
421
1744
          << "TheoryBoolRewriter::preRewrite: equalityParity n[0], n[2][0] "
422
1744
          << parityTmp << " " << n << ": " << resp << std::endl;
423
1744
      return RewriteResponse(REWRITE_AGAIN, resp);
424
    }
425
1388791
    break;
426
  }
427
648800
  default:
428
648800
    return RewriteResponse(REWRITE_DONE, n);
429
  }
430
15932650
  return RewriteResponse(REWRITE_DONE, n);
431
}
432
433
}  // namespace booleans
434
}  // namespace theory
435
28191
}  // namespace cvc5