@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,496 @@
1
+ import type {Graph} from "../../core/graph.js";
2
+ import {PriorityQueue} from "../../data-structures/priority-queue.js";
3
+ import {CSRGraph} from "../../optimized/csr-graph.js";
4
+ import {DirectionOptimizedBFS} from "../../optimized/direction-optimized-bfs.js";
5
+ import {toCSRGraph} from "../../optimized/graph-adapter.js";
6
+ import type {NodeId} from "../../types/index.js";
7
+
8
+ /**
9
+ * Threshold for switching to CSR-optimized implementations.
10
+ * Based on benchmarks showing CSR benefits at this scale due to
11
+ * improved cache locality and reduced memory allocation overhead.
12
+ */
13
+ const LARGE_GRAPH_THRESHOLD = 10000;
14
+
15
+ /**
16
+ * BFS that tracks shortest path counts (for betweenness centrality)
17
+ *
18
+ * This variant of BFS computes:
19
+ * - Shortest distances from source to all reachable nodes
20
+ * - All predecessors on shortest paths
21
+ * - Number of shortest paths (sigma)
22
+ * - Stack of nodes in reverse BFS order
23
+ */
24
+ export function bfsWithPathCounting(
25
+ graph: Graph,
26
+ source: NodeId,
27
+ options: {optimized?: boolean} = {},
28
+ ): {
29
+ distances: Map<NodeId, number>;
30
+ predecessors: Map<NodeId, NodeId[]>;
31
+ sigma: Map<NodeId, number>;
32
+ stack: NodeId[];
33
+ } {
34
+ const useOptimized = options.optimized ?? false;
35
+
36
+ // Use CSR format for large graphs
37
+ if (useOptimized && graph.nodeCount > LARGE_GRAPH_THRESHOLD) {
38
+ const csrGraph = toCSRGraph(graph);
39
+ return bfsWithPathCountingCSR(csrGraph, source);
40
+ }
41
+
42
+ const distances = new Map<NodeId, number>();
43
+ const predecessors = new Map<NodeId, NodeId[]>();
44
+ const sigma = new Map<NodeId, number>();
45
+ const stack: NodeId[] = [];
46
+ const queue: NodeId[] = [];
47
+
48
+ // Initialize
49
+ distances.set(source, 0);
50
+ sigma.set(source, 1);
51
+ queue.push(source);
52
+
53
+ while (queue.length > 0) {
54
+ const current = queue.shift();
55
+ if (current === undefined) {
56
+ continue;
57
+ }
58
+
59
+ stack.push(current);
60
+
61
+ const currentDistance = distances.get(current) ?? 0;
62
+ const currentSigma = sigma.get(current) ?? 0;
63
+
64
+ // Process neighbors
65
+ const neighbors = graph.neighbors(current);
66
+ for (const neighbor of neighbors) {
67
+ // First time visiting this neighbor
68
+ if (!distances.has(neighbor)) {
69
+ distances.set(neighbor, currentDistance + 1);
70
+ queue.push(neighbor);
71
+ }
72
+
73
+ // Found a shortest path to neighbor
74
+ if (distances.get(neighbor) === currentDistance + 1) {
75
+ const neighborSigma = sigma.get(neighbor) ?? 0;
76
+ sigma.set(neighbor, neighborSigma + currentSigma);
77
+
78
+ const preds = predecessors.get(neighbor) ?? [];
79
+ preds.push(current);
80
+ predecessors.set(neighbor, preds);
81
+ }
82
+ }
83
+ }
84
+
85
+ return {
86
+ distances,
87
+ predecessors,
88
+ sigma,
89
+ stack,
90
+ };
91
+ }
92
+
93
+ /**
94
+ * BFS that only returns distances (for closeness centrality)
95
+ *
96
+ * Optimized variant that skips predecessor tracking
97
+ */
98
+ export function bfsDistancesOnly(
99
+ graph: Graph,
100
+ source: NodeId,
101
+ cutoff?: number,
102
+ options: {optimized?: boolean} = {},
103
+ ): Map<NodeId, number> {
104
+ const useOptimized = options.optimized ?? false;
105
+
106
+ // Use CSR format for large graphs
107
+ if (useOptimized && graph.nodeCount > LARGE_GRAPH_THRESHOLD) {
108
+ const csrGraph = toCSRGraph(graph);
109
+ return bfsDistancesOnlyCSR(csrGraph, source, cutoff);
110
+ }
111
+
112
+ const distances = new Map<NodeId, number>();
113
+ const queue: NodeId[] = [];
114
+
115
+ distances.set(source, 0);
116
+ queue.push(source);
117
+
118
+ while (queue.length > 0) {
119
+ const current = queue.shift();
120
+ if (current === undefined) {
121
+ continue;
122
+ }
123
+
124
+ const currentDistance = distances.get(current) ?? 0;
125
+
126
+ // Stop if we've reached the cutoff distance
127
+ if (cutoff !== undefined && currentDistance >= cutoff) {
128
+ continue;
129
+ }
130
+
131
+ const neighbors = graph.neighbors(current);
132
+ for (const neighbor of neighbors) {
133
+ if (!distances.has(neighbor)) {
134
+ distances.set(neighbor, currentDistance + 1);
135
+ queue.push(neighbor);
136
+ }
137
+ }
138
+ }
139
+
140
+ return distances;
141
+ }
142
+
143
+ /**
144
+ * BFS for bipartite checking with partition sets
145
+ *
146
+ * Returns whether the graph is bipartite and the two partitions if it is
147
+ */
148
+ export function bfsColoringWithPartitions(
149
+ graph: Graph,
150
+ ): {
151
+ isBipartite: boolean;
152
+ partitions?: [Set<NodeId>, Set<NodeId>];
153
+ } {
154
+ const colors = new Map<NodeId, number>();
155
+ const partitionA = new Set<NodeId>();
156
+ const partitionB = new Set<NodeId>();
157
+
158
+ // Handle disconnected components
159
+ for (const node of graph.nodes()) {
160
+ if (!colors.has(node.id)) {
161
+ const queue: NodeId[] = [node.id];
162
+ colors.set(node.id, 0);
163
+ partitionA.add(node.id);
164
+
165
+ while (queue.length > 0) {
166
+ const current = queue.shift();
167
+ if (current === undefined) {
168
+ continue;
169
+ }
170
+
171
+ const currentColor = colors.get(current) ?? 0;
172
+ const nextColor = 1 - currentColor;
173
+
174
+ for (const neighbor of graph.neighbors(current)) {
175
+ if (!colors.has(neighbor)) {
176
+ colors.set(neighbor, nextColor);
177
+ queue.push(neighbor);
178
+
179
+ if (nextColor === 0) {
180
+ partitionA.add(neighbor);
181
+ } else {
182
+ partitionB.add(neighbor);
183
+ }
184
+ } else if (colors.get(neighbor) === currentColor) {
185
+ // Same color as current node - not bipartite
186
+ return {isBipartite: false};
187
+ }
188
+ }
189
+ }
190
+ }
191
+ }
192
+
193
+ return {
194
+ isBipartite: true,
195
+ partitions: [partitionA, partitionB],
196
+ };
197
+ }
198
+
199
+ /**
200
+ * BFS for finding augmenting paths (for flow algorithms)
201
+ *
202
+ * Finds a path from source to sink in a residual graph with positive capacity
203
+ */
204
+ export function bfsAugmentingPath(
205
+ residualGraph: Map<string, Map<string, number>>,
206
+ source: string,
207
+ sink: string,
208
+ ): {
209
+ path: string[];
210
+ pathCapacity: number;
211
+ } | null {
212
+ const parent = new Map<string, string | null>();
213
+ const queue: string[] = [];
214
+
215
+ parent.set(source, null);
216
+ queue.push(source);
217
+
218
+ while (queue.length > 0) {
219
+ const current = queue.shift();
220
+ if (current === undefined) {
221
+ continue;
222
+ }
223
+
224
+ if (current === sink) {
225
+ // Reconstruct path
226
+ const path: string[] = [];
227
+ let node: string | null = sink;
228
+ let pathCapacity = Infinity;
229
+
230
+ while (node !== null) {
231
+ path.unshift(node);
232
+ const parentNode = parent.get(node);
233
+
234
+ if (parentNode !== null && parentNode !== undefined) {
235
+ const capacity = residualGraph.get(parentNode)?.get(node) ?? 0;
236
+ pathCapacity = Math.min(pathCapacity, capacity);
237
+ }
238
+
239
+ node = parentNode ?? null;
240
+ }
241
+
242
+ return {path, pathCapacity};
243
+ }
244
+
245
+ const neighbors = residualGraph.get(current);
246
+ if (neighbors) {
247
+ for (const [neighbor, capacity] of neighbors) {
248
+ if (!parent.has(neighbor) && capacity > 0) {
249
+ parent.set(neighbor, current);
250
+ queue.push(neighbor);
251
+ }
252
+ }
253
+ }
254
+ }
255
+
256
+ return null;
257
+ }
258
+
259
+ /**
260
+ * BFS for weighted graphs using priority queue (simplified Dijkstra)
261
+ *
262
+ * Returns distances from source using edge weights
263
+ */
264
+ export function bfsWeightedDistances(
265
+ graph: Graph,
266
+ source: NodeId,
267
+ cutoff?: number,
268
+ options: {optimized?: boolean} = {},
269
+ ): Map<NodeId, number> {
270
+ const useOptimized = options.optimized ?? false;
271
+
272
+ // Use CSR format for large graphs
273
+ if (useOptimized && graph.nodeCount > LARGE_GRAPH_THRESHOLD) {
274
+ const csrGraph = toCSRGraph(graph);
275
+ return bfsWeightedDistancesCSR(csrGraph, source, cutoff);
276
+ }
277
+
278
+ const distances = new Map<NodeId, number>();
279
+ const visited = new Set<NodeId>();
280
+
281
+ // Use efficient priority queue instead of array-based queue
282
+ const queue = new PriorityQueue<NodeId>();
283
+
284
+ distances.set(source, 0);
285
+ queue.enqueue(source, 0);
286
+
287
+ while (!queue.isEmpty()) {
288
+ const current = queue.dequeue();
289
+ if (current === undefined) {
290
+ continue;
291
+ }
292
+
293
+ const currentDist = distances.get(current) ?? 0;
294
+
295
+ if (visited.has(current)) {
296
+ continue;
297
+ }
298
+
299
+ visited.add(current);
300
+
301
+ // Stop if we've reached the cutoff distance
302
+ if (cutoff !== undefined && currentDist >= cutoff) {
303
+ continue;
304
+ }
305
+
306
+ for (const neighbor of graph.neighbors(current)) {
307
+ if (!visited.has(neighbor)) {
308
+ const edge = graph.getEdge(current, neighbor);
309
+ const weight = edge?.weight ?? 1;
310
+ const newDistance = currentDist + weight;
311
+
312
+ const oldDistance = distances.get(neighbor);
313
+ if (oldDistance === undefined || newDistance < oldDistance) {
314
+ distances.set(neighbor, newDistance);
315
+ queue.enqueue(neighbor, newDistance);
316
+ }
317
+ }
318
+ }
319
+ }
320
+
321
+ return distances;
322
+ }
323
+
324
+ /**
325
+ * CSR-optimized version of bfsWithPathCounting
326
+ */
327
+ function bfsWithPathCountingCSR(
328
+ graph: CSRGraph,
329
+ source: NodeId,
330
+ ): {
331
+ distances: Map<NodeId, number>;
332
+ predecessors: Map<NodeId, NodeId[]>;
333
+ sigma: Map<NodeId, number>;
334
+ stack: NodeId[];
335
+ } {
336
+ const distances = new Map<NodeId, number>();
337
+ const predecessors = new Map<NodeId, NodeId[]>();
338
+ const sigma = new Map<NodeId, number>();
339
+ const stack: NodeId[] = [];
340
+ const queue: NodeId[] = [];
341
+
342
+ // Initialize
343
+ distances.set(source, 0);
344
+ sigma.set(source, 1);
345
+ queue.push(source);
346
+
347
+ while (queue.length > 0) {
348
+ const current = queue.shift();
349
+ if (current === undefined) {
350
+ continue;
351
+ }
352
+
353
+ stack.push(current);
354
+
355
+ const currentDistance = distances.get(current) ?? 0;
356
+ const currentSigma = sigma.get(current) ?? 0;
357
+
358
+ // Process neighbors using CSR iteration
359
+ for (const neighbor of graph.neighbors(current)) {
360
+ // First time visiting this neighbor
361
+ if (!distances.has(neighbor)) {
362
+ distances.set(neighbor, currentDistance + 1);
363
+ queue.push(neighbor);
364
+ }
365
+
366
+ // Found a shortest path to neighbor
367
+ if (distances.get(neighbor) === currentDistance + 1) {
368
+ const neighborSigma = sigma.get(neighbor) ?? 0;
369
+ sigma.set(neighbor, neighborSigma + currentSigma);
370
+
371
+ const preds = predecessors.get(neighbor) ?? [];
372
+ preds.push(current);
373
+ predecessors.set(neighbor, preds);
374
+ }
375
+ }
376
+ }
377
+
378
+ return {
379
+ distances,
380
+ predecessors,
381
+ sigma,
382
+ stack,
383
+ };
384
+ }
385
+
386
+ /**
387
+ * CSR-optimized version of bfsDistancesOnly
388
+ */
389
+ function bfsDistancesOnlyCSR(
390
+ graph: CSRGraph,
391
+ source: NodeId,
392
+ cutoff?: number,
393
+ ): Map<NodeId, number> {
394
+ // Use Direction-Optimized BFS for best performance
395
+ if (graph.nodeCount() > LARGE_GRAPH_THRESHOLD) {
396
+ const dobfs = new DirectionOptimizedBFS(graph);
397
+ const result = dobfs.search(source);
398
+
399
+ // Filter by cutoff if specified
400
+ if (cutoff !== undefined) {
401
+ const filtered = new Map<NodeId, number>();
402
+ for (const [nodeId, distance] of result.distances) {
403
+ if (distance <= cutoff) {
404
+ filtered.set(nodeId, distance);
405
+ }
406
+ }
407
+ return filtered;
408
+ }
409
+
410
+ return result.distances;
411
+ }
412
+
413
+ // Fallback to standard BFS on CSR
414
+ const distances = new Map<NodeId, number>();
415
+ const queue: NodeId[] = [];
416
+
417
+ distances.set(source, 0);
418
+ queue.push(source);
419
+
420
+ while (queue.length > 0) {
421
+ const current = queue.shift();
422
+ if (current === undefined) {
423
+ continue;
424
+ }
425
+
426
+ const currentDistance = distances.get(current) ?? 0;
427
+
428
+ // Stop if we've reached the cutoff distance
429
+ if (cutoff !== undefined && currentDistance >= cutoff) {
430
+ continue;
431
+ }
432
+
433
+ for (const neighbor of graph.neighbors(current)) {
434
+ if (!distances.has(neighbor)) {
435
+ distances.set(neighbor, currentDistance + 1);
436
+ queue.push(neighbor);
437
+ }
438
+ }
439
+ }
440
+
441
+ return distances;
442
+ }
443
+
444
+ /**
445
+ * CSR-optimized version of bfsWeightedDistances
446
+ */
447
+ function bfsWeightedDistancesCSR(
448
+ graph: CSRGraph,
449
+ source: NodeId,
450
+ cutoff?: number,
451
+ ): Map<NodeId, number> {
452
+ const distances = new Map<NodeId, number>();
453
+ const visited = new Set<NodeId>();
454
+
455
+ // Use efficient priority queue
456
+ const queue = new PriorityQueue<NodeId>();
457
+
458
+ distances.set(source, 0);
459
+ queue.enqueue(source, 0);
460
+
461
+ while (!queue.isEmpty()) {
462
+ const current = queue.dequeue();
463
+ if (current === undefined) {
464
+ continue;
465
+ }
466
+
467
+ const currentDist = distances.get(current) ?? 0;
468
+
469
+ if (visited.has(current)) {
470
+ continue;
471
+ }
472
+
473
+ visited.add(current);
474
+
475
+ // Stop if we've reached the cutoff distance
476
+ if (cutoff !== undefined && currentDist >= cutoff) {
477
+ continue;
478
+ }
479
+
480
+ for (const neighbor of graph.neighbors(current)) {
481
+ if (!visited.has(neighbor)) {
482
+ // Use actual edge weight from CSR graph, default to 1
483
+ const weight = graph.getEdgeWeight(current, neighbor) ?? 1;
484
+ const newDistance = currentDist + weight;
485
+
486
+ const oldDistance = distances.get(neighbor);
487
+ if (oldDistance === undefined || newDistance < oldDistance) {
488
+ distances.set(neighbor, newDistance);
489
+ queue.enqueue(neighbor, newDistance);
490
+ }
491
+ }
492
+ }
493
+ }
494
+
495
+ return distances;
496
+ }