atsds-egg 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +143 -0
- package/dist/index.d.mts +116 -0
- package/dist/index.mjs +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# E-Graph Support Package for DS
|
|
2
|
+
|
|
3
|
+
An E-Graph (Equality Graph) implementation for the DS deductive system, providing efficient management and manipulation of equivalence classes of terms.
|
|
4
|
+
|
|
5
|
+
This package implements the egg-style E-Graph data structure with deferred congruence closure, enabling efficient equality reasoning. Inspired by the [egg library](https://egraphs-good.github.io/).
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **E-Graph Data Structure**: Manage equivalence classes of terms efficiently
|
|
10
|
+
- **Union-Find**: Path-compressed union-find for disjoint set management
|
|
11
|
+
- **Congruence Closure**: Automatic maintenance of congruence relationships
|
|
12
|
+
- **Deferred Rebuilding**: egg-style deferred rebuilding for performance
|
|
13
|
+
- **Python Integration**: Seamless integration with apyds terms
|
|
14
|
+
- **Type-Safe**: Full type hints for Python 3.11+
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
### Python (pip)
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install apyds-egg
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Requires Python 3.11-3.14.
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### Python Example
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import apyds
|
|
32
|
+
from apyds_egg import EGraph
|
|
33
|
+
|
|
34
|
+
# Create an E-Graph
|
|
35
|
+
eg = EGraph()
|
|
36
|
+
|
|
37
|
+
# Add terms to the E-Graph
|
|
38
|
+
a = eg.add(apyds.Term("a"))
|
|
39
|
+
b = eg.add(apyds.Term("b"))
|
|
40
|
+
x = eg.add(apyds.Term("x"))
|
|
41
|
+
|
|
42
|
+
# Add compound terms
|
|
43
|
+
ax = eg.add(apyds.Term("(+ a x)"))
|
|
44
|
+
bx = eg.add(apyds.Term("(+ b x)"))
|
|
45
|
+
|
|
46
|
+
# Initially, (+ a x) and (+ b x) are in different E-classes
|
|
47
|
+
assert eg.find(ax) != eg.find(bx)
|
|
48
|
+
|
|
49
|
+
# Merge a and b
|
|
50
|
+
eg.merge(a, b)
|
|
51
|
+
|
|
52
|
+
# Rebuild to restore congruence
|
|
53
|
+
eg.rebuild()
|
|
54
|
+
|
|
55
|
+
# Now (+ a x) and (+ b x) are in the same E-class
|
|
56
|
+
assert eg.find(ax) == eg.find(bx)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Core Concepts
|
|
60
|
+
|
|
61
|
+
### E-Graph
|
|
62
|
+
|
|
63
|
+
An E-Graph is a data structure that efficiently represents and maintains equivalence classes of terms. It consists of:
|
|
64
|
+
|
|
65
|
+
- **E-Nodes**: Nodes representing terms with an operator and children
|
|
66
|
+
- **E-classes**: Equivalence classes of E-Nodes
|
|
67
|
+
- **Union-Find**: Data structure for managing E-class equivalence
|
|
68
|
+
- **Congruence**: Two terms are congruent if they have the same operator and their children are in equivalent E-classes
|
|
69
|
+
|
|
70
|
+
### Congruence Closure
|
|
71
|
+
|
|
72
|
+
The E-Graph maintains congruence closure automatically. When two E-classes are merged, the E-Graph rebuilds to ensure that congruent terms remain in the same E-class:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
eg = EGraph()
|
|
76
|
+
|
|
77
|
+
# Add terms
|
|
78
|
+
fa = eg.add(apyds.Term("(f a)"))
|
|
79
|
+
fb = eg.add(apyds.Term("(f b)"))
|
|
80
|
+
|
|
81
|
+
# Merge a and b
|
|
82
|
+
a = eg.add(apyds.Term("a"))
|
|
83
|
+
b = eg.add(apyds.Term("b"))
|
|
84
|
+
eg.merge(a, b)
|
|
85
|
+
|
|
86
|
+
# Rebuild maintains congruence
|
|
87
|
+
eg.rebuild()
|
|
88
|
+
|
|
89
|
+
# Now (f a) and (f b) are equivalent
|
|
90
|
+
assert eg.find(fa) == eg.find(fb)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## API Overview
|
|
94
|
+
|
|
95
|
+
### EGraph
|
|
96
|
+
|
|
97
|
+
- `__init__()`: Create a new E-Graph
|
|
98
|
+
- `add(term: apyds.Term) -> EClassId`: Add a term to the E-Graph
|
|
99
|
+
- `merge(a: EClassId, b: EClassId) -> EClassId`: Merge two E-classes
|
|
100
|
+
- `rebuild() -> None`: Restore congruence closure
|
|
101
|
+
- `find(eclass: EClassId) -> EClassId`: Find canonical E-class representative
|
|
102
|
+
|
|
103
|
+
## Building from Source
|
|
104
|
+
|
|
105
|
+
### Prerequisites
|
|
106
|
+
|
|
107
|
+
- Python 3.11-3.14
|
|
108
|
+
- apyds package
|
|
109
|
+
|
|
110
|
+
### Python Package
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
cd egg
|
|
114
|
+
|
|
115
|
+
# Install dependencies
|
|
116
|
+
uv sync --extra dev
|
|
117
|
+
|
|
118
|
+
# Build package
|
|
119
|
+
uv build
|
|
120
|
+
|
|
121
|
+
# Run tests
|
|
122
|
+
uv run pytest
|
|
123
|
+
|
|
124
|
+
# Run with coverage
|
|
125
|
+
uv run pytest --cov
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
This project is licensed under the GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later).
|
|
131
|
+
|
|
132
|
+
## Repository
|
|
133
|
+
|
|
134
|
+
- **GitHub**: [USTC-KnowledgeComputingLab/ds](https://github.com/USTC-KnowledgeComputingLab/ds) (in `/egg` directory)
|
|
135
|
+
- **Python Package**: [apyds-egg](https://pypi.org/project/apyds-egg/)
|
|
136
|
+
|
|
137
|
+
## Author
|
|
138
|
+
|
|
139
|
+
Hao Zhang <hzhangxyz@outlook.com>
|
|
140
|
+
|
|
141
|
+
## Related
|
|
142
|
+
|
|
143
|
+
This package is a support library for the [DS (Deductive System)](https://github.com/USTC-KnowledgeComputingLab/ds) project. For the main DS library with C++ core and bindings, see the [main repository](https://github.com/USTC-KnowledgeComputingLab/ds).
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { Term } from 'atsds';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* E-Graph implementation for atsds (TypeScript version)
|
|
5
|
+
* E-Graph (Equality Graph) for representing equivalence classes of terms.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
type EClassId = number & {
|
|
9
|
+
__brand: "EClassId";
|
|
10
|
+
};
|
|
11
|
+
declare class DefaultMap<K, V> extends Map<K, V> {
|
|
12
|
+
private readonly factory;
|
|
13
|
+
constructor(factory: () => V);
|
|
14
|
+
get(key: K): V;
|
|
15
|
+
}
|
|
16
|
+
declare class UnionFind<T> {
|
|
17
|
+
/**
|
|
18
|
+
* Union-find data structure for managing disjoint sets.
|
|
19
|
+
*/
|
|
20
|
+
parent: Map<T, T>;
|
|
21
|
+
constructor();
|
|
22
|
+
/**
|
|
23
|
+
* Find the canonical representative of x's set with path compression.
|
|
24
|
+
* @param x - The element to find.
|
|
25
|
+
* @returns The canonical representative of x's set.
|
|
26
|
+
*/
|
|
27
|
+
find(x: T): T;
|
|
28
|
+
/**
|
|
29
|
+
* Union two sets and return the canonical representative.
|
|
30
|
+
* @param a - The first element.
|
|
31
|
+
* @param b - The second element.
|
|
32
|
+
* @returns The canonical representative of the merged set.
|
|
33
|
+
*/
|
|
34
|
+
union(a: T, b: T): T;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* ENode - Node in the E-Graph with an operator and children.
|
|
38
|
+
*/
|
|
39
|
+
declare class ENode {
|
|
40
|
+
op: string;
|
|
41
|
+
children: EClassId[];
|
|
42
|
+
/**
|
|
43
|
+
* @param op - The operator
|
|
44
|
+
* @param children - The children E-class IDs
|
|
45
|
+
*/
|
|
46
|
+
constructor(op: string, children: EClassId[]);
|
|
47
|
+
/**
|
|
48
|
+
* Canonicalize children using the find function.
|
|
49
|
+
* @param find - Function to find the canonical E-class ID.
|
|
50
|
+
* @returns A new ENode with canonicalized children.
|
|
51
|
+
*/
|
|
52
|
+
canonicalize(find: (id: EClassId) => EClassId): ENode;
|
|
53
|
+
/**
|
|
54
|
+
* Get a string key representation for this ENode for use in Maps/Objects.
|
|
55
|
+
* @returns A unique string representation of this ENode.
|
|
56
|
+
*/
|
|
57
|
+
key(): string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* EGraph - E-Graph for representing equivalence classes of terms.
|
|
61
|
+
*/
|
|
62
|
+
declare class EGraph {
|
|
63
|
+
private next_id;
|
|
64
|
+
private hashcons;
|
|
65
|
+
private unionfind;
|
|
66
|
+
private classes;
|
|
67
|
+
private parents;
|
|
68
|
+
private worklist;
|
|
69
|
+
/**
|
|
70
|
+
* Generate a fresh E-class ID.
|
|
71
|
+
* @returns A new E-class ID.
|
|
72
|
+
*/
|
|
73
|
+
private freshId;
|
|
74
|
+
/**
|
|
75
|
+
* Find the canonical representative of an E-class.
|
|
76
|
+
* @param eclass - The E-class ID to find.
|
|
77
|
+
* @returns The canonical E-class ID.
|
|
78
|
+
*/
|
|
79
|
+
find(eclass: EClassId): EClassId;
|
|
80
|
+
/**
|
|
81
|
+
* Add a term to the E-Graph and return its E-class ID.
|
|
82
|
+
* @param term - An atsds Term to add to the E-Graph.
|
|
83
|
+
* @returns The E-class ID for the added term.
|
|
84
|
+
*/
|
|
85
|
+
add(term: Term): EClassId;
|
|
86
|
+
/**
|
|
87
|
+
* Convert an atsds Term to an ENode.
|
|
88
|
+
* @param term - The Term to convert.
|
|
89
|
+
* @returns The converted ENode.
|
|
90
|
+
*/
|
|
91
|
+
private termToEnode;
|
|
92
|
+
/**
|
|
93
|
+
* Add an ENode to the E-Graph.
|
|
94
|
+
* @param enode - The ENode to add.
|
|
95
|
+
* @returns The E-class ID for the added ENode.
|
|
96
|
+
*/
|
|
97
|
+
private addEnode;
|
|
98
|
+
/**
|
|
99
|
+
* Merge two E-classes and defer congruence restoration.
|
|
100
|
+
* @param a - The first E-class ID to merge.
|
|
101
|
+
* @param b - The second E-class ID to merge.
|
|
102
|
+
* @returns The canonical E-class ID of the merged class.
|
|
103
|
+
*/
|
|
104
|
+
merge(a: EClassId, b: EClassId): EClassId;
|
|
105
|
+
/**
|
|
106
|
+
* Restore congruence by processing the worklist.
|
|
107
|
+
*/
|
|
108
|
+
rebuild(): void;
|
|
109
|
+
/**
|
|
110
|
+
* Restore congruence for a single E-class.
|
|
111
|
+
*/
|
|
112
|
+
private repair;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export { DefaultMap, EGraph, ENode, UnionFind };
|
|
116
|
+
export type { EClassId };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{List as t}from"atsds";class s extends Map{factory;constructor(t){super(),this.factory=t}get(t){return this.has(t)||this.set(t,this.factory()),super.get(t)}}class e{parent;constructor(){this.parent=new Map}find(t){return this.parent.has(t)||this.parent.set(t,t),this.parent.get(t)!==t&&this.parent.set(t,this.find(this.parent.get(t))),this.parent.get(t)}union(t,s){const e=this.find(t),n=this.find(s);return e!==n&&this.parent.set(n,e),e}}class n{op;children;constructor(t,s){this.op=t,this.children=s}canonicalize(t){return new n(this.op,this.children.map(s=>t(s)))}key(){return`${this.op}(${this.children.join(",")})`}}class i{next_id=0;hashcons=new Map;unionfind=new e;classes=new s(()=>new Set);parents=new s(()=>new Set);worklist=new Set;freshId(){const t=this.next_id;return this.next_id+=1,t}find(t){return this.unionfind.find(t)}add(t){const s=this.termToEnode(t);return this.addEnode(s)}termToEnode(s){const e=s.term();if(e instanceof t){const t=[];for(let s=0;s<e.length();s++){const n=e.getitem(s),i=this.add(n);t.push(i)}return new n("()",t)}return new n(s.toString(),[])}addEnode(t){const s=(t=t.canonicalize(this.find.bind(this))).key(),e=this.hashcons.get(s);if(e)return this.find(e[1]);const n=this.freshId();this.hashcons.set(s,[t,n]),this.unionfind.parent.set(n,n),this.classes.get(n).add(t);for(const s of t.children)this.parents.get(s).add([t,n]);return n}merge(t,s){const e=this.find(t),n=this.find(s);if(e===n)return e;const i=this.unionfind.union(e,n);for(const t of this.classes.get(n))this.classes.get(i).add(t);this.classes.delete(n);for(const t of this.parents.get(n))this.parents.get(i).add(t);return this.parents.delete(n),this.worklist.add(i),i}rebuild(){for(;this.worklist.size>0;){const t=new Set(Array.from(this.worklist).map(t=>this.find(t)));this.worklist.clear();for(const s of t)this.repair(s)}}repair(t){const s=new Map;for(const[e,n]of this.parents.get(t)){this.hashcons.delete(e.key());const t=e.canonicalize(this.find.bind(this)),i=t.key(),r=this.find(n);s.has(i)?this.merge(r,s.get(i)[1]):(s.set(i,[t,r]),this.hashcons.set(i,[t,r]))}this.parents.get(t).clear();for(const[e,n]of s)this.parents.get(t).add(n)}}export{s as DefaultMap,i as EGraph,n as ENode,e as UnionFind};
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "atsds-egg",
|
|
3
|
+
"description": "E-Graph implementation for atsds",
|
|
4
|
+
"author": "Hao Zhang <hzhangxyz@outlook.com>",
|
|
5
|
+
"license": "AGPL-3.0-or-later",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/USTC-KnowledgeComputingLab/ds.git",
|
|
9
|
+
"directory": "egg"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"exports": "./dist/index.mjs",
|
|
13
|
+
"main": "dist/index.mjs",
|
|
14
|
+
"module": "dist/index.mjs",
|
|
15
|
+
"browser": "dist/index.mjs",
|
|
16
|
+
"types": "dist/index.d.mts",
|
|
17
|
+
"files": [
|
|
18
|
+
"dist/index.d.mts",
|
|
19
|
+
"dist/index.mjs"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "rollup --config rollup.config.mjs",
|
|
23
|
+
"test": "cross-env NODE_OPTIONS='$NODE_OPTIONS --experimental-vm-modules' jest --config=jest.config.mjs",
|
|
24
|
+
"all": "run-s build test"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"atsds": "^0.0.11"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
31
|
+
"@rollup/plugin-typescript": "^12.3.0",
|
|
32
|
+
"@types/jest": "^30.0.0",
|
|
33
|
+
"cross-env": "^10.1.0",
|
|
34
|
+
"jest": "^30.2.0",
|
|
35
|
+
"npm-run-all": "^4.1.5",
|
|
36
|
+
"rollup": "^4.54.0",
|
|
37
|
+
"rollup-plugin-dts": "^6.3.0",
|
|
38
|
+
"ts-jest": "^29.4.6",
|
|
39
|
+
"tslib": "^2.8.1",
|
|
40
|
+
"typescript": "^5.9.3"
|
|
41
|
+
},
|
|
42
|
+
"version": "0.0.11"
|
|
43
|
+
}
|