GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arith/theory_arith.cpp Lines: 134 147 91.2 %
Date: 2021-08-01 Branches: 165 386 42.7 %

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
9838
TheoryArith::TheoryArith(context::Context* c,
39
                         context::UserContext* u,
40
                         OutputChannel& out,
41
                         Valuation valuation,
42
                         const LogicInfo& logicInfo,
43
9838
                         ProofNodeManager* pnm)
44
    : Theory(THEORY_ARITH, c, u, out, valuation, logicInfo, pnm),
45
9838
      d_ppRewriteTimer(smtStatisticsRegistry().registerTimer(
46
19676
          "theory::arith::ppRewriteTimer")),
47
      d_astate(c, u, valuation),
48
      d_im(*this, d_astate, pnm),
49
      d_ppre(c, pnm),
50
      d_bab(d_astate, d_im, d_ppre, pnm),
51
      d_eqSolver(nullptr),
52
9838
      d_internal(new TheoryArithPrivate(*this, c, u, d_bab, pnm)),
53
      d_nonlinearExtension(nullptr),
54
      d_opElim(pnm, logicInfo),
55
      d_arithPreproc(d_astate, d_im, pnm, d_opElim),
56
39352
      d_rewriter(d_opElim)
57
{
58
  // currently a cyclic dependency to TheoryArithPrivate
59
9838
  d_astate.setParent(d_internal);
60
  // indicate we are using the theory state object and inference manager
61
9838
  d_theoryState = &d_astate;
62
9838
  d_inferManager = &d_im;
63
64
9838
  if (options::arithEqSolver())
65
  {
66
39
    d_eqSolver.reset(new EqualitySolver(d_astate, d_im));
67
  }
68
9838
}
69
70
29514
TheoryArith::~TheoryArith(){
71
9838
  delete d_internal;
72
19676
}
73
74
9838
TheoryRewriter* TheoryArith::getTheoryRewriter() { return &d_rewriter; }
75
76
3756
ProofRuleChecker* TheoryArith::getProofChecker()
77
{
78
3756
  return d_internal->getProofChecker();
79
}
80
81
9838
bool TheoryArith::needsEqualityEngine(EeSetupInfo& esi)
82
{
83
  // if the equality solver is enabled, then it is responsible for setting
84
  // up the equality engine
85
9838
  if (d_eqSolver != nullptr)
86
  {
87
39
    return d_eqSolver->needsEqualityEngine(esi);
88
  }
89
  // otherwise, the linear arithmetic solver is responsible for setting up
90
  // the equality engine
91
9799
  return d_internal->needsEqualityEngine(esi);
92
}
93
9838
void TheoryArith::finishInit()
94
{
95
19676
  if (getLogicInfo().isTheoryEnabled(THEORY_ARITH)
96
9838
      && getLogicInfo().areTranscendentalsUsed())
97
  {
98
    // witness is used to eliminate square root
99
4180
    d_valuation.setUnevaluatedKind(kind::WITNESS);
100
    // we only need to add the operators that are not syntax sugar
101
4180
    d_valuation.setUnevaluatedKind(kind::EXPONENTIAL);
102
4180
    d_valuation.setUnevaluatedKind(kind::SINE);
103
4180
    d_valuation.setUnevaluatedKind(kind::PI);
104
  }
105
  // only need to create nonlinear extension if non-linear logic
106
9838
  const LogicInfo& logicInfo = getLogicInfo();
107
9838
  if (logicInfo.isTheoryEnabled(THEORY_ARITH) && !logicInfo.isLinear())
108
  {
109
10268
    d_nonlinearExtension.reset(
110
5134
        new nl::NonlinearExtension(*this, d_astate, d_equalityEngine, d_pnm));
111
  }
112
9838
  if (d_eqSolver != nullptr)
113
  {
114
39
    d_eqSolver->finishInit();
115
  }
116
  // finish initialize in the old linear solver
117
9838
  d_internal->finishInit();
118
9838
}
119
120
781005
void TheoryArith::preRegisterTerm(TNode n)
121
{
122
781005
  if (d_nonlinearExtension != nullptr)
123
  {
124
382695
    d_nonlinearExtension->preRegisterTerm(n);
125
  }
126
781006
  d_internal->preRegisterTerm(n);
127
781004
}
128
129
570356
void TheoryArith::notifySharedTerm(TNode n) { d_internal->notifySharedTerm(n); }
130
131
778612
TrustNode TheoryArith::ppRewrite(TNode atom, std::vector<SkolemLemma>& lems)
132
{
133
1557224
  CodeTimer timer(d_ppRewriteTimer, /* allow_reentrant = */ true);
134
778612
  Debug("arith::preprocess") << "arith::preprocess() : " << atom << endl;
135
136
778612
  if (atom.getKind() == kind::EQUAL)
137
  {
138
30295
    return d_ppre.ppRewriteEq(atom);
139
  }
140
748317
  Assert(Theory::theoryOf(atom) == THEORY_ARITH);
141
  // Eliminate operators. Notice we must do this here since other
142
  // theories may generate lemmas that involve non-standard operators. For
143
  // example, quantifier instantiation may use TO_INTEGER terms; SyGuS may
144
  // introduce non-standard arithmetic terms appearing in grammars.
145
  // call eliminate operators. In contrast to expandDefinitions, we eliminate
146
  // *all* extended arithmetic operators here, including total ones.
147
748323
  return d_arithPreproc.eliminate(atom, lems, false);
148
}
149
150
11635
Theory::PPAssertStatus TheoryArith::ppAssert(
151
    TrustNode tin, TrustSubstitutionMap& outSubstitutions)
152
{
153
11635
  return d_internal->ppAssert(tin, outSubstitutions);
154
}
155
156
105752
void TheoryArith::ppStaticLearn(TNode n, NodeBuilder& learned)
157
{
158
105752
  d_internal->ppStaticLearn(n, learned);
159
105752
}
160
161
1555531
bool TheoryArith::preCheck(Effort level)
162
{
163
1555531
  Trace("arith-check") << "TheoryArith::preCheck " << level << std::endl;
164
1555531
  return d_internal->preCheck(level);
165
}
166
167
1555531
void TheoryArith::postCheck(Effort level)
168
{
169
1555531
  Trace("arith-check") << "TheoryArith::postCheck " << level << std::endl;
170
  // check with the non-linear solver at last call
171
1555531
  if (level == Theory::EFFORT_LAST_CALL)
172
  {
173
3013
    if (d_nonlinearExtension != nullptr)
174
    {
175
3013
      d_nonlinearExtension->check(level);
176
    }
177
3013
    return;
178
  }
179
  // otherwise, check with the linear solver
180
1552518
  if (d_internal->postCheck(level))
181
  {
182
    // linear solver emitted a conflict or lemma, return
183
52721
    return;
184
  }
185
186
1499797
  if (Theory::fullEffort(level))
187
  {
188
58206
    if (d_nonlinearExtension != nullptr)
189
    {
190
31878
      d_nonlinearExtension->check(level);
191
    }
192
26328
    else if (d_internal->foundNonlinear())
193
    {
194
      // set incomplete
195
      d_im.setIncomplete(IncompleteId::ARITH_NL_DISABLED);
196
    }
197
  }
198
}
199
200
5576055
bool TheoryArith::preNotifyFact(
201
    TNode atom, bool pol, TNode fact, bool isPrereg, bool isInternal)
202
{
203
11152110
  Trace("arith-check") << "TheoryArith::preNotifyFact: " << fact
204
5576055
                       << ", isPrereg=" << isPrereg
205
5576055
                       << ", isInternal=" << isInternal << std::endl;
206
  // We do not assert to the equality engine of arithmetic in the standard way,
207
  // hence we return "true" to indicate we are finished with this fact.
208
5576055
  bool ret = true;
209
5576055
  if (d_eqSolver != nullptr)
210
  {
211
    // the equality solver may indicate ret = false, after which the assertion
212
    // will be asserted to the equality engine in the default way.
213
5263
    ret = d_eqSolver->preNotifyFact(atom, pol, fact, isPrereg, isInternal);
214
  }
215
  // we also always also notify the internal solver
216
5576055
  d_internal->preNotifyFact(atom, pol, fact);
217
5576055
  return ret;
218
}
219
220
18809
bool TheoryArith::needsCheckLastEffort() {
221
18809
  if (d_nonlinearExtension != nullptr)
222
  {
223
10087
    return d_nonlinearExtension->needsCheckLastEffort();
224
  }
225
8722
  return false;
226
}
227
228
23387
TrustNode TheoryArith::explain(TNode n)
229
{
230
23387
  if (d_eqSolver != nullptr)
231
  {
232
    // if the equality solver has an explanation for it, use it
233
50
    TrustNode texp = d_eqSolver->explain(n);
234
25
    if (!texp.isNull())
235
    {
236
      return texp;
237
    }
238
  }
239
23387
  return d_internal->explain(n);
240
}
241
242
2484343
void TheoryArith::propagate(Effort e) {
243
2484343
  d_internal->propagate(e);
244
2484343
}
245
246
14565
bool TheoryArith::collectModelInfo(TheoryModel* m,
247
                                   const std::set<Node>& termSet)
248
{
249
  // this overrides behavior to not assert equality engine
250
14565
  return collectModelValues(m, termSet);
251
}
252
253
14565
bool TheoryArith::collectModelValues(TheoryModel* m,
254
                                     const std::set<Node>& termSet)
255
{
256
  // get the model from the linear solver
257
29130
  std::map<Node, Node> arithModel;
258
14565
  d_internal->collectModelValues(termSet, arithModel);
259
  // Double check that the model from the linear solver respects integer types,
260
  // if it does not, add a branch and bound lemma. This typically should never
261
  // be necessary, but is needed in rare cases.
262
14565
  bool addedLemma = false;
263
14565
  bool badAssignment = false;
264
256008
  for (const std::pair<const Node, Node>& p : arithModel)
265
  {
266
241443
    if (p.first.getType().isInteger() && !p.second.getType().isInteger())
267
    {
268
      Assert(false) << "TheoryArithPrivate generated a bad model value for "
269
                       "integer variable "
270
                    << p.first << " : " << p.second;
271
      // must branch and bound
272
      TrustNode lem =
273
          d_bab.branchIntegerVariable(p.first, p.second.getConst<Rational>());
274
      if (d_im.trustedLemma(lem, InferenceId::ARITH_BB_LEMMA))
275
      {
276
        addedLemma = true;
277
      }
278
      badAssignment = true;
279
    }
280
  }
281
14565
  if (addedLemma)
282
  {
283
    // we had to add a branch and bound lemma since the linear solver assigned
284
    // a non-integer value to an integer variable.
285
    return false;
286
  }
287
  // this would imply that linear arithmetic's model failed to satisfy a branch
288
  // and bound lemma
289
14565
  AlwaysAssert(!badAssignment)
290
      << "Bad assignment from TheoryArithPrivate::collectModelValues, and no "
291
         "branching lemma was sent";
292
293
  // if non-linear is enabled, intercept the model, which may repair its values
294
14565
  if (d_nonlinearExtension != nullptr)
295
  {
296
    // Non-linear may repair values to satisfy non-linear constraints (see
297
    // documentation for NonlinearExtension::interceptModel).
298
8937
    d_nonlinearExtension->interceptModel(arithModel, termSet);
299
  }
300
  // We are now ready to assert the model.
301
254555
  for (const std::pair<const Node, Node>& p : arithModel)
302
  {
303
    // maps to constant of comparable type
304
239992
    Assert(p.first.getType().isComparableTo(p.second.getType()));
305
239992
    if (m->assertEquality(p.first, p.second, true))
306
    {
307
239990
      continue;
308
    }
309
    // If we failed to assert an equality, it is likely due to theory
310
    // combination, namely the repaired model for non-linear changed
311
    // an equality status that was agreed upon by both (linear) arithmetic
312
    // and another theory. In this case, we must add a lemma, or otherwise
313
    // we would terminate with an invalid model. Thus, we add a splitting
314
    // lemma of the form ( x = v V x != v ) where v is the model value
315
    // assigned by the non-linear solver to x.
316
2
    if (d_nonlinearExtension != nullptr)
317
    {
318
4
      Node eq = p.first.eqNode(p.second);
319
4
      Node lem = NodeManager::currentNM()->mkNode(kind::OR, eq, eq.negate());
320
2
      bool added = d_im.lemma(lem, InferenceId::ARITH_SPLIT_FOR_NL_MODEL);
321
2
      AlwaysAssert(added) << "The lemma was already in cache. Probably there is something wrong with theory combination...";
322
    }
323
2
    return false;
324
  }
325
14563
  return true;
326
}
327
328
1748
void TheoryArith::notifyRestart(){
329
1748
  d_internal->notifyRestart();
330
1748
}
331
332
15189
void TheoryArith::presolve(){
333
15189
  d_internal->presolve();
334
15189
  if (d_nonlinearExtension != nullptr)
335
  {
336
6392
    d_nonlinearExtension->presolve();
337
  }
338
15189
}
339
340
779969
EqualityStatus TheoryArith::getEqualityStatus(TNode a, TNode b) {
341
779969
  return d_internal->getEqualityStatus(a,b);
342
}
343
344
2850
Node TheoryArith::getModelValue(TNode var) {
345
2850
  return d_internal->getModelValue( var );
346
}
347
348
6013
std::pair<bool, Node> TheoryArith::entailmentCheck(TNode lit)
349
{
350
12026
  ArithEntailmentCheckParameters def;
351
6013
  def.addLookupRowSumAlgorithms();
352
12026
  ArithEntailmentCheckSideEffects ase;
353
6013
  std::pair<bool, Node> res = d_internal->entailmentCheck(lit, def, ase);
354
12026
  return res;
355
}
356
eq::ProofEqEngine* TheoryArith::getProofEqEngine()
357
{
358
  return d_im.getProofEqEngine();
359
}
360
361
}  // namespace arith
362
}  // namespace theory
363
29280
}  // namespace cvc5