@graphty/algorithms / index / ALGORITHMS
Variable: ALGORITHMS ​
constALGORITHMS:object
Defined in: catalog.ts:215
Every algorithm of @graphty/algorithms, keyed by its export name.
Type Declaration ​
adamicAdarForPairs ​
readonlyadamicAdarForPairs:object
adamicAdarForPairs.accelerator ​
readonlyaccelerator:null=null
adamicAdarForPairs.category ​
readonlycategory:"link-prediction"="link-prediction"
adamicAdarForPairs.direction ​
readonlydirection:"any"="any"
adamicAdarForPairs.dispatch ​
readonlydispatch:null=null
adamicAdarForPairs.fn ​
readonlyfn: (s,pairs,o) =>F64=adamicAdarForPairs
Adamic-Adar scores of the given pairs, in order.
Parameters ​
s ​
GraphSnapshot
The snapshot
pairs ​
The pairs
o? ​
Options
Returns ​
F64
One score per pair
adamicAdarForPairs.inputs ​
readonlyinputs: readonly [AlgorithmInput]
adamicAdarForPairs.name ​
readonlyname:"adamicAdarForPairs"="adamicAdarForPairs"
adamicAdarForPairs.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
adamicAdarForPairs.result ​
readonlyresult:"pair-scores"="pair-scores"
adamicAdarForPairs.values ​
readonlyvalues: readonly [] =[]
adamicAdarForPairs.weights ​
readonlyweights:"never"="never"
adamicAdarPrediction ​
readonlyadamicAdarPrediction:object
adamicAdarPrediction.accelerator ​
readonlyaccelerator:null=null
adamicAdarPrediction.category ​
readonlycategory:"link-prediction"="link-prediction"
adamicAdarPrediction.direction ​
readonlydirection:"any"="any"
adamicAdarPrediction.dispatch ​
readonlydispatch:"adamicAdarPrediction"="adamicAdarPrediction"
adamicAdarPrediction.fn ​
readonlyfn: (s,o) =>LinkPredictionResult=adamicAdarPrediction
Adamic-Adar scores of every pair not already joined, ranked.
Parameters ​
s ​
GraphSnapshot
The snapshot
o? ​
Options
Returns ​
The pairs with a positive score, highest first
adamicAdarPrediction.inputs ​
readonlyinputs: readonly [] =[]
adamicAdarPrediction.name ​
readonlyname:"adamicAdarPrediction"="adamicAdarPrediction"
adamicAdarPrediction.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
adamicAdarPrediction.result ​
readonlyresult:"link-predictions"="link-predictions"
adamicAdarPrediction.values ​
readonlyvalues: readonly [] =[]
adamicAdarPrediction.weights ​
readonlyweights:"never"="never"
adamicAdarScore ​
readonlyadamicAdarScore:object
adamicAdarScore.accelerator ​
readonlyaccelerator:null=null
adamicAdarScore.category ​
readonlycategory:"link-prediction"="link-prediction"
adamicAdarScore.direction ​
readonlydirection:"any"="any"
adamicAdarScore.dispatch ​
readonlydispatch:null=null
adamicAdarScore.fn ​
readonlyfn: (s,uNode,vNode,o) =>number=adamicAdarScore
The Adamic-Adar index of two nodes: the sum over distinct common neighbours z of 1 / ln(degree(z)).
Parameters ​
s ​
GraphSnapshot
The snapshot
uNode ​
NodeRef
A node: its index, or { id }
vNode ​
NodeRef
A node: its index, or { id }
o? ​
Options
Returns ​
number
The score
adamicAdarScore.inputs ​
readonlyinputs: readonly [AlgorithmInput,AlgorithmInput]
adamicAdarScore.name ​
readonlyname:"adamicAdarScore"="adamicAdarScore"
adamicAdarScore.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
adamicAdarScore.result ​
readonlyresult:"number"="number"
adamicAdarScore.values ​
readonlyvalues: readonly [] =[]
adamicAdarScore.weights ​
readonlyweights:"never"="never"
allPairsShortestPath ​
readonlyallPairsShortestPath:object
allPairsShortestPath.accelerator ​
readonlyaccelerator:"allPairsShortestPath"="allPairsShortestPath"
allPairsShortestPath.category ​
readonlycategory:"shortest-path"="shortest-path"
allPairsShortestPath.direction ​
readonlydirection:"any"="any"
allPairsShortestPath.dispatch ​
readonlydispatch:"allPairsShortestPath"="allPairsShortestPath"
allPairsShortestPath.fn ​
readonlyfn: (s,options) =>ApspResult=allPairsShortestPath
All-pairs shortest paths over a snapshot.
Parameters ​
s ​
GraphSnapshot
The snapshot
options? ​
ApspOptions = {}
Weights, strategy, paths and the size bound
Returns ​
The distance matrix and the path accessors
allPairsShortestPath.inputs ​
readonlyinputs: readonly [] =[]
allPairsShortestPath.name ​
readonlyname:"allPairsShortestPath"="allPairsShortestPath"
allPairsShortestPath.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
allPairsShortestPath.result ​
readonlyresult:"all-pairs"="all-pairs"
allPairsShortestPath.values ​
readonlyvalues: readonly [] =[]
allPairsShortestPath.weights ​
readonlyweights:"by-default"="by-default"
astar ​
readonlyastar:object
astar.accelerator ​
readonlyaccelerator:null=null
astar.category ​
readonlycategory:"shortest-path"="shortest-path"
astar.direction ​
readonlydirection:"any"="any"
astar.dispatch ​
readonlydispatch:null=null
astar.fn ​
readonlyfn: (g,sourceNode,targetNode,heuristic,options) =>AstarResult=astar
A* between two nodes: Dijkstra keyed by cost so far plus heuristic(node, target), stopping when the target is taken from the queue. An expanded node is never reopened, as in legacy astar, so the path is shortest when the heuristic is consistent. The predecessor is the relaxing ARC, so a parallel edge on the path is the exact edge.
Parameters ​
g ​
AdjacencyView
The adjacency to search
sourceNode ​
NodeRef
The node to start from: its index, or { id }
targetNode ​
NodeRef
The node to reach: its index, or { id }
heuristic ​
(node, target) => number
Estimated remaining cost from a node index to the target index
options? ​
PathOptions = {}
Per-arc weight override
Returns ​
The path, plus the per-node scores and the expansion order
astar.inputs ​
readonlyinputs: readonly [AlgorithmInput,AlgorithmInput, {kind:"heuristic";name:"heuristic"; }]
astar.name ​
readonlyname:"astar"="astar"
astar.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
astar.result ​
readonlyresult:"path"="path"
astar.values ​
readonlyvalues: readonly [] =[]
astar.weights ​
readonlyweights:"always"="always"
bellmanFord ​
readonlybellmanFord:object
bellmanFord.accelerator ​
readonlyaccelerator:"bellmanFord"="bellmanFord"
bellmanFord.category ​
readonlycategory:"shortest-path"="shortest-path"
bellmanFord.direction ​
readonlydirection:"any"="any"
bellmanFord.dispatch ​
readonlydispatch:"bellmanFord"="bellmanFord"
bellmanFord.fn ​
readonlyfn: (g,sourceNode,options) =>BellmanFordResult=bellmanFord
Bellman-Ford single-source shortest paths, index-based, for weights that may be negative. It relaxes every arc, so an undirected edge is relaxed both ways (it has an arc in each direction), and one negative undirected edge is therefore a negative cycle. Rounds stop early once one changes nothing; after nodeCount - 1 rounds one more round decides hasNegativeCycle. The predecessor is the relaxing ARC, as in dijkstra, so a parallel edge on a path is identified exactly.
Parameters ​
g ​
AdjacencyView
The adjacency to search
sourceNode ​
NodeRef
The node to start from: its index, or { id }
options? ​
SsspOptions = {}
Cutoff (a relaxation beyond it is skipped) and per-arc weight override
Returns ​
The distances, the predecessor arcs, the path accessors and the negative-cycle flag
bellmanFord.inputs ​
readonlyinputs: readonly [AlgorithmInput]
bellmanFord.name ​
readonlyname:"bellmanFord"="bellmanFord"
bellmanFord.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
bellmanFord.result ​
readonlyresult:"shortest-paths"="shortest-paths"
bellmanFord.values ​
readonlyvalues: readonly ["dist","predArc"]
bellmanFord.weights ​
readonlyweights:"always"="always"
betweennessCentrality ​
readonlybetweennessCentrality:object
betweennessCentrality.accelerator ​
readonlyaccelerator:"betweennessCentrality"="betweennessCentrality"
betweennessCentrality.category ​
readonlycategory:"centrality"="centrality"
betweennessCentrality.direction ​
readonlydirection:"any"="any"
betweennessCentrality.dispatch ​
readonlydispatch:"betweennessCentrality"="betweennessCentrality"
betweennessCentrality.fn ​
readonlyfn: (s,options) =>BetweennessResult=betweennessCentrality
Node betweenness centrality by Brandes' algorithm: breadth-first, so weights are ignored, as in the legacy betweennessCentrality, whose scores this equals on every graph it can hold. A sampled run (sources or k) is the UNSCALED sum over the sources run, as the WebGPU accelerator reports it.
Parameters ​
s ​
GraphSnapshot
The snapshot
options? ​
BetweennessOptions = {}
Normalisation, endpoints and sampling
Returns ​
One score per node index; iterations is the number of sources run
Throws ​
RangeError for a bad sources or k
betweennessCentrality.inputs ​
readonlyinputs: readonly [] =[]
betweennessCentrality.name ​
readonlyname:"betweennessCentrality"="betweennessCentrality"
betweennessCentrality.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
betweennessCentrality.result ​
readonlyresult:"node-scores"="node-scores"
betweennessCentrality.values ​
readonlyvalues: readonly ["scores"]
betweennessCentrality.weights ​
readonlyweights:"never"="never"
bidirectionalDijkstra ​
readonlybidirectionalDijkstra:object
bidirectionalDijkstra.accelerator ​
readonlyaccelerator:null=null
bidirectionalDijkstra.category ​
readonlycategory:"shortest-path"="shortest-path"
bidirectionalDijkstra.direction ​
readonlydirection:"any"="any"
bidirectionalDijkstra.dispatch ​
readonlydispatch:null=null
bidirectionalDijkstra.fn ​
readonlyfn: (s,sourceNode,targetNode,options) =>PathResult=bidirectionalDijkstra
Bidirectional Dijkstra between two nodes: one search forward from the source, one backward from the target over reverse(), each step taken by the side whose next key is smaller, stopping once the two next keys sum to at least the best path found. That is the point-to-point query legacy dijkstraPath answers. The path is recorded as ARCS, so a parallel edge on it is the exact edge.
Parameters ​
s ​
GraphSnapshot
The snapshot to search
sourceNode ​
NodeRef
The node to start from: its index, or { id }
targetNode ​
NodeRef
The node to reach: its index, or { id }
options? ​
PathOptions = {}
Per-arc weight override
Returns ​
The distance, the node path and the logical edges on it
Throws ​
Error when any weight is negative
bidirectionalDijkstra.inputs ​
readonlyinputs: readonly [AlgorithmInput,AlgorithmInput]
bidirectionalDijkstra.name ​
readonlyname:"bidirectionalDijkstra"="bidirectionalDijkstra"
bidirectionalDijkstra.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
bidirectionalDijkstra.result ​
readonlyresult:"path"="path"
bidirectionalDijkstra.values ​
readonlyvalues: readonly [] =[]
bidirectionalDijkstra.weights ​
readonlyweights:"always"="always"
breadthFirstSearch ​
readonlybreadthFirstSearch:object
breadthFirstSearch.accelerator ​
readonlyaccelerator:"breadthFirstSearch"="breadthFirstSearch"
breadthFirstSearch.category ​
readonlycategory:"traversal"="traversal"
breadthFirstSearch.direction ​
readonlydirection:"any"="any"
breadthFirstSearch.dispatch ​
readonlydispatch:"breadthFirstSearch"="breadthFirstSearch"
breadthFirstSearch.fn ​
readonlyfn: (g,startNode,options) =>BfsResult=breadthFirstSearch
Breadth-first search over out-neighbours. Takes any AdjacencyView, so s.reverse() gives an in-neighbour BFS with no extra code.
Parameters ​
g ​
AdjacencyView
The adjacency to traverse
startNode ​
NodeRef
The node to start from: its index, or { id }
options? ​
BfsOptions = {}
Traversal options
Returns ​
The visit order, the parent array, the depth array and the visited count
breadthFirstSearch.inputs ​
readonlyinputs: readonly [AlgorithmInput]
breadthFirstSearch.name ​
readonlyname:"breadthFirstSearch"="breadthFirstSearch"
breadthFirstSearch.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
breadthFirstSearch.result ​
readonlyresult:"traversal"="traversal"
breadthFirstSearch.values ​
readonlyvalues: readonly ["depth","parent"]
breadthFirstSearch.weights ​
readonlyweights:"never"="never"
closenessCentrality ​
readonlyclosenessCentrality:object
closenessCentrality.accelerator ​
readonlyaccelerator:"closenessCentrality"="closenessCentrality"
closenessCentrality.category ​
readonlycategory:"centrality"="centrality"
closenessCentrality.direction ​
readonlydirection:"any"="any"
closenessCentrality.dispatch ​
readonlydispatch:"closenessCentrality"="closenessCentrality"
closenessCentrality.fn ​
readonlyfn: (s,options) =>ClosenessResult=closenessCentrality
Closeness centrality of every node: 1 / sum(distance to each reached node) by default, the legacy closenessCentrality (hops) and weightedClosenessCentrality (weighted: true) numbers. The weighted route reads the snapshot's f32 arc weights, so it matches legacy to f32 rounding; pass the f64 weights as weights for the exact legacy sums. As in legacy, a node's distance is final once it is searched from, so a negative weight gives the legacy (not the true shortest) distance and never loops. An unreached node adds nothing, and a node that reaches nothing scores 0.
SAMPLED (sources or k): the searches run from the sampled sources over the IN-arcs, so each search measures every node's distance TO that source, and each node's sums run over the sources it reaches (itself excluded) instead of over every node it reaches. Everything else is unchanged: the same formula is applied to those sums, with the same n - 1 under normalized and no extrapolation to the whole graph -- the UNSCALED rule sampled betweenness follows. So a sample of every node gives exactly the exact scores (up to the order floating-point sums are added in), and on a sample of k sources 1 / score is the summed distance to those k sources: multiply the plain score by k / n for the Eppstein-Wang estimate of the exact one. A harmonic score (normalized or not) sums reciprocal distances, so it needs the inverse, n / k; a normalized plain score divides by the number of sources reached, which already rescales it, so it needs no factor. On an undirected snapshot the in-arcs are the out-arcs. Two corners are measured from the source's side and so differ from an exact run even over a full sample: a weighted cutoff (the node just past it is admitted from the source's end of the path) and a negative weight (the legacy settle rule is not symmetric).
Parameters ​
s ​
GraphSnapshot
The snapshot
options? ​
ClosenessOptions = {}
Normalisation, harmonic form, cutoff, weights and sampling
Returns ​
One score per node index; iterations and sourcesUsed are the number of searches run
Throws ​
RangeError for a bad sources or k
closenessCentrality.inputs ​
readonlyinputs: readonly [] =[]
closenessCentrality.name ​
readonlyname:"closenessCentrality"="closenessCentrality"
closenessCentrality.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
closenessCentrality.result ​
readonlyresult:"node-scores"="node-scores"
closenessCentrality.values ​
readonlyvalues: readonly ["scores"]
closenessCentrality.weights ​
readonlyweights:"on-request"="on-request"
commonNeighborsForPairs ​
readonlycommonNeighborsForPairs:object
commonNeighborsForPairs.accelerator ​
readonlyaccelerator:null=null
commonNeighborsForPairs.category ​
readonlycategory:"link-prediction"="link-prediction"
commonNeighborsForPairs.direction ​
readonlydirection:"any"="any"
commonNeighborsForPairs.dispatch ​
readonlydispatch:null=null
commonNeighborsForPairs.fn ​
readonlyfn: (s,pairs,o) =>F64=commonNeighborsForPairs
Common-neighbour scores of the given pairs, in order.
Parameters ​
s ​
GraphSnapshot
The snapshot
pairs ​
The pairs
o? ​
Options
Returns ​
F64
One score per pair
commonNeighborsForPairs.inputs ​
readonlyinputs: readonly [AlgorithmInput]
commonNeighborsForPairs.name ​
readonlyname:"commonNeighborsForPairs"="commonNeighborsForPairs"
commonNeighborsForPairs.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
commonNeighborsForPairs.result ​
readonlyresult:"pair-scores"="pair-scores"
commonNeighborsForPairs.values ​
readonlyvalues: readonly [] =[]
commonNeighborsForPairs.weights ​
readonlyweights:"never"="never"
commonNeighborsPrediction ​
readonlycommonNeighborsPrediction:object
commonNeighborsPrediction.accelerator ​
readonlyaccelerator:null=null
commonNeighborsPrediction.category ​
readonlycategory:"link-prediction"="link-prediction"
commonNeighborsPrediction.direction ​
readonlydirection:"any"="any"
commonNeighborsPrediction.dispatch ​
readonlydispatch:"commonNeighborsPrediction"="commonNeighborsPrediction"
commonNeighborsPrediction.fn ​
readonlyfn: (s,o) =>LinkPredictionResult=commonNeighborsPrediction
Common-neighbour scores of every pair not already joined, ranked.
Parameters ​
s ​
GraphSnapshot
The snapshot
o? ​
Options
Returns ​
The pairs with a positive score, highest first
commonNeighborsPrediction.inputs ​
readonlyinputs: readonly [] =[]
commonNeighborsPrediction.name ​
readonlyname:"commonNeighborsPrediction"="commonNeighborsPrediction"
commonNeighborsPrediction.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
commonNeighborsPrediction.result ​
readonlyresult:"link-predictions"="link-predictions"
commonNeighborsPrediction.values ​
readonlyvalues: readonly [] =[]
commonNeighborsPrediction.weights ​
readonlyweights:"never"="never"
commonNeighborsScore ​
readonlycommonNeighborsScore:object
commonNeighborsScore.accelerator ​
readonlyaccelerator:null=null
commonNeighborsScore.category ​
readonlycategory:"link-prediction"="link-prediction"
commonNeighborsScore.direction ​
readonlydirection:"any"="any"
commonNeighborsScore.dispatch ​
readonlydispatch:null=null
commonNeighborsScore.fn ​
readonlyfn: (s,u,v,o) =>number=commonNeighborsScore
The number of distinct common neighbours of two nodes, by a merge of their sorted rows.
Parameters ​
s ​
GraphSnapshot
The snapshot
u ​
number
A node index
v ​
number
A node index
o? ​
Options
Returns ​
number
The count of distinct common neighbours
commonNeighborsScore.inputs ​
readonlyinputs: readonly [AlgorithmInput,AlgorithmInput]
commonNeighborsScore.name ​
readonlyname:"commonNeighborsScore"="commonNeighborsScore"
commonNeighborsScore.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
commonNeighborsScore.result ​
readonlyresult:"number"="number"
commonNeighborsScore.values ​
readonlyvalues: readonly [] =[]
commonNeighborsScore.weights ​
readonlyweights:"never"="never"
compareAdamicAdarWithCommonNeighbors ​
readonlycompareAdamicAdarWithCommonNeighbors:object
compareAdamicAdarWithCommonNeighbors.accelerator ​
readonlyaccelerator:null=null
compareAdamicAdarWithCommonNeighbors.category ​
readonlycategory:"link-prediction"="link-prediction"
compareAdamicAdarWithCommonNeighbors.direction ​
readonlydirection:"any"="any"
compareAdamicAdarWithCommonNeighbors.dispatch ​
readonlydispatch:null=null
compareAdamicAdarWithCommonNeighbors.fn ​
readonlyfn: (s,edges,nonEdges,o) =>object=compareAdamicAdarWithCommonNeighbors
Both evaluations on the same held-out sets. The common-neighbour side always intersects the two OUT rows, whatever directed says, as the legacy function of this name does; the Adamic-Adar side honours directed.
Parameters ​
s ​
GraphSnapshot
The training snapshot
edges ​
The held-out edges
nonEdges ​
The non-edges
o? ​
Options
Returns ​
object
The two sets of metrics
adamicAdar ​
adamicAdar:
LinkPredictionMetrics
commonNeighbors ​
commonNeighbors:
LinkPredictionMetrics
compareAdamicAdarWithCommonNeighbors.inputs ​
readonlyinputs: readonly [AlgorithmInput,AlgorithmInput]
compareAdamicAdarWithCommonNeighbors.name ​
readonlyname:"compareAdamicAdarWithCommonNeighbors"="compareAdamicAdarWithCommonNeighbors"
compareAdamicAdarWithCommonNeighbors.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
compareAdamicAdarWithCommonNeighbors.result ​
readonlyresult:"metrics"="metrics"
compareAdamicAdarWithCommonNeighbors.values ​
readonlyvalues: readonly [] =[]
compareAdamicAdarWithCommonNeighbors.weights ​
readonlyweights:"never"="never"
condensation ​
readonlycondensation:object
condensation.accelerator ​
readonlyaccelerator:null=null
condensation.category ​
readonlycategory:"components"="components"
condensation.direction ​
readonlydirection:"directed"="directed"
condensation.dispatch ​
readonlydispatch:null=null
condensation.fn ​
readonlyfn: (s,options) =>CondensationResult=condensation
The condensation of a directed graph: its strongly connected components, and the DAG with one node per component that contract() builds from their labels. The labels already run 0..k-1, so contract keeps them as block indices and the condensed node numbering is the component numbering.
Parameters ​
s ​
GraphSnapshot
A directed snapshot
options? ​
ArcOrderOption = {}
The neighbour order Tarjan tries arcs in, which fixes the component numbering
Returns ​
The components and the condensed graph
Throws ​
Error when the snapshot is undirected
condensation.inputs ​
readonlyinputs: readonly [] =[]
condensation.name ​
readonlyname:"condensation"="condensation"
condensation.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
condensation.result ​
readonlyresult:"partition"="partition"
condensation.values ​
readonlyvalues: readonly ["components.labels"]
condensation.weights ​
readonlyweights:"never"="never"
connectedComponents ​
readonlyconnectedComponents:object
connectedComponents.accelerator ​
readonlyaccelerator:"connectedComponents"="connectedComponents"
connectedComponents.category ​
readonlycategory:"components"="components"
connectedComponents.direction ​
readonlydirection:"undirected"="undirected"
connectedComponents.dispatch ​
readonlydispatch:"connectedComponents"="connectedComponents"
connectedComponents.fn ​
readonlyfn: (s) =>LabelResult=connectedComponents
Connected components of an UNDIRECTED snapshot.
Parameters ​
s ​
GraphSnapshot
An undirected snapshot
Returns ​
The partition
connectedComponents.inputs ​
readonlyinputs: readonly [] =[]
connectedComponents.name ​
readonlyname:"connectedComponents"="connectedComponents"
connectedComponents.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
connectedComponents.result ​
readonlyresult:"partition"="partition"
connectedComponents.values ​
readonlyvalues: readonly ["labels"]
connectedComponents.weights ​
readonlyweights:"never"="never"
degreeCentrality ​
readonlydegreeCentrality:object
degreeCentrality.accelerator ​
readonlyaccelerator:null=null
degreeCentrality.category ​
readonlycategory:"centrality"="centrality"
degreeCentrality.direction ​
readonlydirection:"any"="any"
degreeCentrality.dispatch ​
readonlydispatch:null=null
degreeCentrality.fn ​
readonlyfn: (s,options) =>F64=degreeCentrality
Degree centrality: each node's number of distinct neighbours (in, out, or both on a directed snapshot), optionally divided by n - 1. Equals the legacy degreeCentrality on every graph it can hold, and on a multigraph equals it on the same graph with each pair's parallel edges merged.
Parameters ​
s ​
GraphSnapshot
The snapshot
options? ​
Direction and normalisation
Returns ​
F64
One score per node index
degreeCentrality.inputs ​
readonlyinputs: readonly [] =[]
degreeCentrality.name ​
readonlyname:"degreeCentrality"="degreeCentrality"
degreeCentrality.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
degreeCentrality.result ​
readonlyresult:"node-scores"="node-scores"
degreeCentrality.values ​
readonlyvalues: readonly [""]
degreeCentrality.weights ​
readonlyweights:"never"="never"
degrees ​
readonlydegrees:object
degrees.accelerator ​
readonlyaccelerator:null=null
degrees.category ​
readonlycategory:"centrality"="centrality"
degrees.direction ​
readonlydirection:"any"="any"
degrees.dispatch ​
readonlydispatch:"degrees"="degrees"
degrees.fn ​
readonlyfn: (s) =>DegreesResult=degrees
Each node's edges counted from the edge list: every logical edge once at its declared source (out) and once at its declared target (in), so a self-loop counts once in each half. The orientation is the one each edge was declared with, on an undirected snapshot too, so in plus out is the degree counted once -- where the snapshot's own degree views give an undirected node its whole degree as both halves. Parallel edges each count; merge them first to count a pair once.
Parameters ​
s ​
GraphSnapshot
The snapshot
Returns ​
The two halves, one count per node index
degrees.inputs ​
readonlyinputs: readonly [] =[]
degrees.name ​
readonlyname:"degrees"="degrees"
degrees.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
degrees.result ​
readonlyresult:"node-scores"="node-scores"
degrees.values ​
readonlyvalues: readonly ["inDegree","outDegree"]
degrees.weights ​
readonlyweights:"never"="never"
depthFirstSearch ​
readonlydepthFirstSearch:object
depthFirstSearch.accelerator ​
readonlyaccelerator:null=null
depthFirstSearch.category ​
readonlycategory:"traversal"="traversal"
depthFirstSearch.direction ​
readonlydirection:"any"="any"
depthFirstSearch.dispatch ​
readonlydispatch:"depthFirstSearch"="depthFirstSearch"
depthFirstSearch.fn ​
readonlyfn: (g,startNode,options) =>DfsResult=depthFirstSearch
Depth-first search over out-neighbours, trying them in row (ascending index) order unless options.arcOrder gives another. Takes any AdjacencyView, so s.reverse() walks in-neighbours.
Parameters ​
g ​
AdjacencyView
The adjacency to traverse
startNode ​
NodeRef
The node to start from: its index, or { id }
options? ​
DfsOptions = {}
Traversal options
Returns ​
The visit order, the DFS tree as a parent array, the tree depths and the visited count
depthFirstSearch.inputs ​
readonlyinputs: readonly [AlgorithmInput]
depthFirstSearch.name ​
readonlyname:"depthFirstSearch"="depthFirstSearch"
depthFirstSearch.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
depthFirstSearch.result ​
readonlyresult:"traversal"="traversal"
depthFirstSearch.values ​
readonlyvalues: readonly ["depth","parent"]
depthFirstSearch.weights ​
readonlyweights:"never"="never"
dijkstra ​
readonlydijkstra:object
dijkstra.accelerator ​
readonlyaccelerator:"sssp"="sssp"
dijkstra.category ​
readonlycategory:"shortest-path"="shortest-path"
dijkstra.direction ​
readonlydirection:"any"="any"
dijkstra.dispatch ​
readonlydispatch:"sssp"="sssp"
dijkstra.fn ​
readonlyfn: (g,sourceNode,options) =>SsspResult=dijkstra
Dijkstra over an adjacency view, with the predecessor recorded as the relaxing ARC so a parallel edge on the path is identified exactly.
Parameters ​
g ​
AdjacencyView
The adjacency to search
sourceNode ​
NodeRef
The node to start from: its index, or { id }
options? ​
SsspOptions = {}
Cutoff and per-arc weight override
Returns ​
The distances, the predecessor arcs, and the two path accessors
dijkstra.inputs ​
readonlyinputs: readonly [AlgorithmInput]
dijkstra.name ​
readonlyname:"dijkstra"="dijkstra"
dijkstra.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
dijkstra.result ​
readonlyresult:"shortest-paths"="shortest-paths"
dijkstra.values ​
readonlyvalues: readonly ["dist","predArc"]
dijkstra.weights ​
readonlyweights:"always"="always"
directionOptimizedBfs ​
readonlydirectionOptimizedBfs:object
directionOptimizedBfs.accelerator ​
readonlyaccelerator:null=null
directionOptimizedBfs.category ​
readonlycategory:"traversal"="traversal"
directionOptimizedBfs.direction ​
readonlydirection:"any"="any"
directionOptimizedBfs.dispatch ​
readonlydispatch:null=null
directionOptimizedBfs.fn ​
readonlyfn: (s,sourceNode,options) =>BfsResult=directionOptimizedBfs
Direction-optimising breadth-first search (Beamer, Asanovic and Patterson, SC'12): a top-down step expands the frontier's out-arcs, a bottom-up step has every unvisited node look for a frontier node among its in-neighbours over s.reverse(), and the search switches between the two by the arcs each would read. Both steps give a node the LOWEST-index frontier node that reaches it as its parent, so the result does not depend on which steps ran: depth equals breadthFirstSearch's, and order lists the visited nodes level by level, ascending within a level.
Parameters ​
s ​
GraphSnapshot
The snapshot to traverse
sourceNode ​
NodeRef
The node to start from: its index, or { id }
options? ​
DirectionOptimizedBfsOptions = {}
The switching thresholds
Returns ​
The visit order, the parent array, the depth array and the visited count
directionOptimizedBfs.inputs ​
readonlyinputs: readonly [AlgorithmInput]
directionOptimizedBfs.name ​
readonlyname:"directionOptimizedBfs"="directionOptimizedBfs"
directionOptimizedBfs.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
directionOptimizedBfs.result ​
readonlyresult:"traversal"="traversal"
directionOptimizedBfs.values ​
readonlyvalues: readonly ["depth","parent"]
directionOptimizedBfs.weights ​
readonlyweights:"never"="never"
edgeBetweennessCentrality ​
readonlyedgeBetweennessCentrality:object
edgeBetweennessCentrality.accelerator ​
readonlyaccelerator:"edgeBetweennessCentrality"="edgeBetweennessCentrality"
edgeBetweennessCentrality.category ​
readonlycategory:"centrality"="centrality"
edgeBetweennessCentrality.direction ​
readonlydirection:"any"="any"
edgeBetweennessCentrality.dispatch ​
readonlydispatch:"edgeBetweennessCentrality"="edgeBetweennessCentrality"
edgeBetweennessCentrality.fn ​
readonlyfn: (s,options) =>EdgeScoresResult=edgeBetweennessCentrality
Edge betweenness centrality by Brandes' algorithm, one score per logical edge.
On a directed snapshot an edge's score equals the legacy edgeBetweennessCentrality value keyed "source-target". On an undirected one it is the number of pairs whose shortest paths cross the edge in either direction, which is the SUM of the two values the legacy function reports under "u-v" and "v-u" (each of those holds half), and is the score the WebGPU accelerator returns.
Parameters ​
s ​
GraphSnapshot
The snapshot
options? ​
Normalisation, sampling and the alive-edge mask
Returns ​
One score per logical edge
Throws ​
RangeError for a bad sources or k, or an alive mask shorter than the edge count
edgeBetweennessCentrality.inputs ​
readonlyinputs: readonly [] =[]
edgeBetweennessCentrality.name ​
readonlyname:"edgeBetweennessCentrality"="edgeBetweennessCentrality"
edgeBetweennessCentrality.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
edgeBetweennessCentrality.result ​
readonlyresult:"edge-scores"="edge-scores"
edgeBetweennessCentrality.values ​
readonlyvalues: readonly ["scores"]
edgeBetweennessCentrality.weights ​
readonlyweights:"never"="never"
eigenvectorCentrality ​
readonlyeigenvectorCentrality:object
eigenvectorCentrality.accelerator ​
readonlyaccelerator:"eigenvectorCentrality"="eigenvectorCentrality"
eigenvectorCentrality.category ​
readonlycategory:"centrality"="centrality"
eigenvectorCentrality.direction ​
readonlydirection:"any"="any"
eigenvectorCentrality.dispatch ​
readonlydispatch:"eigenvectorCentrality"="eigenvectorCentrality"
eigenvectorCentrality.fn ​
readonlyfn: (s,o) =>EigenvectorResult=eigenvectorCentrality
Eigenvector centrality by power iteration on (A + I), as networkx does: the identity shift leaves the eigenvectors alone and makes the largest eigenvalue the only one of largest magnitude, so a bipartite or periodic graph converges instead of oscillating. Each node's neighbours are counted once however many parallel edges join them, as in the legacy neighbour sets. When the relation has no cycle (no arcs, or a directed acyclic graph) its matrix is nilpotent and every score is exactly 0.
Parameters ​
s ​
GraphSnapshot
The snapshot
o? ​
EigenvectorOptions = {}
Algorithm options
Returns ​
The scores, the iteration count and the convergence flag
Throws ​
When maxIterations passes do not meet the tolerance
eigenvectorCentrality.inputs ​
readonlyinputs: readonly [] =[]
eigenvectorCentrality.name ​
readonlyname:"eigenvectorCentrality"="eigenvectorCentrality"
eigenvectorCentrality.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
eigenvectorCentrality.result ​
readonlyresult:"node-scores"="node-scores"
eigenvectorCentrality.values ​
readonlyvalues: readonly ["scores"]
eigenvectorCentrality.weights ​
readonlyweights:"never"="never"
evaluateAdamicAdar ​
readonlyevaluateAdamicAdar:object
evaluateAdamicAdar.accelerator ​
readonlyaccelerator:null=null
evaluateAdamicAdar.category ​
readonlycategory:"link-prediction"="link-prediction"
evaluateAdamicAdar.direction ​
readonlydirection:"any"="any"
evaluateAdamicAdar.dispatch ​
readonlydispatch:null=null
evaluateAdamicAdar.fn ​
readonlyfn: (s,edges,nonEdges,o) =>LinkPredictionMetrics=evaluateAdamicAdar
Ranking metrics of the Adamic-Adar score on held-out edges against non-edges.
Parameters ​
s ​
GraphSnapshot
The training snapshot
edges ​
The held-out edges
nonEdges ​
The non-edges
o? ​
Options
Returns ​
The metrics
evaluateAdamicAdar.inputs ​
readonlyinputs: readonly [AlgorithmInput,AlgorithmInput]
evaluateAdamicAdar.name ​
readonlyname:"evaluateAdamicAdar"="evaluateAdamicAdar"
evaluateAdamicAdar.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
evaluateAdamicAdar.result ​
readonlyresult:"metrics"="metrics"
evaluateAdamicAdar.values ​
readonlyvalues: readonly [] =[]
evaluateAdamicAdar.weights ​
readonlyweights:"never"="never"
evaluateCommonNeighbors ​
readonlyevaluateCommonNeighbors:object
evaluateCommonNeighbors.accelerator ​
readonlyaccelerator:null=null
evaluateCommonNeighbors.category ​
readonlycategory:"link-prediction"="link-prediction"
evaluateCommonNeighbors.direction ​
readonlydirection:"any"="any"
evaluateCommonNeighbors.dispatch ​
readonlydispatch:null=null
evaluateCommonNeighbors.fn ​
readonlyfn: (s,edges,nonEdges,o) =>LinkPredictionMetrics=evaluateCommonNeighbors
Ranking metrics of the common-neighbour score on held-out edges against non-edges.
Parameters ​
s ​
GraphSnapshot
The training snapshot
edges ​
The held-out edges
nonEdges ​
The non-edges
o? ​
Options
Returns ​
The metrics
evaluateCommonNeighbors.inputs ​
readonlyinputs: readonly [AlgorithmInput,AlgorithmInput]
evaluateCommonNeighbors.name ​
readonlyname:"evaluateCommonNeighbors"="evaluateCommonNeighbors"
evaluateCommonNeighbors.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
evaluateCommonNeighbors.result ​
readonlyresult:"metrics"="metrics"
evaluateCommonNeighbors.values ​
readonlyvalues: readonly [] =[]
evaluateCommonNeighbors.weights ​
readonlyweights:"never"="never"
findAllIsomorphisms ​
readonlyfindAllIsomorphisms:object
findAllIsomorphisms.accelerator ​
readonlyaccelerator:null=null
findAllIsomorphisms.category ​
readonlycategory:"structure"="structure"
findAllIsomorphisms.direction ​
readonlydirection:"any"="any"
findAllIsomorphisms.dispatch ​
readonlydispatch:null=null
findAllIsomorphisms.fn ​
readonlyfn: (s1,s2,options) =>U32[] =findAllIsomorphisms
Every isomorphism from s1 to s2, with the neighbourhood rules of isGraphIsomorphic. Two empty graphs have one, the empty mapping.
Parameters ​
s1 ​
GraphSnapshot
The first graph
s2 ​
GraphSnapshot
The second graph
options? ​
IsomorphismOptions = {}
Node and edge match predicates
Returns ​
U32[]
One array per isomorphism: the image in s2 of every node of s1
findAllIsomorphisms.inputs ​
readonlyinputs: readonly [{kind:"graph";name:"other"; }]
findAllIsomorphisms.name ​
readonlyname:"findAllIsomorphisms"="findAllIsomorphisms"
findAllIsomorphisms.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
findAllIsomorphisms.result ​
readonlyresult:"isomorphisms"="isomorphisms"
findAllIsomorphisms.values ​
readonlyvalues: readonly [] =[]
findAllIsomorphisms.weights ​
readonlyweights:"never"="never"
getTopAdamicAdarCandidatesForNode ​
readonlygetTopAdamicAdarCandidatesForNode:object
getTopAdamicAdarCandidatesForNode.accelerator ​
readonlyaccelerator:null=null
getTopAdamicAdarCandidatesForNode.category ​
readonlycategory:"link-prediction"="link-prediction"
getTopAdamicAdarCandidatesForNode.direction ​
readonlydirection:"any"="any"
getTopAdamicAdarCandidatesForNode.dispatch ​
readonlydispatch:null=null
getTopAdamicAdarCandidatesForNode.fn ​
readonlyfn: (s,uNode,o) =>LinkPredictionResult=getTopAdamicAdarCandidatesForNode
One node's best Adamic-Adar candidates, ranked.
Parameters ​
s ​
GraphSnapshot
The snapshot
uNode ​
NodeRef
The node: its index, or { id }
o? ​
CandidateOptions = {}
Options
Returns ​
(u, v) pairs with a positive score, highest first
getTopAdamicAdarCandidatesForNode.inputs ​
readonlyinputs: readonly [AlgorithmInput]
getTopAdamicAdarCandidatesForNode.name ​
readonlyname:"getTopAdamicAdarCandidatesForNode"="getTopAdamicAdarCandidatesForNode"
getTopAdamicAdarCandidatesForNode.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
getTopAdamicAdarCandidatesForNode.result ​
readonlyresult:"link-predictions"="link-predictions"
getTopAdamicAdarCandidatesForNode.values ​
readonlyvalues: readonly [] =[]
getTopAdamicAdarCandidatesForNode.weights ​
readonlyweights:"never"="never"
getTopCandidatesForNode ​
readonlygetTopCandidatesForNode:object
getTopCandidatesForNode.accelerator ​
readonlyaccelerator:null=null
getTopCandidatesForNode.category ​
readonlycategory:"link-prediction"="link-prediction"
getTopCandidatesForNode.direction ​
readonlydirection:"any"="any"
getTopCandidatesForNode.dispatch ​
readonlydispatch:null=null
getTopCandidatesForNode.fn ​
readonlyfn: (s,uNode,o) =>LinkPredictionResult=getTopCandidatesForNode
One node's best common-neighbour candidates, ranked.
Parameters ​
s ​
GraphSnapshot
The snapshot
uNode ​
NodeRef
The node: its index, or { id }
o? ​
CandidateOptions = {}
Options
Returns ​
(u, v) pairs with a positive score, highest first
getTopCandidatesForNode.inputs ​
readonlyinputs: readonly [AlgorithmInput]
getTopCandidatesForNode.name ​
readonlyname:"getTopCandidatesForNode"="getTopCandidatesForNode"
getTopCandidatesForNode.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
getTopCandidatesForNode.result ​
readonlyresult:"link-predictions"="link-predictions"
getTopCandidatesForNode.values ​
readonlyvalues: readonly [] =[]
getTopCandidatesForNode.weights ​
readonlyweights:"never"="never"
girvanNewman ​
readonlygirvanNewman:object
girvanNewman.accelerator ​
readonlyaccelerator:null=null
girvanNewman.category ​
readonlycategory:"community"="community"
girvanNewman.direction ​
readonlydirection:"undirected"="undirected"
girvanNewman.dispatch ​
readonlydispatch:"girvanNewman"="girvanNewman"
girvanNewman.fn ​
readonlyfn: (s,options) =>GirvanNewmanResult=girvanNewman
Girvan-Newman divisive community detection (PNAS 99:7821, 2002) over an undirected snapshot: repeatedly delete every edge whose betweenness is the maximum (within 1e-10), recording the connected components after each round as one level of a dendrogram.
Edges are deleted by clearing bits of an alive mask over logical edges that the edge betweenness reads, so no graph is rebuilt. Betweenness is unweighted, as in the legacy function; a pair of nodes joined by parallel edges is one neighbour relation whose share goes to the first alive parallel, so the parallels are deleted one round at a time. Modularity reads the edge weights, the exact f64 ones when the snapshot keeps them, with graph-format's weightedDegree() rule (a self-loop counts twice); on a graph without self-loops it equals the legacy function's.
The run stops when no edge is left, after maxIterations rounds, when a level has maxCommunities communities of at least minCommunitySize nodes, or when that count reaches the node count. Every level is returned whole: the legacy function drops communities below minCommunitySize from the lists it returns, which a caller does with levels[i] and a size count.
Parameters ​
s ​
GraphSnapshot
An undirected snapshot
options? ​
GirvanNewmanOptions = {}
Stopping rules
Returns ​
The levels and their modularity
Throws ​
Error on a directed snapshot
girvanNewman.inputs ​
readonlyinputs: readonly [] =[]
girvanNewman.name ​
readonlyname:"girvanNewman"="girvanNewman"
girvanNewman.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
girvanNewman.result ​
readonlyresult:"hierarchy"="hierarchy"
girvanNewman.values ​
readonlyvalues: readonly [] =[]
girvanNewman.weights ​
readonlyweights:"always"="always"
greedyBipartiteMatching ​
readonlygreedyBipartiteMatching:object
greedyBipartiteMatching.accelerator ​
readonlyaccelerator:null=null
greedyBipartiteMatching.category ​
readonlycategory:"matching"="matching"
greedyBipartiteMatching.direction ​
readonlydirection:"any"="any"
greedyBipartiteMatching.dispatch ​
readonlydispatch:null=null
greedyBipartiteMatching.fn ​
readonlyfn: (s,options) =>BipartiteMatchingResult=greedyBipartiteMatching
Greedy maximal bipartite matching: every left node in index order takes its first unmatched right neighbour in row order (the lowest index; out-arcs before in-arcs). The size depends on those orders, so it can differ from the legacy function, which visits left nodes in its colouring BFS order and neighbours in insertion order; it is at least half the maximum.
Parameters ​
s ​
GraphSnapshot
The snapshot
options? ​
The two sides, and which arcs to follow on a directed snapshot
Returns ​
The partner of every matched left node and the number of pairs
Throws ​
Error when the sides are inferred and the graph is not bipartite
greedyBipartiteMatching.inputs ​
readonlyinputs: readonly [] =[]
greedyBipartiteMatching.name ​
readonlyname:"greedyBipartiteMatching"="greedyBipartiteMatching"
greedyBipartiteMatching.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
greedyBipartiteMatching.result ​
readonlyresult:"matching"="matching"
greedyBipartiteMatching.values ​
readonlyvalues: readonly ["matching"]
greedyBipartiteMatching.weights ​
readonlyweights:"never"="never"
grsbm ​
readonlygrsbm:object
grsbm.accelerator ​
readonlyaccelerator:null=null
grsbm.category ​
readonlycategory:"community"="community"
grsbm.direction ​
readonlydirection:"any"="any"
grsbm.dispatch ​
readonlydispatch:null=null
grsbm.fn ​
readonlyfn: (s,options) =>GrsbmResult=grsbm
GRSBM over a snapshot: recursive spectral bisection. Each cluster's Laplacian (over the arcs between its members, self-loops left out) gives an approximate Fiedler vector; members are sorted by it and cut at the point between 20% and 80% that maximises modularity; the split is kept only when it raises the modularity of the whole partition (every other cluster held as it is). The change is the two halves' community terms minus the cluster's, so scoring a cut costs the arcs of the cluster alone. Clusters are split breadth first. The cut and the modularity arithmetic are the legacy grsbm's; the acceptance rule is not (legacy compared the two halves' terms alone with the parent split's, and kept a split that lowered that by up to 0.01, issue #960), nor is the bisection vector, because legacy's iteration converged to the eigenvector of the Laplacian's largest eigenvalue rather than the Fiedler vector. The port draws from its own generator instead of replacing Math.random.
Modularity uses weightedDegree() (a self-loop counts twice) against totalWeight(), or degree() against the edge count when unweighted. A self-loop follows the standard convention (left out of the Laplacian, twice in the degree), so on a graph with self-loops the result differs from legacy's, which scores the whole graph as one community above 0. On a directed graph the unweighted path keeps legacy's halving of internal arcs; the weighted path does not, so there the whole graph as one community scores 0. Weights must be finite and non-negative (RangeError).
Parameters ​
s ​
GraphSnapshot
The snapshot
options? ​
GrsbmOptions = {}
Returns ​
The leaf partition and the hierarchy
grsbm.inputs ​
readonlyinputs: readonly [] =[]
grsbm.name ​
readonlyname:"grsbm"="grsbm"
grsbm.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
grsbm.result ​
readonlyresult:"partition"="partition"
grsbm.values ​
readonlyvalues: readonly ["labels"]
grsbm.weights ​
readonlyweights:"by-default"="by-default"
hasCycle ​
readonlyhasCycle:object
hasCycle.accelerator ​
readonlyaccelerator:null=null
hasCycle.category ​
readonlycategory:"structure"="structure"
hasCycle.direction ​
readonlydirection:"any"="any"
hasCycle.dispatch ​
readonlydispatch:null=null
hasCycle.fn ​
readonlyfn: (g) =>boolean=hasCycle
Whether the graph has a cycle. Directed: a DFS back arc. Undirected: an edge closing a loop in a union-find forest. A self-loop is a cycle; a parallel pair is not -- the graph is read as simple, the adjacent-skip idiom of graph-format design section 3.5.
Parameters ​
g ​
AdjacencyView
The adjacency to test
Returns ​
boolean
True when a cycle exists
hasCycle.inputs ​
readonlyinputs: readonly [] =[]
hasCycle.name ​
readonlyname:"hasCycle"="hasCycle"
hasCycle.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
hasCycle.result ​
readonlyresult:"boolean"="boolean"
hasCycle.values ​
readonlyvalues: readonly [] =[]
hasCycle.weights ​
readonlyweights:"never"="never"
hierarchicalClustering ​
readonlyhierarchicalClustering:object
hierarchicalClustering.accelerator ​
readonlyaccelerator:null=null
hierarchicalClustering.category ​
readonlycategory:"community"="community"
hierarchicalClustering.direction ​
readonlydirection:"any"="any"
hierarchicalClustering.dispatch ​
readonlydispatch:null=null
hierarchicalClustering.fn ​
readonlyfn: (s,options) =>HierarchicalResult=hierarchicalClustering
Agglomerative hierarchical clustering over hop distances, the index-based port of the legacy hierarchicalClustering: every node starts as its own cluster and the closest pair is merged until one cluster remains or no remaining pair is linked by a path.
The distance between two nodes is the BFS hop count over out-arcs (weights are ignored, as the legacy function ignores them); an unreachable pair has none and is left out of every linkage. The distance between two clusters over the member pairs that have one is their minimum (single), maximum (complete), mean (average), or mean times |A| |B| / (|A| + |B|) (ward). A pair with no connected member pair is never merged. On a directed snapshot the distance is taken in one direction, and which one follows the legacy function exactly, as do the tie-breaks: the merges, their order and their distances equal the legacy function's.
One difference in what the result offers: cut descends into every root of a disconnected snapshot, where the legacy clusters map lists all nodes as one cluster at every height (its forest node has no children, so its cut never splits it).
O(n^2) memory for the distance aggregates and O(n^3) time for the pair search; the legacy function is the same order with Map and Set per step.
Parameters ​
s ​
AdjacencyView
Any snapshot or adjacency view
options? ​
HierarchicalOptions = {}
The linkage
Returns ​
The dendrogram
hierarchicalClustering.inputs ​
readonlyinputs: readonly [] =[]
hierarchicalClustering.name ​
readonlyname:"hierarchicalClustering"="hierarchicalClustering"
hierarchicalClustering.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
hierarchicalClustering.result ​
readonlyresult:"hierarchy"="hierarchy"
hierarchicalClustering.values ​
readonlyvalues: readonly [] =[]
hierarchicalClustering.weights ​
readonlyweights:"never"="never"
hits ​
readonlyhits:object
hits.accelerator ​
readonlyaccelerator:"hits"="hits"
hits.category ​
readonlycategory:"centrality"="centrality"
hits.direction ​
readonlydirection:"any"="any"
hits.dispatch ​
readonlydispatch:"hits"="hits"
hits.fn ​
readonlyfn: (s,o) =>HitsResult=hits
HITS by alternating power iteration: an authority sums the hub scores of the nodes pointing at it, a hub sums the authority scores of the nodes it points at, and both vectors are L2-normalized every round. On an undirected snapshot the two adjacencies are the same array, so the hubs and the authorities coincide.
Parameters ​
s ​
GraphSnapshot
The snapshot
o? ​
HitsOptions = {}
Algorithm options
Returns ​
The two score vectors, the iteration count and the convergence flag
hits.inputs ​
readonlyinputs: readonly [] =[]
hits.name ​
readonlyname:"hits"="hits"
hits.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
hits.result ​
readonlyresult:"node-scores"="node-scores"
hits.values ​
readonlyvalues: readonly ["hubs","authorities"]
hits.weights ​
readonlyweights:"on-request"="on-request"
isBipartite ​
readonlyisBipartite:object
isBipartite.accelerator ​
readonlyaccelerator:null=null
isBipartite.category ​
readonlycategory:"structure"="structure"
isBipartite.direction ​
readonlydirection:"any"="any"
isBipartite.dispatch ​
readonlydispatch:null=null
isBipartite.fn ​
readonlyfn: (s,options) =>BipartiteResult=isBipartite
Two-colour the graph by BFS, one BFS per component in index order. A self-loop makes a graph non-bipartite; parallel arcs change nothing. Arc direction plays no part in bipartiteness, so on a directed snapshot every node is coloured against its in-neighbours (over s.reverse()) as well as its out-neighbours. Following out-arcs only -- as the legacy bipartitePartition does -- makes the answer depend on node order: with the single arc a -> b, a walk that starts at b never meets a, colours it as a new root and then finds the arc inside one side. options.arcs: "out" gives that legacy answer.
Parameters ​
s ​
GraphSnapshot
The snapshot to test
options? ​
BipartiteOptions = {}
Which arcs to follow on a directed snapshot
Returns ​
Whether the graph is bipartite and, when it is, its two sides
isBipartite.inputs ​
readonlyinputs: readonly [] =[]
isBipartite.name ​
readonlyname:"isBipartite"="isBipartite"
isBipartite.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
isBipartite.result ​
readonlyresult:"bipartition"="bipartition"
isBipartite.values ​
readonlyvalues: readonly [] =[]
isBipartite.weights ​
readonlyweights:"never"="never"
isGraphIsomorphic ​
readonlyisGraphIsomorphic:object
isGraphIsomorphic.accelerator ​
readonlyaccelerator:null=null
isGraphIsomorphic.category ​
readonlycategory:"structure"="structure"
isGraphIsomorphic.direction ​
readonlydirection:"any"="any"
isGraphIsomorphic.dispatch ​
readonlydispatch:null=null
isGraphIsomorphic.fn ​
readonlyfn: (s1,s2,options) =>IsomorphismResult=isGraphIsomorphic
Whether two snapshots are isomorphic, by a VF2-style search. Neighbourhoods are simple-graph: parallel arcs count once and a self-loop maps only to a self-loop. On a directed snapshot both arc directions are preserved.
Parameters ​
s1 ​
GraphSnapshot
The first graph
s2 ​
GraphSnapshot
The second graph
options? ​
IsomorphismOptions = {}
Node and edge match predicates
Returns ​
Whether an isomorphism exists and, when it does, the first one found
isGraphIsomorphic.inputs ​
readonlyinputs: readonly [{kind:"graph";name:"other"; }]
isGraphIsomorphic.name ​
readonlyname:"isGraphIsomorphic"="isGraphIsomorphic"
isGraphIsomorphic.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
isGraphIsomorphic.result ​
readonlyresult:"isomorphism"="isomorphism"
isGraphIsomorphic.values ​
readonlyvalues: readonly [] =[]
isGraphIsomorphic.weights ​
readonlyweights:"never"="never"
kargerMinCut ​
readonlykargerMinCut:object
kargerMinCut.accelerator ​
readonlyaccelerator:null=null
kargerMinCut.category ​
readonlycategory:"flow"="flow"
kargerMinCut.direction ​
readonlydirection:"any"="any"
kargerMinCut.dispatch ​
readonlydispatch:"kargerMinCut"="kargerMinCut"
kargerMinCut.fn ​
readonlyfn: (s,options) =>MinCutResult=kargerMinCut
Global minimum cut by Karger's randomised contraction, reading every edge as undirected. Each trial unions the endpoints of the edges in a seeded random order (an IntUnionFind, no graph is contracted) until two groups remain, so every edge is equally likely to be contracted next whatever its weight; the trial's cut is the weight of the edges between the two groups. A graph with more than two components gives a cut of 0 with node 0's component on side. O(m alpha(n)) per trial.
Parameters ​
s ​
GraphSnapshot
The snapshot
options? ​
KargerOptions = {}
Trials, seed and weight override
Returns ​
The lightest cut found; with fewer than two nodes the value is 0 and side is empty
Throws ​
RangeError when randomSeed is not an integer or iterations is not a positive integer
kargerMinCut.inputs ​
readonlyinputs: readonly [] =[]
kargerMinCut.name ​
readonlyname:"kargerMinCut"="kargerMinCut"
kargerMinCut.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
kargerMinCut.result ​
readonlyresult:"cut"="cut"
kargerMinCut.values ​
readonlyvalues: readonly [] =[]
kargerMinCut.weights ​
readonlyweights:"always"="always"
katzCentrality ​
readonlykatzCentrality:object
katzCentrality.accelerator ​
readonlyaccelerator:"katzCentrality"="katzCentrality"
katzCentrality.category ​
readonlycategory:"centrality"="centrality"
katzCentrality.direction ​
readonlydirection:"any"="any"
katzCentrality.dispatch ​
readonlydispatch:"katzCentrality"="katzCentrality"
katzCentrality.fn ​
readonlyfn: (s,o) =>KatzResult=katzCentrality
Katz centrality by power iteration over incoming arcs: x[v] = alpha * sum(x[u] for u -> v) + beta.
reverse() supplies the in-arcs, and is the forward adjacency itself on an undirected snapshot, so one loop serves both directions exactly as the legacy function's in-neighbours / neighbours split does.
Parameters ​
s ​
GraphSnapshot
The snapshot
o? ​
KatzOptions = {}
Algorithm options
Returns ​
The scores, the iteration count and the convergence flag
katzCentrality.inputs ​
readonlyinputs: readonly [] =[]
katzCentrality.name ​
readonlyname:"katzCentrality"="katzCentrality"
katzCentrality.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
katzCentrality.result ​
readonlyresult:"node-scores"="node-scores"
katzCentrality.values ​
readonlyvalues: readonly ["scores"]
katzCentrality.weights ​
readonlyweights:"on-request"="on-request"
kCoreDecomposition ​
readonlykCoreDecomposition:object
kCoreDecomposition.accelerator ​
readonlyaccelerator:"kCoreDecomposition"="kCoreDecomposition"
kCoreDecomposition.category ​
readonlycategory:"components"="components"
kCoreDecomposition.direction ​
readonlydirection:"undirected"="undirected"
kCoreDecomposition.dispatch ​
readonlydispatch:"kCoreDecomposition"="kCoreDecomposition"
kCoreDecomposition.fn ​
readonlyfn: (s) =>CorenessResult=kCoreDecomposition
Core number of every node, by the Batagelj-Zaversnik bucket peel: O(nodes + arcs), no priority queue and no per-node object.
Core numbers are defined on a SIMPLE graph, so a parallel arc counts once (parallels are adjacent within a row, invariant I4) and a self-loop does not count at all, as NetworkX defines it. The legacy implementation's adjacency-of-sets counts a self-loop as one neighbour, so there a node with a self-loop can sit higher, and so can a node without one whose core leaned on self-looped neighbours. The result equals the legacy function's on the graph with every self-loop removed.
Parameters ​
s ​
GraphSnapshot
An undirected snapshot
Returns ​
The core numbers, the largest of them, and the lazy grouping
kCoreDecomposition.inputs ​
readonlyinputs: readonly [] =[]
kCoreDecomposition.name ​
readonlyname:"kCoreDecomposition"="kCoreDecomposition"
kCoreDecomposition.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
kCoreDecomposition.result ​
readonlyresult:"node-scores"="node-scores"
kCoreDecomposition.values ​
readonlyvalues: readonly ["coreness"]
kCoreDecomposition.weights ​
readonlyweights:"never"="never"
kruskalMST ​
readonlykruskalMST:object
kruskalMST.accelerator ​
readonlyaccelerator:"minimumSpanningTree"="minimumSpanningTree"
kruskalMST.category ​
readonlycategory:"spanning-tree"="spanning-tree"
kruskalMST.direction ​
readonlydirection:"any"="any"
kruskalMST.dispatch ​
readonlydispatch:"minimumSpanningTree"="minimumSpanningTree"
kruskalMST.fn ​
readonlyfn: (s,o) =>MstResult=kruskalMST
Kruskal's minimum spanning forest over logical edges.
The sort is a comparator over an f64 key array, tie-broken by edge index, rather than the design's radix sort on the f32 bit pattern (plan departure DEP-8A-D): the tie-break makes it stable by construction and it has to serve both the f32 arc array and an f64 override.
Parameters ​
s ​
GraphSnapshot
The snapshot
o? ​
MstOptions = {}
The optional per-arc weight override
Returns ​
The accepted edge indices and their total weight
kruskalMST.inputs ​
readonlyinputs: readonly [] =[]
kruskalMST.name ​
readonlyname:"kruskalMST"="kruskalMST"
kruskalMST.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
kruskalMST.result ​
readonlyresult:"spanning-tree"="spanning-tree"
kruskalMST.values ​
readonlyvalues: readonly [] =[]
kruskalMST.weights ​
readonlyweights:"always"="always"
labelPropagation ​
readonlylabelPropagation:object
labelPropagation.accelerator ​
readonlyaccelerator:"labelPropagation"="labelPropagation"
labelPropagation.category ​
readonlycategory:"community"="community"
labelPropagation.direction ​
readonlydirection:"any"="any"
labelPropagation.dispatch ​
readonlydispatch:"labelPropagation"="labelPropagation"
labelPropagation.fn ​
readonlyfn: (s,options) =>LabelPropagationResult=labelPropagation
Community detection by fast label propagation (FLPA; Traag and Subelj, Sci. Rep. 13:2701, 2023, Algorithm 3), the queue-driven form of Raghavan, Albert and Kumara's asynchronous label propagation.
Every node starts in its own community. Nodes are visited from a queue that starts as a seeded shuffle of all of them; a visited node adopts the label with the largest summed arc weight among its neighbours, drawing uniformly at random among tied labels (its current label included, with no priority). When its label changes, every neighbour holding a different label is queued again. The run ends when the queue empties -- then every node's label is dominant among its neighbours and converged is true -- or after maxIterations * nodeCount node visits.
Conventions: self-loops are skipped; parallel arcs are summed; on a directed snapshot a node's neighbours are its out-arcs AND its in-arcs, so a reciprocal pair counts twice. With weighted: false each distinct neighbour votes once. A negative, NaN or infinite weight throws. Votes use the exact f64 weights when the snapshot keeps them (as toSnapshot does for a legacy graph whose weights are not all f32-exact), so a weight rounded in the f32 arc array never decides a vote.
One randomSeed gives one result, bit for bit. The partitions differ from the legacy labelPropagation for the same seed: the random stream and the stop rule are different (design design/algorithms/label-propagation-indexed-port-design.md, section 3).
Parameters ​
s ​
GraphSnapshot
Any snapshot
options? ​
Work cap, seed and weighting
Returns ​
The partition, the full-sweep equivalents run, and whether the work queue emptied
labelPropagation.inputs ​
readonlyinputs: readonly [] =[]
labelPropagation.name ​
readonlyname:"labelPropagation"="labelPropagation"
labelPropagation.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
labelPropagation.result ​
readonlyresult:"partition"="partition"
labelPropagation.values ​
readonlyvalues: readonly ["labels"]
labelPropagation.weights ​
readonlyweights:"by-default"="by-default"
labelPropagationSemiSupervised ​
readonlylabelPropagationSemiSupervised:object
labelPropagationSemiSupervised.accelerator ​
readonlyaccelerator:null=null
labelPropagationSemiSupervised.category ​
readonlycategory:"community"="community"
labelPropagationSemiSupervised.direction ​
readonlydirection:"any"="any"
labelPropagationSemiSupervised.dispatch ​
readonlydispatch:null=null
labelPropagationSemiSupervised.fn ​
readonlyfn: (s,seeds,options) =>LabelPropagationResult=labelPropagationSemiSupervised
Semi-supervised label propagation: the FLPA kernel of labelPropagation with some nodes held at a given label (the initial / fixed inputs of igraph's community_label_propagation).
A seeded node keeps its label for the whole run and is never visited. Every other node starts UNLABELED, as a negative initial value means in igraph: it casts no vote, and on a visit it takes the label with the largest summed vote among its labeled neighbours, drawing uniformly among tied labels (its current one included), or stays unlabeled while it has none. So seed labels spread outward from the seeds, and every node joined to a seed by a path of voting arcs (any arc but a self-loop, with a positive weight when weighted) ends with some seed's label once the run converges. Seeds that share a label share one community from the start, and seeds with different labels are never merged, so in the result two seeds carry the same label exactly when their seed labels are equal.
Nodes no seed label reached -- a component with no seed, or a node cut off by the work cap -- end as igraph labels them: each connected group of them (joined by voting arcs) becomes a community of its own, so every node gets a label and the result is an ordinary partition. To tell those groups from the spread labels, compare labels[u] with the labels of the seed nodes. With no seed at all the result is one community per connected component.
The result is renumbered to 0..count-1 in first-seen node order like every other partition here, so a seed label's VALUE is not kept: read a seed's community through labels[seedNode].
converged is true when the work queue emptied, or when maxIterations is 0 and the starting labels already are dominant: no unseeded node has a labeled neighbour whose label outvotes its own (an unlabeled node next to a labeled one is not settled). Seeded nodes are not held to that.
Parameters ​
s ​
GraphSnapshot
Any snapshot
seeds ​
U32
One entry per node: its fixed label, any value but INVALID_INDEX, or INVALID_INDEX for a node free to move
options? ​
Work cap, seed of the visit order and weighting
Returns ​
The partition, the full-sweep equivalents run, and whether the work queue emptied
Throws ​
RangeError when seeds is not one entry per node, or for a bad option or weight
labelPropagationSemiSupervised.inputs ​
readonlyinputs: readonly [{kind:"seed-labels";name:"seeds"; }]
labelPropagationSemiSupervised.name ​
readonlyname:"labelPropagationSemiSupervised"="labelPropagationSemiSupervised"
labelPropagationSemiSupervised.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
labelPropagationSemiSupervised.result ​
readonlyresult:"partition"="partition"
labelPropagationSemiSupervised.values ​
readonlyvalues: readonly ["labels"]
labelPropagationSemiSupervised.weights ​
readonlyweights:"by-default"="by-default"
labelPropagationSynchronous ​
readonlylabelPropagationSynchronous:object
labelPropagationSynchronous.accelerator ​
readonlyaccelerator:"labelPropagation"="labelPropagation"
labelPropagationSynchronous.category ​
readonlycategory:"community"="community"
labelPropagationSynchronous.direction ​
readonlydirection:"any"="any"
labelPropagationSynchronous.dispatch ​
readonlydispatch:"labelPropagationSynchronous"="labelPropagationSynchronous"
labelPropagationSynchronous.fn ​
readonlyfn: (s,options) =>LabelPropagationResult=labelPropagationSynchronous
Label propagation in synchronous passes: every node reads its neighbours' labels from the previous pass, and all nodes move at once. Deterministic, with no random stream.
A node keeps its label when that label is dominant among its neighbours; otherwise it takes, of the labels with the largest summed vote, the one of lowest PRIORITY. A label's priority is a fixed scramble of its value (the murmur3 32-bit finalizer, a one-to-one map, so no two labels share a priority). Ranking labels by their raw value instead ties the outcome to the node numbering: on a path or a cycle numbered in order every interior node is tied between its two neighbours, the lowest label wins every tie, and it spreads one node per two passes -- 1,998 passes to settle a 1,000-node path, into a single community. Under the scrambled priority the winners of those ties are spread along the chain, so a path or a cycle settles in a few passes into short runs of nodes, as labelPropagation does, and a star or a clique still ends as one community.
Synchronous updates alone make two neighbours trade labels for ever (on a single edge a-b, a takes b's label while b takes a's), so passes alternate a swap guard: an even pass (the first is pass 0) lets a node move only to a label of higher priority, an odd pass only to one of lower priority (the up/down rule of cuGraph's Louvain move phase). The run stops after two passes in a row with no move, one up and one down -- then every node's label is dominant and converged is true -- or after maxIterations passes.
The guard stops the single-edge swap but not every cycle: on some weighted graphs a node climbs to a higher-priority label on each up pass and falls back on each down pass. When a pass returns the labels of two passes before, the run can only repeat itself, so it stops there with converged false; the result is then the same for any larger maxIterations. Longer cycles run to the cap.
This is what the legacy labelPropagationAsync does, despite its name, with the swap guard it lacks and the tie rule applied to the finished tally rather than to a running one. Conventions match labelPropagation: self-loops are skipped, parallel arcs summed, a directed snapshot reads out- and in-neighbours, weights are the exact f64 ones when the snapshot keeps them, and a negative, NaN or infinite weight throws.
Parameters ​
s ​
GraphSnapshot
Any snapshot
options? ​
SynchronousLabelPropagationOptions = {}
Pass cap and weighting
Returns ​
The partition, the passes run, and whether every label ended dominant
labelPropagationSynchronous.inputs ​
readonlyinputs: readonly [] =[]
labelPropagationSynchronous.name ​
readonlyname:"labelPropagationSynchronous"="labelPropagationSynchronous"
labelPropagationSynchronous.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
labelPropagationSynchronous.result ​
readonlyresult:"partition"="partition"
labelPropagationSynchronous.values ​
readonlyvalues: readonly ["labels"]
labelPropagationSynchronous.weights ​
readonlyweights:"by-default"="by-default"
leiden ​
readonlyleiden:object
leiden.accelerator ​
readonlyaccelerator:null=null
leiden.category ​
readonlycategory:"community"="community"
leiden.direction ​
readonlydirection:"undirected"="undirected"
leiden.dispatch ​
readonlydispatch:"leiden"="leiden"
leiden.fn ​
readonlyfn: (s,options) =>LeidenResult=leiden
Leiden community detection (Traag, Waltman and van Eck, Sci. Rep. 9:5233, 2019) over an undirected snapshot: fast local moving, refinement into well-connected sub-communities, and aggregation by contract() once per level. Whole passes repeat from the previous answer, keeping a pass only when it raises modularity, until three passes in a row improve it by no more than threshold or maxIterations passes have run; every pass starts by moving single original nodes, so the answer is also a local optimum of those moves.
Modularity follows graph-format's weightedDegree() convention (a self-loop counts twice), which on a graph without self-loops equals the legacy leiden's. Weights are the exact f64 ones when the snapshot keeps them. The partition is not the legacy function's move for move: the legacy leiden splits disconnected communities rather than refining them, and visits nodes in another order; what this guarantees is connected communities. Both are randomised heuristics, so for one seed either can come out ahead; on the shared fixtures this one's modularity is at least the legacy's at the default seed and on average over seeds.
Parameters ​
s ​
GraphSnapshot
An undirected snapshot
options? ​
LeidenOptions = {}
Resolution, seed, pass cap and stopping threshold
Returns ​
The partition, its modularity, and the levels processed
Throws ​
Error on a directed snapshot; RangeError for a negative, NaN or infinite weight or a fractional seed
leiden.inputs ​
readonlyinputs: readonly [] =[]
leiden.name ​
readonlyname:"leiden"="leiden"
leiden.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
leiden.result ​
readonlyresult:"partition"="partition"
leiden.values ​
readonlyvalues: readonly ["labels"]
leiden.weights ​
readonlyweights:"always"="always"
louvain ​
readonlylouvain:object
louvain.accelerator ​
readonlyaccelerator:"louvain"="louvain"
louvain.category ​
readonlycategory:"community"="community"
louvain.direction ​
readonlydirection:"undirected"="undirected"
louvain.dispatch ​
readonlydispatch:"louvain"="louvain"
louvain.fn ​
readonlyfn: (s,o) =>LouvainResult=louvain
Louvain community detection over a snapshot: local moving to the best modularity gain, then collapse each community into a node and repeat, until a level stops paying for itself.
The legacy louvain switches implementation at 50 nodes (useOptimized) and its small-graph path has no aggregation phase at all, so this port is not move-for-move identical to either branch of it -- what it guarantees is a partition whose modularity is at least as high, measured by the same formula.
Parameters ​
s ​
GraphSnapshot
An undirected snapshot
o? ​
LouvainOptions = {}
Algorithm options
Returns ​
The partition, its modularity, and the number of levels that improved it
louvain.inputs ​
readonlyinputs: readonly [] =[]
louvain.name ​
readonlyname:"louvain"="louvain"
louvain.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
louvain.result ​
readonlyresult:"partition"="partition"
louvain.values ​
readonlyvalues: readonly ["labels"]
louvain.weights ​
readonlyweights:"always"="always"
markovClustering ​
readonlymarkovClustering:object
markovClustering.accelerator ​
readonlyaccelerator:null=null
markovClustering.category ​
readonlycategory:"community"="community"
markovClustering.direction ​
readonlydirection:"any"="any"
markovClustering.dispatch ​
readonlydispatch:null=null
markovClustering.fn ​
readonlyfn: (s,options) =>MarkovResult=markovClustering
Markov clustering (MCL; van Dongen, 2000), the index-based port of the legacy markovClustering.
The flow matrix starts as the column-normalised adjacency: entry (i, j) is the weight of the arc i -> j, so column j spreads j's flow over the nodes with an arc into it (on an undirected snapshot, its neighbours). With selfLoops every diagonal entry is first set to 1, replacing a self-loop's weight. Each round raises the matrix to the expansion power, raises every entry to the inflation power, renormalises the columns, drops the entries below pruningThreshold and renormalises again, until no entry moves by more than tolerance or maxIterations rounds ran. Node j then joins the attractor with the largest entry in its column, the lowest index on a tie; a node whose column emptied is a community of its own.
The matrix is kept sparse, and every sum is taken in the order the legacy dense loops take it, so the rounds are bit-identical to the legacy function's and so are the communities, their order and the attractors. Communities are labelled in the legacy function's order: the flow clusters by their lowest member, then the nodes whose column emptied. Two differences: parallel arcs are summed (a legacy graph has none), and iterations counts the rounds run, where the legacy function reports one more than maxIterations when it stops at the cap. A negative, NaN or infinite weight throws.
Parameters ​
s ​
AdjacencyView
Any snapshot or adjacency view
options? ​
MarkovOptions = {}
MCL parameters and the weight override
Returns ​
The partition, the attractors, the rounds run and whether they converged
markovClustering.inputs ​
readonlyinputs: readonly [] =[]
markovClustering.name ​
readonlyname:"markovClustering"="markovClustering"
markovClustering.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
markovClustering.result ​
readonlyresult:"partition"="partition"
markovClustering.values ​
readonlyvalues: readonly ["labels"]
markovClustering.weights ​
readonlyweights:"always"="always"
maxFlow ​
readonlymaxFlow:object
maxFlow.accelerator ​
readonlyaccelerator:null=null
maxFlow.category ​
readonlycategory:"flow"="flow"
maxFlow.direction ​
readonlydirection:"any"="any"
maxFlow.dispatch ​
readonlydispatch:"maxFlow"="maxFlow"
maxFlow.fn ​
readonlyfn: (s,sourceNode,sinkNode,options) =>MaxFlowResult=maxFlow
Maximum flow from source to sink by augmenting paths.
Paths, bottlenecks and per-edge flows equal the legacy edmondsKarp / fordFulkerson on a graph with no parallel edges and no two opposite directed edges: the residual is kept per node pair and searched in the legacy row order (see buildResidual). Where two opposite directed edges carry flow, legacy records every push on the edge in the push's direction, so one edge can show more flow than its capacity; this port reports the net flow of the pair on the edges in its direction instead, each within its capacity.
Parameters ​
s ​
GraphSnapshot
The snapshot
sourceNode ​
NodeRef
The source node: its index, or { id }
sinkNode ​
NodeRef
The sink node: its index, or { id }
options? ​
MaxFlowOptions = {}
The path search and the capacity override
Returns ​
The flow value, the per-edge flows, the source side and the cut edges
Throws ​
RangeError when source or sink is not a node index, or they are the same node
maxFlow.inputs ​
readonlyinputs: readonly [AlgorithmInput,AlgorithmInput]
maxFlow.name ​
readonlyname:"maxFlow"="maxFlow"
maxFlow.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
maxFlow.result ​
readonlyresult:"flow"="flow"
maxFlow.values ​
readonlyvalues: readonly ["flow"]
maxFlow.weights ​
readonlyweights:"always"="always"
maximumBipartiteMatching ​
readonlymaximumBipartiteMatching:object
maximumBipartiteMatching.accelerator ​
readonlyaccelerator:null=null
maximumBipartiteMatching.category ​
readonlycategory:"matching"="matching"
maximumBipartiteMatching.direction ​
readonlydirection:"any"="any"
maximumBipartiteMatching.dispatch ​
readonlydispatch:"maximumBipartiteMatching"="maximumBipartiteMatching"
maximumBipartiteMatching.fn ​
readonlyfn: (s,options) =>BipartiteMatchingResult=maximumBipartiteMatching
Maximum matching of a bipartite graph by augmenting paths (Kuhn's algorithm, O(V E)).
Tie rule: among the equally large matchings it returns the one 2.x returned. Left nodes search in the order a breadth-first walk first reaches them (roots in node order, as isBipartite colours), and every node tries its neighbours in the order of the edge that joins them, the earliest added first. The search is depth-first and iterative. Parallel arcs and arc direction (by default) make no difference to the matching size.
Parameters ​
s ​
GraphSnapshot
The snapshot
options? ​
The two sides, and which arcs to follow on a directed snapshot
Returns ​
The partner of every matched left node and the number of pairs
Throws ​
Error when the sides are inferred and the graph is not bipartite
maximumBipartiteMatching.inputs ​
readonlyinputs: readonly [] =[]
maximumBipartiteMatching.name ​
readonlyname:"maximumBipartiteMatching"="maximumBipartiteMatching"
maximumBipartiteMatching.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
maximumBipartiteMatching.result ​
readonlyresult:"matching"="matching"
maximumBipartiteMatching.values ​
readonlyvalues: readonly ["matching"]
maximumBipartiteMatching.weights ​
readonlyweights:"never"="never"
minSTCut ​
readonlyminSTCut:object
minSTCut.accelerator ​
readonlyaccelerator:null=null
minSTCut.category ​
readonlycategory:"flow"="flow"
minSTCut.direction ​
readonlydirection:"any"="any"
minSTCut.dispatch ​
readonlydispatch:"minSTCut"="minSTCut"
minSTCut.fn ​
readonlyfn: (s,sourceNode,sinkNode,options) =>MinCutResult=minSTCut
Minimum s-t cut: the source side and value of a maximum flow (max-flow min-cut theorem). The side is the set reachable from the source in the final residual graph, which is the same for every maximum flow, so the path search does not change it. The search defaults to "ford-fulkerson", as the legacy minSTCut uses: on weights that are not binary fractions another search adds the bottlenecks in another order and the value can differ in its last bits.
Parameters ​
s ​
GraphSnapshot
The snapshot
sourceNode ​
NodeRef
The source node: its index, or { id }
sinkNode ​
NodeRef
The sink node: its index, or { id }
options? ​
MaxFlowOptions = {}
The path search and the capacity override
Returns ​
The cut value, the source side and the cut edges
Throws ​
RangeError as maxFlow does
minSTCut.inputs ​
readonlyinputs: readonly [AlgorithmInput,AlgorithmInput]
minSTCut.name ​
readonlyname:"minSTCut"="minSTCut"
minSTCut.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
minSTCut.result ​
readonlyresult:"cut"="cut"
minSTCut.values ​
readonlyvalues: readonly [] =[]
minSTCut.weights ​
readonlyweights:"always"="always"
modularity ​
readonlymodularity:object
modularity.accelerator ​
readonlyaccelerator:null=null
modularity.category ​
readonlycategory:"community"="community"
modularity.direction ​
readonlydirection:"any"="any"
modularity.dispatch ​
readonlydispatch:null=null
modularity.fn ​
readonlyfn: (s,labels,options) =>number=modularity
Newman's modularity of a partition: sum_c in_c / 2m - gamma * (tot_c / 2m)^2, where 2m is twice the total edge weight, in_c counts every edge inside community c twice and tot_c sums its nodes' weighted degrees. Degrees follow graph-format's weightedDegree() convention -- a self-loop counts twice -- so sum(tot_c) === 2m. On a directed snapshot the direction is ignored: every arc counts as an undirected edge, and a node's degree is its in-weight plus its out-weight.
On an undirected snapshot without self-loops this equals the legacy calculateModularity. On a directed snapshot or one with self-loops the two differ, and this one is the NetworkX value: the legacy degree counts a self-loop once and reads only out-neighbours on a directed graph, so its degrees do not sum to 2m.
It is NOT the legacy calculateMCLModularity, in two ways. That function subtracts the null-model term d(u) d(v) / 2m once per edge whatever the partition: on two separate triangles it scores one community and the two triangles both 1/3, where this returns 0 and 1/2. And it ignores edge weights, where this reads the snapshot's weights by default; pass weights filled with 1 for the unweighted value.
Parameters ​
s ​
AdjacencyView
Any snapshot or adjacency view
labels ​
U32
Community of every node; INVALID_INDEX leaves a node out of every community
options? ​
ModularityOptions = {}
Resolution and the weight override
Returns ​
number
The modularity, 0 when the snapshot has no edge weight
modularity.inputs ​
readonlyinputs: readonly [{kind:"node-labels";name:"labels"; }]
modularity.name ​
readonlyname:"modularity"="modularity"
modularity.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
modularity.result ​
readonlyresult:"number"="number"
modularity.values ​
readonlyvalues: readonly [] =[]
modularity.weights ​
readonlyweights:"always"="always"
nodeClosenessCentrality ​
readonlynodeClosenessCentrality:object
nodeClosenessCentrality.accelerator ​
readonlyaccelerator:null=null
nodeClosenessCentrality.category ​
readonlycategory:"centrality"="centrality"
nodeClosenessCentrality.direction ​
readonlydirection:"any"="any"
nodeClosenessCentrality.dispatch ​
readonlydispatch:null=null
nodeClosenessCentrality.fn ​
readonlyfn: (s,nodeRef,options) =>number=nodeClosenessCentrality
Closeness centrality of one node, with one search rather than n.
Parameters ​
s ​
GraphSnapshot
The snapshot
nodeRef ​
NodeRef
The node: its index, or { id }
options? ​
Omit<ClosenessOptions, "sources" | "k"> = {}
As closenessCentrality, without sampling: one node's exact score is one search
Returns ​
number
The node's score
Throws ​
RangeError for a node index outside [0, nodeCount)
nodeClosenessCentrality.inputs ​
readonlyinputs: readonly [AlgorithmInput]
nodeClosenessCentrality.name ​
readonlyname:"nodeClosenessCentrality"="nodeClosenessCentrality"
nodeClosenessCentrality.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
nodeClosenessCentrality.result ​
readonlyresult:"number"="number"
nodeClosenessCentrality.values ​
readonlyvalues: readonly [] =[]
nodeClosenessCentrality.weights ​
readonlyweights:"on-request"="on-request"
pageRank ​
readonlypageRank:object
pageRank.accelerator ​
readonlyaccelerator:"pageRank"="pageRank"
pageRank.category ​
readonlycategory:"centrality"="centrality"
pageRank.direction ​
readonlydirection:"any"="any"
pageRank.dispatch ​
readonlydispatch:"pageRank"="pageRank"
pageRank.fn ​
readonlyfn: (s,o) =>PageRankResult=pageRank
PageRank by pull over reverse(), with the dangling mass redistributed uniformly. On an undirected snapshot every edge carries rank both ways (a self-loop once), which is PageRank on the directed graph holding both arcs of every edge.
Parameters ​
s ​
GraphSnapshot
The snapshot
o? ​
PageRankOptions = {}
Algorithm options
Returns ​
The scores, the iteration count and the convergence flag
pageRank.inputs ​
readonlyinputs: readonly [] =[]
pageRank.name ​
readonlyname:"pageRank"="pageRank"
pageRank.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
pageRank.result ​
readonlyresult:"node-scores"="node-scores"
pageRank.values ​
readonlyvalues: readonly ["scores"]
pageRank.weights ​
readonlyweights:"on-request"="on-request"
personalizedPageRank ​
readonlypersonalizedPageRank:object
personalizedPageRank.accelerator ​
readonlyaccelerator:"personalizedPageRank"="personalizedPageRank"
personalizedPageRank.category ​
readonlycategory:"centrality"="centrality"
personalizedPageRank.direction ​
readonlydirection:"any"="any"
personalizedPageRank.dispatch ​
readonlydispatch:"personalizedPageRank"="personalizedPageRank"
personalizedPageRank.fn ​
readonlyfn: (s,personalization,o) =>PageRankResult=personalizedPageRank
Personalized PageRank, as the legacy function computes it: the personalization vector, normalised to sum 1, replaces the uniform 1 / n in the teleport, and each node receives d * dangling / n of the dangling mass scaled by its personalization. That keeps only a 1 / n share of the dangling mass, so on a graph with a dangling node the scores sum to less than 1 (networkx spreads all of it). An all-zero vector gives plain PageRank, as the legacy function does for an empty list of personal nodes.
Parameters ​
s ​
GraphSnapshot
The snapshot
personalization ​
F64 | F32
One finite, non-negative mass per node index
o? ​
PageRankOptions = {}
Algorithm options
Returns ​
The scores, the iteration count and the convergence flag
personalizedPageRank.inputs ​
readonlyinputs: readonly [{kind:"node-values";name:"personalization"; }]
personalizedPageRank.name ​
readonlyname:"personalizedPageRank"="personalizedPageRank"
personalizedPageRank.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
personalizedPageRank.result ​
readonlyresult:"node-scores"="node-scores"
personalizedPageRank.values ​
readonlyvalues: readonly ["scores"]
personalizedPageRank.weights ​
readonlyweights:"on-request"="on-request"
primMST ​
readonlyprimMST:object
primMST.accelerator ​
readonlyaccelerator:null=null
primMST.category ​
readonlycategory:"spanning-tree"="spanning-tree"
primMST.direction ​
readonlydirection:"undirected"="undirected"
primMST.dispatch ​
readonlydispatch:"primMST"="primMST"
primMST.fn ​
readonlyfn: (s,o) =>PrimResult=primMST
Prim's minimum spanning tree, grown from a start node by always taking the cheapest arc from the tree to a node outside it (an indexed heap keyed per node, so of several parallel edges the cheapest, and of equal ones the lowest-indexed, is the one taken). edges lists the logical edges in the order they joined the tree and totalWeight sums them in that order, as legacy primMST does. The start index is checked: one outside the graph throws a RangeError.
Parameters ​
s ​
GraphSnapshot
An undirected snapshot
o? ​
PrimOptions = {}
Per-arc weight override, start node and the spanning-forest switch
Returns ​
The accepted edges, their total weight and the discovery arc per node
Throws ​
Error on a directed snapshot, and on a disconnected one unless forest is set
primMST.inputs ​
readonlyinputs: readonly [] =[]
primMST.name ​
readonlyname:"primMST"="primMST"
primMST.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
primMST.result ​
readonlyresult:"spanning-tree"="spanning-tree"
primMST.values ​
readonlyvalues: readonly ["predArc"]
primMST.weights ​
readonlyweights:"always"="always"
spectralClustering ​
readonlyspectralClustering:object
spectralClustering.accelerator ​
readonlyaccelerator:null=null
spectralClustering.category ​
readonlycategory:"community"="community"
spectralClustering.direction ​
readonlydirection:"any"="any"
spectralClustering.dispatch ​
readonlydispatch:null=null
spectralClustering.fn ​
readonlyfn: (s,options) =>SpectralResult=spectralClustering
Spectral clustering, the index-based port of the legacy spectralClustering: the k eigenvectors of the graph Laplacian with the smallest eigenvalues embed every node as a point in k dimensions, and k-means splits the points into at most k clusters.
The Laplacian is applied straight from the CSR rows and never built: D - A (unnormalized), I - D^-1/2 A D^-1/2 (normalized, whose embedded rows are then scaled to unit length, as Ng, Jordan and Weiss do) or I - D^-1 A (randomWalk, whose eigenvectors are D^-1/2 times the normalized ones). A node of degree 0 has a zero row. Self-loops are ignored (they cancel in D - A) and a directed snapshot is read as undirected, a reciprocal pair summing. A negative, NaN or infinite weight throws. With k >= nodeCount every node is its own cluster.
The eigenpairs come from subspace iteration with Rayleigh-Ritz, so they are the true smallest ones when converged is true; converged is false when the iteration stopped at its round cap (a small spectral gap, such as a path of hundreds of nodes), and the eigenpairs are then only approximate. The legacy function uses fixed placeholder eigenvalues for k <= 3 and plain power iteration (which finds the LARGEST eigenvectors) above that, and draws its k-means seeds from Math.random unless given a seed. The partitions therefore differ from the legacy function's; one seed gives one result. Labels are dense in first-seen order, so count is below k when k-means leaves a cluster empty.
Parameters ​
s ​
GraphSnapshot
Any snapshot
options ​
k, the Laplacian, the k-means limits, the seed and the weight override
Returns ​
The partition and the eigenpairs it came from
spectralClustering.inputs ​
readonlyinputs: readonly [] =[]
spectralClustering.name ​
readonlyname:"spectralClustering"="spectralClustering"
spectralClustering.requiredOptions ​
readonlyrequiredOptions: readonly ["k"]
spectralClustering.result ​
readonlyresult:"partition"="partition"
spectralClustering.values ​
readonlyvalues: readonly ["labels"]
spectralClustering.weights ​
readonlyweights:"always"="always"
stoerWagner ​
readonlystoerWagner:object
stoerWagner.accelerator ​
readonlyaccelerator:null=null
stoerWagner.category ​
readonlycategory:"flow"="flow"
stoerWagner.direction ​
readonlydirection:"any"="any"
stoerWagner.dispatch ​
readonlydispatch:"stoerWagner"="stoerWagner"
stoerWagner.fn ​
readonlyfn: (s,options) =>MinCutResult=stoerWagner
Global minimum cut by Stoer-Wagner, reading every edge as undirected (a directed snapshot's arcs count in both directions, so two opposite arcs between one pair add up).
No graph is contracted (graph-format design 14.2): a representative array and member lists merge the last two nodes of each phase, and each phase's maximum-adjacency order accumulates the weights of the ORIGINAL arcs of the merged members in an IndexedMaxHeap. A phase starts from the lowest-index remaining representative and takes equal keys lowest index first, and the first phase with the lightest cut wins, which is the order the legacy implementation uses. O(n (m + n) log n).
Parameters ​
s ​
GraphSnapshot
The snapshot
options? ​
StoerWagnerOptions = {}
The weight override
Returns ​
The cut value, the side holding the last node of the winning phase, and the cut edges; with fewer than two nodes the value is 0 and every node is on side
stoerWagner.inputs ​
readonlyinputs: readonly [] =[]
stoerWagner.name ​
readonlyname:"stoerWagner"="stoerWagner"
stoerWagner.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
stoerWagner.result ​
readonlyresult:"cut"="cut"
stoerWagner.values ​
readonlyvalues: readonly [] =[]
stoerWagner.weights ​
readonlyweights:"always"="always"
stronglyConnectedComponents ​
readonlystronglyConnectedComponents:object
stronglyConnectedComponents.accelerator ​
readonlyaccelerator:null=null
stronglyConnectedComponents.category ​
readonlycategory:"components"="components"
stronglyConnectedComponents.direction ​
readonlydirection:"directed"="directed"
stronglyConnectedComponents.dispatch ​
readonlydispatch:"stronglyConnectedComponents"="stronglyConnectedComponents"
stronglyConnectedComponents.fn ​
readonlyfn: (g,options) =>LabelResult=stronglyConnectedComponents
Strongly connected components by an iterative Tarjan: roots in index order, neighbours in row order unless options.arcOrder gives another, and component c is the c-th to complete -- the numbering the legacy stronglyConnectedComponents gives its result array. Completion order is a reverse topological order of the condensation.
Parameters ​
g ​
AdjacencyView
A directed adjacency
options? ​
ArcOrderOption = {}
The neighbour order
Returns ​
The partition, labels in completion order
Throws ​
Error when the graph is undirected
stronglyConnectedComponents.inputs ​
readonlyinputs: readonly [] =[]
stronglyConnectedComponents.name ​
readonlyname:"stronglyConnectedComponents"="stronglyConnectedComponents"
stronglyConnectedComponents.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
stronglyConnectedComponents.result ​
readonlyresult:"partition"="partition"
stronglyConnectedComponents.values ​
readonlyvalues: readonly ["labels"]
stronglyConnectedComponents.weights ​
readonlyweights:"never"="never"
syncClustering ​
readonlysyncClustering:object
syncClustering.accelerator ​
readonlyaccelerator:null=null
syncClustering.category ​
readonlycategory:"community"="community"
syncClustering.direction ​
readonlydirection:"any"="any"
syncClustering.dispatch ​
readonlydispatch:null=null
syncClustering.fn ​
readonlyfn: (s,options) =>SyncClusteringResult=syncClustering
SynC over a snapshot: node embeddings seeded from degree and a seeded generator, each moved part of the way to the mean of its out-neighbors' embeddings at every step, and clustered by k-means with k-means++ centers. Smoothing gathers a connected community within a few steps, and run long enough it would gather each whole connected part into one point, so the run stops once the cluster assignment has held for 10 iterations (or the loss settles). Unconnected cliques of any size come back as separate clusters, and planted communities as their own clusters. The order of every random draw is the legacy syncClustering's, but legacy took a fixed gradient step that also pulled every node toward the origin, so its results differ (see issues #1600 and #1698). The port draws from its own generator instead of replacing Math.random for the duration of the call.
Weights are ignored and a parallel arc pulls once per arc. The seeding degree is legacy's: in plus out when directed, the stored arc count when undirected, so an undirected self-loop counts once.
Parameters ​
s ​
GraphSnapshot
The snapshot
options ​
Returns ​
Assignments, embeddings and convergence
syncClustering.inputs ​
readonlyinputs: readonly [] =[]
syncClustering.name ​
readonlyname:"syncClustering"="syncClustering"
syncClustering.requiredOptions ​
readonlyrequiredOptions: readonly ["numClusters"]
syncClustering.result ​
readonlyresult:"partition"="partition"
syncClustering.values ​
readonlyvalues: readonly ["labels"]
syncClustering.weights ​
readonlyweights:"never"="never"
teraHAC ​
readonlyteraHAC:object
teraHAC.accelerator ​
readonlyaccelerator:null=null
teraHAC.category ​
readonlycategory:"community"="community"
teraHAC.direction ​
readonlydirection:"any"="any"
teraHAC.dispatch ​
readonlydispatch:null=null
teraHAC.fn ​
readonlyfn: (s,options) =>TeraHacResult=teraHAC
TeraHAC over a snapshot: agglomerative clustering that repeatedly merges the closest pair of clusters, with the same distances, merge order and tie-breaks as the legacy teraHAC -- but over node indices, so the result no longer depends on what the node ids are.
The linkage is kept per ordered cluster pair as an aggregate (minimum, maximum, sum of distances or sum of squared distances) and updated on each merge, instead of rescanning every member pair; the pairwise distances are integers, so the sums, and the means divided out of them, are exact. A pair's distance reads the aggregate in the orientation legacy reads it, which matters on a directed snapshot, and ties go to the pair legacy's stable sort puts first: two original nodes before any pair with a merged cluster, and among those the most recent merge last.
Weights are ignored (legacy counts hops). O(n^2) memory and O(n (n + m)) for the distances; the merging keeps each cluster's closest partner and rescans a row only when that partner merges.
Parameters ​
s ​
GraphSnapshot
The snapshot
options? ​
TeraHacOptions = {}
Returns ​
The flat clustering and the dendrogram
teraHAC.inputs ​
readonlyinputs: readonly [] =[]
teraHAC.name ​
readonlyname:"teraHAC"="teraHAC"
teraHAC.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
teraHAC.result ​
readonlyresult:"partition"="partition"
teraHAC.values ​
readonlyvalues: readonly ["labels"]
teraHAC.weights ​
readonlyweights:"never"="never"
topologicalSort ​
readonlytopologicalSort:object
topologicalSort.accelerator ​
readonlyaccelerator:null=null
topologicalSort.category ​
readonlycategory:"structure"="structure"
topologicalSort.direction ​
readonlydirection:"directed"="directed"
topologicalSort.dispatch ​
readonlydispatch:null=null
topologicalSort.fn ​
readonlyfn: (g,options) =>U32|null=topologicalSort
Topological order of a directed graph: reverse DFS post-order, roots taken in index order and neighbours in row order unless options.arcOrder gives another.
Parameters ​
g ​
AdjacencyView
A directed adjacency
options? ​
ArcOrderOption = {}
The neighbour order
Returns ​
U32 | null
The node indices in topological order, or null when the graph has a cycle
Throws ​
Error when the graph is undirected
topologicalSort.inputs ​
readonlyinputs: readonly [] =[]
topologicalSort.name ​
readonlyname:"topologicalSort"="topologicalSort"
topologicalSort.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
topologicalSort.result ​
readonlyresult:"order"="order"
topologicalSort.values ​
readonlyvalues: readonly [] =[]
topologicalSort.weights ​
readonlyweights:"never"="never"
triangleCount ​
readonlytriangleCount:object
triangleCount.accelerator ​
readonlyaccelerator:"triangleCount"="triangleCount"
triangleCount.category ​
readonlycategory:"structure"="structure"
triangleCount.direction ​
readonlydirection:"any"="any"
triangleCount.dispatch ​
readonlydispatch:"triangleCount"="triangleCount"
triangleCount.fn ​
readonlyfn: (s) =>TriangleCountResult=triangleCount
Triangle counting, with each node's clustering coefficient and the graph's transitivity, over the simple undirected graph underlying the snapshot: arc directions are ignored, parallel edges count once and self-loops not at all, so a directed snapshot and its undirected twin give the same answer. Each edge is oriented from the lower to the higher node in (degree, index) order and the two oriented rows of every oriented edge are intersected, so each triangle is found exactly once: O(m^1.5).
Parameters ​
s ​
GraphSnapshot
Any snapshot
Returns ​
The per-node and total triangles, the clustering coefficient and the transitivity
triangleCount.inputs ​
readonlyinputs: readonly [] =[]
triangleCount.name ​
readonlyname:"triangleCount"="triangleCount"
triangleCount.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
triangleCount.result ​
readonlyresult:"node-scores"="node-scores"
triangleCount.values ​
readonlyvalues: readonly ["perNode","coefficient"]
triangleCount.weights ​
readonlyweights:"never"="never"
weaklyConnectedComponents ​
readonlyweaklyConnectedComponents:object
weaklyConnectedComponents.accelerator ​
readonlyaccelerator:"weaklyConnectedComponents"="weaklyConnectedComponents"
weaklyConnectedComponents.category ​
readonlycategory:"components"="components"
weaklyConnectedComponents.direction ​
readonlydirection:"any"="any"
weaklyConnectedComponents.dispatch ​
readonlydispatch:"weaklyConnectedComponents"="weaklyConnectedComponents"
weaklyConnectedComponents.fn ​
readonlyfn: (s) =>LabelResult=weaklyConnectedComponents
Weakly connected components: the same union-find pass with the direction check dropped.
Parameters ​
s ​
GraphSnapshot
Any snapshot
Returns ​
The partition
weaklyConnectedComponents.inputs ​
readonlyinputs: readonly [] =[]
weaklyConnectedComponents.name ​
readonlyname:"weaklyConnectedComponents"="weaklyConnectedComponents"
weaklyConnectedComponents.requiredOptions ​
readonlyrequiredOptions: readonly [] =[]
weaklyConnectedComponents.result ​
readonlyresult:"partition"="partition"
weaklyConnectedComponents.values ​
readonlyvalues: readonly ["labels"]
weaklyConnectedComponents.weights ​
readonlyweights:"never"="never"
Example ​
import { ALGORITHMS } from "@graphty/algorithms";
for (const a of Object.values(ALGORITHMS)) {
if (a.direction !== "directed" && a.inputs.length === 0 && a.result === "node-scores") {
console.log(a.name, a.weights);
}
}