mirror of
https://github.com/Z3Prover/z3
synced 2025-04-11 03:33:35 +00:00
957 lines
30 KiB
C++
957 lines
30 KiB
C++
/*++
|
|
Copyright (c) 2011 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
bound_propagator.cpp
|
|
|
|
Abstract:
|
|
|
|
Bound propagators for arithmetic.
|
|
Support class for implementing strategies and search procedures
|
|
|
|
Author:
|
|
|
|
Leonardo de Moura (leonardo) 2011-06-18.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
#include"bound_propagator.h"
|
|
#include<cmath>
|
|
|
|
// -------------------------------
|
|
// Bound Relaxation configuration
|
|
//
|
|
// The idea is to minimize errors in floating point computations
|
|
//
|
|
// If RELAX_BOUNDS is undefined, then bound relaxation is disabled.
|
|
// Otherwise, lower bounds l are relaxed using the formula
|
|
// PRECISION * floor(l * INV_PRECISION + TOLERANCE)
|
|
// and upper bounds u as:
|
|
// PRECISION * ceil(u * INV_PRECISION - TOLERANCE)
|
|
// In the LP literature, the suggested values are
|
|
// l := 10^-5 * floor(l*10^5 + 10^-6)
|
|
// u := 10^-5 * ceil(u*10^5 - 10^-6)
|
|
// I'm using the following values because of strict bounds
|
|
// l := 10^-6 * floor(l*10^6 + 10^-7)
|
|
// u := 10^-6 * ceil(u*10^6 - 10^-7)
|
|
#define RELAX_BOUNDS
|
|
#define TOLERANCE 0.0000001
|
|
#define PRECISION 0.000001
|
|
#define INV_PRECISION 1000000.0
|
|
// -------------------------------
|
|
|
|
bound_propagator::bound::bound(numeral_manager & m,
|
|
mpq const & k,
|
|
double approx_k,
|
|
bool lower,
|
|
bool strict,
|
|
unsigned lvl,
|
|
unsigned ts,
|
|
bkind bk,
|
|
unsigned c_idx,
|
|
assumption a,
|
|
bound * prev):
|
|
m_approx_k(approx_k),
|
|
m_lower(lower),
|
|
m_strict(strict),
|
|
m_kind(bk),
|
|
m_level(lvl),
|
|
m_timestamp(ts),
|
|
m_prev(prev) {
|
|
m.set(m_k, k);
|
|
if (bk == DERIVED)
|
|
m_constraint_idx = c_idx;
|
|
else
|
|
m_assumption = a;
|
|
}
|
|
|
|
bound_propagator::bound_propagator(numeral_manager & _m, allocator & a, params_ref const & p):
|
|
m(_m),
|
|
m_allocator(a),
|
|
m_eq_manager(m, a) {
|
|
m_timestamp = 0;
|
|
m_qhead = 0;
|
|
m_conflict = null_var;
|
|
updt_params(p);
|
|
reset_statistics();
|
|
}
|
|
|
|
bound_propagator::~bound_propagator() {
|
|
m.del(m_tmp);
|
|
reset();
|
|
}
|
|
|
|
void bound_propagator::del_constraints_core() {
|
|
constraint_vector::iterator it = m_constraints.begin();
|
|
constraint_vector::iterator end = m_constraints.end();
|
|
for (; it != end; ++it) {
|
|
del_constraint(*it);
|
|
}
|
|
m_constraints.reset();
|
|
}
|
|
|
|
void bound_propagator::del_constraints() {
|
|
SASSERT(scope_lvl() == 0);
|
|
if (m_constraints.empty())
|
|
return;
|
|
del_constraints_core();
|
|
m_constraints.finalize();
|
|
vector<wlist>::iterator it = m_watches.begin();
|
|
vector<wlist>::iterator end = m_watches.end();
|
|
for (; it != end; ++it)
|
|
it->finalize();
|
|
}
|
|
|
|
void bound_propagator::del_constraint(constraint & c) {
|
|
switch (c.m_kind) {
|
|
case LINEAR:
|
|
m_eq_manager.del(c.m_eq);
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void bound_propagator::updt_params(params_ref const & p) {
|
|
m_max_refinements = p.get_uint(":bound-max-refinements", 16);
|
|
m_threshold = p.get_double(":bound-threshold", 0.05);
|
|
m_small_interval = p.get_double(":bound-small-interval", 128);
|
|
m_strict2double = p.get_double(":strict2double", 0.00001);
|
|
}
|
|
|
|
void bound_propagator::get_param_descrs(param_descrs & r) {
|
|
r.insert(":bound-max-refinements", CPK_UINT, "(default: 16) maximum number of bound refinements (per round) for unbounded variables.");
|
|
r.insert(":bound-threshold", CPK_DOUBLE, "(default: 0.05) bound propagation improvement threshold ratio.");
|
|
}
|
|
|
|
void bound_propagator::collect_statistics(statistics & st) const {
|
|
st.update("bound conflicts", m_conflicts);
|
|
st.update("bound propagations", m_propagations);
|
|
st.update("bound false alarms", m_false_alarms);
|
|
}
|
|
|
|
void bound_propagator::reset_statistics() {
|
|
m_conflicts = 0;
|
|
m_propagations = 0;
|
|
m_false_alarms = 0;
|
|
}
|
|
|
|
void bound_propagator::mk_var(var x, bool is_int) {
|
|
m_is_int.reserve(x+1, false);
|
|
m_dead.reserve(x+1, true);
|
|
m_lowers.reserve(x+1, 0);
|
|
m_uppers.reserve(x+1, 0);
|
|
m_lower_refinements.reserve(x+1, 0);
|
|
m_upper_refinements.reserve(x+1, 0);
|
|
m_watches.reserve(x+1);
|
|
|
|
SASSERT(m_dead[x]);
|
|
|
|
m_is_int[x] = is_int;
|
|
m_dead[x] = false;
|
|
m_lowers[x] = 0;
|
|
m_uppers[x] = 0;
|
|
m_lower_refinements[x] = 0;
|
|
m_upper_refinements[x] = 0;
|
|
m_watches[x].reset();
|
|
}
|
|
|
|
void bound_propagator::del_var(var x) {
|
|
SASSERT(!m_dead[x]);
|
|
m_dead[x] = true;
|
|
// mark constraints containing x as dead.
|
|
wlist & wl = m_watches[x];
|
|
wlist::iterator it = wl.begin();
|
|
wlist::iterator end = wl.end();
|
|
for (; it != end; ++it) {
|
|
m_constraints[*it].m_dead = true;
|
|
}
|
|
}
|
|
|
|
void bound_propagator::mk_eq(unsigned sz, mpq * as, var * xs) {
|
|
linear_equation * eq = m_eq_manager.mk(sz, as, xs);
|
|
init_eq(eq);
|
|
}
|
|
|
|
void bound_propagator::mk_eq(unsigned sz, mpz * as, var * xs) {
|
|
linear_equation * eq = m_eq_manager.mk(sz, as, xs);
|
|
init_eq(eq);
|
|
}
|
|
|
|
void bound_propagator::init_eq(linear_equation * eq) {
|
|
if (eq == 0)
|
|
return;
|
|
unsigned c_idx = m_constraints.size();
|
|
m_constraints.push_back(constraint());
|
|
constraint & new_c = m_constraints.back();
|
|
new_c.m_kind = LINEAR;
|
|
new_c.m_dead = false;
|
|
new_c.m_timestamp = 0;
|
|
new_c.m_act = 0;
|
|
new_c.m_counter = 0;
|
|
new_c.m_eq = eq;
|
|
unsigned sz = eq->size();
|
|
for (unsigned i = 0; i < sz; i++) {
|
|
m_watches[eq->x(i)].push_back(c_idx);
|
|
}
|
|
if (propagate(c_idx) && scope_lvl() > 0)
|
|
m_reinit_stack.push_back(c_idx);
|
|
}
|
|
|
|
void bound_propagator::push() {
|
|
m_scopes.push_back(scope());
|
|
scope & s = m_scopes.back();
|
|
s.m_trail_limit = m_trail.size();
|
|
s.m_qhead_old = m_qhead;
|
|
s.m_reinit_stack_limit = m_reinit_stack.size();
|
|
s.m_timestamp_old = m_timestamp;
|
|
s.m_in_conflict = inconsistent();
|
|
}
|
|
|
|
void bound_propagator::undo_trail(unsigned old_sz) {
|
|
SASSERT(old_sz <= m_trail.size());
|
|
unsigned i = m_trail.size();
|
|
while (i > old_sz) {
|
|
--i;
|
|
trail_info & info = m_trail.back();
|
|
var x = info.x();
|
|
bool is_lower = info.is_lower();
|
|
m_trail.pop_back();
|
|
bound * b;
|
|
if (is_lower) {
|
|
b = m_lowers[x];
|
|
m_lowers[x] = b->m_prev;
|
|
}
|
|
else {
|
|
b = m_uppers[x];
|
|
m_uppers[x] = b->m_prev;
|
|
}
|
|
m.del(b->m_k);
|
|
b->~bound();
|
|
m_allocator.deallocate(sizeof(bound), b);
|
|
}
|
|
SASSERT(m_trail.size() == old_sz);
|
|
}
|
|
|
|
void bound_propagator::pop(unsigned num_scopes) {
|
|
unsigned lvl = scope_lvl();
|
|
SASSERT(num_scopes <= lvl);
|
|
unsigned new_lvl = lvl - num_scopes;
|
|
scope & s = m_scopes[new_lvl];
|
|
undo_trail(s.m_trail_limit);
|
|
m_timestamp = s.m_timestamp_old;
|
|
m_qhead = s.m_qhead_old;
|
|
if (!s.m_in_conflict)
|
|
m_conflict = null_var;
|
|
unsigned reinit_stack_sz = s.m_reinit_stack_limit;
|
|
m_scopes.shrink(new_lvl);
|
|
|
|
// reinitialize
|
|
unsigned i = reinit_stack_sz;
|
|
unsigned j = reinit_stack_sz;
|
|
unsigned sz = m_reinit_stack.size();
|
|
for (; i < sz; i++) {
|
|
unsigned c_idx = m_reinit_stack[i];
|
|
bool p = propagate(c_idx);
|
|
if (new_lvl > 0 && p) {
|
|
m_reinit_stack[j] = c_idx;
|
|
j++;
|
|
}
|
|
}
|
|
m_reinit_stack.shrink(j);
|
|
}
|
|
|
|
bool bound_propagator::assert_lower_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a) {
|
|
if (is_int(x)) {
|
|
if (m.is_int(k)) {
|
|
if (strict)
|
|
m.inc(k);
|
|
}
|
|
else {
|
|
m.ceil(k, k);
|
|
}
|
|
SASSERT(m.is_int(k));
|
|
strict = false;
|
|
}
|
|
TRACE("bound_propagator_detail", tout << "new lower x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
|
|
|
|
bound * old_lower = m_lowers[x];
|
|
if (old_lower) {
|
|
bool improves = m.gt(k, old_lower->m_k) || (!old_lower->m_strict && strict && m.eq(k, old_lower->m_k));
|
|
if (!improves) {
|
|
if (bk == DERIVED) {
|
|
TRACE("bound_propagator_detail", tout << "false alarm\n";);
|
|
m_false_alarms++;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (bk == DERIVED) {
|
|
TRACE("bound_propagator_derived", tout << "new lower x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
|
|
m_propagations++;
|
|
}
|
|
|
|
if (scope_lvl() == 0 && bk == DERIVED)
|
|
bk = AXIOM; // don't need justification at level 0
|
|
|
|
double approx_k = m.get_double(k);
|
|
TRACE("new_bound", tout << "x" << x << " lower: " << m.to_string(k) << " approx: " << approx_k << "\n";);
|
|
#ifdef RELAX_BOUNDS
|
|
approx_k = PRECISION*floor(approx_k*INV_PRECISION + TOLERANCE);
|
|
TRACE("new_bound", tout << "x" << x << " lower: " << m.to_string(k) << " relaxed approx: " << approx_k << "\n";);
|
|
#endif
|
|
void * mem = m_allocator.allocate(sizeof(bound));
|
|
bound * new_lower = new (mem) bound(m, k, approx_k, true, strict, scope_lvl(), m_timestamp, bk, c_idx, a, old_lower);
|
|
m_timestamp++;
|
|
m_lowers[x] = new_lower;
|
|
m_trail.push_back(trail_info(x, true));
|
|
m_lower_refinements[x]++;
|
|
check_feasibility(x);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool bound_propagator::assert_upper_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a) {
|
|
if (is_int(x)) {
|
|
if (m.is_int(k)) {
|
|
if (strict)
|
|
m.dec(k);
|
|
}
|
|
else {
|
|
m.floor(k, k);
|
|
}
|
|
SASSERT(m.is_int(k));
|
|
strict = false;
|
|
}
|
|
|
|
TRACE("bound_propagator_detail", tout << "new upper x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
|
|
|
|
bound * old_upper = m_uppers[x];
|
|
if (old_upper) {
|
|
bool improves = m.lt(k, old_upper->m_k) || (!old_upper->m_strict && strict && m.eq(k, old_upper->m_k));
|
|
if (!improves) {
|
|
if (bk == DERIVED) {
|
|
TRACE("bound_propagator_detail", tout << "false alarm\n";);
|
|
m_false_alarms++;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (bk == DERIVED) {
|
|
m_propagations++;
|
|
TRACE("bound_propagator_derived", tout << "new upper x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
|
|
}
|
|
|
|
if (scope_lvl() == 0 && bk == DERIVED)
|
|
bk = AXIOM; // don't need justification at level 0
|
|
|
|
double approx_k = m.get_double(k);
|
|
TRACE("new_bound", tout << "x" << x << " upper: " << m.to_string(k) << " approx: " << approx_k << "\n";);
|
|
#ifdef RELAX_BOUNDS
|
|
approx_k = PRECISION*ceil(approx_k*INV_PRECISION - TOLERANCE);
|
|
TRACE("new_bound", tout << "x" << x << " upper: " << m.to_string(k) << " relaxed approx: " << approx_k << "\n";);
|
|
#endif
|
|
|
|
void * mem = m_allocator.allocate(sizeof(bound));
|
|
bound * new_upper = new (mem) bound(m, k, approx_k, false, strict, scope_lvl(), m_timestamp, bk, c_idx, a, m_uppers[x]);
|
|
m_timestamp++;
|
|
m_uppers[x] = new_upper;
|
|
m_trail.push_back(trail_info(x, false));
|
|
m_upper_refinements[x]++;
|
|
check_feasibility(x);
|
|
return true;
|
|
}
|
|
|
|
bool bound_propagator::get_interval_size(var x, double & r) const {
|
|
bound * l = m_lowers[x];
|
|
bound * u = m_uppers[x];
|
|
if (l && u) {
|
|
r = u->m_approx_k - l->m_approx_k;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template<bool LOWER>
|
|
bool bound_propagator::relevant_bound(var x, double new_k) const {
|
|
TRACE("bound_propagator_detail", tout << "relevant_bound x" << x << " " << new_k << " LOWER: " << LOWER << "\n";
|
|
if (LOWER && has_lower(x)) tout << "old: " << m.to_string(m_lowers[x]->m_k) << " | " << m_lowers[x]->m_approx_k << "\n";
|
|
if (!LOWER && has_upper(x)) tout << "old: " << m.to_string(m_uppers[x]->m_k) << " | " << m_uppers[x]->m_approx_k << "\n";);
|
|
bound * b = LOWER ? m_lowers[x] : m_uppers[x];
|
|
if (b == 0)
|
|
return true; // variable did not have a bound
|
|
|
|
double interval_size;
|
|
bool bounded = get_interval_size(x, interval_size);
|
|
|
|
if (!is_int(x)) {
|
|
// check if the improvement is significant
|
|
double improvement;
|
|
double abs_k = b->m_approx_k;
|
|
if (abs_k < 0.0)
|
|
abs_k -= abs_k;
|
|
if (bounded)
|
|
improvement = m_threshold * std::max(std::min(interval_size, abs_k), 1.0);
|
|
else
|
|
improvement = m_threshold * std::max(abs_k, 1.0);
|
|
|
|
if (LOWER) {
|
|
if (new_k <= b->m_approx_k + improvement) {
|
|
TRACE("bound_propagator", tout << "LOWER new: " << new_k << " old: " << b->m_approx_k << " improvement is too small\n";);
|
|
return false; // improvement is too small
|
|
}
|
|
}
|
|
else {
|
|
if (new_k >= b->m_approx_k - improvement) {
|
|
TRACE("bound_propagator", tout << "UPPER new: " << new_k << " old: " << b->m_approx_k << " improvement is too small\n";);
|
|
return false; // improvement is too small
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (LOWER) {
|
|
if (new_k < b->m_approx_k + 1.0)
|
|
return false; // no improvement
|
|
}
|
|
else {
|
|
if (new_k > b->m_approx_k - 1.0)
|
|
return false; // no improvement
|
|
}
|
|
}
|
|
|
|
if (bounded && interval_size <= m_small_interval)
|
|
return true;
|
|
|
|
if (LOWER)
|
|
return m_lower_refinements[x] < m_max_refinements;
|
|
else
|
|
return m_upper_refinements[x] < m_max_refinements;
|
|
}
|
|
|
|
bool bound_propagator::relevant_lower(var x, double approx_k) const {
|
|
return relevant_bound<true>(x, approx_k);
|
|
}
|
|
|
|
bool bound_propagator::relevant_upper(var x, double approx_k) const {
|
|
return relevant_bound<false>(x, approx_k);
|
|
}
|
|
|
|
void bound_propagator::check_feasibility(var x) {
|
|
if (inconsistent())
|
|
return;
|
|
bound * l = m_lowers[x];
|
|
bound * u = m_uppers[x];
|
|
if (l && u) {
|
|
if (m.lt(l->m_k, u->m_k))
|
|
return;
|
|
if (!l->m_strict && !u->m_strict && m.eq(l->m_k, u->m_k))
|
|
return;
|
|
m_conflict = x;
|
|
m_conflicts++;
|
|
SASSERT(inconsistent());
|
|
TRACE("bound_propagator", tout << "inconsistency detected: x" << x << "\n"; display(tout););
|
|
}
|
|
}
|
|
|
|
void bound_propagator::propagate() {
|
|
m_to_reset_ts.reset();
|
|
|
|
while (m_qhead < m_trail.size()) {
|
|
if (inconsistent())
|
|
break;
|
|
trail_info & info = m_trail[m_qhead];
|
|
var x = info.x();
|
|
bool is_lower = info.is_lower();
|
|
bound * b = is_lower ? m_lowers[x] : m_uppers[x];
|
|
SASSERT(b);
|
|
unsigned ts = b->m_timestamp;
|
|
TRACE("bound_propagator_detail", tout << "propagating x" << x << "\n";);
|
|
m_qhead++;
|
|
wlist const & wl = m_watches[x];
|
|
wlist::const_iterator it = wl.begin();
|
|
wlist::const_iterator end = wl.end();
|
|
for (; it != end; ++it) {
|
|
unsigned c_idx = *it;
|
|
constraint & c = m_constraints[c_idx];
|
|
// We don't need to visit c if it was already propagated using b.
|
|
// Whenever we visit c we store in c.m_timestamp the current timestamp
|
|
// So, we know that c was already propagated any bound using bounds with timestamp lower than c.m_timestamp.
|
|
if (ts >= c.m_timestamp) {
|
|
if (c.m_timestamp == 0)
|
|
m_to_reset_ts.push_back(c_idx);
|
|
c.m_timestamp = m_timestamp;
|
|
propagate(c_idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned_vector::iterator it = m_to_reset_ts.begin();
|
|
unsigned_vector::iterator end = m_to_reset_ts.end();
|
|
for (; it != end; ++it)
|
|
m_constraints[*it].m_timestamp = 0;
|
|
}
|
|
|
|
bool bound_propagator::propagate(unsigned c_idx) {
|
|
constraint const & c = m_constraints[c_idx];
|
|
if (c.m_dead)
|
|
return false;
|
|
if (c.m_kind == LINEAR)
|
|
return propagate_eq(c_idx);
|
|
return false;
|
|
}
|
|
|
|
bool bound_propagator::propagate_eq(unsigned c_idx) {
|
|
constraint const & c = m_constraints[c_idx];
|
|
linear_equation * eq = c.m_eq;
|
|
|
|
#if 0
|
|
{
|
|
static unsigned counter = 0;
|
|
static unsigned visited = 0;
|
|
counter++;
|
|
visited += eq->size();
|
|
if (counter % 1000 == 0)
|
|
verbose_stream() << "[bound-propagator] :propagate-eq " << counter << " :visited-vars " << visited << std::endl;
|
|
}
|
|
#endif
|
|
|
|
TRACE("bound_propagator_detail", tout << "propagating using eq: "; m_eq_manager.display(tout, *eq); tout << "\n";);
|
|
// ll = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_i > 0} -a_i * upper(x_i))
|
|
// uu = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_i < 0} -a_i * upper(x_i))
|
|
unsigned ll_i = UINT_MAX; // position of the variable that couldn't contribute to ll
|
|
unsigned uu_i = UINT_MAX; // position of the variable that coundn't contribute to uu
|
|
bool ll_failed = false;
|
|
bool uu_failed = false;
|
|
double ll = 0.0;
|
|
double uu = 0.0;
|
|
unsigned sz = eq->size();
|
|
for (unsigned i = 0; i < sz; i++) {
|
|
var x_i = eq->x(i);
|
|
double a_i = eq->approx_a(i);
|
|
bound * l_i = m_lowers[x_i];
|
|
bound * u_i = m_uppers[x_i];
|
|
if (a_i < 0.0) {
|
|
if (!ll_failed) {
|
|
if (l_i == 0) {
|
|
if (ll_i == UINT_MAX)
|
|
ll_i = i;
|
|
else
|
|
ll_failed = true;
|
|
}
|
|
else {
|
|
ll -= a_i * l_i->m_approx_k;
|
|
}
|
|
}
|
|
|
|
if (!uu_failed) {
|
|
if (u_i == 0) {
|
|
if (uu_i == UINT_MAX)
|
|
uu_i = i;
|
|
else
|
|
uu_failed = true;
|
|
}
|
|
else {
|
|
uu -= a_i * u_i->m_approx_k;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (!ll_failed) {
|
|
if (u_i == 0) {
|
|
if (ll_i == UINT_MAX)
|
|
ll_i = i;
|
|
else
|
|
ll_failed = true;
|
|
}
|
|
else {
|
|
ll -= a_i * u_i->m_approx_k;
|
|
}
|
|
}
|
|
|
|
if (!uu_failed) {
|
|
if (l_i == 0) {
|
|
if (uu_i == UINT_MAX)
|
|
uu_i = i;
|
|
else
|
|
uu_failed = true;
|
|
}
|
|
else {
|
|
uu -= a_i * l_i->m_approx_k;
|
|
}
|
|
}
|
|
}
|
|
if (ll_failed && uu_failed)
|
|
return false; // nothing to propagate
|
|
}
|
|
|
|
bool propagated = false;
|
|
|
|
SASSERT(!ll_failed || !uu_failed);
|
|
if (ll_i == UINT_MAX || uu_i == UINT_MAX) {
|
|
for (unsigned i = 0; i < sz; i++) {
|
|
var x_i = eq->x(i);
|
|
double a_i = eq->approx_a(i);
|
|
bound * l_i = m_lowers[x_i];
|
|
bound * u_i = m_uppers[x_i];
|
|
// ll = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_i > 0} -a_i * upper(x_i))
|
|
// uu = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_i < 0} -a_i * upper(x_i))
|
|
if (ll_i == UINT_MAX) {
|
|
// can propagate a lower bound for a_i*x_i
|
|
if (a_i > 0.0) {
|
|
// can propagate a lower bound for x_i
|
|
double new_k = (ll + a_i * u_i->m_approx_k)/a_i;
|
|
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, i))
|
|
propagated = true;
|
|
}
|
|
else {
|
|
// a_i < 0.0
|
|
// can propagate a upper bound for x_i
|
|
double new_k = (ll + a_i * l_i->m_approx_k)/a_i;
|
|
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, i))
|
|
propagated = true;
|
|
}
|
|
}
|
|
if (uu_i == UINT_MAX) {
|
|
// can propagate an upper bound for a_i*x_i
|
|
if (a_i > 0.0) {
|
|
// can propagate a upper bound for x_i
|
|
double new_k = (uu + a_i * l_i->m_approx_k)/a_i;
|
|
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, i))
|
|
propagated = true;
|
|
}
|
|
else {
|
|
// a_i < 0.0
|
|
// can propagate a lower bound for x_i
|
|
double new_k = (uu + a_i * u_i->m_approx_k)/a_i;
|
|
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, i))
|
|
propagated = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ll_failed && ll_i != UINT_MAX) {
|
|
// can propagate a lower bound for the monomial at position ll_i
|
|
var x_i = eq->x(ll_i);
|
|
double a_i = eq->approx_a(ll_i);
|
|
double new_k = ll/a_i;
|
|
if (a_i > 0.0) {
|
|
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, ll_i))
|
|
propagated = true;
|
|
}
|
|
else {
|
|
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, ll_i))
|
|
propagated = true;
|
|
}
|
|
}
|
|
|
|
if (!uu_failed && uu_i != UINT_MAX) {
|
|
// can propagate a upper bound for the monomial at position uu_i
|
|
var x_i = eq->x(uu_i);
|
|
double a_i = eq->approx_a(uu_i);
|
|
double new_k = uu/a_i;
|
|
if (a_i > 0.0) {
|
|
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, uu_i))
|
|
propagated = true;
|
|
}
|
|
else {
|
|
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, uu_i))
|
|
propagated = true;
|
|
}
|
|
}
|
|
|
|
return propagated;
|
|
}
|
|
|
|
/**
|
|
\brief Try to propagate a lower bound for the variable stored at position i, using mpq's (rationals).
|
|
When this method is invoked, we know that all other variables have the "right" bounds, and
|
|
using doubles we improve the current known bound.
|
|
*/
|
|
bool bound_propagator::propagate_lower(unsigned c_idx, unsigned i) {
|
|
constraint const & c = m_constraints[c_idx];
|
|
linear_equation * eq = c.m_eq;
|
|
var x_i = eq->x(i);
|
|
mpz const & a_i = eq->a(i);
|
|
unsigned sz = eq->size();
|
|
mpq k;
|
|
bool strict = false;
|
|
bool neg_a_i = m.is_neg(a_i);
|
|
for (unsigned j = 0; j < sz; j++) {
|
|
if (i == j)
|
|
continue;
|
|
var x_j = eq->x(j);
|
|
mpz const & a_j = eq->a(j);
|
|
bound * b_j = (m.is_neg(a_j) == neg_a_i) ? m_uppers[x_j] : m_lowers[x_j];
|
|
TRACE("bound_propagator_step_detail", tout << "k: " << m.to_string(k) << " b_j->m_k: " << m.to_string(b_j->m_k) <<
|
|
" a_j: " << m.to_string(a_j) << "\n";);
|
|
SASSERT(b_j);
|
|
if (b_j->m_strict)
|
|
strict = true;
|
|
m.addmul(k, a_j, b_j->m_k, k);
|
|
}
|
|
TRACE("bound_propagator_step_detail", tout << "k: " << m.to_string(k) << "\n";);
|
|
m.neg(k);
|
|
m.div(k, a_i, k);
|
|
TRACE("bound_propagator_step", tout << "propagating lower x" << x_i << " " << m.to_string(k) << " strict: " << strict << " using\n";
|
|
m_eq_manager.display(tout, *eq); tout << "\n"; display_bounds_of(tout, *eq););
|
|
bool r = assert_lower_core(x_i, k, strict, DERIVED, c_idx, null_assumption);
|
|
m.del(k);
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
\brief Try to propagate a upper bound for the variable stored at position i, using mpq's (rationals).
|
|
When this method is invoked, we know that all other variables have the "right" bounds, and
|
|
using doubles we improve the current known bound.
|
|
*/
|
|
bool bound_propagator::propagate_upper(unsigned c_idx, unsigned i) {
|
|
constraint const & c = m_constraints[c_idx];
|
|
linear_equation * eq = c.m_eq;
|
|
var x_i = eq->x(i);
|
|
mpz const & a_i = eq->a(i);
|
|
unsigned sz = eq->size();
|
|
mpq k;
|
|
bool strict = false;
|
|
bool neg_a_i = m.is_neg(a_i);
|
|
for (unsigned j = 0; j < sz; j++) {
|
|
if (i == j)
|
|
continue;
|
|
var x_j = eq->x(j);
|
|
mpz const & a_j = eq->a(j);
|
|
bound * b_j = (m.is_neg(a_j) == neg_a_i) ? m_lowers[x_j] : m_uppers[x_j];
|
|
SASSERT(b_j);
|
|
if (b_j->m_strict)
|
|
strict = true;
|
|
m.addmul(k, a_j, b_j->m_k, k);
|
|
}
|
|
m.neg(k);
|
|
m.div(k, a_i, k);
|
|
TRACE("bound_propagator_step", tout << "propagating upper x" << x_i << " " << m.to_string(k) << " strict: " << strict << " using\n";
|
|
m_eq_manager.display(tout, *eq); tout << "\n"; display_bounds_of(tout, *eq););
|
|
bool r = assert_upper_core(x_i, k, strict, DERIVED, c_idx, null_assumption);
|
|
m.del(k);
|
|
return r;
|
|
}
|
|
|
|
void bound_propagator::reset() {
|
|
undo_trail(0);
|
|
del_constraints_core();
|
|
m_constraints.finalize();
|
|
m_is_int.finalize();
|
|
m_dead.finalize();
|
|
m_lowers.finalize();
|
|
m_uppers.finalize();
|
|
m_watches.finalize();
|
|
m_trail.finalize();
|
|
m_qhead = 0;
|
|
m_reinit_stack.finalize();
|
|
m_lower_refinements.finalize();
|
|
m_upper_refinements.finalize();
|
|
m_timestamp = 0;
|
|
m_conflict = null_var;
|
|
m_scopes.finalize();
|
|
}
|
|
|
|
bool bound_propagator::lower(var x, mpq & k, bool & strict, unsigned & ts) const {
|
|
bound * b = m_lowers[x];
|
|
if (!b)
|
|
return false;
|
|
m.set(k, b->m_k);
|
|
strict = b->m_strict;
|
|
ts = b->m_timestamp;
|
|
return true;
|
|
}
|
|
|
|
bool bound_propagator::upper(var x, mpq & k, bool & strict, unsigned & ts) const {
|
|
bound * b = m_uppers[x];
|
|
if (!b)
|
|
return false;
|
|
m.set(k, b->m_k);
|
|
strict = b->m_strict;
|
|
ts = b->m_timestamp;
|
|
return true;
|
|
}
|
|
|
|
bound_propagator::bound * bound_propagator::bound::at(unsigned timestamp) {
|
|
bound * r = this;
|
|
while (r != 0 && r->m_timestamp >= timestamp)
|
|
r = r->m_prev;
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
\brief Return true if the coefficient of x in eq is positive
|
|
*/
|
|
bool bound_propagator::is_a_i_pos(linear_equation const & eq, var x) const {
|
|
unsigned i = eq.pos(x);
|
|
if (i == UINT_MAX)
|
|
return false;
|
|
return m.is_pos(eq.a(i));
|
|
}
|
|
|
|
void bound_propagator::explain(var x, bound * b, unsigned ts, assumption_vector & ex) const {
|
|
if (!b)
|
|
return;
|
|
b = b->at(ts);
|
|
if (!b)
|
|
return;
|
|
if (b->m_kind == AXIOM || b->m_kind == DECISION)
|
|
return;
|
|
if (b->m_kind == ASSUMPTION) {
|
|
ex.push_back(b->m_assumption);
|
|
return;
|
|
}
|
|
svector<var_bound> & todo = const_cast<bound_propagator*>(this)->m_todo;
|
|
todo.reset();
|
|
unsigned qhead = 0;
|
|
todo.push_back(var_bound(x, b));
|
|
b->m_mark = true;
|
|
while (qhead < todo.size()) {
|
|
var_bound & vb = todo[qhead];
|
|
qhead ++;
|
|
var x = vb.first;
|
|
bound * b = vb.second;
|
|
SASSERT(b->kind() == ASSUMPTION || b->kind() == DERIVED);
|
|
if (b->kind() == ASSUMPTION) {
|
|
ex.push_back(b->m_assumption);
|
|
continue;
|
|
}
|
|
SASSERT(b->kind() == DERIVED);
|
|
constraint const & c = m_constraints[b->m_constraint_idx];
|
|
switch (c.m_kind) {
|
|
case LINEAR: {
|
|
linear_equation * eq = c.m_eq;
|
|
bool is_lower = b->is_lower();
|
|
if (!is_a_i_pos(*eq, x))
|
|
is_lower = !is_lower;
|
|
unsigned sz = eq->size();
|
|
for (unsigned i = 0; i < sz; i++) {
|
|
var x_i = eq->x(i);
|
|
if (x_i == x)
|
|
continue;
|
|
bound * b = (m.is_neg(eq->a(i)) == is_lower) ? m_lowers[x_i] : m_uppers[x_i];
|
|
SASSERT(b);
|
|
if (b->kind() == DERIVED || b->kind() == ASSUMPTION) {
|
|
if (!b->m_mark) {
|
|
b->m_mark = true;
|
|
todo.push_back(var_bound(x_i, b));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
unsigned sz = todo.size();
|
|
for (unsigned i = 0; i < sz; i++)
|
|
todo[i].second->m_mark = false;
|
|
todo.reset();
|
|
}
|
|
|
|
/**
|
|
\brief Compute lower (upper) bound for the linear polynomial as[0]*xs[0] + ... + as[sz-1]*xs[sz-1]
|
|
|
|
Return false if the lower (upper) bound is -oo (oo)
|
|
*/
|
|
template<bool LOWER, typename Numeral>
|
|
bool bound_propagator::get_bound(unsigned sz, Numeral const * as, var const * xs, mpq & r, bool & st) const {
|
|
st = false;
|
|
m.reset(r);
|
|
for (unsigned i = 0; i < sz; i++) {
|
|
var x_i = xs[i];
|
|
Numeral const & a_i = as[i];
|
|
if (m.is_zero(a_i))
|
|
continue;
|
|
bound * b = (m.is_neg(a_i) == LOWER) ? m_uppers[x_i] : m_lowers[x_i];
|
|
if (!b) {
|
|
m.reset(r);
|
|
return false;
|
|
}
|
|
if (b->m_strict)
|
|
st = true;
|
|
m.addmul(r, a_i, b->m_k, r);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool bound_propagator::lower(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const {
|
|
return get_bound<true, mpq>(sz, as, xs, r, st);
|
|
}
|
|
|
|
bool bound_propagator::upper(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const {
|
|
return get_bound<false, mpq>(sz, as, xs, r, st);
|
|
}
|
|
|
|
void bound_propagator::display_bounds_of(std::ostream & out, linear_equation const & eq) const {
|
|
for (unsigned i = 0; i < eq.size(); i++) {
|
|
display_var_bounds(out, eq.x(i));
|
|
out << "\n";
|
|
}
|
|
}
|
|
|
|
void bound_propagator::display_var_bounds(std::ostream & out, var x, bool approx, bool precise) const {
|
|
if (m_lowers[x]) {
|
|
if (precise)
|
|
out << m.to_string(m_lowers[x]->m_k);
|
|
if (precise && approx)
|
|
out << " | ";
|
|
if (approx)
|
|
out << m_lowers[x]->m_approx_k;
|
|
out << " " << (m_lowers[x]->m_strict ? "<" : "<=");
|
|
}
|
|
else {
|
|
out << "-oo <";
|
|
}
|
|
out << " x" << x << " ";
|
|
if (m_uppers[x]) {
|
|
out << (m_uppers[x]->m_strict ? "<" : "<=") << " ";
|
|
if (precise)
|
|
out << m.to_string(m_uppers[x]->m_k);
|
|
if (precise && approx)
|
|
out << " | ";
|
|
if (approx)
|
|
out << m_uppers[x]->m_approx_k;
|
|
}
|
|
else {
|
|
out << "< oo";
|
|
}
|
|
}
|
|
|
|
void bound_propagator::display_bounds(std::ostream & out, bool approx, bool precise) const {
|
|
unsigned num_vars = m_dead.size();
|
|
for (unsigned x = 0; x < num_vars; x++) {
|
|
if (!is_dead(x)) {
|
|
display_var_bounds(out, x, approx, precise);
|
|
out << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
void bound_propagator::display_constraints(std::ostream & out) const {
|
|
constraint_vector::const_iterator it = m_constraints.begin();
|
|
constraint_vector::const_iterator end = m_constraints.end();
|
|
for (; it != end; ++it) {
|
|
constraint const & c = *it;
|
|
if (c.m_kind == LINEAR) {
|
|
m_eq_manager.display(out, *(c.m_eq));
|
|
out << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
void bound_propagator::display(std::ostream & out) const {
|
|
display_bounds(out);
|
|
display_constraints(out);
|
|
}
|
|
|
|
|
|
|