@graphty/algorithms 2.1.1 → 2.2.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 +139 -50
- package/dist/algorithms.js +15260 -10390
- package/dist/algorithms.js.map +1 -1
- package/dist/algorithms.standalone.js +26675 -21542
- package/dist/algorithms.standalone.js.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.d.ts +18 -0
- package/dist/src/algorithms/centrality/betweenness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.js +40 -1
- package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
- package/dist/src/algorithms/centrality/closeness.d.ts +20 -0
- package/dist/src/algorithms/centrality/closeness.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/closeness.js +93 -8
- package/dist/src/algorithms/centrality/closeness.js.map +1 -1
- package/dist/src/algorithms/centrality/degree.d.ts +9 -0
- package/dist/src/algorithms/centrality/degree.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/degree.js +18 -0
- package/dist/src/algorithms/centrality/degree.js.map +1 -1
- package/dist/src/algorithms/centrality/eigenvector.d.ts +10 -0
- package/dist/src/algorithms/centrality/eigenvector.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/eigenvector.js +29 -0
- package/dist/src/algorithms/centrality/eigenvector.js.map +1 -1
- package/dist/src/algorithms/centrality/hits.d.ts +9 -0
- package/dist/src/algorithms/centrality/hits.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/hits.js +23 -0
- package/dist/src/algorithms/centrality/hits.js.map +1 -1
- package/dist/src/algorithms/centrality/katz.d.ts +9 -0
- package/dist/src/algorithms/centrality/katz.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/katz.js +31 -0
- package/dist/src/algorithms/centrality/katz.js.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.d.ts +13 -13
- package/dist/src/algorithms/centrality/pagerank.d.ts.map +1 -1
- package/dist/src/algorithms/centrality/pagerank.js +47 -10
- package/dist/src/algorithms/centrality/pagerank.js.map +1 -1
- package/dist/src/algorithms/community/girvan-newman-legacy.d.ts +27 -0
- package/dist/src/algorithms/community/girvan-newman-legacy.d.ts.map +1 -0
- package/dist/src/algorithms/community/girvan-newman-legacy.js +335 -0
- package/dist/src/algorithms/community/girvan-newman-legacy.js.map +1 -0
- package/dist/src/algorithms/community/girvan-newman.d.ts +5 -3
- package/dist/src/algorithms/community/girvan-newman.d.ts.map +1 -1
- package/dist/src/algorithms/community/girvan-newman.js +34 -308
- package/dist/src/algorithms/community/girvan-newman.js.map +1 -1
- package/dist/src/algorithms/community/label-propagation.d.ts +9 -6
- package/dist/src/algorithms/community/label-propagation.d.ts.map +1 -1
- package/dist/src/algorithms/community/label-propagation.js +24 -110
- package/dist/src/algorithms/community/label-propagation.js.map +1 -1
- package/dist/src/algorithms/components/connected.d.ts.map +1 -1
- package/dist/src/algorithms/components/connected.js +28 -77
- package/dist/src/algorithms/components/connected.js.map +1 -1
- package/dist/src/algorithms/mst/kruskal.d.ts.map +1 -1
- package/dist/src/algorithms/mst/kruskal.js +59 -3
- package/dist/src/algorithms/mst/kruskal.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bellman-ford.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/bellman-ford.js +46 -0
- package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.js +27 -0
- package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -1
- package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts +15 -5
- package/dist/src/algorithms/shortest-path/floyd-warshall.d.ts.map +1 -1
- package/dist/src/algorithms/shortest-path/floyd-warshall.js +97 -107
- package/dist/src/algorithms/shortest-path/floyd-warshall.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs-unified.d.ts +2 -12
- package/dist/src/algorithms/traversal/bfs-unified.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs-unified.js +47 -308
- package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs-variants.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs-variants.js +5 -25
- package/dist/src/algorithms/traversal/bfs-variants.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs.d.ts +0 -3
- package/dist/src/algorithms/traversal/bfs.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/bfs.js +0 -4
- package/dist/src/algorithms/traversal/bfs.js.map +1 -1
- package/dist/src/algorithms/traversal/dfs.d.ts.map +1 -1
- package/dist/src/algorithms/traversal/dfs.js +33 -154
- package/dist/src/algorithms/traversal/dfs.js.map +1 -1
- package/dist/src/clustering/hierarchical-legacy.d.ts +56 -0
- package/dist/src/clustering/hierarchical-legacy.d.ts.map +1 -0
- package/dist/src/clustering/hierarchical-legacy.js +441 -0
- package/dist/src/clustering/hierarchical-legacy.js.map +1 -0
- package/dist/src/clustering/hierarchical.d.ts +7 -39
- package/dist/src/clustering/hierarchical.d.ts.map +1 -1
- package/dist/src/clustering/hierarchical.js +56 -417
- package/dist/src/clustering/hierarchical.js.map +1 -1
- package/dist/src/clustering/k-core-legacy.d.ts +30 -0
- package/dist/src/clustering/k-core-legacy.d.ts.map +1 -0
- package/dist/src/clustering/k-core-legacy.js +191 -0
- package/dist/src/clustering/k-core-legacy.js.map +1 -0
- package/dist/src/clustering/k-core.d.ts +8 -7
- package/dist/src/clustering/k-core.d.ts.map +1 -1
- package/dist/src/clustering/k-core.js +33 -174
- package/dist/src/clustering/k-core.js.map +1 -1
- package/dist/src/clustering/mcl-legacy.d.ts +42 -0
- package/dist/src/clustering/mcl-legacy.d.ts.map +1 -0
- package/dist/src/clustering/mcl-legacy.js +386 -0
- package/dist/src/clustering/mcl-legacy.js.map +1 -0
- package/dist/src/clustering/mcl.d.ts +9 -32
- package/dist/src/clustering/mcl.d.ts.map +1 -1
- package/dist/src/clustering/mcl.js +48 -374
- package/dist/src/clustering/mcl.js.map +1 -1
- package/dist/src/index.d.ts +29 -3
- 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/indexed/accelerator.d.ts +141 -11
- package/dist/src/indexed/accelerator.d.ts.map +1 -1
- package/dist/src/indexed/accelerator.js +319 -5
- package/dist/src/indexed/accelerator.js.map +1 -1
- package/dist/src/indexed/all-pairs.d.ts +59 -0
- package/dist/src/indexed/all-pairs.d.ts.map +1 -0
- package/dist/src/indexed/all-pairs.js +274 -0
- package/dist/src/indexed/all-pairs.js.map +1 -0
- package/dist/src/indexed/bellman-ford.d.ts +25 -0
- package/dist/src/indexed/bellman-ford.d.ts.map +1 -0
- package/dist/src/indexed/bellman-ford.js +59 -0
- package/dist/src/indexed/bellman-ford.js.map +1 -0
- package/dist/src/indexed/betweenness.d.ts +85 -0
- package/dist/src/indexed/betweenness.d.ts.map +1 -0
- package/dist/src/indexed/betweenness.js +188 -0
- package/dist/src/indexed/betweenness.js.map +1 -0
- package/dist/src/indexed/bfs.d.ts +69 -2
- package/dist/src/indexed/bfs.d.ts.map +1 -1
- package/dist/src/indexed/bfs.js +148 -1
- package/dist/src/indexed/bfs.js.map +1 -1
- package/dist/src/indexed/bipartite.d.ts +37 -0
- package/dist/src/indexed/bipartite.d.ts.map +1 -0
- package/dist/src/indexed/bipartite.js +54 -0
- package/dist/src/indexed/bipartite.js.map +1 -0
- package/dist/src/indexed/closeness.d.ts +49 -0
- package/dist/src/indexed/closeness.d.ts.map +1 -0
- package/dist/src/indexed/closeness.js +127 -0
- package/dist/src/indexed/closeness.js.map +1 -0
- package/dist/src/indexed/common-neighbors.d.ts +14 -1
- package/dist/src/indexed/common-neighbors.d.ts.map +1 -1
- package/dist/src/indexed/common-neighbors.js +24 -14
- package/dist/src/indexed/common-neighbors.js.map +1 -1
- package/dist/src/indexed/degree.d.ts +19 -0
- package/dist/src/indexed/degree.d.ts.map +1 -0
- package/dist/src/indexed/degree.js +45 -0
- package/dist/src/indexed/degree.js.map +1 -0
- package/dist/src/indexed/delta-pagerank.d.ts +145 -0
- package/dist/src/indexed/delta-pagerank.d.ts.map +1 -0
- package/dist/src/indexed/delta-pagerank.js +428 -0
- package/dist/src/indexed/delta-pagerank.js.map +1 -0
- package/dist/src/indexed/dfs.d.ts +55 -0
- package/dist/src/indexed/dfs.d.ts.map +1 -0
- package/dist/src/indexed/dfs.js +146 -0
- package/dist/src/indexed/dfs.js.map +1 -0
- package/dist/src/indexed/eigenvector.d.ts +48 -0
- package/dist/src/indexed/eigenvector.d.ts.map +1 -0
- package/dist/src/indexed/eigenvector.js +162 -0
- package/dist/src/indexed/eigenvector.js.map +1 -0
- package/dist/src/indexed/facade.d.ts +109 -0
- package/dist/src/indexed/facade.d.ts.map +1 -0
- package/dist/src/indexed/facade.js +204 -0
- package/dist/src/indexed/facade.js.map +1 -0
- package/dist/src/indexed/flow.d.ts +120 -0
- package/dist/src/indexed/flow.d.ts.map +1 -0
- package/dist/src/indexed/flow.js +377 -0
- package/dist/src/indexed/flow.js.map +1 -0
- package/dist/src/indexed/girvan-newman.d.ts +42 -0
- package/dist/src/indexed/girvan-newman.d.ts.map +1 -0
- package/dist/src/indexed/girvan-newman.js +90 -0
- package/dist/src/indexed/girvan-newman.js.map +1 -0
- package/dist/src/indexed/grsbm.d.ts +85 -0
- package/dist/src/indexed/grsbm.d.ts.map +1 -0
- package/dist/src/indexed/grsbm.js +287 -0
- package/dist/src/indexed/grsbm.js.map +1 -0
- package/dist/src/indexed/hierarchical.d.ts +69 -0
- package/dist/src/indexed/hierarchical.d.ts.map +1 -0
- package/dist/src/indexed/hierarchical.js +245 -0
- package/dist/src/indexed/hierarchical.js.map +1 -0
- package/dist/src/indexed/index.d.ts +29 -3
- package/dist/src/indexed/index.d.ts.map +1 -1
- package/dist/src/indexed/index.js +29 -3
- package/dist/src/indexed/index.js.map +1 -1
- package/dist/src/indexed/isomorphism.d.ts +42 -0
- package/dist/src/indexed/isomorphism.d.ts.map +1 -0
- package/dist/src/indexed/isomorphism.js +269 -0
- package/dist/src/indexed/isomorphism.js.map +1 -0
- package/dist/src/indexed/label-propagation.d.ts +134 -0
- package/dist/src/indexed/label-propagation.d.ts.map +1 -0
- package/dist/src/indexed/label-propagation.js +486 -0
- package/dist/src/indexed/label-propagation.js.map +1 -0
- package/dist/src/indexed/leiden.d.ts +52 -0
- package/dist/src/indexed/leiden.d.ts.map +1 -0
- package/dist/src/indexed/leiden.js +384 -0
- package/dist/src/indexed/leiden.js.map +1 -0
- package/dist/src/indexed/link-prediction.d.ts +147 -0
- package/dist/src/indexed/link-prediction.d.ts.map +1 -0
- package/dist/src/indexed/link-prediction.js +317 -0
- package/dist/src/indexed/link-prediction.js.map +1 -0
- package/dist/src/indexed/markov.d.ts +54 -0
- package/dist/src/indexed/markov.d.ts.map +1 -0
- package/dist/src/indexed/markov.js +284 -0
- package/dist/src/indexed/markov.js.map +1 -0
- package/dist/src/indexed/matching.d.ts +53 -0
- package/dist/src/indexed/matching.d.ts.map +1 -0
- package/dist/src/indexed/matching.js +139 -0
- package/dist/src/indexed/matching.js.map +1 -0
- package/dist/src/indexed/min-cut.d.ts +48 -0
- package/dist/src/indexed/min-cut.d.ts.map +1 -0
- package/dist/src/indexed/min-cut.js +175 -0
- package/dist/src/indexed/min-cut.js.map +1 -0
- package/dist/src/indexed/modularity.d.ts +34 -0
- package/dist/src/indexed/modularity.d.ts.map +1 -0
- package/dist/src/indexed/modularity.js +74 -0
- package/dist/src/indexed/modularity.js.map +1 -0
- package/dist/src/indexed/mst.d.ts +29 -1
- package/dist/src/indexed/mst.d.ts.map +1 -1
- package/dist/src/indexed/mst.js +62 -0
- package/dist/src/indexed/mst.js.map +1 -1
- package/dist/src/indexed/pagerank.d.ts +29 -6
- package/dist/src/indexed/pagerank.d.ts.map +1 -1
- package/dist/src/indexed/pagerank.js +107 -11
- package/dist/src/indexed/pagerank.js.map +1 -1
- package/dist/src/indexed/point-to-point.d.ts +53 -0
- package/dist/src/indexed/point-to-point.d.ts.map +1 -0
- package/dist/src/indexed/point-to-point.js +173 -0
- package/dist/src/indexed/point-to-point.js.map +1 -0
- package/dist/src/indexed/scc.d.ts +53 -0
- package/dist/src/indexed/scc.d.ts.map +1 -0
- package/dist/src/indexed/scc.js +110 -0
- package/dist/src/indexed/scc.js.map +1 -0
- package/dist/src/indexed/spectral.d.ts +61 -0
- package/dist/src/indexed/spectral.d.ts.map +1 -0
- package/dist/src/indexed/spectral.js +476 -0
- package/dist/src/indexed/spectral.js.map +1 -0
- package/dist/src/indexed/structures/bit-set.d.ts +45 -0
- package/dist/src/indexed/structures/bit-set.d.ts.map +1 -0
- package/dist/src/indexed/structures/bit-set.js +57 -0
- package/dist/src/indexed/structures/bit-set.js.map +1 -0
- package/dist/src/indexed/structures/max-heap.d.ts +52 -0
- package/dist/src/indexed/structures/max-heap.d.ts.map +1 -0
- package/dist/src/indexed/structures/max-heap.js +66 -0
- package/dist/src/indexed/structures/max-heap.js.map +1 -0
- package/dist/src/indexed/structures/min-heap.d.ts +30 -1
- package/dist/src/indexed/structures/min-heap.d.ts.map +1 -1
- package/dist/src/indexed/structures/min-heap.js +46 -6
- package/dist/src/indexed/structures/min-heap.js.map +1 -1
- package/dist/src/indexed/structures/ring-queue.d.ts +41 -0
- package/dist/src/indexed/structures/ring-queue.d.ts.map +1 -0
- package/dist/src/indexed/structures/ring-queue.js +64 -0
- package/dist/src/indexed/structures/ring-queue.js.map +1 -0
- package/dist/src/indexed/sync.d.ts +50 -0
- package/dist/src/indexed/sync.d.ts.map +1 -0
- package/dist/src/indexed/sync.js +175 -0
- package/dist/src/indexed/sync.js.map +1 -0
- package/dist/src/indexed/terahac.d.ts +58 -0
- package/dist/src/indexed/terahac.d.ts.map +1 -0
- package/dist/src/indexed/terahac.js +244 -0
- package/dist/src/indexed/terahac.js.map +1 -0
- package/dist/src/indexed/to-snapshot.d.ts +45 -2
- package/dist/src/indexed/to-snapshot.d.ts.map +1 -1
- package/dist/src/indexed/to-snapshot.js +140 -6
- package/dist/src/indexed/to-snapshot.js.map +1 -1
- package/dist/src/link-prediction/common-neighbors-legacy.d.ts +71 -0
- package/dist/src/link-prediction/common-neighbors-legacy.d.ts.map +1 -0
- package/dist/src/link-prediction/common-neighbors-legacy.js +172 -0
- package/dist/src/link-prediction/common-neighbors-legacy.js.map +1 -0
- package/dist/src/link-prediction/common-neighbors.d.ts +11 -19
- package/dist/src/link-prediction/common-neighbors.d.ts.map +1 -1
- package/dist/src/link-prediction/common-neighbors.js +56 -123
- package/dist/src/link-prediction/common-neighbors.js.map +1 -1
- package/dist/src/optimized/direction-optimized-bfs.d.ts +6 -0
- package/dist/src/optimized/direction-optimized-bfs.d.ts.map +1 -1
- package/dist/src/optimized/direction-optimized-bfs.js +6 -0
- package/dist/src/optimized/direction-optimized-bfs.js.map +1 -1
- package/dist/src/research/grsbm-legacy.d.ts +82 -0
- package/dist/src/research/grsbm-legacy.d.ts.map +1 -0
- package/dist/src/research/grsbm-legacy.js +416 -0
- package/dist/src/research/grsbm-legacy.js.map +1 -0
- package/dist/src/research/grsbm.d.ts +9 -66
- package/dist/src/research/grsbm.d.ts.map +1 -1
- package/dist/src/research/grsbm.js +67 -391
- package/dist/src/research/grsbm.js.map +1 -1
- package/dist/src/research/sync-legacy.d.ts +48 -0
- package/dist/src/research/sync-legacy.d.ts.map +1 -0
- package/dist/src/research/sync-legacy.js +337 -0
- package/dist/src/research/sync-legacy.js.map +1 -0
- package/dist/src/research/sync.d.ts +10 -32
- package/dist/src/research/sync.d.ts.map +1 -1
- package/dist/src/research/sync.js +28 -316
- package/dist/src/research/sync.js.map +1 -1
- package/package.json +4 -3
- package/src/algorithms/centrality/betweenness.ts +51 -1
- package/src/algorithms/centrality/closeness.ts +112 -12
- package/src/algorithms/centrality/degree.ts +19 -0
- package/src/algorithms/centrality/eigenvector.ts +34 -0
- package/src/algorithms/centrality/hits.ts +24 -0
- package/src/algorithms/centrality/katz.ts +32 -0
- package/src/algorithms/centrality/pagerank.ts +53 -13
- package/src/algorithms/community/girvan-newman-legacy.ts +414 -0
- package/src/algorithms/community/girvan-newman.ts +36 -386
- package/src/algorithms/community/label-propagation.ts +24 -133
- package/src/algorithms/components/connected.ts +28 -95
- package/src/algorithms/mst/kruskal.ts +65 -4
- package/src/algorithms/shortest-path/bellman-ford.ts +49 -0
- package/src/algorithms/shortest-path/dijkstra.ts +29 -0
- package/src/algorithms/shortest-path/floyd-warshall.ts +106 -136
- package/src/algorithms/traversal/bfs-unified.ts +52 -366
- package/src/algorithms/traversal/bfs-variants.ts +5 -28
- package/src/algorithms/traversal/bfs.ts +0 -4
- package/src/algorithms/traversal/dfs.ts +39 -182
- package/src/clustering/hierarchical-legacy.ts +551 -0
- package/src/clustering/hierarchical.ts +64 -520
- package/src/clustering/k-core-legacy.ts +229 -0
- package/src/clustering/k-core.ts +38 -209
- package/src/clustering/mcl-legacy.ts +498 -0
- package/src/clustering/mcl.ts +46 -479
- package/src/index.ts +90 -3
- package/src/indexed/accelerator.ts +502 -16
- package/src/indexed/all-pairs.ts +342 -0
- package/src/indexed/bellman-ford.ts +72 -0
- package/src/indexed/betweenness.ts +258 -0
- package/src/indexed/bfs.ts +189 -3
- package/src/indexed/bipartite.ts +77 -0
- package/src/indexed/closeness.ts +160 -0
- package/src/indexed/common-neighbors.ts +31 -14
- package/src/indexed/degree.ts +55 -0
- package/src/indexed/delta-pagerank.ts +513 -0
- package/src/indexed/dfs.ts +189 -0
- package/src/indexed/eigenvector.ts +202 -0
- package/src/indexed/facade.ts +229 -0
- package/src/indexed/flow.ts +500 -0
- package/src/indexed/girvan-newman.ts +120 -0
- package/src/indexed/grsbm.ts +390 -0
- package/src/indexed/hierarchical.ts +295 -0
- package/src/indexed/index.ts +94 -3
- package/src/indexed/isomorphism.ts +305 -0
- package/src/indexed/label-propagation.ts +582 -0
- package/src/indexed/leiden.ts +436 -0
- package/src/indexed/link-prediction.ts +399 -0
- package/src/indexed/markov.ts +324 -0
- package/src/indexed/matching.ts +192 -0
- package/src/indexed/min-cut.ts +201 -0
- package/src/indexed/modularity.ts +81 -0
- package/src/indexed/mst.ts +79 -1
- package/src/indexed/pagerank.ts +128 -15
- package/src/indexed/point-to-point.ts +220 -0
- package/src/indexed/scc.ts +132 -0
- package/src/indexed/spectral.ts +550 -0
- package/src/indexed/structures/bit-set.ts +65 -0
- package/src/indexed/structures/max-heap.ts +74 -0
- package/src/indexed/structures/min-heap.ts +51 -6
- package/src/indexed/structures/ring-queue.ts +70 -0
- package/src/indexed/sync.ts +229 -0
- package/src/indexed/terahac.ts +290 -0
- package/src/indexed/to-snapshot.ts +156 -6
- package/src/link-prediction/common-neighbors-legacy.ts +252 -0
- package/src/link-prediction/common-neighbors.ts +65 -168
- package/src/optimized/direction-optimized-bfs.ts +6 -1
- package/src/research/grsbm-legacy.ts +586 -0
- package/src/research/grsbm.ts +76 -555
- package/src/research/sync-legacy.ts +456 -0
- package/src/research/sync.ts +30 -431
package/src/research/sync.ts
CHANGED
|
@@ -1,40 +1,20 @@
|
|
|
1
|
-
import type { Graph } from "../core/graph.js";
|
|
2
|
-
import type { NodeId } from "../types/index.js";
|
|
3
|
-
import { euclideanDistance, SeededRandom } from "../utils/math-utilities.js";
|
|
4
|
-
|
|
5
1
|
/**
|
|
6
|
-
*
|
|
2
|
+
* SynC (Synergistic Deep Graph Clustering) over a legacy `Graph`. `syncClustering` delegates to
|
|
3
|
+
* `indexed.syncClustering` over an unweighted snapshot of the graph (SynC never reads a weight);
|
|
4
|
+
* clusters, embeddings, loss, iterations and convergence equal the pre-migration implementation in
|
|
5
|
+
* `sync-legacy.ts`, the floats to rounding. A run that diverges (a large learning rate) amplifies
|
|
6
|
+
* that rounding, so its embeddings and loss can differ from the old code's in the leading digits. A
|
|
7
|
+
* cluster count that is not an integer keeps the old code, which mostly fails on one ("Invalid array
|
|
8
|
+
* length"). Unlike the old code the port never replaces `Math.random`.
|
|
7
9
|
*/
|
|
8
|
-
export interface SynCConfig {
|
|
9
|
-
/** Number of clusters to find */
|
|
10
|
-
numClusters: number;
|
|
11
|
-
/** Maximum number of iterations for convergence */
|
|
12
|
-
maxIterations?: number;
|
|
13
|
-
/** Convergence tolerance */
|
|
14
|
-
tolerance?: number;
|
|
15
|
-
/** Random seed for reproducibility */
|
|
16
|
-
seed?: number;
|
|
17
|
-
/** Learning rate for optimization */
|
|
18
|
-
learningRate?: number;
|
|
19
|
-
/** Regularization parameter */
|
|
20
|
-
lambda?: number;
|
|
21
|
-
}
|
|
22
10
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
loss: number;
|
|
31
|
-
/** Number of iterations performed */
|
|
32
|
-
iterations: number;
|
|
33
|
-
/** Node embeddings learned by the algorithm */
|
|
34
|
-
embeddings: Map<NodeId, number[]>;
|
|
35
|
-
/** Whether the algorithm converged */
|
|
36
|
-
converged: boolean;
|
|
37
|
-
}
|
|
11
|
+
import type { Graph } from "../core/graph.js";
|
|
12
|
+
import { syncClustering as indexedSyncClustering } from "../indexed/sync.js";
|
|
13
|
+
import { toTopologySnapshot } from "../indexed/to-snapshot.js";
|
|
14
|
+
import type { NodeId } from "../types/index.js";
|
|
15
|
+
import { syncClustering as legacySyncClustering, type SynCConfig, type SynCResult } from "./sync-legacy.js";
|
|
16
|
+
|
|
17
|
+
export type { SynCConfig, SynCResult } from "./sync-legacy.js";
|
|
38
18
|
|
|
39
19
|
/**
|
|
40
20
|
* Synergistic Deep Graph Clustering (SynC) Algorithm
|
|
@@ -49,408 +29,27 @@ export interface SynCResult {
|
|
|
49
29
|
* @returns Clustering result with assignments and embeddings
|
|
50
30
|
*/
|
|
51
31
|
export function syncClustering(graph: Graph, config: SynCConfig): SynCResult {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// Set random seed for reproducibility (save original)
|
|
55
|
-
const originalRandom = Math.random;
|
|
56
|
-
const rng = SeededRandom.createGenerator(seed);
|
|
57
|
-
Math.random = rng;
|
|
58
|
-
|
|
59
|
-
const nodes = Array.from(graph.nodes());
|
|
60
|
-
const nodeCount = nodes.length;
|
|
61
|
-
|
|
62
|
-
if (nodeCount === 0) {
|
|
63
|
-
return {
|
|
64
|
-
clusters: new Map(),
|
|
65
|
-
loss: 0,
|
|
66
|
-
iterations: 0,
|
|
67
|
-
embeddings: new Map(),
|
|
68
|
-
converged: true,
|
|
69
|
-
};
|
|
32
|
+
if (!Number.isInteger(config.numClusters)) {
|
|
33
|
+
return legacySyncClustering(graph, config);
|
|
70
34
|
}
|
|
35
|
+
const s = toTopologySnapshot(graph);
|
|
36
|
+
const n = s.nodeCount;
|
|
37
|
+
const r = indexedSyncClustering(s, config);
|
|
71
38
|
|
|
72
|
-
|
|
73
|
-
throw new Error(
|
|
74
|
-
`Invalid number of clusters: ${String(numClusters)}. Must be between 1 and ${String(nodeCount)}`,
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Initialize node embeddings (simplified version using graph features)
|
|
79
|
-
const embeddingDim = Math.min(64, nodeCount);
|
|
39
|
+
const clusters = new Map<NodeId, number>();
|
|
80
40
|
const embeddings = new Map<NodeId, number[]>();
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
embeddings.set(node.id, embedding);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Initialize cluster centers
|
|
89
|
-
const clusterCenters = initializeClusterCenters(embeddings, numClusters);
|
|
90
|
-
|
|
91
|
-
let previousLoss = Infinity;
|
|
92
|
-
let iterations = 0;
|
|
93
|
-
let converged = false;
|
|
94
|
-
|
|
95
|
-
for (iterations = 0; iterations < maxIterations; iterations++) {
|
|
96
|
-
// E-step: Assign nodes to clusters
|
|
97
|
-
const clusters = new Map<NodeId, number>();
|
|
98
|
-
|
|
99
|
-
for (const node of nodes) {
|
|
100
|
-
const nodeEmbedding = embeddings.get(node.id);
|
|
101
|
-
if (!nodeEmbedding) {
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
let bestCluster = 0;
|
|
106
|
-
let minDistance = Infinity;
|
|
107
|
-
|
|
108
|
-
for (let k = 0; k < numClusters; k++) {
|
|
109
|
-
const center = clusterCenters[k];
|
|
110
|
-
if (!center) {
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const distance = euclideanDistance(nodeEmbedding, center);
|
|
115
|
-
if (distance < minDistance) {
|
|
116
|
-
minDistance = distance;
|
|
117
|
-
bestCluster = k;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
clusters.set(node.id, bestCluster);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// M-step: Update embeddings and cluster centers
|
|
125
|
-
updateEmbeddings(graph, embeddings, clusters, learningRate, lambda);
|
|
126
|
-
updateClusterCenters(embeddings, clusters, clusterCenters, numClusters);
|
|
127
|
-
|
|
128
|
-
// Calculate current loss
|
|
129
|
-
const currentLoss = calculateLoss(graph, embeddings, clusters, clusterCenters, lambda);
|
|
130
|
-
|
|
131
|
-
// Check for convergence
|
|
132
|
-
if (Math.abs(previousLoss - currentLoss) < tolerance) {
|
|
133
|
-
converged = true;
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
previousLoss = currentLoss;
|
|
41
|
+
for (let i = 0; i < n; i++) {
|
|
42
|
+
const id = s.ids.idOf(i);
|
|
43
|
+
clusters.set(id, r.labels[i]);
|
|
44
|
+
embeddings.set(id, Array.from(r.embeddings.subarray(i * r.dimensions, (i + 1) * r.dimensions)));
|
|
138
45
|
}
|
|
139
|
-
|
|
140
|
-
// Final cluster assignment
|
|
141
|
-
const finalClusters = new Map<NodeId, number>();
|
|
142
|
-
for (const node of nodes) {
|
|
143
|
-
const nodeEmbedding = embeddings.get(node.id);
|
|
144
|
-
if (!nodeEmbedding) {
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
let bestCluster = 0;
|
|
149
|
-
let minDistance = Infinity;
|
|
150
|
-
|
|
151
|
-
for (let k = 0; k < numClusters; k++) {
|
|
152
|
-
const center = clusterCenters[k];
|
|
153
|
-
if (!center) {
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const distance = euclideanDistance(nodeEmbedding, center);
|
|
158
|
-
if (distance < minDistance) {
|
|
159
|
-
minDistance = distance;
|
|
160
|
-
bestCluster = k;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
finalClusters.set(node.id, bestCluster);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Restore original random function
|
|
168
|
-
Math.random = originalRandom;
|
|
169
|
-
|
|
170
46
|
return {
|
|
171
|
-
clusters
|
|
172
|
-
loss
|
|
173
|
-
|
|
47
|
+
clusters,
|
|
48
|
+
// The old code reported the loss of the round before the converging one.
|
|
49
|
+
loss: r.converged && r.iterations > 1 ? r.previousLoss : r.loss,
|
|
50
|
+
// ...and one round more than it ran when it stopped at the cap.
|
|
51
|
+
iterations: r.iterations + (r.converged ? 0 : 1),
|
|
174
52
|
embeddings,
|
|
175
|
-
converged,
|
|
53
|
+
converged: r.converged,
|
|
176
54
|
};
|
|
177
55
|
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Initialize node embedding based on graph structure and features
|
|
181
|
-
* @param graph - The input graph
|
|
182
|
-
* @param nodeId - The node ID to initialize embedding for
|
|
183
|
-
* @param dim - The embedding dimension
|
|
184
|
-
* @returns The initialized embedding vector
|
|
185
|
-
*/
|
|
186
|
-
function initializeNodeEmbedding(graph: Graph, nodeId: NodeId, dim: number): number[] {
|
|
187
|
-
const embedding = new Array<number>(dim).fill(0);
|
|
188
|
-
|
|
189
|
-
// Use node degree as a base feature
|
|
190
|
-
const degree = graph.degree(nodeId);
|
|
191
|
-
const normalizedDegree = degree / Math.max(1, graph.nodeCount - 1);
|
|
192
|
-
|
|
193
|
-
// Initialize with small random values influenced by graph structure
|
|
194
|
-
for (let i = 0; i < dim; i++) {
|
|
195
|
-
embedding[i] = (Math.random() - 0.5) * 0.1 + normalizedDegree * 0.1;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return embedding;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Initialize cluster centers using k-means++ style initialization
|
|
203
|
-
* @param embeddings - Map of node IDs to embedding vectors
|
|
204
|
-
* @param numClusters - Number of cluster centers to initialize
|
|
205
|
-
* @returns Array of initialized cluster center vectors
|
|
206
|
-
*/
|
|
207
|
-
function initializeClusterCenters(embeddings: Map<NodeId, number[]>, numClusters: number): number[][] {
|
|
208
|
-
const embeddingArray = Array.from(embeddings.values());
|
|
209
|
-
const centers: number[][] = [];
|
|
210
|
-
|
|
211
|
-
// Choose first center randomly
|
|
212
|
-
const firstCenter = embeddingArray[Math.floor(Math.random() * embeddingArray.length)];
|
|
213
|
-
if (firstCenter) {
|
|
214
|
-
centers.push([...firstCenter] as number[]);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Choose remaining centers using k-means++ initialization
|
|
218
|
-
for (let k = 1; k < numClusters; k++) {
|
|
219
|
-
const distances: number[] = [];
|
|
220
|
-
let totalDistance = 0;
|
|
221
|
-
|
|
222
|
-
for (const embedding of embeddingArray) {
|
|
223
|
-
let minDistance = Infinity;
|
|
224
|
-
for (const center of centers) {
|
|
225
|
-
const distance = euclideanDistance(embedding, center);
|
|
226
|
-
minDistance = Math.min(minDistance, distance);
|
|
227
|
-
}
|
|
228
|
-
distances.push(minDistance * minDistance);
|
|
229
|
-
totalDistance += minDistance * minDistance;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Choose next center with probability proportional to squared distance
|
|
233
|
-
let randomValue = Math.random() * totalDistance;
|
|
234
|
-
for (let i = 0; i < embeddingArray.length; i++) {
|
|
235
|
-
const distanceValue = distances[i];
|
|
236
|
-
if (distanceValue !== undefined) {
|
|
237
|
-
randomValue -= distanceValue;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (randomValue <= 0) {
|
|
241
|
-
const newCenter = embeddingArray[i];
|
|
242
|
-
if (newCenter) {
|
|
243
|
-
centers.push([...newCenter] as number[]);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
break;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return centers;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Update node embeddings using gradient descent
|
|
256
|
-
* @param graph - The input graph
|
|
257
|
-
* @param embeddings - Map of node IDs to embedding vectors
|
|
258
|
-
* @param _clusters - Map of node IDs to cluster assignments (reserved for future use)
|
|
259
|
-
* @param learningRate - Learning rate for gradient descent
|
|
260
|
-
* @param lambda - Regularization parameter
|
|
261
|
-
*/
|
|
262
|
-
function updateEmbeddings(
|
|
263
|
-
graph: Graph,
|
|
264
|
-
embeddings: Map<NodeId, number[]>,
|
|
265
|
-
_clusters: Map<NodeId, number>,
|
|
266
|
-
learningRate: number,
|
|
267
|
-
lambda: number,
|
|
268
|
-
): void {
|
|
269
|
-
const gradients = new Map<NodeId, number[]>();
|
|
270
|
-
|
|
271
|
-
// Initialize gradients
|
|
272
|
-
for (const [nodeId, embedding] of embeddings) {
|
|
273
|
-
gradients.set(nodeId, new Array<number>(embedding.length).fill(0));
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Calculate gradients based on graph structure (simplified)
|
|
277
|
-
for (const node of graph.nodes()) {
|
|
278
|
-
const nodeId = node.id;
|
|
279
|
-
const nodeEmbedding = embeddings.get(nodeId);
|
|
280
|
-
if (!nodeEmbedding) {
|
|
281
|
-
continue;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const gradient = gradients.get(nodeId);
|
|
285
|
-
if (!gradient) {
|
|
286
|
-
continue;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// Neighbor reconstruction loss gradient
|
|
290
|
-
for (const neighborId of graph.neighbors(nodeId)) {
|
|
291
|
-
const neighborEmbedding = embeddings.get(neighborId);
|
|
292
|
-
if (!neighborEmbedding) {
|
|
293
|
-
continue;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const diff = nodeEmbedding.map((val: number, i: number) => val - (neighborEmbedding[i] ?? 0));
|
|
297
|
-
|
|
298
|
-
for (let i = 0; i < gradient.length; i++) {
|
|
299
|
-
const gradVal = gradient[i];
|
|
300
|
-
const diffVal = diff[i];
|
|
301
|
-
if (gradVal !== undefined && diffVal !== undefined) {
|
|
302
|
-
gradient[i] = gradVal + lambda * diffVal;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Regularization gradient
|
|
308
|
-
for (let i = 0; i < gradient.length; i++) {
|
|
309
|
-
const gradVal = gradient[i];
|
|
310
|
-
const nodeVal = nodeEmbedding[i];
|
|
311
|
-
if (gradVal !== undefined && nodeVal !== undefined) {
|
|
312
|
-
gradient[i] = gradVal + lambda * nodeVal;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Update embeddings
|
|
318
|
-
for (const [nodeId, embedding] of embeddings) {
|
|
319
|
-
const gradient = gradients.get(nodeId);
|
|
320
|
-
if (!gradient) {
|
|
321
|
-
continue;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
for (let i = 0; i < embedding.length; i++) {
|
|
325
|
-
const embVal = embedding[i];
|
|
326
|
-
const gradVal = gradient[i];
|
|
327
|
-
if (embVal !== undefined && gradVal !== undefined) {
|
|
328
|
-
embedding[i] = embVal - learningRate * gradVal;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Update cluster centers based on current assignments
|
|
336
|
-
* @param embeddings - Map of node IDs to embedding vectors
|
|
337
|
-
* @param clusters - Map of node IDs to cluster assignments
|
|
338
|
-
* @param clusterCenters - Array of cluster center vectors to update
|
|
339
|
-
* @param numClusters - Number of clusters
|
|
340
|
-
*/
|
|
341
|
-
function updateClusterCenters(
|
|
342
|
-
embeddings: Map<NodeId, number[]>,
|
|
343
|
-
clusters: Map<NodeId, number>,
|
|
344
|
-
clusterCenters: number[][],
|
|
345
|
-
numClusters: number,
|
|
346
|
-
): void {
|
|
347
|
-
const dimensions = clusterCenters[0]?.length ?? 0;
|
|
348
|
-
const clusterSums: number[][] = Array.from(
|
|
349
|
-
{ length: numClusters },
|
|
350
|
-
() => new Array(dimensions).fill(0) as number[],
|
|
351
|
-
);
|
|
352
|
-
const clusterCounts = new Array<number>(numClusters).fill(0);
|
|
353
|
-
|
|
354
|
-
// Sum embeddings for each cluster
|
|
355
|
-
for (const [nodeId, clusterIdx] of clusters) {
|
|
356
|
-
const embedding = embeddings.get(nodeId);
|
|
357
|
-
if (!embedding) {
|
|
358
|
-
continue;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
for (let i = 0; i < embedding.length; i++) {
|
|
362
|
-
const clusterSum = clusterSums[clusterIdx];
|
|
363
|
-
if (!clusterSum) {
|
|
364
|
-
continue;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
const sumVal = clusterSum[i];
|
|
368
|
-
const embVal = embedding[i];
|
|
369
|
-
if (sumVal !== undefined && embVal !== undefined) {
|
|
370
|
-
clusterSum[i] = sumVal + embVal;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
if (clusterCounts[clusterIdx] !== undefined) {
|
|
374
|
-
clusterCounts[clusterIdx]++;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// Update cluster centers (average of assigned embeddings)
|
|
379
|
-
for (let k = 0; k < numClusters; k++) {
|
|
380
|
-
const count = clusterCounts[k];
|
|
381
|
-
const center = clusterCenters[k];
|
|
382
|
-
const sum = clusterSums[k];
|
|
383
|
-
if (count !== undefined && count > 0 && center && sum) {
|
|
384
|
-
for (let i = 0; i < center.length; i++) {
|
|
385
|
-
const sumVal = sum[i];
|
|
386
|
-
if (sumVal !== undefined) {
|
|
387
|
-
center[i] = sumVal / count;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* Calculate the total loss function
|
|
396
|
-
* @param graph - The input graph
|
|
397
|
-
* @param embeddings - Map of node IDs to embedding vectors
|
|
398
|
-
* @param clusters - Map of node IDs to cluster assignments
|
|
399
|
-
* @param clusterCenters - Array of cluster center vectors
|
|
400
|
-
* @param lambda - Regularization parameter
|
|
401
|
-
* @returns The total loss value
|
|
402
|
-
*/
|
|
403
|
-
function calculateLoss(
|
|
404
|
-
graph: Graph,
|
|
405
|
-
embeddings: Map<NodeId, number[]>,
|
|
406
|
-
clusters: Map<NodeId, number>,
|
|
407
|
-
clusterCenters: number[][],
|
|
408
|
-
lambda: number,
|
|
409
|
-
): number {
|
|
410
|
-
let clusteringLoss = 0;
|
|
411
|
-
let reconstructionLoss = 0;
|
|
412
|
-
let regularizationLoss = 0;
|
|
413
|
-
|
|
414
|
-
// Clustering loss (distance to cluster centers)
|
|
415
|
-
for (const [nodeId, clusterIdx] of clusters) {
|
|
416
|
-
const embedding = embeddings.get(nodeId);
|
|
417
|
-
if (!embedding) {
|
|
418
|
-
continue;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
const center = clusterCenters[clusterIdx];
|
|
422
|
-
if (!center) {
|
|
423
|
-
continue;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
clusteringLoss += euclideanDistance(embedding, center) ** 2;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Graph reconstruction loss
|
|
430
|
-
for (const node of graph.nodes()) {
|
|
431
|
-
const nodeId = node.id;
|
|
432
|
-
const nodeEmbedding = embeddings.get(nodeId);
|
|
433
|
-
if (!nodeEmbedding) {
|
|
434
|
-
continue;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
for (const neighborId of graph.neighbors(nodeId)) {
|
|
438
|
-
const neighborEmbedding = embeddings.get(neighborId);
|
|
439
|
-
if (!neighborEmbedding) {
|
|
440
|
-
continue;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
const distance = euclideanDistance(nodeEmbedding, neighborEmbedding);
|
|
444
|
-
reconstructionLoss += distance ** 2;
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// Regularization loss
|
|
449
|
-
for (const embedding of embeddings.values()) {
|
|
450
|
-
for (const value of embedding) {
|
|
451
|
-
regularizationLoss += value ** 2;
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
return clusteringLoss + lambda * reconstructionLoss + lambda * regularizationLoss;
|
|
456
|
-
}
|