GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/skolemize.cpp Lines: 184 206 89.3 %
Date: 2021-09-16 Branches: 417 992 42.0 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Mathias Preiner, Andres Noetzli
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 skolemization utility.
14
 */
15
16
#include "theory/quantifiers/skolemize.h"
17
18
#include "expr/dtype.h"
19
#include "expr/dtype_cons.h"
20
#include "expr/skolem_manager.h"
21
#include "options/quantifiers_options.h"
22
#include "options/smt_options.h"
23
#include "proof/proof.h"
24
#include "proof/proof_node_manager.h"
25
#include "theory/quantifiers/quantifiers_attributes.h"
26
#include "theory/quantifiers/quantifiers_state.h"
27
#include "theory/quantifiers/term_registry.h"
28
#include "theory/quantifiers/term_util.h"
29
#include "theory/rewriter.h"
30
#include "theory/sort_inference.h"
31
32
using namespace cvc5::kind;
33
34
namespace cvc5 {
35
namespace theory {
36
namespace quantifiers {
37
38
9942
Skolemize::Skolemize(Env& env,
39
                     QuantifiersState& qs,
40
                     TermRegistry& tr,
41
9942
                     ProofNodeManager* pnm)
42
    : EnvObj(env),
43
      d_qstate(qs),
44
      d_treg(tr),
45
9942
      d_skolemized(userContext()),
46
      d_pnm(pnm),
47
      d_epg(pnm == nullptr
48
                ? nullptr
49
19884
                : new EagerProofGenerator(pnm, userContext(), "Skolemize::epg"))
50
{
51
9942
}
52
53
10686
TrustNode Skolemize::process(Node q)
54
{
55
10686
  Assert(q.getKind() == FORALL);
56
  // do skolemization
57
10686
  if (d_skolemized.find(q) != d_skolemized.end())
58
  {
59
8288
    return TrustNode::null();
60
  }
61
4796
  Node lem;
62
2398
  ProofGenerator* pg = nullptr;
63
5134
  if (isProofEnabled() && !options::dtStcInduction()
64
2722
      && !options::intWfInduction())
65
  {
66
    // if using proofs and not using induction, we use the justified
67
    // skolemization
68
323
    NodeManager* nm = NodeManager::currentNM();
69
323
    SkolemManager* skm = nm->getSkolemManager();
70
646
    std::vector<Node> echildren(q.begin(), q.end());
71
323
    echildren[1] = echildren[1].notNode();
72
646
    Node existsq = nm->mkNode(EXISTS, echildren);
73
646
    Node res = skm->mkSkolemize(existsq, d_skolem_constants[q], "skv");
74
646
    Node qnot = q.notNode();
75
646
    CDProof cdp(d_pnm);
76
323
    cdp.addStep(res, PfRule::SKOLEMIZE, {qnot}, {});
77
646
    std::shared_ptr<ProofNode> pf = cdp.getProofFor(res);
78
646
    std::vector<Node> assumps;
79
323
    assumps.push_back(qnot);
80
646
    std::shared_ptr<ProofNode> pfs = d_pnm->mkScope({pf}, assumps);
81
323
    lem = nm->mkNode(IMPLIES, qnot, res);
82
323
    d_epg->setProofFor(lem, pfs);
83
323
    pg = d_epg.get();
84
646
    Trace("quantifiers-sk")
85
323
        << "Skolemize (with proofs) : " << d_skolem_constants[q] << " for "
86
323
        << std::endl;
87
323
    Trace("quantifiers-sk") << "   " << q << std::endl;
88
323
    Trace("quantifiers-sk") << "   " << res << std::endl;
89
  }
90
  else
91
  {
92
    // otherwise, we use the more general skolemization with inductive
93
    // strengthening, which does not support proofs
94
4150
    Node body = getSkolemizedBody(q);
95
4150
    NodeBuilder nb(kind::OR);
96
2075
    nb << q << body.notNode();
97
2075
    lem = nb;
98
  }
99
2398
  d_skolemized[q] = lem;
100
  // triggered when skolemizing
101
2398
  d_treg.processSkolemization(q, d_skolem_constants[q]);
102
2398
  return TrustNode::mkTrustLemma(lem, pg);
103
}
104
105
33
bool Skolemize::getSkolemConstants(Node q, std::vector<Node>& skolems)
106
{
107
  std::unordered_map<Node, std::vector<Node>>::iterator it =
108
33
      d_skolem_constants.find(q);
109
33
  if (it != d_skolem_constants.end())
110
  {
111
33
    skolems.insert(skolems.end(), it->second.begin(), it->second.end());
112
33
    return true;
113
  }
114
  return false;
115
}
116
117
Node Skolemize::getSkolemConstant(Node q, unsigned i)
118
{
119
  std::unordered_map<Node, std::vector<Node>>::iterator it =
120
      d_skolem_constants.find(q);
121
  if (it != d_skolem_constants.end())
122
  {
123
    if (i < it->second.size())
124
    {
125
      return it->second[i];
126
    }
127
  }
128
  return Node::null();
129
}
130
131
114
void Skolemize::getSelfSel(const DType& dt,
132
                           const DTypeConstructor& dc,
133
                           Node n,
134
                           TypeNode ntn,
135
                           std::vector<Node>& selfSel)
136
{
137
228
  TypeNode tspec;
138
114
  if (dt.isParametric())
139
  {
140
    tspec = dc.getSpecializedConstructorType(n.getType());
141
    Trace("sk-ind-debug") << "Specialized constructor type : " << tspec
142
                          << std::endl;
143
    Assert(tspec.getNumChildren() == dc.getNumArgs());
144
  }
145
228
  Trace("sk-ind-debug") << "Check self sel " << dc.getName() << " "
146
114
                        << dt.getName() << std::endl;
147
114
  NodeManager* nm = NodeManager::currentNM();
148
219
  for (unsigned j = 0; j < dc.getNumArgs(); j++)
149
  {
150
210
    std::vector<Node> ssc;
151
105
    if (dt.isParametric())
152
    {
153
      Trace("sk-ind-debug") << "Compare " << tspec[j] << " " << ntn
154
                            << std::endl;
155
      if (tspec[j] == ntn)
156
      {
157
        ssc.push_back(n);
158
      }
159
    }
160
    else
161
    {
162
210
      TypeNode tn = dc[j].getRangeType();
163
105
      Trace("sk-ind-debug") << "Compare " << tn << " " << ntn << std::endl;
164
105
      if (tn == ntn)
165
      {
166
57
        ssc.push_back(n);
167
      }
168
    }
169
162
    for (unsigned k = 0; k < ssc.size(); k++)
170
    {
171
      Node ss = nm->mkNode(
172
114
          APPLY_SELECTOR_TOTAL, dc.getSelectorInternal(n.getType(), j), n);
173
57
      if (std::find(selfSel.begin(), selfSel.end(), ss) == selfSel.end())
174
      {
175
57
        selfSel.push_back(ss);
176
      }
177
    }
178
  }
179
114
}
180
181
2084
Node Skolemize::mkSkolemizedBody(Node f,
182
                                 Node n,
183
                                 std::vector<TNode>& fvs,
184
                                 std::vector<Node>& sk,
185
                                 Node& sub,
186
                                 std::vector<unsigned>& sub_vars)
187
{
188
2084
  NodeManager* nm = NodeManager::currentNM();
189
  // compute the argument types from the free variables
190
4168
  std::vector<TypeNode> argTypes;
191
2096
  for (TNode v : fvs)
192
  {
193
12
    argTypes.push_back(v.getType());
194
  }
195
2084
  SkolemManager* sm = nm->getSkolemManager();
196
2084
  Assert(sk.empty() || sk.size() == f[0].getNumChildren());
197
  // calculate the variables and substitution
198
4168
  std::vector<TNode> ind_vars;
199
4168
  std::vector<unsigned> ind_var_indicies;
200
4168
  std::vector<TNode> vars;
201
4168
  std::vector<unsigned> var_indicies;
202
5356
  for (unsigned i = 0; i < f[0].getNumChildren(); i++)
203
  {
204
3272
    if (isInductionTerm(f[0][i]))
205
    {
206
86
      ind_vars.push_back(f[0][i]);
207
86
      ind_var_indicies.push_back(i);
208
    }
209
    else
210
    {
211
3186
      vars.push_back(f[0][i]);
212
3186
      var_indicies.push_back(i);
213
    }
214
6544
    Node s;
215
    // make the new function symbol or use existing
216
3272
    if (i >= sk.size())
217
    {
218
3249
      if (argTypes.empty())
219
      {
220
9723
        s = sm->mkDummySkolem(
221
6482
            "skv", f[0][i].getType(), "created during skolemization");
222
      }
223
      else
224
      {
225
16
        TypeNode typ = nm->mkFunctionType(argTypes, f[0][i].getType());
226
        Node op = sm->mkDummySkolem(
227
16
            "skop", typ, "op created during pre-skolemization");
228
        // DOTHIS: set attribute on op, marking that it should not be selected
229
        // as trigger
230
16
        std::vector<Node> funcArgs;
231
8
        funcArgs.push_back(op);
232
8
        funcArgs.insert(funcArgs.end(), fvs.begin(), fvs.end());
233
8
        s = nm->mkNode(kind::APPLY_UF, funcArgs);
234
      }
235
3249
      sk.push_back(s);
236
    }
237
    else
238
    {
239
23
      Assert(sk[i].getType() == f[0][i].getType());
240
    }
241
  }
242
2084
  Node ret;
243
2084
  if (vars.empty())
244
  {
245
63
    ret = n;
246
  }
247
  else
248
  {
249
4042
    std::vector<Node> var_sk;
250
5207
    for (unsigned i = 0; i < var_indicies.size(); i++)
251
    {
252
3186
      var_sk.push_back(sk[var_indicies[i]]);
253
    }
254
2021
    Assert(vars.size() == var_sk.size());
255
2021
    ret = n.substitute(vars.begin(), vars.end(), var_sk.begin(), var_sk.end());
256
  }
257
2084
  if (!ind_vars.empty())
258
  {
259
63
    Trace("sk-ind") << "Ind strengthen : (not " << f << ")" << std::endl;
260
63
    Trace("sk-ind") << "Skolemized is : " << ret << std::endl;
261
126
    Node n_str_ind;
262
126
    TypeNode tn = ind_vars[0].getType();
263
126
    Node k = sk[ind_var_indicies[0]];
264
126
    Node nret = ret.substitute(ind_vars[0], k);
265
    // note : everything is under a negation
266
    // the following constructs ~( R( x, k ) => ~P( x ) )
267
63
    if (options::dtStcInduction() && tn.isDatatype())
268
    {
269
57
      const DType& dt = tn.getDType();
270
114
      std::vector<Node> disj;
271
171
      for (unsigned i = 0; i < dt.getNumConstructors(); i++)
272
      {
273
228
        std::vector<Node> selfSel;
274
114
        getSelfSel(dt, dt[i], k, tn, selfSel);
275
228
        std::vector<Node> conj;
276
114
        conj.push_back(nm->mkNode(APPLY_TESTER, dt[i].getTester(), k).negate());
277
171
        for (unsigned j = 0; j < selfSel.size(); j++)
278
        {
279
57
          conj.push_back(ret.substitute(ind_vars[0], selfSel[j]).negate());
280
        }
281
114
        disj.push_back(conj.size() == 1 ? conj[0] : nm->mkNode(OR, conj));
282
      }
283
57
      Assert(!disj.empty());
284
57
      n_str_ind = disj.size() == 1 ? disj[0] : nm->mkNode(AND, disj);
285
    }
286
6
    else if (options::intWfInduction() && tn.isInteger())
287
    {
288
12
      Node icond = nm->mkNode(GEQ, k, nm->mkConst(Rational(0)));
289
12
      Node iret = ret.substitute(ind_vars[0],
290
12
                                 nm->mkNode(MINUS, k, nm->mkConst(Rational(1))))
291
12
                      .negate();
292
6
      n_str_ind = nm->mkNode(OR, icond.negate(), iret);
293
6
      n_str_ind = nm->mkNode(AND, icond, n_str_ind);
294
    }
295
    else
296
    {
297
      Trace("sk-ind") << "Unknown induction for term : " << ind_vars[0]
298
                      << ", type = " << tn << std::endl;
299
      Assert(false);
300
    }
301
63
    Trace("sk-ind") << "Strengthening is : " << n_str_ind << std::endl;
302
303
126
    std::vector<Node> rem_ind_vars;
304
63
    rem_ind_vars.insert(
305
126
        rem_ind_vars.end(), ind_vars.begin() + 1, ind_vars.end());
306
63
    if (!rem_ind_vars.empty())
307
    {
308
38
      Node bvl = nm->mkNode(BOUND_VAR_LIST, rem_ind_vars);
309
19
      nret = nm->mkNode(FORALL, bvl, nret);
310
19
      nret = Rewriter::rewrite(nret);
311
19
      sub = nret;
312
19
      sub_vars.insert(
313
38
          sub_vars.end(), ind_var_indicies.begin() + 1, ind_var_indicies.end());
314
19
      n_str_ind = nm->mkNode(FORALL, bvl, n_str_ind.negate()).negate();
315
    }
316
63
    ret = nm->mkNode(OR, nret, n_str_ind);
317
  }
318
4168
  Trace("quantifiers-sk-debug") << "mkSkolem body for " << f
319
2084
                                << " returns : " << ret << std::endl;
320
  // if it has an instantiation level, set the skolemized body to that level
321
2084
  if (f.hasAttribute(InstLevelAttribute()))
322
  {
323
    QuantAttributes::setInstantiationLevelAttr(
324
        ret, f.getAttribute(InstLevelAttribute()));
325
  }
326
327
2084
  Trace("quantifiers-sk") << "Skolemize : " << sk << " for " << std::endl;
328
2084
  Trace("quantifiers-sk") << "   " << f << std::endl;
329
330
4168
  return ret;
331
}
332
333
2075
Node Skolemize::getSkolemizedBody(Node f)
334
{
335
2075
  Assert(f.getKind() == FORALL);
336
2075
  std::unordered_map<Node, Node>::iterator it = d_skolem_body.find(f);
337
2075
  if (it == d_skolem_body.end())
338
  {
339
4132
    std::vector<TNode> fvs;
340
4132
    Node sub;
341
4132
    std::vector<unsigned> sub_vars;
342
    Node ret =
343
4132
        mkSkolemizedBody(f, f[1], fvs, d_skolem_constants[f], sub, sub_vars);
344
2066
    d_skolem_body[f] = ret;
345
    // store sub quantifier information
346
2066
    if (!sub.isNull())
347
    {
348
      // if we are skolemizing one at a time, we already know the skolem
349
      // constants of the sub-quantified formula, store them
350
19
      Assert(d_skolem_constants[sub].empty());
351
42
      for (unsigned i = 0; i < sub_vars.size(); i++)
352
      {
353
23
        d_skolem_constants[sub].push_back(d_skolem_constants[f][sub_vars[i]]);
354
      }
355
    }
356
2066
    Assert(d_skolem_constants[f].size() == f[0].getNumChildren());
357
2066
    SortInference* si = d_qstate.getSortInference();
358
2066
    if (si != nullptr)
359
    {
360
      for (unsigned i = 0; i < d_skolem_constants[f].size(); i++)
361
      {
362
        // carry information for sort inference
363
        si->setSkolemVar(f, f[0][i], d_skolem_constants[f][i]);
364
      }
365
    }
366
2066
    return ret;
367
  }
368
9
  return it->second;
369
}
370
371
9188
bool Skolemize::isInductionTerm(Node n)
372
{
373
18376
  TypeNode tn = n.getType();
374
9188
  if (options::dtStcInduction() && tn.isDatatype())
375
  {
376
3909
    const DType& dt = tn.getDType();
377
3909
    return !dt.isCodatatype();
378
  }
379
5279
  if (options::intWfInduction() && tn.isInteger())
380
  {
381
1324
    return true;
382
  }
383
3955
  return false;
384
}
385
386
12
void Skolemize::getSkolemTermVectors(
387
    std::map<Node, std::vector<Node> >& sks) const
388
{
389
12
  std::unordered_map<Node, std::vector<Node>>::const_iterator itk;
390
21
  for (const auto& p : d_skolemized)
391
  {
392
18
    Node q = p.first;
393
9
    itk = d_skolem_constants.find(q);
394
9
    Assert(itk != d_skolem_constants.end());
395
9
    sks[q].insert(sks[q].end(), itk->second.begin(), itk->second.end());
396
  }
397
12
}
398
399
2398
bool Skolemize::isProofEnabled() const { return d_epg != nullptr; }
400
401
}  // namespace quantifiers
402
}  // namespace theory
403
29577
}  // namespace cvc5