Skip to content

Add XZCorrections.to_pauli_flow and Pattern.extract_pauli_flow (#…#534

Open
GrovyleX wants to merge 7 commits into
TeamGraphix:masterfrom
GrovyleX:feat/xzcorrections-to-pauli-flow
Open

Add XZCorrections.to_pauli_flow and Pattern.extract_pauli_flow (#…#534
GrovyleX wants to merge 7 commits into
TeamGraphix:masterfrom
GrovyleX:feat/xzcorrections-to-pauli-flow

Conversation

@GrovyleX

@GrovyleX GrovyleX commented Jun 9, 2026

Copy link
Copy Markdown

Reconstruct a Pauli flow from XZ-corrections - the analogue of to_causal_flow / to_gflow - plus the convenience
Pattern.extract_pauli_flow. The flow is built from the pattern's corrections, not from the open graph (whose Pauli flow is not unique and need not generate the pattern).

  • Tests pass (nox)
  • CHANGELOG updated
  • Formatted with ruff
  • CI checks pass

Context: unitaryHACK 2026. XZCorrections already had to_causal_flow / to_gflow; the Pauli-flow case (Theorem 4 of Ref. [1]) was missing and harder.

Description of the change:

The crux is the anachronical corrections: corrections on X/Y Pauli-measured nodes in the present/past of the corrected node are absorbed by the measurement (M^X X = M^X, M^Y Y = M^Y, M^Z Z = M^Z) and never appear in
the pattern (Theorem 2 vs Theorem 4 in Ref. [1]). to_corrections drops them via correcting_set & future, so they must be reconstructed.

Per measured node, this is a linear system over GF(2): the future part of the correcting set is fixed by the X-corrections; the free variables are the anachronical candidates (non-future X/Y-measured nodes, admitted by P1) plus the node itself where the proposition allows; the equations are the future Z-corrections, P2/P3 on past nodes and P4–P9 on the node. The augmented system is reduced with MatGF2.gauss_elimination and solved with the existing solve_f2_linear_system; an unsolvable node raises FlowGenericError(NoCompatiblePauliFlow). The result is validated with PauliFlow.check_well_formed. extract_pauli_flow` keeps Pauli measurements as axes (no Bloch downcast).

Tests cover the round trip flow → to_corrections → to_pauli_flow → to_corrections (must reproduce the corrections exactly) on the causal/gflow/Pauli fixtures, the Pattern path, anachronical recovery, and the infeasible/malformed cases. Passes ruff, mypy --strict, pyright, pytest.

AI disclosure (unitaryHACK policy): I used an LLM (Claude) to help brainstorm the GF(2) formulation of the Pauli-flow conditions and to speed up some boilerplate. The bulk of the work was mine: I worked through the algorithm against Ref. [1], reviewed every line, wrote and ran the tests, and validated the result myself with round-trip and simulation-equivalence checks. I can explain and defend the whole change.

Related issue: Closes #432.

[1] Browne, Kashefi, Mhalla, Perdrix, Generalized flow and determinism in MBQC, New J. Phys. 9 250 (2007), arXiv:quant-ph/0702212.

…eamGraphix#432)

Reconstruct a Pauli flow from XZ-corrections, the analogue of the existing
`XZCorrections.to_causal_flow` / `to_gflow`, with the convenience method
`Pattern.extract_pauli_flow` (analogous to `extract_causal_flow` /
`extract_gflow`). The flow is reconstructed from the pattern's corrections
rather than from the underlying open graph, whose Pauli flow is not unique and
need not generate the pattern.

Handling the anachronical corrections (the crux of the issue)
-------------------------------------------------------------
Unlike for a gflow, a Pauli flow's correction function cannot be read off the
corrections. Anachronical corrections -- corrections targeting X/Y Pauli-measured
nodes in the present or past of the corrected node -- are absorbed by the
measurement (M^X X = M^X, M^Y Y = M^Y, M^Z Z = M^Z) and never appear in the
pattern (compare the patterns of Theorem 2 and Theorem 4 in Browne et al. 2007).
`PauliFlow.to_corrections` discards them via the `correcting_set & future`
filter, so they must be reconstructed.

For each measured node this is cast as a linear system over GF(2):
- the future part of the correcting set is fixed by the observed X-corrections;
- the free variables are the anachronical candidates -- non-future nodes measured
  along the X or Y axis, the only ones admitted in a correcting set by (P1) --
  plus the node itself where the local proposition allows it;
- the equations encode the odd-neighbourhood constraints: the Z-corrections on
  the future nodes, (P2) and (P3) on the past nodes, and (P4)-(P9) on the node.
The system is reduced with `MatGF2.gauss_elimination` and solved with the
existing `solve_f2_linear_system`. An unsolvable node means no Pauli flow induces
the corrections and raises `FlowGenericError(NoCompatiblePauliFlow)`. The
resulting flow is validated with `PauliFlow.check_well_formed`, which also
rejects partial orders that admit no flow.

Tests verify well-formedness and the round trip
`flow -> to_corrections -> to_pauli_flow -> to_corrections` on the existing
causal / gflow / Pauli fixtures, the `Pattern.extract_pauli_flow` path, explicit
recovery of anachronical correctors, and the infeasible and malformed cases.
Passes ruff, mypy --strict, pyright and pytest.

Developed with LLM assistance (Claude): the GF(2) reduction of the Pauli-flow
propositions and the implementation were drafted with the model, then verified
by hand against the reference and validated with round-trip and
simulation-equivalence tests.
@thierry-martinez

thierry-martinez commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Thank you for your contribution. As you may have noticed, two other pull requests (#526 and #532) already address the same issue.

The overall approach of your PR is very similar to the others: the Pauli‑flow definition yields a system of GF(2) equations, where the anachronical corrections are the unknowns, and solving this system provides the correction sets.

Could you clarify how your contribution differs from, or improves upon, the previous works? Specifically, please explain why you consider your approach to be better.

… `uv` in CI (TeamGraphix#516)

The `setup-uv` composite action ignored the `enable-cache` and
`python-version` inputs. Cache was still enabled by default, which is
the intended behaviour, but the system Python version was used instead
of the one specified. As a result, type‑checking and coverage ran with
Python 3.12 rather than the requested Python 3.14.

This commit fixes the `setup-uv` composite action so that it respects
both the `enable-cache` and `python-version` inputs.

The use of Python 3.12 for type-checking was noticed during the review of TeamGraphix#512 (see comment TeamGraphix#512 (comment)).

In the type-checking workflow, `python-version` was set to 3.13 (and was ignored) with an outdated comment about Qiskit/qiskit-aer#2378, which is now closed (see comment thierry-martinez/graphix-pyzx#1 (comment)). The `python-version` field has been updated to 3.14.
@GrovyleX

Copy link
Copy Markdown
Author

Thank you for your contribution. As you may have noticed, two other pull requests (#526 and #532) already address the same issue.

The overall approach of your PR is very similar to the others: the Pauli‑flow definition yields a system of GF(2) equations, where the anachronical corrections are the unknowns, and solving this system provides the correction sets.

Could you clarify how your contribution differs from, or improves upon, the previous works? Specifically, please explain why you consider your approach to be better.

Yeah, totally fair. The three of us landed on the same GF(2) approach since it kind of falls straight out of the Pauli flow definition, so I'm not claiming a different algorithm.

Where mine's a little ahead: it already does a couple of things from your #526 review - raises a specific NoCompatiblePauliFlow error instead of a generic one when no flow fits, and leaves evolve unimplemented per the template.

Mostly I just want to carry it to the finish. I'm around and can turn comments around fast - happy to add broader test coverage, drop the defensive check_well_formed call, mirror the test_pattern tests, whatever you need to get it merge-ready. Just point me at what's missing.


…raphix#538)

Bumps the python-packages group with 1 update: [ruff](https://github.com/astral-sh/ruff).


Updates `ruff` from 0.15.15 to 0.15.16
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](astral-sh/ruff@0.15.15...0.15.16)

---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.15.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: python-packages
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
thierry-martinez and others added 3 commits June 13, 2026 11:52
* Pin `matplotlib==3.10.9` for extra `dev` dependencies

This commit adds a pin to `matplotlib==3.10.9` in the `dev` extra
dependencies.

Matplotlib 3.11.0 introduces visual changes that break our
graphical-regression tests.  As we do for other packages whose new
releases can break the test suite, we pin a precise version in the
`dev` extra to ensure reproducible CI while keeping the package
unpinned in the default dependencies so users can choose whichever
version they prefer.

We pin `3.10.9` instead of the newer `3.11.0` because the latter drops
support for Python 3.11, which we continue to support until its
official end-of-life (October 2026):
https://devguide.python.org/versions/

* Restore qiskit constraint in `dev` extra

* Remove `pytest --mpl` in `tests_minimal`

We cannot run `pytest --mpl` in `test_minimal` (without extra), since
the visualization output depends on the version of Matplotlib, and the
pinning is only in the `dev` extra.
…ests

- Drop the run-time check_well_formed call: the reconstruction satisfies the Pauli-flow propositions by construction, so validation is moved to the test suite. This also makes to_pauli_flow total on degenerate inputs (e.g. Pattern().extract_xzcorrections().to_pauli_flow() no longer raises).

- Add simulation-equivalence tests (test_pattern.py and the Pauli-only case): the pattern rebuilt from the reconstructed flow must implement the same unitary as the original, a stronger guarantee than matching corrections.

- Use OpenGraph.neighbors instead of the raw networkx graph, and document the deterministic (free-variables-to-zero) reconstruction.
@GrovyleX

Copy link
Copy Markdown
Author

small update: merged latest master for the matplotlib CI fix (#541), and added simulation-equivalence tests on top of the corrections round-trip. instead of just checking the X/Z corrections match, I rebuild the pattern from the reconstructed flow, simulate it, and assert it computes the same unitary, which felt like the stronger correctness guarantee. also made the reconstruction total so it doesn't error on empty/degenerate patterns.

happy to keep iterating on whatever's most useful

@codecov

codecov Bot commented Jun 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.06542% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 89.07%. Comparing base (8bf1ebe) to head (0b94158).
⚠️ Report is 3 commits behind head on master.

Files with missing lines Patch % Lines
graphix/flow/core.py 99.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #534      +/-   ##
==========================================
+ Coverage   88.85%   89.07%   +0.21%     
==========================================
  Files          49       49              
  Lines        7135     7242     +107     
==========================================
+ Hits         6340     6451     +111     
+ Misses        795      791       -4     

☔ 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.

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

2 participants