GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arith/theory_arith.cpp Lines: 130 131 99.2 %
Date: 2021-05-22 Branches: 175 376 46.5 %

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 "expr/proof_checker.h"
19
#include "expr/proof_rule.h"
20
#include "options/smt_options.h"
21
#include "smt/smt_statistics_registry.h"
22
#include "theory/arith/arith_rewriter.h"
23
#include "theory/arith/infer_bounds.h"
24
#include "theory/arith/nl/nonlinear_extension.h"
25
#include "theory/arith/theory_arith_private.h"
26
#include "theory/ext_theory.h"
27
#include "theory/rewriter.h"
28
#include "theory/theory_model.h"
29
30
using namespace std;
31
using namespace cvc5::kind;
32
33
namespace cvc5 {
34
namespace theory {
35
namespace arith {
36
37
9459
TheoryArith::TheoryArith(context::Context* c,
38
                         context::UserContext* u,
39
                         OutputChannel& out,
40
                         Valuation valuation,
41
                         const LogicInfo& logicInfo,
42
9459
                         ProofNodeManager* pnm)
43
    : Theory(THEORY_ARITH, c, u, out, valuation, logicInfo, pnm),
44
      d_internal(
45
9459
          new TheoryArithPrivate(*this, c, u, out, valuation, logicInfo, pnm)),
46
9459
      d_ppRewriteTimer(smtStatisticsRegistry().registerTimer(
47
18918
          "theory::arith::ppRewriteTimer")),
48
      d_ppPfGen(pnm, c, "Arith::ppRewrite"),
49
9459
      d_astate(*d_internal, c, u, valuation),
50
      d_im(*this, d_astate, pnm),
51
      d_nonlinearExtension(nullptr),
52
      d_opElim(pnm, logicInfo),
53
      d_arithPreproc(d_astate, d_im, pnm, d_opElim),
54
47295
      d_rewriter(d_opElim)
55
{
56
  // indicate we are using the theory state object and inference manager
57
9459
  d_theoryState = &d_astate;
58
9459
  d_inferManager = &d_im;
59
9459
}
60
61
28377
TheoryArith::~TheoryArith(){
62
9459
  delete d_internal;
63
18918
}
64
65
9459
TheoryRewriter* TheoryArith::getTheoryRewriter() { return &d_rewriter; }
66
67
3600
ProofRuleChecker* TheoryArith::getProofChecker()
68
{
69
3600
  return d_internal->getProofChecker();
70
}
71
72
9459
bool TheoryArith::needsEqualityEngine(EeSetupInfo& esi)
73
{
74
9459
  return d_internal->needsEqualityEngine(esi);
75
}
76
9459
void TheoryArith::finishInit()
77
{
78
18918
  if (getLogicInfo().isTheoryEnabled(THEORY_ARITH)
79
9459
      && getLogicInfo().areTranscendentalsUsed())
80
  {
81
    // witness is used to eliminate square root
82
3990
    d_valuation.setUnevaluatedKind(kind::WITNESS);
83
    // we only need to add the operators that are not syntax sugar
84
3990
    d_valuation.setUnevaluatedKind(kind::EXPONENTIAL);
85
3990
    d_valuation.setUnevaluatedKind(kind::SINE);
86
3990
    d_valuation.setUnevaluatedKind(kind::PI);
87
  }
88
  // only need to create nonlinear extension if non-linear logic
89
9459
  const LogicInfo& logicInfo = getLogicInfo();
90
9459
  if (logicInfo.isTheoryEnabled(THEORY_ARITH) && !logicInfo.isLinear())
91
  {
92
9828
    d_nonlinearExtension.reset(
93
4914
        new nl::NonlinearExtension(*this, d_astate, d_equalityEngine, d_pnm));
94
  }
95
  // finish initialize internally
96
9459
  d_internal->finishInit();
97
9459
}
98
99
730660
void TheoryArith::preRegisterTerm(TNode n)
100
{
101
730660
  if (d_nonlinearExtension != nullptr)
102
  {
103
376529
    d_nonlinearExtension->preRegisterTerm(n);
104
  }
105
730661
  d_internal->preRegisterTerm(n);
106
730659
}
107
108
582302
void TheoryArith::notifySharedTerm(TNode n) { d_internal->notifySharedTerm(n); }
109
110
749108
TrustNode TheoryArith::ppRewrite(TNode atom, std::vector<SkolemLemma>& lems)
111
{
112
1498216
  CodeTimer timer(d_ppRewriteTimer, /* allow_reentrant = */ true);
113
749108
  Debug("arith::preprocess") << "arith::preprocess() : " << atom << endl;
114
115
749108
  if (atom.getKind() == kind::EQUAL)
116
  {
117
30524
    return ppRewriteEq(atom);
118
  }
119
718584
  Assert(Theory::theoryOf(atom) == THEORY_ARITH);
120
  // Eliminate operators. Notice we must do this here since other
121
  // theories may generate lemmas that involve non-standard operators. For
122
  // example, quantifier instantiation may use TO_INTEGER terms; SyGuS may
123
  // introduce non-standard arithmetic terms appearing in grammars.
124
  // call eliminate operators. In contrast to expandDefinitions, we eliminate
125
  // *all* extended arithmetic operators here, including total ones.
126
718590
  return d_arithPreproc.eliminate(atom, lems, false);
127
}
128
129
31497
TrustNode TheoryArith::ppRewriteEq(TNode atom)
130
{
131
31497
  Assert(atom.getKind() == kind::EQUAL);
132
31497
  if (!options::arithRewriteEq())
133
  {
134
13218
    return TrustNode::null();
135
  }
136
18279
  Assert(atom[0].getType().isReal());
137
36558
  Node leq = NodeBuilder(kind::LEQ) << atom[0] << atom[1];
138
36558
  Node geq = NodeBuilder(kind::GEQ) << atom[0] << atom[1];
139
36558
  Node rewritten = Rewriter::rewrite(leq.andNode(geq));
140
36558
  Debug("arith::preprocess")
141
18279
      << "arith::preprocess() : returning " << rewritten << endl;
142
  // don't need to rewrite terms since rewritten is not a non-standard op
143
18279
  if (proofsEnabled())
144
  {
145
    return d_ppPfGen.mkTrustedRewrite(
146
        atom,
147
        rewritten,
148
3404
        d_pnm->mkNode(PfRule::INT_TRUST, {}, {atom.eqNode(rewritten)}));
149
  }
150
14875
  return TrustNode::mkTrustRewrite(atom, rewritten, nullptr);
151
}
152
153
11527
Theory::PPAssertStatus TheoryArith::ppAssert(
154
    TrustNode tin, TrustSubstitutionMap& outSubstitutions)
155
{
156
11527
  return d_internal->ppAssert(tin, outSubstitutions);
157
}
158
159
104072
void TheoryArith::ppStaticLearn(TNode n, NodeBuilder& learned)
160
{
161
104072
  d_internal->ppStaticLearn(n, learned);
162
104072
}
163
164
1331402
bool TheoryArith::preCheck(Effort level)
165
{
166
1331402
  Trace("arith-check") << "TheoryArith::preCheck " << level << std::endl;
167
1331402
  return d_internal->preCheck(level);
168
}
169
170
1331402
void TheoryArith::postCheck(Effort level)
171
{
172
1331402
  Trace("arith-check") << "TheoryArith::postCheck " << level << std::endl;
173
  // check with the non-linear solver at last call
174
1331402
  if (level == Theory::EFFORT_LAST_CALL)
175
  {
176
3029
    if (d_nonlinearExtension != nullptr)
177
    {
178
3029
      d_nonlinearExtension->check(level);
179
    }
180
3029
    return;
181
  }
182
  // otherwise, check with the linear solver
183
1328373
  if (d_internal->postCheck(level))
184
  {
185
    // linear solver emitted a conflict or lemma, return
186
52090
    return;
187
  }
188
189
1276283
  if (Theory::fullEffort(level))
190
  {
191
48096
    if (d_nonlinearExtension != nullptr)
192
    {
193
26424
      d_nonlinearExtension->check(level);
194
    }
195
21672
    else if (d_internal->foundNonlinear())
196
    {
197
      // set incomplete
198
      d_im.setIncomplete(IncompleteId::ARITH_NL_DISABLED);
199
    }
200
  }
201
}
202
203
5005301
bool TheoryArith::preNotifyFact(
204
    TNode atom, bool pol, TNode fact, bool isPrereg, bool isInternal)
205
{
206
10010602
  Trace("arith-check") << "TheoryArith::preNotifyFact: " << fact
207
5005301
                       << ", isPrereg=" << isPrereg
208
5005301
                       << ", isInternal=" << isInternal << std::endl;
209
5005301
  d_internal->preNotifyFact(atom, pol, fact);
210
  // We do not assert to the equality engine of arithmetic in the standard way,
211
  // hence we return "true" to indicate we are finished with this fact.
212
5005301
  return true;
213
}
214
215
17521
bool TheoryArith::needsCheckLastEffort() {
216
17521
  if (d_nonlinearExtension != nullptr)
217
  {
218
9624
    return d_nonlinearExtension->needsCheckLastEffort();
219
  }
220
7897
  return false;
221
}
222
223
21670
TrustNode TheoryArith::explain(TNode n) { return d_internal->explain(n); }
224
225
2163161
void TheoryArith::propagate(Effort e) {
226
2163161
  d_internal->propagate(e);
227
2163161
}
228
229
13422
bool TheoryArith::collectModelInfo(TheoryModel* m,
230
                                   const std::set<Node>& termSet)
231
{
232
  // this overrides behavior to not assert equality engine
233
13422
  return collectModelValues(m, termSet);
234
}
235
236
13422
bool TheoryArith::collectModelValues(TheoryModel* m,
237
                                     const std::set<Node>& termSet)
238
{
239
  // get the model from the linear solver
240
26844
  std::map<Node, Node> arithModel;
241
13422
  d_internal->collectModelValues(termSet, arithModel);
242
  // if non-linear is enabled, intercept the model, which may repair its values
243
13422
  if (d_nonlinearExtension != nullptr)
244
  {
245
    // Non-linear may repair values to satisfy non-linear constraints (see
246
    // documentation for NonlinearExtension::interceptModel).
247
8496
    d_nonlinearExtension->interceptModel(arithModel, termSet);
248
  }
249
  // We are now ready to assert the model.
250
235208
  for (const std::pair<const Node, Node>& p : arithModel)
251
  {
252
    // maps to constant of comparable type
253
221788
    Assert(p.first.getType().isComparableTo(p.second.getType()));
254
221788
    if (m->assertEquality(p.first, p.second, true))
255
    {
256
221786
      continue;
257
    }
258
    // If we failed to assert an equality, it is likely due to theory
259
    // combination, namely the repaired model for non-linear changed
260
    // an equality status that was agreed upon by both (linear) arithmetic
261
    // and another theory. In this case, we must add a lemma, or otherwise
262
    // we would terminate with an invalid model. Thus, we add a splitting
263
    // lemma of the form ( x = v V x != v ) where v is the model value
264
    // assigned by the non-linear solver to x.
265
2
    if (d_nonlinearExtension != nullptr)
266
    {
267
4
      Node eq = p.first.eqNode(p.second);
268
4
      Node lem = NodeManager::currentNM()->mkNode(kind::OR, eq, eq.negate());
269
2
      bool added = d_im.lemma(lem, InferenceId::ARITH_SPLIT_FOR_NL_MODEL);
270
2
      AlwaysAssert(added) << "The lemma was already in cache. Probably there is something wrong with theory combination...";
271
    }
272
2
    return false;
273
  }
274
13420
  return true;
275
}
276
277
1629
void TheoryArith::notifyRestart(){
278
1629
  d_internal->notifyRestart();
279
1629
}
280
281
14306
void TheoryArith::presolve(){
282
14306
  d_internal->presolve();
283
14306
  if (d_nonlinearExtension != nullptr)
284
  {
285
5753
    d_nonlinearExtension->presolve();
286
  }
287
14306
}
288
289
807886
EqualityStatus TheoryArith::getEqualityStatus(TNode a, TNode b) {
290
807886
  return d_internal->getEqualityStatus(a,b);
291
}
292
293
2876
Node TheoryArith::getModelValue(TNode var) {
294
2876
  return d_internal->getModelValue( var );
295
}
296
297
4830
std::pair<bool, Node> TheoryArith::entailmentCheck(TNode lit)
298
{
299
9660
  ArithEntailmentCheckParameters def;
300
4830
  def.addLookupRowSumAlgorithms();
301
9660
  ArithEntailmentCheckSideEffects ase;
302
4830
  std::pair<bool, Node> res = d_internal->entailmentCheck(lit, def, ase);
303
9660
  return res;
304
}
305
9459
eq::ProofEqEngine* TheoryArith::getProofEqEngine()
306
{
307
9459
  return d_im.getProofEqEngine();
308
}
309
310
}  // namespace arith
311
}  // namespace theory
312
28191
}  // namespace cvc5