3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-23 17:15:31 +00:00

Add (updated and general) solve_for functionality for arithmetic, add congruence_explain to API to retrieve explanation for why two terms are congruent Tweak handling of smt.qi.max_instantations

Add API solve_for(vars).
It takes a list of variables and returns a triangular solved form for the variables.
Currently for arithmetic. The solved form is a list with elements of the form (var, term, guard).
Variables solved in the tail of the list do not occur before in the list.
For example it can return a solution [(x, z, True), (y, x + z, True)] because first x was solved to be z,
then y was solved to be x + z which is the same as 2z.

Add congruent_explain that retuns an explanation for congruent terms.
Terms congruent in the final state after calling SimpleSolver().check() can be queried for
an explanation, i.e., a list of literals that collectively entail the equality under congruence closure.
The literals are asserted in the final state of search.

Adjust smt_context cancellation for the smt.qi.max_instantiations parameter.
It gets checked when qi-queue elements are consumed.
Prior it was checked on insertion time, which didn't allow for processing as many
instantations as there were in the queue. Moreover, it would not cancel the solver.
So it would keep adding instantations to the queue when it was full / depleted the
configuration limit.
This commit is contained in:
Nikolaj Bjorner 2024-12-19 23:26:42 +01:00
parent e4ab2944fe
commit 87f7a20e14
31 changed files with 428 additions and 117 deletions

View file

@ -570,8 +570,6 @@ namespace lp {
A_r().pop(k);
}
void lar_solver::set_upper_bound_witness(lpvar j, u_dependency* dep) {
m_trail.push(vector_value_trail(m_columns, j));
m_columns[j].upper_bound_witness() = dep;
@ -617,35 +615,148 @@ namespace lp {
m_touched_rows.insert(rid);
}
bool lar_solver::solve_for(unsigned j, lar_term& t, mpq& coeff) {
t.clear();
IF_VERBOSE(10, verbose_stream() << "j " << j << " is fixed " << column_is_fixed(j) << " is-base " << is_base(j) << "\n");
if (column_is_fixed(j)) {
coeff = get_value(j);
return true;
void lar_solver::check_fixed(unsigned j) {
if (column_is_fixed(j))
return;
auto explain = [&](constraint_index ci, lp::explanation const& exp) {
u_dependency* d = nullptr;
for (auto const& e : exp)
if (e.ci() != ci)
d = join_deps(d, m_dependencies.mk_leaf(e.ci()));
return d;
};
if (!column_has_lower_bound(j) || column_lower_bound(j) != get_value(j)) {
push();
mpq val = get_value(j);
auto ci = add_var_bound(j, lconstraint_kind::LT, val);
auto r = solve();
lp::explanation exp;
if (r == lp_status::INFEASIBLE)
get_infeasibility_explanation(exp);
pop();
if (r == lp_status::INFEASIBLE) {
auto d = explain(ci, exp);
update_column_type_and_bound(j, lconstraint_kind::GE, val, d);
}
solve();
}
if (!column_has_upper_bound(j) || column_upper_bound(j) != get_value(j)) {
push();
auto val = get_value(j);
auto ci = add_var_bound(j, lconstraint_kind::GT, val);
auto r = solve();
lp::explanation exp;
if (r == lp_status::INFEASIBLE)
get_infeasibility_explanation(exp);
pop();
if (r == lp_status::INFEASIBLE) {
auto d = explain(ci, exp);
update_column_type_and_bound(j, lconstraint_kind::LE, val, d);
}
solve();
}
}
void lar_solver::solve_for(unsigned_vector const& js, vector<solution>& sols) {
uint_set tabu, fixed_checked;
for (auto j : js)
solve_for(j, tabu, sols);
solve(); // clear updated columns.
auto check = [&](unsigned j) {
if (fixed_checked.contains(j))
return;
fixed_checked.insert(j);
check_fixed(j);
};
for (auto const& [j, t] : sols) {
check(j);
for (auto const& v : t)
check(v.j());
}
}
void lar_solver::solve_for(unsigned j, uint_set& tabu, vector<solution>& sols) {
if (tabu.contains(j))
return;
tabu.insert(j);
IF_VERBOSE(10, verbose_stream() << "solve for " << j << " base " << is_base(j) << " " << column_is_fixed(j) << "\n");
if (column_is_fixed(j))
return;
if (!is_base(j)) {
for (const auto & c : A_r().m_columns[j]) {
for (const auto& c : A_r().m_columns[j]) {
lpvar basic_in_row = r_basis()[c.var()];
if (tabu.contains(basic_in_row))
continue;
pivot(j, basic_in_row);
IF_VERBOSE(10, verbose_stream() << "is base " << is_base(j) << " c.var() = " << c.var() << " basic_in_row = " << basic_in_row << "\n");
break;
}
}
if (!is_base(j))
return false;
if (!is_base(j))
return;
lar_term t;
auto const& col = m_columns[j];
auto const& r = basic2row(j);
for (auto const& c : r) {
if (c.var() == j)
continue;
if (column_is_fixed(c.var()))
coeff -= get_value(c.var());
else
t.add_monomial(-c.coeff(), c.var());
if (c.var() != j)
t.add_monomial(-c.coeff(), c.var());
}
for (auto const& v : t)
solve_for(v.j(), tabu, sols);
lp::impq lo, hi;
bool lo_valid = true, hi_valid = true;
for (auto const& v : t) {
if (v.coeff().is_pos()) {
if (lo_valid && column_has_lower_bound(v.j()))
lo += column_lower_bound(v.j()) * v.coeff();
else
lo_valid = false;
if (hi_valid && column_has_upper_bound(v.j()))
hi += column_upper_bound(v.j()) * v.coeff();
else
hi_valid = false;
}
else {
if (lo_valid && column_has_upper_bound(v.j()))
lo += column_upper_bound(v.j()) * v.coeff();
else
lo_valid = false;
if (hi_valid && column_has_lower_bound(v.j()))
hi += column_lower_bound(v.j()) * v.coeff();
else
hi_valid = false;
}
}
if (lo_valid && (!column_has_lower_bound(j) || lo > column_lower_bound(j).x)) {
u_dependency* dep = nullptr;
for (auto const& v : t) {
if (v.coeff().is_pos())
dep = join_deps(dep, m_columns[v.j()].lower_bound_witness());
else
dep = join_deps(dep, m_columns[v.j()].upper_bound_witness());
}
update_column_type_and_bound(j, lo.y == 0 ? lconstraint_kind::GE : lconstraint_kind::GT, lo.x, dep);
}
IF_VERBOSE(10, verbose_stream() << "j = " << j << " t = ";
print_term(t, verbose_stream()) << " coeff = " << coeff << "\n");
return true;
if (hi_valid && (!column_has_upper_bound(j) || hi < column_upper_bound(j).x)) {
u_dependency* dep = nullptr;
for (auto const& v : t) {
if (v.coeff().is_pos())
dep = join_deps(dep, m_columns[v.j()].upper_bound_witness());
else
dep = join_deps(dep, m_columns[v.j()].lower_bound_witness());
}
update_column_type_and_bound(j, hi.y == 0 ? lconstraint_kind::LE : lconstraint_kind::LT, hi.x, dep);
}
if (!column_is_fixed(j))
sols.push_back({j, t});
}

View file

@ -351,7 +351,15 @@ public:
* return the rest of the row as t comprising of non-fixed variables and coeff as sum of fixed variables.
* return false if j has no rows.
*/
bool solve_for(unsigned j, lar_term& t, mpq& coeff);
struct solution {
unsigned j;
lar_term t;
};
void solve_for(unsigned_vector const& js, vector<solution>& sol);
void check_fixed(unsigned j);
void solve_for(unsigned j, uint_set& tabu, vector<solution>& sol);
inline unsigned get_base_column_in_row(unsigned row_index) const {
return m_mpq_lar_core_solver.m_r_solver.get_base_column_in_row(row_index);
@ -591,21 +599,32 @@ public:
return m_columns[j].lower_bound_witness();
}
inline bool column_has_term(lpvar j) const { return m_columns[j].term() != nullptr; }
inline std::ostream& print_column_info(unsigned j, std::ostream& out) const {
m_mpq_lar_core_solver.m_r_solver.print_column_info(j, out);
if (column_has_term(j)) {
print_term_as_indices(get_term(j), out) << "\n";
} else if (column_has_term(j)) {
const lar_term& t = get_term(m_var_register.local_to_external(j));
print_term_as_indices(t, out) << "\n";
}
std::ostream& print_column_info(unsigned j, std::ostream& out) const {
m_mpq_lar_core_solver.m_r_solver.print_column_info(j, out);
if (column_has_term(j))
print_term_as_indices(get_term(j), out) << "\n";
display_column_explanation(out, j);
return out;
}
std::ostream& display_column_explanation(std::ostream& out, unsigned j) const {
const column& ul = m_columns[j];
svector<unsigned> vs1, vs2;
m_dependencies.linearize(ul.lower_bound_witness(), vs1);
m_dependencies.linearize(ul.upper_bound_witness(), vs2);
if (!vs1.empty())
out << "lo: " << vs1;
if (!vs2.empty())
out << "hi: " << vs2;
if (!vs1.empty() || !vs2.empty())
out << "\n";
return out;
}
void subst_known_terms(lar_term*);
inline std::ostream& print_column_bound_info(unsigned j, std::ostream& out) const {
std::ostream& print_column_bound_info(unsigned j, std::ostream& out) const {
return m_mpq_lar_core_solver.m_r_solver.print_column_bound_info(j, out);
}