mirror of
https://github.com/Z3Prover/z3
synced 2026-03-16 18:20:00 +00:00
Move seq_nielsen from src/ast/rewriter to src/smt/seq with new smt_seq component
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
This commit is contained in:
parent
425d3aec25
commit
7c328647de
7 changed files with 12 additions and 5 deletions
|
|
@ -1,311 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2026 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
seq_nielsen.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Nielsen graph implementation for string constraint solving.
|
||||
|
||||
Ports the constraint types and Nielsen graph structures from
|
||||
ZIPT (https://github.com/CEisenhofer/ZIPT/tree/parikh/ZIPT/Constraints)
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2026-03-02
|
||||
Clemens Eisenhofer 2026-03-02
|
||||
|
||||
--*/
|
||||
|
||||
#include "ast/rewriter/seq_nielsen.h"
|
||||
#include "ast/ast_pp.h"
|
||||
|
||||
namespace seq {
|
||||
|
||||
// -----------------------------------------------
|
||||
// dep_tracker
|
||||
// -----------------------------------------------
|
||||
|
||||
dep_tracker::dep_tracker(unsigned num_bits) {
|
||||
unsigned words = (num_bits + 31) / 32;
|
||||
m_bits.resize(words, 0);
|
||||
}
|
||||
|
||||
dep_tracker::dep_tracker(unsigned num_bits, unsigned set_bit) {
|
||||
unsigned words = (num_bits + 31) / 32;
|
||||
m_bits.resize(words, 0);
|
||||
if (set_bit < num_bits) {
|
||||
unsigned word_idx = set_bit / 32;
|
||||
unsigned bit_idx = set_bit % 32;
|
||||
m_bits[word_idx] = 1u << bit_idx;
|
||||
}
|
||||
}
|
||||
|
||||
void dep_tracker::merge(dep_tracker const& other) {
|
||||
if (other.m_bits.empty())
|
||||
return;
|
||||
if (m_bits.size() < other.m_bits.size())
|
||||
m_bits.resize(other.m_bits.size(), 0);
|
||||
for (unsigned i = 0; i < other.m_bits.size(); ++i)
|
||||
m_bits[i] |= other.m_bits[i];
|
||||
}
|
||||
|
||||
bool dep_tracker::is_superset(dep_tracker const& other) const {
|
||||
for (unsigned i = 0; i < other.m_bits.size(); ++i) {
|
||||
unsigned my_bits = (i < m_bits.size()) ? m_bits[i] : 0;
|
||||
if ((my_bits & other.m_bits[i]) != other.m_bits[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dep_tracker::empty() const {
|
||||
for (unsigned b : m_bits)
|
||||
if (b != 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------
|
||||
// str_eq
|
||||
// -----------------------------------------------
|
||||
|
||||
void str_eq::sort() {
|
||||
if (m_lhs && m_rhs && m_lhs->id() > m_rhs->id())
|
||||
std::swap(m_lhs, m_rhs);
|
||||
}
|
||||
|
||||
bool str_eq::is_trivial() const {
|
||||
return m_lhs == m_rhs ||
|
||||
(m_lhs && m_rhs && m_lhs->is_empty() && m_rhs->is_empty());
|
||||
}
|
||||
|
||||
bool str_eq::contains_var(euf::snode* var) const {
|
||||
if (!var) return false;
|
||||
// check if var appears in the token list of lhs or rhs
|
||||
if (m_lhs) {
|
||||
euf::snode_vector tokens;
|
||||
m_lhs->collect_tokens(tokens);
|
||||
for (euf::snode* t : tokens)
|
||||
if (t == var) return true;
|
||||
}
|
||||
if (m_rhs) {
|
||||
euf::snode_vector tokens;
|
||||
m_rhs->collect_tokens(tokens);
|
||||
for (euf::snode* t : tokens)
|
||||
if (t == var) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// -----------------------------------------------
|
||||
// str_mem
|
||||
// -----------------------------------------------
|
||||
|
||||
bool str_mem::is_primitive() const {
|
||||
return m_str && m_str->length() == 1 && m_str->is_var();
|
||||
}
|
||||
|
||||
bool str_mem::contains_var(euf::snode* var) const {
|
||||
if (!var) return false;
|
||||
if (m_str) {
|
||||
euf::snode_vector tokens;
|
||||
m_str->collect_tokens(tokens);
|
||||
for (euf::snode* t : tokens)
|
||||
if (t == var) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// -----------------------------------------------
|
||||
// nielsen_subst
|
||||
// -----------------------------------------------
|
||||
|
||||
bool nielsen_subst::is_eliminating() const {
|
||||
if (!m_var || !m_replacement) return true;
|
||||
// check if var appears in replacement
|
||||
euf::snode_vector tokens;
|
||||
m_replacement->collect_tokens(tokens);
|
||||
for (euf::snode* t : tokens)
|
||||
if (t == m_var) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------
|
||||
// nielsen_edge
|
||||
// -----------------------------------------------
|
||||
|
||||
nielsen_edge::nielsen_edge(nielsen_node* src, nielsen_node* tgt, bool is_progress):
|
||||
m_src(src), m_tgt(tgt), m_is_progress(is_progress) {
|
||||
}
|
||||
|
||||
// -----------------------------------------------
|
||||
// nielsen_node
|
||||
// -----------------------------------------------
|
||||
|
||||
nielsen_node::nielsen_node(nielsen_graph* graph, unsigned id):
|
||||
m_id(id), m_graph(graph), m_is_progress(true) {
|
||||
}
|
||||
|
||||
void nielsen_node::clone_from(nielsen_node const& parent) {
|
||||
m_str_eq.reset();
|
||||
m_str_mem.reset();
|
||||
for (auto const& eq : parent.m_str_eq)
|
||||
m_str_eq.push_back(str_eq(eq.m_lhs, eq.m_rhs, eq.m_dep));
|
||||
for (auto const& mem : parent.m_str_mem)
|
||||
m_str_mem.push_back(str_mem(mem.m_str, mem.m_regex, mem.m_history, mem.m_id, mem.m_dep));
|
||||
}
|
||||
|
||||
void nielsen_node::apply_subst(euf::sgraph& sg, nielsen_subst const& s) {
|
||||
if (!s.m_var) return;
|
||||
for (unsigned i = 0; i < m_str_eq.size(); ++i) {
|
||||
str_eq& eq = m_str_eq[i];
|
||||
eq.m_lhs = sg.subst(eq.m_lhs, s.m_var, s.m_replacement);
|
||||
eq.m_rhs = sg.subst(eq.m_rhs, s.m_var, s.m_replacement);
|
||||
eq.m_dep.merge(s.m_dep);
|
||||
eq.sort();
|
||||
}
|
||||
for (unsigned i = 0; i < m_str_mem.size(); ++i) {
|
||||
str_mem& mem = m_str_mem[i];
|
||||
mem.m_str = sg.subst(mem.m_str, s.m_var, s.m_replacement);
|
||||
// regex is typically ground, but apply subst for generality
|
||||
mem.m_regex = sg.subst(mem.m_regex, s.m_var, s.m_replacement);
|
||||
mem.m_dep.merge(s.m_dep);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------
|
||||
// nielsen_graph
|
||||
// -----------------------------------------------
|
||||
|
||||
nielsen_graph::nielsen_graph(euf::sgraph& sg):
|
||||
m_sg(sg) {
|
||||
}
|
||||
|
||||
nielsen_graph::~nielsen_graph() {
|
||||
reset();
|
||||
}
|
||||
|
||||
nielsen_node* nielsen_graph::mk_node() {
|
||||
unsigned id = m_nodes.size();
|
||||
nielsen_node* n = alloc(nielsen_node, this, id);
|
||||
m_nodes.push_back(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
nielsen_node* nielsen_graph::mk_child(nielsen_node* parent) {
|
||||
nielsen_node* child = mk_node();
|
||||
child->clone_from(*parent);
|
||||
return child;
|
||||
}
|
||||
|
||||
nielsen_edge* nielsen_graph::mk_edge(nielsen_node* src, nielsen_node* tgt, bool is_progress) {
|
||||
nielsen_edge* e = alloc(nielsen_edge, src, tgt, is_progress);
|
||||
m_edges.push_back(e);
|
||||
src->add_outgoing(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
void nielsen_graph::add_str_eq(euf::snode* lhs, euf::snode* rhs) {
|
||||
if (!m_root)
|
||||
m_root = mk_node();
|
||||
dep_tracker dep(m_root->str_eqs().size() + m_root->str_mems().size() + 1,
|
||||
m_root->str_eqs().size());
|
||||
str_eq eq(lhs, rhs, dep);
|
||||
eq.sort();
|
||||
m_root->add_str_eq(eq);
|
||||
}
|
||||
|
||||
void nielsen_graph::add_str_mem(euf::snode* str, euf::snode* regex) {
|
||||
if (!m_root)
|
||||
m_root = mk_node();
|
||||
dep_tracker dep(m_root->str_eqs().size() + m_root->str_mems().size() + 1,
|
||||
m_root->str_eqs().size() + m_root->str_mems().size());
|
||||
euf::snode* history = m_sg.mk_empty();
|
||||
unsigned id = next_mem_id();
|
||||
m_root->add_str_mem(str_mem(str, regex, history, id, dep));
|
||||
}
|
||||
|
||||
void nielsen_graph::inc_run_idx() {
|
||||
if (m_run_idx == UINT_MAX) {
|
||||
for (nielsen_node* n : m_nodes)
|
||||
n->reset_counter();
|
||||
m_run_idx = 1;
|
||||
}
|
||||
else
|
||||
++m_run_idx;
|
||||
}
|
||||
|
||||
void nielsen_graph::reset() {
|
||||
for (nielsen_node* n : m_nodes)
|
||||
dealloc(n);
|
||||
for (nielsen_edge* e : m_edges)
|
||||
dealloc(e);
|
||||
m_nodes.reset();
|
||||
m_edges.reset();
|
||||
m_root = nullptr;
|
||||
m_run_idx = 0;
|
||||
m_depth_bound = 0;
|
||||
m_next_mem_id = 0;
|
||||
}
|
||||
|
||||
std::ostream& nielsen_graph::display(std::ostream& out) const {
|
||||
out << "nielsen_graph with " << m_nodes.size() << " nodes, "
|
||||
<< m_edges.size() << " edges\n";
|
||||
|
||||
for (nielsen_node const* n : m_nodes) {
|
||||
out << " node[" << n->id() << "]";
|
||||
if (n == m_root)
|
||||
out << " (root)";
|
||||
if (n->is_general_conflict())
|
||||
out << " CONFLICT";
|
||||
if (n->is_extended())
|
||||
out << " EXTENDED";
|
||||
out << "\n";
|
||||
|
||||
// display string equalities
|
||||
for (auto const& eq : n->str_eqs()) {
|
||||
out << " str_eq: ";
|
||||
if (eq.m_lhs) out << "lhs[id=" << eq.m_lhs->id() << ",len=" << eq.m_lhs->length() << "]";
|
||||
else out << "null";
|
||||
out << " = ";
|
||||
if (eq.m_rhs) out << "rhs[id=" << eq.m_rhs->id() << ",len=" << eq.m_rhs->length() << "]";
|
||||
else out << "null";
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
// display regex memberships
|
||||
for (auto const& mem : n->str_mems()) {
|
||||
out << " str_mem[" << mem.m_id << "]: ";
|
||||
if (mem.m_str) out << "str[id=" << mem.m_str->id() << ",len=" << mem.m_str->length() << "]";
|
||||
else out << "null";
|
||||
out << " in ";
|
||||
if (mem.m_regex) out << "re[id=" << mem.m_regex->id() << "]";
|
||||
else out << "null";
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
// display outgoing edges
|
||||
for (nielsen_edge const* e : n->outgoing()) {
|
||||
out << " -> node[" << e->tgt()->id() << "]";
|
||||
if (e->is_progress()) out << " (progress)";
|
||||
for (auto const& s : e->subst()) {
|
||||
out << " {";
|
||||
if (s.m_var) out << "var[" << s.m_var->id() << "]";
|
||||
out << " -> ";
|
||||
if (s.m_replacement) out << "repl[" << s.m_replacement->id() << ",len=" << s.m_replacement->length() << "]";
|
||||
else out << "eps";
|
||||
out << "}";
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
if (n->backedge())
|
||||
out << " backedge -> node[" << n->backedge()->id() << "]\n";
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue