Skip to content

docs(yen): say what happens when two nodes are joined by more than one edge - #832

Open
tachsin wants to merge 1 commit into
evenfurther:mainfrom
tachsin:docs/yen-parallel-edges
Open

tachsin wants to merge 1 commit into
evenfurther:mainfrom
tachsin:docs/yen-parallel-edges

Conversation

@tachsin

@tachsin tachsin commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Closes #516.

You suggested there either describing the kind of graph that can be represented, or finding a backward compatible way to return edge ids. This does the first, and shows that the second is already available to the caller without a change here.

What is documented

A path is a sequence of nodes, so two routes differing only in which of several edges between the same pair they take are the same path. Where more than one edge joins two nodes the cheaper is taken, and the others are never reported — so on @HellOwhatAs's graph there is one path from A to B, and asking for k = 2 returns one:

let two_roads = |c: &char| match c {
    'A' => vec![('B', 3), ('B', 2)],
    _ => vec![],
};
assert_eq!(yen(&'A', two_roads, |c| *c == 'B', 2), vec![(vec!['A', 'B'], 2)]);

That behaviour is consistent, and it is what dijkstra does too, but nothing said so and it is not what someone counting routes expects.

And the way round it

When the individual edges matter, putting the edge a node was reached by into the node makes the routes distinct, and both are then found:

let roads = [('A', 'B', 3), ('A', 'B', 2)];
let successors = |&(town, _): &(char, Option<usize>)| { /* ... */ };

let routes = yen(&('A', None), successors, |&(town, _)| town == 'B', 2);
assert_eq!(routes[0], (vec![('A', None), ('B', Some(1))], 2));
assert_eq!(routes[1], (vec![('A', None), ('B', Some(0))], 3));

This needs nothing from the crate — it is the caller's choice of node type. Worth writing down, because the shape of the answer is not obvious from the signature, and it gives the reporter what they were after.

Tests

The graph from the report, the same situation in the middle of a longer path, and the suggested modelling. 295 tests pass, clippy and rustfmt clean.

Documentation only; no code changes.

I have not touched the other option you raised — returning ids alongside edges, with (start, end) as the default. That is a larger API question and this does not foreclose it; if you would rather have that, say so and I will close this.

…e edge

A path here is a sequence of nodes, so two routes that differ only in which of
several edges between the same pair they take are the same path. `yen` reports
the cheaper edge and never the others, and so returns fewer paths than asked for
on such a graph. That is consistent, but it is not what someone counting routes
expects, and nothing said it.

Document it, and document the way round it: carrying the edge a node was reached
by inside the node makes the routes distinct, and both are then found. That
needs nothing from this crate, and is worth writing down because the shape of
the answer is not obvious from the signature.

Tests cover the graph from the report and the same situation in the middle of a
longer path, along with the suggested modelling.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Yen's Algorithm cannot handle multiple edges

1 participant