GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/preprocessing/passes/non_clausal_simp.cpp Lines: 217 225 96.4 %
Date: 2021-08-17 Branches: 490 1180 41.5 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Aina Niemetz, Andrew Reynolds, Gereon Kremer
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
 * Non-clausal simplification preprocessing pass.
14
 *
15
 * Run the nonclausal solver and try to solve all assigned theory literals.
16
 */
17
18
#include "preprocessing/passes/non_clausal_simp.h"
19
20
#include <vector>
21
22
#include "context/cdo.h"
23
#include "options/smt_options.h"
24
#include "preprocessing/assertion_pipeline.h"
25
#include "preprocessing/preprocessing_pass_context.h"
26
#include "smt/preprocess_proof_generator.h"
27
#include "smt/smt_statistics_registry.h"
28
#include "theory/booleans/circuit_propagator.h"
29
#include "theory/theory.h"
30
#include "theory/theory_engine.h"
31
#include "theory/theory_model.h"
32
#include "theory/trust_substitutions.h"
33
34
using namespace cvc5;
35
using namespace cvc5::theory;
36
37
namespace cvc5 {
38
namespace preprocessing {
39
namespace passes {
40
41
/* -------------------------------------------------------------------------- */
42
43
9850
NonClausalSimp::Statistics::Statistics()
44
9850
    : d_numConstantProps(smtStatisticsRegistry().registerInt(
45
9850
        "preprocessing::passes::NonClausalSimp::NumConstantProps"))
46
{
47
9850
}
48
49
50
/* -------------------------------------------------------------------------- */
51
52
9850
NonClausalSimp::NonClausalSimp(PreprocessingPassContext* preprocContext)
53
    : PreprocessingPass(preprocContext, "non-clausal-simp"),
54
9850
      d_pnm(preprocContext->getProofNodeManager()),
55
9850
      d_llpg(d_pnm ? new smt::PreprocessProofGenerator(
56
                         d_pnm,
57
3766
                         preprocContext->getUserContext(),
58
3766
                         "NonClausalSimp::llpg")
59
                   : nullptr),
60
9850
      d_llra(d_pnm ? new LazyCDProof(d_pnm,
61
                                     nullptr,
62
3766
                                     preprocContext->getUserContext(),
63
3766
                                     "NonClausalSimp::llra")
64
                   : nullptr),
65
54464
      d_tsubsList(preprocContext->getUserContext())
66
{
67
9850
}
68
69
11020
PreprocessingPassResult NonClausalSimp::applyInternal(
70
    AssertionPipeline* assertionsToPreprocess)
71
{
72
11020
  d_preprocContext->spendResource(Resource::PreprocessStep);
73
74
  theory::booleans::CircuitPropagator* propagator =
75
11020
      d_preprocContext->getCircuitPropagator();
76
77
95026
  for (size_t i = 0, size = assertionsToPreprocess->size(); i < size; ++i)
78
  {
79
168012
    Trace("non-clausal-simplify") << "Assertion #" << i << " : "
80
84006
                                  << (*assertionsToPreprocess)[i] << std::endl;
81
  }
82
83
11020
  if (propagator->getNeedsFinish())
84
  {
85
4205
    propagator->finish();
86
4205
    propagator->setNeedsFinish(false);
87
  }
88
11020
  propagator->initialize();
89
90
  // Assert all the assertions to the propagator
91
11020
  Trace("non-clausal-simplify") << "asserting to propagator" << std::endl;
92
95026
  for (size_t i = 0, size = assertionsToPreprocess->size(); i < size; ++i)
93
  {
94
84006
    Assert(Rewriter::rewrite((*assertionsToPreprocess)[i])
95
           == (*assertionsToPreprocess)[i]);
96
    // Don't reprocess substitutions
97
84006
    if (assertionsToPreprocess->isSubstsIndex(i))
98
    {
99
3039
      continue;
100
    }
101
161934
    Trace("non-clausal-simplify")
102
80967
        << "asserting " << (*assertionsToPreprocess)[i] << std::endl;
103
161934
    Debug("cores") << "propagator->assertTrue: " << (*assertionsToPreprocess)[i]
104
80967
                   << std::endl;
105
80967
    propagator->assertTrue((*assertionsToPreprocess)[i]);
106
  }
107
108
11020
  Trace("non-clausal-simplify") << "propagating" << std::endl;
109
22040
  TrustNode conf = propagator->propagate();
110
11020
  if (!conf.isNull())
111
  {
112
    // If in conflict, just return false
113
1852
    Trace("non-clausal-simplify")
114
926
        << "conflict in non-clausal propagation" << std::endl;
115
926
    assertionsToPreprocess->clear();
116
926
    assertionsToPreprocess->pushBackTrusted(conf);
117
926
    propagator->setNeedsFinish(true);
118
926
    return PreprocessingPassResult::CONFLICT;
119
  }
120
121
20188
  Trace("non-clausal-simplify")
122
20188
      << "Iterate through " << propagator->getLearnedLiterals().size()
123
10094
      << " learned literals." << std::endl;
124
  // No conflict, go through the literals and solve them
125
10094
  context::Context* u = d_preprocContext->getUserContext();
126
10094
  TrustSubstitutionMap& ttls = d_preprocContext->getTopLevelSubstitutions();
127
10094
  CVC5_UNUSED SubstitutionMap& top_level_substs = ttls.get();
128
  // constant propagations
129
  std::shared_ptr<TrustSubstitutionMap> constantPropagations =
130
      std::make_shared<TrustSubstitutionMap>(
131
20188
          u, d_pnm, "NonClausalSimp::cprop", PfRule::PREPROCESS_LEMMA);
132
10094
  SubstitutionMap& cps = constantPropagations->get();
133
  // new substitutions
134
  std::shared_ptr<TrustSubstitutionMap> newSubstitutions =
135
      std::make_shared<TrustSubstitutionMap>(
136
20188
          u, d_pnm, "NonClausalSimp::newSubs", PfRule::PREPROCESS_LEMMA);
137
10094
  SubstitutionMap& nss = newSubstitutions->get();
138
139
10094
  size_t j = 0;
140
10094
  std::vector<TrustNode>& learned_literals = propagator->getLearnedLiterals();
141
  // if proofs are enabled, we will need to track the proofs of learned literals
142
10094
  if (isProofEnabled())
143
  {
144
1811
    d_tsubsList.push_back(constantPropagations);
145
1811
    d_tsubsList.push_back(newSubstitutions);
146
30130
    for (const TrustNode& tll : learned_literals)
147
    {
148
28319
      d_llpg->notifyNewTrustedAssert(tll);
149
    }
150
  }
151
124055
  for (size_t i = 0, size = learned_literals.size(); i < size; ++i)
152
  {
153
    // Simplify the literal we learned wrt previous substitutions
154
227540
    Node learnedLiteral = learned_literals[i].getNode();
155
114118
    Assert(Rewriter::rewrite(learnedLiteral) == learnedLiteral);
156
114118
    Assert(top_level_substs.apply(learnedLiteral) == learnedLiteral);
157
    // process the learned literal with substitutions and const propagations
158
114118
    learnedLiteral = processLearnedLit(
159
        learnedLiteral, newSubstitutions.get(), constantPropagations.get());
160
228236
    Trace("non-clausal-simplify")
161
114118
        << "Process learnedLiteral, after constProp : " << learnedLiteral
162
114118
        << std::endl;
163
    // It might just simplify to a constant
164
114118
    if (learnedLiteral.isConst())
165
    {
166
696
      if (learnedLiteral.getConst<bool>())
167
      {
168
        // If the learned literal simplifies to true, it's redundant
169
539
        continue;
170
      }
171
      else
172
      {
173
        // If the learned literal simplifies to false, we're in conflict
174
314
        Trace("non-clausal-simplify")
175
157
            << "conflict with " << learned_literals[i].getNode() << std::endl;
176
157
        assertionsToPreprocess->clear();
177
314
        Node n = NodeManager::currentNM()->mkConst<bool>(false);
178
157
        assertionsToPreprocess->push_back(n, false, false, d_llpg.get());
179
157
        propagator->setNeedsFinish(true);
180
157
        return PreprocessingPassResult::CONFLICT;
181
      }
182
    }
183
184
    // Solve it with the corresponding theory, possibly adding new
185
    // substitutions to newSubstitutions
186
113422
    Trace("non-clausal-simplify") << "solving " << learnedLiteral << std::endl;
187
188
    TrustNode tlearnedLiteral =
189
226844
        TrustNode::mkTrustLemma(learnedLiteral, d_llpg.get());
190
    Theory::PPAssertStatus solveStatus =
191
340266
        d_preprocContext->getTheoryEngine()->solve(tlearnedLiteral,
192
226844
                                                   *newSubstitutions.get());
193
194
113422
    switch (solveStatus)
195
    {
196
48112
      case Theory::PP_ASSERT_STATUS_SOLVED:
197
      {
198
        // The literal should rewrite to true
199
96224
        Trace("non-clausal-simplify")
200
48112
            << "solved " << learnedLiteral << std::endl;
201
48112
        Assert(Rewriter::rewrite(nss.apply(learnedLiteral)).isConst());
202
        // else fall through
203
48112
        break;
204
      }
205
      case Theory::PP_ASSERT_STATUS_CONFLICT:
206
      {
207
        // If in conflict, we return false
208
        Trace("non-clausal-simplify")
209
            << "conflict while solving " << learnedLiteral << std::endl;
210
        assertionsToPreprocess->clear();
211
        Node n = NodeManager::currentNM()->mkConst<bool>(false);
212
        assertionsToPreprocess->push_back(n);
213
        propagator->setNeedsFinish(true);
214
        return PreprocessingPassResult::CONFLICT;
215
      }
216
65310
      default:
217
130620
        if (learnedLiteral.getKind() == kind::EQUAL
218
130620
            && (learnedLiteral[0].isConst() || learnedLiteral[1].isConst()))
219
        {
220
          // constant propagation
221
11078
          TNode t;
222
11078
          TNode c;
223
5539
          if (learnedLiteral[0].isConst())
224
          {
225
214
            t = learnedLiteral[1];
226
214
            c = learnedLiteral[0];
227
          }
228
          else
229
          {
230
5325
            t = learnedLiteral[0];
231
5325
            c = learnedLiteral[1];
232
          }
233
5539
          Assert(!t.isConst());
234
5539
          Assert(cps.apply(t, true) == t);
235
5539
          Assert(top_level_substs.apply(t) == t);
236
5539
          Assert(nss.apply(t) == t);
237
          // also add to learned literal
238
11078
          ProofGenerator* cpg = constantPropagations->addSubstitutionSolved(
239
5539
              t, c, tlearnedLiteral);
240
          // We need to justify (= t c) as a literal, since it is reasserted
241
          // to the assertion pipeline below. We do this with the proof
242
          // generator returned by the above call.
243
5539
          if (isProofEnabled())
244
          {
245
1457
            d_llpg->notifyNewAssert(t.eqNode(c), cpg);
246
          }
247
        }
248
        else
249
        {
250
          // Keep the literal
251
59771
          learned_literals[j++] = learned_literals[i];
252
          // Its a literal that could not be processed as a substitution or
253
          // conflict. In this case, we notify the context of the learned
254
          // literal, which will process it with the learned literal manager.
255
59771
          d_preprocContext->notifyLearnedLiteral(learnedLiteral);
256
        }
257
65310
        break;
258
    }
259
  }
260
261
#ifdef CVC5_ASSERTIONS
262
  // NOTE: When debugging this code, consider moving this check inside of the
263
  // loop over propagator->getLearnedLiterals(). This check has been moved
264
  // outside because it is costly for certain inputs (see bug 508).
265
  //
266
  // Check data structure invariants:
267
  // 1. for each lhs of top_level_substs, does not appear anywhere in rhs of
268
  // top_level_substs or anywhere in constantPropagations
269
  // 2. each lhs of constantPropagations rewrites to itself
270
  // 3. if l -> r is a constant propagation and l is a subterm of l' with l' ->
271
  // r' another constant propagation, then l'[l/r] -> r' should be a
272
  //    constant propagation too
273
  // 4. each lhs of constantPropagations is different from each rhs
274
57452
  for (SubstitutionMap::iterator pos = nss.begin(); pos != nss.end(); ++pos)
275
  {
276
47515
    Assert((*pos).first.isVar());
277
47515
    Assert(top_level_substs.apply((*pos).first) == (*pos).first);
278
47515
    Assert(top_level_substs.apply((*pos).second) == (*pos).second);
279
95030
    Node app = nss.apply((*pos).second);
280
47515
    Assert(nss.apply(app) == app);
281
  }
282
15454
  for (SubstitutionMap::iterator pos = cps.begin(); pos != cps.end(); ++pos)
283
  {
284
5517
    Assert((*pos).second.isConst());
285
5517
    Assert(Rewriter::rewrite((*pos).first) == (*pos).first);
286
5517
    Assert(cps.apply((*pos).second) == (*pos).second);
287
  }
288
#endif /* CVC5_ASSERTIONS */
289
290
  // Resize the learnt
291
19874
  Trace("non-clausal-simplify")
292
9937
      << "Resize non-clausal learned literals to " << j << std::endl;
293
9937
  learned_literals.resize(j);
294
295
19874
  std::unordered_set<TNode> s;
296
89086
  for (size_t i = 0, size = assertionsToPreprocess->size(); i < size; ++i)
297
  {
298
158298
    Node assertion = (*assertionsToPreprocess)[i];
299
158298
    TrustNode assertionNew = newSubstitutions->applyTrusted(assertion);
300
79149
    Trace("non-clausal-simplify") << "assertion = " << assertion << std::endl;
301
79149
    if (!assertionNew.isNull())
302
    {
303
57438
      Trace("non-clausal-simplify")
304
28719
          << "assertionNew = " << assertionNew.getNode() << std::endl;
305
28719
      assertionsToPreprocess->replaceTrusted(i, assertionNew);
306
28719
      assertion = assertionNew.getNode();
307
28719
      Assert(Rewriter::rewrite(assertion) == assertion);
308
    }
309
    for (;;)
310
    {
311
81245
      assertionNew = constantPropagations->applyTrusted(assertion);
312
80197
      if (assertionNew.isNull())
313
      {
314
79149
        break;
315
      }
316
1048
      Assert(assertionNew.getNode() != assertion);
317
1048
      assertionsToPreprocess->replaceTrusted(i, assertionNew);
318
1048
      assertion = assertionNew.getNode();
319
1048
      d_statistics.d_numConstantProps += 1;
320
2096
      Trace("non-clausal-simplify")
321
1048
          << "assertionNew = " << assertion << std::endl;
322
    }
323
79149
    s.insert(assertion);
324
158298
    Trace("non-clausal-simplify")
325
79149
        << "non-clausal preprocessed: " << assertion << std::endl;
326
  }
327
328
  // If necessary, add as assertions if needed (when incremental). This is
329
  // necessary because certain variables cannot truly be eliminated when
330
  // we are in incremental mode. For example, say our first call to check-sat
331
  // is a formula F containing variable x. On the second call to check-sat,
332
  // say we solve a top-level assertion (= x t). Since the solver already has
333
  // constraints involving x, we must still keep (= x t) as an assertion.
334
  // However, notice that we do not retract the substitution { x -> t }. This
335
  // means that all *subsequent* assertions after (= x t) will replace x by t.
336
9937
  if (assertionsToPreprocess->storeSubstsInAsserts())
337
  {
338
5469
    for (const std::pair<const Node, const Node>& pos: nss)
339
    {
340
5290
      Node lhs = pos.first;
341
      // If using incremental, we must check whether this variable has occurred
342
      // before now. If it has, we must add as an assertion.
343
2645
      if (d_preprocContext->getSymsInAssertions().contains(lhs))
344
      {
345
        // if it has, the substitution becomes an assertion
346
2734
        TrustNode trhs = newSubstitutions->applyTrusted(lhs);
347
1367
        Assert(!trhs.isNull());
348
2734
        Trace("non-clausal-simplify")
349
1367
            << "substitute: will notify SAT layer of substitution: "
350
1367
            << trhs.getProven() << std::endl;
351
1367
        assertionsToPreprocess->addSubstitutionNode(trhs.getProven(),
352
                                                    trhs.getGenerator());
353
      }
354
    }
355
  }
356
357
9937
  Assert(assertionsToPreprocess->getRealAssertionsEnd()
358
         <= assertionsToPreprocess->size());
359
  // Learned literals to conjoin. If proofs are enabled, all these are
360
  // justified by d_llpg.
361
19874
  std::vector<Node> learnedLitsToConjoin;
362
363
69166
  for (size_t i = 0; i < learned_literals.size(); ++i)
364
  {
365
97295
    Node learned = learned_literals[i].getNode();
366
59229
    Assert(top_level_substs.apply(learned) == learned);
367
    // process learned literal
368
59229
    learned = processLearnedLit(
369
        learned, newSubstitutions.get(), constantPropagations.get());
370
59229
    if (s.find(learned) != s.end())
371
    {
372
21163
      continue;
373
    }
374
38066
    s.insert(learned);
375
38066
    learnedLitsToConjoin.push_back(learned);
376
76132
    Trace("non-clausal-simplify")
377
38066
        << "non-clausal learned : " << learned << std::endl;
378
  }
379
9937
  learned_literals.clear();
380
381
15454
  for (SubstitutionMap::iterator pos = cps.begin(); pos != cps.end(); ++pos)
382
  {
383
10927
    Node cProp = (*pos).first.eqNode((*pos).second);
384
5517
    Assert(top_level_substs.apply(cProp) == cProp);
385
    // process learned literal (substitutions only)
386
5517
    cProp = processLearnedLit(cProp, newSubstitutions.get(), nullptr);
387
5517
    if (s.find(cProp) != s.end())
388
    {
389
107
      continue;
390
    }
391
5410
    s.insert(cProp);
392
5410
    learnedLitsToConjoin.push_back(cProp);
393
10820
    Trace("non-clausal-simplify")
394
5410
        << "non-clausal constant propagation : " << cProp << std::endl;
395
  }
396
397
  // Add new substitutions to topLevelSubstitutions
398
  // Note that we don't have to keep rhs's in full solved form
399
  // because SubstitutionMap::apply does a fixed-point iteration when
400
  // substituting
401
9937
  ttls.addSubstitutions(*newSubstitutions.get());
402
403
9937
  if (!learnedLitsToConjoin.empty())
404
  {
405
2461
    size_t replIndex = assertionsToPreprocess->getRealAssertionsEnd() - 1;
406
4922
    Node newConj = NodeManager::currentNM()->mkAnd(learnedLitsToConjoin);
407
4922
    Trace("non-clausal-simplify")
408
2461
        << "non-clausal simplification, reassert: " << newConj << std::endl;
409
2461
    ProofGenerator* pg = nullptr;
410
2461
    if (isProofEnabled())
411
    {
412
      // justify in d_llra
413
15336
      for (const Node& lit : learnedLitsToConjoin)
414
      {
415
14896
        d_llra->addLazyStep(lit, d_llpg.get());
416
      }
417
440
      if (learnedLitsToConjoin.size() > 1)
418
      {
419
325
        d_llra->addStep(newConj, PfRule::AND_INTRO, learnedLitsToConjoin, {});
420
325
        pg = d_llra.get();
421
      }
422
      else
423
      {
424
        // otherwise we ask the learned literal proof generator directly
425
115
        pg = d_llpg.get();
426
      }
427
    }
428
    // ------- from d_llpg    --------- from d_llpg
429
    //  conj[0]       ....    d_conj[n]
430
    // -------------------------------- AND_INTRO
431
    //  newConj
432
    // where newConj is conjoined at the given index
433
2461
    assertionsToPreprocess->conjoin(replIndex, newConj, pg);
434
  }
435
436
9937
  propagator->setNeedsFinish(true);
437
438
  // Note that typically ttls.apply(assert)==assert here.
439
  // However, this invariant is invalidated for cases where we use explicit
440
  // equality assertions for variables solved in incremental mode that already
441
  // exist in assertions, as described above.
442
443
9937
  return PreprocessingPassResult::NO_CONFLICT;
444
}
445
446
58946
bool NonClausalSimp::isProofEnabled() const { return d_pnm != nullptr; }
447
448
178864
Node NonClausalSimp::processLearnedLit(Node lit,
449
                                       theory::TrustSubstitutionMap* subs,
450
                                       theory::TrustSubstitutionMap* cp)
451
{
452
357728
  TrustNode tlit;
453
178864
  if (subs != nullptr)
454
  {
455
178864
    tlit = subs->applyTrusted(lit);
456
178864
    if (!tlit.isNull())
457
    {
458
40710
      lit = processRewrittenLearnedLit(tlit);
459
    }
460
357728
    Trace("non-clausal-simplify")
461
178864
        << "Process learnedLiteral, after newSubs : " << lit << std::endl;
462
  }
463
  // apply to fixed point
464
178864
  if (cp != nullptr)
465
  {
466
    for (;;)
467
    {
468
173631
      tlit = cp->applyTrusted(lit);
469
173489
      if (tlit.isNull())
470
      {
471
173347
        break;
472
      }
473
142
      Assert(lit != tlit.getNode());
474
142
      lit = processRewrittenLearnedLit(tlit);
475
142
      d_statistics.d_numConstantProps += 1;
476
    }
477
  }
478
357728
  return lit;
479
}
480
481
40852
Node NonClausalSimp::processRewrittenLearnedLit(TrustNode trn)
482
{
483
40852
  if (isProofEnabled())
484
  {
485
15198
    d_llpg->notifyTrustedPreprocessed(trn);
486
  }
487
  // return the node
488
40852
  return trn.getNode();
489
}
490
491
}  // namespace passes
492
}  // namespace preprocessing
493
29337
}  // namespace cvc5