3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-03-07 13:54:53 +00:00

Merge remote-tracking branch 'origin/c3' into copilot/port-nielsen-graph-generation

This commit is contained in:
Nikolaj Bjorner 2026-03-02 16:56:55 -08:00
commit fedb610da1
10 changed files with 1569 additions and 45 deletions

1139
.github/workflows/zipt-code-reviewer.lock.yml generated vendored Normal file

File diff suppressed because it is too large Load diff

253
.github/workflows/zipt-code-reviewer.md vendored Normal file
View file

@ -0,0 +1,253 @@
---
description: Reviews Z3 string/sequence graph implementation (euf_sgraph, euf_seq_plugin, src/smt/seq) by comparing with the ZIPT reference implementation and reporting improvements as git diffs in GitHub issues
on:
schedule:
- cron: "0 0,6,12,18 * * *"
workflow_dispatch:
permissions: read-all
network:
allowed:
- defaults
- github
tools:
cache-memory: true
github:
toolsets: [default]
view: {}
glob: {}
edit: {}
web-fetch: {}
bash:
- "git diff:*"
- "git log:*"
- "git show:*"
- "git status"
- "clang-format:*"
safe-outputs:
create-issue:
title-prefix: "[zipt-review] "
labels: [code-quality, automated, string-solver]
max: 3
missing-tool:
create-issue: true
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
persist-credentials: false
---
# ZIPT Code Reviewer
You are an expert C++ code reviewer specializing in string constraint solvers and the Z3 theorem prover. Your mission is to compare Z3's string/sequence graph implementation with the reference ZIPT implementation, identify concrete code improvements, and present them as git diffs in a GitHub issue.
## Current Context
- **Repository**: ${{ github.repository }}
- **Workspace**: ${{ github.workspace }}
- **ZIPT Reference**: https://github.com/CEisenhofer/ZIPT/tree/parikh/ZIPT
## Phase 1: Read Z3 Source Files
Read each of the following Z3 source files in full:
### String Graph (euf_sgraph / euf_snode)
- `src/ast/euf/euf_snode.h`
- `src/ast/euf/euf_sgraph.h`
- `src/ast/euf/euf_sgraph.cpp`
### Sequence Plugin (euf_seq_plugin)
- `src/ast/euf/euf_seq_plugin.h`
- `src/ast/euf/euf_seq_plugin.cpp`
### SMT Sequence Theory (src/smt/seq*)
Use the glob tool to find all relevant files:
```
src/smt/seq*.h
src/smt/seq*.cpp
```
Read each matched file.
## Phase 2: Fetch ZIPT Reference Implementation
The ZIPT project (https://github.com/CEisenhofer/ZIPT/tree/parikh/ZIPT) is the reference C# implementation that the Z3 string solver is ported from. Fetch the relevant source files to understand the reference algorithms.
### Step 2.1: Discover ZIPT File Structure
Fetch the ZIPT repository tree to understand the structure:
```
https://raw.githubusercontent.com/CEisenhofer/ZIPT/parikh/ZIPT/
```
Try fetching these likely ZIPT source directories and files:
1. Repository root listing: `https://api.github.com/repos/CEisenhofer/ZIPT/git/trees/parikh?recursive=1`
2. Key ZIPT source files (fetch the ones you find relevant from the tree):
- Look for files related to: string graphs, sequence plugins, Nielsen graph, Parikh constraints, polynomial hashing, substitution caching
- The ZIPT project is written in C#; the Z3 implementation is a C++ port
When fetching files, use the raw content URL pattern:
```
https://raw.githubusercontent.com/CEisenhofer/ZIPT/parikh/ZIPT/<path>
```
### Step 2.2: Identify Corresponding ZIPT Files
For each Z3 file you read in Phase 1, identify the ZIPT file(s) that implement the same functionality. Focus on:
- String/sequence graph data structures (snode, sgraph equivalents)
- Concat associativity propagation
- Nullable computation
- Kleene star / regex handling
- Polynomial hash matrix computation
- Substitution caching
## Phase 3: Analyze and Identify Improvements
Compare the Z3 C++ implementation against the ZIPT C# reference. For each file pair, look for:
### 3.1 Algorithmic Improvements
- Missing algorithms or edge cases present in ZIPT but absent from Z3
- More efficient data structures used in ZIPT
- Better asymptotic complexity in ZIPT for key operations
- Missing optimizations (e.g., short-circuit evaluations, caching strategies)
### 3.2 Correctness Issues
- Logic discrepancies between Z3 and ZIPT for the same algorithm
- Missing null/empty checks present in ZIPT
- Incorrect handling of edge cases (empty strings, epsilon, absorbing elements)
- Off-by-one errors or boundary condition mistakes
### 3.3 Code Quality Improvements
- Functions in ZIPT that are cleaner or more modular than the Z3 port
- Missing early-exit conditions
- Redundant computations that ZIPT avoids
- Better naming or structure in ZIPT that could improve Z3 readability
### 3.4 Missing Features
- ZIPT functionality not yet ported to Z3
- Incomplete ports where only part of the ZIPT logic was transferred
## Phase 4: Implement Improvements as Code Changes
For each improvement identified in Phase 3:
1. **Assess feasibility**: Only implement improvements that are:
- Self-contained (don't require large architectural changes)
- Verifiable (you can confirm correctness by reading the code)
- Safe (don't change public API signatures)
2. **Apply the change** using the edit tool to modify the Z3 source file
3. **Track each change**: Note the file, line range, and rationale
Focus on at most **5 concrete, high-value improvements** per run to keep changes focused and reviewable.
## Phase 5: Generate Git Diff
After applying all changes:
```bash
# Check what was modified
git status
# Generate a unified diff of all changes
git diff > /tmp/zipt-improvements.diff
# Read the diff
cat /tmp/zipt-improvements.diff
```
If no changes were made because no improvements were found or all were too risky, exit gracefully:
```
✅ ZIPT code review complete. No concrete improvements found in this run.
Files examined: [list files]
ZIPT files compared: [list files]
```
## Phase 6: Create GitHub Issue
If improvements were found and changes were applied, create a GitHub issue using the safe-outputs configuration.
Structure the issue body as follows:
```markdown
## ZIPT Code Review: Improvements from Reference Implementation
**Date**: [today's date]
**Files Reviewed**: [list of Z3 files examined]
**ZIPT Reference**: https://github.com/CEisenhofer/ZIPT/tree/parikh/ZIPT
### Summary
[2-3 sentence summary of what was found and changed]
### Improvements Applied
For each improvement:
#### Improvement N: [Short title]
**File**: `path/to/z3/file.cpp`
**Rationale**: [Why this improves the code, with reference to the ZIPT equivalent]
**ZIPT Reference**: [URL or file path of the corresponding ZIPT code]
### Git Diff
The following diff can be applied with `git apply`:
```diff
[FULL GIT DIFF OUTPUT HERE]
```
To apply:
```bash
git apply - << 'EOF'
[FULL GIT DIFF OUTPUT HERE]
EOF
```
### Testing
After applying this diff, build and test with:
```bash
mkdir -p build && cd build
cmake ..
make -j$(nproc)
make test-z3
./test-z3 euf_sgraph
./test-z3 euf_seq_plugin
```
---
*Generated by ZIPT Code Reviewer agent — comparing Z3 implementation with CEisenhofer/ZIPT@parikh*
```
## Important Guidelines
### Scope
- **Only** examine the files listed in Phase 1
- **Only** compare against the ZIPT reference at https://github.com/CEisenhofer/ZIPT/tree/parikh/ZIPT
- Do **not** modify test files
- Do **not** change public API signatures
### Quality Bar
- Every change must be demonstrably better than the current code
- Cite the specific ZIPT file and function for each improvement
- Prefer small, surgical changes over large refactors
### Exit Conditions
Exit without creating an issue if:
- ZIPT repository is unreachable
- No concrete, safe improvements can be identified
- All identified improvements require architectural changes beyond the scope of a single diff

1
.gitignore vendored
View file

@ -117,3 +117,4 @@ genaisrc/genblogpost.genai.mts
bazel-*
# Local issue tracking
.beads
build/

View file

@ -22,6 +22,7 @@ Author:
#include "ast/euf/euf_seq_plugin.h"
#include "ast/euf/euf_egraph.h"
#include "ast/euf/euf_sgraph.h"
#include "ast/ast_pp.h"
namespace euf {
@ -46,6 +47,9 @@ namespace euf {
}
unsigned enode_concat_hash::operator()(enode* n) const {
snode* sn = sg.find(n->get_expr());
if (sn && sn->has_cached_hash())
return sn->assoc_hash();
if (!is_any_concat(n, seq))
return n->get_id();
enode_vector leaves;
@ -71,15 +75,22 @@ namespace euf {
return true;
}
seq_plugin::seq_plugin(egraph& g):
seq_plugin::seq_plugin(egraph& g, sgraph* sg):
plugin(g),
m_seq(g.get_manager()),
m_rewriter(g.get_manager()),
m_concat_hash(m_seq),
m_sg(sg ? *sg : *alloc(sgraph, g.get_manager(), g, false)),
m_sg_owned(sg == nullptr),
m_concat_hash(m_seq, m_sg),
m_concat_eq(m_seq),
m_concat_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY, m_concat_hash, m_concat_eq) {
}
seq_plugin::~seq_plugin() {
if (m_sg_owned)
dealloc(&m_sg);
}
void seq_plugin::register_node(enode* n) {
m_queue.push_back(n);
push_undo(undo_kind::undo_add_concat);

View file

@ -47,14 +47,15 @@ Author:
namespace euf {
class egraph;
class sgraph;
// Associativity-respecting hash for enode concat trees.
// Flattens concat(concat(a,b),c) and concat(a,concat(b,c))
// to the same leaf sequence [a,b,c] before hashing.
// Uses cached snode hash matrices from the sgraph for O(1) hashing.
// Handles both str.++ (OP_SEQ_CONCAT) and re.++ (OP_RE_CONCAT).
struct enode_concat_hash {
seq_util const& seq;
enode_concat_hash(seq_util const& s) : seq(s) {}
sgraph& sg;
enode_concat_hash(seq_util const& s, sgraph& sg) : seq(s), sg(sg) {}
unsigned operator()(enode* n) const;
};
@ -75,6 +76,8 @@ namespace euf {
seq_util m_seq;
seq_rewriter m_rewriter;
sgraph& m_sg;
bool m_sg_owned = false; // whether we own the sgraph
svector<undo_kind> m_undo;
// queue of merges and registrations to process
@ -150,7 +153,8 @@ namespace euf {
bool same_loop_body(enode* a, enode* b, unsigned& lo1, unsigned& hi1, unsigned& lo2, unsigned& hi2);
public:
seq_plugin(egraph& g);
seq_plugin(egraph& g, sgraph* sg = nullptr);
~seq_plugin() override;
theory_id get_id() const override { return m_seq.get_family_id(); }

View file

@ -23,15 +23,35 @@ Author:
namespace euf {
sgraph::sgraph(ast_manager& m):
// substitution cache stored on snode for ZIPT-style optimization
struct snode_subst_cache {
struct entry {
unsigned var_id;
unsigned repl_id;
snode* result;
};
svector<entry> m_entries;
snode* find(unsigned var_id, unsigned repl_id) const {
for (auto const& e : m_entries)
if (e.var_id == var_id && e.repl_id == repl_id)
return e.result;
return nullptr;
}
void insert(unsigned var_id, unsigned repl_id, snode* result) {
m_entries.push_back({var_id, repl_id, result});
}
};
sgraph::sgraph(ast_manager& m, egraph& eg, bool add_plugin):
m(m),
m_seq(m),
m_rewriter(m),
m_egraph(m),
m_exprs(m),
m_str_sort(m_seq.str.mk_string_sort(), m) {
m_egraph(eg),
m_str_sort(m_seq.str.mk_string_sort(), m),
m_add_plugin(add_plugin) {
// create seq_plugin and register it with the egraph
m_egraph.add_plugin(alloc(seq_plugin, m_egraph));
if (add_plugin)
m_egraph.add_plugin(alloc(seq_plugin, m_egraph, this));
// register on_make callback so sgraph creates snodes for new enodes
std::function<void(enode*)> on_make = [this](enode* n) {
expr* e = n->get_expr();
@ -42,6 +62,8 @@ namespace euf {
}
sgraph::~sgraph() {
for (auto* c : m_subst_caches)
dealloc(c);
}
snode_kind sgraph::classify(expr* e) const {
@ -265,16 +287,54 @@ namespace euf {
}
}
static const unsigned HASH_BASE = 31;
// Compute a 2x2 polynomial hash matrix for associativity-respecting hashing.
// Unsigned overflow is intentional and well-defined (mod 2^32).
// M[0][0] tracks HASH_BASE^(num_leaves), which is always nonzero since
// HASH_BASE is odd. M[0][1] is the actual hash value.
void sgraph::compute_hash_matrix(snode* n) {
if (n->is_empty()) {
// identity matrix: concat with empty is identity
n->m_hash_matrix[0][0] = 1;
n->m_hash_matrix[0][1] = 0;
n->m_hash_matrix[1][0] = 0;
n->m_hash_matrix[1][1] = 1;
}
else if (n->is_concat()) {
snode* l = n->arg(0);
snode* r = n->arg(1);
if (l->has_cached_hash() && r->has_cached_hash()) {
// 2x2 matrix multiplication: M(L) * M(R)
n->m_hash_matrix[0][0] = l->m_hash_matrix[0][0] * r->m_hash_matrix[0][0] + l->m_hash_matrix[0][1] * r->m_hash_matrix[1][0];
n->m_hash_matrix[0][1] = l->m_hash_matrix[0][0] * r->m_hash_matrix[0][1] + l->m_hash_matrix[0][1] * r->m_hash_matrix[1][1];
n->m_hash_matrix[1][0] = l->m_hash_matrix[1][0] * r->m_hash_matrix[0][0] + l->m_hash_matrix[1][1] * r->m_hash_matrix[1][0];
n->m_hash_matrix[1][1] = l->m_hash_matrix[1][0] * r->m_hash_matrix[0][1] + l->m_hash_matrix[1][1] * r->m_hash_matrix[1][1];
}
}
else {
// leaf/token: [[HASH_BASE, value], [0, 1]]
// +1 avoids zero hash values; wraps safely on unsigned overflow
unsigned v = n->get_expr() ? n->get_expr()->get_id() + 1 : n->id() + 1;
n->m_hash_matrix[0][0] = HASH_BASE;
n->m_hash_matrix[0][1] = v;
n->m_hash_matrix[1][0] = 0;
n->m_hash_matrix[1][1] = 1;
}
}
snode* sgraph::mk_snode(expr* e, snode_kind k, unsigned num_args, snode* const* args) {
unsigned id = m_nodes.size();
snode* n = snode::mk(m_region, e, k, id, num_args, args);
compute_metadata(n);
compute_hash_matrix(n);
m_nodes.push_back(n);
m_exprs.push_back(e);
if (e) {
unsigned eid = e->get_id();
m_expr2snode.reserve(eid + 1, nullptr);
m_expr2snode[eid] = n;
// pin expression via egraph (the egraph has an expr trail)
mk_enode(e);
}
++m_stats.m_num_nodes;
return n;
@ -350,7 +410,6 @@ namespace euf {
}
}
m_nodes.shrink(old_sz);
m_exprs.shrink(old_sz);
m_scopes.shrink(new_lvl);
m_num_scopes = new_lvl;
m_egraph.pop(num_scopes);
@ -418,9 +477,23 @@ namespace euf {
return replacement;
if (n->is_empty() || n->is_char())
return n;
if (n->is_concat())
return mk_concat(subst(n->arg(0), var, replacement),
subst(n->arg(1), var, replacement));
if (n->is_concat()) {
// check substitution cache (ZIPT-style optimization)
if (n->m_subst_cache) {
snode* cached = n->m_subst_cache->find(var->id(), replacement->id());
if (cached)
return cached;
}
snode* result = mk_concat(subst(n->arg(0), var, replacement),
subst(n->arg(1), var, replacement));
// cache the result
if (!n->m_subst_cache) {
n->m_subst_cache = alloc(snode_subst_cache);
m_subst_caches.push_back(n->m_subst_cache);
}
n->m_subst_cache->insert(var->id(), replacement->id(), result);
return result;
}
// for non-concat compound nodes (power, star, etc.), no substitution into children
return n;
}

View file

@ -10,7 +10,7 @@ Abstract:
Sequence/string graph layer
Encapsulates string and regex expressions for the string solver.
Implements the string graph layer from ZIPT (https://github.com/CEisenhofer/ZIPT).
Implements the string graph layer from ZIPT (https://github.com/CEisenhofer/ZIPT/tree/parikh/ZIPT).
The sgraph maps Z3 sequence/regex AST expressions to snode structures
organized as binary concatenation trees with metadata, and owns an
egraph with a seq_plugin for congruence closure.
@ -82,14 +82,17 @@ namespace euf {
ast_manager& m;
seq_util m_seq;
seq_rewriter m_rewriter;
egraph m_egraph;
egraph& m_egraph;
region m_region;
snode_vector m_nodes;
expr_ref_vector m_exprs; // pin expressions
sort_ref m_str_sort; // cached string sort
unsigned_vector m_scopes;
unsigned m_num_scopes = 0;
stats m_stats;
bool m_add_plugin; // whether sgraph created the seq_plugin
// tracks allocated subst caches for cleanup
ptr_vector<snode_subst_cache> m_subst_caches;
// maps expression id to snode
ptr_vector<snode> m_expr2snode;
@ -97,10 +100,11 @@ namespace euf {
snode* mk_snode(expr* e, snode_kind k, unsigned num_args, snode* const* args);
snode_kind classify(expr* e) const;
void compute_metadata(snode* n);
void compute_hash_matrix(snode* n);
void collect_re_predicates(snode* re, expr_ref_vector& preds);
public:
sgraph(ast_manager& m);
sgraph(ast_manager& m, egraph& eg, bool add_plugin = true);
~sgraph();
ast_manager& get_manager() const { return m; }

View file

@ -32,6 +32,7 @@ namespace euf {
class sgraph;
class snode;
struct snode_subst_cache;
typedef ptr_vector<snode> snode_vector;
@ -68,6 +69,13 @@ namespace euf {
unsigned m_level = 0; // tree depth/level (0 for empty, 1 for singletons)
unsigned m_length = 0; // token count, number of leaf tokens in the tree
// hash matrix for associativity-respecting hashing (2x2 polynomial hash matrix)
// all zeros means not cached, non-zero means cached
unsigned m_hash_matrix[2][2] = {{0,0},{0,0}};
// substitution cache (lazy-initialized, owned by sgraph)
snode_subst_cache* m_subst_cache = nullptr;
snode* m_args[0]; // variable-length array, allocated via get_snode_size(num_args)
friend class sgraph;
@ -101,6 +109,12 @@ namespace euf {
unsigned level() const { return m_level; }
unsigned length() const { return m_length; }
// associativity-respecting hash: cached if the 2x2 matrix is non-zero.
// M[0][0] = HASH_BASE^(num_leaves) which is always nonzero since HASH_BASE
// is odd and gcd(odd, 2^32) = 1, so the check is safe.
bool has_cached_hash() const { return m_hash_matrix[0][0] != 0; }
unsigned assoc_hash() const { return m_hash_matrix[0][1]; }
bool is_empty() const { return m_kind == snode_kind::s_empty; }
bool is_char() const { return m_kind == snode_kind::s_char; }
bool is_var() const { return m_kind == snode_kind::s_var; }

View file

@ -31,7 +31,8 @@ static void test_sgraph_basic() {
std::cout << "test_sgraph_basic\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
seq_util seq(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);
@ -72,7 +73,8 @@ static void test_sgraph_backtrack() {
std::cout << "test_sgraph_backtrack\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
seq_util seq(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);
@ -99,7 +101,8 @@ static void test_seq_plugin_assoc() {
std::cout << "test_seq_plugin_assoc\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
euf::egraph& g = sg.get_egraph();
seq_util seq(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);
@ -125,7 +128,8 @@ static void test_seq_plugin_empty() {
std::cout << "test_seq_plugin_empty\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
euf::egraph& g = sg.get_egraph();
seq_util seq(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);
@ -150,7 +154,8 @@ static void test_seq_plugin_star_merge() {
std::cout << "test_seq_plugin_star_merge\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
euf::egraph& g = sg.get_egraph();
seq_util seq(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);
@ -178,7 +183,8 @@ static void test_seq_plugin_nullable_absorb() {
std::cout << "test_seq_plugin_nullable_absorb\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
euf::egraph& g = sg.get_egraph();
seq_util seq(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);
@ -202,7 +208,8 @@ static void test_sgraph_egraph_sync() {
std::cout << "test_sgraph_egraph_sync\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
euf::egraph& g = sg.get_egraph();
seq_util seq(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);

View file

@ -27,7 +27,8 @@ static void test_sgraph_classify() {
std::cout << "test_sgraph_classify\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
seq_util seq(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);
@ -85,7 +86,8 @@ static void test_sgraph_regex() {
std::cout << "test_sgraph_regex\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
seq_util seq(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);
@ -160,7 +162,8 @@ static void test_sgraph_power() {
std::cout << "test_sgraph_power\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
seq_util seq(m);
arith_util arith(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);
@ -185,7 +188,8 @@ static void test_sgraph_push_pop() {
std::cout << "test_sgraph_push_pop\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
seq_util seq(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);
@ -221,7 +225,8 @@ static void test_sgraph_nested_scopes() {
std::cout << "test_sgraph_nested_scopes\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
seq_util seq(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);
@ -260,7 +265,8 @@ static void test_sgraph_find_idempotent() {
std::cout << "test_sgraph_find_idempotent\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
seq_util seq(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);
@ -276,7 +282,8 @@ static void test_sgraph_mk_concat() {
std::cout << "test_sgraph_mk_concat\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
seq_util seq(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);
@ -310,7 +317,8 @@ static void test_sgraph_mk_power() {
std::cout << "test_sgraph_mk_power\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
seq_util seq(m);
arith_util arith(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);
@ -335,7 +343,8 @@ static void test_sgraph_first_last() {
std::cout << "test_sgraph_first_last\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
seq_util seq(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);
@ -372,7 +381,8 @@ static void test_sgraph_concat_metadata() {
std::cout << "test_sgraph_concat_metadata\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
seq_util seq(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);
@ -414,7 +424,8 @@ static void test_sgraph_display() {
std::cout << "test_sgraph_display\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
seq_util seq(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);
@ -436,7 +447,8 @@ static void test_sgraph_factory() {
std::cout << "test_sgraph_factory\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
// mk_var
euf::snode* x = sg.mk_var(symbol("x"));
@ -485,7 +497,8 @@ static void test_sgraph_indexing() {
std::cout << "test_sgraph_indexing\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
euf::snode* a = sg.mk_char('A');
euf::snode* b = sg.mk_char('B');
@ -532,7 +545,8 @@ static void test_sgraph_drop() {
std::cout << "test_sgraph_drop\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
euf::snode* a = sg.mk_char('A');
euf::snode* b = sg.mk_char('B');
@ -586,7 +600,8 @@ static void test_sgraph_subst() {
std::cout << "test_sgraph_subst\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
euf::snode* x = sg.mk_var(symbol("x"));
euf::snode* y = sg.mk_var(symbol("y"));
@ -620,7 +635,8 @@ static void test_sgraph_complex_concat() {
std::cout << "test_sgraph_complex_concat\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
// build a string "HELLO" = concat(H, concat(E, concat(L, concat(L, O))))
euf::snode* h = sg.mk_char('H');
@ -678,7 +694,8 @@ static void test_sgraph_brzozowski() {
std::cout << "test_sgraph_brzozowski\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
seq_util seq(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);
@ -713,7 +730,8 @@ static void test_sgraph_minterms() {
std::cout << "test_sgraph_minterms\n";
ast_manager m;
reg_decl_plugins(m);
euf::sgraph sg(m);
euf::egraph eg(m);
euf::sgraph sg(m, eg);
seq_util seq(m);
sort_ref str_sort(seq.str.mk_string_sort(), m);