@@ -19,6 +19,13 @@ export interface DegreePrioritisedExpansionResult {
1919
2020 /** Statistics about the expansion */
2121 stats : ExpansionStats ;
22+
23+ /**
24+ * Maps each sampled node to the iteration when it was first discovered.
25+ * Used for computing coverage efficiency metrics (first-discovery iteration,
26+ * budget checkpoint coverage, area-under-curve).
27+ */
28+ nodeDiscoveryIteration : Map < string , number > ;
2229}
2330
2431/**
@@ -118,6 +125,8 @@ export class DegreePrioritisedExpansion<T> {
118125 private readonly paths : Array < { fromSeed : number ; toSeed : number ; nodes : string [ ] } > = [ ] ;
119126 private readonly sampledEdges = new Set < string > ( ) ;
120127 private stats : ExpansionStats ;
128+ /** Tracks when each node was first discovered (iteration number) */
129+ private readonly nodeDiscoveryIteration = new Map < string , number > ( ) ;
121130
122131 /** Track which frontier owns each node for O(1) intersection checking */
123132 private readonly nodeToFrontierIndex = new Map < string , number > ( ) ;
@@ -155,6 +164,9 @@ export class DegreePrioritisedExpansion<T> {
155164
156165 // Track which frontier owns this seed
157166 this . nodeToFrontierIndex . set ( seed , index ) ;
167+
168+ // Seeds are discovered at iteration 0
169+ this . nodeDiscoveryIteration . set ( seed , 0 ) ;
158170 }
159171
160172 this . stats = {
@@ -208,6 +220,11 @@ export class DegreePrioritisedExpansion<T> {
208220 activeState . visited . add ( targetId ) ;
209221 activeState . parents . set ( targetId , { parent : node , edge : relationshipType } ) ;
210222
223+ // Track first discovery iteration (only if not already discovered by another frontier)
224+ if ( ! this . nodeDiscoveryIteration . has ( targetId ) ) {
225+ this . nodeDiscoveryIteration . set ( targetId , this . stats . iterations ) ;
226+ }
227+
211228 // Check for intersection using O(1) lookup BEFORE claiming ownership
212229 // If another frontier already visited this node, we have a path
213230 const otherFrontierIndex = this . nodeToFrontierIndex . get ( targetId ) ;
@@ -255,6 +272,7 @@ export class DegreePrioritisedExpansion<T> {
255272 sampledEdges : this . sampledEdges ,
256273 visitedPerFrontier,
257274 stats : this . stats ,
275+ nodeDiscoveryIteration : this . nodeDiscoveryIteration ,
258276 } ;
259277 }
260278
@@ -339,12 +357,24 @@ export class DegreePrioritisedExpansion<T> {
339357 if ( pathFromB . length > 0 && pathFromB . at ( - 1 ) !== seedB && // Path from B should end at seed B, or be empty if meeting node is seed B
340358 meetingNode !== seedB ) return null ;
341359
342- return [ ...pathFromA , ...pathFromB ] ;
360+ const fullPath = [ ...pathFromA , ...pathFromB ] ;
361+
362+ // Validate simple path (no repeated nodes)
363+ // This can happen when both frontiers explore through the same intermediate
364+ // node before meeting at a further node. In such cases, a shorter path
365+ // connecting the seeds should already exist.
366+ const nodeSet = new Set ( fullPath ) ;
367+ if ( nodeSet . size !== fullPath . length ) {
368+ return null ;
369+ }
370+
371+ return fullPath ;
343372 }
344373
345374 /**
346375 * Create a unique signature for a path to enable O(1) deduplication.
347- * Signature is bidirectional (A-B same as B-A).
376+ * Signature is bidirectional (A-B same as B-A) and includes the actual
377+ * node sequence to distinguish different paths with the same length.
348378 * @param fromSeed
349379 * @param toSeed
350380 * @param nodes
@@ -353,7 +383,9 @@ export class DegreePrioritisedExpansion<T> {
353383 private createPathSignature ( fromSeed : number , toSeed : number , nodes : string [ ] ) : string {
354384 // Sort seed indices to make signature bidirectional
355385 const [ a , b ] = fromSeed < toSeed ? [ fromSeed , toSeed ] : [ toSeed , fromSeed ] ;
356- return `${ a } -${ b } -${ nodes . length } ` ;
386+ // Include actual node sequence, normalized to consistent direction
387+ const normalizedNodes = a === fromSeed ? nodes . join ( "-" ) : [ ...nodes ] . reverse ( ) . join ( "-" ) ;
388+ return `${ a } -${ b } -${ normalizedNodes } ` ;
357389 }
358390
359391 /**
0 commit comments