mirror of
https://github.com/Z3Prover/z3
synced 2025-08-30 15:00:08 +00:00
Rework handling of propagation reasons in viable
This commit is contained in:
parent
49800d6da5
commit
18d966dc02
6 changed files with 113 additions and 149 deletions
|
@ -461,15 +461,9 @@ namespace polysat {
|
||||||
s.inc_activity(v);
|
s.inc_activity(v);
|
||||||
|
|
||||||
m_vars.remove(v);
|
m_vars.remove(v);
|
||||||
for (signed_constraint const c : s.m_viable.get_constraints(v))
|
for (sat::literal const lit : s.m_viable.get_propagation_reason(v))
|
||||||
if (insert_or_replace(c))
|
if (insert_or_replace(s.lit2cnstr(lit)))
|
||||||
return;
|
return;
|
||||||
for (auto const& i : s.m_viable.units(v)) {
|
|
||||||
if (insert_or_replace(s.eq(i.lo(), i.lo_val())))
|
|
||||||
return;
|
|
||||||
if (insert_or_replace(s.eq(i.hi(), i.hi_val())))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logger().log(inf_resolve_value(s, v));
|
logger().log(inf_resolve_value(s, v));
|
||||||
|
|
||||||
revert_pvar(v);
|
revert_pvar(v);
|
||||||
|
@ -627,12 +621,8 @@ namespace polysat {
|
||||||
|
|
||||||
auto const& j = s.m_justification[v];
|
auto const& j = s.m_justification[v];
|
||||||
if (j.is_propagation_by_viable()) {
|
if (j.is_propagation_by_viable()) {
|
||||||
for (signed_constraint c : s.m_viable.get_constraints(v))
|
for (sat::literal const lit : s.m_viable.get_propagation_reason(v))
|
||||||
enqueue_constraint(c);
|
enqueue_lit(lit);
|
||||||
for (auto const& i : s.m_viable.units(v)) {
|
|
||||||
enqueue_constraint(s.eq(i.lo(), i.lo_val()));
|
|
||||||
enqueue_constraint(s.eq(i.hi(), i.hi_val()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (j.is_propagation_by_slicing()) {
|
else if (j.is_propagation_by_slicing()) {
|
||||||
s.m_slicing.explain_value(v, enqueue_lit, enqueue_var);
|
s.m_slicing.explain_value(v, enqueue_lit, enqueue_var);
|
||||||
|
|
|
@ -740,6 +740,10 @@ namespace polysat {
|
||||||
m_viable_fallback.pop_constraint();
|
m_viable_fallback.pop_constraint();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case trail_instr_t::viable_propagation_i: {
|
||||||
|
m_viable.pop_propagation_reason();
|
||||||
|
break;
|
||||||
|
}
|
||||||
case trail_instr_t::assign_i: {
|
case trail_instr_t::assign_i: {
|
||||||
auto v = m_search.back().var();
|
auto v = m_search.back().var();
|
||||||
LOG_V(20, "Undo assign_i: v" << v);
|
LOG_V(20, "Undo assign_i: v" << v);
|
||||||
|
@ -866,7 +870,9 @@ namespace polysat {
|
||||||
// The fallback solver currently does not detect propagations, because we would need to handle justifications differently.
|
// The fallback solver currently does not detect propagations, because we would need to handle justifications differently.
|
||||||
// However, this case may still occur if during viable::intersect, we run into the refinement budget,
|
// However, this case may still occur if during viable::intersect, we run into the refinement budget,
|
||||||
// but here, we continue refinement and actually succeed until propagation.
|
// but here, we continue refinement and actually succeed until propagation.
|
||||||
assign_propagate_by_viable(v, val);
|
// ???
|
||||||
|
UNREACHABLE();
|
||||||
|
SASSERT(m_justification[v].is_propagation_by_viable());
|
||||||
return;
|
return;
|
||||||
case find_t::multiple:
|
case find_t::multiple:
|
||||||
j = justification::decision(m_level + 1);
|
j = justification::decision(m_level + 1);
|
||||||
|
@ -886,19 +892,10 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
void solver::assign_propagate_by_viable(pvar v, rational const& val) {
|
void solver::assign_propagate_by_viable(pvar v, rational const& val) {
|
||||||
// NOTE:
|
|
||||||
// The propagation v := val might depend on a lower level than the current level (m_level).
|
|
||||||
// This can happen if the constraints that cause the propagation have been bool-propagated at an earlier level,
|
|
||||||
// but appear later in the stack (cf. replay).
|
|
||||||
// The level of v should then also be the earlier level instead of m_level.
|
|
||||||
unsigned lvl = 0;
|
unsigned lvl = 0;
|
||||||
for (signed_constraint c : m_viable.get_constraints(v)) {
|
for (sat::literal const lit : m_viable.get_propagation_reason(v)) {
|
||||||
LOG("due to: " << lit_pp(*this, c));
|
VERIFY(m_bvars.is_assigned(lit));
|
||||||
VERIFY(m_bvars.is_assigned(c.blit()));
|
lvl = std::max(lvl, m_bvars.level(lit));
|
||||||
lvl = std::max(lvl, m_bvars.level(c.blit()));
|
|
||||||
for (pvar w : c->vars())
|
|
||||||
if (is_assigned(w)) // TODO: question of which variables are relevant. e.g., v1 > v0 implies v1 > 0 without dependency on v0. maybe add a lemma v1 > v0 --> v1 > 0 on the top level to reduce false variable dependencies? instead of handling such special cases separately everywhere.
|
|
||||||
lvl = std::max(lvl, get_level(w));
|
|
||||||
}
|
}
|
||||||
assign_propagate(v, val, justification::propagation_by_viable(lvl));
|
assign_propagate(v, val, justification::propagation_by_viable(lvl));
|
||||||
}
|
}
|
||||||
|
@ -1597,8 +1594,8 @@ namespace polysat {
|
||||||
auto const& j = m_justification[v];
|
auto const& j = m_justification[v];
|
||||||
out << "\t" << assignment_pp(*this, v, get_value(v)) << " @" << j.level() << " ";
|
out << "\t" << assignment_pp(*this, v, get_value(v)) << " @" << j.level() << " ";
|
||||||
if (j.is_propagation_by_viable())
|
if (j.is_propagation_by_viable())
|
||||||
for (auto const& c : m_viable.get_constraints(v))
|
for (sat::literal const lit : m_viable.get_propagation_reason(v))
|
||||||
out << c << " ";
|
out << lit2cnstr(lit) << " ";
|
||||||
if (j.is_propagation_by_slicing()) {
|
if (j.is_propagation_by_slicing()) {
|
||||||
out << "by slicing: ";
|
out << "by slicing: ";
|
||||||
const_cast<slicing&>(m_slicing).explain_value(v,
|
const_cast<slicing&>(m_slicing).explain_value(v,
|
||||||
|
|
|
@ -64,8 +64,9 @@ namespace polysat {
|
||||||
// c2 ... constraint that is currently false
|
// c2 ... constraint that is currently false
|
||||||
// Try to replace it with a new false constraint (derived from superposition with a true constraint)
|
// Try to replace it with a new false constraint (derived from superposition with a true constraint)
|
||||||
lbool ex_polynomial_superposition::find_replacement(signed_constraint c2, pvar v, conflict& core) {
|
lbool ex_polynomial_superposition::find_replacement(signed_constraint c2, pvar v, conflict& core) {
|
||||||
for (auto c1 : s.m_viable.get_constraints(v)) {
|
for (sat::literal lit1 : s.m_viable.get_propagation_reason(v)) {
|
||||||
if (!c1->contains_var(v)) // side conditions do not contain v; skip them here
|
signed_constraint c1 = s.lit2cnstr(lit1);
|
||||||
|
if (!c1->contains_var(v)) // side conditions and interval endpoint constraints do not contain v; skip them here
|
||||||
continue;
|
continue;
|
||||||
if (!c1.is_pos_eq())
|
if (!c1.is_pos_eq())
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -16,7 +16,7 @@ Author:
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
enum class trail_instr_t {
|
enum class trail_instr_t : std::uint8_t {
|
||||||
qhead_i,
|
qhead_i,
|
||||||
lemma_qhead_i,
|
lemma_qhead_i,
|
||||||
add_lemma_i,
|
add_lemma_i,
|
||||||
|
@ -26,6 +26,7 @@ namespace polysat {
|
||||||
viable_add_i,
|
viable_add_i,
|
||||||
viable_rem_i,
|
viable_rem_i,
|
||||||
viable_constraint_i,
|
viable_constraint_i,
|
||||||
|
viable_propagation_i,
|
||||||
assign_i,
|
assign_i,
|
||||||
assign_bool_i
|
assign_bool_i
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,6 +25,8 @@ TODO: plan to fix the FI "pumping":
|
||||||
- for inequalities, a coefficient 2^k*a means that intervals are periodic because the upper k bits of x are irrelevant;
|
- for inequalities, a coefficient 2^k*a means that intervals are periodic because the upper k bits of x are irrelevant;
|
||||||
storing the interval for x[K-k:0] would take care of this.
|
storing the interval for x[K-k:0] would take care of this.
|
||||||
|
|
||||||
|
TODO: we can now support propagations by fallback solver
|
||||||
|
|
||||||
--*/
|
--*/
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,12 +61,14 @@ namespace polysat {
|
||||||
m_units.push_back({});
|
m_units.push_back({});
|
||||||
m_equal_lin.push_back(nullptr);
|
m_equal_lin.push_back(nullptr);
|
||||||
m_diseq_lin.push_back(nullptr);
|
m_diseq_lin.push_back(nullptr);
|
||||||
|
m_propagation_reasons.push_back({});
|
||||||
}
|
}
|
||||||
|
|
||||||
void viable::pop_var() {
|
void viable::pop_var() {
|
||||||
m_units.pop_back();
|
m_units.pop_back();
|
||||||
m_equal_lin.pop_back();
|
m_equal_lin.pop_back();
|
||||||
m_diseq_lin.pop_back();
|
m_diseq_lin.pop_back();
|
||||||
|
m_propagation_reasons.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
viable::entry* viable::alloc_entry(pvar var) {
|
viable::entry* viable::alloc_entry(pvar var) {
|
||||||
|
@ -176,7 +180,7 @@ namespace polysat {
|
||||||
rational val;
|
rational val;
|
||||||
switch (find_viable(v, val)) {
|
switch (find_viable(v, val)) {
|
||||||
case find_t::singleton:
|
case find_t::singleton:
|
||||||
propagate(v, val);
|
SASSERT(s.m_justification[v].is_propagation_by_viable());
|
||||||
prop = true;
|
prop = true;
|
||||||
break;
|
break;
|
||||||
case find_t::empty:
|
case find_t::empty:
|
||||||
|
@ -194,7 +198,16 @@ namespace polysat {
|
||||||
return prop;
|
return prop;
|
||||||
}
|
}
|
||||||
|
|
||||||
void viable::propagate(pvar v, rational const& val) {
|
void viable::propagate(pvar v, rational const& val, ptr_vector<entry> const& reason) {
|
||||||
|
SASSERT(m_propagation_reasons[v].begin == 0);
|
||||||
|
SASSERT(m_propagation_reasons[v].len == 0);
|
||||||
|
unsigned const begin = m_propagation_reason_storage.size();
|
||||||
|
|
||||||
|
auto add_reason = [this](signed_constraint c) {
|
||||||
|
s.try_assign_eval(c);
|
||||||
|
m_propagation_reason_storage.push_back(c.blit());
|
||||||
|
};
|
||||||
|
|
||||||
// NOTE: all propagations must be justified by a prefix of \Gamma,
|
// NOTE: all propagations must be justified by a prefix of \Gamma,
|
||||||
// otherwise dependencies may be missed during conflict resolution.
|
// otherwise dependencies may be missed during conflict resolution.
|
||||||
// The propagation reason for v := val consists of the following constraints:
|
// The propagation reason for v := val consists of the following constraints:
|
||||||
|
@ -202,31 +215,36 @@ namespace polysat {
|
||||||
// - side conditions
|
// - side conditions
|
||||||
// - i.lo() == i.lo_val() for each unit interval i
|
// - i.lo() == i.lo_val() for each unit interval i
|
||||||
// - i.hi() == i.hi_val() for each unit interval i
|
// - i.hi() == i.hi_val() for each unit interval i
|
||||||
|
for (entry* e : reason) {
|
||||||
// NSB review:
|
for (signed_constraint c : e->side_cond)
|
||||||
// the bounds added by x < p and p < x in forbidden_intervals
|
add_reason(c);
|
||||||
// match_non_max, match_non_zero
|
for (signed_constraint c : e->src)
|
||||||
// use values that are approximations. Then the propagations in
|
add_reason(c);
|
||||||
// try_assign_eval are incorrect.
|
auto const& i = e->interval;
|
||||||
// For example, x > p means x has forbidden interval [0, p + 1[,
|
add_reason(s.eq(i.lo(), i.lo_val()));
|
||||||
// the numeric interval is [0, 1[, but p + 1 == 1 is not ensured
|
add_reason(s.eq(i.hi(), i.hi_val()));
|
||||||
// even p may have free variables.
|
|
||||||
// the proper side condition on p + 1 is -1 > p or -2 >= p or p + 1 != 0
|
|
||||||
// I am disabling match_non_max and match_non_zero from forbidden_interval
|
|
||||||
// The narrowing rules in ule_constraint already handle the bounds propagaitons
|
|
||||||
// as it propagates p != -1 and 0 != q (p < -1, q > 0),
|
|
||||||
//
|
|
||||||
|
|
||||||
for (auto const& c : get_constraints(v)) {
|
|
||||||
s.try_assign_eval(c);
|
|
||||||
}
|
|
||||||
for (auto const& i : units(v)) {
|
|
||||||
s.try_assign_eval(s.eq(i.lo(), i.lo_val()));
|
|
||||||
s.try_assign_eval(s.eq(i.hi(), i.hi_val()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned const len = m_propagation_reason_storage.size() - begin;
|
||||||
|
SASSERT(begin + len == m_propagation_reason_storage.size());
|
||||||
|
m_propagation_reasons[v].begin = begin;
|
||||||
|
m_propagation_reasons[v].len = len;
|
||||||
|
m_propagation_trail.push_back(v);
|
||||||
|
s.m_trail.push_back(trail_instr_t::viable_propagation_i);
|
||||||
|
|
||||||
s.assign_propagate_by_viable(v, val);
|
s.assign_propagate_by_viable(v, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void viable::pop_propagation_reason() {
|
||||||
|
pvar const v = m_propagation_trail.back();
|
||||||
|
m_propagation_trail.pop_back();
|
||||||
|
unsigned begin = m_propagation_reasons[v].begin;
|
||||||
|
unsigned len = m_propagation_reasons[v].len;
|
||||||
|
SASSERT(len > 0);
|
||||||
|
SASSERT(begin + len == m_propagation_reason_storage.size());
|
||||||
|
m_propagation_reason_storage.shrink(begin);
|
||||||
|
}
|
||||||
|
|
||||||
bool viable::intersect(pvar v, signed_constraint const& c) {
|
bool viable::intersect(pvar v, signed_constraint const& c) {
|
||||||
LOG("intersect v" << v << " in " << lit_pp(s, c));
|
LOG("intersect v" << v << " in " << lit_pp(s, c));
|
||||||
if (s.is_assigned(v)) {
|
if (s.is_assigned(v)) {
|
||||||
|
@ -1161,14 +1179,19 @@ namespace polysat {
|
||||||
|
|
||||||
find_t viable::find_viable(pvar v, rational& lo) {
|
find_t viable::find_viable(pvar v, rational& lo) {
|
||||||
rational hi;
|
rational hi;
|
||||||
switch (find_viable2(v, lo, hi)) {
|
ptr_vector<entry> relevant_entries;
|
||||||
|
switch (find_viable2(v, lo, hi, relevant_entries)) {
|
||||||
case l_true:
|
case l_true:
|
||||||
if (hi < 0) {
|
if (hi < 0) {
|
||||||
// fallback solver, treat propagations as decisions for now
|
// fallback solver, treat propagations as decisions for now
|
||||||
// (this is because the propagation justification currently always uses intervals, which is unsound in this case)
|
// (this is because the propagation justification currently always uses intervals, which is unsound in this case)
|
||||||
return find_t::multiple;
|
return find_t::multiple;
|
||||||
}
|
}
|
||||||
return (lo == hi) ? find_t::singleton : find_t::multiple;
|
if (lo == hi) {
|
||||||
|
propagate(v, lo, relevant_entries);
|
||||||
|
return find_t::singleton;
|
||||||
|
}
|
||||||
|
return find_t::multiple;
|
||||||
case l_false:
|
case l_false:
|
||||||
return find_t::empty;
|
return find_t::empty;
|
||||||
default:
|
default:
|
||||||
|
@ -1177,6 +1200,7 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool viable::has_upper_bound(pvar v, rational& out_hi, vector<signed_constraint>& out_c) {
|
bool viable::has_upper_bound(pvar v, rational& out_hi, vector<signed_constraint>& out_c) {
|
||||||
|
NOT_IMPLEMENTED_YET();
|
||||||
entry const* first = m_units[v].get_entries(s.size(v)); // TODO: take other sizes into account
|
entry const* first = m_units[v].get_entries(s.size(v)); // TODO: take other sizes into account
|
||||||
entry const* e = first;
|
entry const* e = first;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
@ -1213,6 +1237,7 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool viable::has_lower_bound(pvar v, rational& out_lo, vector<signed_constraint>& out_c) {
|
bool viable::has_lower_bound(pvar v, rational& out_lo, vector<signed_constraint>& out_c) {
|
||||||
|
NOT_IMPLEMENTED_YET();
|
||||||
entry const* first = m_units[v].get_entries(s.size(v)); // TODO: take other sizes into account
|
entry const* first = m_units[v].get_entries(s.size(v)); // TODO: take other sizes into account
|
||||||
entry const* e = first;
|
entry const* e = first;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
@ -1249,6 +1274,7 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool viable::has_max_forbidden(pvar v, signed_constraint const& c, rational& out_lo, rational& out_hi, vector<signed_constraint>& out_c) {
|
bool viable::has_max_forbidden(pvar v, signed_constraint const& c, rational& out_lo, rational& out_hi, vector<signed_constraint>& out_c) {
|
||||||
|
NOT_IMPLEMENTED_YET();
|
||||||
// TODO:
|
// TODO:
|
||||||
// - skip intervals adjacent to c's interval if they contain side conditions on y?
|
// - skip intervals adjacent to c's interval if they contain side conditions on y?
|
||||||
// constraints over y are allowed if level(c) < level(y) (e.g., boolean propagated)
|
// constraints over y are allowed if level(c) < level(y) (e.g., boolean propagated)
|
||||||
|
@ -1347,8 +1373,6 @@ namespace polysat {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// When iterating over intervals:
|
// When iterating over intervals:
|
||||||
// - instead of only intervals of v, go over intervals of each entry of overlaps
|
// - instead of only intervals of v, go over intervals of each entry of overlaps
|
||||||
// - need a function to map interval from overlap into an interval over v
|
// - need a function to map interval from overlap into an interval over v
|
||||||
|
@ -1438,7 +1462,7 @@ namespace polysat {
|
||||||
// - how to integrate fallback solver?
|
// - how to integrate fallback solver?
|
||||||
// when lowest level fails, we can try more refinement there.
|
// when lowest level fails, we can try more refinement there.
|
||||||
// in case of refinement loop, try fallback solver with constraints only from lower level.
|
// in case of refinement loop, try fallback solver with constraints only from lower level.
|
||||||
lbool viable::find_viable2(pvar v, rational& lo, rational& hi) {
|
lbool viable::find_viable2(pvar v, rational& lo, rational& hi, ptr_vector<entry>& relevant_entries) {
|
||||||
fixed_bits_info fbi;
|
fixed_bits_info fbi;
|
||||||
|
|
||||||
if (!collect_bit_information(v, true, fbi))
|
if (!collect_bit_information(v, true, fbi))
|
||||||
|
@ -1472,7 +1496,7 @@ namespace polysat {
|
||||||
|
|
||||||
rational const& max_value = s.var2pdd(v).max_value();
|
rational const& max_value = s.var2pdd(v).max_value();
|
||||||
|
|
||||||
lbool result_lo = find_on_layers(v, widths, overlaps, fbi, rational::zero(), max_value, lo);
|
lbool result_lo = find_on_layers(v, widths, overlaps, fbi, rational::zero(), max_value, lo, relevant_entries);
|
||||||
if (result_lo == l_false)
|
if (result_lo == l_false)
|
||||||
return l_false; // conflict
|
return l_false; // conflict
|
||||||
if (result_lo == l_undef)
|
if (result_lo == l_undef)
|
||||||
|
@ -1483,7 +1507,7 @@ namespace polysat {
|
||||||
return l_true;
|
return l_true;
|
||||||
}
|
}
|
||||||
|
|
||||||
lbool result_hi = find_on_layers(v, widths, overlaps, fbi, lo + 1, max_value, hi);
|
lbool result_hi = find_on_layers(v, widths, overlaps, fbi, lo + 1, max_value, hi, relevant_entries);
|
||||||
if (result_hi == l_false)
|
if (result_hi == l_false)
|
||||||
hi = lo; // no other viable value
|
hi = lo; // no other viable value
|
||||||
if (result_hi == l_undef)
|
if (result_hi == l_undef)
|
||||||
|
@ -1526,10 +1550,10 @@ namespace polysat {
|
||||||
fixed_bits_info const& fbi,
|
fixed_bits_info const& fbi,
|
||||||
rational const& to_cover_lo,
|
rational const& to_cover_lo,
|
||||||
rational const& to_cover_hi,
|
rational const& to_cover_hi,
|
||||||
rational& val
|
rational& val,
|
||||||
|
ptr_vector<entry>& relevant_entries
|
||||||
) {
|
) {
|
||||||
ptr_vector<entry> refine_todo;
|
ptr_vector<entry> refine_todo;
|
||||||
ptr_vector<entry> relevant_entries;
|
|
||||||
|
|
||||||
// max number of interval refinements before falling back to the univariate solver
|
// max number of interval refinements before falling back to the univariate solver
|
||||||
unsigned const refinement_budget = 100;
|
unsigned const refinement_budget = 100;
|
||||||
|
@ -1938,7 +1962,12 @@ namespace polysat {
|
||||||
entry** intervals_begin = intervals.data() + first_interval;
|
entry** intervals_begin = intervals.data() + first_interval;
|
||||||
unsigned num_intervals = intervals.size() - first_interval - 1;
|
unsigned num_intervals = intervals.size() - first_interval - 1;
|
||||||
|
|
||||||
if (!set_conflict_by_interval_rec(v, w, intervals_begin, num_intervals, core, create_lemma, lemma, vars_to_explain))
|
bool const ok = set_conflict_by_interval_rec(v, w, intervals_begin, num_intervals, core, create_lemma, lemma, vars_to_explain);
|
||||||
|
|
||||||
|
SASSERT(!intervals.back());
|
||||||
|
// intervals.pop_back(); // get rid of the dummy space (we don't use the intervals anymore after setting the conflict, so this is not necessary)
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,6 +93,14 @@ namespace polysat {
|
||||||
ptr_vector<entry> m_diseq_lin; // entries that have distinct non-zero multipliers
|
ptr_vector<entry> m_diseq_lin; // entries that have distinct non-zero multipliers
|
||||||
svector<std::tuple<pvar, entry_kind, entry*>> m_trail; // undo stack
|
svector<std::tuple<pvar, entry_kind, entry*>> m_trail; // undo stack
|
||||||
|
|
||||||
|
struct reason_ptr {
|
||||||
|
unsigned begin = 0;
|
||||||
|
unsigned len = 0;
|
||||||
|
};
|
||||||
|
sat::literal_vector m_propagation_reason_storage;
|
||||||
|
pvar_vector m_propagation_trail;
|
||||||
|
svector<reason_ptr> m_propagation_reasons;
|
||||||
|
|
||||||
unsigned size(pvar v) const;
|
unsigned size(pvar v) const;
|
||||||
|
|
||||||
bool well_formed(entry* e);
|
bool well_formed(entry* e);
|
||||||
|
@ -185,7 +193,7 @@ namespace polysat {
|
||||||
|
|
||||||
void insert(entry* e, pvar v, ptr_vector<entry>& entries, entry_kind k);
|
void insert(entry* e, pvar v, ptr_vector<entry>& entries, entry_kind k);
|
||||||
|
|
||||||
void propagate(pvar v, rational const& val);
|
void propagate(pvar v, rational const& val, ptr_vector<entry> const& reason);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enter conflict state when intervals cover the full domain.
|
* Enter conflict state when intervals cover the full domain.
|
||||||
|
@ -214,7 +222,7 @@ namespace polysat {
|
||||||
* NOTE: out_hi is set to -1 by the fallback solver.
|
* NOTE: out_hi is set to -1 by the fallback solver.
|
||||||
* \return l_true on success, l_false on conflict, l_undef on resource limit
|
* \return l_true on success, l_false on conflict, l_undef on resource limit
|
||||||
*/
|
*/
|
||||||
lbool find_viable2(pvar v, rational& out_lo, rational& out_hi);
|
lbool find_viable2(pvar v, rational& out_lo, rational& out_hi, ptr_vector<entry>& out_relevant_entries);
|
||||||
|
|
||||||
lbool find_on_layers(
|
lbool find_on_layers(
|
||||||
pvar v,
|
pvar v,
|
||||||
|
@ -223,7 +231,8 @@ namespace polysat {
|
||||||
fixed_bits_info const& fbi,
|
fixed_bits_info const& fbi,
|
||||||
rational const& to_cover_lo,
|
rational const& to_cover_lo,
|
||||||
rational const& to_cover_hi,
|
rational const& to_cover_hi,
|
||||||
rational& out_val);
|
rational& out_val,
|
||||||
|
ptr_vector<entry>& out_relevant_entries);
|
||||||
|
|
||||||
lbool find_on_layer(
|
lbool find_on_layer(
|
||||||
pvar v,
|
pvar v,
|
||||||
|
@ -234,8 +243,8 @@ namespace polysat {
|
||||||
rational const& to_cover_lo,
|
rational const& to_cover_lo,
|
||||||
rational const& to_cover_hi,
|
rational const& to_cover_hi,
|
||||||
rational& out_val,
|
rational& out_val,
|
||||||
ptr_vector<entry>& refine_todo,
|
ptr_vector<entry>& out_refine_todo,
|
||||||
ptr_vector<entry>& relevant_entries);
|
ptr_vector<entry>& out_relevant_entries);
|
||||||
|
|
||||||
std::pair<entry*, bool> find_value(rational const& val, entry* entries);
|
std::pair<entry*, bool> find_value(rational const& val, entry* entries);
|
||||||
|
|
||||||
|
@ -252,6 +261,8 @@ namespace polysat {
|
||||||
void pop_viable();
|
void pop_viable();
|
||||||
void push_viable();
|
void push_viable();
|
||||||
|
|
||||||
|
void pop_propagation_reason();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* update state of viable for pvar v
|
* update state of viable for pvar v
|
||||||
* based on affine constraints.
|
* based on affine constraints.
|
||||||
|
@ -297,6 +308,7 @@ namespace polysat {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a next viable value for variable.
|
* Find a next viable value for variable.
|
||||||
|
* Side effect: propagate v in the solver if only a single value is viable.
|
||||||
*/
|
*/
|
||||||
find_t find_viable(pvar v, rational& out_val);
|
find_t find_viable(pvar v, rational& out_val);
|
||||||
|
|
||||||
|
@ -310,90 +322,24 @@ namespace polysat {
|
||||||
|
|
||||||
std::ostream& display(std::ostream& out, char const* delimiter = "") const;
|
std::ostream& display(std::ostream& out, char const* delimiter = "") const;
|
||||||
|
|
||||||
class iterator {
|
class propagation_reason {
|
||||||
entry* curr = nullptr;
|
sat::literal const* m_begin;
|
||||||
bool visited = false;
|
sat::literal const* m_end;
|
||||||
unsigned idx = 0;
|
propagation_reason(sat::literal const* begin, sat::literal const* end): m_begin(begin), m_end(end) {}
|
||||||
|
friend class viable;
|
||||||
public:
|
public:
|
||||||
iterator(entry* curr, bool visited) :
|
sat::literal const* begin() const { return m_begin; }
|
||||||
curr(curr), visited(visited || !curr) {}
|
sat::literal const* end() const { return m_end; }
|
||||||
|
|
||||||
iterator& operator++() {
|
|
||||||
if (idx < curr->side_cond.size() + curr->src.size() - 1)
|
|
||||||
++idx;
|
|
||||||
else {
|
|
||||||
idx = 0;
|
|
||||||
visited = true;
|
|
||||||
curr = curr->next();
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
signed_constraint& operator*() {
|
|
||||||
return idx < curr->side_cond.size() ? curr->side_cond[idx] : curr->src[idx - curr->side_cond.size()];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(iterator const& other) const {
|
|
||||||
return visited == other.visited && curr == other.curr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(iterator const& other) const {
|
|
||||||
return !(*this == other);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class constraints {
|
propagation_reason get_propagation_reason(pvar v) const {
|
||||||
viable const& v;
|
auto const& r = m_propagation_reasons[v];
|
||||||
pvar var;
|
SASSERT(r.len > 0);
|
||||||
public:
|
sat::literal const* begin = &m_propagation_reason_storage[r.begin];
|
||||||
constraints(viable const& v, pvar var) : v(v), var(var) {}
|
sat::literal const* end = begin + r.len;
|
||||||
// TODO: take other widths into account!
|
return { begin, end };
|
||||||
iterator begin() const { return iterator(v.m_units[var].get_entries(v.size(var)), false); }
|
|
||||||
iterator end() const { return iterator(v.m_units[var].get_entries(v.size(var)), true); }
|
|
||||||
};
|
|
||||||
|
|
||||||
constraints get_constraints(pvar v) const {
|
|
||||||
return constraints(*this, v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class int_iterator {
|
|
||||||
entry* curr = nullptr;
|
|
||||||
bool visited = false;
|
|
||||||
public:
|
|
||||||
int_iterator(entry* curr, bool visited) :
|
|
||||||
curr(curr), visited(visited || !curr) {}
|
|
||||||
int_iterator& operator++() {
|
|
||||||
visited = true;
|
|
||||||
curr = curr->next();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
eval_interval const& operator*() {
|
|
||||||
return curr->interval;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(int_iterator const& other) const {
|
|
||||||
return visited == other.visited && curr == other.curr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(int_iterator const& other) const {
|
|
||||||
return !(*this == other);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class intervals {
|
|
||||||
viable const& v;
|
|
||||||
pvar var;
|
|
||||||
public:
|
|
||||||
intervals(viable const& v, pvar var): v(v), var(var) {}
|
|
||||||
// TODO: take other widths into account!
|
|
||||||
int_iterator begin() const { return int_iterator(v.m_units[var].get_entries(v.size(var)), false); }
|
|
||||||
int_iterator end() const { return int_iterator(v.m_units[var].get_entries(v.size(var)), true); }
|
|
||||||
};
|
|
||||||
|
|
||||||
intervals units(pvar v) { return intervals(*this, v); }
|
|
||||||
|
|
||||||
std::ostream& display(std::ostream& out, pvar v, char const* delimiter = "") const;
|
std::ostream& display(std::ostream& out, pvar v, char const* delimiter = "") const;
|
||||||
|
|
||||||
struct var_pp {
|
struct var_pp {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue