3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-07-20 03:12:03 +00:00

elaborating on local-search rephase strategy

This commit is contained in:
Nikolaj Bjorner 2023-02-07 03:17:52 -08:00
parent f3ae7692ca
commit 90a75866fb
9 changed files with 131 additions and 56 deletions

View file

@ -57,11 +57,11 @@ namespace sat {
double sec = m_stopwatch.get_current_seconds(); double sec = m_stopwatch.get_current_seconds();
double kflips_per_sec = (m_flips - m_last_flips) / (1000.0 * sec); double kflips_per_sec = (m_flips - m_last_flips) / (1000.0 * sec);
if (m_last_flips == 0) { if (m_last_flips == 0) {
IF_VERBOSE(0, verbose_stream() << "(sat.ddfw :unsat :models :kflips/sec :flips :restarts :reinits :unsat_vars :shifts"; IF_VERBOSE(1, verbose_stream() << "(sat.ddfw :unsat :models :kflips/sec :flips :restarts :reinits :unsat_vars :shifts";
if (m_par) verbose_stream() << " :par"; if (m_par) verbose_stream() << " :par";
verbose_stream() << ")\n"); verbose_stream() << ")\n");
} }
IF_VERBOSE(0, verbose_stream() << "(sat.ddfw " IF_VERBOSE(1, verbose_stream() << "(sat.ddfw "
<< std::setw(07) << m_min_sz << std::setw(07) << m_min_sz
<< std::setw(07) << m_models.size() << std::setw(07) << m_models.size()
<< std::setw(10) << kflips_per_sec << std::setw(10) << kflips_per_sec
@ -106,7 +106,6 @@ namespace sat {
if (r > 0) { if (r > 0) {
lim_pos -= score(r); lim_pos -= score(r);
if (lim_pos <= 0) { if (lim_pos <= 0) {
if (m_par) update_reward_avg(v);
return v; return v;
} }
} }
@ -139,9 +138,8 @@ namespace sat {
} }
void ddfw::add(solver const& s) { void ddfw::add(solver const& s) {
for (auto& ci : m_clauses) { for (auto& ci : m_clauses)
m_alloc.del_clause(ci.m_clause); m_alloc.del_clause(ci.m_clause);
}
m_clauses.reset(); m_clauses.reset();
m_use_list.reset(); m_use_list.reset();
m_num_non_binary_clauses = 0; m_num_non_binary_clauses = 0;
@ -281,6 +279,7 @@ namespace sat {
ci.add(nlit); ci.add(nlit);
} }
value(v) = !value(v); value(v) = !value(v);
update_reward_avg(v);
} }
bool ddfw::should_reinit_weights() { bool ddfw::should_reinit_weights() {
@ -379,36 +378,35 @@ namespace sat {
return m_par != nullptr && m_flips >= m_parsync_next; return m_par != nullptr && m_flips >= m_parsync_next;
} }
void ddfw::save_priorities() {
m_probs.reset();
for (unsigned v = 0; v < num_vars(); ++v)
m_probs.push_back(-m_vars[v].m_reward_avg);
}
void ddfw::do_parallel_sync() { void ddfw::do_parallel_sync() {
if (m_par->from_solver(*this)) { if (m_par->from_solver(*this))
// Sum exp(xi) / exp(a) = Sum exp(xi - a)
double max_avg = 0;
for (unsigned v = 0; v < num_vars(); ++v) {
max_avg = std::max(max_avg, (double)m_vars[v].m_reward_avg);
}
double sum = 0;
for (unsigned v = 0; v < num_vars(); ++v) {
sum += exp(m_config.m_itau * (m_vars[v].m_reward_avg - max_avg));
}
if (sum == 0) {
sum = 0.01;
}
m_probs.reset();
for (unsigned v = 0; v < num_vars(); ++v) {
m_probs.push_back(exp(m_config.m_itau * (m_vars[v].m_reward_avg - max_avg)) / sum);
}
m_par->to_solver(*this); m_par->to_solver(*this);
}
++m_parsync_count; ++m_parsync_count;
m_parsync_next *= 3; m_parsync_next *= 3;
m_parsync_next /= 2; m_parsync_next /= 2;
} }
void ddfw::save_model() {
m_model.reserve(num_vars());
for (unsigned i = 0; i < num_vars(); ++i)
m_model[i] = to_lbool(value(i));
save_priorities();
}
void ddfw::save_best_values() { void ddfw::save_best_values() {
if (m_unsat.empty()) { if (m_unsat.empty())
m_model.reserve(num_vars()); save_model();
for (unsigned i = 0; i < num_vars(); ++i) else if (m_unsat.size() < m_min_sz) {
m_model[i] = to_lbool(value(i)); if (m_unsat.size() < 50 || m_min_sz * 10 > m_unsat.size() * 11)
save_model();
} }
if (m_unsat.size() < m_min_sz) { if (m_unsat.size() < m_min_sz) {
m_models.reset(); m_models.reset();

View file

@ -164,6 +164,8 @@ namespace sat {
bool_var pick_var(); bool_var pick_var();
void flip(bool_var v); void flip(bool_var v);
void save_best_values(); void save_best_values();
void save_model();
void save_priorities();
// shift activity // shift activity
void shift_weights(); void shift_weights();

View file

@ -214,14 +214,17 @@ namespace sat {
} }
bool parallel::_to_solver(solver& s) { void parallel::_to_solver(solver& s) {
if (m_priorities.empty()) { return;
return false; #if 0
} if (m_priorities.empty())
return;
for (bool_var v = 0; v < m_priorities.size(); ++v) { for (bool_var v = 0; v < m_priorities.size(); ++v) {
s.update_activity(v, m_priorities[v]); s.update_activity(v, m_priorities[v]);
} }
return true; s.m_activity_inc = 128;
#endif
} }
void parallel::from_solver(solver& s) { void parallel::from_solver(solver& s) {
@ -229,16 +232,19 @@ namespace sat {
_from_solver(s); _from_solver(s);
} }
bool parallel::to_solver(solver& s) { void parallel::to_solver(solver& s) {
lock_guard lock(m_mux); lock_guard lock(m_mux);
return _to_solver(s); _to_solver(s);
} }
void parallel::_to_solver(i_local_search& s) { void parallel::_to_solver(i_local_search& s) {
return;
#if 0
m_priorities.reset(); m_priorities.reset();
for (bool_var v = 0; m_solver_copy && v < m_solver_copy->num_vars(); ++v) { for (bool_var v = 0; m_solver_copy && v < m_solver_copy->num_vars(); ++v) {
m_priorities.push_back(s.get_priority(v)); m_priorities.push_back(s.get_priority(v));
} }
#endif
} }
bool parallel::_from_solver(i_local_search& s) { bool parallel::_from_solver(i_local_search& s) {

View file

@ -51,7 +51,7 @@ namespace sat {
bool enable_add(clause const& c) const; bool enable_add(clause const& c) const;
void _get_clauses(solver& s); void _get_clauses(solver& s);
void _from_solver(solver& s); void _from_solver(solver& s);
bool _to_solver(solver& s); void _to_solver(solver& s);
bool _from_solver(i_local_search& s); bool _from_solver(i_local_search& s);
void _to_solver(i_local_search& s); void _to_solver(i_local_search& s);
@ -102,7 +102,7 @@ namespace sat {
// exchange from solver state to local search and back. // exchange from solver state to local search and back.
void from_solver(solver& s); void from_solver(solver& s);
bool to_solver(solver& s); void to_solver(solver& s);
bool from_solver(i_local_search& s); bool from_solver(i_local_search& s);
void to_solver(i_local_search& s); void to_solver(i_local_search& s);

View file

@ -2,7 +2,7 @@ def_module_params('sat',
export=True, export=True,
description='propositional SAT solver', description='propositional SAT solver',
params=(max_memory_param(), params=(max_memory_param(),
('phase', SYMBOL, 'caching', 'phase selection strategy: always_false, always_true, basic_caching, random, caching'), ('phase', SYMBOL, 'caching', 'phase selection strategy: always_false, always_true, basic_caching, random, caching, local_search'),
('phase.sticky', BOOL, True, 'use sticky phase caching'), ('phase.sticky', BOOL, True, 'use sticky phase caching'),
('search.unsat.conflicts', UINT, 400, 'period for solving for unsat (in number of conflicts)'), ('search.unsat.conflicts', UINT, 400, 'period for solving for unsat (in number of conflicts)'),
('search.sat.conflicts', UINT, 400, 'period for solving for sat (in number of conflicts)'), ('search.sat.conflicts', UINT, 400, 'period for solving for sat (in number of conflicts)'),

View file

@ -58,7 +58,7 @@ namespace sat {
clause_vector m_clause_db; clause_vector m_clause_db;
svector<clause_info> m_clauses; svector<clause_info> m_clauses;
bool_vector m_values, m_best_values; bool_vector m_values, m_best_values;
unsigned m_best_min_unsat{ 0 }; unsigned m_best_min_unsat = 0;
vector<unsigned_vector> m_use_list; vector<unsigned_vector> m_use_list;
unsigned_vector m_flat_use_list; unsigned_vector m_flat_use_list;
unsigned_vector m_use_list_index; unsigned_vector m_use_list_index;
@ -67,9 +67,9 @@ namespace sat {
indexed_uint_set m_unsat; indexed_uint_set m_unsat;
random_gen m_rand; random_gen m_rand;
unsigned_vector m_breaks; unsigned_vector m_breaks;
uint64_t m_flips{ 0 }; uint64_t m_flips = 0;
uint64_t m_next_restart{ 0 }; uint64_t m_next_restart = 0;
unsigned m_restart_count{ 0 }; unsigned m_restart_count = 0;
stopwatch m_stopwatch; stopwatch m_stopwatch;
model m_model; model m_model;
@ -140,6 +140,8 @@ namespace sat {
model const& get_model() const override { return m_model; } model const& get_model() const override { return m_model; }
double get_priority(bool_var v) const { return 0; }
std::ostream& display(std::ostream& out) const; std::ostream& display(std::ostream& out) const;
void updt_params(params_ref const& p) override {} void updt_params(params_ref const& p) override {}

View file

@ -40,6 +40,26 @@ Revision History:
namespace sat { namespace sat {
/**
* Special cases of kissat style general backoff calculation.
* The version here calculates
* limit := value*log(C)^2*n*log(n)
* (effort calculation in kissat is based on ticks not clauses)
*
* respectively
* limit := conflicts + value*log(C)^2*n*log(n)
*/
void backoff::delta_effort(solver& s) {
count++;
unsigned d = value * count * log2(count + 1);
unsigned cl = log2(s.num_clauses() + 2);
limit = cl * cl * d;
}
void backoff::delta_conflicts(solver& s) {
delta_effort(s);
limit += s.m_conflicts_since_init;
}
solver::solver(params_ref const & p, reslimit& l): solver::solver(params_ref const & p, reslimit& l):
solver_core(l), solver_core(l),
@ -1349,16 +1369,43 @@ namespace sat {
m_local_search->updt_params(m_params); m_local_search->updt_params(m_params);
m_local_search->set_seed(m_rand()); m_local_search->set_seed(m_rand());
scoped_rl.push_child(&(m_local_search->rlimit())); scoped_rl.push_child(&(m_local_search->rlimit()));
m_local_search->rlimit().push(500000);
m_backoffs.m_local_search.delta_effort(*this);
m_local_search->rlimit().push(m_backoffs.m_local_search.limit);
m_local_search->reinit(*this); m_local_search->reinit(*this);
lbool r = m_local_search->check(_lits.size(), _lits.data(), nullptr); lbool r = m_local_search->check(_lits.size(), _lits.data(), nullptr);
for (unsigned i = 0; i < m_phase.size(); ++i) auto const& mdl = m_local_search->get_model();
m_best_phase[i] = m_local_search->get_value(i); if (mdl.size() == m_best_phase.size()) {
if (r == l_true) { for (unsigned i = 0; i < m_best_phase.size(); ++i)
m_conflicts_since_restart = 0; m_best_phase[i] = l_true == mdl[i];
m_conflicts_since_gc = 0;
m_next_simplify = std::max(m_next_simplify, m_conflicts_since_init + 1); if (r == l_true) {
m_conflicts_since_restart = 0;
m_conflicts_since_gc = 0;
m_next_simplify = std::max(m_next_simplify, m_conflicts_since_init + 1);
}
do_restart(true); do_restart(true);
#if 0
// move higher priority variables to front
// eg., move the first 10% variables to front
svector<std::pair<double, bool_var>> priorities(mdl.size());
for (unsigned i = 0; i < mdl.size(); ++i)
priorities[i] = { m_local_search->get_priority(i), i };
std::sort(priorities.begin(), priorities.end(), [](auto& x, auto& y) { return x.first > y.first; });
for (unsigned i = priorities.size() / 10; i-- > 0; )
move_to_front(priorities[i].second);
#endif
if (l_true == r) {
for (clause const* cp : m_clauses) {
bool is_true = any_of(*cp, [&](auto lit) { return lit.sign() != m_best_phase[lit.var()]; });
if (!is_true) {
verbose_stream() << "clause is false " << *cp << "\n";
}
}
}
} }
} }
@ -1913,6 +1960,7 @@ namespace sat {
m_rephase_lim = 0; m_rephase_lim = 0;
m_rephase_inc = 0; m_rephase_inc = 0;
m_reorder_lim = m_config.m_reorder_base; m_reorder_lim = m_config.m_reorder_base;
m_backoffs.m_local_search.value = 500;
m_reorder_inc = 0; m_reorder_inc = 0;
m_conflicts_since_restart = 0; m_conflicts_since_restart = 0;
m_force_conflict_analysis = false; m_force_conflict_analysis = false;
@ -1928,6 +1976,7 @@ namespace sat {
m_next_simplify = m_config.m_simplify_delay; m_next_simplify = m_config.m_simplify_delay;
m_min_d_tk = 1.0; m_min_d_tk = 1.0;
m_search_lvl = 0; m_search_lvl = 0;
if (m_learned.size() <= 2*m_clauses.size()) if (m_learned.size() <= 2*m_clauses.size())
m_conflicts_since_gc = 0; m_conflicts_since_gc = 0;
m_restart_next_out = 0; m_restart_next_out = 0;
@ -2030,9 +2079,7 @@ namespace sat {
if (m_par) { if (m_par) {
m_par->from_solver(*this); m_par->from_solver(*this);
if (m_par->to_solver(*this)) { m_par->to_solver(*this);
m_activity_inc = 128;
}
} }
if (m_config.m_binspr && !inconsistent()) { if (m_config.m_binspr && !inconsistent()) {
@ -2950,8 +2997,13 @@ namespace sat {
for (auto& p : m_phase) p = (m_rand() % 2) == 0; for (auto& p : m_phase) p = (m_rand() % 2) == 0;
break; break;
case PS_LOCAL_SEARCH: case PS_LOCAL_SEARCH:
if (m_search_state == s_sat) if (m_search_state == s_sat) {
bounded_local_search(); if (m_rand() % 2 == 0)
bounded_local_search();
for (unsigned i = 0; i < m_phase.size(); ++i)
m_phase[i] = m_best_phase[i];
}
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();

View file

@ -88,9 +88,22 @@ namespace sat {
no_drat_params() { set_bool("drat.disable", true); } no_drat_params() { set_bool("drat.disable", true); }
}; };
struct backoff {
unsigned value = 1;
unsigned lo = 0;
unsigned hi = 0;
unsigned limit = 0;
unsigned count = 0;
void delta_effort(solver& s);
void delta_conflicts(solver& s);
};
class solver : public solver_core { class solver : public solver_core {
public: public:
struct abort_solver {}; struct abort_solver {};
struct backoffs {
backoff m_local_search;
};
protected: protected:
enum search_state { s_sat, s_unsat }; enum search_state { s_sat, s_unsat };
@ -159,6 +172,7 @@ namespace sat {
unsigned m_search_next_toggle; unsigned m_search_next_toggle;
unsigned m_phase_counter; unsigned m_phase_counter;
unsigned m_best_phase_size; unsigned m_best_phase_size;
backoffs m_backoffs;
unsigned m_rephase_lim; unsigned m_rephase_lim;
unsigned m_rephase_inc; unsigned m_rephase_inc;
unsigned m_reorder_lim; unsigned m_reorder_lim;
@ -237,6 +251,7 @@ namespace sat {
friend class lut_finder; friend class lut_finder;
friend class npn3_finder; friend class npn3_finder;
friend class proof_trim; friend class proof_trim;
friend struct backoff;
public: public:
solver(params_ref const & p, reslimit& l); solver(params_ref const & p, reslimit& l);
~solver() override; ~solver() override;

View file

@ -90,7 +90,7 @@ namespace sat {
virtual reslimit& rlimit() = 0; virtual reslimit& rlimit() = 0;
virtual model const& get_model() const = 0; virtual model const& get_model() const = 0;
virtual void collect_statistics(statistics& st) const = 0; virtual void collect_statistics(statistics& st) const = 0;
virtual double get_priority(bool_var v) const { return 0; } virtual double get_priority(bool_var v) const = 0;
virtual bool get_value(bool_var v) const { return true; } virtual bool get_value(bool_var v) const { return true; }
}; };