@graphty/algorithms 1.1.0 → 1.3.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.
Files changed (237) hide show
  1. package/README.md +820 -235
  2. package/dist/algorithms.d.ts +42 -2
  3. package/dist/algorithms.js +11099 -2
  4. package/dist/algorithms.js.map +1 -1
  5. package/dist/src/algorithms/centrality/betweenness.d.ts +4 -0
  6. package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
  7. package/dist/src/algorithms/centrality/betweenness.js +124 -170
  8. package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
  9. package/dist/src/algorithms/centrality/closeness.d.ts +26 -1
  10. package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -1
  11. package/dist/src/algorithms/centrality/closeness.js +56 -149
  12. package/dist/src/algorithms/centrality/closeness.js.map +1 -1
  13. package/dist/src/algorithms/centrality/degree.js +3 -3
  14. package/dist/src/algorithms/centrality/degree.js.map +1 -1
  15. package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts +39 -0
  16. package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts.map +1 -0
  17. package/dist/src/algorithms/centrality/delta-pagerank-simple.js +286 -0
  18. package/dist/src/algorithms/centrality/delta-pagerank-simple.js.map +1 -0
  19. package/dist/src/algorithms/centrality/delta-pagerank.d.ts +80 -0
  20. package/dist/src/algorithms/centrality/delta-pagerank.d.ts.map +1 -0
  21. package/dist/src/algorithms/centrality/delta-pagerank.js +294 -0
  22. package/dist/src/algorithms/centrality/delta-pagerank.js.map +1 -0
  23. package/dist/src/algorithms/centrality/index.d.ts +2 -0
  24. package/dist/src/algorithms/centrality/index.d.ts.map +1 -1
  25. package/dist/src/algorithms/centrality/index.js +1 -0
  26. package/dist/src/algorithms/centrality/index.js.map +1 -1
  27. package/dist/src/algorithms/centrality/pagerank.d.ts +17 -0
  28. package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -1
  29. package/dist/src/algorithms/centrality/pagerank.js +45 -0
  30. package/dist/src/algorithms/centrality/pagerank.js.map +1 -1
  31. package/dist/src/algorithms/community/girvan-newman.d.ts.map +1 -1
  32. package/dist/src/algorithms/community/girvan-newman.js +47 -54
  33. package/dist/src/algorithms/community/girvan-newman.js.map +1 -1
  34. package/dist/src/algorithms/community/index.d.ts +2 -0
  35. package/dist/src/algorithms/community/index.d.ts.map +1 -1
  36. package/dist/src/algorithms/community/index.js.map +1 -1
  37. package/dist/src/algorithms/community/label-propagation.d.ts +13 -3
  38. package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -1
  39. package/dist/src/algorithms/community/label-propagation.js +50 -35
  40. package/dist/src/algorithms/community/label-propagation.js.map +1 -1
  41. package/dist/src/algorithms/community/leiden.d.ts +3 -2
  42. package/dist/src/algorithms/community/leiden.d.ts.map +1 -1
  43. package/dist/src/algorithms/community/leiden.js +34 -39
  44. package/dist/src/algorithms/community/leiden.js.map +1 -1
  45. package/dist/src/algorithms/community/louvain-optimized.d.ts +112 -0
  46. package/dist/src/algorithms/community/louvain-optimized.d.ts.map +1 -0
  47. package/dist/src/algorithms/community/louvain-optimized.js +303 -0
  48. package/dist/src/algorithms/community/louvain-optimized.js.map +1 -0
  49. package/dist/src/algorithms/community/louvain.d.ts.map +1 -1
  50. package/dist/src/algorithms/community/louvain.js +17 -76
  51. package/dist/src/algorithms/community/louvain.js.map +1 -1
  52. package/dist/src/algorithms/community/modularity-utils.d.ts +59 -0
  53. package/dist/src/algorithms/community/modularity-utils.d.ts.map +1 -0
  54. package/dist/src/algorithms/community/modularity-utils.js +112 -0
  55. package/dist/src/algorithms/community/modularity-utils.js.map +1 -0
  56. package/dist/src/algorithms/matching/bipartite.d.ts.map +1 -1
  57. package/dist/src/algorithms/matching/bipartite.js +8 -40
  58. package/dist/src/algorithms/matching/bipartite.js.map +1 -1
  59. package/dist/src/algorithms/shortest-path/bellman-ford.d.ts.map +1 -1
  60. package/dist/src/algorithms/shortest-path/bellman-ford.js +1 -12
  61. package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
  62. package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts +37 -0
  63. package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts.map +1 -0
  64. package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js +193 -0
  65. package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js.map +1 -0
  66. package/dist/src/algorithms/shortest-path/dijkstra.d.ts +5 -2
  67. package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -1
  68. package/dist/src/algorithms/shortest-path/dijkstra.js +15 -14
  69. package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -1
  70. package/dist/src/algorithms/traversal/bfs-unified.d.ts +29 -0
  71. package/dist/src/algorithms/traversal/bfs-unified.d.ts.map +1 -0
  72. package/dist/src/algorithms/traversal/bfs-unified.js +333 -0
  73. package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -0
  74. package/dist/src/algorithms/traversal/bfs-variants.d.ts +54 -0
  75. package/dist/src/algorithms/traversal/bfs-variants.d.ts.map +1 -0
  76. package/dist/src/algorithms/traversal/bfs-variants.js +368 -0
  77. package/dist/src/algorithms/traversal/bfs-variants.js.map +1 -0
  78. package/dist/src/algorithms/traversal/bfs.d.ts +4 -18
  79. package/dist/src/algorithms/traversal/bfs.d.ts.map +1 -1
  80. package/dist/src/algorithms/traversal/bfs.js +5 -182
  81. package/dist/src/algorithms/traversal/bfs.js.map +1 -1
  82. package/dist/src/benchmark-all-algorithms.d.ts +23 -0
  83. package/dist/src/benchmark-all-algorithms.d.ts.map +1 -0
  84. package/dist/src/benchmark-all-algorithms.js +462 -0
  85. package/dist/src/benchmark-all-algorithms.js.map +1 -0
  86. package/dist/src/clustering/hierarchical.d.ts +16 -15
  87. package/dist/src/clustering/hierarchical.d.ts.map +1 -1
  88. package/dist/src/clustering/hierarchical.js +209 -223
  89. package/dist/src/clustering/hierarchical.js.map +1 -1
  90. package/dist/src/clustering/index.d.ts +4 -2
  91. package/dist/src/clustering/index.d.ts.map +1 -1
  92. package/dist/src/clustering/index.js +2 -2
  93. package/dist/src/clustering/index.js.map +1 -1
  94. package/dist/src/clustering/k-core.d.ts +16 -15
  95. package/dist/src/clustering/k-core.d.ts.map +1 -1
  96. package/dist/src/clustering/k-core.js +57 -21
  97. package/dist/src/clustering/k-core.js.map +1 -1
  98. package/dist/src/clustering/spectral.d.ts +8 -0
  99. package/dist/src/clustering/spectral.d.ts.map +1 -1
  100. package/dist/src/clustering/spectral.js +158 -35
  101. package/dist/src/clustering/spectral.js.map +1 -1
  102. package/dist/src/core/graph.js +4 -4
  103. package/dist/src/core/graph.js.map +1 -1
  104. package/dist/src/flow/ford-fulkerson.d.ts +17 -11
  105. package/dist/src/flow/ford-fulkerson.d.ts.map +1 -1
  106. package/dist/src/flow/ford-fulkerson.js +141 -217
  107. package/dist/src/flow/ford-fulkerson.js.map +1 -1
  108. package/dist/src/flow/min-cut.d.ts +6 -5
  109. package/dist/src/flow/min-cut.d.ts.map +1 -1
  110. package/dist/src/flow/min-cut.js +17 -8
  111. package/dist/src/flow/min-cut.js.map +1 -1
  112. package/dist/src/index.d.ts +2 -0
  113. package/dist/src/index.d.ts.map +1 -1
  114. package/dist/src/index.js +6 -0
  115. package/dist/src/index.js.map +1 -1
  116. package/dist/src/link-prediction/adamic-adar.d.ts.map +1 -1
  117. package/dist/src/link-prediction/adamic-adar.js +17 -26
  118. package/dist/src/link-prediction/adamic-adar.js.map +1 -1
  119. package/dist/src/link-prediction/common-neighbors.d.ts.map +1 -1
  120. package/dist/src/link-prediction/common-neighbors.js +7 -11
  121. package/dist/src/link-prediction/common-neighbors.js.map +1 -1
  122. package/dist/src/optimized/bit-packed.d.ts +143 -0
  123. package/dist/src/optimized/bit-packed.d.ts.map +1 -0
  124. package/dist/src/optimized/bit-packed.js +292 -0
  125. package/dist/src/optimized/bit-packed.js.map +1 -0
  126. package/dist/src/optimized/csr-graph.d.ts +86 -0
  127. package/dist/src/optimized/csr-graph.d.ts.map +1 -0
  128. package/dist/src/optimized/csr-graph.js +341 -0
  129. package/dist/src/optimized/csr-graph.js.map +1 -0
  130. package/dist/src/optimized/direction-optimized-bfs.d.ts +74 -0
  131. package/dist/src/optimized/direction-optimized-bfs.d.ts.map +1 -0
  132. package/dist/src/optimized/direction-optimized-bfs.js +223 -0
  133. package/dist/src/optimized/direction-optimized-bfs.js.map +1 -0
  134. package/dist/src/optimized/graph-adapter.d.ts +62 -0
  135. package/dist/src/optimized/graph-adapter.d.ts.map +1 -0
  136. package/dist/src/optimized/graph-adapter.js +146 -0
  137. package/dist/src/optimized/graph-adapter.js.map +1 -0
  138. package/dist/src/optimized/index.d.ts +14 -0
  139. package/dist/src/optimized/index.d.ts.map +1 -0
  140. package/dist/src/optimized/index.js +28 -0
  141. package/dist/src/optimized/index.js.map +1 -0
  142. package/dist/src/research/grsbm.d.ts +83 -0
  143. package/dist/src/research/grsbm.d.ts.map +1 -0
  144. package/dist/src/research/grsbm.js +403 -0
  145. package/dist/src/research/grsbm.js.map +1 -0
  146. package/dist/src/research/index.d.ts +12 -0
  147. package/dist/src/research/index.d.ts.map +1 -0
  148. package/dist/src/research/index.js +14 -0
  149. package/dist/src/research/index.js.map +1 -0
  150. package/dist/src/research/sync.d.ts +49 -0
  151. package/dist/src/research/sync.d.ts.map +1 -0
  152. package/dist/src/research/sync.js +316 -0
  153. package/dist/src/research/sync.js.map +1 -0
  154. package/dist/src/research/terahac.d.ts +65 -0
  155. package/dist/src/research/terahac.d.ts.map +1 -0
  156. package/dist/src/research/terahac.js +379 -0
  157. package/dist/src/research/terahac.js.map +1 -0
  158. package/dist/src/types/index.d.ts +8 -0
  159. package/dist/src/types/index.d.ts.map +1 -1
  160. package/dist/src/utils/algorithm-utilities.d.ts +2 -0
  161. package/dist/src/utils/algorithm-utilities.d.ts.map +1 -0
  162. package/dist/src/utils/algorithm-utilities.js +2 -0
  163. package/dist/src/utils/algorithm-utilities.js.map +1 -0
  164. package/dist/src/utils/graph-converters.d.ts +79 -0
  165. package/dist/src/utils/graph-converters.d.ts.map +1 -0
  166. package/dist/src/utils/graph-converters.js +196 -0
  167. package/dist/src/utils/graph-converters.js.map +1 -0
  168. package/dist/src/utils/graph-utilities.d.ts +62 -0
  169. package/dist/src/utils/graph-utilities.d.ts.map +1 -0
  170. package/dist/src/utils/graph-utilities.js +148 -0
  171. package/dist/src/utils/graph-utilities.js.map +1 -0
  172. package/dist/src/utils/index.d.ts +5 -0
  173. package/dist/src/utils/index.d.ts.map +1 -0
  174. package/dist/src/utils/index.js +5 -0
  175. package/dist/src/utils/index.js.map +1 -0
  176. package/dist/src/utils/math-utilities.d.ts +52 -0
  177. package/dist/src/utils/math-utilities.d.ts.map +1 -0
  178. package/dist/src/utils/math-utilities.js +123 -0
  179. package/dist/src/utils/math-utilities.js.map +1 -0
  180. package/dist/src/utils/matrix-utilities.d.ts +2 -0
  181. package/dist/src/utils/matrix-utilities.d.ts.map +1 -0
  182. package/dist/src/utils/matrix-utilities.js +2 -0
  183. package/dist/src/utils/matrix-utilities.js.map +1 -0
  184. package/dist/src/utils/optimization-helpers.d.ts +33 -0
  185. package/dist/src/utils/optimization-helpers.d.ts.map +1 -0
  186. package/dist/src/utils/optimization-helpers.js +47 -0
  187. package/dist/src/utils/optimization-helpers.js.map +1 -0
  188. package/package.json +28 -4
  189. package/src/algorithms/centrality/betweenness.ts +167 -199
  190. package/src/algorithms/centrality/closeness.ts +73 -191
  191. package/src/algorithms/centrality/degree.ts +3 -3
  192. package/src/algorithms/centrality/delta-pagerank-simple.ts +366 -0
  193. package/src/algorithms/centrality/delta-pagerank.ts +435 -0
  194. package/src/algorithms/centrality/index.ts +2 -0
  195. package/src/algorithms/centrality/pagerank.ts +66 -0
  196. package/src/algorithms/community/girvan-newman.ts +52 -59
  197. package/src/algorithms/community/index.ts +2 -0
  198. package/src/algorithms/community/label-propagation.ts +64 -35
  199. package/src/algorithms/community/leiden.ts +40 -41
  200. package/src/algorithms/community/louvain-optimized.ts +445 -0
  201. package/src/algorithms/community/louvain.ts +24 -104
  202. package/src/algorithms/community/modularity-utils.ts +146 -0
  203. package/src/algorithms/matching/bipartite.ts +8 -44
  204. package/src/algorithms/shortest-path/bellman-ford.ts +1 -14
  205. package/src/algorithms/shortest-path/bidirectional-dijkstra.ts +248 -0
  206. package/src/algorithms/shortest-path/dijkstra.ts +17 -15
  207. package/src/algorithms/traversal/bfs-unified.ts +433 -0
  208. package/src/algorithms/traversal/bfs-variants.ts +496 -0
  209. package/src/algorithms/traversal/bfs.ts +10 -228
  210. package/src/benchmark-all-algorithms.ts +548 -0
  211. package/src/clustering/hierarchical.ts +241 -251
  212. package/src/clustering/index.ts +4 -2
  213. package/src/clustering/k-core.ts +87 -39
  214. package/src/clustering/spectral.ts +185 -34
  215. package/src/core/graph.ts +4 -4
  216. package/src/flow/ford-fulkerson.ts +186 -254
  217. package/src/flow/min-cut.ts +21 -11
  218. package/src/index.ts +9 -0
  219. package/src/link-prediction/adamic-adar.ts +17 -30
  220. package/src/link-prediction/common-neighbors.ts +7 -16
  221. package/src/optimized/bit-packed.ts +340 -0
  222. package/src/optimized/csr-graph.ts +455 -0
  223. package/src/optimized/direction-optimized-bfs.ts +291 -0
  224. package/src/optimized/graph-adapter.ts +200 -0
  225. package/src/optimized/index.ts +36 -0
  226. package/src/research/grsbm.ts +580 -0
  227. package/src/research/index.ts +17 -0
  228. package/src/research/sync.ts +440 -0
  229. package/src/research/terahac.ts +514 -0
  230. package/src/types/index.ts +8 -0
  231. package/src/utils/algorithm-utilities.ts +2 -0
  232. package/src/utils/graph-converters.ts +241 -0
  233. package/src/utils/graph-utilities.ts +199 -0
  234. package/src/utils/index.ts +4 -0
  235. package/src/utils/math-utilities.ts +140 -0
  236. package/src/utils/matrix-utilities.ts +2 -0
  237. package/src/utils/optimization-helpers.ts +81 -0
@@ -0,0 +1,241 @@
1
+ import {Graph} from "../core/graph.js";
2
+ import type {NodeId} from "../types/index.js";
3
+
4
+ /**
5
+ * Convert Graph class to Map representation
6
+ * Used by community detection and flow algorithms
7
+ */
8
+ export function graphToMap(graph: Graph): Map<string, Map<string, number>> {
9
+ const map = new Map<string, Map<string, number>>();
10
+
11
+ // Initialize all nodes
12
+ for (const node of graph.nodes()) {
13
+ map.set(String(node.id), new Map());
14
+ }
15
+
16
+ // Add edges with weights
17
+ for (const edge of graph.edges()) {
18
+ const source = String(edge.source);
19
+ const target = String(edge.target);
20
+ const weight = edge.weight ?? 1;
21
+
22
+ const sourceMap = map.get(source);
23
+ if (sourceMap) {
24
+ sourceMap.set(target, weight);
25
+ }
26
+
27
+ // For undirected graphs, add reverse edge
28
+ if (!graph.isDirected) {
29
+ const targetMap = map.get(target);
30
+ if (targetMap) {
31
+ targetMap.set(source, weight);
32
+ }
33
+ }
34
+ }
35
+
36
+ return map;
37
+ }
38
+
39
+ /**
40
+ * Convert Map representation to Graph class
41
+ */
42
+ export function mapToGraph(
43
+ map: Map<string, Map<string, number>>,
44
+ isDirected = true,
45
+ ): Graph {
46
+ const graph = new Graph({directed: isDirected});
47
+
48
+ // Add all nodes first
49
+ for (const node of map.keys()) {
50
+ graph.addNode(node);
51
+ }
52
+
53
+ // Add edges
54
+ const processedEdges = new Set<string>();
55
+
56
+ for (const [source, neighbors] of map) {
57
+ for (const [target, weight] of neighbors) {
58
+ const edgeKey = isDirected ? `${source}->${target}` : [source, target].sort().join("-");
59
+
60
+ // Skip if we've already processed this edge (for undirected graphs)
61
+ if (!isDirected && processedEdges.has(edgeKey)) {
62
+ continue;
63
+ }
64
+
65
+ graph.addEdge(source, target, weight);
66
+ processedEdges.add(edgeKey);
67
+ }
68
+ }
69
+
70
+ return graph;
71
+ }
72
+
73
+ /**
74
+ * Adapter for using Graph algorithms with Map representation
75
+ * Implements a subset of Graph interface methods needed by algorithms
76
+ */
77
+ export class GraphAdapter {
78
+ private map: Map<string, Map<string, number>>;
79
+ public readonly isDirected: boolean;
80
+
81
+ constructor(map: Map<string, Map<string, number>>, isDirected = true) {
82
+ this.map = map;
83
+ this.isDirected = isDirected;
84
+ }
85
+
86
+ /**
87
+ * Get all nodes
88
+ */
89
+ nodes(): {id: NodeId}[] {
90
+ return Array.from(this.map.keys()).map((id) => ({id}));
91
+ }
92
+
93
+ /**
94
+ * Get node count
95
+ */
96
+ get nodeCount(): number {
97
+ return this.map.size;
98
+ }
99
+
100
+ /**
101
+ * Check if node exists
102
+ */
103
+ hasNode(nodeId: NodeId): boolean {
104
+ return this.map.has(String(nodeId));
105
+ }
106
+
107
+ /**
108
+ * Get neighbors of a node
109
+ */
110
+ neighbors(nodeId: NodeId): NodeId[] {
111
+ const neighbors = this.map.get(String(nodeId));
112
+ if (!neighbors) {
113
+ return [];
114
+ }
115
+
116
+ return Array.from(neighbors.keys());
117
+ }
118
+
119
+ /**
120
+ * Get edge between two nodes
121
+ */
122
+ getEdge(source: NodeId, target: NodeId): {source: NodeId, target: NodeId, weight?: number} | null {
123
+ const sourceNeighbors = this.map.get(String(source));
124
+ if (!sourceNeighbors) {
125
+ return null;
126
+ }
127
+
128
+ const weight = sourceNeighbors.get(String(target));
129
+ if (weight === undefined) {
130
+ return null;
131
+ }
132
+
133
+ return {source, target, weight};
134
+ }
135
+
136
+ /**
137
+ * Check if edge exists
138
+ */
139
+ hasEdge(source: NodeId, target: NodeId): boolean {
140
+ const sourceNeighbors = this.map.get(String(source));
141
+ if (!sourceNeighbors) {
142
+ return false;
143
+ }
144
+
145
+ return sourceNeighbors.has(String(target));
146
+ }
147
+
148
+ /**
149
+ * Get all edges
150
+ */
151
+ edges(): {source: NodeId, target: NodeId, weight?: number}[] {
152
+ const edges: {source: NodeId, target: NodeId, weight?: number}[] = [];
153
+ const processedEdges = new Set<string>();
154
+
155
+ for (const [source, neighbors] of this.map) {
156
+ for (const [target, weight] of neighbors) {
157
+ const edgeKey = this.isDirected ? `${source}->${target}` : [source, target].sort().join("-");
158
+
159
+ // Skip if we've already processed this edge (for undirected graphs)
160
+ if (!this.isDirected && processedEdges.has(edgeKey)) {
161
+ continue;
162
+ }
163
+
164
+ edges.push({source, target, weight});
165
+ processedEdges.add(edgeKey);
166
+ }
167
+ }
168
+
169
+ return edges;
170
+ }
171
+
172
+ /**
173
+ * Get edge count
174
+ */
175
+ get edgeCount(): number {
176
+ if (this.isDirected) {
177
+ let count = 0;
178
+ for (const neighbors of this.map.values()) {
179
+ count += neighbors.size;
180
+ }
181
+ return count;
182
+ }
183
+
184
+ // For undirected graphs, count each edge once
185
+ let count = 0;
186
+ const counted = new Set<string>();
187
+
188
+ for (const [source, neighbors] of this.map) {
189
+ for (const target of neighbors.keys()) {
190
+ const edgeKey = [source, target].sort().join("-");
191
+ if (!counted.has(edgeKey)) {
192
+ counted.add(edgeKey);
193
+ count++;
194
+ }
195
+ }
196
+ }
197
+ return count;
198
+ }
199
+
200
+ /**
201
+ * Get node degree
202
+ */
203
+ degree(nodeId: NodeId): number {
204
+ const neighbors = this.map.get(String(nodeId));
205
+ return neighbors ? neighbors.size : 0;
206
+ }
207
+
208
+ /**
209
+ * Get in-degree (for directed graphs)
210
+ */
211
+ inDegree(nodeId: NodeId): number {
212
+ if (!this.isDirected) {
213
+ return this.degree(nodeId);
214
+ }
215
+
216
+ let count = 0;
217
+ const targetId = String(nodeId);
218
+
219
+ for (const neighbors of this.map.values()) {
220
+ if (neighbors.has(targetId)) {
221
+ count++;
222
+ }
223
+ }
224
+
225
+ return count;
226
+ }
227
+
228
+ /**
229
+ * Get out-degree (for directed graphs)
230
+ */
231
+ outDegree(nodeId: NodeId): number {
232
+ return this.degree(nodeId);
233
+ }
234
+
235
+ /**
236
+ * Get underlying map representation
237
+ */
238
+ getMap(): Map<string, Map<string, number>> {
239
+ return this.map;
240
+ }
241
+ }
@@ -0,0 +1,199 @@
1
+ import {Graph} from "../core/graph.js";
2
+ import type {NodeId} from "../types/index.js";
3
+
4
+ /**
5
+ * Reconstructs a path from source to target using a predecessor map
6
+ * @param target - The target node
7
+ * @param predecessor - Map of node to its predecessor in the path
8
+ * @returns Array of nodes from source to target, or empty array if no path exists
9
+ */
10
+ export function reconstructPath<T>(
11
+ target: T,
12
+ predecessor: Map<T, T | null>,
13
+ ): T[] {
14
+ const path: T[] = [];
15
+ let current: T | null = target;
16
+
17
+ // Build path backwards from target to source
18
+ while (current !== null) {
19
+ path.unshift(current);
20
+ const pred = predecessor.get(current);
21
+ if (pred === undefined) {
22
+ // No path exists
23
+ return [];
24
+ }
25
+
26
+ current = pred;
27
+ }
28
+
29
+ return path;
30
+ }
31
+
32
+ /**
33
+ * Find common neighbors between two nodes
34
+ * @param graph - The graph
35
+ * @param source - First node
36
+ * @param target - Second node
37
+ * @param directed - Whether to consider edge direction
38
+ * @returns Set of common neighbor node IDs
39
+ */
40
+ export function getCommonNeighbors(
41
+ graph: Graph,
42
+ source: NodeId,
43
+ target: NodeId,
44
+ directed = false,
45
+ ): Set<NodeId> {
46
+ const sourceNeighbors = new Set(
47
+ directed ? graph.outNeighbors(source) : graph.neighbors(source),
48
+ );
49
+ const targetNeighbors = new Set(
50
+ directed ? graph.outNeighbors(target) : graph.neighbors(target),
51
+ );
52
+
53
+ const common = new Set<NodeId>();
54
+ for (const neighbor of sourceNeighbors) {
55
+ if (targetNeighbors.has(neighbor)) {
56
+ common.add(neighbor);
57
+ }
58
+ }
59
+
60
+ return common;
61
+ }
62
+
63
+ /**
64
+ * Find common neighbors for link prediction in directed graphs
65
+ * This finds nodes that form a path from source to target (source->X->target)
66
+ * @param graph - The graph
67
+ * @param source - First node
68
+ * @param target - Second node
69
+ * @returns Set of intermediate node IDs
70
+ */
71
+ export function getIntermediateNodes(
72
+ graph: Graph,
73
+ source: NodeId,
74
+ target: NodeId,
75
+ ): Set<NodeId> {
76
+ const sourceOutNeighbors = new Set(graph.outNeighbors(source));
77
+ const targetInNeighbors = new Set(graph.inNeighbors(target));
78
+
79
+ const common = new Set<NodeId>();
80
+ for (const neighbor of sourceOutNeighbors) {
81
+ if (targetInNeighbors.has(neighbor)) {
82
+ common.add(neighbor);
83
+ }
84
+ }
85
+
86
+ return common;
87
+ }
88
+
89
+ /**
90
+ * Generate a consistent edge key for undirected graphs
91
+ * @param source - Source node
92
+ * @param target - Target node
93
+ * @param isDirected - Whether the graph is directed
94
+ * @returns A consistent edge identifier
95
+ */
96
+ export function getEdgeKey(
97
+ source: NodeId,
98
+ target: NodeId,
99
+ isDirected: boolean,
100
+ ): string {
101
+ if (isDirected) {
102
+ return `${String(source)}->${String(target)}`;
103
+ }
104
+
105
+ // For undirected graphs, sort to ensure consistency
106
+ const [first, second] = source < target ? [source, target] : [target, source];
107
+ return `${String(first)}-${String(second)}`;
108
+ }
109
+
110
+ /**
111
+ * Calculate total edge weight in a graph
112
+ * @param graph - The graph
113
+ * @returns Total weight of all edges
114
+ */
115
+ export function getTotalEdgeWeight(graph: Graph): number {
116
+ let totalWeight = 0;
117
+ for (const edge of graph.edges()) {
118
+ totalWeight += edge.weight ?? 1;
119
+ }
120
+ return graph.isDirected ? totalWeight : totalWeight / 2;
121
+ }
122
+
123
+ /**
124
+ * Get node degree with optional mode for directed graphs
125
+ * @param graph - The graph
126
+ * @param nodeId - The node ID
127
+ * @param mode - Degree mode for directed graphs
128
+ * @returns Node degree
129
+ */
130
+ export function getNodeDegree(
131
+ graph: Graph,
132
+ nodeId: NodeId,
133
+ mode: "in" | "out" | "total" = "total",
134
+ ): number {
135
+ if (!graph.isDirected || mode === "total") {
136
+ return graph.degree(nodeId);
137
+ }
138
+
139
+ if (mode === "in") {
140
+ return graph.inDegree(nodeId);
141
+ }
142
+
143
+ return graph.outDegree(nodeId);
144
+ }
145
+
146
+ /**
147
+ * Convert directed graph to undirected
148
+ * @param graph - The directed graph
149
+ * @returns A new undirected graph
150
+ */
151
+ export function makeUndirected(graph: Graph): Graph {
152
+ if (!graph.isDirected) {
153
+ return graph;
154
+ }
155
+
156
+ const undirected = new Graph({directed: false});
157
+
158
+ // Add all nodes
159
+ for (const node of graph.nodes()) {
160
+ undirected.addNode(node.id, node.data);
161
+ }
162
+
163
+ // Add edges (will automatically be made bidirectional)
164
+ for (const edge of graph.edges()) {
165
+ // For undirected, we only add each edge once
166
+ if (!undirected.hasEdge(edge.source, edge.target)) {
167
+ undirected.addEdge(edge.source, edge.target, edge.weight, edge.data);
168
+ }
169
+ }
170
+
171
+ return undirected;
172
+ }
173
+
174
+ /**
175
+ * Renumber communities consecutively starting from 0
176
+ * @param communities - Map of node to community ID
177
+ * @returns Map with renumbered community IDs
178
+ */
179
+ export function renumberCommunities<T>(
180
+ communities: Map<T, number>,
181
+ ): Map<T, number> {
182
+ const uniqueCommunities = new Set(communities.values());
183
+ const remapping = new Map<number, number>();
184
+ let newId = 0;
185
+
186
+ for (const community of uniqueCommunities) {
187
+ remapping.set(community, newId++);
188
+ }
189
+
190
+ const renumbered = new Map<T, number>();
191
+ for (const [node, community] of communities) {
192
+ const newCommunity = remapping.get(community);
193
+ if (newCommunity !== undefined) {
194
+ renumbered.set(node, newCommunity);
195
+ }
196
+ }
197
+
198
+ return renumbered;
199
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./algorithm-utilities.js";
2
+ export * from "./graph-utilities.js";
3
+ export * from "./math-utilities.js";
4
+ export * from "./matrix-utilities.js";
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Seeded random number generator for reproducible results
3
+ */
4
+ export class SeededRandom {
5
+ private seed: number;
6
+ private readonly m = 0x80000000; // 2**31
7
+ private readonly a = 1103515245;
8
+ private readonly c = 12345;
9
+
10
+ constructor(seed: number) {
11
+ // Handle negative seeds correctly
12
+ this.seed = ((seed % this.m) + this.m) % this.m;
13
+ }
14
+
15
+ /**
16
+ * Generate next random number between 0 and 1
17
+ */
18
+ next(): number {
19
+ this.seed = ((this.a * this.seed) + this.c) % this.m;
20
+ return this.seed / (this.m - 1);
21
+ }
22
+
23
+ /**
24
+ * Create a generator function for backward compatibility
25
+ */
26
+ static createGenerator(seed: number): () => number {
27
+ const rng = new SeededRandom(seed);
28
+ return () => rng.next();
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Fisher-Yates shuffle algorithm
34
+ * @param array - Array to shuffle (will be modified in place)
35
+ * @param rng - Optional random number generator (defaults to Math.random)
36
+ * @returns The shuffled array (same reference as input)
37
+ */
38
+ export function shuffle<T>(array: T[], rng: () => number = Math.random): T[] {
39
+ for (let i = array.length - 1; i > 0; i--) {
40
+ const j = Math.floor(rng() * (i + 1));
41
+ const temp = array[i];
42
+ const tempJ = array[j];
43
+ if (temp !== undefined && tempJ !== undefined) {
44
+ array[i] = tempJ;
45
+ array[j] = temp;
46
+ }
47
+ }
48
+ return array;
49
+ }
50
+
51
+ /**
52
+ * Calculate Euclidean distance between two vectors
53
+ * @param a - First vector
54
+ * @param b - Second vector
55
+ * @returns Euclidean distance
56
+ * @throws Error if vectors have different lengths
57
+ */
58
+ export function euclideanDistance(a: number[], b: number[]): number {
59
+ if (a.length !== b.length) {
60
+ throw new Error("Vectors must have same length");
61
+ }
62
+
63
+ let sum = 0;
64
+ for (let i = 0; i < a.length; i++) {
65
+ const aVal = a[i];
66
+ const bVal = b[i];
67
+ if (aVal !== undefined && bVal !== undefined) {
68
+ const diff = aVal - bVal;
69
+ sum += diff * diff;
70
+ }
71
+ }
72
+ return Math.sqrt(sum);
73
+ }
74
+
75
+ export const normalize = {
76
+ /**
77
+ * Min-max normalization to [0, 1] range
78
+ */
79
+ minMax(values: Map<string, number>): void {
80
+ const vals = Array.from(values.values());
81
+ const min = Math.min(... vals);
82
+ const max = Math.max(... vals);
83
+ const range = max - min;
84
+
85
+ if (range === 0) {
86
+ return;
87
+ }
88
+
89
+ for (const [key, value] of values) {
90
+ values.set(key, (value - min) / range);
91
+ }
92
+ },
93
+
94
+ /**
95
+ * Normalize by maximum value
96
+ */
97
+ byMax(values: Map<string, number>): void {
98
+ const max = Math.max(... values.values());
99
+ if (max === 0) {
100
+ return;
101
+ }
102
+
103
+ for (const [key, value] of values) {
104
+ values.set(key, value / max);
105
+ }
106
+ },
107
+
108
+ /**
109
+ * L2 (Euclidean) normalization
110
+ */
111
+ l2Norm(values: Map<string, number>): void {
112
+ const sumSquares = Array.from(values.values())
113
+ .reduce((sum, val) => sum + (val * val), 0);
114
+ const norm = Math.sqrt(sumSquares);
115
+
116
+ if (norm === 0) {
117
+ return;
118
+ }
119
+
120
+ for (const [key, value] of values) {
121
+ values.set(key, value / norm);
122
+ }
123
+ },
124
+
125
+ /**
126
+ * Normalize to sum to 1
127
+ */
128
+ sumToOne(values: Map<string, number>): void {
129
+ const sum = Array.from(values.values())
130
+ .reduce((acc, val) => acc + val, 0);
131
+
132
+ if (sum === 0) {
133
+ return;
134
+ }
135
+
136
+ for (const [key, value] of values) {
137
+ values.set(key, value / sum);
138
+ }
139
+ },
140
+ };
@@ -0,0 +1,2 @@
1
+ // Matrix utility functions will be added here
2
+ export {};
@@ -0,0 +1,81 @@
1
+ import type {Graph} from "../core/graph.js";
2
+
3
+ /**
4
+ * Common interface for algorithm options with optimization support
5
+ */
6
+ export interface OptimizableOptions {
7
+ /**
8
+ * Whether to use optimized implementations
9
+ * - true: Force use of optimizations
10
+ * - false: Disable optimizations
11
+ * - "auto": Automatically decide based on graph size (default)
12
+ */
13
+ optimized?: boolean | "auto";
14
+ }
15
+
16
+ /**
17
+ * Check if a graph should use optimizations based on its size
18
+ */
19
+ function shouldUseOptimizations(nodeCount: number, edgeCount: number): boolean {
20
+ // Use optimizations for graphs with more than 10k nodes or 100k edges
21
+ return nodeCount > 10000 || edgeCount > 100000;
22
+ }
23
+
24
+ /**
25
+ * Determine if optimizations should be used for a given graph
26
+ */
27
+ export function shouldOptimize(
28
+ graph: Graph,
29
+ options: OptimizableOptions = {},
30
+ ): boolean {
31
+ const {optimized = "auto"} = options;
32
+
33
+ if (optimized === "auto") {
34
+ // Check graph size
35
+ return shouldUseOptimizations(graph.nodeCount, graph.totalEdgeCount);
36
+ }
37
+
38
+ return optimized;
39
+ }
40
+
41
+ /**
42
+ * Merge optimization options with algorithm-specific options
43
+ */
44
+ export function mergeOptimizationOptions<T extends Record<string, unknown>>(
45
+ graph: Graph,
46
+ options: T & OptimizableOptions,
47
+ ): T & {optimized?: boolean} {
48
+ const {optimized: userOptimized, ... rest} = options;
49
+
50
+ return {
51
+ ... rest,
52
+ optimized: shouldOptimize(graph, userOptimized !== undefined ? {optimized: userOptimized} : {}),
53
+ } as T & {optimized?: boolean};
54
+ }
55
+
56
+ /**
57
+ * Create an optimized version of an algorithm
58
+ * @example
59
+ * const optimizedBetweenness = createOptimizedAlgorithm(
60
+ * betweennessCentrality,
61
+ * (graph, options) => ({...options, optimized: true})
62
+ * );
63
+ */
64
+ export function createOptimizedAlgorithm<
65
+ TArgs extends [Graph, ...unknown[]],
66
+ TResult,
67
+ >(
68
+ algorithm: (... args: TArgs) => TResult,
69
+ optionsTransformer?: (graph: Graph, options: unknown) => unknown,
70
+ ): (... args: TArgs) => TResult {
71
+ return (... args: TArgs): TResult => {
72
+ const [graph, options, ... rest] = args;
73
+
74
+ if (optionsTransformer && options !== undefined) {
75
+ const transformedOptions = optionsTransformer(graph, options);
76
+ return algorithm(... ([graph, transformedOptions, ... rest] as unknown as TArgs));
77
+ }
78
+
79
+ return algorithm(... args);
80
+ };
81
+ }