@graphty/algorithms 1.3.1 → 1.5.0
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 +368 -422
- package/dist/algorithms.js +567 -264
- package/dist/algorithms.js.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.d.ts +10 -0
- package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.js +27 -6
- package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
- package/dist/src/algorithms/centrality/closeness.d.ts +11 -2
- package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/closeness.js +17 -3
- 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 +9 -6
- package/dist/src/algorithms/centrality/degree.js.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts +21 -3
- package/dist/src/algorithms/centrality/delta-pagerank-simple.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js +25 -7
- package/dist/src/algorithms/centrality/delta-pagerank-simple.js.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts +28 -2
- package/dist/src/algorithms/centrality/delta-pagerank.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/delta-pagerank.js +30 -4
- package/dist/src/algorithms/centrality/delta-pagerank.js.map +1 -1
- 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 +1 -1
- package/dist/src/algorithms/centrality/index.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/index.js +1 -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 +14 -0
- package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.js +16 -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 +21 -4
- package/dist/src/algorithms/community/girvan-newman.js.map +1 -1
- 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 +0 -3
- package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -1
- package/dist/src/algorithms/community/label-propagation.js +13 -6
- package/dist/src/algorithms/community/label-propagation.js.map +1 -1
- package/dist/src/algorithms/community/leiden.d.ts +0 -1
- package/dist/src/algorithms/community/leiden.d.ts.map +1 -1
- package/dist/src/algorithms/community/leiden.js +37 -6
- package/dist/src/algorithms/community/leiden.js.map +1 -1
- package/dist/src/algorithms/community/louvain-optimized.d.ts +37 -0
- package/dist/src/algorithms/community/louvain-optimized.d.ts.map +1 -1
- package/dist/src/algorithms/community/louvain-optimized.js +42 -7
- package/dist/src/algorithms/community/louvain-optimized.js.map +1 -1
- 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 +19 -6
- package/dist/src/algorithms/community/louvain.js.map +1 -1
- package/dist/src/algorithms/community/modularity-utils.d.ts +0 -4
- package/dist/src/algorithms/community/modularity-utils.d.ts.map +1 -1
- package/dist/src/algorithms/community/modularity-utils.js +1 -5
- package/dist/src/algorithms/community/modularity-utils.js.map +1 -1
- 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 +10 -0
- 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 +10 -0
- package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts +12 -5
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js +19 -17
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts +15 -0
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.js +15 -0
- 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 +13 -0
- package/dist/src/algorithms/traversal/bfs-unified.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs-unified.js +37 -0
- package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs-variants.d.ts +23 -0
- package/dist/src/algorithms/traversal/bfs-variants.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs-variants.js +34 -0
- package/dist/src/algorithms/traversal/bfs-variants.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs.d.ts +1 -1
- package/dist/src/algorithms/traversal/bfs.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs.js +1 -1
- 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 +2 -0
- package/dist/src/benchmark-all-algorithms.d.ts.map +1 -1
- package/dist/src/benchmark-all-algorithms.js +32 -3
- package/dist/src/benchmark-all-algorithms.js.map +1 -1
- package/dist/src/clustering/hierarchical.d.ts +8 -1
- package/dist/src/clustering/hierarchical.d.ts.map +1 -1
- package/dist/src/clustering/hierarchical.js +22 -6
- package/dist/src/clustering/hierarchical.js.map +1 -1
- package/dist/src/clustering/index.d.ts.map +1 -1
- package/dist/src/clustering/index.js.map +1 -1
- package/dist/src/clustering/k-core.d.ts +2 -5
- package/dist/src/clustering/k-core.d.ts.map +1 -1
- package/dist/src/clustering/k-core.js +14 -12
- 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 +4 -1
- package/dist/src/clustering/spectral.d.ts.map +1 -1
- package/dist/src/clustering/spectral.js +61 -28
- 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 +45 -1
- 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 +4 -2
- package/dist/src/flow/ford-fulkerson.d.ts.map +1 -1
- package/dist/src/flow/ford-fulkerson.js +31 -2
- package/dist/src/flow/ford-fulkerson.js.map +1 -1
- package/dist/src/flow/min-cut.d.ts +0 -3
- package/dist/src/flow/min-cut.d.ts.map +1 -1
- package/dist/src/flow/min-cut.js +17 -4
- package/dist/src/flow/min-cut.js.map +1 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -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 +36 -10
- 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 +27 -6
- 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 +103 -53
- package/dist/src/optimized/bit-packed.d.ts.map +1 -1
- package/dist/src/optimized/bit-packed.js +104 -54
- package/dist/src/optimized/bit-packed.js.map +1 -1
- package/dist/src/optimized/csr-graph.d.ts +87 -24
- package/dist/src/optimized/csr-graph.d.ts.map +1 -1
- package/dist/src/optimized/csr-graph.js +87 -24
- package/dist/src/optimized/csr-graph.js.map +1 -1
- package/dist/src/optimized/direction-optimized-bfs.d.ts +37 -16
- package/dist/src/optimized/direction-optimized-bfs.d.ts.map +1 -1
- package/dist/src/optimized/direction-optimized-bfs.js +39 -20
- package/dist/src/optimized/direction-optimized-bfs.js.map +1 -1
- package/dist/src/optimized/graph-adapter.d.ts +58 -9
- package/dist/src/optimized/graph-adapter.d.ts.map +1 -1
- package/dist/src/optimized/graph-adapter.js +58 -10
- package/dist/src/optimized/graph-adapter.js.map +1 -1
- package/dist/src/optimized/index.d.ts +1 -1
- package/dist/src/optimized/index.d.ts.map +1 -1
- package/dist/src/optimized/index.js +3 -1
- package/dist/src/optimized/index.js.map +1 -1
- 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 +23 -10
- 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 +29 -8
- package/dist/src/research/sync.js.map +1 -1
- package/dist/src/research/terahac.d.ts +0 -1
- package/dist/src/research/terahac.d.ts.map +1 -1
- package/dist/src/research/terahac.js +32 -4
- package/dist/src/research/terahac.js.map +1 -1
- package/dist/src/utils/graph-converters.d.ts +31 -0
- package/dist/src/utils/graph-converters.d.ts.map +1 -1
- package/dist/src/utils/graph-converters.js +31 -0
- package/dist/src/utils/graph-converters.js.map +1 -1
- package/dist/src/utils/graph-utilities.d.ts.map +1 -1
- package/dist/src/utils/graph-utilities.js.map +1 -1
- package/dist/src/utils/math-utilities.d.ts +11 -0
- package/dist/src/utils/math-utilities.d.ts.map +1 -1
- package/dist/src/utils/math-utilities.js +14 -5
- package/dist/src/utils/math-utilities.js.map +1 -1
- package/dist/src/utils/optimization-helpers.d.ts +9 -0
- package/dist/src/utils/optimization-helpers.d.ts.map +1 -1
- package/dist/src/utils/optimization-helpers.js +12 -0
- package/dist/src/utils/optimization-helpers.js.map +1 -1
- 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/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +56 -65
- package/src/algorithms/centrality/betweenness.ts +34 -17
- package/src/algorithms/centrality/closeness.ts +34 -17
- package/src/algorithms/centrality/degree.ts +15 -19
- package/src/algorithms/centrality/delta-pagerank-simple.ts +36 -28
- package/src/algorithms/centrality/delta-pagerank.ts +34 -11
- package/src/algorithms/centrality/eigenvector.ts +14 -15
- package/src/algorithms/centrality/hits.ts +17 -17
- package/src/algorithms/centrality/index.ts +20 -15
- package/src/algorithms/centrality/katz.ts +17 -25
- package/src/algorithms/centrality/pagerank.ts +26 -17
- package/src/algorithms/community/girvan-newman.ts +39 -28
- package/src/algorithms/community/index.ts +6 -7
- package/src/algorithms/community/label-propagation.ts +20 -28
- package/src/algorithms/community/leiden.ts +61 -37
- package/src/algorithms/community/louvain-optimized.ts +49 -20
- package/src/algorithms/community/louvain.ts +35 -25
- package/src/algorithms/community/modularity-utils.ts +5 -17
- package/src/algorithms/components/connected.ts +40 -17
- package/src/algorithms/matching/bipartite.ts +17 -10
- 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 -15
- package/src/algorithms/shortest-path/bidirectional-dijkstra.ts +29 -29
- package/src/algorithms/shortest-path/dijkstra.ts +23 -17
- 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 +64 -60
- package/src/algorithms/traversal/bfs-variants.ts +61 -37
- package/src/algorithms/traversal/bfs.ts +1 -7
- package/src/algorithms/traversal/dfs.ts +61 -42
- package/src/algorithms/traversal/index.ts +3 -3
- package/src/benchmark-all-algorithms.ts +57 -25
- package/src/clustering/hierarchical.ts +40 -34
- package/src/clustering/index.ts +8 -8
- package/src/clustering/k-core.ts +26 -42
- package/src/clustering/mcl.ts +53 -23
- package/src/clustering/spectral.ts +91 -57
- package/src/core/graph.ts +54 -14
- 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 +62 -35
- package/src/flow/min-cut.ts +43 -45
- package/src/index.ts +3 -2
- package/src/link-prediction/adamic-adar.ts +65 -51
- package/src/link-prediction/common-neighbors.ts +45 -36
- package/src/link-prediction/index.ts +1 -1
- package/src/optimized/bit-packed.ts +106 -56
- package/src/optimized/csr-graph.ts +93 -30
- package/src/optimized/direction-optimized-bfs.ts +44 -25
- package/src/optimized/graph-adapter.ts +64 -16
- package/src/optimized/index.ts +7 -9
- package/src/pathfinding/astar.ts +40 -25
- package/src/pathfinding/utils.ts +20 -13
- package/src/research/grsbm.ts +31 -25
- package/src/research/index.ts +4 -4
- package/src/research/sync.ts +40 -24
- package/src/research/terahac.ts +54 -20
- package/src/utils/graph-converters.ts +42 -14
- package/src/utils/graph-utilities.ts +11 -37
- package/src/utils/math-utilities.ts +17 -8
- package/src/utils/optimization-helpers.ts +27 -21
- package/src/utils/priorityQueue.ts +40 -6
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import * as algorithms from "./algorithms/index.js";
|
|
11
|
-
import {Graph} from "./core/graph.js";
|
|
11
|
+
import { Graph } from "./core/graph.js";
|
|
12
12
|
|
|
13
13
|
interface BenchmarkResult {
|
|
14
14
|
algorithm: string;
|
|
@@ -35,8 +35,8 @@ const ALGORITHM_CATEGORIES = {
|
|
|
35
35
|
],
|
|
36
36
|
config: {
|
|
37
37
|
name: "Traversal",
|
|
38
|
-
quick: {nodes: 1000, edges: 5000, runs: 3},
|
|
39
|
-
full: {nodes: 10000, edges: 50000, runs: 10},
|
|
38
|
+
quick: { nodes: 1000, edges: 5000, runs: 3 },
|
|
39
|
+
full: { nodes: 10000, edges: 50000, runs: 10 },
|
|
40
40
|
},
|
|
41
41
|
},
|
|
42
42
|
shortestPath: {
|
|
@@ -54,8 +54,8 @@ const ALGORITHM_CATEGORIES = {
|
|
|
54
54
|
],
|
|
55
55
|
config: {
|
|
56
56
|
name: "Shortest Path",
|
|
57
|
-
quick: {nodes: 100, edges: 500, runs: 1}, // Smaller for O(V³) algorithms
|
|
58
|
-
full: {nodes: 500, edges: 2500, runs: 3},
|
|
57
|
+
quick: { nodes: 100, edges: 500, runs: 1 }, // Smaller for O(V³) algorithms
|
|
58
|
+
full: { nodes: 500, edges: 2500, runs: 3 },
|
|
59
59
|
},
|
|
60
60
|
},
|
|
61
61
|
centrality: {
|
|
@@ -71,8 +71,8 @@ const ALGORITHM_CATEGORIES = {
|
|
|
71
71
|
],
|
|
72
72
|
config: {
|
|
73
73
|
name: "Centrality",
|
|
74
|
-
quick: {nodes: 500, edges: 2500, runs: 1},
|
|
75
|
-
full: {nodes: 2000, edges: 10000, runs: 3},
|
|
74
|
+
quick: { nodes: 500, edges: 2500, runs: 1 },
|
|
75
|
+
full: { nodes: 2000, edges: 10000, runs: 3 },
|
|
76
76
|
},
|
|
77
77
|
},
|
|
78
78
|
components: {
|
|
@@ -90,44 +90,48 @@ const ALGORITHM_CATEGORIES = {
|
|
|
90
90
|
],
|
|
91
91
|
config: {
|
|
92
92
|
name: "Components",
|
|
93
|
-
quick: {nodes: 2000, edges: 10000, runs: 3},
|
|
94
|
-
full: {nodes: 10000, edges: 50000, runs: 10},
|
|
93
|
+
quick: { nodes: 2000, edges: 10000, runs: 3 },
|
|
94
|
+
full: { nodes: 10000, edges: 50000, runs: 10 },
|
|
95
95
|
},
|
|
96
96
|
},
|
|
97
97
|
mst: {
|
|
98
98
|
algorithms: ["kruskalMST", "primMST", "minimumSpanningTree"],
|
|
99
99
|
config: {
|
|
100
100
|
name: "Minimum Spanning Tree",
|
|
101
|
-
quick: {nodes: 1000, edges: 5000, runs: 5},
|
|
102
|
-
full: {nodes: 5000, edges: 25000, runs: 10},
|
|
101
|
+
quick: { nodes: 1000, edges: 5000, runs: 5 },
|
|
102
|
+
full: { nodes: 5000, edges: 25000, runs: 10 },
|
|
103
103
|
},
|
|
104
104
|
},
|
|
105
105
|
community: {
|
|
106
106
|
algorithms: ["girvanNewman", "labelPropagation", "leiden", "louvain"],
|
|
107
107
|
config: {
|
|
108
108
|
name: "Community Detection",
|
|
109
|
-
quick: {nodes: 200, edges: 1000, runs: 1}, // Very expensive algorithms
|
|
110
|
-
full: {nodes: 1000, edges: 5000, runs: 2},
|
|
109
|
+
quick: { nodes: 200, edges: 1000, runs: 1 }, // Very expensive algorithms
|
|
110
|
+
full: { nodes: 1000, edges: 5000, runs: 2 },
|
|
111
111
|
},
|
|
112
112
|
},
|
|
113
113
|
matching: {
|
|
114
114
|
algorithms: ["bipartitePartition", "greedyBipartiteMatching", "maximumBipartiteMatching"],
|
|
115
115
|
config: {
|
|
116
116
|
name: "Matching",
|
|
117
|
-
quick: {nodes: 500, edges: 1000, runs: 3},
|
|
118
|
-
full: {nodes: 2000, edges: 4000, runs: 5},
|
|
117
|
+
quick: { nodes: 500, edges: 1000, runs: 3 },
|
|
118
|
+
full: { nodes: 2000, edges: 4000, runs: 5 },
|
|
119
119
|
},
|
|
120
120
|
},
|
|
121
121
|
} as const;
|
|
122
122
|
|
|
123
123
|
/**
|
|
124
124
|
* Main benchmark runner
|
|
125
|
+
* @param mode - The benchmark mode: "quick" for fast tests or "full" for comprehensive tests
|
|
126
|
+
* @returns Array of benchmark results for all tested algorithms
|
|
125
127
|
*/
|
|
126
128
|
export function runAllAlgorithmsBenchmark(mode: "quick" | "full" = "full"): BenchmarkResult[] {
|
|
127
129
|
const startTime = Date.now();
|
|
128
130
|
const timeLimit = mode === "quick" ? 2 * 60 * 1000 : 60 * 60 * 1000; // 2 min or 1 hour
|
|
129
131
|
|
|
130
|
-
console.log(
|
|
132
|
+
console.log(
|
|
133
|
+
`🚀 ${mode === "quick" ? "Quick" : "Comprehensive"} Benchmark - All ${String(getTotalAlgorithmCount())} Algorithms`,
|
|
134
|
+
);
|
|
131
135
|
console.log(`=${"=".repeat(60)}`);
|
|
132
136
|
console.log(`Time limit: ${mode === "quick" ? "2 minutes" : "1 hour"}`);
|
|
133
137
|
console.log(`Start time: ${new Date().toLocaleTimeString()}\n`);
|
|
@@ -175,7 +179,9 @@ export function runAllAlgorithmsBenchmark(mode: "quick" | "full" = "full"): Benc
|
|
|
175
179
|
console.log("===================");
|
|
176
180
|
console.log(`Total time: ${totalTime.toFixed(1)}s`);
|
|
177
181
|
console.log(`Algorithms tested: ${String(totalTested)}`);
|
|
178
|
-
console.log(
|
|
182
|
+
console.log(
|
|
183
|
+
`Success rate: ${String(totalPassed)}/${String(totalTested)} (${((totalPassed / totalTested) * 100).toFixed(1)}%)`,
|
|
184
|
+
);
|
|
179
185
|
|
|
180
186
|
if (mode === "full") {
|
|
181
187
|
displayDetailedResults(results);
|
|
@@ -186,6 +192,12 @@ export function runAllAlgorithmsBenchmark(mode: "quick" | "full" = "full"): Benc
|
|
|
186
192
|
|
|
187
193
|
/**
|
|
188
194
|
* Benchmark a single algorithm
|
|
195
|
+
* @param algoName - The name of the algorithm to benchmark
|
|
196
|
+
* @param category - The category the algorithm belongs to
|
|
197
|
+
* @param graph - The test graph to run the algorithm on
|
|
198
|
+
* @param runs - Number of times to run the algorithm for averaging
|
|
199
|
+
* @param mode - The benchmark mode (quick or full)
|
|
200
|
+
* @returns The benchmark result containing timing and success information
|
|
189
201
|
*/
|
|
190
202
|
function benchmarkAlgorithm(
|
|
191
203
|
algoName: string,
|
|
@@ -216,7 +228,7 @@ function benchmarkAlgorithm(
|
|
|
216
228
|
const args = getAlgorithmArgs(algoName, graph);
|
|
217
229
|
const start = performance.now();
|
|
218
230
|
|
|
219
|
-
(algo as (...
|
|
231
|
+
(algo as (...args: unknown[]) => unknown)(...args);
|
|
220
232
|
|
|
221
233
|
const time = performance.now() - start;
|
|
222
234
|
times.push(time);
|
|
@@ -251,6 +263,9 @@ function benchmarkAlgorithm(
|
|
|
251
263
|
|
|
252
264
|
/**
|
|
253
265
|
* Get appropriate arguments for each algorithm
|
|
266
|
+
* @param algoName - The name of the algorithm to get arguments for
|
|
267
|
+
* @param graph - The base graph to use for generating arguments
|
|
268
|
+
* @returns Array of arguments to pass to the algorithm function
|
|
254
269
|
*/
|
|
255
270
|
function getAlgorithmArgs(algoName: string, graph: Graph): unknown[] {
|
|
256
271
|
// Get random nodes for algorithms that need them
|
|
@@ -326,6 +341,10 @@ function getAlgorithmArgs(algoName: string, graph: Graph): unknown[] {
|
|
|
326
341
|
|
|
327
342
|
/**
|
|
328
343
|
* Create test graph appropriate for each algorithm category
|
|
344
|
+
* @param nodes - Number of nodes in the test graph
|
|
345
|
+
* @param edges - Target number of edges in the test graph
|
|
346
|
+
* @param category - The algorithm category to optimize the graph structure for
|
|
347
|
+
* @returns A new test graph with the specified structure
|
|
329
348
|
*/
|
|
330
349
|
function createTestGraph(nodes: number, edges: number, category: string): Graph {
|
|
331
350
|
const graph = new Graph();
|
|
@@ -362,6 +381,8 @@ function createTestGraph(nodes: number, edges: number, category: string): Graph
|
|
|
362
381
|
|
|
363
382
|
/**
|
|
364
383
|
* Create a connected random graph
|
|
384
|
+
* @param graph - The graph to add edges to
|
|
385
|
+
* @param targetEdges - Target number of edges to create
|
|
365
386
|
*/
|
|
366
387
|
function createConnectedGraph(graph: Graph, targetEdges: number): void {
|
|
367
388
|
const nodes = graph.nodeCount;
|
|
@@ -387,6 +408,8 @@ function createConnectedGraph(graph: Graph, targetEdges: number): void {
|
|
|
387
408
|
|
|
388
409
|
/**
|
|
389
410
|
* Create small-world graph (Watts-Strogatz model)
|
|
411
|
+
* @param graph - The graph to add edges to
|
|
412
|
+
* @param avgDegree - Target average degree for each node
|
|
390
413
|
*/
|
|
391
414
|
function createSmallWorldGraph(graph: Graph, avgDegree: number): void {
|
|
392
415
|
const nodes = graph.nodeCount;
|
|
@@ -418,6 +441,8 @@ function createSmallWorldGraph(graph: Graph, avgDegree: number): void {
|
|
|
418
441
|
|
|
419
442
|
/**
|
|
420
443
|
* Create graph with community structure
|
|
444
|
+
* @param graph - The graph to add edges to
|
|
445
|
+
* @param targetEdges - Target number of edges to create
|
|
421
446
|
*/
|
|
422
447
|
function createCommunityGraph(graph: Graph, targetEdges: number): void {
|
|
423
448
|
const nodes = graph.nodeCount;
|
|
@@ -433,7 +458,8 @@ function createCommunityGraph(graph: Graph, targetEdges: number): void {
|
|
|
433
458
|
|
|
434
459
|
for (let i = start; i < end && edgesAdded < targetEdges; i++) {
|
|
435
460
|
for (let j = i + 1; j < end && edgesAdded < targetEdges; j++) {
|
|
436
|
-
if (Math.random() < 0.8) {
|
|
461
|
+
if (Math.random() < 0.8) {
|
|
462
|
+
// High probability within community
|
|
437
463
|
graph.addEdge(i, j);
|
|
438
464
|
edgesAdded++;
|
|
439
465
|
}
|
|
@@ -455,9 +481,11 @@ function createCommunityGraph(graph: Graph, targetEdges: number): void {
|
|
|
455
481
|
|
|
456
482
|
/**
|
|
457
483
|
* Create directed version of graph
|
|
484
|
+
* @param undirectedGraph - The undirected graph to convert
|
|
485
|
+
* @returns A new directed graph with the same nodes and edges
|
|
458
486
|
*/
|
|
459
487
|
function createDirectedGraph(undirectedGraph: Graph): Graph {
|
|
460
|
-
const directedGraph = new Graph({directed: true});
|
|
488
|
+
const directedGraph = new Graph({ directed: true });
|
|
461
489
|
|
|
462
490
|
// Copy nodes
|
|
463
491
|
for (const node of undirectedGraph.nodes()) {
|
|
@@ -474,6 +502,8 @@ function createDirectedGraph(undirectedGraph: Graph): Graph {
|
|
|
474
502
|
|
|
475
503
|
/**
|
|
476
504
|
* Create bipartite graph
|
|
505
|
+
* @param nodesPerPartition - Number of nodes in each partition
|
|
506
|
+
* @returns A new bipartite graph with sparse connections between partitions
|
|
477
507
|
*/
|
|
478
508
|
function createBipartiteGraph(nodesPerPartition: number): Graph {
|
|
479
509
|
const graph = new Graph();
|
|
@@ -486,7 +516,8 @@ function createBipartiteGraph(nodesPerPartition: number): Graph {
|
|
|
486
516
|
// Add edges only between partitions
|
|
487
517
|
for (let i = 0; i < nodesPerPartition; i++) {
|
|
488
518
|
for (let j = nodesPerPartition; j < nodesPerPartition * 2; j++) {
|
|
489
|
-
if (Math.random() < 0.3) {
|
|
519
|
+
if (Math.random() < 0.3) {
|
|
520
|
+
// Sparse bipartite graph
|
|
490
521
|
graph.addEdge(i, j);
|
|
491
522
|
}
|
|
492
523
|
}
|
|
@@ -497,14 +528,15 @@ function createBipartiteGraph(nodesPerPartition: number): Graph {
|
|
|
497
528
|
|
|
498
529
|
/**
|
|
499
530
|
* Get total algorithm count
|
|
531
|
+
* @returns The total number of algorithms across all categories
|
|
500
532
|
*/
|
|
501
533
|
function getTotalAlgorithmCount(): number {
|
|
502
|
-
return Object.values(ALGORITHM_CATEGORIES).reduce((total, category) =>
|
|
503
|
-
total + category.algorithms.length, 0);
|
|
534
|
+
return Object.values(ALGORITHM_CATEGORIES).reduce((total, category) => total + category.algorithms.length, 0);
|
|
504
535
|
}
|
|
505
536
|
|
|
506
537
|
/**
|
|
507
538
|
* Display detailed results for full benchmark
|
|
539
|
+
* @param results - Array of benchmark results to display
|
|
508
540
|
*/
|
|
509
541
|
function displayDetailedResults(results: BenchmarkResult[]): void {
|
|
510
542
|
console.log("\n📊 Detailed Results");
|
|
@@ -530,8 +562,8 @@ function displayDetailedResults(results: BenchmarkResult[]): void {
|
|
|
530
562
|
if (successful.length > 0) {
|
|
531
563
|
const avgTime = successful.reduce((sum, r) => sum + r.executionTime, 0) / successful.length;
|
|
532
564
|
console.log(` Average time: ${avgTime.toFixed(2)}ms`);
|
|
533
|
-
const minTime = Math.min(...
|
|
534
|
-
const maxTime = Math.max(...
|
|
565
|
+
const minTime = Math.min(...successful.map((r) => r.executionTime));
|
|
566
|
+
const maxTime = Math.max(...successful.map((r) => r.executionTime));
|
|
535
567
|
console.log(` Range: ${minTime.toFixed(2)}ms - ${maxTime.toFixed(2)}ms`);
|
|
536
568
|
}
|
|
537
569
|
|
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
* structure in graphs.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type {Graph} from "../core/graph.js";
|
|
9
|
+
import type { Graph } from "../core/graph.js";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Convert Graph to adjacency set representation for clustering
|
|
13
|
+
* @param graph - The input graph to convert
|
|
14
|
+
* @returns Map of node IDs to sets of neighbor node IDs
|
|
13
15
|
*/
|
|
14
16
|
function graphToAdjacencySet(graph: Graph): Map<string, Set<string>> {
|
|
15
17
|
const adjacency = new Map<string, Set<string>>();
|
|
@@ -62,11 +64,10 @@ export type LinkageMethod = "single" | "complete" | "average" | "ward";
|
|
|
62
64
|
|
|
63
65
|
/**
|
|
64
66
|
* Compute distance between two nodes based on graph structure
|
|
67
|
+
* @param graph - Adjacency map representation of the graph
|
|
68
|
+
* @param distances - Map to store computed distances between nodes
|
|
65
69
|
*/
|
|
66
|
-
function computeGraphDistance<T>(
|
|
67
|
-
graph: Map<T, Set<T>>,
|
|
68
|
-
distances: Map<T, Map<T, number>>,
|
|
69
|
-
): void {
|
|
70
|
+
function computeGraphDistance<T>(graph: Map<T, Set<T>>, distances: Map<T, Map<T, number>>): void {
|
|
70
71
|
// Use BFS to compute shortest path distances
|
|
71
72
|
for (const start of graph.keys()) {
|
|
72
73
|
const dist = new Map<T, number>();
|
|
@@ -97,6 +98,11 @@ function computeGraphDistance<T>(
|
|
|
97
98
|
|
|
98
99
|
/**
|
|
99
100
|
* Compute distance between two clusters based on linkage method
|
|
101
|
+
* @param cluster1 - First cluster node
|
|
102
|
+
* @param cluster2 - Second cluster node
|
|
103
|
+
* @param distances - Precomputed distance matrix between nodes
|
|
104
|
+
* @param linkage - Linkage method to use for computing cluster distance
|
|
105
|
+
* @returns The distance between the two clusters
|
|
100
106
|
*/
|
|
101
107
|
function clusterDistance<T>(
|
|
102
108
|
cluster1: ClusterNode<T>,
|
|
@@ -126,9 +132,9 @@ function clusterDistance<T>(
|
|
|
126
132
|
|
|
127
133
|
switch (linkage) {
|
|
128
134
|
case "single":
|
|
129
|
-
return Math.min(...
|
|
135
|
+
return Math.min(...allDistances);
|
|
130
136
|
case "complete":
|
|
131
|
-
return Math.max(...
|
|
137
|
+
return Math.max(...allDistances);
|
|
132
138
|
case "average":
|
|
133
139
|
return allDistances.reduce((a, b) => a + b, 0) / allDistances.length;
|
|
134
140
|
case "ward": {
|
|
@@ -139,12 +145,15 @@ function clusterDistance<T>(
|
|
|
139
145
|
return avgDist * ((size1 * size2) / (size1 + size2));
|
|
140
146
|
}
|
|
141
147
|
default:
|
|
142
|
-
return Math.min(...
|
|
148
|
+
return Math.min(...allDistances);
|
|
143
149
|
}
|
|
144
150
|
}
|
|
145
151
|
|
|
146
152
|
/**
|
|
147
153
|
* Internal implementation of agglomerative hierarchical clustering
|
|
154
|
+
* @param graph - Adjacency map representation of the graph
|
|
155
|
+
* @param linkage - Linkage method for cluster distance computation
|
|
156
|
+
* @returns Hierarchical clustering result with dendrogram and cluster assignments
|
|
148
157
|
*/
|
|
149
158
|
function hierarchicalClusteringImpl<T>(
|
|
150
159
|
graph: Map<T, Set<T>>,
|
|
@@ -155,7 +164,7 @@ function hierarchicalClusteringImpl<T>(
|
|
|
155
164
|
|
|
156
165
|
if (n === 0) {
|
|
157
166
|
return {
|
|
158
|
-
root: {id: "empty", members: new Set(), distance: 0, height: 0},
|
|
167
|
+
root: { id: "empty", members: new Set(), distance: 0, height: 0 },
|
|
159
168
|
dendrogram: [],
|
|
160
169
|
clusters: new Map(),
|
|
161
170
|
};
|
|
@@ -173,7 +182,7 @@ function hierarchicalClusteringImpl<T>(
|
|
|
173
182
|
height: 0,
|
|
174
183
|
}));
|
|
175
184
|
|
|
176
|
-
const dendrogram: ClusterNode<T>[] = [...
|
|
185
|
+
const dendrogram: ClusterNode<T>[] = [...clusters];
|
|
177
186
|
let clusterCount = n;
|
|
178
187
|
|
|
179
188
|
// Distance matrix between clusters
|
|
@@ -220,9 +229,8 @@ function hierarchicalClusteringImpl<T>(
|
|
|
220
229
|
continue;
|
|
221
230
|
}
|
|
222
231
|
|
|
223
|
-
const dist =
|
|
224
|
-
clusterDistances.get(cluster2.id)?.get(cluster1.id) ??
|
|
225
|
-
Infinity;
|
|
232
|
+
const dist =
|
|
233
|
+
distMap.get(cluster2.id) ?? clusterDistances.get(cluster2.id)?.get(cluster1.id) ?? Infinity;
|
|
226
234
|
|
|
227
235
|
if (dist < minDist) {
|
|
228
236
|
minDist = dist;
|
|
@@ -239,7 +247,7 @@ function hierarchicalClusteringImpl<T>(
|
|
|
239
247
|
id: `forest-${String(clusterCount++)}`,
|
|
240
248
|
members: new Set(nodes),
|
|
241
249
|
distance: Infinity,
|
|
242
|
-
height: Math.max(...
|
|
250
|
+
height: Math.max(...trees.map((t) => t.height)) + 1,
|
|
243
251
|
trees,
|
|
244
252
|
};
|
|
245
253
|
dendrogram.push(forestRoot);
|
|
@@ -251,7 +259,7 @@ function hierarchicalClusteringImpl<T>(
|
|
|
251
259
|
// Create new cluster
|
|
252
260
|
const newCluster: ClusterNode<T> = {
|
|
253
261
|
id: `cluster-${String(clusterCount++)}`,
|
|
254
|
-
members: new Set([...
|
|
262
|
+
members: new Set([...mergeCluster1.members, ...mergeCluster2.members]),
|
|
255
263
|
left: mergeCluster1,
|
|
256
264
|
right: mergeCluster2,
|
|
257
265
|
distance: minDist,
|
|
@@ -301,7 +309,7 @@ function hierarchicalClusteringImpl<T>(
|
|
|
301
309
|
|
|
302
310
|
if (!root) {
|
|
303
311
|
return {
|
|
304
|
-
root: {id: "empty", members: new Set(), distance: 0, height: 0},
|
|
312
|
+
root: { id: "empty", members: new Set(), distance: 0, height: 0 },
|
|
305
313
|
dendrogram: [],
|
|
306
314
|
clusters: new Map(),
|
|
307
315
|
};
|
|
@@ -320,11 +328,11 @@ function hierarchicalClusteringImpl<T>(
|
|
|
320
328
|
|
|
321
329
|
/**
|
|
322
330
|
* Cut dendrogram at specific height to get clusters
|
|
331
|
+
* @param root - Root node of the dendrogram
|
|
332
|
+
* @param height - Height at which to cut the dendrogram
|
|
333
|
+
* @returns Array of sets representing clusters at the specified height
|
|
323
334
|
*/
|
|
324
|
-
export function cutDendrogram<T>(
|
|
325
|
-
root: ClusterNode<T>,
|
|
326
|
-
height: number,
|
|
327
|
-
): Set<T>[] {
|
|
335
|
+
export function cutDendrogram<T>(root: ClusterNode<T>, height: number): Set<T>[] {
|
|
328
336
|
const clusters: Set<T>[] = [];
|
|
329
337
|
|
|
330
338
|
function traverse(node: ClusterNode<T>): void {
|
|
@@ -354,11 +362,11 @@ export function cutDendrogram<T>(
|
|
|
354
362
|
|
|
355
363
|
/**
|
|
356
364
|
* Cut dendrogram to get exactly k clusters
|
|
365
|
+
* @param root - Root node of the dendrogram
|
|
366
|
+
* @param k - Target number of clusters
|
|
367
|
+
* @returns Array of sets representing exactly k clusters
|
|
357
368
|
*/
|
|
358
|
-
export function cutDendrogramKClusters<T>(
|
|
359
|
-
root: ClusterNode<T>,
|
|
360
|
-
k: number,
|
|
361
|
-
): Set<T>[] {
|
|
369
|
+
export function cutDendrogramKClusters<T>(root: ClusterNode<T>, k: number): Set<T>[] {
|
|
362
370
|
if (k <= 0) {
|
|
363
371
|
return [];
|
|
364
372
|
}
|
|
@@ -390,17 +398,17 @@ export function cutDendrogramKClusters<T>(
|
|
|
390
398
|
/**
|
|
391
399
|
* Compute modularity-based hierarchical clustering
|
|
392
400
|
* Uses modularity gain to decide merges
|
|
401
|
+
* @param graph - The input graph to cluster
|
|
402
|
+
* @returns Hierarchical clustering result with dendrogram and cluster assignments
|
|
393
403
|
*/
|
|
394
|
-
export function modularityHierarchicalClustering(
|
|
395
|
-
graph: Graph,
|
|
396
|
-
): HierarchicalClusteringResult<string> {
|
|
404
|
+
export function modularityHierarchicalClustering(graph: Graph): HierarchicalClusteringResult<string> {
|
|
397
405
|
const adjacencySet = graphToAdjacencySet(graph);
|
|
398
406
|
const nodes = Array.from(adjacencySet.keys());
|
|
399
407
|
const n = nodes.length;
|
|
400
408
|
|
|
401
409
|
if (n === 0) {
|
|
402
410
|
return {
|
|
403
|
-
root: {id: "empty", members: new Set(), distance: 0, height: 0},
|
|
411
|
+
root: { id: "empty", members: new Set(), distance: 0, height: 0 },
|
|
404
412
|
dendrogram: [],
|
|
405
413
|
clusters: new Map(),
|
|
406
414
|
};
|
|
@@ -426,7 +434,7 @@ export function modularityHierarchicalClustering(
|
|
|
426
434
|
height: 0,
|
|
427
435
|
}));
|
|
428
436
|
|
|
429
|
-
const dendrogram: ClusterNode<string>[] = [...
|
|
437
|
+
const dendrogram: ClusterNode<string>[] = [...clusters];
|
|
430
438
|
let clusterCount = n;
|
|
431
439
|
|
|
432
440
|
// Active clusters
|
|
@@ -466,8 +474,7 @@ export function modularityHierarchicalClustering(
|
|
|
466
474
|
}
|
|
467
475
|
}
|
|
468
476
|
|
|
469
|
-
const gain =
|
|
470
|
-
(degreeProduct / (4 * totalEdges * totalEdges));
|
|
477
|
+
const gain = edgesBetween / totalEdges - degreeProduct / (4 * totalEdges * totalEdges);
|
|
471
478
|
|
|
472
479
|
if (gain > maxGain) {
|
|
473
480
|
maxGain = gain;
|
|
@@ -484,7 +491,7 @@ export function modularityHierarchicalClustering(
|
|
|
484
491
|
// Create new cluster
|
|
485
492
|
const newCluster: ClusterNode<string> = {
|
|
486
493
|
id: `cluster-${String(clusterCount++)}`,
|
|
487
|
-
members: new Set([...
|
|
494
|
+
members: new Set([...mergeCluster1.members, ...mergeCluster2.members]),
|
|
488
495
|
left: mergeCluster1,
|
|
489
496
|
right: mergeCluster2,
|
|
490
497
|
distance: -maxGain, // Use negative gain as distance
|
|
@@ -507,7 +514,7 @@ export function modularityHierarchicalClustering(
|
|
|
507
514
|
|
|
508
515
|
if (!root) {
|
|
509
516
|
return {
|
|
510
|
-
root: {id: "empty", members: new Set(), distance: 0, height: 0},
|
|
517
|
+
root: { id: "empty", members: new Set(), distance: 0, height: 0 },
|
|
511
518
|
dendrogram: [],
|
|
512
519
|
clusters: new Map(),
|
|
513
520
|
};
|
|
@@ -527,7 +534,6 @@ export function modularityHierarchicalClustering(
|
|
|
527
534
|
/**
|
|
528
535
|
* Agglomerative hierarchical clustering
|
|
529
536
|
* Builds clusters bottom-up by merging closest pairs
|
|
530
|
-
*
|
|
531
537
|
* @param graph - Undirected graph - accepts Graph class or Map<T, Set<T>>
|
|
532
538
|
* @param linkage - Linkage method for cluster distance
|
|
533
539
|
* @returns Hierarchical clustering result
|
package/src/clustering/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export type {ClusterNode, HierarchicalClusteringResult, LinkageMethod} from "./hierarchical.js";
|
|
2
|
-
export {hierarchicalClustering} from "./hierarchical.js";
|
|
3
|
-
export type {KCoreResult} from "./k-core.js";
|
|
4
|
-
export {getKCore, kCoreDecomposition} from "./k-core.js";
|
|
5
|
-
export type {MCLOptions, MCLResult} from "./mcl.js";
|
|
6
|
-
export {calculateMCLModularity, markovClustering} from "./mcl.js";
|
|
7
|
-
export type {SpectralClusteringOptions, SpectralClusteringResult} from "./spectral.js";
|
|
8
|
-
export {spectralClustering} from "./spectral.js";
|
|
1
|
+
export type { ClusterNode, HierarchicalClusteringResult, LinkageMethod } from "./hierarchical.js";
|
|
2
|
+
export { hierarchicalClustering } from "./hierarchical.js";
|
|
3
|
+
export type { KCoreResult } from "./k-core.js";
|
|
4
|
+
export { getKCore, kCoreDecomposition } from "./k-core.js";
|
|
5
|
+
export type { MCLOptions, MCLResult } from "./mcl.js";
|
|
6
|
+
export { calculateMCLModularity, markovClustering } from "./mcl.js";
|
|
7
|
+
export type { SpectralClusteringOptions, SpectralClusteringResult } from "./spectral.js";
|
|
8
|
+
export { spectralClustering } from "./spectral.js";
|
package/src/clustering/k-core.ts
CHANGED
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
* understanding graph structure.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type {Graph} from "../core/graph.js";
|
|
9
|
+
import type { Graph } from "../core/graph.js";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Convert Graph to adjacency set representation
|
|
13
|
+
* @param graph - The input graph to convert
|
|
14
|
+
* @returns Map of node IDs to sets of neighbor node IDs
|
|
13
15
|
*/
|
|
14
16
|
function graphToAdjacencySet(graph: Graph): Map<string, Set<string>> {
|
|
15
17
|
const adjacency = new Map<string, Set<string>>();
|
|
@@ -49,12 +51,12 @@ export interface KCoreResult<T> {
|
|
|
49
51
|
|
|
50
52
|
/**
|
|
51
53
|
* Internal implementation of K-Core decomposition algorithm
|
|
54
|
+
* @param graph - Adjacency map representation of the graph
|
|
55
|
+
* @returns K-core decomposition result with cores, coreness values, and maximum core number
|
|
52
56
|
*/
|
|
53
|
-
function kCoreDecompositionImpl<T>(
|
|
54
|
-
graph: Map<T, Set<T>>,
|
|
55
|
-
): KCoreResult<T> {
|
|
57
|
+
function kCoreDecompositionImpl<T>(graph: Map<T, Set<T>>): KCoreResult<T> {
|
|
56
58
|
if (graph.size === 0) {
|
|
57
|
-
return {cores: new Map(), coreness: new Map(), maxCore: 0};
|
|
59
|
+
return { cores: new Map(), coreness: new Map(), maxCore: 0 };
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
const n = graph.size;
|
|
@@ -208,22 +210,18 @@ function kCoreDecompositionImpl<T>(
|
|
|
208
210
|
maxCore = Math.max(maxCore, core);
|
|
209
211
|
}
|
|
210
212
|
|
|
211
|
-
return {cores, coreness, maxCore};
|
|
213
|
+
return { cores, coreness, maxCore };
|
|
212
214
|
}
|
|
213
215
|
|
|
214
216
|
/**
|
|
215
217
|
* Extract the k-core subgraph
|
|
216
218
|
* Returns nodes that belong to k-core or higher
|
|
217
|
-
*
|
|
218
219
|
* @param graph - Undirected graph
|
|
219
220
|
* @param k - Core number
|
|
220
221
|
* @returns Set of nodes in k-core or higher
|
|
221
222
|
*/
|
|
222
|
-
export function getKCore(
|
|
223
|
-
graph
|
|
224
|
-
k: number,
|
|
225
|
-
): Set<string> {
|
|
226
|
-
const {coreness} = kCoreDecomposition(graph);
|
|
223
|
+
export function getKCore(graph: Graph, k: number): Set<string> {
|
|
224
|
+
const { coreness } = kCoreDecomposition(graph);
|
|
227
225
|
const kCore = new Set<string>();
|
|
228
226
|
|
|
229
227
|
for (const [node, core] of coreness) {
|
|
@@ -238,15 +236,11 @@ export function getKCore(
|
|
|
238
236
|
/**
|
|
239
237
|
* Get the induced subgraph for k-core
|
|
240
238
|
* Returns the actual subgraph containing only k-core nodes
|
|
241
|
-
*
|
|
242
239
|
* @param graph - Original graph
|
|
243
240
|
* @param k - Core number
|
|
244
241
|
* @returns K-core subgraph
|
|
245
242
|
*/
|
|
246
|
-
export function getKCoreSubgraph(
|
|
247
|
-
graph: Graph,
|
|
248
|
-
k: number,
|
|
249
|
-
): Map<string, Set<string>> {
|
|
243
|
+
export function getKCoreSubgraph(graph: Graph, k: number): Map<string, Set<string>> {
|
|
250
244
|
const kCoreNodes = getKCore(graph, k);
|
|
251
245
|
const adjacencySet = graphToAdjacencySet(graph);
|
|
252
246
|
const subgraph = new Map<string, Set<string>>();
|
|
@@ -270,13 +264,10 @@ export function getKCoreSubgraph(
|
|
|
270
264
|
/**
|
|
271
265
|
* Degeneracy ordering of the graph
|
|
272
266
|
* Orders nodes by their coreness values
|
|
273
|
-
*
|
|
274
267
|
* @param graph - Undirected graph
|
|
275
268
|
* @returns Array of nodes ordered by degeneracy
|
|
276
269
|
*/
|
|
277
|
-
export function degeneracyOrdering(
|
|
278
|
-
graph: Graph,
|
|
279
|
-
): string[] {
|
|
270
|
+
export function degeneracyOrdering(graph: Graph): string[] {
|
|
280
271
|
const adjacencySet = graphToAdjacencySet(graph);
|
|
281
272
|
const degree = new Map<string, number>();
|
|
282
273
|
const remaining = new Map<string, Set<string>>();
|
|
@@ -290,7 +281,7 @@ export function degeneracyOrdering(
|
|
|
290
281
|
|
|
291
282
|
// Build ordering
|
|
292
283
|
while (ordering.length < adjacencySet.size) {
|
|
293
|
-
|
|
284
|
+
// Find minimum degree node
|
|
294
285
|
let minDegree = Infinity;
|
|
295
286
|
let minNode: string | undefined;
|
|
296
287
|
|
|
@@ -327,15 +318,11 @@ export function degeneracyOrdering(
|
|
|
327
318
|
/**
|
|
328
319
|
* Find k-truss subgraph (triangular k-cores)
|
|
329
320
|
* Each edge must be part of at least k-2 triangles
|
|
330
|
-
*
|
|
331
321
|
* @param graph - Undirected graph
|
|
332
322
|
* @param k - Truss number (k >= 2)
|
|
333
323
|
* @returns Edges in k-truss
|
|
334
324
|
*/
|
|
335
|
-
export function kTruss(
|
|
336
|
-
graph: Graph,
|
|
337
|
-
k: number,
|
|
338
|
-
): Set<string> {
|
|
325
|
+
export function kTruss(graph: Graph, k: number): Set<string> {
|
|
339
326
|
if (k < 2) {
|
|
340
327
|
throw new Error("k must be at least 2 for k-truss");
|
|
341
328
|
}
|
|
@@ -349,8 +336,9 @@ export function kTruss(
|
|
|
349
336
|
// Initialize edges
|
|
350
337
|
for (const [u, neighbors] of adjacencySet) {
|
|
351
338
|
for (const v of neighbors) {
|
|
352
|
-
if (u < v) {
|
|
353
|
-
|
|
339
|
+
if (u < v) {
|
|
340
|
+
// Avoid duplicates
|
|
341
|
+
const edge = `${u},${v}`;
|
|
354
342
|
edges.add(edge);
|
|
355
343
|
edgeTriangles.set(edge, 0);
|
|
356
344
|
}
|
|
@@ -367,9 +355,9 @@ export function kTruss(
|
|
|
367
355
|
for (const w of uNeighbors) {
|
|
368
356
|
if (v < w && vNeighbors.has(w)) {
|
|
369
357
|
// Triangle found: u-v-w
|
|
370
|
-
const edge1 = `${
|
|
371
|
-
const edge2 = `${
|
|
372
|
-
const edge3 = `${
|
|
358
|
+
const edge1 = `${u},${v}`;
|
|
359
|
+
const edge2 = `${u},${w}`;
|
|
360
|
+
const edge3 = `${v},${w}`;
|
|
373
361
|
|
|
374
362
|
const edge1Count = edgeTriangles.get(edge1);
|
|
375
363
|
const edge2Count = edgeTriangles.get(edge2);
|
|
@@ -428,8 +416,8 @@ export function kTruss(
|
|
|
428
416
|
if (uNeighbors && vNeighbors) {
|
|
429
417
|
for (const w of uNeighbors) {
|
|
430
418
|
if (vNeighbors.has(w)) {
|
|
431
|
-
const edge1 = u < w ? `${
|
|
432
|
-
const edge2 = v < w ? `${
|
|
419
|
+
const edge1 = u < w ? `${u},${w}` : `${w},${u}`;
|
|
420
|
+
const edge2 = v < w ? `${v},${w}` : `${w},${v}`;
|
|
433
421
|
|
|
434
422
|
if (kTrussEdges.has(edge1)) {
|
|
435
423
|
const edge1Count = edgeTriangles.get(edge1);
|
|
@@ -455,10 +443,10 @@ export function kTruss(
|
|
|
455
443
|
|
|
456
444
|
/**
|
|
457
445
|
* Convert directed graph to undirected for k-core analysis
|
|
446
|
+
* @param directedGraph - Directed graph represented as adjacency map with edge weights
|
|
447
|
+
* @returns Undirected graph represented as adjacency map with neighbor sets
|
|
458
448
|
*/
|
|
459
|
-
export function toUndirected<T>(
|
|
460
|
-
directedGraph: Map<T, Map<T, number>>,
|
|
461
|
-
): Map<T, Set<T>> {
|
|
449
|
+
export function toUndirected<T>(directedGraph: Map<T, Map<T, number>>): Map<T, Set<T>> {
|
|
462
450
|
const undirected = new Map<T, Set<T>>();
|
|
463
451
|
|
|
464
452
|
// Initialize all nodes
|
|
@@ -491,18 +479,14 @@ export function toUndirected<T>(
|
|
|
491
479
|
/**
|
|
492
480
|
* K-Core decomposition algorithm
|
|
493
481
|
* Finds all k-cores in the graph and assigns coreness values to nodes
|
|
494
|
-
*
|
|
495
482
|
* @param graph - Undirected graph - accepts Graph class or Map<T, Set<T>>
|
|
496
483
|
* @returns K-core decomposition results
|
|
497
484
|
*
|
|
498
485
|
* Time Complexity: O(V + E)
|
|
499
486
|
* Space Complexity: O(V)
|
|
500
487
|
*/
|
|
501
|
-
export function kCoreDecomposition(
|
|
502
|
-
graph: Graph,
|
|
503
|
-
): KCoreResult<string> {
|
|
488
|
+
export function kCoreDecomposition(graph: Graph): KCoreResult<string> {
|
|
504
489
|
// Convert Graph to adjacency set representation
|
|
505
490
|
const adjacencySet = graphToAdjacencySet(graph);
|
|
506
491
|
return kCoreDecompositionImpl(adjacencySet);
|
|
507
492
|
}
|
|
508
|
-
|