3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-11 03:33:35 +00:00
z3/lib/theory_arith_core.h
Leonardo de Moura e9eab22e5c Z3 sources
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2012-10-02 11:35:25 -07:00

3081 lines
121 KiB
C++

/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
theory_arith_core.h
Abstract:
<abstract>
Author:
Leonardo de Moura (leonardo) 2008-04-22.
Revision History:
--*/
#ifndef _THEORY_ARITH_CORE_H_
#define _THEORY_ARITH_CORE_H_
#include"smt_context.h"
#include"theory_arith.h"
#include"ast_pp.h"
#include"ast_ll_pp.h"
#include"smt_model_generator.h"
#include"ast_smt2_pp.h"
namespace smt {
template<typename Ext>
void theory_arith<Ext>::found_unsupported_op(app * n) {
if (!m_found_unsupported_op) {
TRACE("arith", tout << "found non supported expression:\n" << mk_pp(n, get_manager()) << "\n";);
get_context().push_trail(value_trail<context, bool>(m_found_unsupported_op));
m_found_unsupported_op = true;
}
}
template<typename Ext>
bool theory_arith<Ext>::process_atoms() const {
if (!adaptive())
return true;
unsigned total_conflicts = get_context().get_num_conflicts();
if (total_conflicts < 10)
return true;
double f = static_cast<double>(get_num_conflicts())/static_cast<double>(total_conflicts);
TRACE_CODE({
static unsigned counter = 0;
counter++;
if (counter % 1000 == 0) {
TRACE("arith_adaptive", tout << "arith_conflicts: " << get_num_conflicts() << " total_conflicts: " << total_conflicts << " factor: " << f << "\n";);
}
});
return f >= adaptive_assertion_threshold();
}
template<typename Ext>
theory_var theory_arith<Ext>::mk_var(enode * n) {
theory_var r = theory::mk_var(n);
SASSERT(r == static_cast<int>(m_columns.size()));
SASSERT(check_vector_sizes());
bool is_int = m_util.is_int(n->get_owner());
TRACE("mk_arith_var", tout << mk_pp(n->get_owner(), get_manager()) << " is_int: " << is_int << "\n";);
m_columns .push_back(column());
m_data .push_back(var_data(is_int));
if (random_initial_value()) {
unsigned val = (m_random()%(random_upper() - random_lower())) + random_lower();
m_value .push_back(inf_numeral(val));
}
else {
m_value .push_back(inf_numeral());
}
m_old_value .push_back(inf_numeral());
m_var_occs .push_back(atoms());
m_unassigned_atoms .push_back(0);
m_var_pos .push_back(-1);
m_bounds[0] .push_back(0);
m_bounds[1] .push_back(0);
if (r >= static_cast<int>(m_to_patch.get_bounds()))
m_to_patch.set_bounds(r + 1);
m_in_update_trail_stack.assure_domain(r);
m_left_basis.assure_domain(r);
m_in_to_check.assure_domain(r);
if (is_pure_monomial(n->get_owner()))
m_nl_monomials.push_back(r);
SASSERT(check_vector_sizes());
TRACE("mk_arith_var",
tout << "#" << n->get_owner_id() << " :=\n" << mk_ll_pp(n->get_owner(), get_manager()) << "\n";
tout << "is_attached_to_var: " << is_attached_to_var(n) << ", var: " << n->get_th_var(get_id()) << "\n";);
get_context().attach_th_var(n, this, r);
return r;
}
template<typename Ext>
inline bool theory_arith<Ext>::reflection_enabled() const {
return m_params.m_arith_reflect;
}
template<typename Ext>
inline bool theory_arith<Ext>::reflect(app * n) const {
if (reflection_enabled())
return true; // reflect everything
// Every underspecified operator must be reflected in the egraph.
// Although it is underspecified, it is still a function.
// For example, a/b != a/0 then we must have b != 0
// I use the Egraph to enforce that.
if (n->get_family_id() == get_id()) {
switch (n->get_decl_kind()) {
case OP_DIV:
case OP_IDIV:
case OP_REM:
case OP_MOD:
return true;
default:
break;
}
}
return false;
}
template<typename Ext>
inline bool theory_arith<Ext>::enable_cgc_for(app * n) const {
// Congruence closure is not enabled for (+ ...) and (* ...) applications.
return !(n->get_family_id() == get_id() && (n->get_decl_kind() == OP_ADD || n->get_decl_kind() == OP_MUL));
}
/**
\brief Create an enode for n.
*/
template<typename Ext>
enode * theory_arith<Ext>::mk_enode(app * n) {
context & ctx = get_context();
if (ctx.e_internalized(n))
return ctx.get_enode(n);
else
return ctx.mk_enode(n, !reflect(n), false, enable_cgc_for(n));
}
/**
\brief Create an enode for n if reflection is enabled.
*/
template<typename Ext>
void theory_arith<Ext>::mk_enode_if_reflect(app * n) {
if (reflection_enabled()) {
// make sure that n is in the e-graph
mk_enode(n);
}
}
/**
\brief Add coeff * v to the row r.
The column is also updated.
*/
template<typename Ext>
template<bool invert>
void theory_arith<Ext>::add_row_entry(unsigned r_id, numeral const & coeff, theory_var v) {
row & r = m_rows[r_id];
column & c = m_columns[v];
int r_idx;
row_entry & r_entry = r.add_row_entry(r_idx);
int c_idx;
col_entry & c_entry = c.add_col_entry(c_idx);
r_entry.m_var = v;
r_entry.m_coeff = coeff;
if (invert)
r_entry.m_coeff .neg();
r_entry.m_col_idx = c_idx;
c_entry.m_row_id = r_id;
c_entry.m_row_idx = r_idx;
}
/**
\brief Internalize the monomial of a polynomial. Store the monomial in the given row.
The monomial is negated before being inserted into the row.
*/
template<typename Ext>
void theory_arith<Ext>::internalize_internal_monomial(app * m, unsigned r_id) {
context & ctx = get_context();
if (ctx.e_internalized(m)) {
enode * e = ctx.get_enode(m);
if (is_attached_to_var(e)) {
// there is already a theory variable (i.e., name) for m.
theory_var v = e->get_th_var(get_id());
add_row_entry<false>(r_id, numeral::minus_one(), v);
return;
}
}
rational _val;
if (m_util.is_mul(m) && m_util.is_numeral(m->get_arg(0), _val)) {
SASSERT(m->get_num_args() == 2);
numeral val(_val);
theory_var v = internalize_term_core(to_app(m->get_arg(1)));
if (reflection_enabled()) {
internalize_term_core(to_app(m->get_arg(0)));
mk_enode(m);
}
add_row_entry<true>(r_id, val, v);
}
else {
theory_var v = internalize_term_core(m);
add_row_entry<false>(r_id, numeral::minus_one(), v);
}
}
/**
\brief Internalize a polynomial (+ h t). Return an alias for the monomial, that is,
a variable v such that v = (+ h t) is a new row in the tableau.
*/
template<typename Ext>
theory_var theory_arith<Ext>::internalize_add(app * n) {
TRACE("add_bug", tout << "n: " << mk_pp(n, get_manager()) << "\n";);
CTRACE("internalize_add_bug", n->get_num_args() == 2 && n->get_arg(0) == n->get_arg(1), tout << "n: " << mk_pp(n, get_manager()) << "\n";);
SASSERT(m_util.is_add(n));
unsigned r_id = mk_row();
unsigned num_args = n->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
internalize_internal_monomial(to_app(n->get_arg(i)), r_id);
}
enode * e = mk_enode(n);
theory_var v = e->get_th_var(get_id());
if (v == null_theory_var) {
v = mk_var(e);
add_row_entry<false>(r_id, numeral::one(), v);
init_row(r_id);
}
else {
// HACK: n was already internalized by the internalize_internal_monomial or internalize_internal_add call above.
// This can happen when one of calls invoke (indirectly) mk_axiom.
// For example, they contain a nested to_int(t) term.
// TODO: reimplement mk_axiom. The current implementation is flaky.
// I should cache the axioms that need to be created. They should only be internalized after we finished internalizing the
// current atom. Several other theories have similar problems.
del_row(r_id);
}
return v;
}
/**
\brief Internalize a term (* x y z) that does not contain a coefficient (numeral).
*/
template<typename Ext>
theory_var theory_arith<Ext>::internalize_mul_core(app * m) {
TRACE("internalize_mul_core", tout << "internalizing...\n" << mk_pp(m,get_manager()) << "\n";);
if (!m_util.is_mul(m))
return internalize_term_core(m);
for (unsigned i = 0; i < m->get_num_args(); i++) {
app * arg = to_app(m->get_arg(i));
SASSERT(!m_util.is_numeral(arg));
theory_var v = internalize_term_core(arg);
if (v == null_theory_var) {
mk_var(mk_enode(arg));
}
}
enode * e = mk_enode(m);
theory_var v = e->get_th_var(get_id());
if (v == null_theory_var) {
v = mk_var(e);
}
return v;
}
/**
\brief Internalize the terms of the form (* c (* t1 ... tn)) and (* t1 ... tn).
Return an alias for the term.
*/
template<typename Ext>
theory_var theory_arith<Ext>::internalize_mul(app * m) {
rational _val;
SASSERT(m_util.is_mul(m));
if (m_util.is_numeral(m->get_arg(0), _val)) {
SASSERT(m->get_num_args() == 2);
numeral val(_val);
SASSERT(!val.is_one());
unsigned r_id = mk_row();
if (reflection_enabled())
internalize_term_core(to_app(m->get_arg(0)));
theory_var v = internalize_mul_core(to_app(m->get_arg(1)));
add_row_entry<true>(r_id, val, v);
enode * e = mk_enode(m);
theory_var s = mk_var(e);
add_row_entry<false>(r_id, numeral::one(), s);
init_row(r_id);
return s;
}
else {
return internalize_mul_core(m);
}
}
template<typename Ext>
theory_var theory_arith<Ext>::mk_binary_op(app * n) {
SASSERT(n->get_num_args() == 2);
context & ctx = get_context();
if (ctx.e_internalized(n))
return expr2var(n);
ctx.internalize(n->get_arg(0), false);
ctx.internalize(n->get_arg(1), false);
enode * e = mk_enode(n);
return mk_var(e);
}
template<typename Ext>
theory_var theory_arith<Ext>::internalize_div(app * n) {
theory_var s = mk_binary_op(n);
context & ctx = get_context();
if (!ctx.relevancy())
mk_div_axiom(n->get_arg(0), n->get_arg(1));
return s;
}
template<typename Ext>
theory_var theory_arith<Ext>::internalize_idiv(app * n) {
theory_var s = mk_binary_op(n);
context & ctx = get_context();
app * mod = m_util.mk_mod(n->get_arg(0), n->get_arg(1));
ctx.internalize(mod, false);
if (ctx.relevancy())
ctx.add_relevancy_dependency(n, mod);
return s;
}
template<typename Ext>
theory_var theory_arith<Ext>::internalize_mod(app * n) {
TRACE("arith_mod", tout << "internalizing...\n" << mk_pp(n, get_manager()) << "\n";);
theory_var s = mk_binary_op(n);
context & ctx = get_context();
if (!ctx.relevancy())
mk_idiv_mod_axioms(n->get_arg(0), n->get_arg(1));
return s;
}
template<typename Ext>
theory_var theory_arith<Ext>::internalize_rem(app * n) {
theory_var s = mk_binary_op(n);
context & ctx = get_context();
if (!ctx.relevancy())
mk_rem_axiom(n->get_arg(0), n->get_arg(1));
return s;
}
template<typename Ext>
void theory_arith<Ext>::mk_axiom(expr * ante, expr * conseq) {
ast_manager & m = get_manager();
TRACE("arith_axiom", tout << mk_pp(ante, m) << "\n" << mk_pp(conseq, m) << "\n";);
context & ctx = get_context();
simplifier & s = ctx.get_simplifier();
expr_ref s_ante(m), s_conseq(m);
proof_ref pr(m);
s(ante, s_ante, pr);
ctx.internalize(s_ante, false);
literal l_ante = ctx.get_literal(s_ante);
s(conseq, s_conseq, pr);
ctx.internalize(s_conseq, false);
literal l_conseq = ctx.get_literal(s_conseq);
literal lits[2] = {l_ante, l_conseq};
ctx.mk_th_axiom(get_id(), 2, lits);
if (ctx.relevancy()) {
if (l_ante == false_literal) {
ctx.mark_as_relevant(l_conseq);
}
else {
// We must mark the antecedent as relevant, otherwise the
// core will not propagate it to the theory of arithmetic.
// In a previous version, we were not doing that.
// The core was assigning it to true, this assignment was inconsistent with
// the state of the theory of arithmetic, but the conflict was not detected
// because it was not propagated to this theory.
ctx.mark_as_relevant(l_ante);
ctx.add_rel_watch(~l_ante, s_conseq); // mark consequent as relevant if antecedent is false.
}
}
}
template<typename Ext>
void theory_arith<Ext>::mk_div_axiom(expr * p, expr * q) {
if (!m_util.is_zero(q)) {
ast_manager & m = get_manager();
TRACE("div_axiom_bug", tout << "expanding div_axiom for: " << mk_pp(p, m) << " / " << mk_pp(q, m) << "\n";);
expr * div = m_util.mk_div(p, q);
expr * zero = m_util.mk_numeral(rational(0), false);
expr_ref eqz(m), eq(m);
eqz = m.mk_eq(q, zero);
eq = m.mk_eq(m_util.mk_mul(q, div), p);
TRACE("div_axiom_bug", tout << "eqz: " << mk_pp(eqz, m) << "\neq: " << mk_pp(eq, m) << "\n";);
mk_axiom(eqz, eq);
}
}
template<typename Ext>
void theory_arith<Ext>::mk_idiv_mod_axioms(expr * dividend, expr * divisor) {
if (!m_util.is_zero(divisor)) {
// if divisor is zero, then idiv and mod are uninterpreted functions.
ast_manager & m = get_manager();
expr * div = m_util.mk_idiv(dividend, divisor);
expr * mod = m_util.mk_mod(dividend, divisor);
expr * zero = m_util.mk_numeral(rational(0), true);
expr * abs_divisor = m.mk_ite(m_util.mk_lt(divisor, zero), m_util.mk_sub(zero, divisor), divisor);
expr_ref eqz(m), eq(m), lower(m), upper(m);
eqz = m.mk_eq(divisor, zero);
eq = m.mk_eq(m_util.mk_add(m_util.mk_mul(divisor, div), mod), dividend);
lower = m_util.mk_le(zero, mod);
upper = m_util.mk_lt(mod, abs_divisor);
mk_axiom(eqz, eq);
mk_axiom(eqz, lower);
mk_axiom(eqz, upper);
rational k;
if (m_params.m_arith_enum_const_mod && m_util.is_numeral(divisor, k) &&
k.is_pos() && k < rational(512)) {
rational j(0);
#if 1
literal_buffer lits;
expr_ref mod_j(m);
context& ctx = get_context();
while(j < k) {
mod_j = m.mk_eq(mod, m_util.mk_numeral(j, true));
ctx.internalize(mod_j, false);
literal lit(ctx.get_literal(mod_j));
lits.push_back(lit);
ctx.mark_as_relevant(lit);
j += rational(1);
}
ctx.mk_th_axiom(get_id(), lits.size(), lits.begin());
#else
// performs slightly worse.
literal_buffer lits;
expr_ref mod_j(m), div_j(m), num_j(m), n_mod_j(m), n_div_j(m);
context& ctx = get_context();
while(j < k) {
num_j = m_util.mk_numeral(j, true);
mod_j = m.mk_eq(mod, num_j);
div_j = m.mk_eq(dividend, m_util.mk_add(m_util.mk_mul(div, divisor), num_j));
n_mod_j = m.mk_not(mod_j);
n_div_j = m.mk_not(div_j);
mk_axiom(n_mod_j, div_j);
mk_axiom(n_div_j, mod_j);
j += rational(1);
}
#endif
}
}
}
template<typename Ext>
void theory_arith<Ext>::mk_rem_axiom(expr * dividend, expr * divisor) {
if (!m_util.is_zero(divisor)) {
// if divisor is zero, then rem is an uninterpreted function.
ast_manager & m = get_manager();
expr * zero = m_util.mk_numeral(rational(0), true);
expr * rem = m_util.mk_rem(dividend, divisor);
expr * mod = m_util.mk_mod(dividend, divisor);
expr_ref dltz(m), eq1(m), eq2(m);
dltz = m_util.mk_lt(divisor, zero);
eq1 = m.mk_eq(rem, mod);
eq2 = m.mk_eq(rem, m_util.mk_sub(zero, mod));
// n < 0 || rem(a,n) = mod(a, n)
mk_axiom(dltz, eq1);
dltz = m.mk_not(dltz);
// !n < 0 || rem(a,n) = -mod(a, n)
mk_axiom(dltz, eq2);
}
}
//
// create the term: s := to_real(to_int(x)) - x
// add the bounds 0 <= s < 1
//
template<typename Ext>
void theory_arith<Ext>::mk_to_int_axiom(app * n) {
SASSERT(m_util.is_to_int(n));
ast_manager & m = get_manager();
expr* x = n->get_arg(0);
// to_int (to_real x) = x
if (m_util.is_to_real(x)) {
mk_axiom(m.mk_false(), m.mk_eq(to_app(x)->get_arg(0), n));
return;
}
expr* to_r = m_util.mk_to_real(n);
expr* lo = m_util.mk_le(to_r, x);
expr* hi = m_util.mk_lt(x, m_util.mk_add(to_r, m_util.mk_numeral(rational(1), false)));
mk_axiom(m.mk_false(), lo);
mk_axiom(m.mk_false(), hi);
}
template<typename Ext>
theory_var theory_arith<Ext>::internalize_to_int(app * n) {
SASSERT(n->get_num_args() == 1);
context & ctx = get_context();
if (ctx.e_internalized(n))
return expr2var(n);
/* theory_var arg = */ internalize_term_core(to_app(n->get_arg(0)));
enode * e = mk_enode(n);
theory_var r = mk_var(e);
if (!ctx.relevancy())
mk_to_int_axiom(n);
return r;
}
//
// Create the axiom (iff (is_int x) (= x (to_real (to_int x))))
//
template<typename Ext>
void theory_arith<Ext>::mk_is_int_axiom(app * n) {
SASSERT(m_util.is_is_int(n));
ast_manager & m = get_manager();
expr* x = n->get_arg(0);
expr* eq = m.mk_eq(m_util.mk_to_real(m_util.mk_to_int(x)), x);
mk_axiom(m.mk_not(n), eq);
mk_axiom(m.mk_not(eq), n);
}
template<typename Ext>
void theory_arith<Ext>::internalize_is_int(app * n) {
SASSERT(n->get_num_args() == 1);
context & ctx = get_context();
if (ctx.b_internalized(n))
return;
/* theory_var arg = */ internalize_term_core(to_app(n->get_arg(0)));
enode * e = mk_enode(n);
/* theory_var r = */ mk_var(e);
if (!ctx.relevancy())
mk_is_int_axiom(n);
}
// create the row: r - arg = 0
template<typename Ext>
theory_var theory_arith<Ext>::internalize_to_real(app * n) {
SASSERT(n->get_num_args() == 1);
context & ctx = get_context();
if (ctx.e_internalized(n))
return expr2var(n);
TRACE("to_real_bug", tout << "to-real\n" << mk_ismt2_pp(n, get_manager()) << "\n";);
theory_var arg = internalize_term_core(to_app(n->get_arg(0)));
// n may be internalized by the call above if n is of the form (to_real (to_int t))
// The internalizer for (to_int t) will create (to_real (to_int t)) and internalize it.
// This is a recurrent bug in Z3. TODO: I should create a queue of axioms that need to be asserted.
// This queue is processes only after we finish the internalization of the current assertion.
if (ctx.e_internalized(n))
return expr2var(n);
enode * e = mk_enode(n);
theory_var r = mk_var(e);
unsigned r_id = mk_row();
add_row_entry<true>(r_id, numeral(1), arg);
add_row_entry<false>(r_id, numeral(1), r);
init_row(r_id);
return r;
}
template<typename Ext>
theory_var theory_arith<Ext>::internalize_numeral(app * n) {
rational _val;
context & ctx = get_context();
bool flag = m_util.is_numeral(n, _val);
numeral val(_val);
SASSERT(flag);
SASSERT(!ctx.e_internalized(n));
enode * e = mk_enode(n);
// internalizer is marking enodes as interpreted whenever the associated ast is a value and a constant.
// e->mark_as_interpreted();
theory_var v = mk_var(e);
inf_numeral ival(val);
bound * l = alloc(bound, v, ival, B_LOWER, false);
bound * u = alloc(bound, v, ival, B_UPPER, false);
set_bound(l, false);
set_bound(u, true);
m_bounds_to_delete.push_back(l);
m_bounds_to_delete.push_back(u);
m_value[v] = ival;
return v;
}
/**
\brief Internalize the given term and return an alias for it.
Return null_theory_var if the term was not implemented by the theory yet.
*/
template<typename Ext>
theory_var theory_arith<Ext>::internalize_term_core(app * n) {
TRACE("arith_internalize_detail", tout << "internalize_term_core:\n" << mk_pp(n, get_manager()) << "\n";);
context & ctx = get_context();
if (ctx.e_internalized(n)) {
enode * e = ctx.get_enode(n);
if (is_attached_to_var(e))
return e->get_th_var(get_id());
}
SASSERT(!m_util.is_sub(n));
SASSERT(!m_util.is_uminus(n));
if (m_util.is_add(n))
return internalize_add(n);
else if (m_util.is_mul(n))
return internalize_mul(n);
else if (m_util.is_div(n))
return internalize_div(n);
else if (m_util.is_idiv(n))
return internalize_idiv(n);
else if (m_util.is_mod(n))
return internalize_mod(n);
else if (m_util.is_rem(n))
return internalize_rem(n);
else if (m_util.is_to_real(n))
return internalize_to_real(n);
else if (m_util.is_to_int(n))
return internalize_to_int(n);
else if (m_util.is_numeral(n))
return internalize_numeral(n);
if (m_util.is_power(n)) {
// unsupported
found_unsupported_op(n);
return mk_binary_op(n);
}
if (m_util.is_irrational_algebraic_numeral(n)) {
// unsupported
found_unsupported_op(n);
enode * e = mk_enode(n);
return mk_var(e);
}
else {
TRACE("arith_internalize_detail", tout << "before:\n" << mk_pp(n, get_manager()) << "\n";);
if (!ctx.e_internalized(n))
ctx.internalize(n, false);
TRACE("arith_internalize_detail", tout << "after:\n" << mk_pp(n, get_manager()) << "\n";);
enode * e = ctx.get_enode(n);
if (!is_attached_to_var(e))
return mk_var(e);
else
return e->get_th_var(get_id());
}
}
/**
\brief Create a new empty row. Return the new row id.
*/
template<typename Ext>
unsigned theory_arith<Ext>::mk_row() {
unsigned r;
if (m_dead_rows.empty()) {
r = m_rows.size();
m_rows.push_back(row());
}
else {
r = m_dead_rows.back();
m_dead_rows.pop_back();
}
m_in_to_check.assure_domain(r);
SASSERT(m_rows[r].size() == 0);
SASSERT(m_rows[r].num_entries() == 0);
return r;
}
/**
\brief Initialize a new row, the last monomial is going to be the owner of the row.
The last monomial must have coeff 1.
*/
template<typename Ext>
void theory_arith<Ext>::init_row(unsigned r_id) {
row & r = m_rows[r_id];
SASSERT(r.m_first_free_idx == -1);
SASSERT(r.size() != 0);
SASSERT(r.size() == r.num_entries());
SASSERT(r[r.size() - 1].m_coeff.is_one());
theory_var s = r[r.size() - 1].m_var;
r.m_base_var = s;
set_var_row(s, r_id);
TRACE("init_row_bug", tout << "before:\n"; display_row_info(tout, r););
if (lazy_pivoting_lvl() > 2) {
set_var_kind(s, QUASI_BASE);
normalize_quasi_base_row(r_id);
SASSERT(!has_var_kind(get_var_row(s), QUASI_BASE));
}
else {
normalize_base_row(r_id);
SASSERT(get_var_kind(s) == BASE);
SASSERT(!has_var_kind(get_var_row(s), BASE));
}
TRACE("init_row_bug", tout << "after:\n"; display_row_info(tout, r););
if (propagation_mode() != BP_NONE)
mark_row_for_bound_prop(r_id);
SASSERT(r.is_coeff_of(s, numeral::one()));
SASSERT(wf_row(r_id));
}
/**
\brief Collect variables in the given row that have the given kind,
but a different from the row main var (i.e., var that owns the row).
The inv of the coefficients is also stored in result
*/
template<typename Ext>
void theory_arith<Ext>::collect_vars(unsigned r_id, var_kind k, buffer<linear_monomial> & result) {
row & r = m_rows[r_id];
theory_var base = r.m_base_var;
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
unsigned idx = 0;
for (; it != end; ++it, ++idx) {
if (!it->is_dead() && get_var_kind(it->m_var) == k && it->m_var != base) {
numeral c = it->m_coeff;
c.neg();
result.push_back(linear_monomial(c, it->m_var));
}
}
}
/**
\brief Normalize row as a quasi base row, it does not contain quasi-base
variables different from r.m_base_var.
*/
template<typename Ext>
void theory_arith<Ext>::normalize_quasi_base_row(unsigned r_id) {
buffer<linear_monomial> to_add;
collect_vars(r_id, QUASI_BASE, to_add);
add_rows(r_id, to_add.size(), to_add.c_ptr());
SASSERT(!has_var_kind(r_id, QUASI_BASE));
}
/**
\brief Convert a quasi-base row into a base row.
*/
template<typename Ext>
void theory_arith<Ext>::quasi_base_row2base_row(unsigned r_id) {
TRACE("quasi_base_row2base_row", tout << "quasi_base_row2base_row...\n";);
buffer<linear_monomial> to_add;
collect_vars(r_id, BASE, to_add);
TRACE("quasi_base_bug_detail",
display_row_info(tout, r_id);
for (unsigned i = 0; i < to_add.size(); i++) {
theory_var v = to_add[i].m_var;
SASSERT(is_base(v));
SASSERT(!has_var_kind(get_var_row(v), BASE));
tout << "coeff: " << to_add[i].m_coeff << ", var: #" << get_enode(v)->get_owner_id() << "\n";
display_row_info(tout, get_var_row(v));
tout << "\n";
});
add_rows(r_id, to_add.size(), to_add.c_ptr());
theory_var s = m_rows[r_id].get_base_var();
set_var_kind(s, BASE);
inf_numeral tmp;
if (get_implied_old_value(s, tmp)) {
// This code is necessary because of the method
// restore_assignment. That is, the invariant
// valid_row_assignment() could be invalidated when
// restore_assignment is executed.
//
// Remark: The method restore_assignment will restore the
// old value of variables, and the value of s should be
// compatible with them.
//
// For example, consider the following scenario:
//
// 1) s is a quasi-base var, s depends on x, and value of x is v0
//
// 2) x is updated to v1, but the update does not affect s (s is a quasi-base var).
//
// 3) quasi_base_row2base_row is executed, and we compute the value of s using
// the current value of x (v1).
//
// 4) a conflict is detected, and the value of x is restored to v0.
//
// 5) if this branch is deleted, the row owned by s will not satisfy
// valid_row_assignment.
//
m_value[s] = tmp;
SASSERT(!m_in_update_trail_stack.contains(s));
save_value(s);
}
m_value[s] = get_implied_value(s);
TRACE("valid_row_assignment_bug", display_row_info(tout, r_id););
SASSERT(!has_var_kind(r_id, BASE));
SASSERT(!has_var_kind(r_id, QUASI_BASE));
SASSERT(valid_row_assignment(m_rows[r_id]));
}
/**
\brief Normalize row as a base row. A base row does not contain quasi-base and base
variables different from r.m_base_var.
*/
template<typename Ext>
void theory_arith<Ext>::normalize_base_row(unsigned r_id) {
if (lazy_pivoting_lvl() > 0)
normalize_quasi_base_row(r_id);
quasi_base_row2base_row(r_id);
}
template<typename Ext>
void theory_arith<Ext>::mk_clause(literal l1, literal l2, unsigned num_params, parameter * params) {
get_context().mk_th_axiom(get_id(), l1, l2, num_params, params);
}
template<typename Ext>
void theory_arith<Ext>::mk_clause(literal l1, literal l2, literal l3, unsigned num_params, parameter * params) {
get_context().mk_th_axiom(get_id(), l1, l2, l3, num_params, params);
}
template<typename Ext>
void theory_arith<Ext>::mk_bound_axioms(atom * a1) {
theory_var v = a1->get_var();
literal l1(a1->get_bool_var());
numeral const & k1(a1->get_k());
atom_kind kind1 = a1->get_atom_kind();
TRACE("mk_bound_axioms", tout << "making bound axioms for v" << v << " " << kind1 << " " << k1 << "\n";);
atoms & occs = m_var_occs[v];
typename atoms::iterator it = occs.begin();
typename atoms::iterator end = occs.end();
for (; it != end; ++it) {
atom * a2 = *it;
literal l2(a2->get_bool_var());
numeral const & k2 = a2->get_k();
atom_kind kind2 = a2->get_atom_kind();
SASSERT(k1 != k2 || kind1 != kind2);
SASSERT(a2->get_var() == v);
parameter coeffs[3] = { parameter(symbol("farkas")), parameter(rational(1)), parameter(rational(1)) };
if (kind1 == A_LOWER) {
if (kind2 == A_LOWER) {
// x >= k1, x >= k2
if (k1 >= k2) mk_clause(~l1, l2, 3, coeffs);
else mk_clause(~l2, l1, 3, coeffs);
}
else {
// x >= k1, x <= k2
if (k1 > k2) mk_clause(~l1, ~l2, 3, coeffs);
else mk_clause(l1, l2, 3, coeffs);
}
}
else {
if (kind2 == A_LOWER) {
// x <= k1, x >= k2
if (k1 < k2) mk_clause(~l1, ~l2, 3, coeffs);
else mk_clause(l1, l2, 3, coeffs);
}
else {
// x <= k1, x <= k2
if (k1 < k2) mk_clause(~l1, l2, 3, coeffs);
else mk_clause(~l2, l1, 3, coeffs);
}
}
}
}
template<typename Ext>
bool theory_arith<Ext>::internalize_atom(app * n, bool gate_ctx) {
TRACE("arith_internalize", tout << "internalising atom:\n" << mk_pp(n, this->get_manager()) << "\n";);
context & ctx = get_context();
SASSERT(m_util.is_le(n) || m_util.is_ge(n) || m_util.is_is_int(n));
SASSERT(!ctx.b_internalized(n));
atom_kind kind;
if (m_util.is_is_int(n)) {
internalize_is_int(n);
if (ctx.b_internalized(n)) {
TRACE("arith_internalize", tout << "term was re-internalized: #" << n->get_id() << "\n";);
return true;
}
bool_var bv = ctx.mk_bool_var(n);
ctx.set_var_theory(bv, get_id());
return true;
}
if (m_util.is_le(n))
kind = A_UPPER;
else
kind = A_LOWER;
app * lhs = to_app(n->get_arg(0));
app * rhs = to_app(n->get_arg(1));
SASSERT(m_util.is_numeral(rhs));
theory_var v = internalize_term_core(lhs);
if (v == null_theory_var) {
TRACE("arith_internalize", tout << "failed to internalize: #" << n->get_id() << "\n";);
return false;
}
if (ctx.b_internalized(n)) {
TRACE("arith_internalize", tout << "term was re-internalized: #" << n->get_id() << "\n";);
return true;
}
bool_var bv = ctx.mk_bool_var(n);
ctx.set_var_theory(bv, get_id());
rational _k;
m_util.is_numeral(rhs, _k);
numeral k(_k);
atom * a = alloc(atom, bv, v, k, kind);
mk_bound_axioms(a);
m_unassigned_atoms[v]++;
atoms & occs = m_var_occs[v];
occs.push_back(a);
m_atoms.push_back(a);
insert_bv2a(bv, a);
TRACE("arith_internalize", tout << "succeeded... v: " << v << " " << kind << " " << k << "\n";);
return true;
}
template<typename Ext>
bool theory_arith<Ext>::internalize_term(app * term) {
TRACE("arith_internalize", tout << "internalising term:\n" << mk_pp(term, this->get_manager()) << "\n";);
theory_var v = internalize_term_core(term);
TRACE("arith_internalize", tout << "theory_var: " << v << "\n";);
return v != null_theory_var;
}
template<typename Ext>
void theory_arith<Ext>::internalize_eq_eh(app * atom, bool_var v) {
if (m_params.m_arith_eager_eq_axioms) {
context & ctx = get_context();
app * lhs = to_app(atom->get_arg(0));
app * rhs = to_app(atom->get_arg(1));
enode * n1 = ctx.get_enode(lhs);
enode * n2 = ctx.get_enode(rhs);
// The expression atom may be a theory axiom. In this case, it may not be in simplified form.
// So, an atom such as (= a a) may occur. The procedure mk_axioms, expects n1 != n2.
// So, we should check it. It doesn't make sense to create an axiom for (= a a) in the arith_eq_adapter.
if (n1->get_th_var(get_id()) != null_theory_var &&
n2->get_th_var(get_id()) != null_theory_var &&
n1 != n2) {
TRACE("mk_axioms_bug", tout << mk_bounded_pp(atom, get_manager(), 5) << "\n";);
m_arith_eq_adapter.mk_axioms(n1, n2);
}
}
}
template<typename Ext>
void theory_arith<Ext>::apply_sort_cnstr(enode * n, sort * s) {
// do nothing...
}
template<typename Ext>
void theory_arith<Ext>::assign_eh(bool_var v, bool is_true) {
atom * a = get_bv2a(v);
if (!a) return;
SASSERT(get_context().get_assignment(a->get_bool_var()) != l_undef);
SASSERT((get_context().get_assignment(a->get_bool_var()) == l_true) == is_true);
a->assign_eh(is_true, get_epsilon(a->get_var()));
m_asserted_bounds.push_back(a);
}
template<typename Ext>
void theory_arith<Ext>::relevant_eh(app * n) {
TRACE("arith_relevant_eh", tout << "relevant_eh: " << mk_pp(n, get_manager()) << "\n";);
if (m_util.is_mod(n))
mk_idiv_mod_axioms(n->get_arg(0), n->get_arg(1));
else if (m_util.is_rem(n))
mk_rem_axiom(n->get_arg(0), n->get_arg(1));
else if (m_util.is_div(n))
mk_div_axiom(n->get_arg(0), n->get_arg(1));
else if (m_util.is_to_int(n))
mk_to_int_axiom(n);
else if (m_util.is_is_int(n))
mk_is_int_axiom(n);
}
template<typename Ext>
void theory_arith<Ext>::new_eq_eh(theory_var v1, theory_var v2) {
TRACE("arith_new_eq_eh", tout << "#" << get_enode(v1)->get_owner_id() << " = #" << get_enode(v2)->get_owner_id() << "\n";);
TRACE("arith_new_eq_eh_detail", tout << mk_pp(get_enode(v1)->get_owner(), get_manager()) << "\n" <<
mk_pp(get_enode(v2)->get_owner(), get_manager()) << "\n";);
enode * n1 = get_enode(v1);
if (!m_util.is_int(n1->get_owner()) &&
!m_util.is_real(n1->get_owner())) {
return;
}
if (m_params.m_arith_eq_bounds) {
enode * n2 = get_enode(v2);
SASSERT(n1->get_root() == n2->get_root());
if (m_util.is_numeral(n1->get_owner())) {
std::swap(v1, v2);
std::swap(n1, n2);
}
rational k;
bound * b1 = 0;
bound * b2 = 0;
if (m_util.is_numeral(n2->get_owner(), k)) {
inf_numeral val(k);
b1 = alloc(eq_bound, v1, val, B_LOWER, n1, n2);
b2 = alloc(eq_bound, v1, val, B_UPPER, n1, n2);
}
else {
if (n1->get_owner_id() > n2->get_owner_id())
std::swap(n1, n2);
sort * st = get_manager().get_sort(n1->get_owner());
app * minus_one = m_util.mk_numeral(rational::minus_one(), st);
app * s = m_util.mk_add(n1->get_owner(), m_util.mk_mul(minus_one, n2->get_owner()));
context & ctx = get_context();
ctx.internalize(s, false);
enode * e_s = ctx.get_enode(s);
ctx.mark_as_relevant(e_s);
SASSERT(is_attached_to_var(e_s));
theory_var v_s = e_s->get_th_var(get_id());
b1 = alloc(eq_bound, v_s, inf_numeral::zero(), B_LOWER, n1, n2);
b2 = alloc(eq_bound, v_s, inf_numeral::zero(), B_UPPER, n1, n2);
}
m_bounds_to_delete.push_back(b1);
m_bounds_to_delete.push_back(b2);
m_asserted_bounds.push_back(b1);
m_asserted_bounds.push_back(b2);
}
else {
m_arith_eq_adapter.new_eq_eh(v1, v2);
}
}
template<typename Ext>
bool theory_arith<Ext>::use_diseqs() const {
return true;
}
template<typename Ext>
void theory_arith<Ext>::new_diseq_eh(theory_var v1, theory_var v2) {
TRACE("arith_new_diseq_eh", tout << mk_bounded_pp(get_enode(v1)->get_owner(), get_manager()) << "\n" <<
mk_bounded_pp(get_enode(v2)->get_owner(), get_manager()) << "\n";);
m_stats.m_assert_diseq++;
m_arith_eq_adapter.new_diseq_eh(v1, v2);
}
template<typename Ext>
void theory_arith<Ext>::restart_eh() {
m_arith_eq_adapter.restart_eh();
}
template<typename Ext>
void theory_arith<Ext>::init_search_eh() {
TRACE("arith_init_search", display(tout););
m_num_conflicts = 0;
m_branch_cut_counter = 0;
m_eager_gcd = m_params.m_arith_eager_gcd;
if (lazy_pivoting_lvl() == 1)
elim_quasi_base_rows();
move_unconstrained_to_base();
m_arith_eq_adapter.init_search_eh();
m_final_check_idx = 0;
m_nl_gb_exhausted = false;
m_nl_strategy_idx = 0;
}
template<typename Ext>
final_check_status theory_arith<Ext>::final_check_core() {
unsigned old_idx = m_final_check_idx;
final_check_status result = FC_DONE;
final_check_status ok;
do {
TRACE("final_check_arith", tout << "m_final_check_idx: " << m_final_check_idx << ", result: " << result << "\n";);
switch (m_final_check_idx) {
case 0:
ok = check_int_feasibility();
TRACE("final_check_arith", tout << "check_int_feasibility(), ok: " << ok << "\n";);
break;
case 1:
if (assume_eqs_core())
ok = FC_CONTINUE;
else
ok = FC_DONE;
TRACE("final_check_arith", tout << "assume_eqs(), ok: " << ok << "\n";);
break;
default:
ok = process_non_linear();
TRACE("final_check_arith", tout << "non_linear(), ok: " << ok << "\n";);
break;
}
m_final_check_idx = (m_final_check_idx + 1) % 3;
switch (ok) {
case FC_DONE:
break;
case FC_GIVEUP:
result = FC_GIVEUP;
break;
case FC_CONTINUE:
TRACE("final_check_arith", tout << "continue arith...\n";);
return FC_CONTINUE;
}
}
while (m_final_check_idx != old_idx);
if (result == FC_DONE && m_found_unsupported_op)
result = FC_GIVEUP;
return result;
}
template<typename Ext>
final_check_status theory_arith<Ext>::final_check_eh() {
TRACE("arith_eq_adapter_info", m_arith_eq_adapter.display_already_processed(tout););
TRACE("arith_final_check", display(tout););
#if 0
if (true /*m_params.m_smtlib_dump_lemmas*/) {
literal_buffer tmp;
for (unsigned i = 0; i < m_asserted_qhead; i++) {
bound * b = m_asserted_bounds[i];
if (b->is_atom()) {
atom* a = static_cast<atom*>(b);
bool_var bv = a->get_bool_var();
lbool is_true = get_context().get_assignment(bv);
if (is_true == l_true) {
tmp.push_back(literal(bv));
}
else if (is_true == l_false) {
tmp.push_back(~literal(bv));
}
}
}
get_context().display_lemma_as_smt_problem(tmp.size(), tmp.c_ptr(), false_literal, "QF_LIA");
}
#endif
if (!propagate_core())
return FC_CONTINUE;
if (delayed_assume_eqs())
return FC_CONTINUE;
get_context().push_trail(value_trail<context, unsigned>(m_final_check_idx));
m_liberal_final_check = true;
m_changed_assignment = false;
final_check_status result = final_check_core();
if (result != FC_DONE)
return result;
if (!m_changed_assignment)
return FC_DONE;
m_liberal_final_check = false;
m_changed_assignment = false;
result = final_check_core();
TRACE("final_check_arith", tout << "result: " << result << "\n";);
return result;
}
template<typename Ext>
bool theory_arith<Ext>::can_propagate() {
return process_atoms() && m_asserted_qhead < m_asserted_bounds.size();
}
template<typename Ext>
void theory_arith<Ext>::propagate() {
TRACE("arith_propagate", tout << "propagate\n"; display(tout););
if (!process_atoms())
return;
propagate_core();
}
template<typename Ext>
bool theory_arith<Ext>::propagate_core() {
CASSERT("arith", wf_rows());
CASSERT("arith", wf_columns());
CASSERT("arith", valid_row_assignment());
propagate_linear_monomials();
while (m_asserted_qhead < m_asserted_bounds.size()) {
bound * b = m_asserted_bounds[m_asserted_qhead];
m_asserted_qhead++;
if (!assert_bound(b)) {
failed();
return false;
}
}
if (!make_feasible()) {
failed();
return false;
}
CASSERT("arith", satisfy_bounds());
discard_update_trail();
SASSERT(m_update_trail_stack.empty());
propagate_bounds();
SASSERT(m_asserted_qhead == m_asserted_bounds.size());
SASSERT(m_update_trail_stack.empty());
CASSERT("arith", wf_rows());
CASSERT("arith", wf_columns());
CASSERT("arith", valid_row_assignment());
CASSERT("arith", satisfy_bounds());
return true;
}
template<typename Ext>
void theory_arith<Ext>::failed() {
restore_assignment();
m_to_patch.reset();
m_to_check.reset();
m_in_to_check.reset();
}
template<typename Ext>
void theory_arith<Ext>::flush_eh() {
std::for_each(m_atoms.begin(), m_atoms.end(), delete_proc<atom>());
m_atoms.reset();
std::for_each(m_bounds_to_delete.begin(), m_bounds_to_delete.end(), delete_proc<bound>());
m_bounds_to_delete.reset();
}
template<typename Ext>
void theory_arith<Ext>::reset_eh() {
m_stats.reset();
m_rows .reset();
m_arith_eq_adapter .reset_eh();
m_dead_rows .reset();
m_columns .reset();
m_data .reset();
m_value .reset();
m_old_value .reset();
m_bounds[0] .reset();
m_bounds[1] .reset();
m_var_occs .reset();
m_unassigned_atoms .reset();
m_bool_var2atom .reset();
m_var_pos .reset();
std::for_each(m_atoms.begin(), m_atoms.end(), delete_proc<atom>());
m_atoms .reset();
std::for_each(m_bounds_to_delete.begin(), m_bounds_to_delete.end(), delete_proc<bound>());
m_bounds_to_delete.reset();
m_asserted_bounds .reset();
m_asserted_qhead = 0;
m_to_patch .reset();
m_left_basis .reset();
m_blands_rule = false;
m_update_trail_stack .reset();
m_in_update_trail_stack .reset();
m_to_check .reset();
m_in_to_check .reset();
m_num_conflicts = 0;
m_bound_trail .reset();
m_unassigned_atoms_trail .reset();
m_scopes .reset();
m_nl_monomials .reset();
m_nl_propagated .reset();
m_nl_rounds = 0;
m_nl_gb_exhausted = false;
m_nl_strategy_idx = 0;
theory::reset_eh();
}
template<typename Ext>
bool theory_arith<Ext>::validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const {
return true;
}
/**
\brief Compute the value of a base or quasi-base variable using
the value of the dependent variables.
*/
template<typename Ext>
typename theory_arith<Ext>::inf_numeral const & theory_arith<Ext>::get_implied_value(theory_var v) const {
SASSERT(is_quasi_base(v) || is_base(v));
inf_numeral & sum = const_cast<theory_arith<Ext> *>(this)->m_tmp;
sum.reset();
unsigned r_id = get_var_row(v);
row const & r = m_rows[r_id];
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it) {
if (!it->is_dead() && it->m_var != v) {
SASSERT(!is_quasi_base(it->m_var));
SASSERT(get_value(it->m_var) == m_value[it->m_var]);
sum += it->m_coeff * get_value(it->m_var);
}
}
sum.neg();
return sum;
}
/**
\brief Compute the value of a base or quasi-base variable using
the old value of the dependent variables. By old, we mean the
value of the variable in the beginning of propagate(). Store the
result in 'result'.
Return true if the old value is different from the current value.
*/
template<typename Ext>
bool theory_arith<Ext>::get_implied_old_value(theory_var v, inf_numeral & result) const {
SASSERT(is_quasi_base(v) || is_base(v));
bool is_diff = false;
result.reset();
unsigned r_id = get_var_row(v);
row const & r = m_rows[r_id];
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it) {
if (!it->is_dead() && it->m_var != v) {
theory_var v2 = it->m_var;
SASSERT(!is_quasi_base(v2));
SASSERT(get_value(v2) == m_value[v2]);
if (m_in_update_trail_stack.contains(v2)) {
result += it->m_coeff * m_old_value[v2];
is_diff = true;
}
else {
result += it->m_coeff * m_value[v2];
}
}
}
result.neg();
return is_diff;
}
template<typename Ext>
theory_arith<Ext>::theory_arith(ast_manager & m, theory_arith_params & params):
theory(m.get_family_id("arith")),
m_params(params),
m_util(m),
m_arith_eq_solver(m),
m_found_unsupported_op(false),
m_arith_eq_adapter(*this, params, m_util),
m_asserted_qhead(0),
m_to_patch(1024),
m_blands_rule(false),
m_random(params.m_arith_random_seed),
m_num_conflicts(0),
m_branch_cut_counter(0),
m_eager_gcd(m_params.m_arith_eager_gcd),
m_final_check_idx(0),
m_var_value_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)),
m_liberal_final_check(true),
m_changed_assignment(false),
m_assume_eq_head(0),
m_nl_rounds(0),
m_nl_gb_exhausted(false),
m_nl_new_exprs(m) {
}
template<typename Ext>
theory_arith<Ext>::~theory_arith() {
}
template<typename Ext>
void theory_arith<Ext>::setup() {
m_random.set_seed(m_params.m_arith_random_seed);
theory::setup();
}
// -----------------------------------
//
// Add Row
//
// -----------------------------------
/**
\brief Set: row1 <- row1 + coeff * row2
*/
template<typename Ext>
void theory_arith<Ext>::add_row(unsigned rid1, const numeral & coeff, unsigned rid2, bool apply_gcd_test) {
m_stats.m_add_rows++;
if (propagation_mode() != BP_NONE)
mark_row_for_bound_prop(rid1);
row & r1 = m_rows[rid1];
row & r2 = m_rows[rid2];
CASSERT("row_assignment_bug", valid_row_assignment(r1));
CASSERT("row_assignment_bug", valid_row_assignment(r2));
r1.compress_if_needed(m_columns);
r2.compress_if_needed(m_columns);
CASSERT("arith", check_null_var_pos());
r1.save_var_pos(m_var_pos);
//
// loop over variables in row2,
// add terms in row2 to row1.
//
#define ADD_ROW(_SET_COEFF_, _ADD_COEFF_) \
typename vector<row_entry>::const_iterator it = r2.begin_entries(); \
typename vector<row_entry>::const_iterator end = r2.end_entries(); \
for (; it != end; ++it) { \
if (!it->is_dead()) { \
theory_var v = it->m_var; \
int pos = m_var_pos[v]; \
if (pos == -1) { \
/* variable v is not in row1 */ \
int row_idx; \
row_entry & r_entry = r1.add_row_entry(row_idx); \
r_entry.m_var = v; \
_SET_COEFF_; \
column & c = m_columns[v]; \
int col_idx; \
col_entry & c_entry = c.add_col_entry(col_idx); \
r_entry.m_col_idx = col_idx; \
c_entry.m_row_id = rid1; \
c_entry.m_row_idx = row_idx; \
} \
else { \
/* variable v is in row1 */ \
row_entry & r_entry = r1[pos]; \
SASSERT(r_entry.m_var == v); \
_ADD_COEFF_; \
if (r_entry.m_coeff.is_zero()) { \
int col_idx = r_entry.m_col_idx; \
r1.del_row_entry(pos); \
column & c = m_columns[v]; \
c.del_col_entry(col_idx); \
} \
m_var_pos[v] = -1; \
} \
} \
} ((void) 0)
if (coeff.is_one()) {
ADD_ROW(r_entry.m_coeff = it->m_coeff,
r_entry.m_coeff += it->m_coeff);
}
else if (coeff.is_minus_one()) {
ADD_ROW(r_entry.m_coeff = it->m_coeff; r_entry.m_coeff.neg(),
r_entry.m_coeff -= it->m_coeff);
}
else {
ADD_ROW(r_entry.m_coeff = it->m_coeff; r_entry.m_coeff *= coeff,
r_entry.m_coeff += it->m_coeff * coeff);
}
r1.reset_var_pos(m_var_pos);
CASSERT("arith", check_null_var_pos());
CASSERT("row_assignment_bug", valid_row_assignment(r1));
CASSERT("row_assignment_bug", valid_row_assignment(r2));
if (apply_gcd_test) {
theory_var v = r1.get_base_var();
if (is_int(v) && !get_value(v).is_int())
gcd_test(r1);
}
}
/**
\brief Set r1 <- r1 + a_xs[0].m_coeff * get_var_row(a_xs[0].m_var) + ... + a_xs[0].m_coeff * get_var_row(a_xs[sz-1].m_var)
\pre For all i in [0..sz-1]. not is_non_base(a_xs[i])
*/
template<typename Ext>
void theory_arith<Ext>::add_rows(unsigned r1, unsigned sz, linear_monomial * a_xs) {
if (sz == 0)
return;
for (unsigned i = 0; i < sz; i++) {
linear_monomial & m = a_xs[i];
numeral c = m.m_coeff;
theory_var v = m.m_var;
SASSERT(!is_non_base(v));
add_row(r1, c, get_var_row(v), false);
}
}
// -----------------------------------
//
// Assignment management
//
// -----------------------------------
template<typename Ext>
void theory_arith<Ext>::save_value(theory_var v) {
SASSERT(!is_quasi_base(v));
if (!m_in_update_trail_stack.contains(v)) {
m_in_update_trail_stack.insert(v);
SASSERT(m_value[v] == get_value(v));
m_old_value[v] = m_value[v];
m_update_trail_stack.push_back(v);
TRACE("save_value", tout << "v" << v << " = " << get_value(v) << "\n";);
}
m_changed_assignment = true;
}
template<typename Ext>
void theory_arith<Ext>::discard_update_trail() {
m_in_update_trail_stack.reset();
m_update_trail_stack.reset();
}
template<typename Ext>
void theory_arith<Ext>::restore_assignment() {
CASSERT("arith", valid_row_assignment());
TRACE("restore_assignment_bug", tout << "START restore_assignment...\n";);
typename svector<unsigned>::iterator it = m_update_trail_stack.begin();
typename svector<unsigned>::iterator end = m_update_trail_stack.end();
for(; it != end; ++it) {
theory_var v = *it;
TRACE("restore_assignment_bug", tout << "restoring v" << v << " <- " << m_old_value[v] << "\n";);
SASSERT(!is_quasi_base(v));
SASSERT(m_in_update_trail_stack.contains(v));
m_value[v] = m_old_value[v];
}
m_update_trail_stack.reset();
m_in_update_trail_stack.reset();
CASSERT("arith", valid_row_assignment());
}
/**
\brief m_value[v] += delta
*/
template<typename Ext>
void theory_arith<Ext>::update_value_core(theory_var v, inf_numeral const & delta) {
save_value(v);
m_value[v] += delta;
if (is_base(v) && !m_to_patch.contains(v) && (below_lower(v) || above_upper(v))) {
m_to_patch.insert(v);
}
}
/**
\brief m_value[v] += delta, and update dependent (non-base) variables.
*/
template<typename Ext>
void theory_arith<Ext>::update_value(theory_var v, inf_numeral const & delta) {
update_value_core(v, delta);
column & c = m_columns[v];
c.compress_if_needed(m_rows);
inf_numeral delta2;
typename svector<col_entry>::const_iterator it = c.begin_entries();
typename svector<col_entry>::const_iterator end = c.end_entries();
for (; it != end; ++it) {
if (!it->is_dead()) {
row & r = m_rows[it->m_row_id];
theory_var s = r.get_base_var();
if (s != null_theory_var && !is_quasi_base(s)) {
delta2 = delta;
delta2 *= r[it->m_row_idx].m_coeff;
delta2.neg();
update_value_core(s, delta2);
}
}
}
}
// -----------------------------------
//
// Pivoting
//
// -----------------------------------
/**
\brief Make x_j the new base variable for row of x_i
x_j is assumed to have coefficient a_ij.
Let row_id = m_base_var[x_i]
rows[row_id] <- rows[row_id] / a_ij
rows[other] <- row[other] - rows[row_id] * a_kj
Where a_kj is the coefficient of x_j in row[other]
*/
template<typename Ext>
template<bool Lazy>
void theory_arith<Ext>::pivot(theory_var x_i, theory_var x_j, numeral const & a_ij, bool apply_gcd_test) {
TRACE("arith_pivot", tout << "pivoting: v" << x_i << ", v" << x_j << "\n";);
m_stats.m_pivots++;
SASSERT(is_base(x_i) || is_quasi_base(x_i));
SASSERT(x_i != x_j);
int r_id = get_var_row(x_i);
row & r = m_rows[r_id];
SASSERT(r.is_coeff_of(x_j, a_ij));
#define DIVIDE_ROW(_ADJUST_COEFF_) \
typename vector<row_entry>::iterator it = r.begin_entries(); \
typename vector<row_entry>::iterator end = r.end_entries(); \
for (; it != end; ++it) { \
if (!it->is_dead()) { \
_ADJUST_COEFF_; \
} \
} ((void) 0)
if (a_ij.is_minus_one()) {
DIVIDE_ROW(it->m_coeff.neg());
}
else if (!a_ij.is_one()) {
numeral tmp = a_ij;
DIVIDE_ROW(it->m_coeff /= tmp);
}
set_var_row(x_i, -1);
set_var_row(x_j, r_id);
SASSERT(r.m_base_var == x_i);
r.m_base_var = x_j;
set_var_kind(x_i, NON_BASE);
set_var_kind(x_j, BASE);
eliminate<Lazy>(x_j, apply_gcd_test);
CASSERT("arith", wf_rows());
CASSERT("arith", wf_columns());
CASSERT("arith", valid_row_assignment());
TRACE("arith_pivot", tout << "after pivoting:\n";
display(tout););
TRACE("pivot_shape", display_rows_shape(tout););
TRACE("pivot_stats", display_rows_stats(tout););
TRACE_CODE({
static unsigned val = 0;
val ++;
if (val % 100 == 0) {
TRACE("pivot_bignums", display_rows_bignums(tout););
}});
}
/**
\brief Eliminate x_i from the rows different from get_var_row(x_i)
If Lazy = true, then x_i is only eliminated from base rows.
*/
template<typename Ext>
template<bool Lazy>
void theory_arith<Ext>::eliminate(theory_var x_i, bool apply_gcd_test) {
SASSERT(is_base(x_i) || is_quasi_base(x_i));
unsigned r_id = get_var_row(x_i);
column & c = m_columns[x_i];
numeral a_kj;
typename svector<col_entry>::iterator it = c.begin_entries();
typename svector<col_entry>::iterator end = c.end_entries();
int i = 0;
int s_pos = -1;
for (; it != end; ++it, ++i) {
if (!it->is_dead()) {
if (it->m_row_id != static_cast<int>(r_id)) {
row & r2 = m_rows[it->m_row_id];
theory_var s2 = r2.m_base_var;
if (s2 != null_theory_var && (!Lazy || is_base(s2))) {
a_kj = r2[it->m_row_idx].m_coeff;
a_kj.neg();
add_row(it->m_row_id, a_kj, r_id, apply_gcd_test);
}
}
else {
s_pos = i;
}
}
}
CTRACE("eliminate", !Lazy && c.size() != 1,
tout << "eliminating v" << x_i << ", Lazy: " << Lazy << ", c.size: " << c.size() << "\n";
display(tout););
SASSERT(Lazy || c.size() == 1);
if (c.size() == 1) {
// When lazy pivoting is used, then after pivoting c may
// not be a singleton
c.compress_singleton(m_rows, s_pos);
}
}
/**
\brief Set x_j to be the new base variable of row
owned by x_i
a_ij - coefficient of x_j in the row owned by x_i
x_i_new_val - new value of x_i
*/
template<typename Ext>
void theory_arith<Ext>::update_and_pivot(theory_var x_i, theory_var x_j, numeral const & a_ij, inf_numeral const & x_i_new_val) {
CASSERT("arith", valid_row_assignment());
TRACE("update_and_pivot_bug_detail", display(tout););
SASSERT(is_base(x_i));
inf_numeral theta = m_value[x_i];
TRACE("update_and_pivot_bug", tout << "theta 1) " << theta << " " << x_i_new_val << "\n";);
theta -= x_i_new_val;
TRACE("update_and_pivot_bug", tout << "theta 2) " << theta << " " << a_ij << "\n";);
theta /= a_ij;
TRACE("update_and_pivot_bug", tout << "theta 3) " << theta << "\n";);
update_value(x_j, theta);
CTRACE("arith", get_value(x_i) != x_i_new_val,
tout << "x_i: " << x_i << ", x_j: " << x_j << ", a_ij: " << a_ij << ", x_i_new_val: " << x_i_new_val << "\n";
tout << "new val: " << get_value(x_i) << ", theta: " << theta << "\n";
display(tout););
SASSERT(get_value(x_i) == x_i_new_val);
if (!m_to_patch.contains(x_j) && (below_lower(x_j) || above_upper(x_j)))
m_to_patch.insert(x_j);
pivot<true>(x_i, x_j, a_ij, m_eager_gcd);
CASSERT("arith", valid_row_assignment());
}
/**
\brief Return the number of base variables that are non free and are v dependent.
The function adds 1 to the result if v is non free.
The function returns with a partial result r if r > best_so_far.
This function is used to select the pivot variable.
*/
template<typename Ext>
int theory_arith<Ext>::get_num_non_free_dep_vars(theory_var v, int best_so_far) {
int result = is_non_free(v);
column & c = m_columns[v];
typename svector<col_entry>::const_iterator it = c.begin_entries();
typename svector<col_entry>::const_iterator end = c.end_entries();
for (; it != end; ++it) {
if (!it->is_dead()) {
row & r = m_rows[it->m_row_id];
theory_var s = r.get_base_var();
if (s != null_theory_var && is_base(s)) {
result += is_non_free(s);
if (result > best_so_far)
return result;
}
}
}
return result;
}
/**
\brief Using Bland's rule, select a variable x_j in the row r defining the base var x_i,
s.t. x_j can be used to patch the error in x_i. Return null_theory_var
if there is no x_j. Otherwise, return x_j and store its coefficient
in out_a_ij.
*/
template<typename Ext>
theory_var theory_arith<Ext>::select_blands_pivot_core(theory_var x_i, bool is_below, numeral & out_a_ij) {
SASSERT(is_base(x_i));
theory_var max = get_num_vars();
theory_var result = max;
row const & r = m_rows[get_var_row(x_i)];
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it) {
if (!it->is_dead()) {
theory_var x_j = it->m_var;
numeral const & a_ij = it->m_coeff;
bool is_neg = is_below ? a_ij.is_neg() : a_ij.is_pos();
bool is_pos = !is_neg;
if (x_i != x_j && ((is_pos && above_lower(x_j)) || (is_neg && below_upper(x_j)))) {
SASSERT(is_non_base(x_j));
if (x_j < result) {
result = x_j;
out_a_ij = a_ij;
}
}
}
}
return result < max ? result : null_theory_var;
}
/**
\brief Select a variable x_j in the row r defining the base var x_i,
s.t. x_j can be used to patch the error in x_i. Return null_theory_var
if there is no x_j. Otherwise, return x_j and store its coefficient
in out_a_ij.
The argument is_below is true (false) if x_i is below its lower
bound (above its upper bound).
*/
template<typename Ext>
template<bool is_below>
theory_var theory_arith<Ext>::select_pivot_core(theory_var x_i, numeral & out_a_ij) {
SASSERT(is_base(x_i));
theory_var max = get_num_vars();
theory_var result = max;
row const & r = m_rows[get_var_row(x_i)];
int n;
int best_col_sz = INT_MAX;
int best_so_far = INT_MAX;
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it) {
if (!it->is_dead()) {
theory_var x_j = it->m_var;
numeral const & a_ij = it->m_coeff;
bool is_neg = is_below ? a_ij.is_neg() : a_ij.is_pos();
bool is_pos = !is_neg;
if (x_i != x_j && ((is_pos && above_lower(x_j)) || (is_neg && below_upper(x_j)))) {
int num = get_num_non_free_dep_vars(x_j, best_so_far);
int col_sz = m_columns[x_j].size();
if (num < best_so_far || (num == best_so_far && col_sz < best_col_sz)) {
result = x_j;
out_a_ij = a_ij;
best_so_far = num;
best_col_sz = col_sz;
n = 1;
}
else if (num == best_so_far && col_sz == best_col_sz) {
n++;
if (m_random()%n == 0) {
result = x_j;
out_a_ij = a_ij;
}
}
}
}
}
return result < max ? result : null_theory_var;
}
/**
\brief Wrapper for select_blands_pivot_core and select_pivot_core
*/
template<typename Ext>
theory_var theory_arith<Ext>::select_pivot(theory_var x_i, bool is_below, numeral & out_a_ij) {
TRACE("select_pivot", tout << "m_blands_rule: " << m_blands_rule << " v" << x_i << "\n";);
CTRACE("select_pivot_info", x_i > 500, get_context().display(tout););
if (m_blands_rule)
return select_blands_pivot_core(x_i, is_below, out_a_ij);
else if (is_below)
return select_pivot_core<true>(x_i, out_a_ij);
else
return select_pivot_core<false>(x_i, out_a_ij);
}
// -----------------------------------
//
// Make feasible
//
// -----------------------------------
/**
\brief Make the given variable feasible. This method assumes
that x_i is a base var. Return false if it was not possible to
make x_i feasible.
*/
template<typename Ext>
bool theory_arith<Ext>::make_var_feasible(theory_var x_i) {
CTRACE("arith_bug", !is_base(x_i),
tout << "x_i: " << x_i << ", below_lower(x_i): " << below_lower(x_i) <<
", above_upper(x_i): " << above_upper(x_i) << "\n"; display(tout););
SASSERT(is_base(x_i));
bool is_below;
if (below_lower(x_i)) {
is_below = true;
}
else if (above_upper(x_i)) {
is_below = false;
}
else {
// x_i is already feasible
return true;
}
TRACE("make_var_feasible", display_row(tout, get_var_row(x_i), false););
numeral a_ij;
theory_var x_j = select_pivot(x_i, is_below, a_ij);
if (x_j != null_theory_var) {
SASSERT(is_base(x_i));
TRACE("pivot_bug", display_row_info(tout, get_var_row(x_i)););
update_and_pivot(x_i, x_j, a_ij, get_bound(x_i, !is_below)->get_value());
return true;
}
else {
// conflict detected
sign_row_conflict(x_i, is_below);
return false;
}
}
template<typename Ext>
theory_var theory_arith<Ext>::select_lg_error_var(bool least) {
TRACE("select_pivot", tout << "starting...\n";);
theory_var best = null_theory_var;
inf_numeral best_error;
inf_numeral curr_error;
typename var_heap::iterator it = m_to_patch.begin();
typename var_heap::iterator end = m_to_patch.end();
for (; it != end; ++it) {
theory_var v = *it;
if (below_lower(v))
curr_error = lower(v)->get_value() - get_value(v);
else if (above_upper(v))
curr_error = get_value(v) - upper(v)->get_value();
else
continue;
SASSERT(curr_error > inf_numeral(0));
if (best == null_theory_var || (!least && curr_error > best_error) || (least && curr_error < best_error)) {
TRACE("select_pivot", tout << "best: " << best << " v: " << v
<< ", best_error: " << best_error << ", curr_error: " << curr_error << "\n";);
best = v;
best_error = curr_error;
}
}
if (best == null_theory_var)
m_to_patch.clear(); // all variables are satisfied
else
m_to_patch.erase(best);
return best;
}
template<typename Ext>
theory_var theory_arith<Ext>::select_smallest_var() {
return m_to_patch.erase_min();
}
template<typename Ext>
theory_var theory_arith<Ext>::select_var_to_fix() {
if (m_blands_rule)
return select_smallest_var();
switch (m_params.m_arith_pivot_strategy) {
case ARITH_PIVOT_GREATEST_ERROR:
return select_greatest_error_var();
case ARITH_PIVOT_LEAST_ERROR:
return select_least_error_var();
default:
return select_smallest_var();
}
}
/**
\brief Return true if it was possible to patch all variables in m_to_patch.
*/
template<typename Ext>
bool theory_arith<Ext>::make_feasible() {
TRACE("arith_make_feasible", tout << "make_feasible\n"; display(tout););
CASSERT("arith", wf_rows());
CASSERT("arith", wf_columns());
CASSERT("arith", valid_row_assignment());
m_left_basis.reset();
m_blands_rule = false;
unsigned num_repeated = 0;
while (!m_to_patch.empty()) {
theory_var v = select_var_to_fix();
if (v == null_theory_var) {
// all variables were satisfied...
SASSERT(m_to_patch.empty());
break;
}
if (!m_blands_rule) {
if (m_left_basis.contains(v)) {
num_repeated++;
if (num_repeated > blands_rule_threshold()) {
TRACE("blands_rule", tout << "using blands rule, " << num_repeated << "\n";);
// std::cerr << "BLANDS RULE...\n";
m_blands_rule = true;
}
}
else {
m_left_basis.insert(v);
}
}
if (!make_var_feasible(v)) {
TRACE("arith_make_feasible", tout << "make_feasible: unsat\n"; display(tout););
return false;
}
TRACE("arith_make_feasible_detail", display(tout););
}
TRACE("arith_make_feasible", tout << "make_feasible: sat\n"; display(tout););
CASSERT("arith", wf_rows());
CASSERT("arith", wf_columns());
CASSERT("arith", valid_row_assignment());
return true;
}
/**
\brief A row is in a sign inconsistency when it is implying a
lower (upper) bound on x_i, which is above (below) its known
upper (lower) bound.
*/
template<typename Ext>
void theory_arith<Ext>::sign_row_conflict(theory_var x_i, bool is_below) {
inf_numeral delta;
row const & r = m_rows[get_var_row(x_i)];
int idx = r.get_idx_of(x_i);
SASSERT(idx >= 0);
bound * b = 0;
// Remark:
// if x_i is an integer variable, then delta can be negative:
//
// Example: x_i <= 0 get_value(x_i) = 1/4
//
// The value is above the upper bound.
// Since x_i is an integer, get_epsilon(x_i) = 1, and delta = -3/4
if (is_below) {
SASSERT(below_lower(x_i));
b = lower(x_i);
if (relax_bounds()) {
delta = b->get_value();
delta -= get_value(x_i);
delta -= get_epsilon(x_i);
if (delta.is_neg())
delta.reset();
}
}
else {
SASSERT(above_upper(x_i));
b = upper(x_i);
if (relax_bounds()) {
delta = get_value(x_i);
delta -= b->get_value();
delta -= get_epsilon(x_i);
if (delta.is_neg())
delta.reset();
}
}
TRACE("sign_row_conflict", tout << "v" << x_i << " is_below: " << is_below << " delta: " << delta << "\n"; display_var(tout, x_i);
tout << "is_below_lower: " << below_lower(x_i) << ", is_above_upper: " << above_upper(x_i) << "\n";);
antecedents& ante = get_antecedents();
explain_bound(r, idx, !is_below, delta, ante);
b->push_justification(ante, numeral(1));
set_conflict(ante.lits().size(), ante.lits().c_ptr(),
ante.eqs().size(), ante.eqs().c_ptr(), ante, is_int(x_i), "farkas");
// display_bounds_in_smtlib();
}
// -----------------------------------
//
// Assert bound
//
// -----------------------------------
/**
\brief Assert x >= k, return false if a conflict is detected.
*/
template<typename Ext>
bool theory_arith<Ext>::assert_lower(bound * b) {
SASSERT(b->get_bound_kind() == B_LOWER);
theory_var v = b->get_var();
inf_numeral const & k = b->get_value();
bound * u = upper(v);
bound * l = lower(v);
if (u && k > u->get_value()) {
sign_bound_conflict(u, b);
return false;
}
if (l && k <= l->get_value()) {
// redundant
return true;
}
switch (get_var_kind(v)) {
case QUASI_BASE:
quasi_base_row2base_row(get_var_row(v));
SASSERT(get_var_kind(v) == BASE);
case BASE:
if (!m_to_patch.contains(v) && get_value(v) < k) {
TRACE("to_patch_bug", tout << "need to be patched (assert_lower): "; display_var(tout, v););
m_to_patch.insert(v);
}
break;
case NON_BASE:
if (get_value(v) < k)
set_value(v, k);
break;
}
push_bound_trail(v, l, false);
set_bound(b, false);
if (propagation_mode() != BP_NONE)
mark_rows_for_bound_prop(v);
return true;
}
/**
\brief Assert x <= k, return false if a conflict is detected.
*/
template<typename Ext>
bool theory_arith<Ext>::assert_upper(bound * b) {
SASSERT(b->get_bound_kind() == B_UPPER);
theory_var v = b->get_var();
inf_numeral const & k = b->get_value();
bound * u = upper(v);
bound * l = lower(v);
if (l && k < l->get_value()) {
sign_bound_conflict(l, b);
return false;
}
if (u && k >= u->get_value()) {
// redundant
return true;
}
switch (get_var_kind(v)) {
case QUASI_BASE:
quasi_base_row2base_row(get_var_row(v));
SASSERT(get_var_kind(v) == BASE);
case BASE:
if (!m_to_patch.contains(v) && get_value(v) > k) {
TRACE("to_patch_bug", tout << "need to be patched (assert upper): "; display_var(tout, v););
m_to_patch.insert(v);
}
break;
case NON_BASE:
if (get_value(v) > k)
set_value(v, k);
break;
}
push_bound_trail(v, u, true);
set_bound(b, true);
if (propagation_mode() != BP_NONE)
mark_rows_for_bound_prop(v);
return true;
}
template<typename Ext>
bool theory_arith<Ext>::assert_bound(bound * b) {
TRACE("assert_bound", display_bound(tout, b););
theory_var v = b->get_var();
if (b->is_atom()) {
CTRACE("unassigned_atoms", m_unassigned_atoms[v] <= 0, display_var(tout, v););
SASSERT(m_unassigned_atoms[v] > 0);
push_dec_unassigned_atoms_trail(v);
m_unassigned_atoms[v]--;
}
bool result = true;
switch (b->get_bound_kind()) {
case B_LOWER:
m_stats.m_assert_lower++;
result = assert_lower(b);
break;
case B_UPPER:
m_stats.m_assert_upper++;
result = assert_upper(b);
break;
}
TRACE("arith_assert", tout << "result: " << result << "\n"; display(tout););
return result;
}
/**
\brief Sign a conflict because the bounds b1 and b2 are contradictory.
*/
template<typename Ext>
void theory_arith<Ext>::sign_bound_conflict(bound * b1, bound * b2) {
SASSERT(b1->get_var() == b2->get_var());
antecedents& ante = get_antecedents();
b1->push_justification(ante, numeral(1));
b2->push_justification(ante, numeral(1));
set_conflict(ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), ante, is_int(b1->get_var()), "farkas");
TRACE("arith_conflict", tout << "bound conflict\n";);
}
// -----------------------------------
//
// Bound propagation
//
// -----------------------------------
/**
\brief Mark the row r1 for bound propagation.
*/
template<typename Ext>
void theory_arith<Ext>::mark_row_for_bound_prop(unsigned r1) {
if (!m_in_to_check.contains(r1) && m_rows[r1].m_base_var != null_theory_var) {
m_in_to_check.insert(r1);
m_to_check.push_back(r1);
}
}
/**
\brief Mark all rows that contain v for bound propagation.
*/
template<typename Ext>
void theory_arith<Ext>::mark_rows_for_bound_prop(theory_var v) {
column const & c = m_columns[v];
typename svector<col_entry>::const_iterator it = c.begin_entries();
typename svector<col_entry>::const_iterator end = c.end_entries();
for (; it != end; ++it) {
if (!it->is_dead())
mark_row_for_bound_prop(it->m_row_id);
}
}
/**
\brief Given a row:
a_1 * x_1 + ... + a_n * x_n = 0
Claim:
If forall i in [1..n]. (a_i > 0 => upper(x_i) != oo) and (a_i < 0 => lower(x_i) != -oo)
Then row implies a lower bound for every monomial in the row.
Proof:
Without loss of generality, we consider the monomial a_1 * x_1
a_1 * x_1 = - a_2 * x_2 - ... - a_n * x_n
= (Sum_{a_i < 0} -a_i * x_i) + (Sum_{a_j > 0} -a_j * x_j)
>= (Sum_{a_i < 0} -a_i * lower(x_i)) + (Sum_{a_j > 0} -a_j * upper(x_j))
If one the condition fails for the monomial a_i * x_i, then the row can still be used
to imply a lower bound for this monomial.
-4*x + 2*y - 3*z = 0
y <= 1
z >= 1
-x = -2*y + 3*z >= -2 + 3 >= 1
-4*x >= 1
Remark: the lower bound is not for the variable, but for the monomial.
Claim:
If forall i in [1..n]. (a_i > 0 => lower(x_i) != oo) and (a_i < 0 => upper(x_i) != -oo)
Then row implies a upper bound for every monomial in the row.
Proof: similar to the previous claim.
The result is stored in lower_idx and upper_idx
- lower_idx >= 0 : row can imply a lower bound for the monomial at 'lower_idx'
- lower_idx == -1 : row can imply a lower bound for every monomial in the row.
- lower_idx == -2 : row cannot be used to imply a lower bound.
- upper_idx >= 0 : row can imply a upper bound for the monomial at 'upper_idx'
- upper_idx == -1 : row can imply a upper bound for every monomial in the row.
- upper_idx == -2 : row cannot be used to imply a upper bound.
*/
template<typename Ext>
void theory_arith<Ext>::is_row_useful_for_bound_prop(row const & r, int & lower_idx, int & upper_idx) const {
lower_idx = -1;
upper_idx = -1;
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (int i = 0; it != end; ++it, ++i) {
if (!it->is_dead()) {
#define UPDATE_IDX(IDX) IDX = IDX == -1 ? i : -2
if (skip_big_coeffs() && it->m_coeff.is_big()) {
TRACE("is_row_useful", tout << "skipping row that contains big number...\n"; display_row_info(tout, r););
lower_idx = -2;
upper_idx = -2;
return;
}
bool is_pos = it->m_coeff.is_pos();
if (lower(it->m_var) == 0) {
if (is_pos) {
UPDATE_IDX(upper_idx);
}
else {
UPDATE_IDX(lower_idx);
}
}
if (upper(it->m_var) == 0) {
if (is_pos) {
UPDATE_IDX(lower_idx);
}
else {
UPDATE_IDX(upper_idx);
}
}
if (lower_idx == -2 && upper_idx == -2)
return;
}
}
}
/**
\brief Imply a lower/upper bound for the monomial stored at position idx.
Then this bound is used to produce a bound for the monomial variable.
*/
template<typename Ext>
void theory_arith<Ext>::imply_bound_for_monomial(row const & r, int idx, bool is_lower) {
row_entry const & entry = r[idx];
if (m_unassigned_atoms[entry.m_var] > 0) {
inf_numeral implied_k;
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (int idx2 = 0; it != end; ++it, ++idx2) {
if (!it->is_dead() && idx != idx2) {
bound * b = get_bound(it->m_var, is_lower ? it->m_coeff.is_pos() : it->m_coeff.is_neg());
SASSERT(b);
// implied_k -= it->m_coeff * b->get_value();
implied_k.submul(it->m_coeff, b->get_value());
}
}
implied_k /= entry.m_coeff;
if (entry.m_coeff.is_pos() == is_lower) {
// implied_k is a lower bound for entry.m_var
bound * curr = lower(entry.m_var);
if (curr == 0 || implied_k > curr->get_value()) {
TRACE("arith_imply_bound",
tout << "implying lower bound for v" << entry.m_var << " " << implied_k << " using row:\n";
display_row_info(tout, r);
display_var(tout, entry.m_var););
mk_implied_bound(r, idx, is_lower, entry.m_var, B_LOWER, implied_k);
}
}
else {
// implied_k is an upper bound for it->m_var
bound * curr = upper(entry.m_var);
if (curr == 0 || implied_k < curr->get_value()) {
TRACE("arith_imply_bound",
tout << "implying upper bound for v" << entry.m_var << " " << implied_k << " using row:\n";
display_row_info(tout, r);
display_var(tout, entry.m_var););
mk_implied_bound(r, idx, is_lower, entry.m_var, B_UPPER, implied_k);
}
}
}
}
/**
\brief Auxiliary method. See is_row_useful_for_bound_prop
If is_lower = true (false), then imply a lower (upper) bound for all
monomials in the row. The monomial bounds are used to compute bounds
for the monomial variables.
*/
template<typename Ext>
void theory_arith<Ext>::imply_bound_for_all_monomials(row const & r, bool is_lower) {
// Traverse the row once and compute
// bb = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_j > 0} -a_j * upper(x_j)) If is_lower = true
// bb = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_j < 0} -a_j * upper(x_j)) If is_lower = false
inf_numeral bb;
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it) {
if (!it->is_dead()) {
inf_numeral const & b = get_bound(it->m_var, is_lower ? it->m_coeff.is_pos() : it->m_coeff.is_neg())->get_value();
// bb -= it->m_coeff * b;
bb.submul(it->m_coeff, b);
}
}
inf_numeral implied_k;
it = r.begin_entries();
for (int idx = 0; it != end; ++it, ++idx) {
if (!it->is_dead() && m_unassigned_atoms[it->m_var] > 0) {
inf_numeral const & b = get_bound(it->m_var, is_lower ? it->m_coeff.is_pos() : it->m_coeff.is_neg())->get_value();
implied_k = bb;
// implied_k += it->m_coeff * b;
implied_k.addmul(it->m_coeff, b);
// implied_k is a bound for the monomial in position it
implied_k /= it->m_coeff;
TRACE("arith_imply_bound",
display_var(tout, it->m_var);
tout << "implied bound: " << (it->m_coeff.is_pos() ? ">=" : "<=") << implied_k << "\n";);
if (it->m_coeff.is_pos() == is_lower) {
// implied_k is a lower bound for it->m_var
bound * curr = lower(it->m_var);
if (curr == 0 || implied_k > curr->get_value()) {
// improved lower bound
TRACE("arith_imply_bound",
tout << "implying lower bound for v" << it->m_var << " " << implied_k << " using row:\n";
display_row_info(tout, r);
display_var(tout, it->m_var););
mk_implied_bound(r, idx, is_lower, it->m_var, B_LOWER, implied_k);
}
}
else {
// implied_k is an upper bound for it->m_var
bound * curr = upper(it->m_var);
if (curr == 0 || implied_k < curr->get_value()) {
// improved upper bound
TRACE("arith_imply_bound",
tout << "implying upper bound for v" << it->m_var << " " << implied_k << " using row:\n";
display_row_info(tout, r);
display_var(tout, it->m_var););
mk_implied_bound(r, idx, is_lower, it->m_var, B_UPPER, implied_k);
}
}
}
}
}
/**
\brief Create an explanation for the lower/upper bound of the variable at position idx.
\remark delta is used for relaxing the explanation. That is, the implied bound can be delta weaker the the
computed value.
\remark the is_lower parameter is a little bit counterintuitive. It says if the other monomials
imply a lower (upper) bound for the monomial at position idx.
Store the result in 'antecedent'
*/
template<typename Ext>
void theory_arith<Ext>::explain_bound(row const & r, int idx, bool is_lower, inf_numeral & delta, antecedents& ante) {
SASSERT(delta >= inf_numeral::zero());
if (!relax_bounds() && (!ante.lits().empty() || !ante.eqs().empty()))
return;
context & ctx = get_context();
ante.reset(); // !!!!TBD: should equality ante also be reset here!!!!
row_entry const & entry = r[idx];
numeral coeff = entry.m_coeff;
if (relax_bounds()) {
// if the variable v at position idx can have a delta increase (decrease) of 'delta', then
// the monomial (coeff * v) at position idx can have a delta increase (decrease) of '|coeff| * delta'
if (coeff.is_neg())
coeff.neg();
delta *= coeff; // adjust delta
}
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
TRACE("arith_proof", display_row(tout, r, false); );
for (int idx2 = 0; it != end; ++it, ++idx2) {
if (!it->is_dead() && idx != idx2) {
bound * b = get_bound(it->m_var, is_lower ? it->m_coeff.is_pos() : it->m_coeff.is_neg());
SASSERT(b);
if (!b->has_justification())
continue;
if (!relax_bounds() || delta.is_zero()) {
b->push_justification(ante, it->m_coeff);
continue;
}
numeral coeff = it->m_coeff;
bool is_b_lower = b->get_bound_kind() == B_LOWER;
if (coeff.is_neg())
coeff.neg();
numeral inv_coeff(1);
inv_coeff /= coeff;
inf_numeral k_1 = b->get_value();
inf_numeral limit_k1;
// if the max decrease (increase) of the curr monomial (coeff * v2) is delta, then
// the maximal decrease (increase) of v2 is (1/|coeff| * delta)
if (is_b_lower) {
limit_k1 = k_1;
// limit_k1 -= delta * coeff;
limit_k1.submul(inv_coeff, delta);
}
else {
limit_k1 = k_1;
// limit_k1 += delta * coeff;
limit_k1.addmul(inv_coeff, delta);
}
TRACE("propagate_bounds_bug", tout << "is_b_lower: " << is_b_lower << " k1: " << k_1 << " limit_k1: "
<< limit_k1 << " delta: " << delta << " coeff: " << coeff << "\n";);
inf_numeral k_2 = k_1;
atom * new_atom = 0;
atoms & as = m_var_occs[it->m_var];
typename atoms::iterator it = as.begin();
typename atoms::iterator end = as.end();
for (; it != end; ++it) {
atom * a = *it;
if (a == b)
continue;
bool_var bv = a->get_bool_var();
lbool val = ctx.get_assignment(bv);
if (val == l_undef)
continue;
// TODO: check if the following line is a bottleneck
a->assign_eh(val == l_true, get_epsilon(a->get_var()));
if (val != l_undef && a->get_bound_kind() == b->get_bound_kind()) {
SASSERT((ctx.get_assignment(bv) == l_true) == a->is_true());
inf_numeral a_val = a->get_value();
if (is_b_lower) {
if (a_val >= limit_k1 && a_val < k_2) {
k_2 = a_val;
new_atom = a;
}
}
else {
if (a_val <= limit_k1 && a_val > k_2) {
k_2 = a_val;
new_atom = a;
}
}
}
}
SASSERT(!is_b_lower || k_2 <= k_1);
SASSERT(is_b_lower || k_2 >= k_1);
if (new_atom == 0) {
b->push_justification(ante, coeff);
continue;
}
SASSERT(!is_b_lower || k_2 < k_1);
SASSERT(is_b_lower || k_2 > k_1);
if (is_b_lower) {
TRACE("propagate_bounds", tout << "coeff: " << coeff << ", k_1 - k_2: " << k_1 - k_2 << ", delta: " << delta << "\n";);
delta -= coeff*(k_1 - k_2);
}
else {
TRACE("propagate_bounds", tout << "coeff: " << coeff << ", k_2 - k_1: " << k_2 - k_1 << ", delta: " << delta << "\n";);
delta -= coeff*(k_2 - k_1);
}
TRACE("propagate_bounds", tout << "delta (after replace): " << delta << "\n";);
new_atom->push_justification(ante, coeff);
SASSERT(delta >= inf_numeral::zero());
}
}
}
template<typename Ext>
void theory_arith<Ext>::mk_implied_bound(row const & r, unsigned idx, bool is_lower, theory_var v, bound_kind kind, inf_numeral const & k) {
atoms & as = m_var_occs[v];
antecedents& ante = get_antecedents();
inf_numeral const & epsilon = get_epsilon(v);
inf_numeral delta;
typename atoms::iterator it = as.begin();
typename atoms::iterator end = as.end();
for (; it != end; ++it) {
atom * a = *it;
bool_var bv = a->get_bool_var();
literal l(bv);
if (get_context().get_assignment(bv) == l_undef) {
numeral const & k2 = a->get_k();
delta.reset();
if (a->get_atom_kind() == A_LOWER) {
// v >= k k >= k2 |- v >= k2
if (kind == B_LOWER && k >= k2) {
if (relax_bounds()) {
delta = k;
delta -= k2;
}
TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", v" << v << " >= " << k2 << ", delta: " << delta << "\n";
display_row(tout, r););
assign_bound_literal(l, r, idx, is_lower, delta, ante);
}
// v <= k k < k2 |- v < k2 |- not v >= k2
if (kind == B_UPPER && k < k2) {
// it is not sufficient to check whether k < k2.
// example:
// k = -1/5*epsilon
// k2 = 0
// Thus, v <= -1/5*epsilon
// (not v >= 0) which is equivalent to v <= -epsilon.
delta = k2;
delta -= k;
delta -= epsilon;
if (delta.is_nonneg()) {
TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", not v" << v << " >= " << k2 << ", delta: " << delta << "\n";
display_row(tout, r););
assign_bound_literal(~l, r, idx, is_lower, delta, ante);
}
}
}
else {
// v >= k k > k2 |- v > k2 |- not v <= k2
if (kind == B_LOWER && k > k2) {
// it is not sufficient to check whether k > k2.
// see example above.
delta = k;
delta -= k2;
delta -= epsilon;
if (delta.is_nonneg()) {
TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", not v" << v << " <= " << k2 << ", delta: " << delta << "\n";
display_row(tout, r););
assign_bound_literal(~l, r, idx, is_lower, delta, ante);
}
}
// v <= k k <= k2 |- v <= k2
if (kind == B_UPPER && k <= k2) {
if (relax_bounds()) {
delta = k2;
delta -= k;
}
TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", v" << v << " <= " << k2 << ", delta: " << delta << "\n";
display_row(tout, r););
assign_bound_literal(l, r, idx, is_lower, delta, ante);
}
}
}
}
}
template<typename Ext>
void theory_arith<Ext>::assign_bound_literal(literal l, row const & r, unsigned idx, bool is_lower, inf_numeral & delta, antecedents& ante) {
m_stats.m_bound_props++;
context & ctx = get_context();
explain_bound(r, idx, is_lower, delta, ante);
if (dump_lemmas()) {
char const * logic = is_int(r.get_base_var()) ? "QF_LIA" : "QF_LRA";
ctx.display_lemma_as_smt_problem(ante.lits().size(), ante.lits().c_ptr(),
ante.eqs().size(), ante.eqs().c_ptr(), l, logic);
}
TRACE("propagate_bounds",
literal_vector::const_iterator it = ante.lits().begin();
literal_vector::const_iterator end = ante.lits().end();
for (; it != end; ++it) {
ctx.display_detailed_literal(tout, *it);
tout << " ";
}
eq_vector::const_iterator it2 = ante.eqs().begin();
eq_vector::const_iterator end2 = ante.eqs().end();
for (; it2 != end2; ++it2) {
tout << "#" << it2->first->get_owner_id() << "=#" << it2->second->get_owner_id() << " ";
}
tout << " --> ";
ctx.display_detailed_literal(tout, l);
tout << "\n";);
if (ante.lits().size() < small_lemma_size() && ante.eqs().empty()) {
literal_vector & lits = m_tmp_literal_vector2;
lits.reset();
lits.push_back(l);
literal_vector::const_iterator it = ante.lits().begin();
literal_vector::const_iterator end = ante.lits().end();
for (; it != end; ++it)
lits.push_back(~(*it));
justification * js = 0;
if (proofs_enabled) {
js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr(),
ante.num_params(), ante.params("assign-bounds"));
}
ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0);
}
else {
region & r = ctx.get_region();
ctx.assign(l, ctx.mk_justification(
ext_theory_propagation_justification(
get_id(), r, ante.lits().size(), ante.lits().c_ptr(),
ante.eqs().size(), ante.eqs().c_ptr(), l,
ante.num_params(), ante.params("assign-bounds"))));
}
}
/**
\brief Traverse rows in m_to_check and try do derive improved bounds for
the variables occurring in them.
*/
template<typename Ext>
void theory_arith<Ext>::propagate_bounds() {
TRACE("propagate_bounds_detail", display(tout););
typename svector<unsigned>::iterator it = m_to_check.begin();
typename svector<unsigned>::iterator end = m_to_check.end();
for (; it != end; ++it) {
row & r = m_rows[*it];
if (r.get_base_var() != null_theory_var) {
if (r.size() < max_lemma_size()) { // Ignore big rows.
int lower_idx;
int upper_idx;
is_row_useful_for_bound_prop(r, lower_idx, upper_idx);
if (lower_idx >= 0) {
imply_bound_for_monomial(r, lower_idx, true);
}
else if (lower_idx == -1) {
imply_bound_for_all_monomials(r, true);
}
if (upper_idx >= 0) {
imply_bound_for_monomial(r, upper_idx, false);
}
else if (upper_idx == -1) {
imply_bound_for_all_monomials(r, false);
}
// sneaking cheap eq detection in this loop
propagate_cheap_eq(*it);
}
#if 0
theory_var v = r.get_base_var();
if (!is_int(v) || get_value(v).is_int()) {
// If an integer value is not assigned to an integer value, then
// bound propagation can diverge.
m_in_to_check.remove(v);
}
#endif
}
}
m_to_check.reset();
m_in_to_check.reset();
}
// -----------------------------------
//
// Justification
//
// -----------------------------------
template<typename Ext>
void theory_arith<Ext>::set_conflict(unsigned num_literals, literal const * lits, unsigned num_eqs, enode_pair const * eqs,
antecedents& bounds, bool is_lia, char const* proof_rule) {
SASSERT(num_literals != 0 || num_eqs != 0);
context & ctx = get_context();
m_stats.m_conflicts++;
m_num_conflicts++;
if (dump_lemmas()) {
char const * logic = is_lia ? "QF_LIA" : "QF_LRA";
ctx.display_lemma_as_smt_problem(num_literals, lits, num_eqs, eqs, false_literal, logic);
}
region & r = ctx.get_region();
TRACE("arith_conflict",
for (unsigned i = 0; i < num_literals; i++) {
ctx.display_detailed_literal(tout, lits[i]);
tout << " ";
if (proofs_enabled) {
tout << "bound: " << bounds.lit_coeffs()[i] << "\n";
}
}
for (unsigned i = 0; i < num_eqs; i++) {
tout << "#" << eqs[i].first->get_owner_id() << "=#" << eqs[i].second->get_owner_id() << " ";
if (proofs_enabled) {
tout << "bound: " << bounds.eq_coeffs()[i] << "\n";
}
}
for (unsigned i = 0; i < bounds.num_params(); ++i) {
tout << bounds.params(proof_rule)[i] << "\n";
}
tout << "\n";);
ctx.set_conflict(
ctx.mk_justification(
ext_theory_conflict_justification(get_id(), r, num_literals, lits, num_eqs, eqs,
bounds.num_params(), bounds.params(proof_rule))));
}
/**
\brief Collect the proofs for the fixed variables in the given row. Store
the proofs in result.
*/
template<typename Ext>
void theory_arith<Ext>::collect_fixed_var_justifications(row const & r, antecedents& antecedents) const {
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it) {
if (!it->is_dead() && is_fixed(it->m_var)) {
lower(it->m_var)->push_justification(antecedents, it->m_coeff);
upper(it->m_var)->push_justification(antecedents, it->m_coeff);
}
}
}
// -----------------------------------
//
// Model generation
//
// -----------------------------------
//
// The arithmetic module uses infinitesimals. So,
// an inf_numeral (n,k) represents n + k*epsilon
// where epsilon is a very small number.
// In order to generate a model, we need to compute
// a value for epsilon in a way all bounds remain
// satisfied.
//
// 1) Handling inequalities: (n1, k1) <= (n2, k2)
//
// The only intersting case is n1 < n2 and k1 > k2.
// Using the definition of infinitesimal numbers
// we have:
// n1 + k1 * epsilon <= n2 + k2 - epsilon
// Therefore:
// epsilon <= (n2 - n1) / (k1 - k2)
//
// Starting at Z3 V2.0, we split disequalities.
// So, we do not need to handle them. If we decide
// to support them again in the future:
//
// 2) Handling disequalities: (n1, k1) /= n2
//
// case a) k1 is positive and n1 < n2
// Thus, epsilon < (n2 - n1) / k1
// => epsilon <= (n2 - n1) / 2*k1
//
// case b) k1 is negative and n1 > n2
// Similarly, epsilon <= (n2 - n1) / 2*k1
//
/**
\brief Update the value of epsilon using the inequality l <= u
*/
template<typename Ext>
void theory_arith<Ext>::update_epsilon(const inf_numeral & l, const inf_numeral & u) {
if (l.get_rational() < u.get_rational() &&
l.get_infinitesimal() > u.get_infinitesimal()) {
numeral new_epsilon = (u.get_rational() - l.get_rational()) / (l.get_infinitesimal() - u.get_infinitesimal());
if (new_epsilon < m_epsilon) {
m_epsilon = new_epsilon;
}
}
SASSERT(m_epsilon.is_pos());
}
template<typename Ext>
void theory_arith<Ext>::compute_epsilon() {
m_epsilon = numeral(1);
theory_var num = get_num_vars();
for (theory_var v = 0; v < num; v++) {
bound * l = lower(v);
bound * u = upper(v);
if (l != 0)
update_epsilon(l->get_value(), get_value(v));
if (u != 0)
update_epsilon(get_value(v), u->get_value());
}
TRACE("epsilon_bug", tout << "epsilon: " << m_epsilon << "\n";);
}
/**
The epsilon computed by compute_epsilon may accidentally make two shared
variables to have the same assignment. This method keeps dividing
epsilon by 2 until this "clash" does not occur.
Here is an example of the problem
Assignment:
x -> 9.5
y -> 10 - epsilon
x and y have different assignments. However, if compute_epsilon sets epsilon
to 0.5, then x and y become 9.5. However, an equality is not propagated to the core
since in the assignment above they are assigned to distinct values.
This bug was reported by Marcello Bersani.
Remark: this is not really a soundness bug. The result sat/unsat produced by Z3 was still correct.
However, the model construction was incorrect. Perhaps, this explains why this bug was not
detected before.
*/
template<typename Ext>
void theory_arith<Ext>::refine_epsilon() {
typedef map<rational, theory_var, obj_hash<rational>, default_eq<rational> > rational2var;
while (true) {
rational2var mapping;
theory_var num = get_num_vars();
bool refine = false;
for (theory_var v = 0; v < num; v++) {
if (is_int(v))
continue;
inf_numeral const & val = get_value(v);
rational value = val.get_rational().to_rational() + m_epsilon.to_rational() * val.get_infinitesimal().to_rational();
theory_var v2;
if (mapping.find(value, v2)) {
SASSERT(!is_int(v2));
if (get_value(v) != get_value(v2)) {
// v and v2 are not known to be equal.
// The choice of m_epsilon is making them equal.
TRACE("refine_epsilon",
tout << "v" << v << " v" << v2 << " " << get_value(v) << " " << get_value(v2) << " " << value << std::endl;
);
refine = true;
break;
}
}
else {
mapping.insert(value, v);
}
}
if (!refine)
return;
numeral two(2);
m_epsilon = m_epsilon / two;
TRACE("refine_epsilon", tout << "new epsilon..." << m_epsilon << std::endl;);
}
}
template<typename Ext>
void theory_arith<Ext>::init_model(model_generator & m) {
TRACE("theory_arith", tout << "init model invoked...\n";);
m_factory = alloc(arith_factory, get_manager());
m.register_factory(m_factory);
compute_epsilon();
refine_epsilon();
}
template<typename Ext>
model_value_proc * theory_arith<Ext>::mk_value(enode * n, model_generator & mg) {
theory_var v = n->get_th_var(get_id());
SASSERT(v != null_theory_var);
inf_numeral const & val = get_value(v);
rational num = val.get_rational().to_rational() + m_epsilon.to_rational() * val.get_infinitesimal().to_rational();
return alloc(expr_wrapper_proc, m_factory->mk_value(num, is_int(v)));
}
// -----------------------------------
//
// Model checker support
//
// -----------------------------------
template<typename Ext>
bool theory_arith<Ext>::get_value(enode * n, expr_ref & r) {
theory_var v = n->get_th_var(get_id());
if (v == null_theory_var) {
// TODO: generate fresh value different from other get_value(v) for all v.
return false;
}
inf_numeral const & val = get_value(v);
if (!val.get_infinitesimal().is_zero()) {
// TODO: add support for infinitesimals
return false;
}
numeral _val = val.get_rational();
r = m_util.mk_numeral(_val.to_rational(), is_int(v));
return true;
}
// -----------------------------------
//
// Backtracking
//
// -----------------------------------
template<typename Ext>
void theory_arith<Ext>::push_scope_eh() {
theory::push_scope_eh();
m_scopes.push_back(scope());
scope & s = m_scopes.back();
s.m_atoms_lim = m_atoms.size();
s.m_bound_trail_lim = m_bound_trail.size();
s.m_unassigned_atoms_trail_lim = m_unassigned_atoms_trail.size();
s.m_asserted_bounds_lim = m_asserted_bounds.size();
s.m_asserted_qhead_old = m_asserted_qhead;
s.m_bounds_to_delete_lim = m_bounds_to_delete.size();
s.m_nl_monomials_lim = m_nl_monomials.size();
s.m_nl_propagated_lim = m_nl_propagated.size();
CASSERT("arith", wf_rows());
CASSERT("arith", wf_columns());
CASSERT("arith", valid_row_assignment());
CASSERT("arith", satisfy_bounds());
}
template<typename Ext>
void theory_arith<Ext>::pop_scope_eh(unsigned num_scopes) {
CASSERT("arith", wf_rows());
CASSERT("arith", wf_columns());
CASSERT("arith", valid_row_assignment());
TRACE("arith_pop_scope_bug", display(tout););
// The m_update_trail_stack may not be empty.
// In an old version, only propagate_core and methods invoked by propagate_core were
// inserting elements in the m_update_trail_stack stack.
// This is not true anymore. The method quasi_base_row2base_row also does that.
// So, restore_assignment must be invoked. In most cases, it is a noop since the stack is empty.
restore_assignment();
m_to_patch.reset();
unsigned lvl = m_scopes.size();
SASSERT(num_scopes <= lvl);
unsigned new_lvl = lvl - num_scopes;
scope & s = m_scopes[new_lvl];
restore_bounds(s.m_bound_trail_lim);
restore_unassigned_atoms(s.m_unassigned_atoms_trail_lim);
m_asserted_bounds.shrink(s.m_asserted_bounds_lim);
m_asserted_qhead = s.m_asserted_qhead_old;
TRACE("arith_pop_scope_bug", tout << "num_vars: " << get_num_vars() << ", num_old_vars: " << get_old_num_vars(num_scopes) << "\n";);
restore_nl_propagated_flag(s.m_nl_propagated_lim);
m_nl_monomials.shrink(s.m_nl_monomials_lim);
del_atoms(s.m_atoms_lim);
del_bounds(s.m_bounds_to_delete_lim);
del_vars(get_old_num_vars(num_scopes));
m_scopes.shrink(new_lvl);
theory::pop_scope_eh(num_scopes);
bool r = make_feasible();
SASSERT(r);
SASSERT(m_to_patch.empty());
m_to_check.reset();
m_in_to_check.reset();
CASSERT("arith", wf_rows());
CASSERT("arith", wf_columns());
CASSERT("arith", valid_row_assignment());
CASSERT("arith", satisfy_bounds());
}
template<typename Ext>
void theory_arith<Ext>::restore_nl_propagated_flag(unsigned old_trail_size) {
typename svector<theory_var>::iterator begin = m_nl_propagated.begin() + old_trail_size;
typename svector<theory_var>::iterator it = m_nl_propagated.end();
while (it != begin) {
--it;
SASSERT(m_data[*it].m_nl_propagated);
m_data[*it].m_nl_propagated = false;
}
m_nl_propagated.shrink(old_trail_size);
}
template<typename Ext>
void theory_arith<Ext>::restore_bounds(unsigned old_trail_size) {
CASSERT("arith", wf_rows());
typename svector<bound_trail>::iterator begin = m_bound_trail.begin() + old_trail_size;
typename svector<bound_trail>::iterator it = m_bound_trail.end();
while (it != begin) {
--it;
theory_var v = it->get_var();
bound * b = it->get_old_bound();
SASSERT(is_base(v) || is_non_base(v));
restore_bound(v, b, it->is_upper());
if (lazy_pivoting_lvl() > 2 && b == 0 && is_base(v) && is_free(v)) {
SASSERT(!has_var_kind(get_var_row(v), BASE));
SASSERT(!has_var_kind(get_var_row(v), QUASI_BASE));
eliminate<false>(v, false);
SASSERT(m_columns[v].size() == 1);
SASSERT(!has_var_kind(get_var_row(v), BASE));
SASSERT(!has_var_kind(get_var_row(v), QUASI_BASE));
set_var_kind(v, QUASI_BASE);
}
}
m_bound_trail.shrink(old_trail_size);
CASSERT("arith", wf_rows());
}
template<typename Ext>
void theory_arith<Ext>::restore_unassigned_atoms(unsigned old_trail_size) {
svector<unsigned>::iterator begin = m_unassigned_atoms_trail.begin() + old_trail_size;
svector<unsigned>::iterator it = m_unassigned_atoms_trail.end();
while (it != begin) {
--it;
m_unassigned_atoms[*it]++;
}
m_unassigned_atoms_trail.shrink(old_trail_size);
}
template<typename Ext>
void theory_arith<Ext>::del_atoms(unsigned old_size) {
typename atoms::iterator begin = m_atoms.begin() + old_size;
typename atoms::iterator it = m_atoms.end();
while (it != begin) {
--it;
atom * a = *it;
theory_var v = a->get_var();
bool_var bv = a->get_bool_var();
erase_bv2a(bv);
SASSERT(m_var_occs[v].back() == a);
m_var_occs[v].pop_back();
dealloc(a);
}
m_atoms.shrink(old_size);
}
template<typename Ext>
void theory_arith<Ext>::del_bounds(unsigned old_size) {
typename ptr_vector<bound>::iterator begin = m_bounds_to_delete.begin() + old_size;
typename ptr_vector<bound>::iterator it = m_bounds_to_delete.end();
while (it != begin) {
--it;
bound * b = *it;
dealloc(b);
}
m_bounds_to_delete.shrink(old_size);
}
template<typename Ext>
void theory_arith<Ext>::del_vars(unsigned old_num_vars) {
int num_vars = get_num_vars();
SASSERT(num_vars >= static_cast<int>(old_num_vars));
if (num_vars != static_cast<int>(old_num_vars)) {
theory_var v = num_vars;
while (v > static_cast<int>(old_num_vars)) {
--v;
switch (get_var_kind(v)) {
case QUASI_BASE:
SASSERT(m_columns[v].size() == 1);
del_row(get_var_row(v));
break;
case BASE:
SASSERT(lazy_pivoting_lvl() != 0 || m_columns[v].size() == 1);
if (lazy_pivoting_lvl() > 0)
eliminate<false>(v, false);
del_row(get_var_row(v));
break;
case NON_BASE: {
col_entry const * entry = get_a_base_row_that_contains(v);
if (entry) {
row & r = m_rows[entry->m_row_id];
SASSERT(is_base(r.get_base_var()));
SASSERT(r[entry->m_row_idx].m_var == v);
pivot<false>(r.get_base_var(), v, r[entry->m_row_idx].m_coeff, false);
SASSERT(is_base(v));
del_row(get_var_row(v));
}
break;
} }
m_in_update_trail_stack.remove(v);
m_left_basis.remove(v);
m_in_to_check.remove(v);
}
m_columns .shrink(old_num_vars);
m_data .shrink(old_num_vars);
m_value .shrink(old_num_vars);
m_old_value .shrink(old_num_vars);
m_var_occs .shrink(old_num_vars);
m_unassigned_atoms.shrink(old_num_vars);
m_var_pos .shrink(old_num_vars);
m_bounds[0] .shrink(old_num_vars);
m_bounds[1] .shrink(old_num_vars);
SASSERT(check_vector_sizes());
}
}
template<typename Ext>
void theory_arith<Ext>::del_row(unsigned r_id) {
row & r = m_rows[r_id];
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it) {
if (!it->is_dead()) {
theory_var v = it->m_var;
column & c = m_columns[v];
c.del_col_entry(it->m_col_idx);
}
}
r.m_base_var = null_theory_var;
r.reset();
m_dead_rows.push_back(r_id);
}
/**
\brief reset and retrieve built-in explanation hints for arithmetic lemmmas.
*/
template<typename Ext>
typename theory_arith<Ext>::antecedents& theory_arith<Ext>::get_antecedents() {
m_tmp_antecedents.reset();
return m_tmp_antecedents;
}
};
#endif /* _THEORY_ARITH_CORE_H_ */