3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-03-18 02:53:46 +00:00

We don't need to handle negative membership constraints explicitly

This commit is contained in:
CEisenhofer 2026-03-04 19:07:36 +01:00
parent 4e7d83f996
commit b2838b472d
7 changed files with 121 additions and 117 deletions

View file

@ -42,11 +42,12 @@ namespace smt {
register_existing_values(nielsen);
collect_var_regex_constraints(state);
// if the last solve returned sat, extract assignments from the
// satisfying leaf node found during DFS.
seq::nielsen_node const* root = nielsen.root();
if (root && root->is_satisfied())
extract_assignments(root);
// extract variable assignments from the satisfying leaf's substitution path
seq::nielsen_node const* sat = nielsen.sat_node();
IF_VERBOSE(1, verbose_stream() << "nseq model init: sat_node=" << (sat ? "set" : "null")
<< " path_len=" << nielsen.sat_path().size() << "\n";);
extract_assignments(nielsen.sat_path());
IF_VERBOSE(1, verbose_stream() << "nseq model: m_var_values has " << m_var_values.size() << " entries\n";);
}
model_value_proc* nseq_model::mk_value(enode* n, model_generator& mg) {
@ -74,6 +75,12 @@ namespace smt {
// look up snode for this expression
euf::snode* sn = m_sg.find(e);
IF_VERBOSE(2, {
verbose_stream() << "nseq mk_value: expr=" << mk_bounded_pp(e, m, 2);
if (sn) verbose_stream() << " snode[" << sn->id() << "] kind=" << (int)sn->kind();
else verbose_stream() << " snode=null";
verbose_stream() << "\n";
});
expr_ref val(m);
if (sn)
val = snode_to_value(sn);
@ -99,25 +106,46 @@ namespace smt {
m_factory = nullptr;
}
void nseq_model::extract_assignments(seq::nielsen_node const* node) {
if (!node)
return;
for (auto const& eq : node->str_eqs()) {
if (!eq.m_lhs || !eq.m_rhs)
continue;
if (eq.m_lhs->is_var() && !m_var_values.contains(eq.m_lhs->id())) {
expr_ref val = snode_to_value(eq.m_rhs);
if (val) {
m_trail.push_back(val);
m_var_values.insert(eq.m_lhs->id(), val);
}
void nseq_model::extract_assignments(svector<seq::nielsen_edge*> const& sat_path) {
IF_VERBOSE(1, verbose_stream() << "nseq extract_assignments: path length=" << sat_path.size() << "\n";);
// compose substitutions root-to-leaf.
// bindings[i] = (var_snode, current_value_snode).
// When a new substitution (s.m_var -> s.m_replacement) is applied,
// substitute s.m_var in all existing values, then record the new binding.
vector<std::pair<euf::snode*, euf::snode*>> bindings;
for (seq::nielsen_edge* e : sat_path) {
for (seq::nielsen_subst const& s : e->subst()) {
if (!s.m_var) continue;
IF_VERBOSE(1, {
verbose_stream() << " subst: snode[" << s.m_var->id() << "]";
if (s.m_var->get_expr()) verbose_stream() << "=" << mk_bounded_pp(s.m_var->get_expr(), m, 2);
verbose_stream() << " -> snode[" << (s.m_replacement ? (int)s.m_replacement->id() : -1) << "]";
if (s.m_replacement && s.m_replacement->get_expr()) verbose_stream() << "=" << mk_bounded_pp(s.m_replacement->get_expr(), m, 2);
verbose_stream() << "\n";
});
for (auto& b : bindings)
b.second = m_sg.subst(b.second, s.m_var, s.m_replacement);
bindings.push_back({s.m_var, s.m_replacement});
}
if (eq.m_rhs->is_var() && !m_var_values.contains(eq.m_rhs->id())) {
expr_ref val = snode_to_value(eq.m_lhs);
if (val) {
m_trail.push_back(val);
m_var_values.insert(eq.m_rhs->id(), val);
}
}
IF_VERBOSE(1, verbose_stream() << "nseq extract_assignments: " << bindings.size() << " bindings\n";);
for (auto const& b : bindings) {
unsigned id = b.first->id();
if (m_var_values.contains(id))
continue;
expr_ref val = snode_to_value(b.second);
IF_VERBOSE(1, {
verbose_stream() << " var snode[" << id << "]";
if (b.first->get_expr()) verbose_stream() << "=" << mk_bounded_pp(b.first->get_expr(), m, 2);
verbose_stream() << " -> ";
if (val) verbose_stream() << mk_bounded_pp(val, m, 3); else verbose_stream() << "(null)";
verbose_stream() << "\n";
});
if (val) {
m_trail.push_back(val);
m_var_values.insert(id, val);
}
}
}
@ -281,26 +309,6 @@ namespace smt {
}
}
// validate negative memberships: str ∉ regex
for (auto const& entry : state.neg_mems()) {
if (!entry.m_str || !entry.m_regex)
continue;
expr* s_expr = entry.m_str->get_expr();
expr* r_expr = entry.m_regex->get_expr();
if (!s_expr || !r_expr)
continue;
expr_ref in_re(m_seq.re.mk_in_re(s_expr, r_expr), m);
expr_ref val(m);
mdl.eval(in_re, val, true);
if (val && m.is_true(val)) {
IF_VERBOSE(0, verbose_stream() << "nseq model: negative membership violated: "
<< mk_bounded_pp(s_expr, m, 3)
<< " not in " << mk_bounded_pp(r_expr, m, 3) << "\n";);
ok = false;
}
}
return ok;
}