Skip to content

Commit

Permalink
Clean up Floyd-Warshall
Browse files Browse the repository at this point in the history
- Use `util.defaults()`
- Change `var` to `let`
- Reduce overall code length -- less ~50 lines
- Remove `cy.getElementById()` calls
- Use `eles.merge()` to build resultant path rather than an intermediate array -- less memory usage
- No significant change in performance

Ref #1224
  • Loading branch information
maxkfranz committed Jul 26, 2018
1 parent 1be861d commit a45a219
Showing 1 changed file with 73 additions and 124 deletions.
197 changes: 73 additions & 124 deletions src/collection/algorithms/floyd-warshall.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,34 @@
import * as is from '../../is';
import { defaults } from '../../util';

var elesfn = ({
const floydWarshallDefaults = defaults({
weight: edge => 1,
directed: false
});

let elesfn = ({

// Implemented from pseudocode from wikipedia
floydWarshall: function( options ){
options = options || {};

var cy = this.cy();
let cy = this.cy();

// Weight function - optional
if( options.weight != null && is.fn( options.weight ) ){
var weightFn = options.weight;
} else {
// If not specified, assume each edge has equal weight (1)
var weightFn = function( e ){return 1;};
}
let { weight, directed } = floydWarshallDefaults(options);
let weightFn = weight;

// directed - optional
if( options.directed != null ){
var directed = options.directed;
} else {
var directed = false;
}
let { nodes, edges } = this.byGroup();
edges.filter( e => !e.isLoop() );

var edges = this.edges().stdFilter( function( e ){ return !e.isLoop(); } );
var nodes = this.nodes();
var numNodes = nodes.length;
let numNodes = nodes.length;

// mapping: node id -> position in nodes array
var id2position = {};
for( var i = 0; i < numNodes; i++ ){
id2position[ nodes[ i ].id() ] = i;
}
let indexOf = node => nodes.indexOf(node);
let atIndex = i => nodes[i];

// Initialize distance matrix
var dist = [];
for( var i = 0; i < numNodes; i++ ){
var newRow = new Array( numNodes );
for( var j = 0; j < numNodes; j++ ){
if( i == j ){
let dist = [];
for( let i = 0; i < numNodes; i++ ){
let newRow = new Array( numNodes );
for( let j = 0; j < numNodes; j++ ){
if( i === j ){
newRow[ j ] = 0;
} else {
newRow[ j ] = Infinity;
Expand All @@ -49,15 +39,17 @@ var elesfn = ({

// Initialize matrix used for path reconstruction
// Initialize distance matrix
var next = [];
var edgeNext = [];
let next = [];
let edgeNext = [];

let initMatrix = function( next ){
for( let i = 0; i < numNodes; i++ ){
let newRow = new Array( numNodes );

var initMatrix = function( next ){
for( var i = 0; i < numNodes; i++ ){
var newRow = new Array( numNodes );
for( var j = 0; j < numNodes; j++ ){
for( let j = 0; j < numNodes; j++ ){
newRow[ j ] = undefined;
}

next.push( newRow );
}
};
Expand All @@ -66,120 +58,77 @@ var elesfn = ({
initMatrix( edgeNext );

// Process edges
for( var i = 0; i < edges.length ; i++ ){
var sourceIndex = id2position[ edges[ i ].source().id() ];
var targetIndex = id2position[ edges[ i ].target().id() ];
var weight = weightFn( edges[ i ] );
for( let i = 0; i < edges.length; i++ ){
let edge = edges[i];
let s = indexOf( edge.source() );
let t = indexOf( edge.target() );
let weight = weightFn( edge );

// Check if already process another edge between same 2 nodes
if( dist[ sourceIndex ][ targetIndex ] > weight ){
dist[ sourceIndex ][ targetIndex ] = weight;
next[ sourceIndex ][ targetIndex ] = targetIndex;
edgeNext[ sourceIndex ][ targetIndex ] = edges[ i ];
if( dist[s][t] > weight ){
dist[s][t] = weight;
next[s][t] = t;
edgeNext[s][t] = edge;
}
}

// If undirected graph, process 'reversed' edges
if( !directed ){
for( var i = 0; i < edges.length ; i++ ){
var sourceIndex = id2position[ edges[ i ].target().id() ];
var targetIndex = id2position[ edges[ i ].source().id() ];
var weight = weightFn( edges[ i ] );

// Check if already process another edge between same 2 nodes
if( dist[ sourceIndex ][ targetIndex ] > weight ){
dist[ sourceIndex ][ targetIndex ] = weight;
next[ sourceIndex ][ targetIndex ] = targetIndex;
edgeNext[ sourceIndex ][ targetIndex ] = edges[ i ];
}
// If undirected graph, process 'reversed' edge
if( !directed && dist[t][s] > weight ){
dist[t][s] = weight;
next[t][s] = s;
edgeNext[t][s] = edge;
}
}

// Main loop
for( var k = 0; k < numNodes; k++ ){
for( var i = 0; i < numNodes; i++ ){
for( var j = 0; j < numNodes; j++ ){
if( dist[ i ][ k ] + dist[ k ][ j ] < dist[ i ][ j ] ){
dist[ i ][ j ] = dist[ i ][ k ] + dist[ k ][ j ];
next[ i ][ j ] = next[ i ][ k ];
for( let k = 0; k < numNodes; k++ ){
for( let i = 0; i < numNodes; i++ ){
for( let j = 0; j < numNodes; j++ ){
if( dist[i][k] + dist[k][j] < dist[i][j] ){
dist[i][j] = dist[i][k] + dist[k][j];
next[i][j] = next[i][k];
}
}
}
}

// Build result object
var position2id = [];
for( var i = 0; i < numNodes; i++ ){
position2id.push( nodes[ i ].id() );
}
let getArgEle = ele => ( is.string(ele) ? cy.filter(ele) : ele )[0];

var res = {
let res = {
distance: function( from, to ){
if( is.string( from ) ){
// from is a selector string
var fromId = (cy.filter( from )[0]).id();
} else {
// from is a node
var fromId = from.id();
}
from = getArgEle(from);
to = getArgEle(to);

if( is.string( to ) ){
// to is a selector string
var toId = (cy.filter( to )[0]).id();
} else {
// to is a node
var toId = to.id();
}

return dist[ id2position[ fromId ] ][ id2position[ toId ] ];
return dist[ indexOf(from) ][ indexOf(to) ];
},

path: function( from, to ){
var reconstructPathAux = function( from, to, next, position2id, edgeNext ){
if( from === to ){
return cy.getElementById( position2id[ from ] );
}
if( next[ from ][ to ] === undefined ){
return undefined;
}
from = getArgEle(from);
to = getArgEle(to);

var path = [ cy.getElementById( position2id[ from ] ) ];
var prev = from;
while( from !== to ){
prev = from;
from = next[ from ][ to ];
let i = indexOf(from);
let j = indexOf(to);
let fromNode = atIndex(i);

var edge = edgeNext[ prev ][ from ];
path.push( edge );
if( i === j ){ return fromNode.collection(); }

path.push( cy.getElementById( position2id[ from ] ) );
}
return path;
};
if( next[i][j] == null ){ return cy.collection(); }

if( is.string( from ) ){
// from is a selector string
var fromId = (cy.filter( from )[0]).id();
} else {
// from is a node
var fromId = from.id();
}
let path = cy.collection();
let prev = i;
let edge;

if( is.string( to ) ){
// to is a selector string
var toId = (cy.filter( to )[0]).id();
} else {
// to is a node
var toId = to.id();
}
path.merge( fromNode );

while( i !== j ){
prev = i;
i = next[i][j];
edge = edgeNext[prev][i];

var pathArr = reconstructPathAux( id2position[ fromId ],
id2position[ toId ],
next,
position2id,
edgeNext );
path.merge( edge );
path.merge( atIndex(i) );
}

return cy.collection( pathArr );
return path;
}
};

Expand Down

0 comments on commit a45a219

Please sign in to comment.