mirror of
https://github.com/Z3Prover/z3
synced 2025-04-27 19:05:51 +00:00
add inequality propagation
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
7b3eaf75ce
commit
c1365b6ba8
3 changed files with 178 additions and 47 deletions
|
@ -53,10 +53,24 @@ namespace polysat {
|
|||
virtual void add_le(var_t v, var_t w, unsigned dep) = 0;
|
||||
virtual void add_lt(var_t v, var_t w, unsigned dep) = 0;
|
||||
virtual void restore_ineq() = 0;
|
||||
virtual bool inconsistent() const = 0;
|
||||
virtual unsigned_vector const& get_unsat_core() const = 0;
|
||||
|
||||
};
|
||||
|
||||
struct ineq {
|
||||
var_t v = UINT_MAX;
|
||||
var_t w = UINT_MAX;
|
||||
bool strict = false;
|
||||
bool is_active = true;
|
||||
unsigned dep = UINT_MAX;
|
||||
ineq(var_t v, var_t w, unsigned dep, bool s) :
|
||||
v(v), w(w), strict(s), dep(dep) {}
|
||||
|
||||
std::ostream& display(std::ostream& out) const {
|
||||
return out << "v" << v << (strict ? " < v" : " <= v") << w;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Ext>
|
||||
class fixplex : public fixplex_base {
|
||||
|
@ -143,19 +157,10 @@ namespace polysat {
|
|||
fix_entry():x(null_var), r(0) {}
|
||||
};
|
||||
|
||||
struct ineq {
|
||||
var_t v = null_var;
|
||||
var_t w = null_var;
|
||||
bool strict = false;
|
||||
bool is_active = true;
|
||||
unsigned dep = UINT_MAX;
|
||||
ineq(var_t v, var_t w, unsigned dep, bool s):
|
||||
v(v), w(w), strict(s), dep(dep) {}
|
||||
};
|
||||
|
||||
enum trail_i {
|
||||
inc_level_i,
|
||||
set_bound_i,
|
||||
set_inconsistent_i,
|
||||
add_ineq_i,
|
||||
add_row_i
|
||||
};
|
||||
|
@ -175,6 +180,7 @@ namespace polysat {
|
|||
random_gen m_random;
|
||||
uint_set m_left_basis;
|
||||
unsigned_vector m_unsat_core;
|
||||
bool m_inconsistent = false;
|
||||
unsigned_vector m_base_vars;
|
||||
stats m_stats;
|
||||
vector<stashed_bound> m_stashed_bounds;
|
||||
|
@ -200,6 +206,7 @@ namespace polysat {
|
|||
|
||||
void push() override;
|
||||
void pop(unsigned n) override;
|
||||
bool inconsistent() const override { return m_inconsistent; }
|
||||
|
||||
lbool make_feasible() override;
|
||||
void add_row(var_t base, unsigned num_vars, var_t const* vars, rational const* coeffs) override;
|
||||
|
@ -226,6 +233,10 @@ namespace polysat {
|
|||
unsigned get_num_vars() const { return m_vars.size(); }
|
||||
void reset();
|
||||
lbool propagate_bounds();
|
||||
|
||||
svector<std::pair<unsigned, ineq>> stack;
|
||||
uint_set on_stack;
|
||||
lbool propagate_ineqs(ineq const& i0);
|
||||
void propagate_eqs();
|
||||
vector<var_eq> const& var_eqs() const { return m_var_eqs; }
|
||||
void reset_eqs() { m_var_eqs.reset(); }
|
||||
|
@ -251,8 +262,8 @@ namespace polysat {
|
|||
void get_offset_eqs(row const& r);
|
||||
void fixed_var_eh(row const& r, var_t x);
|
||||
void eq_eh(var_t x, var_t y, row const& r1, row const& r2);
|
||||
lbool propagate_bounds(row const& r);
|
||||
bool propagate_bounds(ineq const& i);
|
||||
bool propagate_row(row const& r);
|
||||
bool propagate_ineq(ineq const& i);
|
||||
bool propagate_strict_bounds(ineq const& i);
|
||||
bool propagate_non_strict_bounds(ineq const& i);
|
||||
bool new_bound(row const& r, var_t x, mod_interval<numeral> const& range);
|
||||
|
@ -404,6 +415,7 @@ namespace polysat {
|
|||
}
|
||||
};
|
||||
typedef _scoped_numeral<manager> scoped_numeral;
|
||||
|
||||
};
|
||||
|
||||
typedef generic_uint_ext<uint64_t> uint64_ext;
|
||||
|
@ -415,5 +427,11 @@ namespace polysat {
|
|||
}
|
||||
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, ineq const& i) {
|
||||
return i.display(out);
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -96,6 +96,11 @@ namespace polysat {
|
|||
case trail_i::add_ineq_i:
|
||||
restore_ineq();
|
||||
break;
|
||||
case trail_i::set_inconsistent_i:
|
||||
SASSERT(m_inconsistent);
|
||||
m_inconsistent = false;
|
||||
m_unsat_core.reset();
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
@ -126,6 +131,8 @@ namespace polysat {
|
|||
|
||||
template<typename Ext>
|
||||
lbool fixplex<Ext>::make_feasible() {
|
||||
if (m_inconsistent)
|
||||
return l_false;
|
||||
++m_stats.m_num_checks;
|
||||
m_left_basis.reset();
|
||||
unsigned num_iterations = 0;
|
||||
|
@ -537,11 +544,13 @@ namespace polysat {
|
|||
auto hi0 = m_vars[v].hi;
|
||||
m_stashed_bounds.push_back(stashed_bound(v, m_vars[v]));
|
||||
m_trail.push_back(trail_i::set_bound_i);
|
||||
// std::cout << "new bound " << x << " " << m_vars[x] << " " << mod_interval<numeral>(l, h) << " -> ";
|
||||
m_vars[v] &= mod_interval(l, h);
|
||||
if (lo0 != m_vars[v].lo)
|
||||
m_vars[v].m_lo_dep = dep;
|
||||
if (hi0 != m_vars[v].hi)
|
||||
m_vars[v].m_hi_dep = dep;
|
||||
// std::cout << m_vars[x] << "\n";
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
|
@ -614,9 +623,9 @@ namespace polysat {
|
|||
if (idx >= m_ineqs.size())
|
||||
continue;
|
||||
auto& ineq = m_ineqs[idx];
|
||||
m_var_is_touched.setx(ineq.v, false, false);
|
||||
m_var_is_touched.setx(ineq.w, false, false);
|
||||
ineq.is_active = false;
|
||||
m_var_is_touched.setx(ineq.v, false, false);
|
||||
m_var_is_touched.setx(ineq.w, false, false);
|
||||
ineq.is_active = false;
|
||||
}
|
||||
m_ineqs_to_check.reset();
|
||||
}
|
||||
|
@ -646,11 +655,12 @@ namespace polysat {
|
|||
*/
|
||||
template<typename Ext>
|
||||
bool fixplex<Ext>::ineqs_are_violated() {
|
||||
lbool r;
|
||||
for (unsigned i = 0; i < m_ineqs_to_check.size(); ++i) {
|
||||
unsigned idx = m_ineqs_to_check[i];
|
||||
if (idx >= m_ineqs.size())
|
||||
continue;
|
||||
if (!propagate_bounds(m_ineqs[idx]))
|
||||
if (r = propagate_ineqs(m_ineqs[idx]), r == l_false)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -860,7 +870,6 @@ namespace polysat {
|
|||
*/
|
||||
template<typename Ext>
|
||||
void fixplex<Ext>::set_infeasible_base(var_t v) {
|
||||
m_unsat_core.reset();
|
||||
SASSERT(is_base(v));
|
||||
auto row = base2row(v);
|
||||
ptr_vector<u_dependency> todo;
|
||||
|
@ -869,7 +878,12 @@ namespace polysat {
|
|||
todo.push_back(m_vars[v].m_lo_dep);
|
||||
todo.push_back(m_vars[v].m_hi_dep);
|
||||
}
|
||||
SASSERT(!m_inconsistent);
|
||||
SASSERT(m_unsat_core.empty());
|
||||
m_inconsistent = true;
|
||||
m_trail.push_back(trail_i::set_inconsistent_i);
|
||||
m_deps.linearize(todo, m_unsat_core);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1115,15 +1129,87 @@ namespace polysat {
|
|||
template<typename Ext>
|
||||
lbool fixplex<Ext>::propagate_bounds() {
|
||||
lbool r = l_true;
|
||||
for (unsigned i = 0; r == l_true && i < m_rows.size(); ++i)
|
||||
r = propagate_bounds(row(i));
|
||||
if (r != l_true)
|
||||
return r;
|
||||
for (auto ineq : m_ineqs) {
|
||||
if (!propagate_bounds(ineq))
|
||||
for (unsigned i = 0; i < m_rows.size(); ++i)
|
||||
if (!propagate_row(row(i)))
|
||||
return l_false;
|
||||
for (auto ineq : m_ineqs)
|
||||
if (r = propagate_ineqs(ineq), r != l_true)
|
||||
return r;
|
||||
return l_true;
|
||||
}
|
||||
|
||||
//
|
||||
// DFS search propagating inequalities
|
||||
// TBDs:
|
||||
// - manage on_stack: which variables? (not source of root inequality)
|
||||
// - propagate other direction?
|
||||
// - re-propagate if lower bound for w gets increased in stack?
|
||||
// - combine with row propagation?
|
||||
// - search for shorter cycles? conflicts with literals asserted at lower (glue) levels?
|
||||
// - statistics
|
||||
// use diff-logic propagation instead?
|
||||
// - use heap based on potential
|
||||
// - update gamma without overflow issues
|
||||
//
|
||||
|
||||
template<typename Ext>
|
||||
lbool fixplex<Ext>::propagate_ineqs(ineq const& i0) {
|
||||
numeral old_lo = m_vars[i0.w].lo;
|
||||
if (!propagate_ineq(i0))
|
||||
return l_false;
|
||||
if (old_lo == m_vars[i0.w].lo)
|
||||
return l_true;
|
||||
on_stack.reset();
|
||||
stack.reset();
|
||||
stack.push_back(std::make_pair(0, i0));
|
||||
on_stack.insert(i0.v);
|
||||
while (!stack.empty()) {
|
||||
if (!m_limit.inc())
|
||||
return l_undef;
|
||||
auto [ineq_out, i] = stack.back();
|
||||
auto const& ineqs = m_var2ineqs[i.w];
|
||||
for (; ineq_out < ineqs.size(); ++ineq_out) {
|
||||
auto& i_out = m_ineqs[ineqs[ineq_out]];
|
||||
if (i.w != i_out.v)
|
||||
continue;
|
||||
old_lo = m_vars[i_out.w].lo;
|
||||
if (!propagate_ineq(i_out))
|
||||
return l_false;
|
||||
bool is_onstack = on_stack.contains(i_out.w);
|
||||
if (old_lo != m_vars[i_out.w].lo && !is_onstack) {
|
||||
on_stack.insert(i_out.w);
|
||||
stack.back().first = ineq_out + 1;
|
||||
stack.push_back(std::make_pair(0, i_out));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!is_onstack) {
|
||||
on_stack.insert(i_out.w);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool strict = i_out.strict, found = false;
|
||||
unsigned j = stack.size();
|
||||
for (; !found && j-- > 0; ) {
|
||||
ineq i2 = stack[j].second;
|
||||
strict |= i2.strict;
|
||||
if (i2.v == i_out.w)
|
||||
found = true;
|
||||
}
|
||||
if (strict && found) {
|
||||
auto* d = mk_leaf(i_out.dep);
|
||||
for (; j < stack.size(); ++j)
|
||||
d = m_deps.mk_join(d, mk_leaf(stack[j].second.dep));
|
||||
conflict(d);
|
||||
return l_false;
|
||||
}
|
||||
}
|
||||
if (ineq_out == ineqs.size()) {
|
||||
on_stack.remove(i.w);
|
||||
stack.pop_back();
|
||||
}
|
||||
}
|
||||
return r;
|
||||
return l_true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1136,7 +1222,7 @@ namespace polysat {
|
|||
*/
|
||||
|
||||
template<typename Ext>
|
||||
lbool fixplex<Ext>::propagate_bounds(row const& r) {
|
||||
bool fixplex<Ext>::propagate_row(row const& r) {
|
||||
mod_interval<numeral> range(0, 1);
|
||||
numeral free_c = 0;
|
||||
var_t free_v = null_var;
|
||||
|
@ -1152,12 +1238,12 @@ namespace polysat {
|
|||
}
|
||||
range += m_vars[v] * c;
|
||||
if (range.is_free())
|
||||
return l_true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (free_v != null_var) {
|
||||
range = (-range) * free_c;
|
||||
lbool res = new_bound(r, free_v, range) ? l_true : l_false;
|
||||
bool res = new_bound(r, free_v, range);
|
||||
SASSERT(in_bounds(free_v));
|
||||
return res;
|
||||
}
|
||||
|
@ -1165,7 +1251,7 @@ namespace polysat {
|
|||
var_t v = e.var();
|
||||
SASSERT(!is_free(v));
|
||||
auto range1 = range - m_vars[v] * e.coeff();
|
||||
lbool res = new_bound(r, v, range1) ? l_true : l_false;
|
||||
bool res = new_bound(r, v, range1);
|
||||
if (res != l_true)
|
||||
return res;
|
||||
// SASSERT(in_bounds(v));
|
||||
|
@ -1180,6 +1266,8 @@ namespace polysat {
|
|||
auto* vlo = m_vars[v].m_lo_dep, *vhi = m_vars[v].m_hi_dep;
|
||||
auto* wlo = m_vars[w].m_lo_dep, *whi = m_vars[w].m_hi_dep;
|
||||
|
||||
if (v == w)
|
||||
return conflict(i, nullptr), false;
|
||||
if (lo(w) == 0 && !new_bound(i, w, lo(w) + 1, lo(w), wlo))
|
||||
return false;
|
||||
if (hi(w) == 1 && !new_bound(i, w, lo(w), hi(w) - 1, whi))
|
||||
|
@ -1214,9 +1302,9 @@ namespace polysat {
|
|||
|
||||
// manual patch
|
||||
if (is_fixed(w) && lo(w) == 0)
|
||||
return conflict(wlo, whi), false;
|
||||
return conflict(i, wlo, whi), false;
|
||||
if (is_fixed(v) && hi(v) == 0)
|
||||
return conflict(vlo, vhi), false;
|
||||
return conflict(i, vlo, vhi), false;
|
||||
if (!is_free(w) && (lo(w) <= hi(w) || hi(w) == 0) && (lo(v) < hi(v) || hi(v) == 0) && !new_bound(i, v, lo(v), hi(w) - 1, vlo, wlo, whi))
|
||||
return false;
|
||||
if (!is_free(v) && (lo(w) <= hi(w) || hi(w) == 0) && (lo(v) < hi(v) || hi(v) == 0) && !new_bound(i, w, lo(v) + 1, hi(w), vlo, vhi, whi))
|
||||
|
@ -1234,7 +1322,7 @@ namespace polysat {
|
|||
if (hi(w) <= lo(v) && (lo(v) < hi(v) || hi(v) == 0) && !new_bound(i, w, lo(w), 0, vlo, vhi, wlo, whi))
|
||||
return false;
|
||||
if (lo(w) < hi(w) && hi(w) <= lo(v) && (lo(v) < hi(v) || hi(v) == 0))
|
||||
return conflict(vlo, vhi, wlo, whi), false;
|
||||
return conflict(i, vlo, vhi, wlo, whi), false;
|
||||
// if (!is_free(w) && hi(v) < lo(v) && lo(w) != 0 && (lo(w) <= hi(w) || hi(w) == 0) && !new_bound(i, v, lo(w) - 1, hi(v), vlo, vhi, wlo, whi))
|
||||
// return false;
|
||||
|
||||
|
@ -1245,7 +1333,7 @@ namespace polysat {
|
|||
if (lo(w) == 0 && !new_bound(i, w, lo(w) + 1, lo(w), wlo))
|
||||
return false;
|
||||
if (is_fixed(v) && hi(w) <= hi(v) && lo(w) <= hi(w) && !(is_free(w)))
|
||||
return conflict(wlo, whi, vhi, vlo), false;
|
||||
return conflict(i, wlo, whi, vhi, vlo), false;
|
||||
if (lo(w) <= lo(v) && lo(v) <= hi(v) && !new_bound(i, w, lo(v) + 1, lo(v), wlo, vhi, vlo))
|
||||
return false;
|
||||
if (hi(w) <= hi(v) && lo(w) <= hi(w) && !(is_free(w)) && !new_bound(i, v, lo(v), hi(v) - 1, wlo, whi, vhi))
|
||||
|
@ -1263,7 +1351,7 @@ namespace polysat {
|
|||
if (hi(w) <= lo(v) && lo(w) <= hi(w) && !(is_free(w)) && !new_bound(i, v, lo(v) + 1, hi(w) - 1, wlo, whi, vlo))
|
||||
return false;
|
||||
if (is_fixed(w) && hi(v) == 0 && lo(w) <= lo(v))
|
||||
return conflict(wlo, whi, vhi, vlo), false;
|
||||
return conflict(i, wlo, whi, vhi, vlo), false;
|
||||
if (hi(v) == 0 && lo(w) <= lo(v) && !new_bound(i, w, lo(v) + 1, hi(v), wlo, vhi, vlo))
|
||||
return false;
|
||||
if (hi(v) == 0 && !(is_free(v)) && !new_bound(i, v, lo(v), hi(v) - 1, vhi))
|
||||
|
@ -1290,7 +1378,7 @@ namespace polysat {
|
|||
if (hi(w) < lo(w) && hi(w) <= lo(v) && lo(v) < hi(v) && !new_bound(i, w, lo(w), 0, vlo, vhi, wlo, whi))
|
||||
return false;
|
||||
if (lo(w) < hi(w) && hi(w) <= lo(v) && (lo(v) < hi(v) || hi(v) == 0))
|
||||
return conflict(vlo, vhi, wlo, whi), false;
|
||||
return conflict(i, vlo, vhi, wlo, whi), false;
|
||||
|
||||
// automatically generated code.
|
||||
// see scripts/fixplex.py for script
|
||||
|
@ -1366,7 +1454,7 @@ namespace polysat {
|
|||
}
|
||||
|
||||
template<typename Ext>
|
||||
bool fixplex<Ext>::propagate_bounds(ineq const& i) {
|
||||
bool fixplex<Ext>::propagate_ineq(ineq const& i) {
|
||||
if (i.strict)
|
||||
return propagate_strict_bounds(i);
|
||||
else
|
||||
|
@ -1382,6 +1470,9 @@ namespace polysat {
|
|||
void fixplex<Ext>::conflict(u_dependency* a) {
|
||||
m_unsat_core.reset();
|
||||
m_deps.linearize(a, m_unsat_core);
|
||||
SASSERT(!m_inconsistent);
|
||||
m_inconsistent = true;
|
||||
m_trail.push_back(trail_i::set_inconsistent_i);
|
||||
TRACE("polysat", tout << "core: " << m_unsat_core << "\n";);
|
||||
}
|
||||
|
||||
|
@ -1399,10 +1490,8 @@ namespace polysat {
|
|||
template<typename Ext>
|
||||
bool fixplex<Ext>::new_bound(ineq const& i, var_t x, numeral const& l, numeral const& h, u_dependency* a, u_dependency* b, u_dependency* c, u_dependency* d) {
|
||||
bool was_fixed = lo(x) + 1 == hi(x);
|
||||
u_dependency* dep = m_deps.mk_join(mk_leaf(i.dep), m_deps.mk_join(a, m_deps.mk_join(b, m_deps.mk_join(c, d))));
|
||||
// std::cout << "new bound " << x << " " << m_vars[x] << " " << mod_interval<numeral>(l, h) << " -> ";
|
||||
u_dependency* dep = m_deps.mk_join(mk_leaf(i.dep), m_deps.mk_join(a, m_deps.mk_join(b, m_deps.mk_join(c, d))));
|
||||
update_bounds(x, l, h, dep);
|
||||
// std::cout << m_vars[x] << "\n";
|
||||
if (m_vars[x].is_empty())
|
||||
return conflict(m_vars[x].m_lo_dep, m_vars[x].m_hi_dep), false;
|
||||
else if (!was_fixed && lo(x) + 1 == hi(x)) {
|
||||
|
@ -1435,12 +1524,8 @@ namespace polysat {
|
|||
if (vi.m_is_base) out << "b:" << vi.m_base2row << " " << pp(m_rows[vi.m_base2row].m_value) << " ";
|
||||
out << "\n";
|
||||
}
|
||||
for (auto const& i : m_ineqs) {
|
||||
if (i.strict)
|
||||
out << "v" << i.v << " < v" << i.w << "\n";
|
||||
else
|
||||
out << "v" << i.v << " <= v" << i.w << "\n";
|
||||
}
|
||||
for (ineq const& i : m_ineqs)
|
||||
out << i << "\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
|
@ -131,13 +131,40 @@ namespace polysat {
|
|||
std::cout << 29 << " " << e.mul_inverse(29) << " " << 29*e.mul_inverse(29) << "\n";
|
||||
}
|
||||
|
||||
static void test_ineq1() {
|
||||
std::cout << "ineq1\n";
|
||||
static void test_ineq_propagation1() {
|
||||
std::cout << "ineq propagation 1\n";
|
||||
scoped_fp fp;
|
||||
var_t x = 0, y = 1;
|
||||
fp.add_lt(x, y, 1);
|
||||
fp.add_le(y, x, 2);
|
||||
fp.run();
|
||||
std::cout << "core:" << fp.get_unsat_core() << "\n";
|
||||
SASSERT(fp.get_unsat_core().contains(1));
|
||||
SASSERT(fp.get_unsat_core().contains(2));
|
||||
}
|
||||
|
||||
static void test_ineq_propagation2() {
|
||||
std::cout << "ineq propagation 2\n";
|
||||
scoped_fp fp;
|
||||
var_t x = 0, y = 1, z = 2, u = 3, v = 4;
|
||||
fp.add_le(x, y, 1);
|
||||
fp.add_le(y, z, 2);
|
||||
fp.add_le(z, u, 3);
|
||||
fp.add_le(u, v, 4);
|
||||
fp.add_le(v, x, 5);
|
||||
fp.run();
|
||||
for (unsigned i = 0; i < 4; ++i) {
|
||||
SASSERT(!fp.inconsistent());
|
||||
fp.push();
|
||||
fp.add_lt(i, i + 1, 6);
|
||||
fp.run();
|
||||
SASSERT(fp.inconsistent());
|
||||
std::cout << "core:" << fp.get_unsat_core() << "\n";
|
||||
for (unsigned j = 1; j < 5; ++j)
|
||||
SASSERT((j != i + 1) == fp.get_unsat_core().contains(j));
|
||||
fp.get_unsat_core().contains(6);
|
||||
fp.pop(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_ineqs() {
|
||||
|
@ -344,7 +371,8 @@ namespace polysat {
|
|||
|
||||
void tst_fixplex() {
|
||||
|
||||
polysat::test_ineq1();
|
||||
polysat::test_ineq_propagation1();
|
||||
polysat::test_ineq_propagation2();
|
||||
polysat::test_ineqs();
|
||||
|
||||
polysat::test1();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue