#pragma once
#include "util/numeral_buffer.h"
#include "ast/ast_ll_pp.h"
#include "ast/well_sorted.h"
#include "ast/ast_smt2_pp.h"
namespace smt {
template<typename Ext>
void theory_arith<Ext>::move_non_base_vars_to_bounds() {
theory_var num = get_num_vars();
for (theory_var v = 0; v < num; ++v) {
if (is_non_base(v)) {
bound * l = lower(v);
bound * u = upper(v);
const inf_numeral & val = get_value(v);
if (l != nullptr && u != nullptr) {
if (val != l->get_value() && val != u->get_value())
set_value(v, l->get_value());
}
else if (l != nullptr) {
if (val != l->get_value())
set_value(v, l->get_value());
}
else if (u != nullptr) {
if (val != u->get_value())
set_value(v, u->get_value());
}
else {
if (is_int(v) && !val.is_int()) {
inf_numeral new_val(floor(val));
set_value(v, new_val);
}
}
}
}
}
template<typename Ext>
bool theory_arith<Ext>::has_infeasible_int_var() {
int num = get_num_vars();
for (theory_var v = 0; v < num; ++v) {
if (is_int(v) && !get_value(v).is_int())
return true;
}
return false;
}
template<typename Ext>
theory_var theory_arith<Ext>::find_bounded_infeasible_int_base_var() {
theory_var result = null_theory_var;
numeral range;
numeral new_range;
numeral small_range_threshold(1024);
unsigned n = 0;
for (row const& row : m_rows) {
theory_var v = row.get_base_var();
if (v == null_theory_var)
continue;
if (!is_base(v))
continue;
if (!is_int(v))
continue;
if (get_value(v).is_int())
continue;
if (!is_bounded(v))
continue;
numeral const & l = lower_bound(v).get_rational();
numeral const & u = upper_bound(v).get_rational();
new_range = u;
new_range -= l;
if (new_range > small_range_threshold) {
}
else if (result == null_theory_var || new_range < range) {
result = v;
range = new_range;
n = 1;
}
else if (new_range == range) {
n++;
if (m_random() % n == 0) {
result = v;
range = new_range;
}
}
}
return result;
}
template<typename Ext>
theory_var theory_arith<Ext>::find_infeasible_int_base_var() {
theory_var r = find_bounded_infeasible_int_base_var();
CTRACE(find_infeasible_int_base_var, r != null_theory_var, display_var(tout << "bounded infeasible", r););
unsigned n = 0;
#define SELECT_VAR(VAR) if (r == null_theory_var) { n = 1; r = VAR; } else { n++; SASSERT(n >= 2); if (m_random() % n == 0) r = VAR; }
numeral small_value(1024);
if (r == null_theory_var) {
for (auto const& row : m_rows) {
theory_var v = row.get_base_var();
if (v != null_theory_var && is_base(v) && is_int(v) && !get_value(v).is_int()) {
if (abs(get_value(v)) < small_value) {
SELECT_VAR(v);
}
else if (upper(v) && small_value > upper_bound(v) - get_value(v)) {
SELECT_VAR(v);
}
else if (lower(v) && small_value > get_value(v) - lower_bound(v)) {
SELECT_VAR(v);
}
}
}
CTRACE(find_infeasible_int_base_var, r != null_theory_var, tout << "found small value v" << r << "\n");
}
if (r == null_theory_var) {
for (auto const& row : m_rows) {
theory_var v = row.get_base_var();
if (v != null_theory_var && is_base(v) && is_int(v) && !get_value(v).is_int()) {
SELECT_VAR(v);
}
}
CTRACE(find_infeasible_int_base_var, r != null_theory_var, tout << "found base v" << r << "\n");
}
if (r == null_theory_var) {
for (auto const& row : m_rows) {
theory_var v = row.get_base_var();
if (v != null_theory_var && is_quasi_base(v) && is_int(v) && !get_value(v).is_int()) {
quasi_base_row2base_row(get_var_row(v));
SELECT_VAR(v);
}
}
CTRACE(find_infeasible_int_base_var, r != null_theory_var, tout << "found quasi base v" << r << "\n");
}
CASSERT("arith", wf_rows());
CASSERT("arith", wf_columns());
return r;
}
template<typename Ext>
void theory_arith<Ext>::branch_infeasible_int_var(theory_var v) {
SASSERT(is_int(v));
SASSERT(!get_value(v).is_int());
ast_manager & m = get_manager();
m_stats.m_branches++;
numeral k = ceil(get_value(v));
rational _k = k.to_rational();
TRACE(arith_int, tout << "branching v" << v << " = " << get_value(v) << "\n";
display_var(tout, v);
tout << "k = " << k << ", _k = "<< _k << std::endl;
);
expr_ref bound(m);
expr* e = get_enode(v)->get_expr();
bound = m_util.mk_ge(e, m_util.mk_numeral(_k, m_util.is_int(e)));
context & ctx = get_context();
{
std::function<expr*(void)> fn = [&]() { return m.mk_or(bound, m.mk_not(bound)); };
scoped_trace_stream _sts(*this, fn);
IF_VERBOSE(10, verbose_stream() << "branch " << bound << "\n");
TRACE(arith_int, tout << mk_bounded_pp(bound, m) << "\n";);
ctx.internalize(bound, true);
ctx.mark_as_relevant(bound.get());
}
}
template<typename Ext>
bool theory_arith<Ext>::branch_infeasible_int_equality() {
vector<vector<rational> > rows;
unsigned max_row = 1; u_map<unsigned> var2index; u_map<theory_var> index2var; context& ctx = get_context();
ast_manager& m = get_manager();
if (ctx.get_cancel_flag())
return false;
for (auto const& r : m_rows) {
theory_var b = r.get_base_var();
if (b == null_theory_var) {
TRACE(arith_int, display_row(tout << "null: ", r, true); );
continue;
}
bool is_tight = false;
numeral const_coeff(0);
bound* l = lower(b), *u = upper(b);
if (l != nullptr && get_value(b) - inf_numeral(1) < l->get_value()) {
SASSERT(l->get_value() <= get_value(b));
is_tight = true;
const_coeff = l->get_value().get_rational();
}
else if (u != nullptr && get_value(b) + inf_numeral(1) > u->get_value()) {
SASSERT(get_value(b) <= u->get_value());
is_tight = true;
const_coeff = u->get_value().get_rational();
}
if (!is_tight) {
TRACE(arith_int,
display_row(tout << "!tight: ", r, true);
display_var(tout, b);
);
continue;
}
rows.push_back(vector<rational>(max_row));
vector<rational>& row = rows.back();
numeral denom(1);
unsigned index = 0;
typename vector<row_entry>::const_iterator it_r = r.begin_entries();
typename vector<row_entry>::const_iterator end_r = r.end_entries();
for (; it_r != end_r && is_tight; ++it_r) {
if (it_r->is_dead())
continue;
theory_var x = it_r->m_var;
if (x == b)
continue;
numeral coeff = it_r->m_coeff;
if (is_fixed(x)) {
const_coeff += coeff*lower(x)->get_value().get_rational();
continue;
}
if (!is_int(x)) {
TRACE(arith_int, display_row(tout << "!int: ", r, true); );
is_tight = false;
continue;
}
if (var2index.find(x, index)) {
row[index] = coeff.to_rational();
}
else {
row.push_back(coeff.to_rational());
var2index.insert(x, max_row);
index2var.insert(max_row, x);
++max_row;
}
numeral tmp_coeff = denominator(coeff);
denom = lcm(tmp_coeff, denom);
}
if (!is_tight) {
rows.pop_back();
continue;
}
row[0] = const_coeff.to_rational();
numeral tmp_const_coeff = denominator(const_coeff);
denom = lcm(tmp_const_coeff, denom);
if (!denom.is_one()) {
for (unsigned i = 0; i < max_row; ++i) {
row[i] *= denom.to_rational();
}
}
TRACE(arith_int,
tout << "extracted row:\n";
for (unsigned i = 0; i < max_row; ++i) {
tout << row[i] << " ";
}
tout << " = 0\n";
tout << "base value: " << get_value(b) << "\n";
display_row(tout, r, true);
);
}
for (unsigned i = 0; i < rows.size(); ++i) {
unsigned sz = rows[i].size();
SASSERT(sz <= max_row);
if (sz == max_row) {
break;
}
rows[i].resize(max_row);
}
vector<rational> unsat_row;
if (m_arith_eq_solver.solve_integer_equations(rows, unsat_row)) {
return false;
}
buffer<row_entry> pol;
for (unsigned i = 1; i < unsat_row.size(); ++i) {
numeral c(unsat_row[i]);
if (!c.is_zero()) {
theory_var var = null_theory_var;
if (!index2var.find(i, var)) {
UNREACHABLE();
}
pol.push_back(row_entry(c, var));
}
}
if (pol.empty()) {
TRACE(arith_int, tout << "The witness is trivial\n";);
return false;
}
expr_ref p1(get_manager()), p2(get_manager());
mk_polynomial_ge(pol.size(), pol.data(), -unsat_row[0]+rational(1), p1);
for (unsigned i = 0; i < pol.size(); ++i) {
pol[i].m_coeff.neg();
}
mk_polynomial_ge(pol.size(), pol.data(), unsat_row[0]+rational(1), p2);
{
std::function<expr*(void)> fn = [&]() { return m.mk_or(p1, p2); };
scoped_trace_stream _sts(*this, fn);
ctx.internalize(p1, false);
ctx.internalize(p2, false);
literal l1(ctx.get_literal(p1)), l2(ctx.get_literal(p2));
ctx.mark_as_relevant(p1.get());
ctx.mark_as_relevant(p2.get());
ctx.mk_th_axiom(get_id(), l1, l2);
}
TRACE(arith_int,
tout << "cut: (or " << mk_pp(p1, get_manager()) << " " << mk_pp(p2, get_manager()) << ")\n";
);
return true;
}
template<typename Ext>
bool theory_arith<Ext>::constrain_free_vars(row const & r) {
bool result = false;
theory_var b = r.get_base_var();
SASSERT(b != null_theory_var);
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it) {
if (!it->is_dead() && it->m_var != b && is_free(it->m_var)) {
theory_var v = it->m_var;
expr* e = get_enode(v)->get_expr();
bool _is_int = m_util.is_int(e);
expr_ref bound(m_util.mk_ge(e, m_util.mk_numeral(rational::zero(), _is_int)), get_manager());
context & ctx = get_context();
{
std::function<expr*(void)> fn = [&]() { return bound; };
scoped_trace_stream _sts(*this, fn);
ctx.internalize(bound, true);
}
IF_VERBOSE(10, verbose_stream() << "free " << bound << "\n");
ctx.mark_as_relevant(bound.get());
result = true;
}
}
return result;
}
template<typename Ext>
bool theory_arith<Ext>::is_gomory_cut_target(row const & r) {
theory_var b = r.get_base_var();
for (auto& e : r) {
if (!e.is_dead() && e.m_var != b && (!at_bound(e.m_var) || !get_value(e.m_var).is_rational()))
return false;
}
return true;
}
template<typename Ext>
void theory_arith<Ext>::mk_polynomial_ge(unsigned num_args, row_entry const * args, rational const& k, expr_ref & result) {
bool all_int = true;
for (unsigned i = 0; i < num_args && all_int; ++i) {
all_int = is_int(args[i].m_var);
}
ast_manager & m = get_manager();
expr_ref_vector _args(m);
for (unsigned i = 0; i < num_args; ++i) {
rational _k = args[i].m_coeff.to_rational();
expr * x = get_enode(args[i].m_var)->get_expr();
if (m_util.is_int(x) && !all_int)
x = m_util.mk_to_real(x);
if (_k.is_one())
_args.push_back(x);
else
_args.push_back(m_util.mk_mul(m_util.mk_numeral(_k, m_util.is_int(x)), x));
}
expr_ref pol(m);
pol = m_util.mk_add(_args.size(), _args.data());
result = m_util.mk_ge(pol, m_util.mk_numeral(k, all_int));
TRACE(arith_mk_polynomial, tout << "before simplification:\n" << result << "\n";);
proof_ref pr(m);
get_context().get_rewriter()(result, result, pr);
TRACE(arith_mk_polynomial, tout << "after simplification:\n" << result << "\n";);
SASSERT(is_well_sorted(get_manager(), result));
}
template<typename Ext>
class theory_arith<Ext>::gomory_cut_justification : public ext_theory_propagation_justification {
public:
gomory_cut_justification(family_id fid, context& ctx,
unsigned num_lits, literal const * lits,
unsigned num_eqs, enode_pair const * eqs,
antecedents& bounds,
literal consequent):
ext_theory_propagation_justification(fid, ctx, num_lits, lits, num_eqs, eqs, consequent,
bounds.num_params(), bounds.params("gomory-cut")) {
}
theory_id get_from_theory() const override { return null_theory_id; }
};
template<typename Ext>
bool theory_arith<Ext>::mk_gomory_cut(row const & r) {
theory_var x_i = r.get_base_var();
SASSERT(is_int(x_i));
SASSERT(!m_value[x_i].is_int());
bool cfv = constrain_free_vars(r);
if (cfv || !is_gomory_cut_target(r)) {
TRACE(gomory_cut, tout << "failed to apply gomory cut:\n";
tout << "constrain_free_vars(r): " << cfv << "\n";);
return false;
}
TRACE(gomory_cut, tout << "applying cut at:\n"; display_row_info(tout, r););
antecedents ante(*this);
m_stats.m_gomory_cuts++;
numeral k(1);
buffer<row_entry> pol;
numeral f_0 = Ext::fractional_part(m_value[x_i]);
numeral one_minus_f_0 = numeral(1) - f_0;
SASSERT(!f_0.is_zero());
SASSERT(!one_minus_f_0.is_zero());
numeral lcm_den(1);
unsigned num_ints = 0;
for (row_entry const& e : r) {
if (!e.is_dead() && e.m_var != x_i) {
theory_var x_j = e.m_var;
numeral a_ij = e.m_coeff;
a_ij.neg(); if (is_real(x_j)) {
numeral new_a_ij;
if (at_lower(x_j)) {
if (a_ij.is_pos()) {
new_a_ij = a_ij / one_minus_f_0;
}
else {
new_a_ij = a_ij / f_0;
new_a_ij.neg();
}
k.addmul(new_a_ij, lower_bound(x_j).get_rational());
lower(x_j)->push_justification(ante, new_a_ij, coeffs_enabled());
}
else {
SASSERT(at_upper(x_j));
if (a_ij.is_pos()) {
new_a_ij = a_ij / f_0;
new_a_ij.neg(); }
else {
new_a_ij = a_ij / one_minus_f_0;
}
k.addmul(new_a_ij, upper_bound(x_j).get_rational());
upper(x_j)->push_justification(ante, new_a_ij, coeffs_enabled());
}
TRACE(gomory_cut_detail, tout << a_ij << "*v" << x_j << " k: " << k << "\n";);
pol.push_back(row_entry(new_a_ij, x_j));
}
else {
++num_ints;
SASSERT(is_int(x_j));
numeral f_j = Ext::fractional_part(a_ij);
TRACE(gomory_cut_detail,
tout << a_ij << "*v" << x_j << "\n";
tout << "fractional_part: " << Ext::fractional_part(a_ij) << "\n";
tout << "f_j: " << f_j << "\n";
tout << "f_0: " << f_0 << "\n";
tout << "one_minus_f_0: " << one_minus_f_0 << "\n";);
if (!f_j.is_zero()) {
numeral new_a_ij;
if (at_lower(x_j)) {
if (f_j <= one_minus_f_0) {
new_a_ij = f_j / one_minus_f_0;
}
else {
new_a_ij = (numeral(1) - f_j) / f_0;
}
k.addmul(new_a_ij, lower_bound(x_j).get_rational());
lower(x_j)->push_justification(ante, new_a_ij, coeffs_enabled());
}
else {
SASSERT(at_upper(x_j));
if (f_j <= f_0) {
new_a_ij = f_j / f_0;
}
else {
new_a_ij = (numeral(1) - f_j) / one_minus_f_0;
}
new_a_ij.neg(); k.addmul(new_a_ij, upper_bound(x_j).get_rational());
upper(x_j)->push_justification(ante, new_a_ij, coeffs_enabled());
}
TRACE(gomory_cut_detail, tout << "new_a_ij: " << new_a_ij << " k: " << k << "\n";);
pol.push_back(row_entry(new_a_ij, x_j));
lcm_den = lcm(lcm_den, denominator(new_a_ij));
}
}
}
}
CTRACE(empty_pol, pol.empty(), display_row_info(tout, r););
expr_ref bound(get_manager());
if (pol.empty()) {
if (ante.lits().empty() && ante.eqs().empty())
return false;
SASSERT(k.is_pos());
set_conflict(ante, ante, "gomory-cut");
return true;
}
else if (pol.size() == 1) {
theory_var v = pol[0].m_var;
k /= pol[0].m_coeff;
bool is_lower = pol[0].m_coeff.is_pos();
if (is_int(v) && !k.is_int()) {
k = is_lower?ceil(k):floor(k);
}
rational _k = k.to_rational();
if (is_lower)
bound = m_util.mk_ge(get_enode(v)->get_expr(), m_util.mk_numeral(_k, is_int(v)));
else
bound = m_util.mk_le(get_enode(v)->get_expr(), m_util.mk_numeral(_k, is_int(v)));
}
else {
if (num_ints > 0) {
lcm_den = lcm(lcm_den, denominator(k));
TRACE(gomory_cut_detail, tout << "k: " << k << " lcm_den: " << lcm_den << "\n";
for (unsigned i = 0; i < pol.size(); ++i) {
tout << pol[i].m_coeff << " " << pol[i].m_var << "\n";
}
tout << "k: " << k << "\n";);
SASSERT(lcm_den.is_pos());
if (!lcm_den.is_one()) {
unsigned n = pol.size();
for (unsigned i = 0; i < n; ++i) {
pol[i].m_coeff *= lcm_den;
SASSERT(!is_int(pol[i].m_var) || pol[i].m_coeff.is_int());
}
k *= lcm_den;
}
TRACE(gomory_cut_detail, tout << "after *lcm\n";
for (unsigned i = 0; i < pol.size(); ++i) {
tout << pol[i].m_coeff << " * v" << pol[i].m_var << "\n";
}
tout << "k: " << k << "\n";);
}
mk_polynomial_ge(pol.size(), pol.data(), k.to_rational(), bound);
}
TRACE(gomory_cut, tout << "new cut:\n" << bound << "\n"; ante.display(tout););
literal l = null_literal;
context & ctx = get_context();
{
std::function<expr*(void)> fn = [&]() { return bound; };
scoped_trace_stream _sts(*this, fn);
ctx.internalize(bound, true);
}
l = ctx.get_literal(bound);
IF_VERBOSE(10, verbose_stream() << "cut " << bound << "\n");
ctx.mark_as_relevant(l);
auto js = ctx.mk_justification(
gomory_cut_justification(
get_id(), ctx,
ante.lits().size(), ante.lits().data(),
ante.eqs().size(), ante.eqs().data(), ante, l));
if (l == false_literal)
ctx.mk_clause(0, nullptr, js, CLS_TH_LEMMA, nullptr);
else
ctx.assign(l, js);
return true;
}
template<typename Ext>
bool theory_arith<Ext>::gcd_test(row const & r) {
if (!m_params.m_arith_gcd_test)
return true;
m_stats.m_gcd_tests++;
numeral lcm_den = r.get_denominators_lcm();
TRACE(gcd_test_bug, r.display(tout); tout << "lcm: " << lcm_den << "\n";);
numeral consts(0);
numeral gcds(0);
numeral least_coeff(0);
bool least_coeff_is_bounded = false;
for (row_entry const& e : r) {
if (!e.is_dead()) {
if (is_fixed(e.m_var)) {
numeral aux = lcm_den * e.m_coeff;
consts += aux * lower_bound(e.m_var).get_rational();
}
else if (is_real(e.m_var)) {
return true;
}
else if (gcds.is_zero()) {
gcds = abs(lcm_den * e.m_coeff);
least_coeff = gcds;
least_coeff_is_bounded = is_bounded(e.m_var);
}
else {
numeral aux = abs(lcm_den * e.m_coeff);
gcds = gcd(gcds, aux);
if (aux < least_coeff) {
least_coeff = aux;
least_coeff_is_bounded = is_bounded(e.m_var);
}
else if (least_coeff_is_bounded && aux == least_coeff) {
least_coeff_is_bounded = is_bounded(e.m_var);
}
}
SASSERT(gcds.is_int());
SASSERT(least_coeff.is_int());
TRACE(gcd_test_bug, tout << "coeff: " << e.m_coeff << ", gcds: " << gcds
<< " least_coeff: " << least_coeff << " consts: " << consts << "\n";);
}
}
if (gcds.is_zero()) {
return true;
}
if (!(consts / gcds).is_int()) {
TRACE(gcd_test, tout << "row failed the GCD test:\n"; display_row_info(tout, r););
antecedents ante(*this);
m_stats.m_gcd_conflicts++;
collect_fixed_var_justifications(r, ante);
context & ctx = get_context();
ctx.set_conflict(
ctx.mk_justification(
ext_theory_conflict_justification(
get_id(), ctx, ante.lits().size(), ante.lits().data(),
ante.eqs().size(), ante.eqs().data(),
ante.num_params(), ante.params("gcd-test"))));
return false;
}
if (least_coeff.is_one() && !least_coeff_is_bounded) {
SASSERT(gcds.is_one());
return true;
}
if (least_coeff_is_bounded) {
return ext_gcd_test(r, least_coeff, lcm_den, consts);
}
return true;
}
template<typename Ext>
bool theory_arith<Ext>::ext_gcd_test(row const & r, numeral const & least_coeff,
numeral const & lcm_den, numeral const & consts) {
numeral gcds(0);
numeral l(consts);
numeral u(consts);
antecedents ante(*this);
for (auto const& e : r) {
if (!e.is_dead() && !is_fixed(e.m_var)) {
theory_var v = e.m_var;
SASSERT(!is_real(v));
numeral ncoeff = lcm_den * e.m_coeff;
SASSERT(ncoeff.is_int());
numeral abs_ncoeff = abs(ncoeff);
if (abs_ncoeff == least_coeff) {
SASSERT(is_bounded(v));
if (ncoeff.is_pos()) {
l.addmul(ncoeff, lower_bound(v).get_rational());
u.addmul(ncoeff, upper_bound(v).get_rational());
}
else {
l.addmul(ncoeff, upper_bound(v).get_rational());
u.addmul(ncoeff, lower_bound(v).get_rational());
}
lower(v)->push_justification(ante, e.m_coeff, coeffs_enabled());
upper(v)->push_justification(ante, e.m_coeff, coeffs_enabled());
}
else if (gcds.is_zero()) {
gcds = abs_ncoeff;
}
else {
gcds = gcd(gcds, abs_ncoeff);
}
SASSERT(gcds.is_int());
}
}
if (gcds.is_zero()) {
return true;
}
numeral l1 = ceil(l/gcds);
numeral u1 = floor(u/gcds);
if (u1 < l1) {
m_stats.m_gcd_conflicts++;
TRACE(gcd_test, tout << "row failed the extended GCD test:\n"; display_row_info(tout, r););
collect_fixed_var_justifications(r, ante);
context & ctx = get_context();
ctx.set_conflict(
ctx.mk_justification(
ext_theory_conflict_justification(
get_id(), ctx,
ante.lits().size(), ante.lits().data(), ante.eqs().size(), ante.eqs().data(),
ante.num_params(), ante.params("gcd-test"))));
return false;
}
return true;
}
template<typename Ext>
bool theory_arith<Ext>::gcd_test() {
if (!m_params.m_arith_gcd_test)
return true;
if (m_eager_gcd)
return true;
for (auto const& e : m_rows) {
theory_var v = e.get_base_var();
if (v != null_theory_var && is_int(v) && !get_value(v).is_int() && !gcd_test(e)) {
if (m_params.m_arith_adaptive_gcd)
m_eager_gcd = true;
return false;
}
}
return true;
}
template<typename Ext>
bool theory_arith<Ext>::max_min_infeasible_int_vars() {
var_set & already_processed = m_tmp_var_set;
already_processed.reset();
svector<theory_var> vars;
for (;;) {
vars.reset();
for (auto const& e : m_rows) {
theory_var v = e.get_base_var();
if (v != null_theory_var && is_int(v) && !get_value(v).is_int() && !is_bounded(v) && !already_processed.contains(v)) {
vars.push_back(v);
already_processed.insert(v);
}
}
if (vars.empty())
return true;
if (max_min(vars))
return false;
}
}
template<typename Ext>
void theory_arith<Ext>::patch_int_infeasible_vars() {
SASSERT(m_to_patch.empty());
int num = get_num_vars();
bool inf_l, inf_u;
inf_numeral l, u;
numeral m;
for (theory_var v = 0; v < num; ++v) {
if (!is_non_base(v))
continue;
get_freedom_interval(v, inf_l, l, inf_u, u, m);
if (m.is_one() && get_value(v).is_int())
continue;
if ((get_value(v).get_rational() / m).is_int())
continue;
TRACE(patch_int,
tout << "TARGET v" << v << " -> [";
if (inf_l) tout << "-oo"; else tout << ceil(l);
tout << ", ";
if (inf_u) tout << "oo"; else tout << floor(u);
tout << "]";
tout << ", m: " << m << ", val: " << get_value(v) << ", is_int: " << is_int(v) << "\n";);
if (!inf_l)
l = ceil(l);
if (!inf_u)
u = floor(u);
if (!m.is_one()) {
if (!inf_l)
l = m*ceil(l/m);
if (!inf_u)
u = m*floor(u/m);
}
if (!inf_l && !inf_u && l > u)
continue; if (!inf_l)
set_value(v, l);
else if (!inf_u)
set_value(v, u);
else
set_value(v, inf_numeral(0));
}
SASSERT(m_to_patch.empty());
}
template<typename Ext>
void theory_arith<Ext>::fix_non_base_vars() {
int num = get_num_vars();
for (theory_var v = 0; v < num; ++v) {
if (!is_non_base(v))
continue;
if (!is_int(v))
continue;
if (get_value(v).is_int())
continue;
inf_numeral new_val(floor(get_value(v)));
set_value(v, new_val);
}
if (!make_feasible())
failed();
}
template<typename Ext>
final_check_status theory_arith<Ext>::check_int_feasibility() {
TRACE(arith_int_detail, get_context().display(tout););
if (!has_infeasible_int_var()) {
TRACE(arith, tout << "FC_DONE 1...\n"; display(tout););
return FC_DONE;
}
TRACE(arith,
int num = get_num_vars();
for (theory_var v = 0; v < num; ++v) {
if (is_int(v) && !get_value(v).is_int()) {
numeral f1 = get_value(v).get_rational() - floor(get_value(v).get_rational());
numeral f2 = ceil(get_value(v).get_rational()) - get_value(v).get_rational();
if (f2 < f1)
f1 = f2;
tout << "v" << v << " -> " << f1 << " ";
display_var(tout, v);
}
});
TRACE(arith_int_fracs_min_max,
numeral max(0);
numeral min(1);
int num = get_num_vars();
for (theory_var v = 0; v < num; ++v) {
if (is_int(v) && !get_value(v).is_int()) {
numeral f1 = get_value(v).get_rational() - floor(get_value(v).get_rational());
numeral f2 = ceil(get_value(v).get_rational()) - get_value(v).get_rational();
if (f1 < min) min = f1;
if (f2 < min) min = f2;
if (f1 > max) max = f1;
if (f2 > max) max = f2;
}
}
tout << "max: " << max << ", min: " << min << "\n";);
if (m_params.m_arith_ignore_int) {
TRACE(arith, tout << "Ignore int: give up\n";);
return FC_GIVEUP;
}
if (!gcd_test())
return FC_CONTINUE;
if (get_context().inconsistent())
return FC_CONTINUE;
remove_fixed_vars_from_base();
TRACE(arith_int_freedom,
int num = get_num_vars();
bool inf_l; bool inf_u;
inf_numeral l; inf_numeral u;
numeral m;
for (theory_var v = 0; v < num; ++v) {
if (is_non_base(v)) {
get_freedom_interval(v, inf_l, l, inf_u, u, m);
if ((!m.is_one() ) || !get_value(v).is_int()) {
tout << "TARGET v" << v << " -> [";
if (inf_l) tout << "-oo"; else tout << ceil(l);
tout << ", ";
if (inf_u) tout << "oo"; else tout << floor(u);
tout << "]";
tout << ", m: " << m << ", val: " << get_value(v) << ", is_int: " << is_int(v) << "\n";
}
}
});
m_stats.m_patches++;
patch_int_infeasible_vars();
fix_non_base_vars();
if (get_context().inconsistent())
return FC_CONTINUE;
TRACE(arith_int_inf,
int num = get_num_vars();
for (theory_var v = 0; v < num; ++v) {
if (is_int(v) && !get_value(v).is_int()) {
display_var(tout, v);
}
});
TRACE(arith_int_rows,
unsigned num = 0;
for (auto const& e : m_rows) {
theory_var v = e.get_base_var();
if (v == null_theory_var)
continue;
if (is_int(v) && !get_value(v).is_int()) {
num++;
display_simplified_row(tout, e);
tout << "\n";
}
}
tout << "num infeasible: " << num << "\n";);
theory_var int_var = find_infeasible_int_base_var();
if (int_var == null_theory_var) {
m_stats.m_patches_succ++;
TRACE(arith_int_incomp, tout << "FC_DONE 2...\n"; display(tout););
return m_liberal_final_check || !m_changed_assignment ? FC_DONE : FC_CONTINUE;
}
#if 0#endif
m_branch_cut_counter++;
TRACE(gomory_cut, tout << m_branch_cut_counter << ", " << m_params.m_arith_branch_cut_ratio << std::endl;);
if (m_branch_cut_counter % m_params.m_arith_branch_cut_ratio == 0) {
TRACE(opt_verbose, display(tout););
move_non_base_vars_to_bounds();
if (!make_feasible()) {
TRACE(arith_int, tout << "failed to move variables to bounds.\n";);
failed();
return FC_CONTINUE;
}
theory_var int_var = find_infeasible_int_base_var();
if (int_var != null_theory_var) {
TRACE(arith_int, tout << "v" << int_var << " does not have an integer assignment: " << get_value(int_var) << "\n";);
SASSERT(is_base(int_var));
row const & r = m_rows[get_var_row(int_var)];
if (!mk_gomory_cut(r)) {
TRACE(gomory_cut, tout << "silent failure\n";);
}
return FC_CONTINUE;
}
}
else {
if (m_params.m_arith_int_eq_branching && branch_infeasible_int_equality()) {
++m_stats.m_branch_infeasible_int;
return FC_CONTINUE;
}
theory_var int_var = find_infeasible_int_base_var();
if (int_var != null_theory_var) {
TRACE(arith_int, tout << "v" << int_var << " does not have an integer assignment: " << get_value(int_var) << "\n";);
branch_infeasible_int_var(int_var);
++m_stats.m_branch_infeasible_var;
return FC_CONTINUE;
}
}
return m_liberal_final_check || !m_changed_assignment ? FC_DONE : FC_CONTINUE;
}
};