Fix HUGR stall when measured values cross Call boundaries (#427) - #466
Conversation
…ing the HUGR stall on measured values crossing Call boundaries
|
Review-status note for the record: an independent correctness-review pass was dispatched over What the PR does carry: a trace-proven diagnosis, four regression shapes (minimal, same-block |
|
The independent review pass has now completed (re-run after the earlier interruption). Verdict: One High finding -- and it is pre-existing on Everything else clean, by executed stress cases: two simultaneous pending Calls resolve to This supersedes the earlier "review pass incomplete" note. The PR is review-complete from the |
…PECOS into hugr-call-deferred-stall
…urvive branch crossings (#468)
Summary
Closes #427. Guppy programs stalled with "HUGR execution stalled before completion ... starved
deferred nodes" whenever a measurement-derived value was returned from a called function and
then reported via
result(...). This hit the transversal-CNOT documentation examples (theoriginal report,
test_qec_guppyblocks 9 and 10) and reduces to a 14-line program.Root cause (trace-proven)
Measurement outcomes are intentionally lazy: the gate is emitted, the node is marked processed,
and the outcome arrives later when the quantum batch flushes. The callee's CFG therefore
completes before the outcome exists. Call completion then performed a one-time snapshot of
the callee's return values -- copying
None-- and removed the Call fromactive_calls. Whenthe outcome later arrived it populated the measurement node's wire, but nothing forwarded it to
the Call's already-published empty output, so the caller's
bool.readdeferred forever againstan empty wire. Same-block programs never stalled because the consumer reads the measurement's
own wire, which measurement handling populates directly.
Diagnosis was performed first, with instrumentation, and contradicted the initial hypothesis
(the callee CFG was believed still active; the trace showed it had completed) -- the fix was
designed against the traced mechanism, not the guess.
The fix
Invariant: a Call may not be marked processed or leave
active_callswhile any of its returnports is unresolved.
pending_call_returnsset, retaining the propagation context to replay once outcomes arrive.Only the return-copy-and-mark-processed phase defers -- the CFG's own completion bookkeeping
still runs, so the quantum batch that produces the outcome still flushes.
the Call is released.
error, and now never fires for these programs.
bool.readspecial cases, no retry counters or timeouts.Tests
New
test_call_measurement_returns.py: the minimal Call-boundary case, the same-block control,a two-level nested Call chain, and the struct/helper/array integration shape. Mutation check:
removing the post-measurement retry fails exactly the three Call-crossing tests while the
same-block control passes -- the wake-up path is what the tests pin.
Acceptance:
test_qec_guppyblocks 9 and 10 -- failing on dev since the issue was filed --pass. Verified independently of the implementation run.
Verification
clippy
-D warningsclean; fmt clean;cargo test -p pecos-hugr85 + 6 doctests; guppy suite491 passed; pecos suite 2130 passed; qec suite 1147 passed; doc generation clean; pre-commit two
passes exit 0.
Closes #467 (delivered by #468, which merges into this branch; GitHub only auto-closes from PRs merging to the default branch).