GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/smt/model_blocker.cpp Lines: 123 145 84.8 %
Date: 2021-11-07 Branches: 279 764 36.5 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Mathias Preiner, 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 utility for blocking models.
14
 */
15
16
#include "smt/model_blocker.h"
17
18
#include "expr/node.h"
19
#include "expr/node_algorithm.h"
20
#include "theory/quantifiers/term_util.h"
21
#include "theory/rewriter.h"
22
#include "theory/theory_model.h"
23
24
using namespace cvc5::kind;
25
26
namespace cvc5 {
27
28
26
ModelBlocker::ModelBlocker(Env& e) : EnvObj(e) {}
29
30
26
Node ModelBlocker::getModelBlocker(const std::vector<Node>& assertions,
31
                                   theory::TheoryModel* m,
32
                                   options::BlockModelsMode mode,
33
                                   const std::vector<Node>& exprToBlock)
34
{
35
26
  NodeManager* nm = NodeManager::currentNM();
36
  // convert to nodes
37
52
  std::vector<Node> tlAsserts = assertions;
38
52
  std::vector<Node> nodesToBlock = exprToBlock;
39
26
  Trace("model-blocker") << "Compute model blocker, assertions:" << std::endl;
40
52
  Node blocker;
41
26
  if (mode == options::BlockModelsMode::LITERALS)
42
  {
43
10
    Assert(nodesToBlock.empty());
44
    // optimization: filter out top-level unit assertions, as they cannot
45
    // contribute to model blocking.
46
10
    unsigned counter = 0;
47
20
    std::vector<Node> asserts;
48
62
    while (counter < tlAsserts.size())
49
    {
50
52
      Node cur = tlAsserts[counter];
51
26
      counter++;
52
52
      Node catom = cur.getKind() == NOT ? cur[0] : cur;
53
26
      bool cpol = cur.getKind() != NOT;
54
26
      if (catom.getKind() == NOT)
55
      {
56
        tlAsserts.push_back(catom[0]);
57
      }
58
26
      else if (catom.getKind() == AND && cpol)
59
      {
60
        tlAsserts.insert(tlAsserts.end(), catom.begin(), catom.end());
61
      }
62
26
      else if (theory::quantifiers::TermUtil::isBoolConnectiveTerm(catom))
63
      {
64
10
        asserts.push_back(cur);
65
10
        Trace("model-blocker") << "  " << cur << std::endl;
66
      }
67
    }
68
10
    if (asserts.empty())
69
    {
70
      Node blockTriv = nm->mkConst(false);
71
      Trace("model-blocker")
72
          << "...model blocker is (trivially) " << blockTriv << std::endl;
73
      return blockTriv;
74
    }
75
76
20
    Node formula = asserts.size() > 1 ? nm->mkNode(AND, asserts) : asserts[0];
77
20
    std::unordered_map<TNode, Node> visited;
78
20
    std::unordered_map<TNode, Node> implicant;
79
10
    std::unordered_map<TNode, Node>::iterator it;
80
20
    std::vector<TNode> visit;
81
20
    TNode cur;
82
10
    visit.push_back(formula);
83
26
    do
84
    {
85
36
      cur = visit.back();
86
36
      visit.pop_back();
87
36
      it = visited.find(cur);
88
89
36
      Trace("model-blocker-debug") << "Visit : " << cur << std::endl;
90
91
36
      if (it == visited.end())
92
      {
93
22
        visited[cur] = Node::null();
94
44
        Node catom = cur.getKind() == NOT ? cur[0] : cur;
95
22
        bool cpol = cur.getKind() != NOT;
96
        // compute the implicant
97
        // impl is a formula that implies cur that is also satisfied by m
98
44
        Node impl;
99
22
        if (catom.getKind() == NOT)
100
        {
101
          // double negation
102
          impl = catom[0];
103
        }
104
22
        else if (catom.getKind() == OR || catom.getKind() == AND)
105
        {
106
          // if disjunctive
107
10
          if ((catom.getKind() == OR) == cpol)
108
          {
109
            // take the first literal that is satisfied
110
24
            for (Node n : catom)
111
            {
112
              // rewrite, this ensures that e.g. the propositional value of
113
              // quantified formulas can be queried
114
24
              n = rewrite(n);
115
40
              Node vn = m->getValue(n);
116
24
              Assert(vn.isConst());
117
24
              if (vn.getConst<bool>() == cpol)
118
              {
119
8
                impl = cpol ? n : n.negate();
120
8
                break;
121
              }
122
            }
123
          }
124
2
          else if (catom.getKind() == OR)
125
          {
126
            // one step NNF
127
            std::vector<Node> children;
128
            for (const Node& cn : catom)
129
            {
130
              children.push_back(cn.negate());
131
            }
132
            impl = nm->mkNode(AND, children);
133
          }
134
        }
135
12
        else if (catom.getKind() == ITE)
136
        {
137
          Node vcond = m->getValue(cur[0]);
138
          Assert(vcond.isConst());
139
          Node cond = cur[0];
140
          Node branch;
141
          if (vcond.getConst<bool>())
142
          {
143
            branch = cur[1];
144
          }
145
          else
146
          {
147
            cond = cond.negate();
148
            branch = cur[2];
149
          }
150
          impl = nm->mkNode(AND, cond, cpol ? branch : branch.negate());
151
        }
152
34
        else if ((catom.getKind() == EQUAL && catom[0].getType().isBoolean())
153
34
                 || catom.getKind() == XOR)
154
        {
155
          // based on how the children evaluate in the model
156
4
          std::vector<Node> children;
157
6
          for (const Node& cn : catom)
158
          {
159
8
            Node vn = m->getValue(cn);
160
4
            Assert(vn.isConst());
161
4
            children.push_back(vn.getConst<bool>() ? cn : cn.negate());
162
          }
163
2
          impl = nm->mkNode(AND, children);
164
        }
165
        else
166
        {
167
          // literals justified by themselves
168
10
          visited[cur] = cur;
169
10
          Trace("model-blocker-debug") << "...self justified" << std::endl;
170
        }
171
22
        if (visited[cur].isNull())
172
        {
173
12
          visit.push_back(cur);
174
12
          if (impl.isNull())
175
          {
176
2
            Assert(cur.getKind() == AND);
177
2
            Trace("model-blocker-debug") << "...recurse" << std::endl;
178
2
            visit.insert(visit.end(), cur.begin(), cur.end());
179
          }
180
          else
181
          {
182
20
            Trace("model-blocker-debug")
183
10
                << "...implicant : " << impl << std::endl;
184
10
            implicant[cur] = impl;
185
10
            visit.push_back(impl);
186
          }
187
        }
188
      }
189
14
      else if (it->second.isNull())
190
      {
191
24
        Node ret = cur;
192
12
        it = implicant.find(cur);
193
12
        if (it != implicant.end())
194
        {
195
20
          Node impl = it->second;
196
10
          it = visited.find(impl);
197
10
          Assert(it != visited.end());
198
10
          Assert(!it->second.isNull());
199
10
          ret = it->second;
200
20
          Trace("model-blocker-debug")
201
10
              << "...implicant res: " << ret << std::endl;
202
        }
203
        else
204
        {
205
2
          bool childChanged = false;
206
4
          std::vector<Node> children;
207
          // we never recurse to parameterized nodes
208
2
          Assert(cur.getMetaKind() != metakind::PARAMETERIZED);
209
6
          for (const Node& cn : cur)
210
          {
211
4
            it = visited.find(cn);
212
4
            Assert(it != visited.end());
213
4
            Assert(!it->second.isNull());
214
4
            childChanged = childChanged || cn != it->second;
215
4
            children.push_back(it->second);
216
          }
217
2
          if (childChanged)
218
          {
219
            ret = nm->mkNode(cur.getKind(), children);
220
          }
221
2
          Trace("model-blocker-debug") << "...recons res: " << ret << std::endl;
222
        }
223
12
        visited[cur] = ret;
224
      }
225
36
    } while (!visit.empty());
226
10
    Assert(visited.find(formula) != visited.end());
227
10
    Assert(!visited.find(formula)->second.isNull());
228
10
    blocker = visited[formula].negate();
229
  }
230
  else
231
  {
232
16
    Assert(mode == options::BlockModelsMode::VALUES);
233
32
    std::vector<Node> blockers;
234
    // if specific terms were not specified, block all variables of
235
    // the model
236
16
    if (nodesToBlock.empty())
237
    {
238
12
      Trace("model-blocker")
239
6
          << "no specific terms to block recognized" << std::endl;
240
12
      std::unordered_set<Node> symbols;
241
33
      for (Node n : tlAsserts)
242
      {
243
27
        expr::getSymbols(n, symbols);
244
      }
245
24
      for (Node s : symbols)
246
      {
247
18
        if (s.getType().getKind() != kind::FUNCTION_TYPE)
248
        {
249
24
          Node v = m->getValue(s);
250
24
          Node a = nm->mkNode(DISTINCT, s, v);
251
12
          blockers.push_back(a);
252
        }
253
      }
254
    }
255
    // otherwise, block all terms that were specified in get-value
256
    else
257
    {
258
20
      std::unordered_set<Node> terms;
259
20
      for (Node n : nodesToBlock)
260
      {
261
20
        Node v = m->getValue(n);
262
20
        Node a = nm->mkNode(DISTINCT, n, v);
263
10
        blockers.push_back(a);
264
      }
265
    }
266
16
    if (blockers.size() == 0)
267
    {
268
      blocker = nm->mkConst<bool>(true);
269
    }
270
16
    else if (blockers.size() == 1)
271
    {
272
10
      blocker = blockers[0];
273
    }
274
    else
275
    {
276
6
      blocker = nm->mkNode(OR, blockers);
277
    }
278
  }
279
26
  Trace("model-blocker") << "...model blocker is " << blocker << std::endl;
280
26
  return blocker;
281
}
282
283
31137
}  // namespace cvc5