mirror of
https://github.com/Z3Prover/z3
synced 2025-08-12 14:10:54 +00:00
separate the gomory cut functionality in a separate file
Signed-off-by: Lev <levnach@hotmail.com>
This commit is contained in:
parent
26764b076f
commit
324396e403
7 changed files with 284 additions and 232 deletions
|
@ -8,6 +8,7 @@
|
|||
#include "util/lp/lp_utils.h"
|
||||
#include <utility>
|
||||
#include "util/lp/monomial.h"
|
||||
#include "util/lp/gomory.h"
|
||||
namespace lp {
|
||||
|
||||
|
||||
|
@ -101,12 +102,7 @@ bool int_solver::is_gomory_cut_target(const row_strip<mpq>& row) {
|
|||
unsigned j;
|
||||
for (const auto & p : row) {
|
||||
j = p.var();
|
||||
if (is_base(j)) continue;
|
||||
if (is_free(j))
|
||||
return false;
|
||||
if (!at_bound(j))
|
||||
return false;
|
||||
if (!is_zero(get_value(j).y)) {
|
||||
if (!is_base(j) && (!at_bound(j) || !is_zero(get_value(j).y))) {
|
||||
TRACE("gomory_cut", tout << "row is not gomory cut target:\n";
|
||||
display_column(tout, j);
|
||||
tout << "infinitesimal: " << !is_zero(get_value(j).y) << "\n";);
|
||||
|
@ -117,36 +113,6 @@ bool int_solver::is_gomory_cut_target(const row_strip<mpq>& row) {
|
|||
}
|
||||
|
||||
|
||||
void int_solver::real_case_in_gomory_cut(const mpq & a, unsigned x_j, const mpq& f_0, const mpq& one_minus_f_0) {
|
||||
TRACE("gomory_cut_detail_real", tout << "real\n";);
|
||||
mpq new_a;
|
||||
if (at_low(x_j)) {
|
||||
if (a.is_pos()) {
|
||||
new_a = a / one_minus_f_0;
|
||||
}
|
||||
else {
|
||||
new_a = a / f_0;
|
||||
new_a.neg();
|
||||
}
|
||||
m_k->addmul(new_a, lower_bound(x_j).x); // is it a faster operation than
|
||||
// k += lower_bound(x_j).x * new_a;
|
||||
m_ex->push_justification(column_lower_bound_constraint(x_j), new_a);
|
||||
}
|
||||
else {
|
||||
lp_assert(at_upper(x_j));
|
||||
if (a.is_pos()) {
|
||||
new_a = a / f_0;
|
||||
new_a.neg(); // the upper terms are inverted.
|
||||
}
|
||||
else {
|
||||
new_a = a / one_minus_f_0;
|
||||
}
|
||||
m_k->addmul(new_a, upper_bound(x_j).x); // k += upper_bound(x_j).x * new_a;
|
||||
m_ex->push_justification(column_upper_bound_constraint(x_j), new_a);
|
||||
}
|
||||
TRACE("gomory_cut_detail_real", tout << a << "*v" << x_j << " k: " << *m_k << "\n";);
|
||||
m_t->add_monomial(new_a, x_j);
|
||||
}
|
||||
|
||||
constraint_index int_solver::column_upper_bound_constraint(unsigned j) const {
|
||||
return m_lar_solver->get_column_upper_bound_witness(j);
|
||||
|
@ -157,99 +123,6 @@ constraint_index int_solver::column_lower_bound_constraint(unsigned j) const {
|
|||
}
|
||||
|
||||
|
||||
void int_solver::int_case_in_gomory_cut(const mpq & a, unsigned x_j,
|
||||
mpq & lcm_den, const mpq& f_0, const mpq& one_minus_f_0) {
|
||||
lp_assert(is_int(x_j));
|
||||
lp_assert(!a.is_int());
|
||||
mpq f_j = fractional_part(a);
|
||||
TRACE("gomory_cut_detail",
|
||||
tout << a << " x_j" << x_j << " k = " << *m_k << "\n";
|
||||
tout << "f_j: " << f_j << "\n";
|
||||
tout << "f_0: " << f_0 << "\n";
|
||||
tout << "1 - f_0: " << 1 - f_0 << "\n";
|
||||
tout << "at_low(" << x_j << ") = " << at_low(x_j) << std::endl;
|
||||
);
|
||||
lp_assert (!f_j.is_zero());
|
||||
mpq new_a;
|
||||
if (at_low(x_j)) {
|
||||
if (f_j <= one_minus_f_0) {
|
||||
new_a = f_j / one_minus_f_0;
|
||||
}
|
||||
else {
|
||||
new_a = (1 - f_j) / f_0;
|
||||
}
|
||||
m_k->addmul(new_a, lower_bound(x_j).x);
|
||||
m_ex->push_justification(column_lower_bound_constraint(x_j), new_a);
|
||||
}
|
||||
else {
|
||||
lp_assert(at_upper(x_j));
|
||||
if (f_j <= f_0) {
|
||||
new_a = f_j / f_0;
|
||||
}
|
||||
else {
|
||||
new_a = (mpq(1) - f_j) / one_minus_f_0;
|
||||
}
|
||||
new_a.neg(); // the upper terms are inverted
|
||||
m_k->addmul(new_a, upper_bound(x_j).x);
|
||||
m_ex->push_justification(column_upper_bound_constraint(x_j), new_a);
|
||||
}
|
||||
TRACE("gomory_cut_detail", tout << "new_a: " << new_a << " k: " << *m_k << "\n";);
|
||||
m_t->add_monomial(new_a, x_j);
|
||||
lcm_den = lcm(lcm_den, denominator(new_a));
|
||||
}
|
||||
|
||||
lia_move int_solver::report_conflict_from_gomory_cut() {
|
||||
TRACE("empty_pol",);
|
||||
lp_assert(m_k->is_pos());
|
||||
// conflict 0 >= k where k is positive
|
||||
m_k->neg(); // returning 0 <= -k
|
||||
return lia_move::conflict;
|
||||
}
|
||||
|
||||
void int_solver::gomory_cut_adjust_t_and_k(vector<std::pair<mpq, unsigned>> & pol,
|
||||
lar_term & t,
|
||||
mpq &k,
|
||||
bool some_ints,
|
||||
mpq & lcm_den) {
|
||||
if (!some_ints)
|
||||
return;
|
||||
|
||||
t.clear();
|
||||
if (pol.size() == 1) {
|
||||
unsigned v = pol[0].second;
|
||||
lp_assert(is_int(v));
|
||||
bool k_is_int = k.is_int();
|
||||
const mpq& a = pol[0].first;
|
||||
k /= a;
|
||||
if (a.is_pos()) { // we have av >= k
|
||||
if (!k_is_int)
|
||||
k = ceil(k);
|
||||
// switch size
|
||||
t.add_monomial(- mpq(1), v);
|
||||
k.neg();
|
||||
} else {
|
||||
if (!k_is_int)
|
||||
k = floor(k);
|
||||
t.add_monomial(mpq(1), v);
|
||||
}
|
||||
} else if (some_ints) {
|
||||
lcm_den = lcm(lcm_den, denominator(k));
|
||||
lp_assert(lcm_den.is_pos());
|
||||
if (!lcm_den.is_one()) {
|
||||
// normalize coefficients of integer parameters to be integers.
|
||||
for (auto & pi: pol) {
|
||||
pi.first *= lcm_den;
|
||||
SASSERT(!is_int(pi.second) || pi.first.is_int());
|
||||
}
|
||||
k *= lcm_den;
|
||||
}
|
||||
// negate everything to return -pol <= -k
|
||||
for (const auto & pi: pol)
|
||||
t.add_monomial(-pi.first, pi.second);
|
||||
k.neg();
|
||||
}
|
||||
}
|
||||
|
||||
bool int_solver::current_solution_is_inf_on_cut() const {
|
||||
const auto & x = m_lar_solver->m_mpq_lar_core_solver.m_r_x;
|
||||
impq v = m_t->apply(x);
|
||||
|
@ -261,95 +134,11 @@ bool int_solver::current_solution_is_inf_on_cut() const {
|
|||
return v * sign > (*m_k) * sign;
|
||||
}
|
||||
|
||||
void int_solver::adjust_term_and_k_for_some_ints_case_gomory(mpq &lcm_den) {
|
||||
lp_assert(!m_t->is_empty());
|
||||
auto pol = m_t->coeffs_as_vector();
|
||||
m_t->clear();
|
||||
if (pol.size() == 1) {
|
||||
TRACE("gomory_cut_detail", tout << "pol.size() is 1" << std::endl;);
|
||||
unsigned v = pol[0].second;
|
||||
lp_assert(is_int(v));
|
||||
const mpq& a = pol[0].first;
|
||||
(*m_k) /= a;
|
||||
if (a.is_pos()) { // we have av >= k
|
||||
if (!(*m_k).is_int())
|
||||
(*m_k) = ceil((*m_k));
|
||||
// switch size
|
||||
m_t->add_monomial(- mpq(1), v);
|
||||
(*m_k).neg();
|
||||
} else {
|
||||
if (!(*m_k).is_int())
|
||||
(*m_k) = floor((*m_k));
|
||||
m_t->add_monomial(mpq(1), v);
|
||||
}
|
||||
} else {
|
||||
TRACE("gomory_cut_detail", tout << "pol.size() > 1" << std::endl;);
|
||||
lcm_den = lcm(lcm_den, denominator((*m_k)));
|
||||
lp_assert(lcm_den.is_pos());
|
||||
if (!lcm_den.is_one()) {
|
||||
// normalize coefficients of integer parameters to be integers.
|
||||
for (auto & pi: pol) {
|
||||
pi.first *= lcm_den;
|
||||
SASSERT(!is_int(pi.second) || pi.first.is_int());
|
||||
}
|
||||
(*m_k) *= lcm_den;
|
||||
}
|
||||
// negate everything to return -pol <= -(*m_k)
|
||||
for (const auto & pi: pol)
|
||||
m_t->add_monomial(-pi.first, pi.second);
|
||||
(*m_k).neg();
|
||||
}
|
||||
TRACE("gomory_cut_detail", tout << "k = " << (*m_k) << std::endl;);
|
||||
lp_assert((*m_k).is_int());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
lia_move int_solver::mk_gomory_cut( unsigned inf_col, const row_strip<mpq> & row) {
|
||||
|
||||
lp_assert(column_is_int_inf(inf_col));
|
||||
|
||||
TRACE("gomory_cut",
|
||||
tout << "applying cut at:\n"; m_lar_solver->print_row(row, tout); tout << std::endl;
|
||||
for (auto & p : row) {
|
||||
m_lar_solver->m_mpq_lar_core_solver.m_r_solver.print_column_info(p.var(), tout);
|
||||
}
|
||||
tout << "inf_col = " << inf_col << std::endl;
|
||||
);
|
||||
|
||||
// gomory will be t <= k and the current solution has a property t > k
|
||||
*m_k = 1;
|
||||
mpq lcm_den(1);
|
||||
unsigned x_j;
|
||||
mpq a;
|
||||
bool some_int_columns = false;
|
||||
mpq f_0 = int_solver::fractional_part(get_value(inf_col));
|
||||
mpq one_min_f_0 = 1 - f_0;
|
||||
for (const auto & p : row) {
|
||||
x_j = p.var();
|
||||
if (x_j == inf_col)
|
||||
continue;
|
||||
// make the format compatible with the format used in: Integrating Simplex with DPLL(T)
|
||||
a = p.coeff();
|
||||
a.neg();
|
||||
if (is_real(x_j))
|
||||
real_case_in_gomory_cut(a, x_j, f_0, one_min_f_0);
|
||||
else if (!a.is_int()) { // f_j will be zero and no monomial will be added
|
||||
some_int_columns = true;
|
||||
int_case_in_gomory_cut(a, x_j, lcm_den, f_0, one_min_f_0);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_t->is_empty())
|
||||
return report_conflict_from_gomory_cut();
|
||||
if (some_int_columns)
|
||||
adjust_term_and_k_for_some_ints_case_gomory(lcm_den);
|
||||
|
||||
lp_assert(current_solution_is_inf_on_cut());
|
||||
m_lar_solver->subs_term_columns(*m_t);
|
||||
TRACE("gomory_cut", tout<<"precut:"; m_lar_solver->print_term(*m_t, tout); tout << " <= " << *m_k << std::endl;);
|
||||
return lia_move::cut;
|
||||
gomory gc(*m_t, *m_k, *m_ex, inf_col, row, *this);
|
||||
return gc.create_cut();
|
||||
}
|
||||
|
||||
lia_move int_solver::proceed_with_gomory_cut(unsigned j) {
|
||||
|
@ -1121,7 +910,7 @@ bool int_solver::at_bound(unsigned j) const {
|
|||
}
|
||||
}
|
||||
|
||||
bool int_solver::at_low(unsigned j) const {
|
||||
bool int_solver::at_lower(unsigned j) const {
|
||||
auto & mpq_solver = m_lar_solver->m_mpq_lar_core_solver.m_r_solver;
|
||||
switch (mpq_solver.m_column_types[j] ) {
|
||||
case column_type::fixed:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue