3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-07-15 11:35:42 +00:00

WIP seq_split: derivative-based complement split (NOT for merge - soundness bug)

Prototype of the notes.md redesign: Split(~a) for star-free a via r = E(~a) | RE(LF(delta(~a))) using brz_derivative_cofactors, of_pred(lambda) char-classes, per-iterator memo (threaded through head_normalize) + De Morgan fallback at ~(R*)/cyclic revisit.

RESULT: De Morgan split blow-up ELIMINATED (L15: complement/max-split-set 65536 -> 0), validating the core idea. BUT: (1) SOUNDNESS BUG - concat membership over a derivative-split complement returns spurious unsat ((x.y) in ~([0-9]^2): default sat, nseq unsat; single-var x in ~([0-9]^2) is correct). Split-set not collapsed (49 splits) so of_pred not empty-dropped; fault is downstream handling of of_pred(lambda) char-classes in the concat split/primitive path. (2) bottleneck moved to DFS nodes (7 -> 33071). FIX DIRECTION: represent the cofactor char-class as range/union-of-ranges nseq fully supports, not of_pred(lambda). Do not build on this until fixed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Margus Veanes 2026-07-03 22:33:29 +03:00
parent 07c797c32b
commit 22eb098cd9
2 changed files with 52 additions and 11 deletions

View file

@ -27,6 +27,7 @@ Author:
#include "ast/seq_decl_plugin.h"
#include "ast/rewriter/seq_subset.h"
#include "util/obj_hashtable.h"
#include <functional>
class seq_rewriter;
@ -134,7 +135,10 @@ class seq_split {
// One level of the sigma rules: from_re(r) -> a SplitSet term built from the
// immediate subterms. `ok` is set false on an unsupported shape.
expr_ref expand_fromre(expr* r, bool& ok);
expr_ref expand_fromre(expr* r, bool& ok, obj_hashtable<expr>& deriv_memo);
// Build the single-character regex of_pred(lambda c. pred) from a cofactor
// path condition `pred` (a Boolean over the character (:var 0)).
expr_ref mk_charclass_re(expr* pred);
// Distribute a left/right concatenation over a head-normal split-set.
expr_ref distribute_lcat(expr* r, expr* hs);
expr_ref distribute_rcat(expr* hs, expr* r);
@ -146,7 +150,8 @@ class seq_split {
// `ok` is set false on a give-up (unsupported shape, weak-mode Boolean, or
// threshold overrun).
expr_ref head_normalize(expr* t, split_mode mode, unsigned threshold,
split_oracle const& oracle, bool& ok);
split_oracle const& oracle, bool& ok,
obj_hashtable<expr>& deriv_memo);
// Fully drain a suspended split-set into `out` (used for inter/compl bodies).
// Runs an `iterator` to exhaustion; returns false on a give-up.
bool materialize(expr* node, split_mode mode, unsigned threshold,
@ -204,6 +209,10 @@ public:
expr_ref_vector m_work; // GC-safe worklist of suspended split-sets
unsigned m_count = 0; // splits produced so far (vs. threshold)
bool m_giveup = false;
// Complement ~-regex states already expanded via the symbolic-derivative
// rule; re-encountering one (a cycle) falls back to the De Morgan rule so
// the lazy unfolding terminates. Per-iterator (iterators run concurrently).
obj_hashtable<expr> m_deriv_memo;
public:
iterator(seq_split& engine, expr* node, split_mode mode,
unsigned threshold, split_oracle oracle);