1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Haniel Barbosa, 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 |
|
* Implementation of lazy proof utility. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "proof/lazy_proof_chain.h" |
17 |
|
|
18 |
|
#include "options/proof_options.h" |
19 |
|
#include "proof/proof.h" |
20 |
|
#include "proof/proof_ensure_closed.h" |
21 |
|
#include "proof/proof_node.h" |
22 |
|
#include "proof/proof_node_algorithm.h" |
23 |
|
#include "proof/proof_node_manager.h" |
24 |
|
|
25 |
|
namespace cvc5 { |
26 |
|
|
27 |
11295 |
LazyCDProofChain::LazyCDProofChain(ProofNodeManager* pnm, |
28 |
|
bool cyclic, |
29 |
|
context::Context* c, |
30 |
|
ProofGenerator* defGen, |
31 |
|
bool defRec, |
32 |
11295 |
const std::string& name) |
33 |
|
: d_manager(pnm), |
34 |
|
d_cyclic(cyclic), |
35 |
|
d_defRec(defRec), |
36 |
|
d_context(), |
37 |
|
d_gens(c ? c : &d_context), |
38 |
|
d_defGen(defGen), |
39 |
11295 |
d_name(name) |
40 |
|
{ |
41 |
11295 |
} |
42 |
|
|
43 |
18831 |
LazyCDProofChain::~LazyCDProofChain() {} |
44 |
|
|
45 |
|
const std::map<Node, std::shared_ptr<ProofNode>> LazyCDProofChain::getLinks() |
46 |
|
const |
47 |
|
{ |
48 |
|
std::map<Node, std::shared_ptr<ProofNode>> links; |
49 |
|
for (const std::pair<const Node, ProofGenerator* const>& link : d_gens) |
50 |
|
{ |
51 |
|
Assert(link.second); |
52 |
|
std::shared_ptr<ProofNode> pfn = link.second->getProofFor(link.first); |
53 |
|
Assert(pfn); |
54 |
|
links[link.first] = pfn; |
55 |
|
} |
56 |
|
return links; |
57 |
|
} |
58 |
|
|
59 |
37511 |
std::shared_ptr<ProofNode> LazyCDProofChain::getProofFor(Node fact) |
60 |
|
{ |
61 |
75022 |
Trace("lazy-cdproofchain") |
62 |
37511 |
<< "LazyCDProofChain::getProofFor " << fact << "\n"; |
63 |
|
// which facts have had proofs retrieved for. This is maintained to avoid |
64 |
|
// cycles. It also saves the proof node of the fact |
65 |
75022 |
std::unordered_map<Node, std::shared_ptr<ProofNode>> toConnect; |
66 |
|
// leaves of proof node links in the chain, i.e. assumptions, yet to be |
67 |
|
// expanded |
68 |
|
std::unordered_map<Node, std::vector<std::shared_ptr<ProofNode>>> |
69 |
75022 |
assumptionsToExpand; |
70 |
|
// invariant of the loop below, the first iteration notwithstanding: |
71 |
|
// visit = domain(assumptionsToExpand) \ domain(toConnect) |
72 |
75022 |
std::vector<Node> visit{fact}; |
73 |
75022 |
std::unordered_map<Node, bool> visited; |
74 |
75022 |
Node cur; |
75 |
1911141 |
do |
76 |
|
{ |
77 |
1948652 |
cur = visit.back(); |
78 |
1948652 |
visit.pop_back(); |
79 |
1948652 |
auto itVisited = visited.find(cur); |
80 |
|
// pre-traversal |
81 |
1948652 |
if (itVisited == visited.end()) |
82 |
|
{ |
83 |
842560 |
Trace("lazy-cdproofchain") |
84 |
421280 |
<< "LazyCDProofChain::getProofFor: check " << cur << "\n"; |
85 |
421280 |
Assert(toConnect.find(cur) == toConnect.end()); |
86 |
421280 |
bool rec = true; |
87 |
421280 |
ProofGenerator* pg = getGeneratorForInternal(cur, rec); |
88 |
654747 |
if (!pg) |
89 |
|
{ |
90 |
466934 |
Trace("lazy-cdproofchain") |
91 |
233467 |
<< "LazyCDProofChain::getProofFor: nothing to do\n"; |
92 |
|
// nothing to do for this fact, it'll be a leaf in the final proof |
93 |
|
// node, don't post-traverse. |
94 |
233467 |
visited[cur] = true; |
95 |
480049 |
continue; |
96 |
|
} |
97 |
375626 |
Trace("lazy-cdproofchain") |
98 |
375626 |
<< "LazyCDProofChain::getProofFor: Call generator " << pg->identify() |
99 |
187813 |
<< " for chain link " << cur << "\n"; |
100 |
362511 |
std::shared_ptr<ProofNode> curPfn = pg->getProofFor(cur); |
101 |
190875 |
if (curPfn == nullptr) |
102 |
|
{ |
103 |
6124 |
Trace("lazy-cdproofchain") |
104 |
3062 |
<< "LazyCDProofChain::getProofFor: No proof found, skip\n"; |
105 |
3062 |
visited[cur] = true; |
106 |
3062 |
continue; |
107 |
|
} |
108 |
|
// map node whose proof node must be expanded to the respective poof node |
109 |
184751 |
toConnect[cur] = curPfn; |
110 |
194804 |
if (!rec) |
111 |
|
{ |
112 |
|
// we don't want to recursively connect this proof |
113 |
10053 |
visited[cur] = true; |
114 |
10053 |
continue; |
115 |
|
} |
116 |
349396 |
Trace("lazy-cdproofchain-debug") |
117 |
174698 |
<< "LazyCDProofChain::getProofFor: stored proof: " << *curPfn.get() |
118 |
174698 |
<< "\n"; |
119 |
|
// retrieve free assumptions and their respective proof nodes |
120 |
349396 |
std::map<Node, std::vector<std::shared_ptr<ProofNode>>> famap; |
121 |
174698 |
expr::getFreeAssumptionsMap(curPfn, famap); |
122 |
174698 |
if (Trace.isOn("lazy-cdproofchain")) |
123 |
|
{ |
124 |
|
unsigned alreadyToVisit = 0; |
125 |
|
Trace("lazy-cdproofchain") |
126 |
|
<< "LazyCDProofChain::getProofFor: " << famap.size() |
127 |
|
<< " free assumptions:\n"; |
128 |
|
for (auto fap : famap) |
129 |
|
{ |
130 |
|
Trace("lazy-cdproofchain") |
131 |
|
<< "LazyCDProofChain::getProofFor: - " << fap.first << "\n"; |
132 |
|
alreadyToVisit += |
133 |
|
std::find(visit.begin(), visit.end(), fap.first) != visit.end() |
134 |
|
? 1 |
135 |
|
: 0; |
136 |
|
} |
137 |
|
Trace("lazy-cdproofchain") |
138 |
|
<< "LazyCDProofChain::getProofFor: " << alreadyToVisit |
139 |
|
<< " already to visit\n"; |
140 |
|
} |
141 |
|
// mark for post-traversal if we are controlling cycles |
142 |
174698 |
if (d_cyclic) |
143 |
|
{ |
144 |
349396 |
Trace("lazy-cdproofchain") << "LazyCDProofChain::getProofFor: marking " |
145 |
174698 |
<< cur << " for cycle check\n"; |
146 |
174698 |
visit.push_back(cur); |
147 |
174698 |
visited[cur] = false; |
148 |
|
} |
149 |
|
else |
150 |
|
{ |
151 |
|
visited[cur] = true; |
152 |
|
} |
153 |
|
// enqueue free assumptions to process |
154 |
1760153 |
for (const std::pair<const Node, std::vector<std::shared_ptr<ProofNode>>>& |
155 |
174698 |
fap : famap) |
156 |
|
{ |
157 |
|
// check cycles |
158 |
1760153 |
if (d_cyclic) |
159 |
|
{ |
160 |
|
// cycles are assumptions being *currently* expanded and seen again, |
161 |
|
// i.e. in toConnect and not yet post-visited |
162 |
1760153 |
auto itToConnect = toConnect.find(fap.first); |
163 |
1783863 |
if (itToConnect != toConnect.end() && !visited[fap.first]) |
164 |
|
{ |
165 |
|
// Since we have a cycle with an assumption, this fact will be an |
166 |
|
// assumption in the final proof node produced by this |
167 |
|
// method. Thus we erase it as something to be connected, which |
168 |
|
// will keep it as an assumption. |
169 |
47420 |
Trace("lazy-cdproofchain") << "LazyCDProofChain::getProofFor: " |
170 |
23710 |
"removing cyclic assumption " |
171 |
23710 |
<< fap.first << " from expansion\n"; |
172 |
23710 |
continue; |
173 |
|
} |
174 |
|
} |
175 |
|
// We always add assumptions to visit so that their last seen occurrence |
176 |
|
// is expanded (rather than the first seen occurrence, if we were not |
177 |
|
// adding assumptions, say, in assumptionsToExpand). This is so because |
178 |
|
// in the case where we are checking cycles this is necessary (and |
179 |
|
// harmless when we are not). For example the cycle |
180 |
|
// |
181 |
|
// a2 |
182 |
|
// ... |
183 |
|
// ---- |
184 |
|
// a1 |
185 |
|
// ... |
186 |
|
// -------- |
187 |
|
// a0 a1 a2 |
188 |
|
// ... |
189 |
|
// --------------- |
190 |
|
// n |
191 |
|
// |
192 |
|
// in which a2 has a1 as an assumption, which has a2 an assumption, |
193 |
|
// would not be captured if we did not revisit a1, which is an |
194 |
|
// assumption of n and this already occur in assumptionsToExpand when |
195 |
|
// it shows up again as an assumption of a2. |
196 |
1736443 |
visit.push_back(fap.first); |
197 |
|
// add assumption proof nodes to be updated |
198 |
3472886 |
assumptionsToExpand[fap.first].insert( |
199 |
3472886 |
assumptionsToExpand[fap.first].end(), |
200 |
|
fap.second.begin(), |
201 |
5209329 |
fap.second.end()); |
202 |
|
} |
203 |
174698 |
if (d_cyclic) |
204 |
|
{ |
205 |
174698 |
Trace("lazy-cdproofchain") << push; |
206 |
174698 |
Trace("lazy-cdproofchain-debug") << push; |
207 |
|
} |
208 |
|
} |
209 |
1527372 |
else if (!itVisited->second) |
210 |
|
{ |
211 |
174698 |
visited[cur] = true; |
212 |
174698 |
Trace("lazy-cdproofchain") << pop; |
213 |
174698 |
Trace("lazy-cdproofchain-debug") << pop; |
214 |
349396 |
Trace("lazy-cdproofchain") |
215 |
174698 |
<< "LazyCDProofChain::getProofFor: post-visited " << cur << "\n"; |
216 |
|
} |
217 |
|
else |
218 |
|
{ |
219 |
2705348 |
Trace("lazy-cdproofchain") |
220 |
1352674 |
<< "LazyCDProofChain::getProofFor: already fully processed " << cur |
221 |
1352674 |
<< "\n"; |
222 |
|
} |
223 |
1948652 |
} while (!visit.empty()); |
224 |
|
// expand all assumptions marked to be connected |
225 |
184751 |
for (const std::pair<const Node, std::shared_ptr<ProofNode>>& npfn : |
226 |
37511 |
toConnect) |
227 |
|
{ |
228 |
184751 |
auto it = assumptionsToExpand.find(npfn.first); |
229 |
221410 |
if (it == assumptionsToExpand.end()) |
230 |
|
{ |
231 |
36659 |
Assert(npfn.first == fact); |
232 |
36659 |
continue; |
233 |
|
} |
234 |
148092 |
Assert(npfn.second); |
235 |
296184 |
Trace("lazy-cdproofchain") |
236 |
148092 |
<< "LazyCDProofChain::getProofFor: expand assumption " << npfn.first |
237 |
148092 |
<< "\n"; |
238 |
296184 |
Trace("lazy-cdproofchain-debug") |
239 |
148092 |
<< "LazyCDProofChain::getProofFor: ...expand to " << *npfn.second.get() |
240 |
148092 |
<< "\n"; |
241 |
|
// update each assumption proof node |
242 |
521287 |
for (std::shared_ptr<ProofNode> pfn : it->second) |
243 |
|
{ |
244 |
373195 |
d_manager->updateNode(pfn.get(), npfn.second.get()); |
245 |
|
} |
246 |
|
} |
247 |
|
// final proof of fact |
248 |
37511 |
auto it = toConnect.find(fact); |
249 |
37511 |
if (it == toConnect.end()) |
250 |
|
{ |
251 |
852 |
return nullptr; |
252 |
|
} |
253 |
36659 |
return it->second; |
254 |
|
} |
255 |
|
|
256 |
73807 |
void LazyCDProofChain::addLazyStep(Node expected, ProofGenerator* pg) |
257 |
|
{ |
258 |
73807 |
Assert(pg != nullptr); |
259 |
147614 |
Trace("lazy-cdproofchain") << "LazyCDProofChain::addLazyStep: " << expected |
260 |
73807 |
<< " set to generator " << pg->identify() << "\n"; |
261 |
|
// note this will replace the generator for expected, if any |
262 |
73807 |
d_gens.insert(expected, pg); |
263 |
73807 |
} |
264 |
|
|
265 |
|
void LazyCDProofChain::addLazyStep(Node expected, |
266 |
|
ProofGenerator* pg, |
267 |
|
const std::vector<Node>& assumptions, |
268 |
|
const char* ctx) |
269 |
|
{ |
270 |
|
Assert(pg != nullptr); |
271 |
|
Trace("lazy-cdproofchain") << "LazyCDProofChain::addLazyStep: " << expected |
272 |
|
<< " set to generator " << pg->identify() << "\n"; |
273 |
|
// note this will rewrite the generator for expected, if any |
274 |
|
d_gens.insert(expected, pg); |
275 |
|
// check if chain is closed if options::proofEagerChecking() is on |
276 |
|
if (options::proofEagerChecking()) |
277 |
|
{ |
278 |
|
Trace("lazy-cdproofchain") |
279 |
|
<< "LazyCDProofChain::addLazyStep: Checking closed proof...\n"; |
280 |
|
std::shared_ptr<ProofNode> pfn = pg->getProofFor(expected); |
281 |
|
std::vector<Node> allowedLeaves{assumptions.begin(), assumptions.end()}; |
282 |
|
// add all current links in the chain |
283 |
|
for (const std::pair<const Node, ProofGenerator* const>& link : d_gens) |
284 |
|
{ |
285 |
|
allowedLeaves.push_back(link.first); |
286 |
|
} |
287 |
|
if (Trace.isOn("lazy-cdproofchain")) |
288 |
|
{ |
289 |
|
Trace("lazy-cdproofchain") << "Checking relative to leaves...\n"; |
290 |
|
for (const Node& n : allowedLeaves) |
291 |
|
{ |
292 |
|
Trace("lazy-cdproofchain") << " - " << n << "\n"; |
293 |
|
} |
294 |
|
Trace("lazy-cdproofchain") << "\n"; |
295 |
|
} |
296 |
|
pfnEnsureClosedWrt(pfn.get(), allowedLeaves, "lazy-cdproofchain", ctx); |
297 |
|
} |
298 |
|
} |
299 |
|
|
300 |
23735 |
bool LazyCDProofChain::hasGenerator(Node fact) const |
301 |
|
{ |
302 |
23735 |
return d_gens.find(fact) != d_gens.end(); |
303 |
|
} |
304 |
|
|
305 |
|
ProofGenerator* LazyCDProofChain::getGeneratorFor(Node fact) |
306 |
|
{ |
307 |
|
bool rec = true; |
308 |
|
return getGeneratorForInternal(fact, rec); |
309 |
|
} |
310 |
|
|
311 |
421280 |
ProofGenerator* LazyCDProofChain::getGeneratorForInternal(Node fact, bool& rec) |
312 |
|
{ |
313 |
421280 |
auto it = d_gens.find(fact); |
314 |
421280 |
if (it != d_gens.end()) |
315 |
|
{ |
316 |
152581 |
return (*it).second; |
317 |
|
} |
318 |
|
// otherwise, if no explicit generators, we use the default one |
319 |
268699 |
if (d_defGen != nullptr) |
320 |
|
{ |
321 |
35232 |
rec = d_defRec; |
322 |
35232 |
return d_defGen; |
323 |
|
} |
324 |
233467 |
return nullptr; |
325 |
|
} |
326 |
|
|
327 |
72865 |
std::string LazyCDProofChain::identify() const { return d_name; } |
328 |
|
|
329 |
29340 |
} // namespace cvc5 |