mirror of
https://github.com/Z3Prover/z3
synced 2025-04-29 03:45:51 +00:00
Add BDD utilities; use them for narrowing/propagation of linear equality constraints (#5192)
* Add a few helper methods for encoding sets of integers as BDDs * Use BDD functions in solver * Add bdd::find_int * Use bdd::find_int in solver * Add narrowing for linear equality constraints * Simplify code for linear propagation * Add test for later * Narrowing can only handle linear constraints with a single variable * Need to push_cjust
This commit is contained in:
parent
e970fe5034
commit
f72e30e539
10 changed files with 208 additions and 104 deletions
|
@ -895,4 +895,81 @@ namespace dd {
|
|||
bdd& bdd::operator=(bdd const& other) { unsigned r1 = root; root = other.root; m->inc_ref(root); m->dec_ref(r1); return *this; }
|
||||
std::ostream& operator<<(std::ostream& out, bdd const& b) { return b.display(out); }
|
||||
|
||||
|
||||
bdd bdd_manager::mk_int(rational const& val, unsigned w) {
|
||||
bdd b = mk_true();
|
||||
for (unsigned k = w; k-- > 0;)
|
||||
b &= val.get_bit(k) ? mk_var(k) : mk_nvar(k);
|
||||
return b;
|
||||
}
|
||||
|
||||
bool bdd_manager::contains_int(BDD b, rational const& val, unsigned w) {
|
||||
while (!is_const(b)) {
|
||||
unsigned const var = m_level2var[level(b)];
|
||||
if (var >= w)
|
||||
b = lo(b);
|
||||
else
|
||||
b = val.get_bit(var) ? hi(b) : lo(b);
|
||||
}
|
||||
return is_true(b);
|
||||
}
|
||||
|
||||
find_int_t bdd_manager::find_int(BDD b, unsigned w, rational& val) {
|
||||
val = 0;
|
||||
if (is_false(b))
|
||||
return find_int_t::empty;
|
||||
bool is_unique = true;
|
||||
unsigned num_vars = 0;
|
||||
while (!is_true(b)) {
|
||||
++num_vars;
|
||||
if (!is_false(lo(b)) && !is_false(hi(b)))
|
||||
is_unique = false;
|
||||
if (is_false(lo(b))) {
|
||||
val += rational::power_of_two(var(b));
|
||||
b = hi(b);
|
||||
}
|
||||
else
|
||||
b = lo(b);
|
||||
}
|
||||
is_unique &= (num_vars == w);
|
||||
|
||||
return is_unique ? find_int_t::singleton : find_int_t::multiple;
|
||||
}
|
||||
|
||||
bdd bdd_manager::mk_affine(rational const& a, rational const& b, unsigned w) {
|
||||
if (a.is_zero())
|
||||
return b.is_zero() ? mk_true() : mk_false();
|
||||
// a*x + b == 0 (mod 2^w)
|
||||
unsigned const rank_a = a.trailing_zeros();
|
||||
unsigned const rank_b = b.is_zero() ? w : b.trailing_zeros();
|
||||
// We have a', b' odd such that:
|
||||
// 2^rank(a) * a'* x + 2^rank(b) * b' == 0 (mod 2^w)
|
||||
if (rank_a > rank_b) {
|
||||
// <=> 2^(rank(a)-rank(b)) * a' * x + b' == 0 (mod 2^(w-rank(b)))
|
||||
// LHS is always odd => equation cannot be true
|
||||
return mk_false();
|
||||
}
|
||||
else if (b.is_zero()) {
|
||||
// this is just a specialization of the else-branch below
|
||||
return mk_int(rational::zero(), w - rank_a);
|
||||
}
|
||||
else {
|
||||
unsigned const j = w - rank_a;
|
||||
// Let b'' := 2^(rank(b)-rank(a)) * b', then we have:
|
||||
// <=> a' * x + b'' == 0 (mod 2^j)
|
||||
// <=> x == -b'' * inverse_j(a') (mod 2^j)
|
||||
// Now the question is, for what x in Z_{2^w} does this hold?
|
||||
// Answer: for all x where the lower j bits are the same as in the RHS of the last equation,
|
||||
// so we just fix those bits and leave the others unconstrained
|
||||
// (which we can do simply by encoding the RHS as a j-width integer).
|
||||
rational const pow2_rank_a = rational::power_of_two(rank_a);
|
||||
rational const aa = a / pow2_rank_a; // a'
|
||||
rational const bb = b / pow2_rank_a; // b''
|
||||
rational inv_aa;
|
||||
VERIFY(aa.mult_inverse(j, inv_aa));
|
||||
rational const cc = mod(inv_aa * -bb, rational::power_of_two(j));
|
||||
return mk_int(cc, j);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,11 +21,14 @@ Revision History:
|
|||
#include "util/vector.h"
|
||||
#include "util/map.h"
|
||||
#include "util/small_object_allocator.h"
|
||||
#include "util/rational.h"
|
||||
|
||||
namespace dd {
|
||||
|
||||
class bdd;
|
||||
|
||||
enum class find_int_t { empty, singleton, multiple };
|
||||
|
||||
class bdd_manager {
|
||||
friend bdd;
|
||||
|
||||
|
@ -189,6 +192,9 @@ namespace dd {
|
|||
bdd mk_or(bdd const& a, bdd const& b);
|
||||
bdd mk_xor(bdd const& a, bdd const& b);
|
||||
|
||||
bool contains_int(BDD b, rational const& val, unsigned w);
|
||||
find_int_t find_int(BDD b, unsigned w, rational& val);
|
||||
|
||||
void reserve_var(unsigned v);
|
||||
bool well_formed();
|
||||
|
||||
|
@ -219,6 +225,20 @@ namespace dd {
|
|||
bdd mk_forall(unsigned v, bdd const& b);
|
||||
bdd mk_ite(bdd const& c, bdd const& t, bdd const& e);
|
||||
|
||||
/** Encodes the lower w bits of val as BDD, using variable indices 0 to w-1.
|
||||
* The least-significant bit is encoded as variable 0.
|
||||
* val must be an integer.
|
||||
*/
|
||||
bdd mk_int(rational const& val, unsigned w);
|
||||
|
||||
/** Encodes the solutions of the affine relation
|
||||
*
|
||||
* a*x + b == 0 (mod 2^w)
|
||||
*
|
||||
* as BDD.
|
||||
*/
|
||||
bdd mk_affine(rational const& a, rational const& b, unsigned w);
|
||||
|
||||
std::ostream& display(std::ostream& out);
|
||||
std::ostream& display(std::ostream& out, bdd const& b);
|
||||
|
||||
|
@ -256,6 +276,12 @@ namespace dd {
|
|||
double cnf_size() const { return m->cnf_size(root); }
|
||||
double dnf_size() const { return m->dnf_size(root); }
|
||||
unsigned bdd_size() const { return m->bdd_size(*this); }
|
||||
|
||||
/** Checks whether the integer val is contained in the BDD when viewed as set of integers (see also mk_int). */
|
||||
bool contains_int(rational const& val, unsigned w) { return m->contains_int(root, val, w); }
|
||||
|
||||
/** Returns an integer contained in the BDD, if any, and whether the BDD is a singleton. */
|
||||
find_int_t find_int(unsigned w, rational& val) { return m->find_int(root, w, val); }
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, bdd const& b);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue