[mcmf] Pack edges inline into a CSR adjacency - #75
[mcmf] Pack edges inline into a CSR adjacency#75devin-ai-integration[bot] wants to merge 2 commits into
Conversation
Replaces vector<vector<int>> adj + shared AoS edge array with a flat CSR layout whose edge records (dest, rev, cap[, cost]) are stored inline per node, so BFS/DFS/Dijkstra scan contiguous memory with one fewer indirection. Benchmarks: ~1.3x on random/bipartite graphs, ~1.5x on grid graphs for Dinic; ~1.3x for MCMF_SSPA assignment. add_edge now returns an edge id and flow(id) reads back per-edge flow. Adds Library Checker verification for bipartitematching and assignment. Co-Authored-By: Andrew He <he.andrew.mail@gmail.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
There was a problem hiding this comment.
🟡 Min-cost flow solver crashes when its layered-augmentation routine is used on its own
The min-cost Dinic solver's augmentation routine never prepares its internal edge storage (missing build() call at src/mcmf.hpp:266-267), unlike the plain max-flow solver which does, so calling it directly reads memory that was never set up and the program crashes or misbehaves.
Impact: Users who invoke the min-cost solver's per-phase routines directly (rather than the combined solve entry points) hit undefined behavior instead of getting a correct answer.
Missing lazy build in MCMF_Dinic::dinic while Dinic::dinic has it
After this PR the adjacency is packed lazily by build(). Dinic::dinic calls build() at src/mcmf.hpp:397, and MCMF_Dinic::all_flows/max_flow call it at src/mcmf.hpp:294 and src/mcmf.hpp:306, but MCMF_Dinic::dinic (src/mcmf.hpp:266) does not. If it (or dijkstra) is invoked before any solve, adj_start is empty, so adj_start[cur] / adj_start[cur+1] in the BFS loop (src/mcmf.hpp:276) index an empty vector and it.assign(adj_start.begin(), adj_start.end()-1) (src/mcmf.hpp:286) forms an out-of-range iterator. Previously these methods worked standalone since the adjacency was materialized in add_edge. The same gap applies to MCMF_SSPA::dijkstra/path and to flow(), which is const and therefore cannot build either.
(Refers to lines 266-267)
Was this helpful? React with 👍 or 👎 to provide feedback.
GCC Code Coverage Report📂 Overall coverage
|
…lves The edge list with per-edge flow is now the source of truth: each solve packs it into the scratch CSR and writes flows back afterwards, so add_edge works freely between solves and flow(id) is always valid. Co-Authored-By: Andrew He <he.andrew.mail@gmail.com>
Summary
Data-oriented rewrite of the adjacency layout in
mcmf.hpp(all three ofMCMF_SSPA,MCMF_Dinic,Dinic), motivated by benchmarks showing the oldstd::vector<std::vector<int>> adj+ sharededgesarray pays two dependent random loads per scanned edge:The staged edge list (
input, with per-edge netflow) is the source of truth: each solvepack()s it into the scratch CSR (O(N+M), free relative to the solve) andunpack()s flows back afterwards, soadd_edgeworks freely between solves.add_edgereturns an edge id andflow_t flow(int id)reads back net flow (foradd_bi_edge, in[-rev_cap, cap]); the new verify solutions use it to reconstruct matchings. Since forward/reverse edges are no longer ate/e^1, each CSR record carries arevindex.Benchmarks (g++-15 -O2, seeds fixed, 3 trials each;
Dinic<int>/Dinic<int64_t>):MCMF_SSPAassignment (5000+5000, deg 30): ~1.3xAlso benchmarked but not adopted:
vector<vector<edge_t>>with inline payload: ties CSR on unstructured graphs (0.9–1.05x) but loses 1.1–1.25x on grid-like graphs, where the single contiguous allocation keeps consecutive nodes' edges on shared cache lines.lct.hpp: 0.86–0.99x — splay touches every field of a node, so splitting fields only adds cache lines.Adds Library Checker verification:
verify/bipartitematching.test.cpp(Dinic+flow()readback) andverify/assignment.test.cpp(MCMF_SSPAwith costs shifted by 1e9 to satisfy the nonnegative reduced-cost precondition); both AC locally in the normal and sanitizer environments.Link to Devin session: https://app.devin.ai/sessions/13b190fd3b54418395ae04b4e3aaa6e1
Requested by: @ecnerwala