GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/proof/proof_checker.cpp Lines: 89 164 54.3 %
Date: 2021-09-10 Branches: 123 610 20.2 %

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 proof checker.
14
 */
15
16
#include "proof/proof_checker.h"
17
18
#include "expr/skolem_manager.h"
19
#include "options/proof_options.h"
20
#include "proof/proof_node.h"
21
#include "smt/smt_statistics_registry.h"
22
#include "util/rational.h"
23
24
using namespace cvc5::kind;
25
26
namespace cvc5 {
27
28
6374877
Node ProofRuleChecker::check(PfRule id,
29
                             const std::vector<Node>& children,
30
                             const std::vector<Node>& args)
31
{
32
  // call instance-specific checkInternal method
33
6374877
  return checkInternal(id, children, args);
34
}
35
36
1241324
bool ProofRuleChecker::getUInt32(TNode n, uint32_t& i)
37
{
38
  // must be a non-negative integer constant that fits an unsigned int
39
3723972
  if (n.isConst() && n.getType().isInteger()
40
1241324
      && n.getConst<Rational>().sgn() >= 0
41
3723972
      && n.getConst<Rational>().getNumerator().fitsUnsignedInt())
42
  {
43
1241324
    i = n.getConst<Rational>().getNumerator().toUnsignedInt();
44
1241324
    return true;
45
  }
46
  return false;
47
}
48
49
1609
bool ProofRuleChecker::getBool(TNode n, bool& b)
50
{
51
1609
  if (n.isConst() && n.getType().isBoolean())
52
  {
53
1609
    b = n.getConst<bool>();
54
1609
    return true;
55
  }
56
  return false;
57
}
58
59
376450
bool ProofRuleChecker::getKind(TNode n, Kind& k)
60
{
61
  uint32_t i;
62
376450
  if (!getUInt32(n, i))
63
  {
64
    return false;
65
  }
66
376450
  k = static_cast<Kind>(i);
67
376450
  return true;
68
}
69
70
579549
Node ProofRuleChecker::mkKindNode(Kind k)
71
{
72
579549
  if (k == UNDEFINED_KIND)
73
  {
74
    // UNDEFINED_KIND is negative, hence return null to avoid cast
75
    return Node::null();
76
  }
77
579549
  return NodeManager::currentNM()->mkConst(Rational(static_cast<uint32_t>(k)));
78
}
79
80
3781
ProofCheckerStatistics::ProofCheckerStatistics()
81
3781
    : d_ruleChecks(smtStatisticsRegistry().registerHistogram<PfRule>(
82
7562
          "ProofCheckerStatistics::ruleChecks")),
83
3781
      d_totalRuleChecks(smtStatisticsRegistry().registerInt(
84
7562
          "ProofCheckerStatistics::totalRuleChecks"))
85
{
86
3781
}
87
88
3781
ProofChecker::ProofChecker(bool eagerCheck,
89
                           uint32_t pclevel,
90
3781
                           rewriter::RewriteDb* rdb)
91
3781
    : d_eagerCheck(eagerCheck), d_pclevel(pclevel), d_rdb(rdb)
92
{
93
3781
}
94
95
5315366
Node ProofChecker::check(ProofNode* pn, Node expected)
96
{
97
5315366
  return check(pn->getRule(), pn->getChildren(), pn->getArguments(), expected);
98
}
99
100
6391292
Node ProofChecker::check(
101
    PfRule id,
102
    const std::vector<std::shared_ptr<ProofNode>>& children,
103
    const std::vector<Node>& args,
104
    Node expected)
105
{
106
  // optimization: immediately return for ASSUME
107
6391292
  if (id == PfRule::ASSUME)
108
  {
109
635330
    Assert(children.empty());
110
635330
    Assert(args.size() == 1 && args[0].getType().isBoolean());
111
635330
    Assert(expected.isNull() || expected == args[0]);
112
635330
    return expected;
113
  }
114
  // record stat
115
5755962
  d_stats.d_ruleChecks << id;
116
5755962
  ++d_stats.d_totalRuleChecks;
117
5755962
  Trace("pfcheck") << "ProofChecker::check: " << id << std::endl;
118
11511924
  std::vector<Node> cchildren;
119
24731179
  for (const std::shared_ptr<ProofNode>& pc : children)
120
  {
121
18975217
    Assert(pc != nullptr);
122
37950434
    Node cres = pc->getResult();
123
18975217
    if (cres.isNull())
124
    {
125
      Trace("pfcheck") << "ProofChecker::check: failed child" << std::endl;
126
      Unreachable()
127
          << "ProofChecker::check: child proof was invalid (null conclusion)"
128
          << std::endl;
129
      // should not have been able to create such a proof node
130
      return Node::null();
131
    }
132
18975217
    cchildren.push_back(cres);
133
18975217
    if (Trace.isOn("pfcheck"))
134
    {
135
      std::stringstream ssc;
136
      pc->printDebug(ssc);
137
      Trace("pfcheck") << "     child: " << ssc.str() << " : " << cres
138
                       << std::endl;
139
    }
140
  }
141
5755962
  Trace("pfcheck") << "      args: " << args << std::endl;
142
5755962
  Trace("pfcheck") << "  expected: " << expected << std::endl;
143
11511924
  std::stringstream out;
144
  // we use trusted (null) checkers here, since we want the proof generation to
145
  // proceed without failing here. We always enable output since a failure
146
  // implies that we will exit with the error message below.
147
11511924
  Node res = checkInternal(id, cchildren, args, expected, out, true, true);
148
5755962
  if (res.isNull())
149
  {
150
    Trace("pfcheck") << "ProofChecker::check: failed" << std::endl;
151
    Unreachable() << "ProofChecker::check: failed, " << out.str() << std::endl;
152
    // it did not match the given expectation, fail
153
    return Node::null();
154
  }
155
5755962
  Trace("pfcheck") << "ProofChecker::check: success!" << std::endl;
156
5755962
  return res;
157
}
158
159
618915
Node ProofChecker::checkDebug(PfRule id,
160
                              const std::vector<Node>& cchildren,
161
                              const std::vector<Node>& args,
162
                              Node expected,
163
                              const char* traceTag)
164
{
165
1237830
  std::stringstream out;
166
618915
  bool traceEnabled = Trace.isOn(traceTag);
167
  // Since we are debugging, we want to treat trusted (null) checkers as
168
  // a failure. We only enable output if the trace is enabled for efficiency.
169
  Node res =
170
618915
      checkInternal(id, cchildren, args, expected, out, false, traceEnabled);
171
618915
  if (traceEnabled)
172
  {
173
    Trace(traceTag) << "ProofChecker::checkDebug: " << id;
174
    if (res.isNull())
175
    {
176
      Trace(traceTag) << " failed, " << out.str() << std::endl;
177
    }
178
    else
179
    {
180
      Trace(traceTag) << " success" << std::endl;
181
    }
182
    Trace(traceTag) << "cchildren: " << cchildren << std::endl;
183
    Trace(traceTag) << "     args: " << args << std::endl;
184
  }
185
1237830
  return res;
186
}
187
188
6374877
Node ProofChecker::checkInternal(PfRule id,
189
                                 const std::vector<Node>& cchildren,
190
                                 const std::vector<Node>& args,
191
                                 Node expected,
192
                                 std::stringstream& out,
193
                                 bool useTrustedChecker,
194
                                 bool enableOutput)
195
{
196
6374877
  std::map<PfRule, ProofRuleChecker*>::iterator it = d_checker.find(id);
197
6374877
  if (it == d_checker.end())
198
  {
199
    // no checker for the rule
200
    if (enableOutput)
201
    {
202
      out << "no checker for rule " << id << std::endl;
203
    }
204
    return Node::null();
205
  }
206
6374877
  else if (it->second == nullptr)
207
  {
208
    if (useTrustedChecker)
209
    {
210
      Notice() << "ProofChecker::check: trusting PfRule " << id << std::endl;
211
      // trusted checker
212
      return expected;
213
    }
214
    else
215
    {
216
      if (enableOutput)
217
      {
218
        out << "trusted checker for rule " << id << std::endl;
219
      }
220
      return Node::null();
221
    }
222
  }
223
  // check it with the corresponding checker
224
12749754
  Node res = it->second->check(id, cchildren, args);
225
6374877
  if (!expected.isNull())
226
  {
227
9360072
    Node expectedw = expected;
228
4680036
    if (res != expectedw)
229
    {
230
      if (enableOutput)
231
      {
232
        out << "result does not match expected value." << std::endl
233
            << "    PfRule: " << id << std::endl;
234
        for (const Node& c : cchildren)
235
        {
236
          out << "     child: " << c << std::endl;
237
        }
238
        for (const Node& a : args)
239
        {
240
          out << "       arg: " << a << std::endl;
241
        }
242
        out << "    result: " << res << std::endl
243
            << "  expected: " << expected << std::endl;
244
      }
245
      // it did not match the given expectation, fail
246
      return Node::null();
247
    }
248
  }
249
  // fails if pedantic level is not met
250
6374877
  if (d_eagerCheck)
251
  {
252
    std::stringstream serr;
253
    if (isPedanticFailure(id, serr, enableOutput))
254
    {
255
      if (enableOutput)
256
      {
257
        out << serr.str() << std::endl;
258
        if (Trace.isOn("proof-pedantic"))
259
        {
260
          Trace("proof-pedantic")
261
              << "Failed pedantic check for " << id << std::endl;
262
          Trace("proof-pedantic") << "Expected: " << expected << std::endl;
263
          out << "Expected: " << expected << std::endl;
264
        }
265
      }
266
      return Node::null();
267
    }
268
  }
269
6374877
  return res;
270
}
271
272
507701
void ProofChecker::registerChecker(PfRule id, ProofRuleChecker* psc)
273
{
274
507701
  std::map<PfRule, ProofRuleChecker*>::iterator it = d_checker.find(id);
275
507701
  if (it != d_checker.end())
276
  {
277
    // checker is already provided
278
    Notice() << "ProofChecker::registerChecker: checker already exists for "
279
             << id << std::endl;
280
    return;
281
  }
282
507701
  d_checker[id] = psc;
283
}
284
285
61682
void ProofChecker::registerTrustedChecker(PfRule id,
286
                                          ProofRuleChecker* psc,
287
                                          uint32_t plevel)
288
{
289
61682
  AlwaysAssert(plevel <= 10) << "ProofChecker::registerTrustedChecker: "
290
                                "pedantic level must be 0-10, got "
291
                             << plevel << " for " << id;
292
61682
  registerChecker(id, psc);
293
  // overwrites if already there
294
61682
  if (d_plevel.find(id) != d_plevel.end())
295
  {
296
    Notice() << "ProofChecker::registerTrustedRule: already provided pedantic "
297
                "level for "
298
             << id << std::endl;
299
  }
300
61682
  d_plevel[id] = plevel;
301
61682
}
302
303
ProofRuleChecker* ProofChecker::getCheckerFor(PfRule id)
304
{
305
  std::map<PfRule, ProofRuleChecker*>::const_iterator it = d_checker.find(id);
306
  if (it == d_checker.end())
307
  {
308
    return nullptr;
309
  }
310
  return it->second;
311
}
312
313
rewriter::RewriteDb* ProofChecker::getRewriteDatabase() { return d_rdb; }
314
315
2491207
uint32_t ProofChecker::getPedanticLevel(PfRule id) const
316
{
317
2491207
  std::map<PfRule, uint32_t>::const_iterator itp = d_plevel.find(id);
318
2491207
  if (itp != d_plevel.end())
319
  {
320
5633
    return itp->second;
321
  }
322
2485574
  return 0;
323
}
324
325
2491207
bool ProofChecker::isPedanticFailure(PfRule id,
326
                                     std::ostream& out,
327
                                     bool enableOutput) const
328
{
329
2491207
  if (d_pclevel == 0)
330
  {
331
2491207
    return false;
332
  }
333
  std::map<PfRule, uint32_t>::const_iterator itp = d_plevel.find(id);
334
  if (itp != d_plevel.end())
335
  {
336
    if (itp->second <= d_pclevel)
337
    {
338
      if (enableOutput)
339
      {
340
        out << "pedantic level for " << id << " not met (rule level is "
341
            << itp->second << " which is at or below the pedantic level "
342
            << d_pclevel << ")";
343
        bool pedanticTraceEnabled = Trace.isOn("proof-pedantic");
344
        if (!pedanticTraceEnabled)
345
        {
346
          out << ", use -t proof-pedantic for details";
347
        }
348
      }
349
      return true;
350
    }
351
  }
352
  return false;
353
}
354
355
29502
}  // namespace cvc5