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:
commit
fedb610da1
10 changed files with 1569 additions and 45 deletions
1139
.github/workflows/zipt-code-reviewer.lock.yml
generated
vendored
Normal file
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
253
.github/workflows/zipt-code-reviewer.md
vendored
Normal 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
1
.gitignore
vendored
|
|
@ -117,3 +117,4 @@ genaisrc/genblogpost.genai.mts
|
|||
bazel-*
|
||||
# Local issue tracking
|
||||
.beads
|
||||
build/
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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(); }
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue