gitnexus 1.2.6 → 1.2.8

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.
@@ -1,355 +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;
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;