GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/relevance_manager.cpp Lines: 97 169 57.4 %
Date: 2021-09-29 Branches: 117 552 21.2 %

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