GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/relevance_manager.cpp Lines: 120 169 71.0 %
Date: 2021-09-16 Branches: 161 552 29.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
22
RelevanceManager::RelevanceManager(context::UserContext* userContext,
29
22
                                   Valuation val)
30
    : d_val(val),
31
      d_input(userContext),
32
      d_computed(false),
33
      d_success(false),
34
      d_trackRSetExp(false),
35
22
      d_miniscopeTopLevel(true)
36
{
37
22
  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
22
}
47
48
18
void RelevanceManager::notifyPreprocessedAssertions(
49
    const std::vector<Node>& assertions)
50
{
51
  // add to input list, which is user-context dependent
52
36
  std::vector<Node> toProcess;
53
121
  for (const Node& a : assertions)
54
  {
55
103
    if (d_miniscopeTopLevel && a.getKind() == AND)
56
    {
57
      // split top-level AND
58
3
      for (const Node& ac : a)
59
      {
60
2
        toProcess.push_back(ac);
61
      }
62
    }
63
    else
64
    {
65
102
      d_input.push_back(a);
66
    }
67
  }
68
18
  addAssertionsInternal(toProcess);
69
18
}
70
71
void RelevanceManager::notifyPreprocessedAssertion(Node n)
72
{
73
  std::vector<Node> toProcess;
74
  toProcess.push_back(n);
75
  addAssertionsInternal(toProcess);
76
}
77
78
18
void RelevanceManager::addAssertionsInternal(std::vector<Node>& toProcess)
79
{
80
18
  size_t i = 0;
81
22
  while (i < toProcess.size())
82
  {
83
4
    Node a = toProcess[i];
84
2
    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
2
      d_input.push_back(a);
99
    }
100
2
    i++;
101
  }
102
18
}
103
104
136
void RelevanceManager::resetRound()
105
{
106
136
  d_computed = false;
107
136
}
108
109
83
void RelevanceManager::computeRelevance()
110
{
111
83
  d_computed = true;
112
83
  d_rset.clear();
113
83
  d_rsetExp.clear();
114
83
  Trace("rel-manager") << "RelevanceManager::computeRelevance..." << std::endl;
115
166
  std::unordered_map<TNode, int> cache;
116
455
  for (const Node& node: d_input)
117
  {
118
744
    TNode n = node;
119
372
    int val = justify(n, cache);
120
372
    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
83
  Trace("rel-manager") << "...success, size = " << d_rset.size() << std::endl;
133
83
  d_success = true;
134
}
135
136
864
bool RelevanceManager::isBooleanConnective(TNode cur)
137
{
138
864
  Kind k = cur.getKind();
139
430
  return k == NOT || k == IMPLIES || k == AND || k == OR || k == ITE || k == XOR
140
1246
         || (k == EQUAL && cur[0].getType().isBoolean());
141
}
142
143
246
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
246
  size_t nchildren = cur.getNumChildren();
153
246
  Assert(isBooleanConnective(cur));
154
246
  size_t index = childrenJustify.size();
155
246
  Assert(index < nchildren);
156
246
  Assert(cache.find(cur[index]) != cache.end());
157
246
  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
246
  int lastChildJustify = cache[cur[index]];
161
246
  if (k == NOT)
162
  {
163
217
    cache[cur] = -lastChildJustify;
164
  }
165
29
  else if (k == IMPLIES || k == AND || k == OR)
166
  {
167
29
    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
29
      if (lastChildJustify
172
29
          == ((k == AND || (k == IMPLIES && index == 0)) ? -1 : 1))
173
      {
174
18
        cache[cur] = k == AND ? -1 : 1;
175
18
        return false;
176
      }
177
    }
178
11
    if (index + 1 == nchildren)
179
    {
180
      // finished all children, compute the overall value
181
1
      int ret = k == AND ? 1 : -1;
182
2
      for (int cv : childrenJustify)
183
      {
184
1
        if (cv == 0)
185
        {
186
          ret = 0;
187
          break;
188
        }
189
      }
190
1
      cache[cur] = ret;
191
    }
192
    else
193
    {
194
      // continue
195
10
      childrenJustify.push_back(lastChildJustify);
196
10
      return true;
197
1
    }
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
218
  return false;
247
}
248
249
372
int RelevanceManager::justify(TNode n, std::unordered_map<TNode, int>& cache)
250
{
251
  // the vector of values of children
252
744
  std::unordered_map<TNode, std::vector<int>> childJustify;
253
372
  std::unordered_map<TNode, int>::iterator it;
254
372
  std::unordered_map<TNode, std::vector<int>>::iterator itc;
255
744
  std::vector<TNode> visit;
256
744
  TNode cur;
257
372
  visit.push_back(n);
258
492
  do
259
  {
260
864
    cur = visit.back();
261
    // should always have Boolean type
262
864
    Assert(cur.getType().isBoolean());
263
864
    it = cache.find(cur);
264
864
    if (it != cache.end())
265
    {
266
      visit.pop_back();
267
      // already computed value
268
      continue;
269
    }
270
864
    itc = childJustify.find(cur);
271
    // have we traversed to children yet?
272
864
    if (itc == childJustify.end())
273
    {
274
      // are we not a Boolean connective (including NOT)?
275
618
      if (isBooleanConnective(cur))
276
      {
277
        // initialize its children justify vector as empty
278
236
        childJustify[cur].clear();
279
        // start with the first child
280
236
        visit.push_back(cur[0]);
281
      }
282
      else
283
      {
284
382
        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
382
        int ret = 0;
288
        // otherwise we look up the value
289
        bool value;
290
382
        if (d_val.hasSatValue(cur, value))
291
        {
292
382
          ret = value ? 1 : -1;
293
382
          d_rset.insert(cur);
294
382
          if (d_trackRSetExp)
295
          {
296
            d_rsetExp[cur] = n;
297
          }
298
        }
299
382
        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
246
      if (updateJustifyLastChild(cur, itc->second, cache))
307
      {
308
10
        Assert(itc->second.size() < cur.getNumChildren());
309
20
        TNode nextChild = cur[itc->second.size()];
310
10
        visit.push_back(nextChild);
311
      }
312
      else
313
      {
314
236
        visit.pop_back();
315
      }
316
    }
317
864
  } while (!visit.empty());
318
372
  Assert(cache.find(n) != cache.end());
319
744
  return cache[n];
320
}
321
322
2445
bool RelevanceManager::isRelevant(Node lit)
323
{
324
2445
  if (!d_computed)
325
  {
326
83
    computeRelevance();
327
  }
328
2445
  if (!d_success)
329
  {
330
    // always relevant if we failed to compute
331
    return true;
332
  }
333
  // agnostic to negation
334
4851
  while (lit.getKind() == NOT)
335
  {
336
1203
    lit = lit[0];
337
  }
338
2445
  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
559
void RelevanceManager::notifyLemma(Node n)
354
{
355
559
  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
559
}
362
363
115
void RelevanceManager::notifyCandidateModel(TheoryModel* m)
364
{
365
115
  if (d_dman != nullptr)
366
  {
367
4
    d_dman->notifyCandidateModel(d_input, m);
368
  }
369
115
}
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
29577
}  // namespace cvc5