@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
|
@@ -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 } from "../utils/math-utilities.js";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Spectral Clustering implementation
|
|
@@ -27,16 +28,25 @@ export interface SpectralClusteringResult {
|
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Perform spectral clustering on a graph
|
|
31
|
+
*
|
|
32
|
+
* Note: This implementation uses simplified power iteration for eigenvector
|
|
33
|
+
* computation with approximate eigenvalues. For production use cases requiring
|
|
34
|
+
* precise clustering, consider using a proper linear algebra library like ml-matrix.
|
|
35
|
+
*
|
|
36
|
+
* The approximate eigenvalues (0.1, 0.2 for second and third eigenvectors) work
|
|
37
|
+
* well for most graph structures but may produce suboptimal results for graphs
|
|
38
|
+
* with unusual spectral properties.
|
|
39
|
+
* @param graph - The input graph to cluster
|
|
40
|
+
* @param options - Spectral clustering options (k, laplacianType, etc.)
|
|
41
|
+
* @returns Spectral clustering result with communities and cluster assignments
|
|
30
42
|
*/
|
|
31
|
-
export function spectralClustering(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
k
|
|
37
|
-
|
|
38
|
-
maxIterations = 100,
|
|
39
|
-
} = options;
|
|
43
|
+
export function spectralClustering(graph: Graph, options: SpectralClusteringOptions): SpectralClusteringResult {
|
|
44
|
+
const { k, laplacianType = "normalized", maxIterations = 100, tolerance = 1e-4 } = options;
|
|
45
|
+
|
|
46
|
+
// Input validation
|
|
47
|
+
if (k < 1 || !Number.isInteger(k)) {
|
|
48
|
+
throw new Error("k must be a positive integer");
|
|
49
|
+
}
|
|
40
50
|
|
|
41
51
|
const nodes = Array.from(graph.nodes());
|
|
42
52
|
const nodeIds = nodes.map((node) => node.id);
|
|
@@ -47,7 +57,7 @@ export function spectralClustering(
|
|
|
47
57
|
const communities: NodeId[][] = nodeIds.map((id) => [id]);
|
|
48
58
|
const clusterAssignments = new Map<NodeId, number>();
|
|
49
59
|
nodeIds.forEach((id, index) => clusterAssignments.set(id, index));
|
|
50
|
-
return {communities, clusterAssignments};
|
|
60
|
+
return { communities, clusterAssignments };
|
|
51
61
|
}
|
|
52
62
|
|
|
53
63
|
// Build adjacency matrix
|
|
@@ -79,10 +89,10 @@ export function spectralClustering(
|
|
|
79
89
|
normalizeRows(dataPoints);
|
|
80
90
|
}
|
|
81
91
|
|
|
82
|
-
const kmeans = kMeansClustering(dataPoints, k, maxIterations);
|
|
92
|
+
const kmeans = kMeansClustering(dataPoints, k, maxIterations, tolerance);
|
|
83
93
|
|
|
84
94
|
// Build communities
|
|
85
|
-
const communities: NodeId[][] = Array.from({length: k}, () => []);
|
|
95
|
+
const communities: NodeId[][] = Array.from({ length: k }, () => []);
|
|
86
96
|
const clusterAssignments = new Map<NodeId, number>();
|
|
87
97
|
|
|
88
98
|
for (let i = 0; i < nodeIds.length; i++) {
|
|
@@ -124,10 +134,13 @@ export function spectralClustering(
|
|
|
124
134
|
|
|
125
135
|
/**
|
|
126
136
|
* Build adjacency matrix from graph
|
|
137
|
+
* @param graph - The input graph
|
|
138
|
+
* @param nodeIds - Array of node IDs defining matrix index order
|
|
139
|
+
* @returns Square adjacency matrix with edge weights
|
|
127
140
|
*/
|
|
128
141
|
function buildAdjacencyMatrix(graph: Graph, nodeIds: NodeId[]): number[][] {
|
|
129
142
|
const n = nodeIds.length;
|
|
130
|
-
const matrix: number[][] = Array.from({length: n}, () => Array(n).fill(0) as number[]);
|
|
143
|
+
const matrix: number[][] = Array.from({ length: n }, () => Array(n).fill(0) as number[]);
|
|
131
144
|
const nodeToIndex = new Map<NodeId, number>();
|
|
132
145
|
|
|
133
146
|
nodeIds.forEach((id, index) => nodeToIndex.set(id, index));
|
|
@@ -165,10 +178,13 @@ function buildAdjacencyMatrix(graph: Graph, nodeIds: NodeId[]): number[][] {
|
|
|
165
178
|
|
|
166
179
|
/**
|
|
167
180
|
* Build Laplacian matrix from adjacency matrix
|
|
181
|
+
* @param adjacency - Square adjacency matrix
|
|
182
|
+
* @param type - Type of Laplacian (unnormalized, normalized, or randomWalk)
|
|
183
|
+
* @returns The computed Laplacian matrix
|
|
168
184
|
*/
|
|
169
185
|
function buildLaplacianMatrix(adjacency: number[][], type: string): number[][] {
|
|
170
186
|
const n = adjacency.length;
|
|
171
|
-
const laplacian: number[][] = Array.from({length: n}, () => Array(n).fill(0) as number[]);
|
|
187
|
+
const laplacian: number[][] = Array.from({ length: n }, () => Array(n).fill(0) as number[]);
|
|
172
188
|
|
|
173
189
|
// Calculate degree matrix
|
|
174
190
|
const degrees = Array(n).fill(0) as number[];
|
|
@@ -224,7 +240,14 @@ function buildLaplacianMatrix(adjacency: number[][], type: string): number[][] {
|
|
|
224
240
|
const adjacencyVal = adjacencyRow[j];
|
|
225
241
|
const di = degrees[i];
|
|
226
242
|
const dj = degrees[j];
|
|
227
|
-
if (
|
|
243
|
+
if (
|
|
244
|
+
adjacencyVal !== undefined &&
|
|
245
|
+
adjacencyVal > 0 &&
|
|
246
|
+
di !== undefined &&
|
|
247
|
+
dj !== undefined &&
|
|
248
|
+
di > 0 &&
|
|
249
|
+
dj > 0
|
|
250
|
+
) {
|
|
228
251
|
const laplacianRow = laplacian[i];
|
|
229
252
|
if (laplacianRow) {
|
|
230
253
|
laplacianRow[j] = -adjacencyVal / Math.sqrt(di * dj);
|
|
@@ -268,15 +291,21 @@ function buildLaplacianMatrix(adjacency: number[][], type: string): number[][] {
|
|
|
268
291
|
/**
|
|
269
292
|
* Find k smallest eigenvectors using simplified eigendecomposition
|
|
270
293
|
* This is a simplified implementation - in practice, you'd use LAPACK or similar
|
|
294
|
+
* @param matrix - The Laplacian matrix for eigendecomposition
|
|
295
|
+
* @param k - Number of smallest eigenvectors to find
|
|
296
|
+
* @returns Object containing eigenvalues and corresponding eigenvectors
|
|
271
297
|
*/
|
|
272
|
-
function findSmallestEigenvectors(
|
|
298
|
+
function findSmallestEigenvectors(
|
|
299
|
+
matrix: number[][],
|
|
300
|
+
k: number,
|
|
301
|
+
): {
|
|
273
302
|
eigenvalues: number[];
|
|
274
303
|
eigenvectors: number[][];
|
|
275
304
|
} {
|
|
276
305
|
const n = matrix.length;
|
|
277
306
|
|
|
278
307
|
if (n === 0 || k === 0) {
|
|
279
|
-
return {eigenvalues: [], eigenvectors: []};
|
|
308
|
+
return { eigenvalues: [], eigenvectors: [] };
|
|
280
309
|
}
|
|
281
310
|
|
|
282
311
|
// For very small matrices, use the full power iteration approach
|
|
@@ -299,10 +328,12 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
|
|
|
299
328
|
|
|
300
329
|
for (let eigIdx = 0; eigIdx < k; eigIdx++) {
|
|
301
330
|
// Initialize random vector
|
|
302
|
-
let vector = Array(n)
|
|
331
|
+
let vector = Array(n)
|
|
332
|
+
.fill(0)
|
|
333
|
+
.map(() => Math.random() - 0.5);
|
|
303
334
|
|
|
304
335
|
// Normalize initial vector
|
|
305
|
-
const initNorm = Math.sqrt(vector.reduce((sum, val) => sum +
|
|
336
|
+
const initNorm = Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0));
|
|
306
337
|
if (initNorm > 0) {
|
|
307
338
|
vector = vector.map((val) => val / initNorm);
|
|
308
339
|
}
|
|
@@ -314,8 +345,8 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
|
|
|
314
345
|
continue;
|
|
315
346
|
}
|
|
316
347
|
|
|
317
|
-
const dot = vector.reduce((sum, val, idx) => sum +
|
|
318
|
-
vector = vector.map((val, idx) => val -
|
|
348
|
+
const dot = vector.reduce((sum, val, idx) => sum + val * (ejVector[idx] ?? 0), 0);
|
|
349
|
+
vector = vector.map((val, idx) => val - dot * (ejVector[idx] ?? 0));
|
|
319
350
|
}
|
|
320
351
|
|
|
321
352
|
// Power iteration
|
|
@@ -330,7 +361,7 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
|
|
|
330
361
|
if (matrixVal !== undefined && vecVal !== undefined) {
|
|
331
362
|
const nvVal = newVector[i];
|
|
332
363
|
if (nvVal !== undefined) {
|
|
333
|
-
newVector[i] = nvVal +
|
|
364
|
+
newVector[i] = nvVal + matrixVal * vecVal;
|
|
334
365
|
}
|
|
335
366
|
}
|
|
336
367
|
}
|
|
@@ -343,20 +374,20 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
|
|
|
343
374
|
continue;
|
|
344
375
|
}
|
|
345
376
|
|
|
346
|
-
const dot = newVector.reduce((sum, val, idx) => sum +
|
|
377
|
+
const dot = newVector.reduce((sum, val, idx) => sum + val * (ejVector[idx] ?? 0), 0);
|
|
347
378
|
for (let i = 0; i < n; i++) {
|
|
348
379
|
const ejVal = ejVector[i];
|
|
349
380
|
if (ejVal !== undefined) {
|
|
350
381
|
const nvVal = newVector[i];
|
|
351
382
|
if (nvVal !== undefined) {
|
|
352
|
-
newVector[i] = nvVal -
|
|
383
|
+
newVector[i] = nvVal - dot * ejVal;
|
|
353
384
|
}
|
|
354
385
|
}
|
|
355
386
|
}
|
|
356
387
|
}
|
|
357
388
|
|
|
358
389
|
// Normalize
|
|
359
|
-
const norm = Math.sqrt(newVector.reduce((sum, val) => sum +
|
|
390
|
+
const norm = Math.sqrt(newVector.reduce((sum, val) => sum + val * val, 0));
|
|
360
391
|
if (norm > 1e-10) {
|
|
361
392
|
vector = newVector.map((val) => val / norm);
|
|
362
393
|
} else {
|
|
@@ -375,29 +406,30 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
|
|
|
375
406
|
if (matrixVal !== undefined && vecVal !== undefined) {
|
|
376
407
|
const avVal = Av[i];
|
|
377
408
|
if (avVal !== undefined) {
|
|
378
|
-
Av[i] = avVal +
|
|
409
|
+
Av[i] = avVal + matrixVal * vecVal;
|
|
379
410
|
}
|
|
380
411
|
}
|
|
381
412
|
}
|
|
382
413
|
}
|
|
383
414
|
eigenvalue = vector.reduce((sum, val, idx) => {
|
|
384
415
|
const avVal = Av[idx];
|
|
385
|
-
return sum +
|
|
416
|
+
return sum + val * (avVal ?? 0);
|
|
386
417
|
}, 0);
|
|
387
418
|
|
|
388
419
|
eigenvectors.push(vector);
|
|
389
420
|
eigenvalues.push(eigenvalue);
|
|
390
421
|
}
|
|
391
422
|
|
|
392
|
-
return {eigenvalues, eigenvectors};
|
|
423
|
+
return { eigenvalues, eigenvectors };
|
|
393
424
|
}
|
|
394
425
|
|
|
395
426
|
/**
|
|
396
427
|
* Normalize matrix rows to unit length
|
|
428
|
+
* @param matrix - Matrix to normalize in place
|
|
397
429
|
*/
|
|
398
430
|
function normalizeRows(matrix: number[][]): void {
|
|
399
431
|
for (const row of matrix) {
|
|
400
|
-
const norm = Math.sqrt(row.reduce((sum, val) => sum +
|
|
432
|
+
const norm = Math.sqrt(row.reduce((sum, val) => sum + val * val, 0));
|
|
401
433
|
if (norm > 0) {
|
|
402
434
|
for (let j = 0; j < row.length; j++) {
|
|
403
435
|
const val = row[j];
|
|
@@ -411,24 +443,30 @@ function normalizeRows(matrix: number[][]): void {
|
|
|
411
443
|
|
|
412
444
|
/**
|
|
413
445
|
* K-means clustering algorithm
|
|
446
|
+
* @param data - Array of data points, each point is an array of coordinates
|
|
447
|
+
* @param k - Number of clusters to form
|
|
448
|
+
* @param maxIterations - Maximum number of iterations
|
|
449
|
+
* @param tolerance - Convergence tolerance for centroid movement
|
|
450
|
+
* @returns Object containing cluster assignments and final centroids
|
|
414
451
|
*/
|
|
415
452
|
function kMeansClustering(
|
|
416
453
|
data: number[][],
|
|
417
454
|
k: number,
|
|
418
455
|
maxIterations: number,
|
|
419
|
-
|
|
456
|
+
tolerance = 1e-4,
|
|
457
|
+
): { assignments: number[]; centroids: number[][] } {
|
|
420
458
|
const n = data.length;
|
|
421
459
|
const d = data[0]?.length ?? 0;
|
|
422
460
|
|
|
423
461
|
// Handle edge cases
|
|
424
462
|
if (n === 0 || k === 0) {
|
|
425
|
-
return {assignments: [], centroids: []};
|
|
463
|
+
return { assignments: [], centroids: [] };
|
|
426
464
|
}
|
|
427
465
|
|
|
428
466
|
if (k >= n) {
|
|
429
467
|
// Each point is its own cluster
|
|
430
468
|
return {
|
|
431
|
-
assignments: Array.from({length: n}, (_, i) => i),
|
|
469
|
+
assignments: Array.from({ length: n }, (_, i) => i),
|
|
432
470
|
centroids: data.slice(0, n),
|
|
433
471
|
};
|
|
434
472
|
}
|
|
@@ -441,7 +479,7 @@ function kMeansClustering(
|
|
|
441
479
|
const idx = Math.floor(Math.random() * n);
|
|
442
480
|
if (!selectedIndices.has(idx) && data[idx]) {
|
|
443
481
|
selectedIndices.add(idx);
|
|
444
|
-
centroids.push([...
|
|
482
|
+
centroids.push([...data[idx]]);
|
|
445
483
|
}
|
|
446
484
|
}
|
|
447
485
|
|
|
@@ -480,24 +518,27 @@ function kMeansClustering(
|
|
|
480
518
|
assignments[i] = bestCluster;
|
|
481
519
|
}
|
|
482
520
|
|
|
483
|
-
// Check for convergence
|
|
484
|
-
let
|
|
521
|
+
// Check for convergence based on assignment changes
|
|
522
|
+
let assignmentsChanged = false;
|
|
485
523
|
for (let i = 0; i < n; i++) {
|
|
486
524
|
if (assignments[i] !== oldAssignments[i]) {
|
|
487
|
-
|
|
525
|
+
assignmentsChanged = true;
|
|
488
526
|
break;
|
|
489
527
|
}
|
|
490
528
|
}
|
|
491
529
|
|
|
492
|
-
if (!
|
|
530
|
+
if (!assignmentsChanged) {
|
|
493
531
|
break;
|
|
494
532
|
}
|
|
495
533
|
|
|
496
|
-
oldAssignments = [...
|
|
534
|
+
oldAssignments = [...assignments];
|
|
535
|
+
|
|
536
|
+
// Store old centroids for tolerance-based convergence check
|
|
537
|
+
const oldCentroids = centroids.map((c) => [...c]);
|
|
497
538
|
|
|
498
539
|
// Update centroids
|
|
499
540
|
const counts = Array(k).fill(0) as number[];
|
|
500
|
-
const sums: number[][] = Array.from({length: k}, () => Array(d).fill(0) as number[]);
|
|
541
|
+
const sums: number[][] = Array.from({ length: k }, () => Array(d).fill(0) as number[]);
|
|
501
542
|
|
|
502
543
|
for (let i = 0; i < n; i++) {
|
|
503
544
|
const cluster = assignments[i] ?? 0;
|
|
@@ -534,36 +575,49 @@ function kMeansClustering(
|
|
|
534
575
|
for (let j = 0; j < d; j++) {
|
|
535
576
|
const sumVal = sumsRow[j];
|
|
536
577
|
if (sumVal !== undefined) {
|
|
537
|
-
const
|
|
538
|
-
if (
|
|
539
|
-
centroidsRow[j] = sumVal /
|
|
578
|
+
const countValInner = counts[i];
|
|
579
|
+
if (countValInner !== undefined) {
|
|
580
|
+
centroidsRow[j] = sumVal / countValInner;
|
|
540
581
|
}
|
|
541
582
|
}
|
|
542
583
|
}
|
|
543
584
|
}
|
|
544
585
|
}
|
|
545
586
|
}
|
|
546
|
-
}
|
|
547
587
|
|
|
548
|
-
|
|
549
|
-
|
|
588
|
+
// Check for tolerance-based convergence (centroid movement)
|
|
589
|
+
let maxCentroidShift = 0;
|
|
590
|
+
for (let i = 0; i < k; i++) {
|
|
591
|
+
const oldCentroid = oldCentroids[i];
|
|
592
|
+
const newCentroid = centroids[i];
|
|
593
|
+
if (oldCentroid && newCentroid) {
|
|
594
|
+
const shift = euclideanDistance(oldCentroid, newCentroid);
|
|
595
|
+
if (shift > maxCentroidShift) {
|
|
596
|
+
maxCentroidShift = shift;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
550
600
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
function euclideanDistance(a: number[], b: number[]): number {
|
|
555
|
-
let sum = 0;
|
|
556
|
-
for (let i = 0; i < a.length; i++) {
|
|
557
|
-
const diff = (a[i] ?? 0) - (b[i] ?? 0);
|
|
558
|
-
sum += diff * diff;
|
|
601
|
+
if (maxCentroidShift < tolerance) {
|
|
602
|
+
break;
|
|
603
|
+
}
|
|
559
604
|
}
|
|
560
|
-
|
|
605
|
+
|
|
606
|
+
return { assignments, centroids };
|
|
561
607
|
}
|
|
562
608
|
|
|
563
609
|
/**
|
|
564
610
|
* Compute smallest eigenvectors for small k (optimized for k=2, k=3)
|
|
611
|
+
* @param matrix - The Laplacian matrix
|
|
612
|
+
* @param k - Number of eigenvectors to compute (1-3)
|
|
613
|
+
* @param n - Size of the matrix
|
|
614
|
+
* @returns Object containing eigenvalues and eigenvectors
|
|
565
615
|
*/
|
|
566
|
-
function computeSmallestEigenvectorsSimple(
|
|
616
|
+
function computeSmallestEigenvectorsSimple(
|
|
617
|
+
matrix: number[][],
|
|
618
|
+
k: number,
|
|
619
|
+
n: number,
|
|
620
|
+
): {
|
|
567
621
|
eigenvalues: number[];
|
|
568
622
|
eigenvectors: number[][];
|
|
569
623
|
} {
|
|
@@ -579,11 +633,13 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
|
|
|
579
633
|
if (k >= 2) {
|
|
580
634
|
// Use power iteration on I - L/lambda_max to find second smallest
|
|
581
635
|
const maxEig = 2; // For normalized Laplacian, max eigenvalue <= 2
|
|
582
|
-
let vector = Array(n)
|
|
636
|
+
let vector = Array(n)
|
|
637
|
+
.fill(0)
|
|
638
|
+
.map(() => Math.random() - 0.5);
|
|
583
639
|
|
|
584
640
|
// Make orthogonal to first eigenvector
|
|
585
|
-
const dot1 = vector.reduce((sum, val) => sum +
|
|
586
|
-
vector = vector.map((val) => val -
|
|
641
|
+
const dot1 = vector.reduce((sum, val) => sum + val / Math.sqrt(n), 0);
|
|
642
|
+
vector = vector.map((val) => val - dot1 / Math.sqrt(n));
|
|
587
643
|
|
|
588
644
|
// Power iteration on shifted matrix
|
|
589
645
|
for (let iter = 0; iter < 100; iter++) {
|
|
@@ -600,18 +656,18 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
|
|
|
600
656
|
for (let j = 0; j < n; j++) {
|
|
601
657
|
const matrixVal = matrix[i]?.[j] ?? 0;
|
|
602
658
|
const vecVal = vector[j] ?? 0;
|
|
603
|
-
newVector[i] = (newVector[i] ?? 0) - (
|
|
659
|
+
newVector[i] = (newVector[i] ?? 0) - (matrixVal * vecVal) / maxEig;
|
|
604
660
|
}
|
|
605
661
|
}
|
|
606
662
|
|
|
607
663
|
// Orthogonalize against first eigenvector
|
|
608
|
-
const dot = newVector.reduce((sum, val) => sum +
|
|
664
|
+
const dot = newVector.reduce((sum, val) => sum + val / Math.sqrt(n), 0);
|
|
609
665
|
for (let i = 0; i < n; i++) {
|
|
610
|
-
newVector[i] = (newVector[i] ?? 0) -
|
|
666
|
+
newVector[i] = (newVector[i] ?? 0) - dot / Math.sqrt(n);
|
|
611
667
|
}
|
|
612
668
|
|
|
613
669
|
// Normalize
|
|
614
|
-
const norm = Math.sqrt(newVector.reduce((sum, val) => sum +
|
|
670
|
+
const norm = Math.sqrt(newVector.reduce((sum, val) => sum + val * val, 0));
|
|
615
671
|
if (norm > 1e-10) {
|
|
616
672
|
vector = newVector.map((val) => val / norm);
|
|
617
673
|
}
|
|
@@ -623,12 +679,14 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
|
|
|
623
679
|
|
|
624
680
|
// For k = 3, add another eigenvector
|
|
625
681
|
if (k >= 3) {
|
|
626
|
-
let vector = Array(n)
|
|
682
|
+
let vector = Array(n)
|
|
683
|
+
.fill(0)
|
|
684
|
+
.map(() => Math.random() - 0.5);
|
|
627
685
|
|
|
628
686
|
// Orthogonalize against previous eigenvectors
|
|
629
687
|
for (const prev of eigenvectors) {
|
|
630
|
-
const dot = vector.reduce((sum, val, idx) => sum +
|
|
631
|
-
vector = vector.map((val, idx) => val -
|
|
688
|
+
const dot = vector.reduce((sum, val, idx) => sum + val * (prev[idx] ?? 0), 0);
|
|
689
|
+
vector = vector.map((val, idx) => val - dot * (prev[idx] ?? 0));
|
|
632
690
|
}
|
|
633
691
|
|
|
634
692
|
// Similar power iteration
|
|
@@ -645,20 +703,20 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
|
|
|
645
703
|
for (let j = 0; j < n; j++) {
|
|
646
704
|
const matrixVal = matrix[i]?.[j] ?? 0;
|
|
647
705
|
const vecVal = vector[j] ?? 0;
|
|
648
|
-
newVector[i] = (newVector[i] ?? 0) - (
|
|
706
|
+
newVector[i] = (newVector[i] ?? 0) - (matrixVal * vecVal) / 2;
|
|
649
707
|
}
|
|
650
708
|
}
|
|
651
709
|
|
|
652
710
|
// Orthogonalize
|
|
653
711
|
for (const prev of eigenvectors) {
|
|
654
|
-
const dot = newVector.reduce((sum, val, idx) => sum +
|
|
712
|
+
const dot = newVector.reduce((sum, val, idx) => sum + val * (prev[idx] ?? 0), 0);
|
|
655
713
|
for (let i = 0; i < n; i++) {
|
|
656
|
-
newVector[i] = (newVector[i] ?? 0) -
|
|
714
|
+
newVector[i] = (newVector[i] ?? 0) - dot * (prev[i] ?? 0);
|
|
657
715
|
}
|
|
658
716
|
}
|
|
659
717
|
|
|
660
718
|
// Normalize
|
|
661
|
-
const norm = Math.sqrt(newVector.reduce((sum, val) => sum +
|
|
719
|
+
const norm = Math.sqrt(newVector.reduce((sum, val) => sum + val * val, 0));
|
|
662
720
|
if (norm > 1e-10) {
|
|
663
721
|
vector = newVector.map((val) => val / norm);
|
|
664
722
|
}
|
|
@@ -668,5 +726,5 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
|
|
|
668
726
|
eigenvalues.push(0.2); // Approximate
|
|
669
727
|
}
|
|
670
728
|
|
|
671
|
-
return {eigenvalues: eigenvalues.slice(0, k), eigenvectors: eigenvectors.slice(0, k)};
|
|
729
|
+
return { eigenvalues: eigenvalues.slice(0, k), eigenvectors: eigenvectors.slice(0, k) };
|
|
672
730
|
}
|