GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/builtin/proof_checker.cpp Lines: 174 233 74.7 %
Date: 2021-08-01 Branches: 313 1010 31.0 %

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/term_formula_removal.h"
20
#include "theory/evaluator.h"
21
#include "theory/rewriter.h"
22
#include "theory/substitutions.h"
23
#include "theory/theory.h"
24
25
using namespace cvc5::kind;
26
27
namespace cvc5 {
28
namespace theory {
29
namespace builtin {
30
31
3756
void BuiltinProofRuleChecker::registerTo(ProofChecker* pc)
32
{
33
3756
  pc->registerChecker(PfRule::ASSUME, this);
34
3756
  pc->registerChecker(PfRule::SCOPE, this);
35
3756
  pc->registerChecker(PfRule::SUBS, this);
36
3756
  pc->registerChecker(PfRule::REWRITE, this);
37
3756
  pc->registerChecker(PfRule::EVALUATE, this);
38
3756
  pc->registerChecker(PfRule::MACRO_SR_EQ_INTRO, this);
39
3756
  pc->registerChecker(PfRule::MACRO_SR_PRED_INTRO, this);
40
3756
  pc->registerChecker(PfRule::MACRO_SR_PRED_ELIM, this);
41
3756
  pc->registerChecker(PfRule::MACRO_SR_PRED_TRANSFORM, this);
42
3756
  pc->registerChecker(PfRule::THEORY_REWRITE, this);
43
3756
  pc->registerChecker(PfRule::REMOVE_TERM_FORMULA_AXIOM, this);
44
  // trusted rules
45
3756
  pc->registerTrustedChecker(PfRule::THEORY_LEMMA, this, 1);
46
3756
  pc->registerTrustedChecker(PfRule::PREPROCESS, this, 3);
47
3756
  pc->registerTrustedChecker(PfRule::PREPROCESS_LEMMA, this, 3);
48
3756
  pc->registerTrustedChecker(PfRule::THEORY_PREPROCESS, this, 3);
49
3756
  pc->registerTrustedChecker(PfRule::THEORY_PREPROCESS_LEMMA, this, 3);
50
3756
  pc->registerTrustedChecker(PfRule::THEORY_EXPAND_DEF, this, 3);
51
3756
  pc->registerTrustedChecker(PfRule::WITNESS_AXIOM, this, 3);
52
3756
  pc->registerTrustedChecker(PfRule::TRUST_REWRITE, this, 1);
53
3756
  pc->registerTrustedChecker(PfRule::TRUST_SUBS, this, 1);
54
3756
  pc->registerTrustedChecker(PfRule::TRUST_SUBS_MAP, this, 1);
55
3756
  pc->registerTrustedChecker(PfRule::TRUST_SUBS_EQ, this, 3);
56
3756
  pc->registerTrustedChecker(PfRule::THEORY_INFERENCE, this, 3);
57
3756
}
58
59
1388936
Node BuiltinProofRuleChecker::applySubstitutionRewrite(
60
    Node n,
61
    const std::vector<Node>& exp,
62
    MethodId ids,
63
    MethodId ida,
64
    MethodId idr)
65
{
66
2777872
  Node nks = applySubstitution(n, exp, ids, ida);
67
2777872
  return applyRewrite(nks, idr);
68
}
69
70
1631091
Node BuiltinProofRuleChecker::applyRewrite(Node n, MethodId idr)
71
{
72
3262182
  Trace("builtin-pfcheck-debug")
73
1631091
      << "applyRewrite (" << idr << "): " << n << std::endl;
74
1631091
  if (idr == MethodId::RW_REWRITE)
75
  {
76
1591841
    return Rewriter::rewrite(n);
77
  }
78
39250
  if (idr == MethodId::RW_EXT_REWRITE)
79
  {
80
    return d_ext_rewriter.extendedRewrite(n);
81
  }
82
39250
  if (idr == MethodId::RW_REWRITE_EQ_EXT)
83
  {
84
211
    return Rewriter::rewriteEqualityExt(n);
85
  }
86
39039
  if (idr == MethodId::RW_EVALUATE)
87
  {
88
    Evaluator eval;
89
39039
    return eval.eval(n, {}, {}, false);
90
  }
91
  if (idr == MethodId::RW_IDENTITY)
92
  {
93
    // does nothing
94
    return n;
95
  }
96
  // unknown rewriter
97
  Assert(false) << "BuiltinProofRuleChecker::applyRewrite: no rewriter for "
98
                << idr << std::endl;
99
  return n;
100
}
101
102
2124105
bool BuiltinProofRuleChecker::getSubstitutionForLit(Node exp,
103
                                                    TNode& var,
104
                                                    TNode& subs,
105
                                                    MethodId ids)
106
{
107
2124105
  if (ids == MethodId::SB_DEFAULT)
108
  {
109
2087227
    if (exp.getKind() != EQUAL)
110
    {
111
      return false;
112
    }
113
2087227
    var = exp[0];
114
2087227
    subs = exp[1];
115
  }
116
36878
  else if (ids == MethodId::SB_LITERAL)
117
  {
118
36878
    bool polarity = exp.getKind() != NOT;
119
36878
    var = polarity ? exp : exp[0];
120
36878
    subs = NodeManager::currentNM()->mkConst(polarity);
121
  }
122
  else if (ids == MethodId::SB_FORMULA)
123
  {
124
    var = exp;
125
    subs = NodeManager::currentNM()->mkConst(true);
126
  }
127
  else
128
  {
129
    Assert(false) << "BuiltinProofRuleChecker::applySubstitution: no "
130
                     "substitution for "
131
                  << ids << std::endl;
132
    return false;
133
  }
134
2124105
  return true;
135
}
136
137
2124105
bool BuiltinProofRuleChecker::getSubstitutionFor(Node exp,
138
                                                 std::vector<TNode>& vars,
139
                                                 std::vector<TNode>& subs,
140
                                                 std::vector<TNode>& from,
141
                                                 MethodId ids)
142
{
143
4248210
  TNode v;
144
4248210
  TNode s;
145
2124105
  if (exp.getKind() == AND && ids == MethodId::SB_DEFAULT)
146
  {
147
    for (const Node& ec : exp)
148
    {
149
      // non-recursive, do not use nested AND
150
      if (!getSubstitutionForLit(ec, v, s, ids))
151
      {
152
        return false;
153
      }
154
      vars.push_back(v);
155
      subs.push_back(s);
156
      from.push_back(ec);
157
    }
158
    return true;
159
  }
160
2124105
  bool ret = getSubstitutionForLit(exp, v, s, ids);
161
2124105
  vars.push_back(v);
162
2124105
  subs.push_back(s);
163
2124105
  from.push_back(exp);
164
2124105
  return ret;
165
}
166
167
Node BuiltinProofRuleChecker::applySubstitution(Node n,
168
                                                Node exp,
169
                                                MethodId ids,
170
                                                MethodId ida)
171
{
172
  std::vector<Node> expv{exp};
173
  return applySubstitution(n, expv, ids, ida);
174
}
175
176
1408882
Node BuiltinProofRuleChecker::applySubstitution(Node n,
177
                                                const std::vector<Node>& exp,
178
                                                MethodId ids,
179
                                                MethodId ida)
180
{
181
2817764
  std::vector<TNode> vars;
182
2817764
  std::vector<TNode> subs;
183
2817764
  std::vector<TNode> from;
184
3253697
  for (size_t i = 0, nexp = exp.size(); i < nexp; i++)
185
  {
186
1844815
    if (exp[i].isNull())
187
    {
188
      return Node::null();
189
    }
190
1844815
    if (!getSubstitutionFor(exp[i], vars, subs, from, ids))
191
    {
192
      return Node::null();
193
    }
194
  }
195
1408882
  if (ida == MethodId::SBA_SIMUL)
196
  {
197
    // simply apply the simultaneous substitution now
198
    return n.substitute(vars.begin(), vars.end(), subs.begin(), subs.end());
199
  }
200
1408882
  else if (ida == MethodId::SBA_FIXPOINT)
201
  {
202
64960
    SubstitutionMap sm;
203
1792011
    for (size_t i = 0, nvars = vars.size(); i < nvars; i++)
204
    {
205
1759531
      sm.addSubstitution(vars[i], subs[i]);
206
    }
207
64960
    Node ns = sm.apply(n);
208
32480
    return ns;
209
  }
210
1376402
  Assert(ida == MethodId::SBA_SEQUENTIAL);
211
  // we prefer n traversals of the term to n^2/2 traversals of range terms
212
2752804
  Node ns = n;
213
1461686
  for (size_t i = 0, nvars = vars.size(); i < nvars; i++)
214
  {
215
170568
    TNode v = vars[(nvars - 1) - i];
216
170568
    TNode s = subs[(nvars - 1) - i];
217
85284
    ns = ns.substitute(v, s);
218
  }
219
1376402
  return ns;
220
}
221
222
1296511
Node BuiltinProofRuleChecker::checkInternal(PfRule id,
223
                                            const std::vector<Node>& children,
224
                                            const std::vector<Node>& args)
225
{
226
1296511
  NodeManager * nm = NodeManager::currentNM();
227
  // compute what was proven
228
1296511
  if (id == PfRule::ASSUME)
229
  {
230
    Assert(children.empty());
231
    Assert(args.size() == 1);
232
    Assert(args[0].getType().isBoolean());
233
    return args[0];
234
  }
235
1296511
  else if (id == PfRule::SCOPE)
236
  {
237
186247
    Assert(children.size() == 1);
238
186247
    if (args.empty())
239
    {
240
      // no antecedant
241
      return children[0];
242
    }
243
372494
    Node ant = nm->mkAnd(args);
244
    // if the conclusion is false, its the negated antencedant only
245
186247
    if (children[0].isConst() && !children[0].getConst<bool>())
246
    {
247
96759
      return ant.notNode();
248
    }
249
89488
    return nm->mkNode(IMPLIES, ant, children[0]);
250
  }
251
1110264
  else if (id == PfRule::SUBS)
252
  {
253
    Assert(children.size() > 0);
254
    Assert(1 <= args.size() && args.size() <= 2);
255
    MethodId ids = MethodId::SB_DEFAULT;
256
    if (args.size() == 2 && !getMethodId(args[1], ids))
257
    {
258
      return Node::null();
259
    }
260
    std::vector<Node> exp;
261
    for (size_t i = 0, nchild = children.size(); i < nchild; i++)
262
    {
263
      exp.push_back(children[i]);
264
    }
265
    Node res = applySubstitution(args[0], exp, ids);
266
    if (res.isNull())
267
    {
268
      return Node::null();
269
    }
270
    return args[0].eqNode(res);
271
  }
272
1110264
  else if (id == PfRule::REWRITE)
273
  {
274
54481
    Assert(children.empty());
275
54481
    Assert(1 <= args.size() && args.size() <= 2);
276
54481
    MethodId idr = MethodId::RW_REWRITE;
277
54481
    if (args.size() == 2 && !getMethodId(args[1], idr))
278
    {
279
      return Node::null();
280
    }
281
108962
    Node res = applyRewrite(args[0], idr);
282
54481
    if (res.isNull())
283
    {
284
      return Node::null();
285
    }
286
54481
    return args[0].eqNode(res);
287
  }
288
1055783
  else if (id == PfRule::EVALUATE)
289
  {
290
39039
    Assert(children.empty());
291
39039
    Assert(args.size() == 1);
292
78078
    Node res = applyRewrite(args[0], MethodId::RW_EVALUATE);
293
39039
    if (res.isNull())
294
    {
295
      return Node::null();
296
    }
297
39039
    return args[0].eqNode(res);
298
  }
299
1016744
  else if (id == PfRule::MACRO_SR_EQ_INTRO)
300
  {
301
29128
    Assert(1 <= args.size() && args.size() <= 4);
302
    MethodId ids, ida, idr;
303
29128
    if (!getMethodIds(args, ids, ida, idr, 1))
304
    {
305
      return Node::null();
306
    }
307
58256
    Node res = applySubstitutionRewrite(args[0], children, ids, ida, idr);
308
29128
    if (res.isNull())
309
    {
310
      return Node::null();
311
    }
312
29128
    return args[0].eqNode(res);
313
  }
314
987616
  else if (id == PfRule::MACRO_SR_PRED_INTRO)
315
  {
316
122306
    Trace("builtin-pfcheck") << "Check " << id << " " << children.size() << " "
317
61153
                             << args[0] << std::endl;
318
61153
    Assert(1 <= args.size() && args.size() <= 4);
319
    MethodId ids, ida, idr;
320
61153
    if (!getMethodIds(args, ids, ida, idr, 1))
321
    {
322
      return Node::null();
323
    }
324
122306
    Node res = applySubstitutionRewrite(args[0], children, ids, ida, idr);
325
61153
    if (res.isNull())
326
    {
327
      return Node::null();
328
    }
329
61153
    Trace("builtin-pfcheck") << "Result is " << res << std::endl;
330
122306
    Trace("builtin-pfcheck") << "Witness form is "
331
61153
                             << SkolemManager::getOriginalForm(res) << std::endl;
332
    // **** NOTE: can rewrite the witness form here. This enables certain lemmas
333
    // to be provable, e.g. (= k t) where k is a purification Skolem for t.
334
61153
    res = Rewriter::rewrite(SkolemManager::getOriginalForm(res));
335
61153
    if (!res.isConst() || !res.getConst<bool>())
336
    {
337
56
      Trace("builtin-pfcheck")
338
28
          << "Failed to rewrite to true, res=" << res << std::endl;
339
28
      return Node::null();
340
    }
341
61125
    Trace("builtin-pfcheck") << "...success" << std::endl;
342
61125
    return args[0];
343
  }
344
926463
  else if (id == PfRule::MACRO_SR_PRED_ELIM)
345
  {
346
16906
    Trace("builtin-pfcheck") << "Check " << id << " " << children.size() << " "
347
8453
                             << args.size() << std::endl;
348
8453
    Assert(children.size() >= 1);
349
8453
    Assert(args.size() <= 3);
350
16906
    std::vector<Node> exp;
351
8453
    exp.insert(exp.end(), children.begin() + 1, children.end());
352
    MethodId ids, ida, idr;
353
8453
    if (!getMethodIds(args, ids, ida, idr, 0))
354
    {
355
      return Node::null();
356
    }
357
16906
    Node res1 = applySubstitutionRewrite(children[0], exp, ids, ida, idr);
358
8453
    Trace("builtin-pfcheck") << "Returned " << res1 << std::endl;
359
8453
    return res1;
360
  }
361
918010
  else if (id == PfRule::MACRO_SR_PRED_TRANSFORM)
362
  {
363
1290202
    Trace("builtin-pfcheck") << "Check " << id << " " << children.size() << " "
364
645101
                             << args.size() << std::endl;
365
645101
    Assert(children.size() >= 1);
366
645101
    Assert(1 <= args.size() && args.size() <= 4);
367
645101
    Assert(args[0].getType().isBoolean());
368
    MethodId ids, ida, idr;
369
645101
    if (!getMethodIds(args, ids, ida, idr, 1))
370
    {
371
      return Node::null();
372
    }
373
1290202
    std::vector<Node> exp;
374
645101
    exp.insert(exp.end(), children.begin() + 1, children.end());
375
1290202
    Node res1 = applySubstitutionRewrite(children[0], exp, ids, ida, idr);
376
1290202
    Node res2 = applySubstitutionRewrite(args[0], exp, ids, ida, idr);
377
    // if not already equal, do rewriting
378
645101
    if (res1 != res2)
379
    {
380
      // can rewrite the witness forms
381
1544
      res1 = Rewriter::rewrite(SkolemManager::getOriginalForm(res1));
382
1544
      res2 = Rewriter::rewrite(SkolemManager::getOriginalForm(res2));
383
1544
      if (res1.isNull() || res1 != res2)
384
      {
385
200
        Trace("builtin-pfcheck") << "Failed to match results" << std::endl;
386
200
        Trace("builtin-pfcheck-debug") << res1 << " vs " << res2 << std::endl;
387
200
        return Node::null();
388
      }
389
    }
390
644901
    return args[0];
391
  }
392
272909
  else if (id == PfRule::REMOVE_TERM_FORMULA_AXIOM)
393
  {
394
14499
    Assert(children.empty());
395
14499
    Assert(args.size() == 1);
396
14499
    return RemoveTermFormulas::getAxiomFor(args[0]);
397
  }
398
258410
  else if (id == PfRule::PREPROCESS || id == PfRule::PREPROCESS_LEMMA
399
257365
           || id == PfRule::THEORY_PREPROCESS
400
253588
           || id == PfRule::THEORY_PREPROCESS_LEMMA
401
227127
           || id == PfRule::THEORY_EXPAND_DEF || id == PfRule::WITNESS_AXIOM
402
227067
           || id == PfRule::THEORY_LEMMA || id == PfRule::THEORY_REWRITE
403
108
           || id == PfRule::TRUST_REWRITE || id == PfRule::TRUST_SUBS
404
100
           || id == PfRule::TRUST_SUBS_MAP || id == PfRule::TRUST_SUBS_EQ
405
60
           || id == PfRule::THEORY_INFERENCE)
406
  {
407
    // "trusted" rules
408
258410
    Assert(!args.empty());
409
258410
    Assert(args[0].getType().isBoolean());
410
258410
    return args[0];
411
  }
412
413
  // no rule
414
  return Node::null();
415
}
416
417
bool BuiltinProofRuleChecker::getTheoryId(TNode n, TheoryId& tid)
418
{
419
  uint32_t i;
420
  if (!getUInt32(n, i))
421
  {
422
    return false;
423
  }
424
  tid = static_cast<TheoryId>(i);
425
  return true;
426
}
427
428
200930
Node BuiltinProofRuleChecker::mkTheoryIdNode(TheoryId tid)
429
{
430
  return NodeManager::currentNM()->mkConst(
431
200930
      Rational(static_cast<uint32_t>(tid)));
432
}
433
434
}  // namespace builtin
435
}  // namespace theory
436
29280
}  // namespace cvc5