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