GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/smt/proof_post_processor.cpp Lines: 545 596 91.4 %
Date: 2021-05-22 Branches: 1058 2788 37.9 %

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