mistagent 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mistagent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "MistAgent - Terminal AI Assistant",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"mist": "dist/index.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
|
-
"dist/"
|
|
11
|
+
"dist/",
|
|
12
|
+
"vendor/"
|
|
12
13
|
],
|
|
13
14
|
"license": "MIT",
|
|
14
15
|
"keywords": [
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graphology Leiden Algorithm
|
|
3
|
+
* ============================
|
|
4
|
+
*
|
|
5
|
+
* JavaScript implementation of the Leiden community detection
|
|
6
|
+
* algorithm for graphology.
|
|
7
|
+
*
|
|
8
|
+
* Vendored from: https://github.com/graphology/graphology/tree/master/src/communities-leiden
|
|
9
|
+
* License: MIT
|
|
10
|
+
*
|
|
11
|
+
* [Reference]
|
|
12
|
+
* Traag, V. A., et al. "From Louvain to Leiden: Guaranteeing Well-Connected
|
|
13
|
+
* Communities". Scientific Reports, vol. 9, no 1, 2019, p. 5233.
|
|
14
|
+
* https://arxiv.org/abs/1810.08473
|
|
15
|
+
*/
|
|
16
|
+
var resolveDefaults = require('graphology-utils/defaults');
|
|
17
|
+
var isGraph = require('graphology-utils/is-graph');
|
|
18
|
+
var inferType = require('graphology-utils/infer-type');
|
|
19
|
+
var SparseMap = require('mnemonist/sparse-map');
|
|
20
|
+
var SparseQueueSet = require('mnemonist/sparse-queue-set');
|
|
21
|
+
var createRandomIndex = require('pandemonium/random-index').createRandomIndex;
|
|
22
|
+
var utils = require('./utils.cjs');
|
|
23
|
+
|
|
24
|
+
var indices = require('graphology-indices/louvain');
|
|
25
|
+
var addWeightToCommunity = utils.addWeightToCommunity;
|
|
26
|
+
|
|
27
|
+
var UndirectedLouvainIndex = indices.UndirectedLouvainIndex;
|
|
28
|
+
|
|
29
|
+
var UndirectedLeidenAddenda = utils.UndirectedLeidenAddenda;
|
|
30
|
+
|
|
31
|
+
var DEFAULTS = {
|
|
32
|
+
attributes: {
|
|
33
|
+
community: 'community',
|
|
34
|
+
weight: 'weight'
|
|
35
|
+
},
|
|
36
|
+
randomness: 0.01,
|
|
37
|
+
randomWalk: true,
|
|
38
|
+
resolution: 1,
|
|
39
|
+
rng: Math.random,
|
|
40
|
+
weighted: false
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
var EPSILON = 1e-10;
|
|
44
|
+
|
|
45
|
+
function tieBreaker(
|
|
46
|
+
bestCommunity,
|
|
47
|
+
currentCommunity,
|
|
48
|
+
targetCommunity,
|
|
49
|
+
delta,
|
|
50
|
+
bestDelta
|
|
51
|
+
) {
|
|
52
|
+
if (Math.abs(delta - bestDelta) < EPSILON) {
|
|
53
|
+
if (bestCommunity === currentCommunity) {
|
|
54
|
+
return false;
|
|
55
|
+
} else {
|
|
56
|
+
return targetCommunity > bestCommunity;
|
|
57
|
+
}
|
|
58
|
+
} else if (delta > bestDelta) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function undirectedLeiden(detailed, graph, options) {
|
|
66
|
+
var index = new UndirectedLouvainIndex(graph, {
|
|
67
|
+
attributes: {
|
|
68
|
+
weight: options.attributes.weight
|
|
69
|
+
},
|
|
70
|
+
keepDendrogram: detailed,
|
|
71
|
+
resolution: options.resolution,
|
|
72
|
+
weighted: options.weighted
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
var addenda = new UndirectedLeidenAddenda(index, {
|
|
76
|
+
randomness: options.randomness,
|
|
77
|
+
rng: options.rng
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
var randomIndex = createRandomIndex(options.rng);
|
|
81
|
+
|
|
82
|
+
// Communities
|
|
83
|
+
var currentCommunity, targetCommunity;
|
|
84
|
+
var communities = new SparseMap(Float64Array, index.C);
|
|
85
|
+
|
|
86
|
+
// Traversal
|
|
87
|
+
var queue = new SparseQueueSet(index.C),
|
|
88
|
+
start,
|
|
89
|
+
end,
|
|
90
|
+
weight,
|
|
91
|
+
ci,
|
|
92
|
+
ri,
|
|
93
|
+
s,
|
|
94
|
+
i,
|
|
95
|
+
j,
|
|
96
|
+
l;
|
|
97
|
+
|
|
98
|
+
// Metrics
|
|
99
|
+
var degree, targetCommunityDegree;
|
|
100
|
+
|
|
101
|
+
// Moves
|
|
102
|
+
var bestCommunity, bestDelta, deltaIsBetter, delta;
|
|
103
|
+
|
|
104
|
+
// Details
|
|
105
|
+
var deltaComputations = 0,
|
|
106
|
+
nodesVisited = 0,
|
|
107
|
+
moves = [],
|
|
108
|
+
currentMoves;
|
|
109
|
+
|
|
110
|
+
while (true) {
|
|
111
|
+
l = index.C;
|
|
112
|
+
|
|
113
|
+
currentMoves = 0;
|
|
114
|
+
|
|
115
|
+
// Traversal of the graph
|
|
116
|
+
ri = options.randomWalk ? randomIndex(l) : 0;
|
|
117
|
+
|
|
118
|
+
for (s = 0; s < l; s++, ri++) {
|
|
119
|
+
i = ri % l;
|
|
120
|
+
queue.enqueue(i);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
while (queue.size !== 0) {
|
|
124
|
+
i = queue.dequeue();
|
|
125
|
+
nodesVisited++;
|
|
126
|
+
|
|
127
|
+
degree = 0;
|
|
128
|
+
communities.clear();
|
|
129
|
+
|
|
130
|
+
currentCommunity = index.belongings[i];
|
|
131
|
+
|
|
132
|
+
start = index.starts[i];
|
|
133
|
+
end = index.starts[i + 1];
|
|
134
|
+
|
|
135
|
+
// Traversing neighbors
|
|
136
|
+
for (; start < end; start++) {
|
|
137
|
+
j = index.neighborhood[start];
|
|
138
|
+
weight = index.weights[start];
|
|
139
|
+
|
|
140
|
+
targetCommunity = index.belongings[j];
|
|
141
|
+
|
|
142
|
+
// Incrementing metrics
|
|
143
|
+
degree += weight;
|
|
144
|
+
addWeightToCommunity(communities, targetCommunity, weight);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Finding best community to move to
|
|
148
|
+
bestDelta = index.fastDeltaWithOwnCommunity(
|
|
149
|
+
i,
|
|
150
|
+
degree,
|
|
151
|
+
communities.get(currentCommunity) || 0,
|
|
152
|
+
currentCommunity
|
|
153
|
+
);
|
|
154
|
+
bestCommunity = currentCommunity;
|
|
155
|
+
|
|
156
|
+
for (ci = 0; ci < communities.size; ci++) {
|
|
157
|
+
targetCommunity = communities.dense[ci];
|
|
158
|
+
|
|
159
|
+
if (targetCommunity === currentCommunity) continue;
|
|
160
|
+
|
|
161
|
+
targetCommunityDegree = communities.vals[ci];
|
|
162
|
+
|
|
163
|
+
deltaComputations++;
|
|
164
|
+
|
|
165
|
+
delta = index.fastDelta(
|
|
166
|
+
i,
|
|
167
|
+
degree,
|
|
168
|
+
targetCommunityDegree,
|
|
169
|
+
targetCommunity
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
deltaIsBetter = tieBreaker(
|
|
173
|
+
bestCommunity,
|
|
174
|
+
currentCommunity,
|
|
175
|
+
targetCommunity,
|
|
176
|
+
delta,
|
|
177
|
+
bestDelta
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
if (deltaIsBetter) {
|
|
181
|
+
bestDelta = delta;
|
|
182
|
+
bestCommunity = targetCommunity;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (bestDelta < 0) {
|
|
187
|
+
bestCommunity = index.isolate(i, degree);
|
|
188
|
+
|
|
189
|
+
if (bestCommunity === currentCommunity) continue;
|
|
190
|
+
} else {
|
|
191
|
+
if (bestCommunity === currentCommunity) {
|
|
192
|
+
continue;
|
|
193
|
+
} else {
|
|
194
|
+
index.move(i, degree, bestCommunity);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
currentMoves++;
|
|
199
|
+
|
|
200
|
+
// Adding neighbors from other communities to the queue
|
|
201
|
+
start = index.starts[i];
|
|
202
|
+
end = index.starts[i + 1];
|
|
203
|
+
|
|
204
|
+
for (; start < end; start++) {
|
|
205
|
+
j = index.neighborhood[start];
|
|
206
|
+
targetCommunity = index.belongings[j];
|
|
207
|
+
|
|
208
|
+
if (targetCommunity !== bestCommunity) queue.enqueue(j);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
moves.push(currentMoves);
|
|
213
|
+
|
|
214
|
+
if (currentMoves === 0) {
|
|
215
|
+
index.zoomOut();
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (!addenda.onlySingletons()) {
|
|
220
|
+
// We continue working on the induced graph
|
|
221
|
+
addenda.zoomOut();
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
var results = {
|
|
229
|
+
index: index,
|
|
230
|
+
deltaComputations: deltaComputations,
|
|
231
|
+
nodesVisited: nodesVisited,
|
|
232
|
+
moves: moves
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
return results;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Function returning the communities mapping of the graph.
|
|
240
|
+
*
|
|
241
|
+
* @param {boolean} assign - Assign communities to nodes attributes?
|
|
242
|
+
* @param {boolean} detailed - Whether to return detailed information.
|
|
243
|
+
* @param {Graph} graph - Target graph.
|
|
244
|
+
* @param {object} options - Options:
|
|
245
|
+
* @param {object} attributes - Attribute names:
|
|
246
|
+
* @param {string} community - Community node attribute name.
|
|
247
|
+
* @param {string} weight - Weight edge attribute name.
|
|
248
|
+
* @param {number} randomness - Randomness parameter.
|
|
249
|
+
* @param {boolean} randomWalk - Whether to traverse the graph in random order.
|
|
250
|
+
* @param {number} resolution - Resolution parameter.
|
|
251
|
+
* @param {function} rng - RNG function to use.
|
|
252
|
+
* @param {boolean} weighted - Whether to compute the weighted version.
|
|
253
|
+
* @return {object}
|
|
254
|
+
*/
|
|
255
|
+
function leiden(assign, detailed, graph, options) {
|
|
256
|
+
if (!isGraph(graph))
|
|
257
|
+
throw new Error(
|
|
258
|
+
'graphology-communities-leiden: the given graph is not a valid graphology instance.'
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
var type = inferType(graph);
|
|
262
|
+
|
|
263
|
+
if (type === 'mixed')
|
|
264
|
+
throw new Error(
|
|
265
|
+
'graphology-communities-leiden: cannot run the algorithm on a true mixed graph.'
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
if (type === 'directed')
|
|
269
|
+
throw new Error(
|
|
270
|
+
'graphology-communities-leiden: not yet implemented for directed graphs.'
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
// Attributes name
|
|
274
|
+
options = resolveDefaults(options, DEFAULTS);
|
|
275
|
+
|
|
276
|
+
// Empty graph case
|
|
277
|
+
var c = 0;
|
|
278
|
+
|
|
279
|
+
if (graph.size === 0) {
|
|
280
|
+
if (assign) {
|
|
281
|
+
graph.forEachNode(function (node) {
|
|
282
|
+
graph.setNodeAttribute(node, options.attributes.communities, c++);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
var communities = {};
|
|
289
|
+
|
|
290
|
+
graph.forEachNode(function (node) {
|
|
291
|
+
communities[node] = c++;
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
if (!detailed) return communities;
|
|
295
|
+
|
|
296
|
+
return {
|
|
297
|
+
communities: communities,
|
|
298
|
+
count: graph.order,
|
|
299
|
+
deltaComputations: 0,
|
|
300
|
+
dendrogram: null,
|
|
301
|
+
level: 0,
|
|
302
|
+
modularity: NaN,
|
|
303
|
+
moves: null,
|
|
304
|
+
nodesVisited: 0,
|
|
305
|
+
resolution: options.resolution
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
var fn = undirectedLeiden;
|
|
310
|
+
|
|
311
|
+
var results = fn(detailed, graph, options);
|
|
312
|
+
|
|
313
|
+
var index = results.index;
|
|
314
|
+
|
|
315
|
+
// Standard output
|
|
316
|
+
if (!detailed) {
|
|
317
|
+
if (assign) {
|
|
318
|
+
index.assign(options.attributes.community);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return index.collect();
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Detailed output
|
|
326
|
+
var output = {
|
|
327
|
+
count: index.C,
|
|
328
|
+
deltaComputations: results.deltaComputations,
|
|
329
|
+
dendrogram: index.dendrogram,
|
|
330
|
+
level: index.level,
|
|
331
|
+
modularity: index.modularity(),
|
|
332
|
+
moves: results.moves,
|
|
333
|
+
nodesVisited: results.nodesVisited,
|
|
334
|
+
resolution: options.resolution
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
if (assign) {
|
|
338
|
+
index.assign(options.attributes.community);
|
|
339
|
+
return output;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
output.communities = index.collect();
|
|
343
|
+
|
|
344
|
+
return output;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Exporting.
|
|
349
|
+
*/
|
|
350
|
+
var fn = leiden.bind(null, false, false);
|
|
351
|
+
fn.assign = leiden.bind(null, true, false);
|
|
352
|
+
fn.detailed = leiden.bind(null, false, true);
|
|
353
|
+
fn.defaults = DEFAULTS;
|
|
354
|
+
|
|
355
|
+
module.exports = fn;
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graphology Leiden Utils
|
|
3
|
+
* ========================
|
|
4
|
+
*
|
|
5
|
+
* Miscellaneous utilities used by the Leiden algorithm.
|
|
6
|
+
*
|
|
7
|
+
* Vendored from: https://github.com/graphology/graphology/tree/master/src/communities-leiden
|
|
8
|
+
* License: MIT
|
|
9
|
+
*/
|
|
10
|
+
var SparseMap = require('mnemonist/sparse-map');
|
|
11
|
+
var createRandom = require('pandemonium/random').createRandom;
|
|
12
|
+
|
|
13
|
+
function addWeightToCommunity(map, community, weight) {
|
|
14
|
+
var currentWeight = map.get(community);
|
|
15
|
+
|
|
16
|
+
if (typeof currentWeight === 'undefined') currentWeight = 0;
|
|
17
|
+
|
|
18
|
+
currentWeight += weight;
|
|
19
|
+
|
|
20
|
+
map.set(community, currentWeight);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function UndirectedLeidenAddenda(index, options) {
|
|
24
|
+
options = options || {};
|
|
25
|
+
|
|
26
|
+
var rng = options.rng || Math.random;
|
|
27
|
+
var randomness = 'randomness' in options ? options.randomness : 0.01;
|
|
28
|
+
|
|
29
|
+
this.index = index;
|
|
30
|
+
this.random = createRandom(rng);
|
|
31
|
+
this.randomness = randomness;
|
|
32
|
+
this.rng = rng;
|
|
33
|
+
|
|
34
|
+
var NodesPointerArray = index.counts.constructor;
|
|
35
|
+
var WeightsArray = index.weights.constructor;
|
|
36
|
+
|
|
37
|
+
var order = index.C;
|
|
38
|
+
this.resolution = index.resolution;
|
|
39
|
+
|
|
40
|
+
// Used to group nodes by communities
|
|
41
|
+
this.B = index.C;
|
|
42
|
+
this.C = 0;
|
|
43
|
+
this.communitiesOffsets = new NodesPointerArray(order);
|
|
44
|
+
this.nodesSortedByCommunities = new NodesPointerArray(order);
|
|
45
|
+
this.communitiesBounds = new NodesPointerArray(order + 1);
|
|
46
|
+
|
|
47
|
+
// Used to merge nodes subsets
|
|
48
|
+
this.communityWeights = new WeightsArray(order);
|
|
49
|
+
this.degrees = new WeightsArray(order);
|
|
50
|
+
this.nonSingleton = new Uint8Array(order);
|
|
51
|
+
this.externalEdgeWeightPerCommunity = new WeightsArray(order);
|
|
52
|
+
this.belongings = new NodesPointerArray(order);
|
|
53
|
+
this.neighboringCommunities = new SparseMap(WeightsArray, order);
|
|
54
|
+
this.cumulativeIncrement = new Float64Array(order);
|
|
55
|
+
this.macroCommunities = null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
UndirectedLeidenAddenda.prototype.groupByCommunities = function () {
|
|
59
|
+
var index = this.index;
|
|
60
|
+
|
|
61
|
+
var n, i, c, b, o;
|
|
62
|
+
|
|
63
|
+
n = 0;
|
|
64
|
+
o = 0;
|
|
65
|
+
|
|
66
|
+
for (i = 0; i < index.C; i++) {
|
|
67
|
+
c = index.counts[i];
|
|
68
|
+
|
|
69
|
+
if (c !== 0) {
|
|
70
|
+
this.communitiesBounds[o++] = n;
|
|
71
|
+
n += c;
|
|
72
|
+
this.communitiesOffsets[i] = n;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this.communitiesBounds[o] = n;
|
|
77
|
+
|
|
78
|
+
o = 0;
|
|
79
|
+
|
|
80
|
+
for (i = 0; i < index.C; i++) {
|
|
81
|
+
b = index.belongings[i];
|
|
82
|
+
o = --this.communitiesOffsets[b];
|
|
83
|
+
this.nodesSortedByCommunities[o] = i;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
this.B = index.C - index.U;
|
|
87
|
+
this.C = index.C;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
UndirectedLeidenAddenda.prototype.communities = function () {
|
|
91
|
+
var communities = new Array(this.B);
|
|
92
|
+
|
|
93
|
+
var i, j, community, start, stop;
|
|
94
|
+
|
|
95
|
+
for (i = 0; i < this.B; i++) {
|
|
96
|
+
start = this.communitiesBounds[i];
|
|
97
|
+
stop = this.communitiesBounds[i + 1];
|
|
98
|
+
community = [];
|
|
99
|
+
|
|
100
|
+
for (j = start; j < stop; j++) {
|
|
101
|
+
community.push(j);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
communities[i] = community;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return communities;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
UndirectedLeidenAddenda.prototype.mergeNodesSubset = function (start, stop) {
|
|
111
|
+
var index = this.index;
|
|
112
|
+
var currentMacroCommunity =
|
|
113
|
+
index.belongings[this.nodesSortedByCommunities[start]];
|
|
114
|
+
var neighboringCommunities = this.neighboringCommunities;
|
|
115
|
+
|
|
116
|
+
var totalNodeWeight = 0;
|
|
117
|
+
|
|
118
|
+
var i, j, w;
|
|
119
|
+
var ei, el, et;
|
|
120
|
+
|
|
121
|
+
// Initializing singletons
|
|
122
|
+
for (j = start; j < stop; j++) {
|
|
123
|
+
i = this.nodesSortedByCommunities[j];
|
|
124
|
+
|
|
125
|
+
this.belongings[i] = i;
|
|
126
|
+
this.nonSingleton[i] = 0;
|
|
127
|
+
this.degrees[i] = 0;
|
|
128
|
+
totalNodeWeight += index.loops[i] / 2;
|
|
129
|
+
|
|
130
|
+
this.communityWeights[i] = index.loops[i];
|
|
131
|
+
this.externalEdgeWeightPerCommunity[i] = 0;
|
|
132
|
+
|
|
133
|
+
ei = index.starts[i];
|
|
134
|
+
el = index.starts[i + 1];
|
|
135
|
+
|
|
136
|
+
for (; ei < el; ei++) {
|
|
137
|
+
et = index.neighborhood[ei];
|
|
138
|
+
w = index.weights[ei];
|
|
139
|
+
|
|
140
|
+
this.degrees[i] += w;
|
|
141
|
+
|
|
142
|
+
if (index.belongings[et] !== currentMacroCommunity) continue;
|
|
143
|
+
|
|
144
|
+
totalNodeWeight += w;
|
|
145
|
+
this.externalEdgeWeightPerCommunity[i] += w;
|
|
146
|
+
this.communityWeights[i] += w;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
var microDegrees = this.externalEdgeWeightPerCommunity.slice();
|
|
151
|
+
|
|
152
|
+
var s, ri, ci;
|
|
153
|
+
var order = stop - start;
|
|
154
|
+
|
|
155
|
+
var degree,
|
|
156
|
+
bestCommunity,
|
|
157
|
+
qualityValueIncrement,
|
|
158
|
+
maxQualityValueIncrement,
|
|
159
|
+
totalTransformedQualityValueIncrement,
|
|
160
|
+
targetCommunity,
|
|
161
|
+
targetCommunityDegree,
|
|
162
|
+
targetCommunityWeight;
|
|
163
|
+
|
|
164
|
+
var r, lo, hi, mid, chosenCommunity;
|
|
165
|
+
|
|
166
|
+
ri = this.random(start, stop - 1);
|
|
167
|
+
|
|
168
|
+
for (s = start; s < stop; s++, ri++) {
|
|
169
|
+
j = start + (ri % order);
|
|
170
|
+
|
|
171
|
+
i = this.nodesSortedByCommunities[j];
|
|
172
|
+
|
|
173
|
+
if (this.nonSingleton[i] === 1) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (
|
|
178
|
+
this.externalEdgeWeightPerCommunity[i] <
|
|
179
|
+
this.communityWeights[i] *
|
|
180
|
+
(totalNodeWeight / 2 - this.communityWeights[i]) *
|
|
181
|
+
this.resolution
|
|
182
|
+
) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
this.communityWeights[i] = 0;
|
|
187
|
+
this.externalEdgeWeightPerCommunity[i] = 0;
|
|
188
|
+
|
|
189
|
+
neighboringCommunities.clear();
|
|
190
|
+
neighboringCommunities.set(i, 0);
|
|
191
|
+
|
|
192
|
+
degree = 0;
|
|
193
|
+
|
|
194
|
+
ei = index.starts[i];
|
|
195
|
+
el = index.starts[i + 1];
|
|
196
|
+
|
|
197
|
+
for (; ei < el; ei++) {
|
|
198
|
+
et = index.neighborhood[ei];
|
|
199
|
+
|
|
200
|
+
if (index.belongings[et] !== currentMacroCommunity) continue;
|
|
201
|
+
|
|
202
|
+
w = index.weights[ei];
|
|
203
|
+
|
|
204
|
+
degree += w;
|
|
205
|
+
|
|
206
|
+
addWeightToCommunity(neighboringCommunities, this.belongings[et], w);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
bestCommunity = i;
|
|
210
|
+
maxQualityValueIncrement = 0;
|
|
211
|
+
totalTransformedQualityValueIncrement = 0;
|
|
212
|
+
|
|
213
|
+
for (ci = 0; ci < neighboringCommunities.size; ci++) {
|
|
214
|
+
targetCommunity = neighboringCommunities.dense[ci];
|
|
215
|
+
targetCommunityDegree = neighboringCommunities.vals[ci];
|
|
216
|
+
targetCommunityWeight = this.communityWeights[targetCommunity];
|
|
217
|
+
|
|
218
|
+
if (
|
|
219
|
+
this.externalEdgeWeightPerCommunity[targetCommunity] >=
|
|
220
|
+
targetCommunityWeight *
|
|
221
|
+
(totalNodeWeight / 2 - targetCommunityWeight) *
|
|
222
|
+
this.resolution
|
|
223
|
+
) {
|
|
224
|
+
qualityValueIncrement =
|
|
225
|
+
targetCommunityDegree -
|
|
226
|
+
((degree + index.loops[i]) *
|
|
227
|
+
targetCommunityWeight *
|
|
228
|
+
this.resolution) /
|
|
229
|
+
totalNodeWeight;
|
|
230
|
+
|
|
231
|
+
if (qualityValueIncrement > maxQualityValueIncrement) {
|
|
232
|
+
bestCommunity = targetCommunity;
|
|
233
|
+
maxQualityValueIncrement = qualityValueIncrement;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (qualityValueIncrement >= 0)
|
|
237
|
+
totalTransformedQualityValueIncrement += Math.exp(
|
|
238
|
+
qualityValueIncrement / this.randomness
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
this.cumulativeIncrement[ci] = totalTransformedQualityValueIncrement;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (
|
|
246
|
+
totalTransformedQualityValueIncrement < Number.MAX_VALUE &&
|
|
247
|
+
totalTransformedQualityValueIncrement < Infinity
|
|
248
|
+
) {
|
|
249
|
+
r = totalTransformedQualityValueIncrement * this.rng();
|
|
250
|
+
lo = -1;
|
|
251
|
+
hi = neighboringCommunities.size + 1;
|
|
252
|
+
|
|
253
|
+
while (lo < hi - 1) {
|
|
254
|
+
mid = (lo + hi) >>> 1;
|
|
255
|
+
|
|
256
|
+
if (this.cumulativeIncrement[mid] >= r) hi = mid;
|
|
257
|
+
else lo = mid;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
chosenCommunity = neighboringCommunities.dense[hi];
|
|
261
|
+
} else {
|
|
262
|
+
chosenCommunity = bestCommunity;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
this.communityWeights[chosenCommunity] += degree + index.loops[i];
|
|
266
|
+
|
|
267
|
+
ei = index.starts[i];
|
|
268
|
+
el = index.starts[i + 1];
|
|
269
|
+
|
|
270
|
+
for (; ei < el; ei++) {
|
|
271
|
+
et = index.neighborhood[ei];
|
|
272
|
+
|
|
273
|
+
if (index.belongings[et] !== currentMacroCommunity) continue;
|
|
274
|
+
|
|
275
|
+
targetCommunity = this.belongings[et];
|
|
276
|
+
|
|
277
|
+
if (targetCommunity === chosenCommunity) {
|
|
278
|
+
this.externalEdgeWeightPerCommunity[chosenCommunity] -=
|
|
279
|
+
microDegrees[et];
|
|
280
|
+
} else {
|
|
281
|
+
this.externalEdgeWeightPerCommunity[chosenCommunity] +=
|
|
282
|
+
microDegrees[et];
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (chosenCommunity !== i) {
|
|
287
|
+
this.belongings[i] = chosenCommunity;
|
|
288
|
+
this.nonSingleton[chosenCommunity] = 1;
|
|
289
|
+
this.C--;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
var microCommunities = this.neighboringCommunities;
|
|
294
|
+
microCommunities.clear();
|
|
295
|
+
|
|
296
|
+
for (j = start; j < stop; j++) {
|
|
297
|
+
i = this.nodesSortedByCommunities[j];
|
|
298
|
+
microCommunities.set(this.belongings[i], 1);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return microCommunities.dense.slice(0, microCommunities.size);
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
UndirectedLeidenAddenda.prototype.refinePartition = function () {
|
|
305
|
+
this.groupByCommunities();
|
|
306
|
+
|
|
307
|
+
this.macroCommunities = new Array(this.B);
|
|
308
|
+
|
|
309
|
+
var i, start, stop, mapping;
|
|
310
|
+
|
|
311
|
+
var bounds = this.communitiesBounds;
|
|
312
|
+
|
|
313
|
+
for (i = 0; i < this.B; i++) {
|
|
314
|
+
start = bounds[i];
|
|
315
|
+
stop = bounds[i + 1];
|
|
316
|
+
|
|
317
|
+
mapping = this.mergeNodesSubset(start, stop);
|
|
318
|
+
this.macroCommunities[i] = mapping;
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
UndirectedLeidenAddenda.prototype.split = function () {
|
|
323
|
+
var index = this.index;
|
|
324
|
+
var isolates = this.neighboringCommunities;
|
|
325
|
+
|
|
326
|
+
isolates.clear();
|
|
327
|
+
|
|
328
|
+
var i, community, isolated;
|
|
329
|
+
|
|
330
|
+
for (i = 0; i < index.C; i++) {
|
|
331
|
+
community = this.belongings[i];
|
|
332
|
+
|
|
333
|
+
if (i !== community) continue;
|
|
334
|
+
|
|
335
|
+
isolated = index.isolate(i, this.degrees[i]);
|
|
336
|
+
isolates.set(community, isolated);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
for (i = 0; i < index.C; i++) {
|
|
340
|
+
community = this.belongings[i];
|
|
341
|
+
|
|
342
|
+
if (i === community) continue;
|
|
343
|
+
|
|
344
|
+
isolated = isolates.get(community);
|
|
345
|
+
index.move(i, this.degrees[i], isolated);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
var j, macro;
|
|
349
|
+
|
|
350
|
+
for (i = 0; i < this.macroCommunities.length; i++) {
|
|
351
|
+
macro = this.macroCommunities[i];
|
|
352
|
+
|
|
353
|
+
for (j = 0; j < macro.length; j++) macro[j] = isolates.get(macro[j]);
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
UndirectedLeidenAddenda.prototype.zoomOut = function () {
|
|
358
|
+
var index = this.index;
|
|
359
|
+
this.refinePartition();
|
|
360
|
+
this.split();
|
|
361
|
+
|
|
362
|
+
var newLabels = index.zoomOut();
|
|
363
|
+
|
|
364
|
+
var macro, leader, follower;
|
|
365
|
+
|
|
366
|
+
var i, j;
|
|
367
|
+
|
|
368
|
+
for (i = 0; i < this.macroCommunities.length; i++) {
|
|
369
|
+
macro = this.macroCommunities[i];
|
|
370
|
+
leader = newLabels[macro[0]];
|
|
371
|
+
|
|
372
|
+
for (j = 1; j < macro.length; j++) {
|
|
373
|
+
follower = newLabels[macro[j]];
|
|
374
|
+
index.expensiveMove(follower, leader);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
UndirectedLeidenAddenda.prototype.onlySingletons = function () {
|
|
380
|
+
var index = this.index;
|
|
381
|
+
|
|
382
|
+
var i;
|
|
383
|
+
|
|
384
|
+
for (i = 0; i < index.C; i++) {
|
|
385
|
+
if (index.counts[i] > 1) return false;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return true;
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
exports.addWeightToCommunity = addWeightToCommunity;
|
|
392
|
+
exports.UndirectedLeidenAddenda = UndirectedLeidenAddenda;
|