GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/smt_util/boolean_simplification.h Lines: 54 68 79.4 %
Date: 2021-09-17 Branches: 74 188 39.4 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Morgan Deters, Tim King, Andres Noetzli
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
 * Simple, commonly-used routines for Boolean simplification.
14
 */
15
16
#include "cvc5_private.h"
17
18
#ifndef CVC5__BOOLEAN_SIMPLIFICATION_H
19
#define CVC5__BOOLEAN_SIMPLIFICATION_H
20
21
#include <vector>
22
#include <algorithm>
23
24
#include "base/check.h"
25
#include "expr/node.h"
26
27
namespace cvc5 {
28
29
/**
30
 * A class to contain a number of useful functions for simple
31
 * simplification of nodes.  One never uses it as an object (and
32
 * it cannot be constructed).  It is used as a namespace.
33
 */
34
class BooleanSimplification {
35
  // cannot construct one of these
36
  BooleanSimplification() = delete;
37
  BooleanSimplification(const BooleanSimplification&) = delete;
38
39
  CVC5_WARN_UNUSED_RESULT static bool push_back_associative_commute_recursive(
40
      Node n, std::vector<Node>& buffer, Kind k, Kind notK, bool negateNode);
41
42
 public:
43
  /**
44
   * The threshold for removing duplicates.  (See removeDuplicates().)
45
   */
46
  static const uint32_t DUPLICATE_REMOVAL_THRESHOLD = 10;
47
48
  /**
49
   * Remove duplicate nodes from a vector, modifying it in-place.
50
   * If the vector has size >= DUPLICATE_REMOVAL_THRESHOLD, this
51
   * function is a no-op.
52
   */
53
24
  static void removeDuplicates(std::vector<Node>& buffer)
54
  {
55
24
    if(buffer.size() < DUPLICATE_REMOVAL_THRESHOLD) {
56
24
      std::sort(buffer.begin(), buffer.end());
57
      std::vector<Node>::iterator new_end =
58
24
        std::unique(buffer.begin(), buffer.end());
59
24
      buffer.erase(new_end, buffer.end());
60
    }
61
24
  }
62
63
  /**
64
   * Takes a node with kind AND, collapses all AND and (NOT OR)-kinded
65
   * children of it (as far as possible---see
66
   * push_back_associative_commute()), removes duplicates, and returns
67
   * the resulting Node.
68
   */
69
8
  static Node simplifyConflict(Node andNode)
70
  {
71
8
    AssertArgument(!andNode.isNull(), andNode);
72
8
    AssertArgument(andNode.getKind() == kind::AND, andNode);
73
74
12
    std::vector<Node> buffer;
75
6
    push_back_associative_commute(andNode, buffer, kind::AND, kind::OR);
76
77
6
    removeDuplicates(buffer);
78
79
6
    if(buffer.size() == 1) {
80
      return buffer[0];
81
    }
82
83
12
    NodeBuilder nb(kind::AND);
84
6
    nb.append(buffer);
85
6
    return nb;
86
  }
87
88
  /**
89
   * Takes a node with kind OR, collapses all OR and (NOT AND)-kinded
90
   * children of it (as far as possible---see
91
   * push_back_associative_commute()), removes duplicates, and returns
92
   * the resulting Node.
93
   */
94
20
  static Node simplifyClause(Node orNode)
95
  {
96
20
    AssertArgument(!orNode.isNull(), orNode);
97
20
    AssertArgument(orNode.getKind() == kind::OR, orNode);
98
99
36
    std::vector<Node> buffer;
100
18
    push_back_associative_commute(orNode, buffer, kind::OR, kind::AND);
101
102
18
    removeDuplicates(buffer);
103
104
18
    Assert(buffer.size() > 0);
105
18
    if(buffer.size() == 1) {
106
      return buffer[0];
107
    }
108
109
36
    NodeBuilder nb(kind::OR);
110
18
    nb.append(buffer);
111
18
    return nb;
112
  }
113
114
  /**
115
   * Takes a node with kind IMPLIES, converts it to an OR, then
116
   * simplifies the result with simplifyClause().
117
   *
118
   * The input doesn't actually have to be Horn, it seems, but that's
119
   * the common case(?), hence the name.
120
   */
121
10
  static Node simplifyHornClause(Node implication)
122
  {
123
10
    AssertArgument(implication.getKind() == kind::IMPLIES, implication);
124
125
16
    TNode left = implication[0];
126
16
    TNode right = implication[1];
127
128
16
    Node notLeft = negate(left);
129
16
    Node clause = NodeBuilder(kind::OR) << notLeft << right;
130
131
16
    return simplifyClause(clause);
132
  }
133
134
  /**
135
   * Aids in reforming a node.  Takes a node of (N-ary) kind k and
136
   * copies its children into an output vector, collapsing its k-kinded
137
   * children into it.  Also collapses negations of notK.  For example:
138
   *
139
   *   push_back_associative_commute( [OR [OR a b] [OR b c d] [NOT [AND e f]]],
140
   *                                  buffer, kind::OR, kind::AND )
141
   *   yields a "buffer" vector of [a b b c d e f]
142
   *
143
   * @param n the node to operate upon
144
   * @param buffer the output vector (must be empty on entry)
145
   * @param k the kind to collapse (should equal the kind of node n)
146
   * @param notK the "negation" of kind k (e.g. OR's negation is AND),
147
   * or kind::UNDEFINED_KIND if none.
148
   * @param negateChildren true if the children of the resulting node
149
   * (that is, the elements in buffer) should all be negated; you want
150
   * this if e.g. you're simplifying the (OR...) in (NOT (OR...)),
151
   * intending to make the result an AND.
152
   */
153
24
  static inline void push_back_associative_commute(Node n,
154
                                                   std::vector<Node>& buffer,
155
                                                   Kind k,
156
                                                   Kind notK,
157
                                                   bool negateChildren = false)
158
  {
159
24
    AssertArgument(buffer.empty(), buffer);
160
24
    AssertArgument(!n.isNull(), n);
161
24
    AssertArgument(k != kind::UNDEFINED_KIND && k != kind::NULL_EXPR, k);
162
24
    AssertArgument(notK != kind::NULL_EXPR, notK);
163
24
    AssertArgument(n.getKind() == k, n,
164
                   "expected node to have kind %s", kindToString(k).c_str());
165
166
    bool b CVC5_UNUSED =
167
24
        push_back_associative_commute_recursive(n, buffer, k, notK, false);
168
169
24
    if(buffer.size() == 0) {
170
      // all the TRUEs for an AND (resp FALSEs for an OR) were simplified away
171
      buffer.push_back(NodeManager::currentNM()->mkConst(k == kind::AND ? true : false));
172
    }
173
24
  }/* push_back_associative_commute() */
174
175
  /**
176
   * Negates a node, doing all the double-negation elimination
177
   * that's possible.
178
   *
179
   * @param n the node to negate (cannot be the null node)
180
   */
181
36
  static Node negate(TNode n)
182
  {
183
36
    AssertArgument(!n.isNull(), n);
184
185
34
    bool polarity = true;
186
68
    TNode base = n;
187
58
    while(base.getKind() == kind::NOT){
188
12
      base = base[0];
189
12
      polarity = !polarity;
190
    }
191
34
    if(n.isConst()) {
192
      return NodeManager::currentNM()->mkConst(!n.getConst<bool>());
193
    }
194
34
    if(polarity){
195
30
      return base.notNode();
196
    }else{
197
4
      return base;
198
    }
199
  }
200
201
  /**
202
   * Simplify an OR, AND, or IMPLIES.  This function is the identity
203
   * for all other kinds.
204
   */
205
  inline static Node simplify(TNode n)
206
  {
207
    switch(n.getKind()) {
208
    case kind::AND:
209
      return simplifyConflict(n);
210
211
    case kind::OR:
212
      return simplifyClause(n);
213
214
    case kind::IMPLIES:
215
      return simplifyHornClause(n);
216
217
    default:
218
      return n;
219
    }
220
  }
221
222
};/* class BooleanSimplification */
223
224
}  // namespace cvc5
225
226
#endif /* CVC5__BOOLEAN_SIMPLIFICATION_H */