GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/smt/proof_post_processor.cpp Lines: 524 570 91.9 %
Date: 2021-08-20 Branches: 1057 2724 38.8 %

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 "options/smt_options.h"
21
#include "preprocessing/assertion_pipeline.h"
22
#include "proof/proof_node_manager.h"
23
#include "smt/smt_engine.h"
24
#include "theory/builtin/proof_checker.h"
25
#include "theory/bv/bitblast/proof_bitblaster.h"
26
#include "theory/rewriter.h"
27
#include "theory/theory.h"
28
#include "util/rational.h"
29
30
using namespace cvc5::kind;
31
using namespace cvc5::theory;
32
33
namespace cvc5 {
34
namespace smt {
35
36
3771
ProofPostprocessCallback::ProofPostprocessCallback(ProofNodeManager* pnm,
37
                                                   SmtEngine* smte,
38
                                                   ProofGenerator* pppg,
39
3771
                                                   bool updateScopedAssumptions)
40
    : d_pnm(pnm),
41
      d_smte(smte),
42
      d_pppg(pppg),
43
      d_wfpm(pnm),
44
3771
      d_updateScopedAssumptions(updateScopedAssumptions)
45
{
46
3771
  d_true = NodeManager::currentNM()->mkConst(true);
47
3771
}
48
49
2805
void ProofPostprocessCallback::initializeUpdate()
50
{
51
2805
  d_assumpToProof.clear();
52
2805
  d_wfAssumptions.clear();
53
2805
}
54
55
11780
void ProofPostprocessCallback::setEliminateRule(PfRule rule)
56
{
57
11780
  d_elimRules.insert(rule);
58
11780
}
59
60
2434070
bool ProofPostprocessCallback::shouldUpdate(std::shared_ptr<ProofNode> pn,
61
                                            const std::vector<Node>& fa,
62
                                            bool& continueUpdate)
63
{
64
2434070
  PfRule id = pn->getRule();
65
2434070
  if (d_elimRules.find(id) != d_elimRules.end())
66
  {
67
157095
    return true;
68
  }
69
  // other than elimination rules, we always update assumptions as long as
70
  // d_updateScopedAssumptions is true or they are *not* in scope, i.e., not in
71
  // fa
72
4553950
  if (id != PfRule::ASSUME
73
6553181
      || (!d_updateScopedAssumptions
74
2276975
          && std::find(fa.begin(), fa.end(), pn->getResult()) != fa.end()))
75
  {
76
3998462
    Trace("smt-proof-pp-debug")
77
1999231
        << "... not updating in-scope assumption " << pn->getResult() << "\n";
78
1999231
    return false;
79
  }
80
277744
  return true;
81
}
82
83
499083
bool ProofPostprocessCallback::update(Node res,
84
                                      PfRule id,
85
                                      const std::vector<Node>& children,
86
                                      const std::vector<Node>& args,
87
                                      CDProof* cdp,
88
                                      bool& continueUpdate)
89
{
90
998166
  Trace("smt-proof-pp-debug") << "- Post process " << id << " " << children
91
499083
                              << " / " << args << std::endl;
92
93
499083
  if (id == PfRule::ASSUME)
94
  {
95
    // we cache based on the assumption node, not the proof node, since there
96
    // may be multiple occurrences of the same node.
97
555488
    Node f = args[0];
98
555488
    std::shared_ptr<ProofNode> pfn;
99
    std::map<Node, std::shared_ptr<ProofNode>>::iterator it =
100
277744
        d_assumpToProof.find(f);
101
277744
    if (it != d_assumpToProof.end())
102
    {
103
243442
      Trace("smt-proof-pp-debug") << "...already computed" << std::endl;
104
243442
      pfn = it->second;
105
    }
106
    else
107
    {
108
34302
      Trace("smt-proof-pp-debug") << "...get proof" << std::endl;
109
34302
      Assert(d_pppg != nullptr);
110
      // get proof from preprocess proof generator
111
34302
      pfn = d_pppg->getProofFor(f);
112
34302
      Trace("smt-proof-pp-debug") << "...finished get proof" << std::endl;
113
      // print for debugging
114
34302
      if (pfn == nullptr)
115
      {
116
32012
        Trace("smt-proof-pp-debug")
117
16006
            << "...no proof, possibly an input assumption" << std::endl;
118
      }
119
      else
120
      {
121
18296
        Assert(pfn->getResult() == f);
122
18296
        if (Trace.isOn("smt-proof-pp"))
123
        {
124
          Trace("smt-proof-pp")
125
              << "=== Connect proof for preprocessing: " << f << std::endl;
126
          Trace("smt-proof-pp") << *pfn.get() << std::endl;
127
        }
128
      }
129
34302
      d_assumpToProof[f] = pfn;
130
    }
131
277744
    if (pfn == nullptr || pfn->getRule() == PfRule::ASSUME)
132
    {
133
260991
      Trace("smt-proof-pp-debug") << "...do not add proof" << std::endl;
134
      // no update
135
260991
      return false;
136
    }
137
16753
    Trace("smt-proof-pp-debug") << "...add proof" << std::endl;
138
    // connect the proof
139
16753
    cdp->addProof(pfn);
140
16753
    return true;
141
  }
142
442678
  Node ret = expandMacros(id, children, args, cdp);
143
221339
  Trace("smt-proof-pp-debug") << "...expanded = " << !ret.isNull() << std::endl;
144
221339
  return !ret.isNull();
145
}
146
147
64244
bool ProofPostprocessCallback::updateInternal(Node res,
148
                                              PfRule id,
149
                                              const std::vector<Node>& children,
150
                                              const std::vector<Node>& args,
151
                                              CDProof* cdp)
152
{
153
64244
  bool continueUpdate = true;
154
64244
  return update(res, id, children, args, cdp, continueUpdate);
155
}
156
157
38391
Node ProofPostprocessCallback::eliminateCrowdingLits(
158
    const std::vector<Node>& clauseLits,
159
    const std::vector<Node>& targetClauseLits,
160
    const std::vector<Node>& children,
161
    const std::vector<Node>& args,
162
    CDProof* cdp)
163
{
164
38391
  Trace("smt-proof-pp-debug2") << push;
165
38391
  NodeManager* nm = NodeManager::currentNM();
166
76782
  Node trueNode = nm->mkConst(true);
167
  // get crowding lits and the position of the last clause that includes
168
  // them. The factoring step must be added after the last inclusion and before
169
  // its elimination.
170
76782
  std::unordered_set<TNode> crowding;
171
76782
  std::vector<std::pair<Node, size_t>> lastInclusion;
172
  // positions of eliminators of crowding literals, which are the positions of
173
  // the clauses that eliminate crowding literals *after* their last inclusion
174
76782
  std::vector<size_t> eliminators;
175
1932948
  for (size_t i = 0, size = clauseLits.size(); i < size; ++i)
176
  {
177
5683671
    if (!crowding.count(clauseLits[i])
178
8971833
        && std::find(
179
1644081
               targetClauseLits.begin(), targetClauseLits.end(), clauseLits[i])
180
5182719
               == targetClauseLits.end())
181
    {
182
568522
      Node crowdLit = clauseLits[i];
183
284261
      crowding.insert(crowdLit);
184
284261
      Trace("smt-proof-pp-debug2") << "crowding lit " << crowdLit << "\n";
185
      // found crowding lit, now get its last inclusion position, which is the
186
      // position of the last resolution link that introduces the crowding
187
      // literal. Note that this position has to be *before* the last link, as a
188
      // link *after* the last inclusion must eliminate the crowding literal.
189
      size_t j;
190
6448134
      for (j = children.size() - 1; j > 0; --j)
191
      {
192
        // notice that only non-singleton clauses may be introducing the
193
        // crowding literal, so we only care about non-singleton OR nodes. We
194
        // check then against the kind and whether the whole OR node occurs as a
195
        // pivot of the respective resolution
196
6448134
        if (children[j - 1].getKind() != kind::OR)
197
        {
198
876138
          continue;
199
        }
200
5571996
        uint64_t pivotIndex = 2 * (j - 1);
201
11143992
        if (args[pivotIndex] == children[j - 1]
202
11143992
            || args[pivotIndex].notNode() == children[j - 1])
203
        {
204
15096
          continue;
205
        }
206
16670700
        if (std::find(children[j - 1].begin(), children[j - 1].end(), crowdLit)
207
16670700
            != children[j - 1].end())
208
        {
209
284261
          break;
210
        }
211
      }
212
284261
      Assert(j > 0);
213
284261
      lastInclusion.emplace_back(crowdLit, j - 1);
214
215
284261
      Trace("smt-proof-pp-debug2") << "last inc " << j - 1 << "\n";
216
      // get elimination position, starting from the following link as the last
217
      // inclusion one. The result is the last (in the chain, but first from
218
      // this point on) resolution link that eliminates the crowding literal. A
219
      // literal l is eliminated by a link if it contains a literal l' with
220
      // opposite polarity to l.
221
2408611
      for (; j < children.size(); ++j)
222
      {
223
1346436
        bool posFirst = args[(2 * j) - 1] == trueNode;
224
2408611
        Node pivot = args[(2 * j)];
225
2692872
        Trace("smt-proof-pp-debug2")
226
1346436
            << "\tcheck w/ args " << posFirst << " / " << pivot << "\n";
227
        // To eliminate the crowding literal (crowdLit), the clause must contain
228
        // it with opposite polarity. There are three successful cases,
229
        // according to the pivot and its sign
230
        //
231
        // - crowdLit is the same as the pivot and posFirst is true, which means
232
        //   that the clause contains its negation and eliminates it
233
        //
234
        // - crowdLit is the negation of the pivot and posFirst is false, so the
235
        //   clause contains the node whose negation is crowdLit. Note that this
236
        //   case may either be crowdLit.notNode() == pivot or crowdLit ==
237
        //   pivot.notNode().
238
2833950
        if ((crowdLit == pivot && posFirst)
239
2551794
            || (crowdLit.notNode() == pivot && !posFirst)
240
3898230
            || (pivot.notNode() == crowdLit && !posFirst))
241
        {
242
284261
          Trace("smt-proof-pp-debug2") << "\t\tfound it!\n";
243
284261
          eliminators.push_back(j);
244
284261
          break;
245
        }
246
      }
247
284261
      AlwaysAssert(j < children.size());
248
    }
249
  }
250
38391
  Assert(!lastInclusion.empty());
251
  // order map so that we process crowding literals in the order of the clauses
252
  // that last introduce them
253
875630
  auto cmp = [](std::pair<Node, size_t>& a, std::pair<Node, size_t>& b) {
254
875630
    return a.second < b.second;
255
875630
  };
256
38391
  std::sort(lastInclusion.begin(), lastInclusion.end(), cmp);
257
  // order eliminators
258
38391
  std::sort(eliminators.begin(), eliminators.end());
259
38391
  if (Trace.isOn("smt-proof-pp-debug"))
260
  {
261
    Trace("smt-proof-pp-debug") << "crowding lits last inclusion:\n";
262
    for (const auto& pair : lastInclusion)
263
    {
264
      Trace("smt-proof-pp-debug")
265
          << "\t- [" << pair.second << "] : " << pair.first << "\n";
266
    }
267
    Trace("smt-proof-pp-debug") << "eliminators:";
268
    for (size_t elim : eliminators)
269
    {
270
      Trace("smt-proof-pp-debug") << " " << elim;
271
    }
272
    Trace("smt-proof-pp-debug") << "\n";
273
  }
274
  // TODO (cvc4-wishues/issues/77): implement also simpler version and compare
275
  //
276
  // We now start to break the chain, one step at a time. Naively this breaking
277
  // down would be one resolution/factoring to each crowding literal, but we can
278
  // merge some of the cases. Effectively we do the following:
279
  //
280
  //
281
  // lastClause   children[start] ... children[end]
282
  // ---------------------------------------------- CHAIN_RES
283
  //         C
284
  //    ----------- FACTORING
285
  //    lastClause'                children[start'] ... children[end']
286
  //    -------------------------------------------------------------- CHAIN_RES
287
  //                                    ...
288
  //
289
  // where
290
  //   lastClause_0 = children[0]
291
  //   start_0 = 1
292
  //   end_0 = eliminators[0] - 1
293
  //   start_i+1 = nextGuardedElimPos - 1
294
  //
295
  // The important point is how end_i+1 is computed. It is based on what we call
296
  // the "nextGuardedElimPos", i.e., the next elimination position that requires
297
  // removal of duplicates. The intuition is that a factoring step may eliminate
298
  // the duplicates of crowding literals l1 and l2. If the last inclusion of l2
299
  // is before the elimination of l1, then we can go ahead and also perform the
300
  // elimination of l2 without another factoring. However if another literal l3
301
  // has its last inclusion after the elimination of l2, then the elimination of
302
  // l3 is the next guarded elimination.
303
  //
304
  // To do the above computation then we determine, after a resolution/factoring
305
  // step, the first crowded literal to have its last inclusion after "end". The
306
  // first elimination position to be bigger than the position of that crowded
307
  // literal is the next guarded elimination position.
308
38391
  size_t lastElim = 0;
309
38391
  Node lastClause = children[0];
310
76782
  std::vector<Node> childrenRes;
311
76782
  std::vector<Node> childrenResArgs;
312
76782
  Node resPlaceHolder;
313
38391
  size_t nextGuardedElimPos = eliminators[0];
314
  do
315
  {
316
218723
    size_t start = lastElim + 1;
317
218723
    size_t end = nextGuardedElimPos - 1;
318
437446
    Trace("smt-proof-pp-debug2")
319
218723
        << "res with:\n\tlastClause: " << lastClause << "\n\tstart: " << start
320
218723
        << "\n\tend: " << end << "\n";
321
218723
    childrenRes.push_back(lastClause);
322
    // note that the interval of insert is exclusive in the end, so we add 1
323
656169
    childrenRes.insert(childrenRes.end(),
324
437446
                       children.begin() + start,
325
1093615
                       children.begin() + end + 1);
326
656169
    childrenResArgs.insert(childrenResArgs.end(),
327
437446
                           args.begin() + (2 * start) - 1,
328
1093615
                           args.begin() + (2 * end) + 1);
329
218723
    Trace("smt-proof-pp-debug2") << "res children: " << childrenRes << "\n";
330
218723
    Trace("smt-proof-pp-debug2") << "res args: " << childrenResArgs << "\n";
331
437446
    resPlaceHolder = d_pnm->getChecker()->checkDebug(PfRule::CHAIN_RESOLUTION,
332
                                                     childrenRes,
333
                                                     childrenResArgs,
334
437446
                                                     Node::null(),
335
                                                     "");
336
437446
    Trace("smt-proof-pp-debug2")
337
218723
        << "resPlaceHorder: " << resPlaceHolder << "\n";
338
218723
    cdp->addStep(
339
        resPlaceHolder, PfRule::CHAIN_RESOLUTION, childrenRes, childrenResArgs);
340
    // I need to add factoring if end < children.size(). Otherwise, this is
341
    // to be handled by the caller
342
218723
    if (end < children.size() - 1)
343
    {
344
540996
      lastClause = d_pnm->getChecker()->checkDebug(
345
540996
          PfRule::FACTORING, {resPlaceHolder}, {}, Node::null(), "");
346
180332
      if (!lastClause.isNull())
347
      {
348
180332
        cdp->addStep(lastClause, PfRule::FACTORING, {resPlaceHolder}, {});
349
      }
350
      else
351
      {
352
        lastClause = resPlaceHolder;
353
      }
354
180332
      Trace("smt-proof-pp-debug2") << "lastClause: " << lastClause << "\n";
355
    }
356
    else
357
    {
358
38391
      lastClause = resPlaceHolder;
359
38391
      break;
360
    }
361
    // update for next round
362
180332
    childrenRes.clear();
363
180332
    childrenResArgs.clear();
364
180332
    lastElim = end;
365
366
    // find the position of the last inclusion of the next crowded literal
367
180332
    size_t nextCrowdedInclusionPos = lastInclusion.size();
368
1632834
    for (size_t i = 0, size = lastInclusion.size(); i < size; ++i)
369
    {
370
1594443
      if (lastInclusion[i].second > lastElim)
371
      {
372
141941
        nextCrowdedInclusionPos = i;
373
141941
        break;
374
      }
375
    }
376
360664
    Trace("smt-proof-pp-debug2")
377
180332
        << "nextCrowdedInclusion/Pos: "
378
180332
        << lastInclusion[nextCrowdedInclusionPos].second << "/"
379
180332
        << nextCrowdedInclusionPos << "\n";
380
    // if there is none, then the remaining literals will be used in the next
381
    // round
382
180332
    if (nextCrowdedInclusionPos == lastInclusion.size())
383
    {
384
38391
      nextGuardedElimPos = children.size();
385
    }
386
    else
387
    {
388
141941
      nextGuardedElimPos = children.size();
389
1207135
      for (size_t i = 0, size = eliminators.size(); i < size; ++i)
390
      {
391
        //  nextGuardedElimPos is the largest element of
392
        // eliminators bigger the next crowded literal's last inclusion
393
1207135
        if (eliminators[i] > lastInclusion[nextCrowdedInclusionPos].second)
394
        {
395
141941
          nextGuardedElimPos = eliminators[i];
396
141941
          break;
397
        }
398
      }
399
141941
      Assert(nextGuardedElimPos < children.size());
400
    }
401
360664
    Trace("smt-proof-pp-debug2")
402
360664
        << "nextGuardedElimPos: " << nextGuardedElimPos << "\n";
403
  } while (true);
404
76782
  Trace("smt-proof-pp-debug2") << pop;
405
76782
  return lastClause;
406
}
407
408
307672
Node ProofPostprocessCallback::expandMacros(PfRule id,
409
                                            const std::vector<Node>& children,
410
                                            const std::vector<Node>& args,
411
                                            CDProof* cdp)
412
{
413
307672
  if (d_elimRules.find(id) == d_elimRules.end())
414
  {
415
    // not eliminated
416
    return Node::null();
417
  }
418
  // macro elimination
419
307672
  if (id == PfRule::MACRO_SR_EQ_INTRO)
420
  {
421
    // (TRANS
422
    //   (SUBS <children> :args args[0:1])
423
    //   (REWRITE :args <t.substitute(x1,t1). ... .substitute(xn,tn)> args[2]))
424
181806
    std::vector<Node> tchildren;
425
181806
    Node t = args[0];
426
181806
    Node ts;
427
90903
    if (!children.empty())
428
    {
429
27386
      std::vector<Node> sargs;
430
13693
      sargs.push_back(t);
431
13693
      MethodId ids = MethodId::SB_DEFAULT;
432
13693
      if (args.size() >= 2)
433
      {
434
10344
        if (getMethodId(args[1], ids))
435
        {
436
10344
          sargs.push_back(args[1]);
437
        }
438
      }
439
13693
      MethodId ida = MethodId::SBA_SEQUENTIAL;
440
13693
      if (args.size() >= 3)
441
      {
442
2574
        if (getMethodId(args[2], ida))
443
        {
444
2574
          sargs.push_back(args[2]);
445
        }
446
      }
447
13693
      ts = builtin::BuiltinProofRuleChecker::applySubstitution(
448
          t, children, ids, ida);
449
27386
      Trace("smt-proof-pp-debug")
450
13693
          << "...eq intro subs equality is " << t << " == " << ts << ", from "
451
13693
          << ids << " " << ida << std::endl;
452
13693
      if (ts != t)
453
      {
454
16594
        Node eq = t.eqNode(ts);
455
        // apply SUBS proof rule if necessary
456
8297
        if (!updateInternal(eq, PfRule::SUBS, children, sargs, cdp))
457
        {
458
          // if we specified that we did not want to eliminate, add as step
459
          cdp->addStep(eq, PfRule::SUBS, children, sargs);
460
        }
461
8297
        tchildren.push_back(eq);
462
      }
463
    }
464
    else
465
    {
466
      // no substitute
467
77210
      ts = t;
468
    }
469
181806
    std::vector<Node> rargs;
470
90903
    rargs.push_back(ts);
471
90903
    MethodId idr = MethodId::RW_REWRITE;
472
90903
    if (args.size() >= 4)
473
    {
474
40
      if (getMethodId(args[3], idr))
475
      {
476
40
        rargs.push_back(args[3]);
477
      }
478
    }
479
    builtin::BuiltinProofRuleChecker* builtinPfC =
480
        static_cast<builtin::BuiltinProofRuleChecker*>(
481
90903
            d_pnm->getChecker()->getCheckerFor(PfRule::MACRO_SR_EQ_INTRO));
482
181806
    Node tr = builtinPfC->applyRewrite(ts, idr);
483
181806
    Trace("smt-proof-pp-debug")
484
90903
        << "...eq intro rewrite equality is " << ts << " == " << tr << ", from "
485
90903
        << idr << std::endl;
486
90903
    if (ts != tr)
487
    {
488
111894
      Node eq = ts.eqNode(tr);
489
      // apply REWRITE proof rule
490
55947
      if (!updateInternal(eq, PfRule::REWRITE, {}, rargs, cdp))
491
      {
492
        // if not elimianted, add as step
493
        cdp->addStep(eq, PfRule::REWRITE, {}, rargs);
494
      }
495
55947
      tchildren.push_back(eq);
496
    }
497
90903
    if (t == tr)
498
    {
499
      // typically not necessary, but done to be robust
500
33723
      cdp->addStep(t.eqNode(tr), PfRule::REFL, {}, {t});
501
33723
      return t.eqNode(tr);
502
    }
503
    // must add TRANS if two step
504
57180
    return addProofForTrans(tchildren, cdp);
505
  }
506
216769
  else if (id == PfRule::MACRO_SR_PRED_INTRO)
507
  {
508
16340
    std::vector<Node> tchildren;
509
16340
    std::vector<Node> sargs = args;
510
    // take into account witness form, if necessary
511
8170
    bool reqWitness = d_wfpm.requiresWitnessFormIntro(args[0]);
512
16340
    Trace("smt-proof-pp-debug")
513
8170
        << "...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
16340
    Node conc = expandMacros(PfRule::MACRO_SR_EQ_INTRO, children, sargs, cdp);
526
16340
    Trace("smt-proof-pp-debug")
527
8170
        << "...pred intro conclusion is " << conc << std::endl;
528
8170
    Assert(!conc.isNull());
529
8170
    Assert(conc.getKind() == EQUAL);
530
8170
    Assert(conc[0] == args[0]);
531
8170
    tchildren.push_back(conc);
532
8170
    if (reqWitness)
533
    {
534
6036
      Node weq = addProofForWitnessForm(conc[1], cdp);
535
3018
      Trace("smt-proof-pp-debug") << "...weq is " << weq << std::endl;
536
3018
      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
5786
        Node weqr = expandMacros(PfRule::MACRO_SR_EQ_INTRO, {}, {weq[1]}, cdp);
542
2893
        addToTransChildren(weqr, tchildren);
543
      }
544
    }
545
    // apply transitivity if necessary
546
16340
    Node eq = addProofForTrans(tchildren, cdp);
547
8170
    Assert(!eq.isNull());
548
8170
    Assert(eq.getKind() == EQUAL);
549
8170
    Assert(eq[0] == args[0]);
550
8170
    Assert(eq[1] == d_true);
551
552
8170
    cdp->addStep(eq[0], PfRule::TRUE_ELIM, {eq}, {});
553
8170
    return eq[0];
554
  }
555
208599
  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
4424
    std::vector<Node> schildren(children.begin() + 1, children.end());
561
4424
    std::vector<Node> srargs;
562
2212
    srargs.push_back(children[0]);
563
2212
    srargs.insert(srargs.end(), args.begin(), args.end());
564
4424
    Node conc = expandMacros(PfRule::MACRO_SR_EQ_INTRO, schildren, srargs, cdp);
565
2212
    Assert(!conc.isNull());
566
2212
    Assert(conc.getKind() == EQUAL);
567
2212
    Assert(conc[0] == children[0]);
568
    // apply equality resolve
569
2212
    cdp->addStep(conc[1], PfRule::EQ_RESOLVE, {children[0], conc}, {});
570
2212
    return conc[1];
571
  }
572
206387
  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
76078
    Trace("smt-proof-pp-debug")
588
38039
        << "Transform " << children[0] << " == " << args[0] << std::endl;
589
38039
    if (CDProof::isSame(children[0], args[0]))
590
    {
591
4290
      Trace("smt-proof-pp-debug") << "...nothing to do" << std::endl;
592
      // nothing to do
593
4290
      return children[0];
594
    }
595
67498
    std::vector<Node> tchildren;
596
67498
    std::vector<Node> schildren(children.begin() + 1, children.end());
597
67498
    std::vector<Node> sargs = args;
598
    // first, compute if we need
599
33749
    bool reqWitness = d_wfpm.requiresWitnessFormTransform(children[0], args[0]);
600
33749
    Trace("smt-proof-pp-debug") << "...reqWitness=" << reqWitness << std::endl;
601
    // convert both sides, in three steps, take symmetry of second chain
602
101247
    for (unsigned r = 0; r < 2; r++)
603
    {
604
134996
      std::vector<Node> tchildrenr;
605
      // first rewrite children[0], then args[0]
606
67498
      sargs[0] = r == 0 ? children[0] : args[0];
607
      // t = apply_SR(t)
608
134996
      Node eq = expandMacros(PfRule::MACRO_SR_EQ_INTRO, schildren, sargs, cdp);
609
134996
      Trace("smt-proof-pp-debug")
610
67498
          << "transform subs_rewrite (" << r << "): " << eq << std::endl;
611
67498
      Assert(!eq.isNull() && eq.getKind() == EQUAL && eq[0] == sargs[0]);
612
67498
      addToTransChildren(eq, tchildrenr);
613
      // apply_SR(t) = toWitness(apply_SR(t))
614
67498
      if (reqWitness)
615
      {
616
22392
        Node weq = addProofForWitnessForm(eq[1], cdp);
617
22392
        Trace("smt-proof-pp-debug")
618
11196
            << "transform toWitness (" << r << "): " << weq << std::endl;
619
11196
        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
11120
              expandMacros(PfRule::MACRO_SR_EQ_INTRO, {}, {weq[1]}, cdp);
626
11120
          Trace("smt-proof-pp-debug") << "transform rewrite_witness (" << r
627
5560
                                      << "): " << weqr << std::endl;
628
5560
          addToTransChildren(weqr, tchildrenr);
629
        }
630
      }
631
134996
      Trace("smt-proof-pp-debug")
632
67498
          << "transform connect (" << r << ")" << std::endl;
633
      // add to overall chain
634
67498
      if (r == 0)
635
      {
636
        // add the current chain to the overall chain
637
33749
        tchildren.insert(tchildren.end(), tchildrenr.begin(), tchildrenr.end());
638
      }
639
      else
640
      {
641
        // add the current chain to cdp
642
67498
        Node eqr = addProofForTrans(tchildrenr, cdp);
643
33749
        if (!eqr.isNull())
644
        {
645
42184
          Trace("smt-proof-pp-debug") << "transform connect sym " << tchildren
646
21092
                                      << " " << eqr << std::endl;
647
          // take symmetry of above and add it to the overall chain
648
21092
          addToTransChildren(eqr, tchildren, true);
649
        }
650
      }
651
134996
      Trace("smt-proof-pp-debug")
652
67498
          << "transform finish (" << r << ")" << std::endl;
653
    }
654
655
    // apply transitivity if necessary
656
67498
    Node eq = addProofForTrans(tchildren, cdp);
657
658
33749
    cdp->addStep(args[0], PfRule::EQ_RESOLVE, {children[0], eq}, {});
659
33749
    return args[0];
660
  }
661
168348
  else if (id == PfRule::MACRO_RESOLUTION
662
168348
           || id == PfRule::MACRO_RESOLUTION_TRUST)
663
  {
664
    // first generate the naive chain_resolution
665
186464
    std::vector<Node> chainResArgs{args.begin() + 1, args.end()};
666
93232
    Node chainConclusion = d_pnm->getChecker()->checkDebug(
667
186464
        PfRule::CHAIN_RESOLUTION, children, chainResArgs, Node::null(), "");
668
93232
    Trace("smt-proof-pp-debug") << "Original conclusion: " << args[0] << "\n";
669
186464
    Trace("smt-proof-pp-debug")
670
93232
        << "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
93232
    if (chainConclusion == args[0])
684
    {
685
35396
      cdp->addStep(
686
          chainConclusion, PfRule::CHAIN_RESOLUTION, children, chainResArgs);
687
35396
      return chainConclusion;
688
    }
689
57836
    NodeManager* nm = NodeManager::currentNM();
690
    // If we got here, then chainConclusion is NECESSARILY an OR node
691
57836
    Assert(chainConclusion.getKind() == kind::OR);
692
    // get the literals in the chain conclusion
693
    std::vector<Node> chainConclusionLits{chainConclusion.begin(),
694
115672
                                          chainConclusion.end()};
695
    std::set<Node> chainConclusionLitsSet{chainConclusion.begin(),
696
115672
                                          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
115672
    std::vector<Node> conclusionLits;
700
    // whether conclusion is singleton
701
57836
    if (chainConclusionLitsSet.count(args[0]))
702
    {
703
9803
      conclusionLits.push_back(args[0]);
704
    }
705
    else
706
    {
707
48033
      Assert(args[0].getKind() == kind::OR);
708
48033
      conclusionLits.insert(
709
96066
          conclusionLits.end(), args[0].begin(), args[0].end());
710
    }
711
    std::set<Node> conclusionLitsSet{conclusionLits.begin(),
712
115672
                                     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
57836
    if (chainConclusionLitsSet != conclusionLitsSet)
717
    {
718
38391
      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
38391
      chainConclusionLits.clear();
727
38391
      if (chainConclusionLitsSet.count(chainConclusion))
728
      {
729
        chainConclusionLits.push_back(chainConclusion);
730
      }
731
      else
732
      {
733
38391
        Assert(chainConclusion.getKind() == kind::OR);
734
115173
        chainConclusionLits.insert(chainConclusionLits.end(),
735
                                   chainConclusion.begin(),
736
115173
                                   chainConclusion.end());
737
      }
738
    }
739
    else
740
    {
741
19445
      cdp->addStep(
742
          chainConclusion, PfRule::CHAIN_RESOLUTION, children, chainResArgs);
743
    }
744
115672
    Trace("smt-proof-pp-debug")
745
57836
        << "Conclusion after chain_res/elimCrowd: " << chainConclusion << "\n";
746
115672
    Trace("smt-proof-pp-debug")
747
57836
        << "Conclusion lits: " << chainConclusionLits << "\n";
748
    // Placeholder for running conclusion
749
115672
    Node n = chainConclusion;
750
    // factoring
751
57836
    if (chainConclusionLits.size() != conclusionLits.size())
752
    {
753
      // We build it rather than taking conclusionLits because the order may be
754
      // different
755
111890
      std::vector<Node> factoredLits;
756
111890
      std::unordered_set<TNode> clauseSet;
757
1078629
      for (size_t i = 0, size = chainConclusionLits.size(); i < size; ++i)
758
      {
759
1022684
        if (clauseSet.count(chainConclusionLits[i]))
760
        {
761
464129
          continue;
762
        }
763
558555
        factoredLits.push_back(n[i]);
764
558555
        clauseSet.insert(n[i]);
765
      }
766
55945
      Node factored = factoredLits.empty()
767
                          ? nm->mkConst(false)
768
55945
                          : factoredLits.size() == 1
769
9803
                                ? factoredLits[0]
770
177638
                                : nm->mkNode(kind::OR, factoredLits);
771
55945
      cdp->addStep(factored, PfRule::FACTORING, {n}, {});
772
55945
      n = factored;
773
    }
774
    // either same node or n as a clause
775
57836
    Assert(n == args[0] || n.getKind() == kind::OR);
776
    // reordering
777
57836
    if (n != args[0])
778
    {
779
38163
      cdp->addStep(args[0], PfRule::REORDERING, {n}, {args[0]});
780
    }
781
57836
    return args[0];
782
  }
783
75116
  else if (id == PfRule::SUBS)
784
  {
785
8297
    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
16594
    Node t = args[0];
803
    // get the kind of substitution
804
8297
    MethodId ids = MethodId::SB_DEFAULT;
805
8297
    if (args.size() >= 2)
806
    {
807
6413
      getMethodId(args[1], ids);
808
    }
809
8297
    MethodId ida = MethodId::SBA_SEQUENTIAL;
810
8297
    if (args.size() >= 3)
811
    {
812
2528
      getMethodId(args[2], ida);
813
    }
814
16594
    std::vector<std::shared_ptr<CDProof>> pfs;
815
16594
    std::vector<TNode> vsList;
816
16594
    std::vector<TNode> ssList;
817
16594
    std::vector<TNode> fromList;
818
16594
    std::vector<ProofGenerator*> pgs;
819
    // first, compute the entire substitution
820
558958
    for (size_t i = 0, nchild = children.size(); i < nchild; i++)
821
    {
822
      // get the substitution
823
1101322
      builtin::BuiltinProofRuleChecker::getSubstitutionFor(
824
550661
          children[i], vsList, ssList, fromList, ids);
825
      // ensure proofs for each formula in fromList
826
550661
      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
16594
    std::vector<Node> vvec;
838
16594
    std::vector<Node> svec;
839
558958
    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
1101074
      TNode var = vsList[i];
845
1101074
      TNode subs = ssList[i];
846
1101074
      TNode childFrom = fromList[i];
847
1101322
      Trace("smt-proof-pp-debug")
848
550661
          << "...process " << var << " -> " << subs << " (" << childFrom << ", "
849
550661
          << ids << ")" << std::endl;
850
      // apply the current substitution to the range
851
550661
      if (!vvec.empty() && ida == MethodId::SBA_SEQUENTIAL)
852
      {
853
        Node ss =
854
1700
            subs.substitute(vvec.begin(), vvec.end(), svec.begin(), svec.end());
855
974
        if (ss != subs)
856
        {
857
496
          Trace("smt-proof-pp-debug")
858
248
              << "......updated to " << var << " -> " << ss
859
248
              << " based on previous substitution" << std::endl;
860
          // make the proof for the tranitivity step
861
496
          std::shared_ptr<CDProof> pf = std::make_shared<CDProof>(d_pnm);
862
248
          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
496
                                  true);
871
          // add previous rewrite steps
872
897
          for (unsigned j = 0, nvars = vvec.size(); j < nvars; j++)
873
          {
874
            // substitutions are pre-rewrites
875
649
            tcg.addRewriteStep(vvec[j], svec[j], pgs[j], true);
876
          }
877
          // get the proof for the update to the current substitution
878
496
          Node seqss = subs.eqNode(ss);
879
496
          std::shared_ptr<ProofNode> pfn = tcg.getProofFor(seqss);
880
248
          Assert(pfn != nullptr);
881
          // add the proof
882
248
          pf->addProof(pfn);
883
          // get proof for childFrom from cdp
884
248
          pfn = cdp->getProofFor(childFrom);
885
248
          pf->addProof(pfn);
886
          // ensure we have a proof of var = subs
887
496
          Node veqs = addProofForSubsStep(var, subs, childFrom, pf.get());
888
          // transitivity
889
248
          pf->addStep(var.eqNode(ss), PfRule::TRANS, {veqs, seqss}, {});
890
          // add to the substitution
891
248
          vvec.push_back(var);
892
248
          svec.push_back(ss);
893
248
          pgs.push_back(pf.get());
894
248
          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
550413
      addProofForSubsStep(var, subs, childFrom, cdp);
902
550413
      vvec.push_back(var);
903
550413
      svec.push_back(subs);
904
550413
      pgs.push_back(cdp);
905
    }
906
    // should be implied by the substitution now
907
8297
    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
16594
                             true);
916
558958
    for (unsigned j = 0, nvars = vvec.size(); j < nvars; j++)
917
    {
918
      // substitutions are pre-rewrites
919
550661
      tcpg.addRewriteStep(vvec[j], svec[j], pgs[j], true);
920
    }
921
    // add the proof constructed by the term conversion utility
922
16594
    std::shared_ptr<ProofNode> pfn = tcpg.getProofForRewriting(t);
923
16594
    Node eq = pfn->getResult();
924
    Node ts = builtin::BuiltinProofRuleChecker::applySubstitution(
925
16594
        t, children, ids, ida);
926
16594
    Node eqq = t.eqNode(ts);
927
8297
    if (eq != eqq)
928
    {
929
7
      pfn = nullptr;
930
    }
931
    // should give a proof, if not, then tcpg does not agree with the
932
    // substitution.
933
8297
    if (pfn == nullptr)
934
    {
935
7
      Warning() << "resort to TRUST_SUBS" << std::endl
936
                << eq << std::endl
937
                << eqq << std::endl
938
                << "from " << children << " applied to " << t << std::endl;
939
7
      cdp->addStep(eqq, PfRule::TRUST_SUBS, {}, {eqq});
940
    }
941
    else
942
    {
943
8290
      cdp->addProof(pfn);
944
    }
945
8297
    return eqq;
946
  }
947
66819
  else if (id == PfRule::REWRITE)
948
  {
949
    // get the kind of rewrite
950
60462
    MethodId idr = MethodId::RW_REWRITE;
951
60462
    if (args.size() >= 2)
952
    {
953
29
      getMethodId(args[1], idr);
954
    }
955
    builtin::BuiltinProofRuleChecker* builtinPfC =
956
        static_cast<builtin::BuiltinProofRuleChecker*>(
957
60462
            d_pnm->getChecker()->getCheckerFor(PfRule::REWRITE));
958
120924
    Node ret = builtinPfC->applyRewrite(args[0], idr);
959
120924
    Node eq = args[0].eqNode(ret);
960
60462
    if (idr == MethodId::RW_REWRITE || idr == MethodId::RW_REWRITE_EQ_EXT)
961
    {
962
      // rewrites from theory::Rewriter
963
60462
      bool isExtEq = (idr == MethodId::RW_REWRITE_EQ_EXT);
964
      // use rewrite with proof interface
965
60462
      Rewriter* rr = d_smte->getRewriter();
966
120924
      TrustNode trn = rr->rewriteWithProof(args[0], isExtEq);
967
120924
      std::shared_ptr<ProofNode> pfn = trn.toProofNode();
968
60462
      if (pfn == nullptr)
969
      {
970
58
        Trace("smt-proof-pp-debug")
971
29
            << "Use TRUST_REWRITE for " << eq << std::endl;
972
        // did not have a proof of rewriting, probably isExtEq is true
973
29
        if (isExtEq)
974
        {
975
          // update to THEORY_REWRITE with idr
976
29
          Assert(args.size() >= 1);
977
29
          TheoryId theoryId = Theory::theoryOf(args[0].getType());
978
58
          Node tid = builtin::BuiltinProofRuleChecker::mkTheoryIdNode(theoryId);
979
29
          cdp->addStep(eq, PfRule::THEORY_REWRITE, {}, {eq, tid, args[1]});
980
        }
981
        else
982
        {
983
          // this should never be applied
984
          cdp->addStep(eq, PfRule::TRUST_REWRITE, {}, {eq});
985
        }
986
      }
987
      else
988
      {
989
60433
        cdp->addProof(pfn);
990
      }
991
120924
      Assert(trn.getNode() == ret)
992
60462
          << "Unexpected rewrite " << args[0] << std::endl
993
60462
          << "Got: " << trn.getNode() << std::endl
994
60462
          << "Expected: " << ret;
995
    }
996
    else if (idr == MethodId::RW_EVALUATE)
997
    {
998
      // change to evaluate, which is never eliminated
999
      cdp->addStep(eq, PfRule::EVALUATE, {}, {args[0]});
1000
    }
1001
    else
1002
    {
1003
      // don't know how to eliminate
1004
      return Node::null();
1005
    }
1006
60462
    if (args[0] == ret)
1007
    {
1008
      // should not be necessary typically
1009
      cdp->addStep(eq, PfRule::REFL, {}, {args[0]});
1010
    }
1011
60462
    return eq;
1012
  }
1013
6357
  else if (id == PfRule::THEORY_REWRITE)
1014
  {
1015
    Assert(!args.empty());
1016
    Node eq = args[0];
1017
    Assert(eq.getKind() == EQUAL);
1018
    // try to replay theory rewrite
1019
    // first, check that maybe its just an evaluation step
1020
    ProofChecker* pc = d_pnm->getChecker();
1021
    Node ceval =
1022
        pc->checkDebug(PfRule::EVALUATE, {}, {eq[0]}, eq, "smt-proof-pp-debug");
1023
    if (!ceval.isNull() && ceval == eq)
1024
    {
1025
      cdp->addStep(eq, PfRule::EVALUATE, {}, {eq[0]});
1026
      return eq;
1027
    }
1028
    // otherwise no update
1029
    Trace("final-pf-hole") << "hole: " << id << " : " << eq << std::endl;
1030
  }
1031
6357
  else if (id == PfRule::MACRO_ARITH_SCALE_SUM_UB)
1032
  {
1033
4791
    Debug("macro::arith") << "Expand MACRO_ARITH_SCALE_SUM_UB" << std::endl;
1034
4791
    if (Debug.isOn("macro::arith"))
1035
    {
1036
      for (const auto& child : children)
1037
      {
1038
        Debug("macro::arith") << "  child: " << child << std::endl;
1039
      }
1040
      Debug("macro::arith") << "   args: " << args << std::endl;
1041
    }
1042
4791
    Assert(args.size() == children.size());
1043
4791
    NodeManager* nm = NodeManager::currentNM();
1044
9582
    ProofStepBuffer steps{d_pnm->getChecker()};
1045
1046
    // Scale all children, accumulating
1047
9582
    std::vector<Node> scaledRels;
1048
30556
    for (size_t i = 0; i < children.size(); ++i)
1049
    {
1050
51530
      TNode child = children[i];
1051
51530
      TNode scalar = args[i];
1052
25765
      bool isPos = scalar.getConst<Rational>() > 0;
1053
      Node scalarCmp =
1054
51530
          nm->mkNode(isPos ? GT : LT, scalar, nm->mkConst(Rational(0)));
1055
      // (= scalarCmp true)
1056
51530
      Node scalarCmpOrTrue = steps.tryStep(PfRule::EVALUATE, {}, {scalarCmp});
1057
25765
      Assert(!scalarCmpOrTrue.isNull());
1058
      // scalarCmp
1059
25765
      steps.addStep(PfRule::TRUE_ELIM, {scalarCmpOrTrue}, {}, scalarCmp);
1060
      // (and scalarCmp relation)
1061
      Node scalarCmpAndRel =
1062
51530
          steps.tryStep(PfRule::AND_INTRO, {scalarCmp, child}, {});
1063
25765
      Assert(!scalarCmpAndRel.isNull());
1064
      // (=> (and scalarCmp relation) scaled)
1065
      Node impl =
1066
          steps.tryStep(isPos ? PfRule::ARITH_MULT_POS : PfRule::ARITH_MULT_NEG,
1067
                        {},
1068
51530
                        {scalar, child});
1069
25765
      Assert(!impl.isNull());
1070
      // scaled
1071
      Node scaled =
1072
51530
          steps.tryStep(PfRule::MODUS_PONENS, {scalarCmpAndRel, impl}, {});
1073
25765
      Assert(!scaled.isNull());
1074
25765
      scaledRels.emplace_back(scaled);
1075
    }
1076
1077
9582
    Node sumBounds = steps.tryStep(PfRule::ARITH_SUM_UB, scaledRels, {});
1078
4791
    cdp->addSteps(steps);
1079
9582
    Debug("macro::arith") << "Expansion done. Proved: " << sumBounds
1080
4791
                          << std::endl;
1081
4791
    return sumBounds;
1082
  }
1083
1566
  else if (id == PfRule::BV_BITBLAST)
1084
  {
1085
3132
    bv::BBProof bb(nullptr, d_pnm, true);
1086
3132
    Node eq = args[0];
1087
1566
    Assert(eq.getKind() == EQUAL);
1088
1566
    bb.bbAtom(eq[0]);
1089
3132
    Node bbAtom = bb.getStoredBBAtom(eq[0]);
1090
1566
    bb.getProofGenerator()->addProofTo(eq[0].eqNode(bbAtom), cdp);
1091
1566
    return eq;
1092
  }
1093
1094
  // TRUST, PREPROCESS, THEORY_LEMMA, THEORY_PREPROCESS?
1095
1096
  return Node::null();
1097
}
1098
1099
14214
Node ProofPostprocessCallback::addProofForWitnessForm(Node t, CDProof* cdp)
1100
{
1101
28428
  Node tw = SkolemManager::getOriginalForm(t);
1102
14214
  Node eq = t.eqNode(tw);
1103
14214
  if (t == tw)
1104
  {
1105
    // not necessary, add REFL step
1106
5761
    cdp->addStep(eq, PfRule::REFL, {}, {t});
1107
5761
    return eq;
1108
  }
1109
16906
  std::shared_ptr<ProofNode> pn = d_wfpm.getProofFor(eq);
1110
8453
  if (pn != nullptr)
1111
  {
1112
    // add the proof
1113
8453
    cdp->addProof(pn);
1114
  }
1115
  else
1116
  {
1117
    Assert(false) << "ProofPostprocessCallback::addProofForWitnessForm: failed "
1118
                     "to add proof for witness form of "
1119
                  << t;
1120
  }
1121
8453
  return eq;
1122
}
1123
1124
132848
Node ProofPostprocessCallback::addProofForTrans(
1125
    const std::vector<Node>& tchildren, CDProof* cdp)
1126
{
1127
132848
  size_t tsize = tchildren.size();
1128
132848
  if (tsize > 1)
1129
  {
1130
33842
    Node lhs = tchildren[0][0];
1131
33842
    Node rhs = tchildren[tsize - 1][1];
1132
33842
    Node eq = lhs.eqNode(rhs);
1133
16921
    cdp->addStep(eq, PfRule::TRANS, tchildren, {});
1134
16921
    return eq;
1135
  }
1136
115927
  else if (tsize == 1)
1137
  {
1138
103270
    return tchildren[0];
1139
  }
1140
12657
  return Node::null();
1141
}
1142
1143
550661
Node ProofPostprocessCallback::addProofForSubsStep(Node var,
1144
                                                   Node subs,
1145
                                                   Node assump,
1146
                                                   CDProof* cdp)
1147
{
1148
  // ensure we have a proof of var = subs
1149
550661
  Node veqs = var.eqNode(subs);
1150
550661
  if (veqs != assump)
1151
  {
1152
    // should be true intro or false intro
1153
3885
    Assert(subs.isConst());
1154
11655
    cdp->addStep(
1155
        veqs,
1156
3885
        subs.getConst<bool>() ? PfRule::TRUE_INTRO : PfRule::FALSE_INTRO,
1157
        {assump},
1158
3885
        {});
1159
  }
1160
550661
  return veqs;
1161
}
1162
1163
111257
bool ProofPostprocessCallback::addToTransChildren(Node eq,
1164
                                                  std::vector<Node>& tchildren,
1165
                                                  bool isSymm)
1166
{
1167
111257
  Assert(!eq.isNull());
1168
111257
  Assert(eq.getKind() == kind::EQUAL);
1169
111257
  if (eq[0] == eq[1])
1170
  {
1171
38873
    return false;
1172
  }
1173
144768
  Node equ = isSymm ? eq[1].eqNode(eq[0]) : eq;
1174
72384
  Assert(tchildren.empty()
1175
         || (tchildren[tchildren.size() - 1].getKind() == kind::EQUAL
1176
             && tchildren[tchildren.size() - 1][1] == equ[0]));
1177
72384
  tchildren.push_back(equ);
1178
72384
  return true;
1179
}
1180
1181
3771
ProofPostproccess::ProofPostproccess(ProofNodeManager* pnm,
1182
                                     SmtEngine* smte,
1183
                                     ProofGenerator* pppg,
1184
3771
                                     bool updateScopedAssumptions)
1185
    : d_pnm(pnm),
1186
      d_cb(pnm, smte, pppg, updateScopedAssumptions),
1187
      // the update merges subproofs
1188
      d_updater(d_pnm, d_cb, true),
1189
      d_finalCb(pnm),
1190
3771
      d_finalizer(d_pnm, d_finalCb)
1191
{
1192
3771
}
1193
1194
3771
ProofPostproccess::~ProofPostproccess() {}
1195
1196
2805
void ProofPostproccess::process(std::shared_ptr<ProofNode> pf)
1197
{
1198
  // Initialize the callback, which computes necessary static information about
1199
  // how to process, including how to process assumptions in pf.
1200
2805
  d_cb.initializeUpdate();
1201
  // now, process
1202
2805
  d_updater.process(pf);
1203
  // take stats and check pedantic
1204
2805
  d_finalCb.initializeUpdate();
1205
2805
  d_finalizer.process(pf);
1206
1207
5610
  std::stringstream serr;
1208
2805
  bool wasPedanticFailure = d_finalCb.wasPedanticFailure(serr);
1209
2805
  if (wasPedanticFailure)
1210
  {
1211
    AlwaysAssert(!wasPedanticFailure)
1212
        << "ProofPostproccess::process: pedantic failure:" << std::endl
1213
        << serr.str();
1214
  }
1215
2805
}
1216
1217
11780
void ProofPostproccess::setEliminateRule(PfRule rule)
1218
{
1219
11780
  d_cb.setEliminateRule(rule);
1220
11780
}
1221
1222
}  // namespace smt
1223
29358
}  // namespace cvc5