GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/uf/eq_proof.cpp Lines: 618 717 86.2 %
Date: 2021-05-22 Branches: 1490 3784 39.4 %

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