3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-23 17:15:31 +00:00

enable new propagate_from_containing_slice

This commit is contained in:
Jakob Rath 2024-02-08 15:11:36 +01:00
parent 0ada7f8e40
commit 8d45c954c5
2 changed files with 49 additions and 87 deletions

View file

@ -41,6 +41,7 @@ namespace polysat {
rational value;
fixed_slice() = default;
fixed_slice(rational value, unsigned offset, unsigned length) : offset(offset), length(length), value(std::move(value)) {}
unsigned end() const { return offset + length; }
};
struct fixed_claim : public fixed_slice {

View file

@ -97,7 +97,6 @@ namespace polysat {
entry* e = find_overlap(w, layer, value);
if (!e)
continue;
m_explain.push_back({ e, value });
m_explain_kind = explain_t::assignment;
return false;
@ -652,19 +651,27 @@ namespace polysat {
fixed_slice_extra_vector fixed;
offset_slice_extra_vector subslices;
#if 0
verbose_stream() << "fixed subslices of v" << m_var << ":\n";
c.s.get_fixed_sub_slices(m_var, fixed, subslices); // TODO: move into m_fixed bits?
// TODO: the conflict should be from the subslice with highest level. we might want to skip the others.
unsigned max_level = 0;
for (auto const& slice : subslices)
max_level = std::max(max_level, slice.level);
// order by:
// - level descending
// usually, a sub-slice at higher level is responsible for the assignment.
// not always: e.g., could assign slice at level 5 but merge makes it a sub-slice only at level 10.
// (seems to work by not only considering max-level sub-slices.)
// - size ascending
// e.g., prefers constant 'c' if we have pvars for both 'c' and 'concat(c,...)'
std::sort(subslices.begin(), subslices.end(), [&](auto const& a, auto const& b) -> bool {
return a.level > b.level
|| (a.level == b.level && c.size(a.v) < c.size(b.v));
});
for (auto const& slice : subslices)
if (auto d = propagate_from_containing_slice(e, value, e_deps, fixed, slice); !d.is_null())
return d;
#else
for (auto const& slice : m_overlaps)
if (auto d = propagate_from_containing_slice(e, value, e_deps, fixed, {slice.v, slice.offset, 0}); !d.is_null())
return d;
#endif
return null_dependency;
}
@ -674,7 +681,7 @@ namespace polysat {
unsigned w_level = slice.level; // level where value of w was fixed
if (w == m_var)
return null_dependency;
verbose_stream() << "v" << m_var << " size " << c.size(m_var) << ", v" << w << " size " << c.size(w) << " offset " << offset << " level " << w_level << "\n";
// verbose_stream() << "v" << m_var << " size " << c.size(m_var) << ", v" << w << " size " << c.size(w) << " offset " << offset << " level " << w_level << "\n";
// Let:
// v = m_var[e->bit_width-1:0]
@ -704,7 +711,7 @@ namespace polysat {
unsigned const y_sz = std::min(w_sz, v_sz - z_sz);
unsigned const x_sz = v_sz - y_sz - z_sz;
rational const& w_shift = rational::power_of_two(w_sz - y_sz);
verbose_stream() << "v_sz " << v_sz << " w_sz " << w_sz << " / x_sz " << x_sz << " y_sz " << y_sz << " z_sz " << z_sz << "\n";
// verbose_stream() << "v_sz " << v_sz << " w_sz " << w_sz << " / x_sz " << x_sz << " y_sz " << y_sz << " z_sz " << z_sz << "\n";
SASSERT_EQ(v_sz, x_sz + y_sz + z_sz);
SASSERT(!e->interval.is_full());
@ -712,9 +719,9 @@ namespace polysat {
rational const& hi = e->interval.hi_val();
r_interval const v_ivl = r_interval::proper(lo, hi);
verbose_stream() << "propagate interval " << v_ivl << " from v" << m_var << " to v" << w << "[" << y_sz << "]" << "\n";
#if 0
IF_VERBOSE(3, {
verbose_stream() << "propagate interval " << v_ivl << " from v" << m_var << " to v" << w << "[" << y_sz << "]" << "\n";
});
dependency_vector deps;
deps.append(e_deps); // explains e
@ -756,13 +763,18 @@ namespace polysat {
unsigned const b = std::max(best_end, x_off);
unsigned const a = remaining_x_end - b;
SASSERT(remaining_x_sz >= a);
ivl = chop_off_upper(ivl, a, b);
remaining_x_sz -= a;
remaining_x_end -= a;
IF_VERBOSE(4, {
verbose_stream() << " chop " << a << " upper bits\n";
verbose_stream() << " => " << ivl << "\n";
});
}
if (best_end > x_off) {
verbose_stream() << " using fixed slice " << best.value << "[" << best.length << "]@" << best.offset << "\n";
SASSERT(remaining_x_end == best_end);
SASSERT(remaining_x_end <= best.end());
// chop off portion with fixed value
@ -774,10 +786,14 @@ namespace polysat {
value = mod2k(value, a);
ivl = chop_off_upper(ivl, a, b, &value);
deps.push_back(best.dep); // justification for the fixed value
remaining_x_sz -= a;
remaining_x_end -= a;
deps.push_back(best.dep); // justification for the fixed value
IF_VERBOSE(4, {
verbose_stream() << " chop " << a << " upper bits with value " << value << " from fixed slice " << best.value << "[" << best.length << "]@" << best.offset << "\n";
verbose_stream() << " => " << ivl << "\n";
});
}
}
@ -815,14 +831,19 @@ namespace polysat {
// there is a gap without a fixed value
unsigned const b = best_off - remaining_z_off;
unsigned const a = y_sz + z_sz - b;
SASSERT(remaining_x_sz >= a);
SASSERT(remaining_z_sz >= b);
ivl = chop_off_lower(ivl, a, b);
remaining_z_sz -= b;
remaining_z_off += b;
IF_VERBOSE(4, {
verbose_stream() << " chop " << b << " lower bits\n";
verbose_stream() << " => " << ivl << "\n";
});
}
if (best_off < z_sz) {
verbose_stream() << " using fixed slice " << best.value << "[" << best.length << "]@" << best.offset << "\n";
SASSERT_EQ(best_off, remaining_z_off);
unsigned const b = std::min(best.end(), z_sz) - remaining_z_off;
unsigned const a = y_sz + z_sz - b;
@ -832,18 +853,24 @@ namespace polysat {
value = mod2k(value, b);
ivl = chop_off_lower(ivl, a, b, &value);
deps.push_back(best.dep); // justification for the fixed value
remaining_z_sz -= b;
remaining_z_off += b;
deps.push_back(best.dep); // justification for the fixed value
IF_VERBOSE(4, {
verbose_stream() << " chop " << b << " lower bits with value " << value << " from fixed slice " << best.value << "[" << best.length << "]@" << best.offset << "\n";
verbose_stream() << " => " << ivl << "\n";
});
}
}
if (ivl.is_empty())
return null_dependency;
verbose_stream() << "=> v" << w << "[" << y_sz << "] not in " << ivl << "\n"; // << " using x=" << x_value << " z=" << z_value << "\n";
IF_VERBOSE(3, {
verbose_stream() << "=> v" << w << "[" << y_sz << "] not in " << ivl << "\n";
});
if (ivl.is_full()) {
// TODO: set conflict
NOT_IMPLEMENTED_YET();
@ -854,72 +881,6 @@ namespace polysat {
return c.propagate(sc, deps, "propagate from containing slice");
}
#else
// TODO: by the time we call viable::assign, m_fixed_bits only contains a single slice with 'value'.
// if we had access to the actually fixed slices (not just fixed because of the latest assignment),
// we could chop off fixed and non-fixed parts according to actually fixed slices;
// getting rid of the distinction into two separate cases below.
// more general interval, without taking into account fixed values
{
r_interval const yz_ivl = chop_off_upper(v_ivl, x_sz, y_sz + z_sz); // chop off x-part
r_interval const y_ivl = chop_off_lower(yz_ivl, y_sz, z_sz); // chop off z-part
if (y_ivl.is_full()) {
// no value for y
UNREACHABLE(); // without fixed x or z, this is only possible if e->interval was full
}
else if (!y_ivl.is_empty()) {
// proper interval
verbose_stream() << "=> v" << w << "[" << y_sz << "] not in " << y_ivl << "\n";
auto sc = ~cs.ult(w_shift * (c.var(w) - y_ivl.lo()), w_shift * (y_ivl.hi() - y_ivl.lo()));
dependency_vector deps;
deps.append(e_deps); // explains e
deps.push_back(offset_claim{m_var, slice}); // explains m_var[...] = w
return c.propagate(sc, deps, "propagate from containing slice (general)");
}
else {
SASSERT(y_ivl.is_empty());
// y is unconstrained, nothing to do here
}
}
// tighter interval, but depends on fixed values
{
rational const var_x_value = x_sz > 0 ? machine_div2k(value, y_sz + z_sz) : rational(0);
rational const x_value = x_sz > 0 ? mod2k(machine_div2k(value, y_sz + z_sz), x_sz) : rational(0);
rational const z_value = z_sz > 0 ? mod2k(value, z_sz) : rational(0);
r_interval const yz_ivl = chop_off_upper(v_ivl, x_sz, y_sz + z_sz, &x_value); // chop off x-part
r_interval const y_ivl = chop_off_lower(yz_ivl, y_sz, z_sz, &z_value); // chop off z-part
if (y_ivl.is_full()) {
// no value for y
// TODO: the signed_constraint to propagate would be 'false'
NOT_IMPLEMENTED_YET();
}
else if (!y_ivl.is_empty()) {
// proper interval
verbose_stream() << "=> v" << w << "[" << y_sz << "] not in " << y_ivl << " using x=" << x_value << " z=" << z_value << "\n";
auto sc = ~cs.ult(w_shift * (c.var(w) - y_ivl.lo()), w_shift * (y_ivl.hi() - y_ivl.lo()));
dependency_vector deps;
deps.append(e_deps); // explains e
deps.push_back(offset_claim{m_var, slice}); // explains m_var[...] = w
if (x_sz > 0) {
deps.push_back(fixed_claim{m_var, var_x_value, y_sz + z_sz, x_sz + (c.size(m_var) - v_sz)}); // m_var[upper] = var_x_value
// stronger but crashes in bv_plugin::explain_slice, TODO: why?
// deps.push_back(fixed_claim{m_var, x_value, y_sz + z_sz, x_sz}); // v[upper] = x_value
}
if (z_sz > 0)
deps.push_back(fixed_claim{m_var, z_value, 0, z_sz}); // v[lower] = z_value
return c.propagate(sc, deps, "propagate from containing slice (fixed)");
}
else {
SASSERT(y_ivl.is_empty());
// y is unconstrained, nothing to do here
}
}
#endif
return null_dependency;
}