Extract a generic depth-first graph traversal algorithm#1144
Extract a generic depth-first graph traversal algorithm#1144tslil-topos wants to merge 1 commit intomainfrom
Conversation
…-first traversal algorithm
5c22d0e to
1c4ecfb
Compare
epatters
left a comment
There was a problem hiding this comment.
Thanks tslil, this is nice! A few comments below.
honestly i'm not sure that this refactor brings immediate value, in fact looking around at the algorithms it's quite possible that toposort could have been re-written in terms of spec_order
I'm inclined to agree that this is a preemptive refactor, which can run the risk of being premature, but I've got a feeling that one way or another we're going end up writing more graph algorithms that require a DFS. So hopefully it will pay off.
| }; | ||
| for succ in successors { | ||
| if succ == nx { | ||
| return Err(format!("Self loop at node {:#?}", nx)); |
There was a problem hiding this comment.
Not a graph algorithms person myself, but I feel like the more common/expected behavior of a DFS algorithm is that when it encounters a cycle, including as a special case a self-loop, it should detect it and avoid looping infinitely and otherwise proceed without error, instead of returning an Err as here.
(I suspect this is just leftover logic from the toposort alg from which it was extracted.)
| mut on_complete: Option<F1>, | ||
| mut on_discover: Option<F2>, | ||
| ) -> Result<(), String> | ||
| where |
There was a problem hiding this comment.
It's always ungainly in Rust, which has only positional arguments, to have so arguments to a function or method. In this case, what I'd probably do is split up the internal state persisted across invocations and the arguments for a single run, something like:
struct DFS<V> {
discovered: HashSet<V>,
traversal_direction: TraversalDirection
}
impl<V> DFS<V> {
pub fn traverse(graph, start_vertex, ...)
}If you're not familiar with it, you might also check out the builder pattern, which is an idiomatic way to work around Rust's lack of keyword arguments.
What
catlogcontains atoposortfunction for graphs which uses mutli-start DFS (twice), this DFS was previously inlined. This change extracts the DFS procedure forFinGraphinto a hopefully more re-usable methoddepth_first_traversalfor the library.Closes #719
Notes
TraversalDirectionbecauseGraphis the category-theoretic graph (we need to know whether we're going into or out of a node)depth_first_traversalVecs for the stack on every invocation ofdepth_first_traversal. It's difficult for me to reason about this without understanding the compiler optimisation moretoposortcould have been re-written in terms ofspec_order