Summary
UndirectedAdaptor<G> removes edge direction by chaining the incoming and outgoing traversals of the wrapped directed graph.
For self-loops, the same underlying edge is both incoming and outgoing, so the adaptor yields it twice.
This causes neighbors(...) and edges(...) to double-count a single self-loop edge.
Reproducer
use petgraph::{prelude::*, visit::{IntoEdges, IntoNeighbors, UndirectedAdaptor}};
fn main() {
let mut g = DiGraph::<(), ()>::new();
let a = g.add_node(());
g.add_edge(a, a, ());
let ug = UndirectedAdaptor(&g);
let neighbors = ug.neighbors(a).collect::<Vec<_>>();
let edges = ug.edges(a).collect::<Vec<_>>();
println!("neighbors = {:?}", neighbors);
println!("edges.len() = {}", edges.len());
}
Observed behavior:
neighbors(a) contains a twice,
edges(a) yields the same self-loop twice.
Possible root cause
The current implementation removes direction by concatenating:
- incoming neighbors / edges, and
- outgoing neighbors / edges,
but does not deduplicate the overlap case where both traversals refer to the same underlying self-loop.
That makes the adaptor overcount incident structure for valid inputs.
Suggested fix
Special-case self-loops when combining incoming and outgoing traversals so that the same underlying edge is yielded only once.
Summary
UndirectedAdaptor<G>removes edge direction by chaining the incoming and outgoing traversals of the wrapped directed graph.For self-loops, the same underlying edge is both incoming and outgoing, so the adaptor yields it twice.
This causes
neighbors(...)andedges(...)to double-count a single self-loop edge.Reproducer
Observed behavior:
neighbors(a)containsatwice,edges(a)yields the same self-loop twice.Possible root cause
The current implementation removes direction by concatenating:
but does not deduplicate the overlap case where both traversals refer to the same underlying self-loop.
That makes the adaptor overcount incident structure for valid inputs.
Suggested fix
Special-case self-loops when combining incoming and outgoing traversals so that the same underlying edge is yielded only once.