GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/expr/skolem_manager.cpp Lines: 154 157 98.1 %
Date: 2021-05-21 Branches: 308 745 41.3 %

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 skolem manager class.
14
 */
15
16
#include "expr/skolem_manager.h"
17
18
#include <sstream>
19
20
#include "expr/attribute.h"
21
#include "expr/bound_var_manager.h"
22
#include "expr/node_algorithm.h"
23
24
using namespace cvc5::kind;
25
26
namespace cvc5 {
27
28
// Attributes are global maps from Nodes to data. Thus, note that these could
29
// be implemented as internal maps in SkolemManager.
30
struct WitnessFormAttributeId
31
{
32
};
33
typedef expr::Attribute<WitnessFormAttributeId, Node> WitnessFormAttribute;
34
35
struct SkolemFormAttributeId
36
{
37
};
38
typedef expr::Attribute<SkolemFormAttributeId, Node> SkolemFormAttribute;
39
40
struct OriginalFormAttributeId
41
{
42
};
43
typedef expr::Attribute<OriginalFormAttributeId, Node> OriginalFormAttribute;
44
45
748
const char* toString(SkolemFunId id)
46
{
47
748
  switch (id)
48
  {
49
81
    case SkolemFunId::DIV_BY_ZERO: return "DIV_BY_ZERO";
50
59
    case SkolemFunId::INT_DIV_BY_ZERO: return "INT_DIV_BY_ZERO";
51
32
    case SkolemFunId::MOD_BY_ZERO: return "MOD_BY_ZERO";
52
11
    case SkolemFunId::SQRT: return "SQRT";
53
547
    case SkolemFunId::SELECTOR_WRONG: return "SELECTOR_WRONG";
54
18
    case SkolemFunId::SEQ_NTH_OOB: return "SEQ_NTH_OOB";
55
    default: return "?";
56
  }
57
}
58
59
748
std::ostream& operator<<(std::ostream& out, SkolemFunId id)
60
{
61
748
  out << toString(id);
62
748
  return out;
63
}
64
65
4866
Node SkolemManager::mkSkolem(Node v,
66
                             Node pred,
67
                             const std::string& prefix,
68
                             const std::string& comment,
69
                             int flags,
70
                             ProofGenerator* pg)
71
{
72
  // We do not currently insist that pred does not contain witness terms
73
4866
  Assert(v.getKind() == BOUND_VARIABLE);
74
  // make the witness term
75
4866
  NodeManager* nm = NodeManager::currentNM();
76
9732
  Node bvl = nm->mkNode(BOUND_VAR_LIST, v);
77
  // Make the witness term, where notice that pred may contain skolems. We do
78
  // not recursively convert pred to witness form, since witness terms should
79
  // be treated as opaque. Moreover, the use of witness forms leads to
80
  // variable shadowing issues in e.g. skolemization.
81
9732
  Node w = nm->mkNode(WITNESS, bvl, pred);
82
  // store the mapping to proof generator if it exists
83
4866
  if (pg != nullptr)
84
  {
85
    // We cache based on the existential of the original predicate
86
2994
    Node q = nm->mkNode(EXISTS, bvl, pred);
87
    // Notice this may overwrite an existing proof generator. This does not
88
    // matter since either should be able to prove q.
89
1497
    d_gens[q] = pg;
90
  }
91
4866
  Node k = mkSkolemInternal(w, prefix, comment, flags);
92
  // set witness form attribute for k
93
  WitnessFormAttribute wfa;
94
4866
  k.setAttribute(wfa, w);
95
9732
  Trace("sk-manager-skolem")
96
4866
      << "skolem: " << k << " witness " << w << std::endl;
97
9732
  return k;
98
}
99
100
1173
Node SkolemManager::mkSkolemize(Node q,
101
                                std::vector<Node>& skolems,
102
                                const std::string& prefix,
103
                                const std::string& comment,
104
                                int flags,
105
                                ProofGenerator* pg)
106
{
107
1173
  Trace("sk-manager-debug") << "mkSkolemize " << q << std::endl;
108
1173
  Assert(q.getKind() == EXISTS);
109
1173
  Node currQ = q;
110
3451
  for (const Node& av : q[0])
111
  {
112
2278
    Assert(currQ.getKind() == EXISTS && av == currQ[0][0]);
113
    // currQ is updated to the result of skolemizing its first variable in
114
    // the method below.
115
4556
    Node sk = skolemize(currQ, currQ, prefix, comment, flags);
116
4556
    Trace("sk-manager-debug")
117
2278
        << "made skolem " << sk << " for " << av << std::endl;
118
2278
    skolems.push_back(sk);
119
  }
120
1173
  if (pg != nullptr)
121
  {
122
    // Same as above, this may overwrite an existing proof generator
123
    d_gens[q] = pg;
124
  }
125
1173
  Trace("sk-manager-debug") << "...mkSkolemize returns " << currQ << std::endl;
126
1173
  return currQ;
127
}
128
129
2278
Node SkolemManager::skolemize(Node q,
130
                              Node& qskolem,
131
                              const std::string& prefix,
132
                              const std::string& comment,
133
                              int flags)
134
{
135
2278
  Assert(q.getKind() == EXISTS);
136
4556
  Node v;
137
4556
  std::vector<Node> ovars;
138
2278
  Trace("sk-manager-debug") << "mkSkolemize..." << std::endl;
139
2278
  NodeManager* nm = NodeManager::currentNM();
140
7312
  for (const Node& av : q[0])
141
  {
142
9590
    if (v.isNull())
143
    {
144
2278
      v = av;
145
2278
      continue;
146
    }
147
5034
    ovars.push_back(av);
148
  }
149
2278
  Assert(!v.isNull());
150
  // make the predicate with one variable stripped off
151
4556
  Node pred = q[1];
152
2278
  Trace("sk-manager-debug") << "make exists predicate" << std::endl;
153
2278
  if (!ovars.empty())
154
  {
155
    // keeps the same variables
156
2210
    Node bvl = nm->mkNode(BOUND_VAR_LIST, ovars);
157
    // update the predicate
158
1105
    pred = nm->mkNode(EXISTS, bvl, pred);
159
  }
160
2278
  Trace("sk-manager-debug") << "call sub mkSkolem" << std::endl;
161
  // don't use a proof generator, since this may be an intermediate, partially
162
  // skolemized formula.
163
2278
  Node k = mkSkolem(v, pred, prefix, comment, flags, nullptr);
164
2278
  Assert(k.getType() == v.getType());
165
4556
  TNode tv = v;
166
4556
  TNode tk = k;
167
4556
  Trace("sk-manager-debug")
168
2278
      << "qskolem apply " << tv << " -> " << tk << " to " << pred << std::endl;
169
  // the quantified formula with one step of skolemization
170
2278
  qskolem = pred.substitute(tv, tk);
171
2278
  Trace("sk-manager-debug") << "qskolem done substitution" << std::endl;
172
4556
  return k;
173
}
174
175
45680
Node SkolemManager::mkPurifySkolem(Node t,
176
                                   const std::string& prefix,
177
                                   const std::string& comment,
178
                                   int flags)
179
{
180
91360
  Node to = getOriginalForm(t);
181
  // We do not currently insist that to does not contain witness terms
182
183
45680
  Node k = mkSkolemInternal(to, prefix, comment, flags);
184
  // set original form attribute for k
185
  OriginalFormAttribute ofa;
186
45680
  k.setAttribute(ofa, to);
187
188
91360
  Trace("sk-manager-skolem")
189
45680
      << "skolem: " << k << " purify " << to << std::endl;
190
91360
  return k;
191
}
192
193
3173
Node SkolemManager::mkSkolemFunction(SkolemFunId id, TypeNode tn, Node cacheVal)
194
{
195
6346
  std::tuple<SkolemFunId, TypeNode, Node> key(id, tn, cacheVal);
196
  std::map<std::tuple<SkolemFunId, TypeNode, Node>, Node>::iterator it =
197
3173
      d_skolemFuns.find(key);
198
3173
  if (it == d_skolemFuns.end())
199
  {
200
748
    NodeManager* nm = NodeManager::currentNM();
201
1496
    std::stringstream ss;
202
748
    ss << "SKOLEM_FUN_" << id;
203
1496
    Node k = nm->mkSkolem(ss.str(), tn, "an internal skolem function");
204
748
    d_skolemFuns[key] = k;
205
748
    return k;
206
  }
207
2425
  return it->second;
208
}
209
210
115912
Node SkolemManager::mkDummySkolem(const std::string& prefix,
211
                                  const TypeNode& type,
212
                                  const std::string& comment,
213
                                  int flags)
214
{
215
115912
  return NodeManager::currentNM()->mkSkolem(prefix, type, comment, flags);
216
}
217
218
75
ProofGenerator* SkolemManager::getProofGenerator(Node t) const
219
{
220
75
  std::map<Node, ProofGenerator*>::const_iterator it = d_gens.find(t);
221
75
  if (it != d_gens.end())
222
  {
223
    return it->second;
224
  }
225
75
  return nullptr;
226
}
227
228
1554
Node SkolemManager::getWitnessForm(Node k)
229
{
230
1554
  Assert(k.getKind() == SKOLEM);
231
  // simply look up the witness form for k via an attribute
232
  WitnessFormAttribute wfa;
233
1554
  return k.getAttribute(wfa);
234
}
235
236
199428
Node SkolemManager::getOriginalForm(Node n)
237
{
238
199428
  if (n.isNull())
239
  {
240
4
    return n;
241
  }
242
398848
  Trace("sk-manager-debug")
243
199424
      << "SkolemManager::getOriginalForm " << n << std::endl;
244
  OriginalFormAttribute ofa;
245
199424
  NodeManager* nm = NodeManager::currentNM();
246
398848
  std::unordered_map<TNode, Node> visited;
247
199424
  std::unordered_map<TNode, Node>::iterator it;
248
398848
  std::vector<TNode> visit;
249
398848
  TNode cur;
250
199424
  visit.push_back(n);
251
561363
  do
252
  {
253
760787
    cur = visit.back();
254
760787
    visit.pop_back();
255
760787
    it = visited.find(cur);
256
257
760787
    if (it == visited.end())
258
    {
259
507622
      if (cur.hasAttribute(ofa))
260
      {
261
327118
        visited[cur] = cur.getAttribute(ofa);
262
      }
263
      else
264
      {
265
180504
        visited[cur] = Node::null();
266
180504
        visit.push_back(cur);
267
180504
        if (cur.getMetaKind() == metakind::PARAMETERIZED)
268
        {
269
23009
          visit.push_back(cur.getOperator());
270
        }
271
538354
        for (const Node& cn : cur)
272
        {
273
357850
          visit.push_back(cn);
274
        }
275
      }
276
    }
277
253165
    else if (it->second.isNull())
278
    {
279
361008
      Node ret = cur;
280
180504
      bool childChanged = false;
281
361008
      std::vector<Node> children;
282
180504
      if (cur.getMetaKind() == metakind::PARAMETERIZED)
283
      {
284
23009
        it = visited.find(cur.getOperator());
285
23009
        Assert(it != visited.end());
286
23009
        Assert(!it->second.isNull());
287
23009
        childChanged = childChanged || cur.getOperator() != it->second;
288
23009
        children.push_back(it->second);
289
      }
290
538354
      for (const Node& cn : cur)
291
      {
292
357850
        it = visited.find(cn);
293
357850
        Assert(it != visited.end());
294
357850
        Assert(!it->second.isNull());
295
357850
        childChanged = childChanged || cn != it->second;
296
357850
        children.push_back(it->second);
297
      }
298
180504
      if (childChanged)
299
      {
300
40399
        ret = nm->mkNode(cur.getKind(), children);
301
      }
302
180504
      cur.setAttribute(ofa, ret);
303
180504
      visited[cur] = ret;
304
    }
305
760787
  } while (!visit.empty());
306
199424
  Assert(visited.find(n) != visited.end());
307
199424
  Assert(!visited.find(n)->second.isNull());
308
199424
  Trace("sk-manager-debug") << "..return " << visited[n] << std::endl;
309
199424
  return visited[n];
310
}
311
312
50546
Node SkolemManager::mkSkolemInternal(Node w,
313
                                     const std::string& prefix,
314
                                     const std::string& comment,
315
                                     int flags)
316
{
317
  // note that witness, original forms are independent, but share skolems
318
50546
  NodeManager* nm = NodeManager::currentNM();
319
  // w is not necessarily a witness term
320
  SkolemFormAttribute sfa;
321
101092
  Node k;
322
  // could already have a skolem if we used w already
323
50546
  if (w.hasAttribute(sfa))
324
  {
325
9492
    return w.getAttribute(sfa);
326
  }
327
  // make the new skolem
328
41054
  if (flags & NodeManager::SKOLEM_BOOL_TERM_VAR)
329
  {
330
748
    Assert (w.getType().isBoolean());
331
748
    k = nm->mkBooleanTermVariable();
332
  }
333
  else
334
  {
335
40306
    k = nm->mkSkolem(prefix, w.getType(), comment, flags);
336
  }
337
  // set skolem form attribute for w
338
41054
  w.setAttribute(sfa, k);
339
82108
  Trace("sk-manager") << "SkolemManager::mkSkolem: " << k << " : " << w
340
41054
                      << std::endl;
341
41054
  return k;
342
}
343
344
27735
}  // namespace cvc5