@graphty/algorithms 1.2.0 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +375 -276
- package/dist/algorithms.d.ts +42 -2
- package/dist/algorithms.js +11402 -2
- package/dist/algorithms.js.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.d.ts +14 -0
- package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.js +144 -169
- package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
- package/dist/src/algorithms/centrality/closeness.d.ts +35 -1
- package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/closeness.js +70 -149
- package/dist/src/algorithms/centrality/closeness.js.map +1 -1
- package/dist/src/algorithms/centrality/degree.d.ts +7 -0
- package/dist/src/algorithms/centrality/degree.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/degree.js +12 -9
- package/dist/src/algorithms/centrality/degree.js.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts +57 -0
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js +304 -0
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js.map +1 -0
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts +106 -0
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts.map +1 -0
- package/dist/src/algorithms/centrality/delta-pagerank.js +320 -0
- package/dist/src/algorithms/centrality/delta-pagerank.js.map +1 -0
- package/dist/src/algorithms/centrality/eigenvector.d.ts +10 -3
- package/dist/src/algorithms/centrality/eigenvector.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/eigenvector.js +11 -4
- package/dist/src/algorithms/centrality/eigenvector.js.map +1 -1
- package/dist/src/algorithms/centrality/hits.d.ts +10 -3
- package/dist/src/algorithms/centrality/hits.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/hits.js +11 -4
- package/dist/src/algorithms/centrality/hits.js.map +1 -1
- package/dist/src/algorithms/centrality/index.d.ts +3 -1
- package/dist/src/algorithms/centrality/index.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/index.js +2 -1
- package/dist/src/algorithms/centrality/index.js.map +1 -1
- package/dist/src/algorithms/centrality/katz.d.ts +10 -3
- package/dist/src/algorithms/centrality/katz.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/katz.js +13 -8
- package/dist/src/algorithms/centrality/katz.js.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.d.ts +31 -0
- package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.js +61 -1
- package/dist/src/algorithms/centrality/pagerank.js.map +1 -1
- package/dist/src/algorithms/community/girvan-newman.d.ts +2 -3
- package/dist/src/algorithms/community/girvan-newman.d.ts.map +1 -1
- package/dist/src/algorithms/community/girvan-newman.js +67 -57
- package/dist/src/algorithms/community/girvan-newman.js.map +1 -1
- package/dist/src/algorithms/community/index.d.ts +2 -0
- package/dist/src/algorithms/community/index.d.ts.map +1 -1
- package/dist/src/algorithms/community/index.js.map +1 -1
- package/dist/src/algorithms/community/label-propagation.d.ts +11 -4
- package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -1
- package/dist/src/algorithms/community/label-propagation.js +60 -38
- package/dist/src/algorithms/community/label-propagation.js.map +1 -1
- package/dist/src/algorithms/community/leiden.d.ts +3 -3
- package/dist/src/algorithms/community/leiden.d.ts.map +1 -1
- package/dist/src/algorithms/community/leiden.js +70 -44
- package/dist/src/algorithms/community/leiden.js.map +1 -1
- package/dist/src/algorithms/community/louvain-optimized.d.ts +149 -0
- package/dist/src/algorithms/community/louvain-optimized.d.ts.map +1 -0
- package/dist/src/algorithms/community/louvain-optimized.js +338 -0
- package/dist/src/algorithms/community/louvain-optimized.js.map +1 -0
- package/dist/src/algorithms/community/louvain.d.ts +2 -3
- package/dist/src/algorithms/community/louvain.d.ts.map +1 -1
- package/dist/src/algorithms/community/louvain.js +35 -81
- package/dist/src/algorithms/community/louvain.js.map +1 -1
- package/dist/src/algorithms/community/modularity-utils.d.ts +55 -0
- package/dist/src/algorithms/community/modularity-utils.d.ts.map +1 -0
- package/dist/src/algorithms/community/modularity-utils.js +108 -0
- package/dist/src/algorithms/community/modularity-utils.js.map +1 -0
- package/dist/src/algorithms/components/connected.d.ts +23 -0
- package/dist/src/algorithms/components/connected.d.ts.map +1 -1
- package/dist/src/algorithms/components/connected.js +29 -3
- package/dist/src/algorithms/components/connected.js.map +1 -1
- package/dist/src/algorithms/matching/bipartite.d.ts +10 -0
- package/dist/src/algorithms/matching/bipartite.d.ts.map +1 -1
- package/dist/src/algorithms/matching/bipartite.js +18 -40
- package/dist/src/algorithms/matching/bipartite.js.map +1 -1
- package/dist/src/algorithms/matching/index.d.ts.map +1 -1
- package/dist/src/algorithms/matching/index.js.map +1 -1
- package/dist/src/algorithms/matching/isomorphism.d.ts +8 -0
- package/dist/src/algorithms/matching/isomorphism.d.ts.map +1 -1
- package/dist/src/algorithms/matching/isomorphism.js +36 -2
- package/dist/src/algorithms/matching/isomorphism.js.map +1 -1
- package/dist/src/algorithms/mst/index.d.ts.map +1 -1
- package/dist/src/algorithms/mst/index.js.map +1 -1
- package/dist/src/algorithms/mst/kruskal.d.ts +14 -0
- package/dist/src/algorithms/mst/kruskal.d.ts.map +1 -1
- package/dist/src/algorithms/mst/kruskal.js +17 -3
- package/dist/src/algorithms/mst/kruskal.js.map +1 -1
- package/dist/src/algorithms/mst/prim.d.ts +9 -0
- package/dist/src/algorithms/mst/prim.d.ts.map +1 -1
- package/dist/src/algorithms/mst/prim.js +9 -0
- package/dist/src/algorithms/mst/prim.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bellman-ford.d.ts +10 -0
- package/dist/src/algorithms/shortest-path/bellman-ford.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/bellman-ford.js +11 -12
- package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts +44 -0
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts.map +1 -0
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js +195 -0
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js.map +1 -0
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts +20 -2
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.js +30 -14
- package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -1
- package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts +17 -0
- package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/floyd-warshall.js +17 -0
- package/dist/src/algorithms/shortest-path/floyd-warshall.js.map +1 -1
- package/dist/src/algorithms/shortest-path/index.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/index.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs-unified.d.ts +42 -0
- package/dist/src/algorithms/traversal/bfs-unified.d.ts.map +1 -0
- package/dist/src/algorithms/traversal/bfs-unified.js +370 -0
- package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -0
- package/dist/src/algorithms/traversal/bfs-variants.d.ts +77 -0
- package/dist/src/algorithms/traversal/bfs-variants.d.ts.map +1 -0
- package/dist/src/algorithms/traversal/bfs-variants.js +402 -0
- package/dist/src/algorithms/traversal/bfs-variants.js.map +1 -0
- package/dist/src/algorithms/traversal/bfs.d.ts +4 -18
- package/dist/src/algorithms/traversal/bfs.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs.js +5 -182
- package/dist/src/algorithms/traversal/bfs.js.map +1 -1
- package/dist/src/algorithms/traversal/dfs.d.ts +10 -0
- package/dist/src/algorithms/traversal/dfs.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/dfs.js +48 -0
- package/dist/src/algorithms/traversal/dfs.js.map +1 -1
- package/dist/src/algorithms/traversal/index.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/index.js.map +1 -1
- package/dist/src/benchmark-all-algorithms.d.ts +25 -0
- package/dist/src/benchmark-all-algorithms.d.ts.map +1 -0
- package/dist/src/benchmark-all-algorithms.js +491 -0
- package/dist/src/benchmark-all-algorithms.js.map +1 -0
- package/dist/src/clustering/hierarchical.d.ts +23 -15
- package/dist/src/clustering/hierarchical.d.ts.map +1 -1
- package/dist/src/clustering/hierarchical.js +226 -224
- package/dist/src/clustering/hierarchical.js.map +1 -1
- package/dist/src/clustering/index.d.ts +4 -2
- package/dist/src/clustering/index.d.ts.map +1 -1
- package/dist/src/clustering/index.js +2 -2
- package/dist/src/clustering/index.js.map +1 -1
- package/dist/src/clustering/k-core.d.ts +17 -19
- package/dist/src/clustering/k-core.d.ts.map +1 -1
- package/dist/src/clustering/k-core.js +68 -30
- package/dist/src/clustering/k-core.js.map +1 -1
- package/dist/src/clustering/mcl.d.ts +6 -0
- package/dist/src/clustering/mcl.d.ts.map +1 -1
- package/dist/src/clustering/mcl.js +34 -3
- package/dist/src/clustering/mcl.js.map +1 -1
- package/dist/src/clustering/spectral.d.ts +11 -0
- package/dist/src/clustering/spectral.d.ts.map +1 -1
- package/dist/src/clustering/spectral.js +99 -47
- package/dist/src/clustering/spectral.js.map +1 -1
- package/dist/src/core/graph.d.ts +45 -0
- package/dist/src/core/graph.d.ts.map +1 -1
- package/dist/src/core/graph.js +49 -5
- package/dist/src/core/graph.js.map +1 -1
- package/dist/src/data-structures/index.d.ts.map +1 -1
- package/dist/src/data-structures/index.js.map +1 -1
- package/dist/src/data-structures/priority-queue.d.ts +18 -1
- package/dist/src/data-structures/priority-queue.d.ts.map +1 -1
- package/dist/src/data-structures/priority-queue.js +26 -5
- package/dist/src/data-structures/priority-queue.js.map +1 -1
- package/dist/src/data-structures/union-find.d.ts +21 -0
- package/dist/src/data-structures/union-find.d.ts.map +1 -1
- package/dist/src/data-structures/union-find.js +21 -0
- package/dist/src/data-structures/union-find.js.map +1 -1
- package/dist/src/flow/ford-fulkerson.d.ts +20 -12
- package/dist/src/flow/ford-fulkerson.d.ts.map +1 -1
- package/dist/src/flow/ford-fulkerson.js +170 -217
- package/dist/src/flow/ford-fulkerson.js.map +1 -1
- package/dist/src/flow/min-cut.d.ts +6 -8
- package/dist/src/flow/min-cut.d.ts.map +1 -1
- package/dist/src/flow/min-cut.js +34 -12
- package/dist/src/flow/min-cut.js.map +1 -1
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +6 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/link-prediction/adamic-adar.d.ts +26 -0
- package/dist/src/link-prediction/adamic-adar.d.ts.map +1 -1
- package/dist/src/link-prediction/adamic-adar.js +47 -30
- package/dist/src/link-prediction/adamic-adar.js.map +1 -1
- package/dist/src/link-prediction/common-neighbors.d.ts +21 -0
- package/dist/src/link-prediction/common-neighbors.d.ts.map +1 -1
- package/dist/src/link-prediction/common-neighbors.js +31 -14
- package/dist/src/link-prediction/common-neighbors.js.map +1 -1
- package/dist/src/link-prediction/index.d.ts.map +1 -1
- package/dist/src/optimized/bit-packed.d.ts +193 -0
- package/dist/src/optimized/bit-packed.d.ts.map +1 -0
- package/dist/src/optimized/bit-packed.js +342 -0
- package/dist/src/optimized/bit-packed.js.map +1 -0
- package/dist/src/optimized/csr-graph.d.ts +149 -0
- package/dist/src/optimized/csr-graph.d.ts.map +1 -0
- package/dist/src/optimized/csr-graph.js +404 -0
- package/dist/src/optimized/csr-graph.js.map +1 -0
- package/dist/src/optimized/direction-optimized-bfs.d.ts +95 -0
- package/dist/src/optimized/direction-optimized-bfs.d.ts.map +1 -0
- package/dist/src/optimized/direction-optimized-bfs.js +242 -0
- package/dist/src/optimized/direction-optimized-bfs.js.map +1 -0
- package/dist/src/optimized/graph-adapter.d.ts +111 -0
- package/dist/src/optimized/graph-adapter.d.ts.map +1 -0
- package/dist/src/optimized/graph-adapter.js +194 -0
- package/dist/src/optimized/graph-adapter.js.map +1 -0
- package/dist/src/optimized/index.d.ts +14 -0
- package/dist/src/optimized/index.d.ts.map +1 -0
- package/dist/src/optimized/index.js +30 -0
- package/dist/src/optimized/index.js.map +1 -0
- package/dist/src/pathfinding/astar.d.ts +25 -9
- package/dist/src/pathfinding/astar.d.ts.map +1 -1
- package/dist/src/pathfinding/astar.js +25 -10
- package/dist/src/pathfinding/astar.js.map +1 -1
- package/dist/src/pathfinding/utils.d.ts +15 -6
- package/dist/src/pathfinding/utils.d.ts.map +1 -1
- package/dist/src/pathfinding/utils.js +19 -7
- package/dist/src/pathfinding/utils.js.map +1 -1
- package/dist/src/research/grsbm.d.ts +0 -1
- package/dist/src/research/grsbm.d.ts.map +1 -1
- package/dist/src/research/grsbm.js +28 -23
- package/dist/src/research/grsbm.js.map +1 -1
- package/dist/src/research/index.d.ts.map +1 -1
- package/dist/src/research/index.js.map +1 -1
- package/dist/src/research/sync.d.ts +0 -1
- package/dist/src/research/sync.d.ts.map +1 -1
- package/dist/src/research/sync.js +31 -36
- package/dist/src/research/sync.js.map +1 -1
- package/dist/src/research/terahac.d.ts +2 -1
- package/dist/src/research/terahac.d.ts.map +1 -1
- package/dist/src/research/terahac.js +39 -7
- package/dist/src/research/terahac.js.map +1 -1
- package/dist/src/types/index.d.ts +8 -0
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/utils/algorithm-utilities.d.ts +2 -0
- package/dist/src/utils/algorithm-utilities.d.ts.map +1 -0
- package/dist/src/utils/algorithm-utilities.js +2 -0
- package/dist/src/utils/algorithm-utilities.js.map +1 -0
- package/dist/src/utils/graph-converters.d.ts +110 -0
- package/dist/src/utils/graph-converters.d.ts.map +1 -0
- package/dist/src/utils/graph-converters.js +227 -0
- package/dist/src/utils/graph-converters.js.map +1 -0
- package/dist/src/utils/graph-utilities.d.ts +62 -0
- package/dist/src/utils/graph-utilities.d.ts.map +1 -0
- package/dist/src/utils/graph-utilities.js +148 -0
- package/dist/src/utils/graph-utilities.js.map +1 -0
- package/dist/src/utils/index.d.ts +5 -0
- package/dist/src/utils/index.d.ts.map +1 -0
- package/dist/src/utils/index.js +5 -0
- package/dist/src/utils/index.js.map +1 -0
- package/dist/src/utils/math-utilities.d.ts +63 -0
- package/dist/src/utils/math-utilities.d.ts.map +1 -0
- package/dist/src/utils/math-utilities.js +132 -0
- package/dist/src/utils/math-utilities.js.map +1 -0
- package/dist/src/utils/matrix-utilities.d.ts +2 -0
- package/dist/src/utils/matrix-utilities.d.ts.map +1 -0
- package/dist/src/utils/matrix-utilities.js +2 -0
- package/dist/src/utils/matrix-utilities.js.map +1 -0
- package/dist/src/utils/optimization-helpers.d.ts +42 -0
- package/dist/src/utils/optimization-helpers.d.ts.map +1 -0
- package/dist/src/utils/optimization-helpers.js +59 -0
- package/dist/src/utils/optimization-helpers.js.map +1 -0
- package/dist/src/utils/priorityQueue.d.ts +24 -0
- package/dist/src/utils/priorityQueue.d.ts.map +1 -1
- package/dist/src/utils/priorityQueue.js +34 -4
- package/dist/src/utils/priorityQueue.js.map +1 -1
- package/package.json +58 -39
- package/src/algorithms/centrality/betweenness.ts +185 -200
- package/src/algorithms/centrality/closeness.ts +93 -194
- package/src/algorithms/centrality/degree.ts +18 -22
- package/src/algorithms/centrality/delta-pagerank-simple.ts +374 -0
- package/src/algorithms/centrality/delta-pagerank.ts +458 -0
- package/src/algorithms/centrality/eigenvector.ts +14 -15
- package/src/algorithms/centrality/hits.ts +17 -17
- package/src/algorithms/centrality/index.ts +20 -13
- package/src/algorithms/centrality/katz.ts +17 -25
- package/src/algorithms/centrality/pagerank.ts +91 -16
- package/src/algorithms/community/girvan-newman.ts +90 -86
- package/src/algorithms/community/index.ts +6 -5
- package/src/algorithms/community/label-propagation.ts +69 -48
- package/src/algorithms/community/leiden.ts +88 -65
- package/src/algorithms/community/louvain-optimized.ts +474 -0
- package/src/algorithms/community/louvain.ts +52 -122
- package/src/algorithms/community/modularity-utils.ts +134 -0
- package/src/algorithms/components/connected.ts +40 -17
- package/src/algorithms/matching/bipartite.ts +24 -53
- package/src/algorithms/matching/index.ts +4 -4
- package/src/algorithms/matching/isomorphism.ts +53 -23
- package/src/algorithms/mst/index.ts +3 -4
- package/src/algorithms/mst/kruskal.ts +21 -6
- package/src/algorithms/mst/prim.ts +13 -4
- package/src/algorithms/shortest-path/bellman-ford.ts +16 -28
- package/src/algorithms/shortest-path/bidirectional-dijkstra.ts +248 -0
- package/src/algorithms/shortest-path/dijkstra.ts +38 -30
- package/src/algorithms/shortest-path/floyd-warshall.ts +21 -4
- package/src/algorithms/shortest-path/index.ts +5 -5
- package/src/algorithms/traversal/bfs-unified.ts +437 -0
- package/src/algorithms/traversal/bfs-variants.ts +520 -0
- package/src/algorithms/traversal/bfs.ts +5 -229
- package/src/algorithms/traversal/dfs.ts +61 -42
- package/src/algorithms/traversal/index.ts +3 -3
- package/src/benchmark-all-algorithms.ts +580 -0
- package/src/clustering/hierarchical.ts +268 -272
- package/src/clustering/index.ts +8 -6
- package/src/clustering/k-core.ts +93 -61
- package/src/clustering/mcl.ts +53 -23
- package/src/clustering/spectral.ts +130 -72
- package/src/core/graph.ts +58 -18
- package/src/data-structures/index.ts +2 -2
- package/src/data-structures/priority-queue.ts +36 -11
- package/src/data-structures/union-find.ts +22 -1
- package/src/flow/ford-fulkerson.ts +221 -262
- package/src/flow/min-cut.ts +59 -51
- package/src/index.ts +9 -2
- package/src/link-prediction/adamic-adar.ts +75 -74
- package/src/link-prediction/common-neighbors.ts +49 -49
- package/src/link-prediction/index.ts +1 -1
- package/src/optimized/bit-packed.ts +390 -0
- package/src/optimized/csr-graph.ts +518 -0
- package/src/optimized/direction-optimized-bfs.ts +310 -0
- package/src/optimized/graph-adapter.ts +248 -0
- package/src/optimized/index.ts +34 -0
- package/src/pathfinding/astar.ts +40 -25
- package/src/pathfinding/utils.ts +20 -13
- package/src/research/grsbm.ts +36 -39
- package/src/research/index.ts +4 -4
- package/src/research/sync.ts +41 -54
- package/src/research/terahac.ts +63 -21
- package/src/types/index.ts +8 -0
- package/src/utils/algorithm-utilities.ts +2 -0
- package/src/utils/graph-converters.ts +269 -0
- package/src/utils/graph-utilities.ts +173 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/math-utilities.ts +149 -0
- package/src/utils/matrix-utilities.ts +2 -0
- package/src/utils/optimization-helpers.ts +87 -0
- package/src/utils/priorityQueue.ts +40 -6
package/src/pathfinding/astar.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {graphNode} from "../utils/graphNode.js";
|
|
2
|
-
import {MinPriorityQueue} from "../utils/priorityQueue.js";
|
|
3
|
-
import {pathfindingUtils} from "./utils.js";
|
|
1
|
+
import { graphNode } from "../utils/graphNode.js";
|
|
2
|
+
import { MinPriorityQueue } from "../utils/priorityQueue.js";
|
|
3
|
+
import { pathfindingUtils } from "./utils.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* A* pathfinding algorithm
|
|
7
7
|
* Finds the shortest path from start to goal using a heuristic function
|
|
8
|
-
*
|
|
9
8
|
* @param graph - The graph represented as an adjacency list
|
|
10
9
|
* @param start - The starting node
|
|
11
10
|
* @param goal - The goal node
|
|
@@ -20,7 +19,7 @@ export function astar<T>(
|
|
|
20
19
|
start: T,
|
|
21
20
|
goal: T,
|
|
22
21
|
heuristic: (node: T, goal: T) => number,
|
|
23
|
-
): {path: T[]
|
|
22
|
+
): { path: T[]; cost: number } | null {
|
|
24
23
|
if (!graph.has(start) || !graph.has(goal)) {
|
|
25
24
|
return null;
|
|
26
25
|
}
|
|
@@ -40,7 +39,7 @@ export function astar<T>(
|
|
|
40
39
|
return null;
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
openSet.insert({node: start, distance: startFScore});
|
|
42
|
+
openSet.insert({ node: start, distance: startFScore });
|
|
44
43
|
|
|
45
44
|
while (!openSet.isEmpty()) {
|
|
46
45
|
const current = openSet.extractMin();
|
|
@@ -58,7 +57,7 @@ export function astar<T>(
|
|
|
58
57
|
return null;
|
|
59
58
|
}
|
|
60
59
|
|
|
61
|
-
return {path, cost: goalCost};
|
|
60
|
+
return { path, cost: goalCost };
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
closedSet.add(currentNode);
|
|
@@ -90,7 +89,7 @@ export function astar<T>(
|
|
|
90
89
|
// Add to open set if not already there
|
|
91
90
|
const neighborFScore = fScore.get(neighbor);
|
|
92
91
|
if (neighborFScore !== undefined) {
|
|
93
|
-
openSet.insert({node: neighbor, distance: neighborFScore});
|
|
92
|
+
openSet.insert({ node: neighbor, distance: neighborFScore });
|
|
94
93
|
}
|
|
95
94
|
}
|
|
96
95
|
}
|
|
@@ -102,6 +101,11 @@ export function astar<T>(
|
|
|
102
101
|
/**
|
|
103
102
|
* A* pathfinding with path reconstruction details
|
|
104
103
|
* Returns detailed information about the search process
|
|
104
|
+
* @param graph - The graph represented as an adjacency list
|
|
105
|
+
* @param start - The starting node
|
|
106
|
+
* @param goal - The goal node
|
|
107
|
+
* @param heuristic - Heuristic function that estimates distance from node to goal
|
|
108
|
+
* @returns Detailed result including path, cost, visited nodes, and score maps
|
|
105
109
|
*/
|
|
106
110
|
export function astarWithDetails<T>(
|
|
107
111
|
graph: Map<T, Map<T, number>>,
|
|
@@ -109,12 +113,12 @@ export function astarWithDetails<T>(
|
|
|
109
113
|
goal: T,
|
|
110
114
|
heuristic: (node: T, goal: T) => number,
|
|
111
115
|
): {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
path: T[] | null;
|
|
117
|
+
cost: number;
|
|
118
|
+
visited: Set<T>;
|
|
119
|
+
gScores: Map<T, number>;
|
|
120
|
+
fScores: Map<T, number>;
|
|
121
|
+
} {
|
|
118
122
|
const openSet = new MinPriorityQueue<graphNode<T>>();
|
|
119
123
|
const gScore = new Map<T, number>();
|
|
120
124
|
const fScore = new Map<T, number>();
|
|
@@ -134,7 +138,7 @@ export function astarWithDetails<T>(
|
|
|
134
138
|
};
|
|
135
139
|
}
|
|
136
140
|
|
|
137
|
-
openSet.insert({node: start, distance: startFScore});
|
|
141
|
+
openSet.insert({ node: start, distance: startFScore });
|
|
138
142
|
|
|
139
143
|
while (!openSet.isEmpty()) {
|
|
140
144
|
const current = openSet.extractMin();
|
|
@@ -181,7 +185,7 @@ export function astarWithDetails<T>(
|
|
|
181
185
|
fScore.set(neighbor, tentativeGScore + heuristic(neighbor, goal));
|
|
182
186
|
const neighborFScore = fScore.get(neighbor);
|
|
183
187
|
if (neighborFScore !== undefined) {
|
|
184
|
-
openSet.insert({node: neighbor, distance: neighborFScore});
|
|
188
|
+
openSet.insert({ node: neighbor, distance: neighborFScore });
|
|
185
189
|
}
|
|
186
190
|
}
|
|
187
191
|
}
|
|
@@ -201,29 +205,40 @@ export function astarWithDetails<T>(
|
|
|
201
205
|
*/
|
|
202
206
|
export const heuristics = {
|
|
203
207
|
/**
|
|
204
|
-
|
|
205
|
-
|
|
208
|
+
* Manhattan distance heuristic for grid-based graphs
|
|
209
|
+
* @param a - First coordinate as [x, y] tuple
|
|
210
|
+
* @param b - Second coordinate as [x, y] tuple
|
|
211
|
+
* @returns The Manhattan distance between the two points
|
|
212
|
+
*/
|
|
206
213
|
manhattan: (a: [number, number], b: [number, number]): number => {
|
|
207
214
|
return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]);
|
|
208
215
|
},
|
|
209
216
|
|
|
210
217
|
/**
|
|
211
|
-
|
|
212
|
-
|
|
218
|
+
* Euclidean distance heuristic
|
|
219
|
+
* @param a - First coordinate as [x, y] tuple
|
|
220
|
+
* @param b - Second coordinate as [x, y] tuple
|
|
221
|
+
* @returns The Euclidean distance between the two points
|
|
222
|
+
*/
|
|
213
223
|
euclidean: (a: [number, number], b: [number, number]): number => {
|
|
214
224
|
return Math.sqrt(Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2));
|
|
215
225
|
},
|
|
216
226
|
|
|
217
227
|
/**
|
|
218
|
-
|
|
219
|
-
|
|
228
|
+
* Chebyshev distance heuristic (diagonal movement allowed)
|
|
229
|
+
* @param a - First coordinate as [x, y] tuple
|
|
230
|
+
* @param b - Second coordinate as [x, y] tuple
|
|
231
|
+
* @returns The Chebyshev distance between the two points
|
|
232
|
+
*/
|
|
220
233
|
chebyshev: (a: [number, number], b: [number, number]): number => {
|
|
221
234
|
return Math.max(Math.abs(a[0] - b[0]), Math.abs(a[1] - b[1]));
|
|
222
235
|
},
|
|
223
236
|
|
|
224
237
|
/**
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
238
|
+
* Zero heuristic (makes A* behave like Dijkstra)
|
|
239
|
+
* @param _a - First node (unused)
|
|
240
|
+
* @param _b - Second node (unused)
|
|
241
|
+
* @returns Always returns 0
|
|
242
|
+
*/
|
|
228
243
|
zero: <T>(_a: T, _b: T): number => 0,
|
|
229
244
|
};
|
package/src/pathfinding/utils.ts
CHANGED
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
export const pathfindingUtils = {
|
|
6
6
|
/**
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
* Reconstructs a path from start to goal using the cameFrom map
|
|
8
|
+
* @param cameFrom - Map of each node to its predecessor on the path
|
|
9
|
+
* @param goal - The goal node to reconstruct the path to
|
|
10
|
+
* @returns An array of nodes representing the path from start to goal
|
|
11
|
+
*/
|
|
9
12
|
reconstructPath<T>(cameFrom: Map<T, T>, goal: T): T[] {
|
|
10
13
|
const path: T[] = [goal];
|
|
11
14
|
let current = goal;
|
|
@@ -24,13 +27,13 @@ export const pathfindingUtils = {
|
|
|
24
27
|
},
|
|
25
28
|
|
|
26
29
|
/**
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
): Map<string, Map<string, number>> {
|
|
30
|
+
* Creates a grid graph for testing pathfinding algorithms
|
|
31
|
+
* @param width - The width of the grid
|
|
32
|
+
* @param height - The height of the grid
|
|
33
|
+
* @param obstacles - Set of obstacle coordinates as "x,y" strings
|
|
34
|
+
* @returns A grid graph with 4-directional movement
|
|
35
|
+
*/
|
|
36
|
+
createGridGraph(width: number, height: number, obstacles = new Set<string>()): Map<string, Map<string, number>> {
|
|
34
37
|
const graph = new Map<string, Map<string, number>>();
|
|
35
38
|
|
|
36
39
|
for (let x = 0; x < width; x++) {
|
|
@@ -44,7 +47,10 @@ export const pathfindingUtils = {
|
|
|
44
47
|
|
|
45
48
|
// Add 4-directional neighbors
|
|
46
49
|
const directions = [
|
|
47
|
-
[0, 1],
|
|
50
|
+
[0, 1],
|
|
51
|
+
[1, 0],
|
|
52
|
+
[0, -1],
|
|
53
|
+
[-1, 0],
|
|
48
54
|
];
|
|
49
55
|
|
|
50
56
|
for (const dir of directions) {
|
|
@@ -71,8 +77,10 @@ export const pathfindingUtils = {
|
|
|
71
77
|
},
|
|
72
78
|
|
|
73
79
|
/**
|
|
74
|
-
|
|
75
|
-
|
|
80
|
+
* Parses a grid coordinate string
|
|
81
|
+
* @param coord - The coordinate string in "x,y" format
|
|
82
|
+
* @returns A tuple of [x, y] coordinates
|
|
83
|
+
*/
|
|
76
84
|
parseGridCoordinate(coord: string): [number, number] {
|
|
77
85
|
const parts = coord.split(",").map(Number);
|
|
78
86
|
const x = parts[0];
|
|
@@ -84,4 +92,3 @@ export const pathfindingUtils = {
|
|
|
84
92
|
return [x, y];
|
|
85
93
|
},
|
|
86
94
|
};
|
|
87
|
-
|
package/src/research/grsbm.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type {Graph} from "../core/graph.js";
|
|
2
|
-
import type {NodeId} from "../types/index.js";
|
|
1
|
+
import type { Graph } from "../core/graph.js";
|
|
2
|
+
import type { NodeId } from "../types/index.js";
|
|
3
|
+
import { SeededRandom } from "../utils/math-utilities.js";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Configuration options for the GRSBM (Greedy Recursive Spectral Bisection) algorithm
|
|
@@ -79,7 +80,6 @@ export interface ClusterExplanation {
|
|
|
79
80
|
* community structure by tracking the reasoning behind each split.
|
|
80
81
|
*
|
|
81
82
|
* Based on: "Explainable Community Detection via Hierarchical Spectral Clustering" (2024)
|
|
82
|
-
*
|
|
83
83
|
* @param graph - Input graph to cluster
|
|
84
84
|
* @param config - Configuration options
|
|
85
85
|
* @returns Hierarchical clustering result with explanations
|
|
@@ -95,7 +95,9 @@ export function grsbm(graph: Graph, config: GRSBMConfig = {}): GRSBMResult {
|
|
|
95
95
|
} = config;
|
|
96
96
|
|
|
97
97
|
// Set random seed for reproducibility
|
|
98
|
-
|
|
98
|
+
const rng = SeededRandom.createGenerator(seed);
|
|
99
|
+
const originalRandom = Math.random;
|
|
100
|
+
Math.random = rng;
|
|
99
101
|
|
|
100
102
|
const nodes = Array.from(graph.nodes());
|
|
101
103
|
const nodeCount = nodes.length;
|
|
@@ -106,7 +108,7 @@ export function grsbm(graph: Graph, config: GRSBMConfig = {}): GRSBMResult {
|
|
|
106
108
|
|
|
107
109
|
// Initialize root cluster with all nodes
|
|
108
110
|
const rootMembers = new Set(nodes.map((node) => node.id));
|
|
109
|
-
const initialModularity = calculateModularity(graph, new Map([...
|
|
111
|
+
const initialModularity = calculateModularity(graph, new Map([...rootMembers].map((id) => [id, 0])));
|
|
110
112
|
|
|
111
113
|
const root: GRSBMCluster = {
|
|
112
114
|
id: "root",
|
|
@@ -130,25 +132,18 @@ export function grsbm(graph: Graph, config: GRSBMConfig = {}): GRSBMResult {
|
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
// Check stopping criteria
|
|
133
|
-
if (currentCluster.depth >= maxDepth ||
|
|
134
|
-
currentCluster.members.size < minClusterSize * 2) {
|
|
135
|
+
if (currentCluster.depth >= maxDepth || currentCluster.members.size < minClusterSize * 2) {
|
|
135
136
|
continue;
|
|
136
137
|
}
|
|
137
138
|
|
|
138
139
|
// Attempt spectral bisection
|
|
139
|
-
const bisectionResult = spectralBisection(
|
|
140
|
-
graph,
|
|
141
|
-
currentCluster,
|
|
142
|
-
numEigenvectors,
|
|
143
|
-
tolerance,
|
|
144
|
-
maxIterations,
|
|
145
|
-
);
|
|
140
|
+
const bisectionResult = spectralBisection(graph, currentCluster, numEigenvectors, tolerance, maxIterations);
|
|
146
141
|
|
|
147
142
|
if (!bisectionResult) {
|
|
148
143
|
continue; // Could not split this cluster
|
|
149
144
|
}
|
|
150
145
|
|
|
151
|
-
const {leftMembers, rightMembers, spectralScore, keyNodes, spectralValues, reason} = bisectionResult;
|
|
146
|
+
const { leftMembers, rightMembers, spectralScore, keyNodes, spectralValues, reason } = bisectionResult;
|
|
152
147
|
|
|
153
148
|
// Create child clusters
|
|
154
149
|
const leftCluster: GRSBMCluster = {
|
|
@@ -180,7 +175,8 @@ export function grsbm(graph: Graph, config: GRSBMConfig = {}): GRSBMResult {
|
|
|
180
175
|
const modularityImprovement = newModularity - currentCluster.modularity;
|
|
181
176
|
|
|
182
177
|
// Only split if modularity improves or is neutral (allow small splits)
|
|
183
|
-
if (modularityImprovement >= -0.01) {
|
|
178
|
+
if (modularityImprovement >= -0.01) {
|
|
179
|
+
// Small tolerance for neutral splits
|
|
184
180
|
leftCluster.modularity = newModularity;
|
|
185
181
|
rightCluster.modularity = newModularity;
|
|
186
182
|
|
|
@@ -228,6 +224,9 @@ export function grsbm(graph: Graph, config: GRSBMConfig = {}): GRSBMResult {
|
|
|
228
224
|
|
|
229
225
|
assignClusterIds(root);
|
|
230
226
|
|
|
227
|
+
// Restore original random function
|
|
228
|
+
Math.random = originalRandom;
|
|
229
|
+
|
|
231
230
|
return {
|
|
232
231
|
root,
|
|
233
232
|
clusters,
|
|
@@ -239,6 +238,12 @@ export function grsbm(graph: Graph, config: GRSBMConfig = {}): GRSBMResult {
|
|
|
239
238
|
|
|
240
239
|
/**
|
|
241
240
|
* Perform spectral bisection on a cluster
|
|
241
|
+
* @param graph - The input graph
|
|
242
|
+
* @param cluster - The cluster to bisect
|
|
243
|
+
* @param numEigenvectors - Number of eigenvectors to compute
|
|
244
|
+
* @param tolerance - Convergence tolerance
|
|
245
|
+
* @param maxIterations - Maximum number of iterations
|
|
246
|
+
* @returns Bisection result with left/right members, scores, and explanation, or null if bisection failed
|
|
242
247
|
*/
|
|
243
248
|
function spectralBisection(
|
|
244
249
|
graph: Graph,
|
|
@@ -272,9 +277,7 @@ function spectralBisection(
|
|
|
272
277
|
}
|
|
273
278
|
|
|
274
279
|
// Find optimal split point
|
|
275
|
-
const sortedIndices = eigenvector
|
|
276
|
-
.map((value, index) => ({value, index}))
|
|
277
|
-
.sort((a, b) => a.value - b.value);
|
|
280
|
+
const sortedIndices = eigenvector.map((value, index) => ({ value, index })).sort((a, b) => a.value - b.value);
|
|
278
281
|
|
|
279
282
|
let bestSplitIndex = Math.floor(n / 2);
|
|
280
283
|
let bestModularity = -Infinity;
|
|
@@ -329,7 +332,7 @@ function spectralBisection(
|
|
|
329
332
|
|
|
330
333
|
// Identify key nodes (those with extreme spectral values)
|
|
331
334
|
const extremeThreshold = 0.1;
|
|
332
|
-
const sortedValues = [...
|
|
335
|
+
const sortedValues = [...eigenvector].sort((a, b) => Math.abs(b) - Math.abs(a));
|
|
333
336
|
const threshold = sortedValues[Math.floor(sortedValues.length * extremeThreshold)] ?? 0;
|
|
334
337
|
|
|
335
338
|
const keyNodes: NodeId[] = [];
|
|
@@ -363,6 +366,9 @@ function spectralBisection(
|
|
|
363
366
|
|
|
364
367
|
/**
|
|
365
368
|
* Create Laplacian matrix for a subgraph
|
|
369
|
+
* @param graph - The input graph
|
|
370
|
+
* @param nodes - The nodes to include in the subgraph
|
|
371
|
+
* @returns The Laplacian matrix as a 2D array
|
|
366
372
|
*/
|
|
367
373
|
function createLaplacianMatrix(graph: Graph, nodes: NodeId[]): number[][] {
|
|
368
374
|
const n = nodes.length;
|
|
@@ -377,7 +383,7 @@ function createLaplacianMatrix(graph: Graph, nodes: NodeId[]): number[][] {
|
|
|
377
383
|
}
|
|
378
384
|
|
|
379
385
|
// Initialize Laplacian matrix
|
|
380
|
-
const laplacian = Array.from({length: n}, () => new Array<number>(n).fill(0));
|
|
386
|
+
const laplacian = Array.from({ length: n }, () => new Array<number>(n).fill(0));
|
|
381
387
|
|
|
382
388
|
// Fill adjacency part and calculate degrees
|
|
383
389
|
const degrees = new Array<number>(n).fill(0);
|
|
@@ -414,6 +420,10 @@ function createLaplacianMatrix(graph: Graph, nodes: NodeId[]): number[][] {
|
|
|
414
420
|
|
|
415
421
|
/**
|
|
416
422
|
* Compute the Fiedler vector (second smallest eigenvector) using power iteration
|
|
423
|
+
* @param laplacian - The Laplacian matrix
|
|
424
|
+
* @param tolerance - Convergence tolerance
|
|
425
|
+
* @param maxIterations - Maximum number of iterations
|
|
426
|
+
* @returns The Fiedler vector, or null if computation failed
|
|
417
427
|
*/
|
|
418
428
|
function computeFiedlerVector(laplacian: number[][], tolerance: number, maxIterations: number): number[] | null {
|
|
419
429
|
const n = laplacian.length;
|
|
@@ -437,7 +447,7 @@ function computeFiedlerVector(laplacian: number[][], tolerance: number, maxItera
|
|
|
437
447
|
}
|
|
438
448
|
|
|
439
449
|
// Normalize
|
|
440
|
-
const norm = Math.sqrt(vector.reduce((sum: number, val: number) => sum +
|
|
450
|
+
const norm = Math.sqrt(vector.reduce((sum: number, val: number) => sum + val * val, 0));
|
|
441
451
|
if (norm > 0) {
|
|
442
452
|
for (let i = 0; i < n; i++) {
|
|
443
453
|
const val = vector[i];
|
|
@@ -488,7 +498,7 @@ function computeFiedlerVector(laplacian: number[][], tolerance: number, maxItera
|
|
|
488
498
|
}
|
|
489
499
|
|
|
490
500
|
// Normalize
|
|
491
|
-
const newNorm = Math.sqrt(newVector.reduce((sum, val) => sum +
|
|
501
|
+
const newNorm = Math.sqrt(newVector.reduce((sum, val) => sum + val * val, 0));
|
|
492
502
|
if (newNorm < tolerance) {
|
|
493
503
|
break; // Converged to zero vector
|
|
494
504
|
}
|
|
@@ -522,6 +532,9 @@ function computeFiedlerVector(laplacian: number[][], tolerance: number, maxItera
|
|
|
522
532
|
|
|
523
533
|
/**
|
|
524
534
|
* Calculate modularity of a graph partitioning
|
|
535
|
+
* @param graph - The input graph
|
|
536
|
+
* @param assignment - Map from node ID to community ID
|
|
537
|
+
* @returns The modularity score
|
|
525
538
|
*/
|
|
526
539
|
function calculateModularity(graph: Graph, assignment: Map<NodeId, number>): number {
|
|
527
540
|
const totalEdges = graph.uniqueEdgeCount;
|
|
@@ -571,19 +584,3 @@ function calculateModularity(graph: Graph, assignment: Map<NodeId, number>): num
|
|
|
571
584
|
|
|
572
585
|
return modularity;
|
|
573
586
|
}
|
|
574
|
-
|
|
575
|
-
/**
|
|
576
|
-
* Simple seeded random number generator for reproducibility
|
|
577
|
-
*/
|
|
578
|
-
function seedRandom(seed: number): () => number {
|
|
579
|
-
const m = 0x80000000; // 2**31
|
|
580
|
-
const a = 1103515245;
|
|
581
|
-
const c = 12345;
|
|
582
|
-
|
|
583
|
-
seed = seed % m;
|
|
584
|
-
|
|
585
|
-
return function() {
|
|
586
|
-
seed = ((a * seed) + c) % m;
|
|
587
|
-
return seed / (m - 1);
|
|
588
|
-
};
|
|
589
|
-
}
|
package/src/research/index.ts
CHANGED
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
// Synergistic Deep Graph Clustering
|
|
10
|
-
export {syncClustering, type SynCConfig, type SynCResult} from "./sync.js";
|
|
10
|
+
export { syncClustering, type SynCConfig, type SynCResult } from "./sync.js";
|
|
11
11
|
|
|
12
12
|
// TeraHAC - Hierarchical Agglomerative Clustering
|
|
13
|
-
export {teraHAC, type TeraHACConfig, type TeraHACResult} from "./terahac.js";
|
|
14
|
-
export {type ClusterNode as TeraHACClusterNode} from "./terahac.js";
|
|
13
|
+
export { teraHAC, type TeraHACConfig, type TeraHACResult } from "./terahac.js";
|
|
14
|
+
export { type ClusterNode as TeraHACClusterNode } from "./terahac.js";
|
|
15
15
|
|
|
16
16
|
// GRSBM - Greedy Recursive Spectral Bisection with Modularity
|
|
17
|
-
export {type ClusterExplanation, grsbm, type GRSBMCluster, type GRSBMConfig, type GRSBMResult} from "./grsbm.js";
|
|
17
|
+
export { type ClusterExplanation, grsbm, type GRSBMCluster, type GRSBMConfig, type GRSBMResult } from "./grsbm.js";
|
package/src/research/sync.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type {Graph} from "../core/graph.js";
|
|
2
|
-
import type {NodeId} from "../types/index.js";
|
|
1
|
+
import type { Graph } from "../core/graph.js";
|
|
2
|
+
import type { NodeId } from "../types/index.js";
|
|
3
|
+
import { euclideanDistance, SeededRandom } from "../utils/math-utilities.js";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Configuration options for the SynC (Synergistic Deep Graph Clustering) algorithm
|
|
@@ -43,24 +44,17 @@ export interface SynCResult {
|
|
|
43
44
|
* embeddings and cluster assignments while preserving graph structure.
|
|
44
45
|
*
|
|
45
46
|
* Based on: "Synergistic Deep Graph Clustering" (arXiv:2406.15797, June 2024)
|
|
46
|
-
*
|
|
47
47
|
* @param graph - Input graph to cluster
|
|
48
48
|
* @param config - Configuration options
|
|
49
49
|
* @returns Clustering result with assignments and embeddings
|
|
50
50
|
*/
|
|
51
51
|
export function syncClustering(graph: Graph, config: SynCConfig): SynCResult {
|
|
52
|
-
const {
|
|
53
|
-
numClusters,
|
|
54
|
-
maxIterations = 100,
|
|
55
|
-
tolerance = 1e-6,
|
|
56
|
-
seed = 42,
|
|
57
|
-
learningRate = 0.01,
|
|
58
|
-
lambda = 0.1,
|
|
59
|
-
} = config;
|
|
52
|
+
const { numClusters, maxIterations = 100, tolerance = 1e-6, seed = 42, learningRate = 0.01, lambda = 0.1 } = config;
|
|
60
53
|
|
|
61
54
|
// Set random seed for reproducibility (save original)
|
|
62
55
|
const originalRandom = Math.random;
|
|
63
|
-
|
|
56
|
+
const rng = SeededRandom.createGenerator(seed);
|
|
57
|
+
Math.random = rng;
|
|
64
58
|
|
|
65
59
|
const nodes = Array.from(graph.nodes());
|
|
66
60
|
const nodeCount = nodes.length;
|
|
@@ -76,7 +70,9 @@ export function syncClustering(graph: Graph, config: SynCConfig): SynCResult {
|
|
|
76
70
|
}
|
|
77
71
|
|
|
78
72
|
if (numClusters <= 0 || numClusters > nodeCount) {
|
|
79
|
-
throw new Error(
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Invalid number of clusters: ${String(numClusters)}. Must be between 1 and ${String(nodeCount)}`,
|
|
75
|
+
);
|
|
80
76
|
}
|
|
81
77
|
|
|
82
78
|
// Initialize node embeddings (simplified version using graph features)
|
|
@@ -182,6 +178,10 @@ export function syncClustering(graph: Graph, config: SynCConfig): SynCResult {
|
|
|
182
178
|
|
|
183
179
|
/**
|
|
184
180
|
* Initialize node embedding based on graph structure and features
|
|
181
|
+
* @param graph - The input graph
|
|
182
|
+
* @param nodeId - The node ID to initialize embedding for
|
|
183
|
+
* @param dim - The embedding dimension
|
|
184
|
+
* @returns The initialized embedding vector
|
|
185
185
|
*/
|
|
186
186
|
function initializeNodeEmbedding(graph: Graph, nodeId: NodeId, dim: number): number[] {
|
|
187
187
|
const embedding = new Array<number>(dim).fill(0);
|
|
@@ -192,7 +192,7 @@ function initializeNodeEmbedding(graph: Graph, nodeId: NodeId, dim: number): num
|
|
|
192
192
|
|
|
193
193
|
// Initialize with small random values influenced by graph structure
|
|
194
194
|
for (let i = 0; i < dim; i++) {
|
|
195
|
-
embedding[i] = (
|
|
195
|
+
embedding[i] = (Math.random() - 0.5) * 0.1 + normalizedDegree * 0.1;
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
return embedding;
|
|
@@ -200,6 +200,9 @@ function initializeNodeEmbedding(graph: Graph, nodeId: NodeId, dim: number): num
|
|
|
200
200
|
|
|
201
201
|
/**
|
|
202
202
|
* Initialize cluster centers using k-means++ style initialization
|
|
203
|
+
* @param embeddings - Map of node IDs to embedding vectors
|
|
204
|
+
* @param numClusters - Number of cluster centers to initialize
|
|
205
|
+
* @returns Array of initialized cluster center vectors
|
|
203
206
|
*/
|
|
204
207
|
function initializeClusterCenters(embeddings: Map<NodeId, number[]>, numClusters: number): number[][] {
|
|
205
208
|
const embeddingArray = Array.from(embeddings.values());
|
|
@@ -208,7 +211,7 @@ function initializeClusterCenters(embeddings: Map<NodeId, number[]>, numClusters
|
|
|
208
211
|
// Choose first center randomly
|
|
209
212
|
const firstCenter = embeddingArray[Math.floor(Math.random() * embeddingArray.length)];
|
|
210
213
|
if (firstCenter) {
|
|
211
|
-
centers.push([...
|
|
214
|
+
centers.push([...firstCenter] as number[]);
|
|
212
215
|
}
|
|
213
216
|
|
|
214
217
|
// Choose remaining centers using k-means++ initialization
|
|
@@ -237,7 +240,7 @@ function initializeClusterCenters(embeddings: Map<NodeId, number[]>, numClusters
|
|
|
237
240
|
if (randomValue <= 0) {
|
|
238
241
|
const newCenter = embeddingArray[i];
|
|
239
242
|
if (newCenter) {
|
|
240
|
-
centers.push([...
|
|
243
|
+
centers.push([...newCenter] as number[]);
|
|
241
244
|
}
|
|
242
245
|
|
|
243
246
|
break;
|
|
@@ -250,6 +253,11 @@ function initializeClusterCenters(embeddings: Map<NodeId, number[]>, numClusters
|
|
|
250
253
|
|
|
251
254
|
/**
|
|
252
255
|
* Update node embeddings using gradient descent
|
|
256
|
+
* @param graph - The input graph
|
|
257
|
+
* @param embeddings - Map of node IDs to embedding vectors
|
|
258
|
+
* @param clusters - Map of node IDs to cluster assignments
|
|
259
|
+
* @param learningRate - Learning rate for gradient descent
|
|
260
|
+
* @param lambda - Regularization parameter
|
|
253
261
|
*/
|
|
254
262
|
function updateEmbeddings(
|
|
255
263
|
graph: Graph,
|
|
@@ -291,7 +299,7 @@ function updateEmbeddings(
|
|
|
291
299
|
const gradVal = gradient[i];
|
|
292
300
|
const diffVal = diff[i];
|
|
293
301
|
if (gradVal !== undefined && diffVal !== undefined) {
|
|
294
|
-
gradient[i] = gradVal +
|
|
302
|
+
gradient[i] = gradVal + lambda * diffVal;
|
|
295
303
|
}
|
|
296
304
|
}
|
|
297
305
|
}
|
|
@@ -301,7 +309,7 @@ function updateEmbeddings(
|
|
|
301
309
|
const gradVal = gradient[i];
|
|
302
310
|
const nodeVal = nodeEmbedding[i];
|
|
303
311
|
if (gradVal !== undefined && nodeVal !== undefined) {
|
|
304
|
-
gradient[i] = gradVal +
|
|
312
|
+
gradient[i] = gradVal + lambda * nodeVal;
|
|
305
313
|
}
|
|
306
314
|
}
|
|
307
315
|
}
|
|
@@ -317,7 +325,7 @@ function updateEmbeddings(
|
|
|
317
325
|
const embVal = embedding[i];
|
|
318
326
|
const gradVal = gradient[i];
|
|
319
327
|
if (embVal !== undefined && gradVal !== undefined) {
|
|
320
|
-
embedding[i] = embVal -
|
|
328
|
+
embedding[i] = embVal - learningRate * gradVal;
|
|
321
329
|
}
|
|
322
330
|
}
|
|
323
331
|
}
|
|
@@ -325,6 +333,10 @@ function updateEmbeddings(
|
|
|
325
333
|
|
|
326
334
|
/**
|
|
327
335
|
* Update cluster centers based on current assignments
|
|
336
|
+
* @param embeddings - Map of node IDs to embedding vectors
|
|
337
|
+
* @param clusters - Map of node IDs to cluster assignments
|
|
338
|
+
* @param clusterCenters - Array of cluster center vectors to update
|
|
339
|
+
* @param numClusters - Number of clusters
|
|
328
340
|
*/
|
|
329
341
|
function updateClusterCenters(
|
|
330
342
|
embeddings: Map<NodeId, number[]>,
|
|
@@ -333,8 +345,9 @@ function updateClusterCenters(
|
|
|
333
345
|
numClusters: number,
|
|
334
346
|
): void {
|
|
335
347
|
const dimensions = clusterCenters[0]?.length ?? 0;
|
|
336
|
-
const clusterSums: number[][] = Array.from(
|
|
337
|
-
|
|
348
|
+
const clusterSums: number[][] = Array.from(
|
|
349
|
+
{ length: numClusters },
|
|
350
|
+
() => new Array(dimensions).fill(0) as number[],
|
|
338
351
|
);
|
|
339
352
|
const clusterCounts = new Array<number>(numClusters).fill(0);
|
|
340
353
|
|
|
@@ -380,6 +393,12 @@ function updateClusterCenters(
|
|
|
380
393
|
|
|
381
394
|
/**
|
|
382
395
|
* Calculate the total loss function
|
|
396
|
+
* @param graph - The input graph
|
|
397
|
+
* @param embeddings - Map of node IDs to embedding vectors
|
|
398
|
+
* @param clusters - Map of node IDs to cluster assignments
|
|
399
|
+
* @param clusterCenters - Array of cluster center vectors
|
|
400
|
+
* @param lambda - Regularization parameter
|
|
401
|
+
* @returns The total loss value
|
|
383
402
|
*/
|
|
384
403
|
function calculateLoss(
|
|
385
404
|
graph: Graph,
|
|
@@ -433,37 +452,5 @@ function calculateLoss(
|
|
|
433
452
|
}
|
|
434
453
|
}
|
|
435
454
|
|
|
436
|
-
return clusteringLoss +
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
/**
|
|
440
|
-
* Calculate Euclidean distance between two vectors
|
|
441
|
-
*/
|
|
442
|
-
function euclideanDistance(a: number[], b: number[]): number {
|
|
443
|
-
let sum = 0;
|
|
444
|
-
for (let i = 0; i < a.length; i++) {
|
|
445
|
-
const aVal = a[i];
|
|
446
|
-
const bVal = b[i];
|
|
447
|
-
if (aVal !== undefined && bVal !== undefined) {
|
|
448
|
-
const diff = aVal - bVal;
|
|
449
|
-
sum += diff * diff;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
return Math.sqrt(sum);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
/**
|
|
456
|
-
* Simple seeded random number generator for reproducibility
|
|
457
|
-
*/
|
|
458
|
-
function seedRandom(seed: number): () => number {
|
|
459
|
-
const m = 0x80000000; // 2**31
|
|
460
|
-
const a = 1103515245;
|
|
461
|
-
const c = 12345;
|
|
462
|
-
|
|
463
|
-
seed = seed % m;
|
|
464
|
-
|
|
465
|
-
return function() {
|
|
466
|
-
seed = ((a * seed) + c) % m;
|
|
467
|
-
return seed / (m - 1);
|
|
468
|
-
};
|
|
455
|
+
return clusteringLoss + lambda * reconstructionLoss + lambda * regularizationLoss;
|
|
469
456
|
}
|