3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-05-16 23:25:36 +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);
}
// For nth_u (underspecified nth), return a fresh value of the element sort.
// NSB review: this looks plain wrong.
// For nth_u (underspecified nth): the Nielsen character-peel /
// 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)) {
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) {
m_trail.push_back(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`
// 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
// key because the SAME variable snode appears in two distinct roles in a
// Nielsen substitution such as D -> "cc" D: the outer variable being
// defined (is_recursive == false, value == value of its replacement) and
// the inner "leftover" remainder (is_recursive == true, value == ""). A
// single id-keyed map would let the leftover clobber the outer value.
// the inner "leftover" remainder (is_recursive == true, value == "").
uint_set seen;
u_map<expr *> var2value;
u_map<expr *> node2value;
@ -381,12 +395,17 @@ namespace smt {
val = rv;
}
else if (is_recursive) {
// recursive "leftover" remainder of a Nielsen substitution:
// the substitution path captured all required characters, so
// the remaining occurrence is the empty string. Do NOT pad it
// via get_var_value (the shared length belongs to the outer
// variable, not this remainder).
val = m_seq.str.mk_empty(curr->get_sort());
// recursive "leftover" remainder of a Nielsen substitution
// such as x -> [nth_u(x,k)] ++ x. If the variable still
// carries a primitive membership at the satisfying node
// (recorded in m_var_regex), the leftover is a genuine
// non-empty witness of that residual regex, not an
// 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 {
// 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) {
try {
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;
TRACE(seq, tout << (is_true ? "" : "¬") << mk_bounded_pp(e, m, 3) << "\n";);
if (m_seq.str.is_in_re(e, s, re)) {
@ -890,7 +890,7 @@ namespace smt {
set_conflict(eqs, lits);
#ifdef Z3DEBUG
#if 0
#if 1
// Pass constraints to a subsolver to check correctness modulo legacy solver
{
smt_params p;