1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Aina Niemetz |
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. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "proof/proof.h" |
17 |
|
|
18 |
|
#include "proof/proof_checker.h" |
19 |
|
#include "proof/proof_node.h" |
20 |
|
#include "proof/proof_node_manager.h" |
21 |
|
|
22 |
|
using namespace cvc5::kind; |
23 |
|
|
24 |
|
namespace cvc5 { |
25 |
|
|
26 |
14934 |
CDProof::CDProof(ProofNodeManager* pnm, |
27 |
|
context::Context* c, |
28 |
|
const std::string& name, |
29 |
14934 |
bool autoSymm) |
30 |
|
: d_manager(pnm), |
31 |
|
d_context(), |
32 |
|
d_nodes(c ? c : &d_context), |
33 |
|
d_name(name), |
34 |
14934 |
d_autoSymm(autoSymm) |
35 |
|
{ |
36 |
14934 |
} |
37 |
|
|
38 |
14994 |
CDProof::~CDProof() {} |
39 |
|
|
40 |
40250 |
std::shared_ptr<ProofNode> CDProof::getProofFor(Node fact) |
41 |
|
{ |
42 |
80500 |
std::shared_ptr<ProofNode> pf = getProofSymm(fact); |
43 |
40250 |
if (pf != nullptr) |
44 |
|
{ |
45 |
30203 |
return pf; |
46 |
|
} |
47 |
|
// add as assumption |
48 |
20094 |
std::vector<Node> pargs = {fact}; |
49 |
20094 |
std::vector<std::shared_ptr<ProofNode>> passume; |
50 |
|
std::shared_ptr<ProofNode> pfa = |
51 |
20094 |
d_manager->mkNode(PfRule::ASSUME, passume, pargs, fact); |
52 |
10047 |
d_nodes.insert(fact, pfa); |
53 |
10047 |
return pfa; |
54 |
|
} |
55 |
|
|
56 |
481396 |
std::shared_ptr<ProofNode> CDProof::getProof(Node fact) const |
57 |
|
{ |
58 |
481396 |
NodeProofNodeMap::iterator it = d_nodes.find(fact); |
59 |
481396 |
if (it != d_nodes.end()) |
60 |
|
{ |
61 |
204086 |
return (*it).second; |
62 |
|
} |
63 |
277310 |
return nullptr; |
64 |
|
} |
65 |
|
|
66 |
238757 |
std::shared_ptr<ProofNode> CDProof::getProofSymm(Node fact) |
67 |
|
{ |
68 |
238757 |
Trace("cdproof") << "CDProof::getProofSymm: " << fact << std::endl; |
69 |
477514 |
std::shared_ptr<ProofNode> pf = getProof(fact); |
70 |
238757 |
if (pf != nullptr && !isAssumption(pf.get())) |
71 |
|
{ |
72 |
78356 |
Trace("cdproof") << "...existing non-assume " << pf->getRule() << std::endl; |
73 |
78356 |
return pf; |
74 |
|
} |
75 |
160401 |
else if (!d_autoSymm) |
76 |
|
{ |
77 |
|
Trace("cdproof") << "...not auto considering symmetry" << std::endl; |
78 |
|
return pf; |
79 |
|
} |
80 |
320802 |
Node symFact = getSymmFact(fact); |
81 |
160401 |
if (symFact.isNull()) |
82 |
|
{ |
83 |
50403 |
Trace("cdproof") << "...no possible symm" << std::endl; |
84 |
|
// no symmetry possible, return original proof (possibly assumption) |
85 |
50403 |
return pf; |
86 |
|
} |
87 |
|
// See if a proof exists for the opposite direction, if so, add the step. |
88 |
|
// Notice that SYMM is also disallowed. |
89 |
219996 |
std::shared_ptr<ProofNode> pfs = getProof(symFact); |
90 |
109998 |
if (pfs != nullptr) |
91 |
|
{ |
92 |
|
// The symmetric fact exists, and the current one either does not, or is |
93 |
|
// an assumption. We make a new proof that applies SYMM to pfs. |
94 |
29242 |
std::vector<std::shared_ptr<ProofNode>> pschild; |
95 |
25539 |
pschild.push_back(pfs); |
96 |
29242 |
std::vector<Node> args; |
97 |
25539 |
if (pf == nullptr) |
98 |
|
{ |
99 |
21836 |
Trace("cdproof") << "...fresh make symm" << std::endl; |
100 |
43672 |
std::shared_ptr<ProofNode> psym = d_manager->mkSymm(pfs, fact); |
101 |
21836 |
Assert(psym != nullptr); |
102 |
21836 |
d_nodes.insert(fact, psym); |
103 |
21836 |
return psym; |
104 |
|
} |
105 |
3703 |
else if (!isAssumption(pfs.get())) |
106 |
|
{ |
107 |
|
// if its not an assumption, make the connection |
108 |
|
Trace("cdproof") << "...update symm" << std::endl; |
109 |
|
// update pf |
110 |
|
bool sret = d_manager->updateNode(pf.get(), PfRule::SYMM, pschild, args); |
111 |
|
AlwaysAssert(sret); |
112 |
|
} |
113 |
|
} |
114 |
|
else |
115 |
|
{ |
116 |
168918 |
Trace("cdproof") << "...no symm, return " |
117 |
84459 |
<< (pf == nullptr ? "null" : "non-null") << std::endl; |
118 |
|
} |
119 |
|
// return original proof (possibly assumption) |
120 |
88162 |
return pf; |
121 |
|
} |
122 |
|
|
123 |
118698 |
bool CDProof::addStep(Node expected, |
124 |
|
PfRule id, |
125 |
|
const std::vector<Node>& children, |
126 |
|
const std::vector<Node>& args, |
127 |
|
bool ensureChildren, |
128 |
|
CDPOverwrite opolicy) |
129 |
|
{ |
130 |
237396 |
Trace("cdproof") << "CDProof::addStep: " << identify() << " : " << id << " " |
131 |
118698 |
<< expected << ", ensureChildren = " << ensureChildren |
132 |
118698 |
<< ", overwrite policy = " << opolicy << std::endl; |
133 |
237396 |
Trace("cdproof-debug") << "CDProof::addStep: " << identify() |
134 |
118698 |
<< " : children: " << children << "\n"; |
135 |
237396 |
Trace("cdproof-debug") << "CDProof::addStep: " << identify() |
136 |
118698 |
<< " : args: " << args << "\n"; |
137 |
|
// We must always provide expected to this method |
138 |
118698 |
Assert(!expected.isNull()); |
139 |
|
|
140 |
237396 |
std::shared_ptr<ProofNode> pprev = getProofSymm(expected); |
141 |
118698 |
if (pprev != nullptr) |
142 |
|
{ |
143 |
37581 |
if (!shouldOverwrite(pprev.get(), id, opolicy)) |
144 |
|
{ |
145 |
|
// we should not overwrite the current step |
146 |
37442 |
Trace("cdproof") << "...success, no overwrite" << std::endl; |
147 |
37442 |
return true; |
148 |
|
} |
149 |
278 |
Trace("cdproof") << "existing proof " << pprev->getRule() |
150 |
139 |
<< ", overwrite..." << std::endl; |
151 |
|
// we will overwrite the existing proof node by updating its contents below |
152 |
|
} |
153 |
|
// collect the child proofs, for each premise |
154 |
162512 |
std::vector<std::shared_ptr<ProofNode>> pchildren; |
155 |
159354 |
for (const Node& c : children) |
156 |
|
{ |
157 |
78098 |
Trace("cdproof") << "- get child " << c << std::endl; |
158 |
156196 |
std::shared_ptr<ProofNode> pc = getProofSymm(c); |
159 |
78098 |
if (pc == nullptr) |
160 |
|
{ |
161 |
29913 |
if (ensureChildren) |
162 |
|
{ |
163 |
|
// failed to get a proof for a child, fail |
164 |
|
Trace("cdproof") << "...fail, no child" << std::endl; |
165 |
|
return false; |
166 |
|
} |
167 |
29913 |
Trace("cdproof") << "--- add assume" << std::endl; |
168 |
|
// otherwise, we initialize it as an assumption |
169 |
59826 |
std::vector<Node> pcargs = {c}; |
170 |
59826 |
std::vector<std::shared_ptr<ProofNode>> pcassume; |
171 |
29913 |
pc = d_manager->mkNode(PfRule::ASSUME, pcassume, pcargs, c); |
172 |
|
// assumptions never fail to check |
173 |
29913 |
Assert(pc != nullptr); |
174 |
29913 |
d_nodes.insert(c, pc); |
175 |
|
} |
176 |
78098 |
pchildren.push_back(pc); |
177 |
|
} |
178 |
|
|
179 |
|
// the user may have provided SYMM of an assumption |
180 |
81256 |
if (id == PfRule::SYMM) |
181 |
|
{ |
182 |
|
Assert(pchildren.size() == 1); |
183 |
|
if (isAssumption(pchildren[0].get())) |
184 |
|
{ |
185 |
|
// the step we are constructing is a (symmetric fact of an) assumption, so |
186 |
|
// there is no use adding it to the proof. |
187 |
|
return true; |
188 |
|
} |
189 |
|
} |
190 |
|
|
191 |
81256 |
bool ret = true; |
192 |
|
// create or update it |
193 |
162512 |
std::shared_ptr<ProofNode> pthis; |
194 |
81256 |
if (pprev == nullptr) |
195 |
|
{ |
196 |
81117 |
Trace("cdproof") << " new node " << expected << "..." << std::endl; |
197 |
81117 |
pthis = d_manager->mkNode(id, pchildren, args, expected); |
198 |
81117 |
if (pthis == nullptr) |
199 |
|
{ |
200 |
|
// failed to construct the node, perhaps due to a proof checking failure |
201 |
|
Trace("cdproof") << "...fail, proof checking" << std::endl; |
202 |
|
return false; |
203 |
|
} |
204 |
81117 |
d_nodes.insert(expected, pthis); |
205 |
|
} |
206 |
|
else |
207 |
|
{ |
208 |
139 |
Trace("cdproof") << " update node " << expected << "..." << std::endl; |
209 |
|
// update its value |
210 |
139 |
pthis = pprev; |
211 |
|
// We return the value of updateNode here. This means this method may return |
212 |
|
// false if this call failed, regardless of whether we already have a proof |
213 |
|
// step for expected. |
214 |
139 |
ret = d_manager->updateNode(pthis.get(), id, pchildren, args); |
215 |
|
} |
216 |
81256 |
if (ret) |
217 |
|
{ |
218 |
|
// the result of the proof node should be expected |
219 |
81256 |
Assert(pthis->getResult() == expected); |
220 |
|
|
221 |
|
// notify new proof |
222 |
81256 |
notifyNewProof(expected); |
223 |
|
} |
224 |
|
|
225 |
81256 |
Trace("cdproof") << "...return " << ret << std::endl; |
226 |
81256 |
return ret; |
227 |
|
} |
228 |
|
|
229 |
82967 |
void CDProof::notifyNewProof(Node expected) |
230 |
|
{ |
231 |
82967 |
if (!d_autoSymm) |
232 |
|
{ |
233 |
|
return; |
234 |
|
} |
235 |
|
// ensure SYMM proof is also linked to an existing proof, if it is an |
236 |
|
// assumption. |
237 |
165934 |
Node symExpected = getSymmFact(expected); |
238 |
82967 |
if (!symExpected.isNull()) |
239 |
|
{ |
240 |
43244 |
Trace("cdproof") << " check connect symmetry " << symExpected << std::endl; |
241 |
|
// if it exists, we may need to update it |
242 |
86488 |
std::shared_ptr<ProofNode> pfs = getProof(symExpected); |
243 |
43244 |
if (pfs != nullptr) |
244 |
|
{ |
245 |
|
Trace("cdproof") << " connect via getProofSymm method..." << std::endl; |
246 |
|
// call the get function with symmetry, which will do the update |
247 |
|
std::shared_ptr<ProofNode> pfss = getProofSymm(symExpected); |
248 |
|
} |
249 |
|
else |
250 |
|
{ |
251 |
43244 |
Trace("cdproof") << " no connect" << std::endl; |
252 |
|
} |
253 |
|
} |
254 |
|
} |
255 |
|
|
256 |
53044 |
bool CDProof::addStep(Node expected, |
257 |
|
const ProofStep& step, |
258 |
|
bool ensureChildren, |
259 |
|
CDPOverwrite opolicy) |
260 |
|
{ |
261 |
159132 |
return addStep(expected, |
262 |
53044 |
step.d_rule, |
263 |
|
step.d_children, |
264 |
|
step.d_args, |
265 |
|
ensureChildren, |
266 |
106088 |
opolicy); |
267 |
|
} |
268 |
|
|
269 |
242 |
bool CDProof::addSteps(const ProofStepBuffer& psb, |
270 |
|
bool ensureChildren, |
271 |
|
CDPOverwrite opolicy) |
272 |
|
{ |
273 |
242 |
const std::vector<std::pair<Node, ProofStep>>& steps = psb.getSteps(); |
274 |
540 |
for (const std::pair<Node, ProofStep>& ps : steps) |
275 |
|
{ |
276 |
298 |
if (!addStep(ps.first, ps.second, ensureChildren, opolicy)) |
277 |
|
{ |
278 |
|
return false; |
279 |
|
} |
280 |
|
} |
281 |
242 |
return true; |
282 |
|
} |
283 |
|
|
284 |
1711 |
bool CDProof::addProof(std::shared_ptr<ProofNode> pn, |
285 |
|
CDPOverwrite opolicy, |
286 |
|
bool doCopy) |
287 |
|
{ |
288 |
1711 |
if (!doCopy) |
289 |
|
{ |
290 |
|
// If we aren't doing a deep copy, we either store pn or link its top |
291 |
|
// node into the existing pointer |
292 |
3422 |
Node curFact = pn->getResult(); |
293 |
3422 |
std::shared_ptr<ProofNode> cur = getProofSymm(curFact); |
294 |
1711 |
if (cur == nullptr) |
295 |
|
{ |
296 |
|
// Assert that the checker of this class agrees with (the externally |
297 |
|
// provided) pn. This ensures that if pn was checked by a different |
298 |
|
// checker than the one of the manager in this class, then it is double |
299 |
|
// checked here, so that this class maintains the invariant that all of |
300 |
|
// its nodes in d_nodes have been checked by the underlying checker. |
301 |
1646 |
Assert(d_manager->getChecker() == nullptr |
302 |
|
|| d_manager->getChecker()->check(pn.get(), curFact) == curFact); |
303 |
|
// just store the proof for fact |
304 |
1646 |
d_nodes.insert(curFact, pn); |
305 |
|
} |
306 |
65 |
else if (shouldOverwrite(cur.get(), pn->getRule(), opolicy)) |
307 |
|
{ |
308 |
|
// We update cur to have the structure of the top node of pn. Notice that |
309 |
|
// the interface to update this node will ensure that the proof apf is a |
310 |
|
// proof of the assumption. If it does not, then pn was wrong. |
311 |
|
if (!d_manager->updateNode( |
312 |
|
cur.get(), pn->getRule(), pn->getChildren(), pn->getArguments())) |
313 |
|
{ |
314 |
|
return false; |
315 |
|
} |
316 |
|
} |
317 |
|
// also need to connect via SYMM if necessary |
318 |
1711 |
notifyNewProof(curFact); |
319 |
1711 |
return true; |
320 |
|
} |
321 |
|
std::unordered_map<ProofNode*, bool> visited; |
322 |
|
std::unordered_map<ProofNode*, bool>::iterator it; |
323 |
|
std::vector<ProofNode*> visit; |
324 |
|
ProofNode* cur; |
325 |
|
Node curFact; |
326 |
|
visit.push_back(pn.get()); |
327 |
|
bool retValue = true; |
328 |
|
do |
329 |
|
{ |
330 |
|
cur = visit.back(); |
331 |
|
curFact = cur->getResult(); |
332 |
|
visit.pop_back(); |
333 |
|
it = visited.find(cur); |
334 |
|
if (it == visited.end()) |
335 |
|
{ |
336 |
|
// visit the children |
337 |
|
visited[cur] = false; |
338 |
|
visit.push_back(cur); |
339 |
|
const std::vector<std::shared_ptr<ProofNode>>& cs = cur->getChildren(); |
340 |
|
for (const std::shared_ptr<ProofNode>& c : cs) |
341 |
|
{ |
342 |
|
visit.push_back(c.get()); |
343 |
|
} |
344 |
|
} |
345 |
|
else if (!it->second) |
346 |
|
{ |
347 |
|
// we always call addStep, which may or may not overwrite the |
348 |
|
// current step |
349 |
|
std::vector<Node> pexp; |
350 |
|
const std::vector<std::shared_ptr<ProofNode>>& cs = cur->getChildren(); |
351 |
|
for (const std::shared_ptr<ProofNode>& c : cs) |
352 |
|
{ |
353 |
|
Assert(!c->getResult().isNull()); |
354 |
|
pexp.push_back(c->getResult()); |
355 |
|
} |
356 |
|
// can ensure children at this point |
357 |
|
bool res = addStep( |
358 |
|
curFact, cur->getRule(), pexp, cur->getArguments(), true, opolicy); |
359 |
|
// should always succeed |
360 |
|
Assert(res); |
361 |
|
retValue = retValue && res; |
362 |
|
visited[cur] = true; |
363 |
|
} |
364 |
|
} while (!visit.empty()); |
365 |
|
|
366 |
|
return retValue; |
367 |
|
} |
368 |
|
|
369 |
6863 |
bool CDProof::hasStep(Node fact) |
370 |
|
{ |
371 |
13726 |
std::shared_ptr<ProofNode> pf = getProof(fact); |
372 |
6863 |
if (pf != nullptr && !isAssumption(pf.get())) |
373 |
|
{ |
374 |
6609 |
return true; |
375 |
|
} |
376 |
254 |
else if (!d_autoSymm) |
377 |
|
{ |
378 |
|
return false; |
379 |
|
} |
380 |
508 |
Node symFact = getSymmFact(fact); |
381 |
254 |
if (symFact.isNull()) |
382 |
|
{ |
383 |
68 |
return false; |
384 |
|
} |
385 |
186 |
pf = getProof(symFact); |
386 |
186 |
if (pf != nullptr && !isAssumption(pf.get())) |
387 |
|
{ |
388 |
|
return true; |
389 |
|
} |
390 |
186 |
return false; |
391 |
|
} |
392 |
|
|
393 |
275 |
ProofNodeManager* CDProof::getManager() const { return d_manager; } |
394 |
|
|
395 |
37646 |
bool CDProof::shouldOverwrite(ProofNode* pn, PfRule newId, CDPOverwrite opol) |
396 |
|
{ |
397 |
37646 |
Assert(pn != nullptr); |
398 |
|
// we overwrite only if opol is CDPOverwrite::ALWAYS, or if |
399 |
|
// opol is CDPOverwrite::ASSUME_ONLY and the previously |
400 |
|
// provided proof pn was an assumption and the currently provided step is not |
401 |
|
return opol == CDPOverwrite::ALWAYS |
402 |
37785 |
|| (opol == CDPOverwrite::ASSUME_ONLY && isAssumption(pn) |
403 |
39454 |
&& newId != PfRule::ASSUME); |
404 |
|
} |
405 |
|
|
406 |
142156 |
bool CDProof::isAssumption(ProofNode* pn) |
407 |
|
{ |
408 |
142156 |
PfRule rule = pn->getRule(); |
409 |
142156 |
if (rule == PfRule::ASSUME) |
410 |
|
{ |
411 |
16524 |
return true; |
412 |
|
} |
413 |
125632 |
else if (rule != PfRule::SYMM) |
414 |
|
{ |
415 |
119959 |
return false; |
416 |
|
} |
417 |
5673 |
pn = ProofNodeManager::cancelDoubleSymm(pn); |
418 |
5673 |
rule = pn->getRule(); |
419 |
5673 |
if (rule == PfRule::ASSUME) |
420 |
|
{ |
421 |
|
return true; |
422 |
|
} |
423 |
5673 |
else if (rule != PfRule::SYMM) |
424 |
|
{ |
425 |
|
return false; |
426 |
|
} |
427 |
5673 |
const std::vector<std::shared_ptr<ProofNode>>& pc = pn->getChildren(); |
428 |
5673 |
Assert(pc.size() == 1); |
429 |
5673 |
return pc[0]->getRule() == PfRule::ASSUME; |
430 |
|
} |
431 |
|
|
432 |
62549 |
bool CDProof::isSame(TNode f, TNode g) |
433 |
|
{ |
434 |
62549 |
if (f == g) |
435 |
|
{ |
436 |
34714 |
return true; |
437 |
|
} |
438 |
27835 |
Kind fk = f.getKind(); |
439 |
27835 |
Kind gk = g.getKind(); |
440 |
27835 |
if (fk == EQUAL && gk == EQUAL && f[0] == g[1] && f[1] == g[0]) |
441 |
|
{ |
442 |
|
// symmetric equality |
443 |
9875 |
return true; |
444 |
|
} |
445 |
22145 |
if (fk == NOT && gk == NOT && f[0].getKind() == EQUAL |
446 |
19671 |
&& g[0].getKind() == EQUAL && f[0][0] == g[0][1] && f[0][1] == g[0][0]) |
447 |
|
{ |
448 |
|
// symmetric disequality |
449 |
104 |
return true; |
450 |
|
} |
451 |
17856 |
return false; |
452 |
|
} |
453 |
|
|
454 |
335510 |
Node CDProof::getSymmFact(TNode f) |
455 |
|
{ |
456 |
335510 |
bool polarity = f.getKind() != NOT; |
457 |
671020 |
TNode fatom = polarity ? f : f[0]; |
458 |
335510 |
if (fatom.getKind() != EQUAL || fatom[0] == fatom[1]) |
459 |
|
{ |
460 |
93201 |
return Node::null(); |
461 |
|
} |
462 |
484618 |
Node symFact = fatom[1].eqNode(fatom[0]); |
463 |
242309 |
return polarity ? symFact : symFact.notNode(); |
464 |
|
} |
465 |
|
|
466 |
412726 |
std::string CDProof::identify() const { return d_name; } |
467 |
|
|
468 |
22746 |
} // namespace cvc5 |