Skip to content

Commit 2d041a2

Browse files
committed
Implementation checks now return structured errors
1 parent ebc393f commit 2d041a2

File tree

2 files changed

+88
-8
lines changed

2 files changed

+88
-8
lines changed

webgraph/src/graphs/csr_graph.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,17 +367,18 @@ mod test {
367367

368368
use super::*;
369369
#[test]
370-
fn test_csr_graph() {
370+
fn test_csr_graph() -> anyhow::Result<()> {
371371
let arcs = vec![(0, 1), (0, 2), (1, 2), (1, 3), (2, 4), (3, 4)];
372372
let g = VecGraph::from_arcs(arcs.iter().copied());
373373

374374
let csr = <CsrGraph>::from_seq_graph(&g);
375-
labels::check_impl(&csr);
375+
labels::check_impl(&csr)?;
376376
// We should be able to use eq_sorted
377377
assert!(graph::eq(&g, &csr).is_ok());
378378

379379
let _csr = CompressedCsrGraph::from_graph(&g);
380380
/*graph::eq(&g, &csr);
381381
labels::check_impl(&csr);*/
382+
Ok(())
382383
}
383384
}

webgraph/src/traits/labels.rs

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -426,25 +426,104 @@ pub trait RandomAccessLabeling: SequentialLabeling {
426426
fn outdegree(&self, node_id: usize) -> usize;
427427
}
428428

429+
/// Error types that can occur during checking the implementation of a random
430+
/// access labeling.
431+
#[derive(Error, Debug, Clone, PartialEq, Eq)]
432+
pub enum CheckImplError {
433+
/// The number of nodes returned by [`iter`](SequentialLabeling::iter)
434+
/// is different from the number returned by
435+
/// [`num_nodes`](SequentialLabeling::num_nodes).
436+
#[error("Different number of nodes: {iter} (iter) != {method} (num_nodes)")]
437+
NumNodes { iter: usize, method: usize },
438+
439+
/// The number of successors returned by [`iter`](SequentialLabeling::iter)
440+
/// is different from the number returned by
441+
/// [`num_arcs`](RandomAccessLabeling::num_arcs).
442+
#[error("Different number of nodes: {iter} (iter) != {method} (num_arcs)")]
443+
NumArcs { iter: u64, method: u64 },
444+
445+
/// The two implementations return different labels for a specific node.
446+
#[error("Different successors for node {node}: at index {index} {sequential} (sequential) != {random_access} (random access)")]
447+
Successors {
448+
node: usize,
449+
index: usize,
450+
sequential: String,
451+
random_access: String,
452+
},
453+
454+
/// The graphs have different outdegrees for a specific node.
455+
#[error("Different outdegree for node {node}: {sequential} (sequential) != {random_access} (random access)")]
456+
Outdegree {
457+
node: usize,
458+
sequential: usize,
459+
random_access: usize,
460+
},
461+
}
462+
429463
/// Checks the sequential vs. random-access implementation of a sorted
430464
/// random-access labeling.
431465
///
432466
/// Note that this method will check that the sequential and random-access
433467
/// iterators on labels of each node are identical, and that the number of
434468
/// nodes returned by the sequential iterator is the same as the number of
435469
/// nodes returned by [`num_nodes`](SequentialLabeling::num_nodes).
436-
pub fn check_impl<L: RandomAccessLabeling>(l: L) -> bool
470+
pub fn check_impl<L: RandomAccessLabeling>(l: L) -> Result<(), CheckImplError>
437471
where
438-
L::Label: PartialEq,
472+
L::Label: PartialEq + std::fmt::Debug,
439473
{
440474
let mut num_nodes = 0;
441-
for_!((node, succ) in l.iter() {
475+
let mut num_arcs: u64 = 0;
476+
for_!((node, succ_iter) in l.iter() {
442477
num_nodes += 1;
443-
if !succ.into_iter().eq(l.labels(node).into_iter()) {
444-
return false;
478+
let mut succ_iter = succ_iter.into_iter();
479+
let mut succ = l.labels(node).into_iter();
480+
let mut index = 0;
481+
loop {
482+
match (succ_iter.next(), succ.next()) {
483+
(None, None) => break,
484+
(Some(s0), Some(s1)) => {
485+
if s0 != s1 {
486+
return Err(CheckImplError::Successors {
487+
node,
488+
index,
489+
sequential: format!("{:?}", s0),
490+
random_access: format!("{:?}", s1),
491+
});
492+
}
493+
}
494+
(None, Some(_)) => {
495+
return Err(CheckImplError::Outdegree {
496+
node,
497+
sequential: index,
498+
random_access: index + 1 + succ.count(),
499+
});
500+
}
501+
(Some(_), None) => {
502+
return Err(CheckImplError::Outdegree {
503+
node,
504+
sequential: index + 1 + succ_iter.count(),
505+
random_access: index,
506+
});
507+
}
508+
}
509+
index += 1;
445510
}
511+
num_arcs += index as u64;
446512
});
447-
num_nodes == l.num_nodes()
513+
514+
if num_nodes != l.num_nodes() {
515+
Err(CheckImplError::NumNodes {
516+
method: l.num_nodes(),
517+
iter: num_nodes,
518+
})
519+
} else if num_arcs != l.num_arcs() {
520+
Err(CheckImplError::NumArcs {
521+
method: l.num_arcs(),
522+
iter: num_arcs,
523+
})
524+
} else {
525+
Ok(())
526+
}
448527
}
449528

450529
/// A struct used to make it easy to implement sequential access

0 commit comments

Comments
 (0)