GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/arith/nl/iand_solver.cpp Lines: 116 152 76.3 %
Date: 2021-08-06 Branches: 256 736 34.8 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds, Makai Mann, Gereon Kremer
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
 * Implementation of integer and (IAND) solver.
14
 */
15
16
#include "theory/arith/nl/iand_solver.h"
17
18
#include "options/arith_options.h"
19
#include "options/smt_options.h"
20
#include "preprocessing/passes/bv_to_int.h"
21
#include "theory/arith/arith_msum.h"
22
#include "theory/arith/arith_state.h"
23
#include "theory/arith/arith_utilities.h"
24
#include "theory/arith/inference_manager.h"
25
#include "theory/arith/nl/nl_model.h"
26
#include "theory/rewriter.h"
27
#include "util/bitvector.h"
28
#include "util/iand.h"
29
30
using namespace cvc5::kind;
31
32
namespace cvc5 {
33
namespace theory {
34
namespace arith {
35
namespace nl {
36
37
5145
IAndSolver::IAndSolver(InferenceManager& im, ArithState& state, NlModel& model)
38
    : d_im(im),
39
      d_model(model),
40
5145
      d_initRefine(state.getUserContext())
41
{
42
5145
  NodeManager* nm = NodeManager::currentNM();
43
5145
  d_false = nm->mkConst(false);
44
5145
  d_true = nm->mkConst(true);
45
5145
  d_zero = nm->mkConst(Rational(0));
46
5145
  d_one = nm->mkConst(Rational(1));
47
5145
  d_two = nm->mkConst(Rational(2));
48
5145
}
49
50
5145
IAndSolver::~IAndSolver() {}
51
52
2484
void IAndSolver::initLastCall(const std::vector<Node>& assertions,
53
                              const std::vector<Node>& false_asserts,
54
                              const std::vector<Node>& xts)
55
{
56
2484
  d_iands.clear();
57
58
2484
  Trace("iand-mv") << "IAND terms : " << std::endl;
59
19313
  for (const Node& a : xts)
60
  {
61
16829
    Kind ak = a.getKind();
62
16829
    if (ak != IAND)
63
    {
64
      // don't care about other terms
65
16658
      continue;
66
    }
67
171
    size_t bsize = a.getOperator().getConst<IntAnd>().d_size;
68
171
    d_iands[bsize].push_back(a);
69
  }
70
71
2484
  Trace("iand") << "We have " << d_iands.size() << " IAND terms." << std::endl;
72
2484
}
73
74
2484
void IAndSolver::checkInitialRefine()
75
{
76
2484
  Trace("iand-check") << "IAndSolver::checkInitialRefine" << std::endl;
77
2484
  NodeManager* nm = NodeManager::currentNM();
78
2650
  for (const std::pair<const unsigned, std::vector<Node> >& is : d_iands)
79
  {
80
    // the reference bitwidth
81
166
    unsigned k = is.first;
82
337
    for (const Node& i : is.second)
83
    {
84
171
      if (d_initRefine.find(i) != d_initRefine.end())
85
      {
86
        // already sent initial axioms for i in this user context
87
80
        continue;
88
      }
89
91
      d_initRefine.insert(i);
90
182
      Node op = i.getOperator();
91
      // initial refinement lemmas
92
182
      std::vector<Node> conj;
93
      // iand(x,y)=iand(y,x) is guaranteed by rewriting
94
91
      Assert(i[0] <= i[1]);
95
      // conj.push_back(i.eqNode(nm->mkNode(IAND, op, i[1], i[0])));
96
      // 0 <= iand(x,y) < 2^k
97
91
      conj.push_back(nm->mkNode(LEQ, d_zero, i));
98
91
      conj.push_back(nm->mkNode(LT, i, d_iandUtils.twoToK(k)));
99
      // iand(x,y)<=x
100
91
      conj.push_back(nm->mkNode(LEQ, i, i[0]));
101
      // iand(x,y)<=y
102
91
      conj.push_back(nm->mkNode(LEQ, i, i[1]));
103
      // x=y => iand(x,y)=x
104
91
      conj.push_back(nm->mkNode(IMPLIES, i[0].eqNode(i[1]), i.eqNode(i[0])));
105
182
      Node lem = conj.size() == 1 ? conj[0] : nm->mkNode(AND, conj);
106
182
      Trace("iand-lemma") << "IAndSolver::Lemma: " << lem << " ; INIT_REFINE"
107
91
                          << std::endl;
108
91
      d_im.addPendingLemma(lem, InferenceId::ARITH_NL_IAND_INIT_REFINE);
109
    }
110
  }
111
2484
}
112
113
297
void IAndSolver::checkFullRefine()
114
{
115
297
  Trace("iand-check") << "IAndSolver::checkFullRefine";
116
297
  Trace("iand-check") << "IAND terms: " << std::endl;
117
324
  for (const std::pair<const unsigned, std::vector<Node> >& is : d_iands)
118
  {
119
    // the reference bitwidth
120
27
    unsigned k = is.first;
121
56
    for (const Node& i : is.second)
122
    {
123
57
      Node valAndXY = d_model.computeAbstractModelValue(i);
124
57
      Node valAndXYC = d_model.computeConcreteModelValue(i);
125
29
      if (Trace.isOn("iand-check"))
126
      {
127
        Node x = i[0];
128
        Node y = i[1];
129
130
        Node valX = d_model.computeConcreteModelValue(x);
131
        Node valY = d_model.computeConcreteModelValue(y);
132
133
        Trace("iand-check")
134
            << "* " << i << ", value = " << valAndXY << std::endl;
135
        Trace("iand-check") << "  actual (" << valX << ", " << valY
136
                            << ") = " << valAndXYC << std::endl;
137
        // print the bit-vector versions
138
        Node bvalX = convertToBvK(k, valX);
139
        Node bvalY = convertToBvK(k, valY);
140
        Node bvalAndXY = convertToBvK(k, valAndXY);
141
        Node bvalAndXYC = convertToBvK(k, valAndXYC);
142
143
        Trace("iand-check") << "  bv-value = " << bvalAndXY << std::endl;
144
        Trace("iand-check") << "  bv-actual (" << bvalX << ", " << bvalY
145
                            << ") = " << bvalAndXYC << std::endl;
146
      }
147
30
      if (valAndXY == valAndXYC)
148
      {
149
1
        Trace("iand-check") << "...already correct" << std::endl;
150
1
        continue;
151
      }
152
153
      // ************* additional lemma schemas go here
154
82
      if (options::iandMode() == options::IandMode::SUM)
155
      {
156
4
        Node lem = sumBasedLemma(i);  // add lemmas based on sum mode
157
4
        Trace("iand-lemma")
158
2
            << "IAndSolver::Lemma: " << lem << " ; SUM_REFINE" << std::endl;
159
        // note that lemma can contain div/mod, and will be preprocessed in the
160
        // prop engine
161
2
        d_im.addPendingLemma(
162
            lem, InferenceId::ARITH_NL_IAND_SUM_REFINE, nullptr, true);
163
      }
164
26
      else if (options::iandMode() == options::IandMode::BITWISE)
165
      {
166
24
        Node lem = bitwiseLemma(i);  // check for violated bitwise axioms
167
24
        Trace("iand-lemma")
168
12
            << "IAndSolver::Lemma: " << lem << " ; BITWISE_REFINE" << std::endl;
169
        // note that lemma can contain div/mod, and will be preprocessed in the
170
        // prop engine
171
12
        d_im.addPendingLemma(
172
            lem, InferenceId::ARITH_NL_IAND_BITWISE_REFINE, nullptr, true);
173
      }
174
      else
175
      {
176
        // this is the most naive model-based schema based on model values
177
28
        Node lem = valueBasedLemma(i);
178
28
        Trace("iand-lemma")
179
14
            << "IAndSolver::Lemma: " << lem << " ; VALUE_REFINE" << std::endl;
180
        // send the value lemma
181
14
        d_im.addPendingLemma(lem,
182
                             InferenceId::ARITH_NL_IAND_VALUE_REFINE,
183
                             nullptr,
184
                             true);
185
      }
186
    }
187
  }
188
297
}
189
190
Node IAndSolver::convertToBvK(unsigned k, Node n) const
191
{
192
  Assert(n.isConst() && n.getType().isInteger());
193
  NodeManager* nm = NodeManager::currentNM();
194
  Node iToBvOp = nm->mkConst(IntToBitVector(k));
195
  Node bn = nm->mkNode(kind::INT_TO_BITVECTOR, iToBvOp, n);
196
  return Rewriter::rewrite(bn);
197
}
198
199
Node IAndSolver::mkIAnd(unsigned k, Node x, Node y) const
200
{
201
  NodeManager* nm = NodeManager::currentNM();
202
  Node iAndOp = nm->mkConst(IntAnd(k));
203
  Node ret = nm->mkNode(IAND, iAndOp, x, y);
204
  ret = Rewriter::rewrite(ret);
205
  return ret;
206
}
207
208
Node IAndSolver::mkIOr(unsigned k, Node x, Node y) const
209
{
210
  Node ret = mkINot(k, mkIAnd(k, mkINot(k, x), mkINot(k, y)));
211
  ret = Rewriter::rewrite(ret);
212
  return ret;
213
}
214
215
Node IAndSolver::mkINot(unsigned k, Node x) const
216
{
217
  NodeManager* nm = NodeManager::currentNM();
218
  Node ret = nm->mkNode(MINUS, d_iandUtils.twoToKMinusOne(k), x);
219
  ret = Rewriter::rewrite(ret);
220
  return ret;
221
}
222
223
14
Node IAndSolver::valueBasedLemma(Node i)
224
{
225
14
  Assert(i.getKind() == IAND);
226
28
  Node x = i[0];
227
28
  Node y = i[1];
228
229
28
  Node valX = d_model.computeConcreteModelValue(x);
230
28
  Node valY = d_model.computeConcreteModelValue(y);
231
232
14
  NodeManager* nm = NodeManager::currentNM();
233
28
  Node valC = nm->mkNode(IAND, i.getOperator(), valX, valY);
234
14
  valC = Rewriter::rewrite(valC);
235
236
  Node lem = nm->mkNode(
237
14
      IMPLIES, nm->mkNode(AND, x.eqNode(valX), y.eqNode(valY)), i.eqNode(valC));
238
28
  return lem;
239
}
240
241
2
Node IAndSolver::sumBasedLemma(Node i)
242
{
243
2
  Assert(i.getKind() == IAND);
244
4
  Node x = i[0];
245
4
  Node y = i[1];
246
2
  size_t bvsize = i.getOperator().getConst<IntAnd>().d_size;
247
2
  uint64_t granularity = options::BVAndIntegerGranularity();
248
2
  NodeManager* nm = NodeManager::currentNM();
249
  Node lem = nm->mkNode(
250
2
      EQUAL, i, d_iandUtils.createSumNode(x, y, bvsize, granularity));
251
4
  return lem;
252
}
253
254
12
Node IAndSolver::bitwiseLemma(Node i)
255
{
256
12
  Assert(i.getKind() == IAND);
257
24
  Node x = i[0];
258
24
  Node y = i[1];
259
260
12
  unsigned bvsize = i.getOperator().getConst<IntAnd>().d_size;
261
12
  uint64_t granularity = options::BVAndIntegerGranularity();
262
263
24
  Rational absI = d_model.computeAbstractModelValue(i).getConst<Rational>();
264
24
  Rational concI = d_model.computeConcreteModelValue(i).getConst<Rational>();
265
266
12
  Assert(absI.isIntegral());
267
12
  Assert(concI.isIntegral());
268
269
24
  BitVector bvAbsI = BitVector(bvsize, absI.getNumerator());
270
24
  BitVector bvConcI = BitVector(bvsize, concI.getNumerator());
271
272
12
  NodeManager* nm = NodeManager::currentNM();
273
12
  Node lem = d_true;
274
275
  // compare each bit to bvI
276
24
  Node cond;
277
24
  Node bitIAnd;
278
  unsigned high_bit;
279
38
  for (unsigned j = 0; j < bvsize; j += granularity)
280
  {
281
26
    high_bit = j + granularity - 1;
282
    // don't let high_bit pass bvsize
283
26
    if (high_bit >= bvsize)
284
    {
285
4
      high_bit = bvsize - 1;
286
    }
287
288
    // check if the abstraction differs from the concrete one on these bits
289
26
    if (bvAbsI.extract(high_bit, j) != bvConcI.extract(high_bit, j))
290
    {
291
16
      bitIAnd = d_iandUtils.createBitwiseIAndNode(x, y, high_bit, j);
292
      // enforce bitwise equality
293
16
      lem = nm->mkNode(
294
32
          AND, lem, d_iandUtils.iextract(high_bit, j, i).eqNode(bitIAnd));
295
    }
296
  }
297
24
  return lem;
298
}
299
300
}  // namespace nl
301
}  // namespace arith
302
}  // namespace theory
303
29322
}  // namespace cvc5