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