mirror of
https://github.com/Z3Prover/z3
synced 2026-08-14 17:55:36 +00:00
seq_monadic: add unsat-core extraction with minimization toggle
check() now records the dependencies of an unsat subset in m_core, exposed via
ptr_vector<u_dependency> const& core(). On l_false it calls minimize_core:
with the new m_min_core flag (set_min_core, default true) it deletion-minimizes
to a minimal unsat subset containing only constraints that participate in the
contradiction; with the flag off it returns all membership dependencies.
Add unit tests asserting the core omits irrelevant constraints (e.g. x in a*,
x in ~a*, y in b* has core {x-constraints} only), exercising both flag states.
The test harness runs with minimization disabled by default.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 57b9b87e-950a-49ea-bbb3-ed585646a5a9
This commit is contained in:
parent
1e09b4e6ae
commit
1ba30df028
3 changed files with 141 additions and 4 deletions
|
|
@ -424,10 +424,8 @@ void seq_monadic::add(expr* term, expr* regex, u_dependency* d) {
|
|||
m_memberships.push_back({ expr_ref(term, m), expr_ref(regex, m), d });
|
||||
}
|
||||
|
||||
lbool seq_monadic::check() {
|
||||
lbool seq_monadic::decide(vector<std::tuple<expr_ref, expr_ref, u_dependency*>> const& memberships) {
|
||||
m_model.reset();
|
||||
vector<std::tuple<expr_ref, expr_ref, u_dependency*>> memberships;
|
||||
memberships.swap(m_memberships); // consume the asserted memberships
|
||||
if (memberships.empty())
|
||||
return l_true; // empty conjunction is vacuously true
|
||||
m_pin.reset();
|
||||
|
|
@ -463,3 +461,42 @@ lbool seq_monadic::check() {
|
|||
}
|
||||
return decide_dnf(combined);
|
||||
}
|
||||
|
||||
void seq_monadic::minimize_core(vector<std::tuple<expr_ref, expr_ref, u_dependency*>> const& memberships) {
|
||||
m_core.reset();
|
||||
if (!m_min_core) {
|
||||
// No minimization: the core is simply every asserted membership's dependency.
|
||||
for (auto const& [term, regex, d] : memberships)
|
||||
if (d)
|
||||
m_core.push_back(d);
|
||||
return;
|
||||
}
|
||||
// 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]);
|
||||
if (decide(trial) == l_false)
|
||||
keep.swap(trial); // membership i is not needed for unsat
|
||||
else
|
||||
++i; // membership i is needed; keep it
|
||||
}
|
||||
for (auto const& [term, regex, d] : keep)
|
||||
if (d)
|
||||
m_core.push_back(d);
|
||||
}
|
||||
|
||||
lbool seq_monadic::check() {
|
||||
m_core.reset();
|
||||
vector<std::tuple<expr_ref, expr_ref, u_dependency*>> memberships;
|
||||
memberships.swap(m_memberships); // consume the asserted memberships
|
||||
lbool r = decide(memberships);
|
||||
if (r == l_false)
|
||||
minimize_core(memberships);
|
||||
return r;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue