1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Gereon Kremer |
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 |
|
* Trust substitutions. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "theory/trust_substitutions.h" |
17 |
|
|
18 |
|
#include "theory/rewriter.h" |
19 |
|
|
20 |
|
namespace cvc5 { |
21 |
|
namespace theory { |
22 |
|
|
23 |
30687 |
TrustSubstitutionMap::TrustSubstitutionMap(context::Context* c, |
24 |
|
ProofNodeManager* pnm, |
25 |
|
std::string name, |
26 |
|
PfRule trustId, |
27 |
30687 |
MethodId ids) |
28 |
|
: d_ctx(c), |
29 |
|
d_subs(c), |
30 |
|
d_tsubs(c), |
31 |
|
d_tspb(nullptr), |
32 |
|
d_subsPg(nullptr), |
33 |
|
d_applyPg(nullptr), |
34 |
|
d_helperPf(nullptr), |
35 |
|
d_name(name), |
36 |
|
d_trustId(trustId), |
37 |
|
d_ids(ids), |
38 |
30687 |
d_eqtIndex(c) |
39 |
|
{ |
40 |
30687 |
setProofNodeManager(pnm); |
41 |
30687 |
} |
42 |
|
|
43 |
34455 |
void TrustSubstitutionMap::setProofNodeManager(ProofNodeManager* pnm) |
44 |
|
{ |
45 |
34455 |
if (pnm != nullptr) |
46 |
|
{ |
47 |
|
// should not set the proof node manager more than once |
48 |
7390 |
Assert(d_tspb == nullptr); |
49 |
7390 |
d_tspb.reset(new TheoryProofStepBuffer(pnm->getChecker())), |
50 |
14780 |
d_subsPg.reset(new LazyCDProof( |
51 |
7390 |
pnm, nullptr, d_ctx, "TrustSubstitutionMap::subsPg")); |
52 |
14780 |
d_applyPg.reset( |
53 |
7390 |
new LazyCDProof(pnm, nullptr, d_ctx, "TrustSubstitutionMap::applyPg")); |
54 |
7390 |
d_helperPf.reset(new CDProofSet<LazyCDProof>(pnm, d_ctx)); |
55 |
|
} |
56 |
34455 |
} |
57 |
|
|
58 |
61193 |
void TrustSubstitutionMap::addSubstitution(TNode x, TNode t, ProofGenerator* pg) |
59 |
|
{ |
60 |
122386 |
Trace("trust-subs") << "TrustSubstitutionMap::addSubstitution: add " << x |
61 |
61193 |
<< " -> " << t << std::endl; |
62 |
61193 |
d_subs.addSubstitution(x, t); |
63 |
61193 |
if (isProofEnabled()) |
64 |
|
{ |
65 |
24740 |
TrustNode tnl = TrustNode::mkTrustRewrite(x, t, pg); |
66 |
12370 |
d_tsubs.push_back(tnl); |
67 |
|
// add to lazy proof |
68 |
12370 |
d_subsPg->addLazyStep(tnl.getProven(), pg, d_trustId); |
69 |
|
} |
70 |
61193 |
} |
71 |
|
|
72 |
2360 |
void TrustSubstitutionMap::addSubstitution(TNode x, |
73 |
|
TNode t, |
74 |
|
PfRule id, |
75 |
|
const std::vector<Node>& children, |
76 |
|
const std::vector<Node>& args) |
77 |
|
{ |
78 |
2360 |
if (!isProofEnabled()) |
79 |
|
{ |
80 |
1431 |
addSubstitution(x, t, nullptr); |
81 |
1431 |
return; |
82 |
|
} |
83 |
929 |
LazyCDProof* stepPg = d_helperPf->allocateProof(nullptr, d_ctx); |
84 |
1858 |
Node eq = x.eqNode(t); |
85 |
929 |
stepPg->addStep(eq, id, children, args); |
86 |
929 |
addSubstitution(x, t, stepPg); |
87 |
|
} |
88 |
|
|
89 |
53634 |
ProofGenerator* TrustSubstitutionMap::addSubstitutionSolved(TNode x, |
90 |
|
TNode t, |
91 |
|
TrustNode tn) |
92 |
|
{ |
93 |
107268 |
Trace("trust-subs") << "TrustSubstitutionMap::addSubstitutionSolved: add " |
94 |
107268 |
<< x << " -> " << t << " from " << tn.getProven() |
95 |
53634 |
<< std::endl; |
96 |
53634 |
if (!isProofEnabled() || tn.getGenerator() == nullptr) |
97 |
|
{ |
98 |
|
// no generator or not proof enabled, nothing to do |
99 |
47077 |
addSubstitution(x, t, nullptr); |
100 |
47077 |
Trace("trust-subs") << "...no proof" << std::endl; |
101 |
47077 |
return nullptr; |
102 |
|
} |
103 |
13114 |
Node eq = x.eqNode(t); |
104 |
13114 |
Node proven = tn.getProven(); |
105 |
|
// notice that this checks syntactic equality, not CDProof::isSame since |
106 |
|
// tn.getGenerator() is not necessarily robust to symmetry. |
107 |
6557 |
if (eq == proven) |
108 |
|
{ |
109 |
|
// no rewrite required, just use the generator |
110 |
5391 |
addSubstitution(x, t, tn.getGenerator()); |
111 |
5391 |
Trace("trust-subs") << "...use generator directly" << std::endl; |
112 |
5391 |
return tn.getGenerator(); |
113 |
|
} |
114 |
1166 |
LazyCDProof* solvePg = d_helperPf->allocateProof(nullptr, d_ctx); |
115 |
|
// Try to transform tn.getProven() to (= x t) here, if necessary |
116 |
1166 |
if (!d_tspb->applyPredTransform(proven, eq, {})) |
117 |
|
{ |
118 |
|
// failed to rewrite, we add a trust step which assumes eq is provable |
119 |
|
// from proven, and proceed as normal. |
120 |
13 |
Trace("trust-subs") << "...failed to rewrite " << proven << std::endl; |
121 |
13 |
d_tspb->addStep(PfRule::TRUST_SUBS_EQ, {proven}, {eq}, eq); |
122 |
|
} |
123 |
1166 |
Trace("trust-subs") << "...successful rewrite" << std::endl; |
124 |
1166 |
solvePg->addSteps(*d_tspb.get()); |
125 |
1166 |
d_tspb->clear(); |
126 |
|
// link the given generator |
127 |
1166 |
solvePg->addLazyStep(proven, tn.getGenerator()); |
128 |
1166 |
addSubstitution(x, t, solvePg); |
129 |
1166 |
return solvePg; |
130 |
|
} |
131 |
|
|
132 |
9938 |
void TrustSubstitutionMap::addSubstitutions(TrustSubstitutionMap& t) |
133 |
|
{ |
134 |
9938 |
if (!isProofEnabled()) |
135 |
|
{ |
136 |
|
// just use the basic utility |
137 |
8194 |
d_subs.addSubstitutions(t.get()); |
138 |
8194 |
return; |
139 |
|
} |
140 |
|
// call addSubstitution above in sequence |
141 |
6472 |
for (const TrustNode& tns : t.d_tsubs) |
142 |
|
{ |
143 |
9456 |
Node proven = tns.getProven(); |
144 |
4728 |
addSubstitution(proven[0], proven[1], tns.getGenerator()); |
145 |
|
} |
146 |
|
} |
147 |
|
|
148 |
723655 |
TrustNode TrustSubstitutionMap::applyTrusted(Node n, bool doRewrite) |
149 |
|
{ |
150 |
1447310 |
Trace("trust-subs") << "TrustSubstitutionMap::addSubstitution: apply " << n |
151 |
723655 |
<< std::endl; |
152 |
1447310 |
Node ns = d_subs.apply(n, doRewrite); |
153 |
723653 |
Trace("trust-subs") << "...subs " << ns << std::endl; |
154 |
723653 |
if (n == ns) |
155 |
|
{ |
156 |
|
// no change |
157 |
606974 |
return TrustNode::null(); |
158 |
|
} |
159 |
116679 |
if (!isProofEnabled()) |
160 |
|
{ |
161 |
|
// no proofs, use null generator |
162 |
72416 |
return TrustNode::mkTrustRewrite(n, ns, nullptr); |
163 |
|
} |
164 |
88526 |
Node eq = n.eqNode(ns); |
165 |
|
// remember the index |
166 |
44263 |
d_eqtIndex[eq] = d_tsubs.size(); |
167 |
|
// this class will provide a proof if asked |
168 |
44263 |
return TrustNode::mkTrustRewrite(n, ns, this); |
169 |
|
} |
170 |
|
|
171 |
304542 |
Node TrustSubstitutionMap::apply(Node n, bool doRewrite) |
172 |
|
{ |
173 |
304542 |
return d_subs.apply(n, doRewrite); |
174 |
|
} |
175 |
|
|
176 |
11854 |
std::shared_ptr<ProofNode> TrustSubstitutionMap::getProofFor(Node eq) |
177 |
|
{ |
178 |
11854 |
Assert(eq.getKind() == kind::EQUAL); |
179 |
23708 |
Node n = eq[0]; |
180 |
23708 |
Node ns = eq[1]; |
181 |
|
// Easy case: if n is in the domain of the substitution, maybe it is already |
182 |
|
// a proof in the substitution proof generator. This is moreover required |
183 |
|
// to avoid cyclic proofs below. For example, if { x -> 5 } is a substitution, |
184 |
|
// and we are asked to transform x, resulting in 5, we hence must provide |
185 |
|
// a proof of (= x 5) based on the substitution. However, it must be the |
186 |
|
// case that (= x 5) is a proven fact of the substitution generator. Hence |
187 |
|
// we avoid a proof that looks like: |
188 |
|
// ---------- from d_subsPg |
189 |
|
// (= x 5) |
190 |
|
// ---------- MACRO_SR_EQ_INTRO{x} |
191 |
|
// (= x 5) |
192 |
|
// by taking the premise proof directly. |
193 |
11854 |
if (d_subsPg->hasStep(eq) || d_subsPg->hasGenerator(eq)) |
194 |
|
{ |
195 |
78 |
return d_subsPg->getProofFor(eq); |
196 |
|
} |
197 |
11776 |
NodeUIntMap::iterator it = d_eqtIndex.find(eq); |
198 |
11776 |
Assert(it != d_eqtIndex.end()); |
199 |
23552 |
Trace("trust-subs-pf") << "TrustSubstitutionMap::getProofFor, # assumptions= " |
200 |
11776 |
<< it->second << std::endl; |
201 |
23552 |
Node cs = getSubstitution(it->second); |
202 |
11776 |
Assert(eq != cs); |
203 |
23552 |
std::vector<Node> pfChildren; |
204 |
11776 |
if (!cs.isConst()) |
205 |
|
{ |
206 |
|
// note we will get more proof reuse if we do not special case AND here. |
207 |
4289 |
if (cs.getKind() == kind::AND) |
208 |
|
{ |
209 |
663074 |
for (const Node& csc : cs) |
210 |
|
{ |
211 |
659156 |
pfChildren.push_back(csc); |
212 |
|
// connect substitution generator into apply generator |
213 |
659156 |
d_applyPg->addLazyStep(csc, d_subsPg.get()); |
214 |
|
} |
215 |
|
} |
216 |
|
else |
217 |
|
{ |
218 |
371 |
pfChildren.push_back(cs); |
219 |
|
// connect substitution generator into apply generator |
220 |
371 |
d_applyPg->addLazyStep(cs, d_subsPg.get()); |
221 |
|
} |
222 |
|
} |
223 |
11776 |
Trace("trust-subs-pf") << "...apply eq intro" << std::endl; |
224 |
|
// We use fixpoint as the substitution-apply identifier. Notice that it |
225 |
|
// suffices to use SBA_SEQUENTIAL here, but SBA_FIXPOINT is typically |
226 |
|
// more efficient. This is because for substitution of size n, sequential |
227 |
|
// substitution can either be implemented as n traversals of the term to |
228 |
|
// apply the substitution to, or a single traversal of the term, but n^2/2 |
229 |
|
// traversals of the range of the substitution to prepare a simultaneous |
230 |
|
// substitution. Both of these options are inefficient. |
231 |
11776 |
if (!d_tspb->applyEqIntro(n, ns, pfChildren, d_ids, MethodId::SBA_FIXPOINT)) |
232 |
|
{ |
233 |
|
// if we fail for any reason, we must use a trusted step instead |
234 |
|
d_tspb->addStep(PfRule::TRUST_SUBS_MAP, pfChildren, {eq}, eq); |
235 |
|
} |
236 |
11776 |
Trace("trust-subs-pf") << "...made steps" << std::endl; |
237 |
|
// ------- ------- from external proof generators |
238 |
|
// x1 = t1 ... xn = tn |
239 |
|
// ----------------------- AND_INTRO |
240 |
|
// ... |
241 |
|
// --------- MACRO_SR_EQ_INTRO (or TRUST_SUBS_MAP if we failed above) |
242 |
|
// n == ns |
243 |
|
// add it to the apply proof generator. |
244 |
|
// |
245 |
|
// Notice that we use a single child to MACRO_SR_EQ_INTRO here. This is an |
246 |
|
// optimization motivated by the fact that n may be large and reused many |
247 |
|
// time. For instance, if this class is being used to track substitutions |
248 |
|
// derived during non-clausal simplification during preprocessing, it is |
249 |
|
// a fixed (possibly) large substitution applied to many terms. Having |
250 |
|
// a single child saves us from creating many proofs with n children, where |
251 |
|
// notice this proof is reused. |
252 |
11776 |
d_applyPg->addSteps(*d_tspb.get()); |
253 |
11776 |
d_tspb->clear(); |
254 |
11776 |
Trace("trust-subs-pf") << "...finish, make proof" << std::endl; |
255 |
11776 |
return d_applyPg->getProofFor(eq); |
256 |
|
} |
257 |
|
|
258 |
492 |
std::string TrustSubstitutionMap::identify() const { return d_name; } |
259 |
|
|
260 |
40645 |
SubstitutionMap& TrustSubstitutionMap::get() { return d_subs; } |
261 |
|
|
262 |
243804 |
bool TrustSubstitutionMap::isProofEnabled() const |
263 |
|
{ |
264 |
243804 |
return d_subsPg != nullptr; |
265 |
|
} |
266 |
|
|
267 |
11776 |
Node TrustSubstitutionMap::getSubstitution(size_t index) |
268 |
|
{ |
269 |
11776 |
Assert(index <= d_tsubs.size()); |
270 |
23552 |
std::vector<Node> csubsChildren; |
271 |
671303 |
for (size_t i = 0; i < index; i++) |
272 |
|
{ |
273 |
659527 |
csubsChildren.push_back(d_tsubs[i].getProven()); |
274 |
|
} |
275 |
11776 |
std::reverse(csubsChildren.begin(), csubsChildren.end()); |
276 |
11776 |
Node cs = NodeManager::currentNM()->mkAnd(csubsChildren); |
277 |
11776 |
if (cs.getKind() == kind::AND) |
278 |
|
{ |
279 |
3918 |
d_subsPg->addStep(cs, PfRule::AND_INTRO, csubsChildren, {}); |
280 |
|
} |
281 |
23552 |
return cs; |
282 |
|
} |
283 |
|
|
284 |
|
} // namespace theory |
285 |
29340 |
} // namespace cvc5 |