3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-24 09:35:32 +00:00

parallel verison of ccc

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2017-04-25 16:56:39 -07:00
parent 07ef79d664
commit c637240c40
3 changed files with 204 additions and 118 deletions

View file

@ -127,6 +127,7 @@ lbool ccc::cube(svector<decision>& decisions, lookahead& lh) {
lh.push(~l, lh.c_fixed_truth);
decisions.back().negate();
decisions.back().m_id = branch2;
decisions.back().m_spawn_id = 0;
r = cube(decisions, lh);
if (r == l_false) {
lh.pop();
@ -141,6 +142,7 @@ void ccc::update_closure_level(decision const& d, int offset) {
m_last_closure_level = (d.m_depth + 3*m_last_closure_level) / 4 + offset;
}
unsigned ccc::spawn_conquer(svector<decision> const& decisions) {
unsigned result = 0;
//
@ -152,20 +154,89 @@ unsigned ccc::spawn_conquer(svector<decision> const& decisions) {
if (!m_free_threads.empty() && m_last_closure_level >= decisions.size()) {
result = m_free_threads.back();
m_free_threads.pop_back();
IF_VERBOSE(0, verbose_stream() << "spawn " << decisions.size() << " " << result << "\n";);
IF_VERBOSE(0, verbose_stream() << "spawn " << result << " with " << decisions.size() << " decisions\n";);
}
return result;
}
void ccc::free_conquer(unsigned thread_id) {
if (thread_id != 0) {
IF_VERBOSE(0, verbose_stream() << "free conquer " << thread_id << "\n";);
m_free_threads.push_back(thread_id);
}
}
bool ccc::get_solved(svector<decision>& decisions) {
// check if CDCL solver got ahead.
bool do_pop = false;
while (true) {
solution sol;
bool is_empty = true;
#pragma omp critical (ccc_solved)
{
if (do_pop) m_solved.pop_front();
if (!m_solved.empty()) {
sol = m_solved.top();
is_empty = false;
}
}
if (is_empty) {
return false;
}
do_pop = true;
unsigned branch_id = sol.m_branch_id;
unsigned thread_id = sol.m_thread_id;
free_conquer(thread_id);
for (unsigned i = decisions.size(); i > 0; ) {
--i;
decision& d = decisions[i];
if (branch_id == d.m_id) {
if (d.m_spawn_id == thread_id && thread_id != 0) {
SASSERT(d.m_spawn_id > 0);
IF_VERBOSE(0, verbose_stream() << thread_id << ": spawn close " << branch_id << " " << " " << d.m_depth << "\n";);
++m_ccc_stats.m_spawn_closed;
d.close();
update_closure_level(d, -1);
break;
}
else {
IF_VERBOSE(0, verbose_stream() << thread_id << ": conquer " << branch_id << " " << d.m_depth << " " << decisions.size() << " " << d.get_literal(thread_id) << "\n";);
++m_ccc_stats.m_cdcl_closed;
update_closure_level(d, 1);
return true;
}
}
// branch is even, d has moved to the next branch
if (branch_id == (d.m_id & ~0x1) && d.m_spawn_id == thread_id && thread_id != 0) {
IF_VERBOSE(0, verbose_stream() << thread_id << ": spawn conquer " << branch_id << " " << " " << d.m_depth << "\n";);
++m_ccc_stats.m_cdcl_closed;
update_closure_level(d, 1);
return true;
}
}
}
}
lbool ccc::conquer(solver& s, unsigned thread_id) {
SASSERT(thread_id > 0);
void ccc::put_decision(decision const& d) {
for (unsigned i = 0; i < m_num_conquer; ++i) {
#pragma omp critical (ccc_decisions)
{
while (!m_decisions[i].empty()) {
decision d = m_decisions[i].back();
if (d.m_depth < d.m_depth || d.m_spawn_id != 0) {
break;
}
m_decisions[i].pop_back();
}
m_decisions[i].push(d);
}
}
}
// ---------------------
// conquer state machine
lbool ccc::conquer::search() {
try {
if (s.inconsistent()) return l_false;
s.init_search();
@ -174,13 +245,11 @@ lbool ccc::conquer(solver& s, unsigned thread_id) {
s.cleanup();
s.simplify_problem();
if (s.inconsistent()) return l_false;
svector<decision> decisions;
while (true) {
SASSERT(!s.inconsistent());
lbool r = bounded_search(s, decisions, thread_id);
lbool r = bounded_search();
if (r != l_undef)
return r;
@ -195,102 +264,47 @@ lbool ccc::conquer(solver& s, unsigned thread_id) {
}
}
void ccc::replay_decisions(solver& s, svector<decision>& decisions, unsigned thread_id) {
void ccc::conquer::replay_decisions() {
s.propagate(true);
for (unsigned i = s.scope_lvl(); !s.inconsistent() && i < decisions.size(); ++i) {
decision const& d = decisions[i];
IF_VERBOSE(2, verbose_stream() << thread_id << ": replay " << d.get_literal(thread_id) << " " << s.value(d.get_literal(thread_id)) << "\n";);
if (!push_decision(s, decisions, d, thread_id)) {
if (!push_decision(d)) {
// negation of decision is implied.
// check_non_model("replay", decisions);
decisions.resize(i);
return;
IF_VERBOSE(0, verbose_stream() << thread_id << ": backjump to level " << i << " of " << decisions.size() << "\n";);
while (decisions.size() > i) {
pop_decision(decisions.back());
decisions.pop_back();
}
break;
}
if (d.m_spawn_id == thread_id && d.is_left()) {
// we pushed the right branch on this thread.
IF_VERBOSE(0, verbose_stream() << thread_id << ": skip left branch on level " << i + 1 << " of " << decisions.size() << "\n";);
break;
}
}
}
bool ccc::get_solved(svector<decision>& decisions) {
// check if CDCL solver got ahead.
bool found = false;
void ccc::conquer::pop_decision(decision const& d) {
unsigned tid = 0;
if (d.is_spawned(thread_id)) {
tid = thread_id;
m_spawned = false;
IF_VERBOSE(0, verbose_stream() << thread_id << " retire spawn\n";);
}
#pragma omp critical (ccc_solved)
{
while (!m_solved.empty()) {
solution const& sol = m_solved.top();
unsigned branch_id = sol.m_branch_id;
unsigned thread_id = sol.m_thread_id;
SASSERT(thread_id > 0);
free_conquer(thread_id);
for (unsigned i = decisions.size(); i > 0; ) {
--i;
decision& d = decisions[i];
if (branch_id == d.m_id) {
if (d.m_spawn_id == thread_id && thread_id != 0) {
SASSERT(d.m_spawn_id > 0);
IF_VERBOSE(0, verbose_stream() << "spawn close " << branch_id << " " << thread_id << " " << d.m_depth << "\n";);
++m_ccc_stats.m_spawn_closed;
d.close();
update_closure_level(d, -1);
}
else {
IF_VERBOSE(0, verbose_stream() << "conquer " << branch_id << " " << thread_id << " " << d.m_depth << " " << d.get_literal(thread_id) << "\n";);
found = true;
++m_ccc_stats.m_cdcl_closed;
update_closure_level(d, 1);
}
break;
}
// branch is even, d has moved to the next branch
if (branch_id == (d.m_id & ~0x1) && d.m_spawn_id == thread_id && thread_id != 0) {
IF_VERBOSE(0, verbose_stream() << "spawn conquer " << branch_id << " " << thread_id << " " << d.m_depth << "\n";);
found = true;
++m_ccc_stats.m_cdcl_closed;
update_closure_level(d, 1);
break;
}
}
if (found) {
break;
}
// IF_VERBOSE(1, verbose_stream() << "not found: " << branch_id << " " << decisions.size() << "\n";);
m_solved.pop();
}
}
return found;
}
void ccc::put_decision(decision const& d) {
#pragma omp critical (ccc_decisions)
{
for (unsigned i = 0; i < m_num_conquer; ++i) {
m_decisions[i].push(d);
}
super.m_solved.push(solution(tid, d.m_id));
}
}
bool ccc::get_decision(unsigned thread_id, decision& d) {
SASSERT(0 < thread_id && thread_id <= m_decisions.size());
bool result = false;
#pragma omp critical (ccc_decisions)
{
if (!m_decisions[thread_id - 1].empty()) {
d = m_decisions[thread_id - 1].pop();
result = true;
}
}
return result;
}
bool ccc::push_decision(solver& s, svector<decision> const& decisions, decision const& d, unsigned thread_id) {
bool ccc::conquer::push_decision(decision const& d) {
literal lit = d.get_literal(thread_id);
switch (s.value(lit)) {
case l_false:
thread_id = (d.m_spawn_id == thread_id || (!decisions.empty() && decisions.back().m_spawn_id == thread_id)) ? thread_id : 0;
#pragma omp critical (ccc_solved)
{
m_solved.push(solution(thread_id, d.m_id));
}
//TBD:
s.m_restart_threshold = s.m_config.m_restart_initial;
//s.m_conflicts_since_last_restart = 0;
@ -304,62 +318,90 @@ bool ccc::push_decision(solver& s, svector<decision> const& decisions, decision
s.propagate(true);
break;
}
m_spawned |= d.is_spawned(thread_id);
return true;
}
bool ccc::cube_decision(solver& s, svector<decision>& decisions, unsigned thread_id) {
bool ccc::conquer::cube_decision() {
decision d;
bool use_cube_decision = false;
SASSERT(s.m_qhead == s.m_trail.size());
get_cube:
if (!get_decision(thread_id, d)) {
return false;
}
if (!decisions.empty() && decisions.back().m_depth + 1 < d.m_depth) {
goto get_cube;
}
while (true) {
if (!super.get_decision(thread_id, d)) {
return false;
}
if (!decisions.empty() && decisions.back().m_spawn_id == thread_id && decisions.back().m_depth < d.m_depth) {
goto get_cube;
if (d.is_spawned(thread_id)) IF_VERBOSE(0, verbose_stream() << thread_id << ": spawned d:" << d.m_depth << " decisions: " << decisions.size() << "\n";);
if (!decisions.empty() && decisions.back().m_depth + 1 < d.m_depth) {
if (d.is_spawned(thread_id)) {
pop_decision(d);
}
}
else {
break;
}
}
SASSERT(decisions.empty() || decisions.back().m_depth + 1 >= d.m_depth);
if (!decisions.empty() && decisions.back().is_spawned(thread_id) && decisions.back().m_depth == d.m_depth) {
SASSERT(d.m_spawn_id == 0);
SASSERT(decisions.back().is_left());
SASSERT(!d.is_left());
IF_VERBOSE(0, verbose_stream() << thread_id << " inherit spawn\n";);
d.m_spawn_id = thread_id;
decisions.back().m_spawn_id = 0;
m_spawned = false;
}
SASSERT(decisions.empty() || decisions.back().m_depth + 1 >= d.m_depth);
while (!decisions.empty() && decisions.back().m_depth >= d.m_depth) {
// check_non_model("cube decision", decisions);
if (decisions.back().is_spawned(thread_id)) {
pop_decision(decisions.back());
}
decisions.pop_back();
}
SASSERT(decisions.empty() || decisions.back().m_depth + 1 == d.m_depth);
SASSERT(decisions.empty() || decisions.back().m_id == d.m_parent);
if (m_spawned) {
decisions.push_back(d);
return true;
}
s.pop_reinit(s.scope_lvl() - decisions.size());
SASSERT(s.m_qhead == s.m_trail.size());
SASSERT(s.scope_lvl() == decisions.size());
literal lit = d.get_literal(thread_id);
IF_VERBOSE(1, verbose_stream() << thread_id << ": cube " << decisions.size() << " " << d.get_literal(thread_id) << "\n";);
IF_VERBOSE(0, verbose_stream() << thread_id << ": cube " << decisions.size() << " " << d.get_literal(thread_id) << "\n";);
IF_VERBOSE(2, pp(verbose_stream() << thread_id << ": push ", decisions) << " @ " << s.scope_lvl() << " " << s.value(lit) << "\n";
if (s.value(lit) == l_false) verbose_stream() << "level: " << s.lvl(lit) << "\n";);
if (push_decision(s, decisions, d, thread_id)) {
if (push_decision(d)) {
decisions.push_back(d);
}
else {
pop_decision(d);
}
return true;
}
lbool ccc::bounded_search(solver& s, svector<decision>& decisions, unsigned thread_id) {
lbool ccc::conquer::bounded_search() {
while (true) {
s.checkpoint();
bool done = false;
while (!done) {
replay_decisions(s, decisions, thread_id);
replay_decisions();
lbool is_sat = s.propagate_and_backjump_step(done);
if (is_sat != l_true) return is_sat;
}
s.gc();
if (!cube_decision(s, decisions, thread_id) && !s.decide()) {
if (!cube_decision() && !s.decide()) {
lbool is_sat = s.final_check();
if (is_sat != l_undef) {
return is_sat;
@ -369,6 +411,20 @@ lbool ccc::bounded_search(solver& s, svector<decision>& decisions, unsigned thre
}
bool ccc::get_decision(unsigned thread_id, decision& d) {
SASSERT(0 < thread_id && thread_id <= m_decisions.size());
bool result = false;
#pragma omp critical (ccc_decisions)
{
if (!m_decisions[thread_id - 1].empty()) {
d = m_decisions[thread_id - 1].pop_front();
result = true;
}
}
return result;
}
lbool ccc::search() {
enum par_exception_kind {
DEFAULT_EX,
@ -381,7 +437,7 @@ lbool ccc::search() {
scoped_limits scoped_rlimit(m_s.rlimit());
vector<reslimit> limits;
ptr_vector<solver> solvers;
ptr_vector<conquer> solvers;
int finished_id = -1;
std::string ex_msg;
par_exception_kind ex_kind;
@ -395,10 +451,10 @@ lbool ccc::search() {
for (int i = 1; i < num_threads; ++i) {
limits.push_back(reslimit());
m_s.m_params.set_uint("random_seed", m_s.m_rand());
solver* s1 = alloc(sat::solver, m_s.m_params, limits.back());
conquer* s1 = alloc(conquer, *this, m_s.m_params, limits.back(), i);
solvers.push_back(s1);
s1->copy(m_s);
scoped_rlimit.push_child(&s1->rlimit());
s1->s.copy(m_s);
scoped_rlimit.push_child(&(s1->s.rlimit()));
m_decisions.push_back(queue<decision>());
}
for (unsigned i = 1; i < m_num_conquer; ++i) {
@ -413,7 +469,7 @@ lbool ccc::search() {
r = cube();
}
else {
r = conquer(*solvers[i-1], i);
r = solvers[i-1]->search();
}
bool first = false;
#pragma omp critical (par_solver)
@ -426,7 +482,7 @@ lbool ccc::search() {
}
if (first) {
for (unsigned j = 0; j < solvers.size(); ++j) {
solvers[j]->rlimit().cancel();
solvers[j]->s.rlimit().cancel();
}
// cancel lookahead solver:
m_cancel = true;
@ -444,11 +500,11 @@ lbool ccc::search() {
if (finished_id > 0 && result == l_true) {
// set model from auxiliary solver
m_model = solvers[finished_id - 1]->get_model();
m_model = solvers[finished_id - 1]->s.get_model();
}
for (unsigned i = 0; i < solvers.size(); ++i) {
solvers[i]->collect_statistics(m_lh_stats);
solvers[i]->s.collect_statistics(m_lh_stats);
dealloc(solvers[i]);
}

View file

@ -42,7 +42,14 @@ namespace sat {
void close() { SASSERT(m_spawn_id > 0); m_spawn_id = -m_spawn_id; }
bool is_closed() const { return m_spawn_id < 0; }
void negate() { m_literal.neg(); }
literal get_literal(unsigned thread_id) const { return thread_id == m_spawn_id ? ~m_literal : m_literal; }
bool is_left() const { return 0 == (m_id & 0x1); }
bool is_spawned(unsigned thread_id) const { return m_spawn_id == thread_id; }
// the left branch has an even branch_id.
// the branch is spawned if it is even and the spawn_id is the same as the thread_id, and furthermore it is exploring the left branch.
// it may explore the right branch, but is then not in a spawned mode.
// we retain the spawn id so that the spawned thread can be re-spun.
literal get_literal(unsigned thread_id) const { return ((m_id & 0x1) == 0 && thread_id == m_spawn_id) ? ~m_literal : m_literal; }
std::ostream& pp(std::ostream& out) const;
};
@ -50,6 +57,7 @@ namespace sat {
unsigned m_thread_id;
unsigned m_branch_id;
solution(unsigned t, unsigned s): m_thread_id(t), m_branch_id(s) {}
solution(): m_thread_id(0), m_branch_id(0) {}
};
struct stats {
@ -61,6 +69,22 @@ namespace sat {
}
};
struct conquer {
ccc& super;
solver s;
svector<decision> decisions;
unsigned thread_id;
bool m_spawned;
conquer(ccc& super, params_ref const& p, reslimit& l, unsigned tid): super(super), s(p, l), thread_id(tid), m_spawned(false) {}
lbool search();
bool cube_decision();
lbool bounded_search();
bool push_decision(decision const& d);
void pop_decision(decision const& d);
void replay_decisions();
};
solver& m_s;
queue<solution> m_solved;
vector<queue<decision> > m_decisions;
@ -72,21 +96,15 @@ namespace sat {
unsigned m_last_closure_level;
::statistics m_lh_stats;
stats m_ccc_stats;
lbool conquer(solver& s, unsigned thread_id);
bool cube_decision(solver& s, svector<decision>& decisions, unsigned thread_id);
lbool bounded_search(solver& s, svector<decision>& decisions, unsigned thread_id);
bool push_decision(solver& s, svector<decision> const& decisions, decision const& d, unsigned thread_id);
lbool cube();
lbool cube(svector<decision>& decisions, lookahead& lh);
void put_decision(decision const& d);
bool get_decision(unsigned thread_id, decision& d);
bool get_solved(svector<decision>& decisions);
bool get_decision(unsigned thread_id, decision& d);
void update_closure_level(decision const& d, int offset);
void replay_decisions(solver& s, svector<decision>& decisions, unsigned thread_id);
static std::ostream& pp(std::ostream& out, svector<decision> const& v);