@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,433 @@
1
+ import type {Graph} from "../../core/graph.js";
2
+ import {CSRGraph} from "../../optimized/csr-graph.js";
3
+ import {DirectionOptimizedBFS} from "../../optimized/direction-optimized-bfs.js";
4
+ import {toCSRGraph} from "../../optimized/graph-adapter.js";
5
+ import type {NodeId, ShortestPathResult, TraversalOptions, TraversalResult} from "../../types/index.js";
6
+ import {reconstructPath} from "../../utils/graph-utilities.js";
7
+
8
+ /**
9
+ * Unified BFS implementation that automatically optimizes for large graphs
10
+ *
11
+ * This module provides a single, user-friendly BFS implementation that:
12
+ * - Automatically uses Direction-Optimized BFS for large graphs (>10k nodes)
13
+ * - Maintains backward compatibility with existing APIs
14
+ * - Requires no configuration or understanding of optimization thresholds
15
+ * - Provides the best performance by default
16
+ */
17
+
18
+ // Cache for CSR conversions to avoid repeated conversions
19
+ const csrCache = new WeakMap<Graph, CSRGraph>();
20
+
21
+ /**
22
+ * Get or create CSR representation of a graph
23
+ */
24
+ function getCSRGraph(graph: Graph): CSRGraph {
25
+ let csrGraph = csrCache.get(graph);
26
+ if (!csrGraph) {
27
+ csrGraph = toCSRGraph(graph);
28
+ csrCache.set(graph, csrGraph);
29
+ }
30
+
31
+ return csrGraph;
32
+ }
33
+
34
+ /**
35
+ * Perform breadth-first search starting from a given node
36
+ *
37
+ * Automatically uses the most optimized implementation based on graph size.
38
+ * No configuration needed - just call this function for the best performance.
39
+ */
40
+ export function breadthFirstSearch(
41
+ graph: Graph,
42
+ startNode: NodeId,
43
+ options: TraversalOptions = {},
44
+ ): TraversalResult {
45
+ if (!graph.hasNode(startNode)) {
46
+ throw new Error(`Start node ${String(startNode)} not found in graph`);
47
+ }
48
+
49
+ // Automatically use optimized implementation for large graphs
50
+ if (graph.nodeCount > 10000) {
51
+ return breadthFirstSearchOptimized(graph, startNode, options);
52
+ }
53
+
54
+ // Standard implementation for smaller graphs
55
+ return breadthFirstSearchStandard(graph, startNode, options);
56
+ }
57
+
58
+ /**
59
+ * Standard BFS implementation for smaller graphs
60
+ */
61
+ function breadthFirstSearchStandard(
62
+ graph: Graph,
63
+ startNode: NodeId,
64
+ options: TraversalOptions = {},
65
+ ): TraversalResult {
66
+ const visited = new Set<NodeId>();
67
+ const queue: {node: NodeId, level: number}[] = [];
68
+ const order: NodeId[] = [];
69
+ const tree = new Map<NodeId, NodeId | null>();
70
+
71
+ // Initialize with start node
72
+ queue.push({node: startNode, level: 0});
73
+ visited.add(startNode);
74
+ tree.set(startNode, null);
75
+
76
+ while (queue.length > 0) {
77
+ const current = queue.shift();
78
+ if (!current) {
79
+ break;
80
+ }
81
+
82
+ order.push(current.node);
83
+
84
+ // Call visitor callback if provided
85
+ if (options.visitCallback) {
86
+ options.visitCallback(current.node, current.level);
87
+ }
88
+
89
+ // Early termination if target found
90
+ if (options.targetNode && current.node === options.targetNode) {
91
+ break;
92
+ }
93
+
94
+ // Explore neighbors
95
+ for (const neighbor of graph.neighbors(current.node)) {
96
+ if (!visited.has(neighbor)) {
97
+ visited.add(neighbor);
98
+ tree.set(neighbor, current.node);
99
+ queue.push({node: neighbor, level: current.level + 1});
100
+ }
101
+ }
102
+ }
103
+
104
+ return {visited, order, tree};
105
+ }
106
+
107
+ /**
108
+ * Optimized BFS implementation using Direction-Optimized BFS
109
+ */
110
+ function breadthFirstSearchOptimized(
111
+ graph: Graph,
112
+ startNode: NodeId,
113
+ options: TraversalOptions = {},
114
+ ): TraversalResult {
115
+ const csrGraph = getCSRGraph(graph);
116
+
117
+ const dobfs = new DirectionOptimizedBFS(csrGraph, {
118
+ alpha: 15.0,
119
+ beta: 20.0,
120
+ });
121
+
122
+ const result = dobfs.search(startNode);
123
+
124
+ // Convert result to TraversalResult format
125
+ const visited = new Set<NodeId>();
126
+ const order: NodeId[] = [];
127
+ const tree = new Map<NodeId, NodeId | null>();
128
+
129
+ // Build traversal order using BFS from distances
130
+ const nodesByDistance = new Map<number, NodeId[]>();
131
+ let maxDistance = 0;
132
+
133
+ for (const [nodeId, distance] of result.distances) {
134
+ visited.add(nodeId);
135
+ tree.set(nodeId, result.parents.get(nodeId) ?? null);
136
+
137
+ if (!nodesByDistance.has(distance)) {
138
+ nodesByDistance.set(distance, []);
139
+ }
140
+
141
+ nodesByDistance.get(distance)?.push(nodeId);
142
+ maxDistance = Math.max(maxDistance, distance);
143
+ }
144
+
145
+ // Reconstruct BFS order
146
+ for (let d = 0; d <= maxDistance; d++) {
147
+ const nodes = nodesByDistance.get(d);
148
+ if (nodes) {
149
+ order.push(... nodes);
150
+ }
151
+ }
152
+
153
+ // Handle visit callback if provided
154
+ if (options.visitCallback) {
155
+ for (const [nodeId, distance] of result.distances) {
156
+ options.visitCallback(nodeId, distance);
157
+ }
158
+ }
159
+
160
+ return {visited, order, tree};
161
+ }
162
+
163
+ /**
164
+ * Find shortest path between two nodes using BFS
165
+ *
166
+ * Automatically optimized for large graphs. Returns null if no path exists.
167
+ */
168
+ export function shortestPathBFS(
169
+ graph: Graph,
170
+ source: NodeId,
171
+ target: NodeId,
172
+ ): ShortestPathResult | null {
173
+ if (!graph.hasNode(source)) {
174
+ throw new Error(`Source node ${String(source)} not found in graph`);
175
+ }
176
+
177
+ if (!graph.hasNode(target)) {
178
+ throw new Error(`Target node ${String(target)} not found in graph`);
179
+ }
180
+
181
+ // Special case: source equals target
182
+ if (source === target) {
183
+ return {
184
+ distance: 0,
185
+ path: [source],
186
+ predecessor: new Map([[source, null]]),
187
+ };
188
+ }
189
+
190
+ // Use optimized implementation for large graphs
191
+ if (graph.nodeCount > 10000) {
192
+ return shortestPathBFSOptimized(graph, source, target);
193
+ }
194
+
195
+ // Standard implementation
196
+ return shortestPathBFSStandard(graph, source, target);
197
+ }
198
+
199
+ /**
200
+ * Standard shortest path BFS
201
+ */
202
+ function shortestPathBFSStandard(
203
+ graph: Graph,
204
+ source: NodeId,
205
+ target: NodeId,
206
+ ): ShortestPathResult | null {
207
+ const visited = new Set<NodeId>();
208
+ const queue: {node: NodeId, distance: number}[] = [];
209
+ const predecessor = new Map<NodeId, NodeId | null>();
210
+
211
+ // Initialize BFS
212
+ queue.push({node: source, distance: 0});
213
+ visited.add(source);
214
+ predecessor.set(source, null);
215
+
216
+ while (queue.length > 0) {
217
+ const current = queue.shift();
218
+ if (!current) {
219
+ break;
220
+ }
221
+
222
+ // Target found
223
+ if (current.node === target) {
224
+ const path = reconstructPath(target, predecessor);
225
+ return {
226
+ distance: current.distance,
227
+ path,
228
+ predecessor,
229
+ };
230
+ }
231
+
232
+ // Explore neighbors
233
+ for (const neighbor of graph.neighbors(current.node)) {
234
+ if (!visited.has(neighbor)) {
235
+ visited.add(neighbor);
236
+ predecessor.set(neighbor, current.node);
237
+ queue.push({node: neighbor, distance: current.distance + 1});
238
+ }
239
+ }
240
+ }
241
+
242
+ // No path found
243
+ return null;
244
+ }
245
+
246
+ /**
247
+ * Optimized shortest path using Direction-Optimized BFS
248
+ */
249
+ function shortestPathBFSOptimized(
250
+ graph: Graph,
251
+ source: NodeId,
252
+ target: NodeId,
253
+ ): ShortestPathResult | null {
254
+ const csrGraph = getCSRGraph(graph);
255
+
256
+ const dobfs = new DirectionOptimizedBFS(csrGraph, {
257
+ alpha: 15.0,
258
+ beta: 20.0,
259
+ });
260
+
261
+ // Perform BFS
262
+ const result = dobfs.search(source);
263
+
264
+ // Check if target was reached
265
+ const distance = result.distances.get(target);
266
+ if (distance === undefined) {
267
+ return null; // No path found
268
+ }
269
+
270
+ // Reconstruct path
271
+ const path = reconstructPath(target, result.parents);
272
+
273
+ return {
274
+ distance,
275
+ path,
276
+ predecessor: result.parents,
277
+ };
278
+ }
279
+
280
+ /**
281
+ * Find shortest paths from source to all reachable nodes
282
+ *
283
+ * Automatically optimized for large graphs.
284
+ */
285
+ export function singleSourceShortestPathBFS(
286
+ graph: Graph,
287
+ source: NodeId,
288
+ ): Map<NodeId, ShortestPathResult> {
289
+ if (!graph.hasNode(source)) {
290
+ throw new Error(`Source node ${String(source)} not found in graph`);
291
+ }
292
+
293
+ // Use optimized implementation for large graphs
294
+ if (graph.nodeCount > 10000) {
295
+ return singleSourceShortestPathBFSOptimized(graph, source);
296
+ }
297
+
298
+ // Standard implementation
299
+ return singleSourceShortestPathBFSStandard(graph, source);
300
+ }
301
+
302
+ /**
303
+ * Standard single-source shortest paths
304
+ */
305
+ function singleSourceShortestPathBFSStandard(
306
+ graph: Graph,
307
+ source: NodeId,
308
+ ): Map<NodeId, ShortestPathResult> {
309
+ const results = new Map<NodeId, ShortestPathResult>();
310
+ const visited = new Set<NodeId>();
311
+ const queue: {node: NodeId, distance: number}[] = [];
312
+ const predecessor = new Map<NodeId, NodeId | null>();
313
+ const distances = new Map<NodeId, number>();
314
+
315
+ // Initialize BFS
316
+ queue.push({node: source, distance: 0});
317
+ visited.add(source);
318
+ predecessor.set(source, null);
319
+ distances.set(source, 0);
320
+
321
+ while (queue.length > 0) {
322
+ const current = queue.shift();
323
+ if (!current) {
324
+ break;
325
+ }
326
+
327
+ // Explore neighbors
328
+ for (const neighbor of graph.neighbors(current.node)) {
329
+ if (!visited.has(neighbor)) {
330
+ visited.add(neighbor);
331
+ predecessor.set(neighbor, current.node);
332
+ distances.set(neighbor, current.distance + 1);
333
+ queue.push({node: neighbor, distance: current.distance + 1});
334
+ }
335
+ }
336
+ }
337
+
338
+ // Build results after BFS completes
339
+ // This avoids copying the predecessor map for each node
340
+ for (const [node, distance] of distances) {
341
+ const path = reconstructPath(node, predecessor);
342
+ results.set(node, {
343
+ distance,
344
+ path,
345
+ predecessor, // Share the same predecessor map
346
+ });
347
+ }
348
+
349
+ return results;
350
+ }
351
+
352
+ /**
353
+ * Optimized single-source shortest paths
354
+ */
355
+ function singleSourceShortestPathBFSOptimized(
356
+ graph: Graph,
357
+ source: NodeId,
358
+ ): Map<NodeId, ShortestPathResult> {
359
+ const csrGraph = getCSRGraph(graph);
360
+
361
+ const dobfs = new DirectionOptimizedBFS(csrGraph, {
362
+ alpha: 15.0,
363
+ beta: 20.0,
364
+ });
365
+
366
+ const bfsResult = dobfs.search(source);
367
+
368
+ // Convert to expected format
369
+ const results = new Map<NodeId, ShortestPathResult>();
370
+
371
+ for (const [nodeId, distance] of bfsResult.distances) {
372
+ // Reconstruct path for each node
373
+ const path = reconstructPath(nodeId, bfsResult.parents);
374
+
375
+ results.set(nodeId, {
376
+ distance,
377
+ path,
378
+ predecessor: bfsResult.parents, // Share the same predecessor map
379
+ });
380
+ }
381
+
382
+ return results;
383
+ }
384
+
385
+ /**
386
+ * Check if the graph is bipartite using BFS coloring
387
+ *
388
+ * Note: This function does not use Direction-Optimized BFS as the
389
+ * coloring logic is specific and doesn't benefit from the optimization.
390
+ */
391
+ export function isBipartite(graph: Graph): boolean {
392
+ if (graph.isDirected) {
393
+ throw new Error("Bipartite test requires an undirected graph");
394
+ }
395
+
396
+ const color = new Map<NodeId, 0 | 1>();
397
+ const visited = new Set<NodeId>();
398
+
399
+ // Check each connected component
400
+ for (const node of Array.from(graph.nodes())) {
401
+ if (!visited.has(node.id)) {
402
+ const queue: NodeId[] = [node.id];
403
+ color.set(node.id, 0);
404
+ visited.add(node.id);
405
+
406
+ while (queue.length > 0) {
407
+ const current = queue.shift();
408
+ if (!current) {
409
+ break;
410
+ }
411
+
412
+ const currentColor = color.get(current);
413
+ if (currentColor === undefined) {
414
+ continue;
415
+ }
416
+
417
+ for (const neighbor of Array.from(graph.neighbors(current))) {
418
+ if (!visited.has(neighbor)) {
419
+ // Color with opposite color
420
+ color.set(neighbor, currentColor === 0 ? 1 : 0);
421
+ visited.add(neighbor);
422
+ queue.push(neighbor);
423
+ } else if (color.get(neighbor) === currentColor) {
424
+ // Same color as current node - not bipartite
425
+ return false;
426
+ }
427
+ }
428
+ }
429
+ }
430
+ }
431
+
432
+ return true;
433
+ }