GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/skolemize.cpp Lines: 182 204 89.2 %
Date: 2021-05-22 Branches: 416 1000 41.6 %

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