perf(cliques): pivot in Bron-Kerbosch instead of branching on every candidate - #803
Merged
Merged
Conversation
…andidate The implementation branches on every remaining candidate in turn, so a clique of k vertices is rediscovered through many of the orders its vertices can be reached in, and each of those branches allocates three hash sets on the way down. Choose a pivot from the candidates and the excluded set, as Bron and Kerbosch describe: a maximal clique either leaves the pivot out or contains one of its neighbours, so the candidates adjacent to the pivot need not be branched on here and are reached through a deeper call instead. The same cliques come out, in a different order. The three working sets become vectors. They are only ever filtered and scanned, never looked up by key, and they shrink quickly with depth, so hashing every vertex at every level costs more than the linear scans it saves. One subtlety: `connected` is a caller-supplied predicate and is free to report a vertex as connected to itself, which the previous code was indifferent to. Pivoting is not: a pivot excluding itself from its own branch set could drop a candidate that is never branched on, and the enumeration would stop early. A vertex is therefore never treated as its own neighbour. Both conventions are covered by the tests. Checked exhaustively against a brute-force enumeration of every subset over 60 random graphs. About 88% off a 130 vertex graph with edge probability 0.5.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #801.
Adds the pivot to Bron-Kerbosch. A maximal clique either leaves the pivot out or contains one of its neighbours, so the candidates adjacent to the pivot are not branched on at this level and are reached through a deeper call instead. The same cliques come out, in a different order.
The three working sets also become vectors. They are only ever filtered and scanned, never looked up by key, and they shrink quickly with depth, so hashing every vertex at every level costs more than the linear scans it saves. That removes the per-branch clone of the candidate set and two hash set allocations per candidate.
Worth reviewing
connectedis caller-supplied and may report a vertex as connected to itself. The previous code was indifferent to that; pivoting is not, because a pivot excluding itself from its own branch set could drop a candidate that is then never branched on, ending the enumeration early. A vertex is therefore never treated as its own neighbour, which keeps both conventions working. This is the behavioural assumption raised in #801.The order in which cliques are emitted changes. It was not specified before either — it followed hash iteration order — and the vertices are now deduplicated in the order given, so it is at least deterministic.
Checking
Verified against a brute-force enumeration of all 2^n subsets over 60 random graphs, with
connectedreflexive on half the rounds and irreflexive on the other half. The two existing tests are unchanged.About 88% off a 130 vertex graph with edge probability 0.5, measured against this branch's parent.
All timings measured on Windows 11, Intel Core Ultra 7 265K, 64 GB RAM.