3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-07-20 22:15:49 +00:00

Bug/Performance fixes

Added argument to harvest non-primitive membership constraint benchmarks
This commit is contained in:
CEisenhofer 2026-07-01 14:47:36 +02:00
parent f5baba1068
commit 1f448a10a3
9 changed files with 314 additions and 13 deletions

View file

@ -518,6 +518,10 @@ namespace smt {
void theory_nseq::eager_structural_check() {
if (!get_fparams().m_nseq_eager)
return;
// Benchmark-harvest mode must see the full constraint set in final_check; the
// eager closure could resolve conflicts during propagation and bypass harvesting.
if (get_fparams().m_nseq_harvest)
return;
// Only re-run when the Nielsen-relevant constraint set actually grew.
if (m_eager_dirty == m_eager_seen)
return;
@ -1001,6 +1005,8 @@ namespace smt {
m_nielsen.set_signature_split(get_fparams().m_nseq_signature);
m_nielsen.set_regex_factorization_threshold(get_fparams().m_nseq_regex_factorization_threshold);
m_nielsen.set_regex_factorization_eager(get_fparams().m_nseq_regex_factorization_eager);
m_nielsen.set_harvest(get_fparams().m_nseq_harvest);
m_nielsen.set_harvest_dir(get_fparams().m_nseq_harvest_dir.str());
// assert length constraints derived from string equalities
if (assert_length_constraints()) {
@ -1034,7 +1040,10 @@ namespace smt {
// Regex membership pre-check: before running DFS, check intersection
// emptiness for each variable's regex constraints. This handles
// regex-only problems that the DFS cannot efficiently solve.
if (!m_nielsen.root()->is_currently_conflict() && get_fparams().m_nseq_regex_precheck) {
// In benchmark-harvest mode the pre-check would short-circuit SAT/UNSAT on
// the regex memberships before any word-equation rewriting happens, so skip it.
if (!m_nielsen.root()->is_currently_conflict() && get_fparams().m_nseq_regex_precheck
&& !get_fparams().m_nseq_harvest) {
switch (check_regex_memberships_precheck()) {
case l_true:
// conflict was asserted inside check_regex_memberships_precheck
@ -1099,6 +1108,19 @@ namespace smt {
return FC_DONE;
}
// Benchmark-harvest mode: solve() returns unknown after dumping the snapshots
// reachable under the current assignment. Block the current assignment so the
// SAT solver moves on to a different set of primitive constraints to harvest
// from. This is intentionally unsound (the run only ends once the SAT solver
// exhausts all assignments, yielding overall unsat) — for benchmark generation.
if (get_fparams().m_nseq_harvest) {
IF_VERBOSE(1, verbose_stream() << "nseq final_check: harvest, blocking assignment\n";);
if (block_current_assignment())
return FC_CONTINUE;
// nothing left to block → let the search conclude
return FC_GIVEUP;
}
TRACE(seq, display(tout << "unknown\n"));
IF_VERBOSE(1, verbose_stream() << "nseq final_check: solve UNKNOWN, FC_GIVEUP\n";);
return FC_GIVEUP;
@ -1160,6 +1182,44 @@ namespace smt {
return all_sat;
}
// -----------------------------------------------------------------------
// Benchmark-harvest: block the current assignment
// -----------------------------------------------------------------------
bool theory_nseq::block_current_assignment() {
// Build a clause that excludes the current assignment of the constraint
// literals (memberships, disequalities) and equalities feeding the Nielsen
// graph, so the SAT solver moves on to a different combination.
literal_vector clause;
for (auto const& item : m_prop_queue) {
if (std::holds_alternative<eq_item>(item)) {
auto const& eq = std::get<eq_item>(item);
// the two terms are currently equal in the egraph; forbid that
clause.push_back(~mk_eq(eq.m_l->get_expr(), eq.m_r->get_expr(), false));
}
else if (std::holds_alternative<deq_item>(item)) {
auto const& deq = std::get<deq_item>(item);
switch (ctx.get_assignment(deq.lit)) {
case l_true: clause.push_back(~deq.lit); break;
case l_false: clause.push_back(deq.lit); break;
default: break;
}
}
else if (std::holds_alternative<mem_item>(item)) {
auto const& mem = std::get<mem_item>(item);
switch (ctx.get_assignment(mem.lit)) {
case l_true: clause.push_back(~mem.lit); break;
case l_false: clause.push_back(mem.lit); break;
default: break;
}
}
}
if (clause.empty())
return false;
ctx.mk_th_axiom(get_id(), clause.size(), clause.data());
return true;
}
// -----------------------------------------------------------------------
// Conflict explanation
// -----------------------------------------------------------------------