Skip to content

Commit 02b17c8

Browse files
committed
Interim, adding parent e distance to BFSOrder
1 parent aced31b commit 02b17c8

File tree

3 files changed

+70
-23
lines changed

3 files changed

+70
-23
lines changed

algo/src/visits/breadth_first/seq.rs

Lines changed: 66 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ impl<'a, G: RandomAccessGraph> Sequential<EventPred> for Seq<'a, G> {
257257
}
258258

259259
impl<'a, 'b, G: RandomAccessGraph> IntoIterator for &'a mut Seq<'b, G> {
260-
type Item = usize;
260+
type Item = IterItem;
261261
type IntoIter = BfsOrder<'a, 'b, G>;
262262

263263
fn into_iter(self) -> Self::IntoIter {
@@ -273,6 +273,12 @@ pub struct BfsOrder<'a, 'b, G: RandomAccessGraph> {
273273
/// This allows initializing the BFS from all orphan nodes without reading
274274
/// the reverse graph.
275275
start: usize,
276+
/// The current node being visited.
277+
current_node: usize,
278+
/// The current distance from the root.
279+
distance: usize,
280+
/// The successors of the current node, this is done to be able to return
281+
/// also the predecessor.
276282
succ: <<G as RandomAccessLabeling>::Labels<'a> as IntoIterator>::IntoIter,
277283
/// Number of visited nodes, used to compute the length of the iterator.
278284
visited_nodes: usize,
@@ -285,38 +291,77 @@ impl<'a, 'b, G: RandomAccessGraph> BfsOrder<'a, 'b, G> {
285291
BfsOrder {
286292
visit,
287293
start: 0,
294+
current_node: 0,
295+
distance: 0,
288296
succ,
289297
visited_nodes: 0,
290298
}
291299
}
292300
}
293301

302+
pub struct IterItem {
303+
pub parent: usize,
304+
pub node: usize,
305+
pub distance: usize,
306+
}
307+
294308
impl<'a, 'b, G: RandomAccessGraph> Iterator for BfsOrder<'a, 'b, G> {
295-
type Item = usize;
309+
type Item = IterItem;
296310

297-
fn next(&mut self) -> Option<usize> {
298-
let current_node = match self.visit.queue.pop_front() {
299-
Some(Some(node)) => node.into(),
300-
_ => {
301-
while self.visit.visited[self.start] {
302-
self.start += 1;
303-
if self.start >= self.visit.graph.num_nodes() {
304-
return None;
305-
}
311+
fn next(&mut self) -> Option<Self::Item> {
312+
loop {
313+
// fast path, if the successors iterator is not exhausted, we can just return the next node
314+
if let Some(succ) = self.succ.next() {
315+
if self.visit.visited[succ] {
316+
continue; // skip already visited nodes
306317
}
307-
self.visit.visited.set(self.start, true);
308-
self.start
309-
}
310-
};
311-
312-
for succ in self.visit.graph.successors(current_node) {
313-
if !self.visit.visited[succ] {
314-
self.visit.queue.push_back(NonMaxUsize::new(succ));
318+
// if it's a new node, we visit it and add it to the queue
319+
// of nodes whose successors we will visit
320+
self.visit.queue.push_back(Some(
321+
NonMaxUsize::new(succ).expect("node index should never be usize::MAX"),
322+
));
315323
self.visit.visited.set(succ as _, true);
324+
self.visited_nodes += 1;
325+
return Some(IterItem {
326+
parent: self.current_node,
327+
node: succ,
328+
distance: self.distance,
329+
});
316330
}
331+
332+
// the successors are exhausted, so we need to move to the next node
333+
self.current_node = match self.visit.queue.pop_front() {
334+
// if we have a node, we can continue visiting its successors
335+
Some(Some(node)) => node.into(),
336+
// new level separator, so we increment the distance
337+
Some(None) => {
338+
self.distance += 1;
339+
self.visit.queue.push_back(None);
340+
// TODO: this assumes the iter are fuse
341+
continue;
342+
}
343+
// if the queue is empty, we need to find the next unvisited node
344+
None => {
345+
while self.visit.visited[self.start] {
346+
self.start += 1;
347+
if self.start >= self.visit.graph.num_nodes() {
348+
return None;
349+
}
350+
}
351+
self.visit.visited.set(self.start, true);
352+
self.distance = 0; // new visits, new distance
353+
self.start
354+
}
355+
};
356+
// reset the successors iterator for the new current node
357+
self.succ = self.visit.graph.successors(self.current_node).into_iter();
358+
self.visited_nodes += 1;
359+
return Some(IterItem {
360+
parent: self.current_node,
361+
node: self.current_node,
362+
distance: self.distance,
363+
});
317364
}
318-
self.visited_nodes += 1;
319-
Some(current_node)
320365
}
321366
}
322367

algo/tests/test_breadth_first.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,7 @@ fn test_start() -> Result<()> {
462462

463463
let order: Vec<_> = webgraph_algo::visits::breadth_first::Seq::new(&graph)
464464
.into_iter()
465+
.map(|x| x.node)
465466
.collect();
466467

467468
assert_eq!(order, vec![0, 2, 3, 1, 5, 4]);
@@ -486,6 +487,7 @@ fn test_start_orphan() -> Result<()> {
486487

487488
let order: Vec<_> = webgraph_algo::visits::breadth_first::Seq::new(&graph)
488489
.into_iter()
490+
.map(|x| x.node)
489491
.collect();
490492

491493
assert_eq!(order, vec![0, 4, 2, 3, 1, 5]);

cli/src/perm/bfs.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ where
6969
let mut perm = vec![0; graph.num_nodes()];
7070
pl.start("Computing BFS permutation...");
7171
let mut visit = webgraph_algo::visits::breadth_first::Seq::new(&graph);
72-
for (i, node_id) in visit.into_iter().enumerate() {
73-
perm[node_id] = i;
72+
for (i, event) in visit.into_iter().enumerate() {
73+
perm[event.node] = i;
7474
pl.light_update();
7575
}
7676
pl.done();

0 commit comments

Comments
 (0)