1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Morgan Deters, 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 module for processing assertions for an SMT engine. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "smt/process_assertions.h" |
17 |
|
|
18 |
|
#include <utility> |
19 |
|
|
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/strings_options.h" |
28 |
|
#include "options/uf_options.h" |
29 |
|
#include "preprocessing/assertion_pipeline.h" |
30 |
|
#include "preprocessing/preprocessing_pass.h" |
31 |
|
#include "preprocessing/preprocessing_pass_registry.h" |
32 |
|
#include "printer/printer.h" |
33 |
|
#include "smt/assertions.h" |
34 |
|
#include "smt/dump.h" |
35 |
|
#include "smt/expand_definitions.h" |
36 |
|
#include "smt/smt_engine.h" |
37 |
|
#include "smt/smt_engine_stats.h" |
38 |
|
#include "theory/logic_info.h" |
39 |
|
#include "theory/theory_engine.h" |
40 |
|
|
41 |
|
using namespace std; |
42 |
|
using namespace cvc5::preprocessing; |
43 |
|
using namespace cvc5::theory; |
44 |
|
using namespace cvc5::kind; |
45 |
|
|
46 |
|
namespace cvc5 { |
47 |
|
namespace smt { |
48 |
|
|
49 |
|
/** Useful for counting the number of recursive calls. */ |
50 |
|
class ScopeCounter |
51 |
|
{ |
52 |
|
public: |
53 |
14297 |
ScopeCounter(unsigned& d) : d_depth(d) { ++d_depth; } |
54 |
14297 |
~ScopeCounter() { --d_depth; } |
55 |
|
|
56 |
|
private: |
57 |
|
unsigned& d_depth; |
58 |
|
}; |
59 |
|
|
60 |
13209 |
ProcessAssertions::ProcessAssertions(SmtEngine& smt, |
61 |
|
ResourceManager& rm, |
62 |
13209 |
SmtEngineStatistics& stats) |
63 |
|
: d_smt(smt), |
64 |
|
d_resourceManager(rm), |
65 |
|
d_smtStats(stats), |
66 |
13209 |
d_preprocessingPassContext(nullptr) |
67 |
|
{ |
68 |
13209 |
d_true = NodeManager::currentNM()->mkConst(true); |
69 |
13209 |
} |
70 |
|
|
71 |
10562 |
ProcessAssertions::~ProcessAssertions() |
72 |
|
{ |
73 |
10562 |
} |
74 |
|
|
75 |
9942 |
void ProcessAssertions::finishInit(PreprocessingPassContext* pc) |
76 |
|
{ |
77 |
9942 |
Assert(d_preprocessingPassContext == nullptr); |
78 |
9942 |
d_preprocessingPassContext = pc; |
79 |
|
|
80 |
9942 |
PreprocessingPassRegistry& ppReg = PreprocessingPassRegistry::getInstance(); |
81 |
|
// TODO: this will likely change when we add support for actually assembling |
82 |
|
// preprocessing pipelines. For now, we just create an instance of each |
83 |
|
// available preprocessing pass. |
84 |
19884 |
std::vector<std::string> passNames = ppReg.getAvailablePasses(); |
85 |
347970 |
for (const std::string& passName : passNames) |
86 |
|
{ |
87 |
338028 |
d_passes[passName].reset( |
88 |
|
ppReg.createPass(d_preprocessingPassContext, passName)); |
89 |
|
} |
90 |
9942 |
} |
91 |
|
|
92 |
10562 |
void ProcessAssertions::cleanup() { d_passes.clear(); } |
93 |
|
|
94 |
14048 |
void ProcessAssertions::spendResource(Resource r) |
95 |
|
{ |
96 |
14048 |
d_resourceManager.spendResource(r); |
97 |
14048 |
} |
98 |
|
|
99 |
13802 |
bool ProcessAssertions::apply(Assertions& as) |
100 |
|
{ |
101 |
13802 |
AssertionPipeline& assertions = as.getAssertionPipeline(); |
102 |
13802 |
Assert(d_preprocessingPassContext != nullptr); |
103 |
|
// Dump the assertions |
104 |
13802 |
dumpAssertions("pre-everything", assertions); |
105 |
|
|
106 |
13802 |
Trace("smt-proc") << "ProcessAssertions::processAssertions() begin" << endl; |
107 |
13802 |
Trace("smt") << "ProcessAssertions::processAssertions()" << endl; |
108 |
|
|
109 |
13802 |
Debug("smt") << "#Assertions : " << assertions.size() << endl; |
110 |
13802 |
Debug("smt") << "#Assumptions: " << assertions.getNumAssumptions() << endl; |
111 |
|
|
112 |
13802 |
if (assertions.size() == 0) |
113 |
|
{ |
114 |
|
// nothing to do |
115 |
|
return true; |
116 |
|
} |
117 |
|
|
118 |
13802 |
if (options::bvGaussElim()) |
119 |
|
{ |
120 |
|
d_passes["bv-gauss"]->apply(&assertions); |
121 |
|
} |
122 |
|
|
123 |
|
// Add dummy assertion in last position - to be used as a |
124 |
|
// placeholder for any new assertions to get added |
125 |
13802 |
assertions.push_back(d_true); |
126 |
|
// any assertions added beyond realAssertionsEnd must NOT affect the |
127 |
|
// equisatisfiability |
128 |
13802 |
assertions.updateRealAssertionsEnd(); |
129 |
|
|
130 |
|
// Assertions are NOT guaranteed to be rewritten by this point |
131 |
|
|
132 |
27604 |
Trace("smt-proc") |
133 |
13802 |
<< "ProcessAssertions::processAssertions() : pre-definition-expansion" |
134 |
13802 |
<< endl; |
135 |
13802 |
dumpAssertions("pre-definition-expansion", assertions); |
136 |
|
// Apply substitutions first. If we are non-incremental, this has only the |
137 |
|
// effect of replacing defined functions with their definitions. |
138 |
|
// We do not call theory-specific expand definitions here, since we want |
139 |
|
// to give the opportunity to rewrite/preprocess terms before expansion. |
140 |
13804 |
d_passes["apply-substs"]->apply(&assertions); |
141 |
27600 |
Trace("smt-proc") |
142 |
13800 |
<< "ProcessAssertions::processAssertions() : post-definition-expansion" |
143 |
13800 |
<< endl; |
144 |
13800 |
dumpAssertions("post-definition-expansion", assertions); |
145 |
|
|
146 |
13800 |
Debug("smt") << " assertions : " << assertions.size() << endl; |
147 |
|
|
148 |
13800 |
if (options::globalNegate()) |
149 |
|
{ |
150 |
|
// global negation of the formula |
151 |
6 |
d_passes["global-negate"]->apply(&assertions); |
152 |
6 |
as.flipGlobalNegated(); |
153 |
|
} |
154 |
|
|
155 |
13800 |
if (options::nlExtPurify()) |
156 |
|
{ |
157 |
6 |
d_passes["nl-ext-purify"]->apply(&assertions); |
158 |
|
} |
159 |
|
|
160 |
13800 |
if (options::solveRealAsInt()) |
161 |
|
{ |
162 |
13 |
d_passes["real-to-int"]->apply(&assertions); |
163 |
|
} |
164 |
|
|
165 |
13800 |
if (options::solveIntAsBV() > 0) |
166 |
|
{ |
167 |
18 |
d_passes["int-to-bv"]->apply(&assertions); |
168 |
|
} |
169 |
|
|
170 |
13799 |
if (options::ackermann()) |
171 |
|
{ |
172 |
70 |
d_passes["ackermann"]->apply(&assertions); |
173 |
|
} |
174 |
|
|
175 |
13799 |
if (options::bvAbstraction()) |
176 |
|
{ |
177 |
4 |
d_passes["bv-abstraction"]->apply(&assertions); |
178 |
|
} |
179 |
|
|
180 |
13799 |
Debug("smt") << " assertions : " << assertions.size() << endl; |
181 |
|
|
182 |
13799 |
bool noConflict = true; |
183 |
|
|
184 |
13799 |
if (options::extRewPrep()) |
185 |
|
{ |
186 |
24 |
d_passes["ext-rew-pre"]->apply(&assertions); |
187 |
|
} |
188 |
|
|
189 |
|
// Unconstrained simplification |
190 |
13799 |
if (options::unconstrainedSimp()) |
191 |
|
{ |
192 |
182 |
d_passes["rewrite"]->apply(&assertions); |
193 |
182 |
d_passes["unconstrained-simplifier"]->apply(&assertions); |
194 |
|
} |
195 |
|
|
196 |
13799 |
if (options::bvIntroducePow2()) |
197 |
|
{ |
198 |
2 |
d_passes["bv-intro-pow2"]->apply(&assertions); |
199 |
|
} |
200 |
|
|
201 |
|
// Lift bit-vectors of size 1 to bool |
202 |
13799 |
if (options::bitvectorToBool()) |
203 |
|
{ |
204 |
147 |
d_passes["bv-to-bool"]->apply(&assertions); |
205 |
|
} |
206 |
13799 |
if (options::solveBVAsInt() != options::SolveBVAsIntMode::OFF) |
207 |
|
{ |
208 |
203 |
d_passes["bv-to-int"]->apply(&assertions); |
209 |
|
} |
210 |
13799 |
if (options::foreignTheoryRewrite()) |
211 |
|
{ |
212 |
2 |
d_passes["foreign-theory-rewrite"]->apply(&assertions); |
213 |
|
} |
214 |
|
|
215 |
|
// Since this pass is not robust for the information tracking necessary for |
216 |
|
// unsat cores, it's only applied if we are not doing unsat core computation |
217 |
13799 |
d_passes["apply-substs"]->apply(&assertions); |
218 |
|
|
219 |
|
// Assertions MUST BE guaranteed to be rewritten by this point |
220 |
13799 |
d_passes["rewrite"]->apply(&assertions); |
221 |
|
|
222 |
|
// Convert non-top-level Booleans to bit-vectors of size 1 |
223 |
13799 |
if (options::boolToBitvector() != options::BoolToBVMode::OFF) |
224 |
|
{ |
225 |
12 |
d_passes["bool-to-bv"]->apply(&assertions); |
226 |
|
} |
227 |
13799 |
if (options::sepPreSkolemEmp()) |
228 |
|
{ |
229 |
1 |
d_passes["sep-skolem-emp"]->apply(&assertions); |
230 |
|
} |
231 |
|
|
232 |
13799 |
if (d_smt.getLogicInfo().isQuantified()) |
233 |
|
{ |
234 |
|
// remove rewrite rules, apply pre-skolemization to existential quantifiers |
235 |
8169 |
d_passes["quantifiers-preprocess"]->apply(&assertions); |
236 |
|
|
237 |
|
// fmf-fun : assume admissible functions, applying preprocessing reduction |
238 |
|
// to FMF |
239 |
8169 |
if (options::fmfFunWellDefined()) |
240 |
|
{ |
241 |
121 |
d_passes["fun-def-fmf"]->apply(&assertions); |
242 |
|
} |
243 |
|
} |
244 |
13799 |
if (!options::stringLazyPreproc()) |
245 |
|
{ |
246 |
40 |
d_passes["strings-eager-pp"]->apply(&assertions); |
247 |
|
} |
248 |
13799 |
if (options::sortInference() || options::ufssFairnessMonotone()) |
249 |
|
{ |
250 |
24 |
d_passes["sort-inference"]->apply(&assertions); |
251 |
|
} |
252 |
|
|
253 |
13799 |
if (options::pbRewrites()) |
254 |
|
{ |
255 |
2 |
d_passes["pseudo-boolean-processor"]->apply(&assertions); |
256 |
|
} |
257 |
|
|
258 |
|
// rephrasing normal inputs as sygus problems |
259 |
13799 |
if (!d_smt.isInternalSubsolver()) |
260 |
|
{ |
261 |
10261 |
if (options::sygusInference()) |
262 |
|
{ |
263 |
57 |
d_passes["sygus-infer"]->apply(&assertions); |
264 |
|
} |
265 |
10204 |
else if (options::sygusRewSynthInput()) |
266 |
|
{ |
267 |
|
// do candidate rewrite rule synthesis |
268 |
|
d_passes["synth-rr"]->apply(&assertions); |
269 |
|
} |
270 |
|
} |
271 |
|
|
272 |
27598 |
Trace("smt-proc") << "ProcessAssertions::processAssertions() : pre-simplify" |
273 |
13799 |
<< endl; |
274 |
13799 |
dumpAssertions("pre-simplify", assertions); |
275 |
13799 |
Chat() << "simplifying assertions..." << endl; |
276 |
13799 |
noConflict = simplifyAssertions(assertions); |
277 |
13799 |
if (!noConflict) |
278 |
|
{ |
279 |
1094 |
++(d_smtStats.d_simplifiedToFalse); |
280 |
|
} |
281 |
27598 |
Trace("smt-proc") << "ProcessAssertions::processAssertions() : post-simplify" |
282 |
13799 |
<< endl; |
283 |
13799 |
dumpAssertions("post-simplify", assertions); |
284 |
|
|
285 |
13799 |
if (options::doStaticLearning()) |
286 |
|
{ |
287 |
13799 |
d_passes["static-learning"]->apply(&assertions); |
288 |
|
} |
289 |
13799 |
Debug("smt") << " assertions : " << assertions.size() << endl; |
290 |
|
|
291 |
13799 |
if (options::learnedRewrite()) |
292 |
|
{ |
293 |
2 |
d_passes["learned-rewrite"]->apply(&assertions); |
294 |
|
} |
295 |
|
|
296 |
13799 |
if (options::earlyIteRemoval()) |
297 |
|
{ |
298 |
1 |
d_smtStats.d_numAssertionsPre += assertions.size(); |
299 |
1 |
d_passes["ite-removal"]->apply(&assertions); |
300 |
|
// This is needed because when solving incrementally, removeITEs may |
301 |
|
// introduce skolems that were solved for earlier and thus appear in the |
302 |
|
// substitution map. |
303 |
1 |
d_passes["apply-substs"]->apply(&assertions); |
304 |
1 |
d_smtStats.d_numAssertionsPost += assertions.size(); |
305 |
|
} |
306 |
|
|
307 |
13799 |
dumpAssertions("pre-repeat-simplify", assertions); |
308 |
13799 |
if (options::repeatSimp()) |
309 |
|
{ |
310 |
498 |
Trace("smt-proc") |
311 |
249 |
<< "ProcessAssertions::processAssertions() : pre-repeat-simplify" |
312 |
249 |
<< endl; |
313 |
249 |
Chat() << "re-simplifying assertions..." << endl; |
314 |
498 |
ScopeCounter depth(d_simplifyAssertionsDepth); |
315 |
249 |
noConflict &= simplifyAssertions(assertions); |
316 |
498 |
Trace("smt-proc") |
317 |
249 |
<< "ProcessAssertions::processAssertions() : post-repeat-simplify" |
318 |
249 |
<< endl; |
319 |
|
} |
320 |
13799 |
dumpAssertions("post-repeat-simplify", assertions); |
321 |
|
|
322 |
13799 |
if (options::ufHo()) |
323 |
|
{ |
324 |
187 |
d_passes["ho-elim"]->apply(&assertions); |
325 |
|
} |
326 |
|
|
327 |
|
// begin: INVARIANT to maintain: no reordering of assertions or |
328 |
|
// introducing new ones |
329 |
|
|
330 |
13799 |
Debug("smt") << " assertions : " << assertions.size() << endl; |
331 |
|
|
332 |
27598 |
Debug("smt") << "ProcessAssertions::processAssertions() POST SIMPLIFICATION" |
333 |
13799 |
<< endl; |
334 |
13799 |
Debug("smt") << " assertions : " << assertions.size() << endl; |
335 |
|
|
336 |
|
// ensure rewritten |
337 |
13799 |
d_passes["rewrite"]->apply(&assertions); |
338 |
|
// rewrite equalities based on theory-specific rewriting |
339 |
13799 |
d_passes["theory-rewrite-eq"]->apply(&assertions); |
340 |
|
// apply theory preprocess, which includes ITE removal |
341 |
13806 |
d_passes["theory-preprocess"]->apply(&assertions); |
342 |
|
// notice that we do not apply substitutions as a last step here, since |
343 |
|
// the range of substitutions is not theory-preprocessed. |
344 |
|
|
345 |
13792 |
if (options::bitblastMode() == options::BitblastMode::EAGER) |
346 |
|
{ |
347 |
62 |
d_passes["bv-eager-atoms"]->apply(&assertions); |
348 |
|
} |
349 |
|
|
350 |
13792 |
Trace("smt-proc") << "SmtEnginePrivate::processAssertions() end" << endl; |
351 |
13792 |
dumpAssertions("post-everything", assertions); |
352 |
|
|
353 |
13792 |
return noConflict; |
354 |
|
} |
355 |
|
|
356 |
|
// returns false if simplification led to "false" |
357 |
14048 |
bool ProcessAssertions::simplifyAssertions(AssertionPipeline& assertions) |
358 |
|
{ |
359 |
14048 |
spendResource(Resource::PreprocessStep); |
360 |
|
try |
361 |
|
{ |
362 |
26993 |
ScopeCounter depth(d_simplifyAssertionsDepth); |
363 |
|
|
364 |
14048 |
Trace("simplify") << "SmtEnginePrivate::simplify()" << endl; |
365 |
|
|
366 |
14048 |
if (options::simplificationMode() != options::SimplificationMode::NONE) |
367 |
|
{ |
368 |
|
// Perform non-clausal simplification |
369 |
|
PreprocessingPassResult res = |
370 |
10583 |
d_passes["non-clausal-simp"]->apply(&assertions); |
371 |
10583 |
if (res == PreprocessingPassResult::CONFLICT) |
372 |
|
{ |
373 |
1099 |
return false; |
374 |
|
} |
375 |
|
|
376 |
|
// We piggy-back off of the BackEdgesMap in the CircuitPropagator to |
377 |
|
// do the miplib trick. |
378 |
9484 |
if ( // check that option is on |
379 |
9492 |
options::arithMLTrick() && |
380 |
|
// only useful in arith |
381 |
9492 |
d_smt.getLogicInfo().isTheoryEnabled(THEORY_ARITH) && |
382 |
|
// we add new assertions and need this (in practice, this |
383 |
|
// restriction only disables miplib processing during |
384 |
|
// re-simplification, which we don't expect to be useful anyway) |
385 |
8 |
assertions.getRealAssertionsEnd() == assertions.size()) |
386 |
|
{ |
387 |
8 |
d_passes["miplib-trick"]->apply(&assertions); |
388 |
|
} |
389 |
|
else |
390 |
|
{ |
391 |
18952 |
Trace("simplify") << "SmtEnginePrivate::simplify(): " |
392 |
9476 |
<< "skipping miplib pseudobooleans pass..." << endl; |
393 |
|
} |
394 |
|
} |
395 |
|
|
396 |
12949 |
Debug("smt") << " assertions : " << assertions.size() << endl; |
397 |
|
|
398 |
|
// ITE simplification |
399 |
25898 |
if (options::doITESimp() |
400 |
12949 |
&& (d_simplifyAssertionsDepth <= 1 || options::doITESimpOnRepeat())) |
401 |
|
{ |
402 |
1 |
PreprocessingPassResult res = d_passes["ite-simp"]->apply(&assertions); |
403 |
1 |
if (res == PreprocessingPassResult::CONFLICT) |
404 |
|
{ |
405 |
|
Chat() << "...ITE simplification found unsat..." << endl; |
406 |
|
return false; |
407 |
|
} |
408 |
|
} |
409 |
|
|
410 |
12949 |
Debug("smt") << " assertions : " << assertions.size() << endl; |
411 |
|
|
412 |
|
// Unconstrained simplification |
413 |
12949 |
if (options::unconstrainedSimp()) |
414 |
|
{ |
415 |
333 |
d_passes["unconstrained-simplifier"]->apply(&assertions); |
416 |
|
} |
417 |
|
|
418 |
25898 |
if (options::repeatSimp() |
419 |
12949 |
&& options::simplificationMode() != options::SimplificationMode::NONE) |
420 |
|
{ |
421 |
|
PreprocessingPassResult res = |
422 |
470 |
d_passes["non-clausal-simp"]->apply(&assertions); |
423 |
470 |
if (res == PreprocessingPassResult::CONFLICT) |
424 |
|
{ |
425 |
4 |
return false; |
426 |
|
} |
427 |
|
} |
428 |
|
|
429 |
12945 |
dumpAssertions("post-repeatsimp", assertions); |
430 |
12945 |
Trace("smt") << "POST repeatSimp" << endl; |
431 |
12945 |
Debug("smt") << " assertions : " << assertions.size() << endl; |
432 |
|
} |
433 |
|
catch (TypeCheckingExceptionPrivate& tcep) |
434 |
|
{ |
435 |
|
// Calls to this function should have already weeded out any |
436 |
|
// typechecking exceptions via (e.g.) ensureBoolean(). But a |
437 |
|
// theory could still create a new expression that isn't |
438 |
|
// well-typed, and we don't want the C++ runtime to abort our |
439 |
|
// process without any error notice. |
440 |
|
InternalError() |
441 |
|
<< "A bad expression was produced. Original exception follows:\n" |
442 |
|
<< tcep; |
443 |
|
} |
444 |
12945 |
return true; |
445 |
|
} |
446 |
|
|
447 |
123337 |
void ProcessAssertions::dumpAssertions(const char* key, |
448 |
|
const AssertionPipeline& assertionList) |
449 |
|
{ |
450 |
123337 |
if (Dump.isOn("assertions") && Dump.isOn(string("assertions:") + key)) |
451 |
|
{ |
452 |
|
// Push the simplified assertions to the dump output stream |
453 |
|
for (unsigned i = 0; i < assertionList.size(); ++i) |
454 |
|
{ |
455 |
|
TNode n = assertionList[i]; |
456 |
|
d_smt.getOutputManager().getPrinter().toStreamCmdAssert( |
457 |
|
d_smt.getOutputManager().getDumpOut(), n); |
458 |
|
} |
459 |
|
} |
460 |
123337 |
} |
461 |
|
|
462 |
|
} // namespace smt |
463 |
29577 |
} // namespace cvc5 |