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

3606 lines
137 KiB
C++

/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
smt_arith.cpp
Abstract:
Arithmetic solver for smt::solver
Author:
Leonardo de Moura (leonardo) 2011-06-25.
Revision History:
--*/
#include"smt_arith.h"
#include"bound_propagator.h"
#include"linear_equation.h"
#include"mpq.h"
#include"mpq_inf.h"
#include"double_manager.h"
#include"small_object_allocator.h"
#include"arith_decl_plugin.h"
#include"ast_smt2_pp.h"
#include"assertion_set_strategy.h"
#include"statistics.h"
#include"lu.h"
#include"cooperate.h"
#include"model.h"
namespace smt {
typedef unsigned var;
class var_set {
unsigned_vector m_var2pos;
unsigned_vector m_vars;
public:
typedef unsigned_vector::const_iterator iterator;
var_set() {}
void reset() {
if (4 * m_vars.size() < m_var2pos.size()) {
unsigned_vector::iterator it = m_vars.begin();
unsigned_vector::iterator end = m_vars.end();
for (; it != end; ++it)
m_var2pos[*it] = UINT_MAX;
}
else {
m_var2pos.reset();
}
m_vars.reset();
}
bool empty() const { return m_vars.empty(); }
unsigned size() const { return m_vars.size(); }
var operator[](unsigned idx) const { return m_vars[idx]; }
bool contains(var x) const {
return x < m_var2pos.size() && m_var2pos[x] != UINT_MAX;
}
void insert(var x) {
if (contains(x))
return;
m_var2pos.reserve(x+1, UINT_MAX);
m_var2pos[x] = m_vars.size();
m_vars.push_back(x);
SASSERT(contains(x));
}
void erase(var x) {
if (contains(x)) {
unsigned pos = m_var2pos[x];
SASSERT(m_vars[pos] == x);
if (pos != m_vars.size() - 1) {
unsigned last_x = m_vars.back();
m_var2pos[last_x] = pos;
m_vars[pos] = last_x;
}
m_vars.pop_back();
m_var2pos[x] = UINT_MAX;
}
SASSERT(!contains(x));
}
iterator begin() const { return m_vars.begin(); }
iterator end() const { return m_vars.end(); }
};
struct arith::imp {
typedef small_object_allocator allocator;
typedef unsynch_mpq_manager numeral_manager;
typedef unsynch_mpq_inf_manager numeral_inf_manager;
typedef svector<mpq_inf> mpq_inf_vector;
typedef numeral_buffer<mpq, unsynch_mpq_manager> mpq_buffer;
typedef numeral_buffer<mpq_inf, unsynch_mpq_inf_manager> mpq_inf_buffer;
typedef numeral_buffer<mpz, unsynch_mpq_manager> mpz_buffer;
typedef svector<var> var_buffer;
typedef ptr_vector<linear_equation> equations;
typedef lu<double_manager> lu_double;
typedef lu<unsynch_mpq_manager> lu_mpq;
typedef numeral_inf_manager::inf_kind inf_kind;
class double_vector : public svector<double> {
public:
typedef double numeral;
typedef double_manager manager;
};
#define null_var UINT_MAX
// "handy" infinitesimals
#define pos_inf numeral_inf_manager::POS
#define neg_inf numeral_inf_manager::NEG
#define zero_inf numeral_inf_manager::ZERO
struct ineq_cnstr {
var m_x;
mpq m_k;
double m_approx_k;
bool m_lower;
atom_id m_atom;
};
typedef svector<ineq_cnstr> ineq_cnstrs;
typedef unsigned_vector ineq_cnstr_ids;
// track the position of a variable in a equation
struct eq_occ {
unsigned m_idx; // equation index
unsigned m_pos; // position in the equation
eq_occ(unsigned idx, unsigned pos):m_idx(idx), m_pos(pos) {}
};
typedef svector<eq_occ> eq_occs;
ast_manager & m;
arith_util m_util;
expr_ref_vector m_var2expr;
obj_map<expr, var> m_expr2var;
allocator m_allocator;
numeral_manager m_num_manager;
numeral_inf_manager m_num_inf_manager;
double_manager m_double_manager;
equations m_equations;
vector<eq_occs> m_eq_occs;
lu_mpq m_lu_mpq;
lu_double m_lu_double;
char_vector m_eliminated;
unsigned_vector m_basic; // var -> eq_idx, UINT_MAX if not basic, otherwise it is a value in [0, m_equations.size())
unsigned_vector m_inv_basic; // eq_idx -> var inverse of m_basic
mpq_inf_buffer m_values;
mpq_inf_buffer m_old_values;
double_vector m_approx_values;
double_vector m_old_approx_values;
var_set m_updated;
var_set m_approx_updated;
var_set m_bad_vars;
var_set m_int_bad_vars;
linear_equation_manager m_eq_manager;
bound_propagator m_asserted_bounds;
bound_propagator m_bounds;
unsigned m_conflict_eq_idx; // != UINT_MAX if the corresponding get_row(m_conflict_eq_idx) is in conflict.
ineq_cnstrs m_ineq_cnstrs;
ineq_cnstr_ids m_atom2ineq_cnstr; // atom idx to ineq cnstr idx
vector<ineq_cnstr_ids> m_var_occs; // ineq_cnstrs that constain a variable
mpq_buffer m_mpq_buffer;
mpz_buffer m_mpz_buffer;
var_buffer m_var_buffer;
typedef std::pair<var, linear_equation *> elim_var_info;
typedef svector<elim_var_info> elim_vars;
elim_vars m_elim_vars;
volatile bool m_cancel;
// Modes of operation for mk_feasible
bool m_use_approx; // use approximated values
bool m_use_asserted; // consider only asserted bounds
bool m_approx_forbidden; // disable approx mode when approximation gets lost trying to find feasible solution.
// statistics
unsigned m_eliminated_vars;
unsigned m_lu_factorizations;
unsigned m_pivots;
unsigned m_approx_pivots;
unsigned m_max_l;
unsigned m_max_u;
unsigned m_branches;
unsigned m_cuts;
// configuration
unsigned m_refactor_threshold;
unsigned m_blands_rule_factor;
unsigned m_approx_threshold_factor; // factor*num_eqs == max pivots to try when using approximated
bool m_elim_vars_enabled;
bool m_elim_int_vars_enabled;
bool m_branch_vars_only; // do not branch on auxiliary variables
bool m_cuts_enabled;
// backtracking
struct scope {
bool m_bad_vars_empty;
unsigned m_conflict_eq_idx_old;
};
svector<scope> m_scopes;
// Data-structures for branch & bound
unsigned_vector m_bb_var;
char_vector m_bb_lower;
char_vector m_bb_first;
mpq_buffer m_bb_k;
// -----------------------
//
// Basic
//
// -----------------------
imp(ast_manager & _m, params_ref const & p):
m(_m),
m_util(m),
m_var2expr(m),
m_allocator("arith"),
m_num_inf_manager(m_num_manager),
m_double_manager(p),
m_lu_mpq(m_num_manager, p),
m_lu_double(m_double_manager, p),
m_values(m_num_inf_manager),
m_old_values(m_num_inf_manager),
m_eq_manager(m_num_manager, m_allocator),
m_asserted_bounds(m_num_manager, m_allocator, p),
m_bounds(m_num_manager, m_allocator, p),
m_mpq_buffer(m_num_manager),
m_mpz_buffer(m_num_manager),
m_bb_k(m_num_manager) {
m_approx_forbidden = false;
m_cancel = false;
m_conflict_eq_idx = UINT_MAX;
updt_params_core(p);
reset_statistics();
m_use_approx = false;
set_mode_core(false, false);
}
~imp() {
del_equations();
del_ineq_cnstrs();
del_elim_vars();
}
void updt_params(params_ref const & p) {
m_double_manager.updt_params(p);
m_asserted_bounds.updt_params(p);
m_bounds.updt_params(p);
m_lu_mpq.updt_params(p);
m_lu_double.updt_params(p);
updt_params_core(p);
}
void updt_params_core(params_ref const & p) {
m_refactor_threshold = p.get_uint(":lu-refactor", 20);
m_blands_rule_factor = p.get_uint(":blands-rule-factor", 4);
m_elim_vars_enabled = p.get_bool(":arith-elim-vars", false);
m_elim_int_vars_enabled = p.get_bool(":arith-elim-int-vars", true);
m_approx_threshold_factor = p.get_uint(":approx-max-pivots", 32);
m_branch_vars_only = p.get_bool(":arith-branch-vars-only", true);
m_cuts_enabled = p.get_bool(":arith-cuts", false);
}
void set_cancel(bool f) {
m_cancel = f;
}
void reset_statistics() {
m_eliminated_vars = 0;
m_lu_factorizations = 0;
m_pivots = 0;
m_approx_pivots = 0;
m_max_l = 0;
m_max_u = 0;
m_branches = 0;
m_cuts = 0;
m_bounds.reset_statistics();
}
void collect_statistics(statistics & st) const {
st.update("elim tableau vars", m_eliminated_vars);
st.update("LU factorizations", m_lu_factorizations);
st.update("pivots", m_pivots);
st.update("approx pivots", m_approx_pivots);
st.update("max L", m_max_l);
st.update("max U", m_max_u);
st.update("bb decisions", m_branches);
st.update("cuts", m_cuts);
m_bounds.collect_statistics(st);
}
void checkpoint() {
if (m_cancel)
throw strategy_exception(STE_CANCELED_MSG);
cooperate("arith");
}
unsigned scope_lvl() const {
return m_scopes.size();
}
void del_equations() {
equations::iterator it = m_equations.begin();
equations::iterator end = m_equations.end();
for (; it != end; ++it)
m_eq_manager.del(*it);
}
void del_ineq_cnstrs() {
ineq_cnstrs::iterator it = m_ineq_cnstrs.begin();
ineq_cnstrs::iterator end = m_ineq_cnstrs.end();
for (; it != end; ++it) {
m_num_manager.del(it->m_k);
}
}
void del_elim_vars() {
elim_vars::iterator it = m_elim_vars.begin();
elim_vars::iterator end = m_elim_vars.end();
for (; it != end; ++it) {
m_eq_manager.del(it->second);
}
}
bool is_int(var x) const {
return m_asserted_bounds.is_int(x);
}
bool is_unconstrained(var x) const {
return
!m_asserted_bounds.has_lower(x) &&
!m_asserted_bounds.has_upper(x) &&
m_var_occs[x].empty();
}
bool was_eliminated(var x) const {
return m_eliminated[x] != 0;
}
bool basic(var x) const {
return m_basic[x] != UINT_MAX;
}
bool nonbasic(var x) const {
return m_basic[x] == UINT_MAX;
}
unsigned num_vars() const {
return m_var2expr.size();
}
bound_propagator const & bp() const { return m_use_asserted ? m_asserted_bounds : m_bounds; }
bound_propagator & bp() { return m_use_asserted ? m_asserted_bounds : m_bounds; }
bool has_lower(var x) const { return bp().has_lower(x); }
bool has_upper(var x) const { return bp().has_upper(x); }
#define MK_LOWER_METHOD(NAME, OP, NO_BOUND_RESULT) \
bool NAME(var x) const { \
if (!bp().has_lower(x)) \
return NO_BOUND_RESULT; \
if (m_use_approx) { \
return m_double_manager.OP(m_approx_values[x], bp().approx_lower(x)); \
} \
else { \
bool strict; \
mpq const & l = bp().lower(x, strict); \
return m_num_inf_manager.OP(m_values[x], l, strict ? pos_inf : zero_inf); \
} \
}
#define MK_UPPER_METHOD(NAME, OP, NO_BOUND_RESULT) \
bool NAME(var x) const { \
if (!bp().has_upper(x)) \
return NO_BOUND_RESULT; \
if (m_use_approx) { \
return m_double_manager.OP(m_approx_values[x], bp().approx_upper(x)); \
} \
else { \
bool strict; \
mpq const & u = bp().upper(x, strict); \
return m_num_inf_manager.OP(m_values[x], u, strict ? neg_inf : zero_inf); \
} \
}
MK_LOWER_METHOD(at_lower, eq, false);
MK_LOWER_METHOD(below_lower, lt, false);
MK_LOWER_METHOD(above_lower, gt, true);
MK_UPPER_METHOD(at_upper, eq, false);
MK_UPPER_METHOD(above_upper, gt, false);
MK_UPPER_METHOD(below_upper, lt, true);
void save_value(var x) {
SASSERT(!m_use_approx);
if (!m_updated.contains(x)) {
m_updated.insert(x);
m_num_inf_manager.set(m_old_values[x], m_values[x]);
}
}
void save_approx_value(var x) {
SASSERT(m_use_approx);
if (!m_approx_updated.contains(x)) {
m_approx_updated.insert(x);
m_old_approx_values[x] = m_approx_values[x];
}
}
bool int_feasible(var x) const {
return !is_int(x) || m_num_inf_manager.is_int(m_values[x]);
}
void check_int(var x) {
if (!int_feasible(x))
m_int_bad_vars.insert(x);
}
void set_value(var x, mpq_inf const & val) {
SASSERT(!m_use_approx);
save_value(x);
m_num_inf_manager.set(m_values[x], val);
check_int(x);
}
void set_value(var x, mpq const & val, inf_kind k) {
SASSERT(!m_use_approx);
save_value(x);
m_num_inf_manager.set(m_values[x], val, k);
check_int(x);
}
void set_value(var x, double val) {
SASSERT(m_use_approx);
save_approx_value(x);
m_approx_values[x] = val;
}
void acc_value(var x, mpq_inf const & delta) {
SASSERT(!m_use_approx);
save_value(x);
m_num_inf_manager.add(m_values[x], delta, m_values[x]);
check_int(x);
}
void acc_value(var x, double val) {
SASSERT(m_use_approx);
save_approx_value(x);
m_approx_values[x] += val;
if (m_double_manager.is_zero(m_approx_values[x]))
m_approx_values[x] = 0.0;
}
/**
\brief Assign x to its lower bound
*/
void lower2value(var x) {
if (m_use_approx) {
set_value(x, bp().approx_lower(x));
}
else {
bool strict;
mpq const & l = bp().lower(x, strict);
set_value(x, l, strict ? pos_inf : zero_inf);
}
}
/**
\brief Assign x to its upper bound
*/
void upper2value(var x) {
if (m_use_approx) {
set_value(x, bp().approx_upper(x));
}
else {
bool strict;
mpq const & u = bp().upper(x, strict);
set_value(x, u, strict ? neg_inf : zero_inf);
}
}
// -----------------------
//
// Model Generation
//
// -----------------------
void update_epsilon(mpq_inf const & l, mpq_inf const & u, mpq & epsilon) {
numeral_manager & nm = m_num_manager;
if (nm.lt(l.first, u.first) && nm.gt(l.second, u.second)) {
mpq new_epsilon;
mpq tmp;
nm.sub(u.first, l.first, new_epsilon);
nm.sub(l.second, u.second, tmp);
nm.div(new_epsilon, tmp, new_epsilon);
if (nm.lt(new_epsilon, epsilon))
nm.swap(new_epsilon, epsilon);
nm.del(new_epsilon);
nm.del(tmp);
}
SASSERT(nm.is_pos(epsilon));
}
void compute_epsilon(mpq & epsilon) {
m_num_manager.set(epsilon, 1);
mpq_inf v;
mpq k;
unsigned num = num_vars();
for (unsigned x = 0; x < num; x++) {
bool strict; unsigned ts;
if (m_bounds.lower(x, k, strict, ts)) {
m_num_inf_manager.set(v, k, strict ? pos_inf : zero_inf);
update_epsilon(v, m_values[x], epsilon);
}
if (m_bounds.upper(x, k, strict, ts)) {
m_num_inf_manager.set(v, k, strict ? neg_inf : zero_inf);
update_epsilon(m_values[x], v, epsilon);
}
}
}
void update_elim_vars_assignment() {
mpq_inf val;
mpq_inf tmp;
mpq inv_a;
unsigned i = m_elim_vars.size();
while (i > 0) {
--i;
elim_var_info & info = m_elim_vars[i];
m_num_inf_manager.reset(val);
var x = info.first;
unsigned x_pos = UINT_MAX;
linear_equation const & eq = *(info.second);
unsigned sz = eq.size();
for (unsigned j = 0; j < sz; j++) {
var x_j = eq.x(j);
if (x_j == x) {
x_pos = j;
continue;
}
m_num_inf_manager.mul(m_values[x_j], eq.a(j), tmp);
m_num_inf_manager.add(val, tmp, val);
}
SASSERT(x_pos != UINT_MAX);
m_num_manager.set(inv_a, eq.a(x_pos));
m_num_manager.inv(inv_a);
m_num_inf_manager.mul(val, inv_a, val);
m_num_inf_manager.neg(val);
m_num_inf_manager.set(m_values[x], val);
}
m_num_inf_manager.del(val);
m_num_inf_manager.del(tmp);
m_num_manager.del(inv_a);
}
void mk_model(model * md) {
//
update_elim_vars_assignment();
numeral_manager & nm = m_num_manager;
mpq epsilon;
mpq val;
compute_epsilon(epsilon);
for (unsigned x = 0; x < num_vars(); x++) {
expr * t = m_var2expr.get(x);
if (t == 0)
continue;
if (!is_uninterp_const(t))
continue;
nm.set(val, m_values[x].first);
nm.addmul(val, m_values[x].second, epsilon, val);
func_decl * d = to_app(t)->get_decl();
md->register_decl(d, m_util.mk_numeral(rational(val), is_int(x)));
}
nm.del(epsilon);
nm.del(val);
}
// -----------------------
//
// Backtracking
//
// -----------------------
void push() {
m_scopes.push_back(scope());
scope & s = m_scopes.back();
s.m_bad_vars_empty = m_bad_vars.empty();
s.m_conflict_eq_idx_old = m_conflict_eq_idx;
m_bounds.push();
m_asserted_bounds.push();
SASSERT(m_bounds.scope_lvl() == scope_lvl());
SASSERT(m_asserted_bounds.scope_lvl() == scope_lvl());
}
void pop(unsigned num_scopes) {
unsigned lvl = scope_lvl();
SASSERT(num_scopes <= lvl);
unsigned new_lvl = lvl - num_scopes;
scope & s = m_scopes[new_lvl];
bool bad_vars_empty = s.m_bad_vars_empty;
m_conflict_eq_idx = s.m_conflict_eq_idx_old;
m_scopes.shrink(new_lvl);
m_bounds.pop(num_scopes);
m_asserted_bounds.pop(num_scopes);
if (!bad_vars_empty)
reset_bad_vars();
SASSERT(m_bounds.scope_lvl() == scope_lvl());
SASSERT(m_asserted_bounds.scope_lvl() == scope_lvl());
}
// -----------------------
//
// Compilation
//
// -----------------------
var mk_var(expr * t) {
var x;
if (t != 0) {
if (m_util.is_to_real(t))
t = to_app(t)->get_arg(0);
if (m_expr2var.find(t, x))
return x;
}
x = m_var2expr.size();
m_var2expr.push_back(t);
bool is_int = true;
if (t != 0) {
m_expr2var.insert(t, x);
is_int = m_util.is_int(t);
}
m_eliminated.push_back(false);
m_basic.push_back(UINT_MAX);
m_values.push_back(mpq_inf());
m_old_values.push_back(mpq_inf());
m_approx_values.push_back(0.0);
m_old_approx_values.push_back(0.0);
m_asserted_bounds.mk_var(x, is_int);
m_bounds.mk_var(x, is_int);
m_var_occs.push_back(ineq_cnstr_ids());
return x;
}
void expr2lp(expr * t, mpq_buffer & as, var_buffer & xs) {
mpq c_mpq_val;
if (m_util.is_add(t)) {
rational c_val;
unsigned num = to_app(t)->get_num_args();
for (unsigned i = 0; i < num; i++) {
expr * mon = to_app(t)->get_arg(i);
expr * c, * x;
if (m_util.is_mul(mon, c, x) && m_util.is_numeral(c, c_val)) {
m_num_manager.set(c_mpq_val, c_val.to_mpq());
as.push_back(c_mpq_val);
xs.push_back(mk_var(x));
}
else {
as.push_back(mpq(1));
xs.push_back(mk_var(mon));
}
}
}
else {
as.push_back(mpq(1));
xs.push_back(mk_var(t));
}
m_num_manager.del(c_mpq_val);
}
var mk_lp(expr * t) {
if (m_util.is_to_real(t))
t = to_app(t)->get_arg(0);
var x;
if (m_expr2var.find(t, x))
return x;
x = mk_var(t);
if (m_util.is_add(t)) {
m_mpq_buffer.reset();
m_var_buffer.reset();
expr2lp(t, m_mpq_buffer, m_var_buffer);
m_mpq_buffer.push_back(mpq(-1));
m_var_buffer.push_back(x);
linear_equation * new_eq = m_eq_manager.mk(m_mpq_buffer.size(), m_mpq_buffer.c_ptr(), m_var_buffer.c_ptr(), true);
SASSERT(new_eq);
unsigned eq_idx = m_equations.size();
m_basic[x] = eq_idx;
m_inv_basic.push_back(x);
m_equations.push_back(new_eq);
SASSERT(m_inv_basic.size() == m_equations.size());
SASSERT(basic(x));
}
return x;
}
void mk_ineq_cnstr(var x, mpq const & k, bool lower, atom_id p) {
unsigned cnstr_id = m_ineq_cnstrs.size();
m_ineq_cnstrs.push_back(ineq_cnstr());
ineq_cnstr & cnstr = m_ineq_cnstrs.back();
cnstr.m_x = x;
m_num_manager.set(cnstr.m_k, k);
cnstr.m_approx_k = m_num_manager.get_double(k);
cnstr.m_lower = lower;
cnstr.m_atom = p;
m_atom2ineq_cnstr.reserve(p+1, UINT_MAX);
m_atom2ineq_cnstr[p] = cnstr_id;
m_var_occs[x].push_back(cnstr_id);
}
void mk_ineq(expr * t, bool neg, atom_id p) {
TRACE("mk_ineq", tout << "mk_ineq, neg: " << neg << ", p: " << p << "\n" << mk_ismt2_pp(t, m) << "\n";);
SASSERT(m_util.is_le(t) || m_util.is_ge(t));
SASSERT(!neg || p == null_atom_id);
bool strict = false;
bool le;
if (m_util.is_le(t)) {
if (neg) {
le = false;
strict = true;
}
else {
le = true;
}
}
else {
SASSERT(m_util.is_ge(t));
if (neg) {
le = true;
strict = true;
}
else {
le = false;
}
}
expr * lhs = to_app(t)->get_arg(0);
expr * rhs = to_app(t)->get_arg(1);
SASSERT(m_util.is_numeral(rhs));
rational c;
m_util.is_numeral(rhs, c);
var x = mk_lp(lhs);
mpq c_prime;
m_num_manager.set(c_prime, c.to_mpq());
TRACE("mk_ineq", tout << "le: " << le << ", strict: " << strict << ", c: " << c << ", c_prime: " <<
m_num_manager.to_string(c_prime) << "\n";);
if (p == null_atom_id) {
// inequality is an axiom
if (le)
assert_upper(x, c_prime, strict);
else
assert_lower(x, c_prime, strict);
}
else {
SASSERT(!strict);
mk_ineq_cnstr(x, c_prime, !le, p);
}
m_num_manager.del(c_prime);
}
void assert_upper(var x, mpq const & k, bool strict) {
m_bounds.assert_upper(x, k, strict);
m_asserted_bounds.assert_upper(x, k, strict);
if (above_upper(x))
m_bad_vars.insert(x);
}
void assert_lower(var x, mpq const & k, bool strict) {
m_bounds.assert_lower(x, k, strict);
m_asserted_bounds.assert_lower(x, k, strict);
if (below_lower(x))
m_bad_vars.insert(x);
}
// -----------------------
//
// Approximate <-> Precise
//
// -----------------------
/**
\brief "Copy" nonbasic m_values to m_approx_values
*/
void mpq2double_nonbasic_values() {
unsigned num = num_vars();
for (unsigned x = 0; x < num; x++) {
if (nonbasic(x))
m_approx_values[x] = m_num_inf_manager.get_double(m_values[x]);
}
}
/**
\brief "Copy" nonbasic m_approx_values to m_values.
It actually checks whether m_approx_values[x] is at lower or uppers,
and copy the corresponding value.
*/
void double2mpq_nonbasic_values() {
unsigned num = num_vars();
for (unsigned x = 0; x < num; x++) {
if (nonbasic(x)) {
if (at_lower(x)) {
bool strict;
mpq const & l = bp().lower(x, strict);
m_num_inf_manager.set(m_values[x], l, strict ? pos_inf : zero_inf);
}
else if (at_upper(x)) {
bool strict;
mpq const & u = bp().upper(x, strict);
m_num_inf_manager.set(m_values[x], u, strict ? neg_inf : zero_inf);
}
else {
m_num_inf_manager.set(m_values[x], 0);
}
}
}
}
/**
\brief Set the value of the basic variables using the value of the non-basic ones.
*/
template<typename LU, typename Values, typename NumInfMng>
void update_basic_values(LU & _lu, Values & values, NumInfMng & infm) {
unsigned num = m_equations.size();
typename LU::dense_vector & row = _lu.get_tmp_row(num_vars());
typename NumInfMng::numeral val;
typename NumInfMng::numeral aux;
typename LU::manager & nm = _lu.m();
for (unsigned eq_idx = 0; eq_idx < num; eq_idx++) {
var x_b = m_inv_basic[eq_idx];
row.reset();
get_row(_lu, eq_idx, row, true, m_use_asserted, true);
infm.reset(val);
typename LU::dense_vector::iterator it = row.begin_non_zeros();
typename LU::dense_vector::iterator end = row.end_non_zeros();
for (; it != end; ++it) {
var x_n = *it;
if (nm.is_zero(row[x_n]))
continue;
infm.mul(values[x_n], row[x_n], aux);
infm.add(val, aux, val);
}
if (infm.is_zero(val)) {
infm.set(values[x_b], 0);
}
else {
infm.neg(val);
infm.swap(values[x_b], val);
}
}
infm.del(val);
infm.del(aux);
}
void update_mpq_basic_values() {
update_basic_values(m_lu_mpq, m_values, m_num_inf_manager);
}
void update_double_basic_values() {
update_basic_values(m_lu_double, m_approx_values, m_double_manager);
}
/**
\brief Faster version of update_basic_values. Instead of extracting rows,
I comput the Column C = N*V_n, where N is the matrix containing the coefficient of the non-basic variables,
and V_n is the assignment of the non_basic variables. Then, I solve Ax = C
The resultant column x is the assignment for the basic variables.
The assignment is a mpq_inf, so the update is performed in two steps.
inf == true, the infinitesimal part is updated,
inf == false, the rational part is updated
*/
void fast_update_basic_values_mpq(bool inf) {
lu_mpq::dense_vector & C = m_lu_mpq.get_tmp_col();
C.reset();
mpq aux;
// 1. Compute column C'
unsigned num = m_equations.size();
for (unsigned eq_idx = 0; eq_idx < num; eq_idx++) {
mpq & c_i = C.get(eq_idx);
linear_equation const & eq = *(m_equations[eq_idx]);
unsigned sz = eq.size();
for (unsigned i = 0; i < sz; i++) {
var x = eq.x(i);
if (basic(x))
continue;
if (inf)
m_num_manager.mul(eq.a(i), m_values[x].second, aux);
else
m_num_manager.mul(eq.a(i), m_values[x].first, aux);
m_num_manager.add(c_i, aux, c_i);
}
}
TRACE("update_basic_values", tout << "inf: " << inf << " "; C.display_non_zeros(tout); tout << "\n";);
// 2. Solve
m_lu_mpq.solve_Ax_eq_y(C);
C.elim_zeros();
// 3. Update basic variable assignment
for (unsigned eq_idx = 0; eq_idx < num; eq_idx++) {
var x_b = m_inv_basic[eq_idx];
mpq & c_i = C.get(eq_idx);
m_num_manager.neg(c_i);
TRACE("update_basic_values", tout << "inf: " << inf << " x" << x_b << " " << m_num_manager.to_string(c_i) << "\n";);
if (inf)
m_num_manager.swap(m_values[x_b].second, c_i);
else
m_num_manager.swap(m_values[x_b].first, c_i);
}
m_num_manager.del(aux);
}
void fast_update_mpq_basic_values() {
fast_update_basic_values_mpq(false);
fast_update_basic_values_mpq(true);
SASSERT(check_eqs_satisfied_core()); // use core to make sure it will be checked even when m_use_approx = false
}
void fast_update_double_basic_values() {
lu_double::dense_vector & C = m_lu_double.get_tmp_col();
// 1. Compute column C'
unsigned num = m_equations.size();
for (unsigned eq_idx = 0; eq_idx < num; eq_idx++) {
double & c_i = C.get(eq_idx);
c_i = 0.0;
linear_equation const & eq = *(m_equations[eq_idx]);
unsigned sz = eq.size();
for (unsigned i = 0; i < sz; i++) {
var x = eq.x(i);
if (basic(x))
continue;
c_i += eq.approx_a(i) * m_approx_values[x];
}
}
TRACE("update_basic_values", C.display_non_zeros(tout); tout << "\n";);
// 2. Solve
m_lu_double.solve_Ax_eq_y(C);
C.elim_zeros();
// 3. Update basic variable assignment
for (unsigned eq_idx = 0; eq_idx < num; eq_idx++) {
var x_b = m_inv_basic[eq_idx];
m_approx_values[x_b] = -C[eq_idx];
}
}
void reset_bad_vars() {
m_bad_vars.reset();
unsigned num = num_vars();
for (var x = 0; x < num; x++) {
if (below_lower(x) || above_upper(x))
m_bad_vars.insert(x);
}
}
void reset_int_bad_vars() {
m_int_bad_vars.reset();
unsigned num = num_vars();
for (var x = 0; x < num; x++) {
check_int(x);
}
}
void set_mode_core(bool use_approx, bool use_asserted) {
if (m_use_approx != use_approx) {
if (use_approx) {
init_lu_double();
mpq2double_nonbasic_values();
// update_double_basic_values();
fast_update_double_basic_values();
}
else {
init_lu_mpq();
double2mpq_nonbasic_values();
// update_mpq_basic_values();
fast_update_mpq_basic_values();
}
}
m_approx_updated.reset();
m_updated.reset();
m_use_approx = use_approx;
m_use_asserted = use_asserted;
reset_bad_vars();
if (!m_use_approx)
reset_int_bad_vars();
}
void set_mode(bool use_approx, bool use_asserted) {
if (use_approx == m_use_approx && use_asserted == m_use_asserted)
return;
set_mode_core(use_approx, use_asserted);
TRACE("set_mode", display(tout););
}
// -----------------------
//
// Simplex & make_feasible
//
// -----------------------
template<typename LU>
void update_lu_stats(LU const & _lu) {
unsigned l_sz = _lu.L_size();
unsigned u_sz = _lu.U_size();
if (l_sz > m_max_l)
m_max_l = l_sz;
if (u_sz > m_max_u)
m_max_u = u_sz;
}
template<typename LU>
void init_lu_core(LU & _lu) {
m_lu_factorizations++;
TRACE("arith_lu", display(tout););
typename LU::numeral a;
unsigned num_eqs = m_equations.size();
_lu.init(num_eqs);
for (unsigned i = 0; i < num_eqs; i++) {
#ifdef Z3DEBUG
unsigned num_entries_added = 0;
#endif
linear_equation const & eq = *(m_equations[i]);
unsigned sz = eq.size();
for (unsigned j = 0; j < sz; j++) {
var x = eq.x(j);
if (basic(x)) {
eq.get_a(_lu.m(), j, a);
_lu.add_entry(a, m_basic[x]);
DEBUG_CODE(num_entries_added++;);
}
}
SASSERT(num_entries_added <= num_eqs);
_lu.end_row();
}
_lu.m().del(a);
_lu.fact();
update_lu_stats(_lu);
TRACE("init_lu", display(tout););
}
void init_lu_double() { init_lu_core(m_lu_double); }
void init_lu_mpq() { init_lu_core(m_lu_mpq); }
void init_lu() {
if (m_use_approx)
init_lu_double();
else
init_lu_mpq();
}
// Extract a column from m_equations. The result is stored in c.
template<typename Vector>
void get_column(var j, Vector & c) {
TRACE("get_column", tout << "get_column x" << j << "\n"; display_eqs(tout););
typename Vector::manager & m = c.m();
c.reset();
eq_occs const & occs = m_eq_occs[j];
for (unsigned i = 0; i < occs.size(); i++) {
eq_occ const & occ = occs[i];
unsigned eq_idx = occ.m_idx;
linear_equation const & eq = *(m_equations[eq_idx]);
SASSERT(eq.x(occ.m_pos) == j);
eq.get_a(m, occ.m_pos, c.get(eq_idx));
}
TRACE("get_column", tout << "result: "; c.display_non_zeros(tout); tout << "\n";);
}
// Extract a non basic column. The result is stored in c.
template<typename LU>
void get_nonbasic_column(LU & _lu, var j, typename LU::dense_vector & c) {
get_column(j, c);
_lu.solve_Ax_eq_y(c);
}
void get_nonbasic_column_double(var j, lu_double::dense_vector & c) { get_nonbasic_column(m_lu_double, j, c); }
void get_nonbasic_column_mpq(var j, lu_mpq::dense_vector & c) { get_nonbasic_column(m_lu_mpq, j, c); }
// Get the linear combination needed for extracting a row.
template<typename LU>
void get_lin_comb_for_row(LU & _lu, unsigned eq_idx, typename LU::dense_vector & r) {
typename LU::numeral one(1);
SASSERT(eq_idx < m_equations.size());
r.reset();
_lu.m().set(r.get(eq_idx), one);
_lu.solve_xA_eq_y(r);
}
// Apply the given linear combination to the equaltions in m_equations, and store
// the result in r.
// If include_fixed = false, then fixed variables are ignored.
// If only_nonbasic = true, then only non-basic variables are considered.
// If use_asserted = true, then m_asserted_bounds is used for detecting fixed variables, otherwise m_bounds is used.
template<typename Vector>
void extract_row(Vector const & lin_comb, Vector & r, bool only_nonbasic = true, bool use_asserted = false, bool include_fixed = false) const {
r.reset();
bound_propagator const & bp = use_asserted ? m_asserted_bounds : m_bounds;
typename Vector::manager & m = lin_comb.m();
typename Vector::numeral a;
typename Vector::iterator it = lin_comb.begin_non_zeros();
typename Vector::iterator end = lin_comb.end_non_zeros();
for (; it != end; ++it) {
unsigned eq_idx = *it;
typename Vector::numeral const & b = lin_comb[eq_idx];
if (m.is_zero(b))
continue;
linear_equation const & eq = *(m_equations[eq_idx]);
unsigned sz = eq.size();
for (unsigned i = 0; i < sz; i++) {
var x_i = eq.x(i);
if (only_nonbasic && basic(x_i))
continue;
if (!include_fixed && bp.is_fixed(x_i))
continue;
eq.get_a(m, i, a);
typename Vector::numeral & r_i = r.get(x_i);
m.addmul(r_i, a, b, r_i);
}
}
r.elim_zeros();
m.del(a);
}
// Get the non-basic variables occurring in the given row of B^{-1}N.
template<typename LU>
void get_row(LU & _lu, unsigned eq_idx, typename LU::dense_vector & r,
bool only_nonbasic = true, bool use_asserted = false, bool include_fixed = false) {
TRACE("get_row", tout << "original: "; m_eq_manager.display(tout, *(m_equations[eq_idx])); tout << "\n";);
typename LU::dense_vector & lc = _lu.get_tmp_vector();
get_lin_comb_for_row(_lu, eq_idx, lc);
extract_row(lc, r, only_nonbasic, use_asserted, include_fixed);
TRACE("get_row",
tout << "eq " << eq_idx << "\n";
tout << "linear combination: "; r.display_non_zeros(tout); tout << "\n";;
tout << "result: "; r.display_pol(tout); tout << "\n";);
}
void get_row_double(unsigned eq_idx, lu_double::dense_vector & r,
bool only_nonbasic = true, bool use_asserted = false, bool include_fixed = false) {
get_row(m_lu_double, eq_idx, r, only_nonbasic, use_asserted, include_fixed);
}
void get_row_mpq(unsigned eq_idx, lu_mpq::dense_vector & r,
bool only_nonbasic = true, bool use_asserted = false, bool include_fixed = false) {
get_row(m_lu_mpq, eq_idx, r, only_nonbasic, use_asserted, include_fixed);
}
void tst_extract_non_basic_columns(std::ostream & out) {
unsigned sz = m_lu_double.size();
lu_double::dense_vector v(m_double_manager, sz);
out << "columns:\n";
for (var x = 0; x < num_vars(); x++) {
if (basic(x) || was_eliminated(x))
continue;
v.reset();
get_nonbasic_column_double(x, v);
out << "x" << x << ": ";
v.display(out);
out << "\n";
}
}
void tst_extract_rows(std::ostream & out) {
lu_double::dense_vector v(m_double_manager, num_vars());
out << "rows:\n";
unsigned num_eqs = m_equations.size();
for (unsigned i = 0; i < num_eqs; i++) {
get_row_double(i, v);
out << "eq " << i << ": ";
v.display_pol(out);
out << "\n";
}
}
template<typename LU>
void pivot(LU & _lu, var x_b, var x_n) {
if (m_use_approx)
m_approx_pivots++;
else
m_pivots++;
SASSERT(basic(x_b));
SASSERT(!basic(x_n));
unsigned eq_idx = m_basic[x_b];
SASSERT(m_inv_basic[eq_idx] == x_b);
unsigned normalized_threshold = std::min((m_equations.size()/10) + 1, m_refactor_threshold);
#if 1
// TODO: incremental update is producing too much imprecision when using floating point numbers.
// So: I'm disabling it for now.
if (!_lu.m().precise()) normalized_threshold = 0;
#endif
if (_lu.get_num_replacements() < normalized_threshold) {
try {
typename LU::dense_vector & c = _lu.get_tmp_col();
get_column(x_n, c);
TRACE("pivot", tout << "column x" << m_basic[x_b] << " "; c.display_non_zeros(tout); tout << "\n";);
_lu.replace_column(m_basic[x_b], c);
update_lu_stats(_lu);
m_basic[x_b] = UINT_MAX;
m_basic[x_n] = eq_idx;
m_inv_basic[eq_idx] = x_n;
TRACE("pivot_bug", display(tout););
CASSERT("arith", !_lu.m().precise() || check_lu());
return;
}
catch (lu_exception) {
IF_VERBOSE(ST_VERBOSITY_LVL, verbose_stream() << "(arith-approx-failed)\n";);
}
}
// refactor from scratch
m_basic[x_b] = UINT_MAX;
m_basic[x_n] = eq_idx;
m_inv_basic[eq_idx] = x_n;
init_lu_core(_lu);
CASSERT("arith", !_lu.m().precise() || check_lu());
}
void pivot_double(var x_b, var x_n) { pivot(m_lu_double, x_b, x_n); }
void pivot_mpq(var x_b, var x_n) { pivot(m_lu_mpq, x_b, x_n); }
template<typename LU>
void tst_random_pivoting(LU & _lu, unsigned num) {
TRACE("random_pivoting", display_eqs(tout););
init_lu_core(_lu);
random_gen rgen;
typename LU::numeral small(10000);
_lu.m().inv(small);
typename LU::numeral neg_small;
_lu.m().set(neg_small, small);
_lu.m().neg(neg_small);
typename LU::dense_vector row(_lu.m(), num_vars());
unsigned num_eqs = m_equations.size();
for (unsigned i = 0; i < num; i++) {
verbose_stream() << "+"; verbose_stream().flush();
// select random equation/row
TRACE("random_pivoting", display_basic(tout); display_LU(tout, _lu););
unsigned eq_idx = rgen() % num_eqs;
var x_b = m_inv_basic[eq_idx];
get_row(_lu, eq_idx, row);
TRACE("random_pivoting", tout << "x" << x_b << " : "; row.display_pol(tout); tout << "\n";);
TRACE("pivot_row", tout << "x" << x_b << " : "; row.display_pol(tout); tout << "\n";);
typename LU::dense_vector::iterator it = row.begin_non_zeros();
typename LU::dense_vector::iterator end = row.end_non_zeros();
if (it == end) {
// row doesn't have non-basic variable
TRACE("random_pivoting", tout << "eq_idx: " << eq_idx << " does not have non-basic variables\n";);
continue;
}
unsigned num_non_zeros = end - it;
var x_n = *(it + (rgen() % num_non_zeros));
if (!LU::manager::precise()) {
if (_lu.m().lt(neg_small, row[x_n]) && _lu.m().lt(row[x_n], small)) {
verbose_stream() << "*";
continue;
}
}
TRACE("random_pivoting", tout << "pivoting x" << x_b << " with x" << x_n << "\n";);
pivot(_lu, x_b, x_n);
}
}
/**
\brief Check LU mpq (this method does not really work for m_lu_double due to floating point imprecision).
*/
bool check_lu() const {
if (m_use_approx)
return true;
lu_mpq::dense_vector row(const_cast<imp*>(this)->m_num_manager, num_vars());
unsigned num_eqs = m_equations.size();
for (unsigned eq_idx = 0; eq_idx < num_eqs; eq_idx++) {
var x_b = m_inv_basic[eq_idx];
row.reset();
const_cast<imp*>(this)->get_row_mpq(eq_idx, row, false /* get basic too */, false, true /* also include fixed vars */);
TRACE("check_lu", tout << "x" << x_b << ", eq " << eq_idx << ": "; row.display_pol(tout); tout << "\n";);
// row must contain one and only one basic variable: x_b
// The coefficient of x_b must be 1
bool found_x_b = false;
lu_mpq::dense_vector::iterator it = row.begin_non_zeros();
lu_mpq::dense_vector::iterator end = row.end_non_zeros();
for (; it != end; ++it) {
var x = *it;
if (m_num_manager.is_zero(row[x]))
continue;
if (x == x_b) {
CTRACE("arith_bug", !m_num_manager.is_one(row[x]), tout << "x" << x_b << ": "; row.display_pol(tout); tout << "\n";
display(tout););
SASSERT(m_num_manager.is_one(row[x]));
found_x_b = true;
continue;
}
CTRACE("arith_bug", basic(x), tout << "row " << eq_idx << " contains unexpected basic variable x" << x << "\n";
row.display_pol(tout); tout << "\n"; display(tout););
SASSERT(!basic(x));
}
SASSERT(found_x_b);
}
return true;
}
template<typename LU, typename Values, typename NumInfMng, typename Numeral>
void update_dependents(LU & _lu, Values & values, NumInfMng & infm, var x_n, Numeral const & old_val) {
Numeral aux;
Numeral delta;
infm.sub(values[x_n], old_val, delta);
typename LU::dense_vector & c = _lu.get_tmp_col();
typename LU::dense_vector::manager & nm = c.m();
get_nonbasic_column(_lu, x_n, c);
TRACE("fix_nonbasic_var", tout << "fixing x" << x_n << ", old_val: " << infm.to_string(old_val) << ", new_val: " <<
infm.to_string(values[x_n]) << ", delta: " << infm.to_string(delta) << "\n";
tout << "column: "; c.display_non_zeros(tout); tout << "\n";);
typename LU::dense_vector::iterator it = c.begin_non_zeros();
typename LU::dense_vector::iterator end = c.end_non_zeros();
for (; it != end; ++it) {
unsigned eq_idx = *it;
if (nm.is_zero(c[eq_idx]))
continue;
var x_b = m_inv_basic[eq_idx];
SASSERT(m_basic[x_b] == eq_idx);
infm.mul(delta, c[eq_idx], aux);
infm.neg(aux);
TRACE("fix_nonbasic_var", tout << "adjusting x" << x_b << " of: " << eq_idx << " with " << infm.to_string(aux) << "\n";);
acc_value(x_b, aux);
if (below_lower(x_b) || above_upper(x_b))
m_bad_vars.insert(x_b);
}
infm.del(aux);
infm.del(delta);
}
/**
\brief Fix a nonbasic variable by moving it to offending bound and propagating the
update to the basic variables.
*/
template<typename LU, typename Values, typename NumInfMng>
void fix_nonbasic_var(LU & _lu, Values & values, NumInfMng & infm, var x_n) {
SASSERT(below_lower(x_n) || above_upper(x_n));
SASSERT(nonbasic(x_n));
typename NumInfMng::numeral old_val;
infm.set(old_val, values[x_n]);
if (below_lower(x_n)) {
lower2value(x_n);
}
else {
SASSERT(above_upper(x_n));
upper2value(x_n);
}
update_dependents(_lu, values, infm, x_n, old_val);
infm.del(old_val);
}
void fix_nonbasic_var(var x) {
if (!below_lower(x) && !above_upper(x))
return; // nothing to be done
SASSERT(nonbasic(x));
if (m_use_approx)
fix_nonbasic_var(m_lu_double, m_approx_values, m_double_manager, x);
else
fix_nonbasic_var(m_lu_mpq, m_values, m_num_inf_manager, x);
}
/**
\brief Remove the given variables from m_bad_vars.
*/
void erase_from_bad_vars(var_set & bad_vars, var_buffer const & vars) {
var_buffer::const_iterator it2 = vars.begin();
var_buffer::const_iterator end2 = vars.end();
for (; it2 != end2; ++it2) {
bad_vars.erase(*it2);
SASSERT(!bad_vars.contains(*it2));
}
}
var_buffer m_to_delete;
void fix_nonbasic_vars() {
TRACE("fix_nonbasic_vars", display_LU(tout););
m_to_delete.reset();
unsigned sz = m_bad_vars.size();
for (unsigned i = 0; i < sz; i++) {
var x = m_bad_vars[i];
if (basic(x))
continue;
TRACE("fix_nonbasic_vars", tout << "fixed x" << x << "\n";);
fix_nonbasic_var(x);
m_to_delete.push_back(x);
}
erase_from_bad_vars(m_bad_vars, m_to_delete);
SASSERT(check_no_nonbasic_bad_var());
}
/**
\brief Remove satisfied variables from m_bad_vars
*/
void cleanup_bad_vars() {
m_to_delete.reset();
var_set::iterator it = m_bad_vars.begin();
var_set::iterator end = m_bad_vars.end();
for (; it != end; ++it) {
var x = *it;
if (!below_lower(x) && !above_upper(x))
m_to_delete.push_back(x);
}
erase_from_bad_vars(m_bad_vars, m_to_delete);
}
void cleanup_int_bad_vars() {
m_to_delete.reset();
var_set::iterator it = m_int_bad_vars.begin();
var_set::iterator end = m_int_bad_vars.end();
for (; it != end; ++it) {
var x = *it;
TRACE("int_bad_vars", tout << "x" << x << ", int_feasible: " << int_feasible(x) << ", val: "
<< m_num_inf_manager.to_string(m_values[x]) << "\n";);
if (int_feasible(x))
m_to_delete.push_back(x);
}
erase_from_bad_vars(m_int_bad_vars, m_to_delete);
}
/**
\brief Return the smallest variable in m_bad_vars.
*/
var get_smallest_bad_var() const {
var r = null_var; // NOTE: null_var is the biggest variable
var_set::iterator it = m_bad_vars.begin();
var_set::iterator end = m_bad_vars.end();
for (; it != end; ++it) {
var x = *it;
SASSERT(x < null_var);
if (x < r)
r = x;
}
return r;
}
/**
\brief Return the variable in m_bad_vars with the least/greatest error
*/
template<bool LEAST>
var get_lg_error_bad_var() {
var r = null_var;
if (m_use_approx) {
double error_r;
double error_x;
var_set::iterator it = m_bad_vars.begin();
var_set::iterator end = m_bad_vars.end();
for (; it != end; ++it) {
var x = *it;
if (below_lower(x))
error_x = bp().approx_lower(x) - m_approx_values[x];
else if (above_upper(x))
error_x = m_approx_values[x] - bp().approx_upper(x);
else
continue;
if (r == null_var || (LEAST && error_x < error_r) || (!LEAST && error_x > error_r)) {
r = x;
error_r = error_x;
}
}
TRACE("get_bad_var", tout << "x" << r << " error: " << error_r << "\n";);
return r;
}
else {
mpq error_r; // ignore infinitesimals
mpq error_x;
var_set::iterator it = m_bad_vars.begin();
var_set::iterator end = m_bad_vars.end();
for (; it != end; ++it) {
var x = *it;
if (below_lower(x))
m_num_manager.sub(bp().lower(x), m_values[x].first, error_x);
else if (above_upper(x))
m_num_manager.sub(m_values[x].first, bp().upper(x), error_x);
else
continue;
SASSERT(m_num_manager.is_nonneg(error_x));
if (r == null_var || (LEAST && m_num_manager.lt(error_x, error_r)) || (!LEAST && m_num_manager.gt(error_x, error_r))) {
r = x;
m_num_manager.swap(error_x, error_r);
}
}
m_num_manager.del(error_r);
m_num_manager.del(error_x);
TRACE("get_bad_var", tout << "x" << r << " error: " << m_num_manager.to_string(error_r) << "\n";);
return r;
}
}
/**
\brief Return the variable in m_bad_vars with the greatest error
*/
var get_least_error_bad_var() { return get_lg_error_bad_var<true>(); }
/**
\brief Return the variable in m_bad_vars with the least error
*/
var get_greatest_error_bad_var() { return get_lg_error_bad_var<false>(); }
unsigned get_num_bounds(var x) const {
unsigned r = 0;
if (has_lower(x)) r++;
if (has_upper(x)) r++;
return r;
}
// Return true if x1 is a better pivot than x2
template<typename Vector>
bool better_pivot(var x_1, var x_2, Vector const & row) const {
SASSERT(x_1 != null_var);
if (x_2 == null_var)
return true;
typename Vector::manager & nm = row.m();
unsigned num_bounds1 = get_num_bounds(x_1);
unsigned num_bounds2 = get_num_bounds(x_2);
if (num_bounds1 == 0 && num_bounds2 > 0)
return true;
if (num_bounds1 > 0 && num_bounds2 == 0)
return false;
if (nm.gt(row[x_1], row[x_2]))
return true;
if (nm.lt(row[x_1], row[x_2]))
return false;
unsigned num_occs1 = m_eq_occs[x_1].size();
unsigned num_occs2 = m_eq_occs[x_2].size();
return num_occs1 < num_occs2;
}
template<typename LU, typename Values, typename NumInfMng>
var select_pivot(LU & _lu, Values & values, NumInfMng & infm, var x_b, bool is_below, bool blands_rule) {
SASSERT(basic(x_b));
typename LU::dense_vector & row = _lu.get_tmp_row(num_vars());
typename LU::dense_vector::manager & nm = row.m();
unsigned eq_idx = m_basic[x_b];
get_row(_lu, eq_idx, row, true, m_use_asserted, false /* there is not point in collecting fixed variables */);
TRACE("select_pivot", tout << "select_pivot x_b: x" << x_b << ", is_below: " << is_below << "\n";
bp().display_var_bounds(tout, x_b, true, true); tout << "\nval: " << infm.to_string(values[x_b]) << "\n";
row.display_pol(tout); tout << "\n";
tout << "below_lower(x_b): " << below_lower(x_b) << "\n";
tout << "above_upper(x_b): " << above_upper(x_b) << "\n";);
var r = null_var;
typename LU::dense_vector::iterator it = row.begin_non_zeros();
typename LU::dense_vector::iterator end = row.end_non_zeros();
for (; it != end; ++it) {
var x_n = *it;
if (nm.is_zero(row[x_n]))
continue; // leftover
bool is_neg = is_below ? nm.is_neg(row[x_n]) : nm.is_pos(row[x_n]);
bool is_pos = !is_neg;
if ((is_pos && above_lower(x_n)) || (is_neg && below_upper(x_n))) {
// x_n is a candidate for pivoting
if (blands_rule) {
if (x_n < r) {
r = x_n;
TRACE("select_pivot", tout << "new-candidate: x" << r << "\n";);
}
}
else if (better_pivot(x_n, r, row)) {
r = x_n;
TRACE("select_pivot", tout << "new-candidate: x" << r << ", num-bounds: " << get_num_bounds(r)
<< " num-occs: " << m_eq_occs[r].size() << "\n";);
}
}
}
return r;
}
/**
\brief Fix a basic variable.
*/
template<typename LU, typename Values, typename NumInfMng>
bool fix_basic_var(LU & _lu, Values & values, NumInfMng & infm, var x_b, bool blands_rule) {
bool is_below;
if (below_lower(x_b)) {
is_below = true;
}
else if (above_upper(x_b)) {
is_below = false;
}
else {
return true; // nothing to be done
}
var x_n = select_pivot(_lu, values, infm, x_b, is_below, blands_rule);
if (x_n == null_var)
return false;
SASSERT(basic(x_b));
SASSERT(nonbasic(x_n));
TRACE("fix_basic_var", tout << "pivoting x" << x_b << " x" << x_n << "\n";);
pivot(_lu, x_b, x_n);
SASSERT(nonbasic(x_b));
fix_nonbasic_var(_lu, values, infm, x_b);
TRACE("fix_basic_var", tout << "x" << x_b << " -> " << infm.to_string(values[x_b]) << "\n";
bp().display_var_bounds(tout, x_b); tout << "\n";);
SASSERT(!below_lower(x_b));
SASSERT(!above_upper(x_b));
return true;
}
bool fix_basic_var(var x_b, bool blands_rule) {
if (m_use_approx)
return fix_basic_var(m_lu_double, m_approx_values, m_double_manager, x_b, blands_rule);
else
return fix_basic_var(m_lu_mpq, m_values, m_num_inf_manager, x_b, blands_rule);
}
// Debugging: make sure m_bad_vars does not contain nonbasic variables.
bool check_no_nonbasic_bad_var() const {
var_set::iterator it = m_bad_vars.begin();
var_set::iterator end = m_bad_vars.end();
for (; it != end; ++it) {
CTRACE("arith_bug", nonbasic(*it), tout << "nonbasic var at bad_vars x" << *it << "\n";);
SASSERT(!nonbasic(*it));
}
return true;
}
void set_conflict(var x_b) {
m_conflict_eq_idx = m_basic[x_b];
}
bool inconsistent() const {
return m_conflict_eq_idx != UINT_MAX || m_bounds.inconsistent() || m_asserted_bounds.inconsistent();
}
void reset_updated() {
if (m_use_approx)
m_approx_updated.reset();
else
m_updated.reset();
}
#define DISPLAY_STAT(NAME, VAL) if (VAL > 0) out << " " << NAME << " " << VAL;
void display_progress(std::ostream & out) const {
out << "(arith";
DISPLAY_STAT(":pivots", m_pivots);
DISPLAY_STAT(":approx-pivots", m_approx_pivots);
DISPLAY_STAT(":branches", m_branches);
DISPLAY_STAT(":cuts", m_cuts);
out << ")\n";
}
/**
\brief Try to satisfy all bounds.
Result:
l_true: succeeded
l_false: problem is unsat
l_undef: gave up
*/
lbool make_feasible_core(unsigned max_pivots = UINT_MAX) {
if (inconsistent())
return l_false;
TRACE("make_feasible_detail", display(tout););
if (m_bad_vars.empty())
return l_true;
reset_updated();
TRACE("make_feasible_begin", display(tout););
CASSERT("arith", check_eqs_satisfied());
CASSERT("arith", check_bad_vars());
fix_nonbasic_vars();
// limit for switching to blands rule
unsigned limit = num_vars() * m_blands_rule_factor;
unsigned counter = 0;
while (true) {
CASSERT("arith", check_bad_vars());
CASSERT("arith", check_eqs_satisfied());
CASSERT("arith", check_basic_assignment());
CASSERT("arith", check_no_nonbasic_bad_var());
TRACE("make_feasible_step", display(tout););
checkpoint();
counter++;
cleanup_bad_vars();
if (m_bad_vars.empty()) {
TRACE("make_feasible", tout << "satisfied:\n"; display(tout););
return l_true;
}
// var x_b = counter >= limit ? get_smallest_bad_var() : get_least_error_bad_var();
var x_b = counter >= limit ? get_smallest_bad_var() : get_greatest_error_bad_var();
// verbose_stream() << "x" << x_b << " "; verbose_stream().flush();
TRACE("make_feasible", tout << "next var to be fixed: x" << x_b << "\n";);
SASSERT(basic(x_b));
if (!fix_basic_var(x_b, counter >= limit)) {
TRACE("make_feasible", tout << "inconsistent:\n"; display(tout););
SASSERT(basic(x_b));
set_conflict(x_b);
return l_false;
}
m_bad_vars.erase(x_b);
#ifndef _EXTERNAL_RELEASE
IF_VERBOSE(ST_VERBOSITY_LVL, if (counter % 100 == 0) display_progress(verbose_stream()););
#endif
if (counter > max_pivots)
return l_undef;
}
}
void reset_conflict() {
m_conflict_eq_idx = UINT_MAX;
}
unsigned_vector m_cached_basic;
/**
\brief When using floating point numbers, we may reach an "invalid" basis.
We say a basis is invalid the corresponding matrix is singular.
To avoid this problem, we cache the last "valid" basis.
*/
void cache_basis() {
m_cached_basic.reset();
unsigned_vector::iterator it = m_basic.begin();
unsigned_vector::iterator end = m_basic.end();
for (; it != end; ++it)
m_cached_basic.push_back(*it);
}
/**
\brief Restore last cached basis.
*/
void restore_basis() {
SASSERT(m_basic.size() == m_cached_basic.size());
unsigned sz = m_basic.size();
for (var x = 0; x < sz; x++) {
if (m_cached_basic[x] != UINT_MAX) {
// variables was on the basis
m_basic[x] = m_cached_basic[x];
m_inv_basic[m_basic[x]] = x;
}
else {
// variable was not on the basis
m_basic[x] = UINT_MAX;
}
}
}
lbool make_feasible_approx_then_precise() {
// find "promising" basis using approx.
cache_basis();
try {
set_mode(true, m_use_asserted);
unsigned limit = m_equations.size() * m_approx_threshold_factor;
lbool r = make_feasible_core(limit);
if (r == l_undef) {
// approximation failed to satisfy all constraints.
// so, disable it.
m_approx_forbidden = true;
}
}
catch (lu_exception) {
IF_VERBOSE(ST_VERBOSITY_LVL, verbose_stream() << "(arith-approx-failed)\n";);
restore_basis();
}
reset_conflict(); // do not trust approx solver
// try again using precise
set_mode(false, m_use_asserted);
TRACE("make_feasible_approx", tout << "second round m_use_approx: " << m_use_approx << "\n";);
try {
// may fail because the basis computed by the approximation is not valid.
return make_feasible_core();
}
catch (lu_exception) {
restore_basis();
init_lu_mpq();
return make_feasible_core();
}
}
lbool make_feasible() {
if (inconsistent())
return l_false;
if (m_bad_vars.empty())
return l_true;
SASSERT(check_basic_assignment());
CASSERT("arith", check_bad_vars());
lbool r;
if (m_approx_threshold_factor == 0 || m_approx_forbidden)
r = make_feasible_core();
else
r = make_feasible_approx_then_precise();
CASSERT("arith", check_bad_vars());
SASSERT(r != l_true || check_bounds_satisfied());
SASSERT(check_basic_assignment());
if (r != l_true)
restore_assignment();
cleanup_int_bad_vars();
TRACE("make_feasible_result", tout << "r: " << r << "\n"; display(tout););
return r;
}
void restore_assignment() {
CASSERT("arith", check_eqs_satisfied());
CASSERT("arith", check_bad_vars());
TRACE("restore_assignment", tout << "restore_assignment:\n"; display_assignment(tout););
var_set::iterator it1 = m_approx_updated.begin();
var_set::iterator end1 = m_approx_updated.end();
for (; it1 != end1; ++it1) {
var x = *it1;
m_approx_values[x] = m_old_approx_values[x];
}
m_approx_updated.reset();
var_set::iterator it2 = m_updated.begin();
var_set::iterator end2 = m_updated.end();
for (; it2 != end2; ++it2) {
var x = *it2;
m_num_inf_manager.swap(m_values[x], m_old_values[x]);
check_int(x);
}
m_updated.reset();
SASSERT(m_approx_updated.empty());
SASSERT(m_updated.empty());
TRACE("restore_assignment", tout << "restore_assignment:\n"; display_assignment(tout););
CASSERT("arith", check_eqs_satisfied());
CASSERT("arith", check_bad_vars());
}
// -----------------------
//
// make_int_feasible
//
// -----------------------
random_gen m_rand;
void init_make_int_feasible() {
set_mode(false, false); // use derived bounds
if (m_cuts_enabled) {
move_nonbasic_to_bounds();
make_feasible();
add_cuts();
// if (make_feasible() != l_false && !m_int_bad_vars.empty())
// add_cuts();
make_feasible();
}
}
// Buffer for storing cuts that will be added.
struct new_cut_buffer {
var_buffer m_ys;
ptr_vector<linear_equation> m_eqs;
mpq_buffer m_cs;
svector<bool> m_is_lower;
new_cut_buffer(numeral_manager & m):m_cs(m) {}
void save(var y, linear_equation * eq, mpq const & c, bool is_lower) {
m_ys.push_back(y);
m_eqs.push_back(eq);
m_cs.push_back(c);
m_is_lower.push_back(is_lower);
}
unsigned size() const { return m_ys.size(); }
bool empty() const { return m_ys.empty(); }
};
void insert_cuts(new_cut_buffer & new_cuts) {
for (unsigned i = 0; i < new_cuts.size(); i++) {
m_cuts++;
unsigned eq_idx = m_equations.size();
var y = new_cuts.m_ys[i];
m_basic[y] = eq_idx;
m_inv_basic.push_back(y);
m_equations.push_back(new_cuts.m_eqs[i]);
m_eq_occs.push_back(eq_occs()); // for y
init_eq_occs(eq_idx);
mpq const & c = new_cuts.m_cs[i];
if (new_cuts.m_is_lower[i])
assert_lower(y, c, false);
else
assert_upper(y, c, false);
// register_propagation_eq(*(new_cuts.m_eqs[i]));
}
}
// dual == false ==> as*xs >= c
// dual == true ==> as*xs <= c
void add_cut(mpq_buffer & as, var_buffer & xs, mpq const & c, bool dual, new_cut_buffer & new_cuts) {
mpq val;
var y = mk_var(0);
// compute initial value of y
for (unsigned i = 0; i < xs.size(); i++) {
m_num_manager.addmul(val, as[i], m_values[xs[i]].first, val);
}
m_num_inf_manager.set(m_values[y], val);
xs.push_back(y);
as.push_back(mpq(-1));
m_num_manager.del(val);
linear_equation * new_eq = m_eq_manager.mk(as.size(), as.c_ptr(), xs.c_ptr());
TRACE("cut", tout << "new cut y" << y << " val: " << m_num_manager.to_string(val) << "\n";
m_eq_manager.display(tout, *new_eq); tout << "\n";);
new_cuts.save(y, new_eq, c, !dual);
}
void add_cuts() {
mpq k(1);
mpq c;
new_cut_buffer new_cuts(m_num_manager);
lu_mpq::dense_vector & row = m_lu_mpq.get_tmp_row(num_vars());
for (var x = 0; x < num_vars(); x++) {
if (!basic(x) || was_eliminated(x) || int_feasible(x))
continue;
row.reset();
get_row_mpq(m_basic[x], row, true, m_use_asserted, true);
if (mk_jdm_cut(k, x, row, m_mpq_buffer, m_var_buffer, c))
add_cut(m_mpq_buffer, m_var_buffer, c, false, new_cuts);
}
if (!new_cuts.empty()) {
insert_cuts(new_cuts);
init_lu();
TRACE("add_cuts", display(tout););
make_feasible();
}
m_num_manager.del(c);
}
bool is_cut_var(var x) const {
// TODO: refine... I'm using the fact that a variable introduced by a cut is not associated with an expr
return m_var2expr.get(x) == 0;
}
/**
\brief A cut variable is not "useful" if it is on the basis.
So, we garbage collect any cut that is
*/
void gc_cuts() {
}
bool is_nonslact_var(var x) const {
// TODO: refine...
expr * t = m_var2expr.get(x);
return t != 0 && is_uninterp_const(t);
}
var bb_select_rand_var() {
#if 0
// Give preference to non cut vars
#define BB_MAX_TRIES 8
unsigned counter = 0;
while (!m_int_bad_vars.empty()) {
var_set::iterator it = m_int_bad_vars.begin();
var_set::iterator end = m_int_bad_vars.end();
unsigned num_candidates = end - it;
unsigned idx = m_rand() % num_candidates;
var x = it[idx];
if (int_feasible(x))
m_int_bad_vars.erase(x);
if (!is_cut_var(x))
return x;
counter++;
if (counter >= BB_MAX_TRIES)
break;
}
if (m_int_bad_vars.empty())
return null_var;
// search for non-cut var
var x = null_var;
unsigned num = 1;
var_set::iterator it = m_int_bad_vars.begin();
var_set::iterator end = m_int_bad_vars.end();
for (; it != end; ++it) {
if (int_feasible(x))
continue;
if (is_cut_var(*it))
continue;
if (x == null_var || m_rand() % num == 0)
x = *it;
num++;
}
#endif
// pick a random variable
while (!m_int_bad_vars.empty()) {
var_set::iterator it = m_int_bad_vars.begin();
unsigned num_candidates = m_int_bad_vars.size();
unsigned idx = m_rand() % num_candidates;
var x = it[idx];
if (!int_feasible(x))
return x;
m_int_bad_vars.erase(x);
}
return null_var;
}
var_buffer m_bb_var_buffer;
var bb_select_rand_nonslack_var() {
m_bb_var_buffer.reset();
var_set::iterator it = m_int_bad_vars.begin();
var_set::iterator end = m_int_bad_vars.end();
for (; it != end; ++it) {
var x = *it;
if (int_feasible(x))
continue;
if (is_nonslact_var(x))
m_bb_var_buffer.push_back(x);
}
if (m_bb_var_buffer.empty())
return bb_select_rand_var();
unsigned idx = rand() % m_bb_var_buffer.size();
return m_bb_var_buffer[idx];
}
/**
\brief Select a variable x >= k (or x <= k) s.t. |k| <= threshold, and |k| is maximal.
*/
var bb_select_largest_smaller_than(mpz const & threshold) {
var best = null_var;
mpz k_best;
mpz aux;
var_set::iterator it = m_int_bad_vars.begin();
var_set::iterator end = m_int_bad_vars.end();
for (; it != end; ++it) {
var x = *it;
if (int_feasible(x))
continue;
if (has_lower(x) || has_upper(x))
continue;
if (m_num_manager.is_neg(m_values[x].first))
m_num_manager.ceil(m_values[x].first, aux);
else
m_num_manager.floor(m_values[x].first, aux);
m_num_manager.abs(aux);
if (m_num_manager.le(aux, threshold) && (best == null_var || m_num_manager.gt(aux, k_best))) {
best = x;
m_num_manager.swap(aux, k_best);
}
}
m_num_manager.del(k_best);
m_num_manager.del(aux);
return best;
}
var bb_select_var() {
if (m_branch_vars_only)
return bb_select_rand_nonslack_var();
else
return bb_select_rand_var();
#if 0
mpz limit1, limit2;
var x;
m_num_manager.set(limit1, 128);
x = bb_select_largest_smaller_than(limit1);
if (x != null_var)
return x;
m_num_manager.set(limit2, 1024);
x = bb_select_largest_smaller_than(limit2);
if (x != null_var)
return x;
return bb_select_rand_var();
#endif
}
bool bb_plunging(var x) {
SASSERT(!int_feasible(x));
mpq k;
m_num_inf_manager.ceil(m_values[x], k);
push();
bb_assert_decision(x, true, k);
if (make_feasible() == l_false) {
pop(1);
m_num_inf_manager.floor(m_values[x], k);
bb_assert_decision(x, false, k);
return true;
}
pop(1);
push();
m_num_inf_manager.floor(m_values[x], k);
bb_assert_decision(x, false, k);
if (make_feasible() == l_false) {
pop(1);
m_num_inf_manager.ceil(m_values[x], k);
bb_assert_decision(x, true, k);
return true;
}
pop(1);
return false;
}
var_buffer m_todo_plunging;
void bb_plunging() {
if (inconsistent())
return;
m_todo_plunging.reset();
var_set::iterator it = m_int_bad_vars.begin();
var_set::iterator end = m_int_bad_vars.end();
for (; it != end; ++it) {
var x = *it;
if (!int_feasible(x))
m_todo_plunging.push_back(x);
}
while (!m_todo_plunging.empty()) {
if (inconsistent())
return;
if (bb_plunging(m_todo_plunging.back())) {
// verbose_stream() << "plunging worked: x" << m_todo_plunging.back() << "\n";
}
m_todo_plunging.pop_back();
}
}
/**
\brief Propagation procedure for branch&bound
*/
bool bb_propagate() {
CASSERT("arith", check_eqs_satisfied() && check_bad_vars());
propagate_bounds(); // propagate_bounds
CASSERT("arith", check_eqs_satisfied() && check_bad_vars());
make_feasible(); // make sure the problem is real feasible
// bb_plunging();
return !inconsistent();
}
void bb_push(var x, bool lower, mpq const & k) {
push();
m_bb_var.push_back(x);
m_bb_lower.push_back(lower);
m_bb_first.push_back(true);
m_bb_k.push_back(mpq());
m_num_manager.set(m_bb_k.back(), k);
SASSERT(m_bb_var.size() == m_bb_lower.size());
SASSERT(m_bb_k.size() == m_bb_lower.size());
SASSERT(m_bb_first.size() == m_bb_lower.size());
}
void bb_pop() {
pop(1);
m_bb_var.pop_back();
m_bb_lower.pop_back();
m_bb_first.pop_back();
m_bb_k.pop_back();
SASSERT(m_bb_var.size() == m_bb_lower.size());
SASSERT(m_bb_k.size() == m_bb_lower.size());
SASSERT(m_bb_first.size() == m_bb_lower.size());
}
void bb_assert_decision(var x, bool lower, mpq const & k) {
if (lower) {
m_bounds.assert_decided_lower(x, k);
}
else {
m_bounds.assert_decided_upper(x, k);
}
if (above_upper(x) || below_lower(x))
m_bad_vars.insert(x);
}
void bb_branch(var x) {
m_branches++;
SASSERT(m_bad_vars.empty());
SASSERT(!m_num_inf_manager.is_int(m_values[x]));
TRACE("make_int_feasible", tout << "x" << x << " -> " << m_num_inf_manager.to_string(m_values[x]) << "\n";);
bool lower;
#if 1
if (has_lower(x) && has_upper(x))
lower = (m_rand() % 2) == 0;
else if (has_upper(x))
lower = true;
else if (has_lower(x))
lower = false;
else
lower = (m_rand() % 2) == 0;
#else
lower = (m_rand() % 2) == 0;
#endif
mpq k;
if (lower)
m_num_inf_manager.ceil(m_values[x], k);
else
m_num_inf_manager.floor(m_values[x], k);
bb_push(x, lower, k);
bb_assert_decision(x, lower, k);
TRACE("bb_branch", tout << "branching on x" << x << " " << (lower ? ">=" : "<=") << " " << m_num_manager.to_string(k) << "\n";
tout << "new-lvl: " << m_bb_var.size() << "\n";);
m_num_manager.del(k);
}
bool bb_resolve_conflict() {
SASSERT(inconsistent());
while (!m_bb_var.empty()) {
if (m_bb_first.back() == false) {
// tried both branches...
TRACE("make_int_feasible", tout << "backtracking lvl: " << m_bb_var.size() << "\n";);
bb_pop();
continue;
}
pop(1); // pop just the bounds
push();
var x = m_bb_var.back();
m_bb_first.back() = false;
SASSERT(m_bb_first.back() == false);
bool lower = m_bb_lower.back() != 0;
if (lower) {
// k <= x is inconsistent, then flip to x <= k-1
m_num_manager.dec(m_bb_k.back());
}
else {
// x <= k is inconsistent, then flip to k+1 <= x
m_num_manager.inc(m_bb_k.back());
}
lower = !lower;
m_bb_lower.back() = lower;
// assert new decision
bb_assert_decision(x, lower, m_bb_k.back());
TRACE("bb_branch", tout << "[flip] branching on x" << x << " " << (lower ? ">=" : "<=")
<< " " << m_num_manager.to_string(m_bb_k.back()) << "\n"; tout << "lvl: " << m_bb_var.size() << "\n";);
return true;
}
return false;
}
void bb_restart() {
while (!m_bb_var.empty()) {
bb_pop();
}
}
lbool bb_bounded_search(unsigned limit) {
if (inconsistent())
return l_false;
unsigned counter = 0;
while (true) {
while (!bb_propagate()) {
if (!bb_resolve_conflict())
return l_false;
}
TRACE("make_int_feasible_step", display_bounds(tout); display_assignment(tout); display_bad_vars(tout););
SASSERT(!inconsistent());
var x = bb_select_var();
// verbose_stream() << "lvl: " << m_bb_var.size() << " x" << x << "\n";
if (x == null_var)
return l_true;
bb_branch(x);
counter++;
if (counter >= limit)
break;
TRACE("make_int_feasible", tout << "selected: x" << x << "\n";);
#ifndef _EXTERNAL_RELEASE
IF_VERBOSE(ST_VERBOSITY_LVL, if (counter % 100 == 0) display_progress(verbose_stream()););
#endif
}
return l_undef;
}
/**
\brief Add bounds to all integer variables that are unbounded
*/
void bb_add_bounds(mpq const & limit) {
mpq neg_limit;
m_num_manager.set(neg_limit, limit);
m_num_manager.neg(neg_limit);
unsigned num = num_vars();
for (var x = 0; x < num; x++) {
if (is_int(x) && !was_eliminated(x)) {
if (!has_lower(x))
bb_assert_decision(x, true, neg_limit);
if (!has_upper(x))
bb_assert_decision(x, false, limit);
}
}
m_num_manager.del(neg_limit);
}
lbool make_int_feasible_core() {
lbool r = bb_bounded_search(1024*16);
bb_restart();
return r;
}
lbool bb_model_finder(unsigned range, unsigned num_branches) {
mpq limit;
m_num_manager.set(limit, range);
push(); // protect state
bb_add_bounds(limit);
lbool r = bb_bounded_search(num_branches);
if (r == l_false)
r = l_undef;
m_num_manager.del(limit);
pop(1); // restore state
return r;
}
lbool make_int_feasible() {
if (inconsistent())
return l_false;
init_make_int_feasible();
TRACE("make_int_feasible_begin", display(tout););
TRACE("make_int_feasible_begin", display_int_infeasible_rows(tout););
unsigned num_branches = m_int_bad_vars.size() * 2;
bool find_model = true;
unsigned range = 32;
lbool r;
while (true) {
SASSERT(scope_lvl() == 0);
if (find_model)
r = bb_model_finder(range, num_branches);
else
r = bb_bounded_search(num_branches);
if (r != l_undef)
break;
find_model = !find_model;
bb_restart();
// init_make_int_feasible();
num_branches += (m_equations.size() / 10) + 1;
}
// lbool r = bb_model_finder(128);
TRACE("make_int_feasible_result", tout << "r: " << r << "\n"; display(tout););
#if 0
switch (r) {
case l_true: verbose_stream() << "sat\n"; break;
case l_undef: verbose_stream() << "unknown\n"; break;
case l_false: verbose_stream() << "unsat\n"; break;
}
#endif
return r;
}
// -----------------------
//
// Preprocessing
//
// -----------------------
void register_propagation_eq(linear_equation const & eq) {
// copy coeffs and vars to temporary buffers just to be safe
m_var_buffer.reset();
m_mpz_buffer.reset();
unsigned sz = eq.size();
for (unsigned j = 0; j < sz; j++) {
m_mpz_buffer.push_back(eq.a(j));
m_var_buffer.push_back(eq.x(j));
}
m_bounds.mk_eq(m_mpz_buffer.size(), m_mpz_buffer.c_ptr(), m_var_buffer.c_ptr());
}
// Copy constraints (only linear_equations at this point) to m_bounds.
void init_bound_propagation_cnstrs() {
SASSERT(scope_lvl() == 0);
m_bounds.del_constraints();
unsigned num_eqs = m_equations.size();
for (unsigned i = 0; i < num_eqs; i++) {
linear_equation const & eq = *(m_equations[i]);
register_propagation_eq(eq);
}
}
/**
\brief Select a (unconstrained) variable in eq for elimination.
If there is more than one option, then select the one with the fewest number of occurrences.
If eq does not contain an unconstrained variable then return null_var.
An integer variable is selected only if it has unary coefficient, and eq does not contain real variables.
*/
var choose_elim_var(linear_equation const & eq, vector<unsigned_vector> const & use_list) const {
bool has_real = false;
unsigned sz = eq.size();
for (unsigned i = 0; i < sz; i++) {
if (!is_int(eq.x(i))) {
has_real = true;
break;
}
}
var best_x = null_var;
for (unsigned i = 0; i < sz; i++) {
var x = eq.x(i);
if (!is_unconstrained(x))
continue;
if (is_int(x)) {
if (has_real)
continue;
if (!m_num_manager.is_one(eq.a(i)) && !m_num_manager.is_minus_one(eq.a(i)))
continue;
}
if (best_x == null_var || use_list[x].size() < use_list[best_x].size())
best_x = x;
}
return best_x;
}
/**
\brief Update use list for replacing eq with new_eq, the use list of var except is not touched.
*/
void update_use_list(unsigned eq_idx, linear_equation const & eq, linear_equation const & new_eq, vector<unsigned_vector> & use_list,
var except) {
unsigned sz = eq.size();
unsigned new_sz = new_eq.size();
unsigned j = 0;
unsigned new_j = 0;
while (true) {
if (j == sz) {
// add remaining variables in new_eq to use list
for (; new_j < new_sz; new_j++)
use_list[new_eq.x(new_j)].push_back(eq_idx);
break;
}
if (new_j == new_sz) {
// remove remaining variables in eq from use list
for (; j < sz; j++) {
var x = eq.x(j);
if (x != except)
use_list[x].erase(eq_idx);
}
break;
}
var x = eq.x(j);
var new_x = new_eq.x(new_j);
if (x < new_x) {
// variable x was removed
if (x != except)
use_list[x].erase(eq_idx);
j++;
}
else if (x > new_x) {
// variable new_x was added
use_list[new_x].push_back(eq_idx);
new_j++;
}
else {
// keep variable
j++;
new_j++;
}
}
}
/**
\brief Eliminate x from all equations but eq_idx.
*/
void eliminate_var_from_other_eqs(var x, unsigned eq_idx, vector<unsigned_vector> & use_list) {
linear_equation * eq1 = m_equations[eq_idx];
unsigned i1 = eq1->pos(x);
SASSERT(i1 != UINT_MAX);
mpz b1;
m_num_manager.set(b1, eq1->a(i1));
m_num_manager.neg(b1);
unsigned_vector & occs = use_list[x];
unsigned_vector::iterator it = occs.begin();
unsigned_vector::iterator end = occs.end();
for (; it != end; ++it) {
unsigned eq2_idx = *it;
if (eq_idx == eq2_idx)
continue;
linear_equation * eq2 = m_equations[eq2_idx];
unsigned i2 = eq2->pos(x);
SASSERT(i2 != UINT_MAX);
mpz const & b2 = eq2->a(i2);
linear_equation * new_eq2 = m_eq_manager.mk(b2, *eq1, b1, *eq2);
CTRACE("arith_preprocess_new_bug", new_eq2->pos(x) != UINT_MAX,
tout << "x" << x << "\n";
tout << m_num_manager.to_string(b2) << " * "; m_eq_manager.display(tout, *eq1); tout << "\n";
tout << m_num_manager.to_string(b1) << " * "; m_eq_manager.display(tout, *eq2); tout << "\n";
tout << "----->\n";
m_eq_manager.display(tout, *new_eq2);
tout << "\n";);
SASSERT(new_eq2 != 0);
SASSERT(new_eq2->pos(x) == UINT_MAX);
// update use list
update_use_list(eq2_idx, *eq2, *new_eq2, use_list, x);
m_eq_manager.del(eq2);
m_equations[eq2_idx] = new_eq2;
}
//
m_num_manager.del(b1);
occs.reset();
occs.push_back(eq_idx); // x occurs only in eq_idx
}
void eliminate_var(var x, unsigned eq_idx, vector<unsigned_vector> & use_list) {
eliminate_var_from_other_eqs(x, eq_idx, use_list);
linear_equation * eq1 = m_equations[eq_idx];
// remove x and eq_idx from use list
unsigned_vector & occs = use_list[x];
occs.reset();
unsigned sz = eq1->size();
for (unsigned i = 0; i < sz; i++) {
var x_i = eq1->x(i);
use_list[x_i].erase(eq_idx);
}
// mark x as eliminated.
m_eliminated[x] = true;
m_elim_vars.push_back(elim_var_info(x, eq1));
}
void mk_tmp_use_list(vector<unsigned_vector> & use_list) {
use_list.resize(num_vars());
unsigned num_eqs = m_equations.size();
for (unsigned eq_idx = 0; eq_idx < num_eqs; ++eq_idx) {
linear_equation const & eq = *(m_equations[eq_idx]);
unsigned sz = eq.size();
for (unsigned i = 0; i < sz; i++)
use_list[eq.x(i)].push_back(eq_idx);
}
}
void remove_equations(svector<bool> const & to_remove) {
unsigned num_eqs = m_equations.size();
unsigned j = 0;
for (unsigned i = 0; i < num_eqs; i++) {
if (to_remove[i])
continue;
linear_equation * eq = m_equations[i];
var x_b = m_inv_basic[i];
SASSERT(m_basic[x_b] == i);
m_basic[x_b] = j;
m_equations[j] = eq;
m_inv_basic[j] = x_b;
j++;
}
TRACE("arith_preprocess_bug", for (unsigned i = 0; i < j; i++) display_eq_basics(tout, i););
m_equations.shrink(j);
m_inv_basic.shrink(j);
}
void elim_unconstrained_vars() {
SASSERT(elim_var_applicable());
// build temporary use list
vector<unsigned_vector> use_list;
mk_tmp_use_list(use_list);
// eliminate
unsigned old_eliminated_vars = m_eliminated_vars;
unsigned num_eqs = m_equations.size();
svector<bool> to_remove;
to_remove.resize(num_eqs, false);
for (unsigned eq_idx = 0; eq_idx < num_eqs; ++eq_idx) {
linear_equation * eq = m_equations[eq_idx];
var x = choose_elim_var(*eq, use_list);
if (x == null_var)
continue;
var x_b = m_inv_basic[eq_idx];
SASSERT(m_basic[x_b] == eq_idx);
m_basic[x_b] = UINT_MAX; // x_b is not basic anymore.
m_eliminated_vars++;
TRACE("arith_preprocess", tout << "eliminating: x" << x << " using "; m_eq_manager.display(tout, *eq); tout << "\n";);
SASSERT(!was_eliminated(x));
SASSERT(!basic(x));
eliminate_var(x, eq_idx, use_list);
to_remove[eq_idx] = true;
}
report_st_progress(":elim-tableau-vars", m_eliminated_vars - old_eliminated_vars);
remove_equations(to_remove);
}
// Compress basic variables ids after rows have been eliminated.
void compress_basic_ids() {
unsigned_vector id2var;
unsigned num = num_vars();
for (var x = 0; x < num; x++) {
if (basic(x)) {
unsigned id = m_basic[x];
id2var.reserve(id+1, null_var);
id2var[id] = x;
}
}
unsigned next_id = 0;
unsigned sz = id2var.size();
for (unsigned i = 0; i < sz; i++) {
var x = id2var[i];
if (x != null_var) {
m_basic[x] = next_id;
m_inv_basic[next_id] = x;
next_id++;
}
}
}
/**
Make int_eqs[eq_idx] == true if eq_idx constains only integer variables.
*/
void mark_int_eqs(svector<bool> & int_eqs) {
unsigned num_eqs = m_equations.size();
int_eqs.reset();
for (unsigned i = 0; i < num_eqs; i++) {
linear_equation const & eq = *(m_equations[i]);
unsigned sz = eq.size();
unsigned j;
for (j = 0; j < sz; j++) {
if (!is_int(eq.x(j)))
break;
}
int_eqs.push_back(j == sz);
}
}
/**
\brief Eliminate integer variable x by producing the equation c * eq_idx1 + d * eq_idx2.
The coefficient of x in this equation is 1.
This function replaces eq_idx1 with c * eq_idx1 + d * eq_idx2, and invokes eliminate_var(x, eq_idx1, use_list)
*/
void eliminate_int_var(var x, unsigned eq_idx1, unsigned eq_idx2, mpz const & c, mpz const & d, vector<unsigned_vector> & use_list) {
linear_equation * eq1 = m_equations[eq_idx1];
linear_equation * new_eq1 = m_eq_manager.mk(c, *eq1, d, *(m_equations[eq_idx2]));
SASSERT(new_eq1->pos(x) != UINT_MAX);
SASSERT(m_num_manager.is_one(new_eq1->a(new_eq1->pos(x)))); // coefficient of x in the new equation is one.
update_use_list(eq_idx1, *eq1, *new_eq1, use_list, null_var);
m_eq_manager.del(eq1);
m_equations[eq_idx1] = new_eq1;
eliminate_var(x, eq_idx1, use_list);
}
void elim_int_unconstrained_vars() {
SASSERT(elim_var_applicable());
vector<unsigned_vector> use_list;
mk_tmp_use_list(use_list);
unsigned old_eliminated_vars = m_eliminated_vars;
svector<bool> int_eqs;
mark_int_eqs(int_eqs);
mpz c, d, g;
numeral_manager & nm = m_num_manager;
unsigned num_eqs = m_equations.size();
svector<bool> to_remove;
to_remove.resize(num_eqs, false);
for (var x = 0; x < num_vars(); x++) {
if (was_eliminated(x) || !is_int(x) || !is_unconstrained(x))
continue;
TRACE("elim_int", tout << "visiting x" << x << "\n";);
unsigned_vector & occs = use_list[x];
unsigned_vector::iterator it = occs.begin();
unsigned_vector::iterator end = occs.end();
for (; it != end; ++it) {
unsigned eq_idx1 = *it;
if (!int_eqs[eq_idx1])
continue; // skip: contains real variables...
linear_equation const & eq1 = *(m_equations[eq_idx1]);
SASSERT(elim_var_applicable(eq_idx1));
unsigned pos1 = eq1.pos(x);
if (pos1 == UINT_MAX)
continue;
mpz const & a1 = eq1.a(pos1);
if (nm.is_one(a1) || nm.is_minus_one(a1)) {
// easy case
m_eliminated_vars++;
var x_b = m_inv_basic[eq_idx1];
SASSERT(m_basic[x_b] == eq_idx1);
m_basic[x_b] = UINT_MAX; // x_b is not basic anymore.
eliminate_var(x, eq_idx1, use_list);
to_remove[eq_idx1] = true;
break;
}
unsigned_vector::iterator it2 = it+1;
for (; it2 != end; ++it2) {
unsigned eq_idx2 = *it2;
if (!int_eqs[eq_idx2])
continue;
linear_equation const & eq2 = *(m_equations[eq_idx2]);
SASSERT(elim_var_applicable(eq_idx2));
unsigned pos2 = eq2.pos(x);
if (pos2 == UINT_MAX)
continue;
mpz const & a2 = eq2.a(pos2);
if (nm.is_one(a2) || nm.is_minus_one(a2)) {
// easy case
m_eliminated_vars++;
var x_b = m_inv_basic[eq_idx2];
SASSERT(m_basic[x_b] == eq_idx2);
m_basic[x_b] = UINT_MAX; // x_b is not basic anymore.
eliminate_var(x, eq_idx2, use_list);
to_remove[eq_idx2] = true;
goto elim_int_var_processed;
}
nm.gcd(a1, a2, c, d, g);
if (nm.is_one(g)) {
TRACE("elim_int", tout << "found candidate eqs for eliminating x" << x << "\n";
tout << "a1: " << nm.to_string(a1) << ", a2: " << nm.to_string(a2) << "\n";
tout << "c: " << nm.to_string(c) << ", d: " << nm.to_string(d) << ", g: " << nm.to_string(g) << "\n";
m_eq_manager.display(tout, eq1); tout << "\n";
m_eq_manager.display(tout, eq2); tout << "\n";);
var x_b2 = m_inv_basic[eq_idx2];
// c * eq1 + d * eq2 --> generates an equation where x has coefficient 1.
// So, we can eliminate x, delete eq1 (or eq2), and replace x everywhere with c*eq1 + d*eq2.
eliminate_int_var(x, eq_idx1, eq_idx2, c, d, use_list);
var x_b = m_inv_basic[eq_idx1];
SASSERT(m_basic[x_b] == eq_idx1);
m_basic[x_b] = UINT_MAX; // x_b is not basic anymore.
to_remove[eq_idx1] = true;
m_eliminated_vars++;
// the basic variable of eq_idx2 may occur in many equations... eliminate it from all but eq_idx2
// the idea is to make sure that elim_var_applicable is true for all equations.
linear_equation const & new_eq2 = *(m_equations[eq_idx2]);
SASSERT(new_eq2.pos(x_b2) != UINT_MAX); // eq2 still contains its basic variable
SASSERT(elim_var_applicable(eq_idx2)); // the only basic variable in eq2 is still x_b2
eliminate_var_from_other_eqs(x_b2, eq_idx2, use_list);
goto elim_int_var_processed;
}
}
}
elim_int_var_processed:
;
}
nm.del(c);
nm.del(d);
nm.del(g);
report_st_progress(":elim-int-tableau-vars", m_eliminated_vars - old_eliminated_vars);
remove_equations(to_remove);
}
// assert axiom x == 0
void assert_eq_zero_axiom(var x) {
mpq zero(0);
assert_upper(x, zero, false);
assert_lower(x, zero, false);
}
// Eliminate variables fixed at zero from equations.
bool elim_zero_vars() {
bool eliminated = false;
SASSERT(scope_lvl() == 0);
unsigned num_eqs = m_equations.size();
svector<bool> to_remove;
to_remove.resize(num_eqs, false);
for (unsigned eq_idx = 0; eq_idx < num_eqs; eq_idx++) {
linear_equation * eq = m_equations[eq_idx];
unsigned sz = eq->size();
unsigned k;
for (k = 0; k < sz; k++) {
var x_k = eq->x(k);
// To eliminate a basic variable, we have to
// create the LU factorization, and extract the actual row, and then select
// a variable to enter the basis. Perhaps, it is not worth to do that at this point.
// It is better to wait the variable to leave the basis.
if (!basic(x_k) && m_bounds.is_zero(x_k))
break;
}
if (k == sz)
continue;
// equation has zero variables
m_var_buffer.reset();
m_mpz_buffer.reset();
for (k = 0; k < sz; k++) {
var x_k = eq->x(k);
if (!basic(x_k) && m_bounds.is_zero(x_k))
continue;
// keep non-zero vars and the basic variable
m_var_buffer.push_back(x_k);
m_mpz_buffer.push_back(eq->a(k));
}
SASSERT(m_var_buffer.size() < sz);
eliminated = true;
unsigned new_sz = m_var_buffer.size();
SASSERT(new_sz != 0);
linear_equation * new_eq = m_eq_manager.mk(new_sz, m_mpz_buffer.c_ptr(), m_var_buffer.c_ptr());
if (new_eq != 0) {
m_equations[eq_idx] = new_eq;
}
else {
assert_eq_zero_axiom(m_var_buffer[0]);
SASSERT(new_sz == 1);
to_remove[eq_idx] = true;
var x_b = m_inv_basic[eq_idx];
m_basic[x_b] = UINT_MAX;
m_eliminated_vars++;
}
m_eq_manager.del(eq);
}
remove_equations(to_remove);
return eliminated;
}
bool propagate_bounds() {
unsigned qhead = m_bounds.qhead();
m_bounds.propagate();
if (m_bounds.inconsistent())
return false;
TRACE("propagate_bounds", tout << "propagate_bounds, m_use_asserted: " << m_use_asserted << "\n";);
if (!m_use_asserted) {
// update bad vars using new bounds
bound_propagator::trail_iterator it = m_bounds.begin_trail() + qhead;
bound_propagator::trail_iterator end = m_bounds.end_trail();
for (; it != end; ++it) {
var x = it->x();
bool is_lower = it->is_lower();
TRACE("propagate_bounds", tout << "propagated x" << x << " is_lower: " << is_lower << "\n";);
if (is_lower) {
if (below_lower(x))
m_bad_vars.insert(x);
}
else {
if (above_upper(x))
m_bad_vars.insert(x);
}
}
}
return true;
}
void init_eq_occs(unsigned eq_idx) {
linear_equation const & eq = *(m_equations[eq_idx]);
unsigned sz = eq.size();
for (unsigned i = 0; i < sz; i++)
m_eq_occs[eq.x(i)].push_back(eq_occ(eq_idx, i));
}
void init_eq_occs() {
m_eq_occs.reset();
m_eq_occs.resize(num_vars());
unsigned num_eqs = m_equations.size();
for (unsigned eq_idx = 0; eq_idx < num_eqs; ++eq_idx) {
init_eq_occs(eq_idx);
}
}
/**
\brief We apply variable elimination only if
For all equation eq, the only and only one basic variable x_b in eq is m_inv_basic[eq].
This condition prevents us from ending up with a basis that corresponds to a
singular matrix.
After compilation, the set of equations always satisfies this condition, since
the basic variable "owning" each equation is the slack introduced during compilation.
*/
bool elim_var_applicable(unsigned eq_idx) const {
linear_equation const & eq = *(m_equations[eq_idx]);
unsigned sz = eq.size();
for (unsigned i = 0; i < sz; i++) {
var x = eq.x(i);
if (basic(x) && m_basic[x] != eq_idx)
return false;
}
return true;
}
bool elim_var_applicable() const {
unsigned num_eqs = m_equations.size();
for (unsigned eq_idx = 0; eq_idx < num_eqs; eq_idx++) {
if (!elim_var_applicable(eq_idx))
return false;
}
return true;
}
void preprocess() {
CASSERT("arith", check_invariant());
IF_VERBOSE(ST_VERBOSITY_LVL, display_status(verbose_stream()););
TRACE("arith_preprocess", tout << "eqs before elimination\n"; display_eqs(tout););
if (m_elim_vars_enabled && elim_var_applicable()) {
elim_unconstrained_vars();
// TODO: the following transformation is producing a singular basis
if (m_elim_int_vars_enabled)
elim_int_unconstrained_vars();
}
init_bound_propagation_cnstrs();
if (!propagate_bounds())
return; // inconsistency detected.
IF_VERBOSE(ST_VERBOSITY_LVL, display_status(verbose_stream()););
TRACE("arith_preprocess", tout << "eqs after elimination\n"; display_eqs(tout); display_eliminated_vars(tout););
#if 1
if (elim_zero_vars()) {
init_bound_propagation_cnstrs();
propagate_bounds();
IF_VERBOSE(ST_VERBOSITY_LVL, display_status(verbose_stream()););
}
#endif
init_eq_occs();
CASSERT("arith", check_invariant());
TRACE("arith_preprocess_bug", for (unsigned i = 0; i < m_equations.size(); i++) display_eq_basics(tout, i););
init_lu();
TRACE("arith_int_preprocess", display_unconstrained_vars(tout); display_eqs(tout););
CASSERT("arith", check_invariant());
TRACE("arith_preprocess", display(tout););
// tst_random_pivoting(m_lu_double, 2000);
// tst_random_pivoting(m_lu_mpq, 2000);
}
// -----------------------
//
// Cuts
//
// -----------------------
/**
\brief Move non-basic variables to bounds.
*/
void move_nonbasic_to_bounds() {
mpq_inf old_val;
unsigned num = num_vars();
for (var x = 0; x < num; x++) {
if (basic(x))
continue;
if (at_lower(x) || at_upper(x))
continue;
m_num_inf_manager.set(old_val, m_values[x]);
if (has_lower(x))
lower2value(x);
else if (has_upper(x))
upper2value(x);
else
continue;
update_dependents(m_lu_mpq, m_values, m_num_inf_manager, x, old_val);
}
m_num_inf_manager.del(old_val);
}
bool is_jdm_cut_applicable(mpq const & k, var x_i, lu_mpq::dense_vector const & row) {
if (!m_num_inf_manager.is_rational(m_values[x_i]))
return false;
numeral_manager & nm = m_num_manager;
mpq a;
nm.mul(m_values[x_i].first, k, a);
if (nm.is_int(a)) {
nm.del(a);
return false;
}
// check if cut is applicable
lu_mpq::dense_vector::iterator it = row.begin_non_zeros();
lu_mpq::dense_vector::iterator end = row.end_non_zeros();
for (; it != end; ++it) {
var x_j = *it;
if (!is_int(x_j)) {
TRACE("cut", tout << "failed: row constains non int var\n";);
return false;
}
m_num_manager.mul(k, row[x_j], a);
if (nm.is_int(a)) {
// integer coeffs can be ignored when x_j is assigned to an integer
if (!m_num_inf_manager.is_int(m_values[x_j]))
return false;
continue;
}
if (!at_lower(x_j) && !at_upper(x_j)) {
TRACE("cut", tout << "failed: variable is not at bound x" << x_j << "\n";);
return false;
}
}
nm.del(a);
return true;
}
bool mk_jdm_cut(mpq const & k, var x_i, lu_mpq::dense_vector const & row, mpq_buffer & as, var_buffer & xs, mpq & c) {
if (!is_jdm_cut_applicable(k, x_i, row))
return false;
numeral_manager & nm = m_num_manager;
SASSERT(nm.is_pos(k));
as.reset();
xs.reset();
nm.reset(c);
mpq a, a_prime;
nm.mul(m_values[x_i].first, k, c);
nm.ceil(c, c);
lu_mpq::dense_vector::iterator it = row.begin_non_zeros();
lu_mpq::dense_vector::iterator end = row.end_non_zeros();
for (; it != end; ++it) {
var x_j = *it;
nm.mul(row[x_j], k, a);
if (nm.is_int(a)) {
// I
nm.addmul(c, a, m_values[x_j].first, c);
// ignore monomial
}
else {
if (nm.is_neg(a)) {
if (at_lower(x_j)) {
// L-
// doesn't contribute to c.
// add -a*x_j
nm.neg(a);
as.push_back(a);
}
else {
// U-
nm.floor(a, a_prime);
nm.addmul(c, a_prime, m_values[x_j].first, c);
// new coeff floor(a_ij) - a_ij
nm.sub(a_prime, a, a_prime);
as.push_back(a_prime);
}
}
else {
if (at_lower(x_j)) {
// L+
nm.ceil(a, a_prime);
nm.addmul(c, a_prime, m_values[x_j].first, c);
// new coeff ceil(a_ij) - a_ij
nm.sub(a_prime, a, a_prime);
as.push_back(a_prime);
}
else {
// U+
// doesn't contribute to c
// add -a*x_j
nm.neg(a);
as.push_back(a);
}
}
xs.push_back(x_j);
}
}
TRACE("cut",
tout << "new cut:\n";
for (unsigned i = 0; i < xs.size(); i++) {
if (i > 0) tout << " + ";
tout << nm.to_string(as[i]) << "*x" << xs[i];
}
tout << " >= " << nm.to_string(c) << "\n";
tout << "\n";);
nm.del(a);
nm.del(a_prime);
return true;
}
bool mk_jdm_dual_cut(mpq const & k, var x_i, lu_mpq::dense_vector const & row, mpq_buffer & as, var_buffer & xs, mpq & c) {
if (!is_jdm_cut_applicable(k, x_i, row))
return false;
numeral_manager & nm = m_num_manager;
SASSERT(nm.is_pos(k));
as.reset();
xs.reset();
nm.reset(c);
mpq a, a_prime;
nm.mul(m_values[x_i].first, k, c);
nm.floor(c, c);
lu_mpq::dense_vector::iterator it = row.begin_non_zeros();
lu_mpq::dense_vector::iterator end = row.end_non_zeros();
for (; it != end; ++it) {
var x_j = *it;
nm.mul(row[x_j], k, a);
if (nm.is_int(a)) {
// I
nm.addmul(c, a, m_values[x_j].first, c);
// ignore monomial
}
else {
if (nm.is_neg(a)) {
if (at_lower(x_j)) {
// L-
nm.floor(a, a_prime);
nm.addmul(c, a_prime, m_values[x_j].first, c);
// new coeff floor(a_ij) - a_ij
nm.sub(a_prime, a, a_prime);
as.push_back(a_prime);
}
else {
// U-
// doesn't contribute to c.
// add -a*x_j
nm.neg(a);
as.push_back(a);
}
}
else {
if (at_lower(x_j)) {
// L+
// doesn't contribute to c
// add -a*x_j
nm.neg(a);
as.push_back(a);
}
else {
// U+
nm.ceil(a, a_prime);
nm.addmul(c, a_prime, m_values[x_j].first, c);
// new coeff ceil(a_ij) - a_ij
nm.sub(a_prime, a, a_prime);
as.push_back(a_prime);
}
}
xs.push_back(x_j);
}
}
TRACE("cut",
tout << "new cut:\n";
for (unsigned i = 0; i < xs.size(); i++) {
if (i > 0) tout << " + ";
tout << nm.to_string(as[i]) << "*x" << xs[i];
}
tout << " <= " << nm.to_string(c) << "\n";
tout << "\n";);
nm.del(a);
nm.del(a_prime);
return true;
}
// -----------------------
//
// Status
//
// -----------------------
unsigned matrix_size() const {
unsigned r = 0;
equations::const_iterator it = m_equations.begin();
equations::const_iterator end = m_equations.end();
for (; it != end; ++it)
r += (*it)->size();
return r;
}
void display_status(std::ostream & out) {
out << "(arith :vars " << num_vars() << " :eliminated " << m_elim_vars.size() << " :eqs " << m_equations.size()
<< " :matrix-size " << matrix_size() << ")\n";
}
// -----------------------
//
// Pretty printing
//
// -----------------------
void display_int_infeasible_rows(std::ostream & out) {
mpq c;
out << "rows of int infeasible vars:\n";
lu_mpq::dense_vector & row = m_lu_mpq.get_tmp_row(num_vars());
for (var x = 0; x < num_vars(); x++) {
if (!basic(x) || was_eliminated(x) || int_feasible(x))
continue;
row.reset();
get_row_mpq(m_basic[x], row, true, m_use_asserted, true);
out << "x" << x << " -> " << m_num_inf_manager.to_string(m_values[x]) << " : "; row.display_pol(out); out << "\n";
mk_jdm_cut(mpq(1), x, row, m_mpq_buffer, m_var_buffer, c);
mk_jdm_dual_cut(mpq(1), x, row, m_mpq_buffer, m_var_buffer, c);
}
m_num_manager.del(c);
}
/**
\brief Display unbounded variables that were not eliminated.
*/
void display_unconstrained_vars(std::ostream & out) const {
for (var x = 0; x < num_vars(); x++) {
if (!was_eliminated(x) && is_unconstrained(x)) {
out << "x" << x << " ";
}
}
out << "\n";
}
void display_ineq_cnstr(std::ostream & out, ineq_cnstr const & c) const {
out << "p" << c.m_atom << " := x" << c.m_x << " " << (c.m_lower ? ">=" : "<=") << " " << m_num_manager.to_string(c.m_k) << "\n";
}
void display_ineq_cnstrs(std::ostream & out) const {
out << "ineq-constraints:\n";
for (unsigned i = 0; i < m_ineq_cnstrs.size(); i++) {
display_ineq_cnstr(out, m_ineq_cnstrs[i]);
}
}
void display_eliminated_vars(std::ostream & out) const {
out << "eliminated variables:\n";
elim_vars::const_iterator it = m_elim_vars.begin();
elim_vars::const_iterator end = m_elim_vars.end();
for (; it != end; ++it) {
out << "x" << it->first << " --> ";
m_eq_manager.display(out, *(it->second));
out << "\n";
}
}
void display_eq_basics(std::ostream & out, unsigned eq_idx) const {
out << "eq " << eq_idx << ":";
linear_equation const & eq = *(m_equations[eq_idx]);
unsigned sz = eq.size();
for (unsigned i = 0; i < sz; i++) {
if (basic(eq.x(i)))
out << " x" << eq.x(i) << "|" << m_basic[eq.x(i)];
}
out << "\n";
}
void display_eqs(std::ostream & out) const {
out << "equations:\n";
for (unsigned i = 0; i < m_equations.size(); i++) {
linear_equation const & eq = *(m_equations[i]);
out << "eq " << i << ": ";
m_eq_manager.display(out, eq);
out << "\n";
}
}
void display_basic(std::ostream & out) const {
out << "basic:";
for (unsigned x = 0; x < num_vars(); x++) {
if (basic(x))
out << " (x" << x << " -> " << m_basic[x] << ")";
}
out << "\n";
}
void display_definitions(std::ostream & out) const {
out << "definitions:\n";
for (unsigned i = 0; i < num_vars(); i++) {
out << "x" << std::left << std::setw(6) << i << " : ";
if (m_var2expr.get(i) == 0)
out << "<internal>";
else
out << mk_ismt2_pp(m_var2expr.get(i), m, 10);
out << "\n";
}
}
template<typename LU>
void display_LU(std::ostream & out, LU const & _lu) const {
_lu.display(out);
_lu.display(out, &m_inv_basic);
}
void display_bad_vars(std::ostream & out) const {
if (m_bad_vars.empty()) {
out << "all constraints are satisfied\n";
return;
}
out << "bad vars: ";
var_set::iterator it = m_bad_vars.begin();
var_set::iterator end = m_bad_vars.end();
for (; it != end; ++it) {
out << "x" << *it << " ";
}
out << "\n";
}
void display_int_bad_vars(std::ostream & out) const {
if (m_int_bad_vars.empty()) {
out << "integrality constraints are satisfied\n";
return;
}
out << "int bad vars: ";
var_set::iterator it = m_int_bad_vars.begin();
var_set::iterator end = m_int_bad_vars.end();
for (; it != end; ++it) {
out << "x" << *it << " ";
}
out << "\n";
}
void display_assignment(std::ostream & out) const {
out << "assignment:\n";
for (var x = 0; x < num_vars(); x++) {
if (was_eliminated(x))
continue;
if (m_use_approx) {
out << "x" << x << " -> " << m_approx_values[x];
}
else {
out << "x" << x << " -> " << m_num_inf_manager.to_string(m_values[x]);
}
if (is_int(x))
out << " *";
out << "\n";
}
}
void display_LU(std::ostream & out) const {
if (m_use_approx)
m_lu_double.display(out);
else
m_lu_mpq.display(out);
}
void display_bounds(std::ostream & out) const {
// display only bounds of variables that were not eliminated.
out << "bounds:\n";
for (var x = 0; x < num_vars(); x++) {
if (was_eliminated(x))
continue;
if (m_use_approx)
bp().display_var_bounds(out, x, true, false);
else
bp().display_var_bounds(out, x, false, true);
out << "\n";
}
}
void display(std::ostream & out) const {
display_definitions(out);
display_basic(out);
display_eqs(out);
display_bounds(out);
display_LU(out);
display_assignment(out);
display_bad_vars(out);
display_int_bad_vars(out);
display_ineq_cnstrs(out);
display_eliminated_vars(out);
}
// -----------------------
//
// Invariants
//
// -----------------------
bool check_bounds_satisfied() const {
for (var x = 0; x < num_vars(); x++) {
CTRACE("arith_bug", above_upper(x) || below_lower(x), tout << "bad var: x" << x << "\n"; display(tout););
SASSERT(!above_upper(x));
SASSERT(!below_lower(x));
}
return true;
}
/**
\brief Check if m_bad_vars contains all variables not satisfying their bounds.
*/
bool check_bad_vars() const {
if (!inconsistent()) {
for (var x = 0; x < num_vars(); x++) {
if (above_upper(x) || below_lower(x)) {
CTRACE("arith_bug", !m_bad_vars.contains(x), tout << "missing bad var: x" << x << "\n"; display(tout););
SASSERT(m_bad_vars.contains(x));
}
}
}
return true;
}
/**
\brief When precise mode is on, this method checks if the assignment of the basic
variables is consistent with the non-basic ones.
*/
bool check_basic_assignment_core() {
if (m_use_approx)
return true;
unsigned num = m_equations.size();
lu_mpq::dense_vector & row = m_lu_mpq.get_tmp_row(num_vars());
mpq_inf val;
mpq_inf aux;
for (unsigned eq_idx = 0; eq_idx < num; eq_idx++) {
var x_b = m_inv_basic[eq_idx];
SASSERT(m_basic[x_b] == eq_idx);
row.reset();
get_row_mpq(eq_idx, row, true, m_use_asserted, true);
m_num_inf_manager.reset(val);
lu_mpq::dense_vector::iterator it = row.begin_non_zeros();
lu_mpq::dense_vector::iterator end = row.end_non_zeros();
for (; it != end; ++it) {
var x_n = *it;
SASSERT(nonbasic(x_n));
if (m_num_manager.is_zero(row[x_n]))
continue;
m_num_inf_manager.mul(m_values[x_n], row[x_n], aux);
m_num_inf_manager.add(val, aux, val);
}
m_num_inf_manager.neg(val);
CTRACE("arith_bug", !m_num_inf_manager.eq(m_values[x_b], val),
tout << "x_b: " << x_b << " val: " << m_num_inf_manager.to_string(m_values[x_b])
<< ", computed val: " << m_num_inf_manager.to_string(val) << "\n";
tout << "row: "; row.display_pol(tout); tout << "\n";
display(tout););
SASSERT(m_num_inf_manager.eq(m_values[x_b], val));
}
m_num_inf_manager.del(val);
m_num_inf_manager.del(aux);
return true;
}
bool check_basic_assignment() const {
return const_cast<imp*>(this)->check_basic_assignment_core();
}
bool check_eqs_satisfied_core() {
mpq_inf val;
mpq_inf aux;
unsigned num = m_equations.size();
for (unsigned eq_idx = 0; eq_idx < num; eq_idx++) {
linear_equation const & eq = *(m_equations[eq_idx]);
m_num_inf_manager.reset(val);
unsigned sz = eq.size();
for (unsigned i = 0; i < sz; i++) {
var x = eq.x(i);
mpz const & a = eq.a(i);
m_num_inf_manager.mul(m_values[x], a, aux);
m_num_inf_manager.add(val, aux, val);
}
CTRACE("arith_bug", !m_num_inf_manager.is_zero(val),
m_eq_manager.display(tout, eq); tout << "\nval: " << m_num_inf_manager.to_string(val) << "\n";
display(tout););
SASSERT(m_num_inf_manager.is_zero(val));
}
m_num_inf_manager.del(val);
m_num_inf_manager.del(aux);
return true;
}
bool check_eqs_satisfied() const {
if (m_use_approx)
return true;
return const_cast<imp*>(this)->check_eqs_satisfied_core();
}
// Debugging: check eq contains at least one basic variable.
bool check_has_basic(unsigned eq_idx) const {
linear_equation const & eq = *(m_equations[eq_idx]);
for (unsigned i = 0; i < eq.size(); i++) {
if (basic(eq.x(i)))
return true;
}
TRACE("arith_bug", m_eq_manager.display(tout, eq); tout << "\n";);
UNREACHABLE();
return false;
}
// Debugging: check eq does not contain eliminated variables
bool check_no_elim_var(linear_equation const & eq) const {
for (unsigned i = 0; i < eq.size(); i++) {
SASSERT(!was_eliminated(eq.x(i)));
}
return true;
}
svector<bool> m_visited;
bool check_invariant() const {
SASSERT(m_inv_basic.size() == m_equations.size());
svector<bool> & visited = const_cast<imp*>(this)->m_visited;
visited.reserve(m_equations.size(), false);
unsigned num_basic = 0;
// check basic variables
for (unsigned x = 0; x < num_vars(); x++) {
if (basic(x)) {
SASSERT(m_inv_basic[m_basic[x]] == x);
num_basic++;
SASSERT(!was_eliminated(x));
SASSERT(m_basic[x] < m_equations.size());
SASSERT(!visited[m_basic[x]]);
visited[m_basic[x]] = true;
}
}
SASSERT(num_basic == m_equations.size());
for (unsigned x = 0; x < num_vars(); x++) {
if (basic(x)) {
SASSERT(visited[m_basic[x]]);
visited[m_basic[x]] = false;
}
}
// check all eqs contain at least one basic variable and no eliminated variable
for (unsigned i = 0; i < m_equations.size(); i++) {
SASSERT(m_basic[m_inv_basic[i]] == i);
linear_equation const & eq = *(m_equations[i]);
SASSERT(check_no_elim_var(eq));
SASSERT(check_has_basic(i));
}
// check ineq_cnstrs
for (unsigned i = 0; i < m_ineq_cnstrs.size(); i++) {
SASSERT(m_atom2ineq_cnstr[m_ineq_cnstrs[i].m_atom] == i);
SASSERT(!was_eliminated(m_ineq_cnstrs[i].m_x));
}
// check m_atom2ineq_cnstr
for (unsigned i = 0; i < m_atom2ineq_cnstr.size(); i++) {
if (m_atom2ineq_cnstr[i] != UINT_MAX) {
SASSERT(m_ineq_cnstrs[m_atom2ineq_cnstr[i]].m_atom == i);
}
}
return true;
}
};
arith::arith(ast_manager & m, params_ref const & p):
m_params(p) {
m_imp = alloc(imp, m, p);
}
arith::~arith() {
imp * d = m_imp;
#pragma omp critical (smt_arith)
{
m_imp = 0;
}
dealloc(d);
}
void arith::updt_params(params_ref const & p) {
m_params = p;
m_imp->updt_params(p);
}
void arith::assert_axiom(expr * t, bool neg) {
TRACE("arith", tout << "asserting neg: " << neg << "\n" << mk_ismt2_pp(t, m_imp->m) << "\n";);
m_imp->mk_ineq(t, neg, null_atom_id);
}
void arith::mk_atom(expr * t, atom_id p) {
TRACE("arith", tout << "mk_atom p: " << p << "\n" << mk_ismt2_pp(t, m_imp->m) << "\n";);
m_imp->mk_ineq(t, false, p);
}
void arith::asserted(atom_id id, bool is_true) {
}
bool arith::inconsistent() const {
// TODO
return false;
}
void arith::push() {
}
void arith::pop(unsigned num_scopes) {
}
void arith::set_cancel(bool f) {
#pragma omp critical (smt_arith)
{
if (m_imp)
m_imp->set_cancel(f);
}
}
void arith::reset() {
ast_manager & m = m_imp->m;
#pragma omp critical (smt_arith)
{
dealloc(m_imp);
m_imp = alloc(imp, m, m_params);
}
}
void arith::preprocess() {
m_imp->preprocess();
}
void arith::simplify() {
}
void arith::display(std::ostream & out) const {
m_imp->display(out);
}
void arith::collect_statistics(statistics & st) const {
m_imp->collect_statistics(st);
}
void arith::reset_statistics() {
m_imp->reset_statistics();
}
lbool arith::check() {
m_imp->make_feasible();
return m_imp->make_int_feasible();
}
void arith::mk_model(model * md) {
m_imp->mk_model(md);
}
};