1 /********************* */
2 /*! \file theory_preprocessor.cpp
4 ** Top contributors (to current version):
5 ** Andrew Reynolds, Morgan Deters, Dejan Jovanovic
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2020 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 The theory preprocessor
15 #include "theory/theory_preprocessor.h"
17 #include "expr/lazy_proof.h"
18 #include "expr/skolem_manager.h"
19 #include "theory/logic_info.h"
20 #include "theory/rewriter.h"
21 #include "theory/theory_engine.h"
28 TheoryPreprocessor::TheoryPreprocessor(TheoryEngine
& engine
,
29 RemoveTermFormulas
& tfr
,
30 context::UserContext
* userContext
,
31 ProofNodeManager
* pnm
)
33 d_logicInfo(engine
.getLogicInfo()),
34 d_ppCache(userContext
),
36 d_tpg(pnm
? new TConvProofGenerator(
39 TConvPolicy::FIXPOINT
,
40 TConvCachePolicy::NEVER
,
41 "TheoryPreprocessor::preprocess_rewrite",
45 d_tpgRew(pnm
? new TConvProofGenerator(pnm
,
47 TConvPolicy::FIXPOINT
,
48 TConvCachePolicy::NEVER
,
49 "TheoryPreprocessor::rewrite")
52 d_lp(pnm
? new LazyCDProof(pnm
,
55 "TheoryPreprocessor::LazyCDProof")
60 // Make the main term conversion sequence generator, which tracks up to
61 // three conversions made in succession:
62 // (1) theory preprocessing+rewriting
63 // (2) term formula removal
65 // Steps (1) and (3) use a common term conversion generator.
66 std::vector
<ProofGenerator
*> ts
;
67 ts
.push_back(d_tpg
.get());
68 ts
.push_back(d_tfr
.getTConvProofGenerator());
69 ts
.push_back(d_tpg
.get());
70 d_tspg
.reset(new TConvSeqProofGenerator(
71 pnm
, ts
, userContext
, "TheoryPreprocessor::sequence"));
72 // Make the "no preprocess" term conversion sequence generator, which
73 // applies only steps (2) and (3), where notice (3) must use the
74 // "pure rewrite" term conversion (d_tpgRew).
75 std::vector
<ProofGenerator
*> tsNoPp
;
76 tsNoPp
.push_back(d_tfr
.getTConvProofGenerator());
77 tsNoPp
.push_back(d_tpgRew
.get());
78 d_tspgNoPp
.reset(new TConvSeqProofGenerator(
79 pnm
, tsNoPp
, userContext
, "TheoryPreprocessor::sequence_no_pp"));
83 TheoryPreprocessor::~TheoryPreprocessor() {}
85 TrustNode
TheoryPreprocessor::preprocess(TNode node
,
86 std::vector
<TrustNode
>& newLemmas
,
87 std::vector
<Node
>& newSkolems
,
88 bool doTheoryPreprocess
)
90 // In this method, all rewriting steps of node are stored in d_tpg.
92 Trace("tpp-debug") << "TheoryPreprocessor::preprocess: start " << node
93 << ", doTheoryPreprocess=" << doTheoryPreprocess
95 // Run theory preprocessing, maybe
97 if (doTheoryPreprocess
)
99 // run theory preprocessing
100 TrustNode tpp
= theoryPreprocess(node
);
101 ppNode
= tpp
.getNode();
105 TrustNode ttfr
= d_tfr
.run(ppNode
, newLemmas
, newSkolems
, false);
106 Node rtfNode
= ttfr
.getNode();
108 if (Debug
.isOn("lemma-ites"))
110 Debug("lemma-ites") << "removed ITEs from lemma: " << ttfr
.getNode()
112 Debug("lemma-ites") << " + now have the following " << newLemmas
.size()
113 << " lemma(s):" << endl
;
114 for (size_t i
= 0, lsize
= newLemmas
.size(); i
<= lsize
; ++i
)
116 Debug("lemma-ites") << " + " << newLemmas
[i
].getNode() << endl
;
118 Debug("lemma-ites") << endl
;
120 // Rewrite the main node, we rewrite and store the proof step, if necessary,
121 // in d_tpg, which maintains the fact that d_tpg can prove the rewrite.
122 Node retNode
= rewriteWithProof(rtfNode
);
124 if (Trace
.isOn("tpp-debug"))
128 Trace("tpp-debug") << "after preprocessing : " << ppNode
<< std::endl
;
130 if (rtfNode
!= ppNode
)
132 Trace("tpp-debug") << "after rtf : " << rtfNode
<< std::endl
;
134 if (retNode
!= rtfNode
)
136 Trace("tpp-debug") << "after rewriting : " << retNode
<< std::endl
;
138 Trace("tpp-debug") << "TheoryPreprocessor::preprocess: finish" << std::endl
;
143 Assert(newLemmas
.empty());
144 return TrustNode::null();
147 // Now, sequence the conversion steps if proofs are enabled.
149 if (isProofEnabled())
151 std::vector
<Node
> cterms
;
152 cterms
.push_back(node
);
153 if (doTheoryPreprocess
)
155 cterms
.push_back(ppNode
);
157 cterms
.push_back(rtfNode
);
158 cterms
.push_back(retNode
);
160 // node -> ppNode via preprocessing + rewriting (if doTheoryPreprocess)
161 // ppNode -> rtfNode via term formula removal
162 // rtfNode -> retNode via rewriting
163 if (!doTheoryPreprocess
)
165 // If preprocessing is not performed, we cannot use the main sequence
166 // generator, instead we use d_tspgNoPp.
167 // We register the top-level rewrite in the pure rewrite term converter.
168 d_tpgRew
->addRewriteStep(
169 rtfNode
, retNode
, PfRule::REWRITE
, {}, {rtfNode
});
170 // Now get the trust node from the sequence generator
171 tret
= d_tspgNoPp
->mkTrustRewriteSequence(cterms
);
175 // we wil use the sequence generator
176 tret
= d_tspg
->mkTrustRewriteSequence(cterms
);
178 tret
.debugCheckClosed("tpp-debug", "TheoryPreprocessor::lemma_ret");
182 tret
= TrustNode::mkTrustRewrite(node
, retNode
, nullptr);
185 // now, rewrite the lemmas
186 Trace("tpp-debug") << "TheoryPreprocessor::preprocess: process lemmas"
188 for (size_t i
= 0, lsize
= newLemmas
.size(); i
< lsize
; ++i
)
190 // get the trust node to process
191 TrustNode trn
= newLemmas
[i
];
192 trn
.debugCheckClosed(
193 "tpp-debug", "TheoryPreprocessor::lemma_new_initial", false);
194 Assert(trn
.getKind() == TrustNodeKind::LEMMA
);
195 Node assertion
= trn
.getNode();
196 // rewrite, which is independent of d_tpg, since additional lemmas
197 // are justified separately.
198 Node rewritten
= Rewriter::rewrite(assertion
);
199 if (assertion
!= rewritten
)
201 if (isProofEnabled())
203 Assert(d_lp
!= nullptr);
204 // store in the lazy proof
205 d_lp
->addLazyStep(assertion
,
207 PfRule::THEORY_PREPROCESS_LEMMA
,
209 "TheoryPreprocessor::rewrite_lemma_new");
210 d_lp
->addStep(rewritten
,
211 PfRule::MACRO_SR_PRED_TRANSFORM
,
215 newLemmas
[i
] = TrustNode::mkTrustLemma(rewritten
, d_lp
.get());
217 Assert(!isProofEnabled() || newLemmas
[i
].getGenerator() != nullptr);
218 newLemmas
[i
].debugCheckClosed("tpp-debug", "TheoryPreprocessor::lemma_new");
220 Trace("tpp-debug") << "Preprocessed: " << node
<< " " << retNode
<< std::endl
;
224 struct preprocess_stack_element
228 preprocess_stack_element(TNode n
) : node(n
), children_added(false) {}
231 TrustNode
TheoryPreprocessor::theoryPreprocess(TNode assertion
)
233 Trace("theory::preprocess")
234 << "TheoryPreprocessor::theoryPreprocess(" << assertion
<< ")" << endl
;
237 // Do a topological sort of the subexpressions and substitute them
238 vector
<preprocess_stack_element
> toVisit
;
239 toVisit
.push_back(assertion
);
241 while (!toVisit
.empty())
243 // The current node we are processing
244 preprocess_stack_element
& stackHead
= toVisit
.back();
245 TNode current
= stackHead
.node
;
247 Trace("theory::preprocess-debug")
248 << "TheoryPreprocessor::theoryPreprocess(" << assertion
249 << "): processing " << current
<< endl
;
251 // If node already in the cache we're done, pop from the stack
252 NodeMap::iterator find
= d_ppCache
.find(current
);
253 if (find
!= d_ppCache
.end())
259 if (!d_logicInfo
.isTheoryEnabled(Theory::theoryOf(current
))
260 && Theory::theoryOf(current
) != THEORY_SAT_SOLVER
)
263 ss
<< "The logic was specified as " << d_logicInfo
.getLogicString()
264 << ", which doesn't include " << Theory::theoryOf(current
)
265 << ", but got a preprocessing-time fact for that theory." << endl
266 << "The fact:" << endl
268 throw LogicException(ss
.str());
271 // If this is an atom, we preprocess its terms with the theory ppRewriter
272 if (Theory::theoryOf(current
) != THEORY_BOOL
)
274 Node ppRewritten
= ppTheoryRewrite(current
);
275 d_ppCache
[current
] = ppRewritten
;
276 Assert(Rewriter::rewrite(d_ppCache
[current
].get())
277 == d_ppCache
[current
].get());
281 // Not yet substituted, so process
282 if (stackHead
.children_added
)
284 // Children have been processed, so substitute
285 NodeBuilder
<> builder(current
.getKind());
286 if (current
.getMetaKind() == kind::metakind::PARAMETERIZED
)
288 builder
<< current
.getOperator();
290 for (unsigned i
= 0; i
< current
.getNumChildren(); ++i
)
292 Assert(d_ppCache
.find(current
[i
]) != d_ppCache
.end());
293 builder
<< d_ppCache
[current
[i
]].get();
295 // Mark the substitution and continue
296 Node result
= builder
;
297 if (result
!= current
)
299 result
= rewriteWithProof(result
);
301 Trace("theory::preprocess-debug")
302 << "TheoryPreprocessor::theoryPreprocess(" << assertion
303 << "): setting " << current
<< " -> " << result
<< endl
;
304 d_ppCache
[current
] = result
;
309 // Mark that we have added the children if any
310 if (current
.getNumChildren() > 0)
312 stackHead
.children_added
= true;
313 // We need to add the children
314 for (TNode::iterator child_it
= current
.begin();
315 child_it
!= current
.end();
318 TNode childNode
= *child_it
;
319 NodeMap::iterator childFind
= d_ppCache
.find(childNode
);
320 if (childFind
== d_ppCache
.end())
322 toVisit
.push_back(childNode
);
328 // No children, so we're done
329 Trace("theory::preprocess-debug")
330 << "SubstitutionMap::internalSubstitute(" << assertion
331 << "): setting " << current
<< " -> " << current
<< endl
;
332 d_ppCache
[current
] = current
;
337 // Return the substituted version
338 Node res
= d_ppCache
[assertion
];
339 return TrustNode::mkTrustRewrite(assertion
, res
, d_tpg
.get());
342 // Recursively traverse a term and call the theory rewriter on its sub-terms
343 Node
TheoryPreprocessor::ppTheoryRewrite(TNode term
)
345 NodeMap::iterator find
= d_ppCache
.find(term
);
346 if (find
!= d_ppCache
.end())
348 return (*find
).second
;
350 unsigned nc
= term
.getNumChildren();
353 return preprocessWithProof(term
);
355 Trace("theory-pp") << "ppTheoryRewrite { " << term
<< endl
;
358 // do not rewrite inside quantifiers
359 if (!term
.isClosure())
361 NodeBuilder
<> newNode(term
.getKind());
362 if (term
.getMetaKind() == kind::metakind::PARAMETERIZED
)
364 newNode
<< term
.getOperator();
367 for (i
= 0; i
< nc
; ++i
)
369 newNode
<< ppTheoryRewrite(term
[i
]);
371 newTerm
= Node(newNode
);
373 newTerm
= rewriteWithProof(newTerm
);
374 newTerm
= preprocessWithProof(newTerm
);
375 d_ppCache
[term
] = newTerm
;
376 Trace("theory-pp") << "ppTheoryRewrite returning " << newTerm
<< "}" << endl
;
380 Node
TheoryPreprocessor::rewriteWithProof(Node term
)
382 Node termr
= Rewriter::rewrite(term
);
383 // store rewrite step if tracking proofs and it rewrites
384 if (isProofEnabled())
386 // may rewrite the same term more than once, thus check hasRewriteStep
389 Trace("tpp-debug") << "TheoryPreprocessor: addRewriteStep (rewriting) "
390 << term
<< " -> " << termr
<< std::endl
;
391 // always use term context hash 0 (default)
392 d_tpg
->addRewriteStep(term
, termr
, PfRule::REWRITE
, {}, {term
});
398 Node
TheoryPreprocessor::preprocessWithProof(Node term
)
400 // Important that it is in rewritten form, to ensure that the rewrite steps
401 // recorded in d_tpg are functional. In other words, there should not
402 // be steps from the same term to multiple rewritten forms, which would be
403 // the case if we registered a preprocessing step for a non-rewritten term.
404 Assert(term
== Rewriter::rewrite(term
));
405 // call ppRewrite for the given theory
406 TrustNode trn
= d_engine
.theoryOf(term
)->ppRewrite(term
);
409 // no change, return original term
412 Node termr
= trn
.getNode();
413 Assert(term
!= termr
);
414 if (isProofEnabled())
416 if (trn
.getGenerator() != nullptr)
418 Trace("tpp-debug") << "TheoryPreprocessor: addRewriteStep (generator) "
419 << term
<< " -> " << termr
<< std::endl
;
420 trn
.debugCheckClosed("tpp-debug",
421 "TheoryPreprocessor::preprocessWithProof");
422 // always use term context hash 0 (default)
423 d_tpg
->addRewriteStep(
424 term
, termr
, trn
.getGenerator(), PfRule::ASSUME
, true);
428 Trace("tpp-debug") << "TheoryPreprocessor: addRewriteStep (trusted) "
429 << term
<< " -> " << termr
<< std::endl
;
431 d_tpg
->addRewriteStep(
432 term
, termr
, PfRule::THEORY_PREPROCESS
, {}, {term
.eqNode(termr
)});
435 termr
= rewriteWithProof(termr
);
436 return ppTheoryRewrite(termr
);
439 bool TheoryPreprocessor::isProofEnabled() const { return d_tpg
!= nullptr; }
441 } // namespace theory