RFC: Should BGL visitors be allowed to participate in algorithm control flow? #494
Becheler
started this conversation in
API & Usability
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
This RFC discusses a starter proposal to contrast
Motivation
Algorithms rely on visitors to inspect the internal algorithm state. Users can override visitor methods to log progress, inspect statistics etc. But visitors today are strict observers: they take immutable parameters in, their hook methods return
void.For example, workshop participants asking to stop Dijkstra early hit the same wall: the only supported mechanism is to throw an exception from inside the visitor and catch it outside the algorithm call. The pattern works, is documented, but it is:
-fno-exceptionsbuildsAlso, iterative optimization algorithms rely on more complex state(s) that dictate their termination criterion. Numerous variations of such algorithms in the literature are actually variations on the trade-off quality versus runtime, that is degrading output quality by lowering iterations for better runtime. Currently there is no clear design decision in BGL on how to generalize such criteria.
This RFC asks whether BGL visitors should be allowed to influence algorithm flow more directly for example by returning a value from their hooks that the algorithm acts on.
Current state
Visitors today are strict observers. Hook methods return
void. To terminate early, users currently write:This is documented but actively rejected by users in workshop settings and in #466 .
Proposed convention
Allow visitor hooks to return
boolinstead ofvoid. A return offalsesignals "do not continue";truemeans continue normally. Existing void-returning hooks remain valid and are interpreted astrue.The exact meaning of "do not continue" (terminate the algorithm immediately, terminate at the next safe point, skip the current iteration only) is documented per-hook per-algorithm. For Dijkstra's
examine_vertex, the natural meaning is "the algorithm exits as soon as the current vertex's processing completes."This is additive, not a redesign. The existing void-returning visitor concept stays exactly as it is.
Backwards compatibility
voidmust continue to work without modification .constexpr if: See on Compiler Explorer: https://godbolt.org/z/zrEzx8sWrAlgorithms where flow control from visitors would be useful
Three concrete cases. Each surfaces a different shape of the same underlying need.
Early termination with Dijkstra or BFS
Single-source shortest path is overkill when the user only wants a single-pair path: they need to stop as soon as the target is finalized. Today the only mechanism is to throw from
examine_vertexand catch outside. This is costly on large graphs.With bool-returning visitors:
Termination criterion with Louvain
Modularity-optimizing community detection alternates two phases: local moving and network reduction. Each admits several natural stopping conditions:
Unlike PageRank's single
Donepredicate, these are heterogeneous, often combined, and impractical to expose as separate callables: the API would grow a parameter per criterion. The current Louvain implementation hard-codes some criteria, gives a fixed-function variant taking two phase-specific thresholds, but this can't match the flexibility found in NetworkX, igraph, and other reference implementations.A bool-returning per-iteration hook collapses the policy into one user-pluggable extension point:
Generic Louvain also generalizes the quality function beyond modularity. That's currently a separate policy parameter, which would have redundant responsibility with a value-returning visitor (a natural extension beyond this RFC's bool case).
Termination criterion with Personalized PageRank
Power-method iteration converges (or diverges) based on a residual the caller wants to control. Today this lives in a separate
Donepredicate (a mechanism that exists precisely because visitors can't influence flow). If visitors return bool, the convergence check becomes a natural visitor hook, removing the special-case parameter and unifying stopping mechanisms across traversal and iterative algorithms:The same visitor pattern extends to value-returning hooks (see Question 1 below). The concept sketched here for PersonalizedPageRank #493 illustrates that design where hooks return values used by the algorithm, for example a per-iteration rescale factor:
And the algorithm could be coded against this concept (pseudo-code below, not literal C++):
Related
Rust's standard library formalizes this pattern as the
ControlFlow<B, C>enum. C++ has no equivalent in the standard library, it would look like:The idea would be to allow users to write things like:
Or things like:
Worth knowing about for Question 1 below: if return-type scope broadens beyond
bool, aControlFlow-style sum type sits between a flat enum and arbitrary types in the design space.Questions
Return-type scope. What may visitor hooks return?
boolonly : simple + covers Dijkstra-style early termination but may not cover iterative convergence checks (PPR, Louvain).residual < tolconvergence style directly, but conflates termination criterion with metrics.ControlFlowshape:Continue/Stop/Diverged) : explicit, library-wide vocabulary, most discoverable.Hook scope. Which existing hooks may return non-void? Dijkstra's
examine_vertexis canonical. Enumerate per algorithm, or allow any hook?Semantics of "stop." "Terminate at next safe point" as one library-wide convention, or specified per hook?
Relationship to
Donepredicates. Does a flow-controlling visitor subsume the existingDonecallable inpage_rank-style algorithms (Donebecomes redundant), or do they stay orthogonal?Migration of throw-to-terminate. Deprecate, keep both indefinitely, or just recommend the new way going forward?
Beta Was this translation helpful? Give feedback.
All reactions