File size: 3,745 Bytes
bc20498 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
import { defaults } from '../../util';
import { inPlaceSumNormalize } from '../../math';
const pageRankDefaults = defaults({
dampingFactor: 0.8,
precision: 0.000001,
iterations: 200,
weight: edge => 1
});
let elesfn = ({
pageRank: function( options ){
let { dampingFactor, precision, iterations, weight } = pageRankDefaults(options);
let cy = this._private.cy;
let { nodes, edges } = this.byGroup();
let numNodes = nodes.length;
let numNodesSqd = numNodes * numNodes;
let numEdges = edges.length;
// Construct transposed adjacency matrix
// First lets have a zeroed matrix of the right size
// We'll also keep track of the sum of each column
let matrix = new Array(numNodesSqd);
let columnSum = new Array(numNodes);
let additionalProb = (1 - dampingFactor) / numNodes;
// Create null matrix
for( let i = 0; i < numNodes; i++ ){
for( let j = 0; j < numNodes; j++ ){
let n = i * numNodes + j;
matrix[n] = 0;
}
columnSum[i] = 0;
}
// Now, process edges
for( let i = 0; i < numEdges; i++ ){
let edge = edges[ i ];
let srcId = edge.data('source');
let tgtId = edge.data('target');
// Don't include loops in the matrix
if( srcId === tgtId ){ continue; }
let s = nodes.indexOfId( srcId );
let t = nodes.indexOfId( tgtId );
let w = weight( edge );
let n = t * numNodes + s;
// Update matrix
matrix[n] += w;
// Update column sum
columnSum[s] += w;
}
// Add additional probability based on damping factor
// Also, take into account columns that have sum = 0
let p = 1.0 / numNodes + additionalProb; // Shorthand
// Traverse matrix, column by column
for( let j = 0; j < numNodes; j++ ){
if( columnSum[j] === 0 ){
// No 'links' out from node jth, assume equal probability for each possible node
for( let i = 0; i < numNodes; i++ ){
let n = i * numNodes + j;
matrix[n] = p;
}
} else {
// Node jth has outgoing link, compute normalized probabilities
for( let i = 0; i < numNodes; i++ ){
let n = i * numNodes + j;
matrix[n] = matrix[n] / columnSum[j] + additionalProb;
}
}
}
// Compute dominant eigenvector using power method
let eigenvector = new Array(numNodes);
let temp = new Array(numNodes);
let previous;
// Start with a vector of all 1's
// Also, initialize a null vector which will be used as shorthand
for( let i = 0; i < numNodes; i++ ){
eigenvector[i] = 1;
}
for( let iter = 0; iter < iterations; iter++ ){
// Temp array with all 0's
for( let i = 0; i < numNodes; i++ ){
temp[i] = 0;
}
// Multiply matrix with previous result
for( let i = 0; i < numNodes; i++ ){
for( let j = 0; j < numNodes; j++ ){
let n = i * numNodes + j;
temp[i] += matrix[n] * eigenvector[j];
}
}
inPlaceSumNormalize( temp );
previous = eigenvector;
eigenvector = temp;
temp = previous;
let diff = 0;
// Compute difference (squared module) of both vectors
for( let i = 0; i < numNodes; i++ ){
let delta = previous[i] - eigenvector[i];
diff += delta * delta;
}
// If difference is less than the desired threshold, stop iterating
if( diff < precision ){
break;
}
}
// Construct result
let res = {
rank: function( node ){
node = cy.collection(node)[0];
return eigenvector[ nodes.indexOf(node) ];
}
};
return res;
} // pageRank
}); // elesfn
export default elesfn;
|