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