1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Haniel Barbosa, Gereon Kremer |
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 |
|
* The module for processing proof nodes. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "cvc5_private.h" |
17 |
|
|
18 |
|
#ifndef CVC5__SMT__PROOF_POST_PROCESSOR_H |
19 |
|
#define CVC5__SMT__PROOF_POST_PROCESSOR_H |
20 |
|
|
21 |
|
#include <map> |
22 |
|
#include <sstream> |
23 |
|
#include <unordered_set> |
24 |
|
|
25 |
|
#include "expr/proof_node_updater.h" |
26 |
|
#include "smt/witness_form.h" |
27 |
|
#include "util/statistics_stats.h" |
28 |
|
|
29 |
|
namespace cvc5 { |
30 |
|
|
31 |
|
class SmtEngine; |
32 |
|
|
33 |
|
namespace smt { |
34 |
|
|
35 |
|
/** |
36 |
|
* A callback class used by SmtEngine for post-processing proof nodes by |
37 |
|
* connecting proofs of preprocessing, and expanding macro PfRule applications. |
38 |
|
*/ |
39 |
|
class ProofPostprocessCallback : public ProofNodeUpdaterCallback |
40 |
|
{ |
41 |
|
public: |
42 |
|
ProofPostprocessCallback(ProofNodeManager* pnm, |
43 |
|
SmtEngine* smte, |
44 |
|
ProofGenerator* pppg, |
45 |
|
bool updateScopedAssumptions); |
46 |
3600 |
~ProofPostprocessCallback() {} |
47 |
|
/** |
48 |
|
* Initialize, called once for each new ProofNode to process. This initializes |
49 |
|
* static information to be used by successive calls to update. |
50 |
|
*/ |
51 |
|
void initializeUpdate(); |
52 |
|
/** |
53 |
|
* Set eliminate rule, which adds rule to the list of rules we will eliminate |
54 |
|
* during update. This adds rule to d_elimRules. Supported rules for |
55 |
|
* elimination include MACRO_*, SUBS and REWRITE. Otherwise, this method |
56 |
|
* has no effect. |
57 |
|
*/ |
58 |
|
void setEliminateRule(PfRule rule); |
59 |
|
/** Should proof pn be updated? */ |
60 |
|
bool shouldUpdate(std::shared_ptr<ProofNode> pn, |
61 |
|
const std::vector<Node>& fa, |
62 |
|
bool& continueUpdate) override; |
63 |
|
/** Update the proof rule application. */ |
64 |
|
bool update(Node res, |
65 |
|
PfRule id, |
66 |
|
const std::vector<Node>& children, |
67 |
|
const std::vector<Node>& args, |
68 |
|
CDProof* cdp, |
69 |
|
bool& continueUpdate) override; |
70 |
|
|
71 |
|
private: |
72 |
|
/** Common constants */ |
73 |
|
Node d_true; |
74 |
|
/** The proof node manager */ |
75 |
|
ProofNodeManager* d_pnm; |
76 |
|
/** Pointer to the SmtEngine, which should have proofs enabled */ |
77 |
|
SmtEngine* d_smte; |
78 |
|
/** The preprocessing proof generator */ |
79 |
|
ProofGenerator* d_pppg; |
80 |
|
/** The witness form proof generator */ |
81 |
|
WitnessFormGenerator d_wfpm; |
82 |
|
/** The witness form assumptions used in the proof */ |
83 |
|
std::vector<Node> d_wfAssumptions; |
84 |
|
/** Kinds of proof rules we are eliminating */ |
85 |
|
std::unordered_set<PfRule, PfRuleHashFunction> d_elimRules; |
86 |
|
/** Whether we post-process assumptions in scope. */ |
87 |
|
bool d_updateScopedAssumptions; |
88 |
|
//---------------------------------reset at the begining of each update |
89 |
|
/** Mapping assumptions to their proof from preprocessing */ |
90 |
|
std::map<Node, std::shared_ptr<ProofNode> > d_assumpToProof; |
91 |
|
//---------------------------------end reset at the begining of each update |
92 |
|
/** |
93 |
|
* Expand rules in the given application, add the expanded proof to cdp. |
94 |
|
* The set of rules we expand is configured by calls to setEliminateRule |
95 |
|
* above. This method calls update to perform possible post-processing in the |
96 |
|
* rules it introduces as a result of the expansion. |
97 |
|
* |
98 |
|
* @param id The rule of the application |
99 |
|
* @param children The children of the application |
100 |
|
* @param args The arguments of the application |
101 |
|
* @param cdp The proof to add to |
102 |
|
* @return The conclusion of the rule, or null if this rule is not eliminated. |
103 |
|
*/ |
104 |
|
Node expandMacros(PfRule id, |
105 |
|
const std::vector<Node>& children, |
106 |
|
const std::vector<Node>& args, |
107 |
|
CDProof* cdp); |
108 |
|
/** |
109 |
|
* Update the proof rule application, called during expand macros when |
110 |
|
* we wish to apply the update method. This method has the same behavior |
111 |
|
* as update apart from ignoring the continueUpdate flag. |
112 |
|
*/ |
113 |
|
bool updateInternal(Node res, |
114 |
|
PfRule id, |
115 |
|
const std::vector<Node>& children, |
116 |
|
const std::vector<Node>& args, |
117 |
|
CDProof* cdp); |
118 |
|
/** |
119 |
|
* Add proof for witness form. This returns the equality t = toWitness(t) |
120 |
|
* and ensures that the proof of this equality has been added to cdp. |
121 |
|
* Notice the proof of this fact may have open assumptions of the form: |
122 |
|
* k = toWitness(k) |
123 |
|
* where k is a skolem. Furthermore, note that all open assumptions of this |
124 |
|
* form are available via d_wfpm.getWitnessFormEqs() in the remainder of |
125 |
|
* the lifetime of this class. |
126 |
|
*/ |
127 |
|
Node addProofForWitnessForm(Node t, CDProof* cdp); |
128 |
|
/** |
129 |
|
* Apply transivity if necessary for the arguments. The nodes in |
130 |
|
* tchildren have been ordered such that they are legal arguments to TRANS. |
131 |
|
* |
132 |
|
* Returns the conclusion of the transitivity step, which is null if |
133 |
|
* tchildren is empty. Also note if tchildren contains a single element, |
134 |
|
* then no TRANS step is necessary to add to cdp. |
135 |
|
* |
136 |
|
* @param tchildren The children of a TRANS step |
137 |
|
* @param cdp The proof to add the TRANS step to |
138 |
|
* @return The conclusion of the TRANS step. |
139 |
|
*/ |
140 |
|
Node addProofForTrans(const std::vector<Node>& tchildren, CDProof* cdp); |
141 |
|
/** |
142 |
|
* Add proof for substitution step. Some substitutions are derived based |
143 |
|
* on viewing a formula as a Boolean assignment (see MethodId::SB_LITERAL for |
144 |
|
* example). This method ensures that the proof of var == subs exists |
145 |
|
* in cdp, where var, subs were derived from BuiltinProofRuleChecker's |
146 |
|
* getSubstitution method. |
147 |
|
* |
148 |
|
* @param var The variable of the substitution |
149 |
|
* @param subs The substituted term |
150 |
|
* @param assump The formula the substitution was derived from |
151 |
|
* @param cdp The proof to add to |
152 |
|
* @return var == subs, the conclusion of the substitution step. |
153 |
|
*/ |
154 |
|
Node addProofForSubsStep(Node var, Node subs, Node assump, CDProof* cdp); |
155 |
|
/** Add eq (or its symmetry) to transivity children, if not reflexive */ |
156 |
|
bool addToTransChildren(Node eq, |
157 |
|
std::vector<Node>& tchildren, |
158 |
|
bool isSymm = false); |
159 |
|
|
160 |
|
/** |
161 |
|
* When given children and args lead to different sets of literals in a |
162 |
|
* conclusion depending on whether macro resolution or chain resolution is |
163 |
|
* applied, the literals that appear in the chain resolution result, but not |
164 |
|
* in the macro resolution result, from now on "crowding literals", are |
165 |
|
* literals removed implicitly by macro resolution. For example |
166 |
|
* |
167 |
|
* l0 v l0 v l0 v l1 v l2 ~l0 v l1 ~l1 |
168 |
|
* (1) ----------------------------------------- MACRO_RES |
169 |
|
* l2 |
170 |
|
* |
171 |
|
* but |
172 |
|
* |
173 |
|
* l0 v l0 v l0 v l1 v l2 ~l0 v l1 ~l1 |
174 |
|
* (2) ---------------------------------------- CHAIN_RES |
175 |
|
* l0 v l0 v l1 v l2 |
176 |
|
* |
177 |
|
* where l0 and l1 are crowding literals in the second proof. |
178 |
|
* |
179 |
|
* There are two views for how MACRO_RES implicitly removes the crowding |
180 |
|
* literal, i.e., how MACRO_RES can be expanded into CHAIN_RES so that |
181 |
|
* crowding literals are removed. The first is that (1) becomes |
182 |
|
* |
183 |
|
* l0 v l0 v l0 v l1 v l2 ~l0 v l1 ~l0 v l1 ~l0 v l1 ~l1 ~l1 ~l1 ~l1 |
184 |
|
* ---------------------------------------------------------------- CHAIN_RES |
185 |
|
* l2 |
186 |
|
* |
187 |
|
* via the repetition of the premise responsible for removing more than one |
188 |
|
* occurrence of the crowding literal. The issue however is that this |
189 |
|
* expansion is exponential. Note that (2) has two occurrences of l0 and one |
190 |
|
* of l1 as crowding literals. However, by repeating ~l0 v l1 two times to |
191 |
|
* remove l0, the clause ~l1, which would originally need to be repeated only |
192 |
|
* one time, now has to be repeated two extra times on top of that one. With |
193 |
|
* multiple crowding literals and their elimination depending on premises that |
194 |
|
* themselves add crowding literals one can easily end up with resolution |
195 |
|
* chains going from dozens to thousands of premises. Such examples do occur |
196 |
|
* in practice, even in our regressions. |
197 |
|
* |
198 |
|
* The second way of expanding MACRO_RES, which avoids this exponential |
199 |
|
* behavior, is so that (1) becomes |
200 |
|
* |
201 |
|
* l0 v l0 v l0 v l1 v l2 |
202 |
|
* (4) ---------------------- FACTORING |
203 |
|
* l0 v l1 v l2 ~l0 v l1 |
204 |
|
* ------------------------------------------- CHAIN_RES |
205 |
|
* l1 v l1 v l2 |
206 |
|
* ------------- FACTORING |
207 |
|
* l1 v l2 ~l1 |
208 |
|
* ------------------------------ CHAIN_RES |
209 |
|
* l2 |
210 |
|
* |
211 |
|
* This method first determines what are the crowding literals by checking |
212 |
|
* what literals occur in clauseLits that do not occur in targetClauseLits |
213 |
|
* (the latter contains the literals from the original MACRO_RES conclusion |
214 |
|
* while the former the literals from a direct application of CHAIN_RES). Then |
215 |
|
* it builds a proof such as (4) and adds the steps to cdp. The final |
216 |
|
* conclusion is returned. |
217 |
|
* |
218 |
|
* Note that in the example the CHAIN_RES steps introduced had only two |
219 |
|
* premises, and could thus be replaced by a RESOLUTION step, but since we |
220 |
|
* general there can be more than two premises we always use CHAIN_RES. |
221 |
|
* |
222 |
|
* @param clauseLits literals in the conclusion of a CHAIN_RESOLUTION step |
223 |
|
* with children and args[1:] |
224 |
|
* @param clauseLits literals in the conclusion of a MACRO_RESOLUTION step |
225 |
|
* with children and args |
226 |
|
* @param children a list of clauses |
227 |
|
* @param args a list of arguments to a MACRO_RESOLUTION step |
228 |
|
* @param cdp a CDProof |
229 |
|
* @return The resulting node of transforming MACRO_RESOLUTION into |
230 |
|
* CHAIN_RESOLUTION according to the above idea. |
231 |
|
*/ |
232 |
|
Node eliminateCrowdingLits(const std::vector<Node>& clauseLits, |
233 |
|
const std::vector<Node>& targetClauseLits, |
234 |
|
const std::vector<Node>& children, |
235 |
|
const std::vector<Node>& args, |
236 |
|
CDProof* cdp); |
237 |
|
}; |
238 |
|
|
239 |
|
/** Final callback class, for stats and pedantic checking */ |
240 |
3600 |
class ProofPostprocessFinalCallback : public ProofNodeUpdaterCallback |
241 |
|
{ |
242 |
|
public: |
243 |
|
ProofPostprocessFinalCallback(ProofNodeManager* pnm); |
244 |
|
/** |
245 |
|
* Initialize, called once for each new ProofNode to process. This initializes |
246 |
|
* static information to be used by successive calls to update. |
247 |
|
*/ |
248 |
|
void initializeUpdate(); |
249 |
|
/** Should proof pn be updated? Returns false, adds to stats. */ |
250 |
|
bool shouldUpdate(std::shared_ptr<ProofNode> pn, |
251 |
|
const std::vector<Node>& fa, |
252 |
|
bool& continueUpdate) override; |
253 |
|
/** was pedantic failure */ |
254 |
|
bool wasPedanticFailure(std::ostream& out) const; |
255 |
|
|
256 |
|
private: |
257 |
|
/** Counts number of postprocessed proof nodes for each kind of proof rule */ |
258 |
|
HistogramStat<PfRule> d_ruleCount; |
259 |
|
/** Total number of postprocessed rule applications */ |
260 |
|
IntStat d_totalRuleCount; |
261 |
|
/** The minimum pedantic level of any rule encountered */ |
262 |
|
IntStat d_minPedanticLevel; |
263 |
|
/** The total number of final proofs */ |
264 |
|
IntStat d_numFinalProofs; |
265 |
|
/** Proof node manager (used for pedantic checking) */ |
266 |
|
ProofNodeManager* d_pnm; |
267 |
|
/** Was there a pedantic failure? */ |
268 |
|
bool d_pedanticFailure; |
269 |
|
/** The pedantic failure string for debugging */ |
270 |
|
std::stringstream d_pedanticFailureOut; |
271 |
|
}; |
272 |
|
|
273 |
|
/** |
274 |
|
* The proof postprocessor module. This postprocesses the final proof |
275 |
|
* produced by an SmtEngine. Its main two tasks are to: |
276 |
|
* (1) Connect proofs of preprocessing, |
277 |
|
* (2) Expand macro PfRule applications. |
278 |
|
*/ |
279 |
|
class ProofPostproccess |
280 |
|
{ |
281 |
|
public: |
282 |
|
/** |
283 |
|
* @param pnm The proof node manager we are using |
284 |
|
* @param smte The SMT engine whose proofs are being post-processed |
285 |
|
* @param pppg The proof generator for pre-processing proofs |
286 |
|
* @param updateScopedAssumptions Whether we post-process assumptions in |
287 |
|
* scope. Since doing so is sound and only problematic depending on who is |
288 |
|
* consuming the proof, it's true by default. |
289 |
|
*/ |
290 |
|
ProofPostproccess(ProofNodeManager* pnm, |
291 |
|
SmtEngine* smte, |
292 |
|
ProofGenerator* pppg, |
293 |
|
bool updateScopedAssumptions = true); |
294 |
|
~ProofPostproccess(); |
295 |
|
/** post-process */ |
296 |
|
void process(std::shared_ptr<ProofNode> pf); |
297 |
|
/** set eliminate rule */ |
298 |
|
void setEliminateRule(PfRule rule); |
299 |
|
|
300 |
|
private: |
301 |
|
/** The proof node manager */ |
302 |
|
ProofNodeManager* d_pnm; |
303 |
|
/** The post process callback */ |
304 |
|
ProofPostprocessCallback d_cb; |
305 |
|
/** |
306 |
|
* The updater, which is responsible for expanding macros in the final proof |
307 |
|
* and connecting preprocessed assumptions to input assumptions. |
308 |
|
*/ |
309 |
|
ProofNodeUpdater d_updater; |
310 |
|
/** The post process callback for finalization */ |
311 |
|
ProofPostprocessFinalCallback d_finalCb; |
312 |
|
/** |
313 |
|
* The finalizer, which is responsible for taking stats and checking for |
314 |
|
* (lazy) pedantic failures. |
315 |
|
*/ |
316 |
|
ProofNodeUpdater d_finalizer; |
317 |
|
}; |
318 |
|
|
319 |
|
} // namespace smt |
320 |
|
} // namespace cvc5 |
321 |
|
|
322 |
|
#endif |