mirror of
https://github.com/Z3Prover/z3
synced 2025-04-24 01:25:31 +00:00
Add new tactic bound-simplifier for integer-based bit-vector reasoning.
This commit is contained in:
parent
83662701b6
commit
db79346ef7
14 changed files with 460 additions and 12 deletions
|
@ -2,6 +2,8 @@ z3_add_component(simplifiers
|
|||
SOURCES
|
||||
bit_blaster.cpp
|
||||
bound_manager.cpp
|
||||
bound_propagator.cpp
|
||||
bound_simplifier.cpp
|
||||
bv_slice.cpp
|
||||
card2bv.cpp
|
||||
demodulator_simplifier.cpp
|
||||
|
@ -11,6 +13,7 @@ z3_add_component(simplifiers
|
|||
eliminate_predicates.cpp
|
||||
euf_completion.cpp
|
||||
extract_eqs.cpp
|
||||
linear_equation.cpp
|
||||
max_bv_sharing.cpp
|
||||
model_reconstruction_trail.cpp
|
||||
propagate_values.cpp
|
||||
|
|
942
src/ast/simplifiers/bound_propagator.cpp
Normal file
942
src/ast/simplifiers/bound_propagator.cpp
Normal file
|
@ -0,0 +1,942 @@
|
|||
/*++
|
||||
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 "ast/simplifiers/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() {
|
||||
for (auto & c : m_constraints) {
|
||||
del_constraint(c);
|
||||
}
|
||||
m_constraints.reset();
|
||||
}
|
||||
|
||||
void bound_propagator::del_constraints() {
|
||||
SASSERT(scope_lvl() == 0);
|
||||
if (m_constraints.empty())
|
||||
return;
|
||||
del_constraints_core();
|
||||
m_constraints.finalize();
|
||||
for (auto& wl : m_watches) {
|
||||
wl.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];
|
||||
for (auto c : wl) {
|
||||
m_constraints[c].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 == nullptr)
|
||||
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 == nullptr)
|
||||
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];
|
||||
for (unsigned c_idx : wl) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned c : m_to_reset_ts)
|
||||
m_constraints[c].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 == nullptr) {
|
||||
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 == nullptr) {
|
||||
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 == nullptr) {
|
||||
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 == nullptr) {
|
||||
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 != nullptr && 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;
|
||||
}
|
||||
}
|
||||
for (var_bound& vb : todo)
|
||||
vb.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 {
|
||||
for (constraint const& c : m_constraints) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
263
src/ast/simplifiers/bound_propagator.h
Normal file
263
src/ast/simplifiers/bound_propagator.h
Normal file
|
@ -0,0 +1,263 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bound_propagator.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Bound propagators for arithmetic.
|
||||
Support class for implementing strategies and search procedures
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-18.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/mpq.h"
|
||||
#include "util/vector.h"
|
||||
#include "util/params.h"
|
||||
#include "util/statistics.h"
|
||||
#include "util/numeral_buffer.h"
|
||||
#include "ast/simplifiers/linear_equation.h"
|
||||
|
||||
class bound_propagator {
|
||||
public:
|
||||
typedef unsigned var;
|
||||
typedef unsigned assumption;
|
||||
typedef unsynch_mpq_manager numeral_manager;
|
||||
typedef unsigned_vector assumption_vector;
|
||||
typedef unsigned constraint_id;
|
||||
typedef numeral_buffer<mpz, numeral_manager> mpz_buffer;
|
||||
typedef svector<double> double_vector;
|
||||
static const assumption null_assumption = UINT_MAX;
|
||||
static const var null_var = UINT_MAX;
|
||||
static const unsigned null_constraint_idx = UINT_MAX;
|
||||
class trail_info {
|
||||
unsigned m_x_lower;
|
||||
public:
|
||||
trail_info(var x, bool is_lower):m_x_lower((x << 1) + static_cast<unsigned>(is_lower)) {}
|
||||
trail_info():m_x_lower(UINT_MAX) {}
|
||||
var x() const { return m_x_lower >> 1; }
|
||||
bool is_lower() const { return (m_x_lower & 1) != 0; }
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
enum ckind { LINEAR // only linear equalities so far.
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Constraints don't need justification.
|
||||
*/
|
||||
class constraint {
|
||||
friend class bound_propagator;
|
||||
unsigned m_kind:2;
|
||||
unsigned m_dead:1;
|
||||
unsigned m_timestamp; // Constraint tried to propagate new bounds using bounds with timestamp < m_timestamp.
|
||||
unsigned m_act; // activity
|
||||
unsigned m_counter; // number of times the constraint propagated
|
||||
union {
|
||||
linear_equation * m_eq;
|
||||
};
|
||||
};
|
||||
|
||||
enum bkind { AXIOM, // doesn't need justification
|
||||
ASSUMPTION, // aka external case-split, it is used to connect with external search engine.
|
||||
DERIVED, // implied
|
||||
DECISION // internal case-split
|
||||
};
|
||||
|
||||
struct bound {
|
||||
mpq m_k;
|
||||
double m_approx_k;
|
||||
unsigned m_lower:1;
|
||||
unsigned m_strict:1;
|
||||
unsigned m_mark:1;
|
||||
unsigned m_kind:2;
|
||||
unsigned m_level:27;
|
||||
unsigned m_timestamp;
|
||||
union {
|
||||
assumption m_assumption;
|
||||
unsigned m_constraint_idx;
|
||||
};
|
||||
bound * m_prev;
|
||||
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);
|
||||
|
||||
bound * at(unsigned timestamp);
|
||||
bkind kind() const { return static_cast<bkind>(m_kind); }
|
||||
bool is_lower() const { return m_lower != 0; }
|
||||
};
|
||||
|
||||
typedef ptr_vector<bound> var2bound;
|
||||
typedef svector<var> var_vector;
|
||||
typedef svector<constraint> constraint_vector;
|
||||
typedef unsigned_vector c_idx_vector;
|
||||
typedef c_idx_vector wlist;
|
||||
typedef small_object_allocator allocator;
|
||||
typedef linear_equation_manager lin_eq_manager;
|
||||
|
||||
numeral_manager & m;
|
||||
allocator & m_allocator;
|
||||
lin_eq_manager m_eq_manager;
|
||||
constraint_vector m_constraints;
|
||||
char_vector m_is_int;
|
||||
char_vector m_dead;
|
||||
var2bound m_lowers;
|
||||
var2bound m_uppers;
|
||||
vector<wlist> m_watches;
|
||||
svector<trail_info> m_trail;
|
||||
unsigned m_qhead;
|
||||
c_idx_vector m_reinit_stack;
|
||||
unsigned_vector m_lower_refinements; // number of times a lower bound was propagated for each variable (loop prevention)
|
||||
unsigned_vector m_upper_refinements; // number of times a upper bound was propagated for each variable (loop prevention)
|
||||
unsigned m_timestamp;
|
||||
var m_conflict;
|
||||
mpq m_tmp;
|
||||
|
||||
struct scope {
|
||||
unsigned m_trail_limit;
|
||||
unsigned m_qhead_old;
|
||||
unsigned m_reinit_stack_limit;
|
||||
unsigned m_timestamp_old:31;
|
||||
unsigned m_in_conflict:1;
|
||||
};
|
||||
|
||||
svector<scope> m_scopes;
|
||||
|
||||
unsigned_vector m_to_reset_ts; // temp field: ids of the constraints we must reset the field m_timestamp
|
||||
|
||||
// config
|
||||
unsigned m_max_refinements; // maximum number of refinements per round
|
||||
double m_small_interval;
|
||||
double m_threshold; // improvement threshold
|
||||
double m_strict2double;
|
||||
|
||||
// statistics
|
||||
unsigned m_conflicts;
|
||||
unsigned m_propagations;
|
||||
unsigned m_false_alarms;
|
||||
|
||||
void del_constraint(constraint & cnstr);
|
||||
void del_constraints_core();
|
||||
|
||||
template<bool LOWER>
|
||||
bool relevant_bound(var x, double approx_k) const;
|
||||
bool relevant_lower(var x, double approx_k) const;
|
||||
bool relevant_upper(var x, double approx_k) const;
|
||||
bool get_interval_size(var x, double & r) const;
|
||||
void check_feasibility(var x);
|
||||
|
||||
bool assert_lower_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a);
|
||||
bool assert_upper_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a);
|
||||
|
||||
bool propagate(unsigned c_idx);
|
||||
bool propagate_eq(unsigned c_idx);
|
||||
bool propagate_lower(unsigned c_idx, unsigned i);
|
||||
bool propagate_upper(unsigned c_idx, unsigned i);
|
||||
void undo_trail(unsigned old_sz);
|
||||
|
||||
typedef std::pair<var, bound *> var_bound;
|
||||
svector<var_bound> m_todo;
|
||||
void explain(var x, bound * b, unsigned ts, assumption_vector & ex) const;
|
||||
bool is_a_i_pos(linear_equation const & eq, var x) const;
|
||||
|
||||
template<bool LOWER, typename Numeral>
|
||||
bool get_bound(unsigned sz, Numeral const * as, var const * xs, mpq & r, bool & st) const;
|
||||
|
||||
void init_eq(linear_equation * eq);
|
||||
|
||||
public:
|
||||
bound_propagator(numeral_manager & m, allocator & a, params_ref const & p);
|
||||
~bound_propagator();
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
static void get_param_descrs(param_descrs & r);
|
||||
|
||||
void collect_statistics(statistics & st) const;
|
||||
void reset_statistics();
|
||||
|
||||
double strict2double() const { return m_strict2double; }
|
||||
|
||||
bool is_int(var x) const { return m_is_int[x] != 0; }
|
||||
|
||||
unsigned scope_lvl() const { return m_scopes.size(); }
|
||||
void mk_var(var x, bool is_int);
|
||||
void del_var(var x);
|
||||
bool is_dead(var x) const { return m_dead[x] != 0; }
|
||||
void mk_eq(unsigned sz, mpq * as, var * xs);
|
||||
void mk_eq(unsigned sz, mpz * as, var * xs);
|
||||
void del_constraints();
|
||||
void assert_lower(var x, mpq const & k, bool strict, assumption a = null_assumption) {
|
||||
m.set(m_tmp, k);
|
||||
assert_lower_core(x, m_tmp, strict, a == null_assumption ? AXIOM : ASSUMPTION, 0, a);
|
||||
}
|
||||
void assert_upper(var x, mpq const & k, bool strict, assumption a = null_assumption) {
|
||||
m.set(m_tmp, k);
|
||||
assert_upper_core(x, m_tmp, strict, a == null_assumption ? AXIOM : ASSUMPTION, 0, a);
|
||||
}
|
||||
void assert_decided_lower(var x, mpq const & k) {
|
||||
m.set(m_tmp, k);
|
||||
assert_lower_core(x, m_tmp, false, DECISION, 0, null_assumption);
|
||||
}
|
||||
void assert_decided_upper(var x, mpq const & k) {
|
||||
m.set(m_tmp, k);
|
||||
assert_upper_core(x, m_tmp, false, DECISION, 0, null_assumption);
|
||||
}
|
||||
void propagate();
|
||||
void push();
|
||||
void pop(unsigned num_scopes);
|
||||
void reset();
|
||||
bool has_lower(var x) const { return m_lowers[x] != 0; }
|
||||
bool has_upper(var x) const { return m_uppers[x] != 0; }
|
||||
bool lower(var x, mpq & k, bool & strict, unsigned & ts) const;
|
||||
bool upper(var x, mpq & k, bool & strict, unsigned & ts) const;
|
||||
bool is_fixed(var x) const { return has_lower(x) && has_upper(x) && m.eq(m_lowers[x]->m_k, m_uppers[x]->m_k) && !inconsistent(); }
|
||||
mpq const & lower(var x, bool & strict) const { SASSERT(has_lower(x)); bound * b = m_lowers[x]; strict = b->m_strict; return b->m_k; }
|
||||
mpq const & upper(var x, bool & strict) const { SASSERT(has_upper(x)); bound * b = m_uppers[x]; strict = b->m_strict; return b->m_k; }
|
||||
mpq const & lower(var x) const { SASSERT(has_lower(x)); return m_lowers[x]->m_k; }
|
||||
mpq const & upper(var x) const { SASSERT(has_upper(x)); return m_uppers[x]->m_k; }
|
||||
double approx_lower(var x) const {
|
||||
SASSERT(has_lower(x));
|
||||
return m_lowers[x]->m_strict ? m_lowers[x]->m_approx_k + m_strict2double : m_lowers[x]->m_approx_k;
|
||||
}
|
||||
double approx_upper(var x) const {
|
||||
SASSERT(has_upper(x));
|
||||
return m_uppers[x]->m_strict ? m_uppers[x]->m_approx_k - m_strict2double : m_uppers[x]->m_approx_k;
|
||||
}
|
||||
bool is_zero(var x) const { return has_lower(x) && has_upper(x) && m.is_zero(lower(x)) && m.is_zero(upper(x)); }
|
||||
void explain_lower(var x, unsigned ts, assumption_vector & ex) const { explain(x, m_lowers[x], ts, ex); }
|
||||
void explain_upper(var x, unsigned ts, assumption_vector & ex) const { explain(x, m_uppers[x], ts, ex); }
|
||||
void explain_lower(var x, assumption_vector & ex) const { explain_lower(x, m_timestamp, ex); }
|
||||
void explain_upper(var x, assumption_vector & ex) const { explain_upper(x, m_timestamp, ex); }
|
||||
var conflict_var() const { return m_conflict; }
|
||||
bool inconsistent() const { return m_conflict != null_var; }
|
||||
|
||||
unsigned trail_size() const { return m_trail.size(); }
|
||||
unsigned qhead() const { return m_qhead; }
|
||||
|
||||
typedef svector<trail_info>::const_iterator trail_iterator;
|
||||
|
||||
trail_iterator begin_trail() const { return m_trail.begin(); }
|
||||
trail_iterator end_trail() const { return m_trail.end(); }
|
||||
|
||||
bool lower(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const;
|
||||
bool upper(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const;
|
||||
void display(std::ostream & out) const;
|
||||
void display_var_bounds(std::ostream & out, var x, bool approx = true, bool precise = true) const;
|
||||
void display_bounds(std::ostream & out, bool approx = true, bool precise = true) const;
|
||||
void display_precise_bounds(std::ostream & out) const { display_bounds(out, false, true); }
|
||||
void display_approx_bounds(std::ostream & out) const { display_bounds(out, true, false); }
|
||||
void display_constraints(std::ostream & out) const;
|
||||
void display_bounds_of(std::ostream & out, linear_equation const & eq) const;
|
||||
|
||||
unsigned get_num_false_alarms() const { return m_false_alarms; }
|
||||
unsigned get_num_propagations() const { return m_propagations; }
|
||||
};
|
||||
|
306
src/ast/simplifiers/bound_simplifier.cpp
Normal file
306
src/ast/simplifiers/bound_simplifier.cpp
Normal file
|
@ -0,0 +1,306 @@
|
|||
/*++
|
||||
Copyright (c) 2022 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bounds_simplifier.cpp
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2023-01-22
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/simplifiers/bound_simplifier.h"
|
||||
#include "ast/rewriter/rewriter_def.h"
|
||||
|
||||
struct bound_simplifier::rw_cfg : public default_rewriter_cfg {
|
||||
ast_manager& m;
|
||||
bound_propagator& bp;
|
||||
bound_simplifier& s;
|
||||
arith_util a;
|
||||
rw_cfg(bound_simplifier& s):m(s.m), bp(s.bp), s(s), a(m) {}
|
||||
|
||||
br_status reduce_app(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result, proof_ref& pr) {
|
||||
rational N, hi, lo;
|
||||
bool strict;
|
||||
if (a.is_mod(f) && num_args == 2 && a.is_numeral(args[1], N)) {
|
||||
expr* x = args[0];
|
||||
if (!s.has_upper(x, hi, strict) || strict)
|
||||
return BR_FAILED;
|
||||
if (!s.has_lower(x, lo, strict) || strict)
|
||||
return BR_FAILED;
|
||||
if (hi - lo >= N)
|
||||
return BR_FAILED;
|
||||
if (N > hi && lo >= 0) {
|
||||
result = x;
|
||||
return BR_DONE;
|
||||
}
|
||||
if (2*N > hi && lo >= N) {
|
||||
result = a.mk_sub(x, a.mk_int(N));
|
||||
s.m_rewriter(result);
|
||||
return BR_DONE;
|
||||
}
|
||||
IF_VERBOSE(2, verbose_stream() << "potentially missed simplification: " << mk_pp(x, m) << " " << lo << " " << hi << " not reduced\n");
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
};
|
||||
|
||||
struct bound_simplifier::rw : public rewriter_tpl<rw_cfg> {
|
||||
rw_cfg m_cfg;
|
||||
|
||||
rw(bound_simplifier& s):
|
||||
rewriter_tpl<rw_cfg>(s.m, false, m_cfg),
|
||||
m_cfg(s) {
|
||||
}
|
||||
};
|
||||
|
||||
void bound_simplifier::reduce() {
|
||||
|
||||
m_updated = true;
|
||||
for (unsigned i = 0; i < 5 && m_updated; ++i) {
|
||||
m_updated = false;
|
||||
reset();
|
||||
for (unsigned idx : indices())
|
||||
insert_bound(m_fmls[idx]);
|
||||
for (unsigned idx : indices())
|
||||
tighten_bound(m_fmls[idx]);
|
||||
|
||||
rw rw(*this);
|
||||
|
||||
// TODO: take over propagate_ineq:
|
||||
// bp.propagate();
|
||||
// serialize bounds
|
||||
|
||||
proof_ref pr(m);
|
||||
expr_ref r(m);
|
||||
for (unsigned idx : indices()) {
|
||||
auto const& d = m_fmls[idx];
|
||||
rw(d.fml(), r, pr);
|
||||
if (r != d.fml()) {
|
||||
m_fmls.update(idx, dependent_expr(m, r, mp(d.pr(), pr), d.dep()));
|
||||
m_updated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generalization to summations?
|
||||
|
||||
bool bound_simplifier::is_offset(expr* e, expr* x, rational& n) {
|
||||
expr* y, *z;
|
||||
if (a.is_add(e, y, z)) {
|
||||
if (x != y)
|
||||
std::swap(y, z);
|
||||
return x == y && a.is_numeral(z, n);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bound_simplifier::insert_bound(dependent_expr const& de) {
|
||||
if (de.pr())
|
||||
return false;
|
||||
if (de.dep())
|
||||
return false;
|
||||
rational n, n0;
|
||||
expr* x, *y, *f = de.fml();
|
||||
|
||||
if (m.is_eq(f, x, y)) {
|
||||
if (a.is_numeral(y))
|
||||
std::swap(x, y);
|
||||
if (a.is_numeral(x, n)) {
|
||||
assert_lower(y, n, false);
|
||||
assert_upper(y, n, false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (a.is_le(f, x, y)) {
|
||||
if (a.is_numeral(x, n))
|
||||
assert_lower(y, n, false);
|
||||
else if (a.is_numeral(y, n))
|
||||
assert_upper(x, n, false);
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else if (a.is_ge(f, x, y)) {
|
||||
if (a.is_numeral(x, n))
|
||||
assert_upper(y, n, false);
|
||||
else if (a.is_numeral(y, n))
|
||||
assert_lower(x, n, false);
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else if (m.is_not(f, f)) {
|
||||
if (a.is_le(f, x, y)) {
|
||||
if (a.is_numeral(x, n))
|
||||
assert_upper(y, n, true);
|
||||
else if (a.is_numeral(y, n))
|
||||
assert_lower(x, n, true);
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else if (a.is_ge(f, x, y)) {
|
||||
if (a.is_numeral(x, n))
|
||||
assert_lower(y, n, true);
|
||||
else if (a.is_numeral(y, n))
|
||||
assert_upper(x, n, true);
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void bound_simplifier::tighten_bound(dependent_expr const& de) {
|
||||
if (de.pr())
|
||||
return;
|
||||
if (de.dep())
|
||||
return;
|
||||
rational n, k;
|
||||
expr* x, *y, *f = de.fml();
|
||||
expr* z, *u;
|
||||
bool strict;
|
||||
if (a.is_le(f, x, y)) {
|
||||
// x <= (x + k) mod N && x >= 0 -> x + k < N
|
||||
if (a.is_mod(y, z, u) && a.is_numeral(u, n) && has_lower(x, k, strict) && k >= 0 && is_offset(z, x, k) && k > 0 && k < n) {
|
||||
assert_upper(x, n - k, true);
|
||||
}
|
||||
}
|
||||
if (m.is_not(f, f) && m.is_eq(f, x, y)) {
|
||||
if (a.is_numeral(x))
|
||||
std::swap(x, y);
|
||||
bool strict;
|
||||
if (a.is_numeral(y, n)) {
|
||||
if (has_lower(x, k, strict) && !strict && k == n)
|
||||
assert_lower(x, k, true);
|
||||
else if (has_upper(x, k, strict) && !strict && k == n)
|
||||
assert_upper(x, k, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bound_simplifier::assert_upper(expr* x, rational const& n, bool strict) {
|
||||
scoped_mpq c(nm);
|
||||
nm.set(c, n.to_mpq());
|
||||
bp.assert_upper(to_var(x), c, strict);
|
||||
}
|
||||
|
||||
|
||||
void bound_simplifier::assert_lower(expr* x, rational const& n, bool strict) {
|
||||
scoped_mpq c(nm);
|
||||
nm.set(c, n.to_mpq());
|
||||
bp.assert_lower(to_var(x), c, strict);
|
||||
}
|
||||
|
||||
//
|
||||
// TODO: Use math/interval/interval.h
|
||||
//
|
||||
|
||||
bool bound_simplifier::has_lower(expr* x, rational& n, bool& strict) {
|
||||
if (is_var(x)) {
|
||||
unsigned v = to_var(x);
|
||||
if (bp.has_lower(v)) {
|
||||
mpq const & q = bp.lower(v, strict);
|
||||
n = rational(q);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (a.is_numeral(x, n)) {
|
||||
strict = false;
|
||||
return true;
|
||||
}
|
||||
if (a.is_mod(x)) {
|
||||
n = rational::zero();
|
||||
strict = false;
|
||||
return true;
|
||||
}
|
||||
expr* y, *z;
|
||||
if (a.is_idiv(x, y, z) && has_lower(z, n, strict) && n > 0 && has_lower(y, n, strict))
|
||||
return true;
|
||||
|
||||
if (a.is_add(x)) {
|
||||
rational bound;
|
||||
strict = false;
|
||||
n = 0;
|
||||
bool is_strict;
|
||||
for (expr* arg : *to_app(x)) {
|
||||
if (!has_lower(arg, bound, is_strict))
|
||||
return false;
|
||||
strict |= is_strict;
|
||||
n += bound;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (a.is_mul(x, y, z)) {
|
||||
// TODO: this is done generally using math/interval/interval.h
|
||||
rational bound1, bound2;
|
||||
bool strict1, strict2;
|
||||
if (has_lower(y, bound1, strict1) && !strict1 &&
|
||||
has_lower(z, bound1, strict2) && !strict2 &&
|
||||
bound1 >= 0 && bound2 >= 0) {
|
||||
n = bound1*bound2;
|
||||
strict = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool bound_simplifier::has_upper(expr* x, rational& n, bool& strict) {
|
||||
if (is_var(x)) {
|
||||
unsigned v = to_var(x);
|
||||
if (bp.has_upper(v)) {
|
||||
mpq const & q = bp.upper(v, strict);
|
||||
n = rational(q);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// perform light-weight abstract analysis
|
||||
// y * (u / y) is bounded by u, if y > 0
|
||||
|
||||
if (a.is_numeral(x, n)) {
|
||||
strict = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (a.is_add(x)) {
|
||||
rational bound;
|
||||
strict = false;
|
||||
n = 0;
|
||||
bool is_strict;
|
||||
for (expr* arg : *to_app(x)) {
|
||||
if (!has_upper(arg, bound, is_strict))
|
||||
return false;
|
||||
strict |= is_strict;
|
||||
n += bound;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
expr* y, *z, *u, *v;
|
||||
if (a.is_mul(x, y, z) && a.is_idiv(z, u, v) && v == y && has_lower(y, n, strict) && n > 0 && has_upper(u, n, strict))
|
||||
return true;
|
||||
if (a.is_idiv(x, y, z) && has_lower(z, n, strict) && n > 0 && has_upper(y, n, strict))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void bound_simplifier::reset() {
|
||||
bp.reset();
|
||||
m_var2expr.reset();
|
||||
m_expr2var.reset();
|
||||
m_num_vars = 0;
|
||||
}
|
90
src/ast/simplifiers/bound_simplifier.h
Normal file
90
src/ast/simplifiers/bound_simplifier.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*++
|
||||
Copyright (c) 2022 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bound_simplifier.h
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2023-01-22
|
||||
|
||||
Description:
|
||||
|
||||
Collects bounds of sub-expressions and uses them to simplify modulus
|
||||
expressions.
|
||||
propagate_ineqs_tactic handles other propagations with bounds.
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/rewriter/th_rewriter.h"
|
||||
#include "ast/simplifiers/dependent_expr_state.h"
|
||||
#include "ast/simplifiers/bound_propagator.h"
|
||||
|
||||
|
||||
class bound_simplifier : public dependent_expr_simplifier {
|
||||
arith_util a;
|
||||
th_rewriter m_rewriter;
|
||||
unsynch_mpq_manager nm;
|
||||
small_object_allocator m_alloc;
|
||||
bound_propagator bp;
|
||||
unsigned m_num_vars = 0;
|
||||
ptr_vector<expr> m_var2expr;
|
||||
unsigned_vector m_expr2var;
|
||||
bool m_updated = false;
|
||||
|
||||
struct rw_cfg;
|
||||
struct rw;
|
||||
|
||||
bool insert_bound(dependent_expr const& de);
|
||||
void tighten_bound(dependent_expr const& de);
|
||||
|
||||
void reset();
|
||||
|
||||
expr* to_expr(unsigned v) const {
|
||||
return m_var2expr.get(v, nullptr);
|
||||
}
|
||||
|
||||
bool is_var(expr* e) const {
|
||||
return UINT_MAX != m_expr2var.get(e->get_id(), UINT_MAX);
|
||||
}
|
||||
|
||||
unsigned to_var(expr* e) {
|
||||
unsigned v = m_expr2var.get(e->get_id(), UINT_MAX);
|
||||
if (v == UINT_MAX) {
|
||||
v = m_num_vars++;
|
||||
bp.mk_var(v, a.is_int(e));
|
||||
m_expr2var.setx(e->get_id(), v, UINT_MAX);
|
||||
m_var2expr.setx(v, e, nullptr);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
void assert_lower(expr* x, rational const& n, bool strict);
|
||||
void assert_upper(expr* x, rational const& n, bool strict);
|
||||
|
||||
bool has_upper(expr* x, rational& n, bool& strict);
|
||||
bool has_lower(expr* x, rational& n, bool& strict);
|
||||
|
||||
// e = x + offset
|
||||
bool is_offset(expr* e, expr* x, rational& offset);
|
||||
|
||||
public:
|
||||
|
||||
bound_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls):
|
||||
dependent_expr_simplifier(m, fmls),
|
||||
a(m),
|
||||
m_rewriter(m),
|
||||
bp(nm, m_alloc, p) {
|
||||
}
|
||||
|
||||
char const* name() const override { return "bounds-simplifier"; }
|
||||
|
||||
bool supports_proofs() const override { return false; }
|
||||
|
||||
void reduce() override;
|
||||
};
|
||||
|
|
@ -88,10 +88,13 @@ namespace euf {
|
|||
expr_ref_vector m_args, m_trail;
|
||||
expr_sparse_mark m_nonzero;
|
||||
bool m_enabled = true;
|
||||
bool m_eliminate_mod = true;
|
||||
|
||||
|
||||
// solve u mod r1 = y -> u = r1*mod!1 + y
|
||||
void solve_mod(expr* orig, expr* x, expr* y, expr_dependency* d, dep_eq_vector& eqs) {
|
||||
if (!m_eliminate_mod)
|
||||
return;
|
||||
expr* u, * z;
|
||||
rational r1, r2;
|
||||
if (!a.is_mod(x, u, z))
|
||||
|
@ -296,13 +299,12 @@ break;
|
|||
add_pos(f);
|
||||
m_bm(f, d, p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void updt_params(params_ref const& p) override {
|
||||
tactic_params tp(p);
|
||||
m_enabled = p.get_bool("theory_solver", tp.solve_eqs_ite_solver());
|
||||
m_eliminate_mod = p.get_bool("eliminate_mod", true);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
279
src/ast/simplifiers/linear_equation.cpp
Normal file
279
src/ast/simplifiers/linear_equation.cpp
Normal file
|
@ -0,0 +1,279 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
linear_equation.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic infrastructure for managing linear equations of the form:
|
||||
|
||||
a_1 * x_1 + ... + a_n * x_n = 0
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-28
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include "ast/simplifiers/linear_equation.h"
|
||||
|
||||
/**
|
||||
\brief Return the position of variable x_i in the linear equation.
|
||||
Return UINT_MAX, if the variable is not in the linear_equation.
|
||||
*/
|
||||
unsigned linear_equation::pos(unsigned x_i) const {
|
||||
int low = 0;
|
||||
int high = m_size - 1;
|
||||
while (true) {
|
||||
int mid = low + ((high - low) / 2);
|
||||
var x_mid = m_xs[mid];
|
||||
if (x_i > x_mid) {
|
||||
low = mid + 1;
|
||||
if (low > high)
|
||||
return UINT_MAX;
|
||||
}
|
||||
else if (x_i < x_mid) {
|
||||
high = mid - 1;
|
||||
if (low > high)
|
||||
return UINT_MAX;
|
||||
}
|
||||
else {
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void linear_equation_manager::display(std::ostream & out, linear_equation const & eq) const {
|
||||
unsigned sz = eq.m_size;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
if (i > 0)
|
||||
out << " + ";
|
||||
out << m.to_string(eq.m_as[i]) << "*x" << eq.m_xs[i];
|
||||
}
|
||||
out << " = 0";
|
||||
}
|
||||
|
||||
linear_equation * linear_equation_manager::mk(unsigned sz, mpq * as, var * xs, bool normalized) {
|
||||
SASSERT(sz > 1);
|
||||
|
||||
// compute lcm of the denominators
|
||||
mpz l;
|
||||
mpz r;
|
||||
m.set(l, as[0].denominator());
|
||||
for (unsigned i = 1; i < sz; i++) {
|
||||
m.set(r, as[i].denominator());
|
||||
m.lcm(r, l, l);
|
||||
}
|
||||
|
||||
TRACE("linear_equation_mk", tout << "lcm: " << m.to_string(l) << "\n";);
|
||||
|
||||
// copy l * as to m_int_buffer.
|
||||
m_int_buffer.reset();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
TRACE("linear_equation_mk", tout << "before as[" << i << "]: " << m.to_string(as[i]) << "\n";);
|
||||
m.mul(l, as[i], as[i]);
|
||||
TRACE("linear_equation_mk", tout << "after as[" << i << "]: " << m.to_string(as[i]) << "\n";);
|
||||
SASSERT(m.is_int(as[i]));
|
||||
m_int_buffer.push_back(as[i].numerator());
|
||||
}
|
||||
|
||||
linear_equation * new_eq = mk(sz, m_int_buffer.data(), xs, normalized);
|
||||
|
||||
m.del(r);
|
||||
m.del(l);
|
||||
|
||||
return new_eq;
|
||||
}
|
||||
|
||||
linear_equation * linear_equation_manager::mk_core(unsigned sz, mpz * as, var * xs) {
|
||||
SASSERT(sz > 0);
|
||||
DEBUG_CODE({
|
||||
for (unsigned i = 1; i < sz; i++) {
|
||||
SASSERT(xs[i-1] < xs[i]);
|
||||
}
|
||||
});
|
||||
|
||||
TRACE("linear_equation_bug", for (unsigned i = 0; i < sz; i++) tout << m.to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";);
|
||||
|
||||
mpz g;
|
||||
m.set(g, as[0]);
|
||||
for (unsigned i = 1; i < sz; i++) {
|
||||
if (m.is_one(g))
|
||||
break;
|
||||
if (m.is_neg(as[i])) {
|
||||
m.neg(as[i]);
|
||||
m.gcd(g, as[i], g);
|
||||
m.neg(as[i]);
|
||||
}
|
||||
else {
|
||||
m.gcd(g, as[i], g);
|
||||
}
|
||||
}
|
||||
if (!m.is_one(g)) {
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
m.div(as[i], g, as[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("linear_equation_bug",
|
||||
tout << "g: " << m.to_string(g) << "\n";
|
||||
for (unsigned i = 0; i < sz; i++) tout << m.to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";);
|
||||
|
||||
m.del(g);
|
||||
|
||||
unsigned obj_sz = linear_equation::get_obj_size(sz);
|
||||
void * mem = m_allocator.allocate(obj_sz);
|
||||
linear_equation * new_eq = new (mem) linear_equation();
|
||||
mpz * new_as = reinterpret_cast<mpz*>(reinterpret_cast<char*>(new_eq) + sizeof(linear_equation));
|
||||
double * new_app_as = reinterpret_cast<double*>(reinterpret_cast<char*>(new_as) + sz * sizeof(mpz));
|
||||
var * new_xs = reinterpret_cast<var *>(reinterpret_cast<char*>(new_app_as) + sz * sizeof(double));
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
new (new_as + i) mpz();
|
||||
m.set(new_as[i], as[i]);
|
||||
new_app_as[i] = m.get_double(as[i]);
|
||||
var x_i = xs[i];
|
||||
new_xs[i] = x_i;
|
||||
}
|
||||
new_eq->m_size = sz;
|
||||
new_eq->m_as = new_as;
|
||||
new_eq->m_approx_as = new_app_as;
|
||||
new_eq->m_xs = new_xs;
|
||||
return new_eq;
|
||||
}
|
||||
|
||||
linear_equation * linear_equation_manager::mk(unsigned sz, mpz * as, var * xs, bool normalized) {
|
||||
if (!normalized) {
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m_mark.reserve(x+1, false);
|
||||
m_val_buffer.reserve(x+1);
|
||||
|
||||
if (m_mark[x]) {
|
||||
m.add(m_val_buffer[x], as[i], m_val_buffer[x]);
|
||||
}
|
||||
else {
|
||||
m.set(m_val_buffer[x], as[i]);
|
||||
m_mark[x] = true;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned j = 0;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
if (m_mark[x]) {
|
||||
if (!m.is_zero(m_val_buffer[x])) {
|
||||
xs[j] = xs[i];
|
||||
m.set(as[j], m_val_buffer[x]);
|
||||
j++;
|
||||
}
|
||||
m_mark[x] = false;
|
||||
}
|
||||
}
|
||||
sz = j;
|
||||
if (sz <= 1)
|
||||
return nullptr;
|
||||
}
|
||||
else {
|
||||
DEBUG_CODE({
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m_mark.reserve(x+1, false);
|
||||
SASSERT(!m_mark[x]);
|
||||
m_mark[x] = true;
|
||||
}
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m_mark[x] = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m_val_buffer.reserve(x+1);
|
||||
m.swap(m_val_buffer[x], as[i]);
|
||||
}
|
||||
std::sort(xs, xs+sz);
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m.swap(as[i], m_val_buffer[x]);
|
||||
}
|
||||
|
||||
return mk_core(sz, as, xs);
|
||||
}
|
||||
|
||||
linear_equation * linear_equation_manager::mk(mpz const & b1, linear_equation const & eq1, mpz const & b2, linear_equation const & eq2) {
|
||||
SASSERT(!m.is_zero(b1));
|
||||
SASSERT(!m.is_zero(b2));
|
||||
mpz tmp, new_a;
|
||||
m_int_buffer.reset();
|
||||
m_var_buffer.reset();
|
||||
unsigned sz1 = eq1.size();
|
||||
unsigned sz2 = eq2.size();
|
||||
unsigned i1 = 0;
|
||||
unsigned i2 = 0;
|
||||
while (true) {
|
||||
if (i1 == sz1) {
|
||||
// copy remaining entries from eq2
|
||||
while (i2 < sz2) {
|
||||
m_int_buffer.push_back(eq2.a(i2));
|
||||
m.mul(m_int_buffer.back(), b2, m_int_buffer.back());
|
||||
m_var_buffer.push_back(eq2.x(i2));
|
||||
i2++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (i2 == sz2) {
|
||||
// copy remaining entries from eq1
|
||||
while (i1 < sz1) {
|
||||
m_int_buffer.push_back(eq1.a(i1));
|
||||
m.mul(m_int_buffer.back(), b1, m_int_buffer.back());
|
||||
m_var_buffer.push_back(eq1.x(i1));
|
||||
i1++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
var x1 = eq1.x(i1);
|
||||
var x2 = eq2.x(i2);
|
||||
if (x1 < x2) {
|
||||
m_int_buffer.push_back(eq1.a(i1));
|
||||
m.mul(m_int_buffer.back(), b1, m_int_buffer.back());
|
||||
m_var_buffer.push_back(eq1.x(i1));
|
||||
i1++;
|
||||
}
|
||||
else if (x1 > x2) {
|
||||
m_int_buffer.push_back(eq2.a(i2));
|
||||
m.mul(m_int_buffer.back(), b2, m_int_buffer.back());
|
||||
m_var_buffer.push_back(eq2.x(i2));
|
||||
i2++;
|
||||
}
|
||||
else {
|
||||
m.mul(eq1.a(i1), b1, tmp);
|
||||
m.addmul(tmp, b2, eq2.a(i2), new_a);
|
||||
if (!m.is_zero(new_a)) {
|
||||
m_int_buffer.push_back(new_a);
|
||||
m_var_buffer.push_back(eq1.x(i1));
|
||||
}
|
||||
i1++;
|
||||
i2++;
|
||||
}
|
||||
}
|
||||
m.del(tmp);
|
||||
m.del(new_a);
|
||||
SASSERT(m_int_buffer.size() == m_var_buffer.size());
|
||||
if (m_int_buffer.empty())
|
||||
return nullptr;
|
||||
return mk_core(m_int_buffer.size(), m_int_buffer.data(), m_var_buffer.data());
|
||||
}
|
||||
|
||||
void linear_equation_manager::del(linear_equation * eq) {
|
||||
for (unsigned i = 0; i < eq->m_size; i++) {
|
||||
m.del(eq->m_as[i]);
|
||||
}
|
||||
unsigned obj_sz = linear_equation::get_obj_size(eq->m_size);
|
||||
m_allocator.deallocate(obj_sz, eq);
|
||||
}
|
||||
|
82
src/ast/simplifiers/linear_equation.h
Normal file
82
src/ast/simplifiers/linear_equation.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
linear_equation.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic infrastructure for managing linear equations of the form:
|
||||
|
||||
a_1 * x_1 + ... + a_n * x_n = 0
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-28
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/mpq.h"
|
||||
#include "util/small_object_allocator.h"
|
||||
#include "util/numeral_buffer.h"
|
||||
#include "util/double_manager.h"
|
||||
|
||||
class linear_equation {
|
||||
public:
|
||||
typedef unsigned var;
|
||||
private:
|
||||
static unsigned get_obj_size(unsigned sz) { return sizeof(linear_equation) + sz * (sizeof(mpz) + sizeof(double) + sizeof(var)); }
|
||||
friend class linear_equation_manager;
|
||||
unsigned m_size;
|
||||
mpz * m_as; // precise coefficients
|
||||
double * m_approx_as; // approximated coefficients
|
||||
var * m_xs; // var ids
|
||||
linear_equation() {}
|
||||
public:
|
||||
unsigned size() const { return m_size; }
|
||||
mpz const & a(unsigned idx) const { SASSERT(idx < m_size); return m_as[idx]; }
|
||||
double approx_a(unsigned idx) const { SASSERT(idx < m_size); return m_approx_as[idx]; }
|
||||
var x(unsigned idx) const { SASSERT(idx < m_size); return m_xs[idx]; }
|
||||
unsigned pos(unsigned x_i) const;
|
||||
void get_a(double_manager & m, unsigned idx, double & r) const { r = m_approx_as[idx]; }
|
||||
template<typename NumManager>
|
||||
void get_a(NumManager & m, unsigned idx, mpq & r) const { m.set(r, m_as[idx]); }
|
||||
template<typename NumManager>
|
||||
void get_a(NumManager & m, unsigned idx, mpz & r) const { m.set(r, m_as[idx]); }
|
||||
};
|
||||
|
||||
class linear_equation_manager {
|
||||
public:
|
||||
typedef unsynch_mpq_manager numeral_manager;
|
||||
typedef linear_equation::var var;
|
||||
typedef numeral_buffer<mpz, numeral_manager> mpz_buffer;
|
||||
private:
|
||||
typedef svector<var> var_buffer;
|
||||
|
||||
small_object_allocator & m_allocator;
|
||||
numeral_manager & m;
|
||||
mpz_buffer m_int_buffer;
|
||||
mpz_buffer m_val_buffer;
|
||||
char_vector m_mark;
|
||||
var_buffer m_var_buffer;
|
||||
|
||||
linear_equation * mk_core(unsigned sz, mpz * as, var * xs);
|
||||
|
||||
public:
|
||||
linear_equation_manager(numeral_manager & _m, small_object_allocator & a):m_allocator(a), m(_m), m_int_buffer(m), m_val_buffer(m) {}
|
||||
|
||||
linear_equation * mk(unsigned sz, mpq * as, var * xs, bool normalized = false);
|
||||
linear_equation * mk(unsigned sz, mpz * as, var * xs, bool normalized = false);
|
||||
void del(linear_equation * eq);
|
||||
|
||||
// Return b1 * eq1 + b2 * eq2
|
||||
// return 0 if the b1 * eq1 + b2 * eq2 == 0
|
||||
linear_equation * mk(mpz const & b1, linear_equation const & eq1, mpz const & b2, linear_equation const & eq2);
|
||||
|
||||
void display(std::ostream & out, linear_equation const & eq) const;
|
||||
};
|
||||
|
|
@ -294,6 +294,7 @@ namespace euf {
|
|||
r.insert("theory_solver", CPK_BOOL, "theory solvers.", "true");
|
||||
r.insert("ite_solver", CPK_BOOL, "use if-then-else solver.", "true");
|
||||
r.insert("context_solve", CPK_BOOL, "solve equalities under disjunctions.", "false");
|
||||
r.insert("eliminate_mod", CPK_BOOL, "eliminate modulus from equations", "true");
|
||||
}
|
||||
|
||||
void solve_eqs::collect_statistics(statistics& st) const {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue