3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-07-19 10:52:02 +00:00

experiment with cuts in dio

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
This commit is contained in:
Lev Nachmanson 2025-02-27 16:04:36 -10:00
parent 3d65e9c2fc
commit a3d9803fa5
7 changed files with 191 additions and 76 deletions

View file

@ -148,8 +148,7 @@ namespace lp {
mpq m_total, m_bound; mpq m_total, m_bound;
void limit_all_monoids_from_above() { void limit_all_monoids_from_above() {
int strict = 0; int strict = 0;
m_total.reset(); m_total = m_rs.x;
lp_assert(is_zero(m_total));
for (const auto& p : m_row) { for (const auto& p : m_row) {
bool str; bool str;
m_total -= monoid_min(p.coeff(), p.var(), str); m_total -= monoid_min(p.coeff(), p.var(), str);
@ -174,8 +173,7 @@ namespace lp {
void limit_all_monoids_from_below() { void limit_all_monoids_from_below() {
int strict = 0; int strict = 0;
m_total.reset(); m_total = m_rs.x;
lp_assert(is_zero(m_total));
for (const auto &p : m_row) { for (const auto &p : m_row) {
bool str; bool str;
m_total -= monoid_max(p.coeff(), p.var(), str); m_total -= monoid_max(p.coeff(), p.var(), str);
@ -205,7 +203,7 @@ namespace lp {
// every other monoid is impossible to limit from below // every other monoid is impossible to limit from below
mpq u_coeff; mpq u_coeff;
unsigned j; unsigned j;
m_bound = -m_rs.x; m_bound = m_rs.x;
bool strict = false; bool strict = false;
for (const auto& p : m_row) { for (const auto& p : m_row) {
j = p.var(); j = p.var();
@ -234,7 +232,7 @@ namespace lp {
// every other monoid is impossible to limit from above // every other monoid is impossible to limit from above
mpq l_coeff; mpq l_coeff;
unsigned j; unsigned j;
m_bound = -m_rs.x; m_bound = m_rs.x;
bool strict = false; bool strict = false;
for (const auto &p : m_row) { for (const auto &p : m_row) {
j = p.var(); j = p.var();
@ -275,31 +273,25 @@ namespace lp {
// */ // */
// } // }
void limit_j(unsigned bound_j, const mpq& u, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict) void limit_j(unsigned bound_j, const mpq& u, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict) {
{ auto* lar = &m_bp.lp();
auto* lar = &m_bp.lp(); int bound_sign = (is_lower_bound ? 1 : -1);
const auto& row = this->m_row; int j_sign = (coeff_before_j_is_pos ? 1 : -1) * bound_sign;
auto explain = [row, bound_j, coeff_before_j_is_pos, is_lower_bound, strict, lar]() {
(void) strict; u_dependency* dep = nullptr;
TRACE("bound_analyzer", tout << "explain_bound_on_var_on_coeff, bound_j = " << bound_j << ", coeff_before_j_is_pos = " << coeff_before_j_is_pos << ", is_lower_bound = " << is_lower_bound << ", strict = " << strict << "\n";); for (auto const& r : m_row) {
int bound_sign = (is_lower_bound ? 1 : -1); unsigned j = r.var();
int j_sign = (coeff_before_j_is_pos ? 1 : -1) * bound_sign; if (j == bound_j)
continue;
u_dependency* ret = nullptr; mpq const& a = r.coeff();
for (auto const& r : row) { int a_sign = is_pos(a) ? 1 : -1;
unsigned j = r.var(); int sign = j_sign * a_sign;
if (j == bound_j) u_dependency* witness = sign > 0 ? lar->get_column_upper_bound_witness(j) : lar->get_column_lower_bound_witness(j);
continue; dep = lar->join_deps(dep, witness);
mpq const& a = r.coeff();
int a_sign = is_pos(a) ? 1 : -1;
int sign = j_sign * a_sign;
u_dependency* witness = sign > 0 ? lar->get_column_upper_bound_witness(j) : lar->get_column_lower_bound_witness(j);
ret = lar->join_deps(ret, witness);
}
return ret;
};
m_bp.add_bound(u, bound_j, is_lower_bound, strict, explain);
} }
m_bp.add_bound(u, bound_j, is_lower_bound, strict, dep);
}
void advance_u(unsigned j) { void advance_u(unsigned j) {
m_column_of_u = (m_column_of_u == -1) ? j : -2; m_column_of_u = (m_column_of_u == -1) ? j : -2;

View file

@ -245,27 +245,16 @@ namespace lp {
std::ostream& print_S(std::ostream& out) { std::ostream& print_S(std::ostream& out) {
out << "S:\n"; out << "S:\n";
for (const auto& p : m_k2s.m_map) { for (const auto& p : m_k2s.m_map) {
print_entry(p.second, out, false, false); print_entry(p.second, out, false, false, true);
} }
return out; return out;
} }
std::ostream& print_column_bounds(std::ostream& out, unsigned j) {
out << "j" << j << ":= " << lra.get_column_value(j).x << ": ";
if (lra.column_has_lower_bound(j))
out << lra.column_lower_bound(j).x << " <= ";
out << "x" << j;
if (lra.column_has_upper_bound(j))
out << " <= " << lra.column_upper_bound(j).x;
out << "\n";
return out;
}
std::ostream& print_bounds(std::ostream& out) { std::ostream& print_bounds(std::ostream& out) {
out << "bounds:\n"; out << "bounds:\n";
for (unsigned v = 0; v < m_var_register.size(); ++v) { for (unsigned v = 0; v < m_var_register.size(); ++v) {
unsigned j = m_var_register.local_to_external(v); unsigned j = m_var_register.local_to_external(v);
print_column_bounds(out, j); lra.print_column_info(j, out);
} }
return out; return out;
} }
@ -1446,12 +1435,9 @@ namespace lp {
m_c = mpq(0); m_c = mpq(0);
m_lspace.clear(); m_lspace.clear();
SASSERT(get_extended_term_value(lar_t).is_zero()); SASSERT(get_extended_term_value(lar_t).is_zero());
TRACE("dio", tout << "t:";print_lar_term_L(lar_t, tout) << std::endl; tout << "value:" << get_extended_term_value(lar_t) << std::endl;);
for (const auto& p : lar_t) { for (const auto& p : lar_t) {
SASSERT(p.coeff().is_int());
if (is_fixed(p.j())) { if (is_fixed(p.j())) {
m_c += p.coeff() * lia.lower_bound(p.j()).x; m_c += p.coeff() * lia.lower_bound(p.j()).x;
SASSERT(lia.lower_bound(p.j()).x == lra.get_column_value(p.j()).x);
} }
else { else {
unsigned lj = lar_solver_to_local(p.j()); unsigned lj = lar_solver_to_local(p.j());
@ -1460,9 +1446,6 @@ namespace lp {
q.push(lar_solver_to_local(p.j())); q.push(lar_solver_to_local(p.j()));
} }
} }
TRACE("dio", tout << "m_espace:"; print_term_o(create_term_from_subst_space(), tout) << std::endl;
tout << "m_c:" << m_c << std::endl;
tout << "get_value_of_subs_space:" << get_value_of_espace() << std::endl;);
} }
unsigned lar_solver_to_local(unsigned j) const { unsigned lar_solver_to_local(unsigned j) const {
return m_var_register.external_to_local(j); return m_var_register.external_to_local(j);
@ -1637,7 +1620,136 @@ namespace lp {
} }
} }
struct prop_bound {
mpq m_bound;
unsigned m_j;
bool m_is_low;
bool m_strict;
u_dependency* m_dep;
};
std_vector<prop_bound> m_prop_bounds;
void add_bound(mpq const& bound, unsigned j, bool is_low, bool strict, u_dependency* dep) {
m_prop_bounds.push_back({bound, j, is_low, strict, dep});
}
lconstraint_kind get_bound_kind(const prop_bound& pb) const {
if (!pb.m_is_low) {
return pb.m_strict ? lp::LT : lp::LE;
}
else {
return pb.m_strict ? lp::GT : lp::GE;
}
}
bool cut(const prop_bound& pb) const {
const auto & v = lra.get_column_value(pb.m_j);
switch (get_bound_kind(pb)) {
case LT:
return v >= pb.m_bound;
case LE:
return v > pb.m_bound;
case GT:
return v <= pb.m_bound;
case GE:
return v < pb.m_bound;
default:
UNREACHABLE();
}
return false;
}
std::ostream& print_int_inf_vars(std::ostream& out) const {
out << "Integer infeasible variables:\n";
for (unsigned j = 0; j < lra.column_count(); j++) {
if (!lia.column_is_int_inf(j)) continue;
out << "x" << j << " = " << lra.get_column_value(j).x << " bounds:\n";
if (lra.column_has_lower_bound(j))
out << lra.get_lower_bound(j).x << " <= ";
else
out << "-oo" << " <= ";
out << "x" << j;
if (lra.column_has_upper_bound(j))
out << " <= " << lra.get_upper_bound(j).x;
else
out << " <= " << "oo";
out << "\n";
}
return out;
}
// return true iff lar_solvre is feasible or cancelled
bool create_cut(const prop_bound& pb) {
lra.update_column_type_and_bound(pb.m_j, get_bound_kind(pb), pb.m_bound, pb.m_dep);
auto st = lra.find_feasible_solution();
if ((int)st >= (int)(lp_status::FEASIBLE) || !lia.settings().get_cancel_flag())
return true;
if (st == lp_status::INFEASIBLE) {
lra.get_infeasibility_explanation(m_infeas_explanation);
return false;
}
return true;
}
bool row_has_int_inf(unsigned ei) {
for (const auto& p: m_e_matrix.m_rows[ei]) {
unsigned j = local_to_lar_solver(p.var());
if (lia.column_is_int_inf(j))
return true;
}
return false;
}
int prop_calls = 0;
lia_move tighten_S_row(unsigned ei) {
if (!row_has_int_inf(ei)) return lia_move::undef;
// SASSERT(entry_invariant(ei));
m_espace.clear();
for (const auto& p: m_e_matrix.m_rows[ei]) {
m_espace.add(p.coeff(), p.var());
}
remove_fresh_from_espace();
std_vector<iv> row_in_lar_solver_indices;
for (const auto & p: m_espace.m_data) {
row_in_lar_solver_indices.push_back({p.coeff(), local_to_lar_solver(p.var())});
}
m_prop_bounds.clear();
bound_analyzer_on_row<std_vector<iv>, dioph_eq::imp>::analyze_row(row_in_lar_solver_indices, impq(- m_sum_of_fixed[ei]), *this);
for (auto& pb: m_prop_bounds) {
if (lra.settings().get_cancel_flag())
return lia_move::undef;
if (cut(pb) && lia.column_is_int_inf(pb.m_j)) {
pb.m_dep = lra.join_deps(pb.m_dep, explain_fixed_in_meta_term(m_l_matrix.m_rows[ei])); // todo; not efficient
TRACE("dio", tout << "cut on variable x" << pb.m_j << ", with " << (pb.m_is_low? "lower": "upper") << " bound:" << pb.m_bound << "\n"; tout << "row " << ei << " in lar indices:\n";
print_lar_term_L(row_in_lar_solver_indices, tout) << " + " << m_sum_of_fixed[ei] << "\n";
tout << "bounds:\n";
for (const auto& p: row_in_lar_solver_indices)
lra.print_column_info(p.var(), tout););
if (!create_cut(pb))
return lia_move::conflict;
}
}
return lia_move::undef;
}
lia_move propagate_bounds_on_tightened_columns() { lia_move propagate_bounds_on_tightened_columns() {
TRACE("dio", print_int_inf_vars(tout); print_S(tout););
std::unordered_set<unsigned> rows;
for (unsigned tcol : m_tightened_columns) {
for (const auto& p: m_e_matrix.m_columns[lar_solver_to_local(tcol)] ) {
if (lra.settings().get_cancel_flag())
return lia_move::undef;
if (contains(rows, p.var())) continue;
rows.insert(p.var());
lia_move r = tighten_S_row(p.var());
if (r == lia_move::conflict)
return r;
}
}
return lia_move::undef; return lia_move::undef;
} }
// m_espace contains the coefficients of the term // m_espace contains the coefficients of the term
@ -1751,6 +1863,9 @@ namespace lp {
lra.stats().m_dio_rewrite_conflicts++; lra.stats().m_dio_rewrite_conflicts++;
return lia_move::conflict; return lia_move::conflict;
} }
TRACE("dio", print_int_inf_vars(tout) << "\n";
print_S(tout));
return lia_move::undef; return lia_move::undef;
} }
@ -1763,7 +1878,14 @@ namespace lp {
if (ret == lia_move::conflict) { if (ret == lia_move::conflict) {
lra.stats().m_dio_tighten_conflicts++; lra.stats().m_dio_tighten_conflicts++;
return lia_move::conflict; return lia_move::conflict;
}
if (ret == lia_move::undef) {
TRACE("dio", print_int_inf_vars(tout); print_S(tout););
ret = propagate_bounds_on_tightened_columns();
if (ret == lia_move::conflict)
lra.stats().m_dio_bound_propagation_conflicts++;
} }
return ret; return ret;
} }
@ -1920,7 +2042,7 @@ namespace lp {
lra_pop(); lra_pop();
continue; continue;
} }
auto st = lra.find_feasible_solution(); auto st = lra.find_feasible_solution();
TRACE("dio_br", tout << "st:" << lp_status_to_string(st) << std::endl;); TRACE("dio_br", tout << "st:" << lp_status_to_string(st) << std::endl;);
if ((int)st >= (int)(lp_status::FEASIBLE)) { if ((int)st >= (int)(lp_status::FEASIBLE)) {
// have a feasible solution // have a feasible solution
@ -2285,17 +2407,13 @@ namespace lp {
} }
bool entry_invariant(unsigned ei) const { bool entry_invariant(unsigned ei) const {
if (belongs_to_s(ei)) {
auto it = m_k2s.m_rev_map.find(ei);
SASSERT(it != m_k2s.m_rev_map.end());
unsigned j = it->second;
get_sign_in_e_row(ei, j);
}
for (const auto& p : m_e_matrix.m_rows[ei]) { for (const auto& p : m_e_matrix.m_rows[ei]) {
if (!p.coeff().is_int()) { if (!p.coeff().is_int()) {
return false; return false;
} }
unsigned j = local_to_lar_solver(p.var());
if (is_fixed(j))
return false;
} }
bool ret = bool ret =
@ -2432,12 +2550,13 @@ namespace lp {
} }
std::ostream& print_entry(unsigned i, std::ostream& out, std::ostream& print_entry(unsigned i, std::ostream& out,
bool print_dep = false, bool print_in_S = true) { bool print_dep = false, bool print_in_S = true, bool print_column_info = false) {
unsigned j = m_k2s.has_val(i) ? m_k2s.get_key(i) : UINT_MAX; unsigned j = m_k2s.has_val(i) ? m_k2s.get_key(i) : UINT_MAX;
out << "entry " << i << ": "; out << "entry " << i << ": ";
if (j != UINT_MAX) if (j != UINT_MAX)
out << "x" << j << " "; out << "x" << j << " ";
out << "{\n"; out << "{\n";
print_term_o(get_term_from_entry(i), out << "\t") << ",\n"; print_term_o(get_term_from_entry(i), out << "\t") << ",\n";
// out << "\tstatus:" << (int)e.m_entry_status; // out << "\tstatus:" << (int)e.m_entry_status;
if (print_dep) { if (print_dep) {
@ -2453,13 +2572,15 @@ namespace lp {
out << "in F\n"; out << "in F\n";
} }
else { else {
if (local_to_lar_solver(j) == UINT_MAX) { if (print_in_S) {
out << "FRESH\n";
}
else if (print_in_S) {
out << "in S\n"; out << "in S\n";
} }
} }
if (print_column_info) {
for (const auto& p : get_term_from_entry(i)) {
lra.print_column_info(local_to_lar_solver(p.var()), out) << "\n";
}
}
out << "}\n"; out << "}\n";
return out; return out;
} }

View file

@ -33,11 +33,11 @@ class implied_bound {
bool m_is_lower_bound; bool m_is_lower_bound;
bool m_strict; bool m_strict;
private: private:
std::function<u_dependency*()> m_explain_bound = nullptr; u_dependency* m_dep = nullptr;
public: public:
// s is expected to be the pointer to lp_bound_propagator. // s is expected to be the pointer to lp_bound_propagator.
u_dependency* explain_implied() const { return m_explain_bound(); } u_dependency* dep() const { return m_dep; }
void set_explain(std::function<u_dependency*()> f) { m_explain_bound = f; } void set_dep(u_dependency* dep) { m_dep = dep; }
lconstraint_kind kind() const { lconstraint_kind kind() const {
lconstraint_kind k = m_is_lower_bound? GE : LE; lconstraint_kind k = m_is_lower_bound? GE : LE;
if (m_strict) if (m_strict)
@ -49,12 +49,12 @@ class implied_bound {
unsigned j, unsigned j,
bool is_lower_bound, bool is_lower_bound,
bool is_strict, bool is_strict,
std::function<u_dependency*()> get_dep): u_dependency* dep):
m_bound(a), m_bound(a),
m_j(j), m_j(j),
m_is_lower_bound(is_lower_bound), m_is_lower_bound(is_lower_bound),
m_strict(is_strict), m_strict(is_strict),
m_explain_bound(get_dep) { m_dep(dep) {
} }
}; };
} }

View file

@ -227,8 +227,8 @@ namespace lp {
unsigned m_bounds_refine_depth = 0; unsigned m_bounds_refine_depth = 0;
void add_bound(mpq const& bound, unsigned j, bool is_low, bool strict, const std::function<u_dependency* ()>& explain_bound) { void add_bound(mpq const& bound, unsigned j, bool is_low, bool strict, u_dependency* dep) {
m_prop_bounds.push_back({bound, j, is_low, strict, explain_bound()}); m_prop_bounds.push_back({bound, j, is_low, strict, dep});
} }
lconstraint_kind get_bound_kind(bool upper, bool is_strict) { lconstraint_kind get_bound_kind(bool upper, bool is_strict) {
@ -280,7 +280,7 @@ namespace lp {
return lia_move::undef; return lia_move::undef;
} }
bool should_tighten_bounds() { bool should_tighten_bounds() {
return m_number_of_calls % 4 == 0; return false && m_number_of_calls % 4 == 0;
} }
// needed for the template bound_analyzer_on_row.h // needed for the template bound_analyzer_on_row.h
const lar_solver& lp() const { return lra; } const lar_solver& lp() const { return lra; }

View file

@ -312,7 +312,7 @@ public:
std::function<void(lpvar)> m_fixed_var_eh; std::function<void(lpvar)> m_fixed_var_eh;
template <typename T> template <typename T>
void explain_implied_bound(const implied_bound& ib, lp_bound_propagator<T>& bp) { void explain_implied_bound(const implied_bound& ib, lp_bound_propagator<T>& bp) {
u_dependency* dep = ib.explain_implied(); u_dependency* dep = ib.dep();
for (auto ci : flatten(dep)) for (auto ci : flatten(dep))
bp.consume(mpq(1), ci); // TODO: flatten should provide the coefficients bp.consume(mpq(1), ci); // TODO: flatten should provide the coefficients
/* /*

View file

@ -125,7 +125,7 @@ public:
return (*m_column_types)[j] == column_type::fixed && get_lower_bound(j).y.is_zero(); return (*m_column_types)[j] == column_type::fixed && get_lower_bound(j).y.is_zero();
} }
void add_bound(mpq const& v, unsigned j, bool is_low, bool strict, std::function<u_dependency* ()> explain_bound) { void add_bound(mpq const& v, unsigned j, bool is_low, bool strict, u_dependency* dep) {
lconstraint_kind kind = is_low ? GE : LE; lconstraint_kind kind = is_low ? GE : LE;
if (strict) if (strict)
kind = static_cast<lconstraint_kind>(kind / 2); kind = static_cast<lconstraint_kind>(kind / 2);
@ -140,12 +140,12 @@ public:
found_bound.m_bound = v; found_bound.m_bound = v;
found_bound.m_strict = strict; found_bound.m_strict = strict;
found_bound.set_explain(explain_bound); found_bound.set_dep(dep);
TRACE("add_bound", lp().print_implied_bound(found_bound, tout);); TRACE("add_bound", lp().print_implied_bound(found_bound, tout););
} }
} else { } else {
m_improved_lower_bounds.insert(j, static_cast<unsigned>(m_ibounds.size())); m_improved_lower_bounds.insert(j, static_cast<unsigned>(m_ibounds.size()));
m_ibounds.push_back(implied_bound(v, j, is_low, strict, explain_bound)); m_ibounds.push_back(implied_bound(v, j, is_low, strict, dep));
TRACE("add_bound", lp().print_implied_bound(m_ibounds.back(), tout);); TRACE("add_bound", lp().print_implied_bound(m_ibounds.back(), tout););
} }
} else { // the upper bound case } else { // the upper bound case
@ -156,12 +156,12 @@ public:
found_bound.m_bound = v; found_bound.m_bound = v;
found_bound.m_strict = strict; found_bound.m_strict = strict;
found_bound.set_explain(explain_bound); found_bound.set_dep(dep);
TRACE("add_bound", lp().print_implied_bound(found_bound, tout);); TRACE("add_bound", lp().print_implied_bound(found_bound, tout););
} }
} else { } else {
m_improved_upper_bounds.insert(j, static_cast<unsigned>(m_ibounds.size())); m_improved_upper_bounds.insert(j, static_cast<unsigned>(m_ibounds.size()));
m_ibounds.push_back(implied_bound(v, j, is_low, strict, explain_bound)); m_ibounds.push_back(implied_bound(v, j, is_low, strict, dep));
TRACE("add_bound", lp().print_implied_bound(m_ibounds.back(), tout);); TRACE("add_bound", lp().print_implied_bound(m_ibounds.back(), tout););
} }
} }

View file

@ -138,6 +138,7 @@ struct statistics {
unsigned m_dio_rewrite_conflicts = 0; unsigned m_dio_rewrite_conflicts = 0;
unsigned m_dio_branching_sats = 0; unsigned m_dio_branching_sats = 0;
unsigned m_dio_branching_conflicts = 0; unsigned m_dio_branching_conflicts = 0;
unsigned m_dio_bound_propagation_conflicts = 0;
unsigned m_bounds_tightening_conflicts = 0; unsigned m_bounds_tightening_conflicts = 0;
unsigned m_bounds_tightenings = 0; unsigned m_bounds_tightenings = 0;
::statistics m_st = {}; ::statistics m_st = {};
@ -184,6 +185,7 @@ struct statistics {
st.update("arith-dio-branching-depth", m_dio_branching_depth); st.update("arith-dio-branching-depth", m_dio_branching_depth);
st.update("arith-dio-branching-conflicts", m_dio_branching_conflicts); st.update("arith-dio-branching-conflicts", m_dio_branching_conflicts);
st.update("arith-bounds-tightening-conflicts", m_bounds_tightening_conflicts); st.update("arith-bounds-tightening-conflicts", m_bounds_tightening_conflicts);
st.update("arith-dio-propagation-conflicts", m_dio_bound_propagation_conflicts);
st.update("arith-bounds-tightenings", m_bounds_tightenings); st.update("arith-bounds-tightenings", m_bounds_tightenings);
st.copy(m_st); st.copy(m_st);
} }