1 /********************* */
2 /*! \file proof_checker.cpp
4 ** Top contributors (to current version):
6 ** This file is part of the CVC4 project.
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.\endverbatim
12 ** \brief Implementation of proof checker
15 #include "expr/proof_checker.h"
17 #include "expr/proof_node.h"
18 #include "expr/skolem_manager.h"
19 #include "options/proof_options.h"
20 #include "smt/smt_statistics_registry.h"
22 using namespace CVC4::kind
;
26 Node
ProofRuleChecker::check(PfRule id
,
27 const std::vector
<Node
>& children
,
28 const std::vector
<Node
>& args
)
30 // call instance-specific checkInternal method
31 return checkInternal(id
, children
, args
);
34 bool ProofRuleChecker::getUInt32(TNode n
, uint32_t& i
)
36 // must be a non-negative integer constant that fits an unsigned int
37 if (n
.isConst() && n
.getType().isInteger()
38 && n
.getConst
<Rational
>().sgn() >= 0
39 && n
.getConst
<Rational
>().getNumerator().fitsUnsignedInt())
41 i
= n
.getConst
<Rational
>().getNumerator().toUnsignedInt();
47 bool ProofRuleChecker::getBool(TNode n
, bool& b
)
49 if (n
.isConst() && n
.getType().isBoolean())
51 b
= n
.getConst
<bool>();
57 bool ProofRuleChecker::getKind(TNode n
, Kind
& k
)
64 k
= static_cast<Kind
>(i
);
68 Node
ProofRuleChecker::mkKindNode(Kind k
)
70 if (k
== UNDEFINED_KIND
)
72 // UNDEFINED_KIND is negative, hence return null to avoid cast
75 return NodeManager::currentNM()->mkConst(Rational(static_cast<uint32_t>(k
)));
78 ProofCheckerStatistics::ProofCheckerStatistics()
79 : d_ruleChecks("ProofCheckerStatistics::ruleChecks"),
80 d_totalRuleChecks("ProofCheckerStatistics::totalRuleChecks", 0)
82 smtStatisticsRegistry()->registerStat(&d_ruleChecks
);
83 smtStatisticsRegistry()->registerStat(&d_totalRuleChecks
);
86 ProofCheckerStatistics::~ProofCheckerStatistics()
88 smtStatisticsRegistry()->unregisterStat(&d_ruleChecks
);
89 smtStatisticsRegistry()->unregisterStat(&d_totalRuleChecks
);
92 Node
ProofChecker::check(ProofNode
* pn
, Node expected
)
94 return check(pn
->getRule(), pn
->getChildren(), pn
->getArguments(), expected
);
97 Node
ProofChecker::check(
99 const std::vector
<std::shared_ptr
<ProofNode
>>& children
,
100 const std::vector
<Node
>& args
,
103 // optimization: immediately return for ASSUME
104 if (id
== PfRule::ASSUME
)
106 Assert(children
.empty());
107 Assert(args
.size() == 1 && args
[0].getType().isBoolean());
108 Assert(expected
.isNull() || expected
== args
[0]);
112 d_stats
.d_ruleChecks
<< id
;
113 ++d_stats
.d_totalRuleChecks
;
114 Trace("pfcheck") << "ProofChecker::check: " << id
<< std::endl
;
115 std::vector
<Node
> cchildren
;
116 for (const std::shared_ptr
<ProofNode
>& pc
: children
)
118 Assert(pc
!= nullptr);
119 Node cres
= pc
->getResult();
122 Trace("pfcheck") << "ProofChecker::check: failed child" << std::endl
;
124 << "ProofChecker::check: child proof was invalid (null conclusion)"
126 // should not have been able to create such a proof node
129 cchildren
.push_back(cres
);
130 if (Trace
.isOn("pfcheck"))
132 std::stringstream ssc
;
134 Trace("pfcheck") << " child: " << ssc
.str() << " : " << cres
138 Trace("pfcheck") << " args: " << args
<< std::endl
;
139 Trace("pfcheck") << " expected: " << expected
<< std::endl
;
140 std::stringstream out
;
141 // we use trusted (null) checkers here, since we want the proof generation to
142 // proceed without failing here. We always enable output since a failure
143 // implies that we will exit with the error message below.
144 Node res
= checkInternal(id
, cchildren
, args
, expected
, out
, true, true);
147 Trace("pfcheck") << "ProofChecker::check: failed" << std::endl
;
148 Unreachable() << "ProofChecker::check: failed, " << out
.str() << std::endl
;
149 // it did not match the given expectation, fail
152 Trace("pfcheck") << "ProofChecker::check: success!" << std::endl
;
156 Node
ProofChecker::checkDebug(PfRule id
,
157 const std::vector
<Node
>& cchildren
,
158 const std::vector
<Node
>& args
,
160 const char* traceTag
)
162 std::stringstream out
;
163 bool traceEnabled
= Trace
.isOn(traceTag
);
164 // Since we are debugging, we want to treat trusted (null) checkers as
165 // a failure. We only enable output if the trace is enabled for efficiency.
167 checkInternal(id
, cchildren
, args
, expected
, out
, false, traceEnabled
);
170 Trace(traceTag
) << "ProofChecker::checkDebug: " << id
;
173 Trace(traceTag
) << " failed, " << out
.str() << std::endl
;
177 Trace(traceTag
) << " success" << std::endl
;
179 Trace(traceTag
) << "cchildren: " << cchildren
<< std::endl
;
180 Trace(traceTag
) << " args: " << args
<< std::endl
;
185 Node
ProofChecker::checkInternal(PfRule id
,
186 const std::vector
<Node
>& cchildren
,
187 const std::vector
<Node
>& args
,
189 std::stringstream
& out
,
190 bool useTrustedChecker
,
193 std::map
<PfRule
, ProofRuleChecker
*>::iterator it
= d_checker
.find(id
);
194 if (it
== d_checker
.end())
196 // no checker for the rule
199 out
<< "no checker for rule " << id
<< std::endl
;
203 else if (it
->second
== nullptr)
205 if (useTrustedChecker
)
207 Notice() << "ProofChecker::check: trusting PfRule " << id
<< std::endl
;
215 out
<< "trusted checker for rule " << id
<< std::endl
;
220 // check it with the corresponding checker
221 Node res
= it
->second
->check(id
, cchildren
, args
);
222 if (!expected
.isNull())
224 Node expectedw
= expected
;
225 if (res
!= expectedw
)
229 out
<< "result does not match expected value." << std::endl
230 << " PfRule: " << id
<< std::endl
;
231 for (const Node
& c
: cchildren
)
233 out
<< " child: " << c
<< std::endl
;
235 for (const Node
& a
: args
)
237 out
<< " arg: " << a
<< std::endl
;
239 out
<< " result: " << res
<< std::endl
240 << " expected: " << expected
<< std::endl
;
242 // it did not match the given expectation, fail
246 // fails if pedantic level is not met
247 if (options::proofEagerChecking())
249 std::stringstream serr
;
250 if (isPedanticFailure(id
, serr
, enableOutput
))
254 out
<< serr
.str() << std::endl
;
255 if (Trace
.isOn("proof-pedantic"))
257 Trace("proof-pedantic")
258 << "Failed pedantic check for " << id
<< std::endl
;
259 Trace("proof-pedantic") << "Expected: " << expected
<< std::endl
;
260 out
<< "Expected: " << expected
<< std::endl
;
269 void ProofChecker::registerChecker(PfRule id
, ProofRuleChecker
* psc
)
271 std::map
<PfRule
, ProofRuleChecker
*>::iterator it
= d_checker
.find(id
);
272 if (it
!= d_checker
.end())
274 // checker is already provided
275 Notice() << "ProofChecker::registerChecker: checker already exists for "
282 void ProofChecker::registerTrustedChecker(PfRule id
,
283 ProofRuleChecker
* psc
,
286 AlwaysAssert(plevel
<= 10) << "ProofChecker::registerTrustedChecker: "
287 "pedantic level must be 0-10, got "
288 << plevel
<< " for " << id
;
289 registerChecker(id
, psc
);
290 // overwrites if already there
291 if (d_plevel
.find(id
) != d_plevel
.end())
293 Notice() << "ProofChecker::registerTrustedRule: already provided pedantic "
297 d_plevel
[id
] = plevel
;
300 ProofRuleChecker
* ProofChecker::getCheckerFor(PfRule id
)
302 std::map
<PfRule
, ProofRuleChecker
*>::const_iterator it
= d_checker
.find(id
);
303 if (it
== d_checker
.end())
310 uint32_t ProofChecker::getPedanticLevel(PfRule id
) const
312 std::map
<PfRule
, uint32_t>::const_iterator itp
= d_plevel
.find(id
);
313 if (itp
!= d_plevel
.end())
320 bool ProofChecker::isPedanticFailure(PfRule id
,
322 bool enableOutput
) const
328 std::map
<PfRule
, uint32_t>::const_iterator itp
= d_plevel
.find(id
);
329 if (itp
!= d_plevel
.end())
331 if (itp
->second
<= d_pclevel
)
335 out
<< "pedantic level for " << id
<< " not met (rule level is "
336 << itp
->second
<< " which is at or below the pedantic level "
338 bool pedanticTraceEnabled
= Trace
.isOn("proof-pedantic");
339 if (!pedanticTraceEnabled
)
341 out
<< ", use -t proof-pedantic for details";