3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-09-08 10:41:25 +00:00

attempt to add different hardness functions including heule schur and march, need to re-examine/debug/evaluate

This commit is contained in:
Ilana Shapiro 2025-09-01 12:43:13 -07:00
parent a3024b7c77
commit 042ebf5156
2 changed files with 92 additions and 36 deletions

View file

@ -45,12 +45,20 @@ namespace smt {
namespace smt { namespace smt {
double parallel::worker::eval_hardness() { double parallel::worker::explicit_hardness(expr_ref_vector const& cube) {
double total = 0.0; double total = 0.0;
unsigned clause_count = 0; unsigned clause_count = 0;
// Get the scope level before the cube is assumed
unsigned scope_lvl = ctx->get_scope_level(); unsigned scope_lvl = ctx->get_scope_level();
// Build a set of bool_vars corresponding to the cube literals
svector<bool_var> cube_vars;
for (expr* e : cube) {
bool_var v = ctx->get_bool_var(e);
if (v != null_bool_var) cube_vars.push_back(v);
}
for (auto* cp : ctx->m_aux_clauses) { for (auto* cp : ctx->m_aux_clauses) {
auto& clause = *cp; auto& clause = *cp;
unsigned sz = 0; unsigned sz = 0;
@ -58,19 +66,21 @@ namespace smt {
bool satisfied = false; bool satisfied = false;
for (literal l : clause) { for (literal l : clause) {
expr* e = ctx->bool_var2expr(l.var()); bool_var v = l.var();
if (asms.contains(e)) continue;
// Only consider literals that are part of the cube
if (!cube_vars.contains(v)) continue;
lbool val = ctx->get_assignment(l); lbool val = ctx->get_assignment(l);
unsigned lvl = ctx->get_assign_level(l); unsigned lvl = ctx->get_assign_level(l);
if (lvl <= scope_lvl) continue; // ignore pre-existing assignments // Only include assignments made after the scope level
if (lvl <= scope_lvl) continue;
sz++; sz++;
if (val == l_true) { satisfied = true; break; } if (val == l_true) { satisfied = true; break; }
if (val == l_false) n_false++; if (val == l_false) n_false++;
} }
// LOG_WORKER(1, " n_false: " << n_false << " satisfied: " << satisfied << "\n");
if (satisfied || sz == 0) continue; if (satisfied || sz == 0) continue;
@ -82,6 +92,73 @@ namespace smt {
return clause_count ? total / clause_count : 0.0; return clause_count ? total / clause_count : 0.0;
} }
double parallel::worker::heule_schur_hardness(expr_ref_vector const& cube) {
double total = 0.0;
unsigned n = 0;
for (expr* e : cube) {
double literal_score = 0.0;
for (auto* cp : ctx->m_aux_clauses) {
auto& clause = *cp;
unsigned sz = 0;
bool occurs = false;
for (literal l : clause) {
expr* lit_expr = ctx->bool_var2expr(l.var());
if (asms.contains(lit_expr)) continue; // ignore assumptions
sz++;
if (lit_expr == e) occurs = true;
}
if (occurs && sz > 0) {
literal_score += 1.0 / pow(2.0, sz); // weight inversely by clause size
}
}
total += literal_score;
n++;
}
return n ? total / n : 0.0;
}
double parallel::worker::march_cu_hardness(expr_ref_vector const& cube) {
double total = 0.0;
unsigned n = 0;
for (expr* e : cube) {
double score = 1.0; // start with 1 to avoid zero
for (auto* cp : ctx->m_aux_clauses) {
auto& clause = *cp;
bool occurs = false;
unsigned sz = 0; // clause size counting only non-assumption literals
for (literal l : clause) {
expr* lit_expr = ctx->bool_var2expr(l.var());
if (asms.contains(lit_expr)) continue; // ignore assumptions
sz++;
if (lit_expr == e) {
occurs = true;
}
}
if (occurs && sz > 0) {
score += pow(0.5, static_cast<double>(sz)); // approximate March weight
}
}
total += score;
n++;
}
return n ? total / n : 0.0;
}
void parallel::worker::run() { void parallel::worker::run() {
while (m.inc()) { // inc: increase the limit and check if it is canceled, vs m.limit().is_canceled() is readonly. the .limit() is also not necessary (m.inc() etc provides a convenience wrapper) while (m.inc()) { // inc: increase the limit and check if it is canceled, vs m.limit().is_canceled() is readonly. the .limit() is also not necessary (m.inc() etc provides a convenience wrapper)
vector<expr_ref_vector> cubes; vector<expr_ref_vector> cubes;
@ -95,11 +172,6 @@ namespace smt {
return; return;
} }
unsigned conflicts_before = ctx->m_stats.m_num_conflicts;
unsigned propagations_before = ctx->m_stats.m_num_propagations + ctx->m_stats.m_num_bin_propagations;
unsigned decisions_before = ctx->m_stats.m_num_decisions;
unsigned restarts_before = ctx->m_stats.m_num_restarts;
LOG_WORKER(1, " checking cube\n"); LOG_WORKER(1, " checking cube\n");
lbool r = check_cube(cube); lbool r = check_cube(cube);
@ -118,26 +190,8 @@ namespace smt {
if (m_config.m_iterative_deepening || m_config.m_beam_search) { // let's automatically do iterative deepening for beam search if (m_config.m_iterative_deepening || m_config.m_beam_search) { // let's automatically do iterative deepening for beam search
LOG_WORKER(1, " applying iterative deepening\n"); LOG_WORKER(1, " applying iterative deepening\n");
if (false) { // const double cube_hardness = ctx->m_stats.m_num_decisions / std::max(1u, ctx->m_stats.m_num_conflicts);
// per-cube deltas const double cube_hardness = explicit_hardness(cube); // huele_schur_hardness(cube); march_cu_hardness(cube);
unsigned conflicts_delta = ctx->m_stats.m_num_conflicts - conflicts_before;
unsigned propagations_delta = (ctx->m_stats.m_num_propagations + ctx->m_stats.m_num_bin_propagations) - propagations_before;
unsigned decisions_delta = ctx->m_stats.m_num_decisions - decisions_before;
unsigned restarts_delta = ctx->m_stats.m_num_restarts - restarts_before;
LOG_WORKER(1, " cube deltas: conflicts: " << conflicts_delta << " propagations: " << propagations_delta << " decisions: " << decisions_delta << " restarts: " << restarts_delta << "\n");
// tuned experimentally
const double w_conflicts = 1.0;
const double w_propagations = 0.001;
const double w_decisions = 0.1;
const double w_restarts = 0.5;
const double cube_hardness = w_conflicts * conflicts_delta +
w_propagations * propagations_delta +
w_decisions * decisions_delta +
w_restarts * restarts_delta;
}
const double cube_hardness = eval_hardness();
const double avg_hardness = b.update_avg_cube_hardness(cube_hardness); const double avg_hardness = b.update_avg_cube_hardness(cube_hardness);
const double factor = 1; // can tune for multiple of avg hardness later const double factor = 1; // can tune for multiple of avg hardness later
@ -538,7 +592,7 @@ namespace smt {
------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------------
Final thought (do this!): use greedy strategy by a policy when C_batch, A_batch, A_worker are "small". -- want to do this. switch to frugal strategy after reaching size limit Final thought (do this!): use greedy strategy by a policy when C_batch, A_batch, A_worker are "small". -- want to do this. switch to frugal strategy after reaching size limit
*/ */
void parallel::batch_manager::return_cubes(ast_translation& l2g, vector<expr_ref_vector>const& C_worker, expr_ref_vector const& A_worker, const bool should_split) { void parallel::batch_manager::return_cubes(ast_translation& l2g, vector<expr_ref_vector>const& C_worker, expr_ref_vector const& A_worker, const bool should_split, const double hardness) {
// SASSERT(!m_config.never_cube()); // SASSERT(!m_config.never_cube());
auto atom_in_cube = [&](expr_ref_vector const& cube, expr* atom) { auto atom_in_cube = [&](expr_ref_vector const& cube, expr* atom) {
@ -607,12 +661,12 @@ namespace smt {
// Positive atom branch // Positive atom branch
expr_ref_vector cube_pos = g_cube; expr_ref_vector cube_pos = g_cube;
cube_pos.push_back(atom); cube_pos.push_back(atom);
m_cubes_pq.push(ScoredCube(d, cube_pos)); m_cubes_pq.push(ScoredCube(d / hardness, cube_pos));
// Negative atom branch // Negative atom branch
expr_ref_vector cube_neg = g_cube; expr_ref_vector cube_neg = g_cube;
cube_neg.push_back(m.mk_not(atom)); cube_neg.push_back(m.mk_not(atom));
m_cubes_pq.push(ScoredCube(d, cube_neg)); m_cubes_pq.push(ScoredCube(d / hardness, cube_neg));
m_stats.m_num_cubes += 2; m_stats.m_num_cubes += 2;
m_stats.m_max_cube_depth = std::max(m_stats.m_max_cube_depth, d + 1); m_stats.m_max_cube_depth = std::max(m_stats.m_max_cube_depth, d + 1);
@ -664,7 +718,7 @@ namespace smt {
if ((c.size() >= m_config.m_max_cube_depth || !should_split) if ((c.size() >= m_config.m_max_cube_depth || !should_split)
&& (m_config.m_depth_splitting_only || m_config.m_iterative_deepening || m_config.m_beam_search)) { && (m_config.m_depth_splitting_only || m_config.m_iterative_deepening || m_config.m_beam_search)) {
if (m_config.m_beam_search) { if (m_config.m_beam_search) {
m_cubes_pq.push(ScoredCube(g_cube.size(), g_cube)); // re-enqueue the cube as is m_cubes_pq.push(ScoredCube(g_cube.size() / hardness, g_cube)); // re-enqueue the cube as is
} else { } else {
// need to add the depth set if it doesn't exist yet // need to add the depth set if it doesn't exist yet
if (m_cubes_depth_sets.find(g_cube.size()) == m_cubes_depth_sets.end()) { if (m_cubes_depth_sets.find(g_cube.size()) == m_cubes_depth_sets.end()) {

View file

@ -133,7 +133,7 @@ namespace smt {
// worker threads return unprocessed cubes to the batch manager together with split literal candidates. // worker threads return unprocessed cubes to the batch manager together with split literal candidates.
// the batch manager re-enqueues unprocessed cubes and optionally splits them using the split_atoms returned by this and workers. // the batch manager re-enqueues unprocessed cubes and optionally splits them using the split_atoms returned by this and workers.
// //
void return_cubes(ast_translation& l2g, vector<expr_ref_vector>const& cubes, expr_ref_vector const& split_atoms, const bool should_split=true); void return_cubes(ast_translation& l2g, vector<expr_ref_vector>const& cubes, expr_ref_vector const& split_atoms, const bool should_split=true, const double hardness=1.0);
void report_assumption_used(ast_translation& l2g, expr* assumption); void report_assumption_used(ast_translation& l2g, expr* assumption);
void collect_clause(ast_translation& l2g, unsigned source_worker_id, expr* e); void collect_clause(ast_translation& l2g, unsigned source_worker_id, expr* e);
expr_ref_vector return_shared_clauses(ast_translation& g2l, unsigned& worker_limit, unsigned worker_id); expr_ref_vector return_shared_clauses(ast_translation& g2l, unsigned& worker_limit, unsigned worker_id);
@ -187,7 +187,9 @@ namespace smt {
expr_ref_vector find_backbone_candidates(); expr_ref_vector find_backbone_candidates();
expr_ref_vector get_backbones_from_candidates(expr_ref_vector const& candidates); expr_ref_vector get_backbones_from_candidates(expr_ref_vector const& candidates);
double eval_hardness(); double explicit_hardness(expr_ref_vector const& cube);
double heule_schur_hardness(expr_ref_vector const& cube);
double march_cu_hardness(expr_ref_vector const& cube);
public: public:
worker(unsigned id, parallel& p, expr_ref_vector const& _asms); worker(unsigned id, parallel& p, expr_ref_vector const& _asms);
void run(); void run();