3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-26 10:35:33 +00:00

Add functionality for BDD vectors (#5198)

* Fix XOR over BDDs

* Add operator<< for find_int_t

* Add equality assertion macro that prints expression values on failure

* Adapt contains_int and find_int to take externally-defined bits

* Add more operations on BDD vectors

* Remove old functions

* Additional bddv functions

* Rename some things

* Make bddv a class and add operators

* Fix find_num/contains_num calls
This commit is contained in:
Jakob Rath 2021-04-19 18:05:19 +02:00 committed by GitHub
parent 981839ee73
commit 4da1b7b03c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 499 additions and 138 deletions

View file

@ -74,44 +74,217 @@ namespace dd {
std::cout << c1.bdd_size() << "\n";
}
static void test_xor() {
bdd_manager m(4);
bdd v0 = m.mk_var(0);
bdd v1 = m.mk_var(0);
SASSERT((m.mk_false() ^ v0) == v0);
SASSERT((v0 ^ m.mk_false()) == v0);
SASSERT((m.mk_true() ^ v0) == !v0);
SASSERT((v0 ^ m.mk_true()) == !v0);
SASSERT((v0 ^ v1) == ((v0 && !v1) || (!v0 && v1)));
}
static void test_bddv_ops_on_constants() {
unsigned const num_bits = 4;
rational const modulus = rational::power_of_two(num_bits);
bdd_manager m(num_bits);
SASSERT_EQ(m.to_val(m.mk_zero(num_bits)), rational(0));
SASSERT_EQ(m.to_val(m.mk_ones(num_bits)), modulus - 1);
for (unsigned n = 0; n < 16; ++n) {
rational const nr(n);
SASSERT_EQ(m.to_val(m.mk_num(nr, num_bits)), nr);
}
for (unsigned n = 0; n < 16; ++n) {
for (unsigned k = 0; k < 16; ++k) {
rational const nr(n);
rational const kr(k);
bddv const nv = m.mk_num(nr, num_bits);
bddv const kv = m.mk_num(kr, num_bits);
SASSERT_EQ((nv + kv).to_val(), (nr + kr) % modulus);
SASSERT_EQ((nv * kr).to_val(), (nr * kr) % modulus);
SASSERT_EQ((nv * kv).to_val(), (nr * kr) % modulus);
bdd eq = m.mk_eq(nv, kv);
SASSERT((eq.is_true() || eq.is_false()) && (eq.is_true() == (n == k)));
eq = m.mk_eq(nv, kr);
SASSERT((eq.is_true() || eq.is_false()) && (eq.is_true() == (n == k)));
}
}
}
static void test_bddv_eqfind_small() {
bdd_manager m(4);
unsigned_vector bits;
bits.push_back(0);
bddv const x = m.mk_var(bits);
bddv const one = m.mk_num(rational(1), bits.size());
bdd x_is_one = m.mk_eq(x, one);
std::cout << "x_is_one:\n" << x_is_one << "\n";
rational r;
auto res = x_is_one.find_num(bits, r);
SASSERT_EQ(res, find_result::singleton);
SASSERT_EQ(r, rational(1));
}
static void test_bddv_eqfind() {
unsigned const num_bits = 4;
bdd_manager m(num_bits);
unsigned_vector bits;
bits.push_back(0);
bits.push_back(1);
bits.push_back(4);
bits.push_back(27);
bddv const x = m.mk_var(bits);
bddv const zero = m.mk_zero(num_bits);
bddv const one = m.mk_num(rational(1), num_bits);
bddv const five = m.mk_num(rational(5), num_bits);
SASSERT(m.mk_eq(one, rational(1)).is_true());
SASSERT(m.mk_eq(five, rational(5)).is_true());
SASSERT(m.mk_eq(five, rational(4)).is_false());
SASSERT(m.mk_eq(five, five).is_true());
SASSERT(m.mk_eq(five, one).is_false());
{
bdd const x_is_five = m.mk_eq(x, rational(5));
rational r;
auto res = x_is_five.find_num(bits, r);
SASSERT_EQ(res, find_result::singleton);
SASSERT_EQ(r, rational(5));
}
{
bdd const x_is_five = m.mk_eq(bits, rational(5));
rational r;
auto res = x_is_five.find_num(bits, r);
SASSERT_EQ(res, find_result::singleton);
SASSERT_EQ(r, rational(5));
}
{
bdd const x_is_five = m.mk_eq(x, five);
rational r;
auto res = x_is_five.find_num(bits, r);
SASSERT_EQ(res, find_result::singleton);
SASSERT_EQ(r, rational(5));
}
}
static void test_bddv_mul() {
unsigned const num_bits = 4;
bdd_manager m(num_bits);
unsigned_vector bits;
bits.push_back(0);
bits.push_back(1);
bits.push_back(4);
bits.push_back(27);
bddv const x = m.mk_var(bits);
bddv const zero = m.mk_zero(num_bits);
bddv const one = m.mk_num(rational(1), num_bits);
bddv const five = m.mk_num(rational(5), num_bits);
bddv const six = m.mk_num(rational(6), num_bits);
{
// 5*x == 1 (mod 16) => x == 13 (mod 16)
bdd const five_inv = x * five == one;
rational r;
auto res = five_inv.find_num(bits, r);
SASSERT_EQ(res, find_result::singleton);
SASSERT_EQ(r, rational(13));
}
{
// 6*x == 1 (mod 16) => no solution for x
bdd const six_inv = x * six == one;
rational r;
auto res = six_inv.find_num(bits, r);
SASSERT_EQ(res, find_result::empty);
SASSERT(six_inv.is_false());
}
{
// 6*x == 0 (mod 16) => x is either 0 or 8 (mod 16)
bdd const b = six * x == zero;
rational r;
SASSERT(b.contains_num(rational(0), bits));
SASSERT(b.contains_num(rational(8), bits));
SASSERT((b && (x != rational(0)) && (x != rational(8))).is_false());
SASSERT((b && (x != rational(0))) == (x == rational(8)));
}
SASSERT_EQ((x * zero).get_bits(), (x * rational(0)).get_bits());
SASSERT_EQ((x * one).get_bits(), (x * rational(1)).get_bits());
SASSERT_EQ((x * five).get_bits(), (x * rational(5)).get_bits());
SASSERT_EQ((x * six).get_bits(), (x * rational(6)).get_bits());
}
static void test_int() {
unsigned const w = 3; // bit width
unsigned_vector bits;
bits.push_back(0);
bits.push_back(1);
bits.push_back(2);
bdd_manager m(w);
bddv const x = m.mk_var(bits);
// Encodes the values x satisfying a*x + b == 0 (mod 2^w) as BDD.
auto mk_affine = [] (rational const& a, bddv const& x, rational const& b) {
return (a*x + b == rational(0));
};
vector<bdd> num;
for (unsigned n = 0; n < (1<<w); ++n)
num.push_back(m.mk_int(rational(n), w));
for (unsigned k = 0; k < (1 << w); ++k)
for (unsigned n = 0; n < (1 << w); ++n)
SASSERT(num[k].contains_int(rational(n), w) == (n == k));
num.push_back(m.mk_eq(x, rational(n)));
for (unsigned k = 0; k < (1 << w); ++k) {
for (unsigned n = 0; n < (1 << w); ++n) {
SASSERT(num[k].contains_num(rational(n), bits) == (n == k));
rational r;
SASSERT_EQ((num[n] || num[k]).find_num(bits, r), (n == k) ? find_result::singleton : find_result::multiple);
SASSERT(r == n || r == k);
}
}
bdd s0127 = num[0] || num[1] || num[2] || num[7];
SASSERT(s0127.contains_int(rational(0), w));
SASSERT(s0127.contains_int(rational(1), w));
SASSERT(s0127.contains_int(rational(2), w));
SASSERT(!s0127.contains_int(rational(3), w));
SASSERT(!s0127.contains_int(rational(4), w));
SASSERT(!s0127.contains_int(rational(5), w));
SASSERT(!s0127.contains_int(rational(6), w));
SASSERT(s0127.contains_int(rational(7), w));
SASSERT(s0127.contains_num(rational(0), bits));
SASSERT(s0127.contains_num(rational(1), bits));
SASSERT(s0127.contains_num(rational(2), bits));
SASSERT(!s0127.contains_num(rational(3), bits));
SASSERT(!s0127.contains_num(rational(4), bits));
SASSERT(!s0127.contains_num(rational(5), bits));
SASSERT(!s0127.contains_num(rational(6), bits));
SASSERT(s0127.contains_num(rational(7), bits));
bdd s123 = num[1] || num[2] || num[3];
SASSERT((s0127 && s123) == (num[1] || num[2]));
// larger width constrains additional bits
SASSERT(m.mk_int(rational(6), 3) != m.mk_int(rational(6), 4));
SASSERT(mk_affine(rational(0), x, rational(0)).is_true());
SASSERT(mk_affine(rational(0), x, rational(1)).is_false());
// 2*x == 0 (mod 2^3) has the solutions 0, 4
SASSERT(mk_affine(rational(2), x, rational(0)) == (num[0] || num[4]));
// 4*x + 2 == 0 (mod 2^3) has no solutions
SASSERT(m.mk_affine(rational(4), rational(2), 3).is_false());
SASSERT(mk_affine(rational(4), x, rational(2)).is_false());
// 3*x + 2 == 0 (mod 2^3) has the unique solution 2
SASSERT(m.mk_affine(rational(3), rational(2), 3) == num[2]);
SASSERT(mk_affine(rational(3), x, rational(2)) == num[2]);
// 2*x + 2 == 0 (mod 2^3) has the solutions 3, 7
SASSERT(m.mk_affine(rational(2), rational(2), 3) == (num[3] || num[7]));
// 12*x + 8 == 0 (mod 2^4) has the solutions 2, 6, 10, 14
bdd expected = m.mk_int(rational(2), 4) || m.mk_int(rational(6), 4) || m.mk_int(rational(10), 4) || m.mk_int(rational(14), 4);
SASSERT(m.mk_affine(rational(12), rational(8), 4) == expected);
SASSERT(mk_affine(rational(2), x, rational(2)) == (num[3] || num[7]));
SASSERT(m.mk_affine(rational(0), rational(0), 3).is_true());
SASSERT(m.mk_affine(rational(0), rational(1), 3).is_false());
// 2*x == 0 (mod 2^3) has the solutions 0, 4
SASSERT(m.mk_affine(rational(2), rational(0), 3) == (num[0] || num[4]));
unsigned_vector bits4 = bits;
bits4.push_back(10);
bddv const x4 = m.mk_var(bits4);
// 12*x + 8 == 0 (mod 2^4) has the solutions 2, 6, 10, 14
bdd expected = m.mk_eq(x4, rational(2)) || m.mk_eq(x4, rational(6)) || m.mk_eq(x4, rational(10)) || m.mk_eq(x4, rational(14));
SASSERT(mk_affine(rational(12), x4, rational(8)) == expected);
}
}
@ -120,5 +293,10 @@ void tst_bdd() {
dd::test2();
dd::test3();
dd::test4();
dd::test_xor();
dd::test_bddv_ops_on_constants();
dd::test_bddv_eqfind_small();
dd::test_bddv_eqfind();
dd::test_bddv_mul();
dd::test_int();
}