Branch data Line data Source code
1 : : /******************************************************************************
2 : : * This file is part of the cvc5 project.
3 : : *
4 : : * Copyright (c) 2009-2026 by the authors listed in the file AUTHORS
5 : : * in the top-level source directory and their institutional affiliations.
6 : : * All rights reserved. See the file COPYING in the top-level source
7 : : * directory for licensing information.
8 : : * ****************************************************************************
9 : : *
10 : : * Implementation of a proof as produced by the equality engine.
11 : : */
12 : :
13 : : #include "theory/uf/eq_proof.h"
14 : :
15 : : #include "base/configuration.h"
16 : : #include "options/uf_options.h"
17 : : #include "proof/proof.h"
18 : : #include "proof/proof_checker.h"
19 : : #include "proof/proof_node_algorithm.h"
20 : : #include "proof/proof_node_manager.h"
21 : :
22 : : namespace cvc5::internal {
23 : : namespace theory {
24 : : namespace eq {
25 : :
26 : 0 : void EqProof::debug_print(CVC5_UNUSED const char* c, unsigned tb) const
27 : : {
28 : 0 : std::stringstream ss;
29 : 0 : debug_print(ss, tb);
30 : 0 : Trace(c) << ss.str();
31 : 0 : }
32 : :
33 : 0 : void EqProof::debug_print(std::ostream& os, unsigned tb) const
34 : : {
35 [ - - ]: 0 : for (unsigned i = 0; i < tb; i++)
36 : : {
37 : 0 : os << " ";
38 : : }
39 : 0 : os << d_id << "(";
40 [ - - ][ - - ]: 0 : if (d_children.empty() && d_node.isNull())
[ - - ]
41 : : {
42 : 0 : os << ")";
43 : 0 : return;
44 : : }
45 [ - - ]: 0 : if (!d_node.isNull())
46 : : {
47 : 0 : os << std::endl;
48 [ - - ]: 0 : for (unsigned i = 0; i < tb + 1; ++i)
49 : : {
50 : 0 : os << " ";
51 : : }
52 [ - - ]: 0 : os << d_node << (!d_children.empty() ? "," : "");
53 : : }
54 : 0 : unsigned size = d_children.size();
55 [ - - ]: 0 : for (unsigned i = 0; i < size; ++i)
56 : : {
57 : 0 : os << std::endl;
58 : 0 : d_children[i]->debug_print(os, tb + 1);
59 [ - - ]: 0 : if (i < size - 1)
60 : : {
61 [ - - ]: 0 : for (unsigned j = 0; j < tb + 1; ++j)
62 : : {
63 : 0 : os << " ";
64 : : }
65 : 0 : os << ",";
66 : : }
67 : : }
68 [ - - ]: 0 : if (size > 0)
69 : : {
70 [ - - ]: 0 : for (unsigned i = 0; i < tb; ++i)
71 : : {
72 : 0 : os << " ";
73 : : }
74 : : }
75 : 0 : os << ")" << std::endl;
76 : : }
77 : :
78 : 1789941 : void EqProof::cleanReflPremises(std::vector<Node>& premises) const
79 : : {
80 : 1789941 : std::vector<Node> newPremises;
81 : 1789941 : unsigned size = premises.size();
82 [ + + ]: 6196580 : for (unsigned i = 0; i < size; ++i)
83 : : {
84 [ + + ]: 4406639 : if (premises[i][0] == premises[i][1])
85 : : {
86 : 393409 : continue;
87 : : }
88 : 4013230 : newPremises.push_back(premises[i]);
89 : : }
90 [ + + ]: 1789941 : if (newPremises.size() != size)
91 : : {
92 [ + - ]: 378600 : Trace("eqproof-conv") << "EqProof::cleanReflPremises: removed "
93 : 0 : << (size >= newPremises.size()
94 [ - - ]: 189300 : ? size - newPremises.size()
95 : 0 : : 0)
96 : 189300 : << " refl premises from " << premises << "\n";
97 : 189300 : premises.clear();
98 : 189300 : premises.insert(premises.end(), newPremises.begin(), newPremises.end());
99 [ + - ]: 378600 : Trace("eqproof-conv") << "EqProof::cleanReflPremises: new premises "
100 : 189300 : << premises << "\n";
101 : : }
102 : 1789941 : }
103 : :
104 : 710721 : bool EqProof::expandTransitivityForDisequalities(
105 : : Node conclusion,
106 : : std::vector<Node>& premises,
107 : : CDProof* p,
108 : : std::unordered_set<Node>& assumptions) const
109 : : {
110 [ + - ]: 1421442 : Trace("eqproof-conv")
111 : : << "EqProof::expandTransitivityForDisequalities: check if need "
112 : 0 : "to expand transitivity step to conclude "
113 : 710721 : << conclusion << " from premises " << premises << "\n";
114 : : // Check premises to see if any of the form (= (= t1 t2) false), modulo
115 : : // symmetry
116 : 710721 : unsigned size = premises.size();
117 : : // termPos is, in (= (= t1 t2) false) or (= false (= t1 t2)), the position of
118 : : // the equality. When the i-th premise has that form, offending = i
119 : 710721 : unsigned termPos = 2, offending = size;
120 [ + + ]: 3569869 : for (unsigned i = 0; i < size; ++i)
121 : : {
122 [ - + ][ - + ]: 2859148 : Assert(premises[i].getKind() == Kind::EQUAL);
[ - - ]
123 [ + + ]: 8516255 : for (unsigned j = 0; j < 2; ++j)
124 : : {
125 [ + + ][ - - ]: 11384476 : if (premises[i][j].getKind() == Kind::CONST_BOOLEAN
126 [ + + ][ + - ]: 5740763 : && !premises[i][j].getConst<bool>()
[ - - ]
127 [ + + ][ + + ]: 11433001 : && premises[i][1 - j].getKind() == Kind::EQUAL)
[ + + ][ + + ]
[ - - ]
128 : : {
129 : : // there is only one offending equality
130 [ - + ][ - + ]: 35131 : Assert(offending == size);
[ - - ]
131 : 35131 : offending = i;
132 : 35131 : termPos = 1 - j;
133 : 35131 : break;
134 : : }
135 : : }
136 : : }
137 : : // if no equality of the searched form, nothing to do
138 [ + + ]: 710721 : if (offending == size)
139 : : {
140 [ + - ]: 1351180 : Trace("eqproof-conv")
141 : 675590 : << "EqProof::expandTransitivityForDisequalities: no need.\n";
142 : 675590 : return false;
143 : : }
144 : 35131 : NodeManager* nm = conclusion.getNodeManager();
145 [ + + ][ + - ]: 35131 : Assert(termPos == 0 || termPos == 1);
[ - + ][ - + ]
[ - - ]
146 [ + - ]: 70262 : Trace("eqproof-conv") << "EqProof::expandTransitivityForDisequalities: found "
147 : 0 : "offending equality at index "
148 : 35131 : << offending << " : " << premises[offending] << "\n";
149 : : // collect the premises to be used in the expansion, which are all but the
150 : : // offending one
151 : 35131 : std::vector<Node> expansionPremises;
152 [ + + ]: 144714 : for (unsigned i = 0; i < size; ++i)
153 : : {
154 [ + + ]: 109583 : if (i != offending)
155 : : {
156 : 74452 : expansionPremises.push_back(premises[i]);
157 : : }
158 : : }
159 : : // Eliminate spurious premises. Reasoning below assumes no refl steps.
160 : 35131 : cleanReflPremises(expansionPremises);
161 [ - + ][ - + ]: 35131 : Assert(!expansionPremises.empty());
[ - - ]
162 : : // Check if we are in the substitution case
163 : 35131 : Node expansionConclusion;
164 : 35131 : std::vector<Node> substPremises;
165 : 35131 : bool inSubstCase = false, substConclusionInReverseOrder = false;
166 [ + + ]: 70262 : if ((conclusion[0].getKind() == Kind::CONST_BOOLEAN)
167 : 35131 : != (conclusion[1].getKind() == Kind::CONST_BOOLEAN))
168 : : {
169 : 26234 : inSubstCase = true;
170 : : // reorder offending premise if constant is the first argument
171 [ + + ]: 26234 : if (termPos == 1)
172 : : {
173 : 25064 : premises[offending] =
174 : 50128 : premises[offending][1].eqNode(premises[offending][0]);
175 : : }
176 : : // reorder conclusion if constant is the first argument
177 : 26234 : conclusion = conclusion[1].getKind() == Kind::CONST_BOOLEAN
178 : 52468 : ? conclusion
179 : 26234 : : conclusion[1].eqNode(conclusion[0]);
180 : : // equality term in premise disequality
181 : 26234 : Node premiseTermEq = premises[offending][0];
182 : : // equality term in conclusion disequality
183 : 26234 : Node conclusionTermEq = conclusion[0];
184 [ + - ]: 52468 : Trace("eqproof-conv")
185 : : << "EqProof::expandTransitivityForDisequalities: Substitition "
186 : 0 : "case. Need to build subst from "
187 : 26234 : << premiseTermEq << " to " << conclusionTermEq << "\n";
188 : : // If only one argument in the premise is substituted, premiseTermEq and
189 : : // conclusionTermEq will share one argument and the other argument change
190 : : // defines the single substitution. Otherwise both arguments are replaced,
191 : : // so there are two substitutions.
192 [ + + ]: 131170 : std::vector<Node> subs[2];
193 : 26234 : subs[0].push_back(premiseTermEq[0]);
194 : 26234 : subs[1].push_back(premiseTermEq[1]);
195 : : // which of the arguments of premiseTermEq, if any, is equal to one argument
196 : : // of conclusionTermEq
197 : 26234 : int equalArg = -1;
198 [ + + ]: 78702 : for (unsigned i = 0; i < 2; ++i)
199 : : {
200 [ + + ]: 132629 : for (unsigned j = 0; j < 2; ++j)
201 : : {
202 [ + + ]: 96902 : if (premiseTermEq[i] == conclusionTermEq[j])
203 : : {
204 : 16741 : equalArg = i;
205 : : // identity sub
206 : 16741 : subs[i].push_back(conclusionTermEq[j]);
207 : : // sub that changes argument
208 : 16741 : subs[1 - i].push_back(conclusionTermEq[1 - j]);
209 : : // wither e.g. (= t1 t2), with sub t1->t3, becomes (= t2 t3)
210 : 16741 : substConclusionInReverseOrder = i != j;
211 : 16741 : break;
212 : : }
213 : : }
214 : : }
215 : : // simple case of single substitution
216 [ + + ]: 26234 : if (equalArg >= 0)
217 : : {
218 : : // case of
219 : : // (= (= t1 t2) false) (= t1 x1) ... (= xn t3)
220 : : // -------------------------------------------- EQP::TR
221 : : // (= (= t3 t2) false)
222 : : // where
223 : : //
224 : : // (= t1 x1) ... (= xn t3) - expansion premises
225 : : // ------------------------ TRANS
226 : : // (= t1 t3) - expansion conclusion
227 : : //
228 : : // will be the expansion made to justify the substitution for t1->t3.
229 : 16741 : expansionConclusion = subs[1 - equalArg][0].eqNode(subs[1 - equalArg][1]);
230 [ + - ]: 33482 : Trace("eqproof-conv") << "EqProof::expandTransitivityForDisequalities: "
231 : 0 : "Need to expand premises into "
232 : 16741 : << expansionConclusion << "\n";
233 : : // add refl step for the substitition t2->t2
234 : 33482 : p->addStep(subs[equalArg][0].eqNode(subs[equalArg][1]),
235 : : ProofRule::REFL,
236 : : {},
237 : 16741 : {subs[equalArg][0]});
238 : : }
239 : : else
240 : : {
241 : : // Hard case. We determine, and justify, the substitutions t1->t3/t4 and
242 : : // t2->t3/t4 based on the expansion premises.
243 [ + - ]: 18986 : Trace("eqproof-conv") << "EqProof::expandTransitivityForDisequalities: "
244 : 0 : "Need two substitutions. Look for "
245 : 9493 : << premiseTermEq[0] << " and " << premiseTermEq[1]
246 : 9493 : << " in premises " << expansionPremises << "\n";
247 [ - + ][ - - ]: 9493 : Assert(expansionPremises.size() >= 2)
248 : : << "Less than 2 expansion premises for substituting BOTH terms in "
249 : 0 : "disequality.\nDisequality: "
250 [ - + ][ - - ]: 9493 : << premises[offending]
251 : 0 : << "\nExpansion premises: " << expansionPremises
252 [ - + ][ - + ]: 9493 : << "\nConclusion: " << conclusion << "\n";
[ - - ]
253 : : // Easier case where we can determine the substitutions by directly
254 : : // looking at the premises, i.e. the two expansion premises are for
255 : : // example (= t1 t3) and (= t2 t4)
256 [ + + ]: 9493 : if (expansionPremises.size() == 2)
257 : : {
258 : : // iterate over args to be substituted
259 [ + + ]: 17502 : for (unsigned i = 0; i < 2; ++i)
260 : : {
261 : : // iterate over premises
262 [ + + ]: 35004 : for (unsigned j = 0; j < 2; ++j)
263 : : {
264 : : // iterate over args in premise
265 [ + + ]: 55991 : for (unsigned k = 0; k < 2; ++k)
266 : : {
267 [ + + ]: 44323 : if (premiseTermEq[i] == expansionPremises[j][k])
268 : : {
269 : 11668 : subs[i].push_back(expansionPremises[j][1 - k]);
270 : 11668 : break;
271 : : }
272 : : }
273 : : }
274 [ - + ][ - - ]: 11668 : Assert(subs[i].size() == 2)
275 [ - + ][ - + ]: 11668 : << " did not find term " << subs[i][0] << "\n";
[ - - ]
276 : : // check if argument to be substituted is in the same order in the
277 : : // conclusion
278 : : substConclusionInReverseOrder =
279 : 11668 : premiseTermEq[i] != conclusionTermEq[i];
280 : : }
281 : : }
282 : : else
283 : : {
284 [ + - ]: 7318 : Trace("eqproof-conv") << "EqProof::expandTransitivityForDisequalities: "
285 : : "Build transitivity chains "
286 : 0 : "for two subs among more than 2 premises: "
287 : 3659 : << expansionPremises << "\n";
288 : : // Hardest case. Try building a transitivity chain for (= t1 t3). If it
289 : : // can be built, use the remaining expansion premises to build a chain
290 : : // for (= t2 t4). Otherwise build it for (= t1 t4) and then build it for
291 : : // (= t2 t3). It should always succeed.
292 : 3659 : subs[0].push_back(conclusionTermEq[0]);
293 : 3659 : subs[1].push_back(conclusionTermEq[1]);
294 [ + + ]: 10977 : for (unsigned i = 0; i < 2; ++i)
295 : : {
296 : : // copy premises, since buildTransitivityChain is destructive
297 : : std::vector<Node> copy1ofExpPremises(expansionPremises.begin(),
298 : 7318 : expansionPremises.end());
299 : 7318 : Node transConclusion1 = subs[0][0].eqNode(subs[0][1]);
300 [ - + ]: 7318 : if (!buildTransitivityChain(transConclusion1, copy1ofExpPremises))
301 : : {
302 : 0 : AlwaysAssert(i == 0)
303 : : << "Couldn't find sub at all for substituting BOTH terms in "
304 : 0 : "disequality.\nDisequality: "
305 : 0 : << premises[offending]
306 : 0 : << "\nExpansion premises: " << expansionPremises
307 : 0 : << "\nConclusion: " << conclusion << "\n";
308 : : // Failed. So flip sub and try again
309 : 0 : subs[0][1] = conclusionTermEq[1];
310 : 0 : subs[1][1] = conclusionTermEq[0];
311 : 0 : substConclusionInReverseOrder = false;
312 : 0 : continue;
313 : : }
314 : : // build next chain
315 : : std::vector<Node> copy2ofExpPremises(expansionPremises.begin(),
316 : 7318 : expansionPremises.end());
317 : 7318 : Node transConclusion2 = subs[1][0].eqNode(subs[1][1]);
318 [ - + ]: 7318 : if (!buildTransitivityChain(transConclusion2, copy2ofExpPremises))
319 : : {
320 : 0 : Unreachable() << "Found sub " << transConclusion1
321 : 0 : << " but not other sub " << transConclusion2
322 : 0 : << ".\nDisequality: " << premises[offending]
323 : 0 : << "\nExpansion premises: " << expansionPremises
324 : 0 : << "\nConclusion: " << conclusion << "\n";
325 : : }
326 [ + - ]: 14636 : Trace("eqproof-conv")
327 : : << "EqProof::expandTransitivityForDisequalities: Built trans "
328 : : "chains: "
329 : 7318 : "for two subs among more than 2 premises:\n";
330 [ + - ]: 14636 : Trace("eqproof-conv")
331 : 0 : << "EqProof::expandTransitivityForDisequalities: "
332 : 7318 : << transConclusion1 << " <- " << copy1ofExpPremises << "\n";
333 [ + - ]: 14636 : Trace("eqproof-conv")
334 : 0 : << "EqProof::expandTransitivityForDisequalities: "
335 : 7318 : << transConclusion2 << " <- " << copy2ofExpPremises << "\n";
336 : : // do transitivity steps if need be to justify each substitution
337 : 7318 : if (copy1ofExpPremises.size() > 1
338 [ + + ][ + + ]: 7318 : && !assumptions.count(transConclusion1))
[ + + ]
339 : : {
340 : 6296 : p->addStep(transConclusion1,
341 : : ProofRule::TRANS,
342 : : copy1ofExpPremises,
343 : : {},
344 : : true);
345 : : }
346 : 7318 : if (copy2ofExpPremises.size() > 1
347 [ + + ][ + + ]: 7318 : && !assumptions.count(transConclusion2))
[ + + ]
348 : : {
349 : 6402 : p->addStep(transConclusion2,
350 : : ProofRule::TRANS,
351 : : copy2ofExpPremises,
352 : : {},
353 : : true);
354 : : }
355 [ + - ][ + - ]: 7318 : }
356 : : }
357 : : }
358 [ + - ]: 52468 : Trace("eqproof-conv")
359 : 0 : << "EqProof::expandTransitivityForDisequalities: Built substutitions "
360 : 0 : << subs[0] << " and " << subs[1] << " to map " << premiseTermEq
361 : 26234 : << " -> " << conclusionTermEq << "\n";
362 : 52468 : Assert(subs[0][1] == conclusion[0][0] || subs[0][1] == conclusion[0][1])
363 : 0 : << "EqProof::expandTransitivityForDisequalities: First substitution "
364 [ - + ][ - + ]: 26234 : << subs[0] << " doest not map to conclusion " << conclusion << "\n";
[ - - ]
365 : 52468 : Assert(subs[1][1] == conclusion[0][0] || subs[1][1] == conclusion[0][1])
366 : 0 : << "EqProof::expandTransitivityForDisequalities: Second substitution "
367 [ - + ][ - + ]: 26234 : << subs[1] << " doest not map to conclusion " << conclusion << "\n";
[ - - ]
368 : : // In the premises for the original conclusion, the substitution of
369 : : // premiseTermEq (= t1 t2) into conclusionTermEq (= t3 t4) is stored
370 : : // reversed, i.e. as (= (= t3 t4) (= t1 t2)), since the transitivity with
371 : : // the disequality is built as as
372 : : // (= (= t3 t4) (= t1 t2)) (= (= t1 t2) false)
373 : : // --------------------------------------------------------------------- TR
374 : : // (= (= t3 t4) false)
375 : 26234 : substPremises.push_back(subs[0][1].eqNode(subs[0][0]));
376 : 26234 : substPremises.push_back(subs[1][1].eqNode(subs[1][0]));
377 [ + + ][ - - ]: 104936 : }
378 : : else
379 : : {
380 : : // In simple case the conclusion is always, modulo symmetry, false = true
381 : 8897 : Assert(conclusion[0].getKind() == Kind::CONST_BOOLEAN
382 : : && conclusion[1].getKind() == Kind::CONST_BOOLEAN);
383 : : // The expansion conclusion is the same as the equality term in the
384 : : // disequality, which is going to be justified by a transitivity step from
385 : : // the expansion premises
386 : 8897 : expansionConclusion = premises[offending][termPos];
387 : : }
388 : : // Unless we are in the double-substitution case, the proof has the shape
389 : : //
390 : : // ... ... ... ... - expansionPremises
391 : : // ------------------ TRANS
392 : : // (= (= (t t') false) (= t'' t''') - expansionConclusion
393 : : // ---------------------------------------------- TRANS or PRED_TRANSFORM
394 : : // conclusion
395 : : //
396 : : // although note that if it's a TRANS step, (= t'' t''') will be turned into a
397 : : // predicate equality and the premises are ordered.
398 : : //
399 : : // We build the transitivity step for the expansionConclusion here. It being
400 : : // non-null marks that we are not in the double-substitution case.
401 [ + + ]: 35131 : if (!expansionConclusion.isNull())
402 : : {
403 [ + - ]: 51276 : Trace("eqproof-conv")
404 : 0 : << "EqProof::expandTransitivityForDisequalities: need to derive "
405 : 0 : << expansionConclusion << " with premises " << expansionPremises
406 : 25638 : << "\n";
407 : 51276 : Assert(expansionPremises.size() > 1
408 : : || expansionConclusion == expansionPremises.back()
409 : : || (expansionConclusion[0] == expansionPremises.back()[1]
410 : : && expansionConclusion[1] == expansionPremises.back()[0]))
411 [ - + ][ - - ]: 25638 : << "single expansion premise " << expansionPremises.back()
412 [ - + ][ - + ]: 25638 : << " is not the same as expansionConclusion " << expansionConclusion
[ - - ]
413 : 0 : << " and not its symmetric\n";
414 : : // We track assumptions to avoid cyclic proofs, which can happen in EqProofs
415 : : // such as:
416 : : //
417 : : // (= l1 "") (= "" t)
418 : : // ----------------------- EQP::TR
419 : : // (= l1 "") (= l1 t) (= (= "" t) false)
420 : : // ----------------------------------------------------------------- EQP::TR
421 : : // (= false true)
422 : : //
423 : : // which would lead to the cyclic expansion proof:
424 : : //
425 : : // (= l1 "") (= l1 "") (= "" t)
426 : : // --------- SYMM ----------------------- TRANS
427 : : // (= "" l1) (= l1 t)
428 : : // ------------------------------------------ TRANS
429 : : // (= "" t)
430 [ + + ][ + + ]: 25638 : if (expansionPremises.size() > 1 && !assumptions.count(expansionConclusion))
[ + + ]
431 : : {
432 : : // create transitivity step to derive expected premise
433 : 7585 : buildTransitivityChain(expansionConclusion, expansionPremises);
434 [ + - ]: 15170 : Trace("eqproof-conv")
435 : : << "EqProof::expandTransitivityForDisequalities: add transitivity "
436 : 0 : "step for "
437 : 0 : << expansionConclusion << " with premises " << expansionPremises
438 : 7585 : << "\n";
439 : : // create expansion step
440 : 7585 : p->addStep(
441 : : expansionConclusion, ProofRule::TRANS, expansionPremises, {}, true);
442 : : }
443 : : }
444 [ + - ]: 70262 : Trace("eqproof-conv")
445 : 0 : << "EqProof::expandTransitivityForDisequalities: now derive conclusion "
446 : 35131 : << conclusion;
447 : 35131 : Node offendingNode = premises[offending];
448 : 35131 : premises.clear();
449 : 35131 : premises.push_back(offendingNode);
450 [ + + ]: 35131 : if (inSubstCase)
451 : : {
452 [ + - ][ - - ]: 52468 : Trace("eqproof-conv") << (substConclusionInReverseOrder ? " [inverted]"
453 : 0 : : "")
454 : 0 : << " via subsitution from " << premises[0]
455 : 26234 : << " and (inverted subst) " << substPremises << "\n";
456 : : // By this point, for premise disequality (= (= t1 t2) false), we have
457 : : // potentially already built
458 : : //
459 : : // (= t1 x1) ... (= xn t3) (= t2 y1) ... (= ym t4)
460 : : // ------------------------ TR ------------------------ TR
461 : : // (= t1 t3) (= t2 t4)
462 : : //
463 : : // to justify the substitutions t1->t3 and t2->t4 (where note that if t1=t3
464 : : // or t2=4, the step will actually be a REFL one). Not do
465 : : //
466 : : // ----------- SYMM ----------- SYMM
467 : : // (= t3 t1) (= t4 t2)
468 : : // ---------------------------------------- CONG
469 : : // (= (= t3 t4) (= t1 t2)) (= (= t1 t2) false)
470 : : // --------------------------------------------------------------------- TR
471 : : // (= (= t3 t4) false)
472 : : //
473 : : // where note that the SYMM steps are implicitly added by CDProof.
474 : : Node congConclusion = nm->mkNode(
475 : : Kind::EQUAL,
476 : 52468 : nm->mkNode(Kind::EQUAL, substPremises[0][0], substPremises[1][0]),
477 : 104936 : premises[0][0]);
478 : 26234 : std::vector<Node> cargs;
479 : 26234 : ProofRule rule = expr::getCongRule(congConclusion[0], cargs);
480 : 26234 : p->addStep(congConclusion, rule, substPremises, cargs, true);
481 [ + - ]: 52468 : Trace("eqproof-conv") << "EqProof::expandTransitivityForDisequalities: via "
482 : 0 : "congruence derived "
483 : 26234 : << congConclusion << "\n";
484 : : // transitivity step between that and the original premise
485 : 26234 : premises.insert(premises.begin(), congConclusion);
486 : : Node transConclusion =
487 : 26234 : !substConclusionInReverseOrder
488 : : ? conclusion
489 : 48950 : : nm->mkNode(Kind::EQUAL, congConclusion[0], conclusion[1]);
490 : : // check to avoid cyclic proofs
491 [ + - ]: 26234 : if (!assumptions.count(transConclusion))
492 : : {
493 : 26234 : p->addStep(transConclusion, ProofRule::TRANS, premises, {}, true);
494 [ + - ]: 52468 : Trace("eqproof-conv") << "EqProof::expandTransitivityForDisequalities: "
495 : 0 : "via transitivity derived "
496 : 26234 : << transConclusion << "\n";
497 : : }
498 : : // if order is reversed, finish the proof of conclusion with
499 : : // (= (= t3 t4) false)
500 : : // --------------------- MACRO_SR_PRED_TRANSFORM
501 : : // (= (= t4 t3) false)
502 [ + + ]: 26234 : if (substConclusionInReverseOrder)
503 : : {
504 : 22716 : p->addStep(conclusion,
505 : : ProofRule::MACRO_SR_PRED_TRANSFORM,
506 : : {transConclusion},
507 : : {conclusion},
508 : : true);
509 [ + - ]: 15144 : Trace("eqproof-conv") << "EqProof::expandTransitivityForDisequalities: "
510 : 0 : "via macro transform derived "
511 : 7572 : << conclusion << "\n";
512 : : }
513 : 26234 : }
514 : : else
515 : : {
516 : : // create TRUE_INTRO step for expansionConclusion and add it to the premises
517 [ + - ]: 17794 : Trace("eqproof-conv")
518 : : << " via transitivity.\nEqProof::expandTransitivityForDisequalities: "
519 : 0 : "adding "
520 [ - + ][ - - ]: 8897 : << ProofRule::TRUE_INTRO << " step for " << expansionConclusion[0]
521 : 8897 : << "\n";
522 : : Node newExpansionConclusion =
523 : 8897 : expansionConclusion.eqNode(nm->mkConst<bool>(true));
524 : 17794 : p->addStep(newExpansionConclusion,
525 : : ProofRule::TRUE_INTRO,
526 : : {expansionConclusion},
527 : : {});
528 : 8897 : premises.push_back(newExpansionConclusion);
529 [ + - ]: 8897 : Trace("eqproof-conv") << ProofRule::TRANS << " from " << premises << "\n";
530 : 8897 : buildTransitivityChain(conclusion, premises);
531 : : // create final transitivity step
532 : 8897 : p->addStep(conclusion, ProofRule::TRANS, premises, {}, true);
533 : 8897 : }
534 : 35131 : return true;
535 : 35131 : }
536 : :
537 : : // TEMPORARY NOTE: This may not be enough. Worst case scenario I need to
538 : : // reproduce here a search for arbitrary chains between each of the variables in
539 : : // the conclusion and a constant
540 : 675590 : bool EqProof::expandTransitivityForTheoryDisequalities(
541 : : Node conclusion, std::vector<Node>& premises, CDProof* p) const
542 : : {
543 : : // whether conclusion is a disequality (= (= t1 t2) false), modulo symmetry
544 : 675590 : unsigned termPos = -1;
545 [ + + ]: 2026755 : for (unsigned i = 0; i < 2; ++i)
546 : : {
547 [ + + ][ - - ]: 1351180 : if (conclusion[i].getKind() == Kind::CONST_BOOLEAN
548 [ + + ][ + - ]: 1363045 : && !conclusion[i].getConst<bool>()
[ - - ]
549 [ + + ][ + + ]: 1363045 : && conclusion[1 - i].getKind() == Kind::EQUAL)
[ + + ][ + + ]
[ - - ]
550 : : {
551 : 15 : termPos = i - 1;
552 : 15 : break;
553 : : }
554 : : }
555 : : // no disequality
556 [ + + ]: 675590 : if (termPos == static_cast<unsigned>(-1))
557 : : {
558 : 675575 : return false;
559 : : }
560 [ + - ]: 30 : Trace("eqproof-conv")
561 : : << "EqProof::expandTransitivityForTheoryDisequalities: check if need "
562 : 0 : "to expand transitivity step to conclude disequality "
563 : 15 : << conclusion << " from premises " << premises << "\n";
564 : :
565 : : // Check if the premises are (= t1 c1) and (= t2 c2), modulo symmetry
566 : 15 : std::vector<Node> subChildren, constChildren;
567 [ + + ]: 45 : for (unsigned i = 0; i < 2; ++i)
568 : : {
569 : 30 : Node term = conclusion[termPos][i];
570 [ + + ]: 90 : for (const Node& premise : premises)
571 : : {
572 [ + + ]: 138 : for (unsigned j = 0; j < 2; ++j)
573 : : {
574 : 108 : if (premise[j] == term && premise[1 - j].isConst())
575 : : {
576 : 30 : subChildren.push_back(premise[j].eqNode(premise[1 - j]));
577 : 30 : constChildren.push_back(premise[1 - j]);
578 : 30 : break;
579 : : }
580 : : }
581 : : }
582 : 30 : }
583 [ - + ]: 15 : if (subChildren.size() < 2)
584 : : {
585 : 0 : return false;
586 : : }
587 : : // Now build
588 : : // (= t1 c1) (= t2 c2)
589 : : // ------------------------- CONG ------------------- MACRO_SR_PRED_INTRO
590 : : // (= (= t1 t2) (= c1 c2)) (= (= c1 c2) false)
591 : : // --------------------------------------------------------------------- TR
592 : : // (= (= t1 t2) false)
593 : : Node constApp =
594 : 15 : conclusion.getNodeManager()->mkNode(Kind::EQUAL, constChildren);
595 : 15 : Node constEquality = constApp.eqNode(conclusion[1 - termPos]);
596 [ + - ]: 30 : Trace("eqproof-conv")
597 : 0 : << "EqProof::expandTransitivityForTheoryDisequalities: adding "
598 : 0 : << ProofRule::MACRO_SR_PRED_INTRO << " step for " << constApp << " = "
599 [ - + ][ - - ]: 15 : << conclusion[1 - termPos] << "\n";
600 : 30 : p->addStep(
601 : : constEquality, ProofRule::MACRO_SR_PRED_INTRO, {}, {constEquality});
602 : : // build congruence conclusion (= (= t1 t2) (t c1 c2))
603 : 15 : Node congConclusion = conclusion[termPos].eqNode(constApp);
604 [ + - ]: 30 : Trace("eqproof-conv")
605 : 0 : << "EqProof::expandTransitivityForTheoryDisequalities: adding "
606 : 15 : << ProofRule::CONG << " step for " << congConclusion << " from "
607 : 15 : << subChildren << "\n";
608 : 15 : std::vector<Node> cargs;
609 : 15 : ProofRule rule = expr::getCongRule(conclusion[termPos], cargs);
610 : 15 : p->addStep(congConclusion, rule, {subChildren}, cargs, true);
611 [ + - ]: 30 : Trace("eqproof-conv") << "EqProof::expandTransitivityForDisequalities: via "
612 : 0 : "congruence derived "
613 : 15 : << congConclusion << "\n";
614 : 60 : std::vector<Node> transitivityChildren{congConclusion, constEquality};
615 : 15 : p->addStep(conclusion, ProofRule::TRANS, {transitivityChildren}, {});
616 : 15 : return true;
617 : 15 : }
618 : :
619 : 2863315 : bool EqProof::buildTransitivityChain(Node conclusion,
620 : : std::vector<Node>& premises) const
621 : : {
622 [ + - ]: 5726630 : Trace("eqproof-conv") << push
623 : 0 : << "EqProof::buildTransitivityChain: Build chain for "
624 : 2863315 : << conclusion << " with premises " << premises << "\n";
625 [ + - ]: 21777484 : for (unsigned i = 0, size = premises.size(); i < size; ++i)
626 : : {
627 : 21777484 : bool occurs = false, correctlyOrdered = false;
628 [ + + ]: 21777484 : if (conclusion[0] == premises[i][0])
629 : : {
630 : 929997 : occurs = correctlyOrdered = true;
631 : : }
632 [ + + ]: 20847487 : else if (conclusion[0] == premises[i][1])
633 : : {
634 : 1933318 : occurs = true;
635 : : }
636 [ + + ]: 21777484 : if (occurs)
637 : : {
638 [ + - ]: 5726630 : Trace("eqproof-conv")
639 [ - - ]: 2863315 : << "EqProof::buildTransitivityChain: found " << conclusion[0] << " in"
640 [ - - ][ - + ]: 2863315 : << (correctlyOrdered ? "" : " non-") << " ordered premise "
641 : 2863315 : << premises[i] << "\n";
642 [ + + ][ + + ]: 2863315 : if (conclusion[1] == premises[i][correctlyOrdered ? 1 : 0])
643 : : {
644 [ + - ]: 1452680 : Trace("eqproof-conv")
645 [ - + ][ - - ]: 726340 : << "EqProof::buildTransitivityChain: found " << conclusion[1]
646 : 0 : << " in same premise. Closed chain.\n"
647 : 726340 : << pop;
648 : 726340 : premises.clear();
649 : 726340 : premises.push_back(conclusion);
650 : 2863315 : return true;
651 : : }
652 : : // Build chain with remaining equalities
653 : 2136975 : std::vector<Node> recursivePremises;
654 [ + + ]: 23235446 : for (unsigned j = 0; j < size; ++j)
655 : : {
656 [ + + ]: 21098471 : if (j != i)
657 : : {
658 : 18961496 : recursivePremises.push_back(premises[j]);
659 : : }
660 : : }
661 : : Node newTarget =
662 [ + + ]: 4273950 : premises[i][correctlyOrdered ? 1 : 0].eqNode(conclusion[1]);
663 [ + - ]: 4273950 : Trace("eqproof-conv")
664 : 0 : << "EqProof::buildTransitivityChain: search recursively for "
665 : 2136975 : << newTarget << "\n";
666 [ + - ]: 2136975 : if (buildTransitivityChain(newTarget, recursivePremises))
667 : : {
668 [ + - ]: 4273950 : Trace("eqproof-conv")
669 : 0 : << "EqProof::buildTransitivityChain: closed chain with "
670 : 0 : << 1 + recursivePremises.size() << " of the original "
671 : 2136975 : << premises.size() << " premises\n"
672 : 2136975 : << pop;
673 : : Node premiseNode = correctlyOrdered
674 : 679407 : ? premises[i]
675 : 4273950 : : premises[i][1].eqNode(premises[i][0]);
676 : 2136975 : premises.clear();
677 : 2136975 : premises.push_back(premiseNode);
678 : 4273950 : premises.insert(
679 : 2136975 : premises.end(), recursivePremises.begin(), recursivePremises.end());
680 : 2136975 : return true;
681 : 2136975 : }
682 [ - + ][ - + ]: 4273950 : }
683 : : }
684 [ - - ]: 0 : Trace("eqproof-conv")
685 : 0 : << "EqProof::buildTransitivityChain: Could not build chain for"
686 : 0 : << conclusion << " with premises " << premises << "\n";
687 [ - - ]: 0 : Trace("eqproof-conv") << pop;
688 : 0 : return false;
689 : : }
690 : :
691 : 2716409 : void EqProof::reduceNestedCongruence(
692 : : unsigned i,
693 : : Node conclusion,
694 : : std::vector<std::vector<Node>>& transitivityMatrix,
695 : : CDProof* p,
696 : : std::unordered_map<Node, Node>& visited,
697 : : std::unordered_set<Node>& assumptions,
698 : : bool isNary) const
699 : : {
700 [ + - ]: 5432818 : Trace("eqproof-conv") << "EqProof::reduceNestedCongruence: building for " << i
701 : 2716409 : << "-th arg\n";
702 [ + + ]: 2716409 : if (d_id == MERGED_THROUGH_CONGRUENCE)
703 : : {
704 [ - + ][ - + ]: 2260202 : Assert(d_children.size() == 2);
[ - - ]
705 [ + - ]: 4520404 : Trace("eqproof-conv") << "EqProof::reduceNestedCongruence: it's a "
706 : 0 : "congruence step. Reduce second child\n"
707 : 2260202 : << push;
708 : 4520404 : transitivityMatrix[i].push_back(
709 : 4520404 : d_children[1]->addToProof(p, visited, assumptions));
710 [ + - ]: 4520404 : Trace("eqproof-conv")
711 : 0 : << pop << "EqProof::reduceNestedCongruence: child conclusion "
712 : 2260202 : << transitivityMatrix[i].back() << "\n";
713 : : // if i == 0, first child must be REFL step, standing for (= f f), which can
714 : : // be ignored in a first-order calculus
715 : : // Notice if higher-order is disabled, the following holds:
716 : : // i > 0 || d_children[0]->d_id == MERGED_THROUGH_REFLEXIVITY
717 : : // We don't have access to whether we are higher-order in this context,
718 : : // so the above cannot be an assertion.
719 : : // recurse
720 [ + + ]: 2260202 : if (i > 1)
721 : : {
722 [ + - ]: 1365818 : Trace("eqproof-conv")
723 : 0 : << "EqProof::reduceNestedCongruence: Reduce first child\n"
724 : 682909 : << push;
725 : 682909 : d_children[0]->reduceNestedCongruence(i - 1,
726 : : conclusion,
727 : : transitivityMatrix,
728 : : p,
729 : : visited,
730 : : assumptions,
731 : : isNary);
732 [ + - ]: 682909 : Trace("eqproof-conv") << pop;
733 : : }
734 : : // higher-order case
735 [ + + ]: 1577293 : else if (d_children[0]->d_id != MERGED_THROUGH_REFLEXIVITY)
736 : : {
737 [ + - ]: 690 : Trace("eqproof-conv") << "EqProof::reduceNestedCongruence: HO case. "
738 : 345 : "Processing first child\n";
739 : : // we only handle these cases
740 [ + + ][ + - ]: 345 : Assert(d_children[0]->d_id == MERGED_THROUGH_EQUALITY
[ - + ][ - + ]
[ - - ]
741 : : || d_children[0]->d_id == MERGED_THROUGH_TRANS);
742 : 690 : transitivityMatrix[0].push_back(
743 : 690 : d_children[0]->addToProof(p, visited, assumptions));
744 : : }
745 : 2260202 : return;
746 : : }
747 [ - + ][ - + ]: 456207 : Assert(d_id == MERGED_THROUGH_TRANS)
[ - - ]
748 : 0 : << "id is " << static_cast<MergeReasonType>(d_id) << "\n";
749 [ + - ]: 912414 : Trace("eqproof-conv") << "EqProof::reduceNestedCongruence: it's a "
750 : 456207 : "transitivity step.\n";
751 : 456207 : Assert(d_node.isNull()
752 : : || d_node[0].getNumChildren() == d_node[1].getNumChildren() || isNary)
753 : : << "Non-null (internal cong) transitivity conclusion of different arity "
754 : 0 : "but not marked by isNary flag\n";
755 : : // If handling n-ary kinds and got a transitivity conclusion, we process it
756 : : // with addToProof, store the result into row i, and stop. This marks an
757 : : // "adjustment" of the arity, with empty rows 0..i-1 in the matrix determining
758 : : // the adjustment in addToProof processing the congruence of the original
759 : : // conclusion. See details there.
760 [ + + ][ + + ]: 456207 : if (isNary && !d_node.isNull())
[ + + ]
761 : : {
762 [ + - ]: 1740 : Trace("eqproof-conv") << "EqProof::reduceNestedCongruence: n-ary case, "
763 : 0 : "break recursion and indepedently process "
764 : 0 : << d_node << "\n"
765 : 870 : << push;
766 : 870 : transitivityMatrix[i].push_back(addToProof(p, visited, assumptions));
767 [ + - ]: 1740 : Trace("eqproof-conv") << pop
768 : 0 : << "EqProof::reduceNestedCongruence: Got conclusion "
769 : 0 : << transitivityMatrix[i].back()
770 : 870 : << " from n-ary transitivity processing\n";
771 : 870 : return;
772 : : }
773 : : // Regular recursive processing of each transitivity premise
774 [ + + ]: 1592236 : for (unsigned j = 0, sizeTrans = d_children.size(); j < sizeTrans; ++j)
775 : : {
776 [ + - ]: 1136899 : if (d_children[j]->d_id == MERGED_THROUGH_CONGRUENCE)
777 : : {
778 [ + - ]: 2273798 : Trace("eqproof-conv") << "EqProof::reduceNestedCongruence: Reduce " << j
779 : 0 : << "-th transitivity congruence child\n"
780 : 1136899 : << push;
781 : 1136899 : d_children[j]->reduceNestedCongruence(
782 : : i, conclusion, transitivityMatrix, p, visited, assumptions, isNary);
783 [ + - ]: 1136899 : Trace("eqproof-conv") << pop;
784 : : }
785 : : else
786 : : {
787 [ - - ]: 0 : Trace("eqproof-conv") << "EqProof::reduceNestedCongruence: Add " << j
788 : 0 : << "-th transitivity child to proof\n"
789 : 0 : << push;
790 : 0 : transitivityMatrix[i].push_back(
791 : 0 : d_children[j]->addToProof(p, visited, assumptions));
792 [ - - ]: 0 : Trace("eqproof-conv") << pop;
793 : : }
794 : : }
795 : : }
796 : :
797 : 390238 : Node EqProof::addToProof(CDProof* p) const
798 : : {
799 : 390238 : std::unordered_map<Node, Node> cache;
800 : 390238 : std::unordered_set<Node> assumptions;
801 : 390238 : Node conclusion = addToProof(p, cache, assumptions);
802 [ + - ]: 780476 : Trace("eqproof-conv") << "EqProof::addToProof: root of proof: " << conclusion
803 : 390238 : << "\n";
804 [ + - ]: 780476 : Trace("eqproof-conv") << "EqProof::addToProof: tracked assumptions: "
805 : 390238 : << assumptions << "\n";
806 : : // If conclusion t1 = tn is, modulo symmetry, of the form (= t true/false), in
807 : : // which t is not true/false, it must be turned into t or (not t) with
808 : : // TRUE/FALSE_ELIM.
809 : 390238 : Node newConclusion = conclusion;
810 [ - + ][ - + ]: 390238 : Assert(conclusion.getKind() == Kind::EQUAL);
[ - - ]
811 [ + + ]: 780476 : if ((conclusion[0].getKind() == Kind::CONST_BOOLEAN)
812 : 390238 : != (conclusion[1].getKind() == Kind::CONST_BOOLEAN))
813 : : {
814 [ + - ]: 83552 : Trace("eqproof-conv")
815 : 41776 : << "EqProof::addToProof: process root for TRUE/FALSE_ELIM\n";
816 : : // Index of constant in equality
817 : : unsigned constIndex =
818 : 41776 : conclusion[0].getKind() == Kind::CONST_BOOLEAN ? 0 : 1;
819 : : // The premise for the elimination rule must have the constant as the second
820 : : // argument of the equality. If that's not the case, build it as such,
821 : : // relying on an implicit SYMM step to be added to the proof when justifying
822 : : // t / (not t).
823 : : Node elimPremise =
824 : 52709 : constIndex == 1 ? conclusion : conclusion[1].eqNode(conclusion[0]);
825 : : // Determine whether TRUE_ELIM or FALSE_ELIM, depending on the constant
826 : : // value. The new conclusion, whether t or (not t), is also determined
827 : : // accordingly.
828 : : ProofRule elimRule;
829 [ + + ]: 41776 : if (conclusion[constIndex].getConst<bool>())
830 : : {
831 : 9001 : elimRule = ProofRule::TRUE_ELIM;
832 : 9001 : newConclusion = conclusion[1 - constIndex];
833 : : }
834 : : else
835 : : {
836 : 32775 : elimRule = ProofRule::FALSE_ELIM;
837 : 32775 : newConclusion = conclusion[1 - constIndex].notNode();
838 : : }
839 : : // We also check if the final conclusion t / (not t) has already been
840 : : // justified, so that we can avoid a cyclic proof, which can be due to
841 : : // either t / (not t) being assumption in the original EqProof or it having
842 : : // a non-assumption proof step in the proof of (= t true/false).
843 [ + + ][ + + ]: 41776 : if (!assumptions.count(newConclusion) && !p->hasStep(newConclusion))
[ + + ][ + + ]
[ - - ]
844 : : {
845 [ + - ]: 60052 : Trace("eqproof-conv")
846 : 0 : << "EqProof::addToProof: conclude " << newConclusion << " via "
847 : 30026 : << elimRule << " step for " << elimPremise << "\n";
848 : 60052 : p->addStep(newConclusion, elimRule, {elimPremise}, {});
849 : : }
850 : 41776 : }
851 : 780476 : return newConclusion;
852 : 390238 : }
853 : :
854 : 5571057 : Node EqProof::addToProof(CDProof* p,
855 : : std::unordered_map<Node, Node>& visited,
856 : : std::unordered_set<Node>& assumptions) const
857 : : {
858 : 5571057 : std::unordered_map<Node, Node>::const_iterator it = visited.find(d_node);
859 [ + + ]: 5571057 : if (it != visited.end())
860 : : {
861 [ + - ]: 2313956 : Trace("eqproof-conv") << "EqProof::addToProof: already processed " << d_node
862 : 1156978 : << ", returning " << it->second << "\n";
863 : 1156978 : return it->second;
864 : : }
865 [ + - ]: 8828158 : Trace("eqproof-conv") << "EqProof::addToProof: adding step for " << d_id
866 : 4414079 : << " with conclusion " << d_node << "\n";
867 : : // Assumption
868 [ + + ]: 4414079 : if (d_id == MERGED_THROUGH_EQUALITY)
869 : : {
870 : : // Check that no (= true/false true/false) assumptions
871 [ + - ][ + - ]: 2284369 : if (Configuration::isDebugBuild() && d_node.getKind() == Kind::EQUAL)
[ + - ]
872 : : {
873 [ + + ]: 6853107 : for (unsigned i = 0; i < 2; ++i)
874 : : {
875 : 9137476 : Assert(d_node[i].getKind() != Kind::CONST_BOOLEAN
876 : : || d_node[1 - i].getKind() != Kind::CONST_BOOLEAN)
877 : 0 : << "EqProof::addToProof: fully boolean constant assumption "
878 [ - + ][ - + ]: 4568738 : << d_node << " is disallowed\n";
[ - - ]
879 : : }
880 : : }
881 : : // If conclusion is (= t true/false), we add a proof step
882 : : // t
883 : : // ---------------- TRUE/FALSE_INTRO
884 : : // (= t true/false)
885 : : // according to the value of the Boolean constant
886 : 6853107 : if (d_node.getKind() == Kind::EQUAL
887 [ + - ][ + + ]: 6853107 : && ((d_node[0].getKind() == Kind::CONST_BOOLEAN)
[ - - ]
888 [ + + ][ + - ]: 4568738 : != (d_node[1].getKind() == Kind::CONST_BOOLEAN)))
[ + - ][ - - ]
889 : : {
890 [ + - ]: 117480 : Trace("eqproof-conv")
891 : 58740 : << "EqProof::addToProof: add an intro step for " << d_node << "\n";
892 : : // Index of constant in equality
893 : 58740 : unsigned constIndex = d_node[0].getKind() == Kind::CONST_BOOLEAN ? 0 : 1;
894 : : // The premise for the intro rule is either t or (not t), according to the
895 : : // Boolean constant.
896 : 58740 : Node introPremise;
897 : : ProofRule introRule;
898 [ + + ]: 58740 : if (d_node[constIndex].getConst<bool>())
899 : : {
900 : 14250 : introRule = ProofRule::TRUE_INTRO;
901 : 14250 : introPremise = d_node[1 - constIndex];
902 : : // Track the new assumption. If it's an equality, also its symmetric
903 : 14250 : assumptions.insert(introPremise);
904 [ - + ]: 14250 : if (introPremise.getKind() == Kind::EQUAL)
905 : : {
906 : 0 : assumptions.insert(introPremise[1].eqNode(introPremise[0]));
907 : : }
908 : : }
909 : : else
910 : : {
911 : 44490 : introRule = ProofRule::FALSE_INTRO;
912 : 44490 : introPremise = d_node[1 - constIndex].notNode();
913 : : // Track the new assumption. If it's a disequality, also its symmetric
914 : 44490 : assumptions.insert(introPremise);
915 [ + + ]: 44490 : if (introPremise[0].getKind() == Kind::EQUAL)
916 : : {
917 : 36880 : assumptions.insert(
918 : 73760 : introPremise[0][1].eqNode(introPremise[0][0]).notNode());
919 : : }
920 : : }
921 : : // The original assumption can be e.g. (= false (= t1 t2)) in which case
922 : : // the necessary proof to be built is
923 : : // (not (= t1 t2))
924 : : // -------------------- FALSE_INTRO
925 : : // (= (= t1 t2) false)
926 : : // -------------------- SYMM
927 : : // (= false (= t1 t2))
928 : : //
929 : : // with the SYMM step happening automatically whenever the assumption is
930 : : // used in the proof p
931 : : Node introConclusion =
932 : 103436 : constIndex == 1 ? d_node : d_node[1].eqNode(d_node[0]);
933 : 117480 : p->addStep(introConclusion, introRule, {introPremise}, {});
934 : 58740 : }
935 : : else
936 : : {
937 : 4451258 : p->addStep(d_node, ProofRule::ASSUME, {}, {d_node});
938 : : }
939 : : // If non-equality predicate, turn into one via TRUE/FALSE intro
940 : 2284369 : Node conclusion = d_node;
941 [ - + ]: 2284369 : if (d_node.getKind() != Kind::EQUAL)
942 : : {
943 : : // Track original assumption
944 : 0 : assumptions.insert(d_node);
945 : : ProofRule intro;
946 [ - - ]: 0 : if (d_node.getKind() == Kind::NOT)
947 : : {
948 : 0 : intro = ProofRule::FALSE_INTRO;
949 : : conclusion =
950 : 0 : d_node[0].eqNode(d_node.getNodeManager()->mkConst<bool>(false));
951 : : }
952 : : else
953 : : {
954 : 0 : intro = ProofRule::TRUE_INTRO;
955 : : conclusion =
956 : 0 : d_node.eqNode(d_node.getNodeManager()->mkConst<bool>(true));
957 : : }
958 [ - - ]: 0 : Trace("eqproof-conv") << "EqProof::addToProof: adding " << intro
959 : 0 : << " step for " << d_node << "\n";
960 : 0 : p->addStep(conclusion, intro, {d_node}, {});
961 : : }
962 : : // Keep track of assumptions to avoid cyclic proofs. Both the assumption and
963 : : // its symmetric are added
964 : 2284369 : assumptions.insert(conclusion);
965 : 2284369 : assumptions.insert(conclusion[1].eqNode(conclusion[0]));
966 [ + - ]: 4568738 : Trace("eqproof-conv") << "EqProof::addToProof: tracking assumptions "
967 [ - - ]: 2284369 : << conclusion << ", (= " << conclusion[1] << " "
968 [ - + ][ - + ]: 2284369 : << conclusion[0] << ")\n";
[ - - ]
969 : 2284369 : visited[d_node] = conclusion;
970 : 2284369 : return conclusion;
971 : 2284369 : }
972 : : // Refl and laborious congruence steps for (= (f t1 ... tn) (f t1 ... tn)),
973 : : // which can be safely turned into reflexivity steps. These laborious
974 : : // congruence steps are currently generated in the equality engine because of
975 : : // the suboptimal handling of n-ary operators.
976 : 4259420 : if (d_id == MERGED_THROUGH_REFLEXIVITY
977 : 2129710 : || (d_node.getKind() == Kind::EQUAL && d_node[0] == d_node[1]))
978 : : {
979 [ + - ]: 502917 : Trace("eqproof-conv") << "EqProof::addToProof: refl step\n";
980 : : Node conclusion =
981 [ + - ]: 502917 : d_node.getKind() == Kind::EQUAL ? d_node : d_node.eqNode(d_node);
982 : 1005834 : p->addStep(conclusion, ProofRule::REFL, {}, {conclusion[0]});
983 : 502917 : visited[d_node] = conclusion;
984 : 502917 : return conclusion;
985 : 502917 : }
986 : : // Equalities due to theory reasoning
987 [ + + ]: 1626793 : if (d_id == MERGED_THROUGH_CONSTANTS)
988 : : {
989 : 38770 : Assert(!d_node.isNull()
990 : : && ((d_node.getKind() == Kind::EQUAL && d_node[1].isConst())
991 : : || (d_node.getKind() == Kind::NOT
992 : : && d_node[0].getKind() == Kind::EQUAL
993 : : && d_node[0][0].isConst() && d_node[0][1].isConst())))
994 [ - + ][ - + ]: 19385 : << ". Conclusion " << d_node << " from " << d_id
[ - - ]
995 : 0 : << " was expected to be (= (f t1 ... tn) c) or (not (= c1 c2))\n";
996 [ - + ][ - - ]: 19385 : Assert(!assumptions.count(d_node))
997 [ - + ][ - + ]: 19385 : << "Conclusion " << d_node << " from " << d_id << " is an assumption\n";
[ - - ]
998 : : // The step has the form (not (= c1 c2)). We conclude it via
999 : : // MACRO_SR_PRED_INTRO and turn it into an equality with false, so that the
1000 : : // rest of the reconstruction works
1001 [ + + ]: 19385 : if (d_children.empty())
1002 : : {
1003 : : Node conclusion =
1004 : 592 : d_node[0].eqNode(d_node.getNodeManager()->mkConst<bool>(false));
1005 : 592 : p->addStep(d_node, ProofRule::MACRO_SR_PRED_INTRO, {}, {d_node});
1006 : 592 : p->addStep(conclusion, ProofRule::FALSE_INTRO, {d_node}, {});
1007 : 296 : visited[d_node] = conclusion;
1008 : 296 : return conclusion;
1009 : 296 : }
1010 : : // The step has the form
1011 : : // [(= t1 c1)] ... [(= tn cn)]
1012 : : // ------------------------
1013 : : // (= (f t1 ... tn) c)
1014 : : // where premises equating ti to constants are present when they are not
1015 : : // already constants. Note that the premises may be in any order, e.g. with
1016 : : // the equality for the second term being justified in the first premise.
1017 : : // Moreover, they may be of the form (= ci ti).
1018 : : //
1019 : : // First recursively process premises, if any
1020 : 19089 : std::vector<Node> premises;
1021 [ + + ]: 56307 : for (unsigned i = 0; i < d_children.size(); ++i)
1022 : : {
1023 [ + - ]: 74436 : Trace("eqproof-conv")
1024 : 0 : << "EqProof::addToProof: recurse on child " << i << "\n"
1025 : 37218 : << push;
1026 : 37218 : premises.push_back(d_children[i]->addToProof(p, visited, assumptions));
1027 [ + - ]: 37218 : Trace("eqproof-conv") << pop;
1028 : : }
1029 : : // After building the proper premises we could build a step like
1030 : : // [(= t1 c1)] ... [(= tn cn)]
1031 : : // ---------------------------- MACRO_SR_PRED_INTRO
1032 : : // (= (f t1 ... tn) c)
1033 : : // but note that since the substitution applied by MACRO_SR_PRED_INTRO is
1034 : : // *not* simultenous this could lead to issues if t_{i+1} occurred in some
1035 : : // t_{i}. So we build proofs as
1036 : : //
1037 : : // [(= t1 c1)] ... [(= tn cn)]
1038 : : // ------------------------------- CONG -------------- MACRO_SR_PRED_INTRO
1039 : : // (= (f t1 ... tn) (f c1 ... cn)) (= (f c1 ... cn) c)
1040 : : // ---------------------------------------------------------- TRANS
1041 : : // (= (f t1 ... tn) c)
1042 : 19089 : std::vector<Node> subChildren, constChildren;
1043 [ + + ]: 56307 : for (unsigned i = 0, size = d_node[0].getNumChildren(); i < size; ++i)
1044 : : {
1045 : 37218 : Node term = d_node[0][i];
1046 : : // term already is a constant, add a REFL step
1047 [ + + ]: 37218 : if (term.isConst())
1048 : : {
1049 : 12564 : subChildren.push_back(term.eqNode(term));
1050 : 25128 : p->addStep(subChildren.back(), ProofRule::REFL, {}, {term});
1051 : 12564 : constChildren.push_back(term);
1052 : 12564 : continue;
1053 : : }
1054 : : // Build the equality (= ti ci) as a premise, finding the respective ci is
1055 : : // the original premises
1056 : 24654 : Node constant;
1057 [ + - ]: 38337 : for (const Node& premise : premises)
1058 : : {
1059 [ - + ][ - + ]: 38337 : Assert(premise.getKind() == Kind::EQUAL);
[ - - ]
1060 [ + + ]: 38337 : if (premise[0] == term)
1061 : : {
1062 [ - + ][ - + ]: 5380 : Assert(premise[1].isConst());
[ - - ]
1063 : 5380 : constant = premise[1];
1064 : 5380 : break;
1065 : : }
1066 [ + + ]: 32957 : if (premise[1] == term)
1067 : : {
1068 [ - + ][ - + ]: 19274 : Assert(premise[0].isConst());
[ - - ]
1069 : 19274 : constant = premise[0];
1070 : 19274 : break;
1071 : : }
1072 : : }
1073 [ - + ][ - + ]: 24654 : Assert(!constant.isNull());
[ - - ]
1074 : 24654 : subChildren.push_back(term.eqNode(constant));
1075 : 24654 : constChildren.push_back(constant);
1076 [ + + ]: 37218 : }
1077 : : // build constant application (f c1 ... cn) and equality (= (f c1 ... cn) c)
1078 : 19089 : Kind k = d_node[0].getKind();
1079 : 19089 : std::vector<Node> cargs;
1080 : 19089 : ProofRule rule = expr::getCongRule(d_node[0], cargs);
1081 [ + + ]: 19089 : if (d_node[0].getMetaKind() == kind::metakind::PARAMETERIZED)
1082 : : {
1083 : 4 : constChildren.insert(constChildren.begin(), d_node[0].getOperator());
1084 : : }
1085 : 19089 : Node constApp = d_node.getNodeManager()->mkNode(k, constChildren);
1086 : 19089 : Node constEquality = constApp.eqNode(d_node[1]);
1087 [ + - ]: 38178 : Trace("eqproof-conv") << "EqProof::addToProof: adding "
1088 : 0 : << ProofRule::MACRO_SR_PRED_INTRO << " step for "
1089 [ - + ][ - - ]: 19089 : << constApp << " = " << d_node[1] << "\n";
1090 : 38178 : p->addStep(
1091 : : constEquality, ProofRule::MACRO_SR_PRED_INTRO, {}, {constEquality});
1092 : : // build congruence conclusion (= (f t1 ... tn) (f c1 ... cn))
1093 : 19089 : Node congConclusion = d_node[0].eqNode(constApp);
1094 [ + - ]: 38178 : Trace("eqproof-conv") << "EqProof::addToProof: adding " << rule
1095 : 0 : << " step for " << congConclusion << " from "
1096 : 19089 : << subChildren << "\n";
1097 : 19089 : p->addStep(congConclusion, rule, {subChildren}, cargs, true);
1098 [ + - ]: 38178 : Trace("eqproof-conv") << "EqProof::addToProof: adding " << ProofRule::TRANS
1099 : 19089 : << " step for original conclusion " << d_node << "\n";
1100 : 76356 : std::vector<Node> transitivityChildren{congConclusion, constEquality};
1101 : 19089 : p->addStep(d_node, ProofRule::TRANS, {transitivityChildren}, {});
1102 : 19089 : visited[d_node] = d_node;
1103 : 19089 : return d_node;
1104 : 19089 : }
1105 : : // Transtivity and disequality reasoning steps
1106 [ + + ]: 1607408 : if (d_id == MERGED_THROUGH_TRANS)
1107 : : {
1108 : 1421614 : Assert(d_node.getKind() == Kind::EQUAL
1109 : : || (d_node.getKind() == Kind::NOT
1110 : : && d_node[0].getKind() == Kind::EQUAL))
1111 [ - + ][ - + ]: 710807 : << "EqProof::addToProof: transitivity step conclusion " << d_node
[ - - ]
1112 : 0 : << " is not equality or negated equality\n";
1113 : : // If conclusion is (not (= t1 t2)) change it to (= (= t1 t2) false), which
1114 : : // is the correct conclusion of the equality reasoning step. A FALSE_ELIM
1115 : : // step to revert this is only necessary when this is the root. That step is
1116 : : // done in the non-recursive caller of this function.
1117 : : Node conclusion =
1118 : 710807 : d_node.getKind() != Kind::NOT
1119 : 705973 : ? d_node
1120 : 715641 : : d_node[0].eqNode(d_node.getNodeManager()->mkConst<bool>(false));
1121 : : // If the conclusion is an assumption, its derivation was spurious, so it
1122 : : // can be discarded. Moreover, reconstructing the step may lead to cyclic
1123 : : // proofs, so we *must* cut here.
1124 [ + + ]: 710807 : if (assumptions.count(conclusion))
1125 : : {
1126 : 2 : visited[d_node] = conclusion;
1127 : 2 : return conclusion;
1128 : : }
1129 : : // Process premises recursively
1130 : 710805 : std::vector<Node> children;
1131 [ + + ]: 3582685 : for (unsigned i = 0, size = d_children.size(); i < size; ++i)
1132 : : {
1133 : : // If one of the steps is a "fake congruence" one, marked by a null
1134 : : // conclusion, it must deleted. Its premises are moved down to premises of
1135 : : // the transitivity step.
1136 : 2871880 : EqProof* childProof = d_children[i].get();
1137 : 5743760 : if (childProof->d_id == MERGED_THROUGH_CONGRUENCE
1138 [ + + ][ + + ]: 2871880 : && childProof->d_node.isNull())
[ + + ]
1139 : : {
1140 [ + - ]: 20608 : Trace("eqproof-conv") << "EqProof::addToProof: child proof " << i
1141 : 10304 : << " is fake cong step. Fold it.\n";
1142 [ - + ][ - + ]: 10304 : Assert(childProof->d_children.size() == 2);
[ - - ]
1143 [ + - ]: 10304 : Trace("eqproof-conv") << push;
1144 [ + + ]: 30912 : for (unsigned j = 0, sizeJ = childProof->d_children.size(); j < sizeJ;
1145 : : ++j)
1146 : : {
1147 [ + - ]: 41216 : Trace("eqproof-conv")
1148 : 0 : << "EqProof::addToProof: recurse on child " << j << "\n"
1149 : 20608 : << push;
1150 : 20608 : children.push_back(
1151 : 41216 : childProof->d_children[j]->addToProof(p, visited, assumptions));
1152 [ + - ]: 20608 : Trace("eqproof-conv") << pop;
1153 : : }
1154 [ + - ]: 10304 : Trace("eqproof-conv") << pop;
1155 : 10304 : continue;
1156 : 10304 : }
1157 [ + - ]: 5723152 : Trace("eqproof-conv")
1158 : 0 : << "EqProof::addToProof: recurse on child " << i << "\n"
1159 : 2861576 : << push;
1160 : 2861576 : children.push_back(childProof->addToProof(p, visited, assumptions));
1161 [ + - ]: 2861576 : Trace("eqproof-conv") << pop;
1162 : : }
1163 : : // Eliminate spurious premises. Reasoning below assumes no refl steps.
1164 : 710805 : cleanReflPremises(children);
1165 : : // A recursive premise may have introduced the conclusion as an assumption
1166 : : // while reconstructing a nested congruence. In that case, deriving it here
1167 : : // would overwrite the assumption with a proof that depends on itself.
1168 [ + + ]: 710805 : if (assumptions.count(conclusion))
1169 : : {
1170 : 84 : visited[d_node] = conclusion;
1171 : 84 : return conclusion;
1172 : : }
1173 : : // If any premise is of the form (= (t1 t2) false), then the transitivity
1174 : : // step may be coarse-grained and needs to be expanded. If the expansion
1175 : : // happens it also finalizes the proof of conclusion.
1176 [ + + ]: 710721 : if (!expandTransitivityForDisequalities(
1177 : : conclusion, children, p, assumptions))
1178 : : {
1179 [ - + ][ - + ]: 675590 : Assert(!children.empty());
[ - - ]
1180 : : // similarly, if a disequality is concluded because of theory reasoning,
1181 : : // the step is coarse-grained and needs to be expanded, in which case the
1182 : : // proof is finalized in the call
1183 [ + + ]: 675590 : if (!expandTransitivityForTheoryDisequalities(conclusion, children, p))
1184 : : {
1185 [ + - ]: 1351150 : Trace("eqproof-conv")
1186 : 0 : << "EqProof::addToProof: build chain for transitivity premises"
1187 : 675575 : << children << " to conclude " << conclusion << "\n";
1188 : : // Build ordered transitivity chain from children to derive the
1189 : : // conclusion
1190 : 675575 : buildTransitivityChain(conclusion, children);
1191 : 675575 : Assert(
1192 : : children.size() > 1
1193 : : || (!children.empty()
1194 : : && (children[0] == conclusion
1195 : : || children[0][1].eqNode(children[0][0]) == conclusion)));
1196 : : // Only add transitivity step if there is more than one premise in the
1197 : : // chain. Otherwise the premise will be the conclusion itself and it'll
1198 : : // already have had a step added to it when the premises were
1199 : : // recursively processed.
1200 [ + + ]: 675575 : if (children.size() > 1)
1201 : : {
1202 : 675317 : p->addStep(conclusion, ProofRule::TRANS, children, {}, true);
1203 : : }
1204 : : }
1205 : : }
1206 : 1421442 : Assert(p->hasStep(conclusion) || assumptions.count(conclusion))
1207 [ - + ][ - + ]: 710721 : << "Conclusion " << conclusion
[ - - ]
1208 : 0 : << " does not have a step in the proof neither it's an assumption.\n";
1209 : 710721 : visited[d_node] = conclusion;
1210 : 710721 : return conclusion;
1211 : 710807 : }
1212 [ - + ][ - + ]: 896601 : Assert(d_id == MERGED_THROUGH_CONGRUENCE);
[ - - ]
1213 : : // The processing below is mainly dedicated to flattening congruence steps
1214 : : // (since EqProof assumes currying) and to prossibly reconstructing the
1215 : : // conclusion in case it involves n-ary steps.
1216 [ - + ][ - - ]: 896601 : Assert(d_node.getKind() == Kind::EQUAL)
1217 [ - + ][ - + ]: 896601 : << "EqProof::addToProof: conclusion " << d_node << " is not equality\n";
[ - - ]
1218 : : // The given conclusion is taken as ground truth. If the premises do not
1219 : : // align, for example with (= (f t1) (f t2)) but a premise being (= t2 t1), we
1220 : : // use (= t1 t2) as a premise and rely on a symmetry step to justify it.
1221 : 896601 : unsigned arity = d_node[0].getNumChildren();
1222 : 896601 : Kind k = d_node[0].getKind();
1223 : 896601 : bool isNary = NodeManager::isNAryKind(k);
1224 : :
1225 : : // N-ary operators are fun. The following proof is a valid EqProof
1226 : : //
1227 : : // (= (f t1 t2 t3) (f t6 t5)) (= (f t6 t5) (f t5 t6))
1228 : : // -------------------------------------------------- TRANS
1229 : : // (= (f t1 t2 t3) (f t5 t6)) (= t4 t7)
1230 : : // ------------------------------------------------------------ CONG
1231 : : // (= (f t1 t2 t3 t4) (f t5 t6 t7))
1232 : : //
1233 : : // We modify the above proof to conclude
1234 : : //
1235 : : // (= (f (f t1 t2 t3) t4) (f (f t5 t6) t7))
1236 : : //
1237 : : // which is a valid congruence conclusion (applications of f with the same
1238 : : // arity). For the processing below to be// performed correctly we update
1239 : : // arity to be maximal one among the two applications (4 in the above
1240 : : // example).
1241 [ + + ]: 896601 : if (d_node[0].getNumChildren() != d_node[1].getNumChildren())
1242 : : {
1243 [ - + ][ - + ]: 134 : Assert(isNary) << "We only handle congruences of apps with different "
[ - - ]
1244 : 0 : "number of children for theory n-ary operators";
1245 : 46 : arity =
1246 [ + + ][ + + ]: 180 : d_node[1].getNumChildren() < arity ? arity : d_node[1].getNumChildren();
[ - - ]
1247 [ + - ]: 268 : Trace("eqproof-conv")
1248 : 0 : << "EqProof::addToProof: Mismatching arities in cong conclusion "
1249 : 134 : << d_node << ". Use tentative arity " << arity << "\n";
1250 : : }
1251 : : // For a congruence proof of (= (f a0 ... an-1) (g b0 ... bn-1)), build a
1252 : : // transitivity matrix of n rows where the first row contains a transitivity
1253 : : // chain justifying (= f g) and the next rows (= ai bi)
1254 : 896601 : std::vector<std::vector<Node>> transitivityChildren;
1255 [ + + ]: 3373243 : for (unsigned i = 0; i < arity + 1; ++i)
1256 : : {
1257 : 2476642 : transitivityChildren.push_back(std::vector<Node>());
1258 : : }
1259 : 896601 : reduceNestedCongruence(
1260 : 896601 : arity, d_node, transitivityChildren, p, visited, assumptions, isNary);
1261 : : // The process above may inadvertently make d_node be found to be an
1262 : : // assumption of the proof. In which case the construction of the proof below
1263 : : // would add a cyclic proof. So we test for short-circuit here.
1264 [ + + ]: 896601 : if (assumptions.count(d_node))
1265 : : {
1266 : 2 : visited[d_node] = d_node;
1267 : 2 : return d_node;
1268 : : }
1269 : : // Congruences over n-ary operators may require changing the conclusion (as in
1270 : : // the above example). This is handled in a general manner below according to
1271 : : // whether the transitivity matrix computed by reduceNestedCongruence contains
1272 : : // empty rows
1273 : 896599 : Node conclusion = d_node;
1274 : 896599 : NodeManager* nm = conclusion.getNodeManager();
1275 [ + + ]: 896599 : if (isNary)
1276 : : {
1277 : 492698 : unsigned emptyRows = 0;
1278 [ + + ]: 1328586 : for (unsigned i = 1; i <= arity; ++i)
1279 : : {
1280 [ + + ]: 835888 : if (transitivityChildren[i].empty())
1281 : : {
1282 : 1160 : emptyRows++;
1283 : : }
1284 : : }
1285 : : // Given two n-ary applications f1:(f a0 ... an-1), f2:(f b0 ... bm-1), of
1286 : : // arities n and m, arity = max(n,m), the number emptyRows establishes the
1287 : : // sizes of the prefixes of f1 of f2 that have been equated via a
1288 : : // transitivity step. The prefixes necessarily have different sizes. The
1289 : : // suffixes have the same sizes. The new conclusion will be of the form
1290 : : // (= (f (f a0 ... ak1) ... an-1) (f (f b0 ... bk2) ... bm-1))
1291 : : // where
1292 : : // k1 = emptyRows + 1 - (arity - n)
1293 : : // k2 = emptyRows + 1 - (arity - m)
1294 : : // k1 != k2
1295 : : // n - k1 == m - k2
1296 : : // Note that by construction the equality between the first emptyRows + 1
1297 : : // arguments of each application is justified by the transitivity step in
1298 : : // the row emptyRows + 1 in the matrix.
1299 : : //
1300 : : // All of the above is with the very first row in the matrix, reserved for
1301 : : // justifying the equality between the functions, which is not necessary in
1302 : : // the n-ary case, notwithstanding.
1303 [ + + ]: 492698 : if (emptyRows > 0)
1304 : : {
1305 [ + - ]: 1736 : Trace("eqproof-conv")
1306 : 0 : << "EqProof::addToProof: Found " << emptyRows
1307 : 868 : << " empty rows. Rebuild conclusion " << d_node << "\n";
1308 : : // New transitivity matrix is as before except that the empty rows in the
1309 : : // beginning are eliminated, as the new arity is the maximal arity among
1310 : : // the applications minus the number of empty rows.
1311 : : std::vector<std::vector<Node>> newTransitivityChildren{
1312 : 868 : transitivityChildren.begin() + 1 + emptyRows,
1313 : 868 : transitivityChildren.end()};
1314 : 868 : transitivityChildren.clear();
1315 : 868 : transitivityChildren.push_back(std::vector<Node>());
1316 : 868 : transitivityChildren.insert(transitivityChildren.end(),
1317 : : newTransitivityChildren.begin(),
1318 : : newTransitivityChildren.end());
1319 : : unsigned arityPrefix1 =
1320 : 868 : emptyRows + 1 - (arity - d_node[0].getNumChildren());
1321 [ - + ][ - - ]: 1736 : Assert(arityPrefix1 < d_node[0].getNumChildren())
1322 : 0 : << "arityPrefix1 " << arityPrefix1 << " not smaller than "
1323 : 868 : << d_node[0] << "'s arity " << d_node[0].getNumChildren() << "\n";
1324 : : unsigned arityPrefix2 =
1325 : 868 : emptyRows + 1 - (arity - d_node[1].getNumChildren());
1326 [ - + ][ - - ]: 1736 : Assert(arityPrefix2 < d_node[1].getNumChildren())
1327 : 0 : << "arityPrefix2 " << arityPrefix2 << " not smaller than "
1328 : 868 : << d_node[1] << "'s arity " << d_node[1].getNumChildren() << "\n";
1329 [ + - ]: 1736 : Trace("eqproof-conv") << "EqProof::addToProof: New internal "
1330 : 0 : "applications with arities "
1331 : 868 : << arityPrefix1 << ", " << arityPrefix2 << ":\n";
1332 : 868 : std::vector<Node> childrenPrefix1{d_node[0].begin(),
1333 : 1736 : d_node[0].begin() + arityPrefix1};
1334 : 868 : std::vector<Node> childrenPrefix2{d_node[1].begin(),
1335 : 1736 : d_node[1].begin() + arityPrefix2};
1336 : 868 : Node newFirstChild1 = nm->mkNode(k, childrenPrefix1);
1337 : 868 : Node newFirstChild2 = nm->mkNode(k, childrenPrefix2);
1338 [ + - ]: 1736 : Trace("eqproof-conv")
1339 : 868 : << "EqProof::addToProof:\t " << newFirstChild1 << "\n";
1340 [ + - ]: 1736 : Trace("eqproof-conv")
1341 : 868 : << "EqProof::addToProof:\t " << newFirstChild2 << "\n";
1342 : 2604 : std::vector<Node> newChildren1{newFirstChild1};
1343 : 1736 : newChildren1.insert(newChildren1.end(),
1344 : 1736 : d_node[0].begin() + arityPrefix1,
1345 : : d_node[0].end());
1346 : 2604 : std::vector<Node> newChildren2{newFirstChild2};
1347 : 1736 : newChildren2.insert(newChildren2.end(),
1348 : 1736 : d_node[1].begin() + arityPrefix2,
1349 : : d_node[1].end());
1350 [ + + ][ - - ]: 4340 : conclusion = nm->mkNode(
1351 : : Kind::EQUAL,
1352 : 3472 : {nm->mkNode(k, newChildren1), nm->mkNode(k, newChildren2)});
1353 : : // update arity
1354 [ - + ][ - + ]: 868 : Assert((arity - emptyRows) == conclusion[0].getNumChildren());
[ - - ]
1355 : 868 : arity = arity - emptyRows;
1356 [ + - ]: 1736 : Trace("eqproof-conv")
1357 : 868 : << "EqProof::addToProof: New conclusion " << conclusion << "\n";
1358 : 868 : }
1359 : : }
1360 [ - + ]: 896599 : if (TraceIsOn("eqproof-conv"))
1361 : : {
1362 [ - - ]: 0 : Trace("eqproof-conv")
1363 : 0 : << "EqProof::addToProof: premises from reduced cong of " << conclusion
1364 : 0 : << ":\n";
1365 [ - - ]: 0 : for (unsigned i = 0; i <= arity; ++i)
1366 : : {
1367 [ - - ]: 0 : Trace("eqproof-conv") << "EqProof::addToProof:\t" << i
1368 : 0 : << "-th arg: " << transitivityChildren[i] << "\n";
1369 : : }
1370 : : }
1371 : 896599 : std::vector<Node> children(arity + 1);
1372 : : // Process transitivity matrix to (possibly) generate transitivity steps for
1373 : : // congruence premises (= ai bi)
1374 [ + + ]: 3372071 : for (unsigned i = 0; i <= arity; ++i)
1375 : : {
1376 : 2475472 : Node transConclusion;
1377 : : // We special case the operator case because there is only ever the need to
1378 : : // do something when in some HO case
1379 [ + + ]: 2475472 : if (i == 0)
1380 : : {
1381 : : // no justification for equality between functions, skip
1382 [ + + ]: 896599 : if (transitivityChildren[0].empty())
1383 : : {
1384 : 896254 : continue;
1385 : : }
1386 : : // HO case
1387 [ - + ][ - + ]: 345 : Assert(k == Kind::APPLY_UF) << "Congruence with different functions only "
[ - - ]
1388 : 0 : "allowed for uninterpreted functions.\n";
1389 : : transConclusion =
1390 : 345 : conclusion[0].getOperator().eqNode(conclusion[1].getOperator());
1391 : : }
1392 : : else
1393 : : {
1394 : 1578873 : transConclusion = conclusion[0][i - 1].eqNode(conclusion[1][i - 1]);
1395 : : }
1396 : 1579218 : children[i] = transConclusion;
1397 [ - + ][ - - ]: 1579218 : Assert(!transitivityChildren[i].empty())
1398 : 0 : << "EqProof::addToProof: did not add any justification for " << i
1399 [ - + ][ - + ]: 1579218 : << "-th arg of congruence " << conclusion << "\n";
[ - - ]
1400 : : // If the transitivity conclusion is a reflexivity step, just add it. Note
1401 : : // that this can happen even with the respective transitivityChildren row
1402 : : // containing several equalities in the case of (= ai bi) being the same
1403 : : // n-ary application that was justified by a congruence step, which can
1404 : : // happen in the current equality engine.
1405 [ + + ]: 1579218 : if (transConclusion[0] == transConclusion[1])
1406 : : {
1407 : 1070426 : p->addStep(transConclusion, ProofRule::REFL, {}, {transConclusion[0]});
1408 : 535213 : continue;
1409 : : }
1410 : : // Remove spurious refl steps from the premises for (= ai bi)
1411 : 1044005 : cleanReflPremises(transitivityChildren[i]);
1412 : 2088010 : Assert(transitivityChildren[i].size() > 1 || transitivityChildren[i].empty()
1413 : : || CDProof::isSame(transitivityChildren[i][0], transConclusion))
1414 : 0 : << "EqProof::addToProof: premises " << transitivityChildren[i] << "for "
1415 [ - + ][ - + ]: 1044005 : << i << "-th cong premise " << transConclusion << " don't justify it\n";
[ - - ]
1416 : 1044005 : unsigned sizeTrans = transitivityChildren[i].size();
1417 : : // If no transitivity premise left or if (= ai bi) is already present in
1418 : : // the local proof, nothing else to do. Re-deriving it can create a cyclic
1419 : : // proof when a congruence premise reuses the same fact through
1420 : : // symmetry/rewriting.
1421 [ + + ]: 1044005 : if (sizeTrans == 0 || assumptions.count(transConclusion) > 0
1422 [ + - ][ + + ]: 2088010 : || p->hasFact(transConclusion))
[ + + ][ + + ]
[ - - ]
1423 : : {
1424 : 1024358 : continue;
1425 : : }
1426 : : // If the transitivity conclusion, or its symmetric, occurs in the
1427 : : // transitivity premises, nothing to do, as it is already justified and
1428 : : // doing so again would lead to a cycle.
1429 : 19647 : bool occurs = false;
1430 [ + + ][ + - ]: 61548 : for (unsigned j = 0; j < sizeTrans && !occurs; ++j)
1431 : : {
1432 [ - + ]: 41901 : if (CDProof::isSame(transitivityChildren[i][j], transConclusion))
1433 : : {
1434 : 0 : occurs = true;
1435 : : }
1436 : : }
1437 [ + - ]: 19647 : if (!occurs)
1438 : : {
1439 : : // Build transitivity step
1440 : 19647 : buildTransitivityChain(transConclusion, transitivityChildren[i]);
1441 [ + - ]: 39294 : Trace("eqproof-conv")
1442 : 0 : << "EqProof::addToProof: adding trans step for cong premise "
1443 : 0 : << transConclusion << " with children " << transitivityChildren[i]
1444 : 19647 : << "\n";
1445 : 39294 : p->addStep(
1446 : 19647 : transConclusion, ProofRule::TRANS, transitivityChildren[i], {}, true);
1447 : : }
1448 [ + + ]: 2475472 : }
1449 : : // first-order case
1450 [ + + ]: 896599 : if (children[0].isNull())
1451 : : {
1452 : : // remove placehold for function equality case
1453 : 896254 : children.erase(children.begin());
1454 : : // Get node of the function operator over which congruence is being
1455 : : // applied.
1456 : 896254 : std::vector<Node> args;
1457 : 896254 : ProofRule r = expr::getCongRule(conclusion[0], args);
1458 : : // Add congruence step
1459 [ - + ]: 896254 : if (TraceIsOn("eqproof-conv"))
1460 : : {
1461 [ - - ]: 0 : Trace("eqproof-conv")
1462 : 0 : << "EqProof::addToProof: build cong step of " << conclusion;
1463 [ - - ]: 0 : if (!args.empty())
1464 : : {
1465 [ - - ]: 0 : Trace("eqproof-conv") << " with op " << args[0];
1466 : : }
1467 [ - - ]: 0 : Trace("eqproof-conv") << " and children " << children << "\n";
1468 : : }
1469 : 896254 : p->addStep(conclusion, r, children, args, true);
1470 : 896254 : }
1471 : : // higher-order case
1472 : : else
1473 : : {
1474 : : // Add congruence step
1475 [ + - ]: 690 : Trace("eqproof-conv") << "EqProof::addToProof: build HO-cong step of "
1476 : 0 : << conclusion << " with children " << children
1477 : 345 : << "\n";
1478 : 690 : p->addStep(conclusion,
1479 : : ProofRule::HO_CONG,
1480 : : children,
1481 : : {ProofRuleChecker::mkKindNode(nm, Kind::APPLY_UF)},
1482 : : true);
1483 : : }
1484 : : // If the conclusion of the congruence step changed due to the n-ary handling,
1485 : : // we obtained for example (= (f (f t1 t2 t3) t4) (f (f t5 t6) t7)), which is
1486 : : // flattened into the original conclusion (= (f t1 t2 t3 t4) (f t5 t6 t7)) via
1487 : : // rewriting
1488 [ + + ]: 896599 : if (!CDProof::isSame(conclusion, d_node))
1489 : : {
1490 [ + - ]: 1736 : Trace("eqproof-conv") << "EqProof::addToProof: try to flatten via a "
1491 : 868 : << ProofRule::MACRO_SR_PRED_TRANSFORM
1492 : 0 : << " step the rebuilt conclusion " << conclusion
1493 : 868 : << " into " << d_node << "\n";
1494 : 1736 : Node res = p->getManager()->getChecker()->checkDebug(
1495 : : ProofRule::MACRO_SR_PRED_TRANSFORM,
1496 : : {conclusion},
1497 : 868 : {d_node},
1498 : 0 : Node::null(),
1499 : 3472 : "eqproof-conv");
1500 : : // If rewriting was not able to flatten the rebuilt conclusion into the
1501 : : // original one, we give up and use a TRUST_FLATTENING_REWRITE step,
1502 : : // generating a proof for the original conclusion d_node such as
1503 : : //
1504 : : // Converted EqProof
1505 : : // ---------------------- ------------------- TRUST_FLATTENING_REWRITE
1506 : : // conclusion conclusion = d_node
1507 : : // ------------------------------------------------------- EQ_RESOLVE
1508 : : // d_node
1509 : : //
1510 : : //
1511 : : // If rewriting was able to do it, however, we just add the macro step.
1512 [ + + ]: 868 : if (res.isNull())
1513 : : {
1514 [ + - ]: 24 : Trace("eqproof-conv")
1515 : 12 : << "EqProof::addToProof: adding a trust flattening rewrite step\n";
1516 : 12 : Node bridgeEq = conclusion.eqNode(d_node);
1517 : 12 : p->addTrustedStep(bridgeEq, TrustId::FLATTENING_REWRITE, {}, {});
1518 [ + + ][ - - ]: 36 : p->addStep(d_node, ProofRule::EQ_RESOLVE, {conclusion, bridgeEq}, {});
1519 : 12 : }
1520 : : else
1521 : : {
1522 : 2568 : p->addStep(d_node,
1523 : : ProofRule::MACRO_SR_PRED_TRANSFORM,
1524 : : {conclusion},
1525 : 856 : {d_node},
1526 : : true);
1527 : : }
1528 : 868 : }
1529 : 896599 : visited[d_node] = d_node;
1530 : 896599 : return d_node;
1531 : 896601 : }
1532 : :
1533 : : } // namespace eq
1534 : : } // Namespace theory
1535 : : } // namespace cvc5::internal
|