Skip to content

Commit 4c8d866

Browse files
committed
feat(baselines): add node discovery iteration tracking to baseline algorithms
add nodeDiscoveryIteration tracking to standard-bfs, frontier-balanced, random-priority, delayed-termination, degree-surprise, cross-seed-affinity, and ensemble-expansion. enables coverage efficiency comparison.
1 parent ceb179e commit 4c8d866

File tree

7 files changed

+101
-0
lines changed

7 files changed

+101
-0
lines changed

src/experiments/baselines/cross-seed-affinity.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ export interface CrossSeedAffinityResult {
1818

1919
/** Statistics about the expansion */
2020
stats: CrossSeedAffinityStats;
21+
22+
/** Maps node ID to the iteration when it was first discovered */
23+
nodeDiscoveryIteration: Map<string, number>;
2124
}
2225

2326
/**
@@ -90,6 +93,7 @@ export class CrossSeedAffinityExpansion<T> {
9093
private readonly sampledEdges = new Set<string>();
9194
private readonly frontierVisitCounts = new Map<string, number>();
9295
private stats: CrossSeedAffinityStats;
96+
private readonly nodeDiscoveryIteration = new Map<string, number>();
9397

9498
/**
9599
* Create a new cross-seed-affinity expansion.
@@ -116,6 +120,8 @@ export class CrossSeedAffinityExpansion<T> {
116120
});
117121
// Seeds are visited by their own frontier
118122
this.frontierVisitCounts.set(seed, 1);
123+
// Record seed discovery at iteration 0
124+
this.nodeDiscoveryIteration.set(seed, 0);
119125
}
120126

121127
this.stats = {
@@ -184,6 +190,11 @@ export class CrossSeedAffinityExpansion<T> {
184190
activeState.visited.add(targetId);
185191
activeState.parents.set(targetId, { parent: node, edge: relationshipType });
186192

193+
// Record discovery iteration if this is the first time seeing this node
194+
if (!this.nodeDiscoveryIteration.has(targetId)) {
195+
this.nodeDiscoveryIteration.set(targetId, this.stats.iterations);
196+
}
197+
187198
// Update frontier visit count
188199
const currentCount = this.frontierVisitCounts.get(targetId) ?? 0;
189200
this.frontierVisitCounts.set(targetId, currentCount + 1);
@@ -228,6 +239,7 @@ export class CrossSeedAffinityExpansion<T> {
228239
sampledEdges: this.sampledEdges,
229240
visitedPerFrontier,
230241
stats: this.stats,
242+
nodeDiscoveryIteration: this.nodeDiscoveryIteration,
231243
};
232244
}
233245

src/experiments/baselines/degree-surprise.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ export interface DegreeSurpriseResult {
1818

1919
/** Statistics about the expansion */
2020
stats: DegreeSurpriseStats;
21+
22+
/** Map from node ID to the iteration when it was first discovered */
23+
nodeDiscoveryIteration: Map<string, number>;
2124
}
2225

2326
/**
@@ -85,6 +88,7 @@ export class DegreeSurpriseExpansion<T> {
8588
private readonly paths: Array<{ fromSeed: number; toSeed: number; nodes: string[] }> = [];
8689
private readonly sampledEdges = new Set<string>();
8790
private stats: DegreeSurpriseStats;
91+
private readonly nodeDiscoveryIteration = new Map<string, number>();
8892

8993
/**
9094
* Create a new degree-surprise expansion.
@@ -109,6 +113,7 @@ export class DegreeSurpriseExpansion<T> {
109113
visited: new Set([seed]),
110114
parents: new Map(),
111115
});
116+
this.nodeDiscoveryIteration.set(seed, 0);
112117
}
113118

114119
this.stats = {
@@ -170,6 +175,10 @@ export class DegreeSurpriseExpansion<T> {
170175
activeState.visited.add(targetId);
171176
activeState.parents.set(targetId, { parent: node, edge: relationshipType });
172177

178+
if (!this.nodeDiscoveryIteration.has(targetId)) {
179+
this.nodeDiscoveryIteration.set(targetId, this.stats.iterations);
180+
}
181+
173182
// Compute degree surprise priority for this node
174183
const priority = await this.computeDegreeSurprise(targetId);
175184

@@ -210,6 +219,7 @@ export class DegreeSurpriseExpansion<T> {
210219
sampledEdges: this.sampledEdges,
211220
visitedPerFrontier,
212221
stats: this.stats,
222+
nodeDiscoveryIteration: this.nodeDiscoveryIteration,
213223
};
214224
}
215225

src/experiments/baselines/delayed-termination.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ export interface DelayedTerminationResult {
1818

1919
/** Statistics about the expansion */
2020
stats: DelayedTerminationStats;
21+
22+
/** Map from node ID to the iteration at which it was first discovered */
23+
nodeDiscoveryIteration: Map<string, number>;
2124
}
2225

2326
/**
@@ -95,6 +98,7 @@ export class DelayedTerminationExpansion<T> {
9598
private readonly frontiers: FrontierState[] = [];
9699
private readonly paths: Array<{ fromSeed: number; toSeed: number; nodes: string[] }> = [];
97100
private readonly sampledEdges = new Set<string>();
101+
private readonly nodeDiscoveryIteration = new Map<string, number>();
98102
private stats: DelayedTerminationStats;
99103
private firstOverlapIteration = -1;
100104
private remainingDelayIterations: number;
@@ -126,6 +130,7 @@ export class DelayedTerminationExpansion<T> {
126130
visited: new Set([seed]),
127131
parents: new Map(),
128132
});
133+
this.nodeDiscoveryIteration.set(seed, 0);
129134
}
130135

131136
this.stats = {
@@ -192,6 +197,10 @@ export class DelayedTerminationExpansion<T> {
192197
activeState.visited.add(targetId);
193198
activeState.parents.set(targetId, { parent: node, edge: relationshipType });
194199

200+
if (!this.nodeDiscoveryIteration.has(targetId)) {
201+
this.nodeDiscoveryIteration.set(targetId, this.stats.iterations);
202+
}
203+
195204
// Add to queue (FIFO - end of queue)
196205
activeState.queue.push(targetId);
197206

@@ -235,6 +244,7 @@ export class DelayedTerminationExpansion<T> {
235244
sampledEdges: this.sampledEdges,
236245
visitedPerFrontier,
237246
stats: this.stats,
247+
nodeDiscoveryIteration: this.nodeDiscoveryIteration,
238248
};
239249
}
240250

src/experiments/baselines/ensemble-expansion.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ export interface EnsembleExpansionResult {
1818

1919
/** Statistics about the expansion */
2020
stats: EnsembleExpansionStats;
21+
22+
/** Map from node ID to the iteration when it was first discovered (across all strategies) */
23+
nodeDiscoveryIteration: Map<string, number>;
2124
}
2225

2326
/**
@@ -96,6 +99,7 @@ interface DegreePriorityFrontierState {
9699
*/
97100
export class EnsembleExpansion<T> {
98101
private readonly sampledEdges = new Set<string>();
102+
private readonly nodeDiscoveryIteration = new Map<string, number>();
99103
private stats: EnsembleExpansionStats;
100104

101105
/**
@@ -170,6 +174,7 @@ export class EnsembleExpansion<T> {
170174
sampledEdges: this.sampledEdges,
171175
sampledNodesPerStrategy,
172176
stats: this.stats,
177+
nodeDiscoveryIteration: this.nodeDiscoveryIteration,
173178
};
174179
}
175180

@@ -181,6 +186,7 @@ export class EnsembleExpansion<T> {
181186
private async runBfs(): Promise<Set<string>> {
182187
const frontiers: BfsFrontierState[] = [];
183188
const allVisited = new Set<string>();
189+
let iteration = 0;
184190

185191
for (const [index, seed] of this.seeds.entries()) {
186192
frontiers.push({
@@ -190,9 +196,13 @@ export class EnsembleExpansion<T> {
190196
parents: new Map(),
191197
});
192198
allVisited.add(seed);
199+
if (!this.nodeDiscoveryIteration.has(seed)) {
200+
this.nodeDiscoveryIteration.set(seed, 0);
201+
}
193202
}
194203

195204
while (frontiers.some((f) => f.queue.length > 0)) {
205+
iteration++;
196206
for (const frontier of frontiers) {
197207
if (frontier.queue.length === 0) continue;
198208

@@ -208,6 +218,10 @@ export class EnsembleExpansion<T> {
208218
frontier.parents.set(targetId, node);
209219
frontier.queue.push(targetId);
210220

221+
if (!this.nodeDiscoveryIteration.has(targetId)) {
222+
this.nodeDiscoveryIteration.set(targetId, iteration);
223+
}
224+
211225
const edgeKey = `${node}->${targetId}`;
212226
this.sampledEdges.add(edgeKey);
213227
this.expander.addEdge(node, targetId, relationshipType);
@@ -226,6 +240,7 @@ export class EnsembleExpansion<T> {
226240
private async runDfs(): Promise<Set<string>> {
227241
const frontiers: DfsFrontierState[] = [];
228242
const allVisited = new Set<string>();
243+
let iteration = 0;
229244

230245
for (const [index, seed] of this.seeds.entries()) {
231246
frontiers.push({
@@ -235,9 +250,13 @@ export class EnsembleExpansion<T> {
235250
parents: new Map(),
236251
});
237252
allVisited.add(seed);
253+
if (!this.nodeDiscoveryIteration.has(seed)) {
254+
this.nodeDiscoveryIteration.set(seed, 0);
255+
}
238256
}
239257

240258
while (frontiers.some((f) => f.stack.length > 0)) {
259+
iteration++;
241260
for (const frontier of frontiers) {
242261
if (frontier.stack.length === 0) continue;
243262

@@ -253,6 +272,10 @@ export class EnsembleExpansion<T> {
253272
frontier.parents.set(targetId, node);
254273
frontier.stack.push(targetId);
255274

275+
if (!this.nodeDiscoveryIteration.has(targetId)) {
276+
this.nodeDiscoveryIteration.set(targetId, iteration);
277+
}
278+
256279
const edgeKey = `${node}->${targetId}`;
257280
this.sampledEdges.add(edgeKey);
258281
this.expander.addEdge(node, targetId, relationshipType);
@@ -271,6 +294,7 @@ export class EnsembleExpansion<T> {
271294
private async runDegreePriority(): Promise<Set<string>> {
272295
const frontiers: DegreePriorityFrontierState[] = [];
273296
const allVisited = new Set<string>();
297+
let iteration = 0;
274298

275299
for (const [index, seed] of this.seeds.entries()) {
276300
frontiers.push({
@@ -280,9 +304,13 @@ export class EnsembleExpansion<T> {
280304
parents: new Map(),
281305
});
282306
allVisited.add(seed);
307+
if (!this.nodeDiscoveryIteration.has(seed)) {
308+
this.nodeDiscoveryIteration.set(seed, 0);
309+
}
283310
}
284311

285312
while (frontiers.some((f) => f.frontier.length > 0)) {
313+
iteration++;
286314
for (const frontier of frontiers) {
287315
if (frontier.frontier.length === 0) continue;
288316

@@ -300,6 +328,10 @@ export class EnsembleExpansion<T> {
300328
allVisited.add(targetId);
301329
frontier.parents.set(targetId, item.nodeId);
302330

331+
if (!this.nodeDiscoveryIteration.has(targetId)) {
332+
this.nodeDiscoveryIteration.set(targetId, iteration);
333+
}
334+
303335
const degree = this.expander.getDegree(targetId);
304336
frontier.frontier.push({ nodeId: targetId, degree });
305337

src/experiments/baselines/frontier-balanced.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ export interface FrontierBalancedResult {
1818

1919
/** Statistics about the expansion */
2020
stats: FrontierBalancedStats;
21+
22+
/** Maps each node to the iteration when it was first discovered */
23+
nodeDiscoveryIteration: Map<string, number>;
2124
}
2225

2326
/**
@@ -84,6 +87,7 @@ export class FrontierBalancedExpansion<T> {
8487
private readonly paths: Array<{ fromSeed: number; toSeed: number; nodes: string[] }> = [];
8588
private readonly sampledEdges = new Set<string>();
8689
private stats: FrontierBalancedStats;
90+
private readonly nodeDiscoveryIteration = new Map<string, number>();
8791
private lastActiveFrontier = 0;
8892

8993
/**
@@ -109,6 +113,7 @@ export class FrontierBalancedExpansion<T> {
109113
visited: new Set([seed]),
110114
parents: new Map(),
111115
});
116+
this.nodeDiscoveryIteration.set(seed, 0);
112117
}
113118

114119
this.stats = {
@@ -171,6 +176,10 @@ export class FrontierBalancedExpansion<T> {
171176
activeState.visited.add(targetId);
172177
activeState.parents.set(targetId, { parent: node, edge: relationshipType });
173178

179+
if (!this.nodeDiscoveryIteration.has(targetId)) {
180+
this.nodeDiscoveryIteration.set(targetId, this.stats.iterations);
181+
}
182+
174183
// Add to queue
175184
activeState.queue.push(targetId);
176185

@@ -209,6 +218,7 @@ export class FrontierBalancedExpansion<T> {
209218
sampledEdges: this.sampledEdges,
210219
visitedPerFrontier,
211220
stats: this.stats,
221+
nodeDiscoveryIteration: this.nodeDiscoveryIteration,
212222
};
213223
}
214224

src/experiments/baselines/random-priority.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ export interface RandomPriorityResult {
1818

1919
/** Statistics about the expansion */
2020
stats: RandomPriorityStats;
21+
22+
/** Maps each node to the iteration when it was first discovered */
23+
nodeDiscoveryIteration: Map<string, number>;
2124
}
2225

2326
/**
@@ -109,6 +112,7 @@ export class RandomPriorityExpansion<T> {
109112
private readonly sampledEdges = new Set<string>();
110113
private readonly rng: SeededRandom;
111114
private stats: RandomPriorityStats;
115+
private readonly nodeDiscoveryIteration = new Map<string, number>();
112116

113117
/**
114118
* Create a new random-priority expansion.
@@ -137,6 +141,7 @@ export class RandomPriorityExpansion<T> {
137141
visited: new Set([seedNode]),
138142
parents: new Map(),
139143
});
144+
this.nodeDiscoveryIteration.set(seedNode, 0);
140145
}
141146

142147
this.stats = {
@@ -195,6 +200,10 @@ export class RandomPriorityExpansion<T> {
195200
activeState.visited.add(targetId);
196201
activeState.parents.set(targetId, { parent: node, edge: relationshipType });
197202

203+
if (!this.nodeDiscoveryIteration.has(targetId)) {
204+
this.nodeDiscoveryIteration.set(targetId, this.stats.iterations);
205+
}
206+
198207
// Add to frontier (will be randomly selected later)
199208
activeState.frontier.push(targetId);
200209

@@ -233,6 +242,7 @@ export class RandomPriorityExpansion<T> {
233242
sampledEdges: this.sampledEdges,
234243
visitedPerFrontier,
235244
stats: this.stats,
245+
nodeDiscoveryIteration: this.nodeDiscoveryIteration,
236246
};
237247
}
238248

0 commit comments

Comments
 (0)