1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Yoni Zohar, Makai Mann, Andrew Reynolds |
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 |
|
* Utilities to maintain finite tables that represent the value of iand. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "theory/arith/nl/iand_utils.h" |
17 |
|
|
18 |
|
#include <cmath> |
19 |
|
|
20 |
|
#include "cvc5_private.h" |
21 |
|
#include "theory/arith/nl/nl_model.h" |
22 |
|
#include "theory/rewriter.h" |
23 |
|
|
24 |
|
namespace cvc5 { |
25 |
|
namespace theory { |
26 |
|
namespace arith { |
27 |
|
namespace nl { |
28 |
|
|
29 |
330 |
static Rational intpow2(uint64_t b) |
30 |
|
{ |
31 |
330 |
return Rational(Integer(2).pow(b), Integer(1)); |
32 |
|
} |
33 |
|
|
34 |
330 |
Node pow2(uint64_t k) |
35 |
|
{ |
36 |
330 |
Assert(k >= 0); |
37 |
330 |
NodeManager* nm = NodeManager::currentNM(); |
38 |
330 |
return nm->mkConst<Rational>(intpow2(k)); |
39 |
|
} |
40 |
|
|
41 |
6420 |
bool oneBitAnd(bool a, bool b) { return (a && b); } |
42 |
|
|
43 |
|
// computes (bv_to_int ((_ extract i+size-1 i) (int_to_bv x)))) |
44 |
132 |
Node intExtract(Node x, uint64_t i, uint64_t size) |
45 |
|
{ |
46 |
132 |
Assert(size > 0); |
47 |
132 |
NodeManager* nm = NodeManager::currentNM(); |
48 |
|
// extract definition in integers is: |
49 |
|
// (mod (div a (two_to_the j)) (two_to_the (+ (- i j) 1)))) |
50 |
|
Node extract = |
51 |
|
nm->mkNode(kind::INTS_MODULUS_TOTAL, |
52 |
264 |
nm->mkNode(kind::INTS_DIVISION_TOTAL, x, pow2(i * size)), |
53 |
396 |
pow2(size)); |
54 |
132 |
return extract; |
55 |
|
} |
56 |
|
|
57 |
14373 |
IAndUtils::IAndUtils() |
58 |
|
{ |
59 |
14373 |
NodeManager* nm = NodeManager::currentNM(); |
60 |
14373 |
d_zero = nm->mkConst(Rational(0)); |
61 |
14373 |
d_one = nm->mkConst(Rational(1)); |
62 |
14373 |
d_two = nm->mkConst(Rational(2)); |
63 |
14373 |
} |
64 |
|
|
65 |
82 |
Node IAndUtils::createITEFromTable( |
66 |
|
Node x, |
67 |
|
Node y, |
68 |
|
uint64_t granularity, |
69 |
|
const std::map<std::pair<int64_t, int64_t>, uint64_t>& table) |
70 |
|
{ |
71 |
82 |
NodeManager* nm = NodeManager::currentNM(); |
72 |
82 |
Assert(granularity <= 8); |
73 |
82 |
uint64_t num_of_values = ((uint64_t)pow(2, granularity)); |
74 |
|
// The table represents a function from pairs of integers to integers, where |
75 |
|
// all integers are between 0 (inclusive) and num_of_values (exclusive). |
76 |
|
// additionally, there is a default value (-1, -1). |
77 |
82 |
Assert(table.size() == 1 + ((uint64_t)(num_of_values * num_of_values))); |
78 |
|
// start with the default, most common value. |
79 |
|
// this value is represented in the table by (-1, -1). |
80 |
2159 |
Node ite = nm->mkConst<Rational>(table.at(std::make_pair(-1, -1))); |
81 |
350 |
for (uint64_t i = 0; i < num_of_values; i++) |
82 |
|
{ |
83 |
2228 |
for (uint64_t j = 0; j < num_of_values; j++) |
84 |
|
{ |
85 |
|
// skip the most common value, as it was already stored. |
86 |
1960 |
if (table.at(std::make_pair(i, j)) == table.at(std::make_pair(-1, -1))) |
87 |
|
{ |
88 |
774 |
continue; |
89 |
|
} |
90 |
|
// append the current value to the ite. |
91 |
5930 |
ite = nm->mkNode( |
92 |
|
kind::ITE, |
93 |
4744 |
nm->mkNode(kind::AND, |
94 |
2372 |
nm->mkNode(kind::EQUAL, x, nm->mkConst<Rational>(i)), |
95 |
2372 |
nm->mkNode(kind::EQUAL, y, nm->mkConst<Rational>(j))), |
96 |
2372 |
nm->mkConst<Rational>(table.at(std::make_pair(i, j))), |
97 |
|
ite); |
98 |
|
} |
99 |
|
} |
100 |
82 |
return ite; |
101 |
|
} |
102 |
|
|
103 |
21 |
Node IAndUtils::createSumNode(Node x, |
104 |
|
Node y, |
105 |
|
uint64_t bvsize, |
106 |
|
uint64_t granularity) |
107 |
|
{ |
108 |
21 |
NodeManager* nm = NodeManager::currentNM(); |
109 |
21 |
Assert(0 < granularity && granularity <= 8); |
110 |
|
// Standardize granularity. |
111 |
|
// If it is greater than bvsize, it is set to bvsize. |
112 |
|
// Otherwise, it is set to the closest (going down) divider of bvsize. |
113 |
21 |
if (granularity > bvsize) |
114 |
|
{ |
115 |
|
granularity = bvsize; |
116 |
|
} |
117 |
|
else |
118 |
|
{ |
119 |
21 |
while (bvsize % granularity != 0) |
120 |
|
{ |
121 |
|
granularity = granularity - 1; |
122 |
|
} |
123 |
|
} |
124 |
|
|
125 |
|
// Create the sum. |
126 |
|
// For granularity 1, the sum has bvsize elements. |
127 |
|
// In contrast, if bvsize = granularity, sum has one element. |
128 |
|
// Each element in the sum is an ite that corresponds to the generated table, |
129 |
|
// multiplied by the appropriate power of two. |
130 |
|
// More details are in bv_to_int.h . |
131 |
|
|
132 |
|
// number of elements in the sum expression |
133 |
21 |
uint64_t sumSize = bvsize / granularity; |
134 |
|
// initialize the sum |
135 |
21 |
Node sumNode = nm->mkConst<Rational>(0); |
136 |
|
// compute the table for the current granularity if needed |
137 |
21 |
if (d_bvandTable.find(granularity) == d_bvandTable.end()) |
138 |
|
{ |
139 |
21 |
computeAndTable(granularity); |
140 |
|
} |
141 |
|
const std::map<std::pair<int64_t, int64_t>, uint64_t>& table = |
142 |
21 |
d_bvandTable[granularity]; |
143 |
87 |
for (uint64_t i = 0; i < sumSize; i++) |
144 |
|
{ |
145 |
|
// compute the current blocks of x and y |
146 |
132 |
Node xExtract = intExtract(x, i, granularity); |
147 |
132 |
Node yExtract = intExtract(y, i, granularity); |
148 |
|
// compute the ite for this part |
149 |
132 |
Node sumPart = createITEFromTable(xExtract, yExtract, granularity, table); |
150 |
|
// append the current block to the sum |
151 |
66 |
sumNode = |
152 |
132 |
nm->mkNode(kind::PLUS, |
153 |
|
sumNode, |
154 |
132 |
nm->mkNode(kind::MULT, pow2(i * granularity), sumPart)); |
155 |
|
} |
156 |
21 |
return sumNode; |
157 |
|
} |
158 |
|
|
159 |
16 |
Node IAndUtils::createBitwiseIAndNode(Node x, |
160 |
|
Node y, |
161 |
|
uint64_t high, |
162 |
|
uint64_t low) |
163 |
|
{ |
164 |
16 |
uint64_t granularity = high - low + 1; |
165 |
16 |
Assert(granularity <= 8); |
166 |
|
// compute the table for the current granularity if needed |
167 |
16 |
if (d_bvandTable.find(granularity) == d_bvandTable.end()) |
168 |
|
{ |
169 |
12 |
computeAndTable(granularity); |
170 |
|
} |
171 |
|
const std::map<std::pair<int64_t, int64_t>, uint64_t>& table = |
172 |
16 |
d_bvandTable[granularity]; |
173 |
|
return createITEFromTable( |
174 |
16 |
iextract(high, low, x), iextract(high, low, y), granularity, table); |
175 |
|
} |
176 |
|
|
177 |
48 |
Node IAndUtils::iextract(unsigned i, unsigned j, Node n) const |
178 |
|
{ |
179 |
48 |
NodeManager* nm = NodeManager::currentNM(); |
180 |
|
// ((_ extract i j) n) is n / 2^j mod 2^{i-j+1} |
181 |
96 |
Node n2j = nm->mkNode(kind::INTS_DIVISION_TOTAL, n, twoToK(j)); |
182 |
48 |
Node ret = nm->mkNode(kind::INTS_MODULUS_TOTAL, n2j, twoToK(i - j + 1)); |
183 |
48 |
ret = Rewriter::rewrite(ret); |
184 |
96 |
return ret; |
185 |
|
} |
186 |
|
|
187 |
33 |
void IAndUtils::computeAndTable(uint64_t granularity) |
188 |
|
{ |
189 |
33 |
Assert(d_bvandTable.find(granularity) == d_bvandTable.end()); |
190 |
|
// the table was not yet computed |
191 |
66 |
std::map<std::pair<int64_t, int64_t>, uint64_t> table; |
192 |
33 |
uint64_t num_of_values = ((uint64_t)pow(2, granularity)); |
193 |
|
// populate the table with all the values |
194 |
195 |
for (uint64_t i = 0; i < num_of_values; i++) |
195 |
|
{ |
196 |
1878 |
for (uint64_t j = 0; j < num_of_values; j++) |
197 |
|
{ |
198 |
|
// compute |
199 |
|
// (bv_to_int (bvand ((int_to_bv granularity) i) ((int_to_bv granularity) |
200 |
|
// j))) |
201 |
1716 |
int64_t sum = 0; |
202 |
8136 |
for (uint64_t n = 0; n < granularity; n++) |
203 |
|
{ |
204 |
|
// b is the result of f on the current bit |
205 |
6420 |
bool b = oneBitAnd((((i >> n) & 1) == 1), (((j >> n) & 1) == 1)); |
206 |
|
// add the corresponding power of 2 only if the result is 1 |
207 |
6420 |
if (b) |
208 |
|
{ |
209 |
1605 |
sum += 1 << n; |
210 |
|
} |
211 |
|
} |
212 |
1716 |
table[std::make_pair(i, j)] = sum; |
213 |
|
} |
214 |
|
} |
215 |
|
// optimize the table by identifying and adding the default value |
216 |
33 |
addDefaultValue(table, num_of_values); |
217 |
33 |
Assert(table.size() |
218 |
|
== 1 + (static_cast<uint64_t>(num_of_values * num_of_values))); |
219 |
|
// store the table in the cache and return it |
220 |
33 |
d_bvandTable[granularity] = table; |
221 |
33 |
} |
222 |
|
|
223 |
33 |
void IAndUtils::addDefaultValue( |
224 |
|
std::map<std::pair<int64_t, int64_t>, uint64_t>& table, |
225 |
|
uint64_t num_of_values) |
226 |
|
{ |
227 |
|
// map each result to the number of times it occurs |
228 |
66 |
std::map<uint64_t, uint64_t> counters; |
229 |
228 |
for (uint64_t i = 0; i <= num_of_values; i++) |
230 |
|
{ |
231 |
195 |
counters[i] = 0; |
232 |
|
} |
233 |
1749 |
for (const auto& element : table) |
234 |
|
{ |
235 |
1716 |
uint64_t result = element.second; |
236 |
1716 |
counters[result]++; |
237 |
|
} |
238 |
|
|
239 |
|
// compute the most common result |
240 |
33 |
uint64_t most_common_result = 0; |
241 |
33 |
uint64_t max_num_of_occ = 0; |
242 |
228 |
for (uint64_t i = 0; i <= num_of_values; i++) |
243 |
|
{ |
244 |
195 |
if (counters[i] >= max_num_of_occ) |
245 |
|
{ |
246 |
33 |
max_num_of_occ = counters[i]; |
247 |
33 |
most_common_result = i; |
248 |
|
} |
249 |
|
} |
250 |
|
// sanity check: some value appears at least once. |
251 |
33 |
Assert(max_num_of_occ != 0); |
252 |
|
|
253 |
|
// -1 is the default case of the table. |
254 |
|
// add it to the table |
255 |
33 |
table[std::make_pair(-1, -1)] = most_common_result; |
256 |
33 |
} |
257 |
|
|
258 |
187 |
Node IAndUtils::twoToK(unsigned k) const |
259 |
|
{ |
260 |
|
// could be faster |
261 |
187 |
NodeManager* nm = NodeManager::currentNM(); |
262 |
187 |
Node ret = nm->mkNode(kind::POW, d_two, nm->mkConst(Rational(k))); |
263 |
187 |
ret = Rewriter::rewrite(ret); |
264 |
187 |
return ret; |
265 |
|
} |
266 |
|
|
267 |
|
Node IAndUtils::twoToKMinusOne(unsigned k) const |
268 |
|
{ |
269 |
|
// could be faster |
270 |
|
NodeManager* nm = NodeManager::currentNM(); |
271 |
|
Node ret = nm->mkNode(kind::MINUS, twoToK(k), d_one); |
272 |
|
ret = Rewriter::rewrite(ret); |
273 |
|
return ret; |
274 |
|
} |
275 |
|
|
276 |
|
} // namespace nl |
277 |
|
} // namespace arith |
278 |
|
} // namespace theory |
279 |
28191 |
} // namespace cvc5 |