3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-05-31 06:07:46 +00:00

We need a better witness during model construction

This commit is contained in:
CEisenhofer 2026-05-16 16:21:57 +02:00
parent 501462b494
commit b77d2b3360
2 changed files with 33 additions and 14 deletions

View file

@ -122,11 +122,26 @@ namespace smt {
return alloc(expr_wrapper_proc, e); return alloc(expr_wrapper_proc, e);
} }
// For nth_u (underspecified nth), return a fresh value of the element sort. // For nth_u (underspecified nth): the Nielsen character-peel /
// NSB review: this looks plain wrong. // regex-if-split records the chosen character as a relevant
// equality literal (e.g. (= (seq.nth_u x 0) (_ Char 65))), so the
// enode's equivalence class contains the required character
// constant. Use it; only fall back to a fresh value when the
// peeled character is genuinely unconstrained.
if (m_seq.str.is_nth_u(e)) { if (m_seq.str.is_nth_u(e)) {
sort *srt = e->get_sort(); sort *srt = e->get_sort();
expr *val = m_factory->get_fresh_value(srt); expr* val = nullptr;
enode* it = n;
do {
if (m.is_value(it->get_expr())) {
val = it->get_expr();
break;
}
it = it->get_next();
}
while (it != n);
if (!val)
val = m_factory->get_fresh_value(srt);
if (val) { if (val) {
m_trail.push_back(val); m_trail.push_back(val);
return alloc(expr_wrapper_proc, to_app(val)); return alloc(expr_wrapper_proc, to_app(val));
@ -258,13 +273,12 @@ namespace smt {
// the exact traversal order of collect_dependencies so that the `values` // the exact traversal order of collect_dependencies so that the `values`
// vector (model values for enode dependencies) is consumed consistently. // vector (model values for enode dependencies) is consumed consistently.
// //
// Computed nodes (concat / power / variable) are memoized in node2value // Computed nodes (concat / power / variable) are memorized in node2value
// keyed by (snode id, is_recursive). The recursion flag is part of the // keyed by (snode id, is_recursive). The recursion flag is part of the
// key because the SAME variable snode appears in two distinct roles in a // key because the SAME variable snode appears in two distinct roles in a
// Nielsen substitution such as D -> "cc" D: the outer variable being // Nielsen substitution such as D -> "cc" D: the outer variable being
// defined (is_recursive == false, value == value of its replacement) and // defined (is_recursive == false, value == value of its replacement) and
// the inner "leftover" remainder (is_recursive == true, value == ""). A // the inner "leftover" remainder (is_recursive == true, value == "").
// single id-keyed map would let the leftover clobber the outer value.
uint_set seen; uint_set seen;
u_map<expr *> var2value; u_map<expr *> var2value;
u_map<expr *> node2value; u_map<expr *> node2value;
@ -381,12 +395,17 @@ namespace smt {
val = rv; val = rv;
} }
else if (is_recursive) { else if (is_recursive) {
// recursive "leftover" remainder of a Nielsen substitution: // recursive "leftover" remainder of a Nielsen substitution
// the substitution path captured all required characters, so // such as x -> [nth_u(x,k)] ++ x. If the variable still
// the remaining occurrence is the empty string. Do NOT pad it // carries a primitive membership at the satisfying node
// via get_var_value (the shared length belongs to the outer // (recorded in m_var_regex), the leftover is a genuine
// variable, not this remainder). // non-empty witness of that residual regex, not an
val = m_seq.str.mk_empty(curr->get_sort()); // eliminated remainder. Otherwise the path drove it to the
// empty string.
if (m_var_regex.contains(curr->first()->id()))
val = get_var_value(curr);
else
val = m_seq.str.mk_empty(curr->get_sort());
} }
else { else {
// genuinely free variable (no replacement): respect its // genuinely free variable (no replacement): respect its

View file

@ -296,7 +296,7 @@ namespace smt {
void theory_nseq::assign_eh(bool_var v, bool is_true) { void theory_nseq::assign_eh(bool_var v, bool is_true) {
try { try {
expr* e = ctx.bool_var2expr(v); expr* e = ctx.bool_var2expr(v);
// std::cout << "assigned [" << sat::literal(v, is_true) << "] " << mk_pp(e, m) << " = " << is_true << std::endl; std::cout << "assigned [" << sat::literal(v, is_true) << "] " << mk_pp(e, m) << " = " << is_true << std::endl;
expr *s = nullptr, *re = nullptr, *a = nullptr, *b = nullptr; expr *s = nullptr, *re = nullptr, *a = nullptr, *b = nullptr;
TRACE(seq, tout << (is_true ? "" : "¬") << mk_bounded_pp(e, m, 3) << "\n";); TRACE(seq, tout << (is_true ? "" : "¬") << mk_bounded_pp(e, m, 3) << "\n";);
if (m_seq.str.is_in_re(e, s, re)) { if (m_seq.str.is_in_re(e, s, re)) {
@ -890,7 +890,7 @@ namespace smt {
set_conflict(eqs, lits); set_conflict(eqs, lits);
#ifdef Z3DEBUG #ifdef Z3DEBUG
#if 0 #if 1
// Pass constraints to a subsolver to check correctness modulo legacy solver // Pass constraints to a subsolver to check correctness modulo legacy solver
{ {
smt_params p; smt_params p;