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

fix #3194, remove euclidean solver

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2020-03-08 16:05:13 +01:00
parent 9b3c844c2a
commit 611c14844d
12 changed files with 5 additions and 1319 deletions

View file

@ -77,7 +77,6 @@ z3_add_component(smt
COMPONENT_DEPENDENCIES
bit_blaster
cmd_context
euclid
fpa
grobner
nlsat

View file

@ -59,7 +59,6 @@ def_module_params(module_name='smt',
('arith.nl.grobner_cnfl_to_report', UINT, 1, 'grobner\'s maximum number of conflicts to report'),
('arith.nl.gr_q', UINT, 10, 'grobner\'s quota'),
('arith.nl.grobner_subs_fixed', UINT, 2, '0 - no subs, 1 - substitute, 2 - substitute fixed zeros only'),
('arith.euclidean_solver', BOOL, False, 'eucliean solver for linear integer arithmetic'),
('arith.propagate_eqs', BOOL, True, 'propagate (cheap) equalities'),
('arith.propagation_mode', UINT, 2, '0 - no propagation, 1 - propagate existing literals, 2 - refine bounds'),
('arith.reflect', BOOL, True, 'reflect arithmetical operators to the congruence closure'),

View file

@ -29,7 +29,6 @@ void theory_arith_params::updt_params(params_ref const & _p) {
m_nl_arith_gb = p.arith_nl_gb();
m_nl_arith_branching = p.arith_nl_branching();
m_nl_arith_rounds = p.arith_nl_rounds();
m_arith_euclidean_solver = p.arith_euclidean_solver();
m_arith_propagate_eqs = p.arith_propagate_eqs();
m_arith_branch_cut_ratio = p.arith_branch_cut_ratio();
m_arith_int_eq_branching = p.arith_int_eq_branch();
@ -93,5 +92,4 @@ void theory_arith_params::display(std::ostream & out) const {
DISPLAY_PARAM(m_nl_arith_max_degree);
DISPLAY_PARAM(m_nl_arith_branching);
DISPLAY_PARAM(m_nl_arith_rounds);
DISPLAY_PARAM(m_arith_euclidean_solver);
}

View file

@ -106,8 +106,6 @@ struct theory_arith_params {
bool m_nl_arith_branching;
unsigned m_nl_arith_rounds;
// euclidean solver for tighting bounds
bool m_arith_euclidean_solver;
theory_arith_params(params_ref const & p = params_ref()):
@ -155,8 +153,7 @@ struct theory_arith_params {
m_nl_arith_gb_perturbate(true),
m_nl_arith_max_degree(6),
m_nl_arith_branching(true),
m_nl_arith_rounds(1024),
m_arith_euclidean_solver(false) {
m_nl_arith_rounds(1024) {
updt_params(p);
}

View file

@ -853,9 +853,9 @@ namespace smt {
bool max_min_infeasible_int_vars();
void patch_int_infeasible_vars();
void fix_non_base_vars();
unsynch_mpq_manager m_es_num_manager; // manager for euclidean solver.
struct euclidean_solver_bridge;
bool apply_euclidean_solver();
// unsynch_mpq_manager m_es_num_manager; // manager for euclidean solver.
// struct euclidean_solver_bridge;
// bool apply_euclidean_solver();
final_check_status check_int_feasibility();
// -----------------------------------

View file

@ -23,7 +23,6 @@ Revision History:
#include "ast/ast_ll_pp.h"
#include "ast/well_sorted.h"
#include "ast/ast_smt2_pp.h"
#include "math/euclid/euclidean_solver.h"
namespace smt {
@ -964,335 +963,6 @@ namespace smt {
failed();
}
template<typename Ext>
struct theory_arith<Ext>::euclidean_solver_bridge {
typedef numeral_buffer<mpz, euclidean_solver::numeral_manager> mpz_buffer;
theory_arith & t;
euclidean_solver m_solver;
unsigned_vector m_tv2v; // theory var to euclidean solver var
svector<theory_var> m_j2v; // justification to theory var
// aux fields
unsigned_vector m_xs;
mpz_buffer m_as;
unsigned_vector m_js;
typedef euclidean_solver::var evar;
typedef euclidean_solver::justification ejustification;
euclidean_solver_bridge(theory_arith & _t):t(_t), m_solver(&t.m_es_num_manager), m_as(m_solver.m()) {}
evar mk_var(theory_var v) {
m_tv2v.reserve(v+1, UINT_MAX);
if (m_tv2v[v] == UINT_MAX)
m_tv2v[v] = m_solver.mk_var();
return m_tv2v[v];
}
/**
\brief Given a monomial, retrieve its coefficient and the power product
That is, mon = a * pp
*/
void get_monomial(expr * mon, rational & a, expr * & pp) {
expr * a_expr;
if (t.m_util.is_mul(mon, a_expr, pp) && t.m_util.is_numeral(a_expr, a))
return;
a = rational(1);
pp = mon;
}
/**
\brief Return the theory var associated with the given power product.
*/
theory_var get_theory_var(expr * pp) {
context & ctx = t.get_context();
if (ctx.e_internalized(pp)) {
enode * e = ctx.get_enode(pp);
if (t.is_attached_to_var(e))
return e->get_th_var(t.get_id());
}
return null_theory_var;
}
/**
\brief Create an euclidean_solver variable for the given
power product, if it has a theory variable associated with
it.
*/
evar mk_var(expr * pp) {
theory_var v = get_theory_var(pp);
if (v == null_theory_var)
return UINT_MAX;
return mk_var(v);
}
/**
\brief Return the euclidean_solver variable associated with the given
power product. Return UINT_MAX, if it doesn't have one.
*/
evar get_var(expr * pp) {
theory_var v = get_theory_var(pp);
if (v == null_theory_var || v >= static_cast<int>(m_tv2v.size()))
return UINT_MAX;
return m_tv2v[v];
}
void assert_eqs() {
// traverse definitions looking for equalities
mpz c, a;
mpz one;
euclidean_solver::numeral_manager & m = m_solver.m();
m.set(one, 1);
mpz_buffer & as = m_as;
unsigned_vector & xs = m_xs;
int num = t.get_num_vars();
for (theory_var v = 0; v < num; v++) {
if (!t.is_fixed(v))
continue;
if (!t.is_int(v))
continue; // only integer variables
expr * n = t.get_enode(v)->get_owner();
if (t.m_util.is_numeral(n))
continue; // skip stupid equality c - c = 0
inf_numeral const & val = t.get_value(v);
rational num = val.get_rational().to_rational();
SASSERT(num.is_int());
num.neg();
m.set(c, num.to_mpq().numerator());
ejustification j = m_solver.mk_justification();
m_j2v.reserve(j+1, null_theory_var);
m_j2v[j] = v;
as.reset();
xs.reset();
bool failed = false;
unsigned num_args;
expr * const * args;
if (t.m_util.is_add(n)) {
num_args = to_app(n)->get_num_args();
args = to_app(n)->get_args();
}
else {
num_args = 1;
args = &n;
}
for (unsigned i = 0; i < num_args; i++) {
expr * arg = args[i];
expr * pp;
rational a_val;
get_monomial(arg, a_val, pp);
if (!a_val.is_int()) {
failed = true;
break;
}
evar x = mk_var(pp);
if (x == UINT_MAX) {
failed = true;
break;
}
m.set(a, a_val.to_mpq().numerator());
as.push_back(a);
xs.push_back(x);
}
if (!failed) {
m_solver.assert_eq(as.size(), as.c_ptr(), xs.c_ptr(), c, j);
TRACE("euclidean_solver", tout << "add definition: v" << v << " := " << mk_ismt2_pp(n, t.get_manager()) << "\n";);
}
else {
TRACE("euclidean_solver", tout << "failed for:\n" << mk_ismt2_pp(n, t.get_manager()) << "\n";);
}
}
m.del(a);
m.del(c);
m.del(one);
}
void mk_bound(theory_var v, rational k, bool lower, bound * old_bound, unsigned_vector const & js) {
derived_bound * new_bound = alloc(derived_bound, v, inf_numeral(k), lower ? B_LOWER : B_UPPER);
t.m_tmp_lit_set.reset();
t.m_tmp_eq_set.reset();
if (old_bound != nullptr) {
t.accumulate_justification(*old_bound, *new_bound, numeral(0) /* refine for proof gen */, t.m_tmp_lit_set, t.m_tmp_eq_set);
}
unsigned_vector::const_iterator it = js.begin();
unsigned_vector::const_iterator end = js.end();
for (; it != end; ++it) {
ejustification j = *it;
theory_var fixed_v = m_j2v[j];
SASSERT(fixed_v != null_theory_var);
t.accumulate_justification(*(t.lower(fixed_v)), *new_bound, numeral(0) /* refine for proof gen */, t.m_tmp_lit_set, t.m_tmp_eq_set);
t.accumulate_justification(*(t.upper(fixed_v)), *new_bound, numeral(0) /* refine for proof gen */, t.m_tmp_lit_set, t.m_tmp_eq_set);
}
t.m_bounds_to_delete.push_back(new_bound);
t.m_asserted_bounds.push_back(new_bound);
}
void mk_lower(theory_var v, rational k, bound * old_bound, unsigned_vector const & js) {
mk_bound(v, k, true, old_bound, js);
}
void mk_upper(theory_var v, rational k, bound * old_bound, unsigned_vector const & js) {
mk_bound(v, k, false, old_bound, js);
}
bool tight_bounds(theory_var v) {
SASSERT(!t.is_fixed(v));
euclidean_solver::numeral_manager & m = m_solver.m();
expr * n = t.get_enode(v)->get_owner();
SASSERT(!t.m_util.is_numeral(n)); // should not be a numeral since v is not fixed.
bool propagated = false;
mpz a;
mpz c;
rational g; // gcd of the coefficients of the variables that are not in m_solver
rational c2;
bool init_g = false;
mpz_buffer & as = m_as;
unsigned_vector & xs = m_xs;
as.reset();
xs.reset();
unsigned num_args;
expr * const * args;
if (t.m_util.is_add(n)) {
num_args = to_app(n)->get_num_args();
args = to_app(n)->get_args();
}
else {
num_args = 1;
args = &n;
}
for (unsigned j = 0; j < num_args; j++) {
expr * arg = args[j];
expr * pp;
rational a_val;
get_monomial(arg, a_val, pp);
if (!a_val.is_int())
goto cleanup;
evar x = get_var(pp);
if (x == UINT_MAX) {
a_val = abs(a_val);
if (init_g)
g = gcd(g, a_val);
else
g = a_val;
init_g = true;
if (g.is_one())
goto cleanup; // gcd of the coeffs is one.
}
else {
m.set(a, a_val.to_mpq().numerator());
as.push_back(a);
xs.push_back(x);
}
}
m_js.reset();
m_solver.normalize(as.size(), as.c_ptr(), xs.c_ptr(), c, a, c, m_js);
if (init_g) {
if (!m.is_zero(a))
g = gcd(g, rational(a));
}
else {
g = rational(a);
}
TRACE("euclidean_solver", tout << "tightening " << mk_ismt2_pp(t.get_enode(v)->get_owner(), t.get_manager()) << "\n";
tout << "g: " << g << ", a: " << m.to_string(a) << ", c: " << m.to_string(c) << "\n";
t.display_var(tout, v););
if (g.is_one())
goto cleanup;
CTRACE("euclidean_solver_zero", g.is_zero(), tout << "tightening " << mk_ismt2_pp(t.get_enode(v)->get_owner(), t.get_manager()) << "\n";
tout << "g: " << g << ", a: " << m.to_string(a) << ", c: " << m.to_string(c) << "\n";
t.display(tout);
tout << "------------\nSolver state:\n";
m_solver.display(tout););
if (g.is_zero()) {
// The definition of v is equal to (0 + c).
// That is, v is fixed at c.
// The justification is just m_js, the existing bounds of v are not needed for justifying the new bounds for v.
c2 = rational(c);
TRACE("euclidean_solver_new", tout << "new fixed: " << c2 << "\n";);
propagated = true;
mk_lower(v, c2, nullptr, m_js);
mk_upper(v, c2, nullptr, m_js);
}
else {
TRACE("euclidean_solver", tout << "inequality can be tightned, since all coefficients are multiple of: " << g << "\n";);
// Let l and u be the current lower and upper bounds.
// Then, the following new bounds can be generated:
//
// l' := g*ceil((l - c)/g) + c
// u' := g*floor((l - c)/g) + c
bound * l = t.lower(v);
bound * u = t.upper(v);
c2 = rational(c);
if (l != nullptr) {
rational l_old = l->get_value().get_rational().to_rational();
rational l_new = g*ceil((l_old - c2)/g) + c2;
TRACE("euclidean_solver_new", tout << "new lower: " << l_new << " old: " << l_old << "\n";
tout << "c: " << c2 << " ceil((l_old - c2)/g): " << (ceil((l_old - c2)/g)) << "\n";);
if (l_new > l_old) {
propagated = true;
mk_lower(v, l_new, l, m_js);
}
}
if (u != nullptr) {
rational u_old = u->get_value().get_rational().to_rational();
rational u_new = g*floor((u_old - c2)/g) + c2;
TRACE("euclidean_solver_new", tout << "new upper: " << u_new << " old: " << u_old << "\n";);
if (u_new < u_old) {
propagated = true;
mk_upper(v, u_new, u, m_js);
}
}
}
cleanup:
m.del(a);
m.del(c);
return propagated;
}
bool tight_bounds() {
bool propagated = false;
context & ctx = t.get_context();
// try to apply solution set to every definition
int num = t.get_num_vars();
for (theory_var v = 0; v < num; v++) {
if (t.is_fixed(v))
continue; // skip equations...
if (!t.is_int(v))
continue; // skip non integer definitions...
if (t.lower(v) == nullptr && t.upper(v) == nullptr)
continue; // there is nothing to be tightned
if (tight_bounds(v))
propagated = true;
if (ctx.inconsistent())
break;
}
return propagated;
}
bool operator()() {
TRACE("euclidean_solver", t.display(tout););
assert_eqs();
m_solver.solve();
if (m_solver.inconsistent()) {
// TODO: set conflict
TRACE("euclidean_solver_conflict", tout << "conflict detected...\n"; m_solver.display(tout););
return false;
}
return tight_bounds();
}
};
template<typename Ext>
bool theory_arith<Ext>::apply_euclidean_solver() {
TRACE("euclidean_solver", tout << "executing euclidean solver...\n";);
euclidean_solver_bridge esb(*this);
if (esb()) {
propagate_core();
return true;
}
return false;
}
/**
\brief Return FC_DONE if the assignment is int feasible. Otherwise, apply GCD test,
branch and bound and Gomory Cuts.
@ -1341,9 +1011,6 @@ namespace smt {
if (!gcd_test())
return FC_CONTINUE;
if (m_params.m_arith_euclidean_solver)
apply_euclidean_solver();
if (get_context().inconsistent())
return FC_CONTINUE;