Skip to content

Extract Pauli flow from XZ-corrections (closes #432)#526

Open
Vinny010 wants to merge 11 commits into
TeamGraphix:masterfrom
Vinny010:pauli-flow-extraction
Open

Extract Pauli flow from XZ-corrections (closes #432)#526
Vinny010 wants to merge 11 commits into
TeamGraphix:masterfrom
Vinny010:pauli-flow-extraction

Conversation

@Vinny010

@Vinny010 Vinny010 commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Closes #432.

Implements XZCorrections.to_pauli_flow and the convenience method Pattern.extract_pauli_flow (analogous to extract_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_corrections discards these (the correcting_set & future filter), 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):

  • Pinned future part. For nodes in the future of i, membership in the correction set p(i) is fixed by the observed X-corrections of i (and must reproduce the Z-corrections via the odd neighbourhood). These become constants of the system.
  • Free anachronical variables. The unknowns are exactly the anachronical candidates — non-future nodes measured along the X or Y axes (permitted in p(i) by P1) — and, where the local proposition allows it, i itself.
  • Equations. The odd-neighbourhood constraints: the Z-corrections on the future nodes, the vanishing of the odd neighbourhood on past non-(Y/Z) nodes (P2), the membership/odd-neighbourhood coupling on past Y nodes (P3), and the local proposition on 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 by PauliFlow.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:

  • the three worked examples of the issue (causal, gflow and Pauli; the reconstructed correction functions match the expected p(0) = {1, 3}, p(1) = {2}, p(2) = {3} etc., including the anachronical node 1),
  • a Pauli-measured open graph, and
  • a randomized family of open graphs that admit a Pauli flow.

There are also unit tests for the GF(2) solver. Passes ruff, mypy --strict, pyright, and pytest locally (new tests plus the existing flow / open-graph / pattern suites).

Developed with LLM assistance, reviewed and tested by me.

@codecov

codecov Bot commented Jun 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.45%. Comparing base (1b973fc) to head (377b25b).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #526      +/-   ##
==========================================
+ Coverage   89.18%   89.45%   +0.26%     
==========================================
  Files          49       49              
  Lines        7354     7442      +88     
==========================================
+ Hits         6559     6657      +98     
+ Misses        795      785      -10     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@thierry-martinez thierry-martinez left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much for this clean solution. Here are my initial comments on your PR.

Comment thread graphix/flow/core.py Outdated
Comment thread tests/test_pauli_flow_extraction.py Outdated
Comment thread tests/test_pauli_flow_extraction.py Outdated
Comment thread graphix/flow/core.py Outdated
@Vinny010

Vinny010 commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the thoughtful review, @thierry-martinez! Pushed 9a73dc7 addressing each point:

  • _linalg.solve_f2_linear_system reuse — good catch. Switched to it. The per-node augmented matrix [A | b] is reduced to row echelon form with MatGF2.gauss_elimination(ncols=n_vars) (which propagates the row operations to the RHS column), inconsistency is detected by scanning for a [0…0 | 1] row, and the reduced system is then handed to the existing solver. _solve_gf2 is removed.
  • round on the random angle — purely cosmetic (kept the test failure messages readable when I was iterating). Dropped.
  • Exception narrowing in the randomized test — narrowed to OpenGraphError, which is the only documented raise condition of OpenGraph.to_pattern when no flow exists.
  • pf.check_well_formed() on line 311 — by construction the GF(2) equations of _reconstruct_pauli_correction_function encode P2–P9 exactly, and restricting the anachronical candidates to X/Y-measured nodes guarantees P1; the general flow properties are satisfied by construction too. So I cannot construct an example where the check fails — it’s effectively a regression guard for the algorithm. Added a comment explaining that. Happy to drop the call entirely if you’d prefer.

Let me know if you’d like any other changes.

@veena-vee989

Copy link
Copy Markdown

Hello - you are currently over the PR limit for unitaryHACK, so your PRs will be disregarded until you align with the rules. Reach out to hack@unitary.foundation to be reinstated.

@thierry-martinez thierry-martinez requested a review from matulni June 9, 2026 08:21

@matulni matulni left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this nice work! Here are some comments from my first pass. There's some work before merging, but in the mean time I'm ok with assigning you the issue.

For my own curiosity, would you be willing to share the promtps and skills (or even the reasoning log) of the LLM you used to code this ? This is of course not mandatory, but I'm curious to see how the solution was found. Thanks!

Comment thread graphix/flow/core.py Outdated
See :meth:`XZCorrections.to_pauli_flow`.
"""
og = xz.og
adjacency: dict[int, set[int]] = {n: set(og.graph.neighbors(n)) for n in og.graph.nodes}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
adjacency: dict[int, set[int]] = {n: set(og.graph.neighbors(n)) for n in og.graph.nodes}
adjacency: dict[int, set[int]] = {n: og.neighbors({n}) for n in og.graph.nodes}

Comment thread graphix/flow/core.py
Comment thread graphix/flow/core.py Outdated
solution = solve_f2_linear_system(lhs, MatGF2(b))

correction_set = set(fixed_in_p)
correction_set.update(v for v, bit in zip(variables, solution, strict=True) if int(bit))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
correction_set.update(v for v, bit in zip(variables, solution, strict=True) if int(bit))
correction_set.update(v for v, bit in zip(variables, solution, strict=True) if bit)

Comment thread graphix/flow/core.py Outdated
Comment on lines +1478 to +1479
candidates = sorted(a for a in nonfuture_others if a in non_inputs and labels.get(a) in {Axis.X, Axis.Y})
variables = [*candidates, node] if self_is_var else list(candidates)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think sorting is necessary, but if it is, please add a test which would fail if candidates are not sorted.

Suggested change
candidates = sorted(a for a in nonfuture_others if a in non_inputs and labels.get(a) in {Axis.X, Axis.Y})
variables = [*candidates, node] if self_is_var else list(candidates)
variables = [a for a in nonfuture_others | {node} if a in non_inputs and labels.get(a) in {Axis.X, Axis.Y}]

With the suggestion above, self_is_var is no longer necessary.

Comment thread graphix/flow/core.py Outdated
Comment thread graphix/flow/core.py Outdated
Comment thread graphix/flow/core.py Outdated
Comment thread tests/test_pauli_flow_extraction.py Outdated
Comment thread tests/test_pauli_flow_extraction.py
Comment thread tests/test_pauli_flow_extraction.py Outdated
Comment thread tests/test_pauli_flow_extraction.py Outdated
_assert_round_trip(pattern)


@pytest.mark.filterwarnings("ignore:Open graph with non-inferred Pauli measurements.")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that such warnings should not be triggered here; and if they are, we should address them, because whether Pauli measurements are inferred (or not) affects the Pauli flow.

Comment thread tests/test_pauli_flow_extraction.py Outdated
]


@pytest.mark.filterwarnings("ignore:Open graph with non-inferred Pauli measurements.")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above: these warnings should not be triggered, and they are relevant with respect to Pauli flows.

Comment thread graphix/flow/core.py Outdated
Vinny010 added a commit to Vinny010/graphix that referenced this pull request Jun 10, 2026
…check

Responds to @thierry-martinez's review on TeamGraphix#526:

- Remove the two @pytest.mark.filterwarnings("ignore:Open graph with
  non-inferred Pauli measurements.") suppressions. The warning does not
  actually fire for these tests (verified by promoting it to an error), so
  the suppressions were unnecessary and masked a semantically relevant signal.
- Stop running pf.check_well_formed() systematically in production inside
  XZCorrections.to_pauli_flow: the flow is well formed by construction, and
  the sanity check is exercised in the test-suite via is_well_formed().
  This also avoids the production manifestation of the empty-partial-order
  bug (TeamGraphix#531): Pattern().extract_xzcorrections().to_pauli_flow() no longer
  raises PartialOrderError.
- Add test_to_pauli_flow_empty_pattern covering that empty-pattern case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Vinny010

Copy link
Copy Markdown
Contributor Author

Thanks, @thierry-martinez — both points addressed in the latest commit.

Non-inferred Pauli warning suppressions. You're right that these shouldn't be silenced. I removed both @pytest.mark.filterwarnings("ignore:Open graph with non-inferred Pauli measurements.") markers. I also checked why they were there: it turns out the warning doesn't actually fire for either test — the open graphs use explicit PauliMeasurement (Measurement.Y/Measurement.Z) and non-Pauli Bloch angles, so there are no "secretly Pauli" Bloch measurements. I verified this by promoting that exact warning to an error (-W "error:Open graph with non-inferred Pauli measurements.:UserWarning") and the whole file still passes. So the suppressions were dead weight masking a semantically relevant signal — gone now.

check_well_formed() in production. Agreed with you and @matulni — removed the pf.check_well_formed() call from XZCorrections.to_pauli_flow. The reconstructed flow is well formed by construction (the GF(2) system encodes P2-P9, the anachronical candidates are X/Y-restricted for P1, and the image/domain conditions hold), and the well-formedness is now exercised only in the test-suite via the existing assert pf.is_well_formed() in the round-trip helper.

This also removes the production manifestation of #531: Pattern().extract_xzcorrections().to_pauli_flow() no longer raises PartialOrderError and instead returns the trivial flow. I added test_to_pauli_flow_empty_pattern to lock that in. (The underlying check_well_formed behaviour on an empty partial order is still the separate concern tracked in #531.)

@Vinny010

Copy link
Copy Markdown
Contributor Author

Thanks for the careful review, @matulni — all addressed in the latest push.

Reconstruction (core.py).

  • Took your variables suggestion: it now iterates nonfuture_others | {node} with the single filter, which drops both the sorting and self_is_var. (No sorting-dependent test needed since the order no longer matters.)
  • Merged the P2 and P3 loops into the single for g in nonfuture_others form you proposed, with the assert lab_g is not None — confirmed sound: output nodes are layer 0 of the partial order, so they're in every measured node's future and never reach nonfuture_others.
  • Added the general comment explaining that each row is the indicator of which variables lie in a node's neighbourhood, so row · p is its odd-neighbourhood parity, and that rhs is the required parity (0/1) after folding in the fixed part via const_at.
  • Applied the adjacency = {n: og.neighbors({n}) ...}, the solve_f2_linear_system comment, and the if bit tidy-ups.

Typed error. Added FlowGenericErrorReason.NoPauliFlow and the reconstruction now raises FlowGenericError(NoPauliFlow) instead of a bare FlowError, carrying your "no correction set reconciles the XZ-corrections" message.

Defensive check. Removed pf.check_well_formed() from the production to_pauli_flow path (agreeing with you and @thierry-martinez); the well-formedness is asserted in the test-suite instead.

Tests.

  • The randomized round-trip is now @pytest.mark.parametrize("seed", range(400)) — one independently reproducible case per seed, skipping draws with no edges / no flow.
  • The failure test is parametrized and asserts the typed FlowGenericErrorReason.NoPauliFlow, with added XZ- and YZ-input cases alongside the original Z-input and isolated-XY ones.
  • Mirrored the extraction tests in test_pattern.py: test_extract_pauli_flow_rnd_circuit (the random-circuit round trip) and test_extract_pauli_flow over the worked cases. For the worked cases I assert the round trip on every has_gflow case (a gflow always induces a Pauli flow) and skip the non-gflow ones, since a Pauli flow is strictly more general and its absence can't be inferred from has_gflow alone — happy to add a dedicated has_pflow field if you'd prefer the negative cases covered too.

On the LLM question — I did use an AI coding assistant (Claude) and then verified everything myself by running the full test suite, which is how the edge cases (the anachronical corrections, the empty-pattern path) got shaken out. I'd rather keep my specific prompts and workflow to myself, but appreciate the curiosity — and I'm glad to keep iterating on the PR the normal way.

@Vinny010

Copy link
Copy Markdown
Contributor Author

Hi @thierry-martinez @matulni — quick ready-for-re-review ping with the unitaryHACK deadline tomorrow.

I addressed your Jun 10 review feedback in my Jun 10–11 replies. CI is fully green (24/24) and mergeable state is clean. Whenever you have a moment, would appreciate a final look — happy to make any last tweaks if anything else needs attention.

Thanks for the careful review on this one!

@thierry-martinez thierry-martinez left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After careful consideration, we've decided to award you the bounty for this PR as well. Please leave a comment on issue #432 so we can assign it to you. In the meantime, take a look at the code‑coverage results: https://app.codecov.io/gh/TeamGraphix/graphix/pull/526

Vinny010 added a commit to Vinny010/graphix that referenced this pull request Jun 17, 2026
- Assert the rendered FlowGenericError(NoPauliFlow) message in the existing
  no-flow test, exercising the corresponding branch in exceptions.py.
- Mark the empty-constraint-system early return in _solve_pauli_correction_set
  with `# pragma: no cover`: it is a defensive guard that is unreachable for a
  measured node, since the output nodes are maximally future and the future
  loop therefore always contributes at least one row. The guard is kept so the
  GF(2) reduction never runs on an empty matrix.

Closes the remaining patch-coverage gap reported by Codecov on TeamGraphix#526.

@matulni matulni left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the nice job, LGTM!

(Please wait for a second approval).

Comment thread graphix/flow/core.py Outdated
Comment on lines +1556 to +1558
for i in range(lhs.shape[0]):
if not lhs[i].any() and b[i] != 0:
return None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for i in range(lhs.shape[0]):
if not lhs[i].any() and b[i] != 0:
return None
for coeffs, const in zip(lhs, b, strict=True):
if not np.any(coeffs) and const:
return None

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied in e7fa8ab — thanks, that reads much better. Iterating the rows directly with zip(lhs, b, strict=True) and np.any drops the index bookkeeping entirely. And thanks for the approval!

@matulni

matulni commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Hi @Vinny010, before we merge could you please update the CHANGELOG?

@Vinny010

Copy link
Copy Markdown
Contributor Author

Done — added a CHANGELOG entry under ## Unreleased / ### Added (commit 0ac8592) for the new XZCorrections.to_pauli_flow method and the NoPauliFlow error reason. Thanks, @matulni — ready to merge on your side.

Comment thread graphix/flow/core.py

@thierry-martinez thierry-martinez left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay; I am still checking that the test suite is comprehensive enough.

One problem I have with the current test suite is that the following implementation of XZCorrections.to_pauli_flow passes all the tests:

    def to_pauli_flow(self) -> PauliFlow[_AM_co]:
        pf = self.og.find_pauli_flow()
        if pf is None:
            raise FlowGenericError(FlowGenericErrorReason.NoPauliFlow)
        return pf

It is obvious that this implementation is incorrect: the Pauli flow implemented by the pattern is not necessarily the same as the Pauli flow inferred from the underlying open graph, since the Pauli flow is not unique.

Could you come up with a test that exercises this property?

Vinny010 and others added 7 commits June 18, 2026 04:59
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
- 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 <noreply@anthropic.com>
…check

Responds to @thierry-martinez's review on TeamGraphix#526:

- Remove the two @pytest.mark.filterwarnings("ignore:Open graph with
  non-inferred Pauli measurements.") suppressions. The warning does not
  actually fire for these tests (verified by promoting it to an error), so
  the suppressions were unnecessary and masked a semantically relevant signal.
- Stop running pf.check_well_formed() systematically in production inside
  XZCorrections.to_pauli_flow: the flow is well formed by construction, and
  the sanity check is exercised in the test-suite via is_well_formed().
  This also avoids the production manifestation of the empty-partial-order
  bug (TeamGraphix#531): Pattern().extract_xzcorrections().to_pauli_flow() no longer
  raises PartialOrderError.
- Add test_to_pauli_flow_empty_pattern covering that empty-pattern case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…trized tests

- core.py: simplify the GF(2) variable selection (drop `self_is_var`/sorting),
  merge the P2/P3 constraint loops into one with an explanatory comment, add a
  general comment describing what `matrix`/`rhs` encode, and tidy the
  `adjacency`/`solve_f2_linear_system`/`correction_set.update` lines.
- exceptions.py: add `FlowGenericErrorReason.NoPauliFlow`; raise the typed
  `FlowGenericError(NoPauliFlow)` instead of a bare `FlowError` when no Pauli
  flow reconciles the XZ-corrections.
- tests: parametrize the randomized round-trip over the seed (one case per
  seed, skipping draws with no edges / no flow) and parametrize the
  no-flow failure cases (asserting the typed reason), adding XZ/YZ-input cases.
- test_pattern.py: mirror the causal/gflow extraction tests for the Pauli flow
  (`test_extract_pauli_flow_rnd_circuit` and `test_extract_pauli_flow`).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Assert the rendered FlowGenericError(NoPauliFlow) message in the existing
  no-flow test, exercising the corresponding branch in exceptions.py.
- Mark the empty-constraint-system early return in _solve_pauli_correction_set
  with `# pragma: no cover`: it is a defensive guard that is unreachable for a
  measured node, since the output nodes are maximally future and the future
  loop therefore always contributes at least one row. The guard is kept so the
  GF(2) reduction never runs on an empty matrix.

Closes the remaining patch-coverage gap reported by Codecov on TeamGraphix#526.
Vinny010 and others added 4 commits June 18, 2026 04:59
The suggested reduction (skipping the future-node equation for outputs and
Y/Z-measured nodes) is not equivalent: when a Z-correction targets an output
node in the future, dropping that GF(2) equation breaks the Z-correction
round-trip. Reverted to the full future loop and added a deterministic
regression test (test_extract_pauli_flow_output_zcorrection) that round-trips
on the correct code and fails under the reduction.
Add test_extract_pauli_flow_pins_the_pattern_specific_flow.

The existing round-trip tests all use patterns whose Pauli flow happens
to coincide with the maximally-delayed flow returned by
OpenGraph.find_pauli_flow, so a trivial implementation that delegates to
find_pauli_flow and ignores the XZ-corrections of the pattern entirely
passes every existing case.

The new test pins down a small open graph (path 0-1-2-3, all X-measured,
output 3) that admits two well-formed Pauli flows whose to_corrections()
outputs genuinely differ -- one with an anachronical-only correction
{1} for node 0, one with the future correction {1, 3} -- builds the
XZ-corrections of each, and asserts that the reconstruction returns the
chosen flow, not the one find_pauli_flow would have returned.
@Vinny010 Vinny010 force-pushed the pauli-flow-extraction branch from 27fd0b7 to 377b25b Compare June 18, 2026 01:02
@Vinny010

Copy link
Copy Markdown
Contributor Author

Thanks @thierry-martinez — addressed in 377b25b on the rebased branch.

You were right that the existing round-trip tests don't distinguish the real reconstruction from the trivial pf = self.og.find_pauli_flow() impl: every test case happens to feed in patterns whose flow already coincides with the maximally-delayed flow returned by find_pauli_flow, so the trivial impl rounds-trips perfectly on all of them.

The new test test_extract_pauli_flow_pins_the_pattern_specific_flow pins down a small open graph (path 0-1-2-3, all X-measured, output 3) that admits two distinct, well-formed Pauli flows whose to_corrections() outputs genuinely differ:

  • pf_with_anachronical = {0: {1}, 1: {2}, 2: {3}} — node 0's correction is anachronical-only ({1}), no future X-correction on the output.
  • pf_with_future = {0: {1, 3}, 1: {2}, 2: {3}} — node 0 X-corrects the output.

It then builds the XZ-corrections of each, asserts the reconstruction returns the chosen flow exactly, and asserts both round-trip back to their original corrections. The discriminator: any trivial impl that ignores the XZ-corrections returns the same flow for both inputs, so it can match at most one of them — the assertion fires for whichever case the trivial impl misses.

I verified this empirically by monkey-patching the trivial impl in: with that swap, the five existing scalar tests (causal, gflow, pauli_example, pauli_opengraph, output_zcorrection) still pass, and only the new test fails — confirming it discriminates exactly the property you flagged.

Also: rebased the branch onto current master (small CHANGELOG conflict against #524, resolved by keeping both entries). CI green locally — 84 / 84 pauli-flow-extraction tests + the rest of the flow + pattern suites (422 / 422 in the slice I ran).

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.

Pauli-flow extraction from pattern

4 participants