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:
parent
733f44d141
commit
0bec8520e1
9 changed files with 201 additions and 254 deletions
|
@ -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)); }
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue