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 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).
@@ -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
+ }