graphwise 1.1.1 → 1.2.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 (133) hide show
  1. package/dist/expansion/frontier-balanced.d.ts +12 -0
  2. package/dist/expansion/frontier-balanced.d.ts.map +1 -0
  3. package/dist/expansion/frontier-balanced.unit.test.d.ts +2 -0
  4. package/dist/expansion/frontier-balanced.unit.test.d.ts.map +1 -0
  5. package/dist/expansion/index.d.ts +12 -13
  6. package/dist/expansion/index.d.ts.map +1 -1
  7. package/dist/expansion/random-priority.d.ts +20 -0
  8. package/dist/expansion/random-priority.d.ts.map +1 -0
  9. package/dist/expansion/random-priority.unit.test.d.ts +2 -0
  10. package/dist/expansion/random-priority.unit.test.d.ts.map +1 -0
  11. package/dist/expansion/standard-bfs.d.ts +12 -0
  12. package/dist/expansion/standard-bfs.d.ts.map +1 -0
  13. package/dist/expansion/standard-bfs.unit.test.d.ts +2 -0
  14. package/dist/expansion/standard-bfs.unit.test.d.ts.map +1 -0
  15. package/dist/extraction/index.d.ts +6 -6
  16. package/dist/extraction/index.d.ts.map +1 -1
  17. package/dist/extraction/motif.d.ts.map +1 -1
  18. package/dist/gpu/context.d.ts.map +1 -1
  19. package/dist/gpu/csr.d.ts.map +1 -1
  20. package/dist/gpu/index.cjs +410 -5
  21. package/dist/gpu/index.cjs.map +1 -0
  22. package/dist/gpu/index.d.ts +4 -5
  23. package/dist/gpu/index.d.ts.map +1 -1
  24. package/dist/gpu/index.js +400 -2
  25. package/dist/gpu/index.js.map +1 -0
  26. package/dist/graph/index.cjs +222 -2
  27. package/dist/graph/index.cjs.map +1 -0
  28. package/dist/graph/index.d.ts +3 -3
  29. package/dist/graph/index.d.ts.map +1 -1
  30. package/dist/graph/index.js +221 -1
  31. package/dist/graph/index.js.map +1 -0
  32. package/dist/index/index.cjs +902 -10
  33. package/dist/index/index.cjs.map +1 -1
  34. package/dist/index/index.js +880 -10
  35. package/dist/index/index.js.map +1 -1
  36. package/dist/{kmeans-B0HEOU6k.cjs → kmeans-87ExSUNZ.js} +27 -13
  37. package/dist/{kmeans-DgbsOznU.js.map → kmeans-87ExSUNZ.js.map} +1 -1
  38. package/dist/{kmeans-DgbsOznU.js → kmeans-BIgSyGKu.cjs} +44 -2
  39. package/dist/{kmeans-B0HEOU6k.cjs.map → kmeans-BIgSyGKu.cjs.map} +1 -1
  40. package/dist/ranking/baselines/betweenness.d.ts +13 -0
  41. package/dist/ranking/baselines/betweenness.d.ts.map +1 -0
  42. package/dist/ranking/baselines/betweenness.unit.test.d.ts +2 -0
  43. package/dist/ranking/baselines/betweenness.unit.test.d.ts.map +1 -0
  44. package/dist/ranking/baselines/communicability.d.ts +13 -0
  45. package/dist/ranking/baselines/communicability.d.ts.map +1 -0
  46. package/dist/ranking/baselines/communicability.unit.test.d.ts +2 -0
  47. package/dist/ranking/baselines/communicability.unit.test.d.ts.map +1 -0
  48. package/dist/ranking/baselines/degree-sum.d.ts +13 -0
  49. package/dist/ranking/baselines/degree-sum.d.ts.map +1 -0
  50. package/dist/ranking/baselines/degree-sum.unit.test.d.ts +2 -0
  51. package/dist/ranking/baselines/degree-sum.unit.test.d.ts.map +1 -0
  52. package/dist/ranking/baselines/index.d.ts +20 -0
  53. package/dist/ranking/baselines/index.d.ts.map +1 -0
  54. package/dist/ranking/baselines/jaccard-arithmetic.d.ts +13 -0
  55. package/dist/ranking/baselines/jaccard-arithmetic.d.ts.map +1 -0
  56. package/dist/ranking/baselines/jaccard-arithmetic.unit.test.d.ts +2 -0
  57. package/dist/ranking/baselines/jaccard-arithmetic.unit.test.d.ts.map +1 -0
  58. package/dist/ranking/baselines/katz.d.ts +13 -0
  59. package/dist/ranking/baselines/katz.d.ts.map +1 -0
  60. package/dist/ranking/baselines/katz.unit.test.d.ts +2 -0
  61. package/dist/ranking/baselines/katz.unit.test.d.ts.map +1 -0
  62. package/dist/ranking/baselines/pagerank.d.ts +13 -0
  63. package/dist/ranking/baselines/pagerank.d.ts.map +1 -0
  64. package/dist/ranking/baselines/pagerank.unit.test.d.ts +2 -0
  65. package/dist/ranking/baselines/pagerank.unit.test.d.ts.map +1 -0
  66. package/dist/ranking/baselines/random-ranking.d.ts +21 -0
  67. package/dist/ranking/baselines/random-ranking.d.ts.map +1 -0
  68. package/dist/ranking/baselines/random-ranking.unit.test.d.ts +2 -0
  69. package/dist/ranking/baselines/random-ranking.unit.test.d.ts.map +1 -0
  70. package/dist/ranking/baselines/resistance-distance.d.ts +13 -0
  71. package/dist/ranking/baselines/resistance-distance.d.ts.map +1 -0
  72. package/dist/ranking/baselines/resistance-distance.unit.test.d.ts +2 -0
  73. package/dist/ranking/baselines/resistance-distance.unit.test.d.ts.map +1 -0
  74. package/dist/ranking/baselines/widest-path.d.ts +13 -0
  75. package/dist/ranking/baselines/widest-path.d.ts.map +1 -0
  76. package/dist/ranking/baselines/widest-path.unit.test.d.ts +2 -0
  77. package/dist/ranking/baselines/widest-path.unit.test.d.ts.map +1 -0
  78. package/dist/ranking/index.d.ts +3 -6
  79. package/dist/ranking/index.d.ts.map +1 -1
  80. package/dist/ranking/mi/index.d.ts +9 -9
  81. package/dist/ranking/mi/index.d.ts.map +1 -1
  82. package/dist/schemas/index.d.ts +2 -2
  83. package/dist/schemas/index.d.ts.map +1 -1
  84. package/dist/seeds/index.cjs +398 -3
  85. package/dist/seeds/index.cjs.map +1 -0
  86. package/dist/seeds/index.d.ts +2 -4
  87. package/dist/seeds/index.d.ts.map +1 -1
  88. package/dist/seeds/index.js +396 -1
  89. package/dist/seeds/index.js.map +1 -0
  90. package/dist/seeds/stratified.d.ts.map +1 -1
  91. package/dist/structures/index.cjs +133 -2
  92. package/dist/structures/index.cjs.map +1 -0
  93. package/dist/structures/index.d.ts +1 -2
  94. package/dist/structures/index.d.ts.map +1 -1
  95. package/dist/structures/index.js +132 -1
  96. package/dist/structures/index.js.map +1 -0
  97. package/dist/traversal/index.cjs +152 -5
  98. package/dist/traversal/index.cjs.map +1 -0
  99. package/dist/traversal/index.d.ts +2 -2
  100. package/dist/traversal/index.d.ts.map +1 -1
  101. package/dist/traversal/index.js +148 -1
  102. package/dist/traversal/index.js.map +1 -0
  103. package/dist/utils/index.cjs +172 -9
  104. package/dist/utils/index.cjs.map +1 -0
  105. package/dist/utils/index.d.ts +3 -3
  106. package/dist/utils/index.d.ts.map +1 -1
  107. package/dist/utils/index.js +165 -3
  108. package/dist/utils/index.js.map +1 -0
  109. package/package.json +1 -1
  110. package/dist/gpu-BJRVYBjx.cjs +0 -338
  111. package/dist/gpu-BJRVYBjx.cjs.map +0 -1
  112. package/dist/gpu-BveuXugy.js +0 -315
  113. package/dist/gpu-BveuXugy.js.map +0 -1
  114. package/dist/graph-DLWiziLB.js +0 -222
  115. package/dist/graph-DLWiziLB.js.map +0 -1
  116. package/dist/graph-az06J1YV.cjs +0 -227
  117. package/dist/graph-az06J1YV.cjs.map +0 -1
  118. package/dist/seeds-B6J9oJfU.cjs +0 -404
  119. package/dist/seeds-B6J9oJfU.cjs.map +0 -1
  120. package/dist/seeds-UNZxqm_U.js +0 -393
  121. package/dist/seeds-UNZxqm_U.js.map +0 -1
  122. package/dist/structures-BPfhfqNP.js +0 -133
  123. package/dist/structures-BPfhfqNP.js.map +0 -1
  124. package/dist/structures-CJ_S_7fs.cjs +0 -138
  125. package/dist/structures-CJ_S_7fs.cjs.map +0 -1
  126. package/dist/traversal-CQCjUwUJ.js +0 -149
  127. package/dist/traversal-CQCjUwUJ.js.map +0 -1
  128. package/dist/traversal-QeHaNUWn.cjs +0 -172
  129. package/dist/traversal-QeHaNUWn.cjs.map +0 -1
  130. package/dist/utils-Q_akvlMn.js +0 -164
  131. package/dist/utils-Q_akvlMn.js.map +0 -1
  132. package/dist/utils-spZa1ZvS.cjs +0 -205
  133. package/dist/utils-spZa1ZvS.cjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"seeds-B6J9oJfU.cjs","names":[],"sources":["../src/seeds/grasp.ts","../src/seeds/stratified.ts"],"sourcesContent":["/**\n * GRASP — Graph-agnostic Representative Seed pAir Sampling.\n *\n * Novel blind structural seed selection algorithm that selects structurally\n * representative seed pairs without requiring domain knowledge or loading\n * the full graph into memory.\n *\n * Three phases:\n * 1. Reservoir sampling — stream edges, maintain reservoir of N nodes\n * 2. Structural feature computation — log-degree, clustering coeff, approx PageRank\n * 3. Mini-batch K-means clustering, then sample pairs within and across clusters\n *\n * @packageDocumentation\n */\n\nimport type { ReadableGraph, NodeId } from \"../graph\";\nimport type { Seed } from \"../schemas/index\";\nimport {\n\tminiBatchKMeans,\n\tzScoreNormalise,\n\ttype LabelledFeature,\n\ttype FeatureVector3D,\n} from \"../utils/kmeans\";\n\n/**\n * Configuration options for GRASP seed selection.\n */\nexport interface GraspOptions {\n\t/** Number of clusters for K-means (default: 100) */\n\treadonly nClusters?: number;\n\t/** Number of pairs to sample per cluster (default: 10) */\n\treadonly pairsPerCluster?: number;\n\t/** Ratio of within-cluster pairs vs cross-cluster pairs (default: 0.5) */\n\treadonly withinClusterRatio?: number;\n\t/** Reservoir sample size (default: 200000) */\n\treadonly sampleSize?: number;\n\t/** Random seed for reproducibility (default: 42) */\n\treadonly rngSeed?: number;\n\t/** Number of PageRank iterations for feature computation (default: 10) */\n\treadonly pagerankIterations?: number;\n}\n\n/**\n * A sampled seed pair with structural metadata.\n */\nexport interface GraspSeedPair {\n\t/** Source seed */\n\treadonly source: Seed;\n\t/** Target seed */\n\treadonly target: Seed;\n\t/** Euclidean distance in feature space */\n\treadonly featureDistance: number;\n\t/** Whether both seeds are from the same cluster */\n\treadonly sameCluster: boolean;\n\t/** Cluster index of source (or -1 if unclustered) */\n\treadonly sourceCluster: number;\n\t/** Cluster index of target (or -1 if unclustered) */\n\treadonly targetCluster: number;\n}\n\n/**\n * Result of GRASP seed selection.\n */\nexport interface GraspResult {\n\t/** Sampled seed pairs */\n\treadonly pairs: readonly GraspSeedPair[];\n\t/** Number of clusters used */\n\treadonly nClusters: number;\n\t/** Total nodes sampled */\n\treadonly sampledNodeCount: number;\n\t/** Features computed for sampled nodes */\n\treadonly features: readonly LabelledFeature[];\n\t/** Cluster assignments (nodeId -> cluster index) */\n\treadonly clusterAssignments: ReadonlyMap<string, number>;\n}\n\n/** Default configuration values */\nconst DEFAULTS = {\n\tnClusters: 100,\n\tpairsPerCluster: 10,\n\twithinClusterRatio: 0.5,\n\tsampleSize: 200000,\n\trngSeed: 42,\n\tpagerankIterations: 10,\n} as const;\n\n/**\n * Simple seeded pseudo-random number generator using mulberry32.\n */\nfunction createRNG(seed: number): () => number {\n\tlet state = seed >>> 0;\n\treturn (): number => {\n\t\tstate = (state + 0x6d2b79f5) >>> 0;\n\t\tlet t = Math.imul(state ^ (state >>> 15), state | 1);\n\t\tt = (t ^ (t >>> 7)) * (t | 0x61c88647);\n\t\treturn ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n\t};\n}\n\n/**\n * Reservoir sampling (Vitter's Algorithm R) for streaming node selection.\n *\n * Maintains a uniform sample of nodes as edges are streamed, without\n * requiring the full graph in memory.\n */\nfunction reservoirSample(\n\tgraph: ReadableGraph,\n\tsampleSize: number,\n\trng: () => number,\n): { nodeIds: Set<NodeId>; neighbourMap: Map<NodeId, Set<NodeId>> } {\n\tconst reservoir: NodeId[] = [];\n\tconst neighbourMap = new Map<NodeId, Set<NodeId>>();\n\tconst inReservoir = new Set<NodeId>();\n\n\t// Track total nodes seen for reservoir probability\n\tlet nodesSeen = 0;\n\n\t// Stream all edges and sample nodes\n\tfor (const edge of graph.edges()) {\n\t\t// Process source node\n\t\tconst source = edge.source;\n\t\tif (!inReservoir.has(source)) {\n\t\t\tnodesSeen++;\n\t\t\tif (reservoir.length < sampleSize) {\n\t\t\t\treservoir.push(source);\n\t\t\t\tinReservoir.add(source);\n\t\t\t\tneighbourMap.set(source, new Set<NodeId>());\n\t\t\t} else {\n\t\t\t\t// Reservoir sampling: replace with probability sampleSize/nodesSeen\n\t\t\t\tconst j = Math.floor(rng() * nodesSeen);\n\t\t\t\tif (j < sampleSize) {\n\t\t\t\t\tconst oldNode = reservoir[j];\n\t\t\t\t\tif (oldNode !== undefined) {\n\t\t\t\t\t\tinReservoir.delete(oldNode);\n\t\t\t\t\t\tneighbourMap.delete(oldNode);\n\t\t\t\t\t}\n\t\t\t\t\treservoir[j] = source;\n\t\t\t\t\tinReservoir.add(source);\n\t\t\t\t\tneighbourMap.set(source, new Set<NodeId>());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Process target node\n\t\tconst target = edge.target;\n\t\tif (!inReservoir.has(target)) {\n\t\t\tnodesSeen++;\n\t\t\tif (reservoir.length < sampleSize) {\n\t\t\t\treservoir.push(target);\n\t\t\t\tinReservoir.add(target);\n\t\t\t\tneighbourMap.set(target, new Set<NodeId>());\n\t\t\t} else {\n\t\t\t\tconst j = Math.floor(rng() * nodesSeen);\n\t\t\t\tif (j < sampleSize) {\n\t\t\t\t\tconst oldNode = reservoir[j];\n\t\t\t\t\tif (oldNode !== undefined) {\n\t\t\t\t\t\tinReservoir.delete(oldNode);\n\t\t\t\t\t\tneighbourMap.delete(oldNode);\n\t\t\t\t\t}\n\t\t\t\t\treservoir[j] = target;\n\t\t\t\t\tinReservoir.add(target);\n\t\t\t\t\tneighbourMap.set(target, new Set<NodeId>());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Store neighbour relationships for sampled nodes\n\t\tif (inReservoir.has(source) && inReservoir.has(target)) {\n\t\t\tconst sourceNeighbours = neighbourMap.get(source);\n\t\t\tconst targetNeighbours = neighbourMap.get(target);\n\t\t\tsourceNeighbours?.add(target);\n\t\t\ttargetNeighbours?.add(source);\n\t\t}\n\t}\n\n\treturn { nodeIds: inReservoir, neighbourMap };\n}\n\n/**\n * Compute approximate PageRank scores using power iteration on the reservoir subgraph.\n *\n * This is an approximation since it only considers the sampled nodes and their\n * connections within the reservoir, not the full graph.\n */\nfunction approximatePageRank(\n\tnodeIds: Set<NodeId>,\n\tneighbourMap: Map<NodeId, Set<NodeId>>,\n\titerations: number,\n\tdampingFactor = 0.85,\n): Map<NodeId, number> {\n\tconst n = nodeIds.size;\n\tif (n === 0) return new Map();\n\n\tconst nodeIdList = [...nodeIds];\n\tconst nodeIndex = new Map(nodeIdList.map((id, i) => [id, i] as const));\n\n\t// Initialise PageRank scores uniformly\n\tconst scores = new Float64Array(n).fill(1 / n);\n\tconst newScores = new Float64Array(n);\n\n\t// Power iteration\n\tfor (let iter = 0; iter < iterations; iter++) {\n\t\tnewScores.fill((1 - dampingFactor) / n);\n\n\t\tfor (let i = 0; i < n; i++) {\n\t\t\tconst nodeId = nodeIdList[i];\n\t\t\tif (nodeId === undefined) continue;\n\n\t\t\tconst neighbours = neighbourMap.get(nodeId);\n\t\t\tif (neighbours === undefined) continue;\n\n\t\t\tconst outDegree = neighbours.size;\n\t\t\tif (outDegree === 0) continue;\n\n\t\t\tconst contribution = (dampingFactor * (scores[i] ?? 0)) / outDegree;\n\n\t\t\tfor (const neighbour of neighbours) {\n\t\t\t\tconst neighbourIdx = nodeIndex.get(neighbour);\n\t\t\t\tif (neighbourIdx !== undefined) {\n\t\t\t\t\tnewScores[neighbourIdx] =\n\t\t\t\t\t\t(newScores[neighbourIdx] ?? 0) + contribution;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Swap buffers\n\t\tfor (let i = 0; i < n; i++) {\n\t\t\tscores[i] = newScores[i] ?? 0;\n\t\t}\n\t}\n\n\t// Build result map\n\tconst result = new Map<NodeId, number>();\n\tfor (let i = 0; i < n; i++) {\n\t\tconst nodeId = nodeIdList[i];\n\t\tconst score = scores[i];\n\t\tif (nodeId !== undefined && score !== undefined) {\n\t\t\tresult.set(nodeId, score);\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Compute structural features for sampled nodes.\n *\n * Features:\n * - f1: log(deg(v) + 1) — scale-normalised connectivity\n * - f2: clustering_coefficient(v) — local density\n * - f3: approx_pagerank(v) — positional importance\n */\nfunction computeFeatures(\n\tgraph: ReadableGraph,\n\tnodeIds: Set<NodeId>,\n\tneighbourMap: Map<NodeId, Set<NodeId>>,\n\tpagerankScores: Map<NodeId, number>,\n): LabelledFeature[] {\n\tconst features: LabelledFeature[] = [];\n\n\tfor (const nodeId of nodeIds) {\n\t\tconst degree = graph.degree(nodeId, \"both\");\n\t\tconst neighbours = neighbourMap.get(nodeId);\n\n\t\t// Compute local clustering coefficient using neighbour map\n\t\tlet clusteringCoef = 0;\n\t\tif (neighbours !== undefined && neighbours.size >= 2) {\n\t\t\tlet triangleCount = 0;\n\t\t\tconst neighbourList = [...neighbours];\n\n\t\t\tfor (let i = 0; i < neighbourList.length; i++) {\n\t\t\t\tfor (let j = i + 1; j < neighbourList.length; j++) {\n\t\t\t\t\tconst u = neighbourList[i];\n\t\t\t\t\tconst w = neighbourList[j];\n\t\t\t\t\tif (u !== undefined && w !== undefined) {\n\t\t\t\t\t\t// Check if u and w are connected\n\t\t\t\t\t\tconst uNeighbours = neighbourMap.get(u);\n\t\t\t\t\t\tif (uNeighbours?.has(w) === true) {\n\t\t\t\t\t\t\ttriangleCount++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst possibleTriangles = (degree * (degree - 1)) / 2;\n\t\t\tclusteringCoef = triangleCount / possibleTriangles;\n\t\t}\n\n\t\tconst pagerank = pagerankScores.get(nodeId) ?? 0;\n\n\t\tfeatures.push({\n\t\t\tnodeId,\n\t\t\tf1: Math.log(degree + 1),\n\t\t\tf2: clusteringCoef,\n\t\t\tf3: pagerank,\n\t\t});\n\t}\n\n\treturn features;\n}\n\n/**\n * Sample seed pairs from clusters.\n *\n * For each cluster, samples a mix of within-cluster and cross-cluster pairs.\n */\nfunction samplePairs(\n\tfeatures: readonly LabelledFeature[],\n\tclusterAssignments: ReadonlyMap<string, number>,\n\tnClusters: number,\n\tpairsPerCluster: number,\n\twithinClusterRatio: number,\n\trng: () => number,\n): GraspSeedPair[] {\n\tconst pairs: GraspSeedPair[] = [];\n\n\t// Group nodes by cluster\n\tconst clusterNodes = new Map<number, LabelledFeature[]>();\n\tfor (const feature of features) {\n\t\tconst cluster = clusterAssignments.get(feature.nodeId);\n\t\tif (cluster === undefined) continue;\n\n\t\tlet nodes = clusterNodes.get(cluster);\n\t\tif (nodes === undefined) {\n\t\t\tnodes = [];\n\t\t\tclusterNodes.set(cluster, nodes);\n\t\t}\n\t\tnodes.push(feature);\n\t}\n\n\tconst withinCount = Math.floor(pairsPerCluster * withinClusterRatio);\n\tconst crossCount = pairsPerCluster - withinCount;\n\n\t// Sample pairs for each cluster\n\tfor (let clusterIdx = 0; clusterIdx < nClusters; clusterIdx++) {\n\t\tconst nodes = clusterNodes.get(clusterIdx);\n\t\tif (nodes === undefined || nodes.length < 2) continue;\n\n\t\t// Within-cluster pairs\n\t\tfor (let i = 0; i < withinCount; i++) {\n\t\t\t// Sample two distinct nodes\n\t\t\tconst idx1 = Math.floor(rng() * nodes.length);\n\t\t\tlet idx2 = Math.floor(rng() * nodes.length);\n\t\t\twhile (idx1 === idx2) {\n\t\t\t\tidx2 = Math.floor(rng() * nodes.length);\n\t\t\t}\n\n\t\t\tconst source = nodes[idx1];\n\t\t\tconst target = nodes[idx2];\n\t\t\tif (source === undefined || target === undefined) continue;\n\n\t\t\tconst distance = computeFeatureDistance(source, target);\n\n\t\t\tpairs.push({\n\t\t\t\tsource: { id: source.nodeId },\n\t\t\t\ttarget: { id: target.nodeId },\n\t\t\t\tfeatureDistance: distance,\n\t\t\t\tsameCluster: true,\n\t\t\t\tsourceCluster: clusterIdx,\n\t\t\t\ttargetCluster: clusterIdx,\n\t\t\t});\n\t\t}\n\n\t\t// Cross-cluster pairs\n\t\tfor (let i = 0; i < crossCount; i++) {\n\t\t\tconst source = nodes[Math.floor(rng() * nodes.length)];\n\t\t\tif (source === undefined) continue;\n\n\t\t\t// Pick a random other cluster\n\t\t\tconst otherClusterIdx = Math.floor(rng() * nClusters);\n\t\t\tif (otherClusterIdx === clusterIdx) continue;\n\n\t\t\tconst otherNodes = clusterNodes.get(otherClusterIdx);\n\t\t\tif (otherNodes === undefined || otherNodes.length === 0) continue;\n\n\t\t\tconst target = otherNodes[Math.floor(rng() * otherNodes.length)];\n\t\t\tif (target === undefined) continue;\n\n\t\t\tconst distance = computeFeatureDistance(source, target);\n\n\t\t\tpairs.push({\n\t\t\t\tsource: { id: source.nodeId },\n\t\t\t\ttarget: { id: target.nodeId },\n\t\t\t\tfeatureDistance: distance,\n\t\t\t\tsameCluster: false,\n\t\t\t\tsourceCluster: clusterIdx,\n\t\t\t\ttargetCluster: otherClusterIdx,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn pairs;\n}\n\n/**\n * Compute Euclidean distance between two feature vectors.\n */\nfunction computeFeatureDistance(\n\ta: FeatureVector3D,\n\tb: FeatureVector3D,\n): number {\n\tconst d1 = a.f1 - b.f1;\n\tconst d2 = a.f2 - b.f2;\n\tconst d3 = a.f3 - b.f3;\n\treturn Math.sqrt(d1 * d1 + d2 * d2 + d3 * d3);\n}\n\n/**\n * GRASP — Graph-agnostic Representative Seed pAir Sampling.\n *\n * Selects structurally representative seed pairs without domain knowledge.\n * The algorithm streams edges, samples nodes via reservoir sampling, computes\n * structural features, clusters nodes, and samples pairs within/across clusters.\n *\n * @param graph - The graph to sample seeds from\n * @param options - Configuration options\n * @returns Sampled seed pairs with structural metadata\n *\n * @example\n * ```typescript\n * const graph = new AdjacencyMapGraph();\n * // ... populate graph ...\n *\n * const result = grasp(graph, {\n * nClusters: 50,\n * pairsPerCluster: 20,\n * sampleSize: 100000,\n * });\n *\n * console.log(`Sampled ${result.pairs.length} pairs from ${result.sampledNodeCount} nodes`);\n * ```\n */\nexport function grasp(\n\tgraph: ReadableGraph,\n\toptions: GraspOptions = {},\n): GraspResult {\n\tconst config = {\n\t\t...DEFAULTS,\n\t\t...options,\n\t};\n\n\tconst rng = createRNG(config.rngSeed);\n\n\t// Phase 1: Reservoir sampling\n\tconst { nodeIds, neighbourMap } = reservoirSample(\n\t\tgraph,\n\t\tconfig.sampleSize,\n\t\trng,\n\t);\n\n\t// Phase 2: Approximate PageRank on reservoir subgraph\n\tconst pagerankScores = approximatePageRank(\n\t\tnodeIds,\n\t\tneighbourMap,\n\t\tconfig.pagerankIterations,\n\t);\n\n\t// Phase 2: Compute structural features\n\tlet features = computeFeatures(graph, nodeIds, neighbourMap, pagerankScores);\n\n\t// Normalise features\n\tif (features.length > 0) {\n\t\tfeatures = zScoreNormalise(features);\n\t}\n\n\t// Phase 3: K-means clustering\n\tconst k = Math.min(config.nClusters, features.length);\n\tconst kmeansResult = miniBatchKMeans(features, {\n\t\tk,\n\t\tseed: config.rngSeed,\n\t\tmaxIterations: 100,\n\t});\n\n\t// Phase 3: Sample pairs\n\tconst pairs = samplePairs(\n\t\tfeatures,\n\t\tkmeansResult.assignments,\n\t\tkmeansResult.k,\n\t\tconfig.pairsPerCluster,\n\t\tconfig.withinClusterRatio,\n\t\trng,\n\t);\n\n\treturn {\n\t\tpairs,\n\t\tnClusters: kmeansResult.k,\n\t\tsampledNodeCount: nodeIds.size,\n\t\tfeatures,\n\t\tclusterAssignments: kmeansResult.assignments,\n\t};\n}\n","/**\n * Stratified seed selection — legacy human-defined strata.\n *\n * Requires user-provided field/type classifications. This is included for comparison\n * and for users who have domain metadata.\n *\n * @packageDocumentation\n */\n\nimport type { ReadableGraph, NodeId } from \"../graph/index\";\nimport type { Seed } from \"../schemas/index\";\n\n/**\n * Field classification function type.\n * User provides a function that returns the field name for a node.\n */\nexport type FieldClassifier = (node: {\n\tid: NodeId;\n\ttype?: string;\n}) => string | undefined;\n\n/**\n * Stratum definition for seed pair selection.\n */\nexport interface StratumDefinition {\n\treadonly name: string;\n\treadonly description: string;\n\treadonly predicate: (\n\t\tsource: { id: NodeId; type?: string },\n\t\ttarget: { id: NodeId; type?: string },\n\t) => boolean;\n}\n\n/**\n * Stratum with sampled seed pairs.\n */\nexport interface StratumResult {\n\treadonly name: string;\n\treadonly pairs: readonly SeedPair[];\n}\n\n/**\n * A seed pair with stratum metadata.\n */\nexport interface SeedPair {\n\treadonly source: Seed;\n\treadonly target: Seed;\n\treadonly stratum: string;\n\treadonly sameField: boolean;\n}\n\n/**\n * Result of stratified seed selection.\n */\nexport interface StratifiedResult {\n\treadonly strata: readonly StratumResult[];\n\treadonly totalPairs: number;\n\treadonly errors: readonly Error[];\n}\n\n/**\n * Configuration for stratified seed selection.\n */\nexport interface StratifiedOptions {\n\t/** Function to classify nodes by field */\n\treadonly fieldClassifier: FieldClassifier;\n\t/** Number of pairs to sample per stratum */\n\treadonly pairsPerStratum?: number;\n\t/** Random seed for reproducibility */\n\treadonly rngSeed?: number;\n\t/** Custom stratum definitions */\n\treadonly customStrata?: readonly StratumDefinition[];\n}\n\n/** Default values */\nconst DEFAULTS = {\n\tpairsPerStratum: 10,\n\trngSeed: 42,\n} as const;\n\n/**\n * Simple seeded pseudo-random number generator using mulberry32.\n */\nfunction createRNG(seed: number): () => number {\n\tlet state = seed >>> 0;\n\treturn (): number => {\n\t\tstate = (state + 0x6d2b79f5) >>> 0;\n\t\tlet t = Math.imul(state ^ (state >>> 15), state | 1);\n\t\tt = (t ^ (t >>> 7)) * (t | 0x61c88647);\n\t\treturn ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n\t};\n}\n\n/**\n * Stratified seed selection algorithm.\n *\n * @param graph - The graph to sample seeds from\n * @param options - Configuration options including field classifier\n * @returns Stratified selection result\n *\n * @example\n * ```typescript\n * const graph = new AdjacencyMapGraph();\n * // ... populate graph ...\n *\n * const result = stratified(graph, {\n * fieldClassifier: (node) => node.type === 'paper' ? 'computer-science' : undefined,\n * pairsPerStratum: 20,\n * });\n *\n * for (const stratum of result.strata) {\n * console.log(`${stratum.name}: ${stratum.pairs.length} pairs`);\n * }\n * ```\n */\nexport function stratified(\n\tgraph: ReadableGraph,\n\toptions: StratifiedOptions,\n): StratifiedResult {\n\tconst {\n\t\tfieldClassifier,\n\t\tpairsPerStratum = DEFAULTS.pairsPerStratum,\n\t\trngSeed = DEFAULTS.rngSeed,\n\t\tcustomStrata,\n\t} = options;\n\n\tconst rng = createRNG(rngSeed);\n\tconst strataDefinitions = customStrata ?? [];\n\n\t// Collect all nodes with their field classifications\n\tconst nodesWithFields: { id: NodeId; type?: string; field?: string }[] = [];\n\n\tfor (const nodeId of graph.nodeIds()) {\n\t\tconst node = graph.getNode(nodeId);\n\t\tif (node === undefined) continue;\n\n\t\tconst field = fieldClassifier({ id: nodeId, type: node.type });\n\t\tif (field === undefined) continue;\n\n\t\tnodesWithFields.push({ id: nodeId, type: node.type, field });\n\t}\n\n\tconst errors: Error[] = [];\n\tconst strataResults: StratumResult[] = [];\n\n\t// Process each stratum\n\tfor (const stratum of strataDefinitions) {\n\t\tconst pairs: SeedPair[] = [];\n\t\tconst eligiblePairs: {\n\t\t\tsource: { id: NodeId; type?: string };\n\t\t\ttarget: { id: NodeId; type?: string };\n\t\t}[] = [];\n\n\t\t// Find all node pairs that match this stratum\n\t\tfor (let i = 0; i < nodesWithFields.length; i++) {\n\t\t\tconst source = nodesWithFields[i];\n\t\t\tif (source === undefined) continue;\n\n\t\t\tfor (let j = i + 1; j < nodesWithFields.length; j++) {\n\t\t\t\tif (j === i) continue;\n\t\t\t\tconst target = nodesWithFields[j];\n\t\t\t\tif (target === undefined) continue;\n\n\t\t\t\tif (stratum.predicate(source, target)) {\n\t\t\t\t\teligiblePairs.push({ source, target });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Sample pairs from eligible pairs\n\t\tconst numToSample = Math.min(pairsPerStratum, eligiblePairs.length);\n\t\tfor (let i = 0; i < numToSample; i++) {\n\t\t\tconst idx = Math.floor(rng() * eligiblePairs.length);\n\t\t\tconst pair = eligiblePairs[idx];\n\t\t\tif (pair === undefined) continue;\n\n\t\t\tconst sourceField = fieldClassifier(pair.source);\n\t\t\tconst targetField = fieldClassifier(pair.target);\n\n\t\t\tpairs.push({\n\t\t\t\tsource: { id: pair.source.id },\n\t\t\t\ttarget: { id: pair.target.id },\n\t\t\t\tstratum: stratum.name,\n\t\t\t\tsameField: sourceField === targetField,\n\t\t\t});\n\t\t}\n\n\t\tstrataResults.push({\n\t\t\tname: stratum.name,\n\t\t\tpairs,\n\t\t});\n\t}\n\n\t// Collect errors for empty strata\n\tfor (const stratum of strataDefinitions) {\n\t\tconst result = strataResults.find((r) => r.name === stratum.name);\n\t\tif (result === undefined || result.pairs.length === 0) {\n\t\t\terrors.push(new Error(`No pairs found for stratum: ${stratum.name}`));\n\t\t}\n\t}\n\n\tconst totalPairs = strataResults.reduce((sum, r) => sum + r.pairs.length, 0);\n\n\treturn {\n\t\tstrata: strataResults,\n\t\ttotalPairs,\n\t\terrors,\n\t};\n}\n"],"mappings":"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"}
@@ -1,393 +0,0 @@
1
- import { n as normaliseFeatures, t as miniBatchKMeans } from "./kmeans-DgbsOznU.js";
2
- //#region src/seeds/grasp.ts
3
- /** Default configuration values */
4
- var DEFAULTS$1 = {
5
- nClusters: 100,
6
- pairsPerCluster: 10,
7
- withinClusterRatio: .5,
8
- sampleSize: 2e5,
9
- rngSeed: 42,
10
- pagerankIterations: 10
11
- };
12
- /**
13
- * Simple seeded pseudo-random number generator using mulberry32.
14
- */
15
- function createRNG$1(seed) {
16
- let state = seed >>> 0;
17
- return () => {
18
- state = state + 1831565813 >>> 0;
19
- let t = Math.imul(state ^ state >>> 15, state | 1);
20
- t = (t ^ t >>> 7) * (t | 1640531527);
21
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
22
- };
23
- }
24
- /**
25
- * Reservoir sampling (Vitter's Algorithm R) for streaming node selection.
26
- *
27
- * Maintains a uniform sample of nodes as edges are streamed, without
28
- * requiring the full graph in memory.
29
- */
30
- function reservoirSample(graph, sampleSize, rng) {
31
- const reservoir = [];
32
- const neighbourMap = /* @__PURE__ */ new Map();
33
- const inReservoir = /* @__PURE__ */ new Set();
34
- let nodesSeen = 0;
35
- for (const edge of graph.edges()) {
36
- const source = edge.source;
37
- if (!inReservoir.has(source)) {
38
- nodesSeen++;
39
- if (reservoir.length < sampleSize) {
40
- reservoir.push(source);
41
- inReservoir.add(source);
42
- neighbourMap.set(source, /* @__PURE__ */ new Set());
43
- } else {
44
- const j = Math.floor(rng() * nodesSeen);
45
- if (j < sampleSize) {
46
- const oldNode = reservoir[j];
47
- if (oldNode !== void 0) {
48
- inReservoir.delete(oldNode);
49
- neighbourMap.delete(oldNode);
50
- }
51
- reservoir[j] = source;
52
- inReservoir.add(source);
53
- neighbourMap.set(source, /* @__PURE__ */ new Set());
54
- }
55
- }
56
- }
57
- const target = edge.target;
58
- if (!inReservoir.has(target)) {
59
- nodesSeen++;
60
- if (reservoir.length < sampleSize) {
61
- reservoir.push(target);
62
- inReservoir.add(target);
63
- neighbourMap.set(target, /* @__PURE__ */ new Set());
64
- } else {
65
- const j = Math.floor(rng() * nodesSeen);
66
- if (j < sampleSize) {
67
- const oldNode = reservoir[j];
68
- if (oldNode !== void 0) {
69
- inReservoir.delete(oldNode);
70
- neighbourMap.delete(oldNode);
71
- }
72
- reservoir[j] = target;
73
- inReservoir.add(target);
74
- neighbourMap.set(target, /* @__PURE__ */ new Set());
75
- }
76
- }
77
- }
78
- if (inReservoir.has(source) && inReservoir.has(target)) {
79
- const sourceNeighbours = neighbourMap.get(source);
80
- const targetNeighbours = neighbourMap.get(target);
81
- sourceNeighbours?.add(target);
82
- targetNeighbours?.add(source);
83
- }
84
- }
85
- return {
86
- nodeIds: inReservoir,
87
- neighbourMap
88
- };
89
- }
90
- /**
91
- * Compute approximate PageRank scores using power iteration on the reservoir subgraph.
92
- *
93
- * This is an approximation since it only considers the sampled nodes and their
94
- * connections within the reservoir, not the full graph.
95
- */
96
- function approximatePageRank(nodeIds, neighbourMap, iterations, dampingFactor = .85) {
97
- const n = nodeIds.size;
98
- if (n === 0) return /* @__PURE__ */ new Map();
99
- const nodeIdList = [...nodeIds];
100
- const nodeIndex = new Map(nodeIdList.map((id, i) => [id, i]));
101
- const scores = new Float64Array(n).fill(1 / n);
102
- const newScores = new Float64Array(n);
103
- for (let iter = 0; iter < iterations; iter++) {
104
- newScores.fill((1 - dampingFactor) / n);
105
- for (let i = 0; i < n; i++) {
106
- const nodeId = nodeIdList[i];
107
- if (nodeId === void 0) continue;
108
- const neighbours = neighbourMap.get(nodeId);
109
- if (neighbours === void 0) continue;
110
- const outDegree = neighbours.size;
111
- if (outDegree === 0) continue;
112
- const contribution = dampingFactor * (scores[i] ?? 0) / outDegree;
113
- for (const neighbour of neighbours) {
114
- const neighbourIdx = nodeIndex.get(neighbour);
115
- if (neighbourIdx !== void 0) newScores[neighbourIdx] = (newScores[neighbourIdx] ?? 0) + contribution;
116
- }
117
- }
118
- for (let i = 0; i < n; i++) scores[i] = newScores[i] ?? 0;
119
- }
120
- const result = /* @__PURE__ */ new Map();
121
- for (let i = 0; i < n; i++) {
122
- const nodeId = nodeIdList[i];
123
- const score = scores[i];
124
- if (nodeId !== void 0 && score !== void 0) result.set(nodeId, score);
125
- }
126
- return result;
127
- }
128
- /**
129
- * Compute structural features for sampled nodes.
130
- *
131
- * Features:
132
- * - f1: log(deg(v) + 1) — scale-normalised connectivity
133
- * - f2: clustering_coefficient(v) — local density
134
- * - f3: approx_pagerank(v) — positional importance
135
- */
136
- function computeFeatures(graph, nodeIds, neighbourMap, pagerankScores) {
137
- const features = [];
138
- for (const nodeId of nodeIds) {
139
- const degree = graph.degree(nodeId, "both");
140
- const neighbours = neighbourMap.get(nodeId);
141
- let clusteringCoef = 0;
142
- if (neighbours !== void 0 && neighbours.size >= 2) {
143
- let triangleCount = 0;
144
- const neighbourList = [...neighbours];
145
- for (let i = 0; i < neighbourList.length; i++) for (let j = i + 1; j < neighbourList.length; j++) {
146
- const u = neighbourList[i];
147
- const w = neighbourList[j];
148
- if (u !== void 0 && w !== void 0) {
149
- if (neighbourMap.get(u)?.has(w) === true) triangleCount++;
150
- }
151
- }
152
- const possibleTriangles = degree * (degree - 1) / 2;
153
- clusteringCoef = triangleCount / possibleTriangles;
154
- }
155
- const pagerank = pagerankScores.get(nodeId) ?? 0;
156
- features.push({
157
- nodeId,
158
- f1: Math.log(degree + 1),
159
- f2: clusteringCoef,
160
- f3: pagerank
161
- });
162
- }
163
- return features;
164
- }
165
- /**
166
- * Sample seed pairs from clusters.
167
- *
168
- * For each cluster, samples a mix of within-cluster and cross-cluster pairs.
169
- */
170
- function samplePairs(features, clusterAssignments, nClusters, pairsPerCluster, withinClusterRatio, rng) {
171
- const pairs = [];
172
- const clusterNodes = /* @__PURE__ */ new Map();
173
- for (const feature of features) {
174
- const cluster = clusterAssignments.get(feature.nodeId);
175
- if (cluster === void 0) continue;
176
- let nodes = clusterNodes.get(cluster);
177
- if (nodes === void 0) {
178
- nodes = [];
179
- clusterNodes.set(cluster, nodes);
180
- }
181
- nodes.push(feature);
182
- }
183
- const withinCount = Math.floor(pairsPerCluster * withinClusterRatio);
184
- const crossCount = pairsPerCluster - withinCount;
185
- for (let clusterIdx = 0; clusterIdx < nClusters; clusterIdx++) {
186
- const nodes = clusterNodes.get(clusterIdx);
187
- if (nodes === void 0 || nodes.length < 2) continue;
188
- for (let i = 0; i < withinCount; i++) {
189
- const idx1 = Math.floor(rng() * nodes.length);
190
- let idx2 = Math.floor(rng() * nodes.length);
191
- while (idx1 === idx2) idx2 = Math.floor(rng() * nodes.length);
192
- const source = nodes[idx1];
193
- const target = nodes[idx2];
194
- if (source === void 0 || target === void 0) continue;
195
- const distance = computeFeatureDistance(source, target);
196
- pairs.push({
197
- source: { id: source.nodeId },
198
- target: { id: target.nodeId },
199
- featureDistance: distance,
200
- sameCluster: true,
201
- sourceCluster: clusterIdx,
202
- targetCluster: clusterIdx
203
- });
204
- }
205
- for (let i = 0; i < crossCount; i++) {
206
- const source = nodes[Math.floor(rng() * nodes.length)];
207
- if (source === void 0) continue;
208
- const otherClusterIdx = Math.floor(rng() * nClusters);
209
- if (otherClusterIdx === clusterIdx) continue;
210
- const otherNodes = clusterNodes.get(otherClusterIdx);
211
- if (otherNodes === void 0 || otherNodes.length === 0) continue;
212
- const target = otherNodes[Math.floor(rng() * otherNodes.length)];
213
- if (target === void 0) continue;
214
- const distance = computeFeatureDistance(source, target);
215
- pairs.push({
216
- source: { id: source.nodeId },
217
- target: { id: target.nodeId },
218
- featureDistance: distance,
219
- sameCluster: false,
220
- sourceCluster: clusterIdx,
221
- targetCluster: otherClusterIdx
222
- });
223
- }
224
- }
225
- return pairs;
226
- }
227
- /**
228
- * Compute Euclidean distance between two feature vectors.
229
- */
230
- function computeFeatureDistance(a, b) {
231
- const d1 = a.f1 - b.f1;
232
- const d2 = a.f2 - b.f2;
233
- const d3 = a.f3 - b.f3;
234
- return Math.sqrt(d1 * d1 + d2 * d2 + d3 * d3);
235
- }
236
- /**
237
- * GRASP — Graph-agnostic Representative Seed pAir Sampling.
238
- *
239
- * Selects structurally representative seed pairs without domain knowledge.
240
- * The algorithm streams edges, samples nodes via reservoir sampling, computes
241
- * structural features, clusters nodes, and samples pairs within/across clusters.
242
- *
243
- * @param graph - The graph to sample seeds from
244
- * @param options - Configuration options
245
- * @returns Sampled seed pairs with structural metadata
246
- *
247
- * @example
248
- * ```typescript
249
- * const graph = new AdjacencyMapGraph();
250
- * // ... populate graph ...
251
- *
252
- * const result = grasp(graph, {
253
- * nClusters: 50,
254
- * pairsPerCluster: 20,
255
- * sampleSize: 100000,
256
- * });
257
- *
258
- * console.log(`Sampled ${result.pairs.length} pairs from ${result.sampledNodeCount} nodes`);
259
- * ```
260
- */
261
- function grasp(graph, options = {}) {
262
- const config = {
263
- ...DEFAULTS$1,
264
- ...options
265
- };
266
- const rng = createRNG$1(config.rngSeed);
267
- const { nodeIds, neighbourMap } = reservoirSample(graph, config.sampleSize, rng);
268
- let features = computeFeatures(graph, nodeIds, neighbourMap, approximatePageRank(nodeIds, neighbourMap, config.pagerankIterations));
269
- if (features.length > 0) features = normaliseFeatures(features);
270
- const k = Math.min(config.nClusters, features.length);
271
- const kmeansResult = miniBatchKMeans(features, {
272
- k,
273
- seed: config.rngSeed,
274
- maxIterations: 100
275
- });
276
- return {
277
- pairs: samplePairs(features, kmeansResult.assignments, kmeansResult.k, config.pairsPerCluster, config.withinClusterRatio, rng),
278
- nClusters: kmeansResult.k,
279
- sampledNodeCount: nodeIds.size,
280
- features,
281
- clusterAssignments: kmeansResult.assignments
282
- };
283
- }
284
- //#endregion
285
- //#region src/seeds/stratified.ts
286
- /** Default values */
287
- var DEFAULTS = {
288
- pairsPerStratum: 10,
289
- rngSeed: 42
290
- };
291
- /**
292
- * Simple seeded pseudo-random number generator using mulberry32.
293
- */
294
- function createRNG(seed) {
295
- let state = seed >>> 0;
296
- return () => {
297
- state = state + 1831565813 >>> 0;
298
- let t = Math.imul(state ^ state >>> 15, state | 1);
299
- t = (t ^ t >>> 7) * (t | 1640531527);
300
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
301
- };
302
- }
303
- /**
304
- * Stratified seed selection algorithm.
305
- *
306
- * @param graph - The graph to sample seeds from
307
- * @param options - Configuration options including field classifier
308
- * @returns Stratified selection result
309
- *
310
- * @example
311
- * ```typescript
312
- * const graph = new AdjacencyMapGraph();
313
- * // ... populate graph ...
314
- *
315
- * const result = stratified(graph, {
316
- * fieldClassifier: (node) => node.type === 'paper' ? 'computer-science' : undefined,
317
- * pairsPerStratum: 20,
318
- * });
319
- *
320
- * for (const stratum of result.strata) {
321
- * console.log(`${stratum.name}: ${stratum.pairs.length} pairs`);
322
- * }
323
- * ```
324
- */
325
- function stratified(graph, options) {
326
- const { fieldClassifier, pairsPerStratum = DEFAULTS.pairsPerStratum, rngSeed = DEFAULTS.rngSeed, customStrata } = options;
327
- const rng = createRNG(rngSeed);
328
- const strataDefinitions = customStrata ?? [];
329
- const nodesWithFields = [];
330
- for (const nodeId of graph.nodeIds()) {
331
- const node = graph.getNode(nodeId);
332
- if (node === void 0) continue;
333
- const field = fieldClassifier({
334
- id: nodeId,
335
- type: node.type
336
- });
337
- if (field === void 0) continue;
338
- nodesWithFields.push({
339
- id: nodeId,
340
- type: node.type,
341
- field
342
- });
343
- }
344
- const errors = [];
345
- const strataResults = [];
346
- for (const stratum of strataDefinitions) {
347
- const pairs = [];
348
- const eligiblePairs = [];
349
- for (let i = 0; i < nodesWithFields.length; i++) {
350
- const source = nodesWithFields[i];
351
- if (source === void 0) continue;
352
- for (let j = i + 1; j < nodesWithFields.length; j++) {
353
- if (j === i) continue;
354
- const target = nodesWithFields[j];
355
- if (target === void 0) continue;
356
- if (stratum.predicate(source, target)) eligiblePairs.push({
357
- source,
358
- target
359
- });
360
- }
361
- }
362
- const numToSample = Math.min(pairsPerStratum, eligiblePairs.length);
363
- for (let i = 0; i < numToSample; i++) {
364
- const pair = eligiblePairs[Math.floor(rng() * eligiblePairs.length)];
365
- if (pair === void 0) continue;
366
- const sourceField = fieldClassifier(pair.source);
367
- const targetField = fieldClassifier(pair.target);
368
- pairs.push({
369
- source: { id: pair.source.id },
370
- target: { id: pair.target.id },
371
- stratum: stratum.name,
372
- sameField: sourceField === targetField
373
- });
374
- }
375
- strataResults.push({
376
- name: stratum.name,
377
- pairs
378
- });
379
- }
380
- for (const stratum of strataDefinitions) {
381
- const result = strataResults.find((r) => r.name === stratum.name);
382
- if (result === void 0 || result.pairs.length === 0) errors.push(/* @__PURE__ */ new Error(`No pairs found for stratum: ${stratum.name}`));
383
- }
384
- return {
385
- strata: strataResults,
386
- totalPairs: strataResults.reduce((sum, r) => sum + r.pairs.length, 0),
387
- errors
388
- };
389
- }
390
- //#endregion
391
- export { grasp as n, stratified as t };
392
-
393
- //# sourceMappingURL=seeds-UNZxqm_U.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"seeds-UNZxqm_U.js","names":[],"sources":["../src/seeds/grasp.ts","../src/seeds/stratified.ts"],"sourcesContent":["/**\n * GRASP — Graph-agnostic Representative Seed pAir Sampling.\n *\n * Novel blind structural seed selection algorithm that selects structurally\n * representative seed pairs without requiring domain knowledge or loading\n * the full graph into memory.\n *\n * Three phases:\n * 1. Reservoir sampling — stream edges, maintain reservoir of N nodes\n * 2. Structural feature computation — log-degree, clustering coeff, approx PageRank\n * 3. Mini-batch K-means clustering, then sample pairs within and across clusters\n *\n * @packageDocumentation\n */\n\nimport type { ReadableGraph, NodeId } from \"../graph\";\nimport type { Seed } from \"../schemas/index\";\nimport {\n\tminiBatchKMeans,\n\tzScoreNormalise,\n\ttype LabelledFeature,\n\ttype FeatureVector3D,\n} from \"../utils/kmeans\";\n\n/**\n * Configuration options for GRASP seed selection.\n */\nexport interface GraspOptions {\n\t/** Number of clusters for K-means (default: 100) */\n\treadonly nClusters?: number;\n\t/** Number of pairs to sample per cluster (default: 10) */\n\treadonly pairsPerCluster?: number;\n\t/** Ratio of within-cluster pairs vs cross-cluster pairs (default: 0.5) */\n\treadonly withinClusterRatio?: number;\n\t/** Reservoir sample size (default: 200000) */\n\treadonly sampleSize?: number;\n\t/** Random seed for reproducibility (default: 42) */\n\treadonly rngSeed?: number;\n\t/** Number of PageRank iterations for feature computation (default: 10) */\n\treadonly pagerankIterations?: number;\n}\n\n/**\n * A sampled seed pair with structural metadata.\n */\nexport interface GraspSeedPair {\n\t/** Source seed */\n\treadonly source: Seed;\n\t/** Target seed */\n\treadonly target: Seed;\n\t/** Euclidean distance in feature space */\n\treadonly featureDistance: number;\n\t/** Whether both seeds are from the same cluster */\n\treadonly sameCluster: boolean;\n\t/** Cluster index of source (or -1 if unclustered) */\n\treadonly sourceCluster: number;\n\t/** Cluster index of target (or -1 if unclustered) */\n\treadonly targetCluster: number;\n}\n\n/**\n * Result of GRASP seed selection.\n */\nexport interface GraspResult {\n\t/** Sampled seed pairs */\n\treadonly pairs: readonly GraspSeedPair[];\n\t/** Number of clusters used */\n\treadonly nClusters: number;\n\t/** Total nodes sampled */\n\treadonly sampledNodeCount: number;\n\t/** Features computed for sampled nodes */\n\treadonly features: readonly LabelledFeature[];\n\t/** Cluster assignments (nodeId -> cluster index) */\n\treadonly clusterAssignments: ReadonlyMap<string, number>;\n}\n\n/** Default configuration values */\nconst DEFAULTS = {\n\tnClusters: 100,\n\tpairsPerCluster: 10,\n\twithinClusterRatio: 0.5,\n\tsampleSize: 200000,\n\trngSeed: 42,\n\tpagerankIterations: 10,\n} as const;\n\n/**\n * Simple seeded pseudo-random number generator using mulberry32.\n */\nfunction createRNG(seed: number): () => number {\n\tlet state = seed >>> 0;\n\treturn (): number => {\n\t\tstate = (state + 0x6d2b79f5) >>> 0;\n\t\tlet t = Math.imul(state ^ (state >>> 15), state | 1);\n\t\tt = (t ^ (t >>> 7)) * (t | 0x61c88647);\n\t\treturn ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n\t};\n}\n\n/**\n * Reservoir sampling (Vitter's Algorithm R) for streaming node selection.\n *\n * Maintains a uniform sample of nodes as edges are streamed, without\n * requiring the full graph in memory.\n */\nfunction reservoirSample(\n\tgraph: ReadableGraph,\n\tsampleSize: number,\n\trng: () => number,\n): { nodeIds: Set<NodeId>; neighbourMap: Map<NodeId, Set<NodeId>> } {\n\tconst reservoir: NodeId[] = [];\n\tconst neighbourMap = new Map<NodeId, Set<NodeId>>();\n\tconst inReservoir = new Set<NodeId>();\n\n\t// Track total nodes seen for reservoir probability\n\tlet nodesSeen = 0;\n\n\t// Stream all edges and sample nodes\n\tfor (const edge of graph.edges()) {\n\t\t// Process source node\n\t\tconst source = edge.source;\n\t\tif (!inReservoir.has(source)) {\n\t\t\tnodesSeen++;\n\t\t\tif (reservoir.length < sampleSize) {\n\t\t\t\treservoir.push(source);\n\t\t\t\tinReservoir.add(source);\n\t\t\t\tneighbourMap.set(source, new Set<NodeId>());\n\t\t\t} else {\n\t\t\t\t// Reservoir sampling: replace with probability sampleSize/nodesSeen\n\t\t\t\tconst j = Math.floor(rng() * nodesSeen);\n\t\t\t\tif (j < sampleSize) {\n\t\t\t\t\tconst oldNode = reservoir[j];\n\t\t\t\t\tif (oldNode !== undefined) {\n\t\t\t\t\t\tinReservoir.delete(oldNode);\n\t\t\t\t\t\tneighbourMap.delete(oldNode);\n\t\t\t\t\t}\n\t\t\t\t\treservoir[j] = source;\n\t\t\t\t\tinReservoir.add(source);\n\t\t\t\t\tneighbourMap.set(source, new Set<NodeId>());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Process target node\n\t\tconst target = edge.target;\n\t\tif (!inReservoir.has(target)) {\n\t\t\tnodesSeen++;\n\t\t\tif (reservoir.length < sampleSize) {\n\t\t\t\treservoir.push(target);\n\t\t\t\tinReservoir.add(target);\n\t\t\t\tneighbourMap.set(target, new Set<NodeId>());\n\t\t\t} else {\n\t\t\t\tconst j = Math.floor(rng() * nodesSeen);\n\t\t\t\tif (j < sampleSize) {\n\t\t\t\t\tconst oldNode = reservoir[j];\n\t\t\t\t\tif (oldNode !== undefined) {\n\t\t\t\t\t\tinReservoir.delete(oldNode);\n\t\t\t\t\t\tneighbourMap.delete(oldNode);\n\t\t\t\t\t}\n\t\t\t\t\treservoir[j] = target;\n\t\t\t\t\tinReservoir.add(target);\n\t\t\t\t\tneighbourMap.set(target, new Set<NodeId>());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Store neighbour relationships for sampled nodes\n\t\tif (inReservoir.has(source) && inReservoir.has(target)) {\n\t\t\tconst sourceNeighbours = neighbourMap.get(source);\n\t\t\tconst targetNeighbours = neighbourMap.get(target);\n\t\t\tsourceNeighbours?.add(target);\n\t\t\ttargetNeighbours?.add(source);\n\t\t}\n\t}\n\n\treturn { nodeIds: inReservoir, neighbourMap };\n}\n\n/**\n * Compute approximate PageRank scores using power iteration on the reservoir subgraph.\n *\n * This is an approximation since it only considers the sampled nodes and their\n * connections within the reservoir, not the full graph.\n */\nfunction approximatePageRank(\n\tnodeIds: Set<NodeId>,\n\tneighbourMap: Map<NodeId, Set<NodeId>>,\n\titerations: number,\n\tdampingFactor = 0.85,\n): Map<NodeId, number> {\n\tconst n = nodeIds.size;\n\tif (n === 0) return new Map();\n\n\tconst nodeIdList = [...nodeIds];\n\tconst nodeIndex = new Map(nodeIdList.map((id, i) => [id, i] as const));\n\n\t// Initialise PageRank scores uniformly\n\tconst scores = new Float64Array(n).fill(1 / n);\n\tconst newScores = new Float64Array(n);\n\n\t// Power iteration\n\tfor (let iter = 0; iter < iterations; iter++) {\n\t\tnewScores.fill((1 - dampingFactor) / n);\n\n\t\tfor (let i = 0; i < n; i++) {\n\t\t\tconst nodeId = nodeIdList[i];\n\t\t\tif (nodeId === undefined) continue;\n\n\t\t\tconst neighbours = neighbourMap.get(nodeId);\n\t\t\tif (neighbours === undefined) continue;\n\n\t\t\tconst outDegree = neighbours.size;\n\t\t\tif (outDegree === 0) continue;\n\n\t\t\tconst contribution = (dampingFactor * (scores[i] ?? 0)) / outDegree;\n\n\t\t\tfor (const neighbour of neighbours) {\n\t\t\t\tconst neighbourIdx = nodeIndex.get(neighbour);\n\t\t\t\tif (neighbourIdx !== undefined) {\n\t\t\t\t\tnewScores[neighbourIdx] =\n\t\t\t\t\t\t(newScores[neighbourIdx] ?? 0) + contribution;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Swap buffers\n\t\tfor (let i = 0; i < n; i++) {\n\t\t\tscores[i] = newScores[i] ?? 0;\n\t\t}\n\t}\n\n\t// Build result map\n\tconst result = new Map<NodeId, number>();\n\tfor (let i = 0; i < n; i++) {\n\t\tconst nodeId = nodeIdList[i];\n\t\tconst score = scores[i];\n\t\tif (nodeId !== undefined && score !== undefined) {\n\t\t\tresult.set(nodeId, score);\n\t\t}\n\t}\n\n\treturn result;\n}\n\n/**\n * Compute structural features for sampled nodes.\n *\n * Features:\n * - f1: log(deg(v) + 1) — scale-normalised connectivity\n * - f2: clustering_coefficient(v) — local density\n * - f3: approx_pagerank(v) — positional importance\n */\nfunction computeFeatures(\n\tgraph: ReadableGraph,\n\tnodeIds: Set<NodeId>,\n\tneighbourMap: Map<NodeId, Set<NodeId>>,\n\tpagerankScores: Map<NodeId, number>,\n): LabelledFeature[] {\n\tconst features: LabelledFeature[] = [];\n\n\tfor (const nodeId of nodeIds) {\n\t\tconst degree = graph.degree(nodeId, \"both\");\n\t\tconst neighbours = neighbourMap.get(nodeId);\n\n\t\t// Compute local clustering coefficient using neighbour map\n\t\tlet clusteringCoef = 0;\n\t\tif (neighbours !== undefined && neighbours.size >= 2) {\n\t\t\tlet triangleCount = 0;\n\t\t\tconst neighbourList = [...neighbours];\n\n\t\t\tfor (let i = 0; i < neighbourList.length; i++) {\n\t\t\t\tfor (let j = i + 1; j < neighbourList.length; j++) {\n\t\t\t\t\tconst u = neighbourList[i];\n\t\t\t\t\tconst w = neighbourList[j];\n\t\t\t\t\tif (u !== undefined && w !== undefined) {\n\t\t\t\t\t\t// Check if u and w are connected\n\t\t\t\t\t\tconst uNeighbours = neighbourMap.get(u);\n\t\t\t\t\t\tif (uNeighbours?.has(w) === true) {\n\t\t\t\t\t\t\ttriangleCount++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst possibleTriangles = (degree * (degree - 1)) / 2;\n\t\t\tclusteringCoef = triangleCount / possibleTriangles;\n\t\t}\n\n\t\tconst pagerank = pagerankScores.get(nodeId) ?? 0;\n\n\t\tfeatures.push({\n\t\t\tnodeId,\n\t\t\tf1: Math.log(degree + 1),\n\t\t\tf2: clusteringCoef,\n\t\t\tf3: pagerank,\n\t\t});\n\t}\n\n\treturn features;\n}\n\n/**\n * Sample seed pairs from clusters.\n *\n * For each cluster, samples a mix of within-cluster and cross-cluster pairs.\n */\nfunction samplePairs(\n\tfeatures: readonly LabelledFeature[],\n\tclusterAssignments: ReadonlyMap<string, number>,\n\tnClusters: number,\n\tpairsPerCluster: number,\n\twithinClusterRatio: number,\n\trng: () => number,\n): GraspSeedPair[] {\n\tconst pairs: GraspSeedPair[] = [];\n\n\t// Group nodes by cluster\n\tconst clusterNodes = new Map<number, LabelledFeature[]>();\n\tfor (const feature of features) {\n\t\tconst cluster = clusterAssignments.get(feature.nodeId);\n\t\tif (cluster === undefined) continue;\n\n\t\tlet nodes = clusterNodes.get(cluster);\n\t\tif (nodes === undefined) {\n\t\t\tnodes = [];\n\t\t\tclusterNodes.set(cluster, nodes);\n\t\t}\n\t\tnodes.push(feature);\n\t}\n\n\tconst withinCount = Math.floor(pairsPerCluster * withinClusterRatio);\n\tconst crossCount = pairsPerCluster - withinCount;\n\n\t// Sample pairs for each cluster\n\tfor (let clusterIdx = 0; clusterIdx < nClusters; clusterIdx++) {\n\t\tconst nodes = clusterNodes.get(clusterIdx);\n\t\tif (nodes === undefined || nodes.length < 2) continue;\n\n\t\t// Within-cluster pairs\n\t\tfor (let i = 0; i < withinCount; i++) {\n\t\t\t// Sample two distinct nodes\n\t\t\tconst idx1 = Math.floor(rng() * nodes.length);\n\t\t\tlet idx2 = Math.floor(rng() * nodes.length);\n\t\t\twhile (idx1 === idx2) {\n\t\t\t\tidx2 = Math.floor(rng() * nodes.length);\n\t\t\t}\n\n\t\t\tconst source = nodes[idx1];\n\t\t\tconst target = nodes[idx2];\n\t\t\tif (source === undefined || target === undefined) continue;\n\n\t\t\tconst distance = computeFeatureDistance(source, target);\n\n\t\t\tpairs.push({\n\t\t\t\tsource: { id: source.nodeId },\n\t\t\t\ttarget: { id: target.nodeId },\n\t\t\t\tfeatureDistance: distance,\n\t\t\t\tsameCluster: true,\n\t\t\t\tsourceCluster: clusterIdx,\n\t\t\t\ttargetCluster: clusterIdx,\n\t\t\t});\n\t\t}\n\n\t\t// Cross-cluster pairs\n\t\tfor (let i = 0; i < crossCount; i++) {\n\t\t\tconst source = nodes[Math.floor(rng() * nodes.length)];\n\t\t\tif (source === undefined) continue;\n\n\t\t\t// Pick a random other cluster\n\t\t\tconst otherClusterIdx = Math.floor(rng() * nClusters);\n\t\t\tif (otherClusterIdx === clusterIdx) continue;\n\n\t\t\tconst otherNodes = clusterNodes.get(otherClusterIdx);\n\t\t\tif (otherNodes === undefined || otherNodes.length === 0) continue;\n\n\t\t\tconst target = otherNodes[Math.floor(rng() * otherNodes.length)];\n\t\t\tif (target === undefined) continue;\n\n\t\t\tconst distance = computeFeatureDistance(source, target);\n\n\t\t\tpairs.push({\n\t\t\t\tsource: { id: source.nodeId },\n\t\t\t\ttarget: { id: target.nodeId },\n\t\t\t\tfeatureDistance: distance,\n\t\t\t\tsameCluster: false,\n\t\t\t\tsourceCluster: clusterIdx,\n\t\t\t\ttargetCluster: otherClusterIdx,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn pairs;\n}\n\n/**\n * Compute Euclidean distance between two feature vectors.\n */\nfunction computeFeatureDistance(\n\ta: FeatureVector3D,\n\tb: FeatureVector3D,\n): number {\n\tconst d1 = a.f1 - b.f1;\n\tconst d2 = a.f2 - b.f2;\n\tconst d3 = a.f3 - b.f3;\n\treturn Math.sqrt(d1 * d1 + d2 * d2 + d3 * d3);\n}\n\n/**\n * GRASP — Graph-agnostic Representative Seed pAir Sampling.\n *\n * Selects structurally representative seed pairs without domain knowledge.\n * The algorithm streams edges, samples nodes via reservoir sampling, computes\n * structural features, clusters nodes, and samples pairs within/across clusters.\n *\n * @param graph - The graph to sample seeds from\n * @param options - Configuration options\n * @returns Sampled seed pairs with structural metadata\n *\n * @example\n * ```typescript\n * const graph = new AdjacencyMapGraph();\n * // ... populate graph ...\n *\n * const result = grasp(graph, {\n * nClusters: 50,\n * pairsPerCluster: 20,\n * sampleSize: 100000,\n * });\n *\n * console.log(`Sampled ${result.pairs.length} pairs from ${result.sampledNodeCount} nodes`);\n * ```\n */\nexport function grasp(\n\tgraph: ReadableGraph,\n\toptions: GraspOptions = {},\n): GraspResult {\n\tconst config = {\n\t\t...DEFAULTS,\n\t\t...options,\n\t};\n\n\tconst rng = createRNG(config.rngSeed);\n\n\t// Phase 1: Reservoir sampling\n\tconst { nodeIds, neighbourMap } = reservoirSample(\n\t\tgraph,\n\t\tconfig.sampleSize,\n\t\trng,\n\t);\n\n\t// Phase 2: Approximate PageRank on reservoir subgraph\n\tconst pagerankScores = approximatePageRank(\n\t\tnodeIds,\n\t\tneighbourMap,\n\t\tconfig.pagerankIterations,\n\t);\n\n\t// Phase 2: Compute structural features\n\tlet features = computeFeatures(graph, nodeIds, neighbourMap, pagerankScores);\n\n\t// Normalise features\n\tif (features.length > 0) {\n\t\tfeatures = zScoreNormalise(features);\n\t}\n\n\t// Phase 3: K-means clustering\n\tconst k = Math.min(config.nClusters, features.length);\n\tconst kmeansResult = miniBatchKMeans(features, {\n\t\tk,\n\t\tseed: config.rngSeed,\n\t\tmaxIterations: 100,\n\t});\n\n\t// Phase 3: Sample pairs\n\tconst pairs = samplePairs(\n\t\tfeatures,\n\t\tkmeansResult.assignments,\n\t\tkmeansResult.k,\n\t\tconfig.pairsPerCluster,\n\t\tconfig.withinClusterRatio,\n\t\trng,\n\t);\n\n\treturn {\n\t\tpairs,\n\t\tnClusters: kmeansResult.k,\n\t\tsampledNodeCount: nodeIds.size,\n\t\tfeatures,\n\t\tclusterAssignments: kmeansResult.assignments,\n\t};\n}\n","/**\n * Stratified seed selection — legacy human-defined strata.\n *\n * Requires user-provided field/type classifications. This is included for comparison\n * and for users who have domain metadata.\n *\n * @packageDocumentation\n */\n\nimport type { ReadableGraph, NodeId } from \"../graph/index\";\nimport type { Seed } from \"../schemas/index\";\n\n/**\n * Field classification function type.\n * User provides a function that returns the field name for a node.\n */\nexport type FieldClassifier = (node: {\n\tid: NodeId;\n\ttype?: string;\n}) => string | undefined;\n\n/**\n * Stratum definition for seed pair selection.\n */\nexport interface StratumDefinition {\n\treadonly name: string;\n\treadonly description: string;\n\treadonly predicate: (\n\t\tsource: { id: NodeId; type?: string },\n\t\ttarget: { id: NodeId; type?: string },\n\t) => boolean;\n}\n\n/**\n * Stratum with sampled seed pairs.\n */\nexport interface StratumResult {\n\treadonly name: string;\n\treadonly pairs: readonly SeedPair[];\n}\n\n/**\n * A seed pair with stratum metadata.\n */\nexport interface SeedPair {\n\treadonly source: Seed;\n\treadonly target: Seed;\n\treadonly stratum: string;\n\treadonly sameField: boolean;\n}\n\n/**\n * Result of stratified seed selection.\n */\nexport interface StratifiedResult {\n\treadonly strata: readonly StratumResult[];\n\treadonly totalPairs: number;\n\treadonly errors: readonly Error[];\n}\n\n/**\n * Configuration for stratified seed selection.\n */\nexport interface StratifiedOptions {\n\t/** Function to classify nodes by field */\n\treadonly fieldClassifier: FieldClassifier;\n\t/** Number of pairs to sample per stratum */\n\treadonly pairsPerStratum?: number;\n\t/** Random seed for reproducibility */\n\treadonly rngSeed?: number;\n\t/** Custom stratum definitions */\n\treadonly customStrata?: readonly StratumDefinition[];\n}\n\n/** Default values */\nconst DEFAULTS = {\n\tpairsPerStratum: 10,\n\trngSeed: 42,\n} as const;\n\n/**\n * Simple seeded pseudo-random number generator using mulberry32.\n */\nfunction createRNG(seed: number): () => number {\n\tlet state = seed >>> 0;\n\treturn (): number => {\n\t\tstate = (state + 0x6d2b79f5) >>> 0;\n\t\tlet t = Math.imul(state ^ (state >>> 15), state | 1);\n\t\tt = (t ^ (t >>> 7)) * (t | 0x61c88647);\n\t\treturn ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n\t};\n}\n\n/**\n * Stratified seed selection algorithm.\n *\n * @param graph - The graph to sample seeds from\n * @param options - Configuration options including field classifier\n * @returns Stratified selection result\n *\n * @example\n * ```typescript\n * const graph = new AdjacencyMapGraph();\n * // ... populate graph ...\n *\n * const result = stratified(graph, {\n * fieldClassifier: (node) => node.type === 'paper' ? 'computer-science' : undefined,\n * pairsPerStratum: 20,\n * });\n *\n * for (const stratum of result.strata) {\n * console.log(`${stratum.name}: ${stratum.pairs.length} pairs`);\n * }\n * ```\n */\nexport function stratified(\n\tgraph: ReadableGraph,\n\toptions: StratifiedOptions,\n): StratifiedResult {\n\tconst {\n\t\tfieldClassifier,\n\t\tpairsPerStratum = DEFAULTS.pairsPerStratum,\n\t\trngSeed = DEFAULTS.rngSeed,\n\t\tcustomStrata,\n\t} = options;\n\n\tconst rng = createRNG(rngSeed);\n\tconst strataDefinitions = customStrata ?? [];\n\n\t// Collect all nodes with their field classifications\n\tconst nodesWithFields: { id: NodeId; type?: string; field?: string }[] = [];\n\n\tfor (const nodeId of graph.nodeIds()) {\n\t\tconst node = graph.getNode(nodeId);\n\t\tif (node === undefined) continue;\n\n\t\tconst field = fieldClassifier({ id: nodeId, type: node.type });\n\t\tif (field === undefined) continue;\n\n\t\tnodesWithFields.push({ id: nodeId, type: node.type, field });\n\t}\n\n\tconst errors: Error[] = [];\n\tconst strataResults: StratumResult[] = [];\n\n\t// Process each stratum\n\tfor (const stratum of strataDefinitions) {\n\t\tconst pairs: SeedPair[] = [];\n\t\tconst eligiblePairs: {\n\t\t\tsource: { id: NodeId; type?: string };\n\t\t\ttarget: { id: NodeId; type?: string };\n\t\t}[] = [];\n\n\t\t// Find all node pairs that match this stratum\n\t\tfor (let i = 0; i < nodesWithFields.length; i++) {\n\t\t\tconst source = nodesWithFields[i];\n\t\t\tif (source === undefined) continue;\n\n\t\t\tfor (let j = i + 1; j < nodesWithFields.length; j++) {\n\t\t\t\tif (j === i) continue;\n\t\t\t\tconst target = nodesWithFields[j];\n\t\t\t\tif (target === undefined) continue;\n\n\t\t\t\tif (stratum.predicate(source, target)) {\n\t\t\t\t\teligiblePairs.push({ source, target });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Sample pairs from eligible pairs\n\t\tconst numToSample = Math.min(pairsPerStratum, eligiblePairs.length);\n\t\tfor (let i = 0; i < numToSample; i++) {\n\t\t\tconst idx = Math.floor(rng() * eligiblePairs.length);\n\t\t\tconst pair = eligiblePairs[idx];\n\t\t\tif (pair === undefined) continue;\n\n\t\t\tconst sourceField = fieldClassifier(pair.source);\n\t\t\tconst targetField = fieldClassifier(pair.target);\n\n\t\t\tpairs.push({\n\t\t\t\tsource: { id: pair.source.id },\n\t\t\t\ttarget: { id: pair.target.id },\n\t\t\t\tstratum: stratum.name,\n\t\t\t\tsameField: sourceField === targetField,\n\t\t\t});\n\t\t}\n\n\t\tstrataResults.push({\n\t\t\tname: stratum.name,\n\t\t\tpairs,\n\t\t});\n\t}\n\n\t// Collect errors for empty strata\n\tfor (const stratum of strataDefinitions) {\n\t\tconst result = strataResults.find((r) => r.name === stratum.name);\n\t\tif (result === undefined || result.pairs.length === 0) {\n\t\t\terrors.push(new Error(`No pairs found for stratum: ${stratum.name}`));\n\t\t}\n\t}\n\n\tconst totalPairs = strataResults.reduce((sum, r) => sum + r.pairs.length, 0);\n\n\treturn {\n\t\tstrata: strataResults,\n\t\ttotalPairs,\n\t\terrors,\n\t};\n}\n"],"mappings":"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"}
@@ -1,133 +0,0 @@
1
- //#region src/structures/priority-queue.ts
2
- /**
3
- * Min-heap priority queue implementation using an array-based binary heap.
4
- * Lower priority values have higher precedence (extracted first).
5
- */
6
- var PriorityQueue = class {
7
- heap = [];
8
- /**
9
- * Returns the number of items in the queue.
10
- */
11
- size() {
12
- return this.heap.length;
13
- }
14
- /**
15
- * Returns true if the queue is empty.
16
- */
17
- isEmpty() {
18
- return this.heap.length === 0;
19
- }
20
- /**
21
- * Adds an item with the given priority to the queue.
22
- */
23
- push(item, priority) {
24
- const entry = {
25
- item,
26
- priority
27
- };
28
- this.heap.push(entry);
29
- this.heapifyUp(this.heap.length - 1);
30
- }
31
- /**
32
- * Removes and returns the highest precedence item (lowest priority value).
33
- * Returns undefined if the queue is empty.
34
- */
35
- pop() {
36
- if (this.heap.length === 0) return;
37
- const root = this.heap[0];
38
- if (root === void 0) return;
39
- const last = this.heap.pop();
40
- if (last === void 0) return root;
41
- if (this.heap.length > 0) {
42
- this.heap[0] = last;
43
- this.heapifyDown(0);
44
- }
45
- return root;
46
- }
47
- /**
48
- * Returns the highest precedence item without removing it.
49
- * Returns undefined if the queue is empty.
50
- */
51
- peek() {
52
- return this.heap[0];
53
- }
54
- /**
55
- * Rebuilds the heap from the current array state.
56
- * Useful when priorities have been modified externally (e.g., phase transitions).
57
- */
58
- rebuild() {
59
- const start = Math.floor(this.heap.length / 2) - 1;
60
- for (let i = start; i >= 0; i--) this.heapifyDown(i);
61
- }
62
- /**
63
- * Decreases the priority of an existing item in the queue.
64
- * Returns true if the item was found and updated, false otherwise.
65
- *
66
- * @param item - The item to find
67
- * @param newPriority - The new (lower) priority value
68
- * @param equals - Function to compare items for equality
69
- */
70
- decreaseKey(item, newPriority, equals) {
71
- let foundIndex = -1;
72
- for (let i = 0; i < this.heap.length; i++) {
73
- const entry = this.heap[i];
74
- if (entry !== void 0 && equals(entry.item, item)) {
75
- foundIndex = i;
76
- break;
77
- }
78
- }
79
- if (foundIndex === -1) return false;
80
- const entry = this.heap[foundIndex];
81
- if (entry === void 0) return false;
82
- if (newPriority >= entry.priority) return false;
83
- entry.priority = newPriority;
84
- this.heapifyUp(foundIndex);
85
- return true;
86
- }
87
- /**
88
- * Restores heap property by moving element up from given index.
89
- */
90
- heapifyUp(index) {
91
- let current = index;
92
- while (current > 0) {
93
- const parent = Math.floor((current - 1) / 2);
94
- const currentEntry = this.heap[current];
95
- const parentEntry = this.heap[parent];
96
- if (currentEntry === void 0 || parentEntry === void 0) return;
97
- if (currentEntry.priority >= parentEntry.priority) return;
98
- this.heap[current] = parentEntry;
99
- this.heap[parent] = currentEntry;
100
- current = parent;
101
- }
102
- }
103
- /**
104
- * Restores heap property by moving element down from given index.
105
- */
106
- heapifyDown(index) {
107
- let current = index;
108
- const length = this.heap.length;
109
- while (current < length) {
110
- const left = 2 * current + 1;
111
- const right = 2 * current + 2;
112
- let smallest = current;
113
- const currentEntry = this.heap[current];
114
- if (currentEntry === void 0) return;
115
- const leftEntry = this.heap[left];
116
- if (left < length && leftEntry !== void 0 && leftEntry.priority < currentEntry.priority) smallest = left;
117
- const rightEntry = this.heap[right];
118
- const currentSmallestEntry = this.heap[smallest];
119
- if (right < length && rightEntry !== void 0 && currentSmallestEntry !== void 0 && rightEntry.priority < currentSmallestEntry.priority) smallest = right;
120
- if (smallest === current) return;
121
- const finalSmallestEntry = this.heap[smallest];
122
- if (finalSmallestEntry !== void 0) {
123
- this.heap[current] = finalSmallestEntry;
124
- this.heap[smallest] = currentEntry;
125
- current = smallest;
126
- } else return;
127
- }
128
- }
129
- };
130
- //#endregion
131
- export { PriorityQueue as t };
132
-
133
- //# sourceMappingURL=structures-BPfhfqNP.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"structures-BPfhfqNP.js","names":[],"sources":["../src/structures/priority-queue.ts"],"sourcesContent":["/**\n * Priority queue entry containing an item and its priority.\n */\nexport interface PriorityEntry<T> {\n\titem: T;\n\tpriority: number;\n}\n\n/**\n * Min-heap priority queue implementation using an array-based binary heap.\n * Lower priority values have higher precedence (extracted first).\n */\nexport class PriorityQueue<T> {\n\tprivate heap: PriorityEntry<T>[] = [];\n\n\t/**\n\t * Returns the number of items in the queue.\n\t */\n\tpublic size(): number {\n\t\treturn this.heap.length;\n\t}\n\n\t/**\n\t * Returns true if the queue is empty.\n\t */\n\tpublic isEmpty(): boolean {\n\t\treturn this.heap.length === 0;\n\t}\n\n\t/**\n\t * Adds an item with the given priority to the queue.\n\t */\n\tpublic push(item: T, priority: number): void {\n\t\tconst entry: PriorityEntry<T> = { item, priority };\n\t\tthis.heap.push(entry);\n\t\tthis.heapifyUp(this.heap.length - 1);\n\t}\n\n\t/**\n\t * Removes and returns the highest precedence item (lowest priority value).\n\t * Returns undefined if the queue is empty.\n\t */\n\tpublic pop(): PriorityEntry<T> | undefined {\n\t\tif (this.heap.length === 0) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst root = this.heap[0];\n\t\tif (root === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst last = this.heap.pop();\n\t\tif (last === undefined) {\n\t\t\treturn root;\n\t\t}\n\n\t\tif (this.heap.length > 0) {\n\t\t\tthis.heap[0] = last;\n\t\t\tthis.heapifyDown(0);\n\t\t}\n\n\t\treturn root;\n\t}\n\n\t/**\n\t * Returns the highest precedence item without removing it.\n\t * Returns undefined if the queue is empty.\n\t */\n\tpublic peek(): PriorityEntry<T> | undefined {\n\t\treturn this.heap[0];\n\t}\n\n\t/**\n\t * Rebuilds the heap from the current array state.\n\t * Useful when priorities have been modified externally (e.g., phase transitions).\n\t */\n\tpublic rebuild(): void {\n\t\t// Floyd's algorithm: heapify down from last non-leaf to root\n\t\tconst start = Math.floor(this.heap.length / 2) - 1;\n\t\tfor (let i = start; i >= 0; i--) {\n\t\t\tthis.heapifyDown(i);\n\t\t}\n\t}\n\n\t/**\n\t * Decreases the priority of an existing item in the queue.\n\t * Returns true if the item was found and updated, false otherwise.\n\t *\n\t * @param item - The item to find\n\t * @param newPriority - The new (lower) priority value\n\t * @param equals - Function to compare items for equality\n\t */\n\tpublic decreaseKey(\n\t\titem: T,\n\t\tnewPriority: number,\n\t\tequals: (a: T, b: T) => boolean,\n\t): boolean {\n\t\tlet foundIndex = -1;\n\n\t\tfor (let i = 0; i < this.heap.length; i++) {\n\t\t\tconst entry = this.heap[i];\n\t\t\tif (entry !== undefined && equals(entry.item, item)) {\n\t\t\t\tfoundIndex = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (foundIndex === -1) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst entry = this.heap[foundIndex];\n\t\tif (entry === undefined) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Only update if new priority is lower (higher precedence)\n\t\tif (newPriority >= entry.priority) {\n\t\t\treturn false;\n\t\t}\n\n\t\tentry.priority = newPriority;\n\t\tthis.heapifyUp(foundIndex);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Restores heap property by moving element up from given index.\n\t */\n\tprivate heapifyUp(index: number): void {\n\t\tlet current = index;\n\n\t\twhile (current > 0) {\n\t\t\tconst parent = Math.floor((current - 1) / 2);\n\t\t\tconst currentEntry = this.heap[current];\n\t\t\tconst parentEntry = this.heap[parent];\n\n\t\t\tif (currentEntry === undefined || parentEntry === undefined) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (currentEntry.priority >= parentEntry.priority) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Swap current and parent\n\t\t\tthis.heap[current] = parentEntry;\n\t\t\tthis.heap[parent] = currentEntry;\n\t\t\tcurrent = parent;\n\t\t}\n\t}\n\n\t/**\n\t * Restores heap property by moving element down from given index.\n\t */\n\tprivate heapifyDown(index: number): void {\n\t\tlet current = index;\n\t\tconst length = this.heap.length;\n\n\t\twhile (current < length) {\n\t\t\tconst left = 2 * current + 1;\n\t\t\tconst right = 2 * current + 2;\n\t\t\tlet smallest = current;\n\n\t\t\tconst currentEntry = this.heap[current];\n\t\t\tif (currentEntry === undefined) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Compare with left child\n\t\t\tconst leftEntry = this.heap[left];\n\t\t\tif (\n\t\t\t\tleft < length &&\n\t\t\t\tleftEntry !== undefined &&\n\t\t\t\tleftEntry.priority < currentEntry.priority\n\t\t\t) {\n\t\t\t\tsmallest = left;\n\t\t\t}\n\n\t\t\t// Compare with right child (need to re-fetch smallest after potential update)\n\t\t\tconst rightEntry = this.heap[right];\n\t\t\tconst currentSmallestEntry = this.heap[smallest];\n\t\t\tif (\n\t\t\t\tright < length &&\n\t\t\t\trightEntry !== undefined &&\n\t\t\t\tcurrentSmallestEntry !== undefined &&\n\t\t\t\trightEntry.priority < currentSmallestEntry.priority\n\t\t\t) {\n\t\t\t\tsmallest = right;\n\t\t\t}\n\n\t\t\tif (smallest === current) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Get the final smallest entry (after determining final smallest index)\n\t\t\tconst finalSmallestEntry = this.heap[smallest];\n\t\t\tif (finalSmallestEntry !== undefined) {\n\t\t\t\tthis.heap[current] = finalSmallestEntry;\n\t\t\t\tthis.heap[smallest] = currentEntry;\n\t\t\t\tcurrent = smallest;\n\t\t\t} else {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n}\n"],"mappings":";;;;;AAYA,IAAa,gBAAb,MAA8B;CAC7B,OAAmC,EAAE;;;;CAKrC,OAAsB;AACrB,SAAO,KAAK,KAAK;;;;;CAMlB,UAA0B;AACzB,SAAO,KAAK,KAAK,WAAW;;;;;CAM7B,KAAY,MAAS,UAAwB;EAC5C,MAAM,QAA0B;GAAE;GAAM;GAAU;AAClD,OAAK,KAAK,KAAK,MAAM;AACrB,OAAK,UAAU,KAAK,KAAK,SAAS,EAAE;;;;;;CAOrC,MAA2C;AAC1C,MAAI,KAAK,KAAK,WAAW,EACxB;EAGD,MAAM,OAAO,KAAK,KAAK;AACvB,MAAI,SAAS,KAAA,EACZ;EAGD,MAAM,OAAO,KAAK,KAAK,KAAK;AAC5B,MAAI,SAAS,KAAA,EACZ,QAAO;AAGR,MAAI,KAAK,KAAK,SAAS,GAAG;AACzB,QAAK,KAAK,KAAK;AACf,QAAK,YAAY,EAAE;;AAGpB,SAAO;;;;;;CAOR,OAA4C;AAC3C,SAAO,KAAK,KAAK;;;;;;CAOlB,UAAuB;EAEtB,MAAM,QAAQ,KAAK,MAAM,KAAK,KAAK,SAAS,EAAE,GAAG;AACjD,OAAK,IAAI,IAAI,OAAO,KAAK,GAAG,IAC3B,MAAK,YAAY,EAAE;;;;;;;;;;CAYrB,YACC,MACA,aACA,QACU;EACV,IAAI,aAAa;AAEjB,OAAK,IAAI,IAAI,GAAG,IAAI,KAAK,KAAK,QAAQ,KAAK;GAC1C,MAAM,QAAQ,KAAK,KAAK;AACxB,OAAI,UAAU,KAAA,KAAa,OAAO,MAAM,MAAM,KAAK,EAAE;AACpD,iBAAa;AACb;;;AAIF,MAAI,eAAe,GAClB,QAAO;EAGR,MAAM,QAAQ,KAAK,KAAK;AACxB,MAAI,UAAU,KAAA,EACb,QAAO;AAIR,MAAI,eAAe,MAAM,SACxB,QAAO;AAGR,QAAM,WAAW;AACjB,OAAK,UAAU,WAAW;AAC1B,SAAO;;;;;CAMR,UAAkB,OAAqB;EACtC,IAAI,UAAU;AAEd,SAAO,UAAU,GAAG;GACnB,MAAM,SAAS,KAAK,OAAO,UAAU,KAAK,EAAE;GAC5C,MAAM,eAAe,KAAK,KAAK;GAC/B,MAAM,cAAc,KAAK,KAAK;AAE9B,OAAI,iBAAiB,KAAA,KAAa,gBAAgB,KAAA,EACjD;AAGD,OAAI,aAAa,YAAY,YAAY,SACxC;AAID,QAAK,KAAK,WAAW;AACrB,QAAK,KAAK,UAAU;AACpB,aAAU;;;;;;CAOZ,YAAoB,OAAqB;EACxC,IAAI,UAAU;EACd,MAAM,SAAS,KAAK,KAAK;AAEzB,SAAO,UAAU,QAAQ;GACxB,MAAM,OAAO,IAAI,UAAU;GAC3B,MAAM,QAAQ,IAAI,UAAU;GAC5B,IAAI,WAAW;GAEf,MAAM,eAAe,KAAK,KAAK;AAC/B,OAAI,iBAAiB,KAAA,EACpB;GAID,MAAM,YAAY,KAAK,KAAK;AAC5B,OACC,OAAO,UACP,cAAc,KAAA,KACd,UAAU,WAAW,aAAa,SAElC,YAAW;GAIZ,MAAM,aAAa,KAAK,KAAK;GAC7B,MAAM,uBAAuB,KAAK,KAAK;AACvC,OACC,QAAQ,UACR,eAAe,KAAA,KACf,yBAAyB,KAAA,KACzB,WAAW,WAAW,qBAAqB,SAE3C,YAAW;AAGZ,OAAI,aAAa,QAChB;GAID,MAAM,qBAAqB,KAAK,KAAK;AACrC,OAAI,uBAAuB,KAAA,GAAW;AACrC,SAAK,KAAK,WAAW;AACrB,SAAK,KAAK,YAAY;AACtB,cAAU;SAEV"}