graphwise 1.8.0 → 1.9.0

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.
Files changed (125) hide show
  1. package/dist/adjacency-map-BtKzcuJq.js +229 -0
  2. package/dist/adjacency-map-BtKzcuJq.js.map +1 -0
  3. package/dist/adjacency-map-JqBnMNkF.cjs +234 -0
  4. package/dist/adjacency-map-JqBnMNkF.cjs.map +1 -0
  5. package/dist/async/index.cjs +15 -242
  6. package/dist/async/index.js +2 -229
  7. package/dist/expansion/index.cjs +43 -0
  8. package/dist/expansion/index.js +2 -0
  9. package/dist/expansion-ClDhlMK8.js +1704 -0
  10. package/dist/expansion-ClDhlMK8.js.map +1 -0
  11. package/dist/expansion-DaTroIyv.cjs +1949 -0
  12. package/dist/expansion-DaTroIyv.cjs.map +1 -0
  13. package/dist/extraction/index.cjs +630 -0
  14. package/dist/extraction/index.cjs.map +1 -0
  15. package/dist/extraction/index.js +621 -0
  16. package/dist/extraction/index.js.map +1 -0
  17. package/dist/gpu/csr.d.ts +29 -30
  18. package/dist/gpu/csr.d.ts.map +1 -1
  19. package/dist/gpu/dispatch.d.ts +31 -0
  20. package/dist/gpu/dispatch.d.ts.map +1 -0
  21. package/dist/gpu/dispatch.unit.test.d.ts +5 -0
  22. package/dist/gpu/dispatch.unit.test.d.ts.map +1 -0
  23. package/dist/gpu/index.cjs +15 -410
  24. package/dist/gpu/index.d.ts +3 -1
  25. package/dist/gpu/index.d.ts.map +1 -1
  26. package/dist/gpu/index.js +2 -400
  27. package/dist/gpu/kernels/bfs/kernel.d.ts +59 -0
  28. package/dist/gpu/kernels/bfs/kernel.d.ts.map +1 -0
  29. package/dist/gpu/kernels/bfs/logic.d.ts +47 -0
  30. package/dist/gpu/kernels/bfs/logic.d.ts.map +1 -0
  31. package/dist/gpu/kernels/bfs/logic.unit.test.d.ts +2 -0
  32. package/dist/gpu/kernels/bfs/logic.unit.test.d.ts.map +1 -0
  33. package/dist/gpu/kernels/degree-histogram/kernel.d.ts +32 -0
  34. package/dist/gpu/kernels/degree-histogram/kernel.d.ts.map +1 -0
  35. package/dist/gpu/kernels/degree-histogram/logic.d.ts +45 -0
  36. package/dist/gpu/kernels/degree-histogram/logic.d.ts.map +1 -0
  37. package/dist/gpu/kernels/degree-histogram/logic.unit.test.d.ts +2 -0
  38. package/dist/gpu/kernels/degree-histogram/logic.unit.test.d.ts.map +1 -0
  39. package/dist/gpu/kernels/jaccard/kernel.d.ts +40 -0
  40. package/dist/gpu/kernels/jaccard/kernel.d.ts.map +1 -0
  41. package/dist/gpu/kernels/jaccard/logic.d.ts +43 -0
  42. package/dist/gpu/kernels/jaccard/logic.d.ts.map +1 -0
  43. package/dist/gpu/kernels/jaccard/logic.unit.test.d.ts +2 -0
  44. package/dist/gpu/kernels/jaccard/logic.unit.test.d.ts.map +1 -0
  45. package/dist/gpu/kernels/pagerank/kernel.d.ts +44 -0
  46. package/dist/gpu/kernels/pagerank/kernel.d.ts.map +1 -0
  47. package/dist/gpu/kernels/pagerank/logic.d.ts +50 -0
  48. package/dist/gpu/kernels/pagerank/logic.d.ts.map +1 -0
  49. package/dist/gpu/kernels/pagerank/logic.unit.test.d.ts +2 -0
  50. package/dist/gpu/kernels/pagerank/logic.unit.test.d.ts.map +1 -0
  51. package/dist/gpu/kernels/spmv/kernel.d.ts +43 -0
  52. package/dist/gpu/kernels/spmv/kernel.d.ts.map +1 -0
  53. package/dist/gpu/kernels/spmv/logic.d.ts +31 -0
  54. package/dist/gpu/kernels/spmv/logic.d.ts.map +1 -0
  55. package/dist/gpu/kernels/spmv/logic.unit.test.d.ts +2 -0
  56. package/dist/gpu/kernels/spmv/logic.unit.test.d.ts.map +1 -0
  57. package/dist/gpu/operations.d.ts +76 -0
  58. package/dist/gpu/operations.d.ts.map +1 -0
  59. package/dist/gpu/operations.unit.test.d.ts +5 -0
  60. package/dist/gpu/operations.unit.test.d.ts.map +1 -0
  61. package/dist/gpu/root.d.ts +53 -0
  62. package/dist/gpu/root.d.ts.map +1 -0
  63. package/dist/gpu/root.unit.test.d.ts +2 -0
  64. package/dist/gpu/root.unit.test.d.ts.map +1 -0
  65. package/dist/gpu/types.d.ts +3 -8
  66. package/dist/gpu/types.d.ts.map +1 -1
  67. package/dist/gpu-CHiCN0wa.js +16945 -0
  68. package/dist/gpu-CHiCN0wa.js.map +1 -0
  69. package/dist/gpu-Y6owRVMi.cjs +17028 -0
  70. package/dist/gpu-Y6owRVMi.cjs.map +1 -0
  71. package/dist/graph/index.cjs +2 -229
  72. package/dist/graph/index.js +1 -228
  73. package/dist/index/index.cjs +141 -4040
  74. package/dist/index/index.js +15 -3917
  75. package/dist/jaccard-3rCdilwm.js +39 -0
  76. package/dist/jaccard-3rCdilwm.js.map +1 -0
  77. package/dist/jaccard-Bys9_dGW.cjs +50 -0
  78. package/dist/jaccard-Bys9_dGW.cjs.map +1 -0
  79. package/dist/{kmeans-BIgSyGKu.cjs → kmeans-B8x9D1kt.cjs} +1 -1
  80. package/dist/{kmeans-BIgSyGKu.cjs.map → kmeans-B8x9D1kt.cjs.map} +1 -1
  81. package/dist/{kmeans-87ExSUNZ.js → kmeans-DKkL9rAN.js} +1 -1
  82. package/dist/{kmeans-87ExSUNZ.js.map → kmeans-DKkL9rAN.js.map} +1 -1
  83. package/dist/ops-djAsQQSh.cjs +277 -0
  84. package/dist/ops-djAsQQSh.cjs.map +1 -0
  85. package/dist/ops-upIi6JIi.js +212 -0
  86. package/dist/ops-upIi6JIi.js.map +1 -0
  87. package/dist/priority-queue-BIiD1L0k.cjs +148 -0
  88. package/dist/priority-queue-BIiD1L0k.cjs.map +1 -0
  89. package/dist/priority-queue-CFDd5cBg.js +143 -0
  90. package/dist/priority-queue-CFDd5cBg.js.map +1 -0
  91. package/dist/ranking/index.cjs +43 -0
  92. package/dist/ranking/index.js +4 -0
  93. package/dist/ranking/mi/index.cjs +581 -0
  94. package/dist/ranking/mi/index.cjs.map +1 -0
  95. package/dist/ranking/mi/index.js +555 -0
  96. package/dist/ranking/mi/index.js.map +1 -0
  97. package/dist/ranking-3ez5m67U.js +1016 -0
  98. package/dist/ranking-3ez5m67U.js.map +1 -0
  99. package/dist/ranking-DVvajgUZ.cjs +1093 -0
  100. package/dist/ranking-DVvajgUZ.cjs.map +1 -0
  101. package/dist/seeds/index.cjs +1 -1
  102. package/dist/seeds/index.js +1 -1
  103. package/dist/structures/index.cjs +2 -143
  104. package/dist/structures/index.js +1 -142
  105. package/dist/utils/index.cjs +1 -1
  106. package/dist/utils/index.js +1 -1
  107. package/dist/utils-BodeE2Mo.js +22 -0
  108. package/dist/utils-BodeE2Mo.js.map +1 -0
  109. package/dist/utils-CDtCcsyF.cjs +33 -0
  110. package/dist/utils-CDtCcsyF.cjs.map +1 -0
  111. package/package.json +3 -1
  112. package/dist/async/index.cjs.map +0 -1
  113. package/dist/async/index.js.map +0 -1
  114. package/dist/gpu/context.d.ts +0 -118
  115. package/dist/gpu/context.d.ts.map +0 -1
  116. package/dist/gpu/context.unit.test.d.ts +0 -2
  117. package/dist/gpu/context.unit.test.d.ts.map +0 -1
  118. package/dist/gpu/index.cjs.map +0 -1
  119. package/dist/gpu/index.js.map +0 -1
  120. package/dist/graph/index.cjs.map +0 -1
  121. package/dist/graph/index.js.map +0 -1
  122. package/dist/index/index.cjs.map +0 -1
  123. package/dist/index/index.js.map +0 -1
  124. package/dist/structures/index.cjs.map +0 -1
  125. package/dist/structures/index.js.map +0 -1
@@ -0,0 +1,630 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_adjacency_map = require("../adjacency-map-JqBnMNkF.cjs");
3
+ //#region src/extraction/ego-network.ts
4
+ /**
5
+ * Extract the ego-network (k-hop neighbourhood) of a centre node.
6
+ *
7
+ * The ego-network includes all nodes reachable within k hops from the
8
+ * centre node, plus all edges between those nodes (induced subgraph).
9
+ *
10
+ * For directed graphs, the search follows outgoing edges by default.
11
+ * To include incoming edges, use direction 'both' in the underlying traversal.
12
+ *
13
+ * @param graph - The source graph
14
+ * @param centre - The centre node ID
15
+ * @param options - Extraction options
16
+ * @returns An induced subgraph of the k-hop neighbourhood
17
+ * @throws Error if the centre node does not exist in the graph
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * // 2-hop neighbourhood
22
+ * const ego = extractEgoNetwork(graph, 'A', { hops: 2 });
23
+ * ```
24
+ */
25
+ function extractEgoNetwork(graph, centre, options) {
26
+ const hops = options?.hops ?? 1;
27
+ if (!graph.hasNode(centre)) throw new Error(`Centre node '${centre}' does not exist in the graph`);
28
+ if (hops < 0) throw new Error(`Hops must be non-negative, got ${String(hops)}`);
29
+ const nodesInEgoNetwork = new Set([centre]);
30
+ if (hops > 0) {
31
+ const visited = new Set([centre]);
32
+ const queue = [[centre, 0]];
33
+ while (queue.length > 0) {
34
+ const entry = queue.shift();
35
+ if (entry === void 0) break;
36
+ const [current, distance] = entry;
37
+ if (distance < hops) {
38
+ for (const neighbour of graph.neighbours(current)) if (!visited.has(neighbour)) {
39
+ visited.add(neighbour);
40
+ nodesInEgoNetwork.add(neighbour);
41
+ queue.push([neighbour, distance + 1]);
42
+ }
43
+ }
44
+ }
45
+ }
46
+ const result = graph.directed ? require_adjacency_map.AdjacencyMapGraph.directed() : require_adjacency_map.AdjacencyMapGraph.undirected();
47
+ for (const nodeId of nodesInEgoNetwork) {
48
+ const nodeData = graph.getNode(nodeId);
49
+ if (nodeData !== void 0) result.addNode(nodeData);
50
+ }
51
+ for (const edge of graph.edges()) if (nodesInEgoNetwork.has(edge.source) && nodesInEgoNetwork.has(edge.target)) result.addEdge(edge);
52
+ return result;
53
+ }
54
+ //#endregion
55
+ //#region src/extraction/k-core.ts
56
+ /**
57
+ * Extract the k-core of a graph.
58
+ *
59
+ * The k-core is the maximal connected subgraph where every node has
60
+ * degree at least k. This is computed using a peeling algorithm that
61
+ * iteratively removes nodes with degree less than k.
62
+ *
63
+ * For undirected graphs, degree counts all adjacent nodes.
64
+ * For directed graphs, degree counts both in- and out-neighbours.
65
+ *
66
+ * @param graph - The source graph
67
+ * @param k - The minimum degree threshold
68
+ * @returns A new graph containing the k-core (may be empty)
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * // Extract the 3-core (nodes with at least 3 neighbours)
73
+ * const core3 = extractKCore(graph, 3);
74
+ * ```
75
+ */
76
+ function extractKCore(graph, k) {
77
+ if (k < 0) throw new Error(`k must be non-negative, got ${String(k)}`);
78
+ const remaining = /* @__PURE__ */ new Set();
79
+ const degrees = /* @__PURE__ */ new Map();
80
+ for (const nodeId of graph.nodeIds()) {
81
+ remaining.add(nodeId);
82
+ const deg = graph.directed ? graph.degree(nodeId, "both") : graph.degree(nodeId);
83
+ degrees.set(nodeId, deg);
84
+ }
85
+ const toRemove = [];
86
+ for (const [nodeId, deg] of degrees) if (deg < k) toRemove.push(nodeId);
87
+ while (toRemove.length > 0) {
88
+ const nodeId = toRemove.shift();
89
+ if (nodeId === void 0) break;
90
+ if (!remaining.has(nodeId)) continue;
91
+ remaining.delete(nodeId);
92
+ const neighbours = graph.directed ? graph.neighbours(nodeId, "both") : graph.neighbours(nodeId);
93
+ for (const neighbour of neighbours) if (remaining.has(neighbour)) {
94
+ const newDeg = (degrees.get(neighbour) ?? 0) - 1;
95
+ degrees.set(neighbour, newDeg);
96
+ if (newDeg < k && newDeg === k - 1) toRemove.push(neighbour);
97
+ }
98
+ }
99
+ const result = graph.directed ? require_adjacency_map.AdjacencyMapGraph.directed() : require_adjacency_map.AdjacencyMapGraph.undirected();
100
+ for (const nodeId of remaining) {
101
+ const nodeData = graph.getNode(nodeId);
102
+ if (nodeData !== void 0) result.addNode(nodeData);
103
+ }
104
+ for (const edge of graph.edges()) if (remaining.has(edge.source) && remaining.has(edge.target)) result.addEdge(edge);
105
+ return result;
106
+ }
107
+ //#endregion
108
+ //#region src/extraction/truss.ts
109
+ /**
110
+ * Count triangles involving a given edge.
111
+ *
112
+ * For an edge (u, v), count common neighbours of u and v.
113
+ * Each common neighbour w forms a triangle u-v-w.
114
+ *
115
+ * @param graph - The graph
116
+ * @param u - First endpoint
117
+ * @param v - Second endpoint
118
+ * @returns Number of triangles containing the edge (u, v)
119
+ */
120
+ function countEdgeTriangles(graph, u, v) {
121
+ const uNeighbours = new Set(graph.neighbours(u));
122
+ let count = 0;
123
+ for (const w of graph.neighbours(v)) if (w !== u && uNeighbours.has(w)) count++;
124
+ return count;
125
+ }
126
+ /**
127
+ * Extract the k-truss of a graph.
128
+ *
129
+ * The k-truss is the maximal subgraph where every edge participates in
130
+ * at least k-2 triangles. This is computed by iteratively removing edges
131
+ * with fewer than k-2 triangles, then removing isolated nodes.
132
+ *
133
+ * Note: K-truss is typically defined for undirected graphs. For directed
134
+ * graphs, this treats the graph as undirected for triangle counting.
135
+ *
136
+ * @param graph - The source graph
137
+ * @param k - The minimum triangle count threshold (edge must be in >= k-2 triangles)
138
+ * @returns A new graph containing the k-truss (may be empty)
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * // Extract the 3-truss (edges in at least 1 triangle)
143
+ * const truss3 = extractKTruss(graph, 3);
144
+ * ```
145
+ */
146
+ function extractKTruss(graph, k) {
147
+ if (k < 2) throw new Error(`k must be at least 2, got ${String(k)}`);
148
+ const minTriangles = k - 2;
149
+ const adjacency = /* @__PURE__ */ new Map();
150
+ const edgeData = /* @__PURE__ */ new Map();
151
+ const remainingEdges = /* @__PURE__ */ new Set();
152
+ for (const nodeId of graph.nodeIds()) adjacency.set(nodeId, /* @__PURE__ */ new Set());
153
+ for (const edge of graph.edges()) {
154
+ const { source, target } = edge;
155
+ adjacency.get(source)?.add(target);
156
+ adjacency.get(target)?.add(source);
157
+ const key = source < target ? `${source}::${target}` : `${target}::${source}`;
158
+ edgeData.set(key, edge);
159
+ remainingEdges.add(key);
160
+ }
161
+ const triangleCounts = /* @__PURE__ */ new Map();
162
+ const edgesToRemove = [];
163
+ for (const key of remainingEdges) {
164
+ const edge = edgeData.get(key);
165
+ if (edge !== void 0) {
166
+ const count = countEdgeTriangles(graph, edge.source, edge.target);
167
+ triangleCounts.set(key, count);
168
+ if (count < minTriangles) edgesToRemove.push(key);
169
+ }
170
+ }
171
+ while (edgesToRemove.length > 0) {
172
+ const edgeKey = edgesToRemove.shift();
173
+ if (edgeKey === void 0) break;
174
+ if (!remainingEdges.has(edgeKey)) continue;
175
+ remainingEdges.delete(edgeKey);
176
+ const edge = edgeData.get(edgeKey);
177
+ if (edge === void 0) continue;
178
+ const { source, target } = edge;
179
+ adjacency.get(source)?.delete(target);
180
+ adjacency.get(target)?.delete(source);
181
+ const sourceNeighbours = adjacency.get(source);
182
+ if (sourceNeighbours !== void 0) {
183
+ for (const w of adjacency.get(target) ?? []) if (sourceNeighbours.has(w)) {
184
+ const keySw = source < w ? `${source}::${w}` : `${w}::${source}`;
185
+ const keyTw = target < w ? `${target}::${w}` : `${w}::${target}`;
186
+ for (const keyToUpdate of [keySw, keyTw]) if (remainingEdges.has(keyToUpdate)) {
187
+ const newCount = (triangleCounts.get(keyToUpdate) ?? 0) - 1;
188
+ triangleCounts.set(keyToUpdate, newCount);
189
+ if (newCount < minTriangles && newCount === minTriangles - 1) edgesToRemove.push(keyToUpdate);
190
+ }
191
+ }
192
+ }
193
+ }
194
+ const nodesWithEdges = /* @__PURE__ */ new Set();
195
+ for (const key of remainingEdges) {
196
+ const edge = edgeData.get(key);
197
+ if (edge !== void 0) {
198
+ nodesWithEdges.add(edge.source);
199
+ nodesWithEdges.add(edge.target);
200
+ }
201
+ }
202
+ const result = graph.directed ? require_adjacency_map.AdjacencyMapGraph.directed() : require_adjacency_map.AdjacencyMapGraph.undirected();
203
+ for (const nodeId of nodesWithEdges) {
204
+ const nodeData = graph.getNode(nodeId);
205
+ if (nodeData !== void 0) result.addNode(nodeData);
206
+ }
207
+ for (const key of remainingEdges) {
208
+ const edge = edgeData.get(key);
209
+ if (edge !== void 0 && result.hasNode(edge.source) && result.hasNode(edge.target)) result.addEdge(edge);
210
+ }
211
+ return result;
212
+ }
213
+ /**
214
+ * Compute the truss number for each edge.
215
+ *
216
+ * The truss number of an edge is the largest k such that the edge
217
+ * belongs to the k-truss.
218
+ *
219
+ * @param graph - The source graph
220
+ * @returns Map from edge key (canonical "u::v") to truss number
221
+ *
222
+ * @example
223
+ * ```typescript
224
+ * const trussNumbers = computeTrussNumbers(graph);
225
+ * const edgeKey = 'A::B'; // where A < B lexicographically
226
+ * console.log(`Edge A-B is in the ${trussNumbers.get(edgeKey)}-truss`);
227
+ * ```
228
+ */
229
+ function computeTrussNumbers(graph) {
230
+ const adjacency = /* @__PURE__ */ new Map();
231
+ const edgeData = /* @__PURE__ */ new Map();
232
+ const remainingEdges = /* @__PURE__ */ new Set();
233
+ for (const nodeId of graph.nodeIds()) adjacency.set(nodeId, /* @__PURE__ */ new Set());
234
+ for (const edge of graph.edges()) {
235
+ const { source, target } = edge;
236
+ adjacency.get(source)?.add(target);
237
+ adjacency.get(target)?.add(source);
238
+ const key = source < target ? `${source}::${target}` : `${target}::${source}`;
239
+ edgeData.set(key, edge);
240
+ remainingEdges.add(key);
241
+ }
242
+ const triangleCounts = /* @__PURE__ */ new Map();
243
+ for (const key of remainingEdges) {
244
+ const edge = edgeData.get(key);
245
+ if (edge !== void 0) triangleCounts.set(key, countEdgeTriangles(graph, edge.source, edge.target));
246
+ }
247
+ const trussNumbers = /* @__PURE__ */ new Map();
248
+ const edgesByTriangleCount = /* @__PURE__ */ new Map();
249
+ for (const [key, count] of triangleCounts) {
250
+ if (!edgesByTriangleCount.has(count)) edgesByTriangleCount.set(count, /* @__PURE__ */ new Set());
251
+ edgesByTriangleCount.get(count)?.add(key);
252
+ }
253
+ const sortedCounts = [...edgesByTriangleCount.keys()].sort((a, b) => a - b);
254
+ for (const currentCount of sortedCounts) {
255
+ const bucket = edgesByTriangleCount.get(currentCount);
256
+ if (bucket === void 0) continue;
257
+ while (bucket.size > 0) {
258
+ const edgeKey = bucket.values().next().value;
259
+ if (edgeKey === void 0) break;
260
+ bucket.delete(edgeKey);
261
+ if (!remainingEdges.has(edgeKey)) continue;
262
+ const trussNumber = currentCount + 2;
263
+ trussNumbers.set(edgeKey, trussNumber);
264
+ remainingEdges.delete(edgeKey);
265
+ const edge = edgeData.get(edgeKey);
266
+ if (edge === void 0) continue;
267
+ const { source, target } = edge;
268
+ adjacency.get(source)?.delete(target);
269
+ adjacency.get(target)?.delete(source);
270
+ const sourceNeighbours = adjacency.get(source);
271
+ if (sourceNeighbours !== void 0) {
272
+ for (const w of adjacency.get(target) ?? []) if (sourceNeighbours.has(w)) {
273
+ const keySw = source < w ? `${source}::${w}` : `${w}::${source}`;
274
+ const keyTw = target < w ? `${target}::${w}` : `${w}::${target}`;
275
+ for (const keyToUpdate of [keySw, keyTw]) if (remainingEdges.has(keyToUpdate)) {
276
+ const oldCount = triangleCounts.get(keyToUpdate) ?? 0;
277
+ const newCount = oldCount - 1;
278
+ triangleCounts.set(keyToUpdate, newCount);
279
+ edgesByTriangleCount.get(oldCount)?.delete(keyToUpdate);
280
+ if (!edgesByTriangleCount.has(newCount)) edgesByTriangleCount.set(newCount, /* @__PURE__ */ new Set());
281
+ edgesByTriangleCount.get(newCount)?.add(keyToUpdate);
282
+ }
283
+ }
284
+ }
285
+ }
286
+ }
287
+ return trussNumbers;
288
+ }
289
+ //#endregion
290
+ //#region src/extraction/motif.ts
291
+ /**
292
+ * Canonicalise an edge pattern for hashing.
293
+ *
294
+ * Returns a canonical string representation of a small graph pattern.
295
+ */
296
+ function canonicalisePattern(nodeCount, edges) {
297
+ const permutations = getPermutations(nodeCount);
298
+ let minPattern = null;
299
+ for (const perm of permutations) {
300
+ const transformedEdges = edges.map(([u, v]) => {
301
+ const pu = perm[u] ?? -1;
302
+ const pv = perm[v] ?? -1;
303
+ if (pu < 0 || pv < 0) return;
304
+ return pu < pv ? `${String(pu)}-${String(pv)}` : `${String(pv)}-${String(pu)}`;
305
+ }).filter((edge) => edge !== void 0).sort().join(",");
306
+ if (minPattern === null || transformedEdges < minPattern) minPattern = transformedEdges;
307
+ }
308
+ return minPattern ?? "";
309
+ }
310
+ /**
311
+ * Generate all permutations of [0, n-1].
312
+ */
313
+ function getPermutations(n) {
314
+ if (n === 0) return [[]];
315
+ if (n === 1) return [[0]];
316
+ const result = [];
317
+ const arr = Array.from({ length: n }, (_, i) => i);
318
+ function permute(start) {
319
+ if (start === n - 1) {
320
+ result.push([...arr]);
321
+ return;
322
+ }
323
+ for (let i = start; i < n; i++) {
324
+ const startVal = arr[start];
325
+ const iVal = arr[i];
326
+ if (startVal === void 0 || iVal === void 0) continue;
327
+ arr[start] = iVal;
328
+ arr[i] = startVal;
329
+ permute(start + 1);
330
+ arr[start] = startVal;
331
+ arr[i] = iVal;
332
+ }
333
+ }
334
+ permute(0);
335
+ return result;
336
+ }
337
+ /**
338
+ * Enumerate all 3-node motifs in the graph.
339
+ *
340
+ * A 3-node motif (triad) can be one of 4 isomorphism classes for undirected graphs:
341
+ * - Empty: no edges
342
+ * - 1-edge: single edge
343
+ * - 2-star: two edges sharing a node (path of length 2)
344
+ * - Triangle: three edges (complete graph K3)
345
+ *
346
+ * For directed graphs, there are 16 isomorphism classes.
347
+ *
348
+ * @param graph - The source graph
349
+ * @param includeInstances - Whether to include node instances in the result
350
+ * @returns Motif census with counts and optionally instances
351
+ */
352
+ function enumerate3NodeMotifs(graph, includeInstances) {
353
+ const counts = /* @__PURE__ */ new Map();
354
+ const instances = includeInstances ? /* @__PURE__ */ new Map() : void 0;
355
+ const nodeList = [...graph.nodeIds()];
356
+ const n = nodeList.length;
357
+ for (let i = 0; i < n; i++) {
358
+ const ni = nodeList[i];
359
+ if (ni === void 0) continue;
360
+ for (let j = i + 1; j < n; j++) {
361
+ const nj = nodeList[j];
362
+ if (nj === void 0) continue;
363
+ for (let k = j + 1; k < n; k++) {
364
+ const nk = nodeList[k];
365
+ if (nk === void 0) continue;
366
+ const nodes = [
367
+ ni,
368
+ nj,
369
+ nk
370
+ ];
371
+ const edges = [];
372
+ for (const [u, v] of [
373
+ [0, 1],
374
+ [0, 2],
375
+ [1, 2]
376
+ ]) {
377
+ const nu = nodes[u];
378
+ const nv = nodes[v];
379
+ if (nu === void 0 || nv === void 0) continue;
380
+ if (graph.getEdge(nu, nv) !== void 0) edges.push([u, v]);
381
+ else if (!graph.directed && graph.getEdge(nv, nu) !== void 0) edges.push([u, v]);
382
+ else if (graph.directed && graph.getEdge(nv, nu) !== void 0) edges.push([v, u]);
383
+ }
384
+ const pattern = canonicalisePattern(3, edges);
385
+ const count = counts.get(pattern) ?? 0;
386
+ counts.set(pattern, count + 1);
387
+ if (includeInstances && instances !== void 0) {
388
+ if (!instances.has(pattern)) instances.set(pattern, []);
389
+ const patternInstances = instances.get(pattern);
390
+ if (patternInstances !== void 0) patternInstances.push([
391
+ ni,
392
+ nj,
393
+ nk
394
+ ]);
395
+ }
396
+ }
397
+ }
398
+ }
399
+ if (instances !== void 0) return {
400
+ counts,
401
+ instances
402
+ };
403
+ return { counts };
404
+ }
405
+ /**
406
+ * Enumerate all 4-node motifs in the graph.
407
+ *
408
+ * A 4-node motif can be one of 11 isomorphism classes for undirected graphs
409
+ * (ranging from empty to complete K4), or many more for directed graphs.
410
+ *
411
+ * @param graph - The source graph
412
+ * @param includeInstances - Whether to include node instances in the result
413
+ * @returns Motif census with counts and optionally instances
414
+ */
415
+ function enumerate4NodeMotifs(graph, includeInstances) {
416
+ const counts = /* @__PURE__ */ new Map();
417
+ const instances = includeInstances ? /* @__PURE__ */ new Map() : void 0;
418
+ const nodeList = [...graph.nodeIds()];
419
+ const n = nodeList.length;
420
+ for (let i = 0; i < n; i++) {
421
+ const ni = nodeList[i];
422
+ if (ni === void 0) continue;
423
+ for (let j = i + 1; j < n; j++) {
424
+ const nj = nodeList[j];
425
+ if (nj === void 0) continue;
426
+ for (let k = j + 1; k < n; k++) {
427
+ const nk = nodeList[k];
428
+ if (nk === void 0) continue;
429
+ for (let l = k + 1; l < n; l++) {
430
+ const nl = nodeList[l];
431
+ if (nl === void 0) continue;
432
+ const nodes = [
433
+ ni,
434
+ nj,
435
+ nk,
436
+ nl
437
+ ];
438
+ const edges = [];
439
+ for (const [u, v] of [
440
+ [0, 1],
441
+ [0, 2],
442
+ [0, 3],
443
+ [1, 2],
444
+ [1, 3],
445
+ [2, 3]
446
+ ]) {
447
+ const nu = nodes[u];
448
+ const nv = nodes[v];
449
+ if (nu === void 0 || nv === void 0) continue;
450
+ if (graph.getEdge(nu, nv) !== void 0) edges.push([u, v]);
451
+ else if (!graph.directed && graph.getEdge(nv, nu) !== void 0) edges.push([u, v]);
452
+ else if (graph.directed && graph.getEdge(nv, nu) !== void 0) edges.push([v, u]);
453
+ }
454
+ const pattern = canonicalisePattern(4, edges);
455
+ const count = counts.get(pattern) ?? 0;
456
+ counts.set(pattern, count + 1);
457
+ if (includeInstances && instances !== void 0) {
458
+ if (!instances.has(pattern)) instances.set(pattern, []);
459
+ const patternInstances = instances.get(pattern);
460
+ if (patternInstances !== void 0) patternInstances.push([
461
+ ni,
462
+ nj,
463
+ nk,
464
+ nl
465
+ ]);
466
+ }
467
+ }
468
+ }
469
+ }
470
+ }
471
+ if (instances !== void 0) return {
472
+ counts,
473
+ instances
474
+ };
475
+ return { counts };
476
+ }
477
+ /**
478
+ * Human-readable names for common 3-node motifs.
479
+ */
480
+ var MOTIF_3_NAMES = new Map([
481
+ ["", "empty"],
482
+ ["0-1", "1-edge"],
483
+ ["0-1,0-2", "2-star"],
484
+ ["0-1,1-2", "path-3"],
485
+ ["0-1,0-2,1-2", "triangle"]
486
+ ]);
487
+ /**
488
+ * Human-readable names for common 4-node motifs.
489
+ */
490
+ var MOTIF_4_NAMES = new Map([
491
+ ["", "empty"],
492
+ ["0-1", "1-edge"],
493
+ ["0-1,0-2", "2-star"],
494
+ ["0-1,0-2,0-3", "3-star"],
495
+ ["0-1,0-2,1-2", "triangle"],
496
+ ["0-1,0-2,1-2,2-3", "paw"],
497
+ ["0-1,0-2,2-3", "path-4"],
498
+ ["0-1,0-2,1-3,2-3", "4-cycle"],
499
+ ["0-1,0-2,1-2,0-3,1-3", "diamond"],
500
+ ["0-1,0-2,0-3,1-2,1-3,2-3", "K4"]
501
+ ]);
502
+ /**
503
+ * Enumerate motifs of a given size in the graph.
504
+ *
505
+ * This function counts all occurrences of each distinct motif type
506
+ * (isomorphism class) in the graph. For graphs with many nodes,
507
+ * 4-motif enumeration can be expensive (O(n^4) worst case).
508
+ *
509
+ * @param graph - The source graph
510
+ * @param size - Motif size (3 or 4 nodes)
511
+ * @returns Motif census with counts per motif type
512
+ *
513
+ * @example
514
+ * ```typescript
515
+ * // Count all triangles and other 3-node patterns
516
+ * const census3 = enumerateMotifs(graph, 3);
517
+ * console.log(`Triangles: ${census3.counts.get('0-1,0-2,1-2')}`);
518
+ *
519
+ * // Count 4-node patterns
520
+ * const census4 = enumerateMotifs(graph, 4);
521
+ * ```
522
+ */
523
+ function enumerateMotifs(graph, size) {
524
+ return size === 3 ? enumerate3NodeMotifs(graph, false) : enumerate4NodeMotifs(graph, false);
525
+ }
526
+ /**
527
+ * Enumerate motifs with optional instance tracking.
528
+ *
529
+ * @param graph - The source graph
530
+ * @param size - Motif size (3 or 4 nodes)
531
+ * @param includeInstances - Whether to include node instances
532
+ * @returns Motif census with counts and optionally instances
533
+ */
534
+ function enumerateMotifsWithInstances(graph, size, includeInstances) {
535
+ return size === 3 ? enumerate3NodeMotifs(graph, includeInstances) : enumerate4NodeMotifs(graph, includeInstances);
536
+ }
537
+ /**
538
+ * Get a human-readable name for a motif pattern.
539
+ *
540
+ * @param pattern - The canonical pattern string
541
+ * @param size - Motif size (3 or 4 nodes)
542
+ * @returns A human-readable name, or the pattern itself if unknown
543
+ */
544
+ function getMotifName(pattern, size) {
545
+ return (size === 3 ? MOTIF_3_NAMES : MOTIF_4_NAMES).get(pattern) ?? pattern;
546
+ }
547
+ //#endregion
548
+ //#region src/extraction/induced-subgraph.ts
549
+ /**
550
+ * Extract the induced subgraph containing exactly the specified nodes.
551
+ *
552
+ * The induced subgraph includes all nodes from the input set that exist
553
+ * in the original graph, plus all edges where both endpoints are in the set.
554
+ *
555
+ * @param graph - The source graph
556
+ * @param nodes - Set of node IDs to include in the subgraph
557
+ * @returns A new graph containing the induced subgraph
558
+ *
559
+ * @example
560
+ * ```typescript
561
+ * const subgraph = extractInducedSubgraph(graph, new Set(['A', 'B', 'C']));
562
+ * ```
563
+ */
564
+ function extractInducedSubgraph(graph, nodes) {
565
+ const result = graph.directed ? require_adjacency_map.AdjacencyMapGraph.directed() : require_adjacency_map.AdjacencyMapGraph.undirected();
566
+ for (const nodeId of nodes) {
567
+ const nodeData = graph.getNode(nodeId);
568
+ if (nodeData !== void 0) result.addNode(nodeData);
569
+ }
570
+ for (const edge of graph.edges()) if (result.hasNode(edge.source) && result.hasNode(edge.target)) result.addEdge(edge);
571
+ return result;
572
+ }
573
+ //#endregion
574
+ //#region src/extraction/node-filter.ts
575
+ /**
576
+ * Extract a filtered subgraph based on node and edge predicates.
577
+ *
578
+ * Nodes are first filtered by the node predicate (if provided).
579
+ * Edges are then filtered by the edge predicate (if provided), and only
580
+ * retained if both endpoints pass the node predicate.
581
+ *
582
+ * @param graph - The source graph
583
+ * @param options - Filter options specifying node/edge predicates
584
+ * @returns A new graph containing only nodes and edges that pass the predicates
585
+ *
586
+ * @example
587
+ * ```typescript
588
+ * // Extract subgraph of high-weight nodes
589
+ * const filtered = filterSubgraph(graph, {
590
+ * nodePredicate: (node) => (node.weight ?? 0) > 0.5,
591
+ * removeIsolated: true
592
+ * });
593
+ * ```
594
+ */
595
+ function filterSubgraph(graph, options) {
596
+ const { nodePredicate, edgePredicate, removeIsolated = false } = options ?? {};
597
+ const result = graph.directed ? require_adjacency_map.AdjacencyMapGraph.directed() : require_adjacency_map.AdjacencyMapGraph.undirected();
598
+ const includedNodes = /* @__PURE__ */ new Set();
599
+ for (const nodeId of graph.nodeIds()) {
600
+ const nodeData = graph.getNode(nodeId);
601
+ if (nodeData !== void 0) {
602
+ if (nodePredicate === void 0 || nodePredicate(nodeData)) {
603
+ result.addNode(nodeData);
604
+ includedNodes.add(nodeId);
605
+ }
606
+ }
607
+ }
608
+ for (const edge of graph.edges()) {
609
+ if (!includedNodes.has(edge.source) || !includedNodes.has(edge.target)) continue;
610
+ if (edgePredicate === void 0 || edgePredicate(edge)) result.addEdge(edge);
611
+ }
612
+ if (removeIsolated) {
613
+ const isolatedNodes = [];
614
+ for (const nodeId of result.nodeIds()) if (result.degree(nodeId) === 0) isolatedNodes.push(nodeId);
615
+ for (const nodeId of isolatedNodes) result.removeNode(nodeId);
616
+ }
617
+ return result;
618
+ }
619
+ //#endregion
620
+ exports.computeTrussNumbers = computeTrussNumbers;
621
+ exports.enumerateMotifs = enumerateMotifs;
622
+ exports.enumerateMotifsWithInstances = enumerateMotifsWithInstances;
623
+ exports.extractEgoNetwork = extractEgoNetwork;
624
+ exports.extractInducedSubgraph = extractInducedSubgraph;
625
+ exports.extractKCore = extractKCore;
626
+ exports.extractKTruss = extractKTruss;
627
+ exports.filterSubgraph = filterSubgraph;
628
+ exports.getMotifName = getMotifName;
629
+
630
+ //# sourceMappingURL=index.cjs.map