GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/expr/proof_checker.cpp Lines: 89 159 56.0 %
Date: 2021-05-22 Branches: 127 620 20.5 %

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