GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/smt/proof_post_processor.cpp Lines: 539 585 92.1 %
Date: 2021-09-17 Branches: 1077 2784 38.7 %

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
3796
ProofPostprocessCallback::ProofPostprocessCallback(Env& env,
39
                                                   ProofGenerator* pppg,
40
                                                   rewriter::RewriteDb* rdb,
41
3796
                                                   bool updateScopedAssumptions)
42
    : d_env(env),
43
3796
      d_pnm(env.getProofNodeManager()),
44
      d_pppg(pppg),
45
      d_wfpm(env.getProofNodeManager()),
46
7592
      d_updateScopedAssumptions(updateScopedAssumptions)
47
{
48
3796
  d_true = NodeManager::currentNM()->mkConst(true);
49
3796
}
50
51
2820
void ProofPostprocessCallback::initializeUpdate()
52
{
53
2820
  d_assumpToProof.clear();
54
2820
  d_wfAssumptions.clear();
55
2820
}
56
57
13068
void ProofPostprocessCallback::setEliminateRule(PfRule rule)
58
{
59
13068
  d_elimRules.insert(rule);
60
13068
}
61
62
2510723
bool ProofPostprocessCallback::shouldUpdate(std::shared_ptr<ProofNode> pn,
63
                                            const std::vector<Node>& fa,
64
                                            bool& continueUpdate)
65
{
66
2510723
  PfRule id = pn->getRule();
67
2510723
  if (d_elimRules.find(id) != d_elimRules.end())
68
  {
69
156912
    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
4707622
  if (id != PfRule::ASSUME
75
6781489
      || (!d_updateScopedAssumptions
76
2353811
          && std::find(fa.begin(), fa.end(), pn->getResult()) != fa.end()))
77
  {
78
4147734
    Trace("smt-proof-pp-debug")
79
2073867
        << "... not updating in-scope assumption " << pn->getResult() << "\n";
80
2073867
    return false;
81
  }
82
279944
  return true;
83
}
84
85
501877
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
1003754
  Trace("smt-proof-pp-debug") << "- Post process " << id << " " << children
93
501877
                              << " / " << args << std::endl;
94
95
501877
  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
559888
    Node f = args[0];
100
559888
    std::shared_ptr<ProofNode> pfn;
101
    std::map<Node, std::shared_ptr<ProofNode>>::iterator it =
102
279944
        d_assumpToProof.find(f);
103
279944
    if (it != d_assumpToProof.end())
104
    {
105
245171
      Trace("smt-proof-pp-debug") << "...already computed" << std::endl;
106
245171
      pfn = it->second;
107
    }
108
    else
109
    {
110
34773
      Trace("smt-proof-pp-debug") << "...get proof" << std::endl;
111
34773
      Assert(d_pppg != nullptr);
112
      // get proof from preprocess proof generator
113
34773
      pfn = d_pppg->getProofFor(f);
114
34773
      Trace("smt-proof-pp-debug") << "...finished get proof" << std::endl;
115
      // print for debugging
116
34773
      if (pfn == nullptr)
117
      {
118
32918
        Trace("smt-proof-pp-debug")
119
16459
            << "...no proof, possibly an input assumption" << std::endl;
120
      }
121
      else
122
      {
123
18314
        Assert(pfn->getResult() == f);
124
18314
        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
34773
      d_assumpToProof[f] = pfn;
132
    }
133
279944
    if (pfn == nullptr || pfn->getRule() == PfRule::ASSUME)
134
    {
135
263323
      Trace("smt-proof-pp-debug") << "...do not add proof" << std::endl;
136
      // no update
137
263323
      return false;
138
    }
139
16621
    Trace("smt-proof-pp-debug") << "...add proof" << std::endl;
140
    // connect the proof
141
16621
    cdp->addProof(pfn);
142
16621
    return true;
143
  }
144
443866
  Node ret = expandMacros(id, children, args, cdp);
145
221933
  Trace("smt-proof-pp-debug") << "...expanded = " << !ret.isNull() << std::endl;
146
221933
  return !ret.isNull();
147
}
148
149
65021
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
65021
  bool continueUpdate = true;
156
65021
  return update(res, id, children, args, cdp, continueUpdate);
157
}
158
159
37982
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
37982
  Trace("smt-proof-pp-debug2") << push;
167
37982
  NodeManager* nm = NodeManager::currentNM();
168
75964
  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
75964
  std::unordered_set<TNode> crowding;
173
75964
  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
75964
  std::vector<size_t> eliminators;
177
1886699
  for (size_t i = 0, size = clauseLits.size(); i < size; ++i)
178
  {
179
5546151
    if (!crowding.count(clauseLits[i])
180
8769941
        && std::find(
181
1611895
               targetClauseLits.begin(), targetClauseLits.end(), clauseLits[i])
182
5072507
               == targetClauseLits.end())
183
    {
184
545718
      Node crowdLit = clauseLits[i];
185
272859
      crowding.insert(crowdLit);
186
272859
      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
5529758
      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
5529758
        if (children[j - 1].getKind() != kind::OR)
199
        {
200
871225
          continue;
201
        }
202
4658533
        uint64_t pivotIndex = 2 * (j - 1);
203
9317066
        if (args[pivotIndex] == children[j - 1]
204
9317066
            || args[pivotIndex].notNode() == children[j - 1])
205
        {
206
15099
          continue;
207
        }
208
13930302
        if (std::find(children[j - 1].begin(), children[j - 1].end(), crowdLit)
209
13930302
            != children[j - 1].end())
210
        {
211
272859
          break;
212
        }
213
      }
214
272859
      Assert(j > 0);
215
272859
      lastInclusion.emplace_back(crowdLit, j - 1);
216
217
272859
      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
1949583
      for (; j < children.size(); ++j)
224
      {
225
1111221
        bool posFirst = args[(2 * j) - 1] == trueNode;
226
1949583
        Node pivot = args[(2 * j)];
227
2222442
        Trace("smt-proof-pp-debug2")
228
1111221
            << "\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
2353374
        if ((crowdLit == pivot && posFirst)
241
2091510
            || (crowdLit.notNode() == pivot && !posFirst)
242
3202731
            || (pivot.notNode() == crowdLit && !posFirst))
243
        {
244
272859
          Trace("smt-proof-pp-debug2") << "\t\tfound it!\n";
245
272859
          eliminators.push_back(j);
246
272859
          break;
247
        }
248
      }
249
272859
      AlwaysAssert(j < children.size());
250
    }
251
  }
252
37982
  Assert(!lastInclusion.empty());
253
  // order map so that we process crowding literals in the order of the clauses
254
  // that last introduce them
255
813929
  auto cmp = [](std::pair<Node, size_t>& a, std::pair<Node, size_t>& b) {
256
813929
    return a.second < b.second;
257
813929
  };
258
37982
  std::sort(lastInclusion.begin(), lastInclusion.end(), cmp);
259
  // order eliminators
260
37982
  std::sort(eliminators.begin(), eliminators.end());
261
37982
  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
37982
  size_t lastElim = 0;
311
37982
  Node lastClause = children[0];
312
75964
  std::vector<Node> childrenRes;
313
75964
  std::vector<Node> childrenResArgs;
314
75964
  Node resPlaceHolder;
315
37982
  size_t nextGuardedElimPos = eliminators[0];
316
  do
317
  {
318
212978
    size_t start = lastElim + 1;
319
212978
    size_t end = nextGuardedElimPos - 1;
320
425956
    Trace("smt-proof-pp-debug2")
321
212978
        << "res with:\n\tlastClause: " << lastClause << "\n\tstart: " << start
322
212978
        << "\n\tend: " << end << "\n";
323
212978
    childrenRes.push_back(lastClause);
324
    // note that the interval of insert is exclusive in the end, so we add 1
325
638934
    childrenRes.insert(childrenRes.end(),
326
425956
                       children.begin() + start,
327
1064890
                       children.begin() + end + 1);
328
638934
    childrenResArgs.insert(childrenResArgs.end(),
329
425956
                           args.begin() + (2 * start) - 1,
330
1064890
                           args.begin() + (2 * end) + 1);
331
212978
    Trace("smt-proof-pp-debug2") << "res children: " << childrenRes << "\n";
332
212978
    Trace("smt-proof-pp-debug2") << "res args: " << childrenResArgs << "\n";
333
425956
    resPlaceHolder = d_pnm->getChecker()->checkDebug(PfRule::CHAIN_RESOLUTION,
334
                                                     childrenRes,
335
                                                     childrenResArgs,
336
425956
                                                     Node::null(),
337
                                                     "");
338
425956
    Trace("smt-proof-pp-debug2")
339
212978
        << "resPlaceHorder: " << resPlaceHolder << "\n";
340
212978
    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
212978
    if (end < children.size() - 1)
345
    {
346
524988
      lastClause = d_pnm->getChecker()->checkDebug(
347
524988
          PfRule::FACTORING, {resPlaceHolder}, {}, Node::null(), "");
348
174996
      if (!lastClause.isNull())
349
      {
350
174996
        cdp->addStep(lastClause, PfRule::FACTORING, {resPlaceHolder}, {});
351
      }
352
      else
353
      {
354
        lastClause = resPlaceHolder;
355
      }
356
174996
      Trace("smt-proof-pp-debug2") << "lastClause: " << lastClause << "\n";
357
    }
358
    else
359
    {
360
37982
      lastClause = resPlaceHolder;
361
37982
      break;
362
    }
363
    // update for next round
364
174996
    childrenRes.clear();
365
174996
    childrenResArgs.clear();
366
174996
    lastElim = end;
367
368
    // find the position of the last inclusion of the next crowded literal
369
174996
    size_t nextCrowdedInclusionPos = lastInclusion.size();
370
1457839
    for (size_t i = 0, size = lastInclusion.size(); i < size; ++i)
371
    {
372
1419857
      if (lastInclusion[i].second > lastElim)
373
      {
374
137014
        nextCrowdedInclusionPos = i;
375
137014
        break;
376
      }
377
    }
378
349992
    Trace("smt-proof-pp-debug2")
379
174996
        << "nextCrowdedInclusion/Pos: "
380
174996
        << lastInclusion[nextCrowdedInclusionPos].second << "/"
381
174996
        << nextCrowdedInclusionPos << "\n";
382
    // if there is none, then the remaining literals will be used in the next
383
    // round
384
174996
    if (nextCrowdedInclusionPos == lastInclusion.size())
385
    {
386
37982
      nextGuardedElimPos = children.size();
387
    }
388
    else
389
    {
390
137014
      nextGuardedElimPos = children.size();
391
1076454
      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
1076454
        if (eliminators[i] > lastInclusion[nextCrowdedInclusionPos].second)
396
        {
397
137014
          nextGuardedElimPos = eliminators[i];
398
137014
          break;
399
        }
400
      }
401
137014
      Assert(nextGuardedElimPos < children.size());
402
    }
403
349992
    Trace("smt-proof-pp-debug2")
404
349992
        << "nextGuardedElimPos: " << nextGuardedElimPos << "\n";
405
  } while (true);
406
75964
  Trace("smt-proof-pp-debug2") << pop;
407
75964
  return lastClause;
408
}
409
410
309618
Node ProofPostprocessCallback::expandMacros(PfRule id,
411
                                            const std::vector<Node>& children,
412
                                            const std::vector<Node>& args,
413
                                            CDProof* cdp)
414
{
415
309618
  if (d_elimRules.find(id) == d_elimRules.end())
416
  {
417
    // not eliminated
418
6
    return Node::null();
419
  }
420
  // macro elimination
421
309612
  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
184514
    std::vector<Node> tchildren;
427
184514
    Node t = args[0];
428
184514
    Node ts;
429
92257
    if (!children.empty())
430
    {
431
27836
      std::vector<Node> sargs;
432
13918
      sargs.push_back(t);
433
13918
      MethodId ids = MethodId::SB_DEFAULT;
434
13918
      if (args.size() >= 2)
435
      {
436
10498
        if (getMethodId(args[1], ids))
437
        {
438
10498
          sargs.push_back(args[1]);
439
        }
440
      }
441
13918
      MethodId ida = MethodId::SBA_SEQUENTIAL;
442
13918
      if (args.size() >= 3)
443
      {
444
2572
        if (getMethodId(args[2], ida))
445
        {
446
2572
          sargs.push_back(args[2]);
447
        }
448
      }
449
13918
      ts = builtin::BuiltinProofRuleChecker::applySubstitution(
450
          t, children, ids, ida);
451
27836
      Trace("smt-proof-pp-debug")
452
13918
          << "...eq intro subs equality is " << t << " == " << ts << ", from "
453
13918
          << ids << " " << ida << std::endl;
454
13918
      if (ts != t)
455
      {
456
16918
        Node eq = t.eqNode(ts);
457
        // apply SUBS proof rule if necessary
458
8459
        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
8459
        tchildren.push_back(eq);
464
      }
465
    }
466
    else
467
    {
468
      // no substitute
469
78339
      ts = t;
470
    }
471
184514
    std::vector<Node> rargs;
472
92257
    rargs.push_back(ts);
473
92257
    MethodId idr = MethodId::RW_REWRITE;
474
92257
    if (args.size() >= 4)
475
    {
476
51
      if (getMethodId(args[3], idr))
477
      {
478
51
        rargs.push_back(args[3]);
479
      }
480
    }
481
92257
    Rewriter* rr = d_env.getRewriter();
482
184514
    Node tr = rr->rewriteViaMethod(ts, idr);
483
184514
    Trace("smt-proof-pp-debug")
484
92257
        << "...eq intro rewrite equality is " << ts << " == " << tr << ", from "
485
92257
        << idr << std::endl;
486
92257
    if (ts != tr)
487
    {
488
113124
      Node eq = ts.eqNode(tr);
489
      // apply REWRITE proof rule
490
56562
      if (!updateInternal(eq, PfRule::REWRITE, {}, rargs, cdp))
491
      {
492
        // if not elimianted, add as step
493
        cdp->addStep(eq, PfRule::REWRITE, {}, rargs);
494
      }
495
56562
      tchildren.push_back(eq);
496
    }
497
92257
    if (t == tr)
498
    {
499
      // typically not necessary, but done to be robust
500
34465
      cdp->addStep(t.eqNode(tr), PfRule::REFL, {}, {t});
501
34465
      return t.eqNode(tr);
502
    }
503
    // must add TRANS if two step
504
57792
    return addProofForTrans(tchildren, cdp);
505
  }
506
217355
  else if (id == PfRule::MACRO_SR_PRED_INTRO)
507
  {
508
15698
    std::vector<Node> tchildren;
509
15698
    std::vector<Node> sargs = args;
510
    // take into account witness form, if necessary
511
7849
    bool reqWitness = d_wfpm.requiresWitnessFormIntro(args[0]);
512
15698
    Trace("smt-proof-pp-debug")
513
7849
        << "...pred intro reqWitness=" << reqWitness << std::endl;
514
    // (TRUE_ELIM
515
    // (TRANS
516
    //    (MACRO_SR_EQ_INTRO <children> :args (t args[1:]))
517
    //    ... proof of apply_SR(t) = toWitness(apply_SR(t)) ...
518
    //    (MACRO_SR_EQ_INTRO {} {toWitness(apply_SR(t))})
519
    // ))
520
    // Notice this is an optimized, one sided version of the expansion of
521
    // MACRO_SR_PRED_TRANSFORM below.
522
    // We call the expandMacros method on MACRO_SR_EQ_INTRO, where notice
523
    // that this rule application is immediately expanded in the recursive
524
    // call and not added to the proof.
525
15698
    Node conc = expandMacros(PfRule::MACRO_SR_EQ_INTRO, children, sargs, cdp);
526
15698
    Trace("smt-proof-pp-debug")
527
7849
        << "...pred intro conclusion is " << conc << std::endl;
528
7849
    Assert(!conc.isNull());
529
7849
    Assert(conc.getKind() == EQUAL);
530
7849
    Assert(conc[0] == args[0]);
531
7849
    tchildren.push_back(conc);
532
7849
    if (reqWitness)
533
    {
534
5760
      Node weq = addProofForWitnessForm(conc[1], cdp);
535
2880
      Trace("smt-proof-pp-debug") << "...weq is " << weq << std::endl;
536
2880
      if (addToTransChildren(weq, tchildren))
537
      {
538
        // toWitness(apply_SR(t)) = apply_SR(toWitness(apply_SR(t)))
539
        // rewrite again, don't need substitution. Also we always use the
540
        // default rewriter, due to the definition of MACRO_SR_PRED_INTRO.
541
5496
        Node weqr = expandMacros(PfRule::MACRO_SR_EQ_INTRO, {}, {weq[1]}, cdp);
542
2748
        addToTransChildren(weqr, tchildren);
543
      }
544
    }
545
    // apply transitivity if necessary
546
15698
    Node eq = addProofForTrans(tchildren, cdp);
547
7849
    Assert(!eq.isNull());
548
7849
    Assert(eq.getKind() == EQUAL);
549
7849
    Assert(eq[0] == args[0]);
550
7849
    Assert(eq[1] == d_true);
551
552
7849
    cdp->addStep(eq[0], PfRule::TRUE_ELIM, {eq}, {});
553
7849
    return eq[0];
554
  }
555
209506
  else if (id == PfRule::MACRO_SR_PRED_ELIM)
556
  {
557
    // (EQ_RESOLVE
558
    //   children[0]
559
    //   (MACRO_SR_EQ_INTRO children[1:] :args children[0] ++ args))
560
4726
    std::vector<Node> schildren(children.begin() + 1, children.end());
561
4726
    std::vector<Node> srargs;
562
2363
    srargs.push_back(children[0]);
563
2363
    srargs.insert(srargs.end(), args.begin(), args.end());
564
4726
    Node conc = expandMacros(PfRule::MACRO_SR_EQ_INTRO, schildren, srargs, cdp);
565
2363
    Assert(!conc.isNull());
566
2363
    Assert(conc.getKind() == EQUAL);
567
2363
    Assert(conc[0] == children[0]);
568
    // apply equality resolve
569
2363
    cdp->addStep(conc[1], PfRule::EQ_RESOLVE, {children[0], conc}, {});
570
2363
    return conc[1];
571
  }
572
207143
  else if (id == PfRule::MACRO_SR_PRED_TRANSFORM)
573
  {
574
    // (EQ_RESOLVE
575
    //   children[0]
576
    //   (TRANS
577
    //      (MACRO_SR_EQ_INTRO children[1:] :args (children[0] args[1:]))
578
    //      ... proof of c = wc
579
    //      (MACRO_SR_EQ_INTRO {} wc)
580
    //      (SYMM
581
    //        (MACRO_SR_EQ_INTRO children[1:] :args <args>)
582
    //        ... proof of a = wa
583
    //        (MACRO_SR_EQ_INTRO {} wa))))
584
    // where
585
    // wa = toWitness(apply_SR(args[0])) and
586
    // wc = toWitness(apply_SR(children[0])).
587
77534
    Trace("smt-proof-pp-debug")
588
38767
        << "Transform " << children[0] << " == " << args[0] << std::endl;
589
38767
    if (CDProof::isSame(children[0], args[0]))
590
    {
591
4373
      Trace("smt-proof-pp-debug") << "...nothing to do" << std::endl;
592
      // nothing to do
593
4373
      return children[0];
594
    }
595
68788
    std::vector<Node> tchildren;
596
68788
    std::vector<Node> schildren(children.begin() + 1, children.end());
597
68788
    std::vector<Node> sargs = args;
598
    // first, compute if we need
599
34394
    bool reqWitness = d_wfpm.requiresWitnessFormTransform(children[0], args[0]);
600
34394
    Trace("smt-proof-pp-debug") << "...reqWitness=" << reqWitness << std::endl;
601
    // convert both sides, in three steps, take symmetry of second chain
602
103182
    for (unsigned r = 0; r < 2; r++)
603
    {
604
137576
      std::vector<Node> tchildrenr;
605
      // first rewrite children[0], then args[0]
606
68788
      sargs[0] = r == 0 ? children[0] : args[0];
607
      // t = apply_SR(t)
608
137576
      Node eq = expandMacros(PfRule::MACRO_SR_EQ_INTRO, schildren, sargs, cdp);
609
137576
      Trace("smt-proof-pp-debug")
610
68788
          << "transform subs_rewrite (" << r << "): " << eq << std::endl;
611
68788
      Assert(!eq.isNull() && eq.getKind() == EQUAL && eq[0] == sargs[0]);
612
68788
      addToTransChildren(eq, tchildrenr);
613
      // apply_SR(t) = toWitness(apply_SR(t))
614
68788
      if (reqWitness)
615
      {
616
23332
        Node weq = addProofForWitnessForm(eq[1], cdp);
617
23332
        Trace("smt-proof-pp-debug")
618
11666
            << "transform toWitness (" << r << "): " << weq << std::endl;
619
11666
        if (addToTransChildren(weq, tchildrenr))
620
        {
621
          // toWitness(apply_SR(t)) = apply_SR(toWitness(apply_SR(t)))
622
          // rewrite again, don't need substitution. Also, we always use the
623
          // default rewriter, due to the definition of MACRO_SR_PRED_TRANSFORM.
624
          Node weqr =
625
11862
              expandMacros(PfRule::MACRO_SR_EQ_INTRO, {}, {weq[1]}, cdp);
626
11862
          Trace("smt-proof-pp-debug") << "transform rewrite_witness (" << r
627
5931
                                      << "): " << weqr << std::endl;
628
5931
          addToTransChildren(weqr, tchildrenr);
629
        }
630
      }
631
137576
      Trace("smt-proof-pp-debug")
632
68788
          << "transform connect (" << r << ")" << std::endl;
633
      // add to overall chain
634
68788
      if (r == 0)
635
      {
636
        // add the current chain to the overall chain
637
34394
        tchildren.insert(tchildren.end(), tchildrenr.begin(), tchildrenr.end());
638
      }
639
      else
640
      {
641
        // add the current chain to cdp
642
68788
        Node eqr = addProofForTrans(tchildrenr, cdp);
643
34394
        if (!eqr.isNull())
644
        {
645
43240
          Trace("smt-proof-pp-debug") << "transform connect sym " << tchildren
646
21620
                                      << " " << eqr << std::endl;
647
          // take symmetry of above and add it to the overall chain
648
21620
          addToTransChildren(eqr, tchildren, true);
649
        }
650
      }
651
137576
      Trace("smt-proof-pp-debug")
652
68788
          << "transform finish (" << r << ")" << std::endl;
653
    }
654
655
    // apply transitivity if necessary
656
68788
    Node eq = addProofForTrans(tchildren, cdp);
657
658
34394
    cdp->addStep(args[0], PfRule::EQ_RESOLVE, {children[0], eq}, {});
659
34394
    return args[0];
660
  }
661
168376
  else if (id == PfRule::MACRO_RESOLUTION
662
168376
           || id == PfRule::MACRO_RESOLUTION_TRUST)
663
  {
664
    // first generate the naive chain_resolution
665
179718
    std::vector<Node> chainResArgs{args.begin() + 1, args.end()};
666
89859
    Node chainConclusion = d_pnm->getChecker()->checkDebug(
667
179718
        PfRule::CHAIN_RESOLUTION, children, chainResArgs, Node::null(), "");
668
89859
    Trace("smt-proof-pp-debug") << "Original conclusion: " << args[0] << "\n";
669
179718
    Trace("smt-proof-pp-debug")
670
89859
        << "chainRes conclusion: " << chainConclusion << "\n";
671
    // There are n cases:
672
    // - if the conclusion is the same, just replace
673
    // - if they have the same literals but in different quantity, add a
674
    //   FACTORING step
675
    // - if the order is not the same, add a REORDERING step
676
    // - if there are literals in chainConclusion that are not in the original
677
    //   conclusion, we need to transform the MACRO_RESOLUTION into a series of
678
    //   CHAIN_RESOLUTION + FACTORING steps, so that we explicitly eliminate all
679
    //   these "crowding" literals. We do this via FACTORING so we avoid adding
680
    //   an exponential number of premises, which would happen if we just
681
    //   repeated in the premises the clauses needed for eliminating crowding
682
    //   literals, which could themselves add crowding literals.
683
89859
    if (chainConclusion == args[0])
684
    {
685
34507
      cdp->addStep(
686
          chainConclusion, PfRule::CHAIN_RESOLUTION, children, chainResArgs);
687
34507
      return chainConclusion;
688
    }
689
55352
    NodeManager* nm = NodeManager::currentNM();
690
    // If we got here, then chainConclusion is NECESSARILY an OR node
691
55352
    Assert(chainConclusion.getKind() == kind::OR);
692
    // get the literals in the chain conclusion
693
    std::vector<Node> chainConclusionLits{chainConclusion.begin(),
694
110704
                                          chainConclusion.end()};
695
    std::set<Node> chainConclusionLitsSet{chainConclusion.begin(),
696
110704
                                          chainConclusion.end()};
697
    // is args[0] a singleton clause? If it's not an OR node, then yes.
698
    // Otherwise, it's only a singleton if it occurs in chainConclusionLitsSet
699
110704
    std::vector<Node> conclusionLits;
700
    // whether conclusion is singleton
701
55352
    if (chainConclusionLitsSet.count(args[0]))
702
    {
703
7723
      conclusionLits.push_back(args[0]);
704
    }
705
    else
706
    {
707
47629
      Assert(args[0].getKind() == kind::OR);
708
47629
      conclusionLits.insert(
709
95258
          conclusionLits.end(), args[0].begin(), args[0].end());
710
    }
711
    std::set<Node> conclusionLitsSet{conclusionLits.begin(),
712
110704
                                     conclusionLits.end()};
713
    // If the sets are different, there are "crowding" literals, i.e. literals
714
    // that were removed by implicit multi-usage of premises in the resolution
715
    // chain.
716
55352
    if (chainConclusionLitsSet != conclusionLitsSet)
717
    {
718
37982
      chainConclusion = eliminateCrowdingLits(
719
          chainConclusionLits, conclusionLits, children, args, cdp);
720
      // update vector of lits. Note that the set is no longer used, so we don't
721
      // need to update it
722
      //
723
      // We need again to check whether chainConclusion is a singleton
724
      // clause. As above, it's a singleton if it's in the original
725
      // chainConclusionLitsSet.
726
37982
      chainConclusionLits.clear();
727
37982
      if (chainConclusionLitsSet.count(chainConclusion))
728
      {
729
        chainConclusionLits.push_back(chainConclusion);
730
      }
731
      else
732
      {
733
37982
        Assert(chainConclusion.getKind() == kind::OR);
734
113946
        chainConclusionLits.insert(chainConclusionLits.end(),
735
                                   chainConclusion.begin(),
736
113946
                                   chainConclusion.end());
737
      }
738
    }
739
    else
740
    {
741
17370
      cdp->addStep(
742
          chainConclusion, PfRule::CHAIN_RESOLUTION, children, chainResArgs);
743
    }
744
110704
    Trace("smt-proof-pp-debug")
745
55352
        << "Conclusion after chain_res/elimCrowd: " << chainConclusion << "\n";
746
110704
    Trace("smt-proof-pp-debug")
747
55352
        << "Conclusion lits: " << chainConclusionLits << "\n";
748
    // Placeholder for running conclusion
749
110704
    Node n = chainConclusion;
750
    // factoring
751
55352
    if (chainConclusionLits.size() != conclusionLits.size())
752
    {
753
      // We build it rather than taking conclusionLits because the order may be
754
      // different
755
106872
      std::vector<Node> factoredLits;
756
106872
      std::unordered_set<TNode> clauseSet;
757
1001434
      for (size_t i = 0, size = chainConclusionLits.size(); i < size; ++i)
758
      {
759
947998
        if (clauseSet.count(chainConclusionLits[i]))
760
        {
761
402211
          continue;
762
        }
763
545787
        factoredLits.push_back(n[i]);
764
545787
        clauseSet.insert(n[i]);
765
      }
766
53436
      Node factored = factoredLits.empty()
767
                          ? nm->mkConst(false)
768
53436
                          : factoredLits.size() == 1
769
7723
                                ? factoredLits[0]
770
168031
                                : nm->mkNode(kind::OR, factoredLits);
771
53436
      cdp->addStep(factored, PfRule::FACTORING, {n}, {});
772
53436
      n = factored;
773
    }
774
    // either same node or n as a clause
775
55352
    Assert(n == args[0] || n.getKind() == kind::OR);
776
    // reordering
777
55352
    if (n != args[0])
778
    {
779
37825
      cdp->addStep(args[0], PfRule::REORDERING, {n}, {args[0]});
780
    }
781
55352
    return args[0];
782
  }
783
78517
  else if (id == PfRule::SUBS)
784
  {
785
8459
    NodeManager* nm = NodeManager::currentNM();
786
    // Notice that a naive way to reconstruct SUBS is to do a term conversion
787
    // proof for each substitution.
788
    // The proof of f(a) * { a -> g(b) } * { b -> c } = f(g(c)) is:
789
    //   TRANS( CONG{f}( a=g(b) ), CONG{f}( CONG{g}( b=c ) ) )
790
    // Notice that more optimal proofs are possible that do a single traversal
791
    // over t. This is done by applying later substitutions to the range of
792
    // previous substitutions, until a final simultaneous substitution is
793
    // applied to t.  For instance, in the above example, we first prove:
794
    //   CONG{g}( b = c )
795
    // by applying the second substitution { b -> c } to the range of the first,
796
    // giving us a proof of g(b)=g(c). We then construct the updated proof
797
    // by tranitivity:
798
    //   TRANS( a=g(b), CONG{g}( b=c ) )
799
    // We then apply the substitution { a -> g(c), b -> c } to f(a), to obtain:
800
    //   CONG{f}( TRANS( a=g(b), CONG{g}( b=c ) ) )
801
    // which notice is more compact than the proof above.
802
16918
    Node t = args[0];
803
    // get the kind of substitution
804
8459
    MethodId ids = MethodId::SB_DEFAULT;
805
8459
    if (args.size() >= 2)
806
    {
807
6489
      getMethodId(args[1], ids);
808
    }
809
8459
    MethodId ida = MethodId::SBA_SEQUENTIAL;
810
8459
    if (args.size() >= 3)
811
    {
812
2526
      getMethodId(args[2], ida);
813
    }
814
16918
    std::vector<std::shared_ptr<CDProof>> pfs;
815
16918
    std::vector<TNode> vsList;
816
16918
    std::vector<TNode> ssList;
817
16918
    std::vector<TNode> fromList;
818
16918
    std::vector<ProofGenerator*> pgs;
819
    // first, compute the entire substitution
820
559405
    for (size_t i = 0, nchild = children.size(); i < nchild; i++)
821
    {
822
      // get the substitution
823
1101892
      builtin::BuiltinProofRuleChecker::getSubstitutionFor(
824
550946
          children[i], vsList, ssList, fromList, ids);
825
      // ensure proofs for each formula in fromList
826
550946
      if (children[i].getKind() == AND && ids == MethodId::SB_DEFAULT)
827
      {
828
        for (size_t j = 0, nchildi = children[i].getNumChildren(); j < nchildi;
829
             j++)
830
        {
831
          Node nodej = nm->mkConst(Rational(j));
832
          cdp->addStep(
833
              children[i][j], PfRule::AND_ELIM, {children[i]}, {nodej});
834
        }
835
      }
836
    }
837
16918
    std::vector<Node> vvec;
838
16918
    std::vector<Node> svec;
839
559405
    for (size_t i = 0, nvs = vsList.size(); i < nvs; i++)
840
    {
841
      // Note we process in forward order, since later substitution should be
842
      // applied to earlier ones, and the last child of a SUBS is processed
843
      // first.
844
1101613
      TNode var = vsList[i];
845
1101613
      TNode subs = ssList[i];
846
1101613
      TNode childFrom = fromList[i];
847
1101892
      Trace("smt-proof-pp-debug")
848
550946
          << "...process " << var << " -> " << subs << " (" << childFrom << ", "
849
550946
          << ids << ")" << std::endl;
850
      // apply the current substitution to the range
851
550946
      if (!vvec.empty() && ida == MethodId::SBA_SEQUENTIAL)
852
      {
853
        Node ss =
854
1915
            subs.substitute(vvec.begin(), vvec.end(), svec.begin(), svec.end());
855
1097
        if (ss != subs)
856
        {
857
558
          Trace("smt-proof-pp-debug")
858
279
              << "......updated to " << var << " -> " << ss
859
279
              << " based on previous substitution" << std::endl;
860
          // make the proof for the tranitivity step
861
558
          std::shared_ptr<CDProof> pf = std::make_shared<CDProof>(d_pnm);
862
279
          pfs.push_back(pf);
863
          // prove the updated substitution
864
          TConvProofGenerator tcg(d_pnm,
865
                                  nullptr,
866
                                  TConvPolicy::ONCE,
867
                                  TConvCachePolicy::NEVER,
868
                                  "nested_SUBS_TConvProofGenerator",
869
                                  nullptr,
870
558
                                  true);
871
          // add previous rewrite steps
872
996
          for (unsigned j = 0, nvars = vvec.size(); j < nvars; j++)
873
          {
874
            // substitutions are pre-rewrites
875
717
            tcg.addRewriteStep(vvec[j], svec[j], pgs[j], true);
876
          }
877
          // get the proof for the update to the current substitution
878
558
          Node seqss = subs.eqNode(ss);
879
558
          std::shared_ptr<ProofNode> pfn = tcg.getProofFor(seqss);
880
279
          Assert(pfn != nullptr);
881
          // add the proof
882
279
          pf->addProof(pfn);
883
          // get proof for childFrom from cdp
884
279
          pfn = cdp->getProofFor(childFrom);
885
279
          pf->addProof(pfn);
886
          // ensure we have a proof of var = subs
887
558
          Node veqs = addProofForSubsStep(var, subs, childFrom, pf.get());
888
          // transitivity
889
279
          pf->addStep(var.eqNode(ss), PfRule::TRANS, {veqs, seqss}, {});
890
          // add to the substitution
891
279
          vvec.push_back(var);
892
279
          svec.push_back(ss);
893
279
          pgs.push_back(pf.get());
894
279
          continue;
895
        }
896
      }
897
      // Just use equality from CDProof, but ensure we have a proof in cdp.
898
      // This may involve a TRUE_INTRO/FALSE_INTRO if the substitution step
899
      // uses the assumption childFrom as a Boolean assignment (e.g.
900
      // childFrom = true if we are using MethodId::SB_LITERAL).
901
550667
      addProofForSubsStep(var, subs, childFrom, cdp);
902
550667
      vvec.push_back(var);
903
550667
      svec.push_back(subs);
904
550667
      pgs.push_back(cdp);
905
    }
906
    // should be implied by the substitution now
907
8459
    TConvPolicy tcpolicy = ida == MethodId::SBA_FIXPOINT ? TConvPolicy::FIXPOINT
908
                                                         : TConvPolicy::ONCE;
909
    TConvProofGenerator tcpg(d_pnm,
910
                             nullptr,
911
                             tcpolicy,
912
                             TConvCachePolicy::NEVER,
913
                             "SUBS_TConvProofGenerator",
914
                             nullptr,
915
16918
                             true);
916
559405
    for (unsigned j = 0, nvars = vvec.size(); j < nvars; j++)
917
    {
918
      // substitutions are pre-rewrites
919
550946
      tcpg.addRewriteStep(vvec[j], svec[j], pgs[j], true);
920
    }
921
    // add the proof constructed by the term conversion utility
922
16918
    std::shared_ptr<ProofNode> pfn = tcpg.getProofForRewriting(t);
923
16918
    Node eq = pfn->getResult();
924
    Node ts = builtin::BuiltinProofRuleChecker::applySubstitution(
925
16918
        t, children, ids, ida);
926
16918
    Node eqq = t.eqNode(ts);
927
8459
    if (eq != eqq)
928
    {
929
      pfn = nullptr;
930
    }
931
    // should give a proof, if not, then tcpg does not agree with the
932
    // substitution.
933
8459
    if (pfn == nullptr)
934
    {
935
      Warning() << "resort to TRUST_SUBS" << std::endl
936
                << eq << std::endl
937
                << eqq << std::endl
938
                << "from " << children << " applied to " << t << std::endl;
939
      cdp->addStep(eqq, PfRule::TRUST_SUBS, {}, {eqq});
940
    }
941
    else
942
    {
943
8459
      cdp->addProof(pfn);
944
    }
945
8459
    return eqq;
946
  }
947
70058
  else if (id == PfRule::REWRITE)
948
  {
949
    // get the kind of rewrite
950
62648
    MethodId idr = MethodId::RW_REWRITE;
951
62648
    TheoryId theoryId = Theory::theoryOf(args[0]);
952
62648
    if (args.size() >= 2)
953
    {
954
43
      getMethodId(args[1], idr);
955
    }
956
62648
    Rewriter* rr = d_env.getRewriter();
957
125296
    Node ret = rr->rewriteViaMethod(args[0], idr);
958
125296
    Node eq = args[0].eqNode(ret);
959
62648
    if (idr == MethodId::RW_REWRITE || idr == MethodId::RW_REWRITE_EQ_EXT)
960
    {
961
      // rewrites from theory::Rewriter
962
62642
      bool isExtEq = (idr == MethodId::RW_REWRITE_EQ_EXT);
963
      // use rewrite with proof interface
964
125284
      TrustNode trn = rr->rewriteWithProof(args[0], isExtEq);
965
125284
      std::shared_ptr<ProofNode> pfn = trn.toProofNode();
966
62642
      if (pfn == nullptr)
967
      {
968
74
        Trace("smt-proof-pp-debug")
969
37
            << "Use TRUST_REWRITE for " << eq << std::endl;
970
        // did not have a proof of rewriting, probably isExtEq is true
971
37
        if (isExtEq)
972
        {
973
          // update to THEORY_REWRITE with idr
974
37
          Assert(args.size() >= 1);
975
74
          Node tid = builtin::BuiltinProofRuleChecker::mkTheoryIdNode(theoryId);
976
37
          cdp->addStep(eq, PfRule::THEORY_REWRITE, {}, {eq, tid, args[1]});
977
        }
978
        else
979
        {
980
          // this should never be applied
981
          cdp->addStep(eq, PfRule::TRUST_REWRITE, {}, {eq});
982
        }
983
      }
984
      else
985
      {
986
62605
        cdp->addProof(pfn);
987
      }
988
125284
      Assert(trn.getNode() == ret)
989
62642
          << "Unexpected rewrite " << args[0] << std::endl
990
62642
          << "Got: " << trn.getNode() << std::endl
991
62642
          << "Expected: " << ret;
992
    }
993
6
    else if (idr == MethodId::RW_EVALUATE)
994
    {
995
      // change to evaluate, which is never eliminated
996
      cdp->addStep(eq, PfRule::EVALUATE, {}, {args[0]});
997
    }
998
    else
999
    {
1000
      // try to reconstruct as a standalone rewrite
1001
6
      std::vector<Node> targs;
1002
6
      targs.push_back(eq);
1003
6
      targs.push_back(
1004
12
          builtin::BuiltinProofRuleChecker::mkTheoryIdNode(theoryId));
1005
      // in this case, must be a non-standard rewrite kind
1006
6
      Assert(args.size() >= 2);
1007
6
      targs.push_back(args[1]);
1008
6
      Node eqp = expandMacros(PfRule::THEORY_REWRITE, {}, targs, cdp);
1009
6
      if (eqp.isNull())
1010
      {
1011
        // don't know how to eliminate
1012
6
        return Node::null();
1013
      }
1014
    }
1015
62642
    if (args[0] == ret)
1016
    {
1017
      // should not be necessary typically
1018
      cdp->addStep(eq, PfRule::REFL, {}, {args[0]});
1019
    }
1020
62642
    return eq;
1021
  }
1022
7410
  else if (id == PfRule::THEORY_REWRITE)
1023
  {
1024
    Assert(!args.empty());
1025
    Node eq = args[0];
1026
    Assert(eq.getKind() == EQUAL);
1027
    // try to replay theory rewrite
1028
    // first, check that maybe its just an evaluation step
1029
    ProofChecker* pc = d_pnm->getChecker();
1030
    Node ceval =
1031
        pc->checkDebug(PfRule::EVALUATE, {}, {eq[0]}, eq, "smt-proof-pp-debug");
1032
    if (!ceval.isNull() && ceval == eq)
1033
    {
1034
      cdp->addStep(eq, PfRule::EVALUATE, {}, {eq[0]});
1035
      return eq;
1036
    }
1037
    // otherwise no update
1038
    Trace("final-pf-hole") << "hole: " << id << " : " << eq << std::endl;
1039
  }
1040
7410
  else if (id == PfRule::MACRO_ARITH_SCALE_SUM_UB)
1041
  {
1042
4861
    Debug("macro::arith") << "Expand MACRO_ARITH_SCALE_SUM_UB" << std::endl;
1043
4861
    if (Debug.isOn("macro::arith"))
1044
    {
1045
      for (const auto& child : children)
1046
      {
1047
        Debug("macro::arith") << "  child: " << child << std::endl;
1048
      }
1049
      Debug("macro::arith") << "   args: " << args << std::endl;
1050
    }
1051
4861
    Assert(args.size() == children.size());
1052
4861
    NodeManager* nm = NodeManager::currentNM();
1053
9722
    ProofStepBuffer steps{d_pnm->getChecker()};
1054
1055
    // Scale all children, accumulating
1056
9722
    std::vector<Node> scaledRels;
1057
31071
    for (size_t i = 0; i < children.size(); ++i)
1058
    {
1059
52420
      TNode child = children[i];
1060
52420
      TNode scalar = args[i];
1061
26210
      bool isPos = scalar.getConst<Rational>() > 0;
1062
      Node scalarCmp =
1063
52420
          nm->mkNode(isPos ? GT : LT, scalar, nm->mkConst(Rational(0)));
1064
      // (= scalarCmp true)
1065
52420
      Node scalarCmpOrTrue = steps.tryStep(PfRule::EVALUATE, {}, {scalarCmp});
1066
26210
      Assert(!scalarCmpOrTrue.isNull());
1067
      // scalarCmp
1068
26210
      steps.addStep(PfRule::TRUE_ELIM, {scalarCmpOrTrue}, {}, scalarCmp);
1069
      // (and scalarCmp relation)
1070
      Node scalarCmpAndRel =
1071
52420
          steps.tryStep(PfRule::AND_INTRO, {scalarCmp, child}, {});
1072
26210
      Assert(!scalarCmpAndRel.isNull());
1073
      // (=> (and scalarCmp relation) scaled)
1074
      Node impl =
1075
          steps.tryStep(isPos ? PfRule::ARITH_MULT_POS : PfRule::ARITH_MULT_NEG,
1076
                        {},
1077
52420
                        {scalar, child});
1078
26210
      Assert(!impl.isNull());
1079
      // scaled
1080
      Node scaled =
1081
52420
          steps.tryStep(PfRule::MODUS_PONENS, {scalarCmpAndRel, impl}, {});
1082
26210
      Assert(!scaled.isNull());
1083
26210
      scaledRels.emplace_back(scaled);
1084
    }
1085
1086
9722
    Node sumBounds = steps.tryStep(PfRule::ARITH_SUM_UB, scaledRels, {});
1087
4861
    cdp->addSteps(steps);
1088
9722
    Debug("macro::arith") << "Expansion done. Proved: " << sumBounds
1089
4861
                          << std::endl;
1090
4861
    return sumBounds;
1091
  }
1092
2549
  else if (id == PfRule::STRING_INFERENCE)
1093
  {
1094
    // get the arguments
1095
987
    Node conc;
1096
    InferenceId iid;
1097
    bool isRev;
1098
987
    std::vector<Node> exp;
1099
987
    if (strings::InferProofCons::unpackArgs(args, conc, iid, isRev, exp))
1100
    {
1101
987
      if (strings::InferProofCons::addProofTo(cdp, conc, iid, isRev, exp))
1102
      {
1103
987
        return conc;
1104
      }
1105
    }
1106
  }
1107
1562
  else if (id == PfRule::BV_BITBLAST)
1108
  {
1109
3124
    bv::BBProof bb(d_env, nullptr, d_pnm, true);
1110
3124
    Node eq = args[0];
1111
1562
    Assert(eq.getKind() == EQUAL);
1112
1562
    bb.bbAtom(eq[0]);
1113
3124
    Node bbAtom = bb.getStoredBBAtom(eq[0]);
1114
1562
    bb.getProofGenerator()->addProofTo(eq[0].eqNode(bbAtom), cdp);
1115
1562
    return eq;
1116
  }
1117
1118
  // TRUST, PREPROCESS, THEORY_LEMMA, THEORY_PREPROCESS?
1119
1120
  return Node::null();
1121
}
1122
1123
14546
Node ProofPostprocessCallback::addProofForWitnessForm(Node t, CDProof* cdp)
1124
{
1125
29092
  Node tw = SkolemManager::getOriginalForm(t);
1126
14546
  Node eq = t.eqNode(tw);
1127
14546
  if (t == tw)
1128
  {
1129
    // not necessary, add REFL step
1130
5867
    cdp->addStep(eq, PfRule::REFL, {}, {t});
1131
5867
    return eq;
1132
  }
1133
17358
  std::shared_ptr<ProofNode> pn = d_wfpm.getProofFor(eq);
1134
8679
  if (pn != nullptr)
1135
  {
1136
    // add the proof
1137
8679
    cdp->addProof(pn);
1138
  }
1139
  else
1140
  {
1141
    Assert(false) << "ProofPostprocessCallback::addProofForWitnessForm: failed "
1142
                     "to add proof for witness form of "
1143
                  << t;
1144
  }
1145
8679
  return eq;
1146
}
1147
1148
134429
Node ProofPostprocessCallback::addProofForTrans(
1149
    const std::vector<Node>& tchildren, CDProof* cdp)
1150
{
1151
134429
  size_t tsize = tchildren.size();
1152
134429
  if (tsize > 1)
1153
  {
1154
34726
    Node lhs = tchildren[0][0];
1155
34726
    Node rhs = tchildren[tsize - 1][1];
1156
34726
    Node eq = lhs.eqNode(rhs);
1157
17363
    cdp->addStep(eq, PfRule::TRANS, tchildren, {});
1158
17363
    return eq;
1159
  }
1160
117066
  else if (tsize == 1)
1161
  {
1162
104292
    return tchildren[0];
1163
  }
1164
12774
  return Node::null();
1165
}
1166
1167
550946
Node ProofPostprocessCallback::addProofForSubsStep(Node var,
1168
                                                   Node subs,
1169
                                                   Node assump,
1170
                                                   CDProof* cdp)
1171
{
1172
  // ensure we have a proof of var = subs
1173
550946
  Node veqs = var.eqNode(subs);
1174
550946
  if (veqs != assump)
1175
  {
1176
    // should be true intro or false intro
1177
3963
    Assert(subs.isConst());
1178
11889
    cdp->addStep(
1179
        veqs,
1180
3963
        subs.getConst<bool>() ? PfRule::TRUE_INTRO : PfRule::FALSE_INTRO,
1181
        {assump},
1182
3963
        {});
1183
  }
1184
550946
  return veqs;
1185
}
1186
1187
113633
bool ProofPostprocessCallback::addToTransChildren(Node eq,
1188
                                                  std::vector<Node>& tchildren,
1189
                                                  bool isSymm)
1190
{
1191
113633
  Assert(!eq.isNull());
1192
113633
  Assert(eq.getKind() == kind::EQUAL);
1193
113633
  if (eq[0] == eq[1])
1194
  {
1195
39683
    return false;
1196
  }
1197
147900
  Node equ = isSymm ? eq[1].eqNode(eq[0]) : eq;
1198
73950
  Assert(tchildren.empty()
1199
         || (tchildren[tchildren.size() - 1].getKind() == kind::EQUAL
1200
             && tchildren[tchildren.size() - 1][1] == equ[0]));
1201
73950
  tchildren.push_back(equ);
1202
73950
  return true;
1203
}
1204
1205
3796
ProofPostproccess::ProofPostproccess(Env& env,
1206
                                     ProofGenerator* pppg,
1207
                                     rewriter::RewriteDb* rdb,
1208
3796
                                     bool updateScopedAssumptions)
1209
    : d_cb(env, pppg, rdb, updateScopedAssumptions),
1210
      // the update merges subproofs
1211
3796
      d_updater(env.getProofNodeManager(), d_cb, options::proofPpMerge()),
1212
      d_finalCb(env.getProofNodeManager()),
1213
7592
      d_finalizer(env.getProofNodeManager(), d_finalCb)
1214
{
1215
3796
}
1216
1217
3796
ProofPostproccess::~ProofPostproccess() {}
1218
1219
2820
void ProofPostproccess::process(std::shared_ptr<ProofNode> pf)
1220
{
1221
  // Initialize the callback, which computes necessary static information about
1222
  // how to process, including how to process assumptions in pf.
1223
2820
  d_cb.initializeUpdate();
1224
  // now, process
1225
2820
  d_updater.process(pf);
1226
  // take stats and check pedantic
1227
2820
  d_finalCb.initializeUpdate();
1228
2820
  d_finalizer.process(pf);
1229
1230
5640
  std::stringstream serr;
1231
2820
  bool wasPedanticFailure = d_finalCb.wasPedanticFailure(serr);
1232
2820
  if (wasPedanticFailure)
1233
  {
1234
    AlwaysAssert(!wasPedanticFailure)
1235
        << "ProofPostproccess::process: pedantic failure:" << std::endl
1236
        << serr.str();
1237
  }
1238
2820
}
1239
1240
13068
void ProofPostproccess::setEliminateRule(PfRule rule)
1241
{
1242
13068
  d_cb.setEliminateRule(rule);
1243
13068
}
1244
1245
}  // namespace smt
1246
29577
}  // namespace cvc5