3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-04-15 16:54:11 +00:00

SMTS tree algorithms (#9250)

* Refactor parallel search tree to use global node selection (SMTS-style) instead of DFS traversal.
Introduce effort-based prioritization, allow activation of any open node, and add controlled/gated
expansion to prevent over-partitioning and improve load balancing.

* clean up code

* ablations

* ablations2: effort

* ablations2: activation

* ablations3: more activations

* ablations4: visit all nodes before splitting

* throttle tree size min is based on workers not activated nodes

* ablate random throttling

* ablate nonlinear effort

* clean up code

* ablate throttle

* ablate where add_effort is

* reset

* clean up a function and add comment

---------

Co-authored-by: Ilana Shapiro <ilanashapiro@Ilanas-MBP.localdomain>
Co-authored-by: Ilana Shapiro <ilanashapiro@Ilanas-MacBook-Pro.local>
Co-authored-by: Ilana Shapiro <ilanashapiro@Ilanas-MBP.lan1>
This commit is contained in:
Ilana Shapiro 2026-04-09 09:46:47 -07:00 committed by GitHub
parent c7879ed5ad
commit ceb363d35d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 199 additions and 83 deletions

View file

@ -140,7 +140,7 @@ namespace smt {
auto atom = get_split_atom();
if (!atom)
goto check_cube_start;
b.split(m_l2g, id, node, atom);
b.try_split(m_l2g, id, node, atom, m_config.m_threads_max_conflicts);
simplify();
break;
}
@ -338,22 +338,23 @@ namespace smt {
}
}
void parallel::batch_manager::split(ast_translation &l2g, unsigned source_worker_id,
search_tree::node<cube_config> *node, expr *atom) {
void parallel::batch_manager::try_split(ast_translation &l2g, unsigned source_worker_id,
search_tree::node<cube_config> *node, expr *atom, unsigned effort) {
std::scoped_lock lock(mux);
expr_ref lit(m), nlit(m);
lit = l2g(atom);
nlit = mk_not(m, lit);
IF_VERBOSE(1, verbose_stream() << "Batch manager splitting on literal: " << mk_bounded_pp(lit, m, 3) << "\n");
if (m_state != state::is_running)
return;
// optional heuristic:
// node->get_status() == status::active
// and depth is 'high' enough
// then ignore split, and instead set the status of node to open.
++m_stats.m_num_cubes;
m_stats.m_max_cube_depth = std::max(m_stats.m_max_cube_depth, node->depth() + 1);
m_search_tree.split(node, lit, nlit);
bool did_split = m_search_tree.try_split(node, lit, nlit, effort);
if (did_split) {
++m_stats.m_num_cubes;
m_stats.m_max_cube_depth = std::max(m_stats.m_max_cube_depth, node->depth() + 1);
IF_VERBOSE(1, verbose_stream() << "Batch manager splitting on literal: " << mk_bounded_pp(lit, m, 3) << "\n");
}
}
void parallel::batch_manager::collect_clause(ast_translation &l2g, unsigned source_worker_id, expr *clause) {
@ -512,8 +513,6 @@ namespace smt {
return false;
}
node *t = m_search_tree.activate_node(n);
if (!t)
t = m_search_tree.find_active_node();
if (!t)
return false;
IF_VERBOSE(1, m_search_tree.display(verbose_stream()); verbose_stream() << "\n";);
@ -529,9 +528,10 @@ namespace smt {
return true;
}
void parallel::batch_manager::initialize() {
void parallel::batch_manager::initialize(unsigned initial_max_thread_conflicts) {
m_state = state::is_running;
m_search_tree.reset();
m_search_tree.set_effort_unit(initial_max_thread_conflicts);
}
void parallel::batch_manager::collect_statistics(::statistics &st) const {

View file

@ -96,7 +96,7 @@ namespace smt {
public:
batch_manager(ast_manager& m, parallel& p) : m(m), p(p), m_search_tree(expr_ref(m)) { }
void initialize();
void initialize(unsigned initial_max_thread_conflicts = 1000); // TODO: pass in from worker defaults
void set_unsat(ast_translation& l2g, expr_ref_vector const& unsat_core);
void set_sat(ast_translation& l2g, model& m);
@ -106,7 +106,7 @@ namespace smt {
bool get_cube(ast_translation& g2l, unsigned id, expr_ref_vector& cube, node*& n);
void backtrack(ast_translation& l2g, expr_ref_vector const& core, node* n);
void split(ast_translation& l2g, unsigned id, node* n, expr* atom);
void try_split(ast_translation& l2g, unsigned id, node* n, expr* atom, unsigned effort);
void collect_clause(ast_translation& l2g, unsigned source_worker_id, expr* clause);
expr_ref_vector return_shared_clauses(ast_translation& g2l, unsigned& worker_limit, unsigned worker_id);