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

Arith min max (#6864)

* prepare for dependencies

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* snapshot

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* more refactoring

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* more refactoring

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* build

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* pass in u_dependency_manager

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* address NYIs

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* more refactoring names

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* eq_explanation update

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* add outline of bounds improvement functionality

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* fix unit tests

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* remove unused structs

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* more bounds

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* more bounds

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* convert more internals to use u_dependency instead of constraint_index

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* convert more internals to use u_dependency instead of constraint_index

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* remember to push/pop scopes

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* use the main function for updating bounds

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* na

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* na

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* remove reset of shared dep manager

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

* disable improve-bounds, add statistics

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>

---------

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2023-08-19 17:44:09 -07:00 committed by GitHub
parent c3b344ec47
commit 5e3df9ee77
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 630 additions and 529 deletions

View file

@ -23,7 +23,7 @@ typedef lp::lar_term term;
core::core(lp::lar_solver& s, params_ref const& p, reslimit & lim) :
m_evars(),
m_lar_solver(s),
lra(s),
m_reslim(lim),
m_params(p),
m_tangents(this),
@ -69,7 +69,7 @@ lp::lar_term core::subs_terms_to_columns(const lp::lar_term& t) const {
for (lp::lar_term::ival p : t) {
lpvar j = p.column();
if (lp::tv::is_term(j))
j = m_lar_solver.map_term_index_to_column_index(j);
j = lra.map_term_index_to_column_index(j);
r.add_monomial(p.coeff(), j);
}
return r;
@ -133,7 +133,7 @@ void core::add_monic(lpvar v, unsigned sz, lpvar const* vs) {
for (unsigned i = 0; i < sz; i++) {
lpvar j = vs[i];
if (lp::tv::is_term(j))
j = m_lar_solver.map_term_index_to_column_index(j);
j = lra.map_term_index_to_column_index(j);
m_add_buffer[i] = j;
}
m_emons.add(v, m_add_buffer);
@ -154,7 +154,7 @@ void core::pop(unsigned n) {
rational core::product_value(const monic& m) const {
rational r(1);
for (auto j : m.vars()) {
r *= m_lar_solver.get_column_value(j).x;
r *= lra.get_column_value(j).x;
}
return r;
}
@ -167,10 +167,10 @@ bool core::check_monic(const monic& m) const {
if (!is_relevant(m.var()))
return true;
#endif
if (m_lar_solver.column_is_int(m.var()) && !m_lar_solver.get_column_value(m.var()).is_int())
if (lra.column_is_int(m.var()) && !lra.get_column_value(m.var()).is_int())
return true;
bool ret = product_value(m) == m_lar_solver.get_column_value(m.var()).x;
bool ret = product_value(m) == lra.get_column_value(m.var()).x;
CTRACE("nla_solver_check_monic", !ret, print_monic(m, tout) << '\n';);
return ret;
}
@ -182,7 +182,7 @@ std::ostream& core::print_product(const T & m, std::ostream& out) const {
for (lpvar v : m) {
if (!first) out << "*"; else first = false;
if (lp_settings().print_external_var_name())
out << "(" << m_lar_solver.get_variable_name(v) << "=" << val(v) << ")";
out << "(" << lra.get_variable_name(v) << "=" << val(v) << ")";
else
out << "(j" << v << " = " << val(v) << ")";
@ -228,7 +228,7 @@ std::ostream & core::print_factor_with_vars(const factor& f, std::ostream& out)
std::ostream& core::print_monic(const monic& m, std::ostream& out) const {
if (lp_settings().print_external_var_name())
out << "([" << m.var() << "] = " << m_lar_solver.get_variable_name(m.var()) << " = " << val(m.var()) << " = ";
out << "([" << m.var() << "] = " << lra.get_variable_name(m.var()) << " = " << val(m.var()) << " = ";
else
out << "(j" << m.var() << " = " << val(m.var()) << " = ";
print_product(m.vars(), out) << ")\n";
@ -271,7 +271,7 @@ std::ostream& core::print_explanation(const lp::explanation& exp, std::ostream&
unsigned i = 0;
for (auto p : exp) {
out << "(" << p.ci() << ")";
m_lar_solver.constraints().display(out, [this](lpvar j) { return var_str(j);}, p.ci());
lra.constraints().display(out, [this](lpvar j) { return var_str(j);}, p.ci());
if (++i < exp.size())
out << " ";
}
@ -316,21 +316,20 @@ bool core::explain_lower_bound(const lp::lar_term& t, const rational& rs, lp::ex
bool core::explain_coeff_lower_bound(const lp::lar_term::ival& p, rational& bound, lp::explanation& e) const {
const rational& a = p.coeff();
SASSERT(!a.is_zero());
unsigned c; // the index for the lower or the upper bound
if (a.is_pos()) {
unsigned c = m_lar_solver.get_column_lower_bound_witness(p.column());
if (c + 1 == 0)
auto* dep = lra.get_column_lower_bound_witness(p.column());
if (!dep)
return false;
bound = a * m_lar_solver.get_lower_bound(p.column()).x;
e.push_back(c);
bound = a * lra.get_lower_bound(p.column()).x;
lra.push_explanation(dep, e);
return true;
}
// a.is_neg()
c = m_lar_solver.get_column_upper_bound_witness(p.column());
if (c + 1 == 0)
auto* dep = lra.get_column_upper_bound_witness(p.column());
if (!dep)
return false;
bound = a * m_lar_solver.get_upper_bound(p.column()).x;
e.push_back(c);
bound = a * lra.get_upper_bound(p.column()).x;
lra.push_explanation(dep, e);
return true;
}
@ -338,21 +337,20 @@ bool core::explain_coeff_upper_bound(const lp::lar_term::ival& p, rational& boun
const rational& a = p.coeff();
lpvar j = p.column();
SASSERT(!a.is_zero());
unsigned c; // the index for the lower or the upper bound
if (a.is_neg()) {
unsigned c = m_lar_solver.get_column_lower_bound_witness(j);
if (c + 1 == 0)
auto *dep = lra.get_column_lower_bound_witness(j);
if (!dep)
return false;
bound = a * m_lar_solver.get_lower_bound(j).x;
e.push_back(c);
bound = a * lra.get_lower_bound(j).x;
lra.push_explanation(dep, e);
return true;
}
// a.is_pos()
c = m_lar_solver.get_column_upper_bound_witness(j);
if (c + 1 == 0)
auto* dep = lra.get_column_upper_bound_witness(j);
if (!dep)
return false;
bound = a * m_lar_solver.get_upper_bound(j).x;
e.push_back(c);
bound = a * lra.get_upper_bound(j).x;
lra.push_explanation(dep, e);
return true;
}
@ -413,12 +411,12 @@ bool core::explain_by_equiv(const lp::lar_term& t, lp::explanation& e) const {
return false;
m_evars.explain(signed_var(i, false), signed_var(j, sign), e);
TRACE("nla_solver", tout << "explained :"; m_lar_solver.print_term_as_indices(t, tout););
TRACE("nla_solver", tout << "explained :"; lra.print_term_as_indices(t, tout););
return true;
}
void core::mk_ineq_no_expl_check(new_lemma& lemma, lp::lar_term& t, llc cmp, const rational& rs) {
TRACE("nla_solver_details", m_lar_solver.print_term_as_indices(t, tout << "t = "););
TRACE("nla_solver_details", lra.print_term_as_indices(t, tout << "t = "););
lemma |= ineq(cmp, t, rs);
CTRACE("nla_solver", ineq_holds(ineq(cmp, t, rs)), print_ineq(ineq(cmp, t, rs), tout) << "\n";);
SASSERT(!ineq_holds(ineq(cmp, t, rs)));
@ -485,18 +483,18 @@ int core::vars_sign(const svector<lpvar>& v) {
}
bool core::has_upper_bound(lpvar j) const {
return m_lar_solver.column_has_upper_bound(j);
return lra.column_has_upper_bound(j);
}
bool core::has_lower_bound(lpvar j) const {
return m_lar_solver.column_has_lower_bound(j);
return lra.column_has_lower_bound(j);
}
const rational& core::get_upper_bound(unsigned j) const {
return m_lar_solver.get_upper_bound(j).x;
return lra.get_upper_bound(j).x;
}
const rational& core::get_lower_bound(unsigned j) const {
return m_lar_solver.get_lower_bound(j).x;
return lra.get_lower_bound(j).x;
}
bool core::zero_is_an_inner_point_of_bounds(lpvar j) const {
@ -540,26 +538,26 @@ bool core::sign_contradiction(const monic& m) const {
bool core::var_is_fixed_to_zero(lpvar j) const {
return
m_lar_solver.column_is_fixed(j) &&
m_lar_solver.get_lower_bound(j) == lp::zero_of_type<lp::impq>();
lra.column_is_fixed(j) &&
lra.get_lower_bound(j) == lp::zero_of_type<lp::impq>();
}
bool core::var_is_fixed_to_val(lpvar j, const rational& v) const {
return
m_lar_solver.column_is_fixed(j) &&
m_lar_solver.get_lower_bound(j) == lp::impq(v);
lra.column_is_fixed(j) &&
lra.get_lower_bound(j) == lp::impq(v);
}
bool core::var_is_fixed(lpvar j) const {
return m_lar_solver.column_is_fixed(j);
return lra.column_is_fixed(j);
}
bool core::var_is_free(lpvar j) const {
return m_lar_solver.column_is_free(j);
return lra.column_is_free(j);
}
std::ostream & core::print_ineq(const ineq & in, std::ostream & out) const {
m_lar_solver.print_term_as_indices(in.term(), out);
lra.print_term_as_indices(in.term(), out);
out << " " << lconstraint_kind_string(in.cmp()) << " " << in.rs();
return out;
}
@ -569,14 +567,14 @@ std::ostream & core::print_var(lpvar j, std::ostream & out) const {
print_monic(m_emons[j], out);
}
m_lar_solver.print_column_info(j, out);
lra.print_column_info(j, out);
signed_var jr = m_evars.find(j);
out << "root=";
if (jr.sign()) {
out << "-";
}
out << m_lar_solver.get_variable_name(jr.var()) << "\n";
out << lra.get_variable_name(jr.var()) << "\n";
return out;
}
@ -643,11 +641,11 @@ void core::trace_print_monic_and_factorization(const monic& rm, const factorizat
bool core::var_has_positive_lower_bound(lpvar j) const {
return m_lar_solver.column_has_lower_bound(j) && m_lar_solver.get_lower_bound(j) > lp::zero_of_type<lp::impq>();
return lra.column_has_lower_bound(j) && lra.get_lower_bound(j) > lp::zero_of_type<lp::impq>();
}
bool core::var_has_negative_upper_bound(lpvar j) const {
return m_lar_solver.column_has_upper_bound(j) && m_lar_solver.get_upper_bound(j) < lp::zero_of_type<lp::impq>();
return lra.column_has_upper_bound(j) && lra.get_upper_bound(j) < lp::zero_of_type<lp::impq>();
}
bool core::var_is_separated_from_zero(lpvar j) const {
@ -684,10 +682,10 @@ template bool core::mon_has_zero<unsigned_vector>(const unsigned_vector& product
lp::lp_settings& core::lp_settings() {
return m_lar_solver.settings();
return lra.settings();
}
const lp::lp_settings& core::lp_settings() const {
return m_lar_solver.settings();
return lra.settings();
}
unsigned core::random() { return lp_settings().random_next(); }
@ -695,7 +693,7 @@ unsigned core::random() { return lp_settings().random_next(); }
// we look for octagon constraints here, with a left part +-x +- y
void core::collect_equivs() {
const lp::lar_solver& s = m_lar_solver;
const lp::lar_solver& s = lra;
for (unsigned i = 0; i < s.terms().size(); i++) {
if (!s.term_is_used_as_row(i))
@ -737,7 +735,7 @@ bool core::is_octagon_term(const lp::lar_term& t, bool & sign, lpvar& i, lpvar &
return true;
}
void core::add_equivalence_maybe(const lp::lar_term *t, lpci c0, lpci c1) {
void core::add_equivalence_maybe(const lp::lar_term* t, u_dependency* c0, u_dependency* c1) {
bool sign;
lpvar i, j;
if (!is_octagon_term(*t, sign, i, j))
@ -866,7 +864,7 @@ std::unordered_set<lpvar> core::collect_vars(const lemma& l) const {
}
}
for (auto p : l.expl()) {
const auto& c = m_lar_solver.constraints()[p.ci()];
const auto& c = lra.constraints()[p.ci()];
for (const auto& r : c.coeffs()) {
insert_j(r.second);
}
@ -1125,8 +1123,8 @@ new_lemma& new_lemma::explain_equiv(lpvar a, lpvar b) {
new_lemma& new_lemma::explain_var_separated_from_zero(lpvar j) {
SASSERT(c.var_is_separated_from_zero(j));
if (c.m_lar_solver.column_has_upper_bound(j) &&
(c.m_lar_solver.get_upper_bound(j)< lp::zero_of_type<lp::impq>()))
if (c.lra.column_has_upper_bound(j) &&
(c.lra.get_upper_bound(j)< lp::zero_of_type<lp::impq>()))
explain_existing_upper_bound(j);
else
explain_existing_lower_bound(j);
@ -1136,7 +1134,7 @@ new_lemma& new_lemma::explain_var_separated_from_zero(lpvar j) {
new_lemma& new_lemma::explain_existing_lower_bound(lpvar j) {
SASSERT(c.has_lower_bound(j));
lp::explanation ex;
ex.push_back(c.m_lar_solver.get_column_lower_bound_witness(j));
c.lra.push_explanation(c.lra.get_column_lower_bound_witness(j), ex);
*this &= ex;
TRACE("nla_solver", tout << j << ": " << *this << "\n";);
return *this;
@ -1145,7 +1143,7 @@ new_lemma& new_lemma::explain_existing_lower_bound(lpvar j) {
new_lemma& new_lemma::explain_existing_upper_bound(lpvar j) {
SASSERT(c.has_upper_bound(j));
lp::explanation ex;
ex.push_back(c.m_lar_solver.get_column_upper_bound_witness(j));
c.lra.push_explanation(c.lra.get_column_upper_bound_witness(j), ex);
*this &= ex;
return *this;
}
@ -1155,7 +1153,7 @@ std::ostream& new_lemma::display(std::ostream & out) const {
for (auto p : lemma.expl()) {
out << "(" << p.ci() << ") ";
c.m_lar_solver.constraints().display(out, [this](lpvar j) { return c.var_str(j);}, p.ci());
c.lra.constraints().display(out, [this](lpvar j) { return c.var_str(j);}, p.ci());
}
out << " ==> ";
if (lemma.ineqs().empty()) {
@ -1303,7 +1301,7 @@ bool core::has_real(const monic& m) const {
bool core::is_patch_blocked(lpvar u, const lp::impq& ival) const {
TRACE("nla_solver", tout << "u = " << u << '\n';);
if (m_cautious_patching &&
(!m_lar_solver.inside_bounds(u, ival) || (var_is_int(u) && ival.is_int() == false))) {
(!lra.inside_bounds(u, ival) || (var_is_int(u) && ival.is_int() == false))) {
TRACE("nla_solver", tout << "u = " << u << " blocked, for feas or integr\n";);
return true; // block
}
@ -1334,7 +1332,7 @@ bool core::is_patch_blocked(lpvar u, const lp::impq& ival) const {
bool core::try_to_patch(const rational& v) {
auto is_blocked = [this](lpvar u, const lp::impq& iv) { return is_patch_blocked(u, iv); };
auto change_report = [this](lpvar u) { update_to_refine_of_var(u); };
return m_lar_solver.try_to_patch(m_patched_var, v, is_blocked, change_report);
return lra.try_to_patch(m_patched_var, v, is_blocked, change_report);
}
bool in_power(const svector<lpvar>& vs, unsigned l) {
@ -1343,7 +1341,7 @@ bool in_power(const svector<lpvar>& vs, unsigned l) {
}
bool core::to_refine_is_correct() const {
for (unsigned j = 0; j < m_lar_solver.number_of_vars(); j++) {
for (unsigned j = 0; j < lra.number_of_vars(); j++) {
if (!is_monic_var(j)) continue;
bool valid = check_monic(emons()[j]);
if (valid == m_to_refine.contains(j)) {
@ -1435,17 +1433,17 @@ void core::patch_monomials() {
NOT_IMPLEMENTED_YET();
m_cautious_patching = false;
patch_monomials_on_to_refine();
m_lar_solver.push();
lra.push();
save_tableau();
constrain_nl_in_tableau();
if (solve_tableau() && integrality_holds()) {
m_lar_solver.pop(1);
lra.pop(1);
} else {
m_lar_solver.pop();
lra.pop();
restore_tableau();
m_lar_solver.clear_inf_heap();
lra.clear_inf_heap();
}
SASSERT(m_lar_solver.ax_is_correct());
SASSERT(lra.ax_is_correct());
}
void core::constrain_nl_in_tableau() {
@ -1507,11 +1505,11 @@ void core::check_bounded_divisions(vector<lemma>& l_vec) {
lbool core::check(vector<lemma>& l_vec) {
lp_settings().stats().m_nla_calls++;
TRACE("nla_solver", tout << "calls = " << lp_settings().stats().m_nla_calls << "\n";);
m_lar_solver.get_rid_of_inf_eps();
lra.get_rid_of_inf_eps();
m_lemma_vec = &l_vec;
if (!(m_lar_solver.get_status() == lp::lp_status::OPTIMAL ||
m_lar_solver.get_status() == lp::lp_status::FEASIBLE)) {
TRACE("nla_solver", tout << "unknown because of the m_lar_solver.m_status = " << m_lar_solver.get_status() << "\n";);
if (!(lra.get_status() == lp::lp_status::OPTIMAL ||
lra.get_status() == lp::lp_status::FEASIBLE)) {
TRACE("nla_solver", tout << "unknown because of the lra.m_status = " << lra.get_status() << "\n";);
return l_undef;
}
@ -1528,6 +1526,9 @@ lbool core::check(vector<lemma>& l_vec) {
if (l_vec.empty() && !done())
m_monomial_bounds();
if (l_vec.empty() && !done() && improve_bounds())
return l_false;
if (l_vec.empty() && !done() && run_horner)
m_horner.horner_lemmas();
@ -1635,21 +1636,21 @@ bool core::no_lemmas_hold() const {
}
lbool core::test_check(vector<lemma>& l) {
m_lar_solver.set_status(lp::lp_status::OPTIMAL);
lra.set_status(lp::lp_status::OPTIMAL);
return check(l);
}
std::ostream& core::print_terms(std::ostream& out) const {
for (unsigned i = 0; i< m_lar_solver.terms().size(); i++) {
for (unsigned i = 0; i< lra.terms().size(); i++) {
unsigned ext = lp::tv::mask_term(i);
if (!m_lar_solver.var_is_registered(ext)) {
if (!lra.var_is_registered(ext)) {
out << "term is not registered\n";
continue;
}
const lp::lar_term & t = *m_lar_solver.terms()[i];
const lp::lar_term & t = *lra.terms()[i];
out << "term:"; print_term(t, out) << std::endl;
lpvar j = m_lar_solver.external_to_local(ext);
lpvar j = lra.external_to_local(ext);
print_var(j, out);
}
return out;
@ -1677,7 +1678,7 @@ std::ostream& core::print_term( const lp::lar_term& t, std::ostream& out) const
std::unordered_set<lpvar> core::get_vars_of_expr_with_opening_terms(const nex *e ) {
auto ret = get_vars_of_expr(e);
auto & ls = m_lar_solver;
auto & ls = lra;
svector<lpvar> added;
for (auto j : ret) {
added.push_back(j);
@ -1685,7 +1686,7 @@ std::unordered_set<lpvar> core::get_vars_of_expr_with_opening_terms(const nex *e
for (unsigned i = 0; i < added.size(); ++i) {
lpvar j = added[i];
if (ls.column_corresponds_to_term(j)) {
const auto& t = m_lar_solver.get_term(lp::tv::raw(ls.local_to_external(j)));
const auto& t = lra.get_term(lp::tv::raw(ls.local_to_external(j)));
for (auto p : t) {
if (ret.find(p.column()) == ret.end()) {
added.push_back(p.column());
@ -1705,7 +1706,7 @@ bool core::is_nl_var(lpvar j) const {
unsigned core::get_var_weight(lpvar j) const {
unsigned k;
switch (m_lar_solver.get_column_type(j)) {
switch (lra.get_column_type(j)) {
case lp::column_type::fixed:
k = 0;
@ -1734,7 +1735,7 @@ unsigned core::get_var_weight(lpvar j) const {
void core::set_active_vars_weights(nex_creator& nc) {
nc.set_number_of_vars(m_lar_solver.column_count());
nc.set_number_of_vars(lra.column_count());
for (lpvar j : active_var_set())
nc.set_var_weight(j, get_var_weight(j));
}
@ -1744,8 +1745,8 @@ bool core::influences_nl_var(lpvar j) const {
j = lp::tv::unmask_term(j);
if (is_nl_var(j))
return true;
for (const auto & c : m_lar_solver.A_r().m_columns[j]) {
lpvar basic_in_row = m_lar_solver.r_basis()[c.var()];
for (const auto & c : lra.A_r().m_columns[j]) {
lpvar basic_in_row = lra.r_basis()[c.var()];
if (is_nl_var(basic_in_row))
return true;
}
@ -1762,9 +1763,32 @@ void core::set_use_nra_model(bool m) {
void core::collect_statistics(::statistics & st) {
st.update("arith-nla-explanations", m_stats.m_nla_explanations);
st.update("arith-nla-lemmas", m_stats.m_nla_lemmas);
st.update("arith-nra-calls", m_stats.m_nra_calls);
st.update("arith-nra-calls", m_stats.m_nra_calls);
st.update("arith-bounds-improvements", m_stats.m_bounds_improvements);
}
bool core::improve_bounds() {
return false;
uint_set seen;
bool bounds_improved = false;
auto insert = [&](lpvar v) {
if (seen.contains(v))
return;
seen.insert(v);
if (lra.improve_bound(v, false))
bounds_improved = true, m_stats.m_bounds_improvements++;
if (lra.improve_bound(v, true))
bounds_improved = true, m_stats.m_bounds_improvements++;
};
for (auto & m : m_emons) {
insert(m.var());
for (auto v : m.vars())
insert(v);
}
return bounds_improved;
}
} // end of nla