1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Haniel Barbosa |
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 |
|
* A proof as produced by the equality engine. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "cvc5_private.h" |
17 |
|
#include "expr/node.h" |
18 |
|
#include "theory/uf/equality_engine_types.h" |
19 |
|
|
20 |
|
namespace cvc5 { |
21 |
|
|
22 |
|
class CDProof; |
23 |
|
|
24 |
|
namespace theory { |
25 |
|
namespace eq { |
26 |
|
|
27 |
|
/** |
28 |
|
* An equality proof. |
29 |
|
* |
30 |
|
* This represents the reasoning performed by the Equality Engine to derive |
31 |
|
* facts, represented in terms of the rules in MergeReasonType. Each proof step |
32 |
|
* is annotated with the rule id, the conclusion node and a vector of proofs of |
33 |
|
* the rule's premises. |
34 |
|
**/ |
35 |
2630100 |
class EqProof |
36 |
|
{ |
37 |
|
public: |
38 |
2488730 |
EqProof() : d_id(MERGED_THROUGH_REFLEXIVITY) {} |
39 |
|
/** The proof rule for concluding d_node */ |
40 |
|
MergeReasonType d_id; |
41 |
|
/** The conclusion of this EqProof */ |
42 |
|
Node d_node; |
43 |
|
/** The proofs of the premises for deriving d_node with d_id */ |
44 |
|
std::vector<std::shared_ptr<EqProof>> d_children; |
45 |
|
/** |
46 |
|
* Debug print this proof on debug trace c with tabulation tb. |
47 |
|
*/ |
48 |
|
void debug_print(const char* c, unsigned tb = 0) const; |
49 |
|
/** |
50 |
|
* Debug print this proof on output stream os with tabulation tb. |
51 |
|
*/ |
52 |
|
void debug_print(std::ostream& os, unsigned tb = 0) const; |
53 |
|
|
54 |
|
/** Add to proof |
55 |
|
* |
56 |
|
* Converts EqProof into ProofNode via a series of steps to be stored in |
57 |
|
* CDProof* p. |
58 |
|
* |
59 |
|
* This method can be seen as a best-effort solution until the EqualityEngine |
60 |
|
* is updated to produce ProofNodes directly, if ever. Note that since the |
61 |
|
* original EqProof steps can be coarse-grained (e.g. without proper order, |
62 |
|
* implicit inferences related to disequelaties) and are phrased over curried |
63 |
|
* terms, the transformation requires significant, although cheap (mostly |
64 |
|
* linear on the DAG-size of EqProof), post-processing. |
65 |
|
* |
66 |
|
* An important invariant of the resulting ProofNode is that neither its |
67 |
|
* assumptions nor its conclusion are predicate equalities, i.e. of the form |
68 |
|
* (= t true/false), modulo symmetry. This is so that users of the converted |
69 |
|
* ProofNode don't need to do case analysis on whether assumptions/conclusion |
70 |
|
* are either t or (= t true/false). |
71 |
|
* |
72 |
|
* @param p a pointer to a CDProof to store the conversion of this EqProof |
73 |
|
* @return the node that is the conclusion of the proof as added to p. |
74 |
|
*/ |
75 |
|
Node addToProof(CDProof* p) const; |
76 |
|
|
77 |
|
private: |
78 |
|
/** |
79 |
|
* As above, but with a cache of previously processed nodes and their results |
80 |
|
* (for DAG traversal). The caching is in terms of the original conclusions of |
81 |
|
* EqProof. |
82 |
|
* |
83 |
|
* @param p a pointer to a CDProof to store the conversion of this EqProof |
84 |
|
* @param visited a cache of the original EqProof conclusions and the |
85 |
|
* resulting conclusion after conversion. |
86 |
|
* @param assumptions the assumptions of the original EqProof (and their |
87 |
|
* variations in terms of symmetry and conversion to/from predicate |
88 |
|
* equalities) |
89 |
|
* @return the node that is the conclusion of the proof as added to p. |
90 |
|
*/ |
91 |
|
Node addToProof(CDProof* p, |
92 |
|
std::unordered_map<Node, Node>& visited, |
93 |
|
std::unordered_set<Node>& assumptions) const; |
94 |
|
|
95 |
|
/** Removes all reflexivity steps, i.e. (= t t), from premises. */ |
96 |
|
void cleanReflPremises(std::vector<Node>& premises) const; |
97 |
|
|
98 |
|
/** Expand coarse-grained transitivity steps for disequalities |
99 |
|
* |
100 |
|
* Currently the equality engine can represent disequality reasoning in a |
101 |
|
* rather coarse-grained manner with EqProof. This is always the case when the |
102 |
|
* transitivity step contains a disequality, (= (= t t') false) or its |
103 |
|
* symmetric, as a premise. |
104 |
|
* |
105 |
|
* There are two cases. In the simplest one the general shape of the EqProof |
106 |
|
* is |
107 |
|
* (= t1 t2) (= t3 t2) (= (t1 t3) false) |
108 |
|
* ------------------------------------- EQP::TR |
109 |
|
* false = true |
110 |
|
* |
111 |
|
* which is expanded into |
112 |
|
* (= t3 t2) |
113 |
|
* --------- SYMM |
114 |
|
* (= t1 t2) (= t2 t3) |
115 |
|
* ------------------- TRANS |
116 |
|
* (= (= t1 t3) false) (= t1 t3) |
117 |
|
* --------------------- SYMM ------------------ TRUE_INTRO |
118 |
|
* (= false (= t1 t3)) (= (= t1 t3) true) |
119 |
|
* ----------------------------------------------- TRANS |
120 |
|
* false = true |
121 |
|
* |
122 |
|
* by explicitly adding the transitivity step for inferring (= t1 t3) and its |
123 |
|
* predicate equality. Note that we differentiate (from now on) the EqProof |
124 |
|
* rules ids from those of ProofRule by adding the prefix EQP:: to the former. |
125 |
|
* |
126 |
|
* In the other case, the general shape of the EqProof is |
127 |
|
* |
128 |
|
* (= (= t1 t2) false) (= t1 x1) ... (= xn t3) (= t2 y1) ... (= ym t4) |
129 |
|
* ------------------------------------------------------------------- EQP::TR |
130 |
|
* (= (= t4 t3) false) |
131 |
|
* |
132 |
|
* which is converted into |
133 |
|
* |
134 |
|
* (= t1 x1) ... (= xn t3) (= t2 y1) ... (= ym t4) |
135 |
|
* ------------------------ TR ------------------------ TR |
136 |
|
* (= t1 t3) (= t2 t4) |
137 |
|
* ----------- SYMM ----------- SYMM |
138 |
|
* (= t3 t1) (= t4 t2) |
139 |
|
* ---------------------------------------- CONG |
140 |
|
* (= (= t3 t4) (= t1 t2)) (= (= t1 t2) false) |
141 |
|
* --------------------------------------------------------------------- TR |
142 |
|
* (= (= t3 t4) false) |
143 |
|
* --------------------- MACRO_SR_PRED_TRANSFORM |
144 |
|
* (= (= t4 t3) false) |
145 |
|
* |
146 |
|
* whereas the last step is only necessary if the conclusion has the arguments |
147 |
|
* in reverse order than expected. Note that the original step represents two |
148 |
|
* substitutions happening on the disequality, from t1->t3 and t2->t4, which |
149 |
|
* are implicitly justified by transitivity steps that need to be made |
150 |
|
* explicity. Since there is no sense of ordering among which premisis (other |
151 |
|
* than (= (= t1 t2) false)) are used for which substitution, the transitivity |
152 |
|
* proofs are built greedly by searching the sets of premises. |
153 |
|
* |
154 |
|
* If in either of the above cases then the conclusion is directly derived |
155 |
|
* in the call, so only in the other cases we try to build a transitivity |
156 |
|
* step below |
157 |
|
* |
158 |
|
* @param conclusion the conclusion of the (possibly) coarse-grained |
159 |
|
* transitivity step |
160 |
|
* @param premises the premises of the (possibly) coarse-grained |
161 |
|
* transitivity step |
162 |
|
* @param p a pointer to a CDProof to store the conversion of this EqProof |
163 |
|
* @param assumptions the assumptions (and variants) of the original |
164 |
|
* EqProof. These are necessary to avoid cyclic proofs, which could be |
165 |
|
* generated by creating transitivity steps for assumptions (which depend on |
166 |
|
* themselves). |
167 |
|
* @return True if the EqProof transitivity step is in either of the above |
168 |
|
* cases, symbolizing that the ProofNode justifying the conclusion has already |
169 |
|
* been produced. |
170 |
|
*/ |
171 |
|
bool expandTransitivityForDisequalities( |
172 |
|
Node conclusion, |
173 |
|
std::vector<Node>& premises, |
174 |
|
CDProof* p, |
175 |
|
std::unordered_set<Node>& assumptions) const; |
176 |
|
|
177 |
|
/** Expand coarse-grained transitivity steps for theory disequalities |
178 |
|
* |
179 |
|
* Currently the equality engine can represent disequality reasoning of theory |
180 |
|
* symbols in a rather coarse-grained manner with EqProof. This is the case |
181 |
|
* when EqProof is |
182 |
|
* (= t1 c1) (= t2 c2) |
183 |
|
* ------------------------------------- EQP::TR |
184 |
|
* (= (t1 t2) false) |
185 |
|
* |
186 |
|
* which is converted into |
187 |
|
* |
188 |
|
* (= t1 c1) (= t2 c2) |
189 |
|
* -------------------------- CONG --------------------- MACRO_SR_PRED_INTRO |
190 |
|
* (= (= t1 t2) (= c1 t2)) (= (= c1 c2) false) |
191 |
|
* --------------------------------------------------------- TR |
192 |
|
* (= (= t1 t2) false) |
193 |
|
* |
194 |
|
* @param conclusion the conclusion of the (possibly) coarse-grained |
195 |
|
* transitivity step |
196 |
|
* @param premises the premises of the (possibly) coarse-grained |
197 |
|
* transitivity step |
198 |
|
* @param p a pointer to a CDProof to store the conversion of this EqProof |
199 |
|
* @return True if the EqProof transitivity step is the above case, |
200 |
|
* indicating that the ProofNode justifying the conclusion has already been |
201 |
|
* produced. |
202 |
|
*/ |
203 |
|
bool expandTransitivityForTheoryDisequalities(Node conclusion, |
204 |
|
std::vector<Node>& premises, |
205 |
|
CDProof* p) const; |
206 |
|
|
207 |
|
/** Builds a transitivity chain from equalities to derive a conclusion |
208 |
|
* |
209 |
|
* Given an equality (= t1 tn), and a list of equalities premises, attempts to |
210 |
|
* build a chain (= t1 t2) ... (= tn-1 tn) from premises. This is done in a |
211 |
|
* greedy manner by finding one "link" in the chain at a time, updating the |
212 |
|
* conclusion to be the next link and the premises by removing the used |
213 |
|
* premise, and searching recursively. |
214 |
|
* |
215 |
|
* Consider for example |
216 |
|
* - conclusion: (= t1 t4) |
217 |
|
* - premises: (= t4 t2), (= t2 t3), (= t2 t1), (= t3 t4) |
218 |
|
* |
219 |
|
* It proceeds by searching for t1 in an equality in the premises, in order, |
220 |
|
* which is found in the equality (= t2 t1). Since t2 != t4, it attempts to |
221 |
|
* build a chain with |
222 |
|
* - conclusion (= t2 t4) |
223 |
|
* - premises (= t4 t2), (= t2 t3), (= t3 t4) |
224 |
|
* |
225 |
|
* In the first equality it finds t2 and also t4, which closes the chain, so |
226 |
|
* that premises is updated to (= t2 t4) and the method returns true. Since |
227 |
|
* the recursive call was successful, the original conclusion (= t1 t4) is |
228 |
|
* justified with (= t1 t2) plus the chain built in the recursive call, |
229 |
|
* i.e. (= t1 t2), (= t2 t4). |
230 |
|
* |
231 |
|
* Note that not all the premises were necessary to build a successful |
232 |
|
* chain. Moreover, note that building a successful chain can depend on the |
233 |
|
* order in which the equalities are chosen. When a recursive call fails to |
234 |
|
* close a chain, the chosen equality is dismissed and another is searched for |
235 |
|
* among the remaining ones. |
236 |
|
* |
237 |
|
* This method assumes that premises does not contain reflexivity premises. |
238 |
|
* This is without loss of generality since such premises are spurious for a |
239 |
|
* transitivity step. |
240 |
|
* |
241 |
|
* @param conclusion the conclusion of the transitivity chain of equalities |
242 |
|
* @param premises the list of equalities to build a chain with |
243 |
|
* @return whether premises successfully build a transitivity chain for the |
244 |
|
* conclusion |
245 |
|
*/ |
246 |
|
bool buildTransitivityChain(Node conclusion, |
247 |
|
std::vector<Node>& premises) const; |
248 |
|
|
249 |
|
/** Reduce the a congruence EqProof into a transitivity matrix |
250 |
|
* |
251 |
|
* Given a congruence EqProof of (= (f a0 ... an-1) (f b0 ... bn-1)), reduce |
252 |
|
* its justification into a matrix |
253 |
|
* |
254 |
|
* [0] -> p_{0,0} ... p_{m_0,0} |
255 |
|
* ... |
256 |
|
* [n-1] -> p_{0,n} ... p_{m_n-1,n-1} |
257 |
|
* |
258 |
|
* where f has arity n and each p_{0,i} ... p_{m_i, i} is a transitivity chain |
259 |
|
* (modulo ordering) justifying (= ai bi). |
260 |
|
* |
261 |
|
* Congruence steps in EqProof are binary, representing reasoning over curried |
262 |
|
* applications. In the simplest case the general shape of a congruence |
263 |
|
* EqProof is: |
264 |
|
* P0 |
265 |
|
* ------- EQP::REFL ---------- |
266 |
|
* P1 [] (= a0 b0) |
267 |
|
* --------- ----------------------- EQP::CONG |
268 |
|
* (= a1 b1) [] P2 |
269 |
|
* ------------------------- EQP::CONG ----------- |
270 |
|
* [] (= a2 b2) |
271 |
|
* ------------------------------------------------ EQP::CONG |
272 |
|
* (= (f a0 a1 a2) (f b0 b1 b2)) |
273 |
|
* |
274 |
|
* where [] stands for the null node, symbolizing "equality between partial |
275 |
|
* applications". |
276 |
|
* |
277 |
|
* The reduction of such a proof is done by |
278 |
|
* - converting the proof of the second CONG premise (via addToProof) and |
279 |
|
* adding the resulting node to row i of the matrix |
280 |
|
* - recursively reducing the first proof with i-1 |
281 |
|
* |
282 |
|
* In the above case the transitivity matrix would thus be |
283 |
|
* [0] -> (= a0 b0) |
284 |
|
* [1] -> (= a1 b1) |
285 |
|
* [2] -> (= a2 b2) |
286 |
|
* |
287 |
|
* The more complex case of congruence proofs has transitivity steps as the |
288 |
|
* first child of CONG steps. For example |
289 |
|
* P0 |
290 |
|
* ------- EQP::REFL ---------- |
291 |
|
* P' [] (= a0 c) |
292 |
|
* --------- ----------------------------- EQP::CONG |
293 |
|
* (= b0 c) [] P1 |
294 |
|
* ------------------------- EQP::TRANS ----------- |
295 |
|
* [] (= a1 b1) |
296 |
|
* ----------------------------------------------------- EQP::CONG |
297 |
|
* (= (f a0 a1) (f b0 b1)) |
298 |
|
* |
299 |
|
* where when the first child of CONG is a transitivity step |
300 |
|
* - the premises that are CONG steps are recursively reduced with *the same* |
301 |
|
argument i |
302 |
|
* - the other premises are processed with addToProof and added to the i row |
303 |
|
* in the matrix |
304 |
|
* |
305 |
|
* In the above example the to which the transitivity matrix is |
306 |
|
* [0] -> (= a0 c), (= b0 c) |
307 |
|
* [1] -> (= a1 b1) |
308 |
|
* |
309 |
|
* The remaining complication is that when conclusion is an equality of n-ary |
310 |
|
* applications of *different* arities, there is, necessarily, a transitivity |
311 |
|
* step as a first child a CONG step whose conclusion is an equality of n-ary |
312 |
|
* applications of different arities. For example |
313 |
|
* P0 P1 |
314 |
|
* -------------------------- EQP::TRANS ----------- |
315 |
|
* (= (f a0 a1) (f b0)) (= a2 b1) |
316 |
|
* -------------------------------------------------- EQP::CONG |
317 |
|
* (= (f a0 a1 a2) (f b0 b1)) |
318 |
|
* |
319 |
|
* will be first reduced with i = 2 (maximal arity amorg the original |
320 |
|
* conclusion's applications), adding (= a2 b1) to row 2 after processing |
321 |
|
* P1. The first child is reduced with i = 1. Since it is a TRANS step whose |
322 |
|
* conclusion is an equality of n-ary applications with mismatching arity, P0 |
323 |
|
* is processed with addToProof and the result is added to row 1. Thus the |
324 |
|
* transitivity matrix is |
325 |
|
* [0] -> |
326 |
|
* [1] -> (= (f a0 a1) (f b0)) |
327 |
|
* [2] -> (= a2 b1) |
328 |
|
* |
329 |
|
* The empty row 0 is used by the original caller of reduceNestedCongruence to |
330 |
|
* compute that the above congruence proof's conclusion is |
331 |
|
* (= (f (f a0 a1) a2) (f (f b0) b1)) |
332 |
|
* |
333 |
|
* @param i the i-th argument of the congruent applications, initially being |
334 |
|
* the maximal arity among conclusion's applications. |
335 |
|
* @param conclusion the original congruence conclusion |
336 |
|
* @param transitivityMatrix a matrix of equalities with each row justifying |
337 |
|
* an equality between the congruent applications |
338 |
|
* @param p a pointer to a CDProof to store the conversion of this EqProof |
339 |
|
* @param visited a cache of the original EqProof conclusions and the |
340 |
|
* resulting conclusion after conversion. |
341 |
|
* @param assumptions the assumptions (and variants) of the original EqProof |
342 |
|
* @param isNary whether conclusion is an equality of n-ary applications |
343 |
|
*/ |
344 |
|
void reduceNestedCongruence( |
345 |
|
unsigned i, |
346 |
|
Node conclusion, |
347 |
|
std::vector<std::vector<Node>>& transitivityMatrix, |
348 |
|
CDProof* p, |
349 |
|
std::unordered_map<Node, Node>& visited, |
350 |
|
std::unordered_set<Node>& assumptions, |
351 |
|
bool isNary) const; |
352 |
|
|
353 |
|
}; /* class EqProof */ |
354 |
|
|
355 |
|
} // Namespace eq |
356 |
|
} // Namespace theory |
357 |
|
} // namespace cvc5 |