GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/preprocessing/assertion_pipeline.cpp Lines: 86 86 100.0 %
Date: 2021-05-22 Branches: 135 350 38.6 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Andres Noetzli, Haniel Barbosa
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
 * AssertionPipeline stores a list of assertions modified by
14
 * preprocessing passes.
15
 */
16
17
#include "preprocessing/assertion_pipeline.h"
18
19
#include "expr/node_manager.h"
20
#include "options/smt_options.h"
21
#include "expr/lazy_proof.h"
22
#include "smt/preprocess_proof_generator.h"
23
#include "theory/builtin/proof_checker.h"
24
#include "theory/rewriter.h"
25
26
namespace cvc5 {
27
namespace preprocessing {
28
29
10099
AssertionPipeline::AssertionPipeline()
30
    : d_realAssertionsEnd(0),
31
      d_storeSubstsInAsserts(false),
32
      d_substsIndex(0),
33
      d_assumptionsStart(0),
34
      d_numAssumptions(0),
35
10099
      d_pppg(nullptr)
36
{
37
10099
}
38
39
16959
void AssertionPipeline::clear()
40
{
41
16959
  d_nodes.clear();
42
16959
  d_realAssertionsEnd = 0;
43
16959
  d_assumptionsStart = 0;
44
16959
  d_numAssumptions = 0;
45
16959
}
46
47
127457
void AssertionPipeline::push_back(Node n,
48
                                  bool isAssumption,
49
                                  bool isInput,
50
                                  ProofGenerator* pgen)
51
{
52
127457
  d_nodes.push_back(n);
53
127457
  if (isAssumption)
54
  {
55
2401
    Assert(pgen == nullptr);
56
2401
    if (d_numAssumptions == 0)
57
    {
58
2373
      d_assumptionsStart = d_nodes.size() - 1;
59
    }
60
    // Currently, we assume that assumptions are all added one after another
61
    // and that we store them in the same vector as the assertions. Once we
62
    // split the assertions up into multiple vectors (see issue #2473), we will
63
    // not have this limitation anymore.
64
2401
    Assert(d_assumptionsStart + d_numAssumptions == d_nodes.size() - 1);
65
2401
    d_numAssumptions++;
66
  }
67
254914
  Trace("assert-pipeline") << "Assertions: ...new assertion " << n
68
127457
                           << ", isInput=" << isInput << std::endl;
69
127457
  if (isProofEnabled())
70
  {
71
55882
    if (!isInput)
72
    {
73
      // notice this is always called, regardless of whether pgen is nullptr
74
17810
      d_pppg->notifyNewAssert(n, pgen);
75
    }
76
    else
77
    {
78
38072
      Assert(pgen == nullptr);
79
      // n is an input assertion, whose proof should be ASSUME.
80
38072
      d_pppg->notifyInput(n);
81
    }
82
  }
83
127457
}
84
85
20123
void AssertionPipeline::pushBackTrusted(theory::TrustNode trn)
86
{
87
20123
  Assert(trn.getKind() == theory::TrustNodeKind::LEMMA);
88
  // push back what was proven
89
20123
  push_back(trn.getProven(), false, false, trn.getGenerator());
90
20123
}
91
92
298372
void AssertionPipeline::replace(size_t i, Node n, ProofGenerator* pgen)
93
{
94
298372
  if (n == d_nodes[i])
95
  {
96
    // no change, skip
97
214828
    return;
98
  }
99
167088
  Trace("assert-pipeline") << "Assertions: Replace " << d_nodes[i] << " with "
100
83544
                           << n << std::endl;
101
83544
  if (isProofEnabled())
102
  {
103
32920
    d_pppg->notifyPreprocessed(d_nodes[i], n, pgen);
104
  }
105
83544
  d_nodes[i] = n;
106
}
107
108
243955
void AssertionPipeline::replaceTrusted(size_t i, theory::TrustNode trn)
109
{
110
243955
  if (trn.isNull())
111
  {
112
    // null trust node denotes no change, nothing to do
113
164902
    return;
114
  }
115
79053
  Assert(trn.getKind() == theory::TrustNodeKind::REWRITE);
116
79053
  Assert(trn.getProven()[0] == d_nodes[i]);
117
79053
  replace(i, trn.getNode(), trn.getGenerator());
118
}
119
120
3600
void AssertionPipeline::setProofGenerator(smt::PreprocessProofGenerator* pppg)
121
{
122
3600
  d_pppg = pppg;
123
3600
}
124
125
214453
bool AssertionPipeline::isProofEnabled() const { return d_pppg != nullptr; }
126
127
3296
void AssertionPipeline::enableStoreSubstsInAsserts()
128
{
129
3296
  d_storeSubstsInAsserts = true;
130
3296
  d_substsIndex = d_nodes.size();
131
3296
  d_nodes.push_back(NodeManager::currentNM()->mkConst<bool>(true));
132
3296
}
133
134
9587
void AssertionPipeline::disableStoreSubstsInAsserts()
135
{
136
9587
  d_storeSubstsInAsserts = false;
137
9587
}
138
139
1341
void AssertionPipeline::addSubstitutionNode(Node n, ProofGenerator* pg)
140
{
141
1341
  Assert(d_storeSubstsInAsserts);
142
1341
  Assert(n.getKind() == kind::EQUAL);
143
1341
  conjoin(d_substsIndex, n, pg);
144
1341
}
145
146
3551
void AssertionPipeline::conjoin(size_t i, Node n, ProofGenerator* pg)
147
{
148
3551
  NodeManager* nm = NodeManager::currentNM();
149
7003
  Node newConj = nm->mkNode(kind::AND, d_nodes[i], n);
150
7003
  Node newConjr = theory::Rewriter::rewrite(newConj);
151
7102
  Trace("assert-pipeline") << "Assertions: conjoin " << n << " to "
152
3551
                           << d_nodes[i] << std::endl;
153
7102
  Trace("assert-pipeline-debug") << "conjoin " << n << " to " << d_nodes[i]
154
3551
                                 << ", got " << newConjr << std::endl;
155
3551
  if (newConjr == d_nodes[i])
156
  {
157
    // trivial, skip
158
99
    return;
159
  }
160
3452
  if (isProofEnabled())
161
  {
162
841
    if (newConjr == n)
163
    {
164
      // don't care about the previous proof and can simply plug in the
165
      // proof from pg if the resulting assertion is the same as n.
166
434
      d_pppg->notifyNewAssert(newConjr, pg);
167
    }
168
    else
169
    {
170
      // ---------- from pppg   --------- from pg
171
      // d_nodes[i]                n
172
      // -------------------------------- AND_INTRO
173
      //      d_nodes[i] ^ n
174
      // -------------------------------- MACRO_SR_PRED_TRANSFORM
175
      //   rewrite( d_nodes[i] ^ n )
176
      // allocate a fresh proof which will act as the proof generator
177
407
      LazyCDProof* lcp = d_pppg->allocateHelperProof();
178
407
      lcp->addLazyStep(n, pg, PfRule::PREPROCESS);
179
407
      if (d_nodes[i].isConst() && d_nodes[i].getConst<bool>())
180
      {
181
        // skip the AND_INTRO if the previous d_nodes[i] was true
182
194
        newConj = n;
183
      }
184
      else
185
      {
186
213
        lcp->addLazyStep(d_nodes[i], d_pppg);
187
213
        lcp->addStep(newConj, PfRule::AND_INTRO, {d_nodes[i], n}, {});
188
      }
189
407
      if (newConjr != newConj)
190
      {
191
1215
        lcp->addStep(
192
810
            newConjr, PfRule::MACRO_SR_PRED_TRANSFORM, {newConj}, {newConjr});
193
      }
194
      // Notice we have constructed a proof of a new assertion, where d_pppg
195
      // is referenced in the lazy proof above. If alternatively we had
196
      // constructed a proof of d_nodes[i] = rewrite( d_nodes[i] ^ n ), we would
197
      // have used notifyPreprocessed. However, it is simpler to make the
198
      // above proof.
199
407
      d_pppg->notifyNewAssert(newConjr, lcp);
200
    }
201
  }
202
3452
  d_nodes[i] = newConjr;
203
3452
  Assert(theory::Rewriter::rewrite(newConjr) == newConjr);
204
}
205
206
}  // namespace preprocessing
207
28191
}  // namespace cvc5