graphinius
Version:
Generic graph library in Typescript
152 lines (141 loc) • 5.38 kB
text/typescript
import * as $G from '../core/base/BaseGraph';
import * as $FW from '../traversal/FloydWarshall';
import * as $JO from '../traversal/Johnsons';
/**
* DEMO Version of a betweenness centrality computed via Johnson's or FloydWarshall algorithm
*
* @param graph the graph to perform Floyd-Warshall/Johnsons on
* @param directed for normalization, not used at the moment
* @param sparse decides if using the FW (dense) or Johnsons (sparse)
*
* @returns m*m matrix of values (dist), m*m*m matrix of neighbors (next)
* @constructor
*
* @comment function gives the correct results but is slow.
*
* !!! DO NOT USE FOR PRODUCTION !!!
*
* @todo decide if we still need it...
* @todo in any case, make a CLASS out of it (standardize centrality signatures)
*/
function betweennessCentrality(graph: $G.IGraph, directed?: boolean, sparse?: boolean): {} {
let paths;
sparse = sparse || false;
if (sparse) {
paths = $JO.Johnsons(graph)[1];
}
else {
paths = $FW.changeNextToDirectParents($FW.FloydWarshallAPSP(graph)[1]);
}
let nodes = graph.getNodes();
//getting the nodeKeys
let nodeKeys = Object.keys(nodes);
let map = {};
for (let key in nodes) {
//initializing the map which will be returned at the end - should it contain the keys (numbers), or the node IDs?
map[key] = 0;
}
let N = paths.length;
for (var a = 0; a < N; ++a) {
for (var b = 0; b < N; ++b) {
//if self, or b is directly reachable from a and it is the only shortest path, no betweenness score is handed out
if (a != b && !(paths[a][b].length == 1 && paths[a][b][0] == b) && paths[a][b][0] != null) {
// console.log("called with a and b: "+a+" , "+b);
let tempMap = {};
let leadArray: Array<Array<number>> = [];
let pathCount = 0;
do {
//ends when all paths are traced back
let tracer = b;
let leadCounter = 0;
pathCount++;
while (true) {
//ends when one path is traced back
let previous: Array<number> = paths[a][tracer];
let terminate = false;
//no branching:
if (previous.length == 1 && previous[0] == tracer) {
break;
}
else if (previous.length == 1) {
tracer = previous[0];
//scoring on the fly
tracer in tempMap ? tempMap[tracer] += 1 : tempMap[tracer] = 1;
}
//if there is a branching:
//handle reaching the terminal node here too!
if (previous.length > 1) {
//case: leadArray is empty and we find a branch
if (leadArray.length == 0) {
//leave a trace in the leadArray
leadArray.push([0, previous.length]);
if (previous[0] == tracer) {
terminate = true;
}
else {
tracer = previous[0];
tracer in tempMap ? tempMap[tracer] += 1 : tempMap[tracer] = 1;
}
leadCounter++;
}
//case: branch is covered by the leadArray
else if (leadCounter < leadArray.length) {
let choice = leadArray[leadCounter][0];
if (previous[choice] == tracer) {
terminate = true;
}
else {
tracer = previous[choice];
tracer in tempMap ? tempMap[tracer] += 1 : tempMap[tracer] = 1;
}
leadCounter++;
}
//case: branch is beyond the leadArray (new branching encountered)
else {
//leave a trace in the leadArray
leadArray.push([0, previous.length]);
if (previous[0] == tracer) {
terminate = true;
}
else {
tracer = previous[0];
tracer in tempMap ? tempMap[tracer] += 1 : tempMap[tracer] = 1;
}
leadCounter++;
}
}
if (terminate) {
break;
}
}
// here I need to update the leadArray, if not empty
//reminder: each subarray in leadArray: [current branchpoint, length]
if (leadArray.length > 0) {
leadArray[leadArray.length - 1][0]++;
while (leadArray[leadArray.length - 1][0] == leadArray[leadArray.length - 1][1]) {
//then remove last item from leadArray
leadArray.splice(leadArray.length - 1, 1);
if (leadArray.length == 0) {
break;
}
leadArray[leadArray.length - 1][0]++;
}
}
//console.log("one round over, path count: " + pathCount);
} while (leadArray.length != 0)
//now put the correct scores into the final map
//be careful, the return map uses letters as nodekeys! - one must transform, otherwise one gets rubbish
for (let key in tempMap) {
// console.log("tempMap element " + key);
// console.log(tempMap[key]);
let mapKey = nodeKeys[key];
map[mapKey] += tempMap[key] / pathCount;
}
}
}
}
return map;
}
export {
betweennessCentrality
};