GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/uf/eq_proof.cpp Lines: 627 725 86.5 %
Date: 2021-11-05 Branches: 1506 3772 39.9 %

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