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