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.
- package/README.md +9 -10
- package/dist/cli/analyze.d.ts +1 -1
- package/dist/cli/analyze.js +59 -15
- package/dist/cli/eval-server.js +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/mcp.js +1 -1
- package/dist/core/augmentation/engine.js +20 -20
- package/dist/core/embeddings/embedder.js +7 -0
- package/dist/core/embeddings/embedding-pipeline.js +26 -26
- package/dist/core/ingestion/cluster-enricher.js +16 -16
- package/dist/core/ingestion/filesystem-walker.js +17 -3
- package/dist/core/ingestion/parsing-processor.js +4 -1
- package/dist/core/ingestion/workers/parse-worker.js +13 -4
- package/dist/core/ingestion/workers/worker-pool.js +43 -9
- package/dist/core/kuzu/kuzu-adapter.js +9 -9
- package/dist/core/search/hybrid-search.js +3 -3
- package/dist/core/wiki/graph-queries.js +52 -52
- package/dist/core/wiki/prompts.js +82 -82
- package/dist/mcp/local/local-backend.d.ts +18 -3
- package/dist/mcp/local/local-backend.js +57 -13
- package/dist/mcp/resources.js +4 -4
- package/hooks/claude/gitnexus-hook.cjs +135 -135
- package/hooks/claude/pre-tool-use.sh +78 -78
- package/hooks/claude/session-start.sh +42 -42
- package/package.json +1 -1
- package/vendor/leiden/index.cjs +355 -355
- package/vendor/leiden/utils.cjs +392 -392
package/vendor/leiden/index.cjs
CHANGED
|
@@ -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;
|