Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
z3-z3-4.12.6.src.math.lp.lp_bound_propagator.h Maven / Gradle / Ivy
/*
Copyright (c) 2017 Microsoft Corporation
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
*/
#pragma once
#include
#include "math/lp/lp_settings.h"
#include "util/uint_set.h"
#include "math/lp/implied_bound.h"
#include "util/vector.h"
namespace lp {
template
class lp_bound_propagator {
uint_set m_visited_rows;
// these maps map a column index to the corresponding index in ibounds
u_map m_improved_lower_bounds;
u_map m_improved_upper_bounds;
T& m_imp;
std_vector& m_ibounds;
map, default_eq> m_val2fixed_row;
// works for rows of the form x + y + sum of fixed = 0
map, default_eq> m_row2index_pos;
// works for rows of the form x - y + sum of fixed = 0
map, default_eq> m_row2index_neg;
const vector* m_column_types;
// returns true iff there is only one non-fixed column in the row
bool only_one_nfixed(unsigned r, unsigned& x) {
x = UINT_MAX;
for (const auto& c: lp().get_row(r)) {
if (column_is_fixed(c.var()))
continue;
if (x != UINT_MAX)
return false;
x = c.var();
}
return x != UINT_MAX;
}
public:
const lar_solver& lp() const { return m_imp.lp(); }
lar_solver& lp() { return m_imp.lp(); }
bool upper_bound_is_available(unsigned j) const {
switch (get_column_type(j)) {
case column_type::fixed:
case column_type::boxed:
case column_type::upper_bound:
return true;
default:
return false;
}
}
bool lower_bound_is_available(unsigned j) const {
switch (get_column_type(j)) {
case column_type::fixed:
case column_type::boxed:
case column_type::lower_bound:
return true;
default:
return false;
}
}
private:
void try_add_equation_with_internal_fixed_tables(unsigned r1) {
unsigned v1, v2;
if (!only_one_nfixed(r1, v1))
return;
unsigned r2 = UINT_MAX;
if (!m_val2fixed_row.find(val(v1), r2) || r2 >= lp().row_count()) {
m_val2fixed_row.insert(val(v1), r1);
return;
}
if (!only_one_nfixed(r2, v2) || val(v1) != val(v2) || is_int(v1) != is_int(v2)) {
m_val2fixed_row.insert(val(v1), r1);
return;
}
if (v1 == v2)
return;
#if Z3DEBUG
lp_assert(val(v1) == val(v2));
unsigned debv1, debv2;
lp_assert(only_one_nfixed(r1, debv1) && only_one_nfixed(r2, debv2));
lp_assert(debv1 == v1 && debv2 == v2);
lp_assert(ival(v1).y == ival(v2).y);
#endif
explanation ex;
explain_fixed_in_row(r1, ex);
explain_fixed_in_row(r2, ex);
TRACE("eq", print_row(tout, r1); print_row(tout, r2); tout << v1 << " == " << v2 << " = " << val(v1) << "\n");
add_eq_on_columns(ex, v1, v2, true);
}
static bool is_not_set(unsigned j) { return j == UINT_MAX; }
static bool is_set(unsigned j) { return j != UINT_MAX; }
void reset_cheap_eq_eh() {
m_row2index_pos.reset();
m_row2index_neg.reset();
}
struct reset_cheap_eq {
lp_bound_propagator& p;
reset_cheap_eq(lp_bound_propagator& p) : p(p) {}
~reset_cheap_eq() { p.reset_cheap_eq_eh(); }
};
public:
lp_bound_propagator(T& imp, std_vector & ibounds) : m_imp(imp), m_ibounds(ibounds) {}
const std_vector& ibounds() const { return m_ibounds; }
void init() {
m_improved_upper_bounds.reset();
m_improved_lower_bounds.reset();
m_ibounds.clear();
m_column_types = &lp().get_column_types();
}
column_type get_column_type(unsigned j) const {
return (*m_column_types)[j];
}
const impq& get_lower_bound(unsigned j) const {
return lp().get_lower_bound(j);
}
const mpq& get_lower_bound_rational(unsigned j) const {
return lp().get_lower_bound(j).x;
}
const impq& get_upper_bound(unsigned j) const {
return lp().get_upper_bound(j);
}
const mpq& get_upper_bound_rational(unsigned j) const {
return lp().get_upper_bound(j).x;
}
// require also the zero infinitesemal part
bool column_is_fixed(lpvar j) const {
return (*m_column_types)[j] == column_type::fixed && get_lower_bound(j).y.is_zero();
}
void add_bound(mpq const& v, unsigned j, bool is_low, bool strict, std::function explain_bound) {
lconstraint_kind kind = is_low ? GE : LE;
if (strict)
kind = static_cast(kind / 2);
if (!m_imp.bound_is_interesting(j, kind, v))
return;
if (is_low) {
unsigned k;
if (m_improved_lower_bounds.find(j, k)) {
auto& found_bound = m_ibounds[k];
if (v > found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && strict)) {
found_bound.m_bound = v;
found_bound.m_strict = strict;
found_bound.set_explain(explain_bound);
TRACE("add_bound", lp().print_implied_bound(found_bound, tout););
}
} else {
m_improved_lower_bounds.insert(j, static_cast(m_ibounds.size()));
m_ibounds.push_back(implied_bound(v, j, is_low, strict, explain_bound));
TRACE("add_bound", lp().print_implied_bound(m_ibounds.back(), tout););
}
} else { // the upper bound case
unsigned k;
if (m_improved_upper_bounds.find(j, k)) {
auto& found_bound = m_ibounds[k];
if (v < found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && strict)) {
found_bound.m_bound = v;
found_bound.m_strict = strict;
found_bound.set_explain(explain_bound);
TRACE("add_bound", lp().print_implied_bound(found_bound, tout););
}
} else {
m_improved_upper_bounds.insert(j, static_cast(m_ibounds.size()));
m_ibounds.push_back(implied_bound(v, j, is_low, strict, explain_bound));
TRACE("add_bound", lp().print_implied_bound(m_ibounds.back(), tout););
}
}
}
void consume(const mpq& a, constraint_index ci) {
m_imp.consume(a, ci);
}
const mpq& val(unsigned j) const {
return lp().get_column_value(j).x; // figure out why it is safe to return .x
}
const impq& ival(unsigned j) const {
return lp().get_column_value(j); // figure out why it is safe to return .x
}
unsigned column(unsigned row, unsigned index) {
return lp().get_row(row)[index].var();
}
bool is_equal(lpvar j, lpvar k) const {
return m_imp.is_equal(col_to_imp(j), col_to_imp(k));
}
void clear_for_eq() {
m_visited_rows.reset();
}
std::ostream& print_expl(std::ostream& out, const explanation& exp) const {
for (auto p : exp)
lp().constraints().display(
out, [this](lpvar j) { return lp().get_variable_name(j); }, p.ci());
return out;
}
bool add_eq_on_columns(const explanation& exp, lpvar je, lpvar ke, bool is_fixed) {
lp_assert(je != ke && is_int(je) == is_int(ke));
lp_assert(ival(je) == ival(ke));
TRACE("eq",
tout << "reported idx " << je << ", " << ke << "\n";
print_expl(tout, exp);
tout << "theory_vars v" << lp().local_to_external(je) << " == v" << lp().local_to_external(ke) << "\n";);
bool added = m_imp.add_eq(je, ke, exp, is_fixed);
if (added) {
if (is_fixed)
lp().stats().m_fixed_eqs++;
else
lp().stats().m_offset_eqs++;
}
return added;
}
// column to theory_var
unsigned col_to_imp(unsigned j) const {
return lp().local_to_external(j);
}
// theory_var to column
unsigned imp_to_col(unsigned j) const {
return lp().external_to_local(j);
}
bool is_int(lpvar j) const {
return lp().column_is_int(j);
}
void explain_fixed_in_row(unsigned row, explanation& ex) {
TRACE("eq", tout << lp().get_row(row) << std::endl);
for (const auto& c : lp().get_row(row))
if (lp().column_is_fixed(c.var()))
explain_fixed_column(c.var(), ex);
}
unsigned explain_fixed_in_row_and_get_base(unsigned row, explanation& ex) {
unsigned base = UINT_MAX;
TRACE("eq", tout << lp().get_row(row) << std::endl);
for (const auto& c : lp().get_row(row)) {
if (lp().column_is_fixed(c.var())) {
explain_fixed_column(c.var(), ex);
}
else if (lp().is_base(c.var())) {
base = c.var();
}
}
return base;
}
void explain_fixed_column(unsigned j, explanation& ex) {
SASSERT(column_is_fixed(j));
auto* deps = lp().get_bound_constraint_witnesses_for_column(j);
for (auto ci : lp().flatten(deps))
ex.push_back(ci);
}
#ifdef Z3DEBUG
bool all_fixed_in_row(unsigned row) const {
for (const auto& c : lp().get_row(row))
if (!lp().column_is_fixed(c.var()))
return false;
return true;
}
// bounded by 2
unsigned num_of_non_fixed_in_row(unsigned row_index) const {
unsigned n_of_nfixed = 0;
for (const auto& c : lp().get_row(row_index)) {
if (column_is_fixed(c.var()))
continue;
n_of_nfixed++;
if (n_of_nfixed > 1)
return n_of_nfixed;
}
return n_of_nfixed;
}
#endif
// Let nf is the number of non-fixed columns in the row.
// Then the function returns min(nf, 3).
// if nf == 0, the row is of the form sum of fixed = 0
// if nf == 1, the row is of the form x + sum of fixed = 0, where x is not fixed base
// if nf == 2, the row is of the form x + ay + sum of fixed = 0, x is a non fixed base and y is not fixed
// y_sign is set to a, if abs(a)= 1, and 0 otherwise
unsigned extract_non_fixed(unsigned row_index, unsigned& x, unsigned& y, int& y_sign) const {
unsigned nf = 0; // number of non-fixed columns
y = UINT_MAX;
const auto& row = lp().get_row(row_index);
x = lp().get_base_column_in_row(row_index);
if (!column_is_fixed(x)) {
nf++;
} else {
// we have a fixed base column, exiting
return 0;
}
for (const auto& c : row) {
unsigned j = c.var();
if (j == x) continue;
if (column_is_fixed(j))
continue;
if (++nf > 2)
return nf;
lp_assert(is_not_set(y));
y = j;
if (c.coeff().is_one()) {
y_sign = 1;
} else if (c.coeff().is_minus_one()) {
y_sign = -1;
} else {
// y has a coefficient other than 1 or -1
y_sign = 0;
return nf; // maybe be too low but we don't care
}
}
return nf;
}
void try_add_equation_with_lp_fixed_tables(unsigned row_index, unsigned v_j) {
lp_assert(lp().get_base_column_in_row(row_index) == v_j);
lp_assert(num_of_non_fixed_in_row(row_index) == 1 || column_is_fixed(v_j));
if (column_is_fixed(v_j)) {
return;
}
unsigned j = null_lpvar;
if (!lp().find_in_fixed_tables(val(v_j), is_int(v_j), j)) {
try_add_equation_with_internal_fixed_tables(row_index);
return;
}
TRACE("eq",
tout << "v_j = ";
lp().print_column_info(v_j, tout) << std::endl;
tout << "found j " << j << std::endl; lp().print_column_info(j, tout) << std::endl;
print_row(tout, row_index) << std::endl;
);
explanation ex;
explain_fixed_in_row(row_index, ex);
explain_fixed_column(j, ex);
add_eq_on_columns(ex, j, v_j, true);
}
void cheap_eq_on_nbase(unsigned row_index) {
reset_cheap_eq _reset(*this);
TRACE("eq", tout << "row_index = " << row_index << "\n";
print_row(tout, row_index) << "\n";);
if (!check_insert(m_visited_rows, row_index))
return;
unsigned x, y;
int y_sign;
unsigned nf = extract_non_fixed(row_index, x, y, y_sign);
if (nf == 0 || nf > 2)
return;
if (nf == 1) {
lp_assert(is_not_set(y));
try_add_equation_with_lp_fixed_tables(row_index, x);
return;
}
if (y_sign == 0) {
// the coefficient before y is not 1 or -1
return;
}
lp_assert(y_sign == -1 || y_sign == 1);
lp_assert(lp().is_base(y) == false);
auto& table = y_sign == 1 ? m_row2index_pos : m_row2index_neg;
table.insert(val(x), row_index);
TRACE("eq", tout << "y = " << y << "\n";);
for (const column_cell& c : lp().get_column(y)) {
unsigned i = c.var(); // the running index of the row
if (i == row_index)
continue;
if (!check_insert(m_visited_rows, i))
continue;
unsigned y_nb;
nf = extract_non_fixed(i, x, y_nb, y_sign);
if (nf != 2 || y_sign == 0)
continue;
lp_assert(y_nb == y);
lp_assert(y_sign == 1 || y_sign == -1);
auto& table = y_sign == 1 ? m_row2index_pos : m_row2index_neg;
const auto& v = val(x);
unsigned found_i;;
if (!table.find(v, found_i)) {
table.insert(v, i);
} else {
explanation ex;
unsigned base_of_found = lp().get_base_column_in_row(found_i);
if (is_int(x) != is_int(base_of_found) || ival(x).y != ival(base_of_found).y)
continue;
explain_fixed_in_row(found_i, ex);
explain_fixed_in_row(i, ex);
TRACE("eq", {
print_row(tout, i);
print_row(tout, found_i) << "\n";
lp().print_column_info(base_of_found, tout);
lp().print_column_info(x, tout) << "\n";
});
add_eq_on_columns(ex, x, base_of_found, false);
}
}
}
std::ostream& print_row(std::ostream& out, unsigned row_index) const {
bool first = true;
for (const auto& c : lp().A_r().m_rows[row_index]) {
if (lp().column_is_fixed(c.var()))
continue;
if (c.coeff().is_one()) {
if (!first)
out << "+";
} else if (c.coeff().is_minus_one())
out << "-";
out << lp().get_variable_name(c.var()) << " ";
first = false;
}
out << "\n";
return out;
}
template
bool check_insert(C& table, unsigned j) {
if (table.contains(j))
return false;
table.insert(j);
return true;
}
};
} // namespace lp