GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/builtin/proof_checker.cpp Lines: 148 219 67.6 %
Date: 2021-09-29 Branches: 222 960 23.1 %

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
6271
BuiltinProofRuleChecker::BuiltinProofRuleChecker(Env& env) : d_env(env) {}
33
34
143
void BuiltinProofRuleChecker::registerTo(ProofChecker* pc)
35
{
36
143
  pc->registerChecker(PfRule::ASSUME, this);
37
143
  pc->registerChecker(PfRule::SCOPE, this);
38
143
  pc->registerChecker(PfRule::SUBS, this);
39
143
  pc->registerChecker(PfRule::REWRITE, this);
40
143
  pc->registerChecker(PfRule::EVALUATE, this);
41
143
  pc->registerChecker(PfRule::MACRO_SR_EQ_INTRO, this);
42
143
  pc->registerChecker(PfRule::MACRO_SR_PRED_INTRO, this);
43
143
  pc->registerChecker(PfRule::MACRO_SR_PRED_ELIM, this);
44
143
  pc->registerChecker(PfRule::MACRO_SR_PRED_TRANSFORM, this);
45
143
  pc->registerChecker(PfRule::THEORY_REWRITE, this);
46
143
  pc->registerChecker(PfRule::REMOVE_TERM_FORMULA_AXIOM, this);
47
  // trusted rules
48
143
  pc->registerTrustedChecker(PfRule::THEORY_LEMMA, this, 1);
49
143
  pc->registerTrustedChecker(PfRule::PREPROCESS, this, 3);
50
143
  pc->registerTrustedChecker(PfRule::PREPROCESS_LEMMA, this, 3);
51
143
  pc->registerTrustedChecker(PfRule::THEORY_PREPROCESS, this, 3);
52
143
  pc->registerTrustedChecker(PfRule::THEORY_PREPROCESS_LEMMA, this, 3);
53
143
  pc->registerTrustedChecker(PfRule::THEORY_EXPAND_DEF, this, 3);
54
143
  pc->registerTrustedChecker(PfRule::WITNESS_AXIOM, this, 3);
55
143
  pc->registerTrustedChecker(PfRule::TRUST_REWRITE, this, 1);
56
143
  pc->registerTrustedChecker(PfRule::TRUST_SUBS, this, 1);
57
143
  pc->registerTrustedChecker(PfRule::TRUST_SUBS_MAP, this, 1);
58
143
  pc->registerTrustedChecker(PfRule::TRUST_SUBS_EQ, this, 3);
59
143
  pc->registerTrustedChecker(PfRule::THEORY_INFERENCE, this, 3);
60
143
  pc->registerChecker(PfRule::ALETHE_RULE, this);
61
143
}
62
63
163106
Node BuiltinProofRuleChecker::applySubstitutionRewrite(
64
    Node n,
65
    const std::vector<Node>& exp,
66
    MethodId ids,
67
    MethodId ida,
68
    MethodId idr)
69
{
70
326212
  Node nks = applySubstitution(n, exp, ids, ida);
71
326212
  return d_env.rewriteViaMethod(nks, idr);
72
}
73
74
2293
bool BuiltinProofRuleChecker::getSubstitutionForLit(Node exp,
75
                                                    TNode& var,
76
                                                    TNode& subs,
77
                                                    MethodId ids)
78
{
79
2293
  if (ids == MethodId::SB_DEFAULT)
80
  {
81
2289
    if (exp.getKind() != EQUAL)
82
    {
83
      return false;
84
    }
85
2289
    var = exp[0];
86
2289
    subs = exp[1];
87
  }
88
4
  else if (ids == MethodId::SB_LITERAL)
89
  {
90
4
    bool polarity = exp.getKind() != NOT;
91
4
    var = polarity ? exp : exp[0];
92
4
    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
2293
  return true;
107
}
108
109
2293
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
4586
  TNode v;
116
4586
  TNode s;
117
2293
  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
2293
  bool ret = getSubstitutionForLit(exp, v, s, ids);
133
2293
  vars.push_back(v);
134
2293
  subs.push_back(s);
135
2293
  from.push_back(exp);
136
2293
  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
163109
Node BuiltinProofRuleChecker::applySubstitution(Node n,
149
                                                const std::vector<Node>& exp,
150
                                                MethodId ids,
151
                                                MethodId ida)
152
{
153
326218
  std::vector<TNode> vars;
154
326218
  std::vector<TNode> subs;
155
326218
  std::vector<TNode> from;
156
165401
  for (size_t i = 0, nexp = exp.size(); i < nexp; i++)
157
  {
158
2292
    if (exp[i].isNull())
159
    {
160
      return Node::null();
161
    }
162
2292
    if (!getSubstitutionFor(exp[i], vars, subs, from, ids))
163
    {
164
      return Node::null();
165
    }
166
  }
167
163109
  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
163109
  else if (ida == MethodId::SBA_FIXPOINT)
173
  {
174
592
    SubstitutionMap sm;
175
1397
    for (size_t i = 0, nvars = vars.size(); i < nvars; i++)
176
    {
177
1101
      sm.addSubstitution(vars[i], subs[i]);
178
    }
179
592
    Node ns = sm.apply(n);
180
296
    return ns;
181
  }
182
162813
  Assert(ida == MethodId::SBA_SEQUENTIAL);
183
  // we prefer n traversals of the term to n^2/2 traversals of range terms
184
325626
  Node ns = n;
185
164004
  for (size_t i = 0, nvars = vars.size(); i < nvars; i++)
186
  {
187
2382
    TNode v = vars[(nvars - 1) - i];
188
2382
    TNode s = subs[(nvars - 1) - i];
189
1191
    ns = ns.substitute(v, s);
190
  }
191
162813
  return ns;
192
}
193
194
86431
Node BuiltinProofRuleChecker::checkInternal(PfRule id,
195
                                            const std::vector<Node>& children,
196
                                            const std::vector<Node>& args)
197
{
198
86431
  NodeManager * nm = NodeManager::currentNM();
199
  // compute what was proven
200
86431
  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
86431
  else if (id == PfRule::SCOPE)
208
  {
209
4386
    Assert(children.size() == 1);
210
4386
    if (args.empty())
211
    {
212
      // no antecedant
213
      return children[0];
214
    }
215
8772
    Node ant = nm->mkAnd(args);
216
    // if the conclusion is false, its the negated antencedant only
217
4386
    if (children[0].isConst() && !children[0].getConst<bool>())
218
    {
219
4353
      return ant.notNode();
220
    }
221
33
    return nm->mkNode(IMPLIES, ant, children[0]);
222
  }
223
82045
  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
82045
  else if (id == PfRule::REWRITE)
245
  {
246
50
    Assert(children.empty());
247
50
    Assert(1 <= args.size() && args.size() <= 2);
248
50
    MethodId idr = MethodId::RW_REWRITE;
249
50
    if (args.size() == 2 && !getMethodId(args[1], idr))
250
    {
251
      return Node::null();
252
    }
253
100
    Node res = d_env.rewriteViaMethod(args[0], idr);
254
50
    if (res.isNull())
255
    {
256
      return Node::null();
257
    }
258
50
    return args[0].eqNode(res);
259
  }
260
81995
  else if (id == PfRule::EVALUATE)
261
  {
262
20
    Assert(children.empty());
263
20
    Assert(args.size() == 1);
264
40
    Node res = d_env.rewriteViaMethod(args[0], MethodId::RW_EVALUATE);
265
20
    if (res.isNull())
266
    {
267
      return Node::null();
268
    }
269
20
    return args[0].eqNode(res);
270
  }
271
81975
  else if (id == PfRule::MACRO_SR_EQ_INTRO)
272
  {
273
296
    Assert(1 <= args.size() && args.size() <= 4);
274
    MethodId ids, ida, idr;
275
296
    if (!getMethodIds(args, ids, ida, idr, 1))
276
    {
277
      return Node::null();
278
    }
279
592
    Node res = applySubstitutionRewrite(args[0], children, ids, ida, idr);
280
296
    if (res.isNull())
281
    {
282
      return Node::null();
283
    }
284
296
    return args[0].eqNode(res);
285
  }
286
81679
  else if (id == PfRule::MACRO_SR_PRED_INTRO)
287
  {
288
432
    Trace("builtin-pfcheck") << "Check " << id << " " << children.size() << " "
289
216
                             << args[0] << std::endl;
290
216
    Assert(1 <= args.size() && args.size() <= 4);
291
    MethodId ids, ida, idr;
292
216
    if (!getMethodIds(args, ids, ida, idr, 1))
293
    {
294
      return Node::null();
295
    }
296
432
    Node res = applySubstitutionRewrite(args[0], children, ids, ida, idr);
297
216
    if (res.isNull())
298
    {
299
      return Node::null();
300
    }
301
216
    Trace("builtin-pfcheck") << "Result is " << res << std::endl;
302
432
    Trace("builtin-pfcheck") << "Witness form is "
303
216
                             << 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
216
    res = d_env.getRewriter()->rewrite(SkolemManager::getOriginalForm(res));
307
216
    if (!res.isConst() || !res.getConst<bool>())
308
    {
309
      Trace("builtin-pfcheck")
310
          << "Failed to rewrite to true, res=" << res << std::endl;
311
      return Node::null();
312
    }
313
216
    Trace("builtin-pfcheck") << "...success" << std::endl;
314
216
    return args[0];
315
  }
316
81463
  else if (id == PfRule::MACRO_SR_PRED_ELIM)
317
  {
318
    Trace("builtin-pfcheck") << "Check " << id << " " << children.size() << " "
319
                             << args.size() << std::endl;
320
    Assert(children.size() >= 1);
321
    Assert(args.size() <= 3);
322
    std::vector<Node> exp;
323
    exp.insert(exp.end(), children.begin() + 1, children.end());
324
    MethodId ids, ida, idr;
325
    if (!getMethodIds(args, ids, ida, idr, 0))
326
    {
327
      return Node::null();
328
    }
329
    Node res1 = applySubstitutionRewrite(children[0], exp, ids, ida, idr);
330
    Trace("builtin-pfcheck") << "Returned " << res1 << std::endl;
331
    return res1;
332
  }
333
81463
  else if (id == PfRule::MACRO_SR_PRED_TRANSFORM)
334
  {
335
162594
    Trace("builtin-pfcheck") << "Check " << id << " " << children.size() << " "
336
81297
                             << args.size() << std::endl;
337
81297
    Assert(children.size() >= 1);
338
81297
    Assert(1 <= args.size() && args.size() <= 4);
339
81297
    Assert(args[0].getType().isBoolean());
340
    MethodId ids, ida, idr;
341
81297
    if (!getMethodIds(args, ids, ida, idr, 1))
342
    {
343
      return Node::null();
344
    }
345
162594
    std::vector<Node> exp;
346
81297
    exp.insert(exp.end(), children.begin() + 1, children.end());
347
162594
    Node res1 = applySubstitutionRewrite(children[0], exp, ids, ida, idr);
348
162594
    Node res2 = applySubstitutionRewrite(args[0], exp, ids, ida, idr);
349
    // if not already equal, do rewriting
350
81297
    if (res1 != res2)
351
    {
352
      // can rewrite the witness forms
353
      res1 = d_env.getRewriter()->rewrite(SkolemManager::getOriginalForm(res1));
354
      res2 = d_env.getRewriter()->rewrite(SkolemManager::getOriginalForm(res2));
355
      if (res1.isNull() || res1 != res2)
356
      {
357
        Trace("builtin-pfcheck") << "Failed to match results" << std::endl;
358
        Trace("builtin-pfcheck-debug") << res1 << " vs " << res2 << std::endl;
359
        return Node::null();
360
      }
361
    }
362
81297
    return args[0];
363
  }
364
166
  else if (id == PfRule::REMOVE_TERM_FORMULA_AXIOM)
365
  {
366
78
    Assert(children.empty());
367
78
    Assert(args.size() == 1);
368
78
    return RemoveTermFormulas::getAxiomFor(args[0]);
369
  }
370
88
  else if (id == PfRule::PREPROCESS || id == PfRule::PREPROCESS_LEMMA
371
85
           || id == PfRule::THEORY_PREPROCESS
372
85
           || id == PfRule::THEORY_PREPROCESS_LEMMA
373
85
           || id == PfRule::THEORY_EXPAND_DEF || id == PfRule::WITNESS_AXIOM
374
85
           || id == PfRule::THEORY_LEMMA || id == PfRule::THEORY_REWRITE
375
           || id == PfRule::TRUST_REWRITE || id == PfRule::TRUST_SUBS
376
           || id == PfRule::TRUST_SUBS_MAP || id == PfRule::TRUST_SUBS_EQ
377
           || id == PfRule::THEORY_INFERENCE)
378
  {
379
    // "trusted" rules
380
88
    Assert(!args.empty());
381
88
    Assert(args[0].getType().isBoolean());
382
88
    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
93
Node BuiltinProofRuleChecker::mkTheoryIdNode(TheoryId tid)
401
{
402
  return NodeManager::currentNM()->mkConst(
403
93
      Rational(static_cast<uint32_t>(tid)));
404
}
405
406
}  // namespace builtin
407
}  // namespace theory
408
22746
}  // namespace cvc5