3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-24 17:45:32 +00:00

mk_extract

This commit is contained in:
Jakob Rath 2023-07-19 19:37:21 +02:00
parent 69e54b62c5
commit 73757e3fa4
3 changed files with 100 additions and 69 deletions

View file

@ -157,6 +157,7 @@ namespace polysat {
switch (m_trail.back()) {
case trail_item::add_var: undo_add_var(); break;
case trail_item::split_core: undo_split_core(); break;
case trail_item::mk_extract: undo_mk_extract(); break;
default: UNREACHABLE();
}
m_trail.pop_back();
@ -644,49 +645,56 @@ namespace polysat {
get_base_core<true>(src, out_base);
}
pvar slicing::mk_slice_extract(enode* src, unsigned hi, unsigned lo) {
enode_vector slices;
pvar slicing::mk_extract(enode* src, unsigned hi, unsigned lo) {
enode_vector& slices = m_tmp3;
SASSERT(slices.empty());
mk_slice(src, hi, lo, slices, false, true);
pvar v = null_var;
// try to re-use variable of an existing slice
if (slices.size() == 1)
v = slice2var(slices[0]);
// allocate new variable if we cannot reuse it
if (v == null_var)
v = m_solver.add_var(hi - lo + 1);
#if 0
// slice didn't have a variable yet; so we can re-use it for the new variable
// (we end up with a "phantom" enode that was first created for the variable)
if (slices.size() == 1) {
enode* s = slices[0];
if (slice2var(s) != null_var)
return slice2var(s);
// TODO: optimization: could save a slice-tree by directly assigning slice2var(s) = v for new var v.
SASSERT_EQ(info(s).var, null_var);
info(m_var2slice[v]).var = null_var; // disconnect the "phantom" enode
info(s).var = v;
m_var2slice[v] = s;
}
pvar v = m_solver.add_var(hi - lo + 1);
// TODO: can we use 'compressed' slice trees again if we store the source slice here as dependency?
#endif
// connect new variable
VERIFY(merge(slices, var2slice(v), dep_t()));
slices.reset();
return v;
}
pvar slicing::mk_extract_var(pvar src, unsigned hi, unsigned lo) {
return mk_slice_extract(var2slice(src), hi, lo);
pvar slicing::mk_extract(pvar src, unsigned hi, unsigned lo) {
extract_args args{src, hi, lo};
auto it = m_extract_dedup.find_iterator(args);
if (it != m_extract_dedup.end())
return it->m_value;
pvar const v = mk_extract(var2slice(src), hi, lo);
m_extract_dedup.insert(args, v);
m_extract_trail.push_back(args);
m_trail.push_back(trail_item::mk_extract);
return v;
}
pdd slicing::mk_extract(pvar src, unsigned hi, unsigned lo) {
return m_solver.var(mk_extract_var(src, hi, lo));
void slicing::undo_mk_extract() {
extract_args args = m_extract_trail.back();
m_extract_trail.pop_back();
m_extract_dedup.remove(args);
}
pdd slicing::mk_extract(pdd const& p, unsigned hi, unsigned lo) {
SASSERT(hi >= lo);
SASSERT(p.power_of_2() > hi);
if (p.is_val()) {
// p[hi:lo] = (p >> lo) % 2^(hi - lo + 1)
rational q = mod2k(machine_div2k(p.val(), lo), hi - lo + 1);
return p.manager().mk_val(q);
}
if (!lo) {
// TODO: we could push the extract down into variables of the term instead of introducing a name.
}
return m_solver.var(mk_slice_extract(pdd2slice(p), hi, lo));
}
slicing::enode* slicing::pdd2slice(pdd const& p) {
pvar const v = m_solver.m_names.mk_name(p);
return var2slice(v);
}
pdd slicing::mk_concat(pdd const& p, pdd const& q) {
pvar slicing::mk_concat(unsigned num_args, pvar const* args) {
NOT_IMPLEMENTED_YET();
return null_var;
#if 0
// v := p ++ q (new variable of size |p| + |q|)
// v[:|q|] = p
// v[|q|:] = q
@ -707,6 +715,11 @@ namespace polysat {
tmp.push_back(pdd2slice(q));
VERIFY(merge(tmp, var2slice(v), dep_t()));
return m_solver.var(v);
#endif
}
pvar slicing::mk_concat(std::initializer_list<pvar> args) {
return mk_concat(args.size(), args.begin());
}
void slicing::add_constraint(signed_constraint c) {
@ -838,9 +851,18 @@ namespace polysat {
// properties below only matter for representatives
if (!s->is_root())
continue;
enode_vector s_base;
get_base(s, s_base);
for (enode* n : euf::enode_class(s)) {
// equivalence class only contains slices of equal length
VERIFY_EQ(width(s), width(n));
// bases of equivalent nodes are equivalent
enode_vector n_base;
get_base(n, n_base);
VERIFY_EQ(s_base.size(), n_base.size());
for (unsigned i = s_base.size(); i-- > 0; ) {
VERIFY_EQ(s_base[i]->get_root(), n_base[i]->get_root());
}
}
}
return true;

View file

@ -189,16 +189,32 @@ namespace polysat {
// Check whether two slices are known to be equal
bool is_equal(enode* x, enode* y);
enum class trail_item {
// deduplication of extract terms
struct extract_args {
pvar src;
unsigned hi;
unsigned lo;
bool operator==(extract_args const& other) const { return src == other.src && hi == other.hi && lo == other.lo; }
unsigned hash() const { return mk_mix(src, hi, lo); }
};
using extract_args_eq = default_eq<extract_args>;
using extract_args_hash = obj_hash<extract_args>;
using extract_map = map<extract_args, pvar, extract_args_hash, extract_args_eq>;
extract_map m_extract_dedup;
enum class trail_item : std::uint8_t {
add_var,
split_core,
mk_extract,
};
svector<trail_item> m_trail;
enode_vector m_split_trail;
svector<extract_args> m_extract_trail;
unsigned_vector m_scopes;
void undo_add_var();
void undo_split_core();
void undo_mk_extract();
mutable enode_vector m_tmp1;
mutable enode_vector m_tmp2;
@ -207,18 +223,12 @@ namespace polysat {
sat::literal_set m_marked_lits;
uint_set m_marked_vars;
// get a slice that is equivalent to the given pdd (may introduce new variable)
enode* pdd2slice(pdd const& p);
/** Get variable representing src[hi:lo] */
pvar mk_slice_extract(enode* src, unsigned hi, unsigned lo);
pvar mk_extract(enode* src, unsigned hi, unsigned lo);
bool invariant() const;
bool invariant_needs_congruence() const;
/** Get variable representing x[hi:lo] */
pvar mk_extract_var(pvar x, unsigned hi, unsigned lo);
std::ostream& display(std::ostream& out, enode* s) const;
std::ostream& display_tree(std::ostream& out, enode* s, unsigned indent, unsigned hi, unsigned lo) const;
@ -230,14 +240,12 @@ namespace polysat {
void add_var(unsigned bit_width);
/** Create expression for x[hi:lo] */
pdd mk_extract(pvar x, unsigned hi, unsigned lo);
/** Get or create variable representing x[hi:lo] */
pvar mk_extract(pvar x, unsigned hi, unsigned lo);
/** Create expression for p[hi:lo] */
pdd mk_extract(pdd const& p, unsigned hi, unsigned lo);
/** Create expression for p ++ q */
pdd mk_concat(pdd const& p, pdd const& q);
/** Get or create variable representing x1 ++ x2 ++ ... ++ xn */
pvar mk_concat(unsigned num_args, pvar const* args);
pvar mk_concat(std::initializer_list<pvar> args);
// Track value assignments to variables (and propagate to subslices)
// (could generalize to fixed bits, then we need a way to merge interpreted enodes)

View file

@ -82,13 +82,13 @@ namespace polysat {
pvar x = s.add_var(8);
pvar y = s.add_var(8);
pvar a = sl.mk_extract_var(x, 7, 3);
pvar a = sl.mk_extract(x, 7, 3);
std::cout << sl << "\n";
VERIFY(sl.merge(sl.var2slice(x), sl.var2slice(y), sat::literal(1)));
std::cout << sl << "\n";
pvar b = sl.mk_extract_var(y, 5, 0);
pvar b = sl.mk_extract(y, 5, 0);
std::cout << sl << "\n";
sl.display_tree(std::cout);
@ -113,17 +113,17 @@ namespace polysat {
pvar y = s.add_var(8);
std::cout << sl << "\n";
pvar a = sl.mk_extract_var(x, 7, 3);
pvar a = sl.mk_extract(x, 7, 3);
std::cout << "v" << a << " := v" << x << "[7:3]\n" << sl << "\n";
pvar b = sl.mk_extract_var(y, 5, 0);
pvar b = sl.mk_extract(y, 5, 0);
std::cout << "v" << b << " := v" << y << "[5:0]\n" << sl << "\n";
pvar c = sl.mk_extract_var(x, 5, 0);
pvar c = sl.mk_extract(x, 5, 0);
std::cout << "v" << c << " := v" << x << "[5:0]\n" << sl << "\n";
pdd d = sl.mk_concat(sl.mk_extract(x, 5, 4), sl.mk_extract(y, 3, 0));
pvar d = sl.mk_concat({sl.mk_extract(x, 5, 4), sl.mk_extract(y, 3, 0)});
std::cout << d << " := v" << x << "[5:4] ++ v" << y << "[3:0]\n" << sl << "\n";
std::cout << "v" << b << " = v" << c << "? " << sl.is_equal(sl.var2slice(b), sl.var2slice(c)) << "\n\n";
std::cout << "v" << b << " = " << d << "? " << sl.is_equal(sl.var2slice(b), sl.pdd2slice(d)) << "\n\n";
std::cout << "v" << b << " = v" << d << "? " << sl.is_equal(sl.var2slice(b), sl.var2slice(d)) << "\n\n";
VERIFY(sl.merge(sl.var2slice(x), sl.var2slice(y), sat::literal(123)));
std::cout << "v" << x << " = v" << y << "\n" << sl << "\n";
@ -137,13 +137,13 @@ namespace polysat {
sl.explain_equal(sl.var2slice(b), sl.var2slice(c), reason_lits, reason_vars);
std::cout << " Reason: " << reason_lits << " vars " << reason_vars << "\n";
std::cout << "v" << b << " = " << d << "? " << sl.is_equal(sl.var2slice(b), sl.pdd2slice(d))
std::cout << "v" << b << " = v" << d << "? " << sl.is_equal(sl.var2slice(b), sl.var2slice(d))
<< " root(v" << b << ") = " << sl.var2slice(b)->get_root_id()
<< " root(" << d << ") = " << sl.pdd2slice(d)->get_root_id()
<< " root(v" << d << ") = " << sl.var2slice(d)->get_root_id()
<< "\n";
reason_lits.reset();
reason_vars.reset();
sl.explain_equal(sl.var2slice(b), sl.pdd2slice(d), reason_lits, reason_vars);
sl.explain_equal(sl.var2slice(b), sl.var2slice(d), reason_lits, reason_vars);
std::cout << " Reason: " << reason_lits << " vars " << reason_vars << "\n";
sl.display_tree(std::cout);
@ -171,9 +171,13 @@ namespace polysat {
pvar e = s.add_var(2);
VERIFY(sl.merge(sl.var2slice(a), sl.var2slice(b), sat::literal(101)));
VERIFY(sl.merge(sl.var2slice(d), sl.var2slice(sl.mk_extract_var(c, 1, 0)), sat::literal(102)));
VERIFY(sl.merge(sl.var2slice(c), sl.var2slice(sl.mk_extract_var(b, 3, 0)), sat::literal(103)));
VERIFY(sl.merge(sl.var2slice(e), sl.var2slice(sl.mk_extract_var(a, 1, 0)), sat::literal(104)));
VERIFY(sl.merge(sl.var2slice(d), sl.var2slice(sl.mk_extract(c, 1, 0)), sat::literal(102)));
VERIFY(sl.merge(sl.var2slice(c), sl.var2slice(sl.mk_extract(b, 3, 0)), sat::literal(103)));
VERIFY(sl.merge(sl.var2slice(e), sl.var2slice(sl.mk_extract(a, 1, 0)), sat::literal(104)));
// sl.propagate();
sl.display_tree(std::cout);
VERIFY(sl.invariant());
std::cout << "v" << d << " = v" << e << "? " << sl.is_equal(sl.var2slice(d), sl.var2slice(e))
<< " root(v" << d << ") = " << sl.var2slice(d)->get_root_id()
@ -185,9 +189,6 @@ namespace polysat {
unsigned_vector reason_vars;
sl.explain_equal(sl.var2slice(d), sl.var2slice(e), reason_lits, reason_vars);
std::cout << " Reason: " << reason_lits << " vars " << reason_vars << "\n";
sl.display_tree(std::cout);
VERIFY(sl.invariant());
}
// x[5:2] = y
@ -201,9 +202,9 @@ namespace polysat {
pvar x = s.add_var(6);
std::cout << sl << "\n";
pvar y = sl.mk_extract_var(x, 5, 2);
pvar y = sl.mk_extract(x, 5, 2);
std::cout << "v" << y << " := v" << x << "[5:2]\n" << sl << "\n";
pvar z = sl.mk_extract_var(x, 3, 0);
pvar z = sl.mk_extract(x, 3, 0);
std::cout << "v" << z << " := v" << x << "[3:0]\n" << sl << "\n";
slicing::enode* nine = sl.mk_value_slice(rational(9), 4);
@ -280,12 +281,12 @@ namespace polysat {
void tst_slicing() {
using namespace polysat;
// test_slicing::test1();
// test_slicing::test2();
test_slicing::test1();
test_slicing::test2();
// test_slicing::test3();
// test_slicing::test4();
// test_slicing::test5();
// test_slicing::test6();
test_slicing::test4();
test_slicing::test5();
test_slicing::test6();
test_slicing::test7();
std::cout << "ok\n";
}