3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-08-14 09:45:36 +00:00

Persist derivative cofactor cache across decide() calls

The cofactor cache is a pure function of the regex (and the fixed transition
mode), independent of the membership set, so tearing it down on every solve()/
decide() call forced it to be recomputed n+1 times during minimize_core()'s n
deletion trials. Keep it instead, resetting only when it grows past a size cap.

Encapsulate the cofactor memo, its pinned-key trail, and the coupled range-
predicate (guard_set_cache) into a self-contained cofactor_cache class with
find/insert/reset/maybe_reset, so the three reset in lockstep (the range
predicates' guards are owned by the cofactor vectors).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 57b9b87e-950a-49ea-bbb3-ed585646a5a9
This commit is contained in:
Nikolaj Bjorner 2026-08-02 11:43:52 -07:00
parent ddfd403013
commit c9a480cb37
2 changed files with 39 additions and 27 deletions

View file

@ -29,8 +29,6 @@ TODOs:
- take into account shape of terms to prune the search space (e.g., if the term is xax, then retain the effect of
intersecting with .*a.*).
- use expr_ref in component and replace svector<component> by vector<component>, save on m_pin.
- don't tear down cofactor cache between calls, but use a self-contained set of pinned regexes that don't get reset
between calls. This will allow for a more efficient caching of cofactor computations. Reset the cache upon bloat.
- support units of non-values (element variables).
Model construction would assign values to the elements.
- make unsat core tracking less naive by tracking dependencies at a finer grain.
@ -64,25 +62,17 @@ expr_ref seq_monadic::der_elem(expr* r, expr* elem) {
expr_ref_pair_vector const& seq_monadic::derivative_cofactors(expr* r) {
expr_ref_pair_vector* v = nullptr;
if (m_cofactor_cache.find(r, v))
if (m_cofactors.find(r, v))
return *v;
v = alloc(expr_ref_pair_vector, m);
if (m_mode == transition_mode::light_antimirov)
m_rw.light_ant_derivative_cofactors(r, *v);
else
m_rw.brz_derivative_cofactors(r, *v);
m_pin.push_back(r); // keep the key alive for the cache's lifetime
m_cofactor_cache.insert(r, v);
m_cofactors.insert(r, v); // takes ownership of v and pins the key r
return *v;
}
void seq_monadic::reset_cofactor_cache() {
for (auto const& [k, v] : m_cofactor_cache)
dealloc(v);
m_cofactor_cache.reset();
m_rp_cache.reset(); // the guards live in the cofactor vectors
}
bool seq_monadic::live_states(expr* R, expr_ref_vector& out) {
obj_map<expr, unsigned> id;
expr_ref_vector states(m);
@ -253,7 +243,7 @@ lbool seq_monadic::product_nonempty(svector<component> const& comps, expr_ref* w
if (bail) return;
}
};
guard_set top(m, u(), m_elem_sort, var0, &m_rp_cache);
guard_set top(m, u(), m_elem_sort, var0, &m_cofactors.rp_cache());
rec(0, top);
if (bail)
return l_undef;
@ -356,7 +346,7 @@ void seq_monadic::simplify_dnf(vector<disjunct>& dnf) {
lbool seq_monadic::solve(expr* term, expr* R) {
m_pin.reset();
reset_cofactor_cache();
m_cofactors.maybe_reset(1u << 16);
m_budget = 200000; // global work budget: bail fast on DNF explosion
m_giveup = false;
vector<disjunct> dnf;
@ -453,7 +443,7 @@ lbool seq_monadic::decide(membership_vec const& memberships) {
if (memberships.empty())
return l_true; // empty conjunction is vacuously true
m_pin.reset();
reset_cofactor_cache();
m_cofactors.maybe_reset(1u << 16);
m_budget = 200000;
m_giveup = false;
// Multiply the per-membership DNFs: combined = { d ++ e : d in combined, e in dnf_i }.