3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-05-23 18:39:38 +00:00

fix bug in non-emptiness witness extraction

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2026-03-24 12:34:40 -07:00
parent da9d8c8694
commit 5803c6f202
7 changed files with 198 additions and 61 deletions

View file

@ -503,7 +503,8 @@ namespace seq {
// -----------------------------------------------------------------------
lbool seq_regex::is_empty_bfs(euf::snode* re, unsigned max_states) {
SASSERT(re && re->get_expr());
if (!re)
return l_undef;
if (re->is_fail())
return l_true;
if (re->is_nullable())

View file

@ -673,55 +673,7 @@ namespace smt {
}
}
/*
Return a list of all (cond, leaf) pairs in a given derivative
expression r.
Note: this implementation is inefficient: it simply collects all expressions under an if and
iterates over all combinations.
This method is still used by:
propagate_is_empty
propagate_is_non_empty
*/
void seq_regex::get_cofactors(expr* r, expr_ref_pair_vector& result) {
obj_hashtable<expr> ifs;
expr* cond = nullptr, * r1 = nullptr, * r2 = nullptr;
for (expr* e : subterms::ground(expr_ref(r, m)))
if (m.is_ite(e, cond, r1, r2))
ifs.insert(cond);
expr_ref_vector rs(m);
vector<expr_ref_vector> conds;
conds.push_back(expr_ref_vector(m));
rs.push_back(r);
for (expr* c : ifs) {
unsigned sz = conds.size();
expr_safe_replace rep1(m);
expr_safe_replace rep2(m);
rep1.insert(c, m.mk_true());
rep2.insert(c, m.mk_false());
expr_ref r2(m);
for (unsigned i = 0; i < sz; ++i) {
expr_ref_vector cs = conds[i];
cs.push_back(mk_not(m, c));
conds.push_back(cs);
conds[i].push_back(c);
expr_ref r1(rs.get(i), m);
rep1(r1, r2);
rs[i] = r2;
rep2(r1, r2);
rs.push_back(r2);
}
}
for (unsigned i = 0; i < conds.size(); ++i) {
expr_ref conj = mk_and(conds[i]);
expr_ref r(rs.get(i), m);
ctx.get_rewriter()(r);
if (!m.is_false(conj) && !re().is_empty(r))
result.push_back(conj, r);
}
}
/*
is_empty(r, u) => ~is_nullable(r)
@ -877,4 +829,50 @@ namespace smt {
return std::string("id") + std::to_string(e->get_id());
}
/**
Return a list of all (cond, leaf) pairs in a given
expression r.
Note: this implementation is inefficient: it simply collects all expressions under an if and
iterates over all combinations.
*/
void seq_regex::get_cofactors(expr *r, expr_ref_pair_vector &result) {
obj_hashtable<expr> ifs;
expr *cond = nullptr, *r1 = nullptr, *r2 = nullptr;
for (expr *e : subterms::ground(expr_ref(r, m)))
if (m.is_ite(e, cond, r1, r2))
ifs.insert(cond);
expr_ref_vector rs(m);
vector<expr_ref_vector> conds;
conds.push_back(expr_ref_vector(m));
rs.push_back(r);
for (expr *c : ifs) {
unsigned sz = conds.size();
expr_safe_replace rep1(m);
expr_safe_replace rep2(m);
rep1.insert(c, m.mk_true());
rep2.insert(c, m.mk_false());
expr_ref r2(m);
for (unsigned i = 0; i < sz; ++i) {
expr_ref_vector cs = conds[i];
cs.push_back(m.mk_not(c));
conds.push_back(cs);
conds[i].push_back(c);
expr_ref r1(rs.get(i), m);
rep1(r1, r2);
rs[i] = r2;
rep2(r1, r2);
rs.push_back(r2);
}
}
for (unsigned i = 0; i < conds.size(); ++i) {
expr_ref conj = mk_and(conds[i]);
expr_ref r(rs.get(i), m);
ctx.get_rewriter()(r);
if (!m.is_false(conj) && !re().is_empty(r))
result.push_back(conj, r);
}
}
}

View file

@ -164,7 +164,6 @@ namespace smt {
// returned by derivative_wrapper
expr_ref mk_deriv_accept(expr* s, unsigned i, expr* r);
void get_derivative_targets(expr* r, expr_ref_vector& targets);
void get_cofactors(expr* r, expr_ref_pair_vector& result);
/*
Pretty print the regex of the state id to the out stream,
@ -184,6 +183,9 @@ namespace smt {
bool block_if_empty(expr* r, literal lit);
void get_cofactors(expr *r, expr_ref_pair_vector &result);
public:
seq_regex(theory_seq& th);