Skip to content

Commit 794b92d

Browse files
committed
feat(algorithms): use calculatePriority in traversal algorithms
Updates bidirectional BFS and degree-prioritised expansion to use the new calculatePriority method instead of direct degree computation, enabling thesis-aligned priority weighting.
1 parent a820a46 commit 794b92d

File tree

2 files changed

+47
-15
lines changed

2 files changed

+47
-15
lines changed

src/algorithms/traversal/bidirectional-bfs.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,19 @@ interface BFSState {
4545
/**
4646
* Generic bidirectional breadth-first search with degree-based node prioritization.
4747
*
48+
* **NOTE: This is an earlier design with parameterised termination.**
49+
* For the parameter-free version that aligns with the thesis theoretical framework,
50+
* see DegreePrioritisedExpansion (N≥1 seeds, frontier exhaustion only).
51+
*
52+
* **Design Relationship**:
53+
* - BidirectionalBFS: Earlier design, N=2 only, has termination parameters (targetPaths, maxIterations)
54+
* - DegreePrioritisedExpansion: Refined design, N≥1 with identical code path, parameter-free
55+
*
56+
* This implementation is suitable for production use cases where:
57+
* - Predictable resource consumption is required
58+
* - Finding *a* path quickly is more important than exhaustive sampling
59+
* - Service-level objectives constrain iteration count
60+
*
4861
* Searches for paths between two seed nodes by expanding frontiers from both directions.
4962
* Uses priority queue to process low-degree nodes first, naturally prioritizing specific
5063
* connections over generic ones.
@@ -71,6 +84,8 @@ interface BFSState {
7184
* const result = await bfs.search();
7285
* console.log(`Found ${result.paths.length} paths in ${result.iterations} iterations`);
7386
* ```
87+
*
88+
* @see DegreePrioritisedExpansion - Parameter-free version for N≥1 seeds
7489
*/
7590
export class BidirectionalBFS<T> {
7691
private foundPaths: string[][] = [];
@@ -212,9 +227,9 @@ export class BidirectionalBFS<T> {
212227
state.visited.add(targetId);
213228
state.parents.set(targetId, { parent: nodeId, edge: relationshipType });
214229

215-
// Add to frontier with degree as priority
216-
const degree = this.expander.getDegree(targetId);
217-
state.frontier.push(targetId, degree);
230+
// Add to frontier with thesis priority as priority
231+
const priority = this.expander.calculatePriority(targetId);
232+
state.frontier.push(targetId, priority);
218233
}
219234
}
220235
}
@@ -307,15 +322,15 @@ export class BidirectionalBFS<T> {
307322
for (const nodeId of path) {
308323
// Add to frontier A if not already visited
309324
if (!this.stateA.visited.has(nodeId)) {
310-
const degree = this.expander.getDegree(nodeId);
311-
this.stateA.frontier.push(nodeId, degree);
325+
const priority = this.expander.calculatePriority(nodeId);
326+
this.stateA.frontier.push(nodeId, priority);
312327
this.stateA.visited.add(nodeId);
313328
}
314329

315330
// Add to frontier B if not already visited
316331
if (!this.stateB.visited.has(nodeId)) {
317-
const degree = this.expander.getDegree(nodeId);
318-
this.stateB.frontier.push(nodeId, degree);
332+
const priority = this.expander.calculatePriority(nodeId);
333+
this.stateB.frontier.push(nodeId, priority);
319334
this.stateB.visited.add(nodeId);
320335
}
321336
}

src/algorithms/traversal/degree-prioritised-expansion.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,22 @@ interface FrontierState {
5959
/**
6060
* Degree-Prioritised Bidirectional Expansion
6161
*
62+
* **Thesis Alignment**: This is the primary implementation of the seed-bounded
63+
* sampling algorithm described in Chapter 4 of the thesis. It embodies the
64+
* parameter-free design with implicit termination via frontier exhaustion.
65+
*
66+
* **Design Relationship**:
67+
* - BidirectionalBFS: Earlier design, N=2 only, has termination parameters (targetPaths, maxIterations)
68+
* - DegreePrioritisedExpansion: Refined design (this file), N≥1 with identical code path, parameter-free
69+
*
70+
* Use this implementation when:
71+
* - Representative sampling of graph structure is required
72+
* - Evaluation dataset construction demands bias-free samples
73+
* - N≥1 seed nodes need to be supported
74+
* - Termination should be determined by graph structure, not arbitrary limits
75+
*
76+
* For production pathfinding with resource constraints, consider BidirectionalBFS instead.
77+
*
6278
* A unified algorithm for representative graph sampling between N ≥ 1 seed nodes.
6379
* Uses node degree as priority (low degree = high priority) to explore peripheral
6480
* routes before hub-dominated paths.
@@ -94,6 +110,8 @@ interface FrontierState {
94110
* console.log(`Found ${result.paths.length} paths`);
95111
* console.log(`Sampled ${result.sampledNodes.size} nodes`);
96112
* ```
113+
*
114+
* @see BidirectionalBFS - Parameterised version for N=2 with resource constraints
97115
*/
98116
export class DegreePrioritisedExpansion<T> {
99117
private readonly frontiers: FrontierState[] = [];
@@ -119,8 +137,8 @@ export class DegreePrioritisedExpansion<T> {
119137
// Initialize N frontiers, one per seed
120138
for (const [index, seed] of seeds.entries()) {
121139
const frontier = new PriorityQueue<string>();
122-
const degree = expander.getDegree(seed);
123-
frontier.push(seed, degree);
140+
const priority = expander.calculatePriority(seed);
141+
frontier.push(seed, priority);
124142

125143
this.frontiers.push({
126144
index: index,
@@ -180,9 +198,9 @@ export class DegreePrioritisedExpansion<T> {
180198
activeState.visited.add(targetId);
181199
activeState.parents.set(targetId, { parent: node, edge: relationshipType });
182200

183-
// Add to frontier with degree as priority
184-
const degree = this.expander.getDegree(targetId);
185-
activeState.frontier.push(targetId, degree);
201+
// Add to frontier with thesis priority as priority
202+
const priority = this.expander.calculatePriority(targetId);
203+
activeState.frontier.push(targetId, priority);
186204

187205
// Check intersection with ALL other frontiers (N ≥ 2 only)
188206
// This loop naturally handles all N without branching:
@@ -262,10 +280,9 @@ export class DegreePrioritisedExpansion<T> {
262280
*/
263281
private peekPriority(queue: PriorityQueue<string>): number {
264282
// The priority queue is a min-heap, so the front item has minimum priority.
265-
// We need to access the heap internals for peek - for now use getDegree on first item.
266-
// This is safe because items are added with their degree as priority.
283+
// We use calculatePriority to ensure consistent prioritization across the algorithm.
267284
for (const item of queue) {
268-
return this.expander.getDegree(item);
285+
return this.expander.calculatePriority(item);
269286
}
270287
return Infinity;
271288
}

0 commit comments

Comments
 (0)