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

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