GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arith/congruence_manager.cpp Lines: 359 411 87.3 %
Date: 2021-11-05 Branches: 729 1926 37.9 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Tim King, Alex Ozdemir, Andrew Reynolds
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
 * [[ Add one-line brief description here ]]
14
 *
15
 * [[ Add lengthier description here ]]
16
 * \todo document this file
17
 */
18
19
#include "theory/arith/congruence_manager.h"
20
21
#include "base/output.h"
22
#include "options/arith_options.h"
23
#include "proof/proof_node.h"
24
#include "proof/proof_node_manager.h"
25
#include "smt/env.h"
26
#include "smt/smt_statistics_registry.h"
27
#include "theory/arith/arith_utilities.h"
28
#include "theory/arith/constraint.h"
29
#include "theory/arith/partial_model.h"
30
#include "theory/ee_setup_info.h"
31
#include "theory/rewriter.h"
32
#include "theory/uf/equality_engine.h"
33
#include "theory/uf/proof_equality_engine.h"
34
35
namespace cvc5 {
36
namespace theory {
37
namespace arith {
38
39
15271
ArithCongruenceManager::ArithCongruenceManager(
40
    Env& env,
41
    ConstraintDatabase& cd,
42
    SetupLiteralCallBack setup,
43
    const ArithVariables& avars,
44
15271
    RaiseEqualityEngineConflict raiseConflict)
45
    : EnvObj(env),
46
      d_inConflict(context()),
47
      d_raiseConflict(raiseConflict),
48
      d_notify(*this),
49
      d_keepAlive(context()),
50
      d_propagatations(context()),
51
      d_explanationMap(context()),
52
      d_constraintDatabase(cd),
53
      d_setupLiteral(setup),
54
      d_avariables(avars),
55
      d_ee(nullptr),
56
15271
      d_pnm(d_env.isTheoryProofProducing() ? d_env.getProofNodeManager()
57
                                           : nullptr),
58
      // Construct d_pfGenEe with the SAT context, since its proof include
59
      // unclosed assumptions of theory literals.
60
      d_pfGenEe(new EagerProofGenerator(
61
30542
          d_pnm, context(), "ArithCongruenceManager::pfGenEe")),
62
      // Construct d_pfGenEe with the USER context, since its proofs are closed.
63
      d_pfGenExplain(new EagerProofGenerator(
64
30542
          d_pnm, userContext(), "ArithCongruenceManager::pfGenExplain")),
65
91626
      d_pfee(nullptr)
66
{
67
15271
}
68
69
15266
ArithCongruenceManager::~ArithCongruenceManager() {}
70
71
15230
bool ArithCongruenceManager::needsEqualityEngine(EeSetupInfo& esi)
72
{
73
15230
  Assert(!options().arith.arithEqSolver);
74
15230
  esi.d_notify = &d_notify;
75
15230
  esi.d_name = "arithCong::ee";
76
15230
  return true;
77
}
78
79
15230
void ArithCongruenceManager::finishInit(eq::EqualityEngine* ee)
80
{
81
15230
  if (options().arith.arithEqSolver)
82
  {
83
    // use our own copy
84
    d_allocEe = std::make_unique<eq::EqualityEngine>(
85
        d_env, context(), d_notify, "arithCong::ee", true);
86
    d_ee = d_allocEe.get();
87
    if (d_pnm != nullptr)
88
    {
89
      // allocate an internal proof equality engine
90
      d_allocPfee = std::make_unique<eq::ProofEqEngine>(d_env, *d_ee);
91
      d_ee->setProofEqualityEngine(d_allocPfee.get());
92
    }
93
  }
94
  else
95
  {
96
15230
    Assert(ee != nullptr);
97
    // otherwise, we use the official one
98
15230
    d_ee = ee;
99
  }
100
  // set the congruence kinds on the separate equality engine
101
15230
  d_ee->addFunctionKind(kind::NONLINEAR_MULT);
102
15230
  d_ee->addFunctionKind(kind::EXPONENTIAL);
103
15230
  d_ee->addFunctionKind(kind::SINE);
104
15230
  d_ee->addFunctionKind(kind::IAND);
105
15230
  d_ee->addFunctionKind(kind::POW2);
106
  // the proof equality engine is the one from the equality engine
107
15230
  d_pfee = d_ee->getProofEqualityEngine();
108
  // have proof equality engine only if proofs are enabled
109
15230
  Assert(isProofEnabled() == (d_pfee != nullptr));
110
15230
}
111
112
15271
ArithCongruenceManager::Statistics::Statistics()
113
15271
    : d_watchedVariables(smtStatisticsRegistry().registerInt(
114
30542
        "theory::arith::congruence::watchedVariables")),
115
15271
      d_watchedVariableIsZero(smtStatisticsRegistry().registerInt(
116
30542
          "theory::arith::congruence::watchedVariableIsZero")),
117
15271
      d_watchedVariableIsNotZero(smtStatisticsRegistry().registerInt(
118
30542
          "theory::arith::congruence::watchedVariableIsNotZero")),
119
15271
      d_equalsConstantCalls(smtStatisticsRegistry().registerInt(
120
30542
          "theory::arith::congruence::equalsConstantCalls")),
121
15271
      d_propagations(smtStatisticsRegistry().registerInt(
122
30542
          "theory::arith::congruence::propagations")),
123
15271
      d_propagateConstraints(smtStatisticsRegistry().registerInt(
124
30542
          "theory::arith::congruence::propagateConstraints")),
125
15271
      d_conflicts(smtStatisticsRegistry().registerInt(
126
106897
          "theory::arith::congruence::conflicts"))
127
{
128
15271
}
129
130
15271
ArithCongruenceManager::ArithCongruenceNotify::ArithCongruenceNotify(ArithCongruenceManager& acm)
131
15271
  : d_acm(acm)
132
15271
{}
133
134
bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerPredicate(
135
    TNode predicate, bool value)
136
{
137
  Assert(predicate.getKind() == kind::EQUAL);
138
  Debug("arith::congruences")
139
      << "ArithCongruenceNotify::eqNotifyTriggerPredicate(" << predicate << ", "
140
      << (value ? "true" : "false") << ")" << std::endl;
141
  if (value) {
142
    return d_acm.propagate(predicate);
143
  }
144
  return d_acm.propagate(predicate.notNode());
145
}
146
147
1130484
bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value) {
148
1130484
  Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyTriggerTermEquality(" << t1 << ", " << t2 << ", " << (value ? "true" : "false") << ")" << std::endl;
149
1130484
  if (value) {
150
934631
    return d_acm.propagate(t1.eqNode(t2));
151
  } else {
152
195853
    return d_acm.propagate(t1.eqNode(t2).notNode());
153
  }
154
}
155
2165
void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyConstantTermMerge(TNode t1, TNode t2) {
156
2165
  Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << std::endl;
157
2165
  d_acm.propagate(t1.eqNode(t2));
158
2165
}
159
1612699
void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyNewClass(TNode t) {
160
1612699
}
161
1845437
void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyMerge(TNode t1,
162
                                                                  TNode t2)
163
{
164
1845437
}
165
352692
void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyDisequal(TNode t1, TNode t2, TNode reason) {
166
352692
}
167
168
2418
void ArithCongruenceManager::raiseConflict(Node conflict,
169
                                           std::shared_ptr<ProofNode> pf)
170
{
171
2418
  Assert(!inConflict());
172
2418
  Debug("arith::conflict") << "difference manager conflict   " << conflict << std::endl;
173
2418
  d_inConflict.raise();
174
2418
  d_raiseConflict.raiseEEConflict(conflict, pf);
175
2418
}
176
1135067
bool ArithCongruenceManager::inConflict() const{
177
1135067
  return d_inConflict.isRaised();
178
}
179
180
5647961
bool ArithCongruenceManager::hasMorePropagations() const {
181
5647961
  return !d_propagatations.empty();
182
}
183
888890
const Node ArithCongruenceManager::getNextPropagation() {
184
888890
  Assert(hasMorePropagations());
185
888890
  Node prop = d_propagatations.front();
186
888890
  d_propagatations.dequeue();
187
888890
  return prop;
188
}
189
190
27909
bool ArithCongruenceManager::canExplain(TNode n) const {
191
27909
  return d_explanationMap.find(n) != d_explanationMap.end();
192
}
193
194
17534
Node ArithCongruenceManager::externalToInternal(TNode n) const{
195
17534
  Assert(canExplain(n));
196
17534
  ExplainMap::const_iterator iter = d_explanationMap.find(n);
197
17534
  size_t pos = (*iter).second;
198
17534
  return d_propagatations[pos];
199
}
200
201
797313
void ArithCongruenceManager::pushBack(TNode n){
202
797313
  d_explanationMap.insert(n, d_propagatations.size());
203
797313
  d_propagatations.enqueue(n);
204
205
797313
  ++(d_statistics.d_propagations);
206
797313
}
207
146101
void ArithCongruenceManager::pushBack(TNode n, TNode r){
208
146101
  d_explanationMap.insert(r, d_propagatations.size());
209
146101
  d_explanationMap.insert(n, d_propagatations.size());
210
146101
  d_propagatations.enqueue(n);
211
212
146101
  ++(d_statistics.d_propagations);
213
146101
}
214
void ArithCongruenceManager::pushBack(TNode n, TNode r, TNode w){
215
  d_explanationMap.insert(w, d_propagatations.size());
216
  d_explanationMap.insert(r, d_propagatations.size());
217
  d_explanationMap.insert(n, d_propagatations.size());
218
  d_propagatations.enqueue(n);
219
220
  ++(d_statistics.d_propagations);
221
}
222
223
248012
void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP lb, ConstraintCP ub){
224
248012
  Assert(lb->isLowerBound());
225
248012
  Assert(ub->isUpperBound());
226
248012
  Assert(lb->getVariable() == ub->getVariable());
227
248012
  Assert(lb->getValue().sgn() == 0);
228
248012
  Assert(ub->getValue().sgn() == 0);
229
230
248012
  ++(d_statistics.d_watchedVariableIsZero);
231
232
248012
  ArithVar s = lb->getVariable();
233
496024
  TNode eq = d_watchedEqualities[s];
234
248012
  ConstraintCP eqC = d_constraintDatabase.getConstraint(
235
248012
      s, ConstraintType::Equality, lb->getValue());
236
496024
  NodeBuilder reasonBuilder(Kind::AND);
237
496024
  auto pfLb = lb->externalExplainByAssertions(reasonBuilder);
238
496024
  auto pfUb = ub->externalExplainByAssertions(reasonBuilder);
239
496024
  Node reason = safeConstructNary(reasonBuilder);
240
496024
  std::shared_ptr<ProofNode> pf{};
241
248012
  if (isProofEnabled())
242
  {
243
85896
    pf = d_pnm->mkNode(
244
64422
        PfRule::ARITH_TRICHOTOMY, {pfLb, pfUb}, {eqC->getProofLiteral()});
245
21474
    pf = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {eq});
246
  }
247
248
248012
  d_keepAlive.push_back(reason);
249
496024
  Trace("arith-ee") << "Asserting an equality on " << s << ", on trichotomy"
250
248012
                    << std::endl;
251
248012
  Trace("arith-ee") << "  based on " << lb << std::endl;
252
248012
  Trace("arith-ee") << "  based on " << ub << std::endl;
253
248012
  assertionToEqualityEngine(true, s, reason, pf);
254
248012
}
255
256
261258
void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP eq){
257
261258
  Debug("arith::cong") << "Cong::watchedVariableIsZero: " << *eq << std::endl;
258
259
261258
  Assert(eq->isEquality());
260
261258
  Assert(eq->getValue().sgn() == 0);
261
262
261258
  ++(d_statistics.d_watchedVariableIsZero);
263
264
261258
  ArithVar s = eq->getVariable();
265
266
  //Explain for conflict is correct as these proofs are generated
267
  //and stored eagerly
268
  //These will be safe for propagation later as well
269
522516
  NodeBuilder nb(Kind::AND);
270
  // An open proof of eq from literals now in reason.
271
261258
  if (Debug.isOn("arith::cong"))
272
  {
273
    eq->printProofTree(Debug("arith::cong"));
274
  }
275
522516
  auto pf = eq->externalExplainByAssertions(nb);
276
261258
  if (isProofEnabled())
277
  {
278
115905
    pf = d_pnm->mkNode(
279
77270
        PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {d_watchedEqualities[s]});
280
  }
281
522516
  Node reason = safeConstructNary(nb);
282
283
261258
  d_keepAlive.push_back(reason);
284
261258
  assertionToEqualityEngine(true, s, reason, pf);
285
261258
}
286
287
825268
void ArithCongruenceManager::watchedVariableCannotBeZero(ConstraintCP c){
288
1650536
  Debug("arith::cong::notzero")
289
825268
      << "Cong::watchedVariableCannotBeZero " << *c << std::endl;
290
825268
  ++(d_statistics.d_watchedVariableIsNotZero);
291
292
825268
  ArithVar s = c->getVariable();
293
1650536
  Node disEq = d_watchedEqualities[s].negate();
294
295
  //Explain for conflict is correct as these proofs are generated and stored eagerly
296
  //These will be safe for propagation later as well
297
1650536
  NodeBuilder nb(Kind::AND);
298
  // An open proof of eq from literals now in reason.
299
1650536
  auto pf = c->externalExplainByAssertions(nb);
300
825268
  if (Debug.isOn("arith::cong::notzero"))
301
  {
302
    Debug("arith::cong::notzero") << "  original proof ";
303
    pf->printDebug(Debug("arith::cong::notzero"));
304
    Debug("arith::cong::notzero") << std::endl;
305
  }
306
1650536
  Node reason = safeConstructNary(nb);
307
825268
  if (isProofEnabled())
308
  {
309
62088
    if (c->getType() == ConstraintType::Disequality)
310
    {
311
19993
      Assert(c->getLiteral() == d_watchedEqualities[s].negate());
312
      // We have to prove equivalence to the watched disequality.
313
19993
      pf = d_pnm->mkNode(PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {disEq});
314
    }
315
    else
316
    {
317
84190
      Debug("arith::cong::notzero")
318
42095
          << "  proof modification needed" << std::endl;
319
320
      // Four cases:
321
      //   c has form x_i = d, d > 0     => multiply c by -1 in Farkas proof
322
      //   c has form x_i = d, d > 0     => multiply c by 1 in Farkas proof
323
      //   c has form x_i <= d, d < 0     => multiply c by 1 in Farkas proof
324
      //   c has form x_i >= d, d > 0     => multiply c by -1 in Farkas proof
325
42095
      const bool scaleCNegatively = c->getType() == ConstraintType::LowerBound
326
59256
                                    || (c->getType() == ConstraintType::Equality
327
47324
                                        && c->getValue().sgn() > 0);
328
42095
      const int cSign = scaleCNegatively ? -1 : 1;
329
84190
      TNode isZero = d_watchedEqualities[s];
330
84190
      const auto isZeroPf = d_pnm->mkAssume(isZero);
331
42095
      const auto nm = NodeManager::currentNM();
332
42095
      const auto sumPf = d_pnm->mkNode(
333
          PfRule::MACRO_ARITH_SCALE_SUM_UB,
334
          {isZeroPf, pf},
335
          // Trick for getting correct, opposing signs.
336
84190
          {nm->mkConst(Rational(-1 * cSign)), nm->mkConst(Rational(cSign))});
337
42095
      const auto botPf = d_pnm->mkNode(
338
84190
          PfRule::MACRO_SR_PRED_TRANSFORM, {sumPf}, {nm->mkConst(false)});
339
84190
      std::vector<Node> assumption = {isZero};
340
42095
      pf = d_pnm->mkScope(botPf, assumption, false);
341
42095
      Debug("arith::cong::notzero") << "  new proof ";
342
42095
      pf->printDebug(Debug("arith::cong::notzero"));
343
42095
      Debug("arith::cong::notzero") << std::endl;
344
    }
345
62088
    Assert(pf->getResult() == disEq);
346
  }
347
825268
  d_keepAlive.push_back(reason);
348
825268
  assertionToEqualityEngine(false, s, reason, pf);
349
825268
}
350
351
352
1132649
bool ArithCongruenceManager::propagate(TNode x){
353
1132649
  Debug("arith::congruenceManager")<< "ArithCongruenceManager::propagate("<<x<<")"<<std::endl;
354
1132649
  if(inConflict()){
355
    return true;
356
  }
357
358
2265298
  Node rewritten = Rewriter::rewrite(x);
359
360
  //Need to still propagate this!
361
1132649
  if(rewritten.getKind() == kind::CONST_BOOLEAN){
362
2312
    pushBack(x);
363
364
2312
    if(rewritten.getConst<bool>()){
365
147
      return true;
366
    }else{
367
      // x rewrites to false.
368
2165
      ++(d_statistics.d_conflicts);
369
4330
      TrustNode trn = explainInternal(x);
370
4330
      Node conf = flattenAnd(trn.getNode());
371
2165
      Debug("arith::congruenceManager") << "rewritten to false "<<x<<" with explanation "<< conf << std::endl;
372
2165
      if (isProofEnabled())
373
      {
374
752
        auto pf = trn.getGenerator()->getProofFor(trn.getProven());
375
376
        auto confPf = d_pnm->mkNode(
376
752
            PfRule::MACRO_SR_PRED_TRANSFORM, {pf}, {conf.negate()});
377
376
        raiseConflict(conf, confPf);
378
      }
379
      else
380
      {
381
1789
        raiseConflict(conf);
382
      }
383
2165
      return false;
384
    }
385
  }
386
387
1130337
  Assert(rewritten.getKind() != kind::CONST_BOOLEAN);
388
389
1130337
  ConstraintP c = d_constraintDatabase.lookup(rewritten);
390
1130337
  if(c == NullConstraint){
391
    //using setup as there may not be a corresponding congruence literal yet
392
21937
    d_setupLiteral(rewritten);
393
21937
    c = d_constraintDatabase.lookup(rewritten);
394
21937
    Assert(c != NullConstraint);
395
  }
396
397
2260674
  Debug("arith::congruenceManager")<< "x is "
398
2260674
                                   <<  c->hasProof() << " "
399
2260674
                                   << (x == rewritten) << " "
400
2260674
                                   << c->canBePropagated() << " "
401
1130337
                                   << c->negationHasProof() << std::endl;
402
403
1130337
  if(c->negationHasProof()){
404
506
    TrustNode texpC = explainInternal(x);
405
506
    Node expC = texpC.getNode();
406
253
    ConstraintCP negC = c->getNegation();
407
506
    Node neg = Constraint::externalExplainByAssertions({negC});
408
506
    Node conf = expC.andNode(neg);
409
506
    Node final = flattenAnd(conf);
410
411
253
    ++(d_statistics.d_conflicts);
412
253
    raiseConflict(final);
413
253
    Debug("arith::congruenceManager") << "congruenceManager found a conflict " << final << std::endl;
414
253
    return false;
415
  }
416
417
  // Cases for propagation
418
  // C : c has a proof
419
  // S : x == rewritten
420
  // P : c can be propagated
421
  //
422
  // CSP
423
  // 000 : propagate x, and mark C it as being explained
424
  // 001 : propagate x, and propagate c after marking it as being explained
425
  // 01* : propagate x, mark c but do not propagate c
426
  // 10* : propagate x, do not mark c and do not propagate c
427
  // 11* : drop the constraint, do not propagate x or c
428
429
1130084
  if(!c->hasProof() && x != rewritten){
430
146101
    if(c->assertedToTheTheory()){
431
      pushBack(x, rewritten, c->getWitness());
432
    }else{
433
146101
      pushBack(x, rewritten);
434
    }
435
436
146101
    c->setEqualityEngineProof();
437
146101
    if(c->canBePropagated() && !c->assertedToTheTheory()){
438
439
19255
      ++(d_statistics.d_propagateConstraints);
440
19255
      c->propagate();
441
    }
442
983983
  }else if(!c->hasProof() && x == rewritten){
443
116095
    if(c->assertedToTheTheory()){
444
      pushBack(x, c->getWitness());
445
    }else{
446
116095
      pushBack(x);
447
    }
448
116095
    c->setEqualityEngineProof();
449
867888
  }else if(c->hasProof() && x != rewritten){
450
678906
    if(c->assertedToTheTheory()){
451
677045
      pushBack(x);
452
    }else{
453
1861
      pushBack(x);
454
    }
455
  }else{
456
188982
    Assert(c->hasProof() && x == rewritten);
457
  }
458
1130084
  return true;
459
}
460
461
void ArithCongruenceManager::explain(TNode literal, std::vector<TNode>& assumptions) {
462
  if (literal.getKind() != kind::NOT) {
463
    d_ee->explainEquality(literal[0], literal[1], true, assumptions);
464
  } else {
465
    d_ee->explainEquality(literal[0][0], literal[0][1], false, assumptions);
466
  }
467
}
468
469
void ArithCongruenceManager::enqueueIntoNB(const std::set<TNode> s,
470
                                           NodeBuilder& nb)
471
{
472
  std::set<TNode>::const_iterator it = s.begin();
473
  std::set<TNode>::const_iterator it_end = s.end();
474
  for(; it != it_end; ++it) {
475
    nb << *it;
476
  }
477
}
478
479
19952
TrustNode ArithCongruenceManager::explainInternal(TNode internal)
480
{
481
19952
  if (isProofEnabled())
482
  {
483
4743
    return d_pfee->explain(internal);
484
  }
485
  // otherwise, explain without proof generator
486
30418
  Node exp = d_ee->mkExplainLit(internal);
487
15209
  return TrustNode::mkTrustPropExp(internal, exp, nullptr);
488
}
489
490
17534
TrustNode ArithCongruenceManager::explain(TNode external)
491
{
492
17534
  Trace("arith-ee") << "Ask for explanation of " << external << std::endl;
493
35068
  Node internal = externalToInternal(external);
494
17534
  Trace("arith-ee") << "...internal = " << internal << std::endl;
495
35068
  TrustNode trn = explainInternal(internal);
496
17534
  if (isProofEnabled() && trn.getProven()[1] != external)
497
  {
498
784
    Assert(trn.getKind() == TrustNodeKind::PROP_EXP);
499
784
    Assert(trn.getProven().getKind() == Kind::IMPLIES);
500
784
    Assert(trn.getGenerator() != nullptr);
501
1568
    Trace("arith-ee") << "tweaking proof to prove " << external << " not "
502
784
                      << trn.getProven()[1] << std::endl;
503
1568
    std::vector<std::shared_ptr<ProofNode>> assumptionPfs;
504
1568
    std::vector<Node> assumptions = andComponents(trn.getNode());
505
784
    assumptionPfs.push_back(trn.toProofNode());
506
2930
    for (const auto& a : assumptions)
507
    {
508
2146
      assumptionPfs.push_back(
509
4292
          d_pnm->mkNode(PfRule::TRUE_INTRO, {d_pnm->mkAssume(a)}, {}));
510
    }
511
784
    auto litPf = d_pnm->mkNode(
512
1568
        PfRule::MACRO_SR_PRED_TRANSFORM, {assumptionPfs}, {external});
513
1568
    auto extPf = d_pnm->mkScope(litPf, assumptions);
514
784
    return d_pfGenExplain->mkTrustedPropagation(external, trn.getNode(), extPf);
515
  }
516
16750
  return trn;
517
}
518
519
void ArithCongruenceManager::explain(TNode external, NodeBuilder& out)
520
{
521
  Node internal = externalToInternal(external);
522
523
  std::vector<TNode> assumptions;
524
  explain(internal, assumptions);
525
  std::set<TNode> assumptionSet;
526
  assumptionSet.insert(assumptions.begin(), assumptions.end());
527
528
  enqueueIntoNB(assumptionSet, out);
529
}
530
531
66274
void ArithCongruenceManager::addWatchedPair(ArithVar s, TNode x, TNode y){
532
66274
  Assert(!isWatchedVariable(s));
533
534
132548
  Debug("arith::congruenceManager")
535
66274
    << "addWatchedPair(" << s << ", " << x << ", " << y << ")" << std::endl;
536
537
538
66274
  ++(d_statistics.d_watchedVariables);
539
540
66274
  d_watchedVariables.add(s);
541
542
132548
  Node eq = x.eqNode(y);
543
66274
  d_watchedEqualities.set(s, eq);
544
66274
}
545
546
2495004
void ArithCongruenceManager::assertLitToEqualityEngine(
547
    Node lit, TNode reason, std::shared_ptr<ProofNode> pf)
548
{
549
2495004
  bool isEquality = lit.getKind() != Kind::NOT;
550
4990008
  Node eq = isEquality ? lit : lit[0];
551
2495004
  Assert(eq.getKind() == Kind::EQUAL);
552
553
4990008
  Trace("arith-ee") << "Assert to Eq " << lit << ", reason " << reason
554
2495004
                    << std::endl;
555
2495004
  if (isProofEnabled())
556
  {
557
261042
    if (CDProof::isSame(lit, reason))
558
    {
559
153425
      Trace("arith-pfee") << "Asserting only, b/c implied by symm" << std::endl;
560
      // The equality engine doesn't ref-count for us...
561
153425
      d_keepAlive.push_back(eq);
562
153425
      d_keepAlive.push_back(reason);
563
153425
      d_ee->assertEquality(eq, isEquality, reason);
564
    }
565
107617
    else if (hasProofFor(lit))
566
    {
567
1474
      Trace("arith-pfee") << "Skipping b/c already done" << std::endl;
568
    }
569
    else
570
    {
571
106143
      setProofFor(lit, pf);
572
106143
      Trace("arith-pfee") << "Actually asserting" << std::endl;
573
106143
      if (Debug.isOn("arith-pfee"))
574
      {
575
        Trace("arith-pfee") << "Proof: ";
576
        pf->printDebug(Trace("arith-pfee"));
577
        Trace("arith-pfee") << std::endl;
578
      }
579
      // The proof equality engine *does* ref-count for us...
580
106143
      d_pfee->assertFact(lit, reason, d_pfGenEe.get());
581
    }
582
  }
583
  else
584
  {
585
    // The equality engine doesn't ref-count for us...
586
2233962
    d_keepAlive.push_back(eq);
587
2233962
    d_keepAlive.push_back(reason);
588
2233962
    d_ee->assertEquality(eq, isEquality, reason);
589
  }
590
2495004
}
591
592
1334538
void ArithCongruenceManager::assertionToEqualityEngine(
593
    bool isEquality, ArithVar s, TNode reason, std::shared_ptr<ProofNode> pf)
594
{
595
1334538
  Assert(isWatchedVariable(s));
596
597
2669076
  TNode eq = d_watchedEqualities[s];
598
1334538
  Assert(eq.getKind() == kind::EQUAL);
599
600
2669076
  Node lit = isEquality ? Node(eq) : eq.notNode();
601
2669076
  Trace("arith-ee") << "Assert to Eq " << eq << ", pol " << isEquality
602
1334538
                    << ", reason " << reason << std::endl;
603
1334538
  assertLitToEqualityEngine(lit, reason, pf);
604
1334538
}
605
606
213760
bool ArithCongruenceManager::hasProofFor(TNode f) const
607
{
608
213760
  Assert(isProofEnabled());
609
213760
  if (d_pfGenEe->hasProofFor(f))
610
  {
611
1474
    return true;
612
  }
613
424572
  Node sym = CDProof::getSymmFact(f);
614
212286
  Assert(!sym.isNull());
615
212286
  return d_pfGenEe->hasProofFor(sym);
616
}
617
618
106143
void ArithCongruenceManager::setProofFor(TNode f,
619
                                         std::shared_ptr<ProofNode> pf) const
620
{
621
106143
  Assert(!hasProofFor(f));
622
106143
  d_pfGenEe->mkTrustNode(f, pf);
623
212286
  Node symF = CDProof::getSymmFact(f);
624
212286
  auto symPf = d_pnm->mkNode(PfRule::SYMM, {pf}, {});
625
106143
  d_pfGenEe->mkTrustNode(symF, symPf);
626
106143
}
627
628
959262
void ArithCongruenceManager::equalsConstant(ConstraintCP c){
629
959262
  Assert(c->isEquality());
630
631
959262
  ++(d_statistics.d_equalsConstantCalls);
632
959262
  Debug("equalsConstant") << "equals constant " << c << std::endl;
633
634
959262
  ArithVar x = c->getVariable();
635
1918524
  Node xAsNode = d_avariables.asNode(x);
636
1918524
  Node asRational = mkRationalNode(c->getValue().getNoninfinitesimalPart());
637
638
  // No guarentee this is in normal form!
639
  // Note though, that it happens to be in proof normal form!
640
1918524
  Node eq = xAsNode.eqNode(asRational);
641
959262
  d_keepAlive.push_back(eq);
642
643
1918524
  NodeBuilder nb(Kind::AND);
644
1918524
  auto pf = c->externalExplainByAssertions(nb);
645
1918524
  Node reason = safeConstructNary(nb);
646
959262
  d_keepAlive.push_back(reason);
647
648
959262
  Trace("arith-ee") << "Assert equalsConstant " << eq << ", reason " << reason << std::endl;
649
959262
  assertLitToEqualityEngine(eq, reason, pf);
650
959262
}
651
652
201204
void ArithCongruenceManager::equalsConstant(ConstraintCP lb, ConstraintCP ub){
653
201204
  Assert(lb->isLowerBound());
654
201204
  Assert(ub->isUpperBound());
655
201204
  Assert(lb->getVariable() == ub->getVariable());
656
657
201204
  ++(d_statistics.d_equalsConstantCalls);
658
402408
  Debug("equalsConstant") << "equals constant " << lb << std::endl
659
201204
                          << ub << std::endl;
660
661
201204
  ArithVar x = lb->getVariable();
662
402408
  NodeBuilder nb(Kind::AND);
663
402408
  auto pfLb = lb->externalExplainByAssertions(nb);
664
402408
  auto pfUb = ub->externalExplainByAssertions(nb);
665
402408
  Node reason = safeConstructNary(nb);
666
667
402408
  Node xAsNode = d_avariables.asNode(x);
668
402408
  Node asRational = mkRationalNode(lb->getValue().getNoninfinitesimalPart());
669
670
  // No guarentee this is in normal form!
671
  // Note though, that it happens to be in proof normal form!
672
402408
  Node eq = xAsNode.eqNode(asRational);
673
402408
  std::shared_ptr<ProofNode> pf;
674
201204
  if (isProofEnabled())
675
  {
676
20255
    pf = d_pnm->mkNode(PfRule::ARITH_TRICHOTOMY, {pfLb, pfUb}, {eq});
677
  }
678
201204
  d_keepAlive.push_back(eq);
679
201204
  d_keepAlive.push_back(reason);
680
681
201204
  Trace("arith-ee") << "Assert equalsConstant2 " << eq << ", reason " << reason << std::endl;
682
683
201204
  assertLitToEqualityEngine(eq, reason, pf);
684
201204
}
685
686
4299387
bool ArithCongruenceManager::isProofEnabled() const { return d_pnm != nullptr; }
687
688
784
std::vector<Node> andComponents(TNode an)
689
{
690
784
  auto nm = NodeManager::currentNM();
691
784
  if (an == nm->mkConst(true))
692
  {
693
    return {};
694
  }
695
784
  else if (an.getKind() != Kind::AND)
696
  {
697
    return {an};
698
  }
699
1568
  std::vector<Node> a{};
700
784
  a.reserve(an.getNumChildren());
701
784
  a.insert(a.end(), an.begin(), an.end());
702
784
  return a;
703
}
704
705
}  // namespace arith
706
}  // namespace theory
707
31125
}  // namespace cvc5