1 /********************* */
2 /*! \file term_conversion_proof_generator.cpp
4 ** Top contributors (to current version):
6 ** This file is part of the CVC4 project.
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.\endverbatim
12 ** \brief Implementation of term conversion proof generator utility
15 #include "expr/term_conversion_proof_generator.h"
17 #include "expr/proof_checker.h"
18 #include "expr/proof_node.h"
19 #include "expr/term_context.h"
20 #include "expr/term_context_stack.h"
22 using namespace CVC4::kind
;
26 std::ostream
& operator<<(std::ostream
& out
, TConvPolicy tcpol
)
30 case TConvPolicy::FIXPOINT
: out
<< "FIXPOINT"; break;
31 case TConvPolicy::ONCE
: out
<< "ONCE"; break;
32 default: out
<< "TConvPolicy:unknown"; break;
37 std::ostream
& operator<<(std::ostream
& out
, TConvCachePolicy tcpol
)
41 case TConvCachePolicy::STATIC
: out
<< "STATIC"; break;
42 case TConvCachePolicy::DYNAMIC
: out
<< "DYNAMIC"; break;
43 case TConvCachePolicy::NEVER
: out
<< "NEVER"; break;
44 default: out
<< "TConvCachePolicy:unknown"; break;
49 TConvProofGenerator::TConvProofGenerator(ProofNodeManager
* pnm
,
52 TConvCachePolicy cpol
,
56 : d_proof(pnm
, nullptr, c
, name
+ "::LazyCDProof"),
57 d_preRewriteMap(c
? c
: &d_context
),
58 d_postRewriteMap(c
? c
: &d_context
),
63 d_rewriteOps(rewriteOps
)
67 TConvProofGenerator::~TConvProofGenerator() {}
69 void TConvProofGenerator::addRewriteStep(Node t
,
77 Node eq
= registerRewriteStep(t
, s
, tctx
, isPre
);
80 d_proof
.addLazyStep(eq
, pg
, trustId
, isClosed
);
84 void TConvProofGenerator::addRewriteStep(
85 Node t
, Node s
, ProofStep ps
, bool isPre
, uint32_t tctx
)
87 Node eq
= registerRewriteStep(t
, s
, tctx
, isPre
);
90 d_proof
.addStep(eq
, ps
);
94 void TConvProofGenerator::addRewriteStep(Node t
,
97 const std::vector
<Node
>& children
,
98 const std::vector
<Node
>& args
,
102 Node eq
= registerRewriteStep(t
, s
, tctx
, isPre
);
105 d_proof
.addStep(eq
, id
, children
, args
);
109 bool TConvProofGenerator::hasRewriteStep(Node t
,
113 return !getRewriteStep(t
, tctx
, isPre
).isNull();
116 Node
TConvProofGenerator::getRewriteStep(Node t
,
121 if (d_tcontext
!= nullptr)
123 thash
= TCtxNode::computeNodeHash(t
, tctx
);
125 return getRewriteStepInternal(thash
, isPre
);
128 Node
TConvProofGenerator::registerRewriteStep(Node t
,
138 if (d_tcontext
!= nullptr)
140 thash
= TCtxNode::computeNodeHash(t
, tctx
);
144 // don't use term context ids if not using term context
147 // should not rewrite term to two different things
148 if (!getRewriteStepInternal(thash
, isPre
).isNull())
150 Assert(getRewriteStepInternal(thash
, isPre
) == s
)
151 << identify() << " rewriting " << t
<< " to both " << s
<< " and "
152 << getRewriteStepInternal(thash
, isPre
);
155 NodeNodeMap
& rm
= isPre
? d_preRewriteMap
: d_postRewriteMap
;
157 if (d_cpolicy
== TConvCachePolicy::DYNAMIC
)
165 std::shared_ptr
<ProofNode
> TConvProofGenerator::getProofFor(Node f
)
167 Trace("tconv-pf-gen") << "TConvProofGenerator::getProofFor: " << identify()
168 << ": " << f
<< std::endl
;
169 if (f
.getKind() != EQUAL
)
171 std::stringstream serr
;
172 serr
<< "TConvProofGenerator::getProofFor: " << identify()
173 << ": fail, non-equality " << f
;
174 Unhandled() << serr
.str();
175 Trace("tconv-pf-gen") << serr
.str() << std::endl
;
178 // we use the existing proofs
180 d_proof
.getManager(), &d_proof
, nullptr, d_name
+ "::LazyCDProof");
183 // assertion failure in debug
184 Assert(false) << "TConvProofGenerator::getProofFor: " << identify()
185 << ": don't ask for trivial proofs";
186 lpf
.addStep(f
, PfRule::REFL
, {}, {f
[0]});
190 Node conc
= getProofForRewriting(f
[0], lpf
, d_tcontext
);
193 bool debugTraceEnabled
= Trace
.isOn("tconv-pf-gen-debug");
194 Assert(conc
.getKind() == EQUAL
&& conc
[0] == f
[0]);
195 std::stringstream serr
;
196 serr
<< "TConvProofGenerator::getProofFor: " << toStringDebug()
197 << ": failed, mismatch";
198 if (!debugTraceEnabled
)
200 serr
<< " (see -t tconv-pf-gen-debug for details)";
203 serr
<< " source: " << f
[0] << std::endl
;
204 serr
<< " requested conclusion: " << f
[1] << std::endl
;
205 serr
<< "conclusion from generator: " << conc
[1] << std::endl
;
207 if (debugTraceEnabled
)
209 Trace("tconv-pf-gen-debug") << "Printing rewrite steps..." << std::endl
;
210 for (size_t r
= 0; r
< 2; r
++)
212 const NodeNodeMap
& rm
= r
== 0 ? d_preRewriteMap
: d_postRewriteMap
;
213 serr
<< "Rewrite steps (" << (r
== 0 ? "pre" : "post")
214 << "):" << std::endl
;
215 for (NodeNodeMap::const_iterator it
= rm
.begin(); it
!= rm
.end();
218 serr
<< (*it
).first
<< " -> " << (*it
).second
<< std::endl
;
222 Unhandled() << serr
.str();
226 std::shared_ptr
<ProofNode
> pfn
= lpf
.getProofFor(f
);
227 Trace("tconv-pf-gen") << "... success" << std::endl
;
228 Assert (pfn
!=nullptr);
229 Trace("tconv-pf-gen-debug") << "... proof is " << *pfn
<< std::endl
;
233 Node
TConvProofGenerator::getProofForRewriting(Node t
,
237 NodeManager
* nm
= NodeManager::currentNM();
238 // Invariant: if visited[hash(t)] = s or rewritten[hash(t)] = s and t,s are
239 // distinct, then pf is able to generate a proof of t=s. We must
240 // Node in the domains of the maps below due to hashing creating new (SEXPR)
243 // the final rewritten form of terms
244 std::unordered_map
<Node
, Node
, TNodeHashFunction
> visited
;
245 // the rewritten form of terms we have processed so far
246 std::unordered_map
<Node
, Node
, TNodeHashFunction
> rewritten
;
247 std::unordered_map
<Node
, Node
, TNodeHashFunction
>::iterator it
;
248 std::unordered_map
<Node
, Node
, TNodeHashFunction
>::iterator itr
;
249 std::map
<Node
, std::shared_ptr
<ProofNode
> >::iterator itc
;
250 Trace("tconv-pf-gen-rewrite")
251 << "TConvProofGenerator::getProofForRewriting: " << toStringDebug()
253 Trace("tconv-pf-gen-rewrite") << "Input: " << t
<< std::endl
;
254 // if provided, we use term context for cache
255 std::shared_ptr
<TCtxStack
> visitctx
;
256 // otherwise, visit is used if we don't have a term context
257 std::vector
<TNode
> visit
;
261 visitctx
= std::make_shared
<TCtxStack
>(tctx
);
262 visitctx
->pushInitial(t
);
263 tinitialHash
= TCtxNode::computeNodeHash(t
, tctx
->initialValue());
271 uint32_t curCVal
= 0;
275 // pop the top element
278 std::pair
<Node
, uint32_t> curPair
= visitctx
->getCurrent();
280 curCVal
= curPair
.second
;
281 curHash
= TCtxNode::computeNodeHash(cur
, curCVal
);
290 Trace("tconv-pf-gen-rewrite") << "* visit : " << curHash
<< std::endl
;
291 // has the proof for cur been cached?
292 itc
= d_cache
.find(curHash
);
293 if (itc
!= d_cache
.end())
295 Node res
= itc
->second
->getResult();
296 Assert(res
.getKind() == EQUAL
);
297 Assert(!res
[1].isNull());
298 visited
[curHash
] = res
[1];
299 pf
.addProof(itc
->second
);
302 it
= visited
.find(curHash
);
303 if (it
== visited
.end())
305 Trace("tconv-pf-gen-rewrite") << "- previsit" << std::endl
;
306 visited
[curHash
] = Node::null();
307 // did we rewrite the current node (at pre-rewrite)?
308 Node rcur
= getRewriteStepInternal(curHash
, true);
311 Trace("tconv-pf-gen-rewrite")
312 << "*** " << curHash
<< " prerewrites to " << rcur
<< std::endl
;
313 // d_proof has a proof of cur = rcur. Hence there is nothing
314 // to do here, as pf will reference d_proof to get its proof.
315 if (d_policy
== TConvPolicy::FIXPOINT
)
317 // It may be the case that rcur also rewrites, thus we cannot assign
318 // the final rewritten form for cur yet. Instead we revisit cur after
319 // finishing visiting rcur.
320 rewritten
[curHash
] = rcur
;
323 visitctx
->push(cur
, curCVal
);
324 visitctx
->push(rcur
, curCVal
);
328 visit
.push_back(cur
);
329 visit
.push_back(rcur
);
334 Assert(d_policy
== TConvPolicy::ONCE
);
335 Trace("tconv-pf-gen-rewrite") << "-> (once, prewrite) " << curHash
336 << " = " << rcur
<< std::endl
;
337 // not rewriting again, rcur is final
338 Assert(!rcur
.isNull());
339 visited
[curHash
] = rcur
;
340 doCache(curHash
, cur
, rcur
, pf
);
343 else if (tctx
!= nullptr)
345 visitctx
->push(cur
, curCVal
);
346 // visit operator if apply uf
347 if (d_rewriteOps
&& cur
.getKind() == APPLY_UF
)
349 visitctx
->pushOp(cur
, curCVal
);
351 visitctx
->pushChildren(cur
, curCVal
);
355 visit
.push_back(cur
);
356 // visit operator if apply uf
357 if (d_rewriteOps
&& cur
.getKind() == APPLY_UF
)
359 visit
.push_back(cur
.getOperator());
361 visit
.insert(visit
.end(), cur
.begin(), cur
.end());
364 else if (it
->second
.isNull())
366 itr
= rewritten
.find(curHash
);
367 if (itr
!= rewritten
.end())
369 // only can generate partially rewritten nodes when rewrite again is
371 Assert(d_policy
!= TConvPolicy::ONCE
);
372 // if it was rewritten, check the status of the rewritten node,
373 // which should be finished now
374 Node rcur
= itr
->second
;
375 Trace("tconv-pf-gen-rewrite")
376 << "- postvisit, previously rewritten to " << rcur
<< std::endl
;
377 Node rcurHash
= rcur
;
380 rcurHash
= TCtxNode::computeNodeHash(rcur
, curCVal
);
383 // the final rewritten form of cur is the final form of rcur
384 Node rcurFinal
= visited
[rcurHash
];
385 Assert(!rcurFinal
.isNull());
386 if (rcurFinal
!= rcur
)
388 // must connect via TRANS
389 std::vector
<Node
> pfChildren
;
390 pfChildren
.push_back(cur
.eqNode(rcur
));
391 pfChildren
.push_back(rcur
.eqNode(rcurFinal
));
392 Node result
= cur
.eqNode(rcurFinal
);
393 pf
.addStep(result
, PfRule::TRANS
, pfChildren
, {});
395 Trace("tconv-pf-gen-rewrite")
396 << "-> (rewritten postrewrite) " << curHash
<< " = " << rcurFinal
398 visited
[curHash
] = rcurFinal
;
399 doCache(curHash
, cur
, rcurFinal
, pf
);
403 Trace("tconv-pf-gen-rewrite") << "- postvisit" << std::endl
;
405 Node retHash
= curHash
;
406 bool childChanged
= false;
407 std::vector
<Node
> children
;
408 Kind ck
= cur
.getKind();
409 if (d_rewriteOps
&& ck
== APPLY_UF
)
411 // the operator of APPLY_UF is visited
412 Node cop
= cur
.getOperator();
415 uint32_t coval
= tctx
->computeValueOp(cur
, curCVal
);
416 Node coHash
= TCtxNode::computeNodeHash(cop
, coval
);
417 it
= visited
.find(coHash
);
421 it
= visited
.find(cop
);
423 Assert(it
!= visited
.end());
424 Assert(!it
->second
.isNull());
425 childChanged
= childChanged
|| cop
!= it
->second
;
426 children
.push_back(it
->second
);
428 else if (cur
.getMetaKind() == metakind::PARAMETERIZED
)
430 // all other parametrized operators are unchanged
431 children
.push_back(cur
.getOperator());
433 // get the results of the children
436 for (size_t i
= 0, nchild
= cur
.getNumChildren(); i
< nchild
; i
++)
439 uint32_t cnval
= tctx
->computeValue(cur
, curCVal
, i
);
440 Node cnHash
= TCtxNode::computeNodeHash(cn
, cnval
);
441 it
= visited
.find(cnHash
);
442 Assert(it
!= visited
.end());
443 Assert(!it
->second
.isNull());
444 childChanged
= childChanged
|| cn
!= it
->second
;
445 children
.push_back(it
->second
);
450 // can use simple loop if not term-context-sensitive
451 for (const Node
& cn
: cur
)
453 it
= visited
.find(cn
);
454 Assert(it
!= visited
.end());
455 Assert(!it
->second
.isNull());
456 childChanged
= childChanged
|| cn
!= it
->second
;
457 children
.push_back(it
->second
);
462 ret
= nm
->mkNode(ck
, children
);
463 rewritten
[curHash
] = ret
;
464 // congruence to show (cur = ret)
465 PfRule congRule
= PfRule::CONG
;
466 std::vector
<Node
> pfChildren
;
467 std::vector
<Node
> pfArgs
;
468 pfArgs
.push_back(ProofRuleChecker::mkKindNode(ck
));
469 if (ck
== APPLY_UF
&& children
[0] != cur
.getOperator())
471 // use HO_CONG if the operator changed
472 congRule
= PfRule::HO_CONG
;
473 pfChildren
.push_back(cur
.getOperator().eqNode(children
[0]));
475 else if (kind::metaKindOf(ck
) == kind::metakind::PARAMETERIZED
)
477 pfArgs
.push_back(cur
.getOperator());
479 for (size_t i
= 0, size
= cur
.getNumChildren(); i
< size
; i
++)
481 if (cur
[i
] == ret
[i
])
483 // ensure REFL proof for unchanged children
484 pf
.addStep(cur
[i
].eqNode(cur
[i
]), PfRule::REFL
, {}, {cur
[i
]});
486 pfChildren
.push_back(cur
[i
].eqNode(ret
[i
]));
488 Node result
= cur
.eqNode(ret
);
489 pf
.addStep(result
, congRule
, pfChildren
, pfArgs
);
490 // must update the hash
494 retHash
= TCtxNode::computeNodeHash(ret
, curCVal
);
497 else if (tctx
!= nullptr)
499 // now we need the hash
500 retHash
= TCtxNode::computeNodeHash(cur
, curCVal
);
502 // did we rewrite ret (at post-rewrite)?
503 Node rret
= getRewriteStepInternal(retHash
, false);
504 if (!rret
.isNull() && d_policy
== TConvPolicy::FIXPOINT
)
506 Trace("tconv-pf-gen-rewrite")
507 << "*** " << retHash
<< " postrewrites to " << rret
<< std::endl
;
508 // d_proof should have a proof of ret = rret, hence nothing to do
509 // here, for the same reasons as above. It also may be the case that
510 // rret rewrites, hence we must revisit ret.
511 rewritten
[retHash
] = rret
;
516 visitctx
->push(cur
, curCVal
);
518 visitctx
->push(ret
, curCVal
);
519 visitctx
->push(rret
, curCVal
);
525 visit
.push_back(cur
);
527 visit
.push_back(ret
);
528 visit
.push_back(rret
);
533 // take its rewrite if it rewrote and we have ONCE rewriting policy
534 ret
= rret
.isNull() ? ret
: rret
;
535 Trace("tconv-pf-gen-rewrite")
536 << "-> (postrewrite) " << curHash
<< " = " << ret
<< std::endl
;
538 Assert(!ret
.isNull());
539 visited
[curHash
] = ret
;
540 doCache(curHash
, cur
, ret
, pf
);
546 Trace("tconv-pf-gen-rewrite") << "- already visited" << std::endl
;
548 } while (!(tctx
!= nullptr ? visitctx
->empty() : visit
.empty()));
549 Assert(visited
.find(tinitialHash
) != visited
.end());
550 Assert(!visited
.find(tinitialHash
)->second
.isNull());
551 Trace("tconv-pf-gen-rewrite")
552 << "...finished, return " << visited
[tinitialHash
] << std::endl
;
553 // return the conclusion of the overall proof
554 return t
.eqNode(visited
[tinitialHash
]);
557 void TConvProofGenerator::doCache(Node curHash
,
562 if (d_cpolicy
!= TConvCachePolicy::NEVER
)
564 Node eq
= cur
.eqNode(r
);
565 d_cache
[curHash
] = pf
.getProofFor(eq
);
569 Node
TConvProofGenerator::getRewriteStepInternal(Node t
, bool isPre
) const
571 const NodeNodeMap
& rm
= isPre
? d_preRewriteMap
: d_postRewriteMap
;
572 NodeNodeMap::const_iterator it
= rm
.find(t
);
579 std::string
TConvProofGenerator::identify() const { return d_name
; }
581 std::string
TConvProofGenerator::toStringDebug() const
583 std::stringstream ss
;
584 ss
<< identify() << " (policy=" << d_policy
<< ", cache policy=" << d_cpolicy
585 << (d_tcontext
!= nullptr ? ", term-context-sensitive" : "") << ")";