GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/uf/eq_proof.cpp Lines: 587 721 81.4 %
Date: 2021-08-06 Branches: 1401 3766 37.2 %

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
400454
void EqProof::cleanReflPremises(std::vector<Node>& premises) const
80
{
81
800908
  std::vector<Node> newPremises;
82
400454
  unsigned size = premises.size();
83
1360263
  for (unsigned i = 0; i < size; ++i)
84
  {
85
959809
    if (premises[i][0] == premises[i][1])
86
    {
87
197190
      continue;
88
    }
89
762619
    newPremises.push_back(premises[i]);
90
  }
91
400454
  if (newPremises.size() != size)
92
  {
93
170548
    Trace("eqproof-conv") << "EqProof::cleanReflPremises: removed "
94
85274
                          << (newPremises.size() >= size
95
170548
                                  ? newPremises.size() - size
96
85274
                                  : 0)
97
85274
                          << " refl premises from " << premises << "\n";
98
85274
    premises.clear();
99
85274
    premises.insert(premises.end(), newPremises.begin(), newPremises.end());
100
170548
    Trace("eqproof-conv") << "EqProof::cleanReflPremises: new premises "
101
85274
                          << premises << "\n";
102
  }
103
400454
}
104
105
144466
bool EqProof::expandTransitivityForDisequalities(
106
    Node conclusion,
107
    std::vector<Node>& premises,
108
    CDProof* p,
109
    std::unordered_set<Node>& assumptions) const
110
{
111
288932
  Trace("eqproof-conv")
112
      << "EqProof::expandTransitivityForDisequalities: check if need "
113
144466
         "to expand transitivity step to conclude "
114
144466
      << conclusion << " from premises " << premises << "\n";
115
  // Check premises to see if any of the form (= (= t1 t2) false), modulo
116
  // symmetry
117
144466
  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
144466
  unsigned termPos = 2, offending = size;
121
632171
  for (unsigned i = 0; i < size; ++i)
122
  {
123
487705
    Assert(premises[i].getKind() == kind::EQUAL);
124
1456365
    for (unsigned j = 0; j < 2; ++j)
125
    {
126
2917899
      if (premises[i][j].getKind() == kind::CONST_BOOLEAN
127
981961
          && !premises[i][j].getConst<bool>()
128
2925166
          && premises[i][1 - j].getKind() == kind::EQUAL)
129
      {
130
        // there is only one offending equality
131
3973
        Assert(offending == size);
132
3973
        offending = i;
133
3973
        termPos = 1 - j;
134
3973
        break;
135
      }
136
    }
137
  }
138
  // if no equality of the searched form, nothing to do
139
144466
  if (offending == size)
140
  {
141
140493
    return false;
142
  }
143
3973
  NodeManager* nm = NodeManager::currentNM();
144
3973
  Assert(termPos == 0 || termPos == 1);
145
7946
  Trace("eqproof-conv") << "EqProof::expandTransitivityForDisequalities: found "
146
3973
                           "offending equality at index "
147
3973
                        << offending << " : " << premises[offending] << "\n";
148
  // collect the premises to be used in the expansion, which are all but the
149
  // offending one
150
7946
  std::vector<Node> expansionPremises;
151
14684
  for (unsigned i = 0; i < size; ++i)
152
  {
153
10711
    if (i != offending)
154
    {
155
6738
      expansionPremises.push_back(premises[i]);
156
    }
157
  }
158
  // Eliminate spurious premises. Reasoning below assumes no refl steps.
159
3973
  cleanReflPremises(expansionPremises);
160
3973
  Assert(!expansionPremises.empty());
161
  // Check if we are in the substitution case
162
7946
  Node expansionConclusion;
163
7946
  std::vector<Node> substPremises;
164
3973
  bool inSubstCase = false, substConclusionInReverseOrder = false;
165
7946
  if ((conclusion[0].getKind() == kind::CONST_BOOLEAN)
166
3973
      != (conclusion[1].getKind() == kind::CONST_BOOLEAN))
167
  {
168
2879
    inSubstCase = true;
169
    // reorder offending premise if constant is the first argument
170
2879
    if (termPos == 1)
171
    {
172
2678
      premises[offending] =
173
5356
          premises[offending][1].eqNode(premises[offending][0]);
174
    }
175
    // reorder conclusion if constant is the first argument
176
8637
    conclusion = conclusion[1].getKind() == kind::CONST_BOOLEAN
177
5758
                     ? conclusion
178
                     : conclusion[1].eqNode(conclusion[0]);
179
    // equality term in premise disequality
180
5758
    Node premiseTermEq = premises[offending][0];
181
    // equality term in conclusion disequality
182
5758
    Node conclusionTermEq = conclusion[0];
183
5758
    Trace("eqproof-conv")
184
        << "EqProof::expandTransitivityForDisequalities: Substitition "
185
2879
           "case. Need to build subst from "
186
2879
        << 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
5758
    std::vector<Node> subs[2];
192
2879
    subs[0].push_back(premiseTermEq[0]);
193
2879
    subs[1].push_back(premiseTermEq[1]);
194
    // which of the arguments of premiseTermEq, if any, is equal to one argument
195
    // of conclusionTermEq
196
2879
    int equalArg = -1;
197
8637
    for (unsigned i = 0; i < 2; ++i)
198
    {
199
13451
      for (unsigned j = 0; j < 2; ++j)
200
      {
201
10250
        if (premiseTermEq[i] == conclusionTermEq[j])
202
        {
203
2557
          equalArg = i;
204
          // identity sub
205
2557
          subs[i].push_back(conclusionTermEq[j]);
206
          // sub that changes argument
207
2557
          subs[1 - i].push_back(conclusionTermEq[1 - j]);
208
          // wither e.g. (= t1 t2), with sub t1->t3, becomes (= t2 t3)
209
2557
          substConclusionInReverseOrder = i != j;
210
2557
          break;
211
        }
212
      }
213
    }
214
    // simple case of single substitution
215
2879
    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
2557
      expansionConclusion = subs[1 - equalArg][0].eqNode(subs[1 - equalArg][1]);
229
5114
      Trace("eqproof-conv") << "EqProof::expandTransitivityForDisequalities: "
230
2557
                               "Need to expand premises into "
231
2557
                            << expansionConclusion << "\n";
232
      // add refl step for the substitition t2->t2
233
5114
      p->addStep(subs[equalArg][0].eqNode(subs[equalArg][1]),
234
                 PfRule::REFL,
235
                 {},
236
5114
                 {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
644
      Trace("eqproof-conv") << "EqProof::expandTransitivityForDisequalities: "
243
322
                               "Need two substitutions. Look for "
244
644
                            << premiseTermEq[0] << " and " << premiseTermEq[1]
245
322
                            << " in premises " << expansionPremises << "\n";
246
644
      Assert(expansionPremises.size() >= 2)
247
          << "Less than 2 expansion premises for substituting BOTH terms in "
248
             "disequality.\nDisequality: "
249
322
          << premises[offending]
250
          << "\nExpansion premises: " << expansionPremises
251
322
          << "\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
322
      if (expansionPremises.size() == 2)
256
      {
257
        // iterate over args to be substituted
258
762
        for (unsigned i = 0; i < 2; ++i)
259
        {
260
          // iterate over premises
261
1524
          for (unsigned j = 0; j < 2; ++j)
262
          {
263
            // iterate over args in premise
264
2318
            for (unsigned k = 0; k < 2; ++k)
265
            {
266
1810
              if (premiseTermEq[i] == expansionPremises[j][k])
267
              {
268
508
                subs[i].push_back(expansionPremises[j][1 - k]);
269
508
                break;
270
              }
271
            }
272
          }
273
1016
          Assert(subs[i].size() == 2)
274
508
              << " 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
508
          substConclusionInReverseOrder =
278
1016
              premiseTermEq[i] != conclusionTermEq[i];
279
        }
280
      }
281
      else
282
      {
283
136
        Trace("eqproof-conv") << "EqProof::expandTransitivityForDisequalities: "
284
                                 "Build transitivity chains "
285
68
                                 "for two subs among more than 2 premises: "
286
68
                              << 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
68
        subs[0].push_back(conclusionTermEq[0]);
292
68
        subs[1].push_back(conclusionTermEq[1]);
293
204
        for (unsigned i = 0; i < 2; ++i)
294
        {
295
          // copy premises, since buildTransitivityChain is destructive
296
          std::vector<Node> copy1ofExpPremises(expansionPremises.begin(),
297
272
                                               expansionPremises.end());
298
272
          Node transConclusion1 = subs[0][0].eqNode(subs[0][1]);
299
136
          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
272
                                               expansionPremises.end());
316
272
          Node transConclusion2 = subs[1][0].eqNode(subs[1][1]);
317
136
          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
272
          Trace("eqproof-conv")
326
136
              << "EqProof::expandTransitivityForDisequalities: Built trans "
327
                 "chains: "
328
                 "for two subs among more than 2 premises:\n";
329
272
          Trace("eqproof-conv")
330
136
              << "EqProof::expandTransitivityForDisequalities: "
331
136
              << transConclusion1 << " <- " << copy1ofExpPremises << "\n";
332
272
          Trace("eqproof-conv")
333
136
              << "EqProof::expandTransitivityForDisequalities: "
334
136
              << transConclusion2 << " <- " << copy2ofExpPremises << "\n";
335
          // do transitivity steps if need be to justify each substitution
336
272
          if (copy1ofExpPremises.size() > 1
337
136
              && !assumptions.count(transConclusion1))
338
          {
339
58
            p->addStep(
340
                transConclusion1, PfRule::TRANS, copy1ofExpPremises, {}, true);
341
          }
342
272
          if (copy2ofExpPremises.size() > 1
343
136
              && !assumptions.count(transConclusion2))
344
          {
345
78
            p->addStep(
346
                transConclusion2, PfRule::TRANS, copy2ofExpPremises, {}, true);
347
          }
348
        }
349
      }
350
    }
351
5758
    Trace("eqproof-conv")
352
2879
        << "EqProof::expandTransitivityForDisequalities: Built substutitions "
353
2879
        << subs[0] << " and " << subs[1] << " to map " << premiseTermEq
354
2879
        << " -> " << conclusionTermEq << "\n";
355
5758
    Assert(subs[0][1] == conclusion[0][0] || subs[0][1] == conclusion[0][1])
356
        << "EqProof::expandTransitivityForDisequalities: First substitution "
357
2879
        << subs[0] << " doest not map to conclusion " << conclusion << "\n";
358
5758
    Assert(subs[1][1] == conclusion[0][0] || subs[1][1] == conclusion[0][1])
359
        << "EqProof::expandTransitivityForDisequalities: Second substitution "
360
2879
        << 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
2879
    substPremises.push_back(subs[0][1].eqNode(subs[0][0]));
369
2879
    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
1094
    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
1094
    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
3973
  if (!expansionConclusion.isNull())
395
  {
396
7302
    Trace("eqproof-conv")
397
3651
        << "EqProof::expandTransitivityForDisequalities: need to derive "
398
3651
        << expansionConclusion << " with premises " << expansionPremises
399
3651
        << "\n";
400
7302
    Assert(expansionPremises.size() > 1
401
           || expansionConclusion == expansionPremises.back()
402
           || (expansionConclusion[0] == expansionPremises.back()[1]
403
               && expansionConclusion[1] == expansionPremises.back()[0]))
404
3651
        << "single expansion premise " << expansionPremises.back()
405
3651
        << " 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
3651
    if (expansionPremises.size() > 1 && !assumptions.count(expansionConclusion))
424
    {
425
      // create transitivity step to derive expected premise
426
1009
      buildTransitivityChain(expansionConclusion, expansionPremises);
427
2018
      Trace("eqproof-conv")
428
          << "EqProof::expandTransitivityForDisequalities: add transitivity "
429
1009
             "step for "
430
1009
          << expansionConclusion << " with premises " << expansionPremises
431
1009
          << "\n";
432
      // create expansion step
433
1009
      p->addStep(
434
          expansionConclusion, PfRule::TRANS, expansionPremises, {}, true);
435
    }
436
  }
437
7946
  Trace("eqproof-conv")
438
3973
      << "EqProof::expandTransitivityForDisequalities: now derive conclusion "
439
3973
      << conclusion;
440
3973
  premises.clear();
441
3973
  premises.push_back(premises[offending]);
442
3973
  if (inSubstCase)
443
  {
444
5758
    Trace("eqproof-conv") << (substConclusionInReverseOrder ? " [inverted]"
445
2879
                                                            : "")
446
2879
                          << " via subsitution from " << premises[offending]
447
2879
                          << " 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
5758
        nm->mkNode(kind::EQUAL, substPremises[0][0], substPremises[1][0]),
469
11516
        premises[offending][0]);
470
5758
    p->addStep(congConclusion,
471
               PfRule::CONG,
472
               substPremises,
473
               {ProofRuleChecker::mkKindNode(kind::EQUAL)},
474
2879
               true);
475
5758
    Trace("eqproof-conv") << "EqProof::expandTransitivityForDisequalities: via "
476
2879
                             "congruence derived "
477
2879
                          << congConclusion << "\n";
478
    // transitivity step between that and the original premise
479
2879
    premises.insert(premises.begin(), congConclusion);
480
    Node transConclusion =
481
2879
        !substConclusionInReverseOrder
482
            ? conclusion
483
5758
            : nm->mkNode(kind::EQUAL, congConclusion[0], conclusion[1]);
484
    // check to avoid cyclic proofs
485
2879
    if (!assumptions.count(transConclusion))
486
    {
487
2879
      p->addStep(transConclusion, PfRule::TRANS, premises, {}, true);
488
5758
      Trace("eqproof-conv") << "EqProof::expandTransitivityForDisequalities: "
489
2879
                               "via transitivity derived "
490
2879
                            << 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
2879
    if (substConclusionInReverseOrder)
497
    {
498
972
      p->addStep(conclusion,
499
                 PfRule::MACRO_SR_PRED_TRANSFORM,
500
                 {transConclusion},
501
                 {conclusion},
502
648
                 true);
503
648
      Trace("eqproof-conv") << "EqProof::expandTransitivityForDisequalities: "
504
324
                               "via macro transform derived "
505
324
                            << conclusion << "\n";
506
    }
507
  }
508
  else
509
  {
510
    // create TRUE_INTRO step for expansionConclusion and add it to the premises
511
2188
    Trace("eqproof-conv")
512
        << " via transitivity.\nEqProof::expandTransitivityForDisequalities: "
513
1094
           "adding "
514
1094
        << PfRule::TRUE_INTRO << " step for " << expansionConclusion[0] << "\n";
515
    Node newExpansionConclusion =
516
2188
        expansionConclusion.eqNode(nm->mkConst<bool>(true));
517
2188
    p->addStep(
518
1094
        newExpansionConclusion, PfRule::TRUE_INTRO, {expansionConclusion}, {});
519
1094
    premises.push_back(newExpansionConclusion);
520
1094
    Trace("eqproof-conv") << PfRule::TRANS << " from " << premises << "\n";
521
1094
    buildTransitivityChain(conclusion, premises);
522
    // create final transitivity step
523
1094
    p->addStep(conclusion, PfRule::TRANS, premises, {}, true);
524
  }
525
3973
  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
140493
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
140493
  unsigned termPos = -1;
536
421479
  for (unsigned i = 0; i < 2; ++i)
537
  {
538
842958
    if (conclusion[i].getKind() == kind::CONST_BOOLEAN
539
285247
        && !conclusion[i].getConst<bool>()
540
845698
        && conclusion[1 - i].getKind() == kind::EQUAL)
541
    {
542
      termPos = i - 1;
543
      break;
544
    }
545
  }
546
  // no disequality
547
140493
  if (termPos == static_cast<unsigned>(-1))
548
  {
549
140493
    return false;
550
  }
551
  Trace("eqproof-conv")
552
      << "EqProof::expandTransitivityForTheoryDisequalities: check if need "
553
         "to expand transitivity step to conclude disequality "
554
      << conclusion << " from premises " << premises << "\n";
555
556
  // Check if the premises are (= t1 c1) and (= t2 c2), modulo symmetry
557
  std::vector<Node> subChildren, constChildren;
558
  for (unsigned i = 0; i < 2; ++i)
559
  {
560
    Node term = conclusion[termPos][i];
561
    for (const Node& premise : premises)
562
    {
563
      for (unsigned j = 0; j < 2; ++j)
564
      {
565
        if (premise[j] == term && premise[1 - j].isConst())
566
        {
567
          subChildren.push_back(premise[j].eqNode(premise[1 - j]));
568
          constChildren.push_back(premise[1 - j]);
569
          break;
570
        }
571
      }
572
    }
573
  }
574
  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
  Node constApp = NodeManager::currentNM()->mkNode(kind::EQUAL, constChildren);
585
  Node constEquality = constApp.eqNode(conclusion[1 - termPos]);
586
  Trace("eqproof-conv")
587
      << "EqProof::expandTransitivityForTheoryDisequalities: adding "
588
      << PfRule::MACRO_SR_PRED_INTRO << " step for " << constApp << " = "
589
      << conclusion[1 - termPos] << "\n";
590
  p->addStep(constEquality, PfRule::MACRO_SR_PRED_INTRO, {}, {constEquality});
591
  // build congruence conclusion (= (= t1 t2) (t c1 c2))
592
  Node congConclusion = conclusion[termPos].eqNode(constApp);
593
  Trace("eqproof-conv")
594
      << "EqProof::expandTransitivityForTheoryDisequalities: adding  "
595
      << PfRule::CONG << " step for " << congConclusion << " from "
596
      << subChildren << "\n";
597
  p->addStep(congConclusion,
598
             PfRule::CONG,
599
             {subChildren},
600
             {ProofRuleChecker::mkKindNode(kind::EQUAL)},
601
             true);
602
  Trace("eqproof-conv") << "EqProof::expandTransitivityForDisequalities: via "
603
                           "congruence derived "
604
                        << congConclusion << "\n";
605
  std::vector<Node> transitivityChildren{congConclusion, constEquality};
606
  p->addStep(conclusion, PfRule::TRANS, {transitivityChildren}, {});
607
  return true;
608
}
609
610
506437
bool EqProof::buildTransitivityChain(Node conclusion,
611
                                     std::vector<Node>& premises) const
612
{
613
1012874
  Trace("eqproof-conv") << push
614
506437
                        << "EqProof::buildTransitivityChain: Build chain for "
615
506437
                        << conclusion << " with premises " << premises << "\n";
616
1589817
  for (unsigned i = 0, size = premises.size(); i < size; ++i)
617
  {
618
1589817
    bool occurs = false, correctlyOrdered = false;
619
1589817
    if (conclusion[0] == premises[i][0])
620
    {
621
237510
      occurs = correctlyOrdered = true;
622
    }
623
1352307
    else if (conclusion[0] == premises[i][1])
624
    {
625
268927
      occurs = true;
626
    }
627
1589817
    if (occurs)
628
    {
629
1012874
      Trace("eqproof-conv")
630
1012874
          << "EqProof::buildTransitivityChain: found " << conclusion[0] << " in"
631
1012874
          << (correctlyOrdered ? "" : " non-") << " ordered premise "
632
506437
          << premises[i] << "\n";
633
506437
      if (conclusion[1] == premises[i][correctlyOrdered ? 1 : 0])
634
      {
635
308884
        Trace("eqproof-conv")
636
308884
            << "EqProof::buildTransitivityChain: found " << conclusion[1]
637
154442
            << " in same premise. Closed chain.\n"
638
154442
            << pop;
639
154442
        premises.clear();
640
154442
        premises.push_back(conclusion);
641
660879
        return true;
642
      }
643
      // Build chain with remaining equalities
644
351995
      std::vector<Node> recursivePremises;
645
1790568
      for (unsigned j = 0; j < size; ++j)
646
      {
647
1438573
        if (j != i)
648
        {
649
1086578
          recursivePremises.push_back(premises[j]);
650
        }
651
      }
652
      Node newTarget =
653
351995
          premises[i][correctlyOrdered ? 1 : 0].eqNode(conclusion[1]);
654
703990
      Trace("eqproof-conv")
655
351995
          << "EqProof::buildTransitivityChain: search recursively for "
656
351995
          << newTarget << "\n";
657
351995
      if (buildTransitivityChain(newTarget, recursivePremises))
658
      {
659
703990
        Trace("eqproof-conv")
660
351995
            << "EqProof::buildTransitivityChain: closed chain with "
661
703990
            << 1 + recursivePremises.size() << " of the original "
662
703990
            << premises.size() << " premises\n"
663
351995
            << pop;
664
351995
        premises.clear();
665
1055985
        premises.insert(premises.begin(),
666
                        correctlyOrdered
667
1080076
                            ? premises[i]
668
1080076
                            : premises[i][1].eqNode(premises[i][0]));
669
351995
        premises.insert(
670
703990
            premises.end(), recursivePremises.begin(), recursivePremises.end());
671
351995
        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
757514
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
1515028
  Trace("eqproof-conv") << "EqProof::reduceNestedCongruence: building for " << i
692
757514
                        << "-th arg\n";
693
757514
  if (d_id == MERGED_THROUGH_CONGRUENCE)
694
  {
695
620429
    Assert(d_children.size() == 2);
696
1240858
    Trace("eqproof-conv") << "EqProof::reduceNestedCongruence: it's a "
697
620429
                             "congruence step. Reduce second child\n"
698
620429
                          << push;
699
1240858
    transitivityMatrix[i].push_back(
700
1240858
        d_children[1]->addToProof(p, visited, assumptions));
701
1240858
    Trace("eqproof-conv")
702
620429
        << pop << "EqProof::reduceNestedCongruence: child conclusion "
703
620429
        << 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
620429
    Assert(i > 0 || d_children[0]->d_id == MERGED_THROUGH_REFLEXIVITY
707
           || options::ufHo());
708
    // recurse
709
620429
    if (i > 1)
710
    {
711
309262
      Trace("eqproof-conv")
712
154631
          << "EqProof::reduceNestedCongruence: Reduce first child\n"
713
154631
          << push;
714
154631
      d_children[0]->reduceNestedCongruence(i - 1,
715
                                            conclusion,
716
                                            transitivityMatrix,
717
                                            p,
718
                                            visited,
719
                                            assumptions,
720
                                            isNary);
721
154631
      Trace("eqproof-conv") << pop;
722
    }
723
    // higher-order case
724
465798
    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
620429
    return;
735
  }
736
137085
  Assert(d_id == MERGED_THROUGH_TRANS)
737
      << "id is " << static_cast<MergeReasonType>(d_id) << "\n";
738
137085
  Trace("eqproof-conv") << "EqProof::reduceNestedCongruence: it's a "
739
                           "transitivity step.\n";
740
137085
  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
137085
  if (isNary && !d_node.isNull())
750
  {
751
28
    Trace("eqproof-conv") << "EqProof::reduceNestedCongruence: n-ary case, "
752
14
                             "break recursion and indepedently process "
753
14
                          << d_node << "\n"
754
14
                          << push;
755
14
    transitivityMatrix[i].push_back(addToProof(p, visited, assumptions));
756
28
    Trace("eqproof-conv") << pop
757
14
                          << "EqProof::reduceNestedCongruence: Got conclusion "
758
14
                          << transitivityMatrix[i].back()
759
14
                          << " from n-ary transitivity processing\n";
760
14
    return;
761
  }
762
  // Regular recursive processing of each transitivity premise
763
537118
  for (unsigned j = 0, sizeTrans = d_children.size(); j < sizeTrans; ++j)
764
  {
765
400047
    if (d_children[j]->d_id == MERGED_THROUGH_CONGRUENCE)
766
    {
767
800094
      Trace("eqproof-conv") << "EqProof::reduceNestedCongruence: Reduce " << j
768
400047
                            << "-th transitivity congruence child\n"
769
400047
                            << push;
770
400047
      d_children[j]->reduceNestedCongruence(
771
          i, conclusion, transitivityMatrix, p, visited, assumptions, isNary);
772
400047
      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
58495
Node EqProof::addToProof(CDProof* p) const
787
{
788
116990
  std::unordered_map<Node, Node> cache;
789
116990
  std::unordered_set<Node> assumptions;
790
116990
  Node conclusion = addToProof(p, cache, assumptions);
791
116990
  Trace("eqproof-conv") << "EqProof::addToProof: root of proof: " << conclusion
792
58495
                        << "\n";
793
116990
  Trace("eqproof-conv") << "EqProof::addToProof: tracked assumptions: "
794
58495
                        << 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
58495
  Node newConclusion = conclusion;
799
58495
  Assert(conclusion.getKind() == kind::EQUAL);
800
116990
  if ((conclusion[0].getKind() == kind::CONST_BOOLEAN)
801
58495
      != (conclusion[1].getKind() == kind::CONST_BOOLEAN))
802
  {
803
14446
    Trace("eqproof-conv")
804
7223
        << "EqProof::addToProof: process root for TRUE/FALSE_ELIM\n";
805
    // Index of constant in equality
806
    unsigned constIndex =
807
7223
        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
14446
        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
7223
    if (conclusion[constIndex].getConst<bool>())
819
    {
820
2073
      elimRule = PfRule::TRUE_ELIM;
821
2073
      newConclusion = conclusion[1 - constIndex];
822
    }
823
    else
824
    {
825
5150
      elimRule = PfRule::FALSE_ELIM;
826
5150
      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
7223
    if (!assumptions.count(newConclusion) && !p->hasStep(newConclusion))
833
    {
834
9284
      Trace("eqproof-conv")
835
4642
          << "EqProof::addToProof: conclude " << newConclusion << " via "
836
4642
          << elimRule << " step for " << elimPremise << "\n";
837
4642
      p->addStep(newConclusion, elimRule, {elimPremise}, {});
838
    }
839
  }
840
116990
  return newConclusion;
841
}
842
843
1173582
Node EqProof::addToProof(CDProof* p,
844
                         std::unordered_map<Node, Node>& visited,
845
                         std::unordered_set<Node>& assumptions) const
846
{
847
1173582
  std::unordered_map<Node, Node>::const_iterator it = visited.find(d_node);
848
1173582
  if (it != visited.end())
849
  {
850
831412
    Trace("eqproof-conv") << "EqProof::addToProof: already processed " << d_node
851
415706
                          << ", returning " << it->second << "\n";
852
415706
    return it->second;
853
  }
854
1515752
  Trace("eqproof-conv") << "EqProof::addToProof: adding step for " << d_id
855
757876
                        << " with conclusion " << d_node << "\n";
856
  // Assumption
857
757876
  if (d_id == MERGED_THROUGH_EQUALITY)
858
  {
859
    // Check that no (= true/false true/false) assumptions
860
326019
    if (Configuration::isDebugBuild() && d_node.getKind() == kind::EQUAL)
861
    {
862
978057
      for (unsigned i = 0; i < 2; ++i)
863
      {
864
1304076
        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
652038
            << 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
652038
    if (d_node.getKind() == kind::EQUAL
876
1630095
        && ((d_node[0].getKind() == kind::CONST_BOOLEAN)
877
978057
            != (d_node[1].getKind() == kind::CONST_BOOLEAN)))
878
    {
879
23154
      Trace("eqproof-conv")
880
11577
          << "EqProof::addToProof: add an intro step for " << d_node << "\n";
881
      // Index of constant in equality
882
11577
      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
23154
      Node introPremise;
886
      PfRule introRule;
887
11577
      if (d_node[constIndex].getConst<bool>())
888
      {
889
3902
        introRule = PfRule::TRUE_INTRO;
890
3902
        introPremise = d_node[1 - constIndex];
891
        // Track the new assumption. If it's an equality, also its symmetric
892
3902
        assumptions.insert(introPremise);
893
3902
        if (introPremise.getKind() == kind::EQUAL)
894
        {
895
          assumptions.insert(introPremise[1].eqNode(introPremise[0]));
896
        }
897
      }
898
      else
899
      {
900
7675
        introRule = PfRule::FALSE_INTRO;
901
7675
        introPremise = d_node[1 - constIndex].notNode();
902
        // Track the new assumption. If it's a disequality, also its symmetric
903
7675
        assumptions.insert(introPremise);
904
7675
        if (introPremise[0].getKind() == kind::EQUAL)
905
        {
906
4120
          assumptions.insert(
907
8240
              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
23154
          constIndex == 1 ? d_node : d_node[1].eqNode(d_node[0]);
922
11577
      p->addStep(introConclusion, introRule, {introPremise}, {});
923
    }
924
    else
925
    {
926
314442
      p->addStep(d_node, PfRule::ASSUME, {}, {d_node});
927
    }
928
    // If non-equality predicate, turn into one via TRUE/FALSE intro
929
652038
    Node conclusion = d_node;
930
326019
    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
326019
    assumptions.insert(conclusion);
954
326019
    assumptions.insert(conclusion[1].eqNode(conclusion[0]));
955
652038
    Trace("eqproof-conv") << "EqProof::addToProof: tracking assumptions "
956
652038
                          << conclusion << ", (= " << conclusion[1] << " "
957
326019
                          << conclusion[0] << ")\n";
958
326019
    visited[d_node] = conclusion;
959
326019
    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
863714
  if (d_id == MERGED_THROUGH_REFLEXIVITY
966
863714
      || (d_node.getKind() == kind::EQUAL && d_node[0] == d_node[1]))
967
  {
968
    Node conclusion =
969
164960
        d_node.getKind() == kind::EQUAL ? d_node : d_node.eqNode(d_node);
970
82480
    p->addStep(conclusion, PfRule::REFL, {}, {conclusion[0]});
971
82480
    visited[d_node] = conclusion;
972
82480
    return conclusion;
973
  }
974
  // Equalities due to theory reasoning
975
349377
  if (d_id == MERGED_THROUGH_CONSTANTS)
976
  {
977
4150
    Assert(!d_node.isNull() && d_node.getKind() == kind::EQUAL
978
           && d_node[1].isConst())
979
2075
        << ". Conclusion " << d_node << " from " << d_id
980
        << " was expected to be (= (f t1 ... tn) c)\n";
981
4150
    Assert(!assumptions.count(d_node))
982
2075
        << "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
4150
    std::vector<Node> premises;
994
6234
    for (unsigned i = 0; i < d_children.size(); ++i)
995
    {
996
8318
      Trace("eqproof-conv")
997
4159
          << "EqProof::addToProof: recurse on child " << i << "\n"
998
4159
          << push;
999
4159
      premises.push_back(d_children[i]->addToProof(p, visited, assumptions));
1000
4159
      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
4150
    std::vector<Node> subChildren, constChildren;
1016
6294
    for (unsigned i = 0, size = d_node[0].getNumChildren(); i < size; ++i)
1017
    {
1018
6831
      Node term = d_node[0][i];
1019
      // term already is a constant, add a REFL step
1020
5826
      if (term.isConst())
1021
      {
1022
1607
        subChildren.push_back(term.eqNode(term));
1023
1607
        p->addStep(subChildren.back(), PfRule::REFL, {}, {term});
1024
1607
        constChildren.push_back(term);
1025
1607
        continue;
1026
      }
1027
      // Build the equality (= ti ci) as a premise, finding the respective ci is
1028
      // the original premises
1029
5224
      Node constant;
1030
4242
      for (const Node& premise : premises)
1031
      {
1032
4242
        Assert(premise.getKind() == kind::EQUAL);
1033
4242
        if (premise[0] == term)
1034
        {
1035
764
          Assert(premise[1].isConst());
1036
764
          constant = premise[1];
1037
764
          break;
1038
        }
1039
3478
        if (premise[1] == term)
1040
        {
1041
1848
          Assert(premise[0].isConst());
1042
1848
          constant = premise[0];
1043
1848
          break;
1044
        }
1045
      }
1046
2612
      Assert(!constant.isNull());
1047
2612
      subChildren.push_back(term.eqNode(constant));
1048
2612
      constChildren.push_back(constant);
1049
    }
1050
    // build constant application (f c1 ... cn) and equality (= (f c1 ... cn) c)
1051
2075
    Kind k = d_node[0].getKind();
1052
4150
    std::vector<Node> cargs;
1053
2075
    cargs.push_back(ProofRuleChecker::mkKindNode(k));
1054
2075
    if (d_node[0].getMetaKind() == kind::metakind::PARAMETERIZED)
1055
    {
1056
      constChildren.insert(constChildren.begin(), d_node[0].getOperator());
1057
      cargs.push_back(d_node[0].getOperator());
1058
    }
1059
4150
    Node constApp = NodeManager::currentNM()->mkNode(k, constChildren);
1060
4150
    Node constEquality = constApp.eqNode(d_node[1]);
1061
4150
    Trace("eqproof-conv") << "EqProof::addToProof: adding "
1062
4150
                          << PfRule::MACRO_SR_PRED_INTRO << " step for "
1063
2075
                          << constApp << " = " << d_node[1] << "\n";
1064
2075
    p->addStep(constEquality, PfRule::MACRO_SR_PRED_INTRO, {}, {constEquality});
1065
    // build congruence conclusion (= (f t1 ... tn) (f c1 ... cn))
1066
4150
    Node congConclusion = d_node[0].eqNode(constApp);
1067
4150
    Trace("eqproof-conv") << "EqProof::addToProof: adding  " << PfRule::CONG
1068
2075
                          << " step for " << congConclusion << " from "
1069
2075
                          << subChildren << "\n";
1070
2075
    p->addStep(congConclusion, PfRule::CONG, {subChildren}, cargs, true);
1071
4150
    Trace("eqproof-conv") << "EqProof::addToProof: adding  " << PfRule::TRANS
1072
2075
                          << " step for original conclusion " << d_node << "\n";
1073
4150
    std::vector<Node> transitivityChildren{congConclusion, constEquality};
1074
2075
    p->addStep(d_node, PfRule::TRANS, {transitivityChildren}, {});
1075
2075
    visited[d_node] = d_node;
1076
2075
    return d_node;
1077
  }
1078
  // Transtivity and disequality reasoning steps
1079
347302
  if (d_id == MERGED_THROUGH_TRANS)
1080
  {
1081
288932
    Assert(d_node.getKind() == kind::EQUAL
1082
           || (d_node.getKind() == kind::NOT
1083
               && d_node[0].getKind() == kind::EQUAL))
1084
144466
        << "EqProof::addToProof: transitivity step conclusion " << d_node
1085
        << " is not equality or negated equality\n";
1086
    // If conclusion is (not (= t1 t2)) change it to (= (= t1 t2) false), which
1087
    // is the correct conclusion of the equality reasoning step. A FALSE_ELIM
1088
    // step to revert this is only necessary when this is the root. That step is
1089
    // done in the non-recursive caller of this function.
1090
    Node conclusion =
1091
144466
        d_node.getKind() != kind::NOT
1092
            ? d_node
1093
288932
            : d_node[0].eqNode(NodeManager::currentNM()->mkConst<bool>(false));
1094
    // If the conclusion is an assumption, its derivation was spurious, so it
1095
    // can be discarded. Moreover, reconstructing the step may lead to cyclic
1096
    // proofs, so we *must* cut here.
1097
144466
    if (assumptions.count(conclusion))
1098
    {
1099
      visited[d_node] = conclusion;
1100
      return conclusion;
1101
    }
1102
    // Process premises recursively
1103
288932
    std::vector<Node> children;
1104
633748
    for (unsigned i = 0, size = d_children.size(); i < size; ++i)
1105
    {
1106
      // If one of the steps is a "fake congruence" one, marked by a null
1107
      // conclusion, it must deleted. Its premises are moved down to premises of
1108
      // the transitivity step.
1109
489282
      EqProof* childProof = d_children[i].get();
1110
978564
      if (childProof->d_id == MERGED_THROUGH_CONGRUENCE
1111
489282
          && childProof->d_node.isNull())
1112
      {
1113
2390
        Trace("eqproof-conv") << "EqProof::addToProof: child proof " << i
1114
1195
                              << " is fake cong step. Fold it.\n";
1115
1195
        Assert(childProof->d_children.size() == 2);
1116
1195
        Trace("eqproof-conv") << push;
1117
3585
        for (unsigned j = 0, sizeJ = childProof->d_children.size(); j < sizeJ;
1118
             ++j)
1119
        {
1120
4780
          Trace("eqproof-conv")
1121
2390
              << "EqProof::addToProof: recurse on child " << j << "\n"
1122
2390
              << push;
1123
2390
          children.push_back(
1124
4780
              childProof->d_children[j]->addToProof(p, visited, assumptions));
1125
2390
          Trace("eqproof-conv") << pop;
1126
        }
1127
1195
        Trace("eqproof-conv") << pop;
1128
1195
        continue;
1129
      }
1130
976174
      Trace("eqproof-conv")
1131
488087
          << "EqProof::addToProof: recurse on child " << i << "\n"
1132
488087
          << push;
1133
488087
      children.push_back(childProof->addToProof(p, visited, assumptions));
1134
488087
      Trace("eqproof-conv") << pop;
1135
    }
1136
    // Eliminate spurious premises. Reasoning below assumes no refl steps.
1137
144466
    cleanReflPremises(children);
1138
    // If any premise is of the form (= (t1 t2) false), then the transitivity
1139
    // step may be coarse-grained and needs to be expanded. If the expansion
1140
    // happens it also finalizes the proof of conclusion.
1141
144466
    if (!expandTransitivityForDisequalities(
1142
            conclusion, children, p, assumptions))
1143
    {
1144
140493
      Assert(!children.empty());
1145
      // similarly, if a disequality is concluded because of theory reasoning,
1146
      // the step is coarse-grained and needs to be expanded, in which case the
1147
      // proof is finalized in the call
1148
140493
      if (!expandTransitivityForTheoryDisequalities(conclusion, children, p))
1149
      {
1150
280986
        Trace("eqproof-conv")
1151
140493
            << "EqProof::addToProof: build chain for transitivity premises"
1152
140493
            << children << " to conclude " << conclusion << "\n";
1153
        // Build ordered transitivity chain from children to derive the
1154
        // conclusion
1155
140493
        buildTransitivityChain(conclusion, children);
1156
140493
        Assert(
1157
            children.size() > 1
1158
            || (!children.empty()
1159
                && (children[0] == conclusion
1160
                    || children[0][1].eqNode(children[0][0]) == conclusion)));
1161
        // Only add transitivity step if there is more than one premise in the
1162
        // chain. Otherwise the premise will be the conclusion itself and it'll
1163
        // already have had a step added to it when the premises were
1164
        // recursively processed.
1165
140493
        if (children.size() > 1)
1166
        {
1167
140480
          p->addStep(conclusion, PfRule::TRANS, children, {}, true);
1168
        }
1169
      }
1170
    }
1171
144466
    Assert(p->hasStep(conclusion));
1172
144466
    visited[d_node] = conclusion;
1173
144466
    return conclusion;
1174
  }
1175
202836
  Assert(d_id == MERGED_THROUGH_CONGRUENCE);
1176
  // The processing below is mainly dedicated to flattening congruence steps
1177
  // (since EqProof assumes currying) and to prossibly reconstructing the
1178
  // conclusion in case it involves n-ary steps.
1179
405672
  Assert(d_node.getKind() == kind::EQUAL)
1180
202836
      << "EqProof::addToProof: conclusion " << d_node << " is not equality\n";
1181
  // The given conclusion is taken as ground truth. If the premises do not
1182
  // align, for example with (= (f t1) (f t2)) but a premise being (= t2 t1), we
1183
  // use (= t1 t2) as a premise and rely on a symmetry step to justify it.
1184
202836
  unsigned arity = d_node[0].getNumChildren();
1185
202836
  Kind k = d_node[0].getKind();
1186
202836
  bool isNary = NodeManager::isNAryKind(k);
1187
1188
  // N-ary operators are fun. The following proof is a valid EqProof
1189
  //
1190
  //   (= (f t1 t2 t3) (f t6 t5)) (= (f t6 t5) (f t5 t6))
1191
  //   -------------------------------------------------- TRANS
1192
  //             (= (f t1 t2 t3) (f t5 t6))                      (= t4 t7)
1193
  //          ------------------------------------------------------------ CONG
1194
  //          (= (f t1 t2 t3 t4) (f t5 t6 t7))
1195
  //
1196
  // We modify the above proof to conclude
1197
  //
1198
  //   (= (f (f t1 t2 t3) t4) (f (f t5 t6) t7))
1199
  //
1200
  // which is a valid congruence conclusion (applications of f with the same
1201
  // arity). For the processing below to be//  performed correctly we update
1202
  // arity to be maximal one among the two applications (4 in the above
1203
  // example).
1204
202836
  if (d_node[0].getNumChildren() != d_node[1].getNumChildren())
1205
  {
1206
    Assert(isNary) << "We only handle congruences of apps with different "
1207
                      "number of children for theory n-ary operators";
1208
    arity =
1209
        d_node[1].getNumChildren() < arity ? arity : d_node[1].getNumChildren();
1210
    Trace("eqproof-conv")
1211
        << "EqProof::addToProof: Mismatching arities in cong conclusion "
1212
        << d_node << ". Use tentative arity " << arity << "\n";
1213
  }
1214
  // For a congruence proof of (= (f a0 ... an-1) (g b0 ... bn-1)), build a
1215
  // transitivity matrix of n rows where the first row contains a transitivity
1216
  // chain justifying (= f g) and the next rows (= ai bi)
1217
405672
  std::vector<std::vector<Node>> transitivityChildren;
1218
762912
  for (unsigned i = 0; i < arity + 1; ++i)
1219
  {
1220
560076
    transitivityChildren.push_back(std::vector<Node>());
1221
  }
1222
202836
  reduceNestedCongruence(
1223
      arity, d_node, transitivityChildren, p, visited, assumptions, isNary);
1224
  // Congruences over n-ary operators may require changing the conclusion (as in
1225
  // the above example). This is handled in a general manner below according to
1226
  // whether the transitivity matrix computed by reduceNestedCongruence contains
1227
  // empty rows
1228
405672
  Node conclusion = d_node;
1229
202836
  NodeManager* nm = NodeManager::currentNM();
1230
202836
  if (isNary)
1231
  {
1232
193009
    unsigned emptyRows = 0;
1233
537110
    for (unsigned i = 1; i <= arity; ++i)
1234
    {
1235
344101
      if (transitivityChildren[i].empty())
1236
      {
1237
14
        emptyRows++;
1238
      }
1239
    }
1240
    // Given two n-ary applications f1:(f a0 ... an-1), f2:(f b0 ... bm-1), of
1241
    // arities n and m, arity = max(n,m), the number emptyRows establishes the
1242
    // sizes of the prefixes of f1 of f2 that have been equated via a
1243
    // transitivity step. The prefixes necessarily have different sizes. The
1244
    // suffixes have the same sizes. The new conclusion will be of the form
1245
    //     (= (f (f a0 ... ak1) ... an-1) (f (f b0 ... bk2) ... bm-1))
1246
    // where
1247
    //  k1 = emptyRows + 1 - (arity - n)
1248
    //  k2 = emptyRows + 1 - (arity - m)
1249
    //  k1 != k2
1250
    //  n - k1 == m - k2
1251
    // Note that by construction the equality between the first emptyRows + 1
1252
    // arguments of each application is justified by the transitivity step in
1253
    // the row emptyRows + 1 in the matrix.
1254
    //
1255
    // All of the above is with the very first row in the matrix, reserved for
1256
    // justifying the equality between the functions, which is not necessary in
1257
    // the n-ary case, notwithstanding.
1258
193009
    if (emptyRows > 0)
1259
    {
1260
28
      Trace("eqproof-conv")
1261
14
          << "EqProof::addToProof: Found " << emptyRows
1262
14
          << " empty rows. Rebuild conclusion " << d_node << "\n";
1263
      // New transitivity matrix is as before except that the empty rows in the
1264
      // beginning are eliminated, as the new arity is the maximal arity among
1265
      // the applications minus the number of empty rows.
1266
      std::vector<std::vector<Node>> newTransitivityChildren{
1267
28
          transitivityChildren.begin() + 1 + emptyRows,
1268
42
          transitivityChildren.end()};
1269
14
      transitivityChildren.clear();
1270
14
      transitivityChildren.push_back(std::vector<Node>());
1271
42
      transitivityChildren.insert(transitivityChildren.end(),
1272
                                  newTransitivityChildren.begin(),
1273
42
                                  newTransitivityChildren.end());
1274
      unsigned arityPrefix1 =
1275
14
          emptyRows + 1 - (arity - d_node[0].getNumChildren());
1276
28
      Assert(arityPrefix1 < d_node[0].getNumChildren())
1277
          << "arityPrefix1 " << arityPrefix1 << " not smaller than "
1278
14
          << d_node[0] << "'s arity " << d_node[0].getNumChildren() << "\n";
1279
      unsigned arityPrefix2 =
1280
14
          emptyRows + 1 - (arity - d_node[1].getNumChildren());
1281
28
      Assert(arityPrefix2 < d_node[1].getNumChildren())
1282
          << "arityPrefix2 " << arityPrefix2 << " not smaller than "
1283
14
          << d_node[1] << "'s arity " << d_node[1].getNumChildren() << "\n";
1284
28
      Trace("eqproof-conv") << "EqProof::addToProof: New internal "
1285
14
                               "applications with arities "
1286
14
                            << arityPrefix1 << ", " << arityPrefix2 << ":\n";
1287
      std::vector<Node> childrenPrefix1{d_node[0].begin(),
1288
28
                                        d_node[0].begin() + arityPrefix1};
1289
      std::vector<Node> childrenPrefix2{d_node[1].begin(),
1290
28
                                        d_node[1].begin() + arityPrefix2};
1291
28
      Node newFirstChild1 = nm->mkNode(k, childrenPrefix1);
1292
28
      Node newFirstChild2 = nm->mkNode(k, childrenPrefix2);
1293
28
      Trace("eqproof-conv")
1294
14
          << "EqProof::addToProof:\t " << newFirstChild1 << "\n";
1295
28
      Trace("eqproof-conv")
1296
14
          << "EqProof::addToProof:\t " << newFirstChild2 << "\n";
1297
28
      std::vector<Node> newChildren1{newFirstChild1};
1298
42
      newChildren1.insert(newChildren1.end(),
1299
28
                          d_node[0].begin() + arityPrefix1,
1300
70
                          d_node[0].end());
1301
28
      std::vector<Node> newChildren2{newFirstChild2};
1302
42
      newChildren2.insert(newChildren2.end(),
1303
28
                          d_node[1].begin() + arityPrefix2,
1304
70
                          d_node[1].end());
1305
42
      conclusion = nm->mkNode(kind::EQUAL,
1306
28
                              nm->mkNode(k, newChildren1),
1307
28
                              nm->mkNode(k, newChildren2));
1308
      // update arity
1309
14
      Assert((arity - emptyRows) == conclusion[0].getNumChildren());
1310
14
      arity = arity - emptyRows;
1311
28
      Trace("eqproof-conv")
1312
14
          << "EqProof::addToProof: New conclusion " << conclusion << "\n";
1313
    }
1314
  }
1315
202836
  if (Trace.isOn("eqproof-conv"))
1316
  {
1317
    Trace("eqproof-conv")
1318
        << "EqProof::addToProof: premises from reduced cong of " << conclusion
1319
        << ":\n";
1320
    for (unsigned i = 0; i <= arity; ++i)
1321
    {
1322
      Trace("eqproof-conv") << "EqProof::addToProof:\t" << i
1323
                            << "-th arg: " << transitivityChildren[i] << "\n";
1324
    }
1325
  }
1326
405672
  std::vector<Node> children(arity + 1);
1327
  // Proccess transitivity matrix to (possibly) generate transitivity steps for
1328
  // congruence premises (= ai bi)
1329
762898
  for (unsigned i = 0; i <= arity; ++i)
1330
  {
1331
676742
    Node transConclusion;
1332
    // We special case the operator case because there is only ever the need to
1333
    // do something when in some HO case
1334
560062
    if (i == 0)
1335
    {
1336
      // no justification for equality between functions, skip
1337
202836
      if (transitivityChildren[0].empty())
1338
      {
1339
202828
        continue;
1340
      }
1341
      // HO case
1342
8
      Assert(k == kind::APPLY_UF) << "Congruence with different functions only "
1343
                                     "allowed for uninterpreted functions.\n";
1344
8
      transConclusion =
1345
16
          conclusion[0].getOperator().eqNode(conclusion[1].getOperator());
1346
    }
1347
    else
1348
    {
1349
357226
      transConclusion = conclusion[0][i - 1].eqNode(conclusion[1][i - 1]);
1350
    }
1351
357234
    children[i] = transConclusion;
1352
714468
    Assert(!transitivityChildren[i].empty())
1353
        << "EqProof::addToProof: did not add any justification for " << i
1354
357234
        << "-th arg of congruence " << conclusion << "\n";
1355
    // If the transitivity conclusion is a reflexivity step, just add it. Note
1356
    // that this can happen even with the respective transitivityChildren row
1357
    // containing several equalities in the case of (= ai bi) being the same
1358
    // n-ary application that was justified by a congruence step, which can
1359
    // happen in the current equality engine.
1360
462453
    if (transConclusion[0] == transConclusion[1])
1361
    {
1362
105219
      p->addStep(transConclusion, PfRule::REFL, {}, {transConclusion[0]});
1363
105219
      continue;
1364
    }
1365
    // Remove spurious refl steps from the premises for (= ai bi)
1366
252015
    cleanReflPremises(transitivityChildren[i]);
1367
504030
    Assert(transitivityChildren[i].size() > 1 || transitivityChildren[i].empty()
1368
           || CDProof::isSame(transitivityChildren[i][0], transConclusion))
1369
        << "EqProof::addToProof: premises " << transitivityChildren[i] << "for "
1370
252015
        << i << "-th cong premise " << transConclusion << " don't justify it\n";
1371
252015
    unsigned sizeTrans = transitivityChildren[i].size();
1372
    // If no transitivity premise left or if (= ai bi) is an assumption (which
1373
    // might lead to a cycle with a transtivity step), nothing else to do.
1374
252015
    if (sizeTrans == 0 || assumptions.count(transConclusion) > 0)
1375
    {
1376
135335
      continue;
1377
    }
1378
    // If the transitivity conclusion, or its symmetric, occurs in the
1379
    // transitivity premises, nothing to do, as it is already justified and
1380
    // doing so again would lead to a cycle.
1381
116680
    bool occurs = false;
1382
245832
    for (unsigned j = 0; j < sizeTrans && !occurs; ++j)
1383
    {
1384
129152
      if (CDProof::isSame(transitivityChildren[i][j], transConclusion))
1385
      {
1386
105106
        occurs = true;
1387
      }
1388
    }
1389
116680
    if (!occurs)
1390
    {
1391
      // Build transitivity step
1392
11574
      buildTransitivityChain(transConclusion, transitivityChildren[i]);
1393
23148
      Trace("eqproof-conv")
1394
11574
          << "EqProof::addToProof: adding trans step for cong premise "
1395
11574
          << transConclusion << " with children " << transitivityChildren[i]
1396
11574
          << "\n";
1397
23148
      p->addStep(
1398
11574
          transConclusion, PfRule::TRANS, transitivityChildren[i], {}, true);
1399
    }
1400
  }
1401
  // first-order case
1402
202836
  if (children[0].isNull())
1403
  {
1404
    // remove placehold for function equality case
1405
202828
    children.erase(children.begin());
1406
    // Get node of the function operator over which congruence is being
1407
    // applied.
1408
405656
    std::vector<Node> args;
1409
202828
    args.push_back(ProofRuleChecker::mkKindNode(k));
1410
202828
    if (kind::metaKindOf(k) == kind::metakind::PARAMETERIZED)
1411
    {
1412
195295
      args.push_back(conclusion[0].getOperator());
1413
    }
1414
    // Add congruence step
1415
405656
    Trace("eqproof-conv") << "EqProof::addToProof: build cong step of "
1416
202828
                          << conclusion << " with op " << args[0]
1417
202828
                          << " and children " << children << "\n";
1418
202828
    p->addStep(conclusion, PfRule::CONG, children, args, true);
1419
  }
1420
  // higher-order case
1421
  else
1422
  {
1423
    // Add congruence step
1424
16
    Trace("eqproof-conv") << "EqProof::addToProof: build HO-cong step of "
1425
8
                          << conclusion << " with children " << children
1426
8
                          << "\n";
1427
8
    p->addStep(conclusion, PfRule::HO_CONG, children, {}, true);
1428
  }
1429
  // If the conclusion of the congruence step changed due to the n-ary handling,
1430
  // we obtained for example (= (f (f t1 t2 t3) t4) (f (f t5 t6) t7)), which is
1431
  // flattened into the original conclusion (= (f t1 t2 t3 t4) (f t5 t6 t7)) via
1432
  // rewriting
1433
202836
  if (conclusion != d_node)
1434
  {
1435
28
    Trace("eqproof-conv") << "EqProof::addToProof: add "
1436
28
                          << PfRule::MACRO_SR_PRED_TRANSFORM
1437
14
                          << " step to flatten rebuilt conclusion "
1438
14
                          << conclusion << "into " << d_node << "\n";
1439
42
    p->addStep(
1440
28
        d_node, PfRule::MACRO_SR_PRED_TRANSFORM, {conclusion}, {d_node}, true);
1441
  }
1442
202836
  visited[d_node] = d_node;
1443
202836
  return d_node;
1444
}
1445
1446
}  // namespace eq
1447
}  // Namespace theory
1448
29322
}  // namespace cvc5