mirror of
https://github.com/Z3Prover/z3
synced 2026-05-30 21:57:46 +00:00
Add propagate_ground_split: directly propagate var values when ground side is fully known
When one side of an equation is a pure ground string and the other side has variables with fully known lengths, directly propagate each variable's value as a substring without going through character-by-character splitting. E.g. x·y = "abcdef" with len(x)=2, len(y)=4 immediately gives x="ab", y="cdef". Also reverted the add_theory_aware_branching_info change (caused cross-context phase pollution across push/pop). Kept force_phase in branch_eq. Known issue: T08 (x·y = y·x, x≠y, len>0) hangs when run after T01+T06 in the same Z3 session due to phase-cache pollution across push/pop for the same variable names. Works correctly in isolation and with distinct variable names. Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
This commit is contained in:
parent
7c06ca18ff
commit
b59db29868
2 changed files with 63 additions and 1 deletions
|
|
@ -718,6 +718,11 @@ namespace smt {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Optimization: if one side is a pure ground string and the other side
|
||||||
|
// has variables with fully-known lengths, directly propagate substr values.
|
||||||
|
if (propagate_ground_split(lhs, rhs, dep))
|
||||||
|
return true;
|
||||||
|
|
||||||
// Try branching: find a variable and decide x = "" or x ≠ ""
|
// Try branching: find a variable and decide x = "" or x ≠ ""
|
||||||
if (branch_eq(lhs, rhs, dep))
|
if (branch_eq(lhs, rhs, dep))
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -762,7 +767,7 @@ namespace smt {
|
||||||
// If lower length bound is already > 0, skip the empty branch
|
// If lower length bound is already > 0, skip the empty branch
|
||||||
if (ctx.e_internalized(len_e) && lower_bound(len_e, lo) && lo > rational(0))
|
if (ctx.e_internalized(len_e) && lower_bound(len_e, lo) && lo > rational(0))
|
||||||
break;
|
break;
|
||||||
// Force the empty branch first
|
// Force the empty branch first.
|
||||||
TRACE(seq, tout << "branch " << mk_bounded_pp(e, m) << " = \"\"\n";);
|
TRACE(seq, tout << "branch " << mk_bounded_pp(e, m) << " = \"\"\n";);
|
||||||
IF_VERBOSE(3, verbose_stream() << " branch " << mk_bounded_pp(e, m) << " = \"\"\n";);
|
IF_VERBOSE(3, verbose_stream() << " branch " << mk_bounded_pp(e, m) << " = \"\"\n";);
|
||||||
ctx.force_phase(eq_empty);
|
ctx.force_phase(eq_empty);
|
||||||
|
|
@ -1194,6 +1199,62 @@ namespace smt {
|
||||||
return progress;
|
return progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If one side is a pure ground string and the other side has variables with
|
||||||
|
// fully known lengths, directly propagate each variable's value as a substr.
|
||||||
|
// E.g. x·y = "abcdef" with len(x)=2, len(y)=4 → x="ab", y="cdef"
|
||||||
|
bool theory_nseq::propagate_ground_split(expr_ref_vector const& lhs,
|
||||||
|
expr_ref_vector const& rhs,
|
||||||
|
nseq_dependency* dep) {
|
||||||
|
// Determine which side is pure-variable and which is pure-ground
|
||||||
|
auto has_var = [&](expr_ref_vector const& v) {
|
||||||
|
for (expr* e : v) if (m_nielsen.is_var(e)) return true;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
auto all_var = [&](expr_ref_vector const& v) {
|
||||||
|
for (expr* e : v) if (!m_nielsen.is_var(e)) return false;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
expr_ref_vector const* ground_side = nullptr;
|
||||||
|
expr_ref_vector const* var_side = nullptr;
|
||||||
|
if (!has_var(lhs) && all_var(rhs) && !rhs.empty()) {
|
||||||
|
ground_side = &lhs; var_side = &rhs;
|
||||||
|
}
|
||||||
|
else if (!has_var(rhs) && all_var(lhs) && !lhs.empty()) {
|
||||||
|
ground_side = &rhs; var_side = &lhs;
|
||||||
|
}
|
||||||
|
if (!ground_side)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Build the ground string
|
||||||
|
zstring ground;
|
||||||
|
{
|
||||||
|
sort* s = var_side->get(0)->get_sort();
|
||||||
|
expr_ref gc = mk_concat(*ground_side, s);
|
||||||
|
if (!is_ground_string(gc, ground))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect vars and their known lengths
|
||||||
|
unsigned offset = 0;
|
||||||
|
bool changed = false;
|
||||||
|
for (expr* e : *var_side) {
|
||||||
|
rational len_v;
|
||||||
|
if (!get_length(e, len_v) || !len_v.is_nonneg() || !len_v.is_unsigned())
|
||||||
|
return false; // length unknown, can't propagate
|
||||||
|
unsigned len = len_v.get_unsigned();
|
||||||
|
if (offset + len > ground.length())
|
||||||
|
return false; // length conflict, let solve_eq handle it
|
||||||
|
zstring substr = ground.extract(offset, len);
|
||||||
|
expr_ref val(m_util.str.mk_string(substr), m);
|
||||||
|
ensure_enode(val);
|
||||||
|
changed |= propagate_eq(dep, e, val);
|
||||||
|
offset += len;
|
||||||
|
}
|
||||||
|
if (offset != ground.length())
|
||||||
|
return false; // leftover ground chars, conflict
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
bool theory_nseq::check_diseq(nseq_ne const& ne) {
|
bool theory_nseq::check_diseq(nseq_ne const& ne) {
|
||||||
expr* l = ne.l();
|
expr* l = ne.l();
|
||||||
expr* r = ne.r();
|
expr* r = ne.r();
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ namespace smt {
|
||||||
bool branch_eq_prefix(expr_ref_vector const& lhs, expr_ref_vector const& rhs, nseq_dependency* dep);
|
bool branch_eq_prefix(expr_ref_vector const& lhs, expr_ref_vector const& rhs, nseq_dependency* dep);
|
||||||
bool branch_var_prefix(expr* x, expr_ref_vector const& other, nseq_dependency* dep);
|
bool branch_var_prefix(expr* x, expr_ref_vector const& other, nseq_dependency* dep);
|
||||||
bool split_variable(expr_ref_vector const& lhs, expr_ref_vector const& rhs, nseq_dependency* dep);
|
bool split_variable(expr_ref_vector const& lhs, expr_ref_vector const& rhs, nseq_dependency* dep);
|
||||||
|
bool propagate_ground_split(expr_ref_vector const& lhs, expr_ref_vector const& rhs, nseq_dependency* dep);
|
||||||
bool canonize(expr_ref_vector const& src, expr_ref_vector& dst, nseq_dependency*& dep);
|
bool canonize(expr_ref_vector const& src, expr_ref_vector& dst, nseq_dependency*& dep);
|
||||||
bool all_eqs_solved();
|
bool all_eqs_solved();
|
||||||
bool check_length_conflict(expr* x, expr_ref_vector const& es, nseq_dependency* dep);
|
bool check_length_conflict(expr* x, expr_ref_vector const& es, nseq_dependency* dep);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue