@graphty/algorithms 2.0.6 → 2.1.1
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 -11
- package/dist/algorithms.js +499 -30
- package/dist/algorithms.js.map +1 -1
- package/dist/algorithms.standalone.js +500 -31
- package/dist/algorithms.standalone.js.map +1 -1
- package/dist/src/algorithms/centrality/betweenness.js +2 -2
- package/dist/src/algorithms/centrality/betweenness.js.map +1 -1
- package/dist/src/algorithms/community/girvan-newman.js +1 -1
- package/dist/src/algorithms/community/girvan-newman.js.map +1 -1
- package/dist/src/algorithms/matching/isomorphism.js +3 -3
- package/dist/src/algorithms/matching/isomorphism.js.map +1 -1
- package/dist/src/algorithms/mst/prim.js +1 -1
- package/dist/src/algorithms/mst/prim.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bellman-ford.js +1 -1
- package/dist/src/algorithms/shortest-path/bellman-ford.js.map +1 -1
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js +1 -1
- package/dist/src/algorithms/shortest-path/bidirectional-dijkstra.js.map +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.js +1 -1
- package/dist/src/algorithms/shortest-path/dijkstra.js.map +1 -1
- package/dist/src/algorithms/traversal/bfs-unified.js +2 -2
- package/dist/src/algorithms/traversal/bfs-unified.js.map +1 -1
- package/dist/src/algorithms/traversal/dfs.js +2 -2
- package/dist/src/algorithms/traversal/dfs.js.map +1 -1
- package/dist/src/clustering/spectral.js +2 -2
- package/dist/src/clustering/spectral.js.map +1 -1
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/indexed/accelerator.d.ts +14 -2
- package/dist/src/indexed/accelerator.d.ts.map +1 -1
- package/dist/src/indexed/accelerator.js +8 -0
- package/dist/src/indexed/accelerator.js.map +1 -1
- package/dist/src/indexed/components.d.ts +8 -0
- package/dist/src/indexed/components.d.ts.map +1 -1
- package/dist/src/indexed/components.js +8 -1
- package/dist/src/indexed/components.js.map +1 -1
- package/dist/src/indexed/hits.d.ts +39 -0
- package/dist/src/indexed/hits.d.ts.map +1 -0
- package/dist/src/indexed/hits.js +95 -0
- package/dist/src/indexed/hits.js.map +1 -0
- package/dist/src/indexed/index.d.ts +5 -1
- package/dist/src/indexed/index.d.ts.map +1 -1
- package/dist/src/indexed/index.js +5 -1
- package/dist/src/indexed/index.js.map +1 -1
- package/dist/src/indexed/k-core.d.ts +32 -0
- package/dist/src/indexed/k-core.d.ts.map +1 -0
- package/dist/src/indexed/k-core.js +118 -0
- package/dist/src/indexed/k-core.js.map +1 -0
- package/dist/src/indexed/katz.d.ts +38 -0
- package/dist/src/indexed/katz.d.ts.map +1 -0
- package/dist/src/indexed/katz.js +61 -0
- package/dist/src/indexed/katz.js.map +1 -0
- package/dist/src/indexed/louvain.d.ts +33 -0
- package/dist/src/indexed/louvain.d.ts.map +1 -0
- package/dist/src/indexed/louvain.js +301 -0
- package/dist/src/indexed/louvain.js.map +1 -0
- package/package.json +6 -6
- package/src/algorithms/centrality/betweenness.ts +2 -2
- package/src/algorithms/community/girvan-newman.ts +1 -1
- package/src/algorithms/matching/isomorphism.ts +3 -3
- package/src/algorithms/mst/prim.ts +1 -1
- package/src/algorithms/shortest-path/bellman-ford.ts +3 -3
- package/src/algorithms/shortest-path/bidirectional-dijkstra.ts +1 -1
- package/src/algorithms/shortest-path/dijkstra.ts +1 -1
- package/src/algorithms/traversal/bfs-unified.ts +2 -2
- package/src/algorithms/traversal/dfs.ts +2 -2
- package/src/clustering/spectral.ts +2 -2
- package/src/index.ts +10 -0
- package/src/indexed/accelerator.ts +26 -2
- package/src/indexed/components.ts +8 -1
- package/src/indexed/hits.ts +126 -0
- package/src/indexed/index.ts +5 -7
- package/src/indexed/k-core.ts +138 -0
- package/src/indexed/katz.ts +88 -0
- package/src/indexed/louvain.ts +345 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"katz.js","sourceRoot":"","sources":["../../../src/indexed/katz.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type GraphSnapshot } from "@graphty/graph-format";
|
|
2
|
+
import { type LabelResult } from "./components.js";
|
|
3
|
+
/** Options of the index-based Louvain, matching the legacy `louvain`. @public */
|
|
4
|
+
export interface LouvainOptions {
|
|
5
|
+
/** Resolution gamma: above 1 favours smaller communities; default 1. */
|
|
6
|
+
readonly resolution?: number | undefined;
|
|
7
|
+
/** Cap on aggregation levels, and on node visits per node within a level; default 100. */
|
|
8
|
+
readonly maxIterations?: number | undefined;
|
|
9
|
+
/** Stop when a level improves modularity by less than this; default 1e-6. */
|
|
10
|
+
readonly tolerance?: number | undefined;
|
|
11
|
+
}
|
|
12
|
+
/** Result of the index-based Louvain: a partition plus the modularity it reaches. @public */
|
|
13
|
+
export interface LouvainResult extends LabelResult {
|
|
14
|
+
/** Modularity of the returned partition, at the requested resolution. */
|
|
15
|
+
readonly modularity: number;
|
|
16
|
+
/** Aggregation levels that improved the partition. */
|
|
17
|
+
readonly iterations: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Louvain community detection over a snapshot: local moving to the best modularity gain, then
|
|
21
|
+
* collapse each community into a node and repeat, until a level stops paying for itself.
|
|
22
|
+
*
|
|
23
|
+
* The legacy `louvain` switches implementation at 50 nodes (`useOptimized`) and its small-graph
|
|
24
|
+
* path has no aggregation phase at all, so this port is not move-for-move identical to either
|
|
25
|
+
* branch of it -- what it guarantees is a partition whose modularity is at least as high, measured
|
|
26
|
+
* by the same formula.
|
|
27
|
+
* @param s - An undirected snapshot
|
|
28
|
+
* @param o - Algorithm options
|
|
29
|
+
* @returns The partition, its modularity, and the number of levels that improved it
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
export declare function louvain(s: GraphSnapshot, o?: LouvainOptions): LouvainResult;
|
|
33
|
+
//# sourceMappingURL=louvain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"louvain.d.ts","sourceRoot":"","sources":["../../../src/indexed/louvain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAqB,MAAM,uBAAuB,CAAC;AAE9E,OAAO,EAAE,KAAK,WAAW,EAAc,MAAM,iBAAiB,CAAC;AAE/D,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,0FAA0F;IAC1F,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3C;AAED,6FAA6F;AAC7F,MAAM,WAAW,aAAc,SAAQ,WAAW;IAC9C,yEAAyE;IACzE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,sDAAsD;IACtD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC/B;AAyQD;;;;;;;;;;;;GAYG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,GAAE,cAAmB,GAAG,aAAa,CA8C/E"}
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { renumberPartition } from "@graphty/graph-format";
|
|
2
|
+
import { withGroups } from "./components.js";
|
|
3
|
+
/**
|
|
4
|
+
* Level 0: the snapshot's own arcs, with the self-loop arcs moved into `loop`.
|
|
5
|
+
* @param s - An undirected snapshot
|
|
6
|
+
* @returns The first level
|
|
7
|
+
*/
|
|
8
|
+
function buildLevel0(s) {
|
|
9
|
+
const n = s.nodeCount;
|
|
10
|
+
const rowPtr = new Uint32Array(n + 1);
|
|
11
|
+
for (let u = 0; u < n; u++) {
|
|
12
|
+
let kept = 0;
|
|
13
|
+
const end = s.rowPtr[u + 1];
|
|
14
|
+
for (let a = s.rowPtr[u]; a < end; a++) {
|
|
15
|
+
if (s.colIdx[a] !== u) {
|
|
16
|
+
kept++;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
rowPtr[u + 1] = rowPtr[u] + kept;
|
|
20
|
+
}
|
|
21
|
+
const arcs = rowPtr[n];
|
|
22
|
+
const colIdx = new Uint32Array(arcs);
|
|
23
|
+
const w = new Float64Array(arcs);
|
|
24
|
+
const loop = new Float64Array(n);
|
|
25
|
+
const deg = new Float64Array(n);
|
|
26
|
+
let k = 0;
|
|
27
|
+
for (let u = 0; u < n; u++) {
|
|
28
|
+
let rowWeight = 0;
|
|
29
|
+
const end = s.rowPtr[u + 1];
|
|
30
|
+
for (let a = s.rowPtr[u]; a < end; a++) {
|
|
31
|
+
const v = s.colIdx[a];
|
|
32
|
+
const weight = s.weights === null ? 1 : s.weights[a];
|
|
33
|
+
if (v === u) {
|
|
34
|
+
loop[u] += weight;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
colIdx[k] = v;
|
|
38
|
+
w[k] = weight;
|
|
39
|
+
k++;
|
|
40
|
+
rowWeight += weight;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
deg[u] = rowWeight + 2 * loop[u];
|
|
44
|
+
}
|
|
45
|
+
return { n, rowPtr, colIdx, w, loop, deg };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Modularity of a partition of one level: `sum_c in_c / 2m - gamma * (tot_c / 2m)^2`, with `in_c`
|
|
49
|
+
* counting each internal edge twice. This is the same quantity the legacy `calculateModularity`
|
|
50
|
+
* computes over a `Graph`, written per arc.
|
|
51
|
+
* @param level - The level the partition is over
|
|
52
|
+
* @param comm - Community of every node of that level
|
|
53
|
+
* @param count - Number of communities, so `comm` is within `[0, count)`
|
|
54
|
+
* @param resolution - Resolution gamma
|
|
55
|
+
* @param m2 - Twice the total edge weight
|
|
56
|
+
* @returns The modularity
|
|
57
|
+
*/
|
|
58
|
+
function modularityOf(level, comm, count, resolution, m2) {
|
|
59
|
+
const inside = new Float64Array(count);
|
|
60
|
+
const tot = new Float64Array(count);
|
|
61
|
+
for (let u = 0; u < level.n; u++) {
|
|
62
|
+
const c = comm[u];
|
|
63
|
+
tot[c] += level.deg[u];
|
|
64
|
+
inside[c] += 2 * level.loop[u];
|
|
65
|
+
const end = level.rowPtr[u + 1];
|
|
66
|
+
for (let a = level.rowPtr[u]; a < end; a++) {
|
|
67
|
+
if (comm[level.colIdx[a]] === c) {
|
|
68
|
+
inside[c] += level.w[a];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
let q = 0;
|
|
73
|
+
for (let c = 0; c < count; c++) {
|
|
74
|
+
const share = tot[c] / m2;
|
|
75
|
+
q += inside[c] / m2 - resolution * share * share;
|
|
76
|
+
}
|
|
77
|
+
return q;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Phase one: move each node to the neighbouring community with the largest modularity gain, until
|
|
81
|
+
* no move is left to make.
|
|
82
|
+
*
|
|
83
|
+
* Nodes wait in a queue rather than in repeated sweeps over all of them, which is what makes this
|
|
84
|
+
* affordable on a graph with no community structure: there the last moves are worth a millionth of
|
|
85
|
+
* a point each and go on for dozens of passes, and a pass costs every node and every arc whether or
|
|
86
|
+
* not anything can move. A node enters the queue when a neighbour leaves its community, so the tail
|
|
87
|
+
* of the run costs the handful of nodes that can still move. Same fixed point, and it is the rule
|
|
88
|
+
* networkx's `louvain_communities` follows.
|
|
89
|
+
* @param level - The level to optimise
|
|
90
|
+
* @param comm - Output: community of every node, overwritten with the singleton partition first
|
|
91
|
+
* @param resolution - Resolution gamma
|
|
92
|
+
* @param m2 - Twice the total edge weight
|
|
93
|
+
* @param maxVisitsPerNode - Cap on node visits, as a multiple of the node count
|
|
94
|
+
* @returns Whether any node moved
|
|
95
|
+
*/
|
|
96
|
+
function localMove(level, comm, resolution, m2, maxVisitsPerNode) {
|
|
97
|
+
const { n } = level;
|
|
98
|
+
const tot = new Float64Array(n);
|
|
99
|
+
for (let u = 0; u < n; u++) {
|
|
100
|
+
comm[u] = u;
|
|
101
|
+
tot[u] = level.deg[u];
|
|
102
|
+
}
|
|
103
|
+
const acc = new Float64Array(n);
|
|
104
|
+
// A visit stamp rather than a clear pass: `mark` grows monotonically over the whole run, so a
|
|
105
|
+
// value left in acc by an earlier visit is never mistaken for this one's.
|
|
106
|
+
const stamp = new Int32Array(n).fill(-1);
|
|
107
|
+
const touched = new Uint32Array(n);
|
|
108
|
+
// A ring of n + 1 slots: `queued` keeps a node out of the queue twice, so n entries is the most
|
|
109
|
+
// that can be waiting.
|
|
110
|
+
const capacity = n + 1;
|
|
111
|
+
const queue = new Uint32Array(capacity);
|
|
112
|
+
const queued = new Uint8Array(n);
|
|
113
|
+
let head = 0;
|
|
114
|
+
let tail = n;
|
|
115
|
+
for (let u = 0; u < n; u++) {
|
|
116
|
+
queue[u] = u;
|
|
117
|
+
queued[u] = 1;
|
|
118
|
+
}
|
|
119
|
+
let mark = 0;
|
|
120
|
+
let visits = 0;
|
|
121
|
+
const maxVisits = maxVisitsPerNode * n;
|
|
122
|
+
let movedAny = false;
|
|
123
|
+
while (head !== tail && visits < maxVisits) {
|
|
124
|
+
const u = queue[head];
|
|
125
|
+
head = head + 1 === capacity ? 0 : head + 1;
|
|
126
|
+
queued[u] = 0;
|
|
127
|
+
visits++;
|
|
128
|
+
const cu = comm[u];
|
|
129
|
+
const ku = level.deg[u];
|
|
130
|
+
tot[cu] -= ku;
|
|
131
|
+
mark++;
|
|
132
|
+
let cnt = 0;
|
|
133
|
+
const end = level.rowPtr[u + 1];
|
|
134
|
+
for (let a = level.rowPtr[u]; a < end; a++) {
|
|
135
|
+
const c = comm[level.colIdx[a]];
|
|
136
|
+
if (stamp[c] !== mark) {
|
|
137
|
+
stamp[c] = mark;
|
|
138
|
+
acc[c] = 0;
|
|
139
|
+
touched[cnt++] = c;
|
|
140
|
+
}
|
|
141
|
+
acc[c] += level.w[a];
|
|
142
|
+
}
|
|
143
|
+
let best = cu;
|
|
144
|
+
let bestGain = (stamp[cu] === mark ? acc[cu] : 0) - (resolution * ku * tot[cu]) / m2;
|
|
145
|
+
for (let i = 0; i < cnt; i++) {
|
|
146
|
+
const c = touched[i];
|
|
147
|
+
if (c === cu) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
const gain = acc[c] - (resolution * ku * tot[c]) / m2;
|
|
151
|
+
if (gain > bestGain) {
|
|
152
|
+
bestGain = gain;
|
|
153
|
+
best = c;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
tot[best] += ku;
|
|
157
|
+
if (best !== cu) {
|
|
158
|
+
comm[u] = best;
|
|
159
|
+
movedAny = true;
|
|
160
|
+
// Only a neighbour outside the community u just joined can gain from u's move.
|
|
161
|
+
for (let a = level.rowPtr[u]; a < end; a++) {
|
|
162
|
+
const v = level.colIdx[a];
|
|
163
|
+
if (queued[v] === 0 && comm[v] !== best) {
|
|
164
|
+
queued[v] = 1;
|
|
165
|
+
queue[tail] = v;
|
|
166
|
+
tail = tail + 1 === capacity ? 0 : tail + 1;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return movedAny;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Phase two: every community becomes one node, the edges between two communities merge into one
|
|
175
|
+
* arc, and the edges inside a community become that node's self-loop. Rows are NOT sorted by
|
|
176
|
+
* neighbour index; nothing downstream of here needs them to be.
|
|
177
|
+
* @param level - The level to collapse
|
|
178
|
+
* @param comm - Dense community of every node of that level
|
|
179
|
+
* @param count - Number of communities
|
|
180
|
+
* @returns The next level, with `count` nodes
|
|
181
|
+
*/
|
|
182
|
+
function aggregate(level, comm, count) {
|
|
183
|
+
const start = new Uint32Array(count + 1);
|
|
184
|
+
for (let u = 0; u < level.n; u++) {
|
|
185
|
+
start[comm[u] + 1]++;
|
|
186
|
+
}
|
|
187
|
+
for (let c = 0; c < count; c++) {
|
|
188
|
+
start[c + 1] += start[c];
|
|
189
|
+
}
|
|
190
|
+
const members = new Uint32Array(level.n);
|
|
191
|
+
const fill = start.slice(0, count);
|
|
192
|
+
for (let u = 0; u < level.n; u++) {
|
|
193
|
+
members[fill[comm[u]]++] = u;
|
|
194
|
+
}
|
|
195
|
+
const rowPtr = new Uint32Array(count + 1);
|
|
196
|
+
const colIdx = new Uint32Array(level.colIdx.length);
|
|
197
|
+
const w = new Float64Array(level.colIdx.length);
|
|
198
|
+
const loop = new Float64Array(count);
|
|
199
|
+
const deg = new Float64Array(count);
|
|
200
|
+
const acc = new Float64Array(count);
|
|
201
|
+
const stamp = new Int32Array(count).fill(-1);
|
|
202
|
+
const touched = new Uint32Array(count);
|
|
203
|
+
let k = 0;
|
|
204
|
+
for (let c = 0; c < count; c++) {
|
|
205
|
+
rowPtr[c] = k;
|
|
206
|
+
let internalArcs = 0;
|
|
207
|
+
let cnt = 0;
|
|
208
|
+
for (let i = start[c]; i < start[c + 1]; i++) {
|
|
209
|
+
const u = members[i];
|
|
210
|
+
loop[c] += level.loop[u];
|
|
211
|
+
const end = level.rowPtr[u + 1];
|
|
212
|
+
for (let a = level.rowPtr[u]; a < end; a++) {
|
|
213
|
+
const other = comm[level.colIdx[a]];
|
|
214
|
+
if (other === c) {
|
|
215
|
+
internalArcs += level.w[a];
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (stamp[other] !== c) {
|
|
219
|
+
stamp[other] = c;
|
|
220
|
+
acc[other] = 0;
|
|
221
|
+
touched[cnt++] = other;
|
|
222
|
+
}
|
|
223
|
+
acc[other] += level.w[a];
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// An edge between two members shows up as two arcs, one from each end.
|
|
227
|
+
loop[c] += internalArcs / 2;
|
|
228
|
+
let rowWeight = 0;
|
|
229
|
+
for (let i = 0; i < cnt; i++) {
|
|
230
|
+
const other = touched[i];
|
|
231
|
+
colIdx[k] = other;
|
|
232
|
+
w[k] = acc[other];
|
|
233
|
+
rowWeight += acc[other];
|
|
234
|
+
k++;
|
|
235
|
+
}
|
|
236
|
+
deg[c] = rowWeight + 2 * loop[c];
|
|
237
|
+
}
|
|
238
|
+
rowPtr[count] = k;
|
|
239
|
+
return { n: count, rowPtr, colIdx: colIdx.subarray(0, k), w: w.subarray(0, k), loop, deg };
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Louvain community detection over a snapshot: local moving to the best modularity gain, then
|
|
243
|
+
* collapse each community into a node and repeat, until a level stops paying for itself.
|
|
244
|
+
*
|
|
245
|
+
* The legacy `louvain` switches implementation at 50 nodes (`useOptimized`) and its small-graph
|
|
246
|
+
* path has no aggregation phase at all, so this port is not move-for-move identical to either
|
|
247
|
+
* branch of it -- what it guarantees is a partition whose modularity is at least as high, measured
|
|
248
|
+
* by the same formula.
|
|
249
|
+
* @param s - An undirected snapshot
|
|
250
|
+
* @param o - Algorithm options
|
|
251
|
+
* @returns The partition, its modularity, and the number of levels that improved it
|
|
252
|
+
* @public
|
|
253
|
+
*/
|
|
254
|
+
export function louvain(s, o = {}) {
|
|
255
|
+
if (s.directed) {
|
|
256
|
+
throw new Error("Louvain requires an undirected graph. Pass s.toUndirected().snapshot.");
|
|
257
|
+
}
|
|
258
|
+
const resolution = o.resolution ?? 1;
|
|
259
|
+
const maxIterations = o.maxIterations ?? 100;
|
|
260
|
+
const tolerance = o.tolerance ?? 1e-6;
|
|
261
|
+
const n0 = s.nodeCount;
|
|
262
|
+
const labels = new Uint32Array(n0);
|
|
263
|
+
for (let u = 0; u < n0; u++) {
|
|
264
|
+
labels[u] = u;
|
|
265
|
+
}
|
|
266
|
+
let level = buildLevel0(s);
|
|
267
|
+
let m2 = 0;
|
|
268
|
+
for (let u = 0; u < n0; u++) {
|
|
269
|
+
m2 += level.deg[u];
|
|
270
|
+
}
|
|
271
|
+
if (m2 === 0) {
|
|
272
|
+
// No edge weight to redistribute: every node is its own community and Q is 0 by definition,
|
|
273
|
+
// which is what the legacy `calculateModularity` also answers.
|
|
274
|
+
return { ...withGroups(labels, n0), modularity: 0, iterations: 0 };
|
|
275
|
+
}
|
|
276
|
+
let q = modularityOf(level, labels, n0, resolution, m2);
|
|
277
|
+
let iterations = 0;
|
|
278
|
+
for (let iteration = 0; iteration < maxIterations; iteration++) {
|
|
279
|
+
const comm = new Uint32Array(level.n);
|
|
280
|
+
if (!localMove(level, comm, resolution, m2, maxIterations)) {
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
const dense = renumberPartition(comm);
|
|
284
|
+
const { count } = dense;
|
|
285
|
+
const packed = dense.labels;
|
|
286
|
+
const newQ = modularityOf(level, packed, count, resolution, m2);
|
|
287
|
+
for (let u = 0; u < n0; u++) {
|
|
288
|
+
labels[u] = packed[labels[u]];
|
|
289
|
+
}
|
|
290
|
+
iterations = iteration + 1;
|
|
291
|
+
const gain = newQ - q;
|
|
292
|
+
q = newQ;
|
|
293
|
+
if (gain < tolerance || count === level.n) {
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
level = aggregate(level, packed, count);
|
|
297
|
+
}
|
|
298
|
+
const final = renumberPartition(labels);
|
|
299
|
+
return { ...withGroups(final.labels, final.count), modularity: q, iterations };
|
|
300
|
+
}
|
|
301
|
+
//# sourceMappingURL=louvain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"louvain.js","sourceRoot":"","sources":["../../../src/indexed/louvain.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphty/algorithms",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Graph algorithms library for browser environments implemented in TypeScript",
|
|
5
5
|
"author": "Adam Powers <apowers@ato.ms>",
|
|
6
6
|
"main": "dist/algorithms.js",
|
|
@@ -68,13 +68,14 @@
|
|
|
68
68
|
"@storybook/html-vite": "^9.0.11",
|
|
69
69
|
"@storybook/test": "^8.6.14",
|
|
70
70
|
"@types/benchmark": "^2.1.5",
|
|
71
|
+
"@vitest/browser-playwright": "4.1.11",
|
|
71
72
|
"benchmark": "^2.1.4",
|
|
72
73
|
"chromatic": "^11.0.0",
|
|
73
74
|
"eslint": "^9.29.0",
|
|
74
75
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
75
76
|
"gh-pages": "^6.3.0",
|
|
76
77
|
"globals": "^15.12.0",
|
|
77
|
-
"happy-dom": "^
|
|
78
|
+
"happy-dom": "^20.8.9",
|
|
78
79
|
"knip": "^5.61.3",
|
|
79
80
|
"playwright": "^1.53.0",
|
|
80
81
|
"semantic-release": "^25.0.2",
|
|
@@ -88,11 +89,11 @@
|
|
|
88
89
|
"typescript-eslint": "^8.34.1",
|
|
89
90
|
"vite": "^7.0.5",
|
|
90
91
|
"vitepress": "^1.6.3",
|
|
91
|
-
"vitest": "
|
|
92
|
+
"vitest": "4.1.11"
|
|
92
93
|
},
|
|
93
94
|
"dependencies": {
|
|
94
95
|
"typedfastbitset": "^0.6.1",
|
|
95
|
-
"@graphty/graph-format": "^1.
|
|
96
|
+
"@graphty/graph-format": "^1.1.1"
|
|
96
97
|
},
|
|
97
98
|
"peerDependencies": {
|
|
98
99
|
"@graphty/graph-format": "^1.0.0"
|
|
@@ -123,7 +124,7 @@
|
|
|
123
124
|
"memory:profile": "tsx test/helpers/memory-profiler.ts",
|
|
124
125
|
"lint": "eslint && tsc --noEmit && tsc -p tsconfig.typecheck.json",
|
|
125
126
|
"lint:fix": "eslint --fix",
|
|
126
|
-
"lint:knip": "cd .. &&
|
|
127
|
+
"lint:knip": "cd .. && ./tools/run-knip.sh --workspace algorithms",
|
|
127
128
|
"typecheck": "tsc --noEmit",
|
|
128
129
|
"build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc",
|
|
129
130
|
"build:bundle": "node scripts/build-bundle.js",
|
|
@@ -135,7 +136,6 @@
|
|
|
135
136
|
"examples:run": "node examples/run-all-examples.js",
|
|
136
137
|
"examples:html": "npm run build:bundle && vite --port ${PORT:?start it through servherd, which sets PORT} --strictPort",
|
|
137
138
|
"serve": "npm run build:bundle && vite --port ${PORT:?start it through servherd, which sets PORT} --strictPort",
|
|
138
|
-
"commit": "cz",
|
|
139
139
|
"ready:commit": "npm run build && npm run lint && npm run test:all",
|
|
140
140
|
"watch": "tsc --watch",
|
|
141
141
|
"docs:api": "typedoc && node ../tools/sanitize-api-docs.js docs/api/generated",
|
|
@@ -102,7 +102,7 @@ function accumulateBetweenness(
|
|
|
102
102
|
// Process nodes in reverse BFS order
|
|
103
103
|
for (let i = stack.length - 1; i >= 0; i--) {
|
|
104
104
|
const w = stack[i];
|
|
105
|
-
if (
|
|
105
|
+
if (w === undefined) {
|
|
106
106
|
continue;
|
|
107
107
|
}
|
|
108
108
|
|
|
@@ -162,7 +162,7 @@ function accumulateEdgeBetweenness(
|
|
|
162
162
|
// Process nodes in reverse BFS order
|
|
163
163
|
for (let i = stack.length - 1; i >= 0; i--) {
|
|
164
164
|
const w = stack[i];
|
|
165
|
-
if (
|
|
165
|
+
if (w === undefined) {
|
|
166
166
|
continue;
|
|
167
167
|
}
|
|
168
168
|
|
|
@@ -163,7 +163,7 @@ function getCandidatePairs(
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
// If not found, pick from in1
|
|
166
|
-
if (
|
|
166
|
+
if (node1 === null) {
|
|
167
167
|
for (const [n] of state.in1) {
|
|
168
168
|
if (!state.core1.has(n)) {
|
|
169
169
|
node1 = n;
|
|
@@ -173,7 +173,7 @@ function getCandidatePairs(
|
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
// If still not found, pick any unmapped node
|
|
176
|
-
if (
|
|
176
|
+
if (node1 === null) {
|
|
177
177
|
for (const n of nodes1) {
|
|
178
178
|
if (!state.core1.has(n)) {
|
|
179
179
|
node1 = n;
|
|
@@ -182,7 +182,7 @@ function getCandidatePairs(
|
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
if (
|
|
185
|
+
if (node1 === null) {
|
|
186
186
|
return pairs;
|
|
187
187
|
}
|
|
188
188
|
|
|
@@ -109,7 +109,7 @@ export function bellmanFord(graph: Graph, source: NodeId, options: BellmanFordOp
|
|
|
109
109
|
for (const arc of arcs) {
|
|
110
110
|
const u = arc.from;
|
|
111
111
|
const v = arc.to;
|
|
112
|
-
const {weight} = arc;
|
|
112
|
+
const { weight } = arc;
|
|
113
113
|
|
|
114
114
|
const distanceU = distances.get(u);
|
|
115
115
|
const distanceV = distances.get(v);
|
|
@@ -123,7 +123,7 @@ export function bellmanFord(graph: Graph, source: NodeId, options: BellmanFordOp
|
|
|
123
123
|
updated = true;
|
|
124
124
|
|
|
125
125
|
// Early termination if target reached
|
|
126
|
-
if (options.target && v === options.target) {
|
|
126
|
+
if (options.target !== undefined && v === options.target) {
|
|
127
127
|
break;
|
|
128
128
|
}
|
|
129
129
|
}
|
|
@@ -143,7 +143,7 @@ export function bellmanFord(graph: Graph, source: NodeId, options: BellmanFordOp
|
|
|
143
143
|
for (const arc of arcs) {
|
|
144
144
|
const u = arc.from;
|
|
145
145
|
const v = arc.to;
|
|
146
|
-
const {weight} = arc;
|
|
146
|
+
const { weight } = arc;
|
|
147
147
|
|
|
148
148
|
const distanceU = distances.get(u);
|
|
149
149
|
const distanceV = distances.get(v);
|
|
@@ -55,7 +55,7 @@ export function dijkstra(graph: Graph, source: NodeId, options: DijkstraOptions
|
|
|
55
55
|
visited.add(currentNode);
|
|
56
56
|
|
|
57
57
|
// Early termination if target reached
|
|
58
|
-
if (options.target && currentNode === options.target) {
|
|
58
|
+
if (options.target !== undefined && currentNode === options.target) {
|
|
59
59
|
break;
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -89,7 +89,7 @@ function breadthFirstSearchStandard(graph: Graph, startNode: NodeId, options: Tr
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
// Early termination if target found
|
|
92
|
-
if (options.targetNode && current.node === options.targetNode) {
|
|
92
|
+
if (options.targetNode !== undefined && current.node === options.targetNode) {
|
|
93
93
|
break;
|
|
94
94
|
}
|
|
95
95
|
|
|
@@ -409,7 +409,7 @@ export function isBipartite(graph: Graph): boolean {
|
|
|
409
409
|
|
|
410
410
|
while (queue.length > 0) {
|
|
411
411
|
const current = queue.shift();
|
|
412
|
-
if (
|
|
412
|
+
if (current === undefined) {
|
|
413
413
|
break;
|
|
414
414
|
}
|
|
415
415
|
|
|
@@ -86,7 +86,7 @@ function dfsIterative(
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
// Early termination if target found
|
|
89
|
-
if (options.targetNode && current.node === options.targetNode) {
|
|
89
|
+
if (options.targetNode !== undefined && current.node === options.targetNode) {
|
|
90
90
|
break;
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -135,7 +135,7 @@ function dfsRecursive(
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
// Early termination if target found
|
|
138
|
-
if (options.targetNode && node === options.targetNode) {
|
|
138
|
+
if (options.targetNode !== undefined && node === options.targetNode) {
|
|
139
139
|
return;
|
|
140
140
|
}
|
|
141
141
|
}
|
|
@@ -102,7 +102,7 @@ export function spectralClustering(graph: Graph, options: SpectralClusteringOpti
|
|
|
102
102
|
for (let i = 0; i < nodeIds.length; i++) {
|
|
103
103
|
const clusterId = kmeans.assignments[i] ?? 0;
|
|
104
104
|
const nodeId = nodeIds[i];
|
|
105
|
-
if (
|
|
105
|
+
if (nodeId === undefined) {
|
|
106
106
|
continue;
|
|
107
107
|
}
|
|
108
108
|
|
|
@@ -151,7 +151,7 @@ function buildAdjacencyMatrix(graph: Graph, nodeIds: NodeId[]): number[][] {
|
|
|
151
151
|
|
|
152
152
|
for (let i = 0; i < n; i++) {
|
|
153
153
|
const nodeId = nodeIds[i];
|
|
154
|
-
if (
|
|
154
|
+
if (nodeId === undefined) {
|
|
155
155
|
continue;
|
|
156
156
|
}
|
|
157
157
|
|
package/src/index.ts
CHANGED
|
@@ -85,6 +85,16 @@ export type { BfsOptions, BfsResult } from "./indexed/bfs.js";
|
|
|
85
85
|
export type { CommonNeighborsOptions } from "./indexed/common-neighbors.js";
|
|
86
86
|
export type { LabelResult } from "./indexed/components.js";
|
|
87
87
|
export type { SsspOptions, SsspResult } from "./indexed/dijkstra.js";
|
|
88
|
+
// The `Indexed` prefix, as on the PageRank pair below: the flat names HITSOptions / HITSResult,
|
|
89
|
+
// KatzCentralityOptions and LouvainOptions already belong to the legacy functions above, and two
|
|
90
|
+
// option types one capital letter apart in the same barrel is a trap, not a convenience.
|
|
91
|
+
export type { HitsOptions as IndexedHitsOptions, HitsResult as IndexedHitsResult } from "./indexed/hits.js";
|
|
92
|
+
export type { CorenessResult } from "./indexed/k-core.js";
|
|
93
|
+
export type { KatzOptions as IndexedKatzOptions, KatzResult as IndexedKatzResult } from "./indexed/katz.js";
|
|
94
|
+
export type {
|
|
95
|
+
LouvainOptions as IndexedLouvainOptions,
|
|
96
|
+
LouvainResult as IndexedLouvainResult,
|
|
97
|
+
} from "./indexed/louvain.js";
|
|
88
98
|
export type { MstOptions, MstResult } from "./indexed/mst.js";
|
|
89
99
|
// Aliased: the flat names are taken twice over (types/index.ts:96 and centrality/pagerank.ts:15).
|
|
90
100
|
export type {
|