From 2d16ac415ea453f7f037f0ae9aa3635603148e19 Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Fri, 5 Jul 2013 13:15:55 +0200 Subject: [PATCH] GraphViewer: ModularityJoiner is now independet from the nodes, just relying on edges. Did speed up setup process as iteration is only over connected nodes --- .../js/graphViewer/graph/modularityJoiner.js | 153 ++++-------------- .../specNodeReducer/modularityJoinerSpec.js | 111 ++++++------- 2 files changed, 80 insertions(+), 184 deletions(-) diff --git a/html/admin/js/graphViewer/graph/modularityJoiner.js b/html/admin/js/graphViewer/graph/modularityJoiner.js index c1b31f4328..f1b30d8a8b 100644 --- a/html/admin/js/graphViewer/graph/modularityJoiner.js +++ b/html/admin/js/graphViewer/graph/modularityJoiner.js @@ -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) { diff --git a/html/admin/js/graphViewer/jasmine_test/specNodeReducer/modularityJoinerSpec.js b/html/admin/js/graphViewer/jasmine_test/specNodeReducer/modularityJoinerSpec.js index 2fcef4f2a5..6f4b3d8946 100644 --- a/html/admin/js/graphViewer/jasmine_test/specNodeReducer/modularityJoinerSpec.js +++ b/html/admin/js/graphViewer/jasmine_test/specNodeReducer/modularityJoinerSpec.js @@ -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"); + }); + */ }); /*