Skip to content

Extract a generic depth-first graph traversal algorithm#1144

Open
tslil-topos wants to merge 1 commit intomainfrom
tslil/push-pkrmmqpvxsun
Open

Extract a generic depth-first graph traversal algorithm#1144
tslil-topos wants to merge 1 commit intomainfrom
tslil/push-pkrmmqpvxsun

Conversation

@tslil-topos
Copy link
Collaborator

@tslil-topos tslil-topos commented Mar 18, 2026

What

catlog contains a toposort function for graphs which uses mutli-start DFS (twice), this DFS was previously inlined. This change extracts the DFS procedure for FinGraph into a hopefully more re-usable method depth_first_traversal for the library.

Closes #719

Notes

  • introduces an enum TraversalDirection because Graph is the category-theoretic graph (we need to know whether we're going into or out of a node)
  • adds some test cases for depth_first_traversal
  • there is potentially a performance/memory difference between the old code and the new code because we now create new Vecs for the stack on every invocation of depth_first_traversal. It's difficult for me to reason about this without understanding the compiler optimisation more
  • 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

@tslil-topos tslil-topos self-assigned this Mar 18, 2026
@tslil-topos tslil-topos requested review from epatters and kasbah March 18, 2026 15:33
@tslil-topos tslil-topos force-pushed the tslil/push-pkrmmqpvxsun branch from 5c22d0e to 1c4ecfb Compare March 18, 2026 15:35
@epatters epatters added the core Rust core for categorical logic and general computation label Mar 19, 2026
@epatters epatters changed the title refactor: graph_algorithms extract a generic depth-first traversal algorithm Extract a generic depth-first graph traversal algorithm Mar 19, 2026
Copy link
Member

@epatters epatters left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Rust core for categorical logic and general computation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dedicated depth-first search algorithm for graphs

2 participants