api: /maxflow?trace=true ships the whole trace statelessly (closes #36) - #57
Merged
Conversation
The whole Edmonds-Karp run ships as one array on one request -- every augmentation, complete enough to draw, each a full snapshot rather than a delta, so a display steps backwards by decrementing an index and no server-side cursor is needed. The server prepends a step 0 holding the graph before the first push: the uniform render rule is residuals from step k-1 and path annotation from step k, and iteration 1 otherwise has no "before" to draw against. Synthesising one client-side would put residual-graph construction in the browser. The data layer stops filtering and the CLI starts. Both filters existed only to tidy terminal output and both damaged a drawn trace: stripping the synthetic terminals made a route appear to begin mid-graph, and dropping the unbounded terms de-aligned lane_residuals from path, so a min(...) term could not be anchored to the lane it came from. Both now ship complete -- terminals under the reserved __source__/__sink__ ids, unbounded residuals as null -- and `len(lane_residuals) == len(path) - 1` holds on every augmentation. Each step also carries explicit per-edge flows covering every arc, saturated ones included, which the residual lists cannot say: they drop an arc the moment it saturates, and those are the lanes Day 2 most wants drawn. The CLI's text output is unchanged to the byte, pinned by a golden transcript that was written and passing against the old code before any of this moved.
… the trace The contract test asserted `len(lane_residuals) == max(len(path) - 1, 0)`, which is the one shape of that assertion that would also pass on a real augmentation de-aligned all the way down to no terms -- the exact failure it exists to catch. Step 0 is now asserted as what it is (no path, no terms) and every augmentation carries the equality strictly. The `iterations + 1` check compared the array's length to its own contents, which holds just as well on a trace that lost every augmentation. The textbook theater's three iterations are pinned as a count from outside it. README gains the `/maxflow?trace=true` payload alongside the `/scenario` and `/naive` ones, and the endpoint table stops omitting the new parameter.
bgent19
added a commit
that referenced
this pull request
Aug 14, 2026
origin/classdemos carried the original #57 trace work, which main later superseded via its own implementation. Both expose the same CLI surface (a single --trace flag); main's is the version CI runs and ships, so this merge joins the histories without reverting main's refactor of throughput.py, maxflow.py and cli.py.
This was referenced Aug 14, 2026
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 #36.
GET /maxflowgainstrace: bool = False. When true, the whole Edmonds–Karprun ships as one array on one request — every augmentation, each a complete
snapshot of the residual graph rather than a delta, so stepping backwards is a
decrement of an index and the server keeps no cursor.
What changed
maxflow.py—AugmentationStepstops filtering:lane_residualskeepsone term per arc of
path, INF included. Adds per-arcflows, andmax_flow(trace=True)prepends a step 0 holding the graph before the firstpush, so the frontend's render rule (residuals from k−1, path annotation
from k) has a k−1 at iteration 1.
throughput.py— the two filters that lived here are gone. Paths retainthe synthetic terminals under the reserved
__source__/__sink__ids,unbounded residuals ship as
null, andflowscovers every arc — saturatedones at their capacity, untouched ones at 0.
cli.py— does that filtering now, at print time: terminals, unboundedterms and step 0 are dropped on the way to the terminal.
api.py—MaxFlowResponseplus four nested models document the shape at/docs.trace=falsereturns exactly its original four keys, not a nulltracekey.README.md— a/maxflow?trace=truepayload section alongside the/scenarioand/naiveones.The CLI's output is unchanged, and that is checked
tests/test_cli_maxflow_trace.pypins the textbook transcript literally. Thegolden was captured and confirmed passing against the old code before any
of this moved; after the change both shipped datasets hash identically
(
85de4362…,b7966769…).Review notes
Reviewed on both axes before this PR. Two findings were worth acting on and are
fixed in the second commit:
max(len(path) - 1, 0)to absorb step0 — the one shape of it that would also pass on a real augmentation
de-aligned down to no terms, which is the failure it exists to catch. Step 0
is now asserted as what it is, and every augmentation carries the equality
strictly.
iterations + 1check derived its expected count from the array it waschecking. It is now anchored to the textbook theater's three iterations.
The spec's "on every step" cannot literally hold for a step specified to have
an empty path; that tension is split rather than clamped over.
200 tests pass.