GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/smt/proof_post_processor.cpp Lines: 318 584 54.5 %
Date: 2021-09-29 Branches: 610 2782 21.9 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Haniel Barbosa, 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 module for processing proof nodes.
14
 */
15
16
#include "smt/proof_post_processor.h"
17
18
#include "expr/skolem_manager.h"
19
#include "options/proof_options.h"
20
#include "preprocessing/assertion_pipeline.h"
21
#include "proof/proof_node_manager.h"
22
#include "smt/smt_engine.h"
23
#include "theory/arith/arith_utilities.h"
24
#include "theory/builtin/proof_checker.h"
25
#include "theory/bv/bitblast/bitblast_proof_generator.h"
26
#include "theory/bv/bitblast/proof_bitblaster.h"
27
#include "theory/rewriter.h"
28
#include "theory/strings/infer_proof_cons.h"
29
#include "theory/theory.h"
30
#include "util/rational.h"
31
32
using namespace cvc5::kind;
33
using namespace cvc5::theory;
34
35
namespace cvc5 {
36
namespace smt {
37
38
143
ProofPostprocessCallback::ProofPostprocessCallback(Env& env,
39
                                                   ProofGenerator* pppg,
40
                                                   rewriter::RewriteDb* rdb,
41
143
                                                   bool updateScopedAssumptions)
42
    : d_env(env),
43
143
      d_pnm(env.getProofNodeManager()),
44
      d_pppg(pppg),
45
      d_wfpm(env.getProofNodeManager()),
46
286
      d_updateScopedAssumptions(updateScopedAssumptions)
47
{
48
143
  d_true = NodeManager::currentNM()->mkConst(true);
49
143
}
50
51
69
void ProofPostprocessCallback::initializeUpdate()
52
{
53
69
  d_assumpToProof.clear();
54
69
  d_wfAssumptions.clear();
55
69
}
56
57
121
void ProofPostprocessCallback::setEliminateRule(PfRule rule)
58
{
59
121
  d_elimRules.insert(rule);
60
121
}
61
62
3690
bool ProofPostprocessCallback::shouldUpdate(std::shared_ptr<ProofNode> pn,
63
                                            const std::vector<Node>& fa,
64
                                            bool& continueUpdate)
65
{
66
3690
  PfRule id = pn->getRule();
67
3690
  if (d_elimRules.find(id) != d_elimRules.end())
68
  {
69
58
    return true;
70
  }
71
  // other than elimination rules, we always update assumptions as long as
72
  // d_updateScopedAssumptions is true or they are *not* in scope, i.e., not in
73
  // fa
74
7264
  if (id != PfRule::ASSUME
75
10151
      || (!d_updateScopedAssumptions
76
3632
          && std::find(fa.begin(), fa.end(), pn->getResult()) != fa.end()))
77
  {
78
5774
    Trace("smt-proof-pp-debug")
79
2887
        << "... not updating in-scope assumption " << pn->getResult() << "\n";
80
2887
    return false;
81
  }
82
745
  return true;
83
}
84
85
847
bool ProofPostprocessCallback::update(Node res,
86
                                      PfRule id,
87
                                      const std::vector<Node>& children,
88
                                      const std::vector<Node>& args,
89
                                      CDProof* cdp,
90
                                      bool& continueUpdate)
91
{
92
1694
  Trace("smt-proof-pp-debug") << "- Post process " << id << " " << children
93
847
                              << " / " << args << std::endl;
94
95
847
  if (id == PfRule::ASSUME)
96
  {
97
    // we cache based on the assumption node, not the proof node, since there
98
    // may be multiple occurrences of the same node.
99
1490
    Node f = args[0];
100
1490
    std::shared_ptr<ProofNode> pfn;
101
    std::map<Node, std::shared_ptr<ProofNode>>::iterator it =
102
745
        d_assumpToProof.find(f);
103
745
    if (it != d_assumpToProof.end())
104
    {
105
232
      Trace("smt-proof-pp-debug") << "...already computed" << std::endl;
106
232
      pfn = it->second;
107
    }
108
    else
109
    {
110
513
      Trace("smt-proof-pp-debug") << "...get proof" << std::endl;
111
513
      Assert(d_pppg != nullptr);
112
      // get proof from preprocess proof generator
113
513
      pfn = d_pppg->getProofFor(f);
114
513
      Trace("smt-proof-pp-debug") << "...finished get proof" << std::endl;
115
      // print for debugging
116
513
      if (pfn == nullptr)
117
      {
118
4
        Trace("smt-proof-pp-debug")
119
2
            << "...no proof, possibly an input assumption" << std::endl;
120
      }
121
      else
122
      {
123
511
        Assert(pfn->getResult() == f);
124
511
        if (Trace.isOn("smt-proof-pp"))
125
        {
126
          Trace("smt-proof-pp")
127
              << "=== Connect proof for preprocessing: " << f << std::endl;
128
          Trace("smt-proof-pp") << *pfn.get() << std::endl;
129
        }
130
      }
131
513
      d_assumpToProof[f] = pfn;
132
    }
133
745
    if (pfn == nullptr || pfn->getRule() == PfRule::ASSUME)
134
    {
135
423
      Trace("smt-proof-pp-debug") << "...do not add proof" << std::endl;
136
      // no update
137
423
      return false;
138
    }
139
322
    Trace("smt-proof-pp-debug") << "...add proof" << std::endl;
140
    // connect the proof
141
322
    cdp->addProof(pfn);
142
322
    return true;
143
  }
144
204
  Node ret = expandMacros(id, children, args, cdp);
145
102
  Trace("smt-proof-pp-debug") << "...expanded = " << !ret.isNull() << std::endl;
146
102
  return !ret.isNull();
147
}
148
149
44
bool ProofPostprocessCallback::updateInternal(Node res,
150
                                              PfRule id,
151
                                              const std::vector<Node>& children,
152
                                              const std::vector<Node>& args,
153
                                              CDProof* cdp)
154
{
155
44
  bool continueUpdate = true;
156
44
  return update(res, id, children, args, cdp, continueUpdate);
157
}
158
159
Node ProofPostprocessCallback::eliminateCrowdingLits(
160
    const std::vector<Node>& clauseLits,
161
    const std::vector<Node>& targetClauseLits,
162
    const std::vector<Node>& children,
163
    const std::vector<Node>& args,
164
    CDProof* cdp)
165
{
166
  Trace("smt-proof-pp-debug2") << push;
167
  NodeManager* nm = NodeManager::currentNM();
168
  Node trueNode = nm->mkConst(true);
169
  // get crowding lits and the position of the last clause that includes
170
  // them. The factoring step must be added after the last inclusion and before
171
  // its elimination.
172
  std::unordered_set<TNode> crowding;
173
  std::vector<std::pair<Node, size_t>> lastInclusion;
174
  // positions of eliminators of crowding literals, which are the positions of
175
  // the clauses that eliminate crowding literals *after* their last inclusion
176
  std::vector<size_t> eliminators;
177
  for (size_t i = 0, size = clauseLits.size(); i < size; ++i)
178
  {
179
    if (!crowding.count(clauseLits[i])
180
        && std::find(
181
               targetClauseLits.begin(), targetClauseLits.end(), clauseLits[i])
182
               == targetClauseLits.end())
183
    {
184
      Node crowdLit = clauseLits[i];
185
      crowding.insert(crowdLit);
186
      Trace("smt-proof-pp-debug2") << "crowding lit " << crowdLit << "\n";
187
      // found crowding lit, now get its last inclusion position, which is the
188
      // position of the last resolution link that introduces the crowding
189
      // literal. Note that this position has to be *before* the last link, as a
190
      // link *after* the last inclusion must eliminate the crowding literal.
191
      size_t j;
192
      for (j = children.size() - 1; j > 0; --j)
193
      {
194
        // notice that only non-singleton clauses may be introducing the
195
        // crowding literal, so we only care about non-singleton OR nodes. We
196
        // check then against the kind and whether the whole OR node occurs as a
197
        // pivot of the respective resolution
198
        if (children[j - 1].getKind() != kind::OR)
199
        {
200
          continue;
201
        }
202
        uint64_t pivotIndex = 2 * (j - 1);
203
        if (args[pivotIndex] == children[j - 1]
204
            || args[pivotIndex].notNode() == children[j - 1])
205
        {
206
          continue;
207
        }
208
        if (std::find(children[j - 1].begin(), children[j - 1].end(), crowdLit)
209
            != children[j - 1].end())
210
        {
211
          break;
212
        }
213
      }
214
      Assert(j > 0);
215
      lastInclusion.emplace_back(crowdLit, j - 1);
216
217
      Trace("smt-proof-pp-debug2") << "last inc " << j - 1 << "\n";
218
      // get elimination position, starting from the following link as the last
219
      // inclusion one. The result is the last (in the chain, but first from
220
      // this point on) resolution link that eliminates the crowding literal. A
221
      // literal l is eliminated by a link if it contains a literal l' with
222
      // opposite polarity to l.
223
      for (; j < children.size(); ++j)
224
      {
225
        bool posFirst = args[(2 * j) - 1] == trueNode;
226
        Node pivot = args[(2 * j)];
227
        Trace("smt-proof-pp-debug2")
228
            << "\tcheck w/ args " << posFirst << " / " << pivot << "\n";
229
        // To eliminate the crowding literal (crowdLit), the clause must contain
230
        // it with opposite polarity. There are three successful cases,
231
        // according to the pivot and its sign
232
        //
233
        // - crowdLit is the same as the pivot and posFirst is true, which means
234
        //   that the clause contains its negation and eliminates it
235
        //
236
        // - crowdLit is the negation of the pivot and posFirst is false, so the
237
        //   clause contains the node whose negation is crowdLit. Note that this
238
        //   case may either be crowdLit.notNode() == pivot or crowdLit ==
239
        //   pivot.notNode().
240
        if ((crowdLit == pivot && posFirst)
241
            || (crowdLit.notNode() == pivot && !posFirst)
242
            || (pivot.notNode() == crowdLit && !posFirst))
243
        {
244
          Trace("smt-proof-pp-debug2") << "\t\tfound it!\n";
245
          eliminators.push_back(j);
246
          break;
247
        }
248
      }
249
      AlwaysAssert(j < children.size());
250
    }
251
  }
252
  Assert(!lastInclusion.empty());
253
  // order map so that we process crowding literals in the order of the clauses
254
  // that last introduce them
255
  auto cmp = [](std::pair<Node, size_t>& a, std::pair<Node, size_t>& b) {
256
    return a.second < b.second;
257
  };
258
  std::sort(lastInclusion.begin(), lastInclusion.end(), cmp);
259
  // order eliminators
260
  std::sort(eliminators.begin(), eliminators.end());
261
  if (Trace.isOn("smt-proof-pp-debug"))
262
  {
263
    Trace("smt-proof-pp-debug") << "crowding lits last inclusion:\n";
264
    for (const auto& pair : lastInclusion)
265
    {
266
      Trace("smt-proof-pp-debug")
267
          << "\t- [" << pair.second << "] : " << pair.first << "\n";
268
    }
269
    Trace("smt-proof-pp-debug") << "eliminators:";
270
    for (size_t elim : eliminators)
271
    {
272
      Trace("smt-proof-pp-debug") << " " << elim;
273
    }
274
    Trace("smt-proof-pp-debug") << "\n";
275
  }
276
  // TODO (cvc4-wishues/issues/77): implement also simpler version and compare
277
  //
278
  // We now start to break the chain, one step at a time. Naively this breaking
279
  // down would be one resolution/factoring to each crowding literal, but we can
280
  // merge some of the cases. Effectively we do the following:
281
  //
282
  //
283
  // lastClause   children[start] ... children[end]
284
  // ---------------------------------------------- CHAIN_RES
285
  //         C
286
  //    ----------- FACTORING
287
  //    lastClause'                children[start'] ... children[end']
288
  //    -------------------------------------------------------------- CHAIN_RES
289
  //                                    ...
290
  //
291
  // where
292
  //   lastClause_0 = children[0]
293
  //   start_0 = 1
294
  //   end_0 = eliminators[0] - 1
295
  //   start_i+1 = nextGuardedElimPos - 1
296
  //
297
  // The important point is how end_i+1 is computed. It is based on what we call
298
  // the "nextGuardedElimPos", i.e., the next elimination position that requires
299
  // removal of duplicates. The intuition is that a factoring step may eliminate
300
  // the duplicates of crowding literals l1 and l2. If the last inclusion of l2
301
  // is before the elimination of l1, then we can go ahead and also perform the
302
  // elimination of l2 without another factoring. However if another literal l3
303
  // has its last inclusion after the elimination of l2, then the elimination of
304
  // l3 is the next guarded elimination.
305
  //
306
  // To do the above computation then we determine, after a resolution/factoring
307
  // step, the first crowded literal to have its last inclusion after "end". The
308
  // first elimination position to be bigger than the position of that crowded
309
  // literal is the next guarded elimination position.
310
  size_t lastElim = 0;
311
  Node lastClause = children[0];
312
  std::vector<Node> childrenRes;
313
  std::vector<Node> childrenResArgs;
314
  Node resPlaceHolder;
315
  size_t nextGuardedElimPos = eliminators[0];
316
  do
317
  {
318
    size_t start = lastElim + 1;
319
    size_t end = nextGuardedElimPos - 1;
320
    Trace("smt-proof-pp-debug2")
321
        << "res with:\n\tlastClause: " << lastClause << "\n\tstart: " << start
322
        << "\n\tend: " << end << "\n";
323
    childrenRes.push_back(lastClause);
324
    // note that the interval of insert is exclusive in the end, so we add 1
325
    childrenRes.insert(childrenRes.end(),
326
                       children.begin() + start,
327
                       children.begin() + end + 1);
328
    childrenResArgs.insert(childrenResArgs.end(),
329
                           args.begin() + (2 * start) - 1,
330
                           args.begin() + (2 * end) + 1);
331
    Trace("smt-proof-pp-debug2") << "res children: " << childrenRes << "\n";
332
    Trace("smt-proof-pp-debug2") << "res args: " << childrenResArgs << "\n";
333
    resPlaceHolder = d_pnm->getChecker()->checkDebug(PfRule::CHAIN_RESOLUTION,
334
                                                     childrenRes,
335
                                                     childrenResArgs,
336
                                                     Node::null(),
337
                                                     "");
338
    Trace("smt-proof-pp-debug2")
339
        << "resPlaceHorder: " << resPlaceHolder << "\n";
340
    cdp->addStep(
341
        resPlaceHolder, PfRule::CHAIN_RESOLUTION, childrenRes, childrenResArgs);
342
    // I need to add factoring if end < children.size(). Otherwise, this is
343
    // to be handled by the caller
344
    if (end < children.size() - 1)
345
    {
346
      lastClause = d_pnm->getChecker()->checkDebug(
347
          PfRule::FACTORING, {resPlaceHolder}, {}, Node::null(), "");
348
      if (!lastClause.isNull())
349
      {
350
        cdp->addStep(lastClause, PfRule::FACTORING, {resPlaceHolder}, {});
351
      }
352
      else
353
      {
354
        lastClause = resPlaceHolder;
355
      }
356
      Trace("smt-proof-pp-debug2") << "lastClause: " << lastClause << "\n";
357
    }
358
    else
359
    {
360
      lastClause = resPlaceHolder;
361
      break;
362
    }
363
    // update for next round
364
    childrenRes.clear();
365
    childrenResArgs.clear();
366
    lastElim = end;
367
368
    // find the position of the last inclusion of the next crowded literal
369
    size_t nextCrowdedInclusionPos = lastInclusion.size();
370
    for (size_t i = 0, size = lastInclusion.size(); i < size; ++i)
371
    {
372
      if (lastInclusion[i].second > lastElim)
373
      {
374
        nextCrowdedInclusionPos = i;
375
        break;
376
      }
377
    }
378
    Trace("smt-proof-pp-debug2")
379
        << "nextCrowdedInclusion/Pos: "
380
        << lastInclusion[nextCrowdedInclusionPos].second << "/"
381
        << nextCrowdedInclusionPos << "\n";
382
    // if there is none, then the remaining literals will be used in the next
383
    // round
384
    if (nextCrowdedInclusionPos == lastInclusion.size())
385
    {
386
      nextGuardedElimPos = children.size();
387
    }
388
    else
389
    {
390
      nextGuardedElimPos = children.size();
391
      for (size_t i = 0, size = eliminators.size(); i < size; ++i)
392
      {
393
        //  nextGuardedElimPos is the largest element of
394
        // eliminators bigger the next crowded literal's last inclusion
395
        if (eliminators[i] > lastInclusion[nextCrowdedInclusionPos].second)
396
        {
397
          nextGuardedElimPos = eliminators[i];
398
          break;
399
        }
400
      }
401
      Assert(nextGuardedElimPos < children.size());
402
    }
403
    Trace("smt-proof-pp-debug2")
404
        << "nextGuardedElimPos: " << nextGuardedElimPos << "\n";
405
  } while (true);
406
  Trace("smt-proof-pp-debug2") << pop;
407
  return lastClause;
408
}
409
410
154
Node ProofPostprocessCallback::expandMacros(PfRule id,
411
                                            const std::vector<Node>& children,
412
                                            const std::vector<Node>& args,
413
                                            CDProof* cdp)
414
{
415
154
  if (d_elimRules.find(id) == d_elimRules.end())
416
  {
417
    // not eliminated
418
    return Node::null();
419
  }
420
  // macro elimination
421
154
  if (id == PfRule::MACRO_SR_EQ_INTRO)
422
  {
423
    // (TRANS
424
    //   (SUBS <children> :args args[0:1])
425
    //   (REWRITE :args <t.substitute(x1,t1). ... .substitute(xn,tn)> args[2]))
426
128
    std::vector<Node> tchildren;
427
128
    Node t = args[0];
428
128
    Node ts;
429
64
    if (!children.empty())
430
    {
431
4
      std::vector<Node> sargs;
432
2
      sargs.push_back(t);
433
2
      MethodId ids = MethodId::SB_DEFAULT;
434
2
      if (args.size() >= 2)
435
      {
436
2
        if (getMethodId(args[1], ids))
437
        {
438
2
          sargs.push_back(args[1]);
439
        }
440
      }
441
2
      MethodId ida = MethodId::SBA_SEQUENTIAL;
442
2
      if (args.size() >= 3)
443
      {
444
        if (getMethodId(args[2], ida))
445
        {
446
          sargs.push_back(args[2]);
447
        }
448
      }
449
2
      ts = builtin::BuiltinProofRuleChecker::applySubstitution(
450
          t, children, ids, ida);
451
4
      Trace("smt-proof-pp-debug")
452
2
          << "...eq intro subs equality is " << t << " == " << ts << ", from "
453
2
          << ids << " " << ida << std::endl;
454
2
      if (ts != t)
455
      {
456
2
        Node eq = t.eqNode(ts);
457
        // apply SUBS proof rule if necessary
458
1
        if (!updateInternal(eq, PfRule::SUBS, children, sargs, cdp))
459
        {
460
          // if we specified that we did not want to eliminate, add as step
461
          cdp->addStep(eq, PfRule::SUBS, children, sargs);
462
        }
463
1
        tchildren.push_back(eq);
464
      }
465
    }
466
    else
467
    {
468
      // no substitute
469
62
      ts = t;
470
    }
471
128
    std::vector<Node> rargs;
472
64
    rargs.push_back(ts);
473
64
    MethodId idr = MethodId::RW_REWRITE;
474
64
    if (args.size() >= 4)
475
    {
476
      if (getMethodId(args[3], idr))
477
      {
478
        rargs.push_back(args[3]);
479
      }
480
    }
481
128
    Node tr = d_env.rewriteViaMethod(ts, idr);
482
128
    Trace("smt-proof-pp-debug")
483
64
        << "...eq intro rewrite equality is " << ts << " == " << tr << ", from "
484
64
        << idr << std::endl;
485
64
    if (ts != tr)
486
    {
487
86
      Node eq = ts.eqNode(tr);
488
      // apply REWRITE proof rule
489
43
      if (!updateInternal(eq, PfRule::REWRITE, {}, rargs, cdp))
490
      {
491
        // if not elimianted, add as step
492
        cdp->addStep(eq, PfRule::REWRITE, {}, rargs);
493
      }
494
43
      tchildren.push_back(eq);
495
    }
496
64
    if (t == tr)
497
    {
498
      // typically not necessary, but done to be robust
499
21
      cdp->addStep(t.eqNode(tr), PfRule::REFL, {}, {t});
500
21
      return t.eqNode(tr);
501
    }
502
    // must add TRANS if two step
503
43
    return addProofForTrans(tchildren, cdp);
504
  }
505
90
  else if (id == PfRule::MACRO_SR_PRED_INTRO)
506
  {
507
18
    std::vector<Node> tchildren;
508
18
    std::vector<Node> sargs = args;
509
    // take into account witness form, if necessary
510
9
    bool reqWitness = d_wfpm.requiresWitnessFormIntro(args[0]);
511
18
    Trace("smt-proof-pp-debug")
512
9
        << "...pred intro reqWitness=" << reqWitness << std::endl;
513
    // (TRUE_ELIM
514
    // (TRANS
515
    //    (MACRO_SR_EQ_INTRO <children> :args (t args[1:]))
516
    //    ... proof of apply_SR(t) = toWitness(apply_SR(t)) ...
517
    //    (MACRO_SR_EQ_INTRO {} {toWitness(apply_SR(t))})
518
    // ))
519
    // Notice this is an optimized, one sided version of the expansion of
520
    // MACRO_SR_PRED_TRANSFORM below.
521
    // We call the expandMacros method on MACRO_SR_EQ_INTRO, where notice
522
    // that this rule application is immediately expanded in the recursive
523
    // call and not added to the proof.
524
18
    Node conc = expandMacros(PfRule::MACRO_SR_EQ_INTRO, children, sargs, cdp);
525
18
    Trace("smt-proof-pp-debug")
526
9
        << "...pred intro conclusion is " << conc << std::endl;
527
9
    Assert(!conc.isNull());
528
9
    Assert(conc.getKind() == EQUAL);
529
9
    Assert(conc[0] == args[0]);
530
9
    tchildren.push_back(conc);
531
9
    if (reqWitness)
532
    {
533
      Node weq = addProofForWitnessForm(conc[1], cdp);
534
      Trace("smt-proof-pp-debug") << "...weq is " << weq << std::endl;
535
      if (addToTransChildren(weq, tchildren))
536
      {
537
        // toWitness(apply_SR(t)) = apply_SR(toWitness(apply_SR(t)))
538
        // rewrite again, don't need substitution. Also we always use the
539
        // default rewriter, due to the definition of MACRO_SR_PRED_INTRO.
540
        Node weqr = expandMacros(PfRule::MACRO_SR_EQ_INTRO, {}, {weq[1]}, cdp);
541
        addToTransChildren(weqr, tchildren);
542
      }
543
    }
544
    // apply transitivity if necessary
545
18
    Node eq = addProofForTrans(tchildren, cdp);
546
9
    Assert(!eq.isNull());
547
9
    Assert(eq.getKind() == EQUAL);
548
9
    Assert(eq[0] == args[0]);
549
9
    Assert(eq[1] == d_true);
550
551
9
    cdp->addStep(eq[0], PfRule::TRUE_ELIM, {eq}, {});
552
9
    return eq[0];
553
  }
554
81
  else if (id == PfRule::MACRO_SR_PRED_ELIM)
555
  {
556
    // (EQ_RESOLVE
557
    //   children[0]
558
    //   (MACRO_SR_EQ_INTRO children[1:] :args children[0] ++ args))
559
2
    std::vector<Node> schildren(children.begin() + 1, children.end());
560
2
    std::vector<Node> srargs;
561
1
    srargs.push_back(children[0]);
562
1
    srargs.insert(srargs.end(), args.begin(), args.end());
563
2
    Node conc = expandMacros(PfRule::MACRO_SR_EQ_INTRO, schildren, srargs, cdp);
564
1
    Assert(!conc.isNull());
565
1
    Assert(conc.getKind() == EQUAL);
566
1
    Assert(conc[0] == children[0]);
567
    // apply equality resolve
568
1
    cdp->addStep(conc[1], PfRule::EQ_RESOLVE, {children[0], conc}, {});
569
1
    return conc[1];
570
  }
571
80
  else if (id == PfRule::MACRO_SR_PRED_TRANSFORM)
572
  {
573
    // (EQ_RESOLVE
574
    //   children[0]
575
    //   (TRANS
576
    //      (MACRO_SR_EQ_INTRO children[1:] :args (children[0] args[1:]))
577
    //      ... proof of c = wc
578
    //      (MACRO_SR_EQ_INTRO {} wc)
579
    //      (SYMM
580
    //        (MACRO_SR_EQ_INTRO children[1:] :args <args>)
581
    //        ... proof of a = wa
582
    //        (MACRO_SR_EQ_INTRO {} wa))))
583
    // where
584
    // wa = toWitness(apply_SR(args[0])) and
585
    // wc = toWitness(apply_SR(children[0])).
586
42
    Trace("smt-proof-pp-debug")
587
21
        << "Transform " << children[0] << " == " << args[0] << std::endl;
588
21
    if (CDProof::isSame(children[0], args[0]))
589
    {
590
      Trace("smt-proof-pp-debug") << "...nothing to do" << std::endl;
591
      // nothing to do
592
      return children[0];
593
    }
594
42
    std::vector<Node> tchildren;
595
42
    std::vector<Node> schildren(children.begin() + 1, children.end());
596
42
    std::vector<Node> sargs = args;
597
    // first, compute if we need
598
21
    bool reqWitness = d_wfpm.requiresWitnessFormTransform(children[0], args[0]);
599
21
    Trace("smt-proof-pp-debug") << "...reqWitness=" << reqWitness << std::endl;
600
    // convert both sides, in three steps, take symmetry of second chain
601
63
    for (unsigned r = 0; r < 2; r++)
602
    {
603
84
      std::vector<Node> tchildrenr;
604
      // first rewrite children[0], then args[0]
605
42
      sargs[0] = r == 0 ? children[0] : args[0];
606
      // t = apply_SR(t)
607
84
      Node eq = expandMacros(PfRule::MACRO_SR_EQ_INTRO, schildren, sargs, cdp);
608
84
      Trace("smt-proof-pp-debug")
609
42
          << "transform subs_rewrite (" << r << "): " << eq << std::endl;
610
42
      Assert(!eq.isNull() && eq.getKind() == EQUAL && eq[0] == sargs[0]);
611
42
      addToTransChildren(eq, tchildrenr);
612
      // apply_SR(t) = toWitness(apply_SR(t))
613
42
      if (reqWitness)
614
      {
615
4
        Node weq = addProofForWitnessForm(eq[1], cdp);
616
4
        Trace("smt-proof-pp-debug")
617
2
            << "transform toWitness (" << r << "): " << weq << std::endl;
618
2
        if (addToTransChildren(weq, tchildrenr))
619
        {
620
          // toWitness(apply_SR(t)) = apply_SR(toWitness(apply_SR(t)))
621
          // rewrite again, don't need substitution. Also, we always use the
622
          // default rewriter, due to the definition of MACRO_SR_PRED_TRANSFORM.
623
          Node weqr =
624
              expandMacros(PfRule::MACRO_SR_EQ_INTRO, {}, {weq[1]}, cdp);
625
          Trace("smt-proof-pp-debug") << "transform rewrite_witness (" << r
626
                                      << "): " << weqr << std::endl;
627
          addToTransChildren(weqr, tchildrenr);
628
        }
629
      }
630
84
      Trace("smt-proof-pp-debug")
631
42
          << "transform connect (" << r << ")" << std::endl;
632
      // add to overall chain
633
42
      if (r == 0)
634
      {
635
        // add the current chain to the overall chain
636
21
        tchildren.insert(tchildren.end(), tchildrenr.begin(), tchildrenr.end());
637
      }
638
      else
639
      {
640
        // add the current chain to cdp
641
42
        Node eqr = addProofForTrans(tchildrenr, cdp);
642
21
        if (!eqr.isNull())
643
        {
644
24
          Trace("smt-proof-pp-debug") << "transform connect sym " << tchildren
645
12
                                      << " " << eqr << std::endl;
646
          // take symmetry of above and add it to the overall chain
647
12
          addToTransChildren(eqr, tchildren, true);
648
        }
649
      }
650
84
      Trace("smt-proof-pp-debug")
651
42
          << "transform finish (" << r << ")" << std::endl;
652
    }
653
654
    // apply transitivity if necessary
655
42
    Node eq = addProofForTrans(tchildren, cdp);
656
657
21
    cdp->addStep(args[0], PfRule::EQ_RESOLVE, {children[0], eq}, {});
658
21
    return args[0];
659
  }
660
59
  else if (id == PfRule::MACRO_RESOLUTION
661
59
           || id == PfRule::MACRO_RESOLUTION_TRUST)
662
  {
663
    // first generate the naive chain_resolution
664
22
    std::vector<Node> chainResArgs{args.begin() + 1, args.end()};
665
11
    Node chainConclusion = d_pnm->getChecker()->checkDebug(
666
22
        PfRule::CHAIN_RESOLUTION, children, chainResArgs, Node::null(), "");
667
11
    Trace("smt-proof-pp-debug") << "Original conclusion: " << args[0] << "\n";
668
22
    Trace("smt-proof-pp-debug")
669
11
        << "chainRes conclusion: " << chainConclusion << "\n";
670
    // There are n cases:
671
    // - if the conclusion is the same, just replace
672
    // - if they have the same literals but in different quantity, add a
673
    //   FACTORING step
674
    // - if the order is not the same, add a REORDERING step
675
    // - if there are literals in chainConclusion that are not in the original
676
    //   conclusion, we need to transform the MACRO_RESOLUTION into a series of
677
    //   CHAIN_RESOLUTION + FACTORING steps, so that we explicitly eliminate all
678
    //   these "crowding" literals. We do this via FACTORING so we avoid adding
679
    //   an exponential number of premises, which would happen if we just
680
    //   repeated in the premises the clauses needed for eliminating crowding
681
    //   literals, which could themselves add crowding literals.
682
11
    if (chainConclusion == args[0])
683
    {
684
11
      cdp->addStep(
685
          chainConclusion, PfRule::CHAIN_RESOLUTION, children, chainResArgs);
686
11
      return chainConclusion;
687
    }
688
    NodeManager* nm = NodeManager::currentNM();
689
    // If we got here, then chainConclusion is NECESSARILY an OR node
690
    Assert(chainConclusion.getKind() == kind::OR);
691
    // get the literals in the chain conclusion
692
    std::vector<Node> chainConclusionLits{chainConclusion.begin(),
693
                                          chainConclusion.end()};
694
    std::set<Node> chainConclusionLitsSet{chainConclusion.begin(),
695
                                          chainConclusion.end()};
696
    // is args[0] a singleton clause? If it's not an OR node, then yes.
697
    // Otherwise, it's only a singleton if it occurs in chainConclusionLitsSet
698
    std::vector<Node> conclusionLits;
699
    // whether conclusion is singleton
700
    if (chainConclusionLitsSet.count(args[0]))
701
    {
702
      conclusionLits.push_back(args[0]);
703
    }
704
    else
705
    {
706
      Assert(args[0].getKind() == kind::OR);
707
      conclusionLits.insert(
708
          conclusionLits.end(), args[0].begin(), args[0].end());
709
    }
710
    std::set<Node> conclusionLitsSet{conclusionLits.begin(),
711
                                     conclusionLits.end()};
712
    // If the sets are different, there are "crowding" literals, i.e. literals
713
    // that were removed by implicit multi-usage of premises in the resolution
714
    // chain.
715
    if (chainConclusionLitsSet != conclusionLitsSet)
716
    {
717
      chainConclusion = eliminateCrowdingLits(
718
          chainConclusionLits, conclusionLits, children, args, cdp);
719
      // update vector of lits. Note that the set is no longer used, so we don't
720
      // need to update it
721
      //
722
      // We need again to check whether chainConclusion is a singleton
723
      // clause. As above, it's a singleton if it's in the original
724
      // chainConclusionLitsSet.
725
      chainConclusionLits.clear();
726
      if (chainConclusionLitsSet.count(chainConclusion))
727
      {
728
        chainConclusionLits.push_back(chainConclusion);
729
      }
730
      else
731
      {
732
        Assert(chainConclusion.getKind() == kind::OR);
733
        chainConclusionLits.insert(chainConclusionLits.end(),
734
                                   chainConclusion.begin(),
735
                                   chainConclusion.end());
736
      }
737
    }
738
    else
739
    {
740
      cdp->addStep(
741
          chainConclusion, PfRule::CHAIN_RESOLUTION, children, chainResArgs);
742
    }
743
    Trace("smt-proof-pp-debug")
744
        << "Conclusion after chain_res/elimCrowd: " << chainConclusion << "\n";
745
    Trace("smt-proof-pp-debug")
746
        << "Conclusion lits: " << chainConclusionLits << "\n";
747
    // Placeholder for running conclusion
748
    Node n = chainConclusion;
749
    // factoring
750
    if (chainConclusionLits.size() != conclusionLits.size())
751
    {
752
      // We build it rather than taking conclusionLits because the order may be
753
      // different
754
      std::vector<Node> factoredLits;
755
      std::unordered_set<TNode> clauseSet;
756
      for (size_t i = 0, size = chainConclusionLits.size(); i < size; ++i)
757
      {
758
        if (clauseSet.count(chainConclusionLits[i]))
759
        {
760
          continue;
761
        }
762
        factoredLits.push_back(n[i]);
763
        clauseSet.insert(n[i]);
764
      }
765
      Node factored = factoredLits.empty()
766
                          ? nm->mkConst(false)
767
                          : factoredLits.size() == 1
768
                                ? factoredLits[0]
769
                                : nm->mkNode(kind::OR, factoredLits);
770
      cdp->addStep(factored, PfRule::FACTORING, {n}, {});
771
      n = factored;
772
    }
773
    // either same node or n as a clause
774
    Assert(n == args[0] || n.getKind() == kind::OR);
775
    // reordering
776
    if (n != args[0])
777
    {
778
      cdp->addStep(args[0], PfRule::REORDERING, {n}, {args[0]});
779
    }
780
    return args[0];
781
  }
782
48
  else if (id == PfRule::SUBS)
783
  {
784
1
    NodeManager* nm = NodeManager::currentNM();
785
    // Notice that a naive way to reconstruct SUBS is to do a term conversion
786
    // proof for each substitution.
787
    // The proof of f(a) * { a -> g(b) } * { b -> c } = f(g(c)) is:
788
    //   TRANS( CONG{f}( a=g(b) ), CONG{f}( CONG{g}( b=c ) ) )
789
    // Notice that more optimal proofs are possible that do a single traversal
790
    // over t. This is done by applying later substitutions to the range of
791
    // previous substitutions, until a final simultaneous substitution is
792
    // applied to t.  For instance, in the above example, we first prove:
793
    //   CONG{g}( b = c )
794
    // by applying the second substitution { b -> c } to the range of the first,
795
    // giving us a proof of g(b)=g(c). We then construct the updated proof
796
    // by tranitivity:
797
    //   TRANS( a=g(b), CONG{g}( b=c ) )
798
    // We then apply the substitution { a -> g(c), b -> c } to f(a), to obtain:
799
    //   CONG{f}( TRANS( a=g(b), CONG{g}( b=c ) ) )
800
    // which notice is more compact than the proof above.
801
2
    Node t = args[0];
802
    // get the kind of substitution
803
1
    MethodId ids = MethodId::SB_DEFAULT;
804
1
    if (args.size() >= 2)
805
    {
806
1
      getMethodId(args[1], ids);
807
    }
808
1
    MethodId ida = MethodId::SBA_SEQUENTIAL;
809
1
    if (args.size() >= 3)
810
    {
811
      getMethodId(args[2], ida);
812
    }
813
2
    std::vector<std::shared_ptr<CDProof>> pfs;
814
2
    std::vector<TNode> vsList;
815
2
    std::vector<TNode> ssList;
816
2
    std::vector<TNode> fromList;
817
2
    std::vector<ProofGenerator*> pgs;
818
    // first, compute the entire substitution
819
2
    for (size_t i = 0, nchild = children.size(); i < nchild; i++)
820
    {
821
      // get the substitution
822
2
      builtin::BuiltinProofRuleChecker::getSubstitutionFor(
823
1
          children[i], vsList, ssList, fromList, ids);
824
      // ensure proofs for each formula in fromList
825
1
      if (children[i].getKind() == AND && ids == MethodId::SB_DEFAULT)
826
      {
827
        for (size_t j = 0, nchildi = children[i].getNumChildren(); j < nchildi;
828
             j++)
829
        {
830
          Node nodej = nm->mkConst(Rational(j));
831
          cdp->addStep(
832
              children[i][j], PfRule::AND_ELIM, {children[i]}, {nodej});
833
        }
834
      }
835
    }
836
2
    std::vector<Node> vvec;
837
2
    std::vector<Node> svec;
838
2
    for (size_t i = 0, nvs = vsList.size(); i < nvs; i++)
839
    {
840
      // Note we process in forward order, since later substitution should be
841
      // applied to earlier ones, and the last child of a SUBS is processed
842
      // first.
843
2
      TNode var = vsList[i];
844
2
      TNode subs = ssList[i];
845
2
      TNode childFrom = fromList[i];
846
2
      Trace("smt-proof-pp-debug")
847
1
          << "...process " << var << " -> " << subs << " (" << childFrom << ", "
848
1
          << ids << ")" << std::endl;
849
      // apply the current substitution to the range
850
1
      if (!vvec.empty() && ida == MethodId::SBA_SEQUENTIAL)
851
      {
852
        Node ss =
853
            subs.substitute(vvec.begin(), vvec.end(), svec.begin(), svec.end());
854
        if (ss != subs)
855
        {
856
          Trace("smt-proof-pp-debug")
857
              << "......updated to " << var << " -> " << ss
858
              << " based on previous substitution" << std::endl;
859
          // make the proof for the tranitivity step
860
          std::shared_ptr<CDProof> pf = std::make_shared<CDProof>(d_pnm);
861
          pfs.push_back(pf);
862
          // prove the updated substitution
863
          TConvProofGenerator tcg(d_pnm,
864
                                  nullptr,
865
                                  TConvPolicy::ONCE,
866
                                  TConvCachePolicy::NEVER,
867
                                  "nested_SUBS_TConvProofGenerator",
868
                                  nullptr,
869
                                  true);
870
          // add previous rewrite steps
871
          for (unsigned j = 0, nvars = vvec.size(); j < nvars; j++)
872
          {
873
            // substitutions are pre-rewrites
874
            tcg.addRewriteStep(vvec[j], svec[j], pgs[j], true);
875
          }
876
          // get the proof for the update to the current substitution
877
          Node seqss = subs.eqNode(ss);
878
          std::shared_ptr<ProofNode> pfn = tcg.getProofFor(seqss);
879
          Assert(pfn != nullptr);
880
          // add the proof
881
          pf->addProof(pfn);
882
          // get proof for childFrom from cdp
883
          pfn = cdp->getProofFor(childFrom);
884
          pf->addProof(pfn);
885
          // ensure we have a proof of var = subs
886
          Node veqs = addProofForSubsStep(var, subs, childFrom, pf.get());
887
          // transitivity
888
          pf->addStep(var.eqNode(ss), PfRule::TRANS, {veqs, seqss}, {});
889
          // add to the substitution
890
          vvec.push_back(var);
891
          svec.push_back(ss);
892
          pgs.push_back(pf.get());
893
          continue;
894
        }
895
      }
896
      // Just use equality from CDProof, but ensure we have a proof in cdp.
897
      // This may involve a TRUE_INTRO/FALSE_INTRO if the substitution step
898
      // uses the assumption childFrom as a Boolean assignment (e.g.
899
      // childFrom = true if we are using MethodId::SB_LITERAL).
900
1
      addProofForSubsStep(var, subs, childFrom, cdp);
901
1
      vvec.push_back(var);
902
1
      svec.push_back(subs);
903
1
      pgs.push_back(cdp);
904
    }
905
    // should be implied by the substitution now
906
1
    TConvPolicy tcpolicy = ida == MethodId::SBA_FIXPOINT ? TConvPolicy::FIXPOINT
907
                                                         : TConvPolicy::ONCE;
908
    TConvProofGenerator tcpg(d_pnm,
909
                             nullptr,
910
                             tcpolicy,
911
                             TConvCachePolicy::NEVER,
912
                             "SUBS_TConvProofGenerator",
913
                             nullptr,
914
2
                             true);
915
2
    for (unsigned j = 0, nvars = vvec.size(); j < nvars; j++)
916
    {
917
      // substitutions are pre-rewrites
918
1
      tcpg.addRewriteStep(vvec[j], svec[j], pgs[j], true);
919
    }
920
    // add the proof constructed by the term conversion utility
921
2
    std::shared_ptr<ProofNode> pfn = tcpg.getProofForRewriting(t);
922
2
    Node eq = pfn->getResult();
923
    Node ts = builtin::BuiltinProofRuleChecker::applySubstitution(
924
2
        t, children, ids, ida);
925
2
    Node eqq = t.eqNode(ts);
926
1
    if (eq != eqq)
927
    {
928
      pfn = nullptr;
929
    }
930
    // should give a proof, if not, then tcpg does not agree with the
931
    // substitution.
932
1
    if (pfn == nullptr)
933
    {
934
      Warning() << "resort to TRUST_SUBS" << std::endl
935
                << eq << std::endl
936
                << eqq << std::endl
937
                << "from " << children << " applied to " << t << std::endl;
938
      cdp->addStep(eqq, PfRule::TRUST_SUBS, {}, {eqq});
939
    }
940
    else
941
    {
942
1
      cdp->addProof(pfn);
943
    }
944
1
    return eqq;
945
  }
946
47
  else if (id == PfRule::REWRITE)
947
  {
948
    // get the kind of rewrite
949
43
    MethodId idr = MethodId::RW_REWRITE;
950
43
    TheoryId theoryId = Theory::theoryOf(args[0]);
951
43
    if (args.size() >= 2)
952
    {
953
      getMethodId(args[1], idr);
954
    }
955
43
    Rewriter* rr = d_env.getRewriter();
956
86
    Node ret = d_env.rewriteViaMethod(args[0], idr);
957
86
    Node eq = args[0].eqNode(ret);
958
43
    if (idr == MethodId::RW_REWRITE || idr == MethodId::RW_REWRITE_EQ_EXT)
959
    {
960
      // rewrites from theory::Rewriter
961
43
      bool isExtEq = (idr == MethodId::RW_REWRITE_EQ_EXT);
962
      // use rewrite with proof interface
963
86
      TrustNode trn = rr->rewriteWithProof(args[0], isExtEq);
964
86
      std::shared_ptr<ProofNode> pfn = trn.toProofNode();
965
43
      if (pfn == nullptr)
966
      {
967
        Trace("smt-proof-pp-debug")
968
            << "Use TRUST_REWRITE for " << eq << std::endl;
969
        // did not have a proof of rewriting, probably isExtEq is true
970
        if (isExtEq)
971
        {
972
          // update to THEORY_REWRITE with idr
973
          Assert(args.size() >= 1);
974
          Node tid = builtin::BuiltinProofRuleChecker::mkTheoryIdNode(theoryId);
975
          cdp->addStep(eq, PfRule::THEORY_REWRITE, {}, {eq, tid, args[1]});
976
        }
977
        else
978
        {
979
          // this should never be applied
980
          cdp->addStep(eq, PfRule::TRUST_REWRITE, {}, {eq});
981
        }
982
      }
983
      else
984
      {
985
43
        cdp->addProof(pfn);
986
      }
987
86
      Assert(trn.getNode() == ret)
988
43
          << "Unexpected rewrite " << args[0] << std::endl
989
43
          << "Got: " << trn.getNode() << std::endl
990
43
          << "Expected: " << ret;
991
    }
992
    else if (idr == MethodId::RW_EVALUATE)
993
    {
994
      // change to evaluate, which is never eliminated
995
      cdp->addStep(eq, PfRule::EVALUATE, {}, {args[0]});
996
    }
997
    else
998
    {
999
      // try to reconstruct as a standalone rewrite
1000
      std::vector<Node> targs;
1001
      targs.push_back(eq);
1002
      targs.push_back(
1003
          builtin::BuiltinProofRuleChecker::mkTheoryIdNode(theoryId));
1004
      // in this case, must be a non-standard rewrite kind
1005
      Assert(args.size() >= 2);
1006
      targs.push_back(args[1]);
1007
      Node eqp = expandMacros(PfRule::THEORY_REWRITE, {}, targs, cdp);
1008
      if (eqp.isNull())
1009
      {
1010
        // don't know how to eliminate
1011
        return Node::null();
1012
      }
1013
    }
1014
43
    if (args[0] == ret)
1015
    {
1016
      // should not be necessary typically
1017
      cdp->addStep(eq, PfRule::REFL, {}, {args[0]});
1018
    }
1019
43
    return eq;
1020
  }
1021
4
  else if (id == PfRule::THEORY_REWRITE)
1022
  {
1023
    Assert(!args.empty());
1024
    Node eq = args[0];
1025
    Assert(eq.getKind() == EQUAL);
1026
    // try to replay theory rewrite
1027
    // first, check that maybe its just an evaluation step
1028
    ProofChecker* pc = d_pnm->getChecker();
1029
    Node ceval =
1030
        pc->checkDebug(PfRule::EVALUATE, {}, {eq[0]}, eq, "smt-proof-pp-debug");
1031
    if (!ceval.isNull() && ceval == eq)
1032
    {
1033
      cdp->addStep(eq, PfRule::EVALUATE, {}, {eq[0]});
1034
      return eq;
1035
    }
1036
    // otherwise no update
1037
    Trace("final-pf-hole") << "hole: " << id << " : " << eq << std::endl;
1038
  }
1039
4
  else if (id == PfRule::MACRO_ARITH_SCALE_SUM_UB)
1040
  {
1041
4
    Debug("macro::arith") << "Expand MACRO_ARITH_SCALE_SUM_UB" << std::endl;
1042
4
    if (Debug.isOn("macro::arith"))
1043
    {
1044
      for (const auto& child : children)
1045
      {
1046
        Debug("macro::arith") << "  child: " << child << std::endl;
1047
      }
1048
      Debug("macro::arith") << "   args: " << args << std::endl;
1049
    }
1050
4
    Assert(args.size() == children.size());
1051
4
    NodeManager* nm = NodeManager::currentNM();
1052
8
    ProofStepBuffer steps{d_pnm->getChecker()};
1053
1054
    // Scale all children, accumulating
1055
8
    std::vector<Node> scaledRels;
1056
16
    for (size_t i = 0; i < children.size(); ++i)
1057
    {
1058
24
      TNode child = children[i];
1059
24
      TNode scalar = args[i];
1060
12
      bool isPos = scalar.getConst<Rational>() > 0;
1061
      Node scalarCmp =
1062
24
          nm->mkNode(isPos ? GT : LT, scalar, nm->mkConst(Rational(0)));
1063
      // (= scalarCmp true)
1064
24
      Node scalarCmpOrTrue = steps.tryStep(PfRule::EVALUATE, {}, {scalarCmp});
1065
12
      Assert(!scalarCmpOrTrue.isNull());
1066
      // scalarCmp
1067
12
      steps.addStep(PfRule::TRUE_ELIM, {scalarCmpOrTrue}, {}, scalarCmp);
1068
      // (and scalarCmp relation)
1069
      Node scalarCmpAndRel =
1070
24
          steps.tryStep(PfRule::AND_INTRO, {scalarCmp, child}, {});
1071
12
      Assert(!scalarCmpAndRel.isNull());
1072
      // (=> (and scalarCmp relation) scaled)
1073
      Node impl =
1074
          steps.tryStep(isPos ? PfRule::ARITH_MULT_POS : PfRule::ARITH_MULT_NEG,
1075
                        {},
1076
24
                        {scalar, child});
1077
12
      Assert(!impl.isNull());
1078
      // scaled
1079
      Node scaled =
1080
24
          steps.tryStep(PfRule::MODUS_PONENS, {scalarCmpAndRel, impl}, {});
1081
12
      Assert(!scaled.isNull());
1082
12
      scaledRels.emplace_back(scaled);
1083
    }
1084
1085
8
    Node sumBounds = steps.tryStep(PfRule::ARITH_SUM_UB, scaledRels, {});
1086
4
    cdp->addSteps(steps);
1087
8
    Debug("macro::arith") << "Expansion done. Proved: " << sumBounds
1088
4
                          << std::endl;
1089
4
    return sumBounds;
1090
  }
1091
  else if (id == PfRule::STRING_INFERENCE)
1092
  {
1093
    // get the arguments
1094
    Node conc;
1095
    InferenceId iid;
1096
    bool isRev;
1097
    std::vector<Node> exp;
1098
    if (strings::InferProofCons::unpackArgs(args, conc, iid, isRev, exp))
1099
    {
1100
      if (strings::InferProofCons::addProofTo(cdp, conc, iid, isRev, exp))
1101
      {
1102
        return conc;
1103
      }
1104
    }
1105
  }
1106
  else if (id == PfRule::BV_BITBLAST)
1107
  {
1108
    bv::BBProof bb(d_env, nullptr, d_pnm, true);
1109
    Node eq = args[0];
1110
    Assert(eq.getKind() == EQUAL);
1111
    bb.bbAtom(eq[0]);
1112
    Node bbAtom = bb.getStoredBBAtom(eq[0]);
1113
    bb.getProofGenerator()->addProofTo(eq[0].eqNode(bbAtom), cdp);
1114
    return eq;
1115
  }
1116
1117
  // TRUST, PREPROCESS, THEORY_LEMMA, THEORY_PREPROCESS?
1118
1119
  return Node::null();
1120
}
1121
1122
2
Node ProofPostprocessCallback::addProofForWitnessForm(Node t, CDProof* cdp)
1123
{
1124
4
  Node tw = SkolemManager::getOriginalForm(t);
1125
2
  Node eq = t.eqNode(tw);
1126
2
  if (t == tw)
1127
  {
1128
    // not necessary, add REFL step
1129
2
    cdp->addStep(eq, PfRule::REFL, {}, {t});
1130
2
    return eq;
1131
  }
1132
  std::shared_ptr<ProofNode> pn = d_wfpm.getProofFor(eq);
1133
  if (pn != nullptr)
1134
  {
1135
    // add the proof
1136
    cdp->addProof(pn);
1137
  }
1138
  else
1139
  {
1140
    Assert(false) << "ProofPostprocessCallback::addProofForWitnessForm: failed "
1141
                     "to add proof for witness form of "
1142
                  << t;
1143
  }
1144
  return eq;
1145
}
1146
1147
94
Node ProofPostprocessCallback::addProofForTrans(
1148
    const std::vector<Node>& tchildren, CDProof* cdp)
1149
{
1150
94
  size_t tsize = tchildren.size();
1151
94
  if (tsize > 1)
1152
  {
1153
2
    Node lhs = tchildren[0][0];
1154
2
    Node rhs = tchildren[tsize - 1][1];
1155
2
    Node eq = lhs.eqNode(rhs);
1156
1
    cdp->addStep(eq, PfRule::TRANS, tchildren, {});
1157
1
    return eq;
1158
  }
1159
93
  else if (tsize == 1)
1160
  {
1161
84
    return tchildren[0];
1162
  }
1163
9
  return Node::null();
1164
}
1165
1166
1
Node ProofPostprocessCallback::addProofForSubsStep(Node var,
1167
                                                   Node subs,
1168
                                                   Node assump,
1169
                                                   CDProof* cdp)
1170
{
1171
  // ensure we have a proof of var = subs
1172
1
  Node veqs = var.eqNode(subs);
1173
1
  if (veqs != assump)
1174
  {
1175
    // should be true intro or false intro
1176
1
    Assert(subs.isConst());
1177
3
    cdp->addStep(
1178
        veqs,
1179
1
        subs.getConst<bool>() ? PfRule::TRUE_INTRO : PfRule::FALSE_INTRO,
1180
        {assump},
1181
1
        {});
1182
  }
1183
1
  return veqs;
1184
}
1185
1186
56
bool ProofPostprocessCallback::addToTransChildren(Node eq,
1187
                                                  std::vector<Node>& tchildren,
1188
                                                  bool isSymm)
1189
{
1190
56
  Assert(!eq.isNull());
1191
56
  Assert(eq.getKind() == kind::EQUAL);
1192
56
  if (eq[0] == eq[1])
1193
  {
1194
23
    return false;
1195
  }
1196
66
  Node equ = isSymm ? eq[1].eqNode(eq[0]) : eq;
1197
33
  Assert(tchildren.empty()
1198
         || (tchildren[tchildren.size() - 1].getKind() == kind::EQUAL
1199
             && tchildren[tchildren.size() - 1][1] == equ[0]));
1200
33
  tchildren.push_back(equ);
1201
33
  return true;
1202
}
1203
1204
143
ProofPostproccess::ProofPostproccess(Env& env,
1205
                                     ProofGenerator* pppg,
1206
                                     rewriter::RewriteDb* rdb,
1207
143
                                     bool updateScopedAssumptions)
1208
    : d_cb(env, pppg, rdb, updateScopedAssumptions),
1209
      // the update merges subproofs
1210
143
      d_updater(env.getProofNodeManager(), d_cb, options::proofPpMerge()),
1211
      d_finalCb(env.getProofNodeManager()),
1212
286
      d_finalizer(env.getProofNodeManager(), d_finalCb)
1213
{
1214
143
}
1215
1216
143
ProofPostproccess::~ProofPostproccess() {}
1217
1218
69
void ProofPostproccess::process(std::shared_ptr<ProofNode> pf)
1219
{
1220
  // Initialize the callback, which computes necessary static information about
1221
  // how to process, including how to process assumptions in pf.
1222
69
  d_cb.initializeUpdate();
1223
  // now, process
1224
69
  d_updater.process(pf);
1225
  // take stats and check pedantic
1226
69
  d_finalCb.initializeUpdate();
1227
69
  d_finalizer.process(pf);
1228
1229
138
  std::stringstream serr;
1230
69
  bool wasPedanticFailure = d_finalCb.wasPedanticFailure(serr);
1231
69
  if (wasPedanticFailure)
1232
  {
1233
    AlwaysAssert(!wasPedanticFailure)
1234
        << "ProofPostproccess::process: pedantic failure:" << std::endl
1235
        << serr.str();
1236
  }
1237
69
}
1238
1239
121
void ProofPostproccess::setEliminateRule(PfRule rule)
1240
{
1241
121
  d_cb.setEliminateRule(rule);
1242
121
}
1243
1244
}  // namespace smt
1245
22746
}  // namespace cvc5