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

working on reconciling perf for arithmetic solvers

this update integrates inferences to smt.arith.solver=6 related to grobner basis computation and handling of div/mod axioms to reconcile performance with smt.arith.solver=2.

The default of smt.arth.nl.grobner_subs_fixed is changed to 1 to make comparison with solver=2 more direct.

The selection of cluster equalities for solver=6 was reconciled with how it is done for solver=2.
This commit is contained in:
Nikolaj Bjorner 2022-07-11 07:38:51 -07:00
parent 9d9414c111
commit b68af0c1e5
19 changed files with 357 additions and 282 deletions

View file

@ -557,7 +557,7 @@ std::ostream & core::print_ineq(const ineq & in, std::ostream & out) const {
}
std::ostream & core::print_var(lpvar j, std::ostream & out) const {
if (m_emons.is_monic_var(j)) {
if (is_monic_var(j)) {
print_monic(m_emons[j], out);
}
@ -846,7 +846,7 @@ std::unordered_set<lpvar> core::collect_vars(const lemma& l) const {
std::unordered_set<lpvar> vars;
auto insert_j = [&](lpvar j) {
vars.insert(j);
if (m_emons.is_monic_var(j)) {
if (is_monic_var(j)) {
for (lpvar k : m_emons[j].vars())
vars.insert(k);
}
@ -948,7 +948,7 @@ void core::maybe_add_a_factor(lpvar i,
std::unordered_set<unsigned>& found_rm,
vector<factor> & r) const {
SASSERT(abs(val(i)) == abs(val(c)));
if (!m_emons.is_monic_var(i)) {
if (!is_monic_var(i)) {
i = m_evars.find(i).var();
if (try_insert(i, found_vars)) {
r.push_back(factor(i, factor_type::VAR));
@ -1228,7 +1228,7 @@ bool core::var_breaks_correct_monic_as_factor(lpvar j, const monic& m) const {
}
bool core::var_breaks_correct_monic(lpvar j) const {
if (emons().is_monic_var(j) && !m_to_refine.contains(j)) {
if (is_monic_var(j) && !m_to_refine.contains(j)) {
TRACE("nla_solver", tout << "j = " << j << ", m = "; print_monic(emons()[j], tout) << "\n";);
return true; // changing the value of a correct monic
}
@ -1333,7 +1333,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++) {
if (!emons().is_monic_var(j)) continue;
if (!is_monic_var(j)) continue;
bool valid = check_monic(emons()[j]);
if (valid == m_to_refine.contains(j)) {
TRACE("nla_solver", tout << "inconstency in m_to_refine : ";
@ -1414,7 +1414,7 @@ void core::patch_monomials_on_to_refine() {
void core::patch_monomials() {
m_cautious_patching = true;
patch_monomials_on_to_refine();
if (m_to_refine.size() == 0 || !m_nla_settings.expensive_patching()) {
if (m_to_refine.size() == 0 || !m_nla_settings.expensive_patching) {
return;
}
NOT_IMPLEMENTED_YET();
@ -1530,11 +1530,11 @@ lbool core::check(vector<lemma>& l_vec) {
check_weighted(3, checks);
unsigned num_calls = lp_settings().stats().m_nla_calls;
if (!conflict_found() && m_nla_settings.run_nra() && num_calls % 50 == 0 && num_calls > 500)
if (!conflict_found() && m_nla_settings.run_nra && num_calls % 50 == 0 && num_calls > 500)
ret = bounded_nlsat();
}
if (l_vec.empty() && !done() && m_nla_settings.run_nra() && ret == l_undef) {
if (l_vec.empty() && !done() && m_nla_settings.run_nra && ret == l_undef) {
ret = m_nra.check();
m_stats.m_nra_calls++;
}
@ -1554,7 +1554,7 @@ lbool core::check(vector<lemma>& l_vec) {
}
bool core::should_run_bounded_nlsat() {
if (!m_nla_settings.run_nra())
if (!m_nla_settings.run_nra)
return false;
if (m_nlsat_delay > m_nlsat_fails)
++m_nlsat_fails;
@ -1619,8 +1619,13 @@ std::ostream& core::print_terms(std::ostream& out) const {
}
std::string core::var_str(lpvar j) const {
return is_monic_var(j)?
(product_indices_str(m_emons[j].vars()) + (check_monic(m_emons[j])? "": "_")) : (std::string("j") + lp::T_to_string(j));
std::string result;
if (is_monic_var(j))
result += product_indices_str(m_emons[j].vars()) + (check_monic(m_emons[j])? "": "_");
else
result += std::string("j") + lp::T_to_string(j);
// result += ":w" + lp::T_to_string(get_var_weight(j));
return result;
}
std::ostream& core::print_term( const lp::lar_term& t, std::ostream& out) const {
@ -1632,7 +1637,7 @@ std::ostream& core::print_term( const lp::lar_term& t, std::ostream& out) const
void core::run_grobner() {
unsigned& quota = m_nla_settings.grobner_quota();
unsigned& quota = m_nla_settings.grobner_quota;
if (quota == 1) {
return;
}
@ -1645,13 +1650,14 @@ void core::run_grobner() {
bool conflict = false;
unsigned n = m_pdd_grobner.number_of_conflicts_to_report();
SASSERT(n > 0);
for (auto eq : m_pdd_grobner.equations()) {
for (auto eq : m_pdd_grobner.equations()) {
if (check_pdd_eq(eq)) {
conflict = true;
if (--n == 0)
break;
}
}
TRACE("grobner", m_pdd_grobner.display(tout));
if (conflict) {
IF_VERBOSE(2, verbose_stream() << "grobner conflict\n");
return;
@ -1694,14 +1700,32 @@ void core::configure_grobner() {
m_pdd_grobner.reset();
try {
set_level2var_for_grobner();
for (unsigned i : m_rows) {
add_row_to_grobner(m_lar_solver.A_r().m_rows[i]);
TRACE("grobner",
tout << "base vars: ";
for (lpvar j : active_var_set())
if (m_lar_solver.is_base(j))
tout << "j" << j << " ";
tout << "\n");
for (lpvar j : active_var_set()) {
if (m_lar_solver.is_base(j))
add_row_to_grobner(m_lar_solver.basic2row(j));
if (is_monic_var(j) && var_is_fixed(j)) {
u_dependency* dep = nullptr;
dd::pdd r = m_pdd_manager.mk_val(rational(1));
for (lpvar k : emons()[j].vars())
r *= pdd_expr(rational::one(), k, dep);
r -= val_of_fixed_var_with_deps(j, dep);
m_pdd_grobner.add(r, dep);
}
}
}
catch (...) {
IF_VERBOSE(2, verbose_stream() << "pdd throw\n");
return;
}
TRACE("grobner", m_pdd_grobner.display(tout));
#if 0
IF_VERBOSE(2, m_pdd_grobner.display(verbose_stream()));
dd::pdd_eval eval(m_pdd_manager);
@ -1717,11 +1741,11 @@ void core::configure_grobner() {
struct dd::solver::config cfg;
cfg.m_max_steps = m_pdd_grobner.equations().size();
cfg.m_max_simplified = m_nla_settings.grobner_max_simplified();
cfg.m_eqs_growth = m_nla_settings.grobner_eqs_growth();
cfg.m_expr_size_growth = m_nla_settings.grobner_expr_size_growth();
cfg.m_expr_degree_growth = m_nla_settings.grobner_expr_degree_growth();
cfg.m_number_of_conflicts_to_report = m_nla_settings.grobner_number_of_conflicts_to_report();
cfg.m_max_simplified = m_nla_settings.grobner_max_simplified;
cfg.m_eqs_growth = m_nla_settings.grobner_eqs_growth;
cfg.m_expr_size_growth = m_nla_settings.grobner_expr_size_growth;
cfg.m_expr_degree_growth = m_nla_settings.grobner_expr_degree_growth;
cfg.m_number_of_conflicts_to_report = m_nla_settings.grobner_number_of_conflicts_to_report;
m_pdd_grobner.set(cfg);
m_pdd_grobner.adjust_cfg();
m_pdd_manager.set_max_num_nodes(10000); // or something proportional to the number of initial nodes.
@ -1762,49 +1786,66 @@ bool core::check_pdd_eq(const dd::solver::equation* e) {
};
scoped_dep_interval i(di), i_wd(di);
eval.get_interval<dd::w_dep::without_deps>(e->poly(), i);
if (!di.separated_from_zero(i))
if (!di.separated_from_zero(i)) {
TRACE("grobner", m_pdd_grobner.display(tout << "not separated from 0 ", *e) << "\n";
eval.get_interval_distributed<dd::w_dep::without_deps>(e->poly(), i);
tout << "separated from 0: " << di.separated_from_zero(i) << "\n";
for (auto j : e->poly().free_vars()) {
scoped_dep_interval a(di);
m_intervals.set_var_interval<dd::w_dep::without_deps>(j, a);
m_intervals.display(tout << "j" << j << " ", a); tout << " ";
}
tout << "\n");
return false;
}
eval.get_interval<dd::w_dep::with_deps>(e->poly(), i_wd);
std::function<void (const lp::explanation&)> f = [this](const lp::explanation& e) {
new_lemma lemma(*this, "pdd");
lemma &= e;
};
if (di.check_interval_for_conflict_on_zero(i_wd, e->dep(), f)) {
TRACE("grobner", m_pdd_grobner.display(tout << "conflict ", *e) << "\n");
lp_settings().stats().m_grobner_conflicts++;
return true;
}
else {
TRACE("grobner", m_pdd_grobner.display(tout << "no conflict ", *e) << "\n");
return false;
}
}
void core::add_var_and_its_factors_to_q_and_collect_new_rows(lpvar j, svector<lpvar> & q) {
if (active_var_set_contains(j) || var_is_fixed(j)) return;
TRACE("grobner", tout << "j = " << j << ", " << pp(j););
const auto& matrix = m_lar_solver.A_r();
if (active_var_set_contains(j))
return;
insert_to_active_var_set(j);
for (auto & s : matrix.m_columns[j]) {
unsigned row = s.var();
if (m_rows.contains(row)) continue;
if (matrix.m_rows[row].size() > m_nla_settings.grobner_row_length_limit()) {
TRACE("grobner", tout << "ignore the row " << row << " with the size " << matrix.m_rows[row].size() << "\n";);
continue;
}
m_rows.insert(row);
for (auto& rc : matrix.m_rows[row]) {
add_var_and_its_factors_to_q_and_collect_new_rows(rc.var(), q);
}
if (is_monic_var(j)) {
const monic& m = emons()[j];
for (auto fcn : factorization_factory_imp(m, *this))
for (const factor& fc: fcn)
q.push_back(var(fc));
}
if (!is_monic_var(j))
if (var_is_fixed(j))
return;
const auto& matrix = m_lar_solver.A_r();
for (auto & s : matrix.m_columns[j]) {
unsigned row = s.var();
if (m_rows.contains(row))
continue;
m_rows.insert(row);
unsigned k = m_lar_solver.get_base_column_in_row(row);
if (m_lar_solver.column_is_free(k) && k != j)
continue;
CTRACE("grobner", matrix.m_rows[row].size() > m_nla_settings.grobner_row_length_limit,
tout << "ignore the row " << row << " with the size " << matrix.m_rows[row].size() << "\n";);
if (matrix.m_rows[row].size() > m_nla_settings.grobner_row_length_limit)
continue;
for (auto& rc : matrix.m_rows[row])
add_var_and_its_factors_to_q_and_collect_new_rows(rc.var(), q);
}
const monic& m = emons()[j];
for (auto fcn : factorization_factory_imp(m, *this)) {
for (const factor& fc: fcn) {
q.push_back(var(fc));
}
}
}
const rational& core::val_of_fixed_var_with_deps(lpvar j, u_dependency*& dep) {
@ -1816,41 +1857,36 @@ const rational& core::val_of_fixed_var_with_deps(lpvar j, u_dependency*& dep) {
}
dd::pdd core::pdd_expr(const rational& c, lpvar j, u_dependency*& dep) {
if (m_nla_settings.grobner_subs_fixed() == 1 && var_is_fixed(j)) {
return m_pdd_manager.mk_val(c * val_of_fixed_var_with_deps(j, dep));
}
if (m_nla_settings.grobner_subs_fixed() == 2 && var_is_fixed_to_zero(j)) {
return m_pdd_manager.mk_val(val_of_fixed_var_with_deps(j, dep));
}
if (!is_monic_var(j))
return c * m_pdd_manager.mk_var(j);
u_dependency* zero_dep = dep;
// j is a monic var
dd::pdd r = m_pdd_manager.mk_val(c);
const monic& m = emons()[j];
for (lpvar k : m.vars()) {
if (m_nla_settings.grobner_subs_fixed() && var_is_fixed(k)) {
r *= m_pdd_manager.mk_val(val_of_fixed_var_with_deps(k, dep));
} else if (m_nla_settings.grobner_subs_fixed() == 2 && var_is_fixed_to_zero(k)) {
r = m_pdd_manager.mk_val(val_of_fixed_var_with_deps(k, zero_dep));
sbuffer<lpvar> vars;
vars.push_back(j);
u_dependency* zero_dep = dep;
while (!vars.empty()) {
j = vars.back();
vars.pop_back();
if (m_nla_settings.grobner_subs_fixed > 0 && var_is_fixed_to_zero(j)) {
r = m_pdd_manager.mk_val(val_of_fixed_var_with_deps(j, zero_dep));
dep = zero_dep;
return r;
} else {
r *= m_pdd_manager.mk_var(k);
}
if (m_nla_settings.grobner_subs_fixed == 1 && var_is_fixed(j))
r *= val_of_fixed_var_with_deps(j, dep);
else if (!is_monic_var(j))
r *= m_pdd_manager.mk_var(j);
else
for (lpvar k : emons()[j].vars())
vars.push_back(k);
}
return r;
}
void core::add_row_to_grobner(const vector<lp::row_cell<rational>> & row) {
u_dependency *dep = nullptr;
rational val;
dd::pdd sum = m_pdd_manager.mk_val(rational(0));
for (const auto &p : row) {
sum += pdd_expr(p.coeff(), p.var(), dep);
}
for (const auto &p : row)
sum += pdd_expr(p.coeff(), p.var(), dep);
TRACE("grobner", print_row(row, tout) << " " << sum << "\n");
m_pdd_grobner.add(sum, dep);
}
@ -1858,17 +1894,21 @@ void core::add_row_to_grobner(const vector<lp::row_cell<rational>> & row) {
void core::find_nl_cluster() {
prepare_rows_and_active_vars();
svector<lpvar> q;
for (lpvar j : m_to_refine) {
TRACE("grobner", print_monic(emons()[j], tout) << "\n";);
TRACE("grobner", for (lpvar j : m_to_refine) print_monic(emons()[j], tout) << "\n";);
for (lpvar j : m_to_refine)
q.push_back(j);
}
while (!q.empty()) {
lpvar j = q.back();
q.pop_back();
add_var_and_its_factors_to_q_and_collect_new_rows(j, q);
}
TRACE("grobner", display_matrix_of_m_rows(tout););
TRACE("grobner", tout << "vars in cluster: ";
for (lpvar j : active_var_set()) tout << "j" << j << " "; tout << "\n";
display_matrix_of_m_rows(tout);
/*emons().display(tout << "emons\n");*/
);
}
void core::prepare_rows_and_active_vars() {
@ -1902,18 +1942,16 @@ std::unordered_set<lpvar> core::get_vars_of_expr_with_opening_terms(const nex *e
void core::display_matrix_of_m_rows(std::ostream & out) const {
const auto& matrix = m_lar_solver.A_r();
out << m_rows.size() << " rows" <<"\n";
out << m_rows.size() << " rows" << "\n";
out << "the matrix\n";
for (const auto & r : matrix.m_rows) {
for (const auto & r : matrix.m_rows)
print_row(r, out) << std::endl;
}
}
void core::set_active_vars_weights(nex_creator& nc) {
nc.set_number_of_vars(m_lar_solver.column_count());
for (lpvar j : active_var_set()) {
for (lpvar j : active_var_set())
nc.set_var_weight(j, get_var_weight(j));
}
}
void core::set_level2var_for_grobner() {
@ -1944,6 +1982,11 @@ void core::set_level2var_for_grobner() {
l2v[j] = sorted_vars[j];
m_pdd_manager.reset(l2v);
TRACE("grobner",
for (auto v : sorted_vars)
tout << "j" << v << " w:" << weighted_vars[v] << " ";
tout << "\n");
}
unsigned core::get_var_weight(lpvar j) const {
@ -1954,14 +1997,14 @@ unsigned core::get_var_weight(lpvar j) const {
k = 0;
break;
case lp::column_type::boxed:
k = 2;
k = 3;
break;
case lp::column_type::lower_bound:
case lp::column_type::upper_bound:
k = 4;
k = 6;
break;
case lp::column_type::free_column:
k = 6;
k = 9;
break;
default:
UNREACHABLE();
@ -1969,9 +2012,8 @@ unsigned core::get_var_weight(lpvar j) const {
}
if (is_monic_var(j)) {
k++;
if (m_to_refine.contains(j)) {
if (m_to_refine.contains(j))
k++;
}
}
return k;
}