@graphty/algorithms
Version:
Graph algorithms library for browser environments implemented in TypeScript
109 lines • 4.11 kB
JavaScript
import { bfsDistancesOnly, bfsWeightedDistances } from "../traversal/bfs-variants.js";
/**
* Calculate closeness centrality from distances
*/
function calculateClosenessFromDistances(distances, sourceNode, totalNodes, options) {
if (distances.size <= 1) {
return 0; // No other nodes reachable
}
let centrality = 0;
if (options.harmonic) {
// Harmonic centrality: sum of reciprocals of distances
for (const [targetNode, distance] of distances) {
if (targetNode !== sourceNode && distance > 0 && distance < Infinity) {
centrality += 1 / distance;
}
}
// Normalization for harmonic centrality
if (options.normalized && totalNodes > 1) {
centrality = centrality / (totalNodes - 1);
}
}
else {
// Standard closeness: reciprocal of sum of distances
let totalDistance = 0;
let reachableNodes = 0;
for (const [targetNode, distance] of distances) {
if (targetNode !== sourceNode && distance < Infinity) {
totalDistance += distance;
reachableNodes++;
}
}
if (totalDistance > 0) {
centrality = 1 / totalDistance;
// Wasserman and Faust normalization for disconnected graphs
if (options.normalized && totalNodes > 1) {
centrality = centrality * reachableNodes / (totalNodes - 1);
}
}
}
return centrality;
}
/**
* Calculate closeness centrality for all nodes in the graph
*
* Closeness centrality measures how close a node is to all other nodes
* in the graph. It is the reciprocal of the sum of the shortest path
* distances to all other reachable nodes.
*
* @param graph - The input graph
* @param options - Algorithm options
* @returns Centrality scores for each node
*
* @example
* ```typescript
* const graph = new Graph();
* graph.addEdge("A", "B");
* graph.addEdge("B", "C");
*
* const centrality = closenessCentrality(graph);
* // { A: 0.5, B: 1.0, C: 0.5 }
* ```
*
* Time Complexity: O(V * (V + E)) for unweighted graphs
* Space Complexity: O(V)
*/
export function closenessCentrality(graph, options = {}) {
const nodes = Array.from(graph.nodes()).map((node) => node.id);
const centrality = {};
for (const sourceNode of nodes) {
centrality[String(sourceNode)] = nodeClosenessCentrality(graph, sourceNode, options);
}
return centrality;
}
/**
* Calculate closeness centrality for a specific node
*/
export function nodeClosenessCentrality(graph, node, options = {}) {
if (!graph.hasNode(node)) {
throw new Error(`Node ${String(node)} not found in graph`);
}
// Use optimized BFS variant for unweighted graphs
const distances = bfsDistancesOnly(graph, node, options.cutoff, options.optimized !== undefined ? { optimized: options.optimized } : {});
const totalNodes = graph.nodeCount;
return calculateClosenessFromDistances(distances, node, totalNodes, options);
}
/**
* Calculate weighted closeness centrality using Dijkstra's algorithm
*/
export function weightedClosenessCentrality(graph, options = {}) {
const nodes = Array.from(graph.nodes()).map((node) => node.id);
const centrality = {};
for (const sourceNode of nodes) {
centrality[String(sourceNode)] = nodeWeightedClosenessCentrality(graph, sourceNode, options);
}
return centrality;
}
/**
* Calculate weighted closeness centrality for a specific node using Dijkstra
*/
export function nodeWeightedClosenessCentrality(graph, node, options = {}) {
if (!graph.hasNode(node)) {
throw new Error(`Node ${String(node)} not found in graph`);
}
// Use optimized weighted BFS variant (simplified Dijkstra)
const distances = bfsWeightedDistances(graph, node, options.cutoff, options.optimized !== undefined ? { optimized: options.optimized } : {});
const totalNodes = graph.nodeCount;
return calculateClosenessFromDistances(distances, node, totalNodes, options);
}
//# sourceMappingURL=closeness.js.map