1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, 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 |
|
* Implementation of a utility for updating proof nodes. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "expr/proof_node_updater.h" |
17 |
|
|
18 |
|
#include "expr/lazy_proof.h" |
19 |
|
#include "expr/proof_ensure_closed.h" |
20 |
|
#include "expr/proof_node_algorithm.h" |
21 |
|
#include "expr/proof_node_manager.h" |
22 |
|
|
23 |
|
namespace cvc5 { |
24 |
|
|
25 |
7453 |
ProofNodeUpdaterCallback::ProofNodeUpdaterCallback() {} |
26 |
7453 |
ProofNodeUpdaterCallback::~ProofNodeUpdaterCallback() {} |
27 |
|
|
28 |
|
bool ProofNodeUpdaterCallback::update(Node res, |
29 |
|
PfRule id, |
30 |
|
const std::vector<Node>& children, |
31 |
|
const std::vector<Node>& args, |
32 |
|
CDProof* cdp, |
33 |
|
bool& continueUpdate) |
34 |
|
{ |
35 |
|
return false; |
36 |
|
} |
37 |
|
|
38 |
8943 |
ProofNodeUpdater::ProofNodeUpdater(ProofNodeManager* pnm, |
39 |
|
ProofNodeUpdaterCallback& cb, |
40 |
|
bool mergeSubproofs, |
41 |
8943 |
bool autoSym) |
42 |
|
: d_pnm(pnm), |
43 |
|
d_cb(cb), |
44 |
|
d_debugFreeAssumps(false), |
45 |
|
d_mergeSubproofs(mergeSubproofs), |
46 |
8943 |
d_autoSym(autoSym) |
47 |
|
{ |
48 |
8943 |
} |
49 |
|
|
50 |
7389 |
void ProofNodeUpdater::process(std::shared_ptr<ProofNode> pf) |
51 |
|
{ |
52 |
7389 |
if (d_debugFreeAssumps) |
53 |
|
{ |
54 |
|
if (Trace.isOn("pfnu-debug")) |
55 |
|
{ |
56 |
|
Trace("pfnu-debug2") << "Initial proof: " << *pf.get() << std::endl; |
57 |
|
Trace("pfnu-debug") << "ProofNodeUpdater::process" << std::endl; |
58 |
|
Trace("pfnu-debug") << "Expected free assumptions: " << std::endl; |
59 |
|
for (const Node& fa : d_freeAssumps) |
60 |
|
{ |
61 |
|
Trace("pfnu-debug") << "- " << fa << std::endl; |
62 |
|
} |
63 |
|
std::vector<Node> assump; |
64 |
|
expr::getFreeAssumptions(pf.get(), assump); |
65 |
|
Trace("pfnu-debug") << "Current free assumptions: " << std::endl; |
66 |
|
for (const Node& fa : assump) |
67 |
|
{ |
68 |
|
Trace("pfnu-debug") << "- " << fa << std::endl; |
69 |
|
} |
70 |
|
} |
71 |
|
} |
72 |
14778 |
std::vector<std::shared_ptr<ProofNode>> traversing; |
73 |
7389 |
processInternal(pf, d_freeAssumps, traversing); |
74 |
7389 |
} |
75 |
|
|
76 |
96271 |
void ProofNodeUpdater::processInternal( |
77 |
|
std::shared_ptr<ProofNode> pf, |
78 |
|
const std::vector<Node>& fa, |
79 |
|
std::vector<std::shared_ptr<ProofNode>>& traversing) |
80 |
|
{ |
81 |
96271 |
Trace("pf-process") << "ProofNodeUpdater::process" << std::endl; |
82 |
192542 |
std::unordered_map<std::shared_ptr<ProofNode>, bool> visited; |
83 |
96271 |
std::unordered_map<std::shared_ptr<ProofNode>, bool>::iterator it; |
84 |
192542 |
std::vector<std::shared_ptr<ProofNode>> visit; |
85 |
192542 |
std::shared_ptr<ProofNode> cur; |
86 |
96271 |
visit.push_back(pf); |
87 |
96271 |
std::map<Node, std::shared_ptr<ProofNode>>::iterator itc; |
88 |
|
// A cache from formulas to proof nodes that are in the current scope. |
89 |
|
// Notice that we make a fresh recursive call to process if the current |
90 |
|
// rule is SCOPE below. |
91 |
192542 |
std::map<Node, std::shared_ptr<ProofNode>> resCache; |
92 |
192542 |
Node res; |
93 |
16967980 |
do |
94 |
|
{ |
95 |
17064251 |
cur = visit.back(); |
96 |
17064251 |
visit.pop_back(); |
97 |
17064251 |
it = visited.find(cur); |
98 |
17064251 |
res = cur->getResult(); |
99 |
17064251 |
if (it == visited.end()) |
100 |
|
{ |
101 |
6864224 |
if (d_mergeSubproofs) |
102 |
|
{ |
103 |
2653115 |
itc = resCache.find(res); |
104 |
3198813 |
if (itc != resCache.end()) |
105 |
|
{ |
106 |
|
// already have a proof, merge it into this one |
107 |
545698 |
visited[cur] = true; |
108 |
545698 |
d_pnm->updateNode(cur.get(), itc->second.get()); |
109 |
2070207 |
continue; |
110 |
|
} |
111 |
|
} |
112 |
|
// run update to a fixed point |
113 |
6318526 |
bool continueUpdate = true; |
114 |
6529062 |
while (runUpdate(cur, fa, continueUpdate) && continueUpdate) |
115 |
|
{ |
116 |
105268 |
Trace("pf-process-debug") << "...updated proof." << std::endl; |
117 |
|
} |
118 |
6318526 |
visited[cur] = !continueUpdate; |
119 |
7208455 |
if (!continueUpdate) |
120 |
|
{ |
121 |
|
// no further changes should be made to cur according to the callback |
122 |
1779858 |
Trace("pf-process-debug") |
123 |
889929 |
<< "...marked to not continue update." << std::endl; |
124 |
889929 |
runFinalize(cur, fa, resCache); |
125 |
889929 |
continue; |
126 |
|
} |
127 |
5428597 |
traversing.push_back(cur); |
128 |
5428597 |
visit.push_back(cur); |
129 |
|
// If we are not the top-level proof, we were a scope, or became a scope |
130 |
|
// after updating, we do a separate recursive call to this method. This |
131 |
|
// allows us to properly track the assumptions in scope, which is |
132 |
|
// important for example to merge or to determine updates based on free |
133 |
|
// assumptions. |
134 |
5428597 |
if (cur->getRule() == PfRule::SCOPE && cur != pf) |
135 |
|
{ |
136 |
177764 |
std::vector<Node> nfa; |
137 |
88882 |
nfa.insert(nfa.end(), fa.begin(), fa.end()); |
138 |
88882 |
const std::vector<Node>& args = cur->getArguments(); |
139 |
88882 |
nfa.insert(nfa.end(), args.begin(), args.end()); |
140 |
88882 |
Trace("pfnu-debug2") << "Process new scope with " << args << std::endl; |
141 |
|
// Process in new call separately |
142 |
88882 |
processInternal(cur, nfa, traversing); |
143 |
88882 |
continue; |
144 |
|
} |
145 |
5339715 |
const std::vector<std::shared_ptr<ProofNode>>& ccp = cur->getChildren(); |
146 |
|
// now, process children |
147 |
16879098 |
for (const std::shared_ptr<ProofNode>& cp : ccp) |
148 |
|
{ |
149 |
34618149 |
if (std::find(traversing.begin(), traversing.end(), cp) |
150 |
34618149 |
!= traversing.end()) |
151 |
|
{ |
152 |
|
Unhandled() |
153 |
|
<< "ProofNodeUpdater::processInternal: cyclic proof! (use " |
154 |
|
"--proof-eager-checking)" |
155 |
|
<< std::endl; |
156 |
|
} |
157 |
11539383 |
visit.push_back(cp); |
158 |
|
} |
159 |
|
} |
160 |
10200027 |
else if (!it->second) |
161 |
|
{ |
162 |
5428597 |
Assert(!traversing.empty()); |
163 |
5428597 |
traversing.pop_back(); |
164 |
5428597 |
visited[cur] = true; |
165 |
|
// finalize the node |
166 |
5428597 |
runFinalize(cur, fa, resCache); |
167 |
|
} |
168 |
17064251 |
} while (!visit.empty()); |
169 |
96271 |
Trace("pf-process") << "ProofNodeUpdater::process: finished" << std::endl; |
170 |
96271 |
} |
171 |
|
|
172 |
6423794 |
bool ProofNodeUpdater::runUpdate(std::shared_ptr<ProofNode> cur, |
173 |
|
const std::vector<Node>& fa, |
174 |
|
bool& continueUpdate) |
175 |
|
{ |
176 |
|
// should it be updated? |
177 |
6423794 |
if (!d_cb.shouldUpdate(cur, fa, continueUpdate)) |
178 |
|
{ |
179 |
5158375 |
return false; |
180 |
|
} |
181 |
1265419 |
PfRule id = cur->getRule(); |
182 |
|
// use CDProof to open a scope for which the callback updates |
183 |
2530838 |
CDProof cpf(d_pnm, nullptr, "ProofNodeUpdater::CDProof", d_autoSym); |
184 |
1265419 |
const std::vector<std::shared_ptr<ProofNode>>& cc = cur->getChildren(); |
185 |
2530838 |
std::vector<Node> ccn; |
186 |
2409532 |
for (const std::shared_ptr<ProofNode>& cp : cc) |
187 |
|
{ |
188 |
2288226 |
Node cpres = cp->getResult(); |
189 |
1144113 |
ccn.push_back(cpres); |
190 |
|
// store in the proof |
191 |
1144113 |
cpf.addProof(cp); |
192 |
|
} |
193 |
2530838 |
Node res = cur->getResult(); |
194 |
2530838 |
Trace("pf-process-debug") |
195 |
1265419 |
<< "Updating (" << cur->getRule() << "): " << res << std::endl; |
196 |
|
// only if the callback updated the node |
197 |
1265419 |
if (d_cb.update(res, id, ccn, cur->getArguments(), &cpf, continueUpdate)) |
198 |
|
{ |
199 |
1990394 |
std::shared_ptr<ProofNode> npn = cpf.getProofFor(res); |
200 |
1990394 |
std::vector<Node> fullFa; |
201 |
995197 |
if (d_debugFreeAssumps) |
202 |
|
{ |
203 |
|
expr::getFreeAssumptions(cur.get(), fullFa); |
204 |
|
Trace("pfnu-debug") << "Original proof : " << *cur << std::endl; |
205 |
|
} |
206 |
|
// then, update the original proof node based on this one |
207 |
995197 |
Trace("pf-process-debug") << "Update node..." << std::endl; |
208 |
995197 |
d_pnm->updateNode(cur.get(), npn.get()); |
209 |
995197 |
Trace("pf-process-debug") << "...update node finished." << std::endl; |
210 |
995197 |
if (d_debugFreeAssumps) |
211 |
|
{ |
212 |
|
fullFa.insert(fullFa.end(), fa.begin(), fa.end()); |
213 |
|
// We have that npn is a node we occurring the final updated version of |
214 |
|
// the proof. We can now debug based on the expected set of free |
215 |
|
// assumptions. |
216 |
|
Trace("pfnu-debug") << "Ensure update closed..." << std::endl; |
217 |
|
pfnEnsureClosedWrt( |
218 |
|
npn.get(), fullFa, "pfnu-debug", "ProofNodeUpdater:postupdate"); |
219 |
|
} |
220 |
995197 |
Trace("pf-process-debug") << "..finished" << std::endl; |
221 |
995197 |
return true; |
222 |
|
} |
223 |
270222 |
Trace("pf-process-debug") << "..finished" << std::endl; |
224 |
270222 |
return false; |
225 |
|
} |
226 |
|
|
227 |
6318526 |
void ProofNodeUpdater::runFinalize( |
228 |
|
std::shared_ptr<ProofNode> cur, |
229 |
|
const std::vector<Node>& fa, |
230 |
|
std::map<Node, std::shared_ptr<ProofNode>>& resCache) |
231 |
|
{ |
232 |
6318526 |
if (d_mergeSubproofs) |
233 |
|
{ |
234 |
4214834 |
Node res = cur->getResult(); |
235 |
|
// cache result if we are merging subproofs |
236 |
2107417 |
resCache[res] = cur; |
237 |
|
} |
238 |
6318526 |
if (d_debugFreeAssumps) |
239 |
|
{ |
240 |
|
// We have that npn is a node we occurring the final updated version of |
241 |
|
// the proof. We can now debug based on the expected set of free |
242 |
|
// assumptions. |
243 |
|
Trace("pfnu-debug") << "Ensure update closed..." << std::endl; |
244 |
|
pfnEnsureClosedWrt( |
245 |
|
cur.get(), fa, "pfnu-debug", "ProofNodeUpdater:finalize"); |
246 |
|
} |
247 |
6318526 |
} |
248 |
|
|
249 |
|
void ProofNodeUpdater::setDebugFreeAssumptions( |
250 |
|
const std::vector<Node>& freeAssumps) |
251 |
|
{ |
252 |
|
d_freeAssumps.clear(); |
253 |
|
d_freeAssumps.insert( |
254 |
|
d_freeAssumps.end(), freeAssumps.begin(), freeAssumps.end()); |
255 |
|
d_debugFreeAssumps = true; |
256 |
|
} |
257 |
|
|
258 |
27735 |
} // namespace cvc5 |