mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'devel' of https://github.com/triAGENS/ArangoDB into devel
This commit is contained in:
commit
701f087e8f
|
@ -1,5 +1,7 @@
|
|||
/*jslint indent: 2, nomen: true, maxlen: 100, white: true plusplus: true */
|
||||
/*global $, d3, _, console, alert, ModularityJoiner*/
|
||||
/*global _*/
|
||||
// Will be injected by WebWorkers
|
||||
/*global self*/
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Graph functionality
|
||||
///
|
||||
|
@ -29,6 +31,11 @@
|
|||
|
||||
function NodeReducer(nodes, edges) {
|
||||
"use strict";
|
||||
// We are executed as a worker
|
||||
if (this.importScripts) {
|
||||
this.importScripts("js/lib/underscore.js");
|
||||
}
|
||||
|
||||
|
||||
if (nodes === undefined) {
|
||||
throw "Nodes have to be given.";
|
||||
|
@ -36,10 +43,238 @@ function NodeReducer(nodes, edges) {
|
|||
if (edges === undefined) {
|
||||
throw "Edges have to be given.";
|
||||
}
|
||||
|
||||
var self = this,
|
||||
|
||||
var
|
||||
matrix = null,
|
||||
m = 0,
|
||||
revM = 0,
|
||||
a = null,
|
||||
dQ = null,
|
||||
heap = null,
|
||||
comms = {},
|
||||
isRunning = false,
|
||||
joiner = new ModularityJoiner(nodes, edges),
|
||||
|
||||
////////////////////////////////////
|
||||
// Private functions //
|
||||
////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////
|
||||
// Functions for Modularity Join //
|
||||
/////////////////////////////////////
|
||||
|
||||
setHeapToMax = function(id) {
|
||||
var maxT,
|
||||
maxV = Number.NEGATIVE_INFINITY;
|
||||
_.each(dQ[id], function(v, t) {
|
||||
if (maxV < v) {
|
||||
maxV = v;
|
||||
maxT = t;
|
||||
}
|
||||
});
|
||||
if (maxV < 0) {
|
||||
delete heap[id];
|
||||
return;
|
||||
}
|
||||
heap[id] = maxT;
|
||||
},
|
||||
|
||||
setHeapToMaxInList = function(l, id) {
|
||||
setHeapToMax(id);
|
||||
},
|
||||
|
||||
isSetDQVal = function(i, j) {
|
||||
if (i < j) {
|
||||
return dQ[i] && dQ[i][j];
|
||||
}
|
||||
return dQ[j] && dQ[j][i];
|
||||
},
|
||||
|
||||
// This does not check if everything exists,
|
||||
// do it before!
|
||||
getDQVal = function(i, j) {
|
||||
if (i < j) {
|
||||
return dQ[i][j];
|
||||
}
|
||||
return dQ[j][i];
|
||||
},
|
||||
|
||||
setDQVal = function(i, j, v) {
|
||||
if (i < j) {
|
||||
dQ[i] = dQ[i] || {};
|
||||
dQ[i][j] = v;
|
||||
return;
|
||||
}
|
||||
dQ[j] = dQ[j] || {};
|
||||
dQ[j][i] = v;
|
||||
},
|
||||
|
||||
delDQVal = function(i, j) {
|
||||
if (i < j) {
|
||||
if (!dQ[i]) {
|
||||
return;
|
||||
}
|
||||
delete dQ[i][j];
|
||||
if (_.isEmpty(dQ[i])) {
|
||||
delete dQ[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (i === j) {
|
||||
return;
|
||||
}
|
||||
delDQVal(j, i);
|
||||
},
|
||||
|
||||
updateHeap = function(i, j) {
|
||||
var hv, val;
|
||||
if (i < j) {
|
||||
if (!isSetDQVal(i, j)) {
|
||||
setHeapToMax(i);
|
||||
return;
|
||||
}
|
||||
val = getDQVal(i, j);
|
||||
if (heap[i] === j) {
|
||||
setHeapToMax(i);
|
||||
return;
|
||||
}
|
||||
if (!isSetDQVal(i, heap[i])) {
|
||||
setHeapToMax(i);
|
||||
return;
|
||||
}
|
||||
hv = getDQVal(i, heap[i]);
|
||||
if (hv < val) {
|
||||
heap[i] = j;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (i === j) {
|
||||
return;
|
||||
}
|
||||
updateHeap(j, i);
|
||||
},
|
||||
|
||||
updateDegrees = function(low, high) {
|
||||
a[low]._in += a[high]._in;
|
||||
a[low]._out += a[high]._out;
|
||||
delete a[high];
|
||||
},
|
||||
|
||||
makeAdjacencyMatrix = function() {
|
||||
matrix = {};
|
||||
_.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;
|
||||
});
|
||||
m = edges.length;
|
||||
revM = 1/m;
|
||||
return matrix;
|
||||
},
|
||||
|
||||
makeInitialDegrees = function() {
|
||||
a = {};
|
||||
_.each(nodes, function (n) {
|
||||
a[n._id] = {
|
||||
_in: n._inboundCounter / m,
|
||||
_out: n._outboundCounter / m
|
||||
};
|
||||
});
|
||||
return a;
|
||||
},
|
||||
|
||||
notConnectedPenalty = function(s, t) {
|
||||
return a[s]._out * a[t]._in + a[s]._in * a[t]._out;
|
||||
},
|
||||
|
||||
|
||||
makeInitialDQ = function() {
|
||||
dQ = {};
|
||||
_.each(nodes, function(n1) {
|
||||
var s = n1._id;
|
||||
_.each(nodes, function(n2) {
|
||||
var t = n2._id,
|
||||
ast = 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;
|
||||
}
|
||||
value = ast * revM - notConnectedPenalty(s, t);
|
||||
if (value > 0) {
|
||||
setDQVal(s, t, value);
|
||||
}
|
||||
return;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
makeInitialHeap = function() {
|
||||
heap = {};
|
||||
_.each(dQ, setHeapToMaxInList);
|
||||
return heap;
|
||||
},
|
||||
|
||||
// i < j && i != j != k
|
||||
updateDQAndHeapValue = function (i, j, k) {
|
||||
var val;
|
||||
if (isSetDQVal(k, i)) {
|
||||
val = getDQVal(k, i);
|
||||
if (isSetDQVal(k, j)) {
|
||||
val += getDQVal(k, j);
|
||||
setDQVal(k, i, val);
|
||||
delDQVal(k, j);
|
||||
updateHeap(k, i);
|
||||
updateHeap(k, j);
|
||||
return;
|
||||
}
|
||||
val -= notConnectedPenalty(k, j);
|
||||
if (val < 0) {
|
||||
delDQVal(k, i);
|
||||
}
|
||||
updateHeap(k, i);
|
||||
return;
|
||||
}
|
||||
if (isSetDQVal(k, j)) {
|
||||
val = getDQVal(k, j);
|
||||
val -= notConnectedPenalty(k, i);
|
||||
if (val > 0) {
|
||||
setDQVal(k, i, val);
|
||||
}
|
||||
updateHeap(k, i);
|
||||
delDQVal(k, j);
|
||||
updateHeap(k, j);
|
||||
}
|
||||
},
|
||||
|
||||
updateDQAndHeap = function (low, high) {
|
||||
_.each(dQ, function (list, s) {
|
||||
if (s === low || s === high) {
|
||||
_.each(list, function(v, t) {
|
||||
if (t === high) {
|
||||
delDQVal(low, high);
|
||||
updateHeap(low, high);
|
||||
return;
|
||||
}
|
||||
updateDQAndHeapValue(low, high, t);
|
||||
});
|
||||
return;
|
||||
}
|
||||
updateDQAndHeapValue(low, high, s);
|
||||
});
|
||||
},
|
||||
|
||||
/////////////////////////////
|
||||
// Functions for Buckets //
|
||||
/////////////////////////////
|
||||
|
||||
neighbors = function(sID) {
|
||||
var neigh = [];
|
||||
|
@ -89,40 +324,6 @@ function NodeReducer(nodes, edges) {
|
|||
};
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
dijkstra = function(sID) {
|
||||
var dist = {},
|
||||
leftOvers, next, neigh,
|
||||
filterNotNext = function(e) {
|
||||
return e !== next;
|
||||
},
|
||||
computeNeighbours = function(v) {
|
||||
if (_.contains(leftOvers, v)) {
|
||||
if (dist[v] > dist[next] + 1) {
|
||||
dist[v] = dist[next] + 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
leftOvers = _.pluck(nodes, "_id");
|
||||
_.each(leftOvers, function(n) {
|
||||
dist[n] = Number.POSITIVE_INFINITY;
|
||||
});
|
||||
dist[sID] = 0;
|
||||
while(leftOvers.length > 0) {
|
||||
next = _.min(leftOvers, minDist(dist));
|
||||
if (next === Number.POSITIVE_INFINITY) {
|
||||
break;
|
||||
}
|
||||
leftOvers = leftOvers.filter(filterNotNext);
|
||||
neigh = neighbors(next);
|
||||
_.each(neigh, computeNeighbours);
|
||||
}
|
||||
return dist;
|
||||
},
|
||||
|
||||
*/
|
||||
addNode = function(bucket, node) {
|
||||
bucket.push(node);
|
||||
},
|
||||
|
@ -147,9 +348,106 @@ function NodeReducer(nodes, edges) {
|
|||
propCount++;
|
||||
countMatch++;
|
||||
return countMatch / propCount;
|
||||
};
|
||||
},
|
||||
|
||||
self.getCommunity = function(limit, focus) {
|
||||
|
||||
|
||||
////////////////////////////////////
|
||||
// getters //
|
||||
////////////////////////////////////
|
||||
|
||||
getAdjacencyMatrix = function() {
|
||||
return matrix;
|
||||
},
|
||||
|
||||
getHeap = function() {
|
||||
return heap;
|
||||
},
|
||||
|
||||
getDQ = function() {
|
||||
return dQ;
|
||||
},
|
||||
|
||||
getDegrees = function() {
|
||||
return a;
|
||||
},
|
||||
|
||||
getCommunities = function() {
|
||||
return comms;
|
||||
},
|
||||
|
||||
getBestCommunity = function (communities) {
|
||||
var bestQ = Number.NEGATIVE_INFINITY,
|
||||
bestC;
|
||||
_.each(communities, function (obj) {
|
||||
if (obj.q > bestQ) {
|
||||
bestQ = obj.q;
|
||||
bestC = obj.nodes;
|
||||
}
|
||||
});
|
||||
return bestC;
|
||||
},
|
||||
|
||||
getBest = function() {
|
||||
var bestL, bestS, bestV = Number.NEGATIVE_INFINITY;
|
||||
_.each(heap, function(lID, sID) {
|
||||
if (bestV < dQ[sID][lID]) {
|
||||
bestL = lID;
|
||||
bestS = sID;
|
||||
bestV = dQ[sID][lID];
|
||||
}
|
||||
});
|
||||
if (bestV <= 0) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
sID: bestS,
|
||||
lID: bestL,
|
||||
val: bestV
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////
|
||||
// setup //
|
||||
////////////////////////////////////
|
||||
|
||||
setup = function() {
|
||||
makeAdjacencyMatrix();
|
||||
makeInitialDegrees();
|
||||
makeInitialDQ();
|
||||
makeInitialHeap();
|
||||
comms = {};
|
||||
},
|
||||
|
||||
|
||||
////////////////////////////////////
|
||||
// computation //
|
||||
////////////////////////////////////
|
||||
|
||||
joinCommunity = function(comm) {
|
||||
var s = comm.sID,
|
||||
l = comm.lID,
|
||||
q = comm.val;
|
||||
comms[s] = comms[s] || {nodes: [s], q: 0};
|
||||
if (comms[l]) {
|
||||
comms[s].nodes = comms[s].nodes.concat(comms[l].nodes);
|
||||
comms[s].q += comms[l].q;
|
||||
delete comms[l];
|
||||
} else {
|
||||
comms[s].nodes.push(l);
|
||||
}
|
||||
comms[s].q += q;
|
||||
updateDQAndHeap(s, l);
|
||||
updateDegrees(s, l);
|
||||
},
|
||||
|
||||
////////////////////////////////////
|
||||
// Get only the Best Community //
|
||||
////////////////////////////////////
|
||||
|
||||
getCommunity = function(limit, focus) {
|
||||
if (isRunning) {
|
||||
throw "Still running.";
|
||||
}
|
||||
|
@ -158,17 +456,6 @@ function NodeReducer(nodes, edges) {
|
|||
res = [],
|
||||
dist = {},
|
||||
best,
|
||||
getBest = function (communities) {
|
||||
var bestQ = Number.NEGATIVE_INFINITY,
|
||||
bestC;
|
||||
_.each(communities, function (obj) {
|
||||
if (obj.q > bestQ) {
|
||||
bestQ = obj.q;
|
||||
bestC = obj.nodes;
|
||||
}
|
||||
});
|
||||
return bestC;
|
||||
},
|
||||
sortByDistance = function (a, b) {
|
||||
var d1 = dist[_.min(a,minDist(dist))],
|
||||
d2 = dist[_.min(b,minDist(dist))],
|
||||
|
@ -182,13 +469,13 @@ function NodeReducer(nodes, edges) {
|
|||
isRunning = false;
|
||||
throw "Load some nodes first.";
|
||||
}
|
||||
joiner.setup();
|
||||
best = joiner.getBest();
|
||||
setup();
|
||||
best = getBest();
|
||||
while (best !== null) {
|
||||
joiner.joinCommunity(best);
|
||||
best = joiner.getBest();
|
||||
joinCommunity(best);
|
||||
best = getBest();
|
||||
}
|
||||
coms = joiner.getCommunities();
|
||||
coms = getCommunities();
|
||||
|
||||
if (focus !== undefined) {
|
||||
_.each(coms, function(obj, key) {
|
||||
|
@ -204,10 +491,10 @@ function NodeReducer(nodes, edges) {
|
|||
return res[0];
|
||||
}
|
||||
isRunning = false;
|
||||
return getBest(coms);
|
||||
};
|
||||
return getBestCommunity(coms);
|
||||
},
|
||||
|
||||
self.bucketNodes = function(toSort, numBuckets) {
|
||||
bucketNodes = function(toSort, numBuckets) {
|
||||
var res = [],
|
||||
threshold = 0.5;
|
||||
if (toSort.length <= numBuckets) {
|
||||
|
@ -236,4 +523,28 @@ function NodeReducer(nodes, edges) {
|
|||
return res;
|
||||
};
|
||||
|
||||
////////////////////////////////////
|
||||
// Public functions //
|
||||
////////////////////////////////////
|
||||
|
||||
this.getAdjacencyMatrix = getAdjacencyMatrix;
|
||||
|
||||
this.getHeap = getHeap;
|
||||
|
||||
this.getDQ = getDQ;
|
||||
|
||||
this.getDegrees = getDegrees;
|
||||
|
||||
this.getCommunities = getCommunities;
|
||||
|
||||
this.getBest = getBest;
|
||||
|
||||
this.setup = setup;
|
||||
|
||||
this.joinCommunity = joinCommunity;
|
||||
|
||||
this.getCommunity = getCommunity;
|
||||
|
||||
this.bucketNodes = bucketNodes;
|
||||
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue