@graphty/algorithms 1.3.1 → 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 +358 -413
- package/dist/algorithms.js +564 -261
- 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 +11 -4
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js +18 -16
- 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 +22 -9
- 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 +28 -7
- 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/package.json +49 -45
- 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 +28 -28
- 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 +30 -24
- package/src/research/index.ts +4 -4
- package/src/research/sync.ts +39 -23
- 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
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {Graph} from "../core/graph.js";
|
|
2
|
-
import type {NodeId} from "../types/index.js";
|
|
3
|
-
import {euclideanDistance} from "../utils/math-utilities.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";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Spectral Clustering implementation
|
|
@@ -29,24 +29,19 @@ export interface SpectralClusteringResult {
|
|
|
29
29
|
/**
|
|
30
30
|
* Perform spectral clustering on a graph
|
|
31
31
|
*
|
|
32
|
-
*
|
|
32
|
+
* Note: This implementation uses simplified power iteration for eigenvector
|
|
33
33
|
* computation with approximate eigenvalues. For production use cases requiring
|
|
34
34
|
* precise clustering, consider using a proper linear algebra library like ml-matrix.
|
|
35
35
|
*
|
|
36
36
|
* The approximate eigenvalues (0.1, 0.2 for second and third eigenvectors) work
|
|
37
37
|
* well for most graph structures but may produce suboptimal results for graphs
|
|
38
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
|
|
39
42
|
*/
|
|
40
|
-
export function spectralClustering(
|
|
41
|
-
|
|
42
|
-
options: SpectralClusteringOptions,
|
|
43
|
-
): SpectralClusteringResult {
|
|
44
|
-
const {
|
|
45
|
-
k,
|
|
46
|
-
laplacianType = "normalized",
|
|
47
|
-
maxIterations = 100,
|
|
48
|
-
tolerance = 1e-4,
|
|
49
|
-
} = options;
|
|
43
|
+
export function spectralClustering(graph: Graph, options: SpectralClusteringOptions): SpectralClusteringResult {
|
|
44
|
+
const { k, laplacianType = "normalized", maxIterations = 100, tolerance = 1e-4 } = options;
|
|
50
45
|
|
|
51
46
|
// Input validation
|
|
52
47
|
if (k < 1 || !Number.isInteger(k)) {
|
|
@@ -62,7 +57,7 @@ export function spectralClustering(
|
|
|
62
57
|
const communities: NodeId[][] = nodeIds.map((id) => [id]);
|
|
63
58
|
const clusterAssignments = new Map<NodeId, number>();
|
|
64
59
|
nodeIds.forEach((id, index) => clusterAssignments.set(id, index));
|
|
65
|
-
return {communities, clusterAssignments};
|
|
60
|
+
return { communities, clusterAssignments };
|
|
66
61
|
}
|
|
67
62
|
|
|
68
63
|
// Build adjacency matrix
|
|
@@ -97,7 +92,7 @@ export function spectralClustering(
|
|
|
97
92
|
const kmeans = kMeansClustering(dataPoints, k, maxIterations, tolerance);
|
|
98
93
|
|
|
99
94
|
// Build communities
|
|
100
|
-
const communities: NodeId[][] = Array.from({length: k}, () => []);
|
|
95
|
+
const communities: NodeId[][] = Array.from({ length: k }, () => []);
|
|
101
96
|
const clusterAssignments = new Map<NodeId, number>();
|
|
102
97
|
|
|
103
98
|
for (let i = 0; i < nodeIds.length; i++) {
|
|
@@ -139,10 +134,13 @@ export function spectralClustering(
|
|
|
139
134
|
|
|
140
135
|
/**
|
|
141
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
|
|
142
140
|
*/
|
|
143
141
|
function buildAdjacencyMatrix(graph: Graph, nodeIds: NodeId[]): number[][] {
|
|
144
142
|
const n = nodeIds.length;
|
|
145
|
-
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[]);
|
|
146
144
|
const nodeToIndex = new Map<NodeId, number>();
|
|
147
145
|
|
|
148
146
|
nodeIds.forEach((id, index) => nodeToIndex.set(id, index));
|
|
@@ -180,10 +178,13 @@ function buildAdjacencyMatrix(graph: Graph, nodeIds: NodeId[]): number[][] {
|
|
|
180
178
|
|
|
181
179
|
/**
|
|
182
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
|
|
183
184
|
*/
|
|
184
185
|
function buildLaplacianMatrix(adjacency: number[][], type: string): number[][] {
|
|
185
186
|
const n = adjacency.length;
|
|
186
|
-
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[]);
|
|
187
188
|
|
|
188
189
|
// Calculate degree matrix
|
|
189
190
|
const degrees = Array(n).fill(0) as number[];
|
|
@@ -239,7 +240,14 @@ function buildLaplacianMatrix(adjacency: number[][], type: string): number[][] {
|
|
|
239
240
|
const adjacencyVal = adjacencyRow[j];
|
|
240
241
|
const di = degrees[i];
|
|
241
242
|
const dj = degrees[j];
|
|
242
|
-
if (
|
|
243
|
+
if (
|
|
244
|
+
adjacencyVal !== undefined &&
|
|
245
|
+
adjacencyVal > 0 &&
|
|
246
|
+
di !== undefined &&
|
|
247
|
+
dj !== undefined &&
|
|
248
|
+
di > 0 &&
|
|
249
|
+
dj > 0
|
|
250
|
+
) {
|
|
243
251
|
const laplacianRow = laplacian[i];
|
|
244
252
|
if (laplacianRow) {
|
|
245
253
|
laplacianRow[j] = -adjacencyVal / Math.sqrt(di * dj);
|
|
@@ -283,15 +291,21 @@ function buildLaplacianMatrix(adjacency: number[][], type: string): number[][] {
|
|
|
283
291
|
/**
|
|
284
292
|
* Find k smallest eigenvectors using simplified eigendecomposition
|
|
285
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
|
|
286
297
|
*/
|
|
287
|
-
function findSmallestEigenvectors(
|
|
298
|
+
function findSmallestEigenvectors(
|
|
299
|
+
matrix: number[][],
|
|
300
|
+
k: number,
|
|
301
|
+
): {
|
|
288
302
|
eigenvalues: number[];
|
|
289
303
|
eigenvectors: number[][];
|
|
290
304
|
} {
|
|
291
305
|
const n = matrix.length;
|
|
292
306
|
|
|
293
307
|
if (n === 0 || k === 0) {
|
|
294
|
-
return {eigenvalues: [], eigenvectors: []};
|
|
308
|
+
return { eigenvalues: [], eigenvectors: [] };
|
|
295
309
|
}
|
|
296
310
|
|
|
297
311
|
// For very small matrices, use the full power iteration approach
|
|
@@ -314,10 +328,12 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
|
|
|
314
328
|
|
|
315
329
|
for (let eigIdx = 0; eigIdx < k; eigIdx++) {
|
|
316
330
|
// Initialize random vector
|
|
317
|
-
let vector = Array(n)
|
|
331
|
+
let vector = Array(n)
|
|
332
|
+
.fill(0)
|
|
333
|
+
.map(() => Math.random() - 0.5);
|
|
318
334
|
|
|
319
335
|
// Normalize initial vector
|
|
320
|
-
const initNorm = Math.sqrt(vector.reduce((sum, val) => sum +
|
|
336
|
+
const initNorm = Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0));
|
|
321
337
|
if (initNorm > 0) {
|
|
322
338
|
vector = vector.map((val) => val / initNorm);
|
|
323
339
|
}
|
|
@@ -329,8 +345,8 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
|
|
|
329
345
|
continue;
|
|
330
346
|
}
|
|
331
347
|
|
|
332
|
-
const dot = vector.reduce((sum, val, idx) => sum +
|
|
333
|
-
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));
|
|
334
350
|
}
|
|
335
351
|
|
|
336
352
|
// Power iteration
|
|
@@ -345,7 +361,7 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
|
|
|
345
361
|
if (matrixVal !== undefined && vecVal !== undefined) {
|
|
346
362
|
const nvVal = newVector[i];
|
|
347
363
|
if (nvVal !== undefined) {
|
|
348
|
-
newVector[i] = nvVal +
|
|
364
|
+
newVector[i] = nvVal + matrixVal * vecVal;
|
|
349
365
|
}
|
|
350
366
|
}
|
|
351
367
|
}
|
|
@@ -358,20 +374,20 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
|
|
|
358
374
|
continue;
|
|
359
375
|
}
|
|
360
376
|
|
|
361
|
-
const dot = newVector.reduce((sum, val, idx) => sum +
|
|
377
|
+
const dot = newVector.reduce((sum, val, idx) => sum + val * (ejVector[idx] ?? 0), 0);
|
|
362
378
|
for (let i = 0; i < n; i++) {
|
|
363
379
|
const ejVal = ejVector[i];
|
|
364
380
|
if (ejVal !== undefined) {
|
|
365
381
|
const nvVal = newVector[i];
|
|
366
382
|
if (nvVal !== undefined) {
|
|
367
|
-
newVector[i] = nvVal -
|
|
383
|
+
newVector[i] = nvVal - dot * ejVal;
|
|
368
384
|
}
|
|
369
385
|
}
|
|
370
386
|
}
|
|
371
387
|
}
|
|
372
388
|
|
|
373
389
|
// Normalize
|
|
374
|
-
const norm = Math.sqrt(newVector.reduce((sum, val) => sum +
|
|
390
|
+
const norm = Math.sqrt(newVector.reduce((sum, val) => sum + val * val, 0));
|
|
375
391
|
if (norm > 1e-10) {
|
|
376
392
|
vector = newVector.map((val) => val / norm);
|
|
377
393
|
} else {
|
|
@@ -390,29 +406,30 @@ function findSmallestEigenvectors(matrix: number[][], k: number): {
|
|
|
390
406
|
if (matrixVal !== undefined && vecVal !== undefined) {
|
|
391
407
|
const avVal = Av[i];
|
|
392
408
|
if (avVal !== undefined) {
|
|
393
|
-
Av[i] = avVal +
|
|
409
|
+
Av[i] = avVal + matrixVal * vecVal;
|
|
394
410
|
}
|
|
395
411
|
}
|
|
396
412
|
}
|
|
397
413
|
}
|
|
398
414
|
eigenvalue = vector.reduce((sum, val, idx) => {
|
|
399
415
|
const avVal = Av[idx];
|
|
400
|
-
return sum +
|
|
416
|
+
return sum + val * (avVal ?? 0);
|
|
401
417
|
}, 0);
|
|
402
418
|
|
|
403
419
|
eigenvectors.push(vector);
|
|
404
420
|
eigenvalues.push(eigenvalue);
|
|
405
421
|
}
|
|
406
422
|
|
|
407
|
-
return {eigenvalues, eigenvectors};
|
|
423
|
+
return { eigenvalues, eigenvectors };
|
|
408
424
|
}
|
|
409
425
|
|
|
410
426
|
/**
|
|
411
427
|
* Normalize matrix rows to unit length
|
|
428
|
+
* @param matrix - Matrix to normalize in place
|
|
412
429
|
*/
|
|
413
430
|
function normalizeRows(matrix: number[][]): void {
|
|
414
431
|
for (const row of matrix) {
|
|
415
|
-
const norm = Math.sqrt(row.reduce((sum, val) => sum +
|
|
432
|
+
const norm = Math.sqrt(row.reduce((sum, val) => sum + val * val, 0));
|
|
416
433
|
if (norm > 0) {
|
|
417
434
|
for (let j = 0; j < row.length; j++) {
|
|
418
435
|
const val = row[j];
|
|
@@ -426,25 +443,30 @@ function normalizeRows(matrix: number[][]): void {
|
|
|
426
443
|
|
|
427
444
|
/**
|
|
428
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
|
|
429
451
|
*/
|
|
430
452
|
function kMeansClustering(
|
|
431
453
|
data: number[][],
|
|
432
454
|
k: number,
|
|
433
455
|
maxIterations: number,
|
|
434
456
|
tolerance = 1e-4,
|
|
435
|
-
): {assignments: number[]
|
|
457
|
+
): { assignments: number[]; centroids: number[][] } {
|
|
436
458
|
const n = data.length;
|
|
437
459
|
const d = data[0]?.length ?? 0;
|
|
438
460
|
|
|
439
461
|
// Handle edge cases
|
|
440
462
|
if (n === 0 || k === 0) {
|
|
441
|
-
return {assignments: [], centroids: []};
|
|
463
|
+
return { assignments: [], centroids: [] };
|
|
442
464
|
}
|
|
443
465
|
|
|
444
466
|
if (k >= n) {
|
|
445
467
|
// Each point is its own cluster
|
|
446
468
|
return {
|
|
447
|
-
assignments: Array.from({length: n}, (_, i) => i),
|
|
469
|
+
assignments: Array.from({ length: n }, (_, i) => i),
|
|
448
470
|
centroids: data.slice(0, n),
|
|
449
471
|
};
|
|
450
472
|
}
|
|
@@ -457,7 +479,7 @@ function kMeansClustering(
|
|
|
457
479
|
const idx = Math.floor(Math.random() * n);
|
|
458
480
|
if (!selectedIndices.has(idx) && data[idx]) {
|
|
459
481
|
selectedIndices.add(idx);
|
|
460
|
-
centroids.push([...
|
|
482
|
+
centroids.push([...data[idx]]);
|
|
461
483
|
}
|
|
462
484
|
}
|
|
463
485
|
|
|
@@ -509,14 +531,14 @@ function kMeansClustering(
|
|
|
509
531
|
break;
|
|
510
532
|
}
|
|
511
533
|
|
|
512
|
-
oldAssignments = [...
|
|
534
|
+
oldAssignments = [...assignments];
|
|
513
535
|
|
|
514
536
|
// Store old centroids for tolerance-based convergence check
|
|
515
|
-
const oldCentroids = centroids.map((c) => [...
|
|
537
|
+
const oldCentroids = centroids.map((c) => [...c]);
|
|
516
538
|
|
|
517
539
|
// Update centroids
|
|
518
540
|
const counts = Array(k).fill(0) as number[];
|
|
519
|
-
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[]);
|
|
520
542
|
|
|
521
543
|
for (let i = 0; i < n; i++) {
|
|
522
544
|
const cluster = assignments[i] ?? 0;
|
|
@@ -581,13 +603,21 @@ function kMeansClustering(
|
|
|
581
603
|
}
|
|
582
604
|
}
|
|
583
605
|
|
|
584
|
-
return {assignments, centroids};
|
|
606
|
+
return { assignments, centroids };
|
|
585
607
|
}
|
|
586
608
|
|
|
587
609
|
/**
|
|
588
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
|
|
589
615
|
*/
|
|
590
|
-
function computeSmallestEigenvectorsSimple(
|
|
616
|
+
function computeSmallestEigenvectorsSimple(
|
|
617
|
+
matrix: number[][],
|
|
618
|
+
k: number,
|
|
619
|
+
n: number,
|
|
620
|
+
): {
|
|
591
621
|
eigenvalues: number[];
|
|
592
622
|
eigenvectors: number[][];
|
|
593
623
|
} {
|
|
@@ -603,11 +633,13 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
|
|
|
603
633
|
if (k >= 2) {
|
|
604
634
|
// Use power iteration on I - L/lambda_max to find second smallest
|
|
605
635
|
const maxEig = 2; // For normalized Laplacian, max eigenvalue <= 2
|
|
606
|
-
let vector = Array(n)
|
|
636
|
+
let vector = Array(n)
|
|
637
|
+
.fill(0)
|
|
638
|
+
.map(() => Math.random() - 0.5);
|
|
607
639
|
|
|
608
640
|
// Make orthogonal to first eigenvector
|
|
609
|
-
const dot1 = vector.reduce((sum, val) => sum +
|
|
610
|
-
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));
|
|
611
643
|
|
|
612
644
|
// Power iteration on shifted matrix
|
|
613
645
|
for (let iter = 0; iter < 100; iter++) {
|
|
@@ -624,18 +656,18 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
|
|
|
624
656
|
for (let j = 0; j < n; j++) {
|
|
625
657
|
const matrixVal = matrix[i]?.[j] ?? 0;
|
|
626
658
|
const vecVal = vector[j] ?? 0;
|
|
627
|
-
newVector[i] = (newVector[i] ?? 0) - (
|
|
659
|
+
newVector[i] = (newVector[i] ?? 0) - (matrixVal * vecVal) / maxEig;
|
|
628
660
|
}
|
|
629
661
|
}
|
|
630
662
|
|
|
631
663
|
// Orthogonalize against first eigenvector
|
|
632
|
-
const dot = newVector.reduce((sum, val) => sum +
|
|
664
|
+
const dot = newVector.reduce((sum, val) => sum + val / Math.sqrt(n), 0);
|
|
633
665
|
for (let i = 0; i < n; i++) {
|
|
634
|
-
newVector[i] = (newVector[i] ?? 0) -
|
|
666
|
+
newVector[i] = (newVector[i] ?? 0) - dot / Math.sqrt(n);
|
|
635
667
|
}
|
|
636
668
|
|
|
637
669
|
// Normalize
|
|
638
|
-
const norm = Math.sqrt(newVector.reduce((sum, val) => sum +
|
|
670
|
+
const norm = Math.sqrt(newVector.reduce((sum, val) => sum + val * val, 0));
|
|
639
671
|
if (norm > 1e-10) {
|
|
640
672
|
vector = newVector.map((val) => val / norm);
|
|
641
673
|
}
|
|
@@ -647,12 +679,14 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
|
|
|
647
679
|
|
|
648
680
|
// For k = 3, add another eigenvector
|
|
649
681
|
if (k >= 3) {
|
|
650
|
-
let vector = Array(n)
|
|
682
|
+
let vector = Array(n)
|
|
683
|
+
.fill(0)
|
|
684
|
+
.map(() => Math.random() - 0.5);
|
|
651
685
|
|
|
652
686
|
// Orthogonalize against previous eigenvectors
|
|
653
687
|
for (const prev of eigenvectors) {
|
|
654
|
-
const dot = vector.reduce((sum, val, idx) => sum +
|
|
655
|
-
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));
|
|
656
690
|
}
|
|
657
691
|
|
|
658
692
|
// Similar power iteration
|
|
@@ -669,20 +703,20 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
|
|
|
669
703
|
for (let j = 0; j < n; j++) {
|
|
670
704
|
const matrixVal = matrix[i]?.[j] ?? 0;
|
|
671
705
|
const vecVal = vector[j] ?? 0;
|
|
672
|
-
newVector[i] = (newVector[i] ?? 0) - (
|
|
706
|
+
newVector[i] = (newVector[i] ?? 0) - (matrixVal * vecVal) / 2;
|
|
673
707
|
}
|
|
674
708
|
}
|
|
675
709
|
|
|
676
710
|
// Orthogonalize
|
|
677
711
|
for (const prev of eigenvectors) {
|
|
678
|
-
const dot = newVector.reduce((sum, val, idx) => sum +
|
|
712
|
+
const dot = newVector.reduce((sum, val, idx) => sum + val * (prev[idx] ?? 0), 0);
|
|
679
713
|
for (let i = 0; i < n; i++) {
|
|
680
|
-
newVector[i] = (newVector[i] ?? 0) -
|
|
714
|
+
newVector[i] = (newVector[i] ?? 0) - dot * (prev[i] ?? 0);
|
|
681
715
|
}
|
|
682
716
|
}
|
|
683
717
|
|
|
684
718
|
// Normalize
|
|
685
|
-
const norm = Math.sqrt(newVector.reduce((sum, val) => sum +
|
|
719
|
+
const norm = Math.sqrt(newVector.reduce((sum, val) => sum + val * val, 0));
|
|
686
720
|
if (norm > 1e-10) {
|
|
687
721
|
vector = newVector.map((val) => val / norm);
|
|
688
722
|
}
|
|
@@ -692,5 +726,5 @@ function computeSmallestEigenvectorsSimple(matrix: number[][], k: number, n: num
|
|
|
692
726
|
eigenvalues.push(0.2); // Approximate
|
|
693
727
|
}
|
|
694
728
|
|
|
695
|
-
return {eigenvalues: eigenvalues.slice(0, k), eigenvectors: eigenvectors.slice(0, k)};
|
|
729
|
+
return { eigenvalues: eigenvalues.slice(0, k), eigenvectors: eigenvectors.slice(0, k) };
|
|
696
730
|
}
|
package/src/core/graph.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {Edge, GraphConfig, Node, NodeId} from "../types/index.js";
|
|
1
|
+
import type { Edge, GraphConfig, Node, NodeId } from "../types/index.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Core Graph data structure for the Graphty Algorithms library
|
|
@@ -13,12 +13,16 @@ export class Graph {
|
|
|
13
13
|
private config: GraphConfig;
|
|
14
14
|
private edgeCount: number;
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Creates a new Graph instance.
|
|
18
|
+
* @param config - Configuration options for the graph
|
|
19
|
+
*/
|
|
16
20
|
constructor(config: Partial<GraphConfig> = {}) {
|
|
17
21
|
this.config = {
|
|
18
22
|
directed: false,
|
|
19
23
|
allowSelfLoops: true,
|
|
20
24
|
allowParallelEdges: false,
|
|
21
|
-
...
|
|
25
|
+
...config,
|
|
22
26
|
};
|
|
23
27
|
this.nodeMap = new Map();
|
|
24
28
|
this.adjacencyList = new Map();
|
|
@@ -28,10 +32,12 @@ export class Graph {
|
|
|
28
32
|
|
|
29
33
|
/**
|
|
30
34
|
* Add a node to the graph
|
|
35
|
+
* @param id - The unique identifier for the node
|
|
36
|
+
* @param data - Optional key-value data to attach to the node
|
|
31
37
|
*/
|
|
32
38
|
addNode(id: NodeId, data?: Record<string, unknown>): void {
|
|
33
39
|
if (!this.nodeMap.has(id)) {
|
|
34
|
-
this.nodeMap.set(id, {id, data});
|
|
40
|
+
this.nodeMap.set(id, { id, data });
|
|
35
41
|
this.adjacencyList.set(id, new Map());
|
|
36
42
|
|
|
37
43
|
if (this.config.directed) {
|
|
@@ -42,6 +48,8 @@ export class Graph {
|
|
|
42
48
|
|
|
43
49
|
/**
|
|
44
50
|
* Remove a node from the graph
|
|
51
|
+
* @param id - The unique identifier of the node to remove
|
|
52
|
+
* @returns True if the node was removed, false if it did not exist
|
|
45
53
|
*/
|
|
46
54
|
removeNode(id: NodeId): boolean {
|
|
47
55
|
if (!this.nodeMap.has(id)) {
|
|
@@ -85,6 +93,10 @@ export class Graph {
|
|
|
85
93
|
|
|
86
94
|
/**
|
|
87
95
|
* Add an edge to the graph
|
|
96
|
+
* @param source - The source node identifier
|
|
97
|
+
* @param target - The target node identifier
|
|
98
|
+
* @param weight - The weight of the edge (defaults to 1)
|
|
99
|
+
* @param data - Optional key-value data to attach to the edge
|
|
88
100
|
*/
|
|
89
101
|
addEdge(source: NodeId, target: NodeId, weight = 1, data?: Record<string, unknown>): void {
|
|
90
102
|
// Ensure both nodes exist
|
|
@@ -101,7 +113,7 @@ export class Graph {
|
|
|
101
113
|
throw new Error("Parallel edges are not allowed in this graph");
|
|
102
114
|
}
|
|
103
115
|
|
|
104
|
-
const edge: Edge = {source, target, weight, data};
|
|
116
|
+
const edge: Edge = { source, target, weight, data };
|
|
105
117
|
|
|
106
118
|
// Add to adjacency list
|
|
107
119
|
const sourceAdjacency = this.adjacencyList.get(source);
|
|
@@ -120,7 +132,7 @@ export class Graph {
|
|
|
120
132
|
} else {
|
|
121
133
|
// For undirected graphs, add the reverse edge
|
|
122
134
|
if (source !== target) {
|
|
123
|
-
const reverseEdge: Edge = {source: target, target: source, weight, data};
|
|
135
|
+
const reverseEdge: Edge = { source: target, target: source, weight, data };
|
|
124
136
|
const targetAdjacency = this.adjacencyList.get(target);
|
|
125
137
|
|
|
126
138
|
if (targetAdjacency) {
|
|
@@ -134,6 +146,9 @@ export class Graph {
|
|
|
134
146
|
|
|
135
147
|
/**
|
|
136
148
|
* Remove an edge from the graph
|
|
149
|
+
* @param source - The source node identifier
|
|
150
|
+
* @param target - The target node identifier
|
|
151
|
+
* @returns True if the edge was removed, false if it did not exist
|
|
137
152
|
*/
|
|
138
153
|
removeEdge(source: NodeId, target: NodeId): boolean {
|
|
139
154
|
const sourceEdges = this.adjacencyList.get(source);
|
|
@@ -162,6 +177,8 @@ export class Graph {
|
|
|
162
177
|
|
|
163
178
|
/**
|
|
164
179
|
* Check if a node exists in the graph
|
|
180
|
+
* @param id - The unique identifier of the node to check
|
|
181
|
+
* @returns True if the node exists, false otherwise
|
|
165
182
|
*/
|
|
166
183
|
hasNode(id: NodeId): boolean {
|
|
167
184
|
return this.nodeMap.has(id);
|
|
@@ -169,6 +186,9 @@ export class Graph {
|
|
|
169
186
|
|
|
170
187
|
/**
|
|
171
188
|
* Check if an edge exists in the graph
|
|
189
|
+
* @param source - The source node identifier
|
|
190
|
+
* @param target - The target node identifier
|
|
191
|
+
* @returns True if the edge exists, false otherwise
|
|
172
192
|
*/
|
|
173
193
|
hasEdge(source: NodeId, target: NodeId): boolean {
|
|
174
194
|
const sourceEdges = this.adjacencyList.get(source);
|
|
@@ -177,6 +197,8 @@ export class Graph {
|
|
|
177
197
|
|
|
178
198
|
/**
|
|
179
199
|
* Get a node by ID
|
|
200
|
+
* @param id - The unique identifier of the node to retrieve
|
|
201
|
+
* @returns The node if found, undefined otherwise
|
|
180
202
|
*/
|
|
181
203
|
getNode(id: NodeId): Node | undefined {
|
|
182
204
|
return this.nodeMap.get(id);
|
|
@@ -184,6 +206,9 @@ export class Graph {
|
|
|
184
206
|
|
|
185
207
|
/**
|
|
186
208
|
* Get an edge by source and target
|
|
209
|
+
* @param source - The source node identifier
|
|
210
|
+
* @param target - The target node identifier
|
|
211
|
+
* @returns The edge if found, undefined otherwise
|
|
187
212
|
*/
|
|
188
213
|
getEdge(source: NodeId, target: NodeId): Edge | undefined {
|
|
189
214
|
const sourceEdges = this.adjacencyList.get(source);
|
|
@@ -192,6 +217,7 @@ export class Graph {
|
|
|
192
217
|
|
|
193
218
|
/**
|
|
194
219
|
* Get the number of nodes in the graph
|
|
220
|
+
* @returns The total count of nodes
|
|
195
221
|
*/
|
|
196
222
|
get nodeCount(): number {
|
|
197
223
|
return this.nodeMap.size;
|
|
@@ -199,6 +225,7 @@ export class Graph {
|
|
|
199
225
|
|
|
200
226
|
/**
|
|
201
227
|
* Get the number of edges in the graph
|
|
228
|
+
* @returns The total count of edges
|
|
202
229
|
*/
|
|
203
230
|
get totalEdgeCount(): number {
|
|
204
231
|
return this.edgeCount;
|
|
@@ -206,6 +233,7 @@ export class Graph {
|
|
|
206
233
|
|
|
207
234
|
/**
|
|
208
235
|
* Check if the graph is directed
|
|
236
|
+
* @returns True if the graph is directed, false otherwise
|
|
209
237
|
*/
|
|
210
238
|
get isDirected(): boolean {
|
|
211
239
|
return this.config.directed;
|
|
@@ -213,6 +241,7 @@ export class Graph {
|
|
|
213
241
|
|
|
214
242
|
/**
|
|
215
243
|
* Get all nodes in the graph
|
|
244
|
+
* @returns An iterator over all nodes
|
|
216
245
|
*/
|
|
217
246
|
nodes(): IterableIterator<Node> {
|
|
218
247
|
return this.nodeMap.values();
|
|
@@ -220,6 +249,7 @@ export class Graph {
|
|
|
220
249
|
|
|
221
250
|
/**
|
|
222
251
|
* Get all edges in the graph
|
|
252
|
+
* @yields Each unique edge in the graph
|
|
223
253
|
*/
|
|
224
254
|
*edges(): IterableIterator<Edge> {
|
|
225
255
|
for (const [source, edges] of this.adjacencyList) {
|
|
@@ -236,6 +266,8 @@ export class Graph {
|
|
|
236
266
|
|
|
237
267
|
/**
|
|
238
268
|
* Get neighbors of a node (outgoing edges)
|
|
269
|
+
* @param nodeId - The node identifier to get neighbors for
|
|
270
|
+
* @returns An iterator over the neighbor node identifiers
|
|
239
271
|
*/
|
|
240
272
|
neighbors(nodeId: NodeId): IterableIterator<NodeId> {
|
|
241
273
|
const edges = this.adjacencyList.get(nodeId);
|
|
@@ -244,6 +276,8 @@ export class Graph {
|
|
|
244
276
|
|
|
245
277
|
/**
|
|
246
278
|
* Get incoming neighbors of a node (directed graphs only)
|
|
279
|
+
* @param nodeId - The node identifier to get incoming neighbors for
|
|
280
|
+
* @returns An iterator over the incoming neighbor node identifiers
|
|
247
281
|
*/
|
|
248
282
|
inNeighbors(nodeId: NodeId): IterableIterator<NodeId> {
|
|
249
283
|
if (!this.config.directed) {
|
|
@@ -256,6 +290,8 @@ export class Graph {
|
|
|
256
290
|
|
|
257
291
|
/**
|
|
258
292
|
* Get outgoing neighbors of a node
|
|
293
|
+
* @param nodeId - The node identifier to get outgoing neighbors for
|
|
294
|
+
* @returns An iterator over the outgoing neighbor node identifiers
|
|
259
295
|
*/
|
|
260
296
|
outNeighbors(nodeId: NodeId): IterableIterator<NodeId> {
|
|
261
297
|
return this.neighbors(nodeId);
|
|
@@ -263,6 +299,8 @@ export class Graph {
|
|
|
263
299
|
|
|
264
300
|
/**
|
|
265
301
|
* Get the degree of a node
|
|
302
|
+
* @param nodeId - The node identifier to get the degree for
|
|
303
|
+
* @returns The total degree of the node
|
|
266
304
|
*/
|
|
267
305
|
degree(nodeId: NodeId): number {
|
|
268
306
|
if (this.config.directed) {
|
|
@@ -275,6 +313,8 @@ export class Graph {
|
|
|
275
313
|
|
|
276
314
|
/**
|
|
277
315
|
* Get the in-degree of a node
|
|
316
|
+
* @param nodeId - The node identifier to get the in-degree for
|
|
317
|
+
* @returns The number of incoming edges to the node
|
|
278
318
|
*/
|
|
279
319
|
inDegree(nodeId: NodeId): number {
|
|
280
320
|
if (!this.config.directed) {
|
|
@@ -287,6 +327,8 @@ export class Graph {
|
|
|
287
327
|
|
|
288
328
|
/**
|
|
289
329
|
* Get the out-degree of a node
|
|
330
|
+
* @param nodeId - The node identifier to get the out-degree for
|
|
331
|
+
* @returns The number of outgoing edges from the node
|
|
290
332
|
*/
|
|
291
333
|
outDegree(nodeId: NodeId): number {
|
|
292
334
|
const edges = this.adjacencyList.get(nodeId);
|
|
@@ -295,23 +337,19 @@ export class Graph {
|
|
|
295
337
|
|
|
296
338
|
/**
|
|
297
339
|
* Create a copy of the graph
|
|
340
|
+
* @returns A new Graph instance with the same nodes and edges
|
|
298
341
|
*/
|
|
299
342
|
clone(): Graph {
|
|
300
343
|
const cloned = new Graph(this.config);
|
|
301
344
|
|
|
302
345
|
// Copy nodes
|
|
303
346
|
for (const node of this.nodeMap.values()) {
|
|
304
|
-
cloned.addNode(node.id, node.data ? {...
|
|
347
|
+
cloned.addNode(node.id, node.data ? { ...node.data } : undefined);
|
|
305
348
|
}
|
|
306
349
|
|
|
307
350
|
// Copy edges
|
|
308
351
|
for (const edge of this.edges()) {
|
|
309
|
-
cloned.addEdge(
|
|
310
|
-
edge.source,
|
|
311
|
-
edge.target,
|
|
312
|
-
edge.weight,
|
|
313
|
-
edge.data ? {... edge.data} : undefined,
|
|
314
|
-
);
|
|
352
|
+
cloned.addEdge(edge.source, edge.target, edge.weight, edge.data ? { ...edge.data } : undefined);
|
|
315
353
|
}
|
|
316
354
|
|
|
317
355
|
return cloned;
|
|
@@ -319,9 +357,10 @@ export class Graph {
|
|
|
319
357
|
|
|
320
358
|
/**
|
|
321
359
|
* Get graph configuration
|
|
360
|
+
* @returns A copy of the graph configuration object
|
|
322
361
|
*/
|
|
323
362
|
getConfig(): GraphConfig {
|
|
324
|
-
return {...
|
|
363
|
+
return { ...this.config };
|
|
325
364
|
}
|
|
326
365
|
|
|
327
366
|
/**
|
|
@@ -337,6 +376,7 @@ export class Graph {
|
|
|
337
376
|
/**
|
|
338
377
|
* Get the number of unique edges in the graph
|
|
339
378
|
* For undirected graphs, each edge is counted once
|
|
379
|
+
* @returns The count of unique edges
|
|
340
380
|
*/
|
|
341
381
|
get uniqueEdgeCount(): number {
|
|
342
382
|
if (this.config.directed) {
|
|
@@ -345,7 +385,7 @@ export class Graph {
|
|
|
345
385
|
|
|
346
386
|
// For undirected graphs, we need to count each edge only once
|
|
347
387
|
let count = 0;
|
|
348
|
-
|
|
388
|
+
|
|
349
389
|
for (const _edge of this.edges()) {
|
|
350
390
|
count++;
|
|
351
391
|
}
|