GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/builtin/proof_checker.cpp Lines: 170 219 77.6 %
Date: 2021-11-07 Branches: 304 960 31.7 %

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 equality proof checker.
14
 */
15
16
#include "theory/builtin/proof_checker.h"
17
18
#include "expr/skolem_manager.h"
19
#include "smt/env.h"
20
#include "smt/term_formula_removal.h"
21
#include "theory/rewriter.h"
22
#include "theory/substitutions.h"
23
#include "theory/theory.h"
24
#include "util/rational.h"
25
26
using namespace cvc5::kind;
27
28
namespace cvc5 {
29
namespace theory {
30
namespace builtin {
31
32
15273
BuiltinProofRuleChecker::BuiltinProofRuleChecker(Env& env) : d_env(env) {}
33
34
7989
void BuiltinProofRuleChecker::registerTo(ProofChecker* pc)
35
{
36
7989
  pc->registerChecker(PfRule::ASSUME, this);
37
7989
  pc->registerChecker(PfRule::SCOPE, this);
38
7989
  pc->registerChecker(PfRule::SUBS, this);
39
7989
  pc->registerChecker(PfRule::REWRITE, this);
40
7989
  pc->registerChecker(PfRule::EVALUATE, this);
41
7989
  pc->registerChecker(PfRule::MACRO_SR_EQ_INTRO, this);
42
7989
  pc->registerChecker(PfRule::MACRO_SR_PRED_INTRO, this);
43
7989
  pc->registerChecker(PfRule::MACRO_SR_PRED_ELIM, this);
44
7989
  pc->registerChecker(PfRule::MACRO_SR_PRED_TRANSFORM, this);
45
7989
  pc->registerChecker(PfRule::THEORY_REWRITE, this);
46
7989
  pc->registerChecker(PfRule::REMOVE_TERM_FORMULA_AXIOM, this);
47
  // trusted rules
48
7989
  pc->registerTrustedChecker(PfRule::THEORY_LEMMA, this, 1);
49
7989
  pc->registerTrustedChecker(PfRule::PREPROCESS, this, 3);
50
7989
  pc->registerTrustedChecker(PfRule::PREPROCESS_LEMMA, this, 3);
51
7989
  pc->registerTrustedChecker(PfRule::THEORY_PREPROCESS, this, 3);
52
7989
  pc->registerTrustedChecker(PfRule::THEORY_PREPROCESS_LEMMA, this, 3);
53
7989
  pc->registerTrustedChecker(PfRule::THEORY_EXPAND_DEF, this, 3);
54
7989
  pc->registerTrustedChecker(PfRule::WITNESS_AXIOM, this, 3);
55
7989
  pc->registerTrustedChecker(PfRule::TRUST_REWRITE, this, 1);
56
7989
  pc->registerTrustedChecker(PfRule::TRUST_SUBS, this, 1);
57
7989
  pc->registerTrustedChecker(PfRule::TRUST_SUBS_MAP, this, 1);
58
7989
  pc->registerTrustedChecker(PfRule::TRUST_SUBS_EQ, this, 3);
59
7989
  pc->registerTrustedChecker(PfRule::THEORY_INFERENCE, this, 3);
60
7989
  pc->registerChecker(PfRule::ALETHE_RULE, this);
61
7989
}
62
63
1524573
Node BuiltinProofRuleChecker::applySubstitutionRewrite(
64
    Node n,
65
    const std::vector<Node>& exp,
66
    MethodId ids,
67
    MethodId ida,
68
    MethodId idr)
69
{
70
3049146
  Node nks = applySubstitution(n, exp, ids, ida);
71
3049146
  return d_env.rewriteViaMethod(nks, idr);
72
}
73
74
3023825
bool BuiltinProofRuleChecker::getSubstitutionForLit(Node exp,
75
                                                    TNode& var,
76
                                                    TNode& subs,
77
                                                    MethodId ids)
78
{
79
3023825
  if (ids == MethodId::SB_DEFAULT)
80
  {
81
3008009
    if (exp.getKind() != EQUAL)
82
    {
83
      return false;
84
    }
85
3008009
    var = exp[0];
86
3008009
    subs = exp[1];
87
  }
88
15816
  else if (ids == MethodId::SB_LITERAL)
89
  {
90
15816
    bool polarity = exp.getKind() != NOT;
91
15816
    var = polarity ? exp : exp[0];
92
15816
    subs = NodeManager::currentNM()->mkConst(polarity);
93
  }
94
  else if (ids == MethodId::SB_FORMULA)
95
  {
96
    var = exp;
97
    subs = NodeManager::currentNM()->mkConst(true);
98
  }
99
  else
100
  {
101
    Assert(false) << "BuiltinProofRuleChecker::applySubstitution: no "
102
                     "substitution for "
103
                  << ids << std::endl;
104
    return false;
105
  }
106
3023825
  return true;
107
}
108
109
3023825
bool BuiltinProofRuleChecker::getSubstitutionFor(Node exp,
110
                                                 std::vector<TNode>& vars,
111
                                                 std::vector<TNode>& subs,
112
                                                 std::vector<TNode>& from,
113
                                                 MethodId ids)
114
{
115
6047650
  TNode v;
116
6047650
  TNode s;
117
3023825
  if (exp.getKind() == AND && ids == MethodId::SB_DEFAULT)
118
  {
119
    for (const Node& ec : exp)
120
    {
121
      // non-recursive, do not use nested AND
122
      if (!getSubstitutionForLit(ec, v, s, ids))
123
      {
124
        return false;
125
      }
126
      vars.push_back(v);
127
      subs.push_back(s);
128
      from.push_back(ec);
129
    }
130
    return true;
131
  }
132
3023825
  bool ret = getSubstitutionForLit(exp, v, s, ids);
133
3023825
  vars.push_back(v);
134
3023825
  subs.push_back(s);
135
3023825
  from.push_back(exp);
136
3023825
  return ret;
137
}
138
139
Node BuiltinProofRuleChecker::applySubstitution(Node n,
140
                                                Node exp,
141
                                                MethodId ids,
142
                                                MethodId ida)
143
{
144
  std::vector<Node> expv{exp};
145
  return applySubstitution(n, expv, ids, ida);
146
}
147
148
1548205
Node BuiltinProofRuleChecker::applySubstitution(Node n,
149
                                                const std::vector<Node>& exp,
150
                                                MethodId ids,
151
                                                MethodId ida)
152
{
153
3096410
  std::vector<TNode> vars;
154
3096410
  std::vector<TNode> subs;
155
3096410
  std::vector<TNode> from;
156
4020245
  for (size_t i = 0, nexp = exp.size(); i < nexp; i++)
157
  {
158
2472040
    if (exp[i].isNull())
159
    {
160
      return Node::null();
161
    }
162
2472040
    if (!getSubstitutionFor(exp[i], vars, subs, from, ids))
163
    {
164
      return Node::null();
165
    }
166
  }
167
1548205
  if (ida == MethodId::SBA_SIMUL)
168
  {
169
    // simply apply the simultaneous substitution now
170
    return n.substitute(vars.begin(), vars.end(), subs.begin(), subs.end());
171
  }
172
1548205
  else if (ida == MethodId::SBA_FIXPOINT)
173
  {
174
74276
    SubstitutionMap sm;
175
2473361
    for (size_t i = 0, nvars = vars.size(); i < nvars; i++)
176
    {
177
2436223
      sm.addSubstitution(vars[i], subs[i]);
178
    }
179
74276
    Node ns = sm.apply(n);
180
37138
    return ns;
181
  }
182
1511067
  Assert(ida == MethodId::SBA_SEQUENTIAL);
183
  // we prefer n traversals of the term to n^2/2 traversals of range terms
184
3022134
  Node ns = n;
185
1546884
  for (size_t i = 0, nvars = vars.size(); i < nvars; i++)
186
  {
187
71634
    TNode v = vars[(nvars - 1) - i];
188
71634
    TNode s = subs[(nvars - 1) - i];
189
35817
    ns = ns.substitute(v, s);
190
  }
191
1511067
  return ns;
192
}
193
194
1216910
Node BuiltinProofRuleChecker::checkInternal(PfRule id,
195
                                            const std::vector<Node>& children,
196
                                            const std::vector<Node>& args)
197
{
198
1216910
  NodeManager * nm = NodeManager::currentNM();
199
  // compute what was proven
200
1216910
  if (id == PfRule::ASSUME)
201
  {
202
    Assert(children.empty());
203
    Assert(args.size() == 1);
204
    Assert(args[0].getType().isBoolean());
205
    return args[0];
206
  }
207
1216910
  else if (id == PfRule::SCOPE)
208
  {
209
116775
    Assert(children.size() == 1);
210
116775
    if (args.empty())
211
    {
212
      // no antecedant
213
      return children[0];
214
    }
215
233550
    Node ant = nm->mkAnd(args);
216
    // if the conclusion is false, its the negated antencedant only
217
116775
    if (children[0].isConst() && !children[0].getConst<bool>())
218
    {
219
69076
      return ant.notNode();
220
    }
221
47699
    return nm->mkNode(IMPLIES, ant, children[0]);
222
  }
223
1100135
  else if (id == PfRule::SUBS)
224
  {
225
    Assert(children.size() > 0);
226
    Assert(1 <= args.size() && args.size() <= 2);
227
    MethodId ids = MethodId::SB_DEFAULT;
228
    if (args.size() == 2 && !getMethodId(args[1], ids))
229
    {
230
      return Node::null();
231
    }
232
    std::vector<Node> exp;
233
    for (size_t i = 0, nchild = children.size(); i < nchild; i++)
234
    {
235
      exp.push_back(children[i]);
236
    }
237
    Node res = applySubstitution(args[0], exp, ids);
238
    if (res.isNull())
239
    {
240
      return Node::null();
241
    }
242
    return args[0].eqNode(res);
243
  }
244
1100135
  else if (id == PfRule::REWRITE)
245
  {
246
1849
    Assert(children.empty());
247
1849
    Assert(1 <= args.size() && args.size() <= 2);
248
1849
    MethodId idr = MethodId::RW_REWRITE;
249
1849
    if (args.size() == 2 && !getMethodId(args[1], idr))
250
    {
251
      return Node::null();
252
    }
253
3698
    Node res = d_env.rewriteViaMethod(args[0], idr);
254
1849
    if (res.isNull())
255
    {
256
      return Node::null();
257
    }
258
1849
    return args[0].eqNode(res);
259
  }
260
1098286
  else if (id == PfRule::EVALUATE)
261
  {
262
33294
    Assert(children.empty());
263
33294
    Assert(args.size() == 1);
264
66588
    Node res = d_env.rewriteViaMethod(args[0], MethodId::RW_EVALUATE);
265
33294
    if (res.isNull())
266
    {
267
      return Node::null();
268
    }
269
33294
    return args[0].eqNode(res);
270
  }
271
1064992
  else if (id == PfRule::MACRO_SR_EQ_INTRO)
272
  {
273
31044
    Assert(1 <= args.size() && args.size() <= 4);
274
    MethodId ids, ida, idr;
275
31044
    if (!getMethodIds(args, ids, ida, idr, 1))
276
    {
277
      return Node::null();
278
    }
279
62088
    Node res = applySubstitutionRewrite(args[0], children, ids, ida, idr);
280
31044
    if (res.isNull())
281
    {
282
      return Node::null();
283
    }
284
31044
    return args[0].eqNode(res);
285
  }
286
1033948
  else if (id == PfRule::MACRO_SR_PRED_INTRO)
287
  {
288
10872
    Trace("builtin-pfcheck") << "Check " << id << " " << children.size() << " "
289
5436
                             << args[0] << std::endl;
290
5436
    Assert(1 <= args.size() && args.size() <= 4);
291
    MethodId ids, ida, idr;
292
5436
    if (!getMethodIds(args, ids, ida, idr, 1))
293
    {
294
      return Node::null();
295
    }
296
10872
    Node res = applySubstitutionRewrite(args[0], children, ids, ida, idr);
297
5436
    if (res.isNull())
298
    {
299
      return Node::null();
300
    }
301
5436
    Trace("builtin-pfcheck") << "Result is " << res << std::endl;
302
10872
    Trace("builtin-pfcheck") << "Witness form is "
303
5436
                             << SkolemManager::getOriginalForm(res) << std::endl;
304
    // **** NOTE: can rewrite the witness form here. This enables certain lemmas
305
    // to be provable, e.g. (= k t) where k is a purification Skolem for t.
306
5436
    res = d_env.getRewriter()->rewrite(SkolemManager::getOriginalForm(res));
307
5436
    if (!res.isConst() || !res.getConst<bool>())
308
    {
309
4
      Trace("builtin-pfcheck")
310
2
          << "Failed to rewrite to true, res=" << res << std::endl;
311
2
      return Node::null();
312
    }
313
5434
    Trace("builtin-pfcheck") << "...success" << std::endl;
314
5434
    return args[0];
315
  }
316
1028512
  else if (id == PfRule::MACRO_SR_PRED_ELIM)
317
  {
318
1466
    Trace("builtin-pfcheck") << "Check " << id << " " << children.size() << " "
319
733
                             << args.size() << std::endl;
320
733
    Assert(children.size() >= 1);
321
733
    Assert(args.size() <= 3);
322
1466
    std::vector<Node> exp;
323
733
    exp.insert(exp.end(), children.begin() + 1, children.end());
324
    MethodId ids, ida, idr;
325
733
    if (!getMethodIds(args, ids, ida, idr, 0))
326
    {
327
      return Node::null();
328
    }
329
1466
    Node res1 = applySubstitutionRewrite(children[0], exp, ids, ida, idr);
330
733
    Trace("builtin-pfcheck") << "Returned " << res1 << std::endl;
331
733
    return res1;
332
  }
333
1027779
  else if (id == PfRule::MACRO_SR_PRED_TRANSFORM)
334
  {
335
1487360
    Trace("builtin-pfcheck") << "Check " << id << " " << children.size() << " "
336
743680
                             << args.size() << std::endl;
337
743680
    Assert(children.size() >= 1);
338
743680
    Assert(1 <= args.size() && args.size() <= 4);
339
743680
    Assert(args[0].getType().isBoolean());
340
    MethodId ids, ida, idr;
341
743680
    if (!getMethodIds(args, ids, ida, idr, 1))
342
    {
343
      return Node::null();
344
    }
345
1487360
    std::vector<Node> exp;
346
743680
    exp.insert(exp.end(), children.begin() + 1, children.end());
347
1487360
    Node res1 = applySubstitutionRewrite(children[0], exp, ids, ida, idr);
348
1487360
    Node res2 = applySubstitutionRewrite(args[0], exp, ids, ida, idr);
349
    // if not already equal, do rewriting
350
743680
    if (res1 != res2)
351
    {
352
      // can rewrite the witness forms
353
833
      res1 = d_env.getRewriter()->rewrite(SkolemManager::getOriginalForm(res1));
354
833
      res2 = d_env.getRewriter()->rewrite(SkolemManager::getOriginalForm(res2));
355
833
      if (res1.isNull() || res1 != res2)
356
      {
357
213
        Trace("builtin-pfcheck") << "Failed to match results" << std::endl;
358
213
        Trace("builtin-pfcheck-debug") << res1 << " vs " << res2 << std::endl;
359
213
        return Node::null();
360
      }
361
    }
362
743467
    return args[0];
363
  }
364
284099
  else if (id == PfRule::REMOVE_TERM_FORMULA_AXIOM)
365
  {
366
3324
    Assert(children.empty());
367
3324
    Assert(args.size() == 1);
368
3324
    return RemoveTermFormulas::getAxiomFor(args[0]);
369
  }
370
280775
  else if (id == PfRule::PREPROCESS || id == PfRule::PREPROCESS_LEMMA
371
279633
           || id == PfRule::THEORY_PREPROCESS
372
278746
           || id == PfRule::THEORY_PREPROCESS_LEMMA
373
278527
           || id == PfRule::THEORY_EXPAND_DEF || id == PfRule::WITNESS_AXIOM
374
278514
           || id == PfRule::THEORY_LEMMA || id == PfRule::THEORY_REWRITE
375
3424
           || id == PfRule::TRUST_REWRITE || id == PfRule::TRUST_SUBS
376
3424
           || id == PfRule::TRUST_SUBS_MAP || id == PfRule::TRUST_SUBS_EQ
377
3371
           || id == PfRule::THEORY_INFERENCE)
378
  {
379
    // "trusted" rules
380
280775
    Assert(!args.empty());
381
280775
    Assert(args[0].getType().isBoolean());
382
280775
    return args[0];
383
  }
384
385
  // no rule
386
  return Node::null();
387
}
388
389
109
bool BuiltinProofRuleChecker::getTheoryId(TNode n, TheoryId& tid)
390
{
391
  uint32_t i;
392
109
  if (!getUInt32(n, i))
393
  {
394
    return false;
395
  }
396
109
  tid = static_cast<TheoryId>(i);
397
109
  return true;
398
}
399
400
272132
Node BuiltinProofRuleChecker::mkTheoryIdNode(TheoryId tid)
401
{
402
  return NodeManager::currentNM()->mkConst(
403
272132
      Rational(static_cast<uint32_t>(tid)));
404
}
405
406
}  // namespace builtin
407
}  // namespace theory
408
31137
}  // namespace cvc5