GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/expr/skolem_manager.cpp Lines: 155 158 98.1 %
Date: 2021-05-22 Branches: 309 746 41.4 %

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
2653
const char* toString(SkolemFunId id)
46
{
47
2653
  switch (id)
48
  {
49
81
    case SkolemFunId::DIV_BY_ZERO: return "DIV_BY_ZERO";
50
61
    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
565
    case SkolemFunId::SELECTOR_WRONG: return "SELECTOR_WRONG";
54
1885
    case SkolemFunId::SHARED_SELECTOR: return "SHARED_SELECTOR";
55
18
    case SkolemFunId::SEQ_NTH_OOB: return "SEQ_NTH_OOB";
56
    default: return "?";
57
  }
58
}
59
60
2653
std::ostream& operator<<(std::ostream& out, SkolemFunId id)
61
{
62
2653
  out << toString(id);
63
2653
  return out;
64
}
65
66
5112
Node SkolemManager::mkSkolem(Node v,
67
                             Node pred,
68
                             const std::string& prefix,
69
                             const std::string& comment,
70
                             int flags,
71
                             ProofGenerator* pg)
72
{
73
  // We do not currently insist that pred does not contain witness terms
74
5112
  Assert(v.getKind() == BOUND_VARIABLE);
75
  // make the witness term
76
5112
  NodeManager* nm = NodeManager::currentNM();
77
10224
  Node bvl = nm->mkNode(BOUND_VAR_LIST, v);
78
  // Make the witness term, where notice that pred may contain skolems. We do
79
  // not recursively convert pred to witness form, since witness terms should
80
  // be treated as opaque. Moreover, the use of witness forms leads to
81
  // variable shadowing issues in e.g. skolemization.
82
10224
  Node w = nm->mkNode(WITNESS, bvl, pred);
83
  // store the mapping to proof generator if it exists
84
5112
  if (pg != nullptr)
85
  {
86
    // We cache based on the existential of the original predicate
87
3458
    Node q = nm->mkNode(EXISTS, bvl, pred);
88
    // Notice this may overwrite an existing proof generator. This does not
89
    // matter since either should be able to prove q.
90
1729
    d_gens[q] = pg;
91
  }
92
5112
  Node k = mkSkolemInternal(w, prefix, comment, flags);
93
  // set witness form attribute for k
94
  WitnessFormAttribute wfa;
95
5112
  k.setAttribute(wfa, w);
96
10224
  Trace("sk-manager-skolem")
97
5112
      << "skolem: " << k << " witness " << w << std::endl;
98
10224
  return k;
99
}
100
101
1170
Node SkolemManager::mkSkolemize(Node q,
102
                                std::vector<Node>& skolems,
103
                                const std::string& prefix,
104
                                const std::string& comment,
105
                                int flags,
106
                                ProofGenerator* pg)
107
{
108
1170
  Trace("sk-manager-debug") << "mkSkolemize " << q << std::endl;
109
1170
  Assert(q.getKind() == EXISTS);
110
1170
  Node currQ = q;
111
3451
  for (const Node& av : q[0])
112
  {
113
2281
    Assert(currQ.getKind() == EXISTS && av == currQ[0][0]);
114
    // currQ is updated to the result of skolemizing its first variable in
115
    // the method below.
116
4562
    Node sk = skolemize(currQ, currQ, prefix, comment, flags);
117
4562
    Trace("sk-manager-debug")
118
2281
        << "made skolem " << sk << " for " << av << std::endl;
119
2281
    skolems.push_back(sk);
120
  }
121
1170
  if (pg != nullptr)
122
  {
123
    // Same as above, this may overwrite an existing proof generator
124
    d_gens[q] = pg;
125
  }
126
1170
  Trace("sk-manager-debug") << "...mkSkolemize returns " << currQ << std::endl;
127
1170
  return currQ;
128
}
129
130
2281
Node SkolemManager::skolemize(Node q,
131
                              Node& qskolem,
132
                              const std::string& prefix,
133
                              const std::string& comment,
134
                              int flags)
135
{
136
2281
  Assert(q.getKind() == EXISTS);
137
4562
  Node v;
138
4562
  std::vector<Node> ovars;
139
2281
  Trace("sk-manager-debug") << "mkSkolemize..." << std::endl;
140
2281
  NodeManager* nm = NodeManager::currentNM();
141
7326
  for (const Node& av : q[0])
142
  {
143
9607
    if (v.isNull())
144
    {
145
2281
      v = av;
146
2281
      continue;
147
    }
148
5045
    ovars.push_back(av);
149
  }
150
2281
  Assert(!v.isNull());
151
  // make the predicate with one variable stripped off
152
4562
  Node pred = q[1];
153
2281
  Trace("sk-manager-debug") << "make exists predicate" << std::endl;
154
2281
  if (!ovars.empty())
155
  {
156
    // keeps the same variables
157
2222
    Node bvl = nm->mkNode(BOUND_VAR_LIST, ovars);
158
    // update the predicate
159
1111
    pred = nm->mkNode(EXISTS, bvl, pred);
160
  }
161
2281
  Trace("sk-manager-debug") << "call sub mkSkolem" << std::endl;
162
  // don't use a proof generator, since this may be an intermediate, partially
163
  // skolemized formula.
164
2281
  Node k = mkSkolem(v, pred, prefix, comment, flags, nullptr);
165
2281
  Assert(k.getType() == v.getType());
166
4562
  TNode tv = v;
167
4562
  TNode tk = k;
168
4562
  Trace("sk-manager-debug")
169
2281
      << "qskolem apply " << tv << " -> " << tk << " to " << pred << std::endl;
170
  // the quantified formula with one step of skolemization
171
2281
  qskolem = pred.substitute(tv, tk);
172
2281
  Trace("sk-manager-debug") << "qskolem done substitution" << std::endl;
173
4562
  return k;
174
}
175
176
48357
Node SkolemManager::mkPurifySkolem(Node t,
177
                                   const std::string& prefix,
178
                                   const std::string& comment,
179
                                   int flags)
180
{
181
96714
  Node to = getOriginalForm(t);
182
  // We do not currently insist that to does not contain witness terms
183
184
48357
  Node k = mkSkolemInternal(to, prefix, comment, flags);
185
  // set original form attribute for k
186
  OriginalFormAttribute ofa;
187
48357
  k.setAttribute(ofa, to);
188
189
96714
  Trace("sk-manager-skolem")
190
48357
      << "skolem: " << k << " purify " << to << std::endl;
191
96714
  return k;
192
}
193
194
5685
Node SkolemManager::mkSkolemFunction(SkolemFunId id,
195
                                     TypeNode tn,
196
                                     Node cacheVal,
197
                                     int flags)
198
{
199
11370
  std::tuple<SkolemFunId, TypeNode, Node> key(id, tn, cacheVal);
200
  std::map<std::tuple<SkolemFunId, TypeNode, Node>, Node>::iterator it =
201
5685
      d_skolemFuns.find(key);
202
5685
  if (it == d_skolemFuns.end())
203
  {
204
2653
    NodeManager* nm = NodeManager::currentNM();
205
5306
    std::stringstream ss;
206
2653
    ss << "SKOLEM_FUN_" << id;
207
5306
    Node k = nm->mkSkolem(ss.str(), tn, "an internal skolem function", flags);
208
2653
    d_skolemFuns[key] = k;
209
2653
    return k;
210
  }
211
3032
  return it->second;
212
}
213
214
114050
Node SkolemManager::mkDummySkolem(const std::string& prefix,
215
                                  const TypeNode& type,
216
                                  const std::string& comment,
217
                                  int flags)
218
{
219
114050
  return NodeManager::currentNM()->mkSkolem(prefix, type, comment, flags);
220
}
221
222
75
ProofGenerator* SkolemManager::getProofGenerator(Node t) const
223
{
224
75
  std::map<Node, ProofGenerator*>::const_iterator it = d_gens.find(t);
225
75
  if (it != d_gens.end())
226
  {
227
    return it->second;
228
  }
229
75
  return nullptr;
230
}
231
232
1786
Node SkolemManager::getWitnessForm(Node k)
233
{
234
1786
  Assert(k.getKind() == SKOLEM);
235
  // simply look up the witness form for k via an attribute
236
  WitnessFormAttribute wfa;
237
1786
  return k.getAttribute(wfa);
238
}
239
240
209921
Node SkolemManager::getOriginalForm(Node n)
241
{
242
209921
  if (n.isNull())
243
  {
244
4
    return n;
245
  }
246
419834
  Trace("sk-manager-debug")
247
209917
      << "SkolemManager::getOriginalForm " << n << std::endl;
248
  OriginalFormAttribute ofa;
249
209917
  NodeManager* nm = NodeManager::currentNM();
250
419834
  std::unordered_map<TNode, Node> visited;
251
209917
  std::unordered_map<TNode, Node>::iterator it;
252
419834
  std::vector<TNode> visit;
253
419834
  TNode cur;
254
209917
  visit.push_back(n);
255
594445
  do
256
  {
257
804362
    cur = visit.back();
258
804362
    visit.pop_back();
259
804362
    it = visited.find(cur);
260
261
804362
    if (it == visited.end())
262
    {
263
537507
      if (cur.hasAttribute(ofa))
264
      {
265
346755
        visited[cur] = cur.getAttribute(ofa);
266
      }
267
      else
268
      {
269
190752
        visited[cur] = Node::null();
270
190752
        visit.push_back(cur);
271
190752
        if (cur.getMetaKind() == metakind::PARAMETERIZED)
272
        {
273
27156
          visit.push_back(cur.getOperator());
274
        }
275
567289
        for (const Node& cn : cur)
276
        {
277
376537
          visit.push_back(cn);
278
        }
279
      }
280
    }
281
266855
    else if (it->second.isNull())
282
    {
283
381504
      Node ret = cur;
284
190752
      bool childChanged = false;
285
381504
      std::vector<Node> children;
286
190752
      if (cur.getMetaKind() == metakind::PARAMETERIZED)
287
      {
288
27156
        it = visited.find(cur.getOperator());
289
27156
        Assert(it != visited.end());
290
27156
        Assert(!it->second.isNull());
291
27156
        childChanged = childChanged || cur.getOperator() != it->second;
292
27156
        children.push_back(it->second);
293
      }
294
567289
      for (const Node& cn : cur)
295
      {
296
376537
        it = visited.find(cn);
297
376537
        Assert(it != visited.end());
298
376537
        Assert(!it->second.isNull());
299
376537
        childChanged = childChanged || cn != it->second;
300
376537
        children.push_back(it->second);
301
      }
302
190752
      if (childChanged)
303
      {
304
47506
        ret = nm->mkNode(cur.getKind(), children);
305
      }
306
190752
      cur.setAttribute(ofa, ret);
307
190752
      visited[cur] = ret;
308
    }
309
804362
  } while (!visit.empty());
310
209917
  Assert(visited.find(n) != visited.end());
311
209917
  Assert(!visited.find(n)->second.isNull());
312
209917
  Trace("sk-manager-debug") << "..return " << visited[n] << std::endl;
313
209917
  return visited[n];
314
}
315
316
53469
Node SkolemManager::mkSkolemInternal(Node w,
317
                                     const std::string& prefix,
318
                                     const std::string& comment,
319
                                     int flags)
320
{
321
  // note that witness, original forms are independent, but share skolems
322
53469
  NodeManager* nm = NodeManager::currentNM();
323
  // w is not necessarily a witness term
324
  SkolemFormAttribute sfa;
325
106938
  Node k;
326
  // could already have a skolem if we used w already
327
53469
  if (w.hasAttribute(sfa))
328
  {
329
9716
    return w.getAttribute(sfa);
330
  }
331
  // make the new skolem
332
43753
  if (flags & NodeManager::SKOLEM_BOOL_TERM_VAR)
333
  {
334
775
    Assert (w.getType().isBoolean());
335
775
    k = nm->mkBooleanTermVariable();
336
  }
337
  else
338
  {
339
42978
    k = nm->mkSkolem(prefix, w.getType(), comment, flags);
340
  }
341
  // set skolem form attribute for w
342
43753
  w.setAttribute(sfa, k);
343
87506
  Trace("sk-manager") << "SkolemManager::mkSkolem: " << k << " : " << w
344
43753
                      << std::endl;
345
43753
  return k;
346
}
347
348
28194
}  // namespace cvc5