Branch data Line data Source code
1 : : /******************************************************************************
2 : : * This file is part of the cvc5 project.
3 : : *
4 : : * Copyright (c) 2009-2026 by the authors listed in the file AUTHORS
5 : : * in the top-level source directory and their institutional affiliations.
6 : : * All rights reserved. See the file COPYING in the top-level source
7 : : * directory for licensing information.
8 : : * ****************************************************************************
9 : : *
10 : : * Gaussian Elimination preprocessing pass.
11 : : *
12 : : * Simplify a given equation system modulo a (prime) number via Gaussian
13 : : * Elimination if possible.
14 : : */
15 : :
16 : : #include "preprocessing/passes/bv_gauss.h"
17 : :
18 : : #include <unordered_map>
19 : : #include <vector>
20 : :
21 : : #include "expr/node.h"
22 : : #include "preprocessing/assertion_pipeline.h"
23 : : #include "preprocessing/preprocessing_pass_context.h"
24 : : #include "theory/bv/theory_bv_rewrite_rules_normalization.h"
25 : : #include "theory/bv/theory_bv_utils.h"
26 : : #include "theory/rewriter.h"
27 : : #include "util/bitvector.h"
28 : :
29 : : using namespace cvc5::internal;
30 : : using namespace cvc5::internal::theory;
31 : : using namespace cvc5::internal::theory::bv;
32 : :
33 : : namespace cvc5::internal {
34 : : namespace preprocessing {
35 : : namespace passes {
36 : :
37 : 2901 : bool BVGauss::is_bv_const(Node n)
38 : : {
39 [ + + ]: 2901 : if (n.isConst())
40 : : {
41 : 905 : return true;
42 : : }
43 : 1996 : return rewrite(n).getKind() == Kind::CONST_BITVECTOR;
44 : : }
45 : :
46 : 451 : Node BVGauss::get_bv_const(Node n)
47 : : {
48 [ - + ][ - + ]: 451 : Assert(is_bv_const(n));
[ - - ]
49 : 451 : return rewrite(n);
50 : : }
51 : :
52 : 241 : Integer BVGauss::get_bv_const_value(Node n)
53 : : {
54 [ - + ][ - + ]: 241 : Assert(is_bv_const(n));
[ - - ]
55 : 482 : return get_bv_const(n).getConst<BitVector>().getValue();
56 : : }
57 : :
58 : : /**
59 : : * Determines if an overflow may occur in given 'expr'.
60 : : *
61 : : * Returns 0 if an overflow may occur, and the minimum required
62 : : * bit-width such that no overflow occurs, otherwise.
63 : : *
64 : : * Note that it would suffice for this function to be Boolean.
65 : : * However, it is handy to determine the minimum required bit-width for
66 : : * debugging purposes.
67 : : *
68 : : * Note: getMinBwExpr assumes that 'expr' is rewritten.
69 : : *
70 : : * If not, all operators that are removed via rewriting (e.g., ror, rol, ...)
71 : : * will be handled via the default case, which is not incorrect but also not
72 : : * necessarily the minimum.
73 : : */
74 : 101 : uint32_t BVGauss::getMinBwExpr(Node expr)
75 : : {
76 : 101 : std::vector<Node> visit;
77 : : /* Maps visited nodes to the determined minimum bit-width required. */
78 : 101 : std::unordered_map<Node, unsigned> visited;
79 : 101 : std::unordered_map<Node, unsigned>::iterator it;
80 : :
81 : 101 : visit.push_back(expr);
82 : 101 : NodeManager* nm = nodeManager();
83 [ + + ]: 1459 : while (!visit.empty())
84 : : {
85 : 1361 : Node n = visit.back();
86 : 1361 : visit.pop_back();
87 : 1361 : it = visited.find(n);
88 [ + + ]: 1361 : if (it == visited.end())
89 : : {
90 [ + + ]: 745 : if (is_bv_const(n))
91 : : {
92 : : /* Rewrite const expr, overflows in consts are irrelevant. */
93 : 210 : visited[n] = get_bv_const(n).getConst<BitVector>().getValue().length();
94 : : }
95 : : else
96 : : {
97 : 535 : visited[n] = 0;
98 : 535 : visit.push_back(n);
99 [ + + ]: 1263 : for (const Node& nn : n)
100 : : {
101 : 728 : visit.push_back(nn);
102 : 728 : }
103 : : }
104 : : }
105 [ + + ]: 616 : else if (it->second == 0)
106 : : {
107 : 533 : Kind k = n.getKind();
108 [ - + ][ - + ]: 533 : Assert(k != Kind::CONST_BITVECTOR);
[ - - ]
109 [ - + ][ - + ]: 533 : Assert(!is_bv_const(n));
[ - - ]
110 [ + + ][ + + ]: 533 : switch (k)
[ + - ][ + + ]
111 : : {
112 : 28 : case Kind::BITVECTOR_EXTRACT:
113 : : {
114 : 28 : const unsigned size = bv::utils::getSize(n);
115 : 28 : const unsigned low = bv::utils::getExtractLow(n);
116 : 28 : const unsigned child_min_width = visited[n[0]];
117 : 28 : visited[n] = std::min(
118 [ + - ]: 28 : size, child_min_width >= low ? child_min_width - low : 0u);
119 [ - + ][ - + ]: 28 : Assert(visited[n] <= visited[n[0]]);
[ - - ]
120 : 28 : break;
121 : : }
122 : :
123 : 36 : case Kind::BITVECTOR_ZERO_EXTEND:
124 : : {
125 : 36 : visited[n] = visited[n[0]];
126 : 36 : break;
127 : : }
128 : :
129 : 69 : case Kind::BITVECTOR_MULT:
130 : : {
131 : 69 : Integer maxval = Integer(1);
132 [ + + ]: 213 : for (const Node& nn : n)
133 : : {
134 [ + + ]: 144 : if (is_bv_const(nn))
135 : : {
136 : 57 : maxval *= get_bv_const_value(nn);
137 : : }
138 : : else
139 : : {
140 : 87 : maxval *= BitVector::mkOnes(visited[nn]).getValue();
141 : : }
142 : 144 : }
143 : 69 : unsigned w = maxval.length();
144 [ + + ]: 69 : if (w > bv::utils::getSize(n))
145 : : {
146 : 2 : return 0;
147 : : } /* overflow */
148 : 67 : visited[n] = w;
149 : 67 : break;
150 [ + + ]: 69 : }
151 : :
152 : 164 : case Kind::BITVECTOR_CONCAT:
153 : : {
154 : : unsigned i, wnz, nc;
155 [ + + ]: 325 : for (i = 0, wnz = 0, nc = n.getNumChildren() - 1; i < nc; ++i)
156 : : {
157 : 189 : unsigned wni = bv::utils::getSize(n[i]);
158 [ + + ]: 189 : if (n[i] != bv::utils::mkZero(nm, wni))
159 : : {
160 : 28 : break;
161 : : }
162 : : /* sum of all bit-widths of leading zero concats */
163 : 161 : wnz += wni;
164 : : }
165 : : /* Do not consider leading zero concats, i.e.,
166 : : * min bw of current concat is determined as
167 : : * min bw of first non-zero term
168 : : * plus actual bw of all subsequent terms */
169 : : // Use nSize to ensure deterministic node ID assignments
170 : 164 : unsigned nSize = bv::utils::getSize(n);
171 : 164 : visited[n] = nSize + visited[n[i]] - bv::utils::getSize(n[i]) - wnz;
172 : 164 : break;
173 : : }
174 : :
175 : 3 : case Kind::BITVECTOR_UREM:
176 : : case Kind::BITVECTOR_LSHR:
177 : : case Kind::BITVECTOR_ASHR:
178 : : {
179 : 3 : visited[n] = visited[n[0]];
180 : 3 : break;
181 : : }
182 : :
183 : 0 : case Kind::BITVECTOR_OR:
184 : : case Kind::BITVECTOR_NOR:
185 : : case Kind::BITVECTOR_XOR:
186 : : case Kind::BITVECTOR_XNOR:
187 : : case Kind::BITVECTOR_AND:
188 : : case Kind::BITVECTOR_NAND:
189 : : {
190 : 0 : unsigned wmax = 0;
191 [ - - ]: 0 : for (const Node& nn : n)
192 : : {
193 [ - - ]: 0 : if (visited[nn] > wmax)
194 : : {
195 : 0 : wmax = visited[nn];
196 : : }
197 : 0 : }
198 : 0 : visited[n] = wmax;
199 : 0 : break;
200 : : }
201 : :
202 : 52 : case Kind::BITVECTOR_ADD:
203 : : {
204 : 52 : Integer maxval = Integer(0);
205 [ + + ]: 182 : for (const Node& nn : n)
206 : : {
207 [ - + ]: 130 : if (is_bv_const(nn))
208 : : {
209 : 0 : maxval += get_bv_const_value(nn);
210 : : }
211 : : else
212 : : {
213 : 130 : maxval += BitVector::mkOnes(visited[nn]).getValue();
214 : : }
215 : 130 : }
216 : 52 : unsigned w = maxval.length();
217 [ + + ]: 52 : if (w > bv::utils::getSize(n))
218 : : {
219 : 1 : return 0;
220 : : } /* overflow */
221 : 51 : visited[n] = w;
222 : 51 : break;
223 [ + + ]: 52 : }
224 : :
225 : 181 : default:
226 : : {
227 : : /* BITVECTOR_UDIV (since x / 0 = -1)
228 : : * BITVECTOR_NOT
229 : : * BITVECTOR_NEG
230 : : * BITVECTOR_SHL */
231 : 181 : visited[n] = bv::utils::getSize(n);
232 : : }
233 : : }
234 : : }
235 [ + + ]: 1361 : }
236 [ - + ][ - + ]: 98 : Assert(visited.find(expr) != visited.end());
[ - - ]
237 : 98 : return visited[expr];
238 : 101 : }
239 : :
240 : : /**
241 : : * Apply Gaussian Elimination modulo a (prime) number.
242 : : * The given equation system is represented as a matrix of Integers.
243 : : *
244 : : * Note that given 'prime' does not have to be prime but can be any
245 : : * arbitrary number. However, if 'prime' is indeed prime, GE is guaranteed
246 : : * to succeed, which is not the case, otherwise.
247 : : *
248 : : * Returns INVALID if GE can not be applied, UNIQUE and PARTIAL if GE was
249 : : * successful, and NONE, otherwise.
250 : : *
251 : : * Vectors 'rhs' and 'lhs' represent the right hand side and left hand side
252 : : * of the given matrix, respectively. The resulting matrix (in row echelon
253 : : * form) is stored in 'rhs' and 'lhs', i.e., the given matrix is overwritten
254 : : * with the resulting matrix.
255 : : */
256 : 70 : BVGauss::Result BVGauss::gaussElim(Integer prime,
257 : : std::vector<Integer>& rhs,
258 : : std::vector<std::vector<Integer>>& lhs)
259 : : {
260 [ - + ][ - + ]: 70 : Assert(prime > 0);
[ - - ]
261 [ - + ][ - + ]: 70 : Assert(lhs.size());
[ - - ]
262 [ - + ][ - + ]: 70 : Assert(lhs.size() == rhs.size());
[ - - ]
263 [ - + ][ - + ]: 70 : Assert(lhs.size() <= lhs[0].size());
[ - - ]
264 : :
265 : : /* special case: zero ring */
266 [ + + ]: 70 : if (prime == 1)
267 : : {
268 : 1 : rhs = std::vector<Integer>(rhs.size(), Integer(0));
269 : 3 : lhs = std::vector<std::vector<Integer>>(
270 : 3 : lhs.size(), std::vector<Integer>(lhs[0].size(), Integer(0)));
271 : 1 : return BVGauss::Result::UNIQUE;
272 : : }
273 : :
274 : 69 : size_t nrows = lhs.size();
275 : 69 : size_t ncols = lhs[0].size();
276 : :
277 : : #ifdef CVC5_ASSERTIONS
278 [ - + ][ - + ]: 195 : for (size_t i = 1; i < nrows; ++i) Assert(lhs[i].size() == ncols);
[ + + ][ - - ]
279 : : #endif
280 : : /* (1) if element in pivot column is non-zero and != 1, divide row elements
281 : : * by element in pivot column modulo prime, i.e., multiply row with
282 : : * multiplicative inverse of element in pivot column modulo prime
283 : : *
284 : : * (2) subtract pivot row from all rows below pivot row
285 : : *
286 : : * (3) subtract (multiple of) current row from all rows above s.t. all
287 : : * elements in current pivot column above current row become equal to one
288 : : *
289 : : * Note: we do not normalize the given matrix to values modulo prime
290 : : * beforehand but on-the-fly. */
291 : :
292 : : /* pivot = lhs[pcol][pcol] */
293 [ + + ][ + + ]: 234 : for (size_t pcol = 0, prow = 0; pcol < ncols && prow < nrows; ++pcol, ++prow)
294 : : {
295 : : /* lhs[j][pcol]: element in pivot column */
296 [ + + ]: 510 : for (size_t j = prow; j < nrows; ++j)
297 : : {
298 : : #ifdef CVC5_ASSERTIONS
299 [ + + ]: 572 : for (size_t k = 0; k < pcol; ++k)
300 : : {
301 [ - + ][ - + ]: 227 : Assert(lhs[j][k] == 0);
[ - - ]
302 : : }
303 : : #endif
304 : : /* normalize element in pivot column to modulo prime */
305 : 345 : lhs[j][pcol] = lhs[j][pcol].euclidianDivideRemainder(prime);
306 : : /* exchange rows if pivot elem is 0 */
307 [ + + ]: 345 : if (j == prow)
308 : : {
309 [ + + ]: 209 : while (lhs[j][pcol] == 0)
310 : : {
311 [ + + ]: 85 : for (size_t k = prow + 1; k < nrows; ++k)
312 : : {
313 : 53 : lhs[k][pcol] = lhs[k][pcol].euclidianDivideRemainder(prime);
314 [ + + ]: 53 : if (lhs[k][pcol] != 0)
315 : : {
316 : 27 : std::swap(rhs[j], rhs[k]);
317 : 27 : std::swap(lhs[j], lhs[k]);
318 : 27 : break;
319 : : }
320 : : }
321 [ + + ]: 59 : if (pcol >= ncols - 1) break;
322 [ + + ]: 38 : if (lhs[j][pcol] == 0)
323 : : {
324 : 16 : pcol += 1;
325 [ + + ]: 16 : if (lhs[j][pcol] != 0)
326 : 10 : lhs[j][pcol] = lhs[j][pcol].euclidianDivideRemainder(prime);
327 : : }
328 : : }
329 : : }
330 : :
331 [ + + ]: 345 : if (lhs[j][pcol] != 0)
332 : : {
333 : : /* (1) */
334 [ + + ]: 264 : if (lhs[j][pcol] != 1)
335 : : {
336 : 196 : Integer inv = lhs[j][pcol].modInverse(prime);
337 [ + + ]: 196 : if (inv == -1)
338 : : {
339 : 6 : return BVGauss::Result::INVALID; /* not coprime */
340 : : }
341 [ + + ]: 626 : for (size_t k = pcol; k < ncols; ++k)
342 : : {
343 : 436 : lhs[j][k] = lhs[j][k].modMultiply(inv, prime);
344 [ + + ]: 436 : if (j <= prow) continue; /* pivot */
345 : 246 : lhs[j][k] = lhs[j][k].modAdd(-lhs[prow][k], prime);
346 : : }
347 : 190 : rhs[j] = rhs[j].modMultiply(inv, prime);
348 [ + + ]: 190 : if (j > prow)
349 : : {
350 : 92 : rhs[j] = rhs[j].modAdd(-rhs[prow], prime);
351 : : }
352 [ + + ]: 196 : }
353 : : /* (2) */
354 [ + + ]: 68 : else if (j != prow)
355 : : {
356 [ + + ]: 46 : for (size_t k = pcol; k < ncols; ++k)
357 : : {
358 : 34 : lhs[j][k] = lhs[j][k].modAdd(-lhs[prow][k], prime);
359 : : }
360 : 12 : rhs[j] = rhs[j].modAdd(-rhs[prow], prime);
361 : : }
362 : : }
363 : : }
364 : : /* (3) */
365 [ + + ]: 305 : for (size_t j = 0; j < prow; ++j)
366 : : {
367 : 140 : Integer mul = lhs[j][pcol];
368 [ + + ]: 140 : if (mul != 0)
369 : : {
370 [ + + ]: 276 : for (size_t k = pcol; k < ncols; ++k)
371 : : {
372 : 162 : lhs[j][k] = lhs[j][k].modAdd(-lhs[prow][k] * mul, prime);
373 : : }
374 : 114 : rhs[j] = rhs[j].modAdd(-rhs[prow] * mul, prime);
375 : : }
376 : 140 : }
377 : : }
378 : :
379 : 63 : bool ispart = false;
380 [ + + ]: 236 : for (size_t i = 0; i < nrows; ++i)
381 : : {
382 : 177 : size_t pcol = i;
383 [ + + ][ + + ]: 229 : while (pcol < ncols && lhs[i][pcol] == 0) ++pcol;
[ + + ][ + + ]
[ - - ]
384 [ + + ]: 177 : if (pcol >= ncols)
385 : : {
386 : 29 : rhs[i] = rhs[i].euclidianDivideRemainder(prime);
387 [ + + ]: 29 : if (rhs[i] != 0)
388 : : {
389 : : /* no solution */
390 : 4 : return BVGauss::Result::NONE;
391 : : }
392 : 25 : continue;
393 : : }
394 [ + + ]: 488 : for (size_t j = i; j < ncols; ++j)
395 : : {
396 [ + + ][ - + ]: 340 : if (lhs[i][j] >= prime || lhs[i][j] <= -prime)
[ + + ][ + + ]
[ - - ]
397 : : {
398 : 1 : lhs[i][j] = lhs[i][j].euclidianDivideRemainder(prime);
399 : : }
400 [ + + ][ + + ]: 340 : if (j > pcol && lhs[i][j] != 0)
[ + + ][ + + ]
[ - - ]
401 : : {
402 : 36 : ispart = true;
403 : : }
404 : : }
405 : : }
406 : :
407 [ + + ]: 59 : if (ispart)
408 : : {
409 : 21 : return BVGauss::Result::PARTIAL;
410 : : }
411 : :
412 : 38 : return BVGauss::Result::UNIQUE;
413 : : }
414 : :
415 : : /**
416 : : * Apply Gaussian Elimination on a set of equations modulo some (prime)
417 : : * number given as bit-vector equations.
418 : : *
419 : : * IMPORTANT: Applying GE modulo some number (rather than modulo 2^bw)
420 : : * on a set of bit-vector equations is only sound if this set of equations
421 : : * has a solution that does not produce overflows. Consequently, we only
422 : : * apply GE if the given bit-width guarantees that no overflows can occur
423 : : * in the given set of equations.
424 : : *
425 : : * Note that the given set of equations does not have to be modulo a prime
426 : : * but can be modulo any arbitrary number. However, if it is indeed modulo
427 : : * prime, GE is guaranteed to succeed, which is not the case, otherwise.
428 : : *
429 : : * Returns INVALID if GE can not be applied, UNIQUE and PARTIAL if GE was
430 : : * successful, and NONE, otherwise.
431 : : *
432 : : * The resulting constraints are stored in 'res' as a mapping of unknown
433 : : * to result (modulo prime). These mapped results are added as constraints
434 : : * of the form 'unknown = mapped result' in applyInternal.
435 : : */
436 : 18 : BVGauss::Result BVGauss::gaussElimRewriteForUrem(
437 : : const std::vector<Node>& equations, std::unordered_map<Node, Node>& res)
438 : : {
439 [ - + ][ - + ]: 18 : Assert(res.empty());
[ - - ]
440 : :
441 : 18 : Node prime;
442 : 18 : Integer iprime;
443 : 18 : std::unordered_map<Node, std::vector<Integer>> vars;
444 : 18 : size_t neqs = equations.size();
445 : 18 : std::vector<Integer> rhs;
446 : : std::vector<std::vector<Integer>> lhs =
447 : 36 : std::vector<std::vector<Integer>>(neqs, std::vector<Integer>());
448 : :
449 : 18 : res = std::unordered_map<Node, Node>();
450 : :
451 : 18 : NodeManager* nm = nodeManager();
452 [ + + ]: 64 : for (size_t i = 0; i < neqs; ++i)
453 : : {
454 : 46 : Node eq = equations[i];
455 [ - + ][ - + ]: 46 : Assert(eq.getKind() == Kind::EQUAL);
[ - - ]
456 : 46 : Node urem, eqrhs;
457 : :
458 [ + - ]: 46 : if (eq[0].getKind() == Kind::BITVECTOR_UREM)
459 : : {
460 : 46 : urem = eq[0];
461 [ - + ][ - + ]: 46 : Assert(is_bv_const(eq[1]));
[ - - ]
462 : 46 : eqrhs = eq[1];
463 : : }
464 : : else
465 : : {
466 : 0 : Assert(eq[1].getKind() == Kind::BITVECTOR_UREM);
467 : 0 : urem = eq[1];
468 : 0 : Assert(is_bv_const(eq[0]));
469 : 0 : eqrhs = eq[0];
470 : : }
471 [ - + ]: 46 : if (getMinBwExpr(rewrite(urem[0])) == 0)
472 : : {
473 [ - - ]: 0 : Trace("bv-gauss-elim")
474 : : << "Minimum required bit-width exceeds given bit-width, "
475 : 0 : "will not apply Gaussian Elimination."
476 : 0 : << std::endl;
477 : 0 : return BVGauss::Result::INVALID;
478 : : }
479 : 46 : rhs.push_back(get_bv_const_value(eqrhs));
480 : :
481 [ - + ][ - + ]: 46 : Assert(is_bv_const(urem[1]));
[ - - ]
482 : 46 : Assert(i == 0 || get_bv_const_value(urem[1]) == iprime);
483 [ + + ]: 46 : if (i == 0)
484 : : {
485 : 18 : prime = urem[1];
486 : 18 : iprime = get_bv_const_value(prime);
487 : : }
488 : :
489 : 46 : std::unordered_map<Node, Integer> tmp;
490 : 46 : std::vector<Node> stack;
491 : 46 : stack.push_back(urem[0]);
492 [ + + ]: 198 : while (!stack.empty())
493 : : {
494 : 152 : Node n = stack.back();
495 : 152 : stack.pop_back();
496 : :
497 : : /* Subtract from rhs if const */
498 [ - + ]: 152 : if (is_bv_const(n))
499 : : {
500 : 0 : Integer val = get_bv_const_value(n);
501 [ - - ]: 0 : if (val > 0) rhs.back() -= val;
502 : 0 : continue;
503 : 0 : }
504 : :
505 : : /* Split into matrix columns */
506 : 152 : Kind k = n.getKind();
507 [ + + ]: 152 : if (k == Kind::BITVECTOR_ADD)
508 : : {
509 [ + + ]: 159 : for (const Node& nn : n)
510 : : {
511 : 106 : stack.push_back(nn);
512 : 106 : }
513 : : }
514 [ + + ]: 99 : else if (k == Kind::BITVECTOR_MULT)
515 : : {
516 : 92 : Node n0, n1;
517 : : /* Flatten mult expression. */
518 : 92 : n = RewriteRule<FlattenAssocCommut>::run<true>(n);
519 : : /* Split operands into consts and non-consts */
520 : 92 : NodeBuilder nb_consts(nm, k);
521 : 92 : NodeBuilder nb_nonconsts(nm, k);
522 [ + + ]: 284 : for (const Node& nn : n)
523 : : {
524 : 192 : Node nnrw = rewrite(nn);
525 [ + + ]: 192 : if (is_bv_const(nnrw))
526 : : {
527 : 88 : nb_consts << nnrw;
528 : : }
529 : : else
530 : : {
531 : 104 : nb_nonconsts << nnrw;
532 : : }
533 : 192 : }
534 [ - + ][ - + ]: 92 : Assert(nb_nonconsts.getNumChildren() > 0);
[ - - ]
535 : : /* n0 is const */
536 : 92 : unsigned nc = nb_consts.getNumChildren();
537 [ - + ]: 92 : if (nc > 1)
538 : : {
539 : 0 : n0 = rewrite(nb_consts.constructNode());
540 : : }
541 [ + + ]: 92 : else if (nc == 1)
542 : : {
543 : 88 : n0 = nb_consts[0];
544 : : }
545 : : else
546 : : {
547 : 4 : n0 = bv::utils::mkOne(nm, bv::utils::getSize(n));
548 : : }
549 : : /* n1 is a mult with non-const operands */
550 [ + + ]: 92 : if (nb_nonconsts.getNumChildren() > 1)
551 : : {
552 : 10 : n1 = rewrite(nb_nonconsts.constructNode());
553 : : }
554 : : else
555 : : {
556 : 82 : n1 = nb_nonconsts[0];
557 : : }
558 [ - + ][ - + ]: 92 : Assert(is_bv_const(n0));
[ - - ]
559 [ - + ][ - + ]: 92 : Assert(!is_bv_const(n1));
[ - - ]
560 : 92 : tmp[n1] += get_bv_const_value(n0);
561 : 92 : }
562 : : else
563 : : {
564 : 7 : tmp[n] += Integer(1);
565 : : }
566 [ + - ]: 152 : }
567 : :
568 : : /* Note: "var" is not necessarily a VARIABLE but can be an arbitrary expr */
569 : :
570 [ + + ]: 145 : for (const auto& p : tmp)
571 : : {
572 : 99 : Node var = p.first;
573 : 99 : Integer val = p.second;
574 [ + + ][ + + ]: 99 : if (i > 0 && vars.find(var) == vars.end())
[ + + ]
575 : : {
576 : : /* Add column and fill column elements of rows above with 0. */
577 : 14 : vars[var].insert(vars[var].end(), i, Integer(0));
578 : : }
579 : 99 : vars[var].push_back(val);
580 : 99 : }
581 : :
582 [ + + ]: 166 : for (const auto& p : vars)
583 : : {
584 [ + + ]: 120 : if (tmp.find(p.first) == tmp.end())
585 : : {
586 : 21 : vars[p.first].push_back(Integer(0));
587 : : }
588 : : }
589 [ + - ][ + - ]: 46 : }
[ + - ]
590 : :
591 : 18 : size_t nvars = vars.size();
592 [ - + ]: 18 : if (nvars == 0)
593 : : {
594 : 0 : return BVGauss::Result::INVALID;
595 : : }
596 : 18 : size_t nrows = vars.begin()->second.size();
597 : : #ifdef CVC5_ASSERTIONS
598 [ + + ]: 71 : for (const auto& p : vars)
599 : : {
600 [ - + ][ - + ]: 53 : Assert(p.second.size() == nrows);
[ - - ]
601 : : }
602 : : #endif
603 : :
604 [ - + ]: 18 : if (nrows < 1)
605 : : {
606 : 0 : return BVGauss::Result::INVALID;
607 : : }
608 : :
609 [ + + ]: 64 : for (size_t i = 0; i < nrows; ++i)
610 : : {
611 [ + + ]: 182 : for (const auto& p : vars)
612 : : {
613 : 136 : lhs[i].push_back(p.second[i]);
614 : : }
615 : : }
616 : :
617 : : #ifdef CVC5_ASSERTIONS
618 [ + + ]: 64 : for (const auto& row : lhs)
619 : : {
620 [ - + ][ - + ]: 46 : Assert(row.size() == nvars);
[ - - ]
621 : : }
622 [ - + ][ - + ]: 18 : Assert(lhs.size() == rhs.size());
[ - - ]
623 : : #endif
624 : :
625 [ + + ]: 18 : if (lhs.size() > lhs[0].size())
626 : : {
627 : 1 : return BVGauss::Result::INVALID;
628 : : }
629 : :
630 [ + - ]: 17 : Trace("bv-gauss-elim") << "Applying Gaussian Elimination..." << std::endl;
631 : 17 : BVGauss::Result ret = gaussElim(iprime, rhs, lhs);
632 : :
633 [ + - ][ + - ]: 17 : if (ret != BVGauss::Result::NONE && ret != BVGauss::Result::INVALID)
634 : : {
635 : 17 : std::vector<Node> vvars;
636 [ + + ]: 68 : for (const auto& p : vars)
637 : : {
638 : 51 : vvars.push_back(p.first);
639 : : }
640 [ - + ][ - + ]: 17 : Assert(nvars == vvars.size());
[ - - ]
641 [ - + ][ - + ]: 17 : Assert(nrows == lhs.size());
[ - - ]
642 [ - + ][ - + ]: 17 : Assert(nrows == rhs.size());
[ - - ]
643 [ + + ]: 17 : if (ret == BVGauss::Result::UNIQUE)
644 : : {
645 [ + + ]: 27 : for (size_t i = 0; i < nvars; ++i)
646 : : {
647 : 40 : res[vvars[i]] = nm->mkConst<BitVector>(
648 : 60 : BitVector(bv::utils::getSize(vvars[i]), rhs[i]));
649 : : }
650 : : }
651 : : else
652 : : {
653 [ - + ][ - + ]: 10 : Assert(ret == BVGauss::Result::PARTIAL);
[ - - ]
654 : :
655 [ + - ][ + + ]: 30 : for (size_t pcol = 0, prow = 0; pcol < nvars && prow < nrows;
656 : 20 : ++pcol, ++prow)
657 : : {
658 : 22 : Assert(lhs[prow][pcol] == 0 || lhs[prow][pcol] == 1);
659 [ + + ][ + + ]: 25 : while (pcol < nvars && lhs[prow][pcol] == 0) pcol += 1;
[ + + ][ + + ]
[ - - ]
660 [ + + ]: 22 : if (pcol >= nvars)
661 : : {
662 [ - + ][ - + ]: 2 : Assert(rhs[prow] == 0);
[ - - ]
663 : 2 : break;
664 : : }
665 [ - + ]: 20 : if (lhs[prow][pcol] == 0)
666 : : {
667 : 0 : Assert(rhs[prow] == 0);
668 : 0 : continue;
669 : : }
670 [ - + ][ - + ]: 20 : Assert(lhs[prow][pcol] == 1);
[ - - ]
671 : 20 : std::vector<Node> stack;
672 [ + + ]: 51 : for (size_t i = pcol + 1; i < nvars; ++i)
673 : : {
674 [ + + ]: 31 : if (lhs[prow][i] == 0) continue;
675 : : /* Normalize (no negative numbers, hence no subtraction)
676 : : * e.g., x = 4 - 2y --> x = 4 + 9y (modulo 11) */
677 : 17 : Integer m = iprime - lhs[prow][i];
678 : 17 : Node bv = bv::utils::mkConst(nm, bv::utils::getSize(vvars[i]), m);
679 : 34 : Node mult = nm->mkNode(Kind::BITVECTOR_MULT, vvars[i], bv);
680 : 17 : stack.push_back(mult);
681 : 17 : }
682 : :
683 [ + + ]: 20 : if (stack.empty())
684 : : {
685 : 6 : res[vvars[pcol]] = nm->mkConst<BitVector>(
686 : 9 : BitVector(bv::utils::getSize(vvars[pcol]), rhs[prow]));
687 : : }
688 : : else
689 : : {
690 : 34 : Node tmp = stack.size() == 1 ? stack[0]
691 [ + - ]: 34 : : nm->mkNode(Kind::BITVECTOR_ADD, stack);
692 : :
693 [ + + ]: 17 : if (rhs[prow] != 0)
694 : : {
695 : : tmp =
696 : 64 : nm->mkNode(Kind::BITVECTOR_ADD,
697 : 32 : bv::utils::mkConst(
698 : 16 : nm, bv::utils::getSize(vvars[pcol]), rhs[prow]),
699 : 16 : tmp);
700 : : }
701 [ - + ][ - + ]: 17 : Assert(!is_bv_const(tmp));
[ - - ]
702 : 17 : res[vvars[pcol]] = nm->mkNode(Kind::BITVECTOR_UREM, tmp, prime);
703 : 17 : }
704 : 20 : }
705 : : }
706 : 17 : }
707 : 17 : return ret;
708 : 18 : }
709 : :
710 : 51970 : BVGauss::BVGauss(PreprocessingPassContext* preprocContext,
711 : 51970 : const std::string& name)
712 : 51970 : : PreprocessingPass(preprocContext, name)
713 : : {
714 : 51970 : }
715 : :
716 : 3 : PreprocessingPassResult BVGauss::applyInternal(
717 : : AssertionPipeline* assertionsToPreprocess)
718 : : {
719 : 3 : std::vector<Node> assertions(assertionsToPreprocess->ref());
720 : 3 : std::unordered_map<Node, std::vector<Node>> equations;
721 : :
722 [ + + ]: 13 : while (!assertions.empty())
723 : : {
724 : 10 : Node a = assertions.back();
725 : 10 : assertions.pop_back();
726 : 10 : cvc5::internal::Kind k = a.getKind();
727 : :
728 [ - + ]: 10 : if (k == Kind::AND)
729 : : {
730 [ - - ]: 0 : for (const Node& aa : a)
731 : : {
732 : 0 : assertions.push_back(aa);
733 : 0 : }
734 : : }
735 [ + - ]: 10 : else if (k == Kind::EQUAL)
736 : : {
737 : 10 : Node urem;
738 : :
739 : 10 : if (is_bv_const(a[1]) && a[0].getKind() == Kind::BITVECTOR_UREM)
740 : : {
741 : 10 : urem = a[0];
742 : : }
743 : 0 : else if (is_bv_const(a[0]) && a[1].getKind() == Kind::BITVECTOR_UREM)
744 : : {
745 : 0 : urem = a[1];
746 : : }
747 : : else
748 : : {
749 : 0 : continue;
750 : : }
751 : :
752 : 10 : if (urem[0].getKind() == Kind::BITVECTOR_ADD && is_bv_const(urem[1]))
753 : : {
754 : 10 : equations[urem[1]].push_back(a);
755 : : }
756 [ + - ]: 10 : }
757 [ + - ]: 10 : }
758 : :
759 : 3 : std::unordered_map<Node, Node> subst;
760 : :
761 : 3 : NodeManager* nm = nodeManager();
762 [ + + ]: 7 : for (const auto& eq : equations)
763 : : {
764 [ - + ]: 4 : if (eq.second.size() <= 1)
765 : : {
766 : 0 : continue;
767 : : }
768 : :
769 : 4 : std::unordered_map<Node, Node> res;
770 : 4 : BVGauss::Result ret = gaussElimRewriteForUrem(eq.second, res);
771 [ + - ]: 8 : Trace("bv-gauss-elim") << "result: "
772 : : << (ret == BVGauss::Result::INVALID
773 [ - - ]: 4 : ? "INVALID"
774 : : : (ret == BVGauss::Result::UNIQUE
775 [ - - ]: 0 : ? "UNIQUE"
776 : : : (ret == BVGauss::Result::PARTIAL
777 [ - - ]: 0 : ? "PARTIAL"
778 : 0 : : "NONE")))
779 : 4 : << std::endl;
780 [ + - ]: 4 : if (ret != BVGauss::Result::INVALID)
781 : : {
782 [ - + ]: 4 : if (ret == BVGauss::Result::NONE)
783 : : {
784 : 0 : Node n = nm->mkConst<bool>(false);
785 : 0 : assertionsToPreprocess->push_back(
786 : : n, false, nullptr, TrustId::PREPROCESS_BV_GUASS_LEMMA);
787 : 0 : return PreprocessingPassResult::CONFLICT;
788 : 0 : }
789 : : else
790 : : {
791 [ + + ]: 14 : for (const Node& e : eq.second)
792 : : {
793 : 10 : subst[e] = nm->mkConst<bool>(true);
794 : : }
795 : : /* add resulting constraints */
796 [ + + ]: 14 : for (const auto& p : res)
797 : : {
798 : 20 : Node a = nm->mkNode(Kind::EQUAL, p.first, p.second);
799 [ + - ]: 10 : Trace("bv-gauss-elim") << "added assertion: " << a << std::endl;
800 : : // add new assertion
801 : 10 : assertionsToPreprocess->push_back(
802 : : a, false, nullptr, TrustId::PREPROCESS_BV_GUASS_LEMMA);
803 : 10 : }
804 : : }
805 : : }
806 [ + - ]: 4 : }
807 : :
808 [ + - ]: 3 : if (!subst.empty())
809 : : {
810 : : /* delete (= substitute with true) obsolete assertions */
811 : 3 : const std::vector<Node>& aref = assertionsToPreprocess->ref();
812 [ + + ]: 23 : for (size_t i = 0, asize = aref.size(); i < asize; ++i)
813 : : {
814 : 20 : Node a = aref[i];
815 : 20 : Node as = a.substitute(subst.begin(), subst.end());
816 : : // replace the assertion
817 : 20 : assertionsToPreprocess->replace(
818 : : i, as, nullptr, TrustId::PREPROCESS_BV_GUASS);
819 : 20 : }
820 : : }
821 : 3 : return PreprocessingPassResult::NO_CONFLICT;
822 : 3 : }
823 : :
824 : : } // namespace passes
825 : : } // namespace preprocessing
826 : : } // namespace cvc5::internal
|