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.
- package/dist/adjacency-map-BtKzcuJq.js +229 -0
- package/dist/adjacency-map-BtKzcuJq.js.map +1 -0
- package/dist/adjacency-map-JqBnMNkF.cjs +234 -0
- package/dist/adjacency-map-JqBnMNkF.cjs.map +1 -0
- package/dist/async/index.cjs +15 -242
- package/dist/async/index.js +2 -229
- package/dist/expansion/index.cjs +43 -0
- package/dist/expansion/index.js +2 -0
- package/dist/expansion-ClDhlMK8.js +1704 -0
- package/dist/expansion-ClDhlMK8.js.map +1 -0
- package/dist/expansion-DaTroIyv.cjs +1949 -0
- package/dist/expansion-DaTroIyv.cjs.map +1 -0
- package/dist/extraction/index.cjs +630 -0
- package/dist/extraction/index.cjs.map +1 -0
- package/dist/extraction/index.js +621 -0
- package/dist/extraction/index.js.map +1 -0
- package/dist/gpu/csr.d.ts +29 -30
- package/dist/gpu/csr.d.ts.map +1 -1
- package/dist/gpu/dispatch.d.ts +31 -0
- package/dist/gpu/dispatch.d.ts.map +1 -0
- package/dist/gpu/dispatch.unit.test.d.ts +5 -0
- package/dist/gpu/dispatch.unit.test.d.ts.map +1 -0
- package/dist/gpu/index.cjs +15 -410
- package/dist/gpu/index.d.ts +3 -1
- package/dist/gpu/index.d.ts.map +1 -1
- package/dist/gpu/index.js +2 -400
- package/dist/gpu/kernels/bfs/kernel.d.ts +59 -0
- package/dist/gpu/kernels/bfs/kernel.d.ts.map +1 -0
- package/dist/gpu/kernels/bfs/logic.d.ts +47 -0
- package/dist/gpu/kernels/bfs/logic.d.ts.map +1 -0
- package/dist/gpu/kernels/bfs/logic.unit.test.d.ts +2 -0
- package/dist/gpu/kernels/bfs/logic.unit.test.d.ts.map +1 -0
- package/dist/gpu/kernels/degree-histogram/kernel.d.ts +32 -0
- package/dist/gpu/kernels/degree-histogram/kernel.d.ts.map +1 -0
- package/dist/gpu/kernels/degree-histogram/logic.d.ts +45 -0
- package/dist/gpu/kernels/degree-histogram/logic.d.ts.map +1 -0
- package/dist/gpu/kernels/degree-histogram/logic.unit.test.d.ts +2 -0
- package/dist/gpu/kernels/degree-histogram/logic.unit.test.d.ts.map +1 -0
- package/dist/gpu/kernels/jaccard/kernel.d.ts +40 -0
- package/dist/gpu/kernels/jaccard/kernel.d.ts.map +1 -0
- package/dist/gpu/kernels/jaccard/logic.d.ts +43 -0
- package/dist/gpu/kernels/jaccard/logic.d.ts.map +1 -0
- package/dist/gpu/kernels/jaccard/logic.unit.test.d.ts +2 -0
- package/dist/gpu/kernels/jaccard/logic.unit.test.d.ts.map +1 -0
- package/dist/gpu/kernels/pagerank/kernel.d.ts +44 -0
- package/dist/gpu/kernels/pagerank/kernel.d.ts.map +1 -0
- package/dist/gpu/kernels/pagerank/logic.d.ts +50 -0
- package/dist/gpu/kernels/pagerank/logic.d.ts.map +1 -0
- package/dist/gpu/kernels/pagerank/logic.unit.test.d.ts +2 -0
- package/dist/gpu/kernels/pagerank/logic.unit.test.d.ts.map +1 -0
- package/dist/gpu/kernels/spmv/kernel.d.ts +43 -0
- package/dist/gpu/kernels/spmv/kernel.d.ts.map +1 -0
- package/dist/gpu/kernels/spmv/logic.d.ts +31 -0
- package/dist/gpu/kernels/spmv/logic.d.ts.map +1 -0
- package/dist/gpu/kernels/spmv/logic.unit.test.d.ts +2 -0
- package/dist/gpu/kernels/spmv/logic.unit.test.d.ts.map +1 -0
- package/dist/gpu/operations.d.ts +76 -0
- package/dist/gpu/operations.d.ts.map +1 -0
- package/dist/gpu/operations.unit.test.d.ts +5 -0
- package/dist/gpu/operations.unit.test.d.ts.map +1 -0
- package/dist/gpu/root.d.ts +53 -0
- package/dist/gpu/root.d.ts.map +1 -0
- package/dist/gpu/root.unit.test.d.ts +2 -0
- package/dist/gpu/root.unit.test.d.ts.map +1 -0
- package/dist/gpu/types.d.ts +3 -8
- package/dist/gpu/types.d.ts.map +1 -1
- package/dist/gpu-CHiCN0wa.js +16945 -0
- package/dist/gpu-CHiCN0wa.js.map +1 -0
- package/dist/gpu-Y6owRVMi.cjs +17028 -0
- package/dist/gpu-Y6owRVMi.cjs.map +1 -0
- package/dist/graph/index.cjs +2 -229
- package/dist/graph/index.js +1 -228
- package/dist/index/index.cjs +141 -4040
- package/dist/index/index.js +15 -3917
- package/dist/jaccard-3rCdilwm.js +39 -0
- package/dist/jaccard-3rCdilwm.js.map +1 -0
- package/dist/jaccard-Bys9_dGW.cjs +50 -0
- package/dist/jaccard-Bys9_dGW.cjs.map +1 -0
- package/dist/{kmeans-BIgSyGKu.cjs → kmeans-B8x9D1kt.cjs} +1 -1
- package/dist/{kmeans-BIgSyGKu.cjs.map → kmeans-B8x9D1kt.cjs.map} +1 -1
- package/dist/{kmeans-87ExSUNZ.js → kmeans-DKkL9rAN.js} +1 -1
- package/dist/{kmeans-87ExSUNZ.js.map → kmeans-DKkL9rAN.js.map} +1 -1
- package/dist/ops-djAsQQSh.cjs +277 -0
- package/dist/ops-djAsQQSh.cjs.map +1 -0
- package/dist/ops-upIi6JIi.js +212 -0
- package/dist/ops-upIi6JIi.js.map +1 -0
- package/dist/priority-queue-BIiD1L0k.cjs +148 -0
- package/dist/priority-queue-BIiD1L0k.cjs.map +1 -0
- package/dist/priority-queue-CFDd5cBg.js +143 -0
- package/dist/priority-queue-CFDd5cBg.js.map +1 -0
- package/dist/ranking/index.cjs +43 -0
- package/dist/ranking/index.js +4 -0
- package/dist/ranking/mi/index.cjs +581 -0
- package/dist/ranking/mi/index.cjs.map +1 -0
- package/dist/ranking/mi/index.js +555 -0
- package/dist/ranking/mi/index.js.map +1 -0
- package/dist/ranking-3ez5m67U.js +1016 -0
- package/dist/ranking-3ez5m67U.js.map +1 -0
- package/dist/ranking-DVvajgUZ.cjs +1093 -0
- package/dist/ranking-DVvajgUZ.cjs.map +1 -0
- package/dist/seeds/index.cjs +1 -1
- package/dist/seeds/index.js +1 -1
- package/dist/structures/index.cjs +2 -143
- package/dist/structures/index.js +1 -142
- package/dist/utils/index.cjs +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils-BodeE2Mo.js +22 -0
- package/dist/utils-BodeE2Mo.js.map +1 -0
- package/dist/utils-CDtCcsyF.cjs +33 -0
- package/dist/utils-CDtCcsyF.cjs.map +1 -0
- package/package.json +3 -1
- package/dist/async/index.cjs.map +0 -1
- package/dist/async/index.js.map +0 -1
- package/dist/gpu/context.d.ts +0 -118
- package/dist/gpu/context.d.ts.map +0 -1
- package/dist/gpu/context.unit.test.d.ts +0 -2
- package/dist/gpu/context.unit.test.d.ts.map +0 -1
- package/dist/gpu/index.cjs.map +0 -1
- package/dist/gpu/index.js.map +0 -1
- package/dist/graph/index.cjs.map +0 -1
- package/dist/graph/index.js.map +0 -1
- package/dist/index/index.cjs.map +0 -1
- package/dist/index/index.js.map +0 -1
- package/dist/structures/index.cjs.map +0 -1
- package/dist/structures/index.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/ranking/mi/adamic-adar.ts","../../../src/ranking/mi/cosine.ts","../../../src/ranking/mi/sorensen.ts","../../../src/ranking/mi/resource-allocation.ts","../../../src/ranking/mi/overlap-coefficient.ts","../../../src/ranking/mi/hub-promoted.ts","../../../src/ranking/mi/scale.ts","../../../src/ranking/mi/skew.ts","../../../src/ranking/mi/span.ts","../../../src/ranking/mi/etch.ts","../../../src/ranking/mi/notch.ts","../../../src/ranking/mi/adaptive.ts"],"sourcesContent":["/**\n * Adamic-Adar index for edge salience.\n *\n * Sum of inverse log degrees of common neighbours:\n * MI(u,v) = Σ_{z ∈ N(u) ∩ N(v)} 1 / log(deg(z) + 1)\n *\n * Range: [0, ∞) - higher values indicate stronger association\n * Normalised to [0, 1] by dividing by max possible value.\n */\n\nimport type { NodeId, NodeData, EdgeData, ReadableGraph } from \"../../graph\";\nimport type { AsyncReadableGraph } from \"../../graph/async-interfaces\";\nimport { neighbourSet, neighbourIntersection } from \"../../utils\";\nimport { collectAsyncIterable } from \"../../async/utils\";\nimport type { MIConfig } from \"./types\";\n\n/**\n * Compute Adamic-Adar index between neighbourhoods of two nodes.\n *\n * @param graph - Source graph\n * @param source - Source node ID\n * @param target - Target node ID\n * @param config - Optional configuration\n * @returns Adamic-Adar index (normalised to [0, 1] if configured)\n */\nexport function adamicAdar<N extends NodeData, E extends EdgeData>(\n\tgraph: ReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): number {\n\tconst { epsilon = 1e-10, normalise = true } = config ?? {};\n\n\t// Get neighbourhoods, excluding opposite endpoint\n\tconst sourceNeighbours = neighbourSet(graph, source, target);\n\tconst targetNeighbours = neighbourSet(graph, target, source);\n\n\t// Compute common neighbours\n\tconst commonNeighbours = neighbourIntersection(\n\t\tsourceNeighbours,\n\t\ttargetNeighbours,\n\t);\n\n\t// Sum inverse log degrees of common neighbours\n\tlet score = 0;\n\tfor (const neighbour of commonNeighbours) {\n\t\tconst degree = graph.degree(neighbour);\n\t\tscore += 1 / Math.log(degree + 1);\n\t}\n\n\t// Normalise to [0, 1] if requested\n\tif (normalise && commonNeighbours.size > 0) {\n\t\t// Max possible is when all common neighbours have minimum degree (1)\n\t\t// 1 / log(1 + 1) = 1 / log(2)\n\t\tconst maxScore = commonNeighbours.size / Math.log(2);\n\t\tscore = score / maxScore;\n\t}\n\n\t// Apply epsilon floor for numerical stability\n\treturn Math.max(epsilon, score);\n}\n\n/**\n * Async variant of Adamic-Adar index for use with async graph data sources.\n *\n * Fetches both neighbourhoods concurrently, then fetches degree for each common\n * neighbour to compute the inverse-log-degree weighted sum.\n */\nexport async function adamicAdarAsync<N extends NodeData, E extends EdgeData>(\n\tgraph: AsyncReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): Promise<number> {\n\tconst { epsilon = 1e-10, normalise = true } = config ?? {};\n\n\t// Fetch both neighbourhoods in parallel\n\tconst [sourceArr, targetArr] = await Promise.all([\n\t\tcollectAsyncIterable(graph.neighbours(source)),\n\t\tcollectAsyncIterable(graph.neighbours(target)),\n\t]);\n\n\tconst srcSet = new Set(sourceArr.filter((n) => n !== target));\n\tconst tgtSet = new Set(targetArr.filter((n) => n !== source));\n\n\t// Find common neighbours\n\tconst commonNeighbours: NodeId[] = [];\n\tfor (const n of srcSet) {\n\t\tif (tgtSet.has(n)) commonNeighbours.push(n);\n\t}\n\n\tif (commonNeighbours.length === 0) {\n\t\treturn epsilon;\n\t}\n\n\t// Fetch degrees of all common neighbours in parallel\n\tconst degrees = await Promise.all(\n\t\tcommonNeighbours.map((n) => graph.degree(n)),\n\t);\n\n\t// Sum inverse log degrees\n\tlet score = 0;\n\tfor (const degree of degrees) {\n\t\tscore += 1 / Math.log(degree + 1);\n\t}\n\n\t// Normalise to [0, 1] if requested\n\tif (normalise) {\n\t\t// Max possible is when all common neighbours have minimum degree (1)\n\t\t// 1 / log(1 + 1) = 1 / log(2)\n\t\tconst maxScore = commonNeighbours.length / Math.log(2);\n\t\tscore = score / maxScore;\n\t}\n\n\treturn Math.max(epsilon, score);\n}\n","/**\n * Cosine similarity for edge salience.\n *\n * Measures similarity between neighbourhoods using vector cosine:\n * MI(u,v) = |N(u) ∩ N(v)| / (√|N(u)| × √|N(v)|)\n *\n * Range: [0, 1]\n * - 0: No overlap or one node has no neighbours\n * - 1: Identical neighbourhoods of equal size\n */\n\nimport type { NodeId, NodeData, EdgeData, ReadableGraph } from \"../../graph\";\nimport type { AsyncReadableGraph } from \"../../graph/async-interfaces\";\nimport { neighbourSet, neighbourOverlap } from \"../../utils\";\nimport { collectAsyncIterable } from \"../../async/utils\";\nimport type { MIConfig } from \"./types\";\n\n/**\n * Compute cosine similarity between neighbourhoods of two nodes.\n *\n * @param graph - Source graph\n * @param source - Source node ID\n * @param target - Target node ID\n * @param config - Optional configuration\n * @returns Cosine similarity in [0, 1]\n */\nexport function cosine<N extends NodeData, E extends EdgeData>(\n\tgraph: ReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): number {\n\tconst { epsilon = 1e-10 } = config ?? {};\n\n\t// Get neighbourhoods, excluding opposite endpoint\n\tconst sourceNeighbours = neighbourSet(graph, source, target);\n\tconst targetNeighbours = neighbourSet(graph, target, source);\n\n\t// Compute intersection size\n\tconst { intersection } = neighbourOverlap(sourceNeighbours, targetNeighbours);\n\n\t// Compute denominator: √|N(u)| × √|N(v)|\n\tconst denominator =\n\t\tMath.sqrt(sourceNeighbours.size) * Math.sqrt(targetNeighbours.size);\n\n\t// Avoid division by zero\n\tif (denominator === 0) {\n\t\treturn 0;\n\t}\n\n\tconst score = intersection / denominator;\n\n\t// Apply epsilon floor for numerical stability\n\treturn Math.max(epsilon, score);\n}\n\n/**\n * Async variant of cosine similarity for use with async graph data sources.\n *\n * Fetches both neighbourhoods concurrently, then applies the same formula.\n */\nexport async function cosineAsync<N extends NodeData, E extends EdgeData>(\n\tgraph: AsyncReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): Promise<number> {\n\tconst { epsilon = 1e-10 } = config ?? {};\n\n\t// Fetch both neighbourhoods in parallel\n\tconst [sourceArr, targetArr] = await Promise.all([\n\t\tcollectAsyncIterable(graph.neighbours(source)),\n\t\tcollectAsyncIterable(graph.neighbours(target)),\n\t]);\n\n\tconst srcSet = new Set(sourceArr.filter((n) => n !== target));\n\tconst tgtSet = new Set(targetArr.filter((n) => n !== source));\n\n\t// Compute intersection size\n\tlet intersection = 0;\n\tfor (const n of srcSet) {\n\t\tif (tgtSet.has(n)) intersection++;\n\t}\n\n\t// Compute denominator: √|N(u)| × √|N(v)|\n\tconst denominator = Math.sqrt(srcSet.size) * Math.sqrt(tgtSet.size);\n\n\t// Avoid division by zero\n\tif (denominator === 0) {\n\t\treturn 0;\n\t}\n\n\tconst score = intersection / denominator;\n\n\treturn Math.max(epsilon, score);\n}\n","/**\n * Sorensen-Dice coefficient for edge salience.\n *\n * Harmonic mean of overlap:\n * MI(u,v) = 2|N(u) ∩ N(v)| / (|N(u)| + |N(v)|)\n *\n * Range: [0, 1]\n * - 0: No shared neighbours\n * - 1: Identical neighbourhoods\n */\n\nimport type { NodeId, NodeData, EdgeData, ReadableGraph } from \"../../graph\";\nimport type { AsyncReadableGraph } from \"../../graph/async-interfaces\";\nimport { neighbourSet, neighbourOverlap } from \"../../utils\";\nimport { collectAsyncIterable } from \"../../async/utils\";\nimport type { MIConfig } from \"./types\";\n\n/**\n * Compute Sorensen-Dice similarity between neighbourhoods of two nodes.\n *\n * @param graph - Source graph\n * @param source - Source node ID\n * @param target - Target node ID\n * @param config - Optional configuration\n * @returns Sorensen-Dice coefficient in [0, 1]\n */\nexport function sorensen<N extends NodeData, E extends EdgeData>(\n\tgraph: ReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): number {\n\tconst { epsilon = 1e-10 } = config ?? {};\n\n\t// Get neighbourhoods, excluding opposite endpoint\n\tconst sourceNeighbours = neighbourSet(graph, source, target);\n\tconst targetNeighbours = neighbourSet(graph, target, source);\n\n\t// Compute intersection size\n\tconst { intersection } = neighbourOverlap(sourceNeighbours, targetNeighbours);\n\n\t// Compute denominator: |N(u)| + |N(v)|\n\tconst denominator = sourceNeighbours.size + targetNeighbours.size;\n\n\t// Avoid division by zero\n\tif (denominator === 0) {\n\t\treturn 0;\n\t}\n\n\tconst score = (2 * intersection) / denominator;\n\n\t// Apply epsilon floor for numerical stability\n\treturn Math.max(epsilon, score);\n}\n\n/**\n * Async variant of Sorensen-Dice coefficient for use with async graph data sources.\n *\n * Fetches both neighbourhoods concurrently, then applies the same formula.\n */\nexport async function sorensenAsync<N extends NodeData, E extends EdgeData>(\n\tgraph: AsyncReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): Promise<number> {\n\tconst { epsilon = 1e-10 } = config ?? {};\n\n\t// Fetch both neighbourhoods in parallel\n\tconst [sourceArr, targetArr] = await Promise.all([\n\t\tcollectAsyncIterable(graph.neighbours(source)),\n\t\tcollectAsyncIterable(graph.neighbours(target)),\n\t]);\n\n\tconst srcSet = new Set(sourceArr.filter((n) => n !== target));\n\tconst tgtSet = new Set(targetArr.filter((n) => n !== source));\n\n\t// Compute intersection size\n\tlet intersection = 0;\n\tfor (const n of srcSet) {\n\t\tif (tgtSet.has(n)) intersection++;\n\t}\n\n\t// Compute denominator: |N(u)| + |N(v)|\n\tconst denominator = srcSet.size + tgtSet.size;\n\n\t// Avoid division by zero\n\tif (denominator === 0) {\n\t\treturn 0;\n\t}\n\n\tconst score = (2 * intersection) / denominator;\n\n\treturn Math.max(epsilon, score);\n}\n","/**\n * Resource Allocation index for edge salience.\n *\n * Sum of inverse degrees of common neighbours:\n * MI(u,v) = Σ_{z ∈ N(u) ∩ N(v)} 1 / deg(z)\n *\n * Range: [0, ∞)\n * Assumption: shared neighbours with low degree are more informative than hubs.\n */\n\nimport type { NodeId, NodeData, EdgeData, ReadableGraph } from \"../../graph\";\nimport type { AsyncReadableGraph } from \"../../graph/async-interfaces\";\nimport { neighbourSet, neighbourIntersection } from \"../../utils\";\nimport { collectAsyncIterable } from \"../../async/utils\";\nimport type { MIConfig } from \"./types\";\n\n/**\n * Compute Resource Allocation index between neighbourhoods of two nodes.\n *\n * @param graph - Source graph\n * @param source - Source node ID\n * @param target - Target node ID\n * @param config - Optional configuration\n * @returns Resource Allocation index (normalised to [0, 1] if configured)\n */\nexport function resourceAllocation<N extends NodeData, E extends EdgeData>(\n\tgraph: ReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): number {\n\tconst { epsilon = 1e-10, normalise = true } = config ?? {};\n\n\t// Get neighbourhoods, excluding opposite endpoint\n\tconst sourceNeighbours = neighbourSet(graph, source, target);\n\tconst targetNeighbours = neighbourSet(graph, target, source);\n\n\t// Compute common neighbours\n\tconst commonNeighbours = neighbourIntersection(\n\t\tsourceNeighbours,\n\t\ttargetNeighbours,\n\t);\n\n\t// Sum inverse degrees of common neighbours\n\tlet score = 0;\n\tfor (const neighbour of commonNeighbours) {\n\t\tconst degree = graph.degree(neighbour);\n\t\tif (degree > 0) {\n\t\t\tscore += 1 / degree;\n\t\t}\n\t}\n\n\t// Normalise to [0, 1] if requested\n\tif (normalise && commonNeighbours.size > 0) {\n\t\t// Max possible is when all common neighbours have minimum degree (1)\n\t\tconst maxScore = commonNeighbours.size;\n\t\tscore = score / maxScore;\n\t}\n\n\t// Apply epsilon floor for numerical stability\n\treturn Math.max(epsilon, score);\n}\n\n/**\n * Async variant of Resource Allocation index for use with async graph data sources.\n *\n * Fetches both neighbourhoods concurrently, then fetches degree for each common\n * neighbour to compute the inverse-degree weighted sum.\n */\nexport async function resourceAllocationAsync<\n\tN extends NodeData,\n\tE extends EdgeData,\n>(\n\tgraph: AsyncReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): Promise<number> {\n\tconst { epsilon = 1e-10, normalise = true } = config ?? {};\n\n\t// Fetch both neighbourhoods in parallel\n\tconst [sourceArr, targetArr] = await Promise.all([\n\t\tcollectAsyncIterable(graph.neighbours(source)),\n\t\tcollectAsyncIterable(graph.neighbours(target)),\n\t]);\n\n\tconst srcSet = new Set(sourceArr.filter((n) => n !== target));\n\tconst tgtSet = new Set(targetArr.filter((n) => n !== source));\n\n\t// Find common neighbours\n\tconst commonNeighbours: NodeId[] = [];\n\tfor (const n of srcSet) {\n\t\tif (tgtSet.has(n)) commonNeighbours.push(n);\n\t}\n\n\tif (commonNeighbours.length === 0) {\n\t\treturn epsilon;\n\t}\n\n\t// Fetch degrees of all common neighbours in parallel\n\tconst degrees = await Promise.all(\n\t\tcommonNeighbours.map((n) => graph.degree(n)),\n\t);\n\n\t// Sum inverse degrees\n\tlet score = 0;\n\tfor (const degree of degrees) {\n\t\tif (degree > 0) {\n\t\t\tscore += 1 / degree;\n\t\t}\n\t}\n\n\t// Normalise to [0, 1] if requested\n\tif (normalise) {\n\t\t// Max possible is when all common neighbours have minimum degree (1)\n\t\tconst maxScore = commonNeighbours.length;\n\t\tscore = score / maxScore;\n\t}\n\n\treturn Math.max(epsilon, score);\n}\n","/**\n * Overlap Coefficient for edge salience.\n *\n * Minimum-based neighbourhood overlap:\n * MI(u,v) = |N(u) ∩ N(v)| / min(|N(u)|, |N(v)|)\n *\n * Range: [0, 1]\n * - 0: No shared neighbours or one node has no neighbours\n * - 1: One neighbourhood is a subset of the other\n */\n\nimport type { NodeId, NodeData, EdgeData, ReadableGraph } from \"../../graph\";\nimport type { AsyncReadableGraph } from \"../../graph/async-interfaces\";\nimport { neighbourSet, neighbourOverlap } from \"../../utils\";\nimport { collectAsyncIterable } from \"../../async/utils\";\nimport type { MIConfig } from \"./types\";\n\n/**\n * Compute Overlap Coefficient between neighbourhoods of two nodes.\n *\n * @param graph - Source graph\n * @param source - Source node ID\n * @param target - Target node ID\n * @param config - Optional configuration\n * @returns Overlap Coefficient in [0, 1]\n */\nexport function overlapCoefficient<N extends NodeData, E extends EdgeData>(\n\tgraph: ReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): number {\n\tconst { epsilon = 1e-10 } = config ?? {};\n\n\t// Get neighbourhoods, excluding opposite endpoint\n\tconst sourceNeighbours = neighbourSet(graph, source, target);\n\tconst targetNeighbours = neighbourSet(graph, target, source);\n\n\t// Compute intersection size\n\tconst { intersection } = neighbourOverlap(sourceNeighbours, targetNeighbours);\n\n\t// Compute denominator: min(|N(u)|, |N(v)|)\n\tconst denominator = Math.min(sourceNeighbours.size, targetNeighbours.size);\n\n\t// Avoid division by zero\n\tif (denominator === 0) {\n\t\treturn 0;\n\t}\n\n\tconst score = intersection / denominator;\n\n\t// Apply epsilon floor for numerical stability\n\treturn Math.max(epsilon, score);\n}\n\n/**\n * Async variant of Overlap Coefficient for use with async graph data sources.\n *\n * Fetches both neighbourhoods concurrently, then applies the same formula.\n */\nexport async function overlapCoefficientAsync<\n\tN extends NodeData,\n\tE extends EdgeData,\n>(\n\tgraph: AsyncReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): Promise<number> {\n\tconst { epsilon = 1e-10 } = config ?? {};\n\n\t// Fetch both neighbourhoods in parallel\n\tconst [sourceArr, targetArr] = await Promise.all([\n\t\tcollectAsyncIterable(graph.neighbours(source)),\n\t\tcollectAsyncIterable(graph.neighbours(target)),\n\t]);\n\n\tconst srcSet = new Set(sourceArr.filter((n) => n !== target));\n\tconst tgtSet = new Set(targetArr.filter((n) => n !== source));\n\n\t// Compute intersection size\n\tlet intersection = 0;\n\tfor (const n of srcSet) {\n\t\tif (tgtSet.has(n)) intersection++;\n\t}\n\n\t// Compute denominator: min(|N(u)|, |N(v)|)\n\tconst denominator = Math.min(srcSet.size, tgtSet.size);\n\n\t// Avoid division by zero\n\tif (denominator === 0) {\n\t\treturn 0;\n\t}\n\n\tconst score = intersection / denominator;\n\n\treturn Math.max(epsilon, score);\n}\n","/**\n * Hub Promoted index for edge salience.\n *\n * Hub-promoting neighbourhood overlap:\n * MI(u,v) = |N(u) ∩ N(v)| / min(deg(u), deg(v))\n *\n * Range: [0, 1]\n * Uses node degrees rather than neighbourhood set sizes.\n * Similar to Overlap Coefficient but normalised by degree.\n */\n\nimport type { NodeId, NodeData, EdgeData, ReadableGraph } from \"../../graph\";\nimport type { AsyncReadableGraph } from \"../../graph/async-interfaces\";\nimport { neighbourSet, neighbourOverlap } from \"../../utils\";\nimport { collectAsyncIterable } from \"../../async/utils\";\nimport type { MIConfig } from \"./types\";\n\n/**\n * Compute Hub Promoted index between neighbourhoods of two nodes.\n *\n * @param graph - Source graph\n * @param source - Source node ID\n * @param target - Target node ID\n * @param config - Optional configuration\n * @returns Hub Promoted index in [0, 1]\n */\nexport function hubPromoted<N extends NodeData, E extends EdgeData>(\n\tgraph: ReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): number {\n\tconst { epsilon = 1e-10 } = config ?? {};\n\n\t// Get neighbourhoods, excluding opposite endpoint\n\tconst sourceNeighbours = neighbourSet(graph, source, target);\n\tconst targetNeighbours = neighbourSet(graph, target, source);\n\n\t// Compute intersection size\n\tconst { intersection } = neighbourOverlap(sourceNeighbours, targetNeighbours);\n\n\t// Compute denominator using actual degrees\n\tconst sourceDegree = graph.degree(source);\n\tconst targetDegree = graph.degree(target);\n\tconst denominator = Math.min(sourceDegree, targetDegree);\n\n\t// Avoid division by zero\n\tif (denominator === 0) {\n\t\treturn 0;\n\t}\n\n\tconst score = intersection / denominator;\n\n\t// Apply epsilon floor for numerical stability\n\treturn Math.max(epsilon, score);\n}\n\n/**\n * Async variant of Hub Promoted index for use with async graph data sources.\n *\n * Fetches both neighbourhoods and degrees concurrently, then applies the same formula.\n */\nexport async function hubPromotedAsync<N extends NodeData, E extends EdgeData>(\n\tgraph: AsyncReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): Promise<number> {\n\tconst { epsilon = 1e-10 } = config ?? {};\n\n\t// Fetch both neighbourhoods and degrees in parallel\n\tconst [sourceArr, targetArr, sourceDegree, targetDegree] = await Promise.all([\n\t\tcollectAsyncIterable(graph.neighbours(source)),\n\t\tcollectAsyncIterable(graph.neighbours(target)),\n\t\tgraph.degree(source),\n\t\tgraph.degree(target),\n\t]);\n\n\tconst srcSet = new Set(sourceArr.filter((n) => n !== target));\n\tconst tgtSet = new Set(targetArr.filter((n) => n !== source));\n\n\t// Compute intersection size\n\tlet intersection = 0;\n\tfor (const n of srcSet) {\n\t\tif (tgtSet.has(n)) intersection++;\n\t}\n\n\t// Compute denominator using actual degrees\n\tconst denominator = Math.min(sourceDegree, targetDegree);\n\n\t// Avoid division by zero\n\tif (denominator === 0) {\n\t\treturn 0;\n\t}\n\n\tconst score = intersection / denominator;\n\n\treturn Math.max(epsilon, score);\n}\n","/**\n * SCALE (Structural Coherence via Adjacency Lattice Entropy) MI variant.\n *\n * Density-normalised Jaccard, correcting for graph density variation.\n * Formula: MI(u,v) = Jaccard(u,v) / ρ(G)\n *\n * where ρ(G) = 2 * |E| / (|V| * (|V| - 1)) for undirected graphs\n * ρ(G) = |E| / (|V| * (|V| - 1)) for directed graphs\n *\n * Range: [0, ∞) but typically scales with graph density\n */\n\nimport type { NodeId, NodeData, EdgeData, ReadableGraph } from \"../../graph\";\nimport type { AsyncReadableGraph } from \"../../graph/async-interfaces\";\nimport { computeJaccard } from \"../../utils\";\nimport { collectAsyncIterable } from \"../../async/utils\";\nimport type { MIConfig } from \"./types\";\n\n/**\n * Compute SCALE MI between two nodes.\n */\nexport function scale<N extends NodeData, E extends EdgeData>(\n\tgraph: ReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): number {\n\tconst { epsilon = 1e-10 } = config ?? {};\n\n\tconst { jaccard: jaccardScore } = computeJaccard(graph, source, target);\n\n\t// Compute graph density\n\tconst n = graph.nodeCount;\n\tconst m = graph.edgeCount;\n\n\t// ρ(G) = 2|E| / (|V|(|V|-1)) for undirected; |E| / (|V|(|V|-1)) for directed\n\tconst possibleEdges = n * (n - 1);\n\tconst density =\n\t\tpossibleEdges > 0 ? (graph.directed ? m : 2 * m) / possibleEdges : 0;\n\n\t// Avoid division by zero: if density is 0, fall back to epsilon\n\tif (density === 0) {\n\t\treturn epsilon;\n\t}\n\n\tconst score = jaccardScore / density;\n\n\t// Apply epsilon floor for numerical stability\n\treturn Math.max(epsilon, score);\n}\n\n/**\n * Async variant of SCALE MI for use with async graph data sources.\n *\n * Fetches both neighbourhoods, node count, and edge count concurrently.\n */\nexport async function scaleAsync<N extends NodeData, E extends EdgeData>(\n\tgraph: AsyncReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): Promise<number> {\n\tconst { epsilon = 1e-10 } = config ?? {};\n\n\t// Fetch neighbourhoods, node count, and edge count in parallel\n\tconst [sourceArr, targetArr, n, m] = await Promise.all([\n\t\tcollectAsyncIterable(graph.neighbours(source)),\n\t\tcollectAsyncIterable(graph.neighbours(target)),\n\t\tgraph.nodeCount,\n\t\tgraph.edgeCount,\n\t]);\n\n\tconst srcSet = new Set(sourceArr.filter((node) => node !== target));\n\tconst tgtSet = new Set(targetArr.filter((node) => node !== source));\n\n\t// Compute Jaccard from sets\n\tlet intersection = 0;\n\tfor (const node of srcSet) {\n\t\tif (tgtSet.has(node)) intersection++;\n\t}\n\tconst union = srcSet.size + tgtSet.size - intersection;\n\tconst jaccardScore = union > 0 ? intersection / union : 0;\n\n\t// Compute graph density\n\t// ρ(G) = 2|E| / (|V|(|V|-1)) for undirected; |E| / (|V|(|V|-1)) for directed\n\tconst possibleEdges = n * (n - 1);\n\tconst density =\n\t\tpossibleEdges > 0 ? (graph.directed ? m : 2 * m) / possibleEdges : 0;\n\n\t// Avoid division by zero: if density is 0, fall back to epsilon\n\tif (density === 0) {\n\t\treturn epsilon;\n\t}\n\n\tconst score = jaccardScore / density;\n\n\treturn Math.max(epsilon, score);\n}\n","/**\n * SKEW (Structural Kernel Entropy Weighting) MI variant.\n *\n * IDF-style rarity weighting on endpoints, applied to Jaccard base.\n * Formula: MI(u,v) = Jaccard(u,v) * log(N/deg(u)+1) * log(N/deg(v)+1)\n *\n * Range: [0, ∞) but typically [0, 1] for well-connected graphs\n */\n\nimport type { NodeId, NodeData, EdgeData, ReadableGraph } from \"../../graph\";\nimport type { AsyncReadableGraph } from \"../../graph/async-interfaces\";\nimport { computeJaccard } from \"../../utils\";\nimport { collectAsyncIterable } from \"../../async/utils\";\nimport type { MIConfig } from \"./types\";\n\n/**\n * Compute SKEW MI between two nodes.\n */\nexport function skew<N extends NodeData, E extends EdgeData>(\n\tgraph: ReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): number {\n\tconst { epsilon = 1e-10 } = config ?? {};\n\n\tconst { jaccard: jaccardScore } = computeJaccard(graph, source, target);\n\n\t// Compute IDF-style weights for endpoints\n\tconst N = graph.nodeCount;\n\tconst sourceDegree = graph.degree(source);\n\tconst targetDegree = graph.degree(target);\n\n\tconst sourceIdf = Math.log(N / (sourceDegree + 1));\n\tconst targetIdf = Math.log(N / (targetDegree + 1));\n\n\tconst score = jaccardScore * sourceIdf * targetIdf;\n\n\t// Apply epsilon floor for numerical stability\n\treturn Math.max(epsilon, score);\n}\n\n/**\n * Async variant of SKEW MI for use with async graph data sources.\n *\n * Fetches both neighbourhoods, degrees, and node count concurrently.\n */\nexport async function skewAsync<N extends NodeData, E extends EdgeData>(\n\tgraph: AsyncReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): Promise<number> {\n\tconst { epsilon = 1e-10 } = config ?? {};\n\n\t// Fetch neighbourhoods, node count, and endpoint degrees in parallel\n\tconst [sourceArr, targetArr, N, sourceDegree, targetDegree] =\n\t\tawait Promise.all([\n\t\t\tcollectAsyncIterable(graph.neighbours(source)),\n\t\t\tcollectAsyncIterable(graph.neighbours(target)),\n\t\t\tgraph.nodeCount,\n\t\t\tgraph.degree(source),\n\t\t\tgraph.degree(target),\n\t\t]);\n\n\tconst srcSet = new Set(sourceArr.filter((n) => n !== target));\n\tconst tgtSet = new Set(targetArr.filter((n) => n !== source));\n\n\t// Compute Jaccard from sets\n\tlet intersection = 0;\n\tfor (const n of srcSet) {\n\t\tif (tgtSet.has(n)) intersection++;\n\t}\n\tconst union = srcSet.size + tgtSet.size - intersection;\n\tconst jaccardScore = union > 0 ? intersection / union : 0;\n\n\t// Compute IDF-style weights for endpoints\n\tconst sourceIdf = Math.log(N / (sourceDegree + 1));\n\tconst targetIdf = Math.log(N / (targetDegree + 1));\n\n\tconst score = jaccardScore * sourceIdf * targetIdf;\n\n\treturn Math.max(epsilon, score);\n}\n","/**\n * SPAN (Structural Pattern ANalysis) MI variant.\n *\n * Clustering-coefficient penalty, favouring bridge edges.\n * Formula: MI(u,v) = Jaccard(u,v) * (1 - max(cc(u), cc(v)))\n *\n * Nodes with high clustering coefficient are tightly embedded in triangles;\n * edges between such nodes are less likely to be bridge edges. This variant\n * downweights such edges, favouring paths through bridge edges.\n *\n * Range: [0, 1]\n */\n\nimport type { NodeId, NodeData, EdgeData, ReadableGraph } from \"../../graph\";\nimport type { AsyncReadableGraph } from \"../../graph/async-interfaces\";\nimport { computeJaccard } from \"../../utils\";\nimport { localClusteringCoefficient } from \"../../utils\";\nimport { collectAsyncIterable } from \"../../async/utils\";\nimport type { MIConfig } from \"./types\";\n\n/**\n * Compute SPAN MI between two nodes.\n */\nexport function span<N extends NodeData, E extends EdgeData>(\n\tgraph: ReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): number {\n\tconst { epsilon = 1e-10 } = config ?? {};\n\n\tconst { jaccard: jaccardScore } = computeJaccard(graph, source, target);\n\n\t// Compute clustering coefficients\n\tconst sourceCc = localClusteringCoefficient(graph, source);\n\tconst targetCc = localClusteringCoefficient(graph, target);\n\n\t// Apply bridge penalty: downweight edges between highly-embedded nodes\n\tconst bridgePenalty = 1 - Math.max(sourceCc, targetCc);\n\n\tconst score = jaccardScore * bridgePenalty;\n\n\t// Apply epsilon floor for numerical stability\n\treturn Math.max(epsilon, score);\n}\n\n/**\n * Async variant of SPAN MI for use with async graph data sources.\n *\n * Fetches both neighbourhoods concurrently, then computes the clustering\n * coefficient for each endpoint from the collected neighbour arrays.\n */\nexport async function spanAsync<N extends NodeData, E extends EdgeData>(\n\tgraph: AsyncReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): Promise<number> {\n\tconst { epsilon = 1e-10 } = config ?? {};\n\n\t// Fetch both neighbourhoods in parallel\n\tconst [sourceArr, targetArr] = await Promise.all([\n\t\tcollectAsyncIterable(graph.neighbours(source)),\n\t\tcollectAsyncIterable(graph.neighbours(target)),\n\t]);\n\n\tconst srcSet = new Set(sourceArr.filter((n) => n !== target));\n\tconst tgtSet = new Set(targetArr.filter((n) => n !== source));\n\n\t// Compute Jaccard from sets\n\tlet intersection = 0;\n\tfor (const n of srcSet) {\n\t\tif (tgtSet.has(n)) intersection++;\n\t}\n\tconst union = srcSet.size + tgtSet.size - intersection;\n\tconst jaccardScore = union > 0 ? intersection / union : 0;\n\n\t// Compute clustering coefficients for source and target asynchronously.\n\t// CC(v) = 2 * |triangles| / (deg * (deg - 1))\n\t// We need to check which pairs of neighbours are connected.\n\tconst computeClusteringCoefficient = async (\n\t\tnodeId: NodeId,\n\t\tneighbourArr: readonly NodeId[],\n\t): Promise<number> => {\n\t\tconst degree = neighbourArr.length;\n\t\tif (degree < 2) return 0;\n\n\t\t// Check each pair of neighbours for connectivity in parallel\n\t\tconst pairs: [NodeId, NodeId][] = [];\n\t\tfor (let i = 0; i < neighbourArr.length; i++) {\n\t\t\tfor (let j = i + 1; j < neighbourArr.length; j++) {\n\t\t\t\tconst u = neighbourArr[i];\n\t\t\t\tconst v = neighbourArr[j];\n\t\t\t\tif (u !== undefined && v !== undefined) {\n\t\t\t\t\tpairs.push([u, v]);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst edgeResults = await Promise.all(\n\t\t\tpairs.flatMap(([u, v]) => [graph.getEdge(u, v), graph.getEdge(v, u)]),\n\t\t);\n\n\t\tlet triangleCount = 0;\n\t\tfor (let i = 0; i < pairs.length; i++) {\n\t\t\t// Each pair produced two edge lookups at indices 2*i and 2*i+1\n\t\t\tif (\n\t\t\t\tedgeResults[2 * i] !== undefined ||\n\t\t\t\tedgeResults[2 * i + 1] !== undefined\n\t\t\t) {\n\t\t\t\ttriangleCount++;\n\t\t\t}\n\t\t}\n\n\t\tconst possibleTriangles = (degree * (degree - 1)) / 2;\n\t\treturn triangleCount / possibleTriangles;\n\t};\n\n\tconst [sourceCc, targetCc] = await Promise.all([\n\t\tcomputeClusteringCoefficient(source, sourceArr),\n\t\tcomputeClusteringCoefficient(target, targetArr),\n\t]);\n\n\t// Apply bridge penalty: downweight edges between highly-embedded nodes\n\tconst bridgePenalty = 1 - Math.max(sourceCc, targetCc);\n\n\tconst score = jaccardScore * bridgePenalty;\n\n\treturn Math.max(epsilon, score);\n}\n","/**\n * ETCH (Edge Topology Coherence via Homophily) MI variant.\n *\n * Edge-type rarity weighting applied to Jaccard base.\n * Formula: MI(u,v) = Jaccard(u,v) * rarity(edgeType(u,v))\n * where rarity(t) = log(|E| / count(edges with type t))\n *\n * Edges of rare types (fewer instances in the graph) receive higher salience,\n * making discoveries across unusual edge relationships more significant.\n *\n * Range: [0, ∞) but typically [0, 1] for well-typed edges\n */\n\nimport type { NodeId, NodeData, EdgeData, ReadableGraph } from \"../../graph\";\nimport type { AsyncReadableGraph } from \"../../graph/async-interfaces\";\nimport { computeJaccard, countEdgesOfType } from \"../../utils\";\nimport { collectAsyncIterable } from \"../../async/utils\";\nimport type { MIConfig } from \"./types\";\n\n/**\n * Compute ETCH MI between two nodes.\n */\nexport function etch<N extends NodeData, E extends EdgeData>(\n\tgraph: ReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): number {\n\tconst { epsilon = 1e-10 } = config ?? {};\n\n\tconst { jaccard: jaccardScore } = computeJaccard(graph, source, target);\n\n\t// Get edge between source and target\n\tconst edge = graph.getEdge(source, target);\n\n\t// If edge has no type or doesn't exist, fall back to Jaccard\n\tif (edge?.type === undefined) {\n\t\treturn Math.max(epsilon, jaccardScore);\n\t}\n\n\t// Compute edge rarity: log(total edges / edges of this type)\n\tconst edgeTypeCount = countEdgesOfType(graph, edge.type);\n\n\t// Avoid division by zero\n\tif (edgeTypeCount === 0) {\n\t\treturn Math.max(epsilon, jaccardScore);\n\t}\n\n\tconst rarity = Math.log(graph.edgeCount / edgeTypeCount);\n\tconst score = jaccardScore * rarity;\n\n\t// Apply epsilon floor for numerical stability\n\treturn Math.max(epsilon, score);\n}\n\n/**\n * Async variant of ETCH MI for use with async graph data sources.\n *\n * Fetches both neighbourhoods and edge data concurrently, then counts\n * edges of the same type by iterating the async edge stream.\n */\nexport async function etchAsync<N extends NodeData, E extends EdgeData>(\n\tgraph: AsyncReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): Promise<number> {\n\tconst { epsilon = 1e-10 } = config ?? {};\n\n\t// Fetch neighbourhoods and the source→target edge concurrently\n\tconst [sourceArr, targetArr, edge] = await Promise.all([\n\t\tcollectAsyncIterable(graph.neighbours(source)),\n\t\tcollectAsyncIterable(graph.neighbours(target)),\n\t\tgraph.getEdge(source, target),\n\t]);\n\n\tconst srcSet = new Set(sourceArr.filter((n) => n !== target));\n\tconst tgtSet = new Set(targetArr.filter((n) => n !== source));\n\n\t// Compute Jaccard from sets\n\tlet intersection = 0;\n\tfor (const n of srcSet) {\n\t\tif (tgtSet.has(n)) intersection++;\n\t}\n\tconst union = srcSet.size + tgtSet.size - intersection;\n\tconst jaccardScore = union > 0 ? intersection / union : 0;\n\n\t// If edge has no type or doesn't exist, fall back to Jaccard\n\tif (edge?.type === undefined) {\n\t\treturn Math.max(epsilon, jaccardScore);\n\t}\n\n\tconst edgeType = edge.type;\n\n\t// Count edges of this type by iterating all edges\n\tlet edgeTypeCount = 0;\n\tlet totalEdges = 0;\n\tfor await (const e of graph.edges()) {\n\t\ttotalEdges++;\n\t\tif (e.type === edgeType) edgeTypeCount++;\n\t}\n\n\t// Avoid division by zero\n\tif (edgeTypeCount === 0) {\n\t\treturn Math.max(epsilon, jaccardScore);\n\t}\n\n\tconst rarity = Math.log(totalEdges / edgeTypeCount);\n\tconst score = jaccardScore * rarity;\n\n\treturn Math.max(epsilon, score);\n}\n","/**\n * NOTCH (Neighbourhood Overlap Topology Coherence via Homophily) MI variant.\n *\n * Node-type rarity weighting applied to Jaccard base.\n * Formula: MI(u,v) = Jaccard(u,v) * rarity(nodeType(u)) * rarity(nodeType(v))\n * where rarity(t) = log(|V| / count(nodes with type t))\n *\n * Paths connecting nodes of rare types (fewer instances in the graph) receive higher\n * salience, making discoveries involving unusual node types more significant.\n *\n * Range: [0, ∞) but typically [0, 1] for well-typed nodes\n */\n\nimport type { NodeId, NodeData, EdgeData, ReadableGraph } from \"../../graph\";\nimport type { AsyncReadableGraph } from \"../../graph/async-interfaces\";\nimport { computeJaccard, countNodesOfType } from \"../../utils\";\nimport { collectAsyncIterable } from \"../../async/utils\";\nimport type { MIConfig } from \"./types\";\n\n/**\n * Compute NOTCH MI between two nodes.\n */\nexport function notch<N extends NodeData, E extends EdgeData>(\n\tgraph: ReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): number {\n\tconst { epsilon = 1e-10 } = config ?? {};\n\n\tconst { jaccard: jaccardScore } = computeJaccard(graph, source, target);\n\n\t// Get node data\n\tconst sourceNode = graph.getNode(source);\n\tconst targetNode = graph.getNode(target);\n\n\t// If either node lacks a type, fall back to Jaccard\n\tif (sourceNode?.type === undefined || targetNode?.type === undefined) {\n\t\treturn Math.max(epsilon, jaccardScore);\n\t}\n\n\t// Compute node rarity: log(total nodes / nodes of this type)\n\tconst sourceTypeCount = countNodesOfType(graph, sourceNode.type);\n\tconst targetTypeCount = countNodesOfType(graph, targetNode.type);\n\n\t// Avoid division by zero\n\tif (sourceTypeCount === 0 || targetTypeCount === 0) {\n\t\treturn Math.max(epsilon, jaccardScore);\n\t}\n\n\tconst sourceRarity = Math.log(graph.nodeCount / sourceTypeCount);\n\tconst targetRarity = Math.log(graph.nodeCount / targetTypeCount);\n\n\tconst score = jaccardScore * sourceRarity * targetRarity;\n\n\t// Apply epsilon floor for numerical stability\n\treturn Math.max(epsilon, score);\n}\n\n/**\n * Async variant of NOTCH MI for use with async graph data sources.\n *\n * Fetches both neighbourhoods and node data concurrently, then counts\n * nodes of each type by iterating the async node stream.\n */\nexport async function notchAsync<N extends NodeData, E extends EdgeData>(\n\tgraph: AsyncReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: MIConfig,\n): Promise<number> {\n\tconst { epsilon = 1e-10 } = config ?? {};\n\n\t// Fetch neighbourhoods and node data concurrently\n\tconst [sourceArr, targetArr, sourceNode, targetNode] = await Promise.all([\n\t\tcollectAsyncIterable(graph.neighbours(source)),\n\t\tcollectAsyncIterable(graph.neighbours(target)),\n\t\tgraph.getNode(source),\n\t\tgraph.getNode(target),\n\t]);\n\n\tconst srcSet = new Set(sourceArr.filter((n) => n !== target));\n\tconst tgtSet = new Set(targetArr.filter((n) => n !== source));\n\n\t// Compute Jaccard from sets\n\tlet intersection = 0;\n\tfor (const n of srcSet) {\n\t\tif (tgtSet.has(n)) intersection++;\n\t}\n\tconst union = srcSet.size + tgtSet.size - intersection;\n\tconst jaccardScore = union > 0 ? intersection / union : 0;\n\n\t// If either node lacks a type, fall back to Jaccard\n\tif (sourceNode?.type === undefined || targetNode?.type === undefined) {\n\t\treturn Math.max(epsilon, jaccardScore);\n\t}\n\n\tconst sourceType = sourceNode.type;\n\tconst targetType = targetNode.type;\n\n\t// Count nodes of each type and total nodes by iterating all node IDs\n\tlet totalNodes = 0;\n\tlet sourceTypeCount = 0;\n\tlet targetTypeCount = 0;\n\tfor await (const nodeId of graph.nodeIds()) {\n\t\ttotalNodes++;\n\t\tconst node = await graph.getNode(nodeId);\n\t\tif (node?.type === sourceType) sourceTypeCount++;\n\t\tif (node?.type === targetType) targetTypeCount++;\n\t}\n\n\t// Avoid division by zero\n\tif (sourceTypeCount === 0 || targetTypeCount === 0) {\n\t\treturn Math.max(epsilon, jaccardScore);\n\t}\n\n\tconst sourceRarity = Math.log(totalNodes / sourceTypeCount);\n\tconst targetRarity = Math.log(totalNodes / targetTypeCount);\n\n\tconst score = jaccardScore * sourceRarity * targetRarity;\n\n\treturn Math.max(epsilon, score);\n}\n","/**\n * Unified Adaptive MI - combines multiple MI signals dynamically.\n *\n * Adapts to graph structure by weighting different MI components\n * based on structural properties.\n *\n * Three-component weighted sum:\n * - Structural: Jaccard neighbourhood overlap\n * - Degree: Adamic-Adar inverse-log-degree weighting\n * - Overlap: Overlap coefficient (intersection / min degree)\n *\n * Range: [0, 1] - higher values indicate stronger association\n */\n\nimport type { NodeId, NodeData, EdgeData, ReadableGraph } from \"../../graph\";\nimport type { AsyncReadableGraph } from \"../../graph/async-interfaces\";\nimport { computeJaccard, neighbourOverlap } from \"../../utils\";\nimport { collectAsyncIterable } from \"../../async/utils\";\nimport type { AdaptiveMIConfig } from \"./types\";\nimport { adamicAdar } from \"./adamic-adar\";\nimport { adamicAdarAsync } from \"./adamic-adar\";\n\n/**\n * Compute unified adaptive MI between two connected nodes.\n *\n * Combines structural, degree, and overlap signals with\n * configurable weighting.\n *\n * @param graph - Source graph\n * @param source - Source node ID\n * @param target - Target node ID\n * @param config - Optional configuration with component weights\n * @returns Adaptive MI score in [0, 1]\n */\nexport function adaptive<N extends NodeData, E extends EdgeData>(\n\tgraph: ReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: AdaptiveMIConfig,\n): number {\n\tconst {\n\t\tepsilon = 1e-10,\n\t\tstructuralWeight = 0.4,\n\t\tdegreeWeight = 0.3,\n\t\toverlapWeight = 0.3,\n\t} = config ?? {};\n\n\t// Compute Jaccard and retrieve neighbourhood sets for the overlap coefficient\n\tconst {\n\t\tjaccard: jaccardScore,\n\t\tsourceNeighbours,\n\t\ttargetNeighbours,\n\t} = computeJaccard(graph, source, target);\n\n\t// Component 1: Structural similarity (Jaccard)\n\t// Returns 0 only when both neighbourhood sets are empty (union = 0); otherwise applies epsilon floor\n\tconst structural =\n\t\tsourceNeighbours.size === 0 && targetNeighbours.size === 0\n\t\t\t? 0\n\t\t\t: Math.max(epsilon, jaccardScore);\n\n\t// Component 2: Degree-weighted association (Adamic-Adar, normalised)\n\tconst degreeComponent = adamicAdar(graph, source, target, {\n\t\tepsilon,\n\t\tnormalise: true,\n\t});\n\n\t// Component 3: Overlap coefficient\n\tlet overlap: number;\n\tif (sourceNeighbours.size > 0 && targetNeighbours.size > 0) {\n\t\tconst { intersection } = neighbourOverlap(\n\t\t\tsourceNeighbours,\n\t\t\ttargetNeighbours,\n\t\t);\n\t\tconst minDegree = Math.min(sourceNeighbours.size, targetNeighbours.size);\n\t\toverlap = minDegree > 0 ? intersection / minDegree : epsilon;\n\t} else {\n\t\toverlap = epsilon;\n\t}\n\n\t// Normalise weights\n\tconst totalWeight = structuralWeight + degreeWeight + overlapWeight;\n\n\t// Weighted combination\n\tconst score =\n\t\t(structuralWeight * structural +\n\t\t\tdegreeWeight * degreeComponent +\n\t\t\toverlapWeight * overlap) /\n\t\ttotalWeight;\n\n\treturn Math.max(epsilon, Math.min(1, score));\n}\n\n/**\n * Async variant of Adaptive MI for use with async graph data sources.\n *\n * Fetches both neighbourhoods concurrently, then delegates degree-weighted\n * component to the async Adamic-Adar variant.\n */\nexport async function adaptiveAsync<N extends NodeData, E extends EdgeData>(\n\tgraph: AsyncReadableGraph<N, E>,\n\tsource: NodeId,\n\ttarget: NodeId,\n\tconfig?: AdaptiveMIConfig,\n): Promise<number> {\n\tconst {\n\t\tepsilon = 1e-10,\n\t\tstructuralWeight = 0.4,\n\t\tdegreeWeight = 0.3,\n\t\toverlapWeight = 0.3,\n\t} = config ?? {};\n\n\t// Fetch both neighbourhoods in parallel alongside the Adamic-Adar component\n\tconst [sourceArr, targetArr, degreeComponent] = await Promise.all([\n\t\tcollectAsyncIterable(graph.neighbours(source)),\n\t\tcollectAsyncIterable(graph.neighbours(target)),\n\t\tadamicAdarAsync(graph, source, target, { epsilon, normalise: true }),\n\t]);\n\n\tconst srcSet = new Set(sourceArr.filter((n) => n !== target));\n\tconst tgtSet = new Set(targetArr.filter((n) => n !== source));\n\n\t// Compute Jaccard from sets\n\tlet intersection = 0;\n\tfor (const n of srcSet) {\n\t\tif (tgtSet.has(n)) intersection++;\n\t}\n\tconst union = srcSet.size + tgtSet.size - intersection;\n\tconst jaccardScore = union > 0 ? intersection / union : 0;\n\n\t// Component 1: Structural similarity (Jaccard)\n\tconst structural =\n\t\tsrcSet.size === 0 && tgtSet.size === 0\n\t\t\t? 0\n\t\t\t: Math.max(epsilon, jaccardScore);\n\n\t// Component 3: Overlap coefficient\n\tlet overlap: number;\n\tif (srcSet.size > 0 && tgtSet.size > 0) {\n\t\tconst minDegree = Math.min(srcSet.size, tgtSet.size);\n\t\toverlap = minDegree > 0 ? intersection / minDegree : epsilon;\n\t} else {\n\t\toverlap = epsilon;\n\t}\n\n\t// Normalise weights\n\tconst totalWeight = structuralWeight + degreeWeight + overlapWeight;\n\n\t// Weighted combination\n\tconst score =\n\t\t(structuralWeight * structural +\n\t\t\tdegreeWeight * degreeComponent +\n\t\t\toverlapWeight * overlap) /\n\t\ttotalWeight;\n\n\treturn Math.max(epsilon, Math.min(1, score));\n}\n"],"mappings":"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"}
|