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 |
6183619 |
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 |
6183619 |
return checkInternal(id, children, args); |
34 |
|
} |
35 |
|
|
36 |
1193376 |
bool ProofRuleChecker::getUInt32(TNode n, uint32_t& i) |
37 |
|
{ |
38 |
|
// must be a non-negative integer constant that fits an unsigned int |
39 |
3580128 |
if (n.isConst() && n.getType().isInteger() |
40 |
1193376 |
&& n.getConst<Rational>().sgn() >= 0 |
41 |
3580128 |
&& n.getConst<Rational>().getNumerator().fitsUnsignedInt()) |
42 |
|
{ |
43 |
1193376 |
i = n.getConst<Rational>().getNumerator().toUnsignedInt(); |
44 |
1193376 |
return true; |
45 |
|
} |
46 |
|
return false; |
47 |
|
} |
48 |
|
|
49 |
1690 |
bool ProofRuleChecker::getBool(TNode n, bool& b) |
50 |
|
{ |
51 |
1690 |
if (n.isConst() && n.getType().isBoolean()) |
52 |
|
{ |
53 |
1690 |
b = n.getConst<bool>(); |
54 |
1690 |
return true; |
55 |
|
} |
56 |
|
return false; |
57 |
|
} |
58 |
|
|
59 |
385122 |
bool ProofRuleChecker::getKind(TNode n, Kind& k) |
60 |
|
{ |
61 |
|
uint32_t i; |
62 |
385122 |
if (!getUInt32(n, i)) |
63 |
|
{ |
64 |
|
return false; |
65 |
|
} |
66 |
385122 |
k = static_cast<Kind>(i); |
67 |
385122 |
return true; |
68 |
|
} |
69 |
|
|
70 |
549719 |
Node ProofRuleChecker::mkKindNode(Kind k) |
71 |
|
{ |
72 |
549719 |
if (k == UNDEFINED_KIND) |
73 |
|
{ |
74 |
|
// UNDEFINED_KIND is negative, hence return null to avoid cast |
75 |
|
return Node::null(); |
76 |
|
} |
77 |
549719 |
return NodeManager::currentNM()->mkConst(Rational(static_cast<uint32_t>(k))); |
78 |
|
} |
79 |
|
|
80 |
7989 |
ProofCheckerStatistics::ProofCheckerStatistics() |
81 |
7989 |
: d_ruleChecks(smtStatisticsRegistry().registerHistogram<PfRule>( |
82 |
15978 |
"ProofCheckerStatistics::ruleChecks")), |
83 |
7989 |
d_totalRuleChecks(smtStatisticsRegistry().registerInt( |
84 |
15978 |
"ProofCheckerStatistics::totalRuleChecks")) |
85 |
|
{ |
86 |
7989 |
} |
87 |
|
|
88 |
7989 |
ProofChecker::ProofChecker(bool eagerCheck, |
89 |
|
uint32_t pclevel, |
90 |
7989 |
rewriter::RewriteDb* rdb) |
91 |
7989 |
: d_eagerCheck(eagerCheck), d_pclevel(pclevel), d_rdb(rdb) |
92 |
|
{ |
93 |
7989 |
} |
94 |
|
|
95 |
5096331 |
Node ProofChecker::check(ProofNode* pn, Node expected) |
96 |
|
{ |
97 |
5096331 |
return check(pn->getRule(), pn->getChildren(), pn->getArguments(), expected); |
98 |
|
} |
99 |
|
|
100 |
6231691 |
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 |
6231691 |
if (id == PfRule::ASSUME) |
108 |
|
{ |
109 |
648180 |
Assert(children.empty()); |
110 |
648180 |
Assert(args.size() == 1 && args[0].getType().isBoolean()); |
111 |
648180 |
Assert(expected.isNull() || expected == args[0]); |
112 |
648180 |
return expected; |
113 |
|
} |
114 |
|
// record stat |
115 |
5583511 |
d_stats.d_ruleChecks << id; |
116 |
5583511 |
++d_stats.d_totalRuleChecks; |
117 |
5583511 |
Trace("pfcheck") << "ProofChecker::check: " << id << std::endl; |
118 |
11167022 |
std::vector<Node> cchildren; |
119 |
24078999 |
for (const std::shared_ptr<ProofNode>& pc : children) |
120 |
|
{ |
121 |
18495488 |
Assert(pc != nullptr); |
122 |
36990976 |
Node cres = pc->getResult(); |
123 |
18495488 |
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 |
18495488 |
cchildren.push_back(cres); |
133 |
18495488 |
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 |
5583511 |
Trace("pfcheck") << " args: " << args << std::endl; |
142 |
5583511 |
Trace("pfcheck") << " expected: " << expected << std::endl; |
143 |
11167022 |
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 |
11167022 |
Node res = checkInternal(id, cchildren, args, expected, out, true, true); |
148 |
5583511 |
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 |
5583511 |
Trace("pfcheck") << "ProofChecker::check: success!" << std::endl; |
156 |
5583511 |
return res; |
157 |
|
} |
158 |
|
|
159 |
600108 |
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 |
1200216 |
std::stringstream out; |
166 |
600108 |
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 |
600108 |
checkInternal(id, cchildren, args, expected, out, false, traceEnabled); |
171 |
600108 |
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 |
1200216 |
return res; |
186 |
|
} |
187 |
|
|
188 |
6183619 |
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 |
6183619 |
std::map<PfRule, ProofRuleChecker*>::iterator it = d_checker.find(id); |
197 |
6183619 |
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 |
6183619 |
else if (it->second == nullptr) |
207 |
|
{ |
208 |
|
if (useTrustedChecker) |
209 |
|
{ |
210 |
|
out << "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 |
12367238 |
Node res = it->second->check(id, cchildren, args); |
225 |
6183619 |
if (!expected.isNull()) |
226 |
|
{ |
227 |
8901926 |
Node expectedw = expected; |
228 |
4450963 |
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 |
6183619 |
if (d_eagerCheck) |
251 |
|
{ |
252 |
4492 |
std::stringstream serr; |
253 |
2246 |
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 |
6183619 |
return res; |
270 |
|
} |
271 |
|
|
272 |
1168788 |
void ProofChecker::registerChecker(PfRule id, ProofRuleChecker* psc) |
273 |
|
{ |
274 |
1168788 |
std::map<PfRule, ProofRuleChecker*>::iterator it = d_checker.find(id); |
275 |
1168788 |
if (it != d_checker.end()) |
276 |
|
{ |
277 |
|
// checker is already provided |
278 |
|
Trace("pfcheck") |
279 |
|
<< "ProofChecker::registerChecker: checker already exists for " << id |
280 |
|
<< std::endl; |
281 |
|
return; |
282 |
|
} |
283 |
1168788 |
d_checker[id] = psc; |
284 |
|
} |
285 |
|
|
286 |
137186 |
void ProofChecker::registerTrustedChecker(PfRule id, |
287 |
|
ProofRuleChecker* psc, |
288 |
|
uint32_t plevel) |
289 |
|
{ |
290 |
137186 |
AlwaysAssert(plevel <= 10) << "ProofChecker::registerTrustedChecker: " |
291 |
|
"pedantic level must be 0-10, got " |
292 |
|
<< plevel << " for " << id; |
293 |
137186 |
registerChecker(id, psc); |
294 |
|
// overwrites if already there |
295 |
137186 |
if (d_plevel.find(id) != d_plevel.end()) |
296 |
|
{ |
297 |
|
Trace("proof-pedantic") |
298 |
|
<< "ProofChecker::registerTrustedRule: already provided pedantic " |
299 |
|
"level for " |
300 |
|
<< id << std::endl; |
301 |
|
} |
302 |
137186 |
d_plevel[id] = plevel; |
303 |
137186 |
} |
304 |
|
|
305 |
|
ProofRuleChecker* ProofChecker::getCheckerFor(PfRule id) |
306 |
|
{ |
307 |
|
std::map<PfRule, ProofRuleChecker*>::const_iterator it = d_checker.find(id); |
308 |
|
if (it == d_checker.end()) |
309 |
|
{ |
310 |
|
return nullptr; |
311 |
|
} |
312 |
|
return it->second; |
313 |
|
} |
314 |
|
|
315 |
|
rewriter::RewriteDb* ProofChecker::getRewriteDatabase() { return d_rdb; } |
316 |
|
|
317 |
2491412 |
uint32_t ProofChecker::getPedanticLevel(PfRule id) const |
318 |
|
{ |
319 |
2491412 |
std::map<PfRule, uint32_t>::const_iterator itp = d_plevel.find(id); |
320 |
2491412 |
if (itp != d_plevel.end()) |
321 |
|
{ |
322 |
5691 |
return itp->second; |
323 |
|
} |
324 |
2485721 |
return 0; |
325 |
|
} |
326 |
|
|
327 |
2493487 |
bool ProofChecker::isPedanticFailure(PfRule id, |
328 |
|
std::ostream& out, |
329 |
|
bool enableOutput) const |
330 |
|
{ |
331 |
2493487 |
if (d_pclevel == 0) |
332 |
|
{ |
333 |
2493487 |
return false; |
334 |
|
} |
335 |
|
std::map<PfRule, uint32_t>::const_iterator itp = d_plevel.find(id); |
336 |
|
if (itp != d_plevel.end()) |
337 |
|
{ |
338 |
|
if (itp->second <= d_pclevel) |
339 |
|
{ |
340 |
|
if (enableOutput) |
341 |
|
{ |
342 |
|
out << "pedantic level for " << id << " not met (rule level is " |
343 |
|
<< itp->second << " which is at or below the pedantic level " |
344 |
|
<< d_pclevel << ")"; |
345 |
|
bool pedanticTraceEnabled = Trace.isOn("proof-pedantic"); |
346 |
|
if (!pedanticTraceEnabled) |
347 |
|
{ |
348 |
|
out << ", use -t proof-pedantic for details"; |
349 |
|
} |
350 |
|
} |
351 |
|
return true; |
352 |
|
} |
353 |
|
} |
354 |
|
return false; |
355 |
|
} |
356 |
|
|
357 |
31137 |
} // namespace cvc5 |