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

Add monadic regex end-game solver

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1a1a0632-f993-4bee-b4f8-a87f960836a1
This commit is contained in:
Nikolaj Bjorner 2026-08-01 12:02:53 -07:00
parent 1972f4667b
commit cc93c8d486
10 changed files with 263 additions and 54 deletions

View file

@ -19,7 +19,6 @@ Abstract:
yields the concrete element used to build a witness sequence.
TODOs:
- track unsat cores and expose them as explain functionality
- if perf suffers: use DFS backtracking search instead of DNF expansion (space overhead)
- create a validation harness: expose certificates for correctness that can be checked.
- extend with lower and upper bound constraints
@ -419,12 +418,12 @@ lbool seq_monadic::decide_dnf(vector<disjunct> const& dnf) {
return any_undef ? l_undef : l_false;
}
void seq_monadic::add(expr* term, expr* regex, u_dependency* d) {
void seq_monadic::add(expr* term, expr* regex, void* d) {
m_memberships.push_back({ expr_ref(term, m), expr_ref(regex, m), d });
m_undo_trail.push(push_back_vector(m_memberships));
}
void seq_monadic::add_lo(expr* term, unsigned lo, u_dependency* d) {
void seq_monadic::add_lo(expr* term, unsigned lo, void* d) {
if (lo == 0)
return;
sort* re_sort = re().mk_re(term->get_sort());
@ -435,21 +434,21 @@ void seq_monadic::add_lo(expr* term, unsigned lo, u_dependency* d) {
add(term, regex, d);
}
void seq_monadic::add_hi(expr* term, unsigned hi, u_dependency* d) {
void seq_monadic::add_hi(expr* term, unsigned hi, void* d) {
sort* re_sort = re().mk_re(term->get_sort());
expr_ref all_char(re().mk_full_char(re_sort), m);
expr_ref regex(re().mk_loop_proper(all_char, 0, hi), m);
add(term, regex, d);
}
void seq_monadic::add_len(expr* term, unsigned len, u_dependency* d) {
void seq_monadic::add_len(expr* term, unsigned len, void* d) {
sort* re_sort = re().mk_re(term->get_sort());
expr_ref all_char(re().mk_full_char(re_sort), m);
expr_ref regex(re().mk_loop_proper(all_char, len, len), m);
add(term, regex, d);
}
lbool seq_monadic::decide(vector<std::tuple<expr_ref, expr_ref, u_dependency*>> const& memberships) {
lbool seq_monadic::decide(membership_vec const& memberships) {
m_model.reset();
if (memberships.empty())
return l_true; // empty conjunction is vacuously true
@ -487,7 +486,7 @@ lbool seq_monadic::decide(vector<std::tuple<expr_ref, expr_ref, u_dependency*>>
return decide_dnf(combined);
}
void seq_monadic::minimize_core(vector<std::tuple<expr_ref, expr_ref, u_dependency*>> const& memberships) {
void seq_monadic::minimize_core(membership_vec const& memberships) {
m_core.reset();
if (!m_min_core) {
// No minimization: the core is simply every asserted membership's dependency.
@ -499,13 +498,10 @@ void seq_monadic::minimize_core(vector<std::tuple<expr_ref, expr_ref, u_dependen
// Deletion-based minimization: start from the full unsat set and try to drop each
// membership; a membership is kept only if removing it makes the set no longer
// provably unsat. The result is a minimal unsat subset (relevant constraints only).
vector<std::tuple<expr_ref, expr_ref, u_dependency*>> keep(memberships);
unsigned i = 0;
while (i < keep.size()) {
vector<std::tuple<expr_ref, expr_ref, u_dependency*>> trial;
for (unsigned j = 0; j < keep.size(); ++j)
if (j != i)
trial.push_back(keep[j]);
membership_vec keep(memberships);
for (unsigned i = 0; i < keep.size(); ) {
membership_vec trial(keep);
trial.erase(trial.begin() + i);
if (decide(trial) == l_false)
keep.swap(trial); // membership i is not needed for unsat
else