mirror of
https://github.com/Z3Prover/z3
synced 2026-03-06 13:24:51 +00:00
Fix integration of propagate_from_containing_slice
This commit is contained in:
parent
dfb200a3c9
commit
aa1285288e
6 changed files with 137 additions and 56 deletions
|
|
@ -83,12 +83,13 @@ namespace polysat {
|
|||
return e;
|
||||
}
|
||||
|
||||
bool viable::assign(pvar v, rational const& value) {
|
||||
bool viable::assign(pvar v, rational const& value, dependency dep) {
|
||||
m_var = v;
|
||||
m_explain_kind = explain_t::none;
|
||||
m_num_bits = c.size(v);
|
||||
m_fixed_bits.init(v);
|
||||
m_explain.reset();
|
||||
m_assign_dep = null_dependency;
|
||||
init_overlaps(v);
|
||||
bool first = true;
|
||||
while (true) {
|
||||
|
|
@ -99,6 +100,7 @@ namespace polysat {
|
|||
continue;
|
||||
m_explain.push_back({ e, value });
|
||||
m_explain_kind = explain_t::assignment;
|
||||
m_assign_dep = dep;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -124,6 +126,7 @@ namespace polysat {
|
|||
m_fixed_bits.init(v);
|
||||
init_overlaps(v);
|
||||
m_explain_kind = explain_t::none;
|
||||
m_assign_dep = null_dependency;
|
||||
|
||||
for (unsigned rounds = 0; rounds < 10; ) {
|
||||
entry* n = find_overlap(lo);
|
||||
|
|
@ -558,9 +561,9 @@ namespace polysat {
|
|||
}
|
||||
|
||||
/*
|
||||
* Explain why the current variable is not viable or
|
||||
* or why it can only have a single value.
|
||||
*/
|
||||
* Explain why the current variable is not viable or
|
||||
* or why it can only have a single value.
|
||||
*/
|
||||
dependency_vector viable::explain() {
|
||||
dependency_vector result;
|
||||
auto last = m_explain.back();
|
||||
|
|
@ -635,20 +638,32 @@ namespace polysat {
|
|||
if (m_explain_kind == explain_t::assignment) {
|
||||
// there is just one entry
|
||||
SASSERT(m_explain.size() == 1);
|
||||
SASSERT(!m_assign_dep.is_null());
|
||||
explain_entry(last.e);
|
||||
|
||||
// assignment conflict and propagation from containing slice depends on concrete values,
|
||||
// so we also need the evaluations of linear terms
|
||||
SASSERT(!c.is_assigned(m_var)); // assignment of m_var is justified by m_assign_dep
|
||||
auto index = last.e->constraint_index;
|
||||
if (!index.is_null())
|
||||
result.append(c.explain_weak_eval(c.get_constraint(index)));
|
||||
|
||||
auto exp = propagate_from_containing_slice(last.e, last.value, result);
|
||||
// verbose_stream() << "exp is " << exp << "\n";
|
||||
if (!exp.is_null()) {
|
||||
result.clear();
|
||||
result.push_back(exp);
|
||||
}
|
||||
// 'result' so far contains explanation for entry and its weak evaluation
|
||||
switch (propagate_from_containing_slice(last.e, last.value, result)) {
|
||||
case l_true:
|
||||
// propagated interval onto subslice
|
||||
result = m_containing_slice_deps;
|
||||
break;
|
||||
case l_false:
|
||||
// conflict (projected interval is full)
|
||||
result = m_containing_slice_deps;
|
||||
break;
|
||||
case l_undef:
|
||||
// unable to propagate interval to containing slice
|
||||
// fallback explanation uses assignment of m_var
|
||||
result.push_back(m_assign_dep);
|
||||
break;
|
||||
};
|
||||
}
|
||||
unmark();
|
||||
if (c.inconsistent())
|
||||
|
|
@ -656,20 +671,32 @@ namespace polysat {
|
|||
return result;
|
||||
}
|
||||
|
||||
dependency viable::propagate_from_containing_slice(entry* e, rational const& value, dependency_vector const& e_deps) {
|
||||
/*
|
||||
* l_true: successfully projected interval onto subslice
|
||||
* l_false: also successfully projected interval onto subslice, resulted in full interval
|
||||
* l_undef: failed
|
||||
*
|
||||
* In case of l_true/l_false, conflict will be in m_containing_slice_deps.
|
||||
*/
|
||||
lbool viable::propagate_from_containing_slice(entry* e, rational const& value, dependency_vector const& e_deps) {
|
||||
verbose_stream() << "\n\n\n\n\n\nNon-viable assignment for v" << m_var << " size " << c.size(m_var) << "\n";
|
||||
display_one(verbose_stream() << "entry: ", e) << "\n";
|
||||
verbose_stream() << "value " << value << "\n";
|
||||
// verbose_stream() << "m_overlaps " << m_overlaps << "\n";
|
||||
m_fixed_bits.display(verbose_stream() << "fixed: ") << "\n";
|
||||
|
||||
// TODO: each of subslices corresponds to one in fixed, but may occur with different pvars
|
||||
// for each offset/length with pvar we need to do the projection only once.
|
||||
fixed_slice_extra_vector fixed;
|
||||
offset_slice_extra_vector subslices;
|
||||
c.s.get_fixed_sub_slices(m_var, fixed, subslices); // TODO: move into m_fixed bits?
|
||||
|
||||
TRACE("bv", c.display(tout));
|
||||
|
||||
// this case occurs if e-graph merges e.g. nodes "x - 2" and "3";
|
||||
// polysat will see assignment "x = 5" but no fixed bits
|
||||
if (subslices.empty())
|
||||
return null_dependency;
|
||||
return l_undef;
|
||||
|
||||
unsigned max_level = 0;
|
||||
for (auto const& slice : subslices)
|
||||
|
|
@ -688,19 +715,19 @@ namespace polysat {
|
|||
});
|
||||
|
||||
for (auto const& slice : subslices)
|
||||
if (auto d = propagate_from_containing_slice(e, value, e_deps, fixed, slice); !d.is_null())
|
||||
return d;
|
||||
return null_dependency;
|
||||
if (auto r = propagate_from_containing_slice(e, value, e_deps, fixed, slice); r != l_undef)
|
||||
return r;
|
||||
return l_undef;
|
||||
}
|
||||
|
||||
dependency viable::propagate_from_containing_slice(entry* e, rational const& value, dependency_vector const& e_deps, fixed_slice_extra_vector const& fixed, offset_slice_extra const& slice) {
|
||||
lbool viable::propagate_from_containing_slice(entry* e, rational const& value, dependency_vector const& e_deps, fixed_slice_extra_vector const& fixed, offset_slice_extra const& slice) {
|
||||
pvar w = slice.child;
|
||||
unsigned offset = slice.offset;
|
||||
unsigned w_level = slice.level; // level where value of w was fixed
|
||||
if (w == m_var)
|
||||
return null_dependency;
|
||||
return l_undef;
|
||||
if (w == e->var)
|
||||
return null_dependency;
|
||||
return l_undef;
|
||||
|
||||
// verbose_stream() << "v" << m_var << " size " << c.size(m_var) << ", v" << w << " size " << c.size(w) << " offset " << offset << " level " << w_level << "\n";
|
||||
|
||||
|
|
@ -725,7 +752,7 @@ namespace polysat {
|
|||
// wwwwwwwww
|
||||
unsigned const v_sz = e->bit_width;
|
||||
if (offset >= v_sz)
|
||||
return null_dependency;
|
||||
return l_undef;
|
||||
|
||||
unsigned const w_sz = c.size(w);
|
||||
unsigned const z_sz = offset;
|
||||
|
|
@ -744,9 +771,8 @@ namespace polysat {
|
|||
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
|
||||
deps.push_back(offset_claim{m_var, slice}); // explains m_var[...] = w
|
||||
dependency_vector& deps = m_containing_slice_deps;
|
||||
deps.reset();
|
||||
|
||||
r_interval ivl = v_ivl;
|
||||
|
||||
|
|
@ -756,6 +782,8 @@ namespace polysat {
|
|||
while (remaining_x_sz > 0 && !ivl.is_empty() && !ivl.is_full()) {
|
||||
unsigned remaining_x_end = remaining_x_sz + x_off;
|
||||
// find next fixed slice (prefer lower level)
|
||||
// sort fixed claims by bound (upper: decreasing, lower: increasing), then by merge-level (prefer lower merge level), ignore merge level higher than var? (or just max?)
|
||||
// note that we might choose multiple overlapping ones, if they allow to make more progress?
|
||||
fixed_slice_extra best;
|
||||
unsigned best_end = 0;
|
||||
SASSERT(best_end < x_off); // because y_sz != 0
|
||||
|
|
@ -819,7 +847,7 @@ namespace polysat {
|
|||
}
|
||||
|
||||
if (ivl.is_empty())
|
||||
return null_dependency;
|
||||
return l_undef;
|
||||
|
||||
// chop off z-part
|
||||
unsigned remaining_z_sz = z_sz;
|
||||
|
|
@ -886,24 +914,44 @@ namespace polysat {
|
|||
}
|
||||
|
||||
if (ivl.is_empty())
|
||||
return null_dependency;
|
||||
return l_undef;
|
||||
|
||||
IF_VERBOSE(3, {
|
||||
verbose_stream() << "=> v" << w << "[" << y_sz << "] not in " << ivl << "\n";
|
||||
});
|
||||
|
||||
deps.append(e_deps); // explains e
|
||||
deps.push_back(offset_claim{m_var, slice}); // explains m_var[...] = w
|
||||
|
||||
if (ivl.is_full()) {
|
||||
pdd zero = c.var2pdd(m_var).zero();
|
||||
auto sc = cs.ult(zero, zero); // false
|
||||
return c.propagate(sc, deps, "propagate from containing slice (conflict)");
|
||||
// deps is a conflict
|
||||
return l_false;
|
||||
}
|
||||
else {
|
||||
// proper interval
|
||||
auto sc = ~cs.ult(w_shift * (c.var(w) - ivl.lo()), w_shift * (ivl.hi() - ivl.lo()));
|
||||
return c.propagate(sc, deps, "propagate from containing slice");
|
||||
SASSERT(ivl.is_proper() && !ivl.is_empty());
|
||||
// deps => 2^w_shift w not in ivl
|
||||
signed_constraint sc = ~cs.ult(w_shift * (c.var(w) - ivl.lo()), w_shift * (ivl.hi() - ivl.lo()));
|
||||
dependency d = c.propagate(sc, deps, "propagate from containing slice");
|
||||
|
||||
verbose_stream() << "v" << w << " value " << slice.value << "\n";
|
||||
verbose_stream() << "v" << w << " value-dep " << slice.dep << "\n";
|
||||
|
||||
if (ivl.contains(slice.value)) {
|
||||
// the conflict is projected interval + fixed value
|
||||
deps.reset();
|
||||
deps.push_back(d);
|
||||
deps.push_back(slice.dep);
|
||||
return l_true;
|
||||
}
|
||||
else {
|
||||
// interval propagation worked but it doesn't conflict the currently assigned value
|
||||
// TODO: also skip propagation of the signed constraint in this case?
|
||||
return l_undef;
|
||||
}
|
||||
}
|
||||
|
||||
return null_dependency;
|
||||
return l_undef;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1120,6 +1168,7 @@ namespace polysat {
|
|||
}
|
||||
|
||||
// verbose_stream() << "v" << v << " " << sc << " " << ne->interval << "\n";
|
||||
TRACE("bv", tout << "v" << v << " " << sc << " " << ne->interval << "\n"; display_one(tout, ne) << "\n");
|
||||
|
||||
if (ne->interval.is_currently_empty()) {
|
||||
m_alloc.push_back(ne);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue