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:
parent
9b3c844c2a
commit
611c14844d
12 changed files with 5 additions and 1319 deletions
|
@ -77,7 +77,6 @@ z3_add_component(smt
|
|||
COMPONENT_DEPENDENCIES
|
||||
bit_blaster
|
||||
cmd_context
|
||||
euclid
|
||||
fpa
|
||||
grobner
|
||||
nlsat
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
// -----------------------------------
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue