graphwise 1.0.0 → 1.1.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/README.md +41 -0
- package/dist/expansion/base.d.ts +12 -0
- package/dist/expansion/base.d.ts.map +1 -0
- package/dist/expansion/base.unit.test.d.ts +2 -0
- package/dist/expansion/base.unit.test.d.ts.map +1 -0
- package/dist/expansion/dome.d.ts +16 -0
- package/dist/expansion/dome.d.ts.map +1 -0
- package/dist/expansion/dome.unit.test.d.ts +2 -0
- package/dist/expansion/dome.unit.test.d.ts.map +1 -0
- package/dist/expansion/edge.d.ts +15 -0
- package/dist/expansion/edge.d.ts.map +1 -0
- package/dist/expansion/edge.unit.test.d.ts +2 -0
- package/dist/expansion/edge.unit.test.d.ts.map +1 -0
- package/dist/expansion/hae.d.ts +22 -0
- package/dist/expansion/hae.d.ts.map +1 -0
- package/dist/expansion/hae.unit.test.d.ts +2 -0
- package/dist/expansion/hae.unit.test.d.ts.map +1 -0
- package/dist/expansion/index.d.ts +22 -0
- package/dist/expansion/index.d.ts.map +1 -0
- package/dist/expansion/maze.d.ts +25 -0
- package/dist/expansion/maze.d.ts.map +1 -0
- package/dist/expansion/maze.unit.test.d.ts +2 -0
- package/dist/expansion/maze.unit.test.d.ts.map +1 -0
- package/dist/expansion/pipe.d.ts +15 -0
- package/dist/expansion/pipe.d.ts.map +1 -0
- package/dist/expansion/pipe.unit.test.d.ts +2 -0
- package/dist/expansion/pipe.unit.test.d.ts.map +1 -0
- package/dist/expansion/reach.d.ts +26 -0
- package/dist/expansion/reach.d.ts.map +1 -0
- package/dist/expansion/reach.unit.test.d.ts +2 -0
- package/dist/expansion/reach.unit.test.d.ts.map +1 -0
- package/dist/expansion/sage.d.ts +24 -0
- package/dist/expansion/sage.d.ts.map +1 -0
- package/dist/expansion/sage.unit.test.d.ts +2 -0
- package/dist/expansion/sage.unit.test.d.ts.map +1 -0
- package/dist/expansion/types.d.ts +105 -0
- package/dist/expansion/types.d.ts.map +1 -0
- package/dist/extraction/ego-network.d.ts +32 -0
- package/dist/extraction/ego-network.d.ts.map +1 -0
- package/dist/extraction/ego-network.unit.test.d.ts +5 -0
- package/dist/extraction/ego-network.unit.test.d.ts.map +1 -0
- package/dist/extraction/index.d.ts +20 -0
- package/dist/extraction/index.d.ts.map +1 -0
- package/dist/extraction/induced-subgraph.d.ts +19 -0
- package/dist/extraction/induced-subgraph.d.ts.map +1 -0
- package/dist/extraction/induced-subgraph.unit.test.d.ts +5 -0
- package/dist/extraction/induced-subgraph.unit.test.d.ts.map +1 -0
- package/dist/extraction/k-core.d.ts +24 -0
- package/dist/extraction/k-core.d.ts.map +1 -0
- package/dist/extraction/k-core.unit.test.d.ts +5 -0
- package/dist/extraction/k-core.unit.test.d.ts.map +1 -0
- package/dist/extraction/motif.d.ts +50 -0
- package/dist/extraction/motif.d.ts.map +1 -0
- package/dist/extraction/motif.unit.test.d.ts +5 -0
- package/dist/extraction/motif.unit.test.d.ts.map +1 -0
- package/dist/extraction/node-filter.d.ts +35 -0
- package/dist/extraction/node-filter.d.ts.map +1 -0
- package/dist/extraction/node-filter.unit.test.d.ts +5 -0
- package/dist/extraction/node-filter.unit.test.d.ts.map +1 -0
- package/dist/extraction/truss.d.ts +41 -0
- package/dist/extraction/truss.d.ts.map +1 -0
- package/dist/extraction/truss.unit.test.d.ts +5 -0
- package/dist/extraction/truss.unit.test.d.ts.map +1 -0
- package/dist/gpu/context.d.ts +118 -0
- package/dist/gpu/context.d.ts.map +1 -0
- package/dist/gpu/context.unit.test.d.ts +2 -0
- package/dist/gpu/context.unit.test.d.ts.map +1 -0
- package/dist/gpu/csr.d.ts +97 -0
- package/dist/gpu/csr.d.ts.map +1 -0
- package/dist/gpu/csr.unit.test.d.ts +2 -0
- package/dist/gpu/csr.unit.test.d.ts.map +1 -0
- package/dist/gpu/detect.d.ts +25 -0
- package/dist/gpu/detect.d.ts.map +1 -0
- package/dist/gpu/detect.unit.test.d.ts +2 -0
- package/dist/gpu/detect.unit.test.d.ts.map +1 -0
- package/dist/gpu/index.cjs +6 -0
- package/dist/gpu/index.d.ts +11 -0
- package/dist/gpu/index.d.ts.map +1 -0
- package/dist/gpu/index.js +2 -0
- package/dist/gpu/types.d.ts +50 -0
- package/dist/gpu/types.d.ts.map +1 -0
- package/dist/gpu-BJRVYBjx.cjs +338 -0
- package/dist/gpu-BJRVYBjx.cjs.map +1 -0
- package/dist/gpu-BveuXugy.js +315 -0
- package/dist/gpu-BveuXugy.js.map +1 -0
- package/dist/graph/adjacency-map.d.ts +95 -0
- package/dist/graph/adjacency-map.d.ts.map +1 -0
- package/dist/graph/adjacency-map.unit.test.d.ts +2 -0
- package/dist/graph/adjacency-map.unit.test.d.ts.map +1 -0
- package/dist/graph/index.cjs +3 -0
- package/dist/graph/index.d.ts +9 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +2 -0
- package/dist/graph/interfaces.d.ts +125 -0
- package/dist/graph/interfaces.d.ts.map +1 -0
- package/dist/graph/types.d.ts +72 -0
- package/dist/graph/types.d.ts.map +1 -0
- package/dist/graph-DLWiziLB.js +222 -0
- package/dist/graph-DLWiziLB.js.map +1 -0
- package/dist/graph-az06J1YV.cjs +227 -0
- package/dist/graph-az06J1YV.cjs.map +1 -0
- package/dist/index/index.cjs +1404 -0
- package/dist/index/index.cjs.map +1 -0
- package/dist/index/index.js +1356 -0
- package/dist/index/index.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/kmeans-B0HEOU6k.cjs +234 -0
- package/dist/kmeans-B0HEOU6k.cjs.map +1 -0
- package/dist/kmeans-DgbsOznU.js +223 -0
- package/dist/kmeans-DgbsOznU.js.map +1 -0
- package/dist/ranking/baselines/shortest.d.ts +15 -0
- package/dist/ranking/baselines/shortest.d.ts.map +1 -0
- package/dist/ranking/baselines/shortest.unit.test.d.ts +2 -0
- package/dist/ranking/baselines/shortest.unit.test.d.ts.map +1 -0
- package/dist/ranking/baselines/types.d.ts +30 -0
- package/dist/ranking/baselines/types.d.ts.map +1 -0
- package/dist/ranking/index.d.ts +15 -0
- package/dist/ranking/index.d.ts.map +1 -0
- package/dist/ranking/mi/adamic-adar.d.ts +13 -0
- package/dist/ranking/mi/adamic-adar.d.ts.map +1 -0
- package/dist/ranking/mi/adaptive.d.ts +16 -0
- package/dist/ranking/mi/adaptive.d.ts.map +1 -0
- package/dist/ranking/mi/etch.d.ts +7 -0
- package/dist/ranking/mi/etch.d.ts.map +1 -0
- package/dist/ranking/mi/index.d.ts +18 -0
- package/dist/ranking/mi/index.d.ts.map +1 -0
- package/dist/ranking/mi/jaccard.d.ts +13 -0
- package/dist/ranking/mi/jaccard.d.ts.map +1 -0
- package/dist/ranking/mi/mi-variants.unit.test.d.ts +2 -0
- package/dist/ranking/mi/mi-variants.unit.test.d.ts.map +1 -0
- package/dist/ranking/mi/notch.d.ts +7 -0
- package/dist/ranking/mi/notch.d.ts.map +1 -0
- package/dist/ranking/mi/scale.d.ts +7 -0
- package/dist/ranking/mi/scale.d.ts.map +1 -0
- package/dist/ranking/mi/skew.d.ts +7 -0
- package/dist/ranking/mi/skew.d.ts.map +1 -0
- package/dist/ranking/mi/span.d.ts +7 -0
- package/dist/ranking/mi/span.d.ts.map +1 -0
- package/dist/ranking/mi/types.d.ts +35 -0
- package/dist/ranking/mi/types.d.ts.map +1 -0
- package/dist/ranking/parse.d.ts +56 -0
- package/dist/ranking/parse.d.ts.map +1 -0
- package/dist/ranking/parse.unit.test.d.ts +2 -0
- package/dist/ranking/parse.unit.test.d.ts.map +1 -0
- package/dist/schemas/define.d.ts +18 -0
- package/dist/schemas/define.d.ts.map +1 -0
- package/dist/schemas/define.unit.test.d.ts +2 -0
- package/dist/schemas/define.unit.test.d.ts.map +1 -0
- package/dist/schemas/graph.d.ts +85 -0
- package/dist/schemas/graph.d.ts.map +1 -0
- package/dist/schemas/graph.unit.test.d.ts +2 -0
- package/dist/schemas/graph.unit.test.d.ts.map +1 -0
- package/dist/schemas/index.cjs +3791 -0
- package/dist/schemas/index.cjs.map +1 -0
- package/dist/schemas/index.d.ts +3 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +3782 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/seeds/grasp.d.ts +79 -0
- package/dist/seeds/grasp.d.ts.map +1 -0
- package/dist/seeds/grasp.unit.test.d.ts +2 -0
- package/dist/seeds/grasp.unit.test.d.ts.map +1 -0
- package/dist/seeds/index.cjs +4 -0
- package/dist/seeds/index.d.ts +10 -0
- package/dist/seeds/index.d.ts.map +1 -0
- package/dist/seeds/index.js +2 -0
- package/dist/seeds/stratified.d.ts +85 -0
- package/dist/seeds/stratified.d.ts.map +1 -0
- package/dist/seeds/stratified.unit.test.d.ts +2 -0
- package/dist/seeds/stratified.unit.test.d.ts.map +1 -0
- package/dist/seeds-B6J9oJfU.cjs +404 -0
- package/dist/seeds-B6J9oJfU.cjs.map +1 -0
- package/dist/seeds-UNZxqm_U.js +393 -0
- package/dist/seeds-UNZxqm_U.js.map +1 -0
- package/dist/structures/index.cjs +3 -0
- package/dist/structures/index.d.ts +3 -0
- package/dist/structures/index.d.ts.map +1 -0
- package/dist/structures/index.js +2 -0
- package/dist/structures/priority-queue.d.ts +59 -0
- package/dist/structures/priority-queue.d.ts.map +1 -0
- package/dist/structures/priority-queue.unit.test.d.ts +2 -0
- package/dist/structures/priority-queue.unit.test.d.ts.map +1 -0
- package/dist/structures-BPfhfqNP.js +133 -0
- package/dist/structures-BPfhfqNP.js.map +1 -0
- package/dist/structures-CJ_S_7fs.cjs +138 -0
- package/dist/structures-CJ_S_7fs.cjs.map +1 -0
- package/dist/traversal/bfs.d.ts +50 -0
- package/dist/traversal/bfs.d.ts.map +1 -0
- package/dist/traversal/bfs.unit.test.d.ts +2 -0
- package/dist/traversal/bfs.unit.test.d.ts.map +1 -0
- package/dist/traversal/dfs.d.ts +50 -0
- package/dist/traversal/dfs.d.ts.map +1 -0
- package/dist/traversal/dfs.unit.test.d.ts +2 -0
- package/dist/traversal/dfs.unit.test.d.ts.map +1 -0
- package/dist/traversal/index.cjs +6 -0
- package/dist/traversal/index.d.ts +11 -0
- package/dist/traversal/index.d.ts.map +1 -0
- package/dist/traversal/index.js +2 -0
- package/dist/traversal-CQCjUwUJ.js +149 -0
- package/dist/traversal-CQCjUwUJ.js.map +1 -0
- package/dist/traversal-QeHaNUWn.cjs +172 -0
- package/dist/traversal-QeHaNUWn.cjs.map +1 -0
- package/dist/utils/clustering-coefficient.d.ts +38 -0
- package/dist/utils/clustering-coefficient.d.ts.map +1 -0
- package/dist/utils/clustering-coefficient.unit.test.d.ts +2 -0
- package/dist/utils/clustering-coefficient.unit.test.d.ts.map +1 -0
- package/dist/utils/entropy.d.ts +58 -0
- package/dist/utils/entropy.d.ts.map +1 -0
- package/dist/utils/entropy.unit.test.d.ts +2 -0
- package/dist/utils/entropy.unit.test.d.ts.map +1 -0
- package/dist/utils/index.cjs +13 -0
- package/dist/utils/index.d.ts +9 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/kmeans.d.ts +73 -0
- package/dist/utils/kmeans.d.ts.map +1 -0
- package/dist/utils/kmeans.unit.test.d.ts +2 -0
- package/dist/utils/kmeans.unit.test.d.ts.map +1 -0
- package/dist/utils-Q_akvlMn.js +164 -0
- package/dist/utils-Q_akvlMn.js.map +1 -0
- package/dist/utils-spZa1ZvS.cjs +205 -0
- package/dist/utils-spZa1ZvS.cjs.map +1 -0
- package/package.json +136 -8
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ReadableGraph } from '../graph';
|
|
2
|
+
import { Seed } from '../schemas/index';
|
|
3
|
+
import { LabelledFeature } from '../utils/kmeans';
|
|
4
|
+
/**
|
|
5
|
+
* Configuration options for GRASP seed selection.
|
|
6
|
+
*/
|
|
7
|
+
export interface GraspOptions {
|
|
8
|
+
/** Number of clusters for K-means (default: 100) */
|
|
9
|
+
readonly nClusters?: number;
|
|
10
|
+
/** Number of pairs to sample per cluster (default: 10) */
|
|
11
|
+
readonly pairsPerCluster?: number;
|
|
12
|
+
/** Ratio of within-cluster pairs vs cross-cluster pairs (default: 0.5) */
|
|
13
|
+
readonly withinClusterRatio?: number;
|
|
14
|
+
/** Reservoir sample size (default: 200000) */
|
|
15
|
+
readonly sampleSize?: number;
|
|
16
|
+
/** Random seed for reproducibility (default: 42) */
|
|
17
|
+
readonly rngSeed?: number;
|
|
18
|
+
/** Number of PageRank iterations for feature computation (default: 10) */
|
|
19
|
+
readonly pagerankIterations?: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* A sampled seed pair with structural metadata.
|
|
23
|
+
*/
|
|
24
|
+
export interface GraspSeedPair {
|
|
25
|
+
/** Source seed */
|
|
26
|
+
readonly source: Seed;
|
|
27
|
+
/** Target seed */
|
|
28
|
+
readonly target: Seed;
|
|
29
|
+
/** Euclidean distance in feature space */
|
|
30
|
+
readonly featureDistance: number;
|
|
31
|
+
/** Whether both seeds are from the same cluster */
|
|
32
|
+
readonly sameCluster: boolean;
|
|
33
|
+
/** Cluster index of source (or -1 if unclustered) */
|
|
34
|
+
readonly sourceCluster: number;
|
|
35
|
+
/** Cluster index of target (or -1 if unclustered) */
|
|
36
|
+
readonly targetCluster: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Result of GRASP seed selection.
|
|
40
|
+
*/
|
|
41
|
+
export interface GraspResult {
|
|
42
|
+
/** Sampled seed pairs */
|
|
43
|
+
readonly pairs: readonly GraspSeedPair[];
|
|
44
|
+
/** Number of clusters used */
|
|
45
|
+
readonly nClusters: number;
|
|
46
|
+
/** Total nodes sampled */
|
|
47
|
+
readonly sampledNodeCount: number;
|
|
48
|
+
/** Features computed for sampled nodes */
|
|
49
|
+
readonly features: readonly LabelledFeature[];
|
|
50
|
+
/** Cluster assignments (nodeId -> cluster index) */
|
|
51
|
+
readonly clusterAssignments: ReadonlyMap<string, number>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* GRASP — Graph-agnostic Representative Seed pAir Sampling.
|
|
55
|
+
*
|
|
56
|
+
* Selects structurally representative seed pairs without domain knowledge.
|
|
57
|
+
* The algorithm streams edges, samples nodes via reservoir sampling, computes
|
|
58
|
+
* structural features, clusters nodes, and samples pairs within/across clusters.
|
|
59
|
+
*
|
|
60
|
+
* @param graph - The graph to sample seeds from
|
|
61
|
+
* @param options - Configuration options
|
|
62
|
+
* @returns Sampled seed pairs with structural metadata
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const graph = new AdjacencyMapGraph();
|
|
67
|
+
* // ... populate graph ...
|
|
68
|
+
*
|
|
69
|
+
* const result = grasp(graph, {
|
|
70
|
+
* nClusters: 50,
|
|
71
|
+
* pairsPerCluster: 20,
|
|
72
|
+
* sampleSize: 100000,
|
|
73
|
+
* });
|
|
74
|
+
*
|
|
75
|
+
* console.log(`Sampled ${result.pairs.length} pairs from ${result.sampledNodeCount} nodes`);
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export declare function grasp(graph: ReadableGraph, options?: GraspOptions): GraspResult;
|
|
79
|
+
//# sourceMappingURL=grasp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grasp.d.ts","sourceRoot":"","sources":["../../src/seeds/grasp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAU,MAAM,UAAU,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAGN,KAAK,eAAe,EAEpB,MAAM,iBAAiB,CAAC;AAEzB;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B,oDAAoD;IACpD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,0EAA0E;IAC1E,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,8CAA8C;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,oDAAoD;IACpD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,0EAA0E;IAC1E,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,kBAAkB;IAClB,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IACtB,kBAAkB;IAClB,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IACtB,0CAA0C;IAC1C,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,mDAAmD;IACnD,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,qDAAqD;IACrD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,qDAAqD;IACrD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,yBAAyB;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IACzC,8BAA8B;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,0BAA0B;IAC1B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,0CAA0C;IAC1C,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;IAC9C,oDAAoD;IACpD,QAAQ,CAAC,kBAAkB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzD;AA6UD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,KAAK,CACpB,KAAK,EAAE,aAAa,EACpB,OAAO,GAAE,YAAiB,GACxB,WAAW,CAuDb"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grasp.unit.test.d.ts","sourceRoot":"","sources":["../../src/seeds/grasp.unit.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seed selection algorithms.
|
|
3
|
+
*
|
|
4
|
+
* @module seeds
|
|
5
|
+
*/
|
|
6
|
+
export { grasp } from './grasp';
|
|
7
|
+
export type { GraspOptions, GraspResult, GraspSeedPair } from './grasp';
|
|
8
|
+
export { stratified } from './stratified';
|
|
9
|
+
export type { StratifiedOptions, StratifiedResult, StratumResult, StratumDefinition, SeedPair, FieldClassifier, } from './stratified';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/seeds/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EACX,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,QAAQ,EACR,eAAe,GACf,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ReadableGraph, NodeId } from '../graph/index';
|
|
2
|
+
import { Seed } from '../schemas/index';
|
|
3
|
+
/**
|
|
4
|
+
* Field classification function type.
|
|
5
|
+
* User provides a function that returns the field name for a node.
|
|
6
|
+
*/
|
|
7
|
+
export type FieldClassifier = (node: {
|
|
8
|
+
id: NodeId;
|
|
9
|
+
type?: string;
|
|
10
|
+
}) => string | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Stratum definition for seed pair selection.
|
|
13
|
+
*/
|
|
14
|
+
export interface StratumDefinition {
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly description: string;
|
|
17
|
+
readonly predicate: (source: {
|
|
18
|
+
id: NodeId;
|
|
19
|
+
type?: string;
|
|
20
|
+
}, target: {
|
|
21
|
+
id: NodeId;
|
|
22
|
+
type?: string;
|
|
23
|
+
}) => boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Stratum with sampled seed pairs.
|
|
27
|
+
*/
|
|
28
|
+
export interface StratumResult {
|
|
29
|
+
readonly name: string;
|
|
30
|
+
readonly pairs: readonly SeedPair[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A seed pair with stratum metadata.
|
|
34
|
+
*/
|
|
35
|
+
export interface SeedPair {
|
|
36
|
+
readonly source: Seed;
|
|
37
|
+
readonly target: Seed;
|
|
38
|
+
readonly stratum: string;
|
|
39
|
+
readonly sameField: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Result of stratified seed selection.
|
|
43
|
+
*/
|
|
44
|
+
export interface StratifiedResult {
|
|
45
|
+
readonly strata: readonly StratumResult[];
|
|
46
|
+
readonly totalPairs: number;
|
|
47
|
+
readonly errors: readonly Error[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Configuration for stratified seed selection.
|
|
51
|
+
*/
|
|
52
|
+
export interface StratifiedOptions {
|
|
53
|
+
/** Function to classify nodes by field */
|
|
54
|
+
readonly fieldClassifier: FieldClassifier;
|
|
55
|
+
/** Number of pairs to sample per stratum */
|
|
56
|
+
readonly pairsPerStratum?: number;
|
|
57
|
+
/** Random seed for reproducibility */
|
|
58
|
+
readonly rngSeed?: number;
|
|
59
|
+
/** Custom stratum definitions */
|
|
60
|
+
readonly customStrata?: readonly StratumDefinition[];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Stratified seed selection algorithm.
|
|
64
|
+
*
|
|
65
|
+
* @param graph - The graph to sample seeds from
|
|
66
|
+
* @param options - Configuration options including field classifier
|
|
67
|
+
* @returns Stratified selection result
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* const graph = new AdjacencyMapGraph();
|
|
72
|
+
* // ... populate graph ...
|
|
73
|
+
*
|
|
74
|
+
* const result = stratified(graph, {
|
|
75
|
+
* fieldClassifier: (node) => node.type === 'paper' ? 'computer-science' : undefined,
|
|
76
|
+
* pairsPerStratum: 20,
|
|
77
|
+
* });
|
|
78
|
+
*
|
|
79
|
+
* for (const stratum of result.strata) {
|
|
80
|
+
* console.log(`${stratum.name}: ${stratum.pairs.length} pairs`);
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export declare function stratified(graph: ReadableGraph, options: StratifiedOptions): StratifiedResult;
|
|
85
|
+
//# sourceMappingURL=stratified.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stratified.d.ts","sourceRoot":"","sources":["../../src/seeds/stratified.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;CACd,KAAK,MAAM,GAAG,SAAS,CAAC;AAEzB;;GAEG;AACH,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,CACnB,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,EACrC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KACjC,OAAO,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC;IAC1C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IACjC,0CAA0C;IAC1C,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,4CAA4C;IAC5C,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,sCAAsC;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,iCAAiC;IACjC,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;CACrD;AAqBD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,UAAU,CACzB,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,iBAAiB,GACxB,gBAAgB,CA0FlB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stratified.unit.test.d.ts","sourceRoot":"","sources":["../../src/seeds/stratified.unit.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
const require_kmeans = require("./kmeans-B0HEOU6k.cjs");
|
|
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 = require_kmeans.normaliseFeatures(features);
|
|
270
|
+
const k = Math.min(config.nClusters, features.length);
|
|
271
|
+
const kmeansResult = require_kmeans.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
|
+
Object.defineProperty(exports, "grasp", {
|
|
392
|
+
enumerable: true,
|
|
393
|
+
get: function() {
|
|
394
|
+
return grasp;
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
Object.defineProperty(exports, "stratified", {
|
|
398
|
+
enumerable: true,
|
|
399
|
+
get: function() {
|
|
400
|
+
return stratified;
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
//# sourceMappingURL=seeds-B6J9oJfU.cjs.map
|