GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/smt/process_assertions.cpp Lines: 173 186 93.0 %
Date: 2021-09-29 Branches: 355 694 51.2 %

Line Exec Source
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
8965
  ScopeCounter(unsigned& d) : d_depth(d) { ++d_depth; }
54
8965
  ~ScopeCounter() { --d_depth; }
55
56
 private:
57
  unsigned& d_depth;
58
};
59
60
9514
ProcessAssertions::ProcessAssertions(SmtEngine& smt,
61
                                     ResourceManager& rm,
62
9514
                                     SmtEngineStatistics& stats)
63
    : d_smt(smt),
64
      d_resourceManager(rm),
65
      d_smtStats(stats),
66
9514
      d_preprocessingPassContext(nullptr)
67
{
68
9514
  d_true = NodeManager::currentNM()->mkConst(true);
69
9514
}
70
71
6868
ProcessAssertions::~ProcessAssertions()
72
{
73
6868
}
74
75
6271
void ProcessAssertions::finishInit(PreprocessingPassContext* pc)
76
{
77
6271
  Assert(d_preprocessingPassContext == nullptr);
78
6271
  d_preprocessingPassContext = pc;
79
80
6271
  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
12542
  std::vector<std::string> passNames = ppReg.getAvailablePasses();
85
219485
  for (const std::string& passName : passNames)
86
  {
87
213214
    d_passes[passName].reset(
88
        ppReg.createPass(d_preprocessingPassContext, passName));
89
  }
90
6271
}
91
92
6868
void ProcessAssertions::cleanup() { d_passes.clear(); }
93
94
8752
void ProcessAssertions::spendResource(Resource r)
95
{
96
8752
  d_resourceManager.spendResource(r);
97
8752
}
98
99
8542
bool ProcessAssertions::apply(Assertions& as)
100
{
101
8542
  AssertionPipeline& assertions = as.getAssertionPipeline();
102
8542
  Assert(d_preprocessingPassContext != nullptr);
103
  // Dump the assertions
104
8542
  dumpAssertions("pre-everything", assertions);
105
106
8542
  Trace("smt-proc") << "ProcessAssertions::processAssertions() begin" << endl;
107
8542
  Trace("smt") << "ProcessAssertions::processAssertions()" << endl;
108
109
8542
  Debug("smt") << "#Assertions : " << assertions.size() << endl;
110
8542
  Debug("smt") << "#Assumptions: " << assertions.getNumAssumptions() << endl;
111
112
8542
  if (assertions.size() == 0)
113
  {
114
    // nothing to do
115
    return true;
116
  }
117
118
8542
  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
8542
  assertions.push_back(d_true);
126
  // any assertions added beyond realAssertionsEnd must NOT affect the
127
  // equisatisfiability
128
8542
  assertions.updateRealAssertionsEnd();
129
130
  // Assertions are NOT guaranteed to be rewritten by this point
131
132
17084
  Trace("smt-proc")
133
8542
      << "ProcessAssertions::processAssertions() : pre-definition-expansion"
134
8542
      << endl;
135
8542
  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
8544
  d_passes["apply-substs"]->apply(&assertions);
141
17080
  Trace("smt-proc")
142
8540
      << "ProcessAssertions::processAssertions() : post-definition-expansion"
143
8540
      << endl;
144
8540
  dumpAssertions("post-definition-expansion", assertions);
145
146
8540
  Debug("smt") << " assertions     : " << assertions.size() << endl;
147
148
8540
  if (options::globalNegate())
149
  {
150
    // global negation of the formula
151
5
    d_passes["global-negate"]->apply(&assertions);
152
5
    as.flipGlobalNegated();
153
  }
154
155
8540
  if (options::nlExtPurify())
156
  {
157
4
    d_passes["nl-ext-purify"]->apply(&assertions);
158
  }
159
160
8540
  if (options::solveRealAsInt())
161
  {
162
13
    d_passes["real-to-int"]->apply(&assertions);
163
  }
164
165
8540
  if (options::solveIntAsBV() > 0)
166
  {
167
18
    d_passes["int-to-bv"]->apply(&assertions);
168
  }
169
170
8539
  if (options::ackermann())
171
  {
172
32
    d_passes["ackermann"]->apply(&assertions);
173
  }
174
175
8539
  if (options::bvAbstraction())
176
  {
177
4
    d_passes["bv-abstraction"]->apply(&assertions);
178
  }
179
180
8539
  Debug("smt") << " assertions     : " << assertions.size() << endl;
181
182
8539
  bool noConflict = true;
183
184
8539
  if (options::extRewPrep())
185
  {
186
21
    d_passes["ext-rew-pre"]->apply(&assertions);
187
  }
188
189
  // Unconstrained simplification
190
8539
  if (options::unconstrainedSimp())
191
  {
192
150
    d_passes["rewrite"]->apply(&assertions);
193
150
    d_passes["unconstrained-simplifier"]->apply(&assertions);
194
  }
195
196
8539
  if (options::bvIntroducePow2())
197
  {
198
1
    d_passes["bv-intro-pow2"]->apply(&assertions);
199
  }
200
201
  // Lift bit-vectors of size 1 to bool
202
8539
  if (options::bitvectorToBool())
203
  {
204
107
    d_passes["bv-to-bool"]->apply(&assertions);
205
  }
206
8539
  if (options::solveBVAsInt() != options::SolveBVAsIntMode::OFF)
207
  {
208
103
    d_passes["bv-to-int"]->apply(&assertions);
209
  }
210
8539
  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
8539
  d_passes["apply-substs"]->apply(&assertions);
218
219
  // Assertions MUST BE guaranteed to be rewritten by this point
220
8539
  d_passes["rewrite"]->apply(&assertions);
221
222
  // Convert non-top-level Booleans to bit-vectors of size 1
223
8539
  if (options::boolToBitvector() != options::BoolToBVMode::OFF)
224
  {
225
12
    d_passes["bool-to-bv"]->apply(&assertions);
226
  }
227
8539
  if (options::sepPreSkolemEmp())
228
  {
229
1
    d_passes["sep-skolem-emp"]->apply(&assertions);
230
  }
231
232
8539
  if (d_smt.getLogicInfo().isQuantified())
233
  {
234
    // remove rewrite rules, apply pre-skolemization to existential quantifiers
235
5674
    d_passes["quantifiers-preprocess"]->apply(&assertions);
236
237
    // fmf-fun : assume admissible functions, applying preprocessing reduction
238
    // to FMF
239
5674
    if (options::fmfFunWellDefined())
240
    {
241
73
      d_passes["fun-def-fmf"]->apply(&assertions);
242
    }
243
  }
244
8539
  if (!options::stringLazyPreproc())
245
  {
246
30
    d_passes["strings-eager-pp"]->apply(&assertions);
247
  }
248
8539
  if (options::sortInference() || options::ufssFairnessMonotone())
249
  {
250
22
    d_passes["sort-inference"]->apply(&assertions);
251
  }
252
253
8539
  if (options::pbRewrites())
254
  {
255
2
    d_passes["pseudo-boolean-processor"]->apply(&assertions);
256
  }
257
258
  // rephrasing normal inputs as sygus problems
259
8539
  if (!d_smt.isInternalSubsolver())
260
  {
261
6376
    if (options::sygusInference())
262
    {
263
50
      d_passes["sygus-infer"]->apply(&assertions);
264
    }
265
6326
    else if (options::sygusRewSynthInput())
266
    {
267
      // do candidate rewrite rule synthesis
268
      d_passes["synth-rr"]->apply(&assertions);
269
    }
270
  }
271
272
17078
  Trace("smt-proc") << "ProcessAssertions::processAssertions() : pre-simplify"
273
8539
                    << endl;
274
8539
  dumpAssertions("pre-simplify", assertions);
275
8539
  Chat() << "simplifying assertions..." << endl;
276
8539
  noConflict = simplifyAssertions(assertions);
277
8539
  if (!noConflict)
278
  {
279
712
    ++(d_smtStats.d_simplifiedToFalse);
280
  }
281
17078
  Trace("smt-proc") << "ProcessAssertions::processAssertions() : post-simplify"
282
8539
                    << endl;
283
8539
  dumpAssertions("post-simplify", assertions);
284
285
8539
  if (options::doStaticLearning())
286
  {
287
8539
    d_passes["static-learning"]->apply(&assertions);
288
  }
289
8539
  Debug("smt") << " assertions     : " << assertions.size() << endl;
290
291
8539
  if (options::learnedRewrite())
292
  {
293
1
    d_passes["learned-rewrite"]->apply(&assertions);
294
  }
295
296
8539
  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
8539
  dumpAssertions("pre-repeat-simplify", assertions);
308
8539
  if (options::repeatSimp())
309
  {
310
426
    Trace("smt-proc")
311
213
        << "ProcessAssertions::processAssertions() : pre-repeat-simplify"
312
213
        << endl;
313
213
    Chat() << "re-simplifying assertions..." << endl;
314
426
    ScopeCounter depth(d_simplifyAssertionsDepth);
315
213
    noConflict &= simplifyAssertions(assertions);
316
426
    Trace("smt-proc")
317
213
        << "ProcessAssertions::processAssertions() : post-repeat-simplify"
318
213
        << endl;
319
  }
320
8539
  dumpAssertions("post-repeat-simplify", assertions);
321
322
8539
  if (options::ufHo())
323
  {
324
116
    d_passes["ho-elim"]->apply(&assertions);
325
  }
326
327
  // begin: INVARIANT to maintain: no reordering of assertions or
328
  // introducing new ones
329
330
8539
  Debug("smt") << " assertions     : " << assertions.size() << endl;
331
332
17078
  Debug("smt") << "ProcessAssertions::processAssertions() POST SIMPLIFICATION"
333
8539
               << endl;
334
8539
  Debug("smt") << " assertions     : " << assertions.size() << endl;
335
336
  // ensure rewritten
337
8539
  d_passes["rewrite"]->apply(&assertions);
338
  // rewrite equalities based on theory-specific rewriting
339
8539
  d_passes["theory-rewrite-eq"]->apply(&assertions);
340
  // apply theory preprocess, which includes ITE removal
341
8546
  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
8532
  if (options::bitblastMode() == options::BitblastMode::EAGER)
346
  {
347
32
    d_passes["bv-eager-atoms"]->apply(&assertions);
348
  }
349
350
8532
  Trace("smt-proc") << "SmtEnginePrivate::processAssertions() end" << endl;
351
8532
  dumpAssertions("post-everything", assertions);
352
353
8532
  return noConflict;
354
}
355
356
// returns false if simplification led to "false"
357
8752
bool ProcessAssertions::simplifyAssertions(AssertionPipeline& assertions)
358
{
359
8752
  spendResource(Resource::PreprocessStep);
360
  try
361
  {
362
16784
    ScopeCounter depth(d_simplifyAssertionsDepth);
363
364
8752
    Trace("simplify") << "SmtEnginePrivate::simplify()" << endl;
365
366
8752
    if (options::simplificationMode() != options::SimplificationMode::NONE)
367
    {
368
      // Perform non-clausal simplification
369
      PreprocessingPassResult res =
370
8551
          d_passes["non-clausal-simp"]->apply(&assertions);
371
8551
      if (res == PreprocessingPassResult::CONFLICT)
372
      {
373
718
        return false;
374
      }
375
376
      // We piggy-back off of the BackEdgesMap in the CircuitPropagator to
377
      // do the miplib trick.
378
7833
      if (  // check that option is on
379
7841
          options::arithMLTrick() &&
380
          // only useful in arith
381
7841
          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
15650
        Trace("simplify") << "SmtEnginePrivate::simplify(): "
392
7825
                          << "skipping miplib pseudobooleans pass..." << endl;
393
      }
394
    }
395
396
8034
    Debug("smt") << " assertions     : " << assertions.size() << endl;
397
398
    // ITE simplification
399
16068
    if (options::doITESimp()
400
8034
        && (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
8034
    Debug("smt") << " assertions     : " << assertions.size() << endl;
411
412
    // Unconstrained simplification
413
8034
    if (options::unconstrainedSimp())
414
    {
415
280
      d_passes["unconstrained-simplifier"]->apply(&assertions);
416
    }
417
418
16068
    if (options::repeatSimp()
419
8034
        && options::simplificationMode() != options::SimplificationMode::NONE)
420
    {
421
      PreprocessingPassResult res =
422
404
          d_passes["non-clausal-simp"]->apply(&assertions);
423
404
      if (res == PreprocessingPassResult::CONFLICT)
424
      {
425
2
        return false;
426
      }
427
    }
428
429
8032
    dumpAssertions("post-repeatsimp", assertions);
430
8032
    Trace("smt") << "POST repeatSimp" << endl;
431
8032
    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
8032
  return true;
445
}
446
447
76344
void ProcessAssertions::dumpAssertions(const char* key,
448
                                       const AssertionPipeline& assertionList)
449
{
450
76344
  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
76344
}
461
462
}  // namespace smt
463
22746
}  // namespace cvc5