@graphty/algorithms 1.0.1
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/LICENSE +21 -0
- package/README.md +193 -0
- package/dist/src/algorithms/centrality/betweenness.d.ts +34 -0
- package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/betweenness.js +223 -0
- package/dist/src/algorithms/centrality/betweenness.js.map +1 -0
- package/dist/src/algorithms/centrality/closeness.d.ts +43 -0
- package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/closeness.js +202 -0
- package/dist/src/algorithms/centrality/closeness.js.map +1 -0
- package/dist/src/algorithms/centrality/degree.d.ts +17 -0
- package/dist/src/algorithms/centrality/degree.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/degree.js +86 -0
- package/dist/src/algorithms/centrality/degree.js.map +1 -0
- package/dist/src/algorithms/centrality/index.d.ts +11 -0
- package/dist/src/algorithms/centrality/index.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/index.js +8 -0
- package/dist/src/algorithms/centrality/index.js.map +1 -0
- package/dist/src/algorithms/centrality/pagerank.d.ts +74 -0
- package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/pagerank.js +191 -0
- package/dist/src/algorithms/centrality/pagerank.js.map +1 -0
- package/dist/src/algorithms/components/connected.d.ts +58 -0
- package/dist/src/algorithms/components/connected.d.ts.map +1 -0
- package/dist/src/algorithms/components/connected.js +234 -0
- package/dist/src/algorithms/components/connected.js.map +1 -0
- package/dist/src/algorithms/components/index.d.ts +5 -0
- package/dist/src/algorithms/components/index.d.ts.map +1 -0
- package/dist/src/algorithms/components/index.js +5 -0
- package/dist/src/algorithms/components/index.js.map +1 -0
- package/dist/src/algorithms/index.d.ts +8 -0
- package/dist/src/algorithms/index.d.ts.map +1 -0
- package/dist/src/algorithms/index.js +12 -0
- package/dist/src/algorithms/index.js.map +1 -0
- package/dist/src/algorithms/shortest-path/bellman-ford.d.ts +52 -0
- package/dist/src/algorithms/shortest-path/bellman-ford.d.ts.map +1 -0
- package/dist/src/algorithms/shortest-path/bellman-ford.js +131 -0
- package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -0
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts +26 -0
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -0
- package/dist/src/algorithms/shortest-path/dijkstra.js +199 -0
- package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -0
- package/dist/src/algorithms/shortest-path/index.d.ts +7 -0
- package/dist/src/algorithms/shortest-path/index.d.ts.map +1 -0
- package/dist/src/algorithms/shortest-path/index.js +6 -0
- package/dist/src/algorithms/shortest-path/index.js.map +1 -0
- package/dist/src/algorithms/traversal/bfs.d.ts +25 -0
- package/dist/src/algorithms/traversal/bfs.d.ts.map +1 -0
- package/dist/src/algorithms/traversal/bfs.js +189 -0
- package/dist/src/algorithms/traversal/bfs.js.map +1 -0
- package/dist/src/algorithms/traversal/dfs.d.ts +32 -0
- package/dist/src/algorithms/traversal/dfs.d.ts.map +1 -0
- package/dist/src/algorithms/traversal/dfs.js +259 -0
- package/dist/src/algorithms/traversal/dfs.js.map +1 -0
- package/dist/src/algorithms/traversal/index.d.ts +7 -0
- package/dist/src/algorithms/traversal/index.d.ts.map +1 -0
- package/dist/src/algorithms/traversal/index.js +6 -0
- package/dist/src/algorithms/traversal/index.js.map +1 -0
- package/dist/src/core/graph.d.ts +100 -0
- package/dist/src/core/graph.d.ts.map +1 -0
- package/dist/src/core/graph.js +276 -0
- package/dist/src/core/graph.js.map +1 -0
- package/dist/src/data-structures/index.d.ts +6 -0
- package/dist/src/data-structures/index.d.ts.map +1 -0
- package/dist/src/data-structures/index.js +6 -0
- package/dist/src/data-structures/index.js.map +1 -0
- package/dist/src/data-structures/priority-queue.d.ts +60 -0
- package/dist/src/data-structures/priority-queue.d.ts.map +1 -0
- package/dist/src/data-structures/priority-queue.js +152 -0
- package/dist/src/data-structures/priority-queue.js.map +1 -0
- package/dist/src/data-structures/union-find.d.ts +54 -0
- package/dist/src/data-structures/union-find.d.ts.map +1 -0
- package/dist/src/data-structures/union-find.js +145 -0
- package/dist/src/data-structures/union-find.js.map +1 -0
- package/dist/src/index.d.ts +11 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +13 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/types/index.d.ts +74 -0
- package/dist/src/types/index.d.ts.map +1 -0
- package/dist/src/types/index.js +5 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/test/browser/basic.test.d.ts +2 -0
- package/dist/test/browser/basic.test.d.ts.map +1 -0
- package/dist/test/browser/basic.test.js +70 -0
- package/dist/test/browser/basic.test.js.map +1 -0
- package/dist/test/unit/bellman-ford.test.d.ts +2 -0
- package/dist/test/unit/bellman-ford.test.d.ts.map +1 -0
- package/dist/test/unit/bellman-ford.test.js +231 -0
- package/dist/test/unit/bellman-ford.test.js.map +1 -0
- package/dist/test/unit/betweenness-centrality.test.d.ts +2 -0
- package/dist/test/unit/betweenness-centrality.test.d.ts.map +1 -0
- package/dist/test/unit/betweenness-centrality.test.js +311 -0
- package/dist/test/unit/betweenness-centrality.test.js.map +1 -0
- package/dist/test/unit/bfs.test.d.ts +2 -0
- package/dist/test/unit/bfs.test.d.ts.map +1 -0
- package/dist/test/unit/bfs.test.js +277 -0
- package/dist/test/unit/bfs.test.js.map +1 -0
- package/dist/test/unit/closeness.test.d.ts +2 -0
- package/dist/test/unit/closeness.test.d.ts.map +1 -0
- package/dist/test/unit/closeness.test.js +266 -0
- package/dist/test/unit/closeness.test.js.map +1 -0
- package/dist/test/unit/connected-components.test.d.ts +2 -0
- package/dist/test/unit/connected-components.test.d.ts.map +1 -0
- package/dist/test/unit/connected-components.test.js +355 -0
- package/dist/test/unit/connected-components.test.js.map +1 -0
- package/dist/test/unit/degree-centrality.test.d.ts +2 -0
- package/dist/test/unit/degree-centrality.test.d.ts.map +1 -0
- package/dist/test/unit/degree-centrality.test.js +260 -0
- package/dist/test/unit/degree-centrality.test.js.map +1 -0
- package/dist/test/unit/dfs.test.d.ts +2 -0
- package/dist/test/unit/dfs.test.d.ts.map +1 -0
- package/dist/test/unit/dfs.test.js +350 -0
- package/dist/test/unit/dfs.test.js.map +1 -0
- package/dist/test/unit/dijkstra.test.d.ts +2 -0
- package/dist/test/unit/dijkstra.test.d.ts.map +1 -0
- package/dist/test/unit/dijkstra.test.js +311 -0
- package/dist/test/unit/dijkstra.test.js.map +1 -0
- package/dist/test/unit/graph.test.d.ts +2 -0
- package/dist/test/unit/graph.test.d.ts.map +1 -0
- package/dist/test/unit/graph.test.js +175 -0
- package/dist/test/unit/graph.test.js.map +1 -0
- package/dist/test/unit/pagerank.test.d.ts +2 -0
- package/dist/test/unit/pagerank.test.d.ts.map +1 -0
- package/dist/test/unit/pagerank.test.js +281 -0
- package/dist/test/unit/pagerank.test.js.map +1 -0
- package/dist/test/unit/priority-queue.test.d.ts +2 -0
- package/dist/test/unit/priority-queue.test.d.ts.map +1 -0
- package/dist/test/unit/priority-queue.test.js +189 -0
- package/dist/test/unit/priority-queue.test.js.map +1 -0
- package/dist/test/unit/union-find.test.d.ts +2 -0
- package/dist/test/unit/union-find.test.d.ts.map +1 -0
- package/dist/test/unit/union-find.test.js +245 -0
- package/dist/test/unit/union-find.test.js.map +1 -0
- package/package.json +100 -0
- package/src/algorithms/centrality/betweenness.ts +303 -0
- package/src/algorithms/centrality/closeness.ts +296 -0
- package/src/algorithms/centrality/degree.ts +105 -0
- package/src/algorithms/centrality/index.ts +11 -0
- package/src/algorithms/centrality/pagerank.ts +302 -0
- package/src/algorithms/components/connected.ts +298 -0
- package/src/algorithms/components/index.ts +17 -0
- package/src/algorithms/index.ts +15 -0
- package/src/algorithms/shortest-path/bellman-ford.ts +214 -0
- package/src/algorithms/shortest-path/dijkstra.ts +256 -0
- package/src/algorithms/shortest-path/index.ts +7 -0
- package/src/algorithms/traversal/bfs.ts +236 -0
- package/src/algorithms/traversal/dfs.ts +367 -0
- package/src/algorithms/traversal/index.ts +7 -0
- package/src/core/graph.ts +326 -0
- package/src/data-structures/index.ts +6 -0
- package/src/data-structures/priority-queue.ts +180 -0
- package/src/data-structures/union-find.ts +170 -0
- package/src/index.ts +35 -0
- package/src/types/index.ts +95 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { UnionFind } from "../../src/data-structures/union-find.js";
|
|
3
|
+
describe("UnionFind", () => {
|
|
4
|
+
describe("constructor", () => {
|
|
5
|
+
it("should initialize with separate components", () => {
|
|
6
|
+
const uf = new UnionFind(["a", "b", "c"]);
|
|
7
|
+
expect(uf.size()).toBe(3);
|
|
8
|
+
expect(uf.getComponentCount()).toBe(3);
|
|
9
|
+
expect(uf.connected("a", "b")).toBe(false);
|
|
10
|
+
expect(uf.connected("b", "c")).toBe(false);
|
|
11
|
+
expect(uf.connected("a", "c")).toBe(false);
|
|
12
|
+
});
|
|
13
|
+
it("should handle empty initialization", () => {
|
|
14
|
+
const uf = new UnionFind([]);
|
|
15
|
+
expect(uf.size()).toBe(0);
|
|
16
|
+
expect(uf.getComponentCount()).toBe(0);
|
|
17
|
+
});
|
|
18
|
+
it("should handle single element", () => {
|
|
19
|
+
const uf = new UnionFind(["only"]);
|
|
20
|
+
expect(uf.size()).toBe(1);
|
|
21
|
+
expect(uf.getComponentCount()).toBe(1);
|
|
22
|
+
expect(uf.connected("only", "only")).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
describe("find", () => {
|
|
26
|
+
it("should return the element itself initially", () => {
|
|
27
|
+
const uf = new UnionFind(["a", "b", "c"]);
|
|
28
|
+
expect(uf.find("a")).toBe("a");
|
|
29
|
+
expect(uf.find("b")).toBe("b");
|
|
30
|
+
expect(uf.find("c")).toBe("c");
|
|
31
|
+
});
|
|
32
|
+
it("should throw error for non-existent element", () => {
|
|
33
|
+
const uf = new UnionFind(["a", "b"]);
|
|
34
|
+
expect(() => uf.find("nonexistent")).toThrow("Element nonexistent not found");
|
|
35
|
+
});
|
|
36
|
+
it("should implement path compression", () => {
|
|
37
|
+
const uf = new UnionFind(["a", "b", "c", "d"]);
|
|
38
|
+
// Create a chain: a -> b -> c -> d
|
|
39
|
+
uf.union("a", "b");
|
|
40
|
+
uf.union("b", "c");
|
|
41
|
+
uf.union("c", "d");
|
|
42
|
+
// After path compression, all should point to same root
|
|
43
|
+
const root = uf.find("a");
|
|
44
|
+
expect(uf.find("b")).toBe(root);
|
|
45
|
+
expect(uf.find("c")).toBe(root);
|
|
46
|
+
expect(uf.find("d")).toBe(root);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
describe("union", () => {
|
|
50
|
+
it("should connect two separate elements", () => {
|
|
51
|
+
const uf = new UnionFind(["a", "b", "c"]);
|
|
52
|
+
expect(uf.connected("a", "b")).toBe(false);
|
|
53
|
+
uf.union("a", "b");
|
|
54
|
+
expect(uf.connected("a", "b")).toBe(true);
|
|
55
|
+
expect(uf.getComponentCount()).toBe(2);
|
|
56
|
+
});
|
|
57
|
+
it("should handle union of already connected elements", () => {
|
|
58
|
+
const uf = new UnionFind(["a", "b"]);
|
|
59
|
+
uf.union("a", "b");
|
|
60
|
+
const countBefore = uf.getComponentCount();
|
|
61
|
+
uf.union("a", "b"); // Union again
|
|
62
|
+
expect(uf.getComponentCount()).toBe(countBefore);
|
|
63
|
+
expect(uf.connected("a", "b")).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
it("should connect components transitively", () => {
|
|
66
|
+
const uf = new UnionFind(["a", "b", "c", "d"]);
|
|
67
|
+
uf.union("a", "b");
|
|
68
|
+
uf.union("c", "d");
|
|
69
|
+
expect(uf.getComponentCount()).toBe(2);
|
|
70
|
+
uf.union("b", "c");
|
|
71
|
+
expect(uf.getComponentCount()).toBe(1);
|
|
72
|
+
expect(uf.connected("a", "d")).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
it("should use union by rank for efficiency", () => {
|
|
75
|
+
const uf = new UnionFind(["a", "b", "c", "d", "e", "f", "g", "h"]);
|
|
76
|
+
// Create two trees of different heights
|
|
77
|
+
uf.union("a", "b");
|
|
78
|
+
uf.union("a", "c");
|
|
79
|
+
uf.union("a", "d"); // Tree 1: height 2
|
|
80
|
+
uf.union("e", "f"); // Tree 2: height 1
|
|
81
|
+
// Union should attach smaller tree to larger tree's root
|
|
82
|
+
uf.union("d", "e");
|
|
83
|
+
// All elements should be connected
|
|
84
|
+
expect(uf.connected("a", "f")).toBe(true);
|
|
85
|
+
expect(uf.getComponentCount()).toBe(3); // g, h still separate
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe("connected", () => {
|
|
89
|
+
it("should return true for elements in same component", () => {
|
|
90
|
+
const uf = new UnionFind(["a", "b", "c"]);
|
|
91
|
+
uf.union("a", "b");
|
|
92
|
+
expect(uf.connected("a", "b")).toBe(true);
|
|
93
|
+
expect(uf.connected("b", "a")).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
it("should return false for elements in different components", () => {
|
|
96
|
+
const uf = new UnionFind(["a", "b", "c"]);
|
|
97
|
+
uf.union("a", "b");
|
|
98
|
+
expect(uf.connected("a", "c")).toBe(false);
|
|
99
|
+
expect(uf.connected("c", "a")).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
it("should return false for non-existent elements", () => {
|
|
102
|
+
const uf = new UnionFind(["a", "b"]);
|
|
103
|
+
expect(uf.connected("a", "nonexistent")).toBe(false);
|
|
104
|
+
expect(uf.connected("nonexistent", "a")).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
it("should return true for element with itself", () => {
|
|
107
|
+
const uf = new UnionFind(["a"]);
|
|
108
|
+
expect(uf.connected("a", "a")).toBe(true);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
describe("getComponent", () => {
|
|
112
|
+
it("should return all elements in the same component", () => {
|
|
113
|
+
const uf = new UnionFind(["a", "b", "c", "d"]);
|
|
114
|
+
uf.union("a", "b");
|
|
115
|
+
uf.union("b", "c");
|
|
116
|
+
const component = uf.getComponent("a");
|
|
117
|
+
expect(component).toHaveLength(3);
|
|
118
|
+
expect(component).toContain("a");
|
|
119
|
+
expect(component).toContain("b");
|
|
120
|
+
expect(component).toContain("c");
|
|
121
|
+
expect(component).not.toContain("d");
|
|
122
|
+
});
|
|
123
|
+
it("should return single element for isolated component", () => {
|
|
124
|
+
const uf = new UnionFind(["a", "b"]);
|
|
125
|
+
const component = uf.getComponent("a");
|
|
126
|
+
expect(component).toEqual(["a"]);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
describe("getAllComponents", () => {
|
|
130
|
+
it("should return all components as separate arrays", () => {
|
|
131
|
+
const uf = new UnionFind(["a", "b", "c", "d", "e"]);
|
|
132
|
+
uf.union("a", "b");
|
|
133
|
+
uf.union("c", "d");
|
|
134
|
+
// e remains isolated
|
|
135
|
+
const components = uf.getAllComponents();
|
|
136
|
+
expect(components).toHaveLength(3);
|
|
137
|
+
// Find the components by size
|
|
138
|
+
const sizes = components.map((comp) => comp.length).sort();
|
|
139
|
+
expect(sizes).toEqual([1, 2, 2]);
|
|
140
|
+
// Check that all elements are included
|
|
141
|
+
const allElements = components.flat().sort();
|
|
142
|
+
expect(allElements).toEqual(["a", "b", "c", "d", "e"]);
|
|
143
|
+
});
|
|
144
|
+
it("should return single component when all connected", () => {
|
|
145
|
+
const uf = new UnionFind(["a", "b", "c"]);
|
|
146
|
+
uf.union("a", "b");
|
|
147
|
+
uf.union("b", "c");
|
|
148
|
+
const components = uf.getAllComponents();
|
|
149
|
+
expect(components).toHaveLength(1);
|
|
150
|
+
expect(components[0]).toHaveLength(3);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
describe("getComponentSize", () => {
|
|
154
|
+
it("should return correct size of component", () => {
|
|
155
|
+
const uf = new UnionFind(["a", "b", "c", "d"]);
|
|
156
|
+
uf.union("a", "b");
|
|
157
|
+
uf.union("b", "c");
|
|
158
|
+
expect(uf.getComponentSize("a")).toBe(3);
|
|
159
|
+
expect(uf.getComponentSize("b")).toBe(3);
|
|
160
|
+
expect(uf.getComponentSize("c")).toBe(3);
|
|
161
|
+
expect(uf.getComponentSize("d")).toBe(1);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
describe("addElement", () => {
|
|
165
|
+
it("should add new element as separate component", () => {
|
|
166
|
+
const uf = new UnionFind(["a", "b"]);
|
|
167
|
+
expect(uf.hasElement("c")).toBe(false);
|
|
168
|
+
uf.addElement("c");
|
|
169
|
+
expect(uf.hasElement("c")).toBe(true);
|
|
170
|
+
expect(uf.size()).toBe(3);
|
|
171
|
+
expect(uf.getComponentCount()).toBe(3);
|
|
172
|
+
});
|
|
173
|
+
it("should not change anything if element already exists", () => {
|
|
174
|
+
const uf = new UnionFind(["a", "b"]);
|
|
175
|
+
const sizeBefore = uf.size();
|
|
176
|
+
const countBefore = uf.getComponentCount();
|
|
177
|
+
uf.addElement("a");
|
|
178
|
+
expect(uf.size()).toBe(sizeBefore);
|
|
179
|
+
expect(uf.getComponentCount()).toBe(countBefore);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
describe("hasElement", () => {
|
|
183
|
+
it("should return true for existing elements", () => {
|
|
184
|
+
const uf = new UnionFind(["a", "b", "c"]);
|
|
185
|
+
expect(uf.hasElement("a")).toBe(true);
|
|
186
|
+
expect(uf.hasElement("b")).toBe(true);
|
|
187
|
+
expect(uf.hasElement("c")).toBe(true);
|
|
188
|
+
});
|
|
189
|
+
it("should return false for non-existing elements", () => {
|
|
190
|
+
const uf = new UnionFind(["a", "b"]);
|
|
191
|
+
expect(uf.hasElement("c")).toBe(false);
|
|
192
|
+
expect(uf.hasElement("nonexistent")).toBe(false);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
describe("stress test", () => {
|
|
196
|
+
it("should handle large number of operations efficiently", () => {
|
|
197
|
+
const elements = Array.from({ length: 1000 }, (_, i) => i.toString());
|
|
198
|
+
const uf = new UnionFind(elements);
|
|
199
|
+
// Initially all separate
|
|
200
|
+
expect(uf.getComponentCount()).toBe(1000);
|
|
201
|
+
// Union adjacent elements
|
|
202
|
+
for (let i = 0; i < 999; i++) {
|
|
203
|
+
uf.union(i.toString(), (i + 1).toString());
|
|
204
|
+
}
|
|
205
|
+
// Should now have single component
|
|
206
|
+
expect(uf.getComponentCount()).toBe(1);
|
|
207
|
+
expect(uf.connected("0", "999")).toBe(true);
|
|
208
|
+
// Test multiple finds (should be fast due to path compression)
|
|
209
|
+
for (let i = 0; i < 100; i++) {
|
|
210
|
+
const randomElement = Math.floor(Math.random() * 1000).toString();
|
|
211
|
+
expect(uf.find(randomElement)).toBeDefined();
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
describe("edge cases", () => {
|
|
216
|
+
it("should handle numeric node IDs", () => {
|
|
217
|
+
const uf = new UnionFind([1, 2, 3]);
|
|
218
|
+
uf.union(1, 2);
|
|
219
|
+
expect(uf.connected(1, 2)).toBe(true);
|
|
220
|
+
expect(uf.connected(2, 3)).toBe(false);
|
|
221
|
+
});
|
|
222
|
+
it("should handle mixed string and number IDs", () => {
|
|
223
|
+
const uf = new UnionFind(["a", 1, "b", 2]);
|
|
224
|
+
uf.union("a", 1);
|
|
225
|
+
uf.union("b", 2);
|
|
226
|
+
expect(uf.connected("a", 1)).toBe(true);
|
|
227
|
+
expect(uf.connected("b", 2)).toBe(true);
|
|
228
|
+
expect(uf.connected("a", "b")).toBe(false);
|
|
229
|
+
});
|
|
230
|
+
it("should handle complex component merging", () => {
|
|
231
|
+
const uf = new UnionFind(["a", "b", "c", "d", "e", "f"]);
|
|
232
|
+
// Create two separate components
|
|
233
|
+
uf.union("a", "b");
|
|
234
|
+
uf.union("b", "c"); // Component 1: {a, b, c}
|
|
235
|
+
uf.union("d", "e");
|
|
236
|
+
uf.union("e", "f"); // Component 2: {d, e, f}
|
|
237
|
+
expect(uf.getComponentCount()).toBe(2);
|
|
238
|
+
// Merge the two components
|
|
239
|
+
uf.union("c", "d");
|
|
240
|
+
expect(uf.getComponentCount()).toBe(1);
|
|
241
|
+
expect(uf.connected("a", "f")).toBe(true);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
//# sourceMappingURL=union-find.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"union-find.test.js","sourceRoot":"","sources":["../../../test/unit/union-find.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@graphty/algorithms",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Graph algorithms library for browser environments implemented in TypeScript",
|
|
5
|
+
"author": "Adam Powers <apowers@ato.ms>",
|
|
6
|
+
"main": "dist/algorithms.js",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/algorithms.js",
|
|
11
|
+
"types": "./dist/algorithms.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"types": "dist/algorithms.d.ts",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist/",
|
|
17
|
+
"src/",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"directories": {
|
|
22
|
+
"example": "examples"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"test": "vitest",
|
|
26
|
+
"test:ui": "vitest --ui",
|
|
27
|
+
"test:run": "vitest run --project=default",
|
|
28
|
+
"test:coverage": "vitest run --coverage",
|
|
29
|
+
"test:browser": "vitest --project=browser",
|
|
30
|
+
"test:all": "vitest run --project=default --project=browser",
|
|
31
|
+
"lint": "eslint && tsc --noEmit",
|
|
32
|
+
"lint:fix": "eslint --fix",
|
|
33
|
+
"lint:pkg": "knip",
|
|
34
|
+
"lint:all": "npm run lint && npm run lint:pkg",
|
|
35
|
+
"typecheck": "tsc --noEmit",
|
|
36
|
+
"prepare": "husky",
|
|
37
|
+
"build": "tsc",
|
|
38
|
+
"build:watch": "tsc --watch",
|
|
39
|
+
"dev": "tsc --watch",
|
|
40
|
+
"commit": "cz",
|
|
41
|
+
"ready:commit": "npm run build && npm run lint && npm run test:all"
|
|
42
|
+
},
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/graphty-org/algorithms.git"
|
|
46
|
+
},
|
|
47
|
+
"keywords": [
|
|
48
|
+
"graph",
|
|
49
|
+
"algorithms",
|
|
50
|
+
"typescript",
|
|
51
|
+
"browser",
|
|
52
|
+
"graph-theory",
|
|
53
|
+
"network-analysis",
|
|
54
|
+
"centrality",
|
|
55
|
+
"community-detection",
|
|
56
|
+
"shortest-path",
|
|
57
|
+
"graph-traversal"
|
|
58
|
+
],
|
|
59
|
+
"license": "MIT",
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public",
|
|
62
|
+
"provenance": true
|
|
63
|
+
},
|
|
64
|
+
"bugs": {
|
|
65
|
+
"url": "https://github.com/graphty-org/algorithms/issues"
|
|
66
|
+
},
|
|
67
|
+
"homepage": "https://github.com/graphty-org/algorithms#readme",
|
|
68
|
+
"engines": {
|
|
69
|
+
"node": ">=18.19.0"
|
|
70
|
+
},
|
|
71
|
+
"config": {
|
|
72
|
+
"commitizen": {
|
|
73
|
+
"path": "./node_modules/cz-conventional-changelog"
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"devDependencies": {
|
|
77
|
+
"@commitlint/cli": "^19.8.1",
|
|
78
|
+
"@commitlint/config-conventional": "^19.8.1",
|
|
79
|
+
"@eslint/js": "^9.29.0",
|
|
80
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
81
|
+
"@semantic-release/git": "^10.0.1",
|
|
82
|
+
"@stylistic/eslint-plugin": "^4.4.1",
|
|
83
|
+
"@types/node": "^20.19.2",
|
|
84
|
+
"@vitest/browser": "^3.2.4",
|
|
85
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
86
|
+
"@vitest/ui": "^3.2.4",
|
|
87
|
+
"cz-conventional-changelog": "^3.3.0",
|
|
88
|
+
"eslint": "^9.29.0",
|
|
89
|
+
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
90
|
+
"globals": "^15.12.0",
|
|
91
|
+
"happy-dom": "^18.0.1",
|
|
92
|
+
"husky": "^9.1.7",
|
|
93
|
+
"knip": "^5.61.3",
|
|
94
|
+
"playwright": "^1.53.0",
|
|
95
|
+
"semantic-release": "^24.2.7",
|
|
96
|
+
"typescript": "^5.8.3",
|
|
97
|
+
"typescript-eslint": "^8.34.1",
|
|
98
|
+
"vitest": "^3.2.4"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import type {Graph} from "../../core/graph.js";
|
|
2
|
+
import type {NodeId} from "../../types/index.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Betweenness centrality implementation using Brandes' algorithm
|
|
6
|
+
*
|
|
7
|
+
* Measures the extent to which a node lies on paths between other nodes.
|
|
8
|
+
* Uses the fast O(VE) algorithm by Ulrik Brandes.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Betweenness centrality options
|
|
13
|
+
*/
|
|
14
|
+
export interface BetweennessCentralityOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Whether to normalize the centrality values (default: false)
|
|
17
|
+
*/
|
|
18
|
+
normalized?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Whether to use endpoints in path counting (default: false)
|
|
21
|
+
*/
|
|
22
|
+
endpoints?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Calculate betweenness centrality for all nodes using Brandes' algorithm
|
|
27
|
+
*/
|
|
28
|
+
export function betweennessCentrality(
|
|
29
|
+
graph: Graph,
|
|
30
|
+
options: BetweennessCentralityOptions = {},
|
|
31
|
+
): Record<string, number> {
|
|
32
|
+
const nodes = Array.from(graph.nodes()).map((node) => node.id);
|
|
33
|
+
const centrality: Record<string, number> = {};
|
|
34
|
+
|
|
35
|
+
// Initialize centrality scores
|
|
36
|
+
for (const nodeId of nodes) {
|
|
37
|
+
centrality[String(nodeId)] = 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Brandes' algorithm
|
|
41
|
+
for (const source of nodes) {
|
|
42
|
+
const stack: NodeId[] = [];
|
|
43
|
+
const predecessors = new Map<NodeId, NodeId[]>();
|
|
44
|
+
const sigma = new Map<NodeId, number>(); // Number of shortest paths
|
|
45
|
+
const distance = new Map<NodeId, number>();
|
|
46
|
+
const delta = new Map<NodeId, number>();
|
|
47
|
+
|
|
48
|
+
// Initialize
|
|
49
|
+
for (const nodeId of nodes) {
|
|
50
|
+
predecessors.set(nodeId, []);
|
|
51
|
+
sigma.set(nodeId, 0);
|
|
52
|
+
distance.set(nodeId, -1);
|
|
53
|
+
delta.set(nodeId, 0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
sigma.set(source, 1);
|
|
57
|
+
distance.set(source, 0);
|
|
58
|
+
|
|
59
|
+
const queue: NodeId[] = [source];
|
|
60
|
+
|
|
61
|
+
// BFS to find shortest paths
|
|
62
|
+
while (queue.length > 0) {
|
|
63
|
+
const current = queue.shift();
|
|
64
|
+
if (!current) {
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
stack.push(current);
|
|
69
|
+
|
|
70
|
+
for (const neighbor of graph.neighbors(current)) {
|
|
71
|
+
const currentDistance = distance.get(current);
|
|
72
|
+
let neighborDistance = distance.get(neighbor);
|
|
73
|
+
|
|
74
|
+
if (currentDistance === undefined || neighborDistance === undefined) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// First time we reach this neighbor
|
|
79
|
+
if (neighborDistance < 0) {
|
|
80
|
+
queue.push(neighbor);
|
|
81
|
+
distance.set(neighbor, currentDistance + 1);
|
|
82
|
+
neighborDistance = currentDistance + 1; // Update local variable
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Shortest path to neighbor via current
|
|
86
|
+
if (neighborDistance === currentDistance + 1) {
|
|
87
|
+
const neighborSigma = sigma.get(neighbor) ?? 0;
|
|
88
|
+
const currentSigma = sigma.get(current) ?? 0;
|
|
89
|
+
sigma.set(neighbor, neighborSigma + currentSigma);
|
|
90
|
+
|
|
91
|
+
const preds = predecessors.get(neighbor);
|
|
92
|
+
if (preds) {
|
|
93
|
+
preds.push(current);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Accumulation - back-propagation of dependencies
|
|
100
|
+
while (stack.length > 0) {
|
|
101
|
+
const w = stack.pop();
|
|
102
|
+
if (!w) {
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const wPreds = predecessors.get(w) ?? [];
|
|
107
|
+
const wSigma = sigma.get(w) ?? 0;
|
|
108
|
+
const wDelta = delta.get(w) ?? 0;
|
|
109
|
+
|
|
110
|
+
for (const v of wPreds) {
|
|
111
|
+
const vSigma = sigma.get(v) ?? 0;
|
|
112
|
+
const vDelta = delta.get(v) ?? 0;
|
|
113
|
+
|
|
114
|
+
if (vSigma > 0 && wSigma > 0) {
|
|
115
|
+
const contribution = (vSigma / wSigma) * (1 + wDelta);
|
|
116
|
+
delta.set(v, vDelta + contribution);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (w !== source) {
|
|
121
|
+
const currentCentrality = centrality[String(w)] ?? 0;
|
|
122
|
+
centrality[String(w)] = currentCentrality + wDelta;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// For undirected graphs, divide by 2 (each shortest path is counted twice)
|
|
128
|
+
if (!graph.isDirected) {
|
|
129
|
+
for (const nodeId of nodes) {
|
|
130
|
+
const key = String(nodeId);
|
|
131
|
+
const currentValue = centrality[key];
|
|
132
|
+
if (currentValue !== undefined) {
|
|
133
|
+
centrality[key] = currentValue / 2;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Normalization
|
|
139
|
+
if (options.normalized) {
|
|
140
|
+
const n = nodes.length;
|
|
141
|
+
let normalizationFactor: number;
|
|
142
|
+
|
|
143
|
+
if (graph.isDirected) {
|
|
144
|
+
normalizationFactor = (n - 1) * (n - 2);
|
|
145
|
+
} else {
|
|
146
|
+
normalizationFactor = ((n - 1) * (n - 2)) / 2;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (normalizationFactor > 0) {
|
|
150
|
+
for (const nodeId of nodes) {
|
|
151
|
+
const key = String(nodeId);
|
|
152
|
+
const currentValue = centrality[key];
|
|
153
|
+
if (currentValue !== undefined) {
|
|
154
|
+
centrality[key] = currentValue / normalizationFactor;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return centrality;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Calculate betweenness centrality for a specific node
|
|
165
|
+
*/
|
|
166
|
+
export function nodeBetweennessCentrality(
|
|
167
|
+
graph: Graph,
|
|
168
|
+
targetNode: NodeId,
|
|
169
|
+
options: BetweennessCentralityOptions = {},
|
|
170
|
+
): number {
|
|
171
|
+
if (!graph.hasNode(targetNode)) {
|
|
172
|
+
throw new Error(`Node ${String(targetNode)} not found in graph`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const allCentralities = betweennessCentrality(graph, options);
|
|
176
|
+
return allCentralities[String(targetNode)] ?? 0;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Calculate edge betweenness centrality
|
|
181
|
+
*/
|
|
182
|
+
export function edgeBetweennessCentrality(
|
|
183
|
+
graph: Graph,
|
|
184
|
+
options: BetweennessCentralityOptions = {},
|
|
185
|
+
): Map<string, number> {
|
|
186
|
+
const nodes = Array.from(graph.nodes()).map((node) => node.id);
|
|
187
|
+
const edgeCentrality = new Map<string, number>();
|
|
188
|
+
|
|
189
|
+
// Initialize edge centrality scores
|
|
190
|
+
for (const edge of graph.edges()) {
|
|
191
|
+
const edgeKey = `${String(edge.source)}-${String(edge.target)}`;
|
|
192
|
+
edgeCentrality.set(edgeKey, 0);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Modified Brandes' algorithm for edge betweenness
|
|
196
|
+
for (const source of nodes) {
|
|
197
|
+
const stack: NodeId[] = [];
|
|
198
|
+
const predecessors = new Map<NodeId, NodeId[]>();
|
|
199
|
+
const sigma = new Map<NodeId, number>();
|
|
200
|
+
const distance = new Map<NodeId, number>();
|
|
201
|
+
const delta = new Map<NodeId, number>();
|
|
202
|
+
|
|
203
|
+
// Initialize
|
|
204
|
+
for (const nodeId of nodes) {
|
|
205
|
+
predecessors.set(nodeId, []);
|
|
206
|
+
sigma.set(nodeId, 0);
|
|
207
|
+
distance.set(nodeId, -1);
|
|
208
|
+
delta.set(nodeId, 0);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
sigma.set(source, 1);
|
|
212
|
+
distance.set(source, 0);
|
|
213
|
+
|
|
214
|
+
const queue: NodeId[] = [source];
|
|
215
|
+
|
|
216
|
+
// BFS to find shortest paths
|
|
217
|
+
while (queue.length > 0) {
|
|
218
|
+
const current = queue.shift();
|
|
219
|
+
if (!current) {
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
stack.push(current);
|
|
224
|
+
|
|
225
|
+
for (const neighbor of graph.neighbors(current)) {
|
|
226
|
+
const currentDistance = distance.get(current);
|
|
227
|
+
let neighborDistance = distance.get(neighbor);
|
|
228
|
+
|
|
229
|
+
if (currentDistance === undefined || neighborDistance === undefined) {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (neighborDistance < 0) {
|
|
234
|
+
queue.push(neighbor);
|
|
235
|
+
distance.set(neighbor, currentDistance + 1);
|
|
236
|
+
neighborDistance = currentDistance + 1; // Update local variable
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (neighborDistance === currentDistance + 1) {
|
|
240
|
+
const neighborSigma = sigma.get(neighbor) ?? 0;
|
|
241
|
+
const currentSigma = sigma.get(current) ?? 0;
|
|
242
|
+
sigma.set(neighbor, neighborSigma + currentSigma);
|
|
243
|
+
|
|
244
|
+
const preds = predecessors.get(neighbor);
|
|
245
|
+
if (preds) {
|
|
246
|
+
preds.push(current);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Accumulation for edges
|
|
253
|
+
while (stack.length > 0) {
|
|
254
|
+
const w = stack.pop();
|
|
255
|
+
if (!w) {
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const wPreds = predecessors.get(w) ?? [];
|
|
260
|
+
const wSigma = sigma.get(w) ?? 0;
|
|
261
|
+
const wDelta = delta.get(w) ?? 0;
|
|
262
|
+
|
|
263
|
+
for (const v of wPreds) {
|
|
264
|
+
const vSigma = sigma.get(v) ?? 0;
|
|
265
|
+
|
|
266
|
+
if (vSigma > 0 && wSigma > 0) {
|
|
267
|
+
const edgeContribution = vSigma / wSigma * (1 + wDelta);
|
|
268
|
+
|
|
269
|
+
// Update edge centrality
|
|
270
|
+
const edgeKey = `${String(v)}-${String(w)}`;
|
|
271
|
+
const currentEdgeCentrality = edgeCentrality.get(edgeKey) ?? 0;
|
|
272
|
+
edgeCentrality.set(edgeKey, currentEdgeCentrality + edgeContribution);
|
|
273
|
+
|
|
274
|
+
// Update node delta
|
|
275
|
+
const vDelta = delta.get(v) ?? 0;
|
|
276
|
+
delta.set(v, vDelta + edgeContribution);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// For undirected graphs, divide by 2 (each shortest path is counted twice)
|
|
283
|
+
if (!graph.isDirected) {
|
|
284
|
+
for (const [edgeKey, centrality] of edgeCentrality) {
|
|
285
|
+
edgeCentrality.set(edgeKey, centrality / 2);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Normalization for edges
|
|
290
|
+
if (options.normalized) {
|
|
291
|
+
const n = nodes.length;
|
|
292
|
+
const normalizationFactor = graph.isDirected ? (n - 1) * (n - 2) : ((n - 1) * (n - 2)) / 2;
|
|
293
|
+
|
|
294
|
+
if (normalizationFactor > 0) {
|
|
295
|
+
for (const [edgeKey, centrality] of edgeCentrality) {
|
|
296
|
+
edgeCentrality.set(edgeKey, centrality / normalizationFactor);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return edgeCentrality;
|
|
302
|
+
}
|
|
303
|
+
|