3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-22 16:45:31 +00:00

fixes to arithmetic

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2023-06-06 07:05:34 -07:00
parent a7b3dae262
commit 05f63277cf
4 changed files with 165 additions and 32 deletions

View file

@ -166,7 +166,10 @@ bool core::check_monic(const monic& m) const {
if (!is_relevant(m.var()))
return true;
#endif
SASSERT((!m_lar_solver.column_is_int(m.var())) || m_lar_solver.get_column_value(m.var()).is_int());
if (m_lar_solver.column_is_int(m.var()) && !m_lar_solver.get_column_value(m.var()).is_int()) {
// verbose_stream() << "not integral\n";
return true;
}
bool ret = product_value(m) == m_lar_solver.get_column_value(m.var()).x;
CTRACE("nla_solver_check_monic", !ret, print_monic(m, tout) << '\n';);
return ret;
@ -1404,7 +1407,7 @@ void core::patch_monomial(lpvar j) {
}
}
void core::patch_monomials_on_to_refine() {
void core::patch_monomials_on_to_refine() {
auto to_refine = m_to_refine.index();
// the rest of the function might change m_to_refine, so have to copy
unsigned sz = to_refine.size();
@ -1512,7 +1515,8 @@ lbool core::check(vector<lemma>& l_vec) {
init_to_refine();
patch_monomials();
set_use_nra_model(false);
if (m_to_refine.empty()) { return l_true; }
if (m_to_refine.empty())
return l_true;
init_search();
lbool ret = l_undef;

View file

@ -207,9 +207,9 @@ namespace smt {
if (m.proofs_enabled() && m_proof_hint.empty()) {
m_proof_hint.push_back(parameter(symbol("triangle-eq")));
}
ctx.mk_th_axiom(tid, ~t1_eq_t2_lit, le_lit, m_proof_hint.size(), m_proof_hint.data());
ctx.mk_th_axiom(tid, ~t1_eq_t2_lit, ge_lit, m_proof_hint.size(), m_proof_hint.data());
ctx.mk_th_axiom(tid, t1_eq_t2_lit, ~le_lit, ~ge_lit, m_proof_hint.size(), m_proof_hint.data());
ctx.mk_th_lemma(tid, ~t1_eq_t2_lit, le_lit, m_proof_hint.size(), m_proof_hint.data());
ctx.mk_th_lemma(tid, ~t1_eq_t2_lit, ge_lit, m_proof_hint.size(), m_proof_hint.data());
ctx.mk_th_lemma(tid, t1_eq_t2_lit, ~le_lit, ~ge_lit, m_proof_hint.size(), m_proof_hint.data());
TRACE("arith_eq_adapter", tout << "internalizing: "
<< " " << mk_pp(le, m) << ": " << le_lit
<< " " << mk_pp(ge, m) << ": " << ge_lit

View file

@ -1522,7 +1522,7 @@ namespace smt {
switch (m_final_check_idx) {
case 0:
ok = check_int_feasibility();
if (ok != FC_DONE) verbose_stream() << "int-feas\n";
// if (ok != FC_DONE) verbose_stream() << "int-feas\n";
TRACE("arith", tout << "check_int_feasibility(), ok: " << ok << "\n";);
break;
case 1:
@ -1530,7 +1530,7 @@ namespace smt {
ok = FC_CONTINUE;
else
ok = FC_DONE;
if (ok != FC_DONE) verbose_stream() << "assume-eqs\n";
//if (ok != FC_DONE) verbose_stream() << "assume-eqs\n";
TRACE("arith", tout << "assume_eqs(), ok: " << ok << "\n";);
break;
default:
@ -1567,7 +1567,7 @@ namespace smt {
template<typename Ext>
final_check_status theory_arith<Ext>::final_check_eh() {
verbose_stream() << "final " << ctx.get_scope_level() << " " << ctx.assigned_literals().size() << "\n";
// verbose_stream() << "final " << ctx.get_scope_level() << " " << ctx.assigned_literals().size() << "\n";
// ctx.display(verbose_stream());
// exit(0);
@ -1577,7 +1577,7 @@ namespace smt {
if (!propagate_core())
return FC_CONTINUE;
if (delayed_assume_eqs()) {
verbose_stream() << "delayed-eqs\n";
//verbose_stream() << "delayed-eqs\n";
return FC_CONTINUE;
}
ctx.push_trail(value_trail<unsigned>(m_final_check_idx));

View file

@ -350,7 +350,7 @@ class theory_lra::imp {
SASSERT(a.is_add(n));
scoped_internalize_state st(*this);
for (expr* arg : *n)
internalize_internal_monomial(to_app(arg), st);
internalize_internal_monomial(to_app(arg), st, rational::one());
enode* e = mk_enode(n);
theory_var v = e->get_th_var(get_id());
@ -359,7 +359,7 @@ class theory_lra::imp {
return v;
}
void internalize_internal_monomial(app* arg, scoped_internalize_state& st) {
void internalize_internal_monomial(app* arg, scoped_internalize_state& st, rational const& sign) {
expr* arg1, * arg2;
rational val, val2;
@ -367,13 +367,13 @@ class theory_lra::imp {
enode* e = ctx().get_enode(arg);
if (th.is_attached_to_var(e)) {
// there is already a theory variable (i.e., name) for arg.
st.add(e->get_th_var(get_id()), rational::one());
st.add(e->get_th_var(get_id()), sign);
return;
}
}
if (a.is_extended_numeral(arg, val))
st.add(internalize_numeral(arg, val), rational::one());
st.add(internalize_numeral(arg, val), sign);
else if (a.is_mul(arg, arg1, arg2)) {
if (a.is_extended_numeral(arg2, val))
std::swap(arg1, arg2);
@ -386,7 +386,7 @@ class theory_lra::imp {
return;
}
}
st.add(internalize_term_core(arg), rational::one());
st.add(internalize_term_core(arg), sign);
}
theory_var internalize_numeral(app* n, rational const& val) {
@ -437,15 +437,21 @@ class theory_lra::imp {
TRACE("internalize_mul_core", tout << "internalizing...\n" << mk_pp(t, m) << "\n";);
if (!a.is_mul(t))
return internalize_term_core(t);
svector<lpvar> vars;
for (expr* arg : *t) {
theory_var v = internalize_term_core(to_app(arg));
if (v == null_theory_var)
mk_var(to_app(arg));
v = mk_var(to_app(arg));
vars.push_back(register_theory_var_in_lar_solver(v));
}
enode* e = mk_enode(t);
theory_var v = e->get_th_var(get_id());
if (v == null_theory_var)
v = mk_var(t);
if (v == null_theory_var) {
v = mk_var(t);
m_solver->register_existing_terms();
ensure_nla();
m_nla->add_monic(register_theory_var_in_lar_solver(v), vars.size(), vars.data());
}
return v;
}
@ -460,13 +466,37 @@ class theory_lra::imp {
return s;
}
theory_var internalize_div(app * n) {
theory_var internalize_idiv(app * n) {
rational r(1);
theory_var s = mk_binary_op(n);
if (!a.is_numeral(n->get_arg(1), r) || r.is_zero())
found_underspecified(n);
if (!ctx().relevancy())
mk_div_axiom(n->get_arg(0), n->get_arg(1));
expr* n1, *n2;
VERIFY(a.is_idiv(n, n1, n2));
app_ref mod(a.mk_mod(n1, n2), m);
ctx().internalize(mod, false);
if (ctx().relevancy()) ctx().add_relevancy_dependency(n, mod);
if (m_nla && !a.is_numeral(n2)) {
// shortcut to create non-linear division axioms.
internalize_term(to_app(n));
internalize_term(to_app(n1));
internalize_term(to_app(n2));
theory_var q = mk_var(n);
theory_var x = mk_var(n1);
theory_var y = mk_var(n2);
m_nla->add_idivision(register_theory_var_in_lar_solver(q), register_theory_var_in_lar_solver(x), register_theory_var_in_lar_solver(y));
}
if (a.is_numeral(n2) && a.is_bounded(n1)) {
ensure_nla();
internalize_term(to_app(n));
internalize_term(to_app(n1));
internalize_term(to_app(n2));
theory_var q = mk_var(n);
theory_var x = mk_var(n1);
theory_var y = mk_var(n2);
m_nla->add_bounded_division(register_theory_var_in_lar_solver(q), register_theory_var_in_lar_solver(x), register_theory_var_in_lar_solver(y));
}
if (!ctx().relevancy()) mk_idiv_mod_axioms(n1, n2);
return s;
}
@ -481,6 +511,35 @@ class theory_lra::imp {
return mk_var(n);
}
theory_var internalize_uminus(app* n) {
scoped_internalize_state st(*this);
for (expr* arg : *n)
internalize_internal_monomial(to_app(arg), st, rational::minus_one());
enode* e = mk_enode(n);
theory_var v = e->get_th_var(get_id());
if (v == null_theory_var)
v = internalize_linearized_def(n, st);
return v;
}
theory_var internalize_minus(app* n) {
scoped_internalize_state st(*this);
bool first = true;
for (expr* arg : *n) {
internalize_internal_monomial(to_app(arg), st, first ? rational::one():rational::minus_one());
first = false;
}
enode* e = mk_enode(n);
theory_var v = e->get_th_var(get_id());
if (v == null_theory_var)
v = internalize_linearized_def(n, st);
return v;
}
/**
* \brief Internalize the given term and return an alias for it.
@ -495,8 +554,6 @@ class theory_lra::imp {
return e->get_th_var(get_id());
}
SASSERT(!a.is_uminus(n));
rational val;
if (a.is_add(n))
@ -508,9 +565,13 @@ class theory_lra::imp {
else if (a.is_mod(n))
return internalize_mod(n);
else if (a.is_idiv(n))
return internalize_mod(n);
return internalize_idiv(n);
else if (a.is_div(n))
return internalize_div(n);
return internalize_mod(n);
else if (a.is_uminus(n))
return internalize_uminus(n);
else if (a.is_sub(n))
return internalize_minus(n);
else if (a.get_family_id() == n->get_family_id()) {
if (!a.is_div0(n) && !a.is_mod0(n) && !a.is_idiv0(n) && !a.is_rem0(n))
found_unsupported(n);
@ -604,7 +665,7 @@ class theory_lra::imp {
coeffs[vars.size()] = r * coeffs[index];
vars.push_back(v);
get_lpvar(v);
verbose_stream() << v << "\n";
//verbose_stream() << v << "\n";
st.to_ensure_enode().push_back(n1);
++index;
}
@ -1562,6 +1623,7 @@ public:
}
#if 0
// create axiom for
// u = v + r*w,
/// abs(r) > r >= 0
@ -1580,11 +1642,13 @@ public:
SASSERT(!is_infeasible());
TRACE("arith", tout << term << "\n" << lp().constraints(););
}
#endif
void mk_idiv_mod_axioms(expr * p, expr * q) {
if (a.is_zero(q)) {
return;
}
TRACE("arith", tout << expr_ref(p, m) << " " << expr_ref(q, m) << "\n";);
// if q is zero, then idiv and mod are uninterpreted functions.
expr_ref div(a.mk_idiv(p, q), m);
@ -2029,7 +2093,7 @@ public:
final_check_status final_check_eh() {
verbose_stream() << "final " << ctx().get_scope_level() << " " << ctx().assigned_literals().size() << "\n";
// verbose_stream() << "final " << ctx().get_scope_level() << " " << ctx().assigned_literals().size() << "\n";
//ctx().display(verbose_stream());
//exit(0);
@ -2039,7 +2103,6 @@ public:
if (propagate_core())
return FC_CONTINUE;
if (delayed_assume_eqs()) {
verbose_stream() << "delayed-eqs\n";
return FC_CONTINUE;
}
m_liberal_final_check = true;
@ -2048,12 +2111,18 @@ public:
final_check_status result = final_check_core();
if (result != FC_DONE)
return result;
if (!m_changed_assignment)
if (!m_changed_assignment) {
validate_solution();
return FC_DONE;
}
m_liberal_final_check = false;
m_changed_assignment = false;
result = final_check_core();
TRACE("arith", tout << "result: " << result << "\n";);
if (result == FC_DONE) {
verbose_stream() << "second round\n";
validate_solution();
}
return result;
}
@ -2133,12 +2202,10 @@ public:
switch (m_final_check_idx) {
case 0:
st = check_lia();
if (st != FC_DONE) verbose_stream() << "check-lia\n";
break;
case 1:
if (assume_eqs())
st = FC_CONTINUE;
if (st != FC_DONE) verbose_stream() << "assume-eqs\n";
break;
case 2:
st = check_nla();
@ -2178,6 +2245,13 @@ public:
if (st == FC_CONTINUE)
break;
}
if (st == FC_DONE) {
if (assume_eqs()) {
verbose_stream() << "not done\n";
return FC_CONTINUE;
}
}
return st;
case l_false:
get_infeasibility_explanation_and_set_conflict();
@ -4009,7 +4083,62 @@ public:
for (auto const& eq : m_eqs) {
nctx.assert_expr(m.mk_eq(eq.first->get_expr(), eq.second->get_expr()));
}
}
}
void validate_solution() {
return;
verbose_stream() << "validate solution\n";
unsigned nv = th.get_num_vars();
for (unsigned v = 0; v < nv; ++v) {
auto t = get_tv(v);
auto vi = lp().external_to_column_index(v);
if (!is_registered_var(v))
continue;
auto* n = get_enode(v);
expr* e = n->get_expr(), *e1, *e2;
rational r, r1, r2;
if (use_nra_model())
m_nla->am().display(verbose_stream() << " = ", nl_value(v, *m_a1)) << "\n";
else {
r = lp().get_tv_value(get_tv(v));
verbose_stream() << r << "\n";
if (a.is_mod(e, e1, e2)) {
auto v1 = th.get_th_var(e1);
auto v2 = th.get_th_var(e2);
r1 = lp().get_tv_value(get_tv(v1));
r2 = lp().get_tv_value(get_tv(v2));
if (r2 > 0)
VERIFY(r == r1 % r2);
}
else if (a.is_idiv(e, e1, e2)) {
auto v1 = th.get_th_var(e1);
auto v2 = th.get_th_var(e2);
r1 = lp().get_tv_value(get_tv(v1));
r2 = lp().get_tv_value(get_tv(v2));
verbose_stream() << mk_pp(e, m) << " " << r << " == " << r1 << " div " << r2 << "\n";
if (r2 > 0)
VERIFY(r == div(r1, r2));
}
else if (a.is_add(e)) {
verbose_stream() << "add v" << v << " " << r <<"\n";
}
else if (a.is_numeral(e, r2)) {
VERIFY(r == r2);
}
else if (is_uninterp_const(e)) {
}
else {
verbose_stream() << "other " << enode_pp(n, ctx()) << "\n";
}
}
}
}
theory_lra::inf_eps value(theory_var v) {
lp::impq ival = get_ivalue(v);