Optimize max flow with Dinic's blocking flow algorithm - #40
Open
shakayami wants to merge 1 commit into
Open
Conversation
flow()'s inner dfs returned after a single augmenting path, so the outer
loop rebuilt the level graph for every unit of flow. On unit-capacity
bipartite matching that is O(V*E*flow) instead of Dinic's O(E*sqrt(V)):
the Library Checker bipartitematching workload took 163s at 20% of the
judge's size.
Rewrite dfs to push a whole blocking flow per call, matching ACL's
recursive version: it keeps walking t -> s with the current-arc
optimisation, and after each augmentation resumes from the first arc it
saturated instead of restarting at t. Nodes with no usable predecessor
are still marked level = n so no later path retries them.
bipartitematching, same instance and same answer (matching = 15660):
20% of judge size 162.69s -> 0.32s
100% of judge size --- -> 2.91s
Checked against a separate Edmonds-Karp implementation on 33,000 random
graphs (self-loops, parallel edges, zero capacities, flow_limit cutoffs,
split flow calls), verifying the flow value, per-edge conservation and
that min_cut still matches the flow value.
Full size now fits the benchmark's one-to-four-second budget, so
bipartitematching goes back to COST_FACTOR 5.0 (judge maximum at the
default scale) and the sizing notes that described the old behaviour are
updated.
📊 Performance Benchmark ResultsWorkloads ran at ⏱️ Library Checker workloads (best of N, lower is better)
🧪 API coverage workloads (not judge-calibrated)
📏 Workload sizes
💾 Peak memory (lower is better)
Only the workloads whose module the change touches are measured; a scheduled sweep covers the rest. Each workload mirrors the linked Library Checker problem: same query mix, same operators, same value ranges as that problem's |
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.
Summary
Refactor the
dfsfunction in the max flow implementation to use Dinic's blocking flow algorithm, which processes an entire level graph in one phase rather than finding augmenting paths one at a time. This improves the time complexity from O(V²E) to O(E√V) for unit-capacity graphs.Key Changes
maxflow.py:
dfsto compute a complete blocking flow for the current level graph in a single call, rather than returning after finding one augmenting pathbfs()once per phase (not once per augmenting path), withIterarray reset per phasebenchmarks/workloads.py & benchmarks/README.md:
bipartitematchingcost factor from 0.15 to 5.0, reflecting the improved algorithm performancerange_affine_range_sumandexp/log_of_formal_power_seriesrun at reduced scale due to CPython limitationsImplementation Details
The new algorithm maintains a level graph via BFS and uses DFS to find augmenting paths within that level. Key optimizations include:
https://claude.ai/code/session_019maXFnaUfCveCBZFxmRmo7