1
0
Fork 0

GraphViewer: ModularityJoiner is now independet from the nodes, just relying on edges. Did speed up setup process as iteration is only over connected nodes

This commit is contained in:
Michael Hackstein 2013-07-05 13:15:55 +02:00
parent 2b7bb78fa6
commit 2d16ac415e
2 changed files with 80 additions and 184 deletions

View File

@ -27,17 +27,16 @@
/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
function ModularityJoiner(nodes, edges) {
function ModularityJoiner(edges) {
"use strict";
if (nodes === undefined) {
throw "Nodes have to be given.";
}
if (edges === undefined) {
throw "Edges have to be given.";
}
var matrix = null,
backwardMatrix,
degrees = null,
m = 0,
revM = 0,
a = null,
@ -149,11 +148,19 @@ function ModularityJoiner(nodes, edges) {
makeAdjacencyMatrix = function() {
matrix = {};
backwardMatrix = {};
degrees = {};
_.each(edges, function (e) {
var s = e.source._id,
t = e.target._id;
matrix[s] = matrix[s] || {};
matrix[s][t] = (matrix[s][t] || 0) + 1;
backwardMatrix[t] = backwardMatrix[t] || {};
backwardMatrix[t][s] = (backwardMatrix[t][s] || 0) + 1;
degrees[s] = degrees[s] || {_in: 0, _out:0};
degrees[t] = degrees[t] || {_in: 0, _out:0};
degrees[s]._out++;
degrees[t]._in++;
});
m = edges.length;
revM = 1/m;
@ -162,76 +169,42 @@ function ModularityJoiner(nodes, edges) {
makeInitialDegrees = function() {
a = {};
_.each(nodes, function (n) {
a[n._id] = {
_in: n._inboundCounter / m,
_out: n._outboundCounter / m
_.each(degrees, function (n, id) {
a[id] = {
_in: n._in / m,
_out: n._out / m
};
});
return a;
},
makeInitialDQBKP = function() {
dQ = {};
_.each(nodes, function(n1) {
var s = n1._id;
_.each(nodes, function(n2) {
var t = n2._id,
ast;
if (matrix[s] && matrix[s][t]) {
ast = matrix[s][t];
} else {
ast = 0;
}
if (s < t) {
dQ[s] = dQ[s] || {};
dQ[s][t] = (dQ[s][t] || 0) + ast * revM - a[s]._in * a[t]._out;
return;
}
if (t < s) {
dQ[t] = dQ[t] || {};
dQ[t][s] = (dQ[t][s] || 0) + ast * revM - a[s]._in * a[t]._out;
return;
}
});
});
},
notConnectedPenalty = function(s, t) {
return a[s]._out * a[t]._in + a[s]._in * a[t]._out;
},
neighbors = function(sID) {
var outbound = _.keys(matrix[sID] || {}),
inbound = _.keys(backwardMatrix[sID] || {});
return _.union(outbound, inbound);
},
makeInitialDQ = function() {
dQ = {};
_.each(nodes, function(n1) {
var s = n1._id;
_.each(nodes, function(n2) {
var t = n2._id,
ast = 0,
_.each(matrix, function(tars, s) {
var bw = backwardMatrix[s] || {},
keys = neighbors(s);
_.each(keys, function(t) {
var ast = (tars[t] || 0),
value;
if (t <= s) {
return;
}
if (matrix[s] && matrix[s][t]) {
ast += matrix[s][t];
}
if (matrix[t] && matrix[t][s]) {
ast += matrix[t][s];
}
if (ast === 0) {
return;
}
ast += (bw[t] || 0);
value = ast * revM - notConnectedPenalty(s, t);
if (value > 0) {
setDQVal(s, t, value);
}
return;
});
});
});
},
makeInitialHeap = function() {
heap = {};
@ -239,53 +212,6 @@ function ModularityJoiner(nodes, edges) {
return heap;
},
updateDQAndHeapBKP = function (low, high) {
_.each(dQ, function (list, s) {
if (s < low) {
list[low] += list[high];
delete list[high];
if (heap[s] === low || heap[s] === high) {
setHeapToMaxInList(list, s);
} else {
if (dQ[s][heap[s]] < list[low]) {
heap[s] = low;
}
}
return;
}
if (s === low) {
delete list[high];
if (_.isEmpty(list)) {
delete dQ[s];
delete heap[s];
return;
}
_.each(list, function(v, k) {
if (k < high) {
list[k] += dQ[k][high];
delete dQ[k][high];
return;
}
if (high < k) {
list[k] += dQ[high][k];
return;
}
return;
});
setHeapToMaxInList(list, s);
return;
}
if (_.isEmpty(list)) {
delete dQ[s];
delete heap[s];
}
return;
});
delete dQ[high];
delete heap[high];
},
// i < j && i != j != k
updateDQAndHeapValue = function (i, j, k) {
var val;
@ -335,8 +261,6 @@ function ModularityJoiner(nodes, edges) {
});
},
////////////////////////////////////
// getters //
////////////////////////////////////
@ -430,21 +354,6 @@ function ModularityJoiner(nodes, edges) {
// Evaluate value of community by distance //
//////////////////////////////////////////////
neighbors = function(sID) {
var neigh = [];
_.each(edges, function(e) {
if (e.source._id === sID) {
neigh.push(e.target._id);
return;
}
if (e.target._id === sID) {
neigh.push(e.source._id);
return;
}
});
return neigh;
},
floatDistStep = function(dist, depth, todo) {
if (todo.length === 0) {
return true;
@ -462,7 +371,7 @@ function ModularityJoiner(nodes, edges) {
floatDist = function(sID) {
var dist = {};
_.each(_.pluck(nodes, "_id"), function(n) {
_.each(matrix, function(u, n) {
dist[n] = Number.POSITIVE_INFINITY;
});
dist[sID] = 0;
@ -500,10 +409,6 @@ function ModularityJoiner(nodes, edges) {
}
return val;
};
if (nodes.length === 0 || edges.length === 0) {
isRunning = false;
throw "Load some nodes first.";
}
setup();
best = getBest();
while (best !== null) {

View File

@ -63,21 +63,15 @@
describe('setup process', function() {
it('should throw an error if no nodes are given', function() {
expect(function() {
var s = new ModularityJoiner();
}).toThrow("Nodes have to be given.");
});
it('should throw an error if no edges are given', function() {
expect(function() {
var s = new ModularityJoiner([]);
var s = new ModularityJoiner();
}).toThrow("Edges have to be given.");
});
it('should not throw an error if mandatory information is given', function() {
expect(function() {
var s = new ModularityJoiner([], []);
var s = new ModularityJoiner([]);
}).not.toThrow();
});
@ -104,7 +98,7 @@
}
},
w = new WebWorkerWrapper(ModularityJoiner, cb, n, e);
w = new WebWorkerWrapper(ModularityJoiner, cb, e);
});
waitsFor(function() {
@ -129,7 +123,7 @@
beforeEach(function () {
nodes = [];
edges = [];
joiner = new ModularityJoiner(nodes, edges);
joiner = new ModularityJoiner(edges);
testNetFour = function() {
helper.insertSimpleNodes(nodes, ["0", "1", "2", "3"]);
edges.push(helper.createSimpleEdge(nodes, 0, 1));
@ -685,7 +679,7 @@
});
*/
});
/*
describe('checking the zachary karate club', function() {
beforeEach(function() {
@ -819,19 +813,18 @@
});
it('should be able to find communities', function() {
/*
correct acc to: NMCM09
Red:
21,23,24,19,27,16,30,33,34,29,15,9,31
White:
25, 26, 28, 32
Green:
5, 6, 7, 11, 17
Blue:
1, 2, 3, 4, 10, 14, 20, 22, 18, 13, 12
*/
/////////////////////////////////////////////////////
/// correct acc to: NMCM09 //
/// Red: //
/// 21,23,24,19,27,16,30,33,34,29,15,9,31 //
/// White: //
/// 25, 26, 28, 32 //
/// Green: //
/// 5, 6, 7, 11, 17 //
/// Blue: //
/// 1, 2, 3, 4, 10, 14, 20, 22, 18, 13, 12 //
/////////////////////////////////////////////////////
this.addMatchers({
toContainKarateClubCommunities: function() {
var c1 = [
@ -893,7 +886,7 @@
});
});
*/
describe('checking consistency after join', function() {
beforeEach(function() {
@ -1085,17 +1078,17 @@
// This is the max. number of nodes for the admin UI
// However the format is realy unlikely in the adminUI
/*
it('should be able to handle 3000 nodes', function() {
var start = (new Date()).getTime();
var i, best, nodeCount = 3000;
var start = (new Date()).getTime(),
i, best, nodeCount = 3000, diff;
helper.insertNSimpleNodes(nodes, nodeCount);
helper.insertClique(nodes, edges, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
for (i = 11; i < nodeCount; i++) {
edges.push(helper.createSimpleEdge(nodes, i - 1, i));
edges.push(helper.createSimpleEdge(nodes, i, i - 2));
}
var diff = (new Date()).getTime() - start;
diff = (new Date()).getTime() - start;
console.log("Runtime Fill:", diff, "ms");
start = (new Date()).getTime();
joiner.setup();
@ -1103,17 +1096,15 @@
console.log("Runtime Setup:", diff, "ms");
start = (new Date()).getTime();
best = joiner.getBest();
var step = 0;
while (best !== null) {
joiner.joinCommunity(best);
best = joiner.getBest();
step++;
}
diff = (new Date()).getTime() - start;
console.log("Runtime Compute:", diff, "ms");
});
*/
// This is what we expect in the Admin UI
it('should be able to handle few large satelites', function() {
var start = (new Date()).getTime(),
@ -1603,37 +1594,37 @@
});
/*
it('should be able to handle 10000 nodes', function() {
var start = (new Date()).getTime();
var i, best, nodeCount = 10000;
helper.insertNSimpleNodes(nodes, nodeCount);
helper.insertClique(nodes, edges, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
for (i = 11; i < nodeCount; i++) {
edges.push(helper.createSimpleEdge(nodes, i - 1, i));
edges.push(helper.createSimpleEdge(nodes, i, i - 2));
}
var diff = (new Date()).getTime() - start;
console.log("Runtime Fill:", diff, "ms");
start = (new Date()).getTime();
joiner.setup();
diff = (new Date()).getTime() - start;
console.log("Runtime Setup:", diff, "ms");
start = (new Date()).getTime();
best = joiner.getBest();
var step = 0;
while (best !== null) {
joiner.joinCommunity(best);
best = joiner.getBest();
step++;
}
diff = (new Date()).getTime() - start;
console.log("Runtime Compute:", diff, "ms");
});
*/
});
/*
it('should be able to handle 10000 nodes', function() {
var start = (new Date()).getTime();
var i, best, nodeCount = 10000;
helper.insertNSimpleNodes(nodes, nodeCount);
helper.insertClique(nodes, edges, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
for (i = 11; i < nodeCount; i++) {
edges.push(helper.createSimpleEdge(nodes, i - 1, i));
edges.push(helper.createSimpleEdge(nodes, i, i - 2));
}
var diff = (new Date()).getTime() - start;
console.log("Runtime Fill:", diff, "ms");
start = (new Date()).getTime();
joiner.setup();
diff = (new Date()).getTime() - start;
console.log("Runtime Setup:", diff, "ms");
start = (new Date()).getTime();
best = joiner.getBest();
var step = 0;
while (best !== null) {
joiner.joinCommunity(best);
best = joiner.getBest();
step++;
}
diff = (new Date()).getTime() - start;
console.log("Runtime Compute:", diff, "ms");
});
*/
});
/*