Skip to content

api: /maxflow?trace=true ships the whole trace statelessly (closes #36) - #57

Merged
bgent19 merged 2 commits into
classdemosfrom
worktree-issue-36-maxflow-trace
Aug 13, 2026
Merged

api: /maxflow?trace=true ships the whole trace statelessly (closes #36)#57
bgent19 merged 2 commits into
classdemosfrom
worktree-issue-36-maxflow-trace

Conversation

@bgent19

@bgent19 bgent19 commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Closes #36.

GET /maxflow gains trace: bool = False. When true, the whole Edmonds–Karp
run 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.pyAugmentationStep stops filtering: lane_residuals keeps
    one term per arc of path, INF included. Adds per-arc flows, and
    max_flow(trace=True) prepends a step 0 holding the graph before the first
    push, 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 retain
    the synthetic terminals under the reserved __source__ / __sink__ ids,
    unbounded residuals ship as null, and flows covers every arc — saturated
    ones at their capacity, untouched ones at 0.
  • cli.py — does that filtering now, at print time: terminals, unbounded
    terms and step 0 are dropped on the way to the terminal.
  • api.pyMaxFlowResponse plus four nested models document the shape at
    /docs. trace=false returns exactly its original four keys, not a null
    trace key.
  • README.md — a /maxflow?trace=true payload section alongside the
    /scenario and /naive ones.

The CLI's output is unchanged, and that is checked

tests/test_cli_maxflow_trace.py pins the textbook transcript literally. The
golden 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:

  • The alignment assertion was written as max(len(path) - 1, 0) to absorb step
    0 — 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.
  • The iterations + 1 check derived its expected count from the array it was
    checking. 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.

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
bgent19 merged commit 18b6686 into classdemos Aug 13, 2026
1 check passed
@bgent19
bgent19 deleted the worktree-issue-36-maxflow-trace branch August 13, 2026 22:53
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant