File size: 3,158 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 |
import * as is from '../../is';
import { defaults } from '../../util';
const floydWarshallDefaults = defaults({
weight: edge => 1,
directed: false
});
let elesfn = ({
// Implemented from pseudocode from wikipedia
floydWarshall: function( options ){
let cy = this.cy();
let { weight, directed } = floydWarshallDefaults(options);
let weightFn = weight;
let { nodes, edges } = this.byGroup();
let N = nodes.length;
let Nsq = N * N;
let indexOf = node => nodes.indexOf(node);
let atIndex = i => nodes[i];
// Initialize distance matrix
let dist = new Array(Nsq);
for( let n = 0; n < Nsq; n++ ){
let j = n % N;
let i = (n - j) / N;
if( i === j ){
dist[n] = 0;
} else {
dist[n] = Infinity;
}
}
// Initialize matrix used for path reconstruction
// Initialize distance matrix
let next = new Array(Nsq);
let edgeNext = new Array(Nsq);
// Process edges
for( let i = 0; i < edges.length; i++ ){
let edge = edges[i];
let src = edge.source()[0];
let tgt = edge.target()[0];
if( src === tgt ){ continue; } // exclude loops
let s = indexOf( src );
let t = indexOf( tgt );
let st = s * N + t; // source to target index
let weight = weightFn( edge );
// Check if already process another edge between same 2 nodes
if( dist[st] > weight ){
dist[st] = weight;
next[st] = t;
edgeNext[st] = edge;
}
// If undirected graph, process 'reversed' edge
if( !directed ){
let ts = t * N + s; // target to source index
if( !directed && dist[ts] > weight ){
dist[ts] = weight;
next[ts] = s;
edgeNext[ts] = edge;
}
}
}
// Main loop
for( let k = 0; k < N; k++ ){
for( let i = 0; i < N; i++ ){
let ik = i * N + k;
for( let j = 0; j < N; j++ ){
let ij = i * N + j;
let kj = k * N + j;
if( dist[ik] + dist[kj] < dist[ij] ){
dist[ij] = dist[ik] + dist[kj];
next[ij] = next[ik];
}
}
}
}
let getArgEle = ele => ( is.string(ele) ? cy.filter(ele) : ele )[0];
let indexOfArgEle = ele => indexOf(getArgEle(ele));
let res = {
distance: function( from, to ){
let i = indexOfArgEle(from);
let j = indexOfArgEle(to);
return dist[ i * N + j ];
},
path: function( from, to ){
let i = indexOfArgEle(from);
let j = indexOfArgEle(to);
let fromNode = atIndex(i);
if( i === j ){ return fromNode.collection(); }
if( next[i * N + j] == null ){ return cy.collection(); }
let path = cy.collection();
let prev = i;
let edge;
path.merge( fromNode );
while( i !== j ){
prev = i;
i = next[i * N + j];
edge = edgeNext[prev * N + i];
path.merge( edge );
path.merge( atIndex(i) );
}
return path;
}
};
return res;
} // floydWarshall
}); // elesfn
export default elesfn;
|