3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-08-14 06:45:25 +00:00

slicing: change how values are tracked

Do not eagerly merge equivalence classes with the same constant value.

Reason:
- when we split a value slice, the subslice might already exist in the
  egraph and already have subslices itself, which breaks assumptions in
  split/split_core.
- introduces unnecessary splits

Now:
- wrap constants into fresh function symbol before creating the enode
- check value compatibility in on_merge callback instead
- track pointer to value_node for each enode, and update it in on_merge
This commit is contained in:
Jakob Rath 2023-08-17 17:05:59 +02:00
parent 08928d041a
commit c95ff56d2d
3 changed files with 304 additions and 109 deletions

View file

@ -41,6 +41,7 @@ namespace polysat {
public:
using enode = euf::enode;
using enode_pair = euf::enode_pair;
using enode_vector = euf::enode_vector;
private:
@ -81,6 +82,7 @@ namespace polysat {
// We use the following kinds of enodes:
// - proper slices (of variables)
// - value slices
// - interpreted value nodes ... these are short-lived, and only created to immediately trigger a conflict inside the egraph
// - virtual concat(...) expressions
// - equalities between enodes (to track disequalities; currently not represented in slice_info)
struct slice_info {
@ -92,6 +94,7 @@ namespace polysat {
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]
enode* value_node = nullptr; // the root of an equivalence class stores the value slice here, if any
void reset() { *this = slice_info(); }
bool has_sub() const { return !!sub_hi; }
@ -103,7 +106,7 @@ namespace polysat {
bool is_slice(enode* n) const;
bool is_proper_slice(enode* n) const { return !is_value(n) && is_slice(n); }
bool is_value(enode* n) const { return n->interpreted(); }
bool is_value(enode* n) const;
bool is_concat(enode* n) const;
bool is_equality(enode* n) const { return n->is_equality(); }
@ -148,6 +151,9 @@ namespace polysat {
enode* parent(enode* s) const { return info(s).parent; }
enode* get_value_node(enode* s) const { return info(s->get_root()).value_node; }
void set_value_node(enode* s, enode* value_node);
bool has_sub(enode* s) const { return info(s).has_sub(); }
/// Upper subslice (direct child, not necessarily the representative)
@ -159,6 +165,9 @@ namespace polysat {
// Retrieve (or create) a slice representing the given value.
enode* mk_value_slice(rational const& val, unsigned bit_width);
// Turn value node into unwrapped BV constant node
enode* mk_interpreted_value_node(enode* value_slice);
rational get_value(enode* s) const;
bool try_get_value(enode* s, rational& val) const;
@ -166,13 +175,8 @@ namespace polysat {
void split(enode* s, unsigned cut);
void split_core(enode* s, unsigned cut);
template <bool should_get_root>
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(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 get_root_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.
@ -195,6 +199,7 @@ namespace polysat {
/** Extract reason for x == y */
void explain_equal(pvar x, pvar y, ptr_vector<void>& out_deps);
void egraph_on_make(enode* n);
void egraph_on_merge(enode* root, enode* other);
void egraph_on_propagate(enode* lit, enode* ante);
@ -233,18 +238,19 @@ namespace polysat {
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;
bool is_extract(pvar v) const;
bool is_extract(pvar v, pvar& out_src, pvar& out_hi, pvar& out_lo) const;
// svector<extract_args> m_extract_origin; // pvar -> extract_args
// TODO: add 'm_extract_origin' (pvar -> extract_args)? 1. for dependency tracking when sharing subslice trees; 2. for easily checking if a variable is an extraction of another; 3. also makes the replay easier
// bool is_extract(pvar v) const { return m_extract_origin[v].src != null_var; }
enum class trail_item : std::uint8_t {
add_var,
split_core,
mk_extract,
mk_concat,
set_value_node,
};
svector<trail_item> m_trail;
enode_vector m_split_trail;
enode_vector m_enode_trail;
svector<extract_args> m_extract_trail;
unsigned_vector m_scopes;
@ -260,6 +266,7 @@ namespace polysat {
void undo_add_var();
void undo_split_core();
void undo_mk_extract();
void undo_set_value_node();
mutable enode_vector m_tmp1;
mutable enode_vector m_tmp2;
@ -305,6 +312,8 @@ namespace polysat {
};
friend std::ostream& operator<<(std::ostream& out, dep_pp const& d) { return d.display(out); }
euf::egraph::e_pp e_pp(enode* n) const { return euf::egraph::e_pp(m_egraph, n); }
public:
slicing(solver& s);