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 proof node algorithm utilities. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "proof/proof_node_algorithm.h" |
17 |
|
|
18 |
|
#include "proof/proof_node.h" |
19 |
|
|
20 |
|
namespace cvc5 { |
21 |
|
namespace expr { |
22 |
|
|
23 |
84871 |
void getFreeAssumptions(ProofNode* pn, std::vector<Node>& assump) |
24 |
|
{ |
25 |
169742 |
std::map<Node, std::vector<std::shared_ptr<ProofNode>>> amap; |
26 |
|
std::shared_ptr<ProofNode> spn = std::make_shared<ProofNode>( |
27 |
169742 |
pn->getRule(), pn->getChildren(), pn->getArguments()); |
28 |
84871 |
getFreeAssumptionsMap(spn, amap); |
29 |
114206 |
for (const std::pair<const Node, std::vector<std::shared_ptr<ProofNode>>>& p : |
30 |
84871 |
amap) |
31 |
|
{ |
32 |
114206 |
assump.push_back(p.first); |
33 |
|
} |
34 |
84871 |
} |
35 |
|
|
36 |
391632 |
void getFreeAssumptionsMap( |
37 |
|
std::shared_ptr<ProofNode> pn, |
38 |
|
std::map<Node, std::vector<std::shared_ptr<ProofNode>>>& amap) |
39 |
|
{ |
40 |
|
// proof should not be cyclic |
41 |
|
// visited set false after preorder traversal, true after postorder traversal |
42 |
783264 |
std::unordered_map<ProofNode*, bool> visited; |
43 |
391632 |
std::unordered_map<ProofNode*, bool>::iterator it; |
44 |
783264 |
std::vector<std::shared_ptr<ProofNode>> visit; |
45 |
783264 |
std::vector<std::shared_ptr<ProofNode>> traversing; |
46 |
|
// Maps a bound assumption to the number of bindings it is under |
47 |
|
// e.g. in (SCOPE (SCOPE (ASSUME x) (x y)) (y)), y would be mapped to 2 at |
48 |
|
// (ASSUME x), and x would be mapped to 1. |
49 |
|
// |
50 |
|
// This map is used to track which nodes are in scope while traversing the |
51 |
|
// DAG. The in-scope assumptions are keys in the map. They're removed when |
52 |
|
// their binding count drops to zero. Let's annotate the above example to |
53 |
|
// serve as an illustration: |
54 |
|
// |
55 |
|
// (SCOPE0 (SCOPE1 (ASSUME x) (x y)) (y)) |
56 |
|
// |
57 |
|
// This is how the map changes during the traversal: |
58 |
|
// after previsiting SCOPE0: { y: 1 } |
59 |
|
// after previsiting SCOPE1: { y: 2, x: 1 } |
60 |
|
// at ASSUME: { y: 2, x: 1 } (so x is in scope!) |
61 |
|
// after postvisiting SCOPE1: { y: 1 } |
62 |
|
// after postvisiting SCOPE2: {} |
63 |
|
// |
64 |
783264 |
std::unordered_map<Node, uint32_t> scopeDepth; |
65 |
783264 |
std::shared_ptr<ProofNode> cur; |
66 |
391632 |
visit.push_back(pn); |
67 |
20414291 |
do |
68 |
|
{ |
69 |
20805923 |
cur = visit.back(); |
70 |
20805923 |
visit.pop_back(); |
71 |
20805923 |
it = visited.find(cur.get()); |
72 |
20805923 |
const std::vector<Node>& cargs = cur->getArguments(); |
73 |
20805923 |
if (it == visited.end()) |
74 |
|
{ |
75 |
10221019 |
PfRule id = cur->getRule(); |
76 |
10221019 |
if (id == PfRule::ASSUME) |
77 |
|
{ |
78 |
3874177 |
visited[cur.get()] = true; |
79 |
3874177 |
Assert(cargs.size() == 1); |
80 |
7748354 |
Node f = cargs[0]; |
81 |
3874177 |
if (!scopeDepth.count(f)) |
82 |
|
{ |
83 |
3090268 |
amap[f].push_back(cur); |
84 |
|
} |
85 |
|
} |
86 |
|
else |
87 |
|
{ |
88 |
6346842 |
if (id == PfRule::SCOPE) |
89 |
|
{ |
90 |
|
// mark that its arguments are bound in the current scope |
91 |
998578 |
for (const Node& a : cargs) |
92 |
|
{ |
93 |
819849 |
scopeDepth[a] += 1; |
94 |
|
} |
95 |
|
// will need to unbind the variables below |
96 |
|
} |
97 |
|
// The following loop cannot be merged with the loop above because the |
98 |
|
// same subproof |
99 |
6346842 |
visited[cur.get()] = false; |
100 |
6346842 |
visit.push_back(cur); |
101 |
6346842 |
traversing.push_back(cur); |
102 |
6346842 |
const std::vector<std::shared_ptr<ProofNode>>& cs = cur->getChildren(); |
103 |
20414291 |
for (const std::shared_ptr<ProofNode>& cp : cs) |
104 |
|
{ |
105 |
42202347 |
if (std::find(traversing.begin(), traversing.end(), cp) |
106 |
42202347 |
!= traversing.end()) |
107 |
|
{ |
108 |
|
Unhandled() << "getFreeAssumptionsMap: cyclic proof! (use " |
109 |
|
"--proof-eager-checking)" |
110 |
|
<< std::endl; |
111 |
|
} |
112 |
14067449 |
visit.push_back(cp); |
113 |
|
} |
114 |
|
} |
115 |
|
} |
116 |
10584904 |
else if (!it->second) |
117 |
|
{ |
118 |
6346842 |
Assert(!traversing.empty()); |
119 |
6346842 |
traversing.pop_back(); |
120 |
6346842 |
visited[cur.get()] = true; |
121 |
6346842 |
if (cur->getRule() == PfRule::SCOPE) |
122 |
|
{ |
123 |
|
// unbind its assumptions |
124 |
998578 |
for (const Node& a : cargs) |
125 |
|
{ |
126 |
819849 |
auto scopeCt = scopeDepth.find(a); |
127 |
819849 |
Assert(scopeCt != scopeDepth.end()); |
128 |
819849 |
scopeCt->second -= 1; |
129 |
819849 |
if (scopeCt->second == 0) |
130 |
|
{ |
131 |
749516 |
scopeDepth.erase(scopeCt); |
132 |
|
} |
133 |
|
} |
134 |
|
} |
135 |
|
} |
136 |
20805923 |
} while (!visit.empty()); |
137 |
391632 |
} |
138 |
|
|
139 |
2267395 |
bool containsAssumption(const ProofNode* pn, |
140 |
|
std::unordered_map<const ProofNode*, bool>& caMap) |
141 |
|
{ |
142 |
4534790 |
std::unordered_map<const ProofNode*, bool> visited; |
143 |
2267395 |
std::unordered_map<const ProofNode*, bool>::iterator it; |
144 |
4534790 |
std::vector<const ProofNode*> visit; |
145 |
2267395 |
visit.push_back(pn); |
146 |
2267395 |
bool foundAssumption = false; |
147 |
|
const ProofNode* cur; |
148 |
18756393 |
while (!visit.empty()) |
149 |
|
{ |
150 |
8244499 |
cur = visit.back(); |
151 |
8244499 |
visit.pop_back(); |
152 |
|
// have we already computed? |
153 |
8244499 |
it = caMap.find(cur); |
154 |
12216903 |
if (it != caMap.end()) |
155 |
|
{ |
156 |
|
// if cached, we set found assumption to true if applicable and continue |
157 |
3972404 |
if (it->second) |
158 |
|
{ |
159 |
2558993 |
foundAssumption = true; |
160 |
|
} |
161 |
3972404 |
continue; |
162 |
|
} |
163 |
4272095 |
it = visited.find(cur); |
164 |
4272095 |
if (it == visited.end()) |
165 |
|
{ |
166 |
2267395 |
PfRule r = cur->getRule(); |
167 |
2267395 |
if (r == PfRule::ASSUME) |
168 |
|
{ |
169 |
262695 |
visited[cur] = true; |
170 |
262695 |
caMap[cur] = true; |
171 |
262695 |
foundAssumption = true; |
172 |
|
} |
173 |
2004700 |
else if (!foundAssumption) |
174 |
|
{ |
175 |
|
// if we haven't found an assumption yet, recurse. Otherwise, we will |
176 |
|
// not bother computing whether this subproof contains an assumption |
177 |
|
// since we know its parent already contains one by another child. |
178 |
2004700 |
visited[cur] = false; |
179 |
2004700 |
visit.push_back(cur); |
180 |
|
const std::vector<std::shared_ptr<ProofNode>>& children = |
181 |
2004700 |
cur->getChildren(); |
182 |
5977104 |
for (const std::shared_ptr<ProofNode>& cp : children) |
183 |
|
{ |
184 |
3972404 |
visit.push_back(cp.get()); |
185 |
|
} |
186 |
|
} |
187 |
|
} |
188 |
2004700 |
else if (!it->second) |
189 |
|
{ |
190 |
2004700 |
visited[cur] = true; |
191 |
|
// we contain an assumption if we've found an assumption in a child |
192 |
2004700 |
caMap[cur] = foundAssumption; |
193 |
|
} |
194 |
|
} |
195 |
4534790 |
return caMap[cur]; |
196 |
|
} |
197 |
|
|
198 |
|
bool containsAssumption(const ProofNode* pn) |
199 |
|
{ |
200 |
|
std::unordered_map<const ProofNode*, bool> caMap; |
201 |
|
return containsAssumption(pn, caMap); |
202 |
|
} |
203 |
|
|
204 |
|
bool containsSubproof(ProofNode* pn, ProofNode* pnc) |
205 |
|
{ |
206 |
|
std::unordered_set<const ProofNode*> visited; |
207 |
|
return containsSubproof(pn, pnc, visited); |
208 |
|
} |
209 |
|
|
210 |
|
bool containsSubproof(ProofNode* pn, |
211 |
|
ProofNode* pnc, |
212 |
|
std::unordered_set<const ProofNode*>& visited) |
213 |
|
{ |
214 |
|
std::unordered_map<const ProofNode*, bool>::iterator it; |
215 |
|
std::vector<const ProofNode*> visit; |
216 |
|
visit.push_back(pn); |
217 |
|
const ProofNode* cur; |
218 |
|
while (!visit.empty()) |
219 |
|
{ |
220 |
|
cur = visit.back(); |
221 |
|
visit.pop_back(); |
222 |
|
if (visited.find(cur) == visited.end()) |
223 |
|
{ |
224 |
|
visited.insert(cur); |
225 |
|
if (cur == pnc) |
226 |
|
{ |
227 |
|
return true; |
228 |
|
} |
229 |
|
const std::vector<std::shared_ptr<ProofNode>>& children = |
230 |
|
cur->getChildren(); |
231 |
|
for (const std::shared_ptr<ProofNode>& cp : children) |
232 |
|
{ |
233 |
|
visit.push_back(cp.get()); |
234 |
|
} |
235 |
|
} |
236 |
|
} |
237 |
|
return false; |
238 |
|
} |
239 |
|
|
240 |
|
} // namespace expr |
241 |
29505 |
} // namespace cvc5 |