mirror of
https://github.com/Z3Prover/z3
synced 2025-04-29 03:45:51 +00:00
Fix PDD factor cache in case GC happens while factoring (#5170)
* Add method to find largest power of two that is a divisor * Binary resolve on PDDs * Add unit tests for binary resolve * Fix factor cache access in case GC happens while factoring * Coding conventions * Change to gc_generation
This commit is contained in:
parent
75c87a2869
commit
0e9fc4762a
3 changed files with 192 additions and 9 deletions
|
@ -696,8 +696,8 @@ namespace dd {
|
|||
void pdd_manager::factor(pdd const& p, unsigned v, unsigned degree, pdd& lc, pdd& rest) {
|
||||
unsigned level_v = m_var2level[v];
|
||||
if (degree == 0) {
|
||||
lc = p;
|
||||
rest = zero();
|
||||
lc = zero();
|
||||
rest = p;
|
||||
return;
|
||||
}
|
||||
if (level(p.root) < level_v) {
|
||||
|
@ -707,12 +707,13 @@ namespace dd {
|
|||
}
|
||||
// Memoize nontrivial cases
|
||||
auto* et = m_factor_cache.insert_if_not_there2({p.root, v, degree});
|
||||
factor_entry& e = et->get_data();
|
||||
if (e.is_valid()) {
|
||||
lc = pdd(e.m_lc, this);
|
||||
rest = pdd(e.m_rest, this);
|
||||
factor_entry* e = &et->get_data();
|
||||
if (e->is_valid()) {
|
||||
lc = pdd(e->m_lc, this);
|
||||
rest = pdd(e->m_rest, this);
|
||||
return;
|
||||
}
|
||||
unsigned const gc_generation = m_gc_generation;
|
||||
if (level(p.root) > level_v) {
|
||||
pdd lc1 = zero(), rest1 = zero();
|
||||
pdd vv = mk_var(p.var());
|
||||
|
@ -743,10 +744,118 @@ namespace dd {
|
|||
rest = p;
|
||||
}
|
||||
}
|
||||
e.m_lc = lc.root;
|
||||
e.m_rest = rest.root;
|
||||
if (gc_generation != m_gc_generation) {
|
||||
// Cache was reset while factoring (due to GC),
|
||||
// which means the old entry has been removed and we need to insert it again.
|
||||
auto* et = m_factor_cache.insert_if_not_there2({p.root, v, degree});
|
||||
e = &et->get_data();
|
||||
}
|
||||
e->m_lc = lc.root;
|
||||
e->m_rest = rest.root;
|
||||
}
|
||||
|
||||
template <class Fn>
|
||||
pdd pdd_manager::map_coefficients(pdd const& p, Fn f) {
|
||||
if (p.is_val()) {
|
||||
return mk_val(rational(f(p.val())));
|
||||
} else {
|
||||
pdd x = mk_var(p.var());
|
||||
pdd lo = map_coefficients(p.lo(), f);
|
||||
pdd hi = map_coefficients(p.hi(), f);
|
||||
return x*hi + lo;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform S-polynomial reduction on p by q,
|
||||
* treating monomial with v as leading.
|
||||
*
|
||||
* p = a v^l + b = a' 2^j v^l + b
|
||||
* q = c v^m + d = c' 2^j v^m + d
|
||||
* such that
|
||||
* deg(v, p) = l, i.e., v does not divide a and there is no v^l in b
|
||||
* deg(v, q) = m, i.e., v does not divide c and there is no v^m in d
|
||||
* l >= m
|
||||
* j maximal, i.e., not both of a', c' are divisible by 2
|
||||
*
|
||||
* Then we reduce p by q:
|
||||
*
|
||||
* r = c' p - a' v^(l-m) q
|
||||
* = b c' - a' d v^(l-m)
|
||||
*/
|
||||
bool pdd_manager::resolve(unsigned v, pdd const& p, pdd const& q, pdd& r) {
|
||||
unsigned const l = p.degree(v);
|
||||
unsigned const m = q.degree(v);
|
||||
if (l < m) {
|
||||
// no reduction
|
||||
return false;
|
||||
}
|
||||
if (m == 0) {
|
||||
// no reduction (result would still contain v^l)
|
||||
return false;
|
||||
}
|
||||
pdd a = zero();
|
||||
pdd b = zero();
|
||||
pdd c = zero();
|
||||
pdd d = zero();
|
||||
p.factor(v, l, a, b);
|
||||
q.factor(v, m, c, d);
|
||||
unsigned const j = std::min(max_pow2_divisor(a), max_pow2_divisor(c));
|
||||
SASSERT(j != UINT_MAX); // should only be possible when both l and m are 0
|
||||
rational const pow2j = rational::power_of_two(j);
|
||||
auto div_pow2j = [&pow2j](rational const& r) -> rational {
|
||||
rational result = r / pow2j;
|
||||
SASSERT(result.is_int());
|
||||
return result;
|
||||
};
|
||||
pdd aa = map_coefficients(a, div_pow2j);
|
||||
pdd cc = map_coefficients(c, div_pow2j);
|
||||
pdd vv = one();
|
||||
for (unsigned deg = l - m; deg-- > 0; ) {
|
||||
vv *= mk_var(v);
|
||||
}
|
||||
r = b * cc - aa * d * vv;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the largest j such that 2^j divides p.
|
||||
*/
|
||||
unsigned pdd_manager::max_pow2_divisor(PDD p) {
|
||||
init_mark();
|
||||
unsigned min_j = UINT_MAX;
|
||||
m_todo.push_back(p);
|
||||
while (!m_todo.empty()) {
|
||||
PDD r = m_todo.back();
|
||||
m_todo.pop_back();
|
||||
if (is_marked(r)) {
|
||||
continue;
|
||||
}
|
||||
set_mark(r);
|
||||
if (is_zero(r)) {
|
||||
// skip
|
||||
}
|
||||
else if (is_val(r)) {
|
||||
rational const& c = val(r);
|
||||
if (c.is_odd()) {
|
||||
m_todo.reset();
|
||||
return 0;
|
||||
} else {
|
||||
unsigned j = c.trailing_zeros();
|
||||
min_j = std::min(j, min_j);
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_todo.push_back(lo(r));
|
||||
m_todo.push_back(hi(r));
|
||||
}
|
||||
}
|
||||
return min_j;
|
||||
}
|
||||
|
||||
unsigned pdd_manager::max_pow2_divisor(pdd const& p) {
|
||||
return max_pow2_divisor(p.root);
|
||||
}
|
||||
|
||||
bool pdd_manager::is_linear(pdd const& p) {
|
||||
return is_linear(p.root);
|
||||
|
@ -1142,6 +1251,7 @@ namespace dd {
|
|||
}
|
||||
|
||||
void pdd_manager::gc() {
|
||||
m_gc_generation++;
|
||||
init_dmark();
|
||||
m_free_nodes.reset();
|
||||
SASSERT(well_formed());
|
||||
|
|
|
@ -200,6 +200,7 @@ namespace dd {
|
|||
rational m_freeze_value;
|
||||
rational m_mod2N;
|
||||
unsigned m_power_of_2 { 0 };
|
||||
unsigned m_gc_generation { 0 }; ///< will be incremented on each GC
|
||||
|
||||
void reset_op_cache();
|
||||
void init_nodes(unsigned_vector const& l2v);
|
||||
|
@ -261,6 +262,9 @@ namespace dd {
|
|||
unsigned degree(pdd const& p) const;
|
||||
unsigned degree(PDD p) const;
|
||||
unsigned degree(PDD p, unsigned v);
|
||||
unsigned max_pow2_divisor(PDD p);
|
||||
unsigned max_pow2_divisor(pdd const& p);
|
||||
template <class Fn> pdd map_coefficients(pdd const& p, Fn f);
|
||||
|
||||
void factor(pdd const& p, unsigned v, unsigned degree, pdd& lc, pdd& rest);
|
||||
|
||||
|
@ -320,6 +324,7 @@ namespace dd {
|
|||
pdd reduce(pdd const& a, pdd const& b);
|
||||
pdd subst_val(pdd const& a, vector<std::pair<unsigned, rational>> const& s);
|
||||
pdd subst_val(pdd const& a, unsigned v, rational const& val);
|
||||
bool resolve(unsigned v, pdd const& p, pdd const& q, pdd& r);
|
||||
|
||||
bool is_linear(PDD p) { return degree(p) == 1; }
|
||||
bool is_linear(pdd const& p);
|
||||
|
@ -411,6 +416,7 @@ namespace dd {
|
|||
double tree_size() const { return m.tree_size(*this); }
|
||||
unsigned degree() const { return m.degree(*this); }
|
||||
unsigned degree(unsigned v) const { return m.degree(root, v); }
|
||||
unsigned max_pow2_divisor() const { return m.max_pow2_divisor(root); }
|
||||
unsigned_vector const& free_vars() const { return m.free_vars(*this); }
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue