GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/relevance_manager.cpp Lines: 110 148 74.3 %
Date: 2021-05-22 Branches: 153 508 30.1 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Aina Niemetz
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
 * Implementation of relevance manager.
14
 */
15
16
#include "theory/relevance_manager.h"
17
18
#include <sstream>
19
20
using namespace cvc5::kind;
21
22
namespace cvc5 {
23
namespace theory {
24
25
25
RelevanceManager::RelevanceManager(context::UserContext* userContext,
26
25
                                   Valuation val)
27
25
    : d_val(val), d_input(userContext), d_computed(false), d_success(false)
28
{
29
25
}
30
31
25
void RelevanceManager::notifyPreprocessedAssertions(
32
    const std::vector<Node>& assertions)
33
{
34
  // add to input list, which is user-context dependent
35
50
  std::vector<Node> toProcess;
36
144
  for (const Node& a : assertions)
37
  {
38
119
    if (a.getKind() == AND)
39
    {
40
      // split top-level AND
41
25
      for (const Node& ac : a)
42
      {
43
18
        toProcess.push_back(ac);
44
      }
45
    }
46
    else
47
    {
48
112
      d_input.push_back(a);
49
    }
50
  }
51
25
  addAssertionsInternal(toProcess);
52
25
}
53
54
void RelevanceManager::notifyPreprocessedAssertion(Node n)
55
{
56
  std::vector<Node> toProcess;
57
  toProcess.push_back(n);
58
  addAssertionsInternal(toProcess);
59
}
60
61
25
void RelevanceManager::addAssertionsInternal(std::vector<Node>& toProcess)
62
{
63
25
  size_t i = 0;
64
61
  while (i < toProcess.size())
65
  {
66
36
    Node a = toProcess[i];
67
18
    if (a.getKind() == AND)
68
    {
69
      // split AND
70
      for (const Node& ac : a)
71
      {
72
        toProcess.push_back(ac);
73
      }
74
    }
75
    else
76
    {
77
      // note that a could be a literal, in which case we could add it to
78
      // an "always relevant" set here.
79
18
      d_input.push_back(a);
80
    }
81
18
    i++;
82
  }
83
25
}
84
85
143
void RelevanceManager::resetRound()
86
{
87
143
  d_computed = false;
88
143
}
89
90
82
void RelevanceManager::computeRelevance()
91
{
92
82
  d_computed = true;
93
82
  d_rset.clear();
94
82
  Trace("rel-manager") << "RelevanceManager::computeRelevance..." << std::endl;
95
164
  std::unordered_map<TNode, int> cache;
96
446
  for (const Node& node: d_input)
97
  {
98
728
    TNode n = node;
99
364
    int val = justify(n, cache);
100
364
    if (val != 1)
101
    {
102
      std::stringstream serr;
103
      serr << "RelevanceManager::computeRelevance: WARNING: failed to justify "
104
           << n;
105
      Trace("rel-manager") << serr.str() << std::endl;
106
      Assert(false) << serr.str();
107
      d_success = false;
108
      d_rset.clear();
109
      return;
110
    }
111
  }
112
82
  Trace("rel-manager") << "...success, size = " << d_rset.size() << std::endl;
113
82
  d_success = true;
114
}
115
116
820
bool RelevanceManager::isBooleanConnective(TNode cur)
117
{
118
820
  Kind k = cur.getKind();
119
414
  return k == NOT || k == IMPLIES || k == AND || k == OR || k == ITE || k == XOR
120
1189
         || (k == EQUAL && cur[0].getType().isBoolean());
121
}
122
123
232
bool RelevanceManager::updateJustifyLastChild(
124
    TNode cur,
125
    std::vector<int>& childrenJustify,
126
    std::unordered_map<TNode, int>& cache)
127
{
128
  // This method is run when we are informed that child index of cur
129
  // has justify status lastChildJustify. We return true if we would like to
130
  // compute the next child, in this case we push the status of the current
131
  // child to childrenJustify.
132
232
  size_t nchildren = cur.getNumChildren();
133
232
  Assert(isBooleanConnective(cur));
134
232
  size_t index = childrenJustify.size();
135
232
  Assert(index < nchildren);
136
232
  Assert(cache.find(cur[index]) != cache.end());
137
232
  Kind k = cur.getKind();
138
  // Lookup the last child's value in the overall cache, we may choose to
139
  // add this to childrenJustify if we return true.
140
232
  int lastChildJustify = cache[cur[index]];
141
232
  if (k == NOT)
142
  {
143
203
    cache[cur] = -lastChildJustify;
144
  }
145
29
  else if (k == IMPLIES || k == AND || k == OR)
146
  {
147
15
    if (lastChildJustify != 0)
148
    {
149
      // See if we short circuited? The value for short circuiting is false if
150
      // we are AND or the first child of IMPLIES.
151
15
      if (lastChildJustify
152
15
          == ((k == AND || (k == IMPLIES && index == 0)) ? -1 : 1))
153
      {
154
9
        cache[cur] = k == AND ? -1 : 1;
155
9
        return false;
156
      }
157
    }
158
6
    if (index + 1 == nchildren)
159
    {
160
      // finished all children, compute the overall value
161
      int ret = k == AND ? 1 : -1;
162
      for (int cv : childrenJustify)
163
      {
164
        if (cv == 0)
165
        {
166
          ret = 0;
167
          break;
168
        }
169
      }
170
      cache[cur] = ret;
171
    }
172
    else
173
    {
174
      // continue
175
6
      childrenJustify.push_back(lastChildJustify);
176
6
      return true;
177
    }
178
  }
179
14
  else if (lastChildJustify == 0)
180
  {
181
    // all other cases, an unknown child implies we are unknown
182
    cache[cur] = 0;
183
  }
184
14
  else if (k == ITE)
185
  {
186
14
    if (index == 0)
187
    {
188
7
      Assert(lastChildJustify != 0);
189
      // continue with branch
190
7
      childrenJustify.push_back(lastChildJustify);
191
7
      if (lastChildJustify == -1)
192
      {
193
        // also mark first branch as don't care
194
7
        childrenJustify.push_back(0);
195
      }
196
7
      return true;
197
    }
198
    else
199
    {
200
      // should be in proper branch
201
7
      Assert(childrenJustify[0] == (index == 1 ? 1 : -1));
202
      // we are the value of the branch
203
7
      cache[cur] = lastChildJustify;
204
    }
205
  }
206
  else
207
  {
208
    Assert(k == XOR || k == EQUAL);
209
    Assert(nchildren == 2);
210
    Assert(lastChildJustify != 0);
211
    if (index == 0)
212
    {
213
      // must compute the other child
214
      childrenJustify.push_back(lastChildJustify);
215
      return true;
216
    }
217
    else
218
    {
219
      // both children known, compute value
220
      Assert(childrenJustify.size() == 1 && childrenJustify[0] != 0);
221
      cache[cur] =
222
          ((k == XOR ? -1 : 1) * lastChildJustify == childrenJustify[0]) ? 1
223
                                                                         : -1;
224
    }
225
  }
226
210
  return false;
227
}
228
229
364
int RelevanceManager::justify(TNode n, std::unordered_map<TNode, int>& cache)
230
{
231
  // the vector of values of children
232
728
  std::unordered_map<TNode, std::vector<int>> childJustify;
233
364
  std::unordered_map<TNode, int>::iterator it;
234
364
  std::unordered_map<TNode, std::vector<int>>::iterator itc;
235
728
  std::vector<TNode> visit;
236
728
  TNode cur;
237
364
  visit.push_back(n);
238
464
  do
239
  {
240
828
    cur = visit.back();
241
    // should always have Boolean type
242
828
    Assert(cur.getType().isBoolean());
243
828
    it = cache.find(cur);
244
836
    if (it != cache.end())
245
    {
246
8
      visit.pop_back();
247
      // already computed value
248
8
      continue;
249
    }
250
820
    itc = childJustify.find(cur);
251
    // have we traversed to children yet?
252
820
    if (itc == childJustify.end())
253
    {
254
      // are we not a Boolean connective (including NOT)?
255
588
      if (isBooleanConnective(cur))
256
      {
257
        // initialize its children justify vector as empty
258
219
        childJustify[cur].clear();
259
        // start with the first child
260
219
        visit.push_back(cur[0]);
261
      }
262
      else
263
      {
264
369
        visit.pop_back();
265
        // The atom case, lookup the value in the valuation class to
266
        // see its current value in the SAT solver, if it has one.
267
369
        int ret = 0;
268
        // otherwise we look up the value
269
        bool value;
270
369
        if (d_val.hasSatValue(cur, value))
271
        {
272
369
          ret = value ? 1 : -1;
273
369
          d_rset.insert(cur);
274
        }
275
369
        cache[cur] = ret;
276
      }
277
    }
278
    else
279
    {
280
      // this processes the impact of the current child on the value of cur,
281
      // and possibly requests that a new child is computed.
282
232
      if (updateJustifyLastChild(cur, itc->second, cache))
283
      {
284
13
        Assert(itc->second.size() < cur.getNumChildren());
285
26
        TNode nextChild = cur[itc->second.size()];
286
13
        visit.push_back(nextChild);
287
      }
288
      else
289
      {
290
219
        visit.pop_back();
291
      }
292
    }
293
828
  } while (!visit.empty());
294
364
  Assert(cache.find(n) != cache.end());
295
728
  return cache[n];
296
}
297
298
1995
bool RelevanceManager::isRelevant(Node lit)
299
{
300
1995
  if (!d_computed)
301
  {
302
82
    computeRelevance();
303
  }
304
1995
  if (!d_success)
305
  {
306
    // always relevant if we failed to compute
307
    return true;
308
  }
309
  // agnostic to negation
310
3879
  while (lit.getKind() == NOT)
311
  {
312
942
    lit = lit[0];
313
  }
314
1995
  return d_rset.find(lit) != d_rset.end();
315
}
316
317
const std::unordered_set<TNode>& RelevanceManager::getRelevantAssertions(
318
    bool& success)
319
{
320
  if (!d_computed)
321
  {
322
    computeRelevance();
323
  }
324
  // update success flag
325
  success = d_success;
326
  return d_rset;
327
}
328
329
}  // namespace theory
330
28194
}  // namespace cvc5