GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arith/theory_arith.cpp Lines: 163 181 90.1 %
Date: 2021-11-05 Branches: 208 500 41.6 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Tim King, Alex Ozdemir
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
 * Arithmetic theory.
14
 */
15
16
#include "theory/arith/theory_arith.h"
17
18
#include "options/smt_options.h"
19
#include "proof/proof_checker.h"
20
#include "proof/proof_rule.h"
21
#include "smt/smt_statistics_registry.h"
22
#include "theory/arith/arith_rewriter.h"
23
#include "theory/arith/equality_solver.h"
24
#include "theory/arith/infer_bounds.h"
25
#include "theory/arith/nl/nonlinear_extension.h"
26
#include "theory/arith/theory_arith_private.h"
27
#include "theory/ext_theory.h"
28
#include "theory/rewriter.h"
29
#include "theory/theory_model.h"
30
31
using namespace std;
32
using namespace cvc5::kind;
33
34
namespace cvc5 {
35
namespace theory {
36
namespace arith {
37
38
15271
TheoryArith::TheoryArith(Env& env, OutputChannel& out, Valuation valuation)
39
    : Theory(THEORY_ARITH, env, out, valuation),
40
      d_ppRewriteTimer(
41
30542
          statisticsRegistry().registerTimer("theory::arith::ppRewriteTimer")),
42
      d_astate(env, valuation),
43
      d_im(env, *this, d_astate),
44
      d_ppre(d_env),
45
      d_bab(env, d_astate, d_im, d_ppre, d_pnm),
46
      d_eqSolver(nullptr),
47
15271
      d_internal(new TheoryArithPrivate(*this, env, d_bab)),
48
      d_nonlinearExtension(nullptr),
49
      d_opElim(d_env),
50
      d_arithPreproc(env, d_astate, d_im, d_pnm, d_opElim),
51
61084
      d_rewriter(d_opElim)
52
{
53
  // currently a cyclic dependency to TheoryArithPrivate
54
15271
  d_astate.setParent(d_internal);
55
  // indicate we are using the theory state object and inference manager
56
15271
  d_theoryState = &d_astate;
57
15271
  d_inferManager = &d_im;
58
59
15271
  if (options().arith.arithEqSolver)
60
  {
61
41
    d_eqSolver.reset(new EqualitySolver(env, d_astate, d_im));
62
  }
63
15271
}
64
65
45798
TheoryArith::~TheoryArith(){
66
15266
  delete d_internal;
67
30532
}
68
69
15271
TheoryRewriter* TheoryArith::getTheoryRewriter() { return &d_rewriter; }
70
71
7987
ProofRuleChecker* TheoryArith::getProofChecker()
72
{
73
7987
  return d_internal->getProofChecker();
74
}
75
76
15271
bool TheoryArith::needsEqualityEngine(EeSetupInfo& esi)
77
{
78
  // if the equality solver is enabled, then it is responsible for setting
79
  // up the equality engine
80
15271
  if (d_eqSolver != nullptr)
81
  {
82
41
    return d_eqSolver->needsEqualityEngine(esi);
83
  }
84
  // otherwise, the linear arithmetic solver is responsible for setting up
85
  // the equality engine
86
15230
  return d_internal->needsEqualityEngine(esi);
87
}
88
15271
void TheoryArith::finishInit()
89
{
90
15271
  const LogicInfo& logic = logicInfo();
91
15271
  if (logic.isTheoryEnabled(THEORY_ARITH) && logic.areTranscendentalsUsed())
92
  {
93
    // witness is used to eliminate square root
94
8550
    d_valuation.setUnevaluatedKind(kind::WITNESS);
95
    // we only need to add the operators that are not syntax sugar
96
8550
    d_valuation.setUnevaluatedKind(kind::EXPONENTIAL);
97
8550
    d_valuation.setUnevaluatedKind(kind::SINE);
98
8550
    d_valuation.setUnevaluatedKind(kind::PI);
99
  }
100
  // only need to create nonlinear extension if non-linear logic
101
15271
  if (logic.isTheoryEnabled(THEORY_ARITH) && !logic.isLinear())
102
  {
103
19400
    d_nonlinearExtension.reset(
104
9700
        new nl::NonlinearExtension(d_env, *this, d_astate));
105
  }
106
15271
  if (d_eqSolver != nullptr)
107
  {
108
41
    d_eqSolver->finishInit();
109
  }
110
  // finish initialize in the old linear solver
111
15271
  d_internal->finishInit();
112
15271
}
113
114
903422
void TheoryArith::preRegisterTerm(TNode n)
115
{
116
903422
  if (d_nonlinearExtension != nullptr)
117
  {
118
440640
    d_nonlinearExtension->preRegisterTerm(n);
119
  }
120
903423
  d_internal->preRegisterTerm(n);
121
903421
}
122
123
920199
void TheoryArith::notifySharedTerm(TNode n) { d_internal->notifySharedTerm(n); }
124
125
854272
TrustNode TheoryArith::ppRewrite(TNode atom, std::vector<SkolemLemma>& lems)
126
{
127
1708544
  CodeTimer timer(d_ppRewriteTimer, /* allow_reentrant = */ true);
128
854272
  Debug("arith::preprocess") << "arith::preprocess() : " << atom << endl;
129
130
854272
  if (atom.getKind() == kind::EQUAL)
131
  {
132
28857
    return d_ppre.ppRewriteEq(atom);
133
  }
134
825415
  Assert(Theory::theoryOf(atom) == THEORY_ARITH);
135
  // Eliminate operators. Notice we must do this here since other
136
  // theories may generate lemmas that involve non-standard operators. For
137
  // example, quantifier instantiation may use TO_INTEGER terms; SyGuS may
138
  // introduce non-standard arithmetic terms appearing in grammars.
139
  // call eliminate operators. In contrast to expandDefinitions, we eliminate
140
  // *all* extended arithmetic operators here, including total ones.
141
825421
  return d_arithPreproc.eliminate(atom, lems, false);
142
}
143
144
14973
Theory::PPAssertStatus TheoryArith::ppAssert(
145
    TrustNode tin, TrustSubstitutionMap& outSubstitutions)
146
{
147
14973
  return d_internal->ppAssert(tin, outSubstitutions);
148
}
149
150
250915
void TheoryArith::ppStaticLearn(TNode n, NodeBuilder& learned)
151
{
152
250915
  d_internal->ppStaticLearn(n, learned);
153
250915
}
154
155
2497352
bool TheoryArith::preCheck(Effort level)
156
{
157
2497352
  Trace("arith-check") << "TheoryArith::preCheck " << level << std::endl;
158
2497352
  return d_internal->preCheck(level);
159
}
160
161
2497352
void TheoryArith::postCheck(Effort level)
162
{
163
2497352
  d_im.reset();
164
2497352
  Trace("arith-check") << "TheoryArith::postCheck " << level << std::endl;
165
  // check with the non-linear solver at last call
166
2497352
  if (level == Theory::EFFORT_LAST_CALL)
167
  {
168
3498
    if (d_nonlinearExtension != nullptr)
169
    {
170
      // If we computed lemmas in the last FULL_EFFORT check, send them now.
171
3498
      if (d_im.hasPendingLemma())
172
      {
173
3161
        d_im.doPendingFacts();
174
3161
        d_im.doPendingLemmas();
175
3161
        d_im.doPendingPhaseRequirements();
176
3161
        return;
177
      }
178
337
      d_nonlinearExtension->finalizeModel(getValuation().getModel());
179
    }
180
337
    return;
181
  }
182
  // otherwise, check with the linear solver
183
2493854
  if (d_internal->postCheck(level))
184
  {
185
    // linear solver emitted a conflict or lemma, return
186
67419
    return;
187
  }
188
2426435
  if (d_im.hasSent())
189
  {
190
    return;
191
  }
192
193
2426435
  if (Theory::fullEffort(level))
194
  {
195
74220
    d_arithModelCache.clear();
196
148440
    std::set<Node> termSet;
197
74220
    if (d_nonlinearExtension != nullptr)
198
    {
199
41496
      updateModelCache(termSet);
200
41496
      d_nonlinearExtension->checkFullEffort(d_arithModelCache, termSet);
201
    }
202
32724
    else if (d_internal->foundNonlinear())
203
    {
204
      // set incomplete
205
      d_im.setIncomplete(IncompleteId::ARITH_NL_DISABLED);
206
    }
207
    // If we won't be doing a last call effort check (which implies that
208
    // models will be computed), we must sanity check the integer model
209
    // from the linear solver now. We also must update the model cache
210
    // if we did not do so above.
211
74220
    if (d_nonlinearExtension == nullptr)
212
    {
213
32724
      updateModelCache(termSet);
214
    }
215
74220
    sanityCheckIntegerModel();
216
  }
217
}
218
219
7403893
bool TheoryArith::preNotifyFact(
220
    TNode atom, bool pol, TNode fact, bool isPrereg, bool isInternal)
221
{
222
14807786
  Trace("arith-check") << "TheoryArith::preNotifyFact: " << fact
223
7403893
                       << ", isPrereg=" << isPrereg
224
7403893
                       << ", isInternal=" << isInternal << std::endl;
225
  // We do not assert to the equality engine of arithmetic in the standard way,
226
  // hence we return "true" to indicate we are finished with this fact.
227
7403893
  bool ret = true;
228
7403893
  if (d_eqSolver != nullptr)
229
  {
230
    // the equality solver may indicate ret = false, after which the assertion
231
    // will be asserted to the equality engine in the default way.
232
2425
    ret = d_eqSolver->preNotifyFact(atom, pol, fact, isPrereg, isInternal);
233
  }
234
  // we also always also notify the internal solver
235
7403893
  d_internal->preNotifyFact(atom, pol, fact);
236
7403893
  return ret;
237
}
238
239
27334
bool TheoryArith::needsCheckLastEffort() {
240
27334
  if (d_nonlinearExtension != nullptr)
241
  {
242
14023
    return d_nonlinearExtension->hasNlTerms();
243
  }
244
13311
  return false;
245
}
246
247
28101
TrustNode TheoryArith::explain(TNode n)
248
{
249
28101
  if (d_eqSolver != nullptr)
250
  {
251
    // if the equality solver has an explanation for it, use it
252
54
    TrustNode texp = d_eqSolver->explain(n);
253
27
    if (!texp.isNull())
254
    {
255
      return texp;
256
    }
257
  }
258
28101
  return d_internal->explain(n);
259
}
260
261
3870181
void TheoryArith::propagate(Effort e) {
262
3870181
  d_internal->propagate(e);
263
3870181
}
264
265
22882
bool TheoryArith::collectModelInfo(TheoryModel* m,
266
                                   const std::set<Node>& termSet)
267
{
268
  // this overrides behavior to not assert equality engine
269
22882
  return collectModelValues(m, termSet);
270
}
271
272
22882
bool TheoryArith::collectModelValues(TheoryModel* m,
273
                                     const std::set<Node>& termSet)
274
{
275
22882
  if (Trace.isOn("arith::model"))
276
  {
277
    Trace("arith::model") << "arithmetic model after pruning" << std::endl;
278
    for (const auto& p : d_arithModelCache)
279
    {
280
      Trace("arith::model") << "\t" << p.first << " -> " << p.second << std::endl;
281
    }
282
  }
283
284
22882
  updateModelCache(termSet);
285
286
  // We are now ready to assert the model.
287
463738
  for (const std::pair<const Node, Node>& p : d_arithModelCache)
288
  {
289
440856
    if (termSet.find(p.first) == termSet.end())
290
    {
291
4
      continue;
292
    }
293
    // maps to constant of comparable type
294
440852
    Assert(p.first.getType().isComparableTo(p.second.getType()));
295
440852
    if (m->assertEquality(p.first, p.second, true))
296
    {
297
440852
      continue;
298
    }
299
    Assert(false) << "A model equality could not be asserted: " << p.first
300
                        << " == " << p.second << std::endl;
301
    // If we failed to assert an equality, it is likely due to theory
302
    // combination, namely the repaired model for non-linear changed
303
    // an equality status that was agreed upon by both (linear) arithmetic
304
    // and another theory. In this case, we must add a lemma, or otherwise
305
    // we would terminate with an invalid model. Thus, we add a splitting
306
    // lemma of the form ( x = v V x != v ) where v is the model value
307
    // assigned by the non-linear solver to x.
308
    if (d_nonlinearExtension != nullptr)
309
    {
310
      Node eq = p.first.eqNode(p.second);
311
      Node lem = NodeManager::currentNM()->mkNode(kind::OR, eq, eq.negate());
312
      bool added = d_im.lemma(lem, InferenceId::ARITH_SPLIT_FOR_NL_MODEL);
313
      AlwaysAssert(added) << "The lemma was already in cache. Probably there is something wrong with theory combination...";
314
    }
315
    return false;
316
  }
317
22882
  return true;
318
}
319
320
1957
void TheoryArith::notifyRestart(){
321
1957
  d_internal->notifyRestart();
322
1957
}
323
324
20558
void TheoryArith::presolve(){
325
20558
  d_internal->presolve();
326
20558
}
327
328
816122
EqualityStatus TheoryArith::getEqualityStatus(TNode a, TNode b) {
329
816122
  Debug("arith") << "TheoryArith::getEqualityStatus(" << a << ", " << b << ")" << std::endl;
330
816122
  if (d_arithModelCache.empty())
331
  {
332
    return d_internal->getEqualityStatus(a,b);
333
  }
334
1632244
  Node aval = Rewriter::rewrite(a.substitute(d_arithModelCache.begin(), d_arithModelCache.end()));
335
1632244
  Node bval = Rewriter::rewrite(b.substitute(d_arithModelCache.begin(), d_arithModelCache.end()));
336
816122
  if (aval == bval)
337
  {
338
76895
    return EQUALITY_TRUE_IN_MODEL;
339
  }
340
739227
  return EQUALITY_FALSE_IN_MODEL;
341
}
342
343
2957
Node TheoryArith::getModelValue(TNode var) {
344
2957
  return d_internal->getModelValue( var );
345
}
346
347
8478
std::pair<bool, Node> TheoryArith::entailmentCheck(TNode lit)
348
{
349
16956
  ArithEntailmentCheckParameters def;
350
8478
  def.addLookupRowSumAlgorithms();
351
16956
  ArithEntailmentCheckSideEffects ase;
352
8478
  std::pair<bool, Node> res = d_internal->entailmentCheck(lit, def, ase);
353
16956
  return res;
354
}
355
eq::ProofEqEngine* TheoryArith::getProofEqEngine()
356
{
357
  return d_im.getProofEqEngine();
358
}
359
360
74220
void TheoryArith::updateModelCache(std::set<Node>& termSet)
361
{
362
74220
  if (d_arithModelCache.empty())
363
  {
364
74220
    collectAssertedTerms(termSet);
365
74220
    d_internal->collectModelValues(termSet, d_arithModelCache);
366
  }
367
74220
}
368
22882
void TheoryArith::updateModelCache(const std::set<Node>& termSet)
369
{
370
22882
  if (d_arithModelCache.empty())
371
  {
372
5707
    d_internal->collectModelValues(termSet, d_arithModelCache);
373
  }
374
22882
}
375
74220
bool TheoryArith::sanityCheckIntegerModel()
376
{
377
378
    // Double check that the model from the linear solver respects integer types,
379
    // if it does not, add a branch and bound lemma. This typically should never
380
    // be necessary, but is needed in rare cases.
381
74220
    bool addedLemma = false;
382
74220
    bool badAssignment = false;
383
74220
    Trace("arith-check") << "model:" << std::endl;
384
2270461
    for (const auto& p : d_arithModelCache)
385
    {
386
2196241
      Trace("arith-check") << p.first << " -> " << p.second << std::endl;
387
2196241
      if (p.first.getType().isInteger() && !p.second.getType().isInteger())
388
      {
389
4
        warning() << "TheoryArithPrivate generated a bad model value for "
390
4
                     "integer variable "
391
4
                  << p.first << " : " << p.second << std::endl;
392
        // must branch and bound
393
        TrustNode lem =
394
8
            d_bab.branchIntegerVariable(p.first, p.second.getConst<Rational>());
395
4
        if (d_im.trustedLemma(lem, InferenceId::ARITH_BB_LEMMA))
396
        {
397
4
          addedLemma = true;
398
        }
399
4
        badAssignment = true;
400
      }
401
    }
402
74220
    if (addedLemma)
403
    {
404
      // we had to add a branch and bound lemma since the linear solver assigned
405
      // a non-integer value to an integer variable.
406
4
      return true;
407
    }
408
    // this would imply that linear arithmetic's model failed to satisfy a branch
409
    // and bound lemma
410
74216
    AlwaysAssert(!badAssignment)
411
        << "Bad assignment from TheoryArithPrivate::collectModelValues, and no "
412
          "branching lemma was sent";
413
74216
    return false;
414
}
415
416
}  // namespace arith
417
}  // namespace theory
418
31125
}  // namespace cvc5