1 /********************* */
2 /*! \file process_assertions.cpp
4 ** Top contributors (to current version):
5 ** Andrew Reynolds, Yoni Zohar, Mathias Preiner
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 Implementation of module for processing assertions for an SMT engine.
15 #include "smt/process_assertions.h"
20 #include "expr/node_manager_attributes.h"
21 #include "options/arith_options.h"
22 #include "options/base_options.h"
23 #include "options/bv_options.h"
24 #include "options/quantifiers_options.h"
25 #include "options/sep_options.h"
26 #include "options/smt_options.h"
27 #include "options/uf_options.h"
28 #include "preprocessing/assertion_pipeline.h"
29 #include "preprocessing/preprocessing_pass_registry.h"
30 #include "printer/printer.h"
31 #include "smt/defined_function.h"
33 #include "smt/smt_engine.h"
34 #include "theory/logic_info.h"
35 #include "theory/theory_engine.h"
37 using namespace CVC4::preprocessing
;
38 using namespace CVC4::theory
;
39 using namespace CVC4::kind
;
44 /** Useful for counting the number of recursive calls. */
48 ScopeCounter(unsigned& d
) : d_depth(d
) { ++d_depth
; }
49 ~ScopeCounter() { --d_depth
; }
55 ProcessAssertions::ProcessAssertions(SmtEngine
& smt
,
58 SmtEngineStatistics
& stats
)
61 d_resourceManager(rm
),
63 d_preprocessingPassContext(nullptr)
65 d_true
= NodeManager::currentNM()->mkConst(true);
68 ProcessAssertions::~ProcessAssertions()
72 void ProcessAssertions::finishInit(PreprocessingPassContext
* pc
)
74 Assert(d_preprocessingPassContext
== nullptr);
75 d_preprocessingPassContext
= pc
;
77 PreprocessingPassRegistry
& ppReg
= PreprocessingPassRegistry::getInstance();
78 // TODO: this will likely change when we add support for actually assembling
79 // preprocessing pipelines. For now, we just create an instance of each
80 // available preprocessing pass.
81 std::vector
<std::string
> passNames
= ppReg
.getAvailablePasses();
82 for (const std::string
& passName
: passNames
)
84 d_passes
[passName
].reset(
85 ppReg
.createPass(d_preprocessingPassContext
, passName
));
89 void ProcessAssertions::cleanup() { d_passes
.clear(); }
91 void ProcessAssertions::spendResource(ResourceManager::Resource r
)
93 d_resourceManager
.spendResource(r
);
96 bool ProcessAssertions::apply(Assertions
& as
)
98 AssertionPipeline
& assertions
= as
.getAssertionPipeline();
99 Assert(d_preprocessingPassContext
!= nullptr);
100 // Dump the assertions
101 dumpAssertions("pre-everything", assertions
);
103 Trace("smt-proc") << "ProcessAssertions::processAssertions() begin" << endl
;
104 Trace("smt") << "ProcessAssertions::processAssertions()" << endl
;
106 Debug("smt") << "#Assertions : " << assertions
.size() << endl
;
107 Debug("smt") << "#Assumptions: " << assertions
.getNumAssumptions() << endl
;
109 if (assertions
.size() == 0)
115 if (options::bvGaussElim())
117 d_passes
["bv-gauss"]->apply(&assertions
);
120 // Add dummy assertion in last position - to be used as a
121 // placeholder for any new assertions to get added
122 assertions
.push_back(d_true
);
123 // any assertions added beyond realAssertionsEnd must NOT affect the
124 // equisatisfiability
125 assertions
.updateRealAssertionsEnd();
127 // Assertions are NOT guaranteed to be rewritten by this point
130 << "ProcessAssertions::processAssertions() : pre-definition-expansion"
132 dumpAssertions("pre-definition-expansion", assertions
);
133 // Expand definitions, which replaces defined functions with their definition
134 // and does beta reduction. Notice we pass true as the second argument since
135 // we do not want to call theories to expand definitions here, since we want
136 // to give the opportunity to rewrite/preprocess terms before expansion.
137 d_exDefs
.expandAssertions(assertions
, true);
139 << "ProcessAssertions::processAssertions() : post-definition-expansion"
141 dumpAssertions("post-definition-expansion", assertions
);
143 Debug("smt") << " assertions : " << assertions
.size() << endl
;
145 if (options::globalNegate())
147 // global negation of the formula
148 d_passes
["global-negate"]->apply(&assertions
);
149 as
.flipGlobalNegated();
152 if (options::nlExtPurify())
154 d_passes
["nl-ext-purify"]->apply(&assertions
);
157 if (options::solveRealAsInt())
159 d_passes
["real-to-int"]->apply(&assertions
);
162 if (options::solveIntAsBV() > 0)
164 d_passes
["int-to-bv"]->apply(&assertions
);
167 if (options::ackermann())
169 d_passes
["ackermann"]->apply(&assertions
);
172 if (options::bvAbstraction())
174 d_passes
["bv-abstraction"]->apply(&assertions
);
177 Debug("smt") << " assertions : " << assertions
.size() << endl
;
179 bool noConflict
= true;
181 if (options::extRewPrep())
183 d_passes
["ext-rew-pre"]->apply(&assertions
);
186 // Unconstrained simplification
187 if (options::unconstrainedSimp())
189 d_passes
["rewrite"]->apply(&assertions
);
190 d_passes
["unconstrained-simplifier"]->apply(&assertions
);
193 if (options::bvIntroducePow2())
195 d_passes
["bv-intro-pow2"]->apply(&assertions
);
198 // Lift bit-vectors of size 1 to bool
199 if (options::bitvectorToBool())
201 d_passes
["bv-to-bool"]->apply(&assertions
);
203 if (options::solveBVAsInt() != options::SolveBVAsIntMode::OFF
)
205 d_passes
["bv-to-int"]->apply(&assertions
);
207 if (options::foreignTheoryRewrite())
209 d_passes
["foreign-theory-rewrite"]->apply(&assertions
);
212 // Since this pass is not robust for the information tracking necessary for
213 // unsat cores, it's only applied if we are not doing unsat core computation
214 d_passes
["apply-substs"]->apply(&assertions
);
216 // Assertions MUST BE guaranteed to be rewritten by this point
217 d_passes
["rewrite"]->apply(&assertions
);
219 // Convert non-top-level Booleans to bit-vectors of size 1
220 if (options::boolToBitvector() != options::BoolToBVMode::OFF
)
222 d_passes
["bool-to-bv"]->apply(&assertions
);
224 if (options::sepPreSkolemEmp())
226 d_passes
["sep-skolem-emp"]->apply(&assertions
);
229 if (d_smt
.getLogicInfo().isQuantified())
231 // remove rewrite rules, apply pre-skolemization to existential quantifiers
232 d_passes
["quantifiers-preprocess"]->apply(&assertions
);
233 if (options::macrosQuant())
235 // quantifiers macro expansion
236 d_passes
["quantifier-macros"]->apply(&assertions
);
239 // fmf-fun : assume admissible functions, applying preprocessing reduction
241 if (options::fmfFunWellDefined())
243 d_passes
["fun-def-fmf"]->apply(&assertions
);
247 if (options::sortInference() || options::ufssFairnessMonotone())
249 d_passes
["sort-inference"]->apply(&assertions
);
252 if (options::pbRewrites())
254 d_passes
["pseudo-boolean-processor"]->apply(&assertions
);
257 // rephrasing normal inputs as sygus problems
258 if (!d_smt
.isInternalSubsolver())
260 if (options::sygusInference())
262 d_passes
["sygus-infer"]->apply(&assertions
);
264 else if (options::sygusRewSynthInput())
266 // do candidate rewrite rule synthesis
267 d_passes
["synth-rr"]->apply(&assertions
);
271 Trace("smt-proc") << "ProcessAssertions::processAssertions() : pre-simplify"
273 dumpAssertions("pre-simplify", assertions
);
274 Chat() << "simplifying assertions..." << endl
;
275 noConflict
= simplifyAssertions(assertions
);
278 ++(d_smtStats
.d_simplifiedToFalse
);
280 Trace("smt-proc") << "ProcessAssertions::processAssertions() : post-simplify"
282 dumpAssertions("post-simplify", assertions
);
284 if (options::doStaticLearning())
286 d_passes
["static-learning"]->apply(&assertions
);
288 Debug("smt") << " assertions : " << assertions
.size() << endl
;
290 if (options::earlyIteRemoval())
292 d_smtStats
.d_numAssertionsPre
+= assertions
.size();
293 d_passes
["ite-removal"]->apply(&assertions
);
294 // This is needed because when solving incrementally, removeITEs may
295 // introduce skolems that were solved for earlier and thus appear in the
297 d_passes
["apply-substs"]->apply(&assertions
);
298 d_smtStats
.d_numAssertionsPost
+= assertions
.size();
301 dumpAssertions("pre-repeat-simplify", assertions
);
302 if (options::repeatSimp())
305 << "ProcessAssertions::processAssertions() : pre-repeat-simplify"
307 Chat() << "re-simplifying assertions..." << endl
;
308 ScopeCounter
depth(d_simplifyAssertionsDepth
);
309 noConflict
&= simplifyAssertions(assertions
);
311 << "ProcessAssertions::processAssertions() : post-repeat-simplify"
314 dumpAssertions("post-repeat-simplify", assertions
);
318 d_passes
["ho-elim"]->apply(&assertions
);
321 // begin: INVARIANT to maintain: no reordering of assertions or
322 // introducing new ones
324 Debug("smt") << " assertions : " << assertions
.size() << endl
;
326 Debug("smt") << "ProcessAssertions::processAssertions() POST SIMPLIFICATION"
328 Debug("smt") << " assertions : " << assertions
.size() << endl
;
331 d_passes
["rewrite"]->apply(&assertions
);
332 // rewrite equalities based on theory-specific rewriting
333 d_passes
["theory-rewrite-eq"]->apply(&assertions
);
334 // apply theory preprocess, which includes ITE removal
335 d_passes
["theory-preprocess"]->apply(&assertions
);
336 // This is needed because when solving incrementally, removeITEs may
337 // introduce skolems that were solved for earlier and thus appear in the
339 d_passes
["apply-substs"]->apply(&assertions
);
341 if (options::bitblastMode() == options::BitblastMode::EAGER
)
343 d_passes
["bv-eager-atoms"]->apply(&assertions
);
346 Trace("smt-proc") << "SmtEnginePrivate::processAssertions() end" << endl
;
347 dumpAssertions("post-everything", assertions
);
352 // returns false if simplification led to "false"
353 bool ProcessAssertions::simplifyAssertions(AssertionPipeline
& assertions
)
355 spendResource(ResourceManager::Resource::PreprocessStep
);
358 ScopeCounter
depth(d_simplifyAssertionsDepth
);
360 Trace("simplify") << "SmtEnginePrivate::simplify()" << endl
;
362 if (options::simplificationMode() != options::SimplificationMode::NONE
)
364 // Perform non-clausal simplification
365 PreprocessingPassResult res
=
366 d_passes
["non-clausal-simp"]->apply(&assertions
);
367 if (res
== PreprocessingPassResult::CONFLICT
)
372 // We piggy-back off of the BackEdgesMap in the CircuitPropagator to
373 // do the miplib trick.
374 if ( // check that option is on
375 options::arithMLTrick() &&
376 // only useful in arith
377 d_smt
.getLogicInfo().isTheoryEnabled(THEORY_ARITH
) &&
378 // we add new assertions and need this (in practice, this
379 // restriction only disables miplib processing during
380 // re-simplification, which we don't expect to be useful anyway)
381 assertions
.getRealAssertionsEnd() == assertions
.size())
383 d_passes
["miplib-trick"]->apply(&assertions
);
387 Trace("simplify") << "SmtEnginePrivate::simplify(): "
388 << "skipping miplib pseudobooleans pass..." << endl
;
392 Debug("smt") << " assertions : " << assertions
.size() << endl
;
394 // ITE simplification
395 if (options::doITESimp()
396 && (d_simplifyAssertionsDepth
<= 1 || options::doITESimpOnRepeat()))
398 PreprocessingPassResult res
= d_passes
["ite-simp"]->apply(&assertions
);
399 if (res
== PreprocessingPassResult::CONFLICT
)
401 Chat() << "...ITE simplification found unsat..." << endl
;
406 Debug("smt") << " assertions : " << assertions
.size() << endl
;
408 // Unconstrained simplification
409 if (options::unconstrainedSimp())
411 d_passes
["unconstrained-simplifier"]->apply(&assertions
);
414 if (options::repeatSimp()
415 && options::simplificationMode() != options::SimplificationMode::NONE
)
417 PreprocessingPassResult res
=
418 d_passes
["non-clausal-simp"]->apply(&assertions
);
419 if (res
== PreprocessingPassResult::CONFLICT
)
425 dumpAssertions("post-repeatsimp", assertions
);
426 Trace("smt") << "POST repeatSimp" << endl
;
427 Debug("smt") << " assertions : " << assertions
.size() << endl
;
429 catch (TypeCheckingExceptionPrivate
& tcep
)
431 // Calls to this function should have already weeded out any
432 // typechecking exceptions via (e.g.) ensureBoolean(). But a
433 // theory could still create a new expression that isn't
434 // well-typed, and we don't want the C++ runtime to abort our
435 // process without any error notice.
437 << "A bad expression was produced. Original exception follows:\n"
443 void ProcessAssertions::dumpAssertions(const char* key
,
444 const AssertionPipeline
& assertionList
)
446 if (Dump
.isOn("assertions") && Dump
.isOn(string("assertions:") + key
))
448 // Push the simplified assertions to the dump output stream
449 for (unsigned i
= 0; i
< assertionList
.size(); ++i
)
451 TNode n
= assertionList
[i
];
452 d_smt
.getOutputManager().getPrinter().toStreamCmdAssert(
453 d_smt
.getOutputManager().getDumpOut(), n
);