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 |
26826 |
LazyCDProofChain::LazyCDProofChain(ProofNodeManager* pnm, |
28 |
|
bool cyclic, |
29 |
|
context::Context* c, |
30 |
|
ProofGenerator* defGen, |
31 |
|
bool defRec, |
32 |
26826 |
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 |
26826 |
d_name(name) |
40 |
|
{ |
41 |
26826 |
} |
42 |
|
|
43 |
42804 |
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 |
53765 |
std::shared_ptr<ProofNode> LazyCDProofChain::getProofFor(Node fact) |
60 |
|
{ |
61 |
107530 |
Trace("lazy-cdproofchain") |
62 |
53765 |
<< "LazyCDProofChain::getProofFor of gen " << d_name << "\n"; |
63 |
107530 |
Trace("lazy-cdproofchain") |
64 |
53765 |
<< "LazyCDProofChain::getProofFor: " << fact << "\n"; |
65 |
|
// which facts have had proofs retrieved for. This is maintained to avoid |
66 |
|
// cycles. It also saves the proof node of the fact |
67 |
107530 |
std::unordered_map<Node, std::shared_ptr<ProofNode>> toConnect; |
68 |
|
// leaves of proof node links in the chain, i.e. assumptions, yet to be |
69 |
|
// expanded |
70 |
|
std::unordered_map<Node, std::vector<std::shared_ptr<ProofNode>>> |
71 |
107530 |
assumptionsToExpand; |
72 |
|
// invariant of the loop below, the first iteration notwithstanding: |
73 |
|
// visit = domain(assumptionsToExpand) \ domain(toConnect) |
74 |
107530 |
std::vector<Node> visit{fact}; |
75 |
107530 |
std::unordered_map<Node, bool> visited; |
76 |
107530 |
Node cur; |
77 |
1733499 |
do |
78 |
|
{ |
79 |
1787264 |
cur = visit.back(); |
80 |
1787264 |
visit.pop_back(); |
81 |
1787264 |
auto itVisited = visited.find(cur); |
82 |
|
// pre-traversal |
83 |
1787264 |
if (itVisited == visited.end()) |
84 |
|
{ |
85 |
896266 |
Trace("lazy-cdproofchain") |
86 |
448133 |
<< "LazyCDProofChain::getProofFor: check " << cur << "\n"; |
87 |
448133 |
Assert(toConnect.find(cur) == toConnect.end()); |
88 |
448133 |
bool rec = true; |
89 |
448133 |
ProofGenerator* pg = getGeneratorForInternal(cur, rec); |
90 |
679602 |
if (!pg) |
91 |
|
{ |
92 |
462938 |
Trace("lazy-cdproofchain") |
93 |
231469 |
<< "LazyCDProofChain::getProofFor: nothing to do\n"; |
94 |
|
// nothing to do for this fact, it'll be a leaf in the final proof |
95 |
|
// node, don't post-traverse. |
96 |
231469 |
visited[cur] = true; |
97 |
515636 |
continue; |
98 |
|
} |
99 |
433328 |
Trace("lazy-cdproofchain") |
100 |
433328 |
<< "LazyCDProofChain::getProofFor: Call generator " << pg->identify() |
101 |
216664 |
<< " for chain link " << cur << "\n"; |
102 |
380630 |
std::shared_ptr<ProofNode> curPfn = pg->getProofFor(cur); |
103 |
220772 |
if (curPfn == nullptr) |
104 |
|
{ |
105 |
8216 |
Trace("lazy-cdproofchain") |
106 |
4108 |
<< "LazyCDProofChain::getProofFor: No proof found, skip\n"; |
107 |
4108 |
visited[cur] = true; |
108 |
4108 |
continue; |
109 |
|
} |
110 |
|
// map node whose proof node must be expanded to the respective poof node |
111 |
212556 |
toConnect[cur] = curPfn; |
112 |
|
// We may not want to recursively connect this proof or, if it's |
113 |
|
// assumption, there is nothing to connect, so we skip. Note that in the |
114 |
|
// special case in which curPfn is an assumption and cur is actually the |
115 |
|
// initial fact that getProofFor is called on, the cycle detection below |
116 |
|
// would prevent this method from generating the assumption proof for it, |
117 |
|
// which would be wrong. |
118 |
261136 |
if (!rec || curPfn->getRule() == PfRule::ASSUME) |
119 |
|
{ |
120 |
48580 |
visited[cur] = true; |
121 |
48580 |
continue; |
122 |
|
} |
123 |
327952 |
Trace("lazy-cdproofchain-debug") |
124 |
163976 |
<< "LazyCDProofChain::getProofFor: stored proof: " << *curPfn.get() |
125 |
163976 |
<< "\n"; |
126 |
|
// retrieve free assumptions and their respective proof nodes |
127 |
327942 |
std::map<Node, std::vector<std::shared_ptr<ProofNode>>> famap; |
128 |
163976 |
expr::getFreeAssumptionsMap(curPfn, famap); |
129 |
163976 |
if (Trace.isOn("lazy-cdproofchain")) |
130 |
|
{ |
131 |
|
unsigned alreadyToVisit = 0; |
132 |
|
Trace("lazy-cdproofchain") |
133 |
|
<< "LazyCDProofChain::getProofFor: " << famap.size() |
134 |
|
<< " free assumptions:\n"; |
135 |
|
for (auto fap : famap) |
136 |
|
{ |
137 |
|
Trace("lazy-cdproofchain") |
138 |
|
<< "LazyCDProofChain::getProofFor: - " << fap.first << "\n"; |
139 |
|
alreadyToVisit += |
140 |
|
std::find(visit.begin(), visit.end(), fap.first) != visit.end() |
141 |
|
? 1 |
142 |
|
: 0; |
143 |
|
} |
144 |
|
Trace("lazy-cdproofchain") |
145 |
|
<< "LazyCDProofChain::getProofFor: " << alreadyToVisit |
146 |
|
<< " already to visit\n"; |
147 |
|
} |
148 |
|
// If we are controlling cycle, check whether any of the assumptions of |
149 |
|
// cur would provoke a cycle. In such a case we treat cur as an |
150 |
|
// assumption, removing it from toConnect, and do not proceed to expand |
151 |
|
// any of its assumptions. |
152 |
163976 |
if (d_cyclic) |
153 |
|
{ |
154 |
327952 |
Trace("lazy-cdproofchain") << "LazyCDProofChain::getProofFor: marking " |
155 |
163976 |
<< cur << " for cycle check\n"; |
156 |
163976 |
bool isCyclic = false; |
157 |
|
// enqueue free assumptions to process |
158 |
1733509 |
for (const auto& fap : famap) |
159 |
|
{ |
160 |
|
// A cycle is characterized by cur having an assumption being |
161 |
|
// *currently* expanded that is seen again, i.e. in toConnect and not |
162 |
|
// yet post-visited |
163 |
1569543 |
auto itToConnect = toConnect.find(fap.first); |
164 |
1569543 |
if (itToConnect != toConnect.end() && !visited[fap.first]) |
165 |
|
{ |
166 |
|
// Since we have a cycle with an assumption, cur will be an |
167 |
|
// assumption in the final proof node produced by this |
168 |
|
// method. |
169 |
20 |
Trace("lazy-cdproofchain") |
170 |
10 |
<< "LazyCDProofChain::getProofFor: cyclic assumption " |
171 |
10 |
<< fap.first << "\n"; |
172 |
10 |
isCyclic = true; |
173 |
10 |
break; |
174 |
|
} |
175 |
|
} |
176 |
163976 |
if (isCyclic) |
177 |
|
{ |
178 |
10 |
visited[cur] = true; |
179 |
20 |
Trace("lazy-cdproofchain") |
180 |
10 |
<< "LazyCDProofChain::getProofFor: Removing " << cur |
181 |
10 |
<< " from toConnect\n"; |
182 |
10 |
auto itToConnect = toConnect.find(cur); |
183 |
10 |
toConnect.erase(itToConnect); |
184 |
10 |
continue; |
185 |
|
} |
186 |
163966 |
visit.push_back(cur); |
187 |
163966 |
visited[cur] = false; |
188 |
|
} |
189 |
|
else |
190 |
|
{ |
191 |
|
visited[cur] = true; |
192 |
|
} |
193 |
|
// enqueue free assumptions to process |
194 |
1733499 |
for (const auto& fap : famap) |
195 |
|
{ |
196 |
3139066 |
Trace("lazy-cdproofchain") |
197 |
1569533 |
<< "LazyCDProofChain::getProofFor: marking " << fap.first |
198 |
1569533 |
<< " for revisit and for expansion\n"; |
199 |
|
// We always add assumptions to visit so that their last seen occurrence |
200 |
|
// is expanded (rather than the first seen occurrence, if we were not |
201 |
|
// adding assumptions, say, in assumptionsToExpand). This is so because |
202 |
|
// in the case where we are checking cycles this is necessary (and |
203 |
|
// harmless when we are not). For example the cycle |
204 |
|
// |
205 |
|
// a2 |
206 |
|
// ... |
207 |
|
// ---- |
208 |
|
// a1 |
209 |
|
// ... |
210 |
|
// -------- |
211 |
|
// a0 a1 a2 |
212 |
|
// ... |
213 |
|
// --------------- |
214 |
|
// n |
215 |
|
// |
216 |
|
// in which a2 has a1 as an assumption, which has a2 an assumption, |
217 |
|
// would not be captured if we did not revisit a1, which is an |
218 |
|
// assumption of n and this already occur in assumptionsToExpand when |
219 |
|
// it shows up again as an assumption of a2. |
220 |
1569533 |
visit.push_back(fap.first); |
221 |
|
// add assumption proof nodes to be updated |
222 |
3139066 |
assumptionsToExpand[fap.first].insert( |
223 |
3139066 |
assumptionsToExpand[fap.first].end(), |
224 |
|
fap.second.begin(), |
225 |
4708599 |
fap.second.end()); |
226 |
|
} |
227 |
163966 |
if (d_cyclic) |
228 |
|
{ |
229 |
163966 |
Trace("lazy-cdproofchain") << push; |
230 |
163966 |
Trace("lazy-cdproofchain-debug") << push; |
231 |
|
} |
232 |
|
} |
233 |
1339131 |
else if (!itVisited->second) |
234 |
|
{ |
235 |
163966 |
visited[cur] = true; |
236 |
163966 |
Trace("lazy-cdproofchain") << pop; |
237 |
163966 |
Trace("lazy-cdproofchain-debug") << pop; |
238 |
327932 |
Trace("lazy-cdproofchain") |
239 |
163966 |
<< "LazyCDProofChain::getProofFor: post-visited " << cur << "\n"; |
240 |
|
} |
241 |
|
else |
242 |
|
{ |
243 |
2350330 |
Trace("lazy-cdproofchain") |
244 |
1175165 |
<< "LazyCDProofChain::getProofFor: already fully processed " << cur |
245 |
1175165 |
<< "\n"; |
246 |
|
} |
247 |
1787264 |
} while (!visit.empty()); |
248 |
|
// expand all assumptions marked to be connected |
249 |
212546 |
for (const std::pair<const Node, std::shared_ptr<ProofNode>>& npfn : |
250 |
53765 |
toConnect) |
251 |
|
{ |
252 |
212546 |
auto it = assumptionsToExpand.find(npfn.first); |
253 |
260137 |
if (it == assumptionsToExpand.end()) |
254 |
|
{ |
255 |
47591 |
Assert(npfn.first == fact); |
256 |
47591 |
continue; |
257 |
|
} |
258 |
164955 |
Assert(npfn.second); |
259 |
329910 |
Trace("lazy-cdproofchain") |
260 |
164955 |
<< "LazyCDProofChain::getProofFor: expand assumption " << npfn.first |
261 |
164955 |
<< "\n"; |
262 |
329910 |
Trace("lazy-cdproofchain-debug") |
263 |
164955 |
<< "LazyCDProofChain::getProofFor: ...expand to " << *npfn.second.get() |
264 |
164955 |
<< "\n"; |
265 |
|
// update each assumption proof node |
266 |
534889 |
for (std::shared_ptr<ProofNode> pfn : it->second) |
267 |
|
{ |
268 |
369934 |
d_manager->updateNode(pfn.get(), npfn.second.get()); |
269 |
|
} |
270 |
|
} |
271 |
53765 |
Trace("lazy-cdproofchain") << "===========\n"; |
272 |
|
// final proof of fact |
273 |
53765 |
auto it = toConnect.find(fact); |
274 |
53765 |
if (it == toConnect.end()) |
275 |
|
{ |
276 |
6174 |
return nullptr; |
277 |
|
} |
278 |
47591 |
return it->second; |
279 |
|
} |
280 |
|
|
281 |
83291 |
void LazyCDProofChain::addLazyStep(Node expected, ProofGenerator* pg) |
282 |
|
{ |
283 |
83291 |
Assert(pg != nullptr); |
284 |
166582 |
Trace("lazy-cdproofchain") << "LazyCDProofChain::addLazyStep: " << expected |
285 |
83291 |
<< " set to generator " << pg->identify() << "\n"; |
286 |
|
// note this will replace the generator for expected, if any |
287 |
83291 |
d_gens.insert(expected, pg); |
288 |
83291 |
} |
289 |
|
|
290 |
3 |
void LazyCDProofChain::addLazyStep(Node expected, |
291 |
|
ProofGenerator* pg, |
292 |
|
const std::vector<Node>& assumptions, |
293 |
|
const char* ctx) |
294 |
|
{ |
295 |
3 |
Assert(pg != nullptr); |
296 |
6 |
Trace("lazy-cdproofchain") << "LazyCDProofChain::addLazyStep: " << expected |
297 |
3 |
<< " set to generator " << pg->identify() << "\n"; |
298 |
|
// note this will rewrite the generator for expected, if any |
299 |
3 |
d_gens.insert(expected, pg); |
300 |
|
// check if chain is closed if eager checking is on |
301 |
3 |
if (options::proofCheck() == options::ProofCheckMode::EAGER) |
302 |
|
{ |
303 |
6 |
Trace("lazy-cdproofchain") |
304 |
3 |
<< "LazyCDProofChain::addLazyStep: Checking closed proof...\n"; |
305 |
6 |
std::shared_ptr<ProofNode> pfn = pg->getProofFor(expected); |
306 |
6 |
std::vector<Node> allowedLeaves{assumptions.begin(), assumptions.end()}; |
307 |
|
// add all current links in the chain |
308 |
12 |
for (const std::pair<const Node, ProofGenerator* const>& link : d_gens) |
309 |
|
{ |
310 |
9 |
allowedLeaves.push_back(link.first); |
311 |
|
} |
312 |
3 |
if (Trace.isOn("lazy-cdproofchain")) |
313 |
|
{ |
314 |
|
Trace("lazy-cdproofchain") << "Checking relative to leaves...\n"; |
315 |
|
for (const Node& n : allowedLeaves) |
316 |
|
{ |
317 |
|
Trace("lazy-cdproofchain") << " - " << n << "\n"; |
318 |
|
} |
319 |
|
Trace("lazy-cdproofchain") << "\n"; |
320 |
|
} |
321 |
3 |
pfnEnsureClosedWrt(pfn.get(), allowedLeaves, "lazy-cdproofchain", ctx); |
322 |
|
} |
323 |
3 |
} |
324 |
|
|
325 |
21899 |
bool LazyCDProofChain::hasGenerator(Node fact) const |
326 |
|
{ |
327 |
21899 |
return d_gens.find(fact) != d_gens.end(); |
328 |
|
} |
329 |
|
|
330 |
|
ProofGenerator* LazyCDProofChain::getGeneratorFor(Node fact) |
331 |
|
{ |
332 |
|
bool rec = true; |
333 |
|
return getGeneratorForInternal(fact, rec); |
334 |
|
} |
335 |
|
|
336 |
448133 |
ProofGenerator* LazyCDProofChain::getGeneratorForInternal(Node fact, bool& rec) |
337 |
|
{ |
338 |
448133 |
auto it = d_gens.find(fact); |
339 |
448133 |
if (it != d_gens.end()) |
340 |
|
{ |
341 |
154131 |
return (*it).second; |
342 |
|
} |
343 |
|
// otherwise, if no explicit generators, we use the default one |
344 |
294002 |
if (d_defGen != nullptr) |
345 |
|
{ |
346 |
62533 |
rec = d_defRec; |
347 |
62533 |
return d_defGen; |
348 |
|
} |
349 |
231469 |
return nullptr; |
350 |
|
} |
351 |
|
|
352 |
85378 |
std::string LazyCDProofChain::identify() const { return d_name; } |
353 |
|
|
354 |
31137 |
} // namespace cvc5 |