GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arith/theory_arith.cpp Lines: 152 178 85.4 %
Date: 2021-09-07 Branches: 194 516 37.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
9926
TheoryArith::TheoryArith(Env& env, OutputChannel& out, Valuation valuation)
39
    : Theory(THEORY_ARITH, env, out, valuation),
40
9926
      d_ppRewriteTimer(smtStatisticsRegistry().registerTimer(
41
19852
          "theory::arith::ppRewriteTimer")),
42
      d_astate(env, valuation),
43
      d_im(*this, d_astate, d_pnm),
44
      d_ppre(getSatContext(), d_pnm),
45
      d_bab(d_astate, d_im, d_ppre, d_pnm),
46
      d_eqSolver(nullptr),
47
9926
      d_internal(new TheoryArithPrivate(*this, env, d_bab)),
48
      d_nonlinearExtension(nullptr),
49
      d_opElim(d_pnm, logicInfo()),
50
      d_arithPreproc(d_astate, d_im, d_pnm, d_opElim),
51
39704
      d_rewriter(d_opElim)
52
{
53
  // currently a cyclic dependency to TheoryArithPrivate
54
9926
  d_astate.setParent(d_internal);
55
  // indicate we are using the theory state object and inference manager
56
9926
  d_theoryState = &d_astate;
57
9926
  d_inferManager = &d_im;
58
59
9926
  if (options().arith.arithEqSolver)
60
  {
61
39
    d_eqSolver.reset(new EqualitySolver(d_astate, d_im));
62
  }
63
9926
}
64
65
29769
TheoryArith::~TheoryArith(){
66
9923
  delete d_internal;
67
19846
}
68
69
9926
TheoryRewriter* TheoryArith::getTheoryRewriter() { return &d_rewriter; }
70
71
3786
ProofRuleChecker* TheoryArith::getProofChecker()
72
{
73
3786
  return d_internal->getProofChecker();
74
}
75
76
9926
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
9926
  if (d_eqSolver != nullptr)
81
  {
82
39
    return d_eqSolver->needsEqualityEngine(esi);
83
  }
84
  // otherwise, the linear arithmetic solver is responsible for setting up
85
  // the equality engine
86
9887
  return d_internal->needsEqualityEngine(esi);
87
}
88
9926
void TheoryArith::finishInit()
89
{
90
9926
  const LogicInfo& logic = logicInfo();
91
9926
  if (logic.isTheoryEnabled(THEORY_ARITH) && logic.areTranscendentalsUsed())
92
  {
93
    // witness is used to eliminate square root
94
4252
    d_valuation.setUnevaluatedKind(kind::WITNESS);
95
    // we only need to add the operators that are not syntax sugar
96
4252
    d_valuation.setUnevaluatedKind(kind::EXPONENTIAL);
97
4252
    d_valuation.setUnevaluatedKind(kind::SINE);
98
4252
    d_valuation.setUnevaluatedKind(kind::PI);
99
  }
100
  // only need to create nonlinear extension if non-linear logic
101
9926
  if (logic.isTheoryEnabled(THEORY_ARITH) && !logic.isLinear())
102
  {
103
5208
    d_nonlinearExtension.reset(new nl::NonlinearExtension(*this, d_astate));
104
  }
105
9926
  if (d_eqSolver != nullptr)
106
  {
107
39
    d_eqSolver->finishInit();
108
  }
109
  // finish initialize in the old linear solver
110
9926
  d_internal->finishInit();
111
9926
}
112
113
816009
void TheoryArith::preRegisterTerm(TNode n)
114
{
115
816009
  if (d_nonlinearExtension != nullptr)
116
  {
117
404678
    d_nonlinearExtension->preRegisterTerm(n);
118
  }
119
816010
  d_internal->preRegisterTerm(n);
120
816008
}
121
122
696570
void TheoryArith::notifySharedTerm(TNode n) { d_internal->notifySharedTerm(n); }
123
124
779487
TrustNode TheoryArith::ppRewrite(TNode atom, std::vector<SkolemLemma>& lems)
125
{
126
1558974
  CodeTimer timer(d_ppRewriteTimer, /* allow_reentrant = */ true);
127
779487
  Debug("arith::preprocess") << "arith::preprocess() : " << atom << endl;
128
129
779487
  if (atom.getKind() == kind::EQUAL)
130
  {
131
27269
    return d_ppre.ppRewriteEq(atom);
132
  }
133
752218
  Assert(Theory::theoryOf(atom) == THEORY_ARITH);
134
  // Eliminate operators. Notice we must do this here since other
135
  // theories may generate lemmas that involve non-standard operators. For
136
  // example, quantifier instantiation may use TO_INTEGER terms; SyGuS may
137
  // introduce non-standard arithmetic terms appearing in grammars.
138
  // call eliminate operators. In contrast to expandDefinitions, we eliminate
139
  // *all* extended arithmetic operators here, including total ones.
140
752224
  return d_arithPreproc.eliminate(atom, lems, false);
141
}
142
143
11654
Theory::PPAssertStatus TheoryArith::ppAssert(
144
    TrustNode tin, TrustSubstitutionMap& outSubstitutions)
145
{
146
11654
  return d_internal->ppAssert(tin, outSubstitutions);
147
}
148
149
105454
void TheoryArith::ppStaticLearn(TNode n, NodeBuilder& learned)
150
{
151
105454
  d_internal->ppStaticLearn(n, learned);
152
105454
}
153
154
1786370
bool TheoryArith::preCheck(Effort level)
155
{
156
1786370
  Trace("arith-check") << "TheoryArith::preCheck " << level << std::endl;
157
1786370
  return d_internal->preCheck(level);
158
}
159
160
1786370
void TheoryArith::postCheck(Effort level)
161
{
162
1786370
  d_im.reset();
163
1786370
  Trace("arith-check") << "TheoryArith::postCheck " << level << std::endl;
164
  // check with the non-linear solver at last call
165
1786370
  if (level == Theory::EFFORT_LAST_CALL)
166
  {
167
3263
    if (d_nonlinearExtension != nullptr)
168
    {
169
3263
      d_nonlinearExtension->check(level);
170
    }
171
3263
    return;
172
  }
173
  // otherwise, check with the linear solver
174
1783107
  if (d_internal->postCheck(level))
175
  {
176
    // linear solver emitted a conflict or lemma, return
177
59838
    return;
178
  }
179
1723269
  if (d_im.hasSent())
180
  {
181
    return;
182
  }
183
184
1723269
  if (Theory::fullEffort(level))
185
  {
186
64588
    d_arithModelCache.clear();
187
64588
    if (d_nonlinearExtension != nullptr)
188
    {
189
69488
      std::set<Node> termSet;
190
34744
      updateModelCache(termSet);
191
34744
      d_nonlinearExtension->check(level);
192
34744
      d_nonlinearExtension->interceptModel(d_arithModelCache, termSet);
193
    }
194
29844
    else if (d_internal->foundNonlinear())
195
    {
196
      // set incomplete
197
      d_im.setIncomplete(IncompleteId::ARITH_NL_DISABLED);
198
    }
199
  }
200
}
201
202
5896760
bool TheoryArith::preNotifyFact(
203
    TNode atom, bool pol, TNode fact, bool isPrereg, bool isInternal)
204
{
205
11793520
  Trace("arith-check") << "TheoryArith::preNotifyFact: " << fact
206
5896760
                       << ", isPrereg=" << isPrereg
207
5896760
                       << ", isInternal=" << isInternal << std::endl;
208
  // We do not assert to the equality engine of arithmetic in the standard way,
209
  // hence we return "true" to indicate we are finished with this fact.
210
5896760
  bool ret = true;
211
5896760
  if (d_eqSolver != nullptr)
212
  {
213
    // the equality solver may indicate ret = false, after which the assertion
214
    // will be asserted to the equality engine in the default way.
215
2527
    ret = d_eqSolver->preNotifyFact(atom, pol, fact, isPrereg, isInternal);
216
  }
217
  // we also always also notify the internal solver
218
5896760
  d_internal->preNotifyFact(atom, pol, fact);
219
5896760
  return ret;
220
}
221
222
18972
bool TheoryArith::needsCheckLastEffort() {
223
18972
  if (d_nonlinearExtension != nullptr)
224
  {
225
10255
    return d_nonlinearExtension->needsCheckLastEffort();
226
  }
227
8717
  return false;
228
}
229
230
30413
TrustNode TheoryArith::explain(TNode n)
231
{
232
30413
  if (d_eqSolver != nullptr)
233
  {
234
    // if the equality solver has an explanation for it, use it
235
54
    TrustNode texp = d_eqSolver->explain(n);
236
27
    if (!texp.isNull())
237
    {
238
      return texp;
239
    }
240
  }
241
30413
  return d_internal->explain(n);
242
}
243
244
2779544
void TheoryArith::propagate(Effort e) {
245
2779544
  d_internal->propagate(e);
246
2779544
}
247
248
14730
bool TheoryArith::collectModelInfo(TheoryModel* m,
249
                                   const std::set<Node>& termSet)
250
{
251
  // this overrides behavior to not assert equality engine
252
14730
  return collectModelValues(m, termSet);
253
}
254
255
14730
bool TheoryArith::collectModelValues(TheoryModel* m,
256
                                     const std::set<Node>& termSet)
257
{
258
14730
  if (Trace.isOn("arith::model"))
259
  {
260
    Trace("arith::model") << "arithmetic model after pruning" << std::endl;
261
    for (const auto& p : d_arithModelCache)
262
    {
263
      Trace("arith::model") << "\t" << p.first << " -> " << p.second << std::endl;
264
    }
265
  }
266
267
14730
  updateModelCache(termSet);
268
269
14730
  if (sanityCheckIntegerModel())
270
  {
271
    // We added a lemma
272
    return false;
273
  }
274
275
  // We are now ready to assert the model.
276
258102
  for (const std::pair<const Node, Node>& p : d_arithModelCache)
277
  {
278
243372
    if (termSet.find(p.first) == termSet.end())
279
    {
280
2
      continue;
281
    }
282
    // maps to constant of comparable type
283
243370
    Assert(p.first.getType().isComparableTo(p.second.getType()));
284
243370
    if (m->assertEquality(p.first, p.second, true))
285
    {
286
243370
      continue;
287
    }
288
    Assert(false) << "A model equality could not be asserted: " << p.first
289
                        << " == " << p.second << std::endl;
290
    // If we failed to assert an equality, it is likely due to theory
291
    // combination, namely the repaired model for non-linear changed
292
    // an equality status that was agreed upon by both (linear) arithmetic
293
    // and another theory. In this case, we must add a lemma, or otherwise
294
    // we would terminate with an invalid model. Thus, we add a splitting
295
    // lemma of the form ( x = v V x != v ) where v is the model value
296
    // assigned by the non-linear solver to x.
297
    if (d_nonlinearExtension != nullptr)
298
    {
299
      Node eq = p.first.eqNode(p.second);
300
      Node lem = NodeManager::currentNM()->mkNode(kind::OR, eq, eq.negate());
301
      bool added = d_im.lemma(lem, InferenceId::ARITH_SPLIT_FOR_NL_MODEL);
302
      AlwaysAssert(added) << "The lemma was already in cache. Probably there is something wrong with theory combination...";
303
    }
304
    return false;
305
  }
306
14730
  return true;
307
}
308
309
1679
void TheoryArith::notifyRestart(){
310
1679
  d_internal->notifyRestart();
311
1679
}
312
313
15250
void TheoryArith::presolve(){
314
15250
  d_internal->presolve();
315
15250
  if (d_nonlinearExtension != nullptr)
316
  {
317
6440
    d_nonlinearExtension->presolve();
318
  }
319
15250
}
320
321
944863
EqualityStatus TheoryArith::getEqualityStatus(TNode a, TNode b) {
322
944863
  Debug("arith") << "TheoryArith::getEqualityStatus(" << a << ", " << b << ")" << std::endl;
323
944863
  if (d_arithModelCache.empty())
324
  {
325
911019
    return d_internal->getEqualityStatus(a,b);
326
  }
327
67688
  Node aval = Rewriter::rewrite(a.substitute(d_arithModelCache.begin(), d_arithModelCache.end()));
328
67688
  Node bval = Rewriter::rewrite(b.substitute(d_arithModelCache.begin(), d_arithModelCache.end()));
329
33844
  if (aval == bval)
330
  {
331
11179
    return EQUALITY_TRUE_IN_MODEL;
332
  }
333
22665
  return EQUALITY_FALSE_IN_MODEL;
334
}
335
336
2981
Node TheoryArith::getModelValue(TNode var) {
337
2981
  return d_internal->getModelValue( var );
338
}
339
340
8517
std::pair<bool, Node> TheoryArith::entailmentCheck(TNode lit)
341
{
342
17034
  ArithEntailmentCheckParameters def;
343
8517
  def.addLookupRowSumAlgorithms();
344
17034
  ArithEntailmentCheckSideEffects ase;
345
8517
  std::pair<bool, Node> res = d_internal->entailmentCheck(lit, def, ase);
346
17034
  return res;
347
}
348
eq::ProofEqEngine* TheoryArith::getProofEqEngine()
349
{
350
  return d_im.getProofEqEngine();
351
}
352
353
34744
void TheoryArith::updateModelCache(std::set<Node>& termSet)
354
{
355
34744
  if (d_arithModelCache.empty())
356
  {
357
34744
    collectAssertedTerms(termSet);
358
34744
    d_internal->collectModelValues(termSet, d_arithModelCache);
359
  }
360
34744
}
361
14730
void TheoryArith::updateModelCache(const std::set<Node>& termSet)
362
{
363
14730
  if (d_arithModelCache.empty())
364
  {
365
8191
    d_internal->collectModelValues(termSet, d_arithModelCache);
366
  }
367
14730
}
368
14730
bool TheoryArith::sanityCheckIntegerModel()
369
{
370
371
    // Double check that the model from the linear solver respects integer types,
372
    // if it does not, add a branch and bound lemma. This typically should never
373
    // be necessary, but is needed in rare cases.
374
14730
    bool addedLemma = false;
375
14730
    bool badAssignment = false;
376
14730
    Trace("arith-check") << "model:" << std::endl;
377
258102
    for (const auto& p : d_arithModelCache)
378
    {
379
243372
      Trace("arith-check") << p.first << " -> " << p.second << std::endl;
380
243372
      if (p.first.getType().isInteger() && !p.second.getType().isInteger())
381
      {
382
        Assert(false) << "TheoryArithPrivate generated a bad model value for "
383
                        "integer variable "
384
                      << p.first << " : " << p.second;
385
        // must branch and bound
386
        TrustNode lem =
387
            d_bab.branchIntegerVariable(p.first, p.second.getConst<Rational>());
388
        if (d_im.trustedLemma(lem, InferenceId::ARITH_BB_LEMMA))
389
        {
390
          addedLemma = true;
391
        }
392
        badAssignment = true;
393
      }
394
    }
395
14730
    if (addedLemma)
396
    {
397
      // we had to add a branch and bound lemma since the linear solver assigned
398
      // a non-integer value to an integer variable.
399
      return true;
400
    }
401
    // this would imply that linear arithmetic's model failed to satisfy a branch
402
    // and bound lemma
403
14730
    AlwaysAssert(!badAssignment)
404
        << "Bad assignment from TheoryArithPrivate::collectModelValues, and no "
405
          "branching lemma was sent";
406
14730
    return false;
407
}
408
409
}  // namespace arith
410
}  // namespace theory
411
29502
}  // namespace cvc5