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

use enode* instead of custom slice index; extract explanations from egraph

This commit is contained in:
Jakob Rath 2023-07-17 13:48:05 +02:00
parent 82b1f9297b
commit b8d118a558
2 changed files with 220 additions and 210 deletions

View file

@ -71,32 +71,42 @@ Recycle the z3 egraph?
#include "math/polysat/slicing.h"
#include "math/polysat/solver.h"
#include "math/polysat/log.h"
#include "util/tptr.h"
namespace polysat {
void* slicing::encode_dep(dep_t d) {
if (d == null_dep)
return nullptr;
else
return reinterpret_cast<void*>(static_cast<std::uintptr_t>(d.to_uint()) + 1);
static_assert( sizeof(void*) >= sizeof(std::uintptr_t) );
static_assert( sizeof(std::uintptr_t) > sizeof(decltype(d.to_uint())) );
void* p = box<void>(d.to_uint());
SASSERT_EQ(d, decode_dep(p));
return p;
}
slicing::dep_t slicing::decode_dep(void* d) {
if (!d)
return null_dep;
else
return sat::to_literal(reinterpret_cast<std::uintptr_t>(d) - 1);
slicing::dep_t slicing::decode_dep(void* p) {
return sat::to_literal(unbox<unsigned>(p));
}
void slicing::display_dep(std::ostream& out, void* d) {
out << decode_dep(d);
}
slicing::slicing(solver& s):
m_solver(s),
m_egraph(m_ast),
// m_slice2app(m_ast),
m_expr_storage(m_ast)
m_slice_sort(m_ast),
m_concat_decls(m_ast),
m_egraph(m_ast)
{
m_slice_sort = m_ast.mk_uninterpreted_sort(symbol("slice"));
m_egraph.set_display_justification(display_dep);
}
slicing::slice_info& slicing::info(euf::enode* n) {
return const_cast<slice_info&>(std::as_const(*this).info(n));
}
slicing::slice_info const& slicing::info(euf::enode* n) const {
slice_info const& i = m_info[n->get_id()];
return i.is_slice() ? i : info(i.slice);
}
func_decl* slicing::get_concat_decl(unsigned arity) {
@ -109,14 +119,13 @@ namespace polysat {
SASSERT_EQ(arity, domain.size());
// TODO: mk_fresh_func_decl("concat", ...) if overload doesn't work
func_decl* decl = m_ast.mk_func_decl(symbol("slice-concat"), arity, domain.data(), m_slice_sort);
m_concat_decls.setx(arity, decl, nullptr);
m_concat_decls.setx(arity, decl);
}
return decl;
}
void slicing::push_scope() {
m_scopes.push_back(m_trail.size());
m_expr_scopes.push_back(m_expr_storage.size());
m_egraph.push();
}
@ -127,9 +136,7 @@ namespace polysat {
SASSERT(num_scopes <= lvl);
unsigned const target_lvl = lvl - num_scopes;
unsigned const target_size = m_scopes[target_lvl];
unsigned const target_expr_size = m_expr_scopes[target_lvl];
m_scopes.shrink(target_lvl);
m_expr_scopes.shrink(target_lvl);
while (m_trail.size() > target_size) {
switch (m_trail.back()) {
case trail_item::add_var: undo_add_var(); break;
@ -142,13 +149,12 @@ namespace polysat {
m_trail.pop_back();
}
m_egraph.pop(num_scopes);
m_expr_storage.shrink(target_expr_size);
}
void slicing::add_var(unsigned bit_width) {
pvar const v = m_var2slice.size();
slice const s = alloc_slice(bit_width);
m_slice2var[s] = v;
enode* s = alloc_slice(bit_width);
info(s).var = v;
m_var2slice.push_back(s);
}
@ -156,63 +162,28 @@ namespace polysat {
m_var2slice.pop_back();
}
slicing::slice slicing::alloc_slice(unsigned width) {
slicing::enode* slicing::alloc_slice(unsigned width) {
SASSERT(width > 0);
slice const s = m_slice_cut.size();
m_slice_width.push_back(width);
m_slice_cut.push_back(null_cut);
m_slice_sub.push_back(null_slice);
m_slice2var.push_back(null_var);
// m_mark.push_back(0);
app* a = m_ast.mk_fresh_const("s", m_slice_sort, false); // TODO: what's the effect of "skolem = true"?
m_expr_storage.push_back(a);
euf::enode* n = m_egraph.mk(a, 0, 0, nullptr);
m_slice2enode.push_back(n);
SASSERT(!m_enode2slice.contains(n));
m_enode2slice.insert(n, s);
app* a = m_ast.mk_fresh_const("s", m_slice_sort, false);
euf::enode* n = m_egraph.mk(a, 0, 0, nullptr); // NOTE: the egraph keeps a strong reference to "a"
m_info.reserve(n->get_id() + 1);
slice_info& i = info(n);
i.reset();
i.width = width;
m_trail.push_back(trail_item::alloc_slice);
return s;
return n;
}
void slicing::undo_alloc_slice() {
m_slice_width.pop_back();
m_slice_cut.pop_back();
m_slice_sub.pop_back();
m_slice2var.pop_back();
euf::enode* n = m_slice2enode.back();
SASSERT_EQ(m_enode2slice[n], m_slice_cut.size());
m_enode2slice.remove(n);
m_slice2enode.pop_back();
// m_mark.pop_back();
}
slicing::slice slicing::sub_hi(slice parent) const {
SASSERT(has_sub(parent));
return m_slice_sub[parent];
}
slicing::slice slicing::sub_lo(slice parent) const {
SASSERT(has_sub(parent));
return m_slice_sub[parent] + 1;
}
euf::enode* slicing::sub_hi(euf::enode* n) const {
return slice2enode(sub_hi(enode2slice(n)));
}
euf::enode* slicing::sub_lo(euf::enode* n) const {
return slice2enode(sub_lo(enode2slice(n)));
}
// split a single slice without updating any equivalences
void slicing::split_core(slice s, unsigned cut) {
void slicing::split_core(enode* s, unsigned cut) {
SASSERT(!has_sub(s));
SASSERT(width(s) - 1 >= cut + 1);
slice const sub_hi = alloc_slice(width(s) - cut - 1);
slice const sub_lo = alloc_slice(cut + 1);
m_slice_cut[s] = cut;
m_slice_sub[s] = sub_hi;
SASSERT_EQ(sub_lo, sub_hi + 1);
enode* sub_hi = alloc_slice(width(s) - cut - 1);
enode* sub_lo = alloc_slice(cut + 1);
info(s).set_cut(cut, sub_hi, sub_lo);
m_trail.push_back(trail_item::split_core);
m_split_trail.push_back(s);
// if (has_value(s)) {
@ -228,25 +199,22 @@ namespace polysat {
// app* a = m_ast.mk_app(get_concat_decl(2), hi_n->get_expr(), lo_n->get_expr());
// auto args = {hi_n, lo_n};
// euf::enode* concat_n = m_egraph.mk(a, 0, args.size(), blup.begin());
// m_egraph.merge(s_n, concat_n, nullptr);
// m_egraph.merge(s_n, concat_n, encode_dep(null_dep));
// SASSERT(!concat_n->is_root()); // else we have to register it in enode2slice
}
void slicing::undo_split_core() {
slice s = m_split_trail.back();
enode* s = m_split_trail.back();
m_split_trail.pop_back();
m_slice_cut[s] = null_cut;
m_slice_sub[s] = null_slice;
info(s).set_cut(null_cut, nullptr, nullptr);
}
void slicing::split(slice s, unsigned cut) {
euf::enode* sn = slice2enode(s);
void slicing::split(enode* s, unsigned cut) {
// split all slices in the equivalence class
for (euf::enode* n : euf::enode_class(sn)) {
split_core(enode2slice(n), cut);
}
for (euf::enode* n : euf::enode_class(s))
split_core(n, cut);
// propagate the proper equivalences
for (euf::enode* n : euf::enode_class(sn)) {
for (euf::enode* n : euf::enode_class(s)) {
euf::enode* target = n->get_target();
if (!target)
continue;
@ -281,16 +249,12 @@ namespace polysat {
}
#endif
slicing::slice slicing::find(slice s) const {
return enode2slice(slice2enode(s)->get_root());
}
#if 1
bool slicing::merge_base(slice s1, slice s2, dep_t dep) {
bool slicing::merge_base(enode* s1, enode* s2, dep_t dep) {
SASSERT_EQ(width(s1), width(s2));
SASSERT(!has_sub(s1));
SASSERT(!has_sub(s2));
m_egraph.merge(slice2enode(s1), slice2enode(s2), encode_dep(dep));
m_egraph.merge(s1, s2, encode_dep(dep));
m_egraph.propagate(); // TODO: could do this later maybe
return !m_egraph.inconsistent();
}
@ -330,32 +294,53 @@ namespace polysat {
}
#endif
#if 0
void slicing::push_reason(slice s, dep_vector& out_deps) {
dep_t reason = m_proof_reason[s];
if (reason == null_dep)
return;
out_deps.push_back(reason);
void slicing::begin_explain() {
SASSERT(m_marked_deps.empty());
}
void slicing::explain_equal(slice x, slice y, dep_vector& out_deps) {
// TODO: we currently get duplicates in out_deps (if parents are merged, the subslices are all merged due to the same reason)
void slicing::end_explain() {
m_marked_deps.reset();
}
void slicing::push_dep(void* dp, dep_vector& out_deps) {
dep_t d = decode_dep(dp);
if (d == sat::null_literal)
return;
if (m_marked_deps.contains(d))
return;
m_marked_deps.insert(d);
out_deps.push_back(d);
}
void slicing::explain_class(enode* x, enode* y, dep_vector& out_deps) {
SASSERT_EQ(x->get_root(), y->get_root());
SASSERT(m_tmp_justifications.empty());
m_egraph.begin_explain();
m_egraph.explain_eq(m_tmp_justifications, nullptr, x, y);
m_egraph.end_explain();
for (void* dp : m_tmp_justifications)
push_dep(dp, out_deps);
m_tmp_justifications.reset();
}
void slicing::explain_equal(enode* x, enode* y, dep_vector& out_deps) {
begin_explain();
SASSERT(is_equal(x, y));
slice_vector& xs = m_tmp2;
slice_vector& ys = m_tmp3;
enode_vector& xs = m_tmp2;
enode_vector& ys = m_tmp3;
SASSERT(xs.empty());
SASSERT(ys.empty());
xs.push_back(x);
ys.push_back(y);
while (!xs.empty()) {
SASSERT(!ys.empty());
slice const x = xs.back(); xs.pop_back();
slice const y = ys.back(); ys.pop_back();
enode* const x = xs.back(); xs.pop_back();
enode* const y = ys.back(); ys.pop_back();
if (x == y)
continue;
if (width(x) == width(y)) {
slice const rx = find(x);
slice const ry = find(y);
enode* const rx = find(x);
enode* const ry = find(y);
if (rx == ry)
explain_class(x, y, out_deps);
else {
@ -366,7 +351,7 @@ namespace polysat {
}
}
else if (width(x) > width(y)) {
slice const rx = find(x);
enode* const rx = find(x);
xs.push_back(sub_hi(rx));
xs.push_back(sub_lo(rx));
ys.push_back(y);
@ -374,21 +359,21 @@ namespace polysat {
else {
SASSERT(width(x) < width(y));
xs.push_back(x);
slice const ry = find(y);
enode* const ry = find(y);
ys.push_back(sub_hi(ry));
ys.push_back(sub_lo(ry));
}
}
SASSERT(ys.empty());
end_explain();
}
#endif
bool slicing::merge(slice_vector& xs, slice_vector& ys, dep_t dep) {
bool slicing::merge(enode_vector& xs, enode_vector& ys, dep_t dep) {
// LOG_H2("Merging " << xs << " with " << ys);
while (!xs.empty()) {
SASSERT(!ys.empty());
slice x = xs.back();
slice y = ys.back();
enode* x = xs.back();
enode* y = ys.back();
xs.pop_back();
ys.pop_back();
if (has_sub(x)) {
@ -426,19 +411,19 @@ namespace polysat {
return true;
}
bool slicing::merge(slice_vector& xs, slice y, dep_t dep) {
slice_vector& ys = m_tmp2;
bool slicing::merge(enode_vector& xs, enode* y, dep_t dep) {
enode_vector& ys = m_tmp2;
SASSERT(ys.empty());
ys.push_back(y);
return merge(xs, ys, dep); // will clear xs and ys
}
bool slicing::merge(slice x, slice y, dep_t dep) {
bool slicing::merge(enode* x, enode* y, dep_t dep) {
SASSERT_EQ(width(x), width(y));
if (!has_sub(x) && !has_sub(y))
return merge_base(x, y, dep);
slice_vector& xs = m_tmp2;
slice_vector& ys = m_tmp3;
enode_vector& xs = m_tmp2;
enode_vector& ys = m_tmp3;
SASSERT(xs.empty());
SASSERT(ys.empty());
xs.push_back(x);
@ -446,20 +431,20 @@ namespace polysat {
return merge(xs, ys, dep); // will clear xs and ys
}
bool slicing::is_equal(slice x, slice y) {
bool slicing::is_equal(enode* x, enode* y) {
SASSERT_EQ(width(x), width(y));
x = find(x);
y = find(y);
x = x->get_root();
y = y->get_root();
if (x == y)
return true;
slice_vector& xs = m_tmp2;
slice_vector& ys = m_tmp3;
enode_vector& xs = m_tmp2;
enode_vector& ys = m_tmp3;
SASSERT(xs.empty());
SASSERT(ys.empty());
find_base(x, xs);
find_base(y, ys);
SASSERT(all_of(xs, [this](slice s) { return s == find(s); }));
SASSERT(all_of(ys, [this](slice s) { return s == find(s); }));
SASSERT(all_of(xs, [](enode* s) { return s->is_root(); }));
SASSERT(all_of(ys, [](enode* s) { return s->is_root(); }));
bool result = (xs == ys);
xs.clear();
ys.clear();
@ -470,15 +455,15 @@ namespace polysat {
}
template <bool should_find>
void slicing::get_base_core(slice src, slice_vector& out_base) const {
slice_vector& todo = m_tmp1;
void slicing::get_base_core(enode* src, enode_vector& out_base) const {
enode_vector& todo = m_tmp1;
SASSERT(todo.empty());
todo.push_back(src);
while (!todo.empty()) {
slice s = todo.back();
enode* s = todo.back();
todo.pop_back();
if constexpr (should_find) {
s = find(s);
s = s->get_root();
}
if (!has_sub(s))
out_base.push_back(s);
@ -490,18 +475,18 @@ namespace polysat {
SASSERT(todo.empty());
}
void slicing::get_base(slice src, slice_vector& out_base) const {
void slicing::get_base(enode* src, enode_vector& out_base) const {
get_base_core<false>(src, out_base);
}
void slicing::find_base(slice src, slice_vector& out_base) const {
void slicing::find_base(enode* src, enode_vector& out_base) const {
get_base_core<true>(src, out_base);
}
void slicing::mk_slice(slice src, unsigned const hi, unsigned const lo, slice_vector& out, bool output_full_src, bool output_base) {
void slicing::mk_slice(enode* src, unsigned const hi, unsigned const lo, enode_vector& out, bool output_full_src, bool output_base) {
SASSERT(hi >= lo);
SASSERT(width(src) > hi); // extracted range must be fully contained inside the src slice
auto output_slice = [this, output_base, &out](slice s) {
auto output_slice = [this, output_base, &out](enode* s) {
if (output_base)
get_base(s, out);
else
@ -513,7 +498,7 @@ namespace polysat {
}
if (has_sub(src)) {
// src is split into [src.width-1, cut+1] and [cut, 0]
unsigned const cut = m_slice_cut[src];
unsigned const cut = info(src).cut;
if (lo >= cut + 1) {
// target slice falls into upper subslice
mk_slice(sub_hi(src), hi - cut - 1, lo - cut - 1, out, output_full_src, output_base);
@ -559,16 +544,17 @@ namespace polysat {
UNREACHABLE();
}
pvar slicing::mk_slice_extract(slice src, unsigned hi, unsigned lo) {
slice_vector slices;
pvar slicing::mk_slice_extract(enode* src, unsigned hi, unsigned lo) {
enode_vector slices;
mk_slice(src, hi, lo, slices, false, true);
if (slices.size() == 1) {
slice s = slices[0];
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.
}
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?
VERIFY(merge(slices, var2slice(v), null_dep));
return v;
}
@ -595,7 +581,7 @@ namespace polysat {
return m_solver.var(mk_slice_extract(pdd2slice(p), hi, lo));
}
slicing::slice slicing::pdd2slice(pdd const& p) {
slicing::enode* slicing::pdd2slice(pdd const& p) {
pvar const v = m_solver.m_names.mk_name(p);
return var2slice(v);
}
@ -616,7 +602,7 @@ namespace polysat {
if (q.is_val()) {
}
pvar const v = m_solver.add_var(v_sz);
slice_vector tmp;
enode_vector tmp;
tmp.push_back(pdd2slice(p));
tmp.push_back(pdd2slice(q));
VERIFY(merge(tmp, var2slice(v), null_dep));
@ -635,7 +621,7 @@ namespace polysat {
pdd body = a.is_one() ? (m.mk_var(x) - p) : (m.mk_var(x) + p);
// c is either x = body or x != body, depending on polarity
LOG("Equation from constraint " << c << ": v" << x << " = " << body);
slice const sx = var2slice(x);
enode* const sx = var2slice(x);
if (body.is_val()) {
// Simple assignment x = value
// TODO: set fixed bits
@ -646,7 +632,7 @@ namespace polysat {
// TODO: register name trigger (if a name for value 'body' is created later, then merge x=y at that time)
continue;
}
slice const sy = var2slice(y);
enode* const sy = var2slice(y);
if (c.is_positive()) {
if (!merge(sx, sy, c.blit()))
return;
@ -668,13 +654,13 @@ namespace polysat {
}
std::ostream& slicing::display(std::ostream& out) const {
slice_vector base;
enode_vector base;
for (pvar v = 0; v < m_var2slice.size(); ++v) {
out << "v" << v << ":";
base.reset();
slice const vs = var2slice(v);
enode* const vs = var2slice(v);
find_base(vs, base);
for (slice s : base)
for (enode* s : base)
display(out << " ", s);
// if (has_value(vs)) {
// out << " -- (val:" << get_value(vs) << ")";
@ -687,31 +673,32 @@ namespace polysat {
std::ostream& slicing::display_tree(std::ostream& out) const {
for (pvar v = 0; v < m_var2slice.size(); ++v) {
out << "v" << v << ":\n";
slice const s = var2slice(v);
enode* const s = var2slice(v);
display_tree(out, s, 4, width(s) - 1, 0);
}
out << m_egraph << "\n";
return out;
}
std::ostream& slicing::display_tree(std::ostream& out, slice s, unsigned indent, unsigned hi, unsigned lo) const {
std::ostream& slicing::display_tree(std::ostream& out, enode* s, unsigned indent, unsigned hi, unsigned lo) const {
out << std::string(indent, ' ') << "[" << hi << ":" << lo << "]";
out << " id=" << s;
out << " id=" << s->get_id();
out << " w=" << width(s);
if (find(s) != s)
out << " root=" << find(s);
if (!s->is_root())
out << " root=" << s->get_root_id();;
// if (has_value(s))
// out << " value=" << get_value(s);
out << "\n";
if (has_sub(s)) {
unsigned cut = m_slice_cut[s];
unsigned cut = info(s).cut;
display_tree(out, sub_hi(s), indent + 4, hi, cut + 1 + lo);
display_tree(out, sub_lo(s), indent + 4, cut + lo, lo);
}
return out;
}
std::ostream& slicing::display(std::ostream& out, slice s) const {
out << "{id:" << s << ",w:" << width(s);
std::ostream& slicing::display(std::ostream& out, enode* s) const {
out << "{id:" << s->get_id() << ",w:" << width(s);
// if (has_value(s))
// out << ",val:" << get_value(s);
out << "}";
@ -722,13 +709,17 @@ namespace polysat {
VERIFY(m_tmp1.empty());
VERIFY(m_tmp2.empty());
VERIFY(m_tmp3.empty());
for (slice s = 0; s < m_slice_cut.size(); ++s) {
for (enode* s : m_egraph.nodes()) {
// if the slice is equivalent to a variable, then the variable's slice is in the equivalence class
pvar const v = slice2var(s);
SASSERT_EQ(v != null_var, find(var2slice(v)) == find(s));
VERIFY_EQ(v != null_var, var2slice(v)->get_root() == s->get_root());
// properties below only matter for representatives
if (s != find(s))
if (!s->is_root())
continue;
for (enode* n : euf::enode_class(s)) {
// equivalence class only contains slices of equal length
VERIFY_EQ(width(s), width(n));
}
// if slice has a value, it should be propagated to its sub-slices
// if (has_value(s)) {
// VERIFY(has_value(find_sub_hi(s)));

View file

@ -37,37 +37,54 @@ namespace polysat {
friend class test_slicing;
solver& m_solver;
ast_manager m_ast;
euf::egraph m_egraph;
sort* m_slice_sort;
ptr_vector<func_decl> m_concat_decls;
func_decl* get_concat_decl(unsigned arity);
using dep_t = sat::literal;
using dep_vector = sat::literal_vector;
static constexpr sat::literal null_dep = sat::null_literal;
void* encode_dep(dep_t d);
dep_t decode_dep(void* d);
using slice = unsigned;
using slice_vector = unsigned_vector;
static constexpr slice null_slice = std::numeric_limits<slice>::max();
using enode = euf::enode;
using enode_vector = euf::enode_vector;
static constexpr unsigned null_cut = std::numeric_limits<unsigned>::max();
struct slice_info {
unsigned width = 0;
unsigned cut = null_cut;
euf::enode* sub_hi = nullptr;
euf::enode* sub_lo = nullptr;
unsigned width = 0; // number of bits in the slice
unsigned cut = null_cut; // cut point, or null_cut if no subslices
pvar var = null_var; // slice is equivalent to this variable, if any
enode* slice = nullptr; // if enode corresponds to a concat(...) expression, this field links to the represented slice.
enode* sub_hi = nullptr; // upper subslice s[|s|-1:cut+1]
enode* sub_lo = nullptr; // lower subslice s[cut:0]
void reset() { *this = slice_info(); }
bool is_slice() const { return !slice; }
bool has_sub() const { return !!sub_hi; }
void set_cut(unsigned cut, enode* sub_hi, enode* sub_lo) { this->cut = cut; this->sub_hi = sub_hi; this->sub_lo = sub_lo; }
};
using slice_info_vector = svector<slice_info>;
// using enode = euf::enode<slice_extra>;
solver& m_solver;
ast_manager m_ast;
sort_ref m_slice_sort;
func_decl_ref_vector m_concat_decls;
// expr_ref_vector m_expr_storage;
// unsigned_vector m_expr_scopes;
euf::egraph m_egraph;
slice_info_vector m_info; // indexed by enode::get_id()
enode_vector m_var2slice; // pvar -> slice
func_decl* get_concat_decl(unsigned arity);
static void* encode_dep(dep_t d);
static dep_t decode_dep(void* d);
static void display_dep(std::ostream& out, void* d);
/*
struct val2slice_key {
rational value;
unsigned bit_width;
@ -86,7 +103,9 @@ namespace polysat {
using val2slice_hash = obj_hash<val2slice_key>;
using val2slice_eq = default_eq<val2slice_key>;
using val2slice_map = map<val2slice_key, slice, val2slice_hash, val2slice_eq>;
*/
/*
unsigned_vector m_slice_width; // number of bits in the slice
// Cut point: if slice represents bit-vector x, then x has been sliced into x[|x|-1:cut+1] and x[cut:0].
// The cut point is relative to the parent slice (rather than a root variable, which might not be unique)
@ -98,13 +117,9 @@ namespace polysat {
pvar_vector m_slice2var; // slice -> pvar, or null_var if slice is not equivalent to a variable
slice_vector m_var2slice; // pvar -> slice
// app_ref_vector m_slice2app; // slice -> app*
// ptr_addr_map<app, slice> m_app2slice;
ptr_vector<euf::enode> m_slice2enode;
ptr_addr_map<euf::enode, slice> m_enode2slice;
expr_ref_vector m_expr_storage;
unsigned_vector m_expr_scopes;
*/
#if 0
unsigned_vector m_mark;
@ -124,24 +139,23 @@ namespace polysat {
void mark(slice s) { SASSERT(m_mark_active); m_mark[s] = m_mark_timestamp; }
#endif
slice alloc_slice(unsigned width);
slice_info& info(euf::enode* n);
slice_info const& info(euf::enode* n) const;
slice var2slice(pvar v) const { return m_var2slice[v]; }
pvar slice2var(slice s) const { return m_slice2var[s]; }
// slice app2slice(app* a) const { return m_app2slice[a]; }
// app* slice2app(slice s) const { return m_slice2app[s]; }
slice enode2slice(euf::enode* n) const { return m_enode2slice[n]; }
euf::enode* slice2enode(slice s) const { return m_slice2enode[s]; }
enode* alloc_slice(unsigned width);
unsigned width(slice s) const { return m_slice_width[s]; }
enode* var2slice(pvar v) const { return m_var2slice[v]; }
pvar slice2var(enode* s) const { return info(s).var; }
unsigned width(enode* s) const { return info(s).width; }
bool has_sub(enode* s) const { return info(s).has_sub(); }
bool has_sub(slice s) const { return m_slice_sub[s] != null_slice; }
/// Upper subslice (direct child, not necessarily the representative)
slice sub_hi(slice s) const;
euf::enode* sub_hi(euf::enode* n) const;
enode* sub_hi(enode* s) const { return info(s).sub_hi; }
/// Lower subslice (direct child, not necessarily the representative)
slice sub_lo(slice s) const;
euf::enode* sub_lo(euf::enode* n) const;
enode* sub_lo(enode* s) const { return info(s).sub_lo; }
// slice val2slice(rational const& val, unsigned bit_width) const;
@ -155,40 +169,43 @@ namespace polysat {
// void make_proof_root(slice s);
/// Split slice s into s[|s|-1:cut+1] and s[cut:0]
void split(slice s, unsigned cut);
void split_core(slice s, unsigned cut);
void split(enode* s, unsigned cut);
void split_core(enode* s, unsigned cut);
template <bool should_find>
void get_base_core(slice src, slice_vector& out_base) const;
void get_base_core(enode* src, enode_vector& out_base) const;
/// Retrieve base slices s_1,...,s_n such that src == s_1 ++ ... ++ s_n (actual descendant subslices)
void get_base(slice src, slice_vector& out_base) const;
void get_base(enode* src, enode_vector& out_base) const;
/// Retrieve base slices s_1,...,s_n such that src == s_1 ++ ... ++ s_n (representatives of subslices)
void find_base(slice src, slice_vector& out_base) const;
void find_base(enode* src, enode_vector& out_base) const;
/// Retrieve (or create) base slices s_1,...,s_n such that src[hi:lo] == s_1 ++ ... ++ s_n.
/// If output_full_src is true, return the new base for src, i.e., src == s_1 ++ ... ++ s_n.
/// If output_base is false, return coarsest intermediate slices instead of only base slices.
void mk_slice(slice src, unsigned hi, unsigned lo, slice_vector& out, bool output_full_src = false, bool output_base = true);
void mk_slice(enode* src, unsigned hi, unsigned lo, enode_vector& out, bool output_full_src = false, bool output_base = true);
/// Find representative
slice find(slice s) const;
enode* find(enode* s) const { return s->get_root(); }
// Merge equivalence classes of two base slices.
// Returns true if merge succeeded without conflict.
[[nodiscard]] bool merge_base(slice s1, slice s2, dep_t dep);
[[nodiscard]] bool merge_base(enode* s1, enode* s2, dep_t dep);
// Merge equality s == val and propagate the value downward into sub-slices.
// Returns true if merge succeeded without conflict.
[[nodiscard]] bool merge_value(slice s, rational val, dep_t dep);
[[nodiscard]] bool merge_value(enode* s, rational val, dep_t dep);
void push_reason(slice s, dep_vector& out_deps);
void begin_explain();
void end_explain();
void push_dep(void* dp, dep_vector& out_deps);
// Extract reason why slices x and y are in the same equivalence class
void explain_class(slice x, slice y, dep_vector& out_deps);
void explain_class(enode* x, enode* y, dep_vector& out_deps);
// Extract reason why slices x and y are equal
// (i.e., x and y have the same base, but are not necessarily in the same equivalence class)
void explain_equal(slice x, slice y, dep_vector& out_deps);
void explain_equal(enode* x, enode* y, dep_vector& out_deps);
// Merge equality x_1 ++ ... ++ x_n == y_1 ++ ... ++ y_k
//
@ -199,12 +216,12 @@ namespace polysat {
// The argument vectors will be cleared.
//
// Returns true if merge succeeded without conflict.
[[nodiscard]] bool merge(slice_vector& xs, slice_vector& ys, dep_t dep);
[[nodiscard]] bool merge(slice_vector& xs, slice y, dep_t dep);
[[nodiscard]] bool merge(slice x, slice y, dep_t dep);
[[nodiscard]] bool merge(enode_vector& xs, enode_vector& ys, dep_t dep);
[[nodiscard]] bool merge(enode_vector& xs, enode* y, dep_t dep);
[[nodiscard]] bool merge(enode* x, enode* y, dep_t dep);
// Check whether two slices are known to be equal
bool is_equal(slice x, slice y);
bool is_equal(enode* x, enode* y);
enum class trail_item {
add_var,
@ -214,8 +231,8 @@ namespace polysat {
mk_value_slice,
};
svector<trail_item> m_trail;
slice_vector m_split_trail;
vector<val2slice_key> m_val2slice_trail;
enode_vector m_split_trail;
// vector<val2slice_key> m_val2slice_trail;
unsigned_vector m_scopes;
void undo_add_var();
@ -223,21 +240,26 @@ namespace polysat {
void undo_split_core();
void undo_mk_value_slice();
mutable slice_vector m_tmp1;
mutable slice_vector m_tmp2;
mutable slice_vector m_tmp3;
mutable enode_vector m_tmp1;
mutable enode_vector m_tmp2;
mutable enode_vector m_tmp3;
ptr_vector<void> m_tmp_justifications;
sat::literal_set m_marked_deps;
// get a slice that is equivalent to the given pdd (may introduce new variable)
slice pdd2slice(pdd const& p);
enode* pdd2slice(pdd const& p);
/** Get variable representing src[hi:lo] */
pvar mk_slice_extract(slice src, unsigned hi, unsigned lo);
pvar mk_slice_extract(enode* src, unsigned hi, unsigned lo);
bool invariant() 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;
public:
slicing(solver& s);
@ -265,10 +287,7 @@ namespace polysat {
// - set of variables that share at least one slice with v (need variable, offset/width relative to v)
std::ostream& display(std::ostream& out) const;
std::ostream& display(std::ostream& out, slice s) const;
std::ostream& display_tree(std::ostream& out) const;
std::ostream& display_tree(std::ostream& out, slice s, unsigned indent, unsigned hi, unsigned lo) const;
};
inline std::ostream& operator<<(std::ostream& out, slicing const& s) { return s.display(out); }