3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-05-04 14:25:46 +00:00

adding new viable using forbidden intervals

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2021-11-10 08:23:45 -08:00
parent 733f44d141
commit 0bec8520e1
9 changed files with 201 additions and 254 deletions

View file

@ -12,47 +12,25 @@ Author:
Notes:
Use cheap heuristics to narrow viable sets whenever possible.
If the cheap heuristics fail, compute a BDD representing the viable sets
and narrow the range using the BDDs that are cached.
--*/
#include "math/polysat/viable.h"
#include "math/polysat/solver.h"
#if NEW_VIABLE
#include "math/polysat/viable_set_def.h"
#endif
namespace polysat {
viable::viable(solver& s):
s(s),
m_bdd(1000)
{}
viable::~viable() {
#if NEW_VIABLE
ptr_vector<cached_constraint> entries;
for (auto* e : m_constraint_cache)
entries.push_back(e);
m_constraint_cache.reset();
for (auto* e : entries)
dealloc(e);
#endif
}
void viable::push_viable(pvar v) {
s.m_trail.push_back(trail_instr_t::viable_i);
#if NEW_VIALBLE
m_viable_trail.push_back(std::make_pair(v, alloc(viable_set, *m_viable[v])));
#else
m_viable_trail.push_back(std::make_pair(v, m_viable[v]));
#endif
}
@ -65,13 +43,6 @@ namespace polysat {
// a*v + b == 0 or a*v + b != 0
void viable::intersect_eq(rational const& a, pvar v, rational const& b, bool is_positive) {
#if NEW_VIABLE
push_viable(v);
if (!m_viable[v]->intersect_eq(a, b, is_positive))
intersect_eq_bdd(v, a, b, is_positive);
if (m_viable[v]->is_empty())
s.set_conflict(v);
#else
bddv const& x = var2bits(v).var();
if (b == 0 && a.is_odd()) {
@ -94,17 +65,9 @@ namespace polysat {
bdd xs = is_positive ? lhs.all0() : !lhs.all0();
intersect_viable(v, xs);
}
#endif
}
void viable::intersect_ule(pvar v, rational const& a, rational const& b, rational const& c, rational const& d, bool is_positive) {
#if NEW_VIABLE
push_viable(v);
if (!m_viable[v]->intersect_le(a, b, c, d, is_positive))
intersect_ule_bdd(v, a, b, c, d, is_positive);
if (m_viable[v]->is_empty())
s.set_conflict(v);
#else
bddv const& x = var2bits(v).var();
// hacky special case
if (a == 1 && b == 0 && c == 0 && d == 0)
@ -117,144 +80,40 @@ namespace polysat {
bdd xs = is_positive ? (l <= r) : (l > r);
intersect_viable(v, xs);
}
#endif
}
#if NEW_VIABLE
viable::cached_constraint& viable::cache_constraint(pvar v, cached_constraint& entry0, std::function<bdd(void)>& mk_constraint) {
cached_constraint* other = nullptr;
if (!m_constraint_cache.find(&entry0, other)) {
gc_cached_constraints();
other = alloc(cached_constraint, entry0);
other->repr = mk_constraint();
m_constraint_cache.insert(other);
}
other->m_activity++;
return *other;
}
void viable::gc_cached_constraints() {
//
// TODO: instead of using activity for GC, use the Move-To-Front approach
// see sat/smt/bv_ackerman.h or sat/smt/euf_ackerman.h
// where hash table entries use a dll_base.
//
unsigned max_entries = 10000;
if (m_constraint_cache.size() > max_entries) {
ptr_vector<cached_constraint> entries;
for (auto* e : m_constraint_cache)
entries.push_back(e);
std::stable_sort(entries.begin(), entries.end(), [&](cached_constraint* a, cached_constraint* b) { return a->m_activity < b->m_activity; });
for (auto* e : entries)
e->m_activity /= 2;
for (unsigned i = 0; i < max_entries/2; ++i) {
m_constraint_cache.remove(entries[i]);
dealloc(entries[i]);
}
}
}
void viable::narrow(pvar v, bdd const& is_false) {
rational bound = m_viable[v]->lo;
if (var2bits(v).sup(is_false, bound))
m_viable[v]->update_lo(m_viable[v]->next(bound));
bound = m_viable[v]->prev(m_viable[v]->hi);
if (var2bits(v).inf(is_false, bound))
m_viable[v]->update_hi(m_viable[v]->prev(bound));
}
void viable::intersect_ule_bdd(pvar v, rational const& a, rational const& b, rational const& c, rational const& d, bool is_positive) {
unsigned sz = var2bits(v).num_bits();
std::function<bdd(void)> le = [&]() {
bddv const& x = var2bits(v).var();
return ((a * x) + b) <= ((c * x) + d);
};
cached_constraint entry0(sz, a, b, c, d, m_bdd.mk_true());
cached_constraint& entry = cache_constraint(v, entry0, le);
// le(lo) is false: find min x >= lo, such that le(x) is false, le(x+1) is true
// le(hi) is false: find max x =< hi, such that le(x) is false, le(x-1) is true
bdd gt = is_positive ? !entry.repr : entry.repr;
narrow(v, gt);
}
void viable::intersect_eq_bdd(pvar v, rational const& a, rational const& b, bool is_positive) {
unsigned sz = var2bits(v).num_bits();
std::function<bdd(void)> eq = [&]() {
bddv const& x = var2bits(v).var();
return ((a * x) + b) == rational(0);
};
cached_constraint entry0(sz, a, b, m_bdd.mk_true());
cached_constraint& entry = cache_constraint(v, entry0, eq);
bdd ne = is_positive ? !entry.repr : entry.repr;
narrow(v, ne);
}
#endif
bool viable::has_viable(pvar v) {
#if NEW_VIABLE
return !m_viable[v]->is_empty();
#else
return !m_viable[v].is_false();
#endif
}
bool viable::is_viable(pvar v, rational const& val) {
#if NEW_VIABLE
return m_viable[v]->contains(val);
#else
return var2bits(v).contains(m_viable[v], val);
#endif
}
void viable::add_non_viable(pvar v, rational const& val) {
#if NEW_VIABLE
push_viable(v);
IF_VERBOSE(10, verbose_stream() << " v" << v << " != " << val << "\n");
m_viable[v]->intersect_diff(val);
if (m_viable[v]->is_empty())
s.set_conflict(v);
#else
LOG("pvar " << v << " /= " << val);
SASSERT(is_viable(v, val));
auto const& bits = var2bits(v);
intersect_viable(v, bits.var() != val);
#endif
}
#if !NEW_VIABLE
void viable::intersect_viable(pvar v, bdd vals) {
push_viable(v);
m_viable[v] &= vals;
if (m_viable[v].is_false())
s.set_conflict(v);
}
#endif
dd::find_t viable::find_viable(pvar v, rational & val) {
#if NEW_VIABLE
return m_viable[v]->find_hint(s.m_value[v], val);
#else
return var2bits(v).find_hint(m_viable[v], s.m_value[v], val);
#endif
}
rational viable::min_viable(pvar v) {
#if !NEW_VIABLE
return var2bits(v).min(m_viable[v]);
#else
return rational(0);
#endif
}
rational viable::max_viable(pvar v) {
#if NEW_VIABLE
return m_viable[v]->max();
#else
return var2bits(v).max(m_viable[v]);
#endif
}
dd::fdd const& viable::sz2bits(unsigned sz) {
@ -267,7 +126,6 @@ namespace polysat {
return *bits;
}
#if POLYSAT_LOGGING_ENABLED
void viable::log() {
// only for small problems
for (pvar v = 0; v < std::min(10u, m_viable.size()); ++v)
@ -284,7 +142,6 @@ namespace polysat {
LOG("Viable for v" << v << ": " << xs);
}
}
#endif
dd::fdd const& viable::var2bits(pvar v) { return sz2bits(s.size(v)); }