Extract Pauli flow from XZ-corrections (closes #432)#526
Conversation
Implement `XZCorrections.to_pauli_flow` and the convenience method `Pattern.extract_pauli_flow`, reconstructing a Pauli flow directly from a pattern's XZ-corrections (Theorem 4 of Browne et al. 2007) rather than from the underlying open graph (whose Pauli flow is not unique and need not generate the pattern). The difficulty is the anachronical corrections: corrections targeting X/Y Pauli-measured nodes in the present or past of the corrected node. These are dropped by `PauliFlow.to_corrections` (the `& future` filter) and so never appear in the pattern, so they must be reconstructed. For each measured node this is cast as a GF(2) linear system: the future membership of the correction set is pinned by the observed X-corrections; the free variables are the anachronical (non-future, X/Y-measured) candidates and, where allowed, the node itself; and the equations encode the odd-neighbourhood constraints (Z-corrections on future nodes, P2 on past non-(Y/Z) nodes, the P3 coupling on past Y nodes, and the local proposition P4-P9 on the node). The system is solved over GF(2) with `_solve_gf2`. Tests verify, on the three worked examples of the issue, on a Pauli-measured open graph, and on a randomized family of open graphs that admit a Pauli flow, that the reconstructed flow is well formed and that `to_corrections()` reproduces the pattern's corrections exactly (the decisive round-trip criterion). Passes ruff, mypy --strict, pyright, and pytest locally. Co-Authored-By: Claude Opus 4.8 <[email protected]>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #526 +/- ##
==========================================
+ Coverage 88.85% 89.08% +0.23%
==========================================
Files 49 49
Lines 7135 7240 +105
==========================================
+ Hits 6340 6450 +110
+ Misses 795 790 -5 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Cover the branches where no Pauli flow is compatible with the XZ-corrections: a measured input node that must correct itself, and an isolated XY-measured node whose proposition P4 cannot be satisfied (unsolvable GF(2) system). Addresses the patch-coverage gap reported on the PR. Co-Authored-By: Claude Opus 4.8 <[email protected]>
thierry-martinez
left a comment
There was a problem hiding this comment.
Thank you very much for this clean solution. Here are my initial comments on your PR.
| raise PartialOrderLayerError(PartialOrderLayerErrorReason.FirstLayer, layer_index=0, layer=first_layer) | ||
|
|
||
|
|
||
| def _solve_gf2(matrix: list[list[int]], rhs: list[int], n_vars: int) -> list[int] | None: |
There was a problem hiding this comment.
There is already a GF(2)-linear-system solver implemented in _linalg.solve_f2_linear_system.
| lambda r: Measurement.XY(round(float(r.random()), 3)), | ||
| lambda r: Measurement.XZ(round(float(r.random()), 3)), | ||
| lambda r: Measurement.YZ(round(float(r.random()), 3)), |
There was a problem hiding this comment.
Why do you choose to apply round there rather than leave the random float unchanged?
| pattern = OpenGraph( | ||
| graph=graph, input_nodes=inputs, output_nodes=outputs, measurements=measurements | ||
| ).to_pattern() | ||
| except Exception: # noqa: BLE001, S112 open graph without a flow -> not a valid test case |
There was a problem hiding this comment.
What are the relevant exceptions here? We should at least catch OpenGraphError. Are there any others?
| """ | ||
| correction_function = _reconstruct_pauli_correction_function(self) | ||
| pf: PauliFlow[_AM_co] = PauliFlow(self.og, correction_function, self.partial_order_layers) | ||
| pf.check_well_formed() # Raises a `FlowError` if the reconstructed flow is not well formed. |
There was a problem hiding this comment.
Is it possible to fail at this point? Can you give an example where the correction_function isn’t well‑formed by construction?
- Reuse `graphix._linalg.solve_f2_linear_system` rather than the ad-hoc `_solve_gf2` (which is removed). The per-node augmented matrix `[A | b]` is reduced to row echelon form with `MatGF2.gauss_elimination(ncols=n_vars)`, inconsistency is detected by scanning for `[0...0 | 1]` rows, and the reduced system is then handed to the existing solver. - Update the `XZCorrections.to_pauli_flow` docstring to point at the existing GF(2) solver and to spell out the propositions encoded by the GF(2) system (P1 by construction via the X/Y-axis candidate restriction; P2-P9 directly). - Add a comment on the `pf.check_well_formed()` call: it is a regression guard; the algorithm satisfies the propositions by construction, so a failure there would indicate a bug rather than malformed input. - In the randomized round-trip test, narrow `except Exception` to `OpenGraphError` (the only documented raise of `OpenGraph.to_pattern` when no flow exists) and drop the cosmetic `round` on the random measurement angles. Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
Thanks for the thoughtful review, @thierry-martinez! Pushed 9a73dc7 addressing each point:
Let me know if you’d like any other changes. |
Closes #432.
Implements
XZCorrections.to_pauli_flowand the convenience methodPattern.extract_pauli_flow(analogous toextract_causal_flow/extract_gflow), reconstructing a Pauli flow directly from the pattern's XZ-corrections (Theorem 4 of Browne et al. 2007) rather than from the underlying open graph — whose Pauli flow is not unique and is not guaranteed to generate the pattern.How the anachronical corrections were tackled (the crux of the issue)
The hard part, as the issue notes, is that a Pauli flow's correction sets may contain anachronical corrections: corrections targeting X/Y Pauli-measured nodes that lie in the present or past of the corrected node.
PauliFlow.to_correctionsdiscards these (thecorrecting_set & futurefilter), so they never appear in the pattern and cannot be read off the corrections — they must be reconstructed.For each measured node
i, reconstruction is cast as a linear system over GF(2):i, membership in the correction setp(i)is fixed by the observed X-corrections ofi(and must reproduce the Z-corrections via the odd neighbourhood). These become constants of the system.p(i)by P1) — and, where the local proposition allows it,iitself.i(P4–P9, handling the XY/XZ/YZ planes and the X/Y/Z axes).The system is solved with a small GF(2) Gaussian-elimination helper (
_solve_gf2); failure to solve at any node means no Pauli flow is compatible with the corrections. The resulting flow is then validated byPauliFlow.check_well_formed.Correctness
The decisive check is the round trip: for the reconstructed
pf,pf.to_corrections()must reproduce the pattern's X- and Z-corrections exactly (this is what guarantees it generates this pattern). Tests verify well-formedness and the round trip on:p(0) = {1, 3}, p(1) = {2}, p(2) = {3}etc., including the anachronical node1),There are also unit tests for the GF(2) solver. Passes
ruff,mypy --strict,pyright, andpytestlocally (new tests plus the existing flow / open-graph / pattern suites).Developed with LLM assistance, reviewed and tested by me.