@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
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import type { Graph } from "../../core/graph.js";
|
|
2
|
+
import type { NodeId } from "../../types/index.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Simplified Delta-based PageRank that matches the standard algorithm
|
|
6
|
+
* but only processes active nodes for efficiency.
|
|
7
|
+
*
|
|
8
|
+
* Key optimization: Supports incremental updates when graph structure changes,
|
|
9
|
+
* avoiding full recomputation from scratch.
|
|
10
|
+
*/
|
|
11
|
+
export class SimpleDeltaPageRank {
|
|
12
|
+
private graph: Graph;
|
|
13
|
+
private nodeCount: number;
|
|
14
|
+
private previousScores: Map<NodeId, number> | null = null;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Creates a new SimpleDeltaPageRank instance for the given graph.
|
|
18
|
+
* @param graph - The directed graph to compute PageRank on
|
|
19
|
+
*/
|
|
20
|
+
constructor(graph: Graph) {
|
|
21
|
+
if (!graph.isDirected) {
|
|
22
|
+
throw new Error("PageRank requires a directed graph");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
this.graph = graph;
|
|
26
|
+
this.nodeCount = graph.nodeCount;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Computes PageRank scores for all nodes in the graph using power iteration.
|
|
31
|
+
* @param options - Configuration options for PageRank computation
|
|
32
|
+
* @param options.dampingFactor - Probability of following a link (default: 0.85)
|
|
33
|
+
* @param options.tolerance - Convergence tolerance threshold (default: 1e-6)
|
|
34
|
+
* @param options.maxIterations - Maximum number of iterations (default: 100)
|
|
35
|
+
* @param options.personalization - Personalization vector for Personalized PageRank
|
|
36
|
+
* @param options.weight - Edge attribute name for weighted PageRank
|
|
37
|
+
* @returns Map of node IDs to their PageRank scores
|
|
38
|
+
*/
|
|
39
|
+
public compute(
|
|
40
|
+
options: {
|
|
41
|
+
dampingFactor?: number;
|
|
42
|
+
tolerance?: number;
|
|
43
|
+
maxIterations?: number;
|
|
44
|
+
personalization?: Map<NodeId, number>;
|
|
45
|
+
weight?: string;
|
|
46
|
+
} = {},
|
|
47
|
+
): Map<NodeId, number> {
|
|
48
|
+
const { dampingFactor = 0.85, tolerance = 1e-6, maxIterations = 100, personalization, weight } = options;
|
|
49
|
+
|
|
50
|
+
if (this.nodeCount === 0) {
|
|
51
|
+
return new Map();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Initialize scores
|
|
55
|
+
const scores = new Map<NodeId, number>();
|
|
56
|
+
const newScores = new Map<NodeId, number>();
|
|
57
|
+
|
|
58
|
+
for (const node of this.graph.nodes()) {
|
|
59
|
+
scores.set(node.id, 1.0 / this.nodeCount);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Precompute out-degrees and weights
|
|
63
|
+
const outWeights = new Map<NodeId, number>();
|
|
64
|
+
const danglingNodes = new Set<NodeId>();
|
|
65
|
+
|
|
66
|
+
for (const node of this.graph.nodes()) {
|
|
67
|
+
let totalWeight = 0;
|
|
68
|
+
let outDegree = 0;
|
|
69
|
+
|
|
70
|
+
for (const neighbor of Array.from(this.graph.neighbors(node.id))) {
|
|
71
|
+
outDegree++;
|
|
72
|
+
const edge = this.graph.getEdge(node.id, neighbor);
|
|
73
|
+
totalWeight += edge?.weight ?? 1;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
outWeights.set(node.id, totalWeight);
|
|
77
|
+
if (outDegree === 0) {
|
|
78
|
+
danglingNodes.add(node.id);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Normalize personalization vector if provided
|
|
83
|
+
let personalVector: Map<NodeId, number> | null = null;
|
|
84
|
+
if (personalization) {
|
|
85
|
+
personalVector = new Map(personalization);
|
|
86
|
+
this.normalizeMap(personalVector);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Power iteration with active node tracking
|
|
90
|
+
let activeNodes = new Set<NodeId>(scores.keys());
|
|
91
|
+
let iteration = 0;
|
|
92
|
+
|
|
93
|
+
for (iteration = 0; iteration < maxIterations; iteration++) {
|
|
94
|
+
// Initialize new scores
|
|
95
|
+
for (const nodeId of scores.keys()) {
|
|
96
|
+
if (personalVector) {
|
|
97
|
+
newScores.set(nodeId, (1 - dampingFactor) * (personalVector.get(nodeId) ?? 0));
|
|
98
|
+
} else {
|
|
99
|
+
newScores.set(nodeId, (1 - dampingFactor) / this.nodeCount);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Handle dangling nodes
|
|
104
|
+
let danglingSum = 0;
|
|
105
|
+
for (const danglingNode of danglingNodes) {
|
|
106
|
+
danglingSum += scores.get(danglingNode) ?? 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (danglingSum > 0) {
|
|
110
|
+
const danglingContribution = (dampingFactor * danglingSum) / this.nodeCount;
|
|
111
|
+
for (const nodeId of scores.keys()) {
|
|
112
|
+
const current = newScores.get(nodeId) ?? 0;
|
|
113
|
+
if (personalVector) {
|
|
114
|
+
const personalContrib = danglingContribution * (personalVector.get(nodeId) ?? 0);
|
|
115
|
+
newScores.set(nodeId, current + personalContrib);
|
|
116
|
+
} else {
|
|
117
|
+
newScores.set(nodeId, current + danglingContribution);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Propagate rank from active nodes only
|
|
123
|
+
for (const nodeId of activeNodes) {
|
|
124
|
+
const currentRank = scores.get(nodeId) ?? 0;
|
|
125
|
+
const nodeOutWeight = outWeights.get(nodeId) ?? 0;
|
|
126
|
+
|
|
127
|
+
if (nodeOutWeight > 0) {
|
|
128
|
+
for (const neighbor of Array.from(this.graph.neighbors(nodeId))) {
|
|
129
|
+
let edgeWeight = 1;
|
|
130
|
+
if (weight) {
|
|
131
|
+
const edge = this.graph.getEdge(nodeId, neighbor);
|
|
132
|
+
edgeWeight = edge?.weight ?? 1;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const contribution = dampingFactor * currentRank * (edgeWeight / nodeOutWeight);
|
|
136
|
+
const neighborRank = newScores.get(neighbor) ?? 0;
|
|
137
|
+
newScores.set(neighbor, neighborRank + contribution);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Check convergence and identify active nodes
|
|
143
|
+
const nextActive = new Set<NodeId>();
|
|
144
|
+
let maxDiff = 0;
|
|
145
|
+
|
|
146
|
+
for (const nodeId of scores.keys()) {
|
|
147
|
+
const oldRank = scores.get(nodeId) ?? 0;
|
|
148
|
+
const newRank = newScores.get(nodeId) ?? 0;
|
|
149
|
+
const diff = Math.abs(newRank - oldRank);
|
|
150
|
+
maxDiff = Math.max(maxDiff, diff);
|
|
151
|
+
|
|
152
|
+
// Mark as active if change is significant
|
|
153
|
+
if (diff > tolerance / 10) {
|
|
154
|
+
nextActive.add(nodeId);
|
|
155
|
+
// Also mark neighbors as potentially active
|
|
156
|
+
for (const neighbor of Array.from(this.graph.neighbors(nodeId))) {
|
|
157
|
+
nextActive.add(neighbor);
|
|
158
|
+
}
|
|
159
|
+
for (const neighbor of Array.from(this.graph.inNeighbors(nodeId))) {
|
|
160
|
+
nextActive.add(neighbor);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Swap score maps
|
|
166
|
+
scores.clear();
|
|
167
|
+
for (const [k, v] of newScores) {
|
|
168
|
+
scores.set(k, v);
|
|
169
|
+
}
|
|
170
|
+
newScores.clear();
|
|
171
|
+
|
|
172
|
+
activeNodes = nextActive;
|
|
173
|
+
|
|
174
|
+
if (maxDiff < tolerance) {
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Store scores for potential incremental updates
|
|
180
|
+
this.previousScores = new Map(scores);
|
|
181
|
+
|
|
182
|
+
return scores;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Perform incremental update after graph modification.
|
|
187
|
+
* This is where delta-based approach provides significant speedup.
|
|
188
|
+
* @param modifiedNodes - Set of nodes that were modified (edges added/removed)
|
|
189
|
+
* @param options - Computation options
|
|
190
|
+
* @param options.dampingFactor - Probability of following a link (default: 0.85)
|
|
191
|
+
* @param options.tolerance - Convergence tolerance threshold (default: 1e-6)
|
|
192
|
+
* @param options.maxIterations - Maximum number of iterations (default: 100)
|
|
193
|
+
* @param options.personalization - Personalization vector for Personalized PageRank
|
|
194
|
+
* @param options.weight - Edge attribute name for weighted PageRank
|
|
195
|
+
* @returns Updated PageRank scores
|
|
196
|
+
*/
|
|
197
|
+
public update(
|
|
198
|
+
modifiedNodes: Set<NodeId>,
|
|
199
|
+
options: {
|
|
200
|
+
dampingFactor?: number;
|
|
201
|
+
tolerance?: number;
|
|
202
|
+
maxIterations?: number;
|
|
203
|
+
personalization?: Map<NodeId, number>;
|
|
204
|
+
weight?: string;
|
|
205
|
+
} = {},
|
|
206
|
+
): Map<NodeId, number> {
|
|
207
|
+
if (!this.previousScores) {
|
|
208
|
+
// No previous computation, do full compute
|
|
209
|
+
return this.compute(options);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const { dampingFactor = 0.85, tolerance = 1e-6, maxIterations = 100, personalization, weight } = options;
|
|
213
|
+
|
|
214
|
+
// Initialize scores from previous computation
|
|
215
|
+
const scores = new Map(this.previousScores);
|
|
216
|
+
const newScores = new Map<NodeId, number>();
|
|
217
|
+
|
|
218
|
+
// Ensure all current nodes are included
|
|
219
|
+
for (const node of this.graph.nodes()) {
|
|
220
|
+
if (!scores.has(node.id)) {
|
|
221
|
+
scores.set(node.id, 1.0 / this.nodeCount);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Start with only modified nodes and their neighbors as active
|
|
226
|
+
let activeNodes = new Set<NodeId>();
|
|
227
|
+
for (const nodeId of modifiedNodes) {
|
|
228
|
+
activeNodes.add(nodeId);
|
|
229
|
+
// Add incoming neighbors
|
|
230
|
+
for (const neighbor of Array.from(this.graph.inNeighbors(nodeId))) {
|
|
231
|
+
activeNodes.add(neighbor);
|
|
232
|
+
}
|
|
233
|
+
// Add outgoing neighbors
|
|
234
|
+
for (const neighbor of Array.from(this.graph.neighbors(nodeId))) {
|
|
235
|
+
activeNodes.add(neighbor);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Recompute out-weights for modified nodes
|
|
240
|
+
const outWeights = new Map<NodeId, number>();
|
|
241
|
+
const danglingNodes = new Set<NodeId>();
|
|
242
|
+
|
|
243
|
+
for (const node of this.graph.nodes()) {
|
|
244
|
+
let totalWeight = 0;
|
|
245
|
+
let outDegree = 0;
|
|
246
|
+
|
|
247
|
+
for (const neighbor of Array.from(this.graph.neighbors(node.id))) {
|
|
248
|
+
outDegree++;
|
|
249
|
+
const edge = this.graph.getEdge(node.id, neighbor);
|
|
250
|
+
totalWeight += edge?.weight ?? 1;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
outWeights.set(node.id, totalWeight);
|
|
254
|
+
if (outDegree === 0) {
|
|
255
|
+
danglingNodes.add(node.id);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Normalize personalization vector if provided
|
|
260
|
+
let personalVector: Map<NodeId, number> | null = null;
|
|
261
|
+
if (personalization) {
|
|
262
|
+
personalVector = new Map(personalization);
|
|
263
|
+
this.normalizeMap(personalVector);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Run limited iterations focusing on active nodes
|
|
267
|
+
let iteration = 0;
|
|
268
|
+
|
|
269
|
+
for (iteration = 0; iteration < maxIterations && activeNodes.size > 0; iteration++) {
|
|
270
|
+
// Initialize new scores
|
|
271
|
+
for (const nodeId of scores.keys()) {
|
|
272
|
+
if (personalVector) {
|
|
273
|
+
newScores.set(nodeId, (1 - dampingFactor) * (personalVector.get(nodeId) ?? 0));
|
|
274
|
+
} else {
|
|
275
|
+
newScores.set(nodeId, (1 - dampingFactor) / this.nodeCount);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Handle dangling nodes
|
|
280
|
+
let danglingSum = 0;
|
|
281
|
+
for (const danglingNode of danglingNodes) {
|
|
282
|
+
danglingSum += scores.get(danglingNode) ?? 0;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (danglingSum > 0) {
|
|
286
|
+
const danglingContribution = (dampingFactor * danglingSum) / this.nodeCount;
|
|
287
|
+
for (const nodeId of scores.keys()) {
|
|
288
|
+
const current = newScores.get(nodeId) ?? 0;
|
|
289
|
+
if (personalVector) {
|
|
290
|
+
const personalContrib = danglingContribution * (personalVector.get(nodeId) ?? 0);
|
|
291
|
+
newScores.set(nodeId, current + personalContrib);
|
|
292
|
+
} else {
|
|
293
|
+
newScores.set(nodeId, current + danglingContribution);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Propagate rank from ALL nodes (not just active)
|
|
299
|
+
// This ensures correctness
|
|
300
|
+
for (const nodeId of scores.keys()) {
|
|
301
|
+
const currentRank = scores.get(nodeId) ?? 0;
|
|
302
|
+
const nodeOutWeight = outWeights.get(nodeId) ?? 0;
|
|
303
|
+
|
|
304
|
+
if (nodeOutWeight > 0) {
|
|
305
|
+
for (const neighbor of Array.from(this.graph.neighbors(nodeId))) {
|
|
306
|
+
let edgeWeight = 1;
|
|
307
|
+
if (weight) {
|
|
308
|
+
const edge = this.graph.getEdge(nodeId, neighbor);
|
|
309
|
+
edgeWeight = edge?.weight ?? 1;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const contribution = dampingFactor * currentRank * (edgeWeight / nodeOutWeight);
|
|
313
|
+
const neighborRank = newScores.get(neighbor) ?? 0;
|
|
314
|
+
newScores.set(neighbor, neighborRank + contribution);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Check convergence only for active nodes
|
|
320
|
+
const nextActive = new Set<NodeId>();
|
|
321
|
+
let maxDiff = 0;
|
|
322
|
+
|
|
323
|
+
for (const nodeId of activeNodes) {
|
|
324
|
+
const oldRank = scores.get(nodeId) ?? 0;
|
|
325
|
+
const newRank = newScores.get(nodeId) ?? 0;
|
|
326
|
+
const diff = Math.abs(newRank - oldRank);
|
|
327
|
+
maxDiff = Math.max(maxDiff, diff);
|
|
328
|
+
|
|
329
|
+
// Mark as active if change is significant
|
|
330
|
+
if (diff > tolerance / 10) {
|
|
331
|
+
nextActive.add(nodeId);
|
|
332
|
+
// Also mark neighbors as potentially active
|
|
333
|
+
for (const neighbor of Array.from(this.graph.neighbors(nodeId))) {
|
|
334
|
+
nextActive.add(neighbor);
|
|
335
|
+
}
|
|
336
|
+
for (const neighbor of Array.from(this.graph.inNeighbors(nodeId))) {
|
|
337
|
+
nextActive.add(neighbor);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Swap score maps
|
|
343
|
+
scores.clear();
|
|
344
|
+
for (const [k, v] of newScores) {
|
|
345
|
+
scores.set(k, v);
|
|
346
|
+
}
|
|
347
|
+
newScores.clear();
|
|
348
|
+
|
|
349
|
+
activeNodes = nextActive;
|
|
350
|
+
|
|
351
|
+
if (maxDiff < tolerance) {
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Store scores for future incremental updates
|
|
357
|
+
this.previousScores = new Map(scores);
|
|
358
|
+
|
|
359
|
+
return scores;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
private normalizeMap(map: Map<NodeId, number>): void {
|
|
363
|
+
let sum = 0;
|
|
364
|
+
for (const value of map.values()) {
|
|
365
|
+
sum += value;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (sum > 0) {
|
|
369
|
+
for (const [key, value] of Array.from(map)) {
|
|
370
|
+
map.set(key, value / sum);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|