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,581 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_utils = require("../../utils-CDtCcsyF.cjs");
3
+ const require_utils$1 = require("../../utils/index.cjs");
4
+ const require_jaccard = require("../../jaccard-Bys9_dGW.cjs");
5
+ //#region src/ranking/mi/adamic-adar.ts
6
+ /**
7
+ * Compute Adamic-Adar index between neighbourhoods of two nodes.
8
+ *
9
+ * @param graph - Source graph
10
+ * @param source - Source node ID
11
+ * @param target - Target node ID
12
+ * @param config - Optional configuration
13
+ * @returns Adamic-Adar index (normalised to [0, 1] if configured)
14
+ */
15
+ function adamicAdar(graph, source, target, config) {
16
+ const { epsilon = 1e-10, normalise = true } = config ?? {};
17
+ const commonNeighbours = require_utils$1.neighbourIntersection(require_utils$1.neighbourSet(graph, source, target), require_utils$1.neighbourSet(graph, target, source));
18
+ let score = 0;
19
+ for (const neighbour of commonNeighbours) {
20
+ const degree = graph.degree(neighbour);
21
+ score += 1 / Math.log(degree + 1);
22
+ }
23
+ if (normalise && commonNeighbours.size > 0) {
24
+ const maxScore = commonNeighbours.size / Math.log(2);
25
+ score = score / maxScore;
26
+ }
27
+ return Math.max(epsilon, score);
28
+ }
29
+ /**
30
+ * Async variant of Adamic-Adar index for use with async graph data sources.
31
+ *
32
+ * Fetches both neighbourhoods concurrently, then fetches degree for each common
33
+ * neighbour to compute the inverse-log-degree weighted sum.
34
+ */
35
+ async function adamicAdarAsync(graph, source, target, config) {
36
+ const { epsilon = 1e-10, normalise = true } = config ?? {};
37
+ const [sourceArr, targetArr] = await Promise.all([require_utils.collectAsyncIterable(graph.neighbours(source)), require_utils.collectAsyncIterable(graph.neighbours(target))]);
38
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
39
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
40
+ const commonNeighbours = [];
41
+ for (const n of srcSet) if (tgtSet.has(n)) commonNeighbours.push(n);
42
+ if (commonNeighbours.length === 0) return epsilon;
43
+ const degrees = await Promise.all(commonNeighbours.map((n) => graph.degree(n)));
44
+ let score = 0;
45
+ for (const degree of degrees) score += 1 / Math.log(degree + 1);
46
+ if (normalise) {
47
+ const maxScore = commonNeighbours.length / Math.log(2);
48
+ score = score / maxScore;
49
+ }
50
+ return Math.max(epsilon, score);
51
+ }
52
+ //#endregion
53
+ //#region src/ranking/mi/cosine.ts
54
+ /**
55
+ * Compute cosine similarity between neighbourhoods of two nodes.
56
+ *
57
+ * @param graph - Source graph
58
+ * @param source - Source node ID
59
+ * @param target - Target node ID
60
+ * @param config - Optional configuration
61
+ * @returns Cosine similarity in [0, 1]
62
+ */
63
+ function cosine(graph, source, target, config) {
64
+ const { epsilon = 1e-10 } = config ?? {};
65
+ const sourceNeighbours = require_utils$1.neighbourSet(graph, source, target);
66
+ const targetNeighbours = require_utils$1.neighbourSet(graph, target, source);
67
+ const { intersection } = require_utils$1.neighbourOverlap(sourceNeighbours, targetNeighbours);
68
+ const denominator = Math.sqrt(sourceNeighbours.size) * Math.sqrt(targetNeighbours.size);
69
+ if (denominator === 0) return 0;
70
+ const score = intersection / denominator;
71
+ return Math.max(epsilon, score);
72
+ }
73
+ /**
74
+ * Async variant of cosine similarity for use with async graph data sources.
75
+ *
76
+ * Fetches both neighbourhoods concurrently, then applies the same formula.
77
+ */
78
+ async function cosineAsync(graph, source, target, config) {
79
+ const { epsilon = 1e-10 } = config ?? {};
80
+ const [sourceArr, targetArr] = await Promise.all([require_utils.collectAsyncIterable(graph.neighbours(source)), require_utils.collectAsyncIterable(graph.neighbours(target))]);
81
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
82
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
83
+ let intersection = 0;
84
+ for (const n of srcSet) if (tgtSet.has(n)) intersection++;
85
+ const denominator = Math.sqrt(srcSet.size) * Math.sqrt(tgtSet.size);
86
+ if (denominator === 0) return 0;
87
+ const score = intersection / denominator;
88
+ return Math.max(epsilon, score);
89
+ }
90
+ //#endregion
91
+ //#region src/ranking/mi/sorensen.ts
92
+ /**
93
+ * Compute Sorensen-Dice similarity between neighbourhoods of two nodes.
94
+ *
95
+ * @param graph - Source graph
96
+ * @param source - Source node ID
97
+ * @param target - Target node ID
98
+ * @param config - Optional configuration
99
+ * @returns Sorensen-Dice coefficient in [0, 1]
100
+ */
101
+ function sorensen(graph, source, target, config) {
102
+ const { epsilon = 1e-10 } = config ?? {};
103
+ const sourceNeighbours = require_utils$1.neighbourSet(graph, source, target);
104
+ const targetNeighbours = require_utils$1.neighbourSet(graph, target, source);
105
+ const { intersection } = require_utils$1.neighbourOverlap(sourceNeighbours, targetNeighbours);
106
+ const denominator = sourceNeighbours.size + targetNeighbours.size;
107
+ if (denominator === 0) return 0;
108
+ const score = 2 * intersection / denominator;
109
+ return Math.max(epsilon, score);
110
+ }
111
+ /**
112
+ * Async variant of Sorensen-Dice coefficient for use with async graph data sources.
113
+ *
114
+ * Fetches both neighbourhoods concurrently, then applies the same formula.
115
+ */
116
+ async function sorensenAsync(graph, source, target, config) {
117
+ const { epsilon = 1e-10 } = config ?? {};
118
+ const [sourceArr, targetArr] = await Promise.all([require_utils.collectAsyncIterable(graph.neighbours(source)), require_utils.collectAsyncIterable(graph.neighbours(target))]);
119
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
120
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
121
+ let intersection = 0;
122
+ for (const n of srcSet) if (tgtSet.has(n)) intersection++;
123
+ const denominator = srcSet.size + tgtSet.size;
124
+ if (denominator === 0) return 0;
125
+ const score = 2 * intersection / denominator;
126
+ return Math.max(epsilon, score);
127
+ }
128
+ //#endregion
129
+ //#region src/ranking/mi/resource-allocation.ts
130
+ /**
131
+ * Compute Resource Allocation index between neighbourhoods of two nodes.
132
+ *
133
+ * @param graph - Source graph
134
+ * @param source - Source node ID
135
+ * @param target - Target node ID
136
+ * @param config - Optional configuration
137
+ * @returns Resource Allocation index (normalised to [0, 1] if configured)
138
+ */
139
+ function resourceAllocation(graph, source, target, config) {
140
+ const { epsilon = 1e-10, normalise = true } = config ?? {};
141
+ const commonNeighbours = require_utils$1.neighbourIntersection(require_utils$1.neighbourSet(graph, source, target), require_utils$1.neighbourSet(graph, target, source));
142
+ let score = 0;
143
+ for (const neighbour of commonNeighbours) {
144
+ const degree = graph.degree(neighbour);
145
+ if (degree > 0) score += 1 / degree;
146
+ }
147
+ if (normalise && commonNeighbours.size > 0) {
148
+ const maxScore = commonNeighbours.size;
149
+ score = score / maxScore;
150
+ }
151
+ return Math.max(epsilon, score);
152
+ }
153
+ /**
154
+ * Async variant of Resource Allocation index for use with async graph data sources.
155
+ *
156
+ * Fetches both neighbourhoods concurrently, then fetches degree for each common
157
+ * neighbour to compute the inverse-degree weighted sum.
158
+ */
159
+ async function resourceAllocationAsync(graph, source, target, config) {
160
+ const { epsilon = 1e-10, normalise = true } = config ?? {};
161
+ const [sourceArr, targetArr] = await Promise.all([require_utils.collectAsyncIterable(graph.neighbours(source)), require_utils.collectAsyncIterable(graph.neighbours(target))]);
162
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
163
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
164
+ const commonNeighbours = [];
165
+ for (const n of srcSet) if (tgtSet.has(n)) commonNeighbours.push(n);
166
+ if (commonNeighbours.length === 0) return epsilon;
167
+ const degrees = await Promise.all(commonNeighbours.map((n) => graph.degree(n)));
168
+ let score = 0;
169
+ for (const degree of degrees) if (degree > 0) score += 1 / degree;
170
+ if (normalise) {
171
+ const maxScore = commonNeighbours.length;
172
+ score = score / maxScore;
173
+ }
174
+ return Math.max(epsilon, score);
175
+ }
176
+ //#endregion
177
+ //#region src/ranking/mi/overlap-coefficient.ts
178
+ /**
179
+ * Compute Overlap Coefficient between neighbourhoods of two nodes.
180
+ *
181
+ * @param graph - Source graph
182
+ * @param source - Source node ID
183
+ * @param target - Target node ID
184
+ * @param config - Optional configuration
185
+ * @returns Overlap Coefficient in [0, 1]
186
+ */
187
+ function overlapCoefficient(graph, source, target, config) {
188
+ const { epsilon = 1e-10 } = config ?? {};
189
+ const sourceNeighbours = require_utils$1.neighbourSet(graph, source, target);
190
+ const targetNeighbours = require_utils$1.neighbourSet(graph, target, source);
191
+ const { intersection } = require_utils$1.neighbourOverlap(sourceNeighbours, targetNeighbours);
192
+ const denominator = Math.min(sourceNeighbours.size, targetNeighbours.size);
193
+ if (denominator === 0) return 0;
194
+ const score = intersection / denominator;
195
+ return Math.max(epsilon, score);
196
+ }
197
+ /**
198
+ * Async variant of Overlap Coefficient for use with async graph data sources.
199
+ *
200
+ * Fetches both neighbourhoods concurrently, then applies the same formula.
201
+ */
202
+ async function overlapCoefficientAsync(graph, source, target, config) {
203
+ const { epsilon = 1e-10 } = config ?? {};
204
+ const [sourceArr, targetArr] = await Promise.all([require_utils.collectAsyncIterable(graph.neighbours(source)), require_utils.collectAsyncIterable(graph.neighbours(target))]);
205
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
206
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
207
+ let intersection = 0;
208
+ for (const n of srcSet) if (tgtSet.has(n)) intersection++;
209
+ const denominator = Math.min(srcSet.size, tgtSet.size);
210
+ if (denominator === 0) return 0;
211
+ const score = intersection / denominator;
212
+ return Math.max(epsilon, score);
213
+ }
214
+ //#endregion
215
+ //#region src/ranking/mi/hub-promoted.ts
216
+ /**
217
+ * Compute Hub Promoted index between neighbourhoods of two nodes.
218
+ *
219
+ * @param graph - Source graph
220
+ * @param source - Source node ID
221
+ * @param target - Target node ID
222
+ * @param config - Optional configuration
223
+ * @returns Hub Promoted index in [0, 1]
224
+ */
225
+ function hubPromoted(graph, source, target, config) {
226
+ const { epsilon = 1e-10 } = config ?? {};
227
+ const { intersection } = require_utils$1.neighbourOverlap(require_utils$1.neighbourSet(graph, source, target), require_utils$1.neighbourSet(graph, target, source));
228
+ const sourceDegree = graph.degree(source);
229
+ const targetDegree = graph.degree(target);
230
+ const denominator = Math.min(sourceDegree, targetDegree);
231
+ if (denominator === 0) return 0;
232
+ const score = intersection / denominator;
233
+ return Math.max(epsilon, score);
234
+ }
235
+ /**
236
+ * Async variant of Hub Promoted index for use with async graph data sources.
237
+ *
238
+ * Fetches both neighbourhoods and degrees concurrently, then applies the same formula.
239
+ */
240
+ async function hubPromotedAsync(graph, source, target, config) {
241
+ const { epsilon = 1e-10 } = config ?? {};
242
+ const [sourceArr, targetArr, sourceDegree, targetDegree] = await Promise.all([
243
+ require_utils.collectAsyncIterable(graph.neighbours(source)),
244
+ require_utils.collectAsyncIterable(graph.neighbours(target)),
245
+ graph.degree(source),
246
+ graph.degree(target)
247
+ ]);
248
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
249
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
250
+ let intersection = 0;
251
+ for (const n of srcSet) if (tgtSet.has(n)) intersection++;
252
+ const denominator = Math.min(sourceDegree, targetDegree);
253
+ if (denominator === 0) return 0;
254
+ const score = intersection / denominator;
255
+ return Math.max(epsilon, score);
256
+ }
257
+ //#endregion
258
+ //#region src/ranking/mi/scale.ts
259
+ /**
260
+ * Compute SCALE MI between two nodes.
261
+ */
262
+ function scale(graph, source, target, config) {
263
+ const { epsilon = 1e-10 } = config ?? {};
264
+ const { jaccard: jaccardScore } = require_utils$1.computeJaccard(graph, source, target);
265
+ const n = graph.nodeCount;
266
+ const m = graph.edgeCount;
267
+ const possibleEdges = n * (n - 1);
268
+ const density = possibleEdges > 0 ? (graph.directed ? m : 2 * m) / possibleEdges : 0;
269
+ if (density === 0) return epsilon;
270
+ const score = jaccardScore / density;
271
+ return Math.max(epsilon, score);
272
+ }
273
+ /**
274
+ * Async variant of SCALE MI for use with async graph data sources.
275
+ *
276
+ * Fetches both neighbourhoods, node count, and edge count concurrently.
277
+ */
278
+ async function scaleAsync(graph, source, target, config) {
279
+ const { epsilon = 1e-10 } = config ?? {};
280
+ const [sourceArr, targetArr, n, m] = await Promise.all([
281
+ require_utils.collectAsyncIterable(graph.neighbours(source)),
282
+ require_utils.collectAsyncIterable(graph.neighbours(target)),
283
+ graph.nodeCount,
284
+ graph.edgeCount
285
+ ]);
286
+ const srcSet = new Set(sourceArr.filter((node) => node !== target));
287
+ const tgtSet = new Set(targetArr.filter((node) => node !== source));
288
+ let intersection = 0;
289
+ for (const node of srcSet) if (tgtSet.has(node)) intersection++;
290
+ const union = srcSet.size + tgtSet.size - intersection;
291
+ const jaccardScore = union > 0 ? intersection / union : 0;
292
+ const possibleEdges = n * (n - 1);
293
+ const density = possibleEdges > 0 ? (graph.directed ? m : 2 * m) / possibleEdges : 0;
294
+ if (density === 0) return epsilon;
295
+ const score = jaccardScore / density;
296
+ return Math.max(epsilon, score);
297
+ }
298
+ //#endregion
299
+ //#region src/ranking/mi/skew.ts
300
+ /**
301
+ * Compute SKEW MI between two nodes.
302
+ */
303
+ function skew(graph, source, target, config) {
304
+ const { epsilon = 1e-10 } = config ?? {};
305
+ const { jaccard: jaccardScore } = require_utils$1.computeJaccard(graph, source, target);
306
+ const N = graph.nodeCount;
307
+ const sourceDegree = graph.degree(source);
308
+ const targetDegree = graph.degree(target);
309
+ const sourceIdf = Math.log(N / (sourceDegree + 1));
310
+ const targetIdf = Math.log(N / (targetDegree + 1));
311
+ const score = jaccardScore * sourceIdf * targetIdf;
312
+ return Math.max(epsilon, score);
313
+ }
314
+ /**
315
+ * Async variant of SKEW MI for use with async graph data sources.
316
+ *
317
+ * Fetches both neighbourhoods, degrees, and node count concurrently.
318
+ */
319
+ async function skewAsync(graph, source, target, config) {
320
+ const { epsilon = 1e-10 } = config ?? {};
321
+ const [sourceArr, targetArr, N, sourceDegree, targetDegree] = await Promise.all([
322
+ require_utils.collectAsyncIterable(graph.neighbours(source)),
323
+ require_utils.collectAsyncIterable(graph.neighbours(target)),
324
+ graph.nodeCount,
325
+ graph.degree(source),
326
+ graph.degree(target)
327
+ ]);
328
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
329
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
330
+ let intersection = 0;
331
+ for (const n of srcSet) if (tgtSet.has(n)) intersection++;
332
+ const union = srcSet.size + tgtSet.size - intersection;
333
+ const jaccardScore = union > 0 ? intersection / union : 0;
334
+ const sourceIdf = Math.log(N / (sourceDegree + 1));
335
+ const targetIdf = Math.log(N / (targetDegree + 1));
336
+ const score = jaccardScore * sourceIdf * targetIdf;
337
+ return Math.max(epsilon, score);
338
+ }
339
+ //#endregion
340
+ //#region src/ranking/mi/span.ts
341
+ /**
342
+ * Compute SPAN MI between two nodes.
343
+ */
344
+ function span(graph, source, target, config) {
345
+ const { epsilon = 1e-10 } = config ?? {};
346
+ const { jaccard: jaccardScore } = require_utils$1.computeJaccard(graph, source, target);
347
+ const sourceCc = require_utils$1.localClusteringCoefficient(graph, source);
348
+ const targetCc = require_utils$1.localClusteringCoefficient(graph, target);
349
+ const score = jaccardScore * (1 - Math.max(sourceCc, targetCc));
350
+ return Math.max(epsilon, score);
351
+ }
352
+ /**
353
+ * Async variant of SPAN MI for use with async graph data sources.
354
+ *
355
+ * Fetches both neighbourhoods concurrently, then computes the clustering
356
+ * coefficient for each endpoint from the collected neighbour arrays.
357
+ */
358
+ async function spanAsync(graph, source, target, config) {
359
+ const { epsilon = 1e-10 } = config ?? {};
360
+ const [sourceArr, targetArr] = await Promise.all([require_utils.collectAsyncIterable(graph.neighbours(source)), require_utils.collectAsyncIterable(graph.neighbours(target))]);
361
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
362
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
363
+ let intersection = 0;
364
+ for (const n of srcSet) if (tgtSet.has(n)) intersection++;
365
+ const union = srcSet.size + tgtSet.size - intersection;
366
+ const jaccardScore = union > 0 ? intersection / union : 0;
367
+ const computeClusteringCoefficient = async (nodeId, neighbourArr) => {
368
+ const degree = neighbourArr.length;
369
+ if (degree < 2) return 0;
370
+ const pairs = [];
371
+ for (let i = 0; i < neighbourArr.length; i++) for (let j = i + 1; j < neighbourArr.length; j++) {
372
+ const u = neighbourArr[i];
373
+ const v = neighbourArr[j];
374
+ if (u !== void 0 && v !== void 0) pairs.push([u, v]);
375
+ }
376
+ const edgeResults = await Promise.all(pairs.flatMap(([u, v]) => [graph.getEdge(u, v), graph.getEdge(v, u)]));
377
+ let triangleCount = 0;
378
+ for (let i = 0; i < pairs.length; i++) if (edgeResults[2 * i] !== void 0 || edgeResults[2 * i + 1] !== void 0) triangleCount++;
379
+ const possibleTriangles = degree * (degree - 1) / 2;
380
+ return triangleCount / possibleTriangles;
381
+ };
382
+ const [sourceCc, targetCc] = await Promise.all([computeClusteringCoefficient(source, sourceArr), computeClusteringCoefficient(target, targetArr)]);
383
+ const score = jaccardScore * (1 - Math.max(sourceCc, targetCc));
384
+ return Math.max(epsilon, score);
385
+ }
386
+ //#endregion
387
+ //#region src/ranking/mi/etch.ts
388
+ /**
389
+ * Compute ETCH MI between two nodes.
390
+ */
391
+ function etch(graph, source, target, config) {
392
+ const { epsilon = 1e-10 } = config ?? {};
393
+ const { jaccard: jaccardScore } = require_utils$1.computeJaccard(graph, source, target);
394
+ const edge = graph.getEdge(source, target);
395
+ if (edge?.type === void 0) return Math.max(epsilon, jaccardScore);
396
+ const edgeTypeCount = require_utils$1.countEdgesOfType(graph, edge.type);
397
+ if (edgeTypeCount === 0) return Math.max(epsilon, jaccardScore);
398
+ const score = jaccardScore * Math.log(graph.edgeCount / edgeTypeCount);
399
+ return Math.max(epsilon, score);
400
+ }
401
+ /**
402
+ * Async variant of ETCH MI for use with async graph data sources.
403
+ *
404
+ * Fetches both neighbourhoods and edge data concurrently, then counts
405
+ * edges of the same type by iterating the async edge stream.
406
+ */
407
+ async function etchAsync(graph, source, target, config) {
408
+ const { epsilon = 1e-10 } = config ?? {};
409
+ const [sourceArr, targetArr, edge] = await Promise.all([
410
+ require_utils.collectAsyncIterable(graph.neighbours(source)),
411
+ require_utils.collectAsyncIterable(graph.neighbours(target)),
412
+ graph.getEdge(source, target)
413
+ ]);
414
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
415
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
416
+ let intersection = 0;
417
+ for (const n of srcSet) if (tgtSet.has(n)) intersection++;
418
+ const union = srcSet.size + tgtSet.size - intersection;
419
+ const jaccardScore = union > 0 ? intersection / union : 0;
420
+ if (edge?.type === void 0) return Math.max(epsilon, jaccardScore);
421
+ const edgeType = edge.type;
422
+ let edgeTypeCount = 0;
423
+ let totalEdges = 0;
424
+ for await (const e of graph.edges()) {
425
+ totalEdges++;
426
+ if (e.type === edgeType) edgeTypeCount++;
427
+ }
428
+ if (edgeTypeCount === 0) return Math.max(epsilon, jaccardScore);
429
+ const score = jaccardScore * Math.log(totalEdges / edgeTypeCount);
430
+ return Math.max(epsilon, score);
431
+ }
432
+ //#endregion
433
+ //#region src/ranking/mi/notch.ts
434
+ /**
435
+ * Compute NOTCH MI between two nodes.
436
+ */
437
+ function notch(graph, source, target, config) {
438
+ const { epsilon = 1e-10 } = config ?? {};
439
+ const { jaccard: jaccardScore } = require_utils$1.computeJaccard(graph, source, target);
440
+ const sourceNode = graph.getNode(source);
441
+ const targetNode = graph.getNode(target);
442
+ if (sourceNode?.type === void 0 || targetNode?.type === void 0) return Math.max(epsilon, jaccardScore);
443
+ const sourceTypeCount = require_utils$1.countNodesOfType(graph, sourceNode.type);
444
+ const targetTypeCount = require_utils$1.countNodesOfType(graph, targetNode.type);
445
+ if (sourceTypeCount === 0 || targetTypeCount === 0) return Math.max(epsilon, jaccardScore);
446
+ const sourceRarity = Math.log(graph.nodeCount / sourceTypeCount);
447
+ const targetRarity = Math.log(graph.nodeCount / targetTypeCount);
448
+ const score = jaccardScore * sourceRarity * targetRarity;
449
+ return Math.max(epsilon, score);
450
+ }
451
+ /**
452
+ * Async variant of NOTCH MI for use with async graph data sources.
453
+ *
454
+ * Fetches both neighbourhoods and node data concurrently, then counts
455
+ * nodes of each type by iterating the async node stream.
456
+ */
457
+ async function notchAsync(graph, source, target, config) {
458
+ const { epsilon = 1e-10 } = config ?? {};
459
+ const [sourceArr, targetArr, sourceNode, targetNode] = await Promise.all([
460
+ require_utils.collectAsyncIterable(graph.neighbours(source)),
461
+ require_utils.collectAsyncIterable(graph.neighbours(target)),
462
+ graph.getNode(source),
463
+ graph.getNode(target)
464
+ ]);
465
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
466
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
467
+ let intersection = 0;
468
+ for (const n of srcSet) if (tgtSet.has(n)) intersection++;
469
+ const union = srcSet.size + tgtSet.size - intersection;
470
+ const jaccardScore = union > 0 ? intersection / union : 0;
471
+ if (sourceNode?.type === void 0 || targetNode?.type === void 0) return Math.max(epsilon, jaccardScore);
472
+ const sourceType = sourceNode.type;
473
+ const targetType = targetNode.type;
474
+ let totalNodes = 0;
475
+ let sourceTypeCount = 0;
476
+ let targetTypeCount = 0;
477
+ for await (const nodeId of graph.nodeIds()) {
478
+ totalNodes++;
479
+ const node = await graph.getNode(nodeId);
480
+ if (node?.type === sourceType) sourceTypeCount++;
481
+ if (node?.type === targetType) targetTypeCount++;
482
+ }
483
+ if (sourceTypeCount === 0 || targetTypeCount === 0) return Math.max(epsilon, jaccardScore);
484
+ const sourceRarity = Math.log(totalNodes / sourceTypeCount);
485
+ const targetRarity = Math.log(totalNodes / targetTypeCount);
486
+ const score = jaccardScore * sourceRarity * targetRarity;
487
+ return Math.max(epsilon, score);
488
+ }
489
+ //#endregion
490
+ //#region src/ranking/mi/adaptive.ts
491
+ /**
492
+ * Compute unified adaptive MI between two connected nodes.
493
+ *
494
+ * Combines structural, degree, and overlap signals with
495
+ * configurable weighting.
496
+ *
497
+ * @param graph - Source graph
498
+ * @param source - Source node ID
499
+ * @param target - Target node ID
500
+ * @param config - Optional configuration with component weights
501
+ * @returns Adaptive MI score in [0, 1]
502
+ */
503
+ function adaptive(graph, source, target, config) {
504
+ const { epsilon = 1e-10, structuralWeight = .4, degreeWeight = .3, overlapWeight = .3 } = config ?? {};
505
+ const { jaccard: jaccardScore, sourceNeighbours, targetNeighbours } = require_utils$1.computeJaccard(graph, source, target);
506
+ const structural = sourceNeighbours.size === 0 && targetNeighbours.size === 0 ? 0 : Math.max(epsilon, jaccardScore);
507
+ const degreeComponent = adamicAdar(graph, source, target, {
508
+ epsilon,
509
+ normalise: true
510
+ });
511
+ let overlap;
512
+ if (sourceNeighbours.size > 0 && targetNeighbours.size > 0) {
513
+ const { intersection } = require_utils$1.neighbourOverlap(sourceNeighbours, targetNeighbours);
514
+ const minDegree = Math.min(sourceNeighbours.size, targetNeighbours.size);
515
+ overlap = minDegree > 0 ? intersection / minDegree : epsilon;
516
+ } else overlap = epsilon;
517
+ const totalWeight = structuralWeight + degreeWeight + overlapWeight;
518
+ const score = (structuralWeight * structural + degreeWeight * degreeComponent + overlapWeight * overlap) / totalWeight;
519
+ return Math.max(epsilon, Math.min(1, score));
520
+ }
521
+ /**
522
+ * Async variant of Adaptive MI for use with async graph data sources.
523
+ *
524
+ * Fetches both neighbourhoods concurrently, then delegates degree-weighted
525
+ * component to the async Adamic-Adar variant.
526
+ */
527
+ async function adaptiveAsync(graph, source, target, config) {
528
+ const { epsilon = 1e-10, structuralWeight = .4, degreeWeight = .3, overlapWeight = .3 } = config ?? {};
529
+ const [sourceArr, targetArr, degreeComponent] = await Promise.all([
530
+ require_utils.collectAsyncIterable(graph.neighbours(source)),
531
+ require_utils.collectAsyncIterable(graph.neighbours(target)),
532
+ adamicAdarAsync(graph, source, target, {
533
+ epsilon,
534
+ normalise: true
535
+ })
536
+ ]);
537
+ const srcSet = new Set(sourceArr.filter((n) => n !== target));
538
+ const tgtSet = new Set(targetArr.filter((n) => n !== source));
539
+ let intersection = 0;
540
+ for (const n of srcSet) if (tgtSet.has(n)) intersection++;
541
+ const union = srcSet.size + tgtSet.size - intersection;
542
+ const jaccardScore = union > 0 ? intersection / union : 0;
543
+ const structural = srcSet.size === 0 && tgtSet.size === 0 ? 0 : Math.max(epsilon, jaccardScore);
544
+ let overlap;
545
+ if (srcSet.size > 0 && tgtSet.size > 0) {
546
+ const minDegree = Math.min(srcSet.size, tgtSet.size);
547
+ overlap = minDegree > 0 ? intersection / minDegree : epsilon;
548
+ } else overlap = epsilon;
549
+ const totalWeight = structuralWeight + degreeWeight + overlapWeight;
550
+ const score = (structuralWeight * structural + degreeWeight * degreeComponent + overlapWeight * overlap) / totalWeight;
551
+ return Math.max(epsilon, Math.min(1, score));
552
+ }
553
+ //#endregion
554
+ exports.adamicAdar = adamicAdar;
555
+ exports.adamicAdarAsync = adamicAdarAsync;
556
+ exports.adaptive = adaptive;
557
+ exports.adaptiveAsync = adaptiveAsync;
558
+ exports.cosine = cosine;
559
+ exports.cosineAsync = cosineAsync;
560
+ exports.etch = etch;
561
+ exports.etchAsync = etchAsync;
562
+ exports.hubPromoted = hubPromoted;
563
+ exports.hubPromotedAsync = hubPromotedAsync;
564
+ exports.jaccard = require_jaccard.jaccard;
565
+ exports.jaccardAsync = require_jaccard.jaccardAsync;
566
+ exports.notch = notch;
567
+ exports.notchAsync = notchAsync;
568
+ exports.overlapCoefficient = overlapCoefficient;
569
+ exports.overlapCoefficientAsync = overlapCoefficientAsync;
570
+ exports.resourceAllocation = resourceAllocation;
571
+ exports.resourceAllocationAsync = resourceAllocationAsync;
572
+ exports.scale = scale;
573
+ exports.scaleAsync = scaleAsync;
574
+ exports.skew = skew;
575
+ exports.skewAsync = skewAsync;
576
+ exports.sorensen = sorensen;
577
+ exports.sorensenAsync = sorensenAsync;
578
+ exports.span = span;
579
+ exports.spanAsync = spanAsync;
580
+
581
+ //# sourceMappingURL=index.cjs.map