3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-03-18 11:04:09 +00:00

update generation of empty sequence to take sort argument, fix mk_concat substitution

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2026-03-13 10:00:23 -07:00
parent e384e8f3d4
commit df9df50a71
6 changed files with 146 additions and 121 deletions

View file

@ -461,21 +461,23 @@ namespace euf {
return mk(u);
}
snode* sgraph::mk_empty() {
expr_ref e(m_seq.str.mk_empty(m_str_sort), m);
snode* sgraph::mk_empty_seq(sort* s) {
expr_ref e(m_seq.str.mk_empty(s), m);
return mk(e);
}
snode* sgraph::mk_concat(snode* a, snode* b) {
if (a->is_empty()) return b;
if (b->is_empty()) return a;
expr_ref e(m_seq.str.mk_concat(a->get_expr(), b->get_expr()), m);
return mk(e);
if (m_seq.is_re(a->get_expr()))
return mk(expr_ref(m_seq.re.mk_concat(a->get_expr(), b->get_expr()), m));
else
return mk(expr_ref(m_seq.str.mk_concat(a->get_expr(), b->get_expr()), m));
}
snode* sgraph::drop_first(snode* n) {
if (n->is_empty() || n->is_token())
return mk_empty();
return mk_empty_seq(n->get_sort());
SASSERT(n->is_concat());
snode* l = n->arg(0);
snode* r = n->arg(1);
@ -486,7 +488,7 @@ namespace euf {
snode* sgraph::drop_last(snode* n) {
if (n->is_empty() || n->is_token())
return mk_empty();
return mk_empty_seq(n->get_sort());
SASSERT(n->is_concat());
snode* l = n->arg(0);
snode* r = n->arg(1);
@ -497,7 +499,7 @@ namespace euf {
snode* sgraph::drop_left(snode* n, unsigned count) {
if (count == 0 || n->is_empty()) return n;
if (count >= n->length()) return mk_empty();
if (count >= n->length()) return mk_empty_seq(n->get_sort());
for (unsigned i = 0; i < count; ++i)
n = drop_first(n);
return n;
@ -505,7 +507,7 @@ namespace euf {
snode* sgraph::drop_right(snode* n, unsigned count) {
if (count == 0 || n->is_empty()) return n;
if (count >= n->length()) return mk_empty();
if (count >= n->length()) return mk_empty_seq(n->get_sort());
for (unsigned i = 0; i < count; ++i)
n = drop_last(n);
return n;

View file

@ -128,7 +128,7 @@ namespace euf {
// factory methods for creating snodes with corresponding expressions
snode* mk_var(symbol const& name);
snode* mk_char(unsigned ch);
snode* mk_empty();
snode *mk_empty_seq(sort *s);
snode* mk_concat(snode* a, snode* b);
// drop operations: remove tokens from the front/back of a concat tree

View file

@ -143,6 +143,10 @@ namespace euf {
}
}
sort *get_sort() const {
return m_expr ? m_expr->get_sort() : nullptr;
}
// analogous to ZIPT's Str.First / Str.Last
snode const* first() const {
snode const* s = this;

View file

@ -544,7 +544,7 @@ namespace seq {
m_root = mk_node();
dep_tracker dep;
dep.insert(m_root->str_eqs().size() + m_root->str_mems().size());
euf::snode* history = m_sg.mk_empty();
euf::snode* history = m_sg.mk_empty_seq(str->get_sort());
unsigned id = next_mem_id();
m_root->add_str_mem(str_mem(str, regex, history, id, dep));
++m_num_input_mems;
@ -1055,7 +1055,7 @@ namespace seq {
arith_util arith(m);
for (euf::snode* t : tokens) {
if (t->is_var()) {
nielsen_subst s(t, sg.mk_empty(), dep);
nielsen_subst s(t, sg.mk_empty_seq(t->get_sort()), dep);
apply_subst(sg, s);
changed = true;
}
@ -1069,7 +1069,7 @@ namespace seq {
m_int_constraints.push_back(
int_constraint(pow_exp, zero, int_constraint_kind::eq, dep, m));
}
nielsen_subst s(t, sg.mk_empty(), dep);
nielsen_subst s(t, sg.mk_empty_seq(t->get_sort()), dep);
apply_subst(sg, s);
changed = true;
}
@ -1234,7 +1234,7 @@ namespace seq {
if (!merged) return nullptr;
// Rebuild snode from merged token list
euf::snode* rebuilt = sg.mk_empty();
euf::snode* rebuilt = sg.mk_empty_seq(side->get_sort());
for (unsigned k = 0; k < result.size(); ++k) {
rebuilt = (k == 0) ? result[k] : sg.mk_concat(rebuilt, result[k]);
}
@ -1281,7 +1281,7 @@ namespace seq {
if (!simplified) return nullptr;
if (result.empty()) return sg.mk_empty();
if (result.empty()) return sg.mk_empty_seq(side->get_sort());
euf::snode* rebuilt = result[0];
for (unsigned k = 1; k < result.size(); ++k)
rebuilt = sg.mk_concat(rebuilt, result[k]);
@ -2230,24 +2230,51 @@ namespace seq {
return true;
}
if (rhead->is_char() && lhead->is_var()) {
{
nielsen_node* child = mk_child(node);
nielsen_edge* e = mk_edge(node, child, true);
nielsen_subst s(lhead, m_sg.mk_empty(), eq.m_dep);
e->add_subst(s);
child->apply_subst(m_sg, s);
}
{
euf::snode* replacement = dir_concat(m_sg, rhead, lhead, fwd);
nielsen_node* child = mk_child(node);
nielsen_edge* e = mk_edge(node, child, false);
nielsen_subst s(lhead, replacement, eq.m_dep);
e->add_subst(s);
child->apply_subst(m_sg, s);
}
return true;
euf::snode* lhead = lhs_toks[0];
euf::snode* rhead = rhs_toks[0];
// char·A = y·B → branch 1: y→ε, branch 2: y→char·y
if (lhead->is_char() && rhead->is_var()) {
// branch 1: y → ε (progress)
{
nielsen_node* child = mk_child(node);
nielsen_edge* e = mk_edge(node, child, true);
nielsen_subst s(rhead, m_sg.mk_empty_seq(rhead->get_sort()), eq.m_dep);
e->add_subst(s);
child->apply_subst(m_sg, s);
}
// branch 2: y → char·y (no progress)
{
euf::snode* replacement = m_sg.mk_concat(lhead, rhead);
nielsen_node* child = mk_child(node);
nielsen_edge* e = mk_edge(node, child, false);
nielsen_subst s(rhead, replacement, eq.m_dep);
e->add_subst(s);
child->apply_subst(m_sg, s);
}
return true;
}
// x·A = char·B → branch 1: x→ε, branch 2: x→char·x
if (rhead->is_char() && lhead->is_var()) {
// branch 1: x → ε (progress)
{
nielsen_node* child = mk_child(node);
nielsen_edge* e = mk_edge(node, child, true);
nielsen_subst s(lhead, m_sg.mk_empty_seq(lhead->get_sort()), eq.m_dep);
e->add_subst(s);
child->apply_subst(m_sg, s);
}
// branch 2: x → char·x (no progress)
{
euf::snode* replacement = m_sg.mk_concat(rhead, lhead);
nielsen_node* child = mk_child(node);
nielsen_edge* e = mk_edge(node, child, false);
nielsen_subst s(lhead, replacement, eq.m_dep);
e->add_subst(s);
child->apply_subst(m_sg, s);
}
return true;
}
}
return false;
@ -2270,33 +2297,28 @@ namespace seq {
if (lhead->id() == rhead->id())
continue;
// child 1: x -> ε (progress)
{
nielsen_node* child = mk_child(node);
nielsen_edge* e = mk_edge(node, child, true);
nielsen_subst s(lhead, m_sg.mk_empty(), eq.m_dep);
e->add_subst(s);
child->apply_subst(m_sg, s);
}
// child 2: x -> y·x (forward) or x·y (backward)
{
euf::snode* replacement = dir_concat(m_sg, rhead, lhead, fwd);
nielsen_node* child = mk_child(node);
nielsen_edge* e = mk_edge(node, child, false);
nielsen_subst s(lhead, replacement, eq.m_dep);
e->add_subst(s);
child->apply_subst(m_sg, s);
}
// child 3: y -> x·y (forward) or y·x (backward)
{
euf::snode* replacement = dir_concat(m_sg, lhead, rhead, fwd);
nielsen_node* child = mk_child(node);
nielsen_edge* e = mk_edge(node, child, false);
nielsen_subst s(rhead, replacement, eq.m_dep);
e->add_subst(s);
child->apply_subst(m_sg, s);
}
return true;
euf::snode_vector lhs_toks, rhs_toks;
eq.m_lhs->collect_tokens(lhs_toks);
eq.m_rhs->collect_tokens(rhs_toks);
if (lhs_toks.empty() || rhs_toks.empty())
continue;
euf::snode* lhead = lhs_toks[0];
euf::snode* rhead = rhs_toks[0];
if (!lhead->is_var() || !rhead->is_var())
continue;
if (lhead->id() == rhead->id())
continue;
// x·A = y·B where x,y are distinct variables (classic Nielsen)
// child 1: x → ε (progress)
{
nielsen_node* child = mk_child(node);
nielsen_edge* e = mk_edge(node, child, true);
nielsen_subst s(lhead, m_sg.mk_empty_seq(lhead->get_sort()), eq.m_dep);
e->add_subst(s);
child->apply_subst(m_sg, s);
}
}
return false;
@ -2703,7 +2725,7 @@ namespace seq {
{
nielsen_node* child = mk_child(node);
nielsen_edge* e = mk_edge(node, child, true);
nielsen_subst s(first, m_sg.mk_empty(), mem.m_dep);
nielsen_subst s(first, m_sg.mk_empty_seq(first->get_sort()), mem.m_dep);
e->add_subst(s);
child->apply_subst(m_sg, s);
created = true;
@ -2921,7 +2943,7 @@ namespace seq {
if (base->is_var()) {
child = mk_child(node);
e = mk_edge(node, child, true);
nielsen_subst s1(base, m_sg.mk_empty(), dep);
nielsen_subst s1(base, m_sg.mk_empty_seq(base->get_sort()), dep);
e->add_subst(s1);
child->apply_subst(m_sg, s1);
}
@ -2929,7 +2951,7 @@ namespace seq {
// Branch 2: replace the power token itself with ε (n = 0 semantics)
child = mk_child(node);
e = mk_edge(node, child, true);
nielsen_subst s2(power, m_sg.mk_empty(), dep);
nielsen_subst s2(power, m_sg.mk_empty_seq(power->get_sort()), dep);
e->add_subst(s2);
child->apply_subst(m_sg, s2);
@ -3096,7 +3118,7 @@ namespace seq {
// Side constraint: n = 0
nielsen_node *child = mk_child(node);
nielsen_edge *e = mk_edge(node, child, true);
nielsen_subst s1(power, m_sg.mk_empty(), eq->m_dep);
nielsen_subst s1(power, m_sg.mk_empty_seq(power->get_sort()), eq->m_dep);
e->add_subst(s1);
child->apply_subst(m_sg, s1);
if (exp_n)
@ -3413,7 +3435,7 @@ namespace seq {
{
nielsen_node* child = mk_child(node);
nielsen_edge* e = mk_edge(node, child, true);
nielsen_subst s(first, m_sg.mk_empty(), mem.m_dep);
nielsen_subst s(first, m_sg.mk_empty_seq(first->get_sort()), mem.m_dep);
e->add_subst(s);
child->apply_subst(m_sg, s);
created = true;
@ -3600,7 +3622,7 @@ namespace seq {
{
nielsen_node* child = mk_child(node);
nielsen_edge* e = mk_edge(node, child, true);
nielsen_subst s(power, m_sg.mk_empty(), eq->m_dep);
nielsen_subst s(power, m_sg.mk_empty_seq(power->get_sort()), eq->m_dep);
e->add_subst(s);
child->apply_subst(m_sg, s);
if (exp_n)

View file

@ -421,9 +421,6 @@ namespace smt {
// here the actual Nielsen solving happens
auto result = m_nielsen.solve();
std::cout << "Result: " << (result == seq::nielsen_graph::search_result::sat ? "SAT" : result == seq::nielsen_graph::search_result::unsat ? "UNSAT" : "UNKNOWN") << "\n";
// m_nielsen.to_dot(std::cout);
// std::cout << std::endl;
if (result == seq::nielsen_graph::search_result::sat) {
IF_VERBOSE(1, verbose_stream() << "nseq final_check: solve SAT, sat_node="