GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arith/congruence_manager.cpp Lines: 355 401 88.5 %
Date: 2021-05-21 Branches: 715 1884 38.0 %

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