GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/smt/proof_post_processor.cpp Lines: 562 611 92.0 %
Date: 2021-08-17 Branches: 1098 2816 39.0 %

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