GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/builtin/proof_checker.cpp Lines: 195 281 69.4 %
Date: 2021-05-21 Branches: 337 1086 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
30
const char* toString(MethodId id)
31
{
32
  switch (id)
33
  {
34
    case MethodId::RW_REWRITE: return "RW_REWRITE";
35
    case MethodId::RW_EXT_REWRITE: return "RW_EXT_REWRITE";
36
    case MethodId::RW_REWRITE_EQ_EXT: return "RW_REWRITE_EQ_EXT";
37
    case MethodId::RW_EVALUATE: return "RW_EVALUATE";
38
    case MethodId::RW_IDENTITY: return "RW_IDENTITY";
39
    case MethodId::RW_REWRITE_THEORY_PRE: return "RW_REWRITE_THEORY_PRE";
40
    case MethodId::RW_REWRITE_THEORY_POST: return "RW_REWRITE_THEORY_POST";
41
    case MethodId::SB_DEFAULT: return "SB_DEFAULT";
42
    case MethodId::SB_LITERAL: return "SB_LITERAL";
43
    case MethodId::SB_FORMULA: return "SB_FORMULA";
44
    case MethodId::SBA_SEQUENTIAL: return "SBA_SEQUENTIAL";
45
    case MethodId::SBA_SIMUL: return "SBA_SIMUL";
46
    case MethodId::SBA_FIXPOINT: return "SBA_FIXPOINT";
47
    default: return "MethodId::Unknown";
48
  };
49
}
50
51
std::ostream& operator<<(std::ostream& out, MethodId id)
52
{
53
  out << toString(id);
54
  return out;
55
}
56
57
216846
Node mkMethodId(MethodId id)
58
{
59
216846
  return NodeManager::currentNM()->mkConst(Rational(static_cast<uint32_t>(id)));
60
}
61
62
namespace builtin {
63
64
3117
void BuiltinProofRuleChecker::registerTo(ProofChecker* pc)
65
{
66
3117
  pc->registerChecker(PfRule::ASSUME, this);
67
3117
  pc->registerChecker(PfRule::SCOPE, this);
68
3117
  pc->registerChecker(PfRule::SUBS, this);
69
3117
  pc->registerChecker(PfRule::REWRITE, this);
70
3117
  pc->registerChecker(PfRule::EVALUATE, this);
71
3117
  pc->registerChecker(PfRule::MACRO_SR_EQ_INTRO, this);
72
3117
  pc->registerChecker(PfRule::MACRO_SR_PRED_INTRO, this);
73
3117
  pc->registerChecker(PfRule::MACRO_SR_PRED_ELIM, this);
74
3117
  pc->registerChecker(PfRule::MACRO_SR_PRED_TRANSFORM, this);
75
3117
  pc->registerChecker(PfRule::THEORY_REWRITE, this);
76
3117
  pc->registerChecker(PfRule::REMOVE_TERM_FORMULA_AXIOM, this);
77
  // trusted rules
78
3117
  pc->registerTrustedChecker(PfRule::THEORY_LEMMA, this, 1);
79
3117
  pc->registerTrustedChecker(PfRule::PREPROCESS, this, 3);
80
3117
  pc->registerTrustedChecker(PfRule::PREPROCESS_LEMMA, this, 3);
81
3117
  pc->registerTrustedChecker(PfRule::THEORY_PREPROCESS, this, 3);
82
3117
  pc->registerTrustedChecker(PfRule::THEORY_PREPROCESS_LEMMA, this, 3);
83
3117
  pc->registerTrustedChecker(PfRule::THEORY_EXPAND_DEF, this, 3);
84
3117
  pc->registerTrustedChecker(PfRule::WITNESS_AXIOM, this, 3);
85
3117
  pc->registerTrustedChecker(PfRule::TRUST_REWRITE, this, 1);
86
3117
  pc->registerTrustedChecker(PfRule::TRUST_SUBS, this, 1);
87
3117
  pc->registerTrustedChecker(PfRule::TRUST_SUBS_MAP, this, 1);
88
3117
}
89
90
1204039
Node BuiltinProofRuleChecker::applySubstitutionRewrite(
91
    Node n,
92
    const std::vector<Node>& exp,
93
    MethodId ids,
94
    MethodId ida,
95
    MethodId idr)
96
{
97
2408078
  Node nks = applySubstitution(n, exp, ids, ida);
98
2408078
  return applyRewrite(nks, idr);
99
}
100
101
1392235
Node BuiltinProofRuleChecker::applyRewrite(Node n, MethodId idr)
102
{
103
2784470
  Trace("builtin-pfcheck-debug")
104
1392235
      << "applyRewrite (" << idr << "): " << n << std::endl;
105
1392235
  if (idr == MethodId::RW_REWRITE)
106
  {
107
1392125
    return Rewriter::rewrite(n);
108
  }
109
110
  if (idr == MethodId::RW_EXT_REWRITE)
110
  {
111
    return d_ext_rewriter.extendedRewrite(n);
112
  }
113
110
  if (idr == MethodId::RW_REWRITE_EQ_EXT)
114
  {
115
110
    return Rewriter::rewriteEqualityExt(n);
116
  }
117
  if (idr == MethodId::RW_EVALUATE)
118
  {
119
    Evaluator eval;
120
    return eval.eval(n, {}, {}, false);
121
  }
122
  if (idr == MethodId::RW_IDENTITY)
123
  {
124
    // does nothing
125
    return n;
126
  }
127
  // unknown rewriter
128
  Assert(false) << "BuiltinProofRuleChecker::applyRewrite: no rewriter for "
129
                << idr << std::endl;
130
  return n;
131
}
132
133
2122214
bool BuiltinProofRuleChecker::getSubstitutionForLit(Node exp,
134
                                                    TNode& var,
135
                                                    TNode& subs,
136
                                                    MethodId ids)
137
{
138
2122214
  if (ids == MethodId::SB_DEFAULT)
139
  {
140
2082394
    if (exp.getKind() != EQUAL)
141
    {
142
4
      return false;
143
    }
144
2082390
    var = exp[0];
145
2082390
    subs = exp[1];
146
  }
147
39820
  else if (ids == MethodId::SB_LITERAL)
148
  {
149
39820
    bool polarity = exp.getKind() != NOT;
150
39820
    var = polarity ? exp : exp[0];
151
39820
    subs = NodeManager::currentNM()->mkConst(polarity);
152
  }
153
  else if (ids == MethodId::SB_FORMULA)
154
  {
155
    var = exp;
156
    subs = NodeManager::currentNM()->mkConst(true);
157
  }
158
  else
159
  {
160
    Assert(false) << "BuiltinProofRuleChecker::applySubstitution: no "
161
                     "substitution for "
162
                  << ids << std::endl;
163
    return false;
164
  }
165
2122210
  return true;
166
}
167
168
2122214
bool BuiltinProofRuleChecker::getSubstitutionFor(Node exp,
169
                                                 std::vector<TNode>& vars,
170
                                                 std::vector<TNode>& subs,
171
                                                 std::vector<TNode>& from,
172
                                                 MethodId ids)
173
{
174
4244428
  TNode v;
175
4244428
  TNode s;
176
2122214
  if (exp.getKind() == AND && ids == MethodId::SB_DEFAULT)
177
  {
178
    for (const Node& ec : exp)
179
    {
180
      // non-recursive, do not use nested AND
181
      if (!getSubstitutionForLit(ec, v, s, ids))
182
      {
183
        return false;
184
      }
185
      vars.push_back(v);
186
      subs.push_back(s);
187
      from.push_back(ec);
188
    }
189
    return true;
190
  }
191
2122214
  bool ret = getSubstitutionForLit(exp, v, s, ids);
192
2122214
  vars.push_back(v);
193
2122214
  subs.push_back(s);
194
2122214
  from.push_back(exp);
195
2122214
  return ret;
196
}
197
198
Node BuiltinProofRuleChecker::applySubstitution(Node n,
199
                                                Node exp,
200
                                                MethodId ids,
201
                                                MethodId ida)
202
{
203
  std::vector<Node> expv{exp};
204
  return applySubstitution(n, expv, ids, ida);
205
}
206
207
1225650
Node BuiltinProofRuleChecker::applySubstitution(Node n,
208
                                                const std::vector<Node>& exp,
209
                                                MethodId ids,
210
                                                MethodId ida)
211
{
212
2451300
  std::vector<TNode> vars;
213
2451300
  std::vector<TNode> subs;
214
2451300
  std::vector<TNode> from;
215
3067782
  for (size_t i = 0, nexp = exp.size(); i < nexp; i++)
216
  {
217
1842136
    if (exp[i].isNull())
218
    {
219
      return Node::null();
220
    }
221
1842136
    if (!getSubstitutionFor(exp[i], vars, subs, from, ids))
222
    {
223
4
      return Node::null();
224
    }
225
  }
226
1225646
  if (ida == MethodId::SBA_SIMUL)
227
  {
228
    // simply apply the simultaneous substitution now
229
    return n.substitute(vars.begin(), vars.end(), subs.begin(), subs.end());
230
  }
231
1225646
  else if (ida == MethodId::SBA_FIXPOINT)
232
  {
233
59134
    SubstitutionMap sm;
234
1793942
    for (size_t i = 0, nvars = vars.size(); i < nvars; i++)
235
    {
236
1764375
      sm.addSubstitution(vars[i], subs[i]);
237
    }
238
59134
    Node ns = sm.apply(n);
239
29567
    return ns;
240
  }
241
1196079
  Assert(ida == MethodId::SBA_SEQUENTIAL);
242
  // we prefer n traversals of the term to n^2/2 traversals of range terms
243
2392158
  Node ns = n;
244
1273832
  for (size_t i = 0, nvars = vars.size(); i < nvars; i++)
245
  {
246
155506
    TNode v = vars[(nvars - 1) - i];
247
155506
    TNode s = subs[(nvars - 1) - i];
248
77753
    ns = ns.substitute(v, s);
249
  }
250
1196079
  return ns;
251
}
252
253
83583
bool BuiltinProofRuleChecker::getMethodId(TNode n, MethodId& i)
254
{
255
  uint32_t index;
256
83583
  if (!getUInt32(n, index))
257
  {
258
    return false;
259
  }
260
83583
  i = static_cast<MethodId>(index);
261
83583
  return true;
262
}
263
264
1155533
Node BuiltinProofRuleChecker::checkInternal(PfRule id,
265
                                            const std::vector<Node>& children,
266
                                            const std::vector<Node>& args)
267
{
268
1155533
  NodeManager * nm = NodeManager::currentNM();
269
  // compute what was proven
270
1155533
  if (id == PfRule::ASSUME)
271
  {
272
    Assert(children.empty());
273
    Assert(args.size() == 1);
274
    Assert(args[0].getType().isBoolean());
275
    return args[0];
276
  }
277
1155533
  else if (id == PfRule::SCOPE)
278
  {
279
200820
    Assert(children.size() == 1);
280
200820
    if (args.empty())
281
    {
282
      // no antecedant
283
      return children[0];
284
    }
285
401640
    Node ant = nm->mkAnd(args);
286
    // if the conclusion is false, its the negated antencedant only
287
200820
    if (children[0].isConst() && !children[0].getConst<bool>())
288
    {
289
90839
      return ant.notNode();
290
    }
291
109981
    return nm->mkNode(IMPLIES, ant, children[0]);
292
  }
293
954713
  else if (id == PfRule::SUBS)
294
  {
295
    Assert(children.size() > 0);
296
    Assert(1 <= args.size() && args.size() <= 2);
297
    MethodId ids = MethodId::SB_DEFAULT;
298
    if (args.size() == 2 && !getMethodId(args[1], ids))
299
    {
300
      return Node::null();
301
    }
302
    std::vector<Node> exp;
303
    for (size_t i = 0, nchild = children.size(); i < nchild; i++)
304
    {
305
      exp.push_back(children[i]);
306
    }
307
    Node res = applySubstitution(args[0], exp, ids);
308
    if (res.isNull())
309
    {
310
      return Node::null();
311
    }
312
    return args[0].eqNode(res);
313
  }
314
954713
  else if (id == PfRule::REWRITE)
315
  {
316
44976
    Assert(children.empty());
317
44976
    Assert(1 <= args.size() && args.size() <= 2);
318
44976
    MethodId idr = MethodId::RW_REWRITE;
319
44976
    if (args.size() == 2 && !getMethodId(args[1], idr))
320
    {
321
      return Node::null();
322
    }
323
89952
    Node res = applyRewrite(args[0], idr);
324
44976
    if (res.isNull())
325
    {
326
      return Node::null();
327
    }
328
44976
    return args[0].eqNode(res);
329
  }
330
909737
  else if (id == PfRule::EVALUATE)
331
  {
332
    Assert(children.empty());
333
    Assert(args.size() == 1);
334
    Node res = applyRewrite(args[0], MethodId::RW_EVALUATE);
335
    if (res.isNull())
336
    {
337
      return Node::null();
338
    }
339
    return args[0].eqNode(res);
340
  }
341
909737
  else if (id == PfRule::MACRO_SR_EQ_INTRO)
342
  {
343
26214
    Assert(1 <= args.size() && args.size() <= 4);
344
    MethodId ids, ida, idr;
345
26214
    if (!getMethodIds(args, ids, ida, idr, 1))
346
    {
347
      return Node::null();
348
    }
349
52428
    Node res = applySubstitutionRewrite(args[0], children, ids, ida, idr);
350
26214
    if (res.isNull())
351
    {
352
      return Node::null();
353
    }
354
26214
    return args[0].eqNode(res);
355
  }
356
883523
  else if (id == PfRule::MACRO_SR_PRED_INTRO)
357
  {
358
99708
    Trace("builtin-pfcheck") << "Check " << id << " " << children.size() << " "
359
49854
                             << args[0] << std::endl;
360
49854
    Assert(1 <= args.size() && args.size() <= 4);
361
    MethodId ids, ida, idr;
362
49854
    if (!getMethodIds(args, ids, ida, idr, 1))
363
    {
364
      return Node::null();
365
    }
366
99708
    Node res = applySubstitutionRewrite(args[0], children, ids, ida, idr);
367
49854
    if (res.isNull())
368
    {
369
4
      return Node::null();
370
    }
371
49850
    Trace("builtin-pfcheck") << "Result is " << res << std::endl;
372
99700
    Trace("builtin-pfcheck") << "Witness form is "
373
49850
                             << SkolemManager::getOriginalForm(res) << std::endl;
374
    // **** NOTE: can rewrite the witness form here. This enables certain lemmas
375
    // to be provable, e.g. (= k t) where k is a purification Skolem for t.
376
49850
    res = Rewriter::rewrite(SkolemManager::getOriginalForm(res));
377
49850
    if (!res.isConst() || !res.getConst<bool>())
378
    {
379
24
      Trace("builtin-pfcheck")
380
12
          << "Failed to rewrite to true, res=" << res << std::endl;
381
12
      return Node::null();
382
    }
383
49838
    Trace("builtin-pfcheck") << "...success" << std::endl;
384
49838
    return args[0];
385
  }
386
833669
  else if (id == PfRule::MACRO_SR_PRED_ELIM)
387
  {
388
16818
    Trace("builtin-pfcheck") << "Check " << id << " " << children.size() << " "
389
8409
                             << args.size() << std::endl;
390
8409
    Assert(children.size() >= 1);
391
8409
    Assert(args.size() <= 3);
392
16818
    std::vector<Node> exp;
393
8409
    exp.insert(exp.end(), children.begin() + 1, children.end());
394
    MethodId ids, ida, idr;
395
8409
    if (!getMethodIds(args, ids, ida, idr, 0))
396
    {
397
      return Node::null();
398
    }
399
16818
    Node res1 = applySubstitutionRewrite(children[0], exp, ids, ida, idr);
400
8409
    Trace("builtin-pfcheck") << "Returned " << res1 << std::endl;
401
8409
    return res1;
402
  }
403
825260
  else if (id == PfRule::MACRO_SR_PRED_TRANSFORM)
404
  {
405
1119562
    Trace("builtin-pfcheck") << "Check " << id << " " << children.size() << " "
406
559781
                             << args.size() << std::endl;
407
559781
    Assert(children.size() >= 1);
408
559781
    Assert(1 <= args.size() && args.size() <= 4);
409
559781
    Assert(args[0].getType().isBoolean());
410
    MethodId ids, ida, idr;
411
559781
    if (!getMethodIds(args, ids, ida, idr, 1))
412
    {
413
      return Node::null();
414
    }
415
1119562
    std::vector<Node> exp;
416
559781
    exp.insert(exp.end(), children.begin() + 1, children.end());
417
1119562
    Node res1 = applySubstitutionRewrite(children[0], exp, ids, ida, idr);
418
1119562
    Node res2 = applySubstitutionRewrite(args[0], exp, ids, ida, idr);
419
    // if not already equal, do rewriting
420
559781
    if (res1 != res2)
421
    {
422
      // can rewrite the witness forms
423
668
      res1 = Rewriter::rewrite(SkolemManager::getOriginalForm(res1));
424
668
      res2 = Rewriter::rewrite(SkolemManager::getOriginalForm(res2));
425
668
      if (res1.isNull() || res1 != res2)
426
      {
427
118
        Trace("builtin-pfcheck") << "Failed to match results" << std::endl;
428
118
        Trace("builtin-pfcheck-debug") << res1 << " vs " << res2 << std::endl;
429
118
        return Node::null();
430
      }
431
    }
432
559663
    return args[0];
433
  }
434
265479
  else if (id == PfRule::REMOVE_TERM_FORMULA_AXIOM)
435
  {
436
13056
    Assert(children.empty());
437
13056
    Assert(args.size() == 1);
438
13056
    return RemoveTermFormulas::getAxiomFor(args[0]);
439
  }
440
252423
  else if (id == PfRule::PREPROCESS || id == PfRule::PREPROCESS_LEMMA
441
251522
           || id == PfRule::THEORY_PREPROCESS
442
249179
           || id == PfRule::THEORY_PREPROCESS_LEMMA
443
228304
           || id == PfRule::THEORY_EXPAND_DEF || id == PfRule::WITNESS_AXIOM
444
228229
           || id == PfRule::THEORY_LEMMA || id == PfRule::THEORY_REWRITE
445
76
           || id == PfRule::TRUST_REWRITE || id == PfRule::TRUST_SUBS
446
37
           || id == PfRule::TRUST_SUBS_MAP)
447
  {
448
    // "trusted" rules
449
252423
    Assert(!args.empty());
450
252423
    Assert(args[0].getType().isBoolean());
451
252423
    return args[0];
452
  }
453
454
  // no rule
455
  return Node::null();
456
}
457
458
644258
bool BuiltinProofRuleChecker::getMethodIds(const std::vector<Node>& args,
459
                                           MethodId& ids,
460
                                           MethodId& ida,
461
                                           MethodId& idr,
462
                                           size_t index)
463
{
464
644258
  ids = MethodId::SB_DEFAULT;
465
644258
  ida = MethodId::SBA_SEQUENTIAL;
466
644258
  idr = MethodId::RW_REWRITE;
467
708161
  for (size_t offset = 0; offset <= 2; offset++)
468
  {
469
708094
    if (args.size() > index + offset)
470
    {
471
63903
      MethodId& id = offset == 0 ? ids : (offset == 1 ? ida : idr);
472
63903
      if (!getMethodId(args[index + offset], id))
473
      {
474
        Trace("builtin-pfcheck")
475
            << "Failed to get id from " << args[index + offset] << std::endl;
476
        return false;
477
      }
478
    }
479
    else
480
    {
481
644191
      break;
482
    }
483
  }
484
1288516
  Trace("builtin-pfcheck") << "Got MethodIds ids/ida/idr: " << ids << " / "
485
644258
                           << ida << " / " << idr << "\n";
486
644258
  return true;
487
}
488
489
11391
void BuiltinProofRuleChecker::addMethodIds(std::vector<Node>& args,
490
                                           MethodId ids,
491
                                           MethodId ida,
492
                                           MethodId idr)
493
{
494
11391
  bool ndefRewriter = (idr != MethodId::RW_REWRITE);
495
11391
  bool ndefApply = (ida != MethodId::SBA_SEQUENTIAL);
496
11391
  if (ids != MethodId::SB_DEFAULT || ndefRewriter || ndefApply)
497
  {
498
9856
    args.push_back(mkMethodId(ids));
499
  }
500
11391
  if (ndefApply || ndefRewriter)
501
  {
502
9856
    args.push_back(mkMethodId(ida));
503
  }
504
11391
  if (ndefRewriter)
505
  {
506
41
    args.push_back(mkMethodId(idr));
507
  }
508
11391
}
509
510
bool BuiltinProofRuleChecker::getTheoryId(TNode n, TheoryId& tid)
511
{
512
  uint32_t i;
513
  if (!getUInt32(n, i))
514
  {
515
    return false;
516
  }
517
  tid = static_cast<TheoryId>(i);
518
  return true;
519
}
520
521
197918
Node BuiltinProofRuleChecker::mkTheoryIdNode(TheoryId tid)
522
{
523
  return NodeManager::currentNM()->mkConst(
524
197918
      Rational(static_cast<uint32_t>(tid)));
525
}
526
527
}  // namespace builtin
528
}  // namespace theory
529
27735
}  // namespace cvc5