From 40cae5cfcf5e306731c5e85ca3523b1af36ef9e7 Mon Sep 17 00:00:00 2001 From: Thierry Martinez Date: Tue, 19 May 2026 10:20:22 +0200 Subject: [PATCH 1/3] Fix #468: Remove pyzx interface Module pyzx is removed from Graphix and moved in a separate plugin as we do for other optional dependencies. https://github.com/thierry-martinez/graphix-pyzx/ See related PR: https://github.com/thierry-martinez/graphix-pyzx/pull/1 --- .github/workflows/cov.yml | 2 +- .github/workflows/typecheck.yml | 6 +- CHANGELOG.md | 26 ++- README.md | 11 +- graphix/pyzx.py | 190 -------------------- noxfile.py | 21 +-- pyproject.toml | 3 - tests/test_pyzx.py | 131 -------------- uv.lock | 307 +------------------------------- 9 files changed, 25 insertions(+), 672 deletions(-) delete mode 100644 graphix/pyzx.py delete mode 100644 tests/test_pyzx.py diff --git a/.github/workflows/cov.yml b/.github/workflows/cov.yml index 22b08f6ee..c6c551394 100644 --- a/.github/workflows/cov.yml +++ b/.github/workflows/cov.yml @@ -25,7 +25,7 @@ jobs: python-version: ${{ env.python-version }} - name: Run pytest - run: uv run --extra dev --extra extra pytest --cov=./graphix --cov-report=xml --cov-report=term --doctest-modules + run: uv run --extra dev pytest --cov=./graphix --cov-report=xml --cov-report=term --doctest-modules - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v5 diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index a40d377cd..0b407d299 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -32,9 +32,9 @@ jobs: enable-cache: true python-version: ${{ env.python-version }} - - run: uv run --extra dev --extra extra --extra typing mypy + - run: uv run --extra dev --extra typing mypy - - run: uv run --extra dev --extra extra --extra typing pyright + - run: uv run --extra dev --extra typing pyright # Check that `mypy` find installed `graphix` package (#328) # @@ -67,4 +67,4 @@ jobs: run: | cd ${{ runner.temp }} echo "from graphix import Pattern" > test_graphix_type.py - uv run --project ${{ github.workspace }} --no-sync mypy test_graphix_type.py \ No newline at end of file + uv run --project ${{ github.workspace }} --no-sync mypy test_graphix_type.py diff --git a/CHANGELOG.md b/CHANGELOG.md index fddc3d857..1cb85f851 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,14 +11,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - #490: Introduced new `Instruction` and `Command` namespace classes for instruction and command instantiation. -- #476 Introduced new methods `OpenGraph.extract_circuit`, `CliffordMap.to_tableau` and new function `graphix.circ_ext.compilation.cm_berg_pass`. Circuit extraction can be done natively in Graphix. +- #476: Introduced new methods `OpenGraph.extract_circuit`, `CliffordMap.to_tableau` and new function `graphix.circ_ext.compilation.cm_berg_pass`. Circuit extraction can be done natively in Graphix. -- #505 +- #505: - Added new methods `XZCorrections.to_causal_flow` and `XZCorrections.to_gflow` which subsume `StandardizedPattern.extract_causal_flow` and `StandardizedPattern.extract_gflow`. - Added new methods `XZCorrections.to_bloch` and `XZCorrections.downcast_bloch`. ### Fixed +- #454, #481: Ensure `Pattern.minimize_space` only reduces max-space and does not increase it. + +- #235, #489: Correct sign for `YZ` measurements in `from_pyzx_graph`. ZX diagrams are now correctly converted into open graphs, even if they are reduced. + ### Changed - #490: Exposed more common classes and methods to top level `__init__.py`. @@ -26,27 +30,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Moved `InstructionType`, `InstructionTypeWithoutRZZ`, `CommandType`, `Correction` and `CommandOrNoise` to `TYPE_CHECKING` blocks. - Renamed `DrawAnnotations` to `DrawPatternAnnotations`. -- #479: Added new methods `OpenGraph.draw`, `PauliFlow.draw` and `XZCorrections.draw`. +- #452: Use `uv` for dependency management - #454, #481: New space minimization API that allows users to select or define custom heuristics. -- #476 +- #476: - Added new field `dim` to `PauliString` to represent the dimension of the Hilbert space. - Define `PauliString` on qubit indices and remove all `remap` methods. - Represent `x_map` and `z_map` attributes of `CliffordMap` as sequences of `PauliString` instead of mappings. - Rename `PauliFlow.pauli_strings` property as `PauliFlow.extraction_pauli_strings`. -### Fixed - -- #454, #481: Ensure `Pattern.minimize_space` only reduces max-space and does not increase it. - -- #235, #489: Correct sign for `YZ` measurements in `from_pyzx_graph`. ZX diagrams are now correctly converted into open graphs, even if they are reduced. +- #479: + - Method `Pattern.draw_graph` subsumed by two different methods: `Pattern.draw_flow` and `Pattern.draw_xzcorrections`. + - Added new methods `OpenGraph.draw`, `PauliFlow.draw` and `XZCorrections.draw`. -### Changed - -- #479: Method `Pattern.draw_graph` subsumed by two different methods: `Pattern.draw_flow` and `Pattern.draw_xzcorrections`. - -- #452: Use `uv` for dependency management +- #468, #511: `pyzx` module is moved in a separate plugin: https://github.com/thierry-martinez/graphix-pyzx/ ## [0.3.5] - 2026-03-26 diff --git a/README.md b/README.md index d40169b05..9154fbbdc 100644 --- a/README.md +++ b/README.md @@ -92,12 +92,13 @@ state_out = pattern.simulate_pattern(backend="statevector") ## Graphix plugins -- [graphix-stim-backend](https://github.com/thierry-martinez/graphix-stim-backend): `stim` backend for efficient Clifford pattern simulation -- [graphix-symbolic](https://github.com/TeamGraphix/graphix-symbolic): parameterized patterns with symbolic simulation -- [graphix-ibmq](https://github.com/TeamGraphix/graphix-ibmq): pattern transpiler for IBMQ / `qiskit` -- [graphix-perceval](https://github.com/TeamGraphix/graphix-perceval): pattern transpiler for Quandela's `perceval` simulator and QPU -- [graphix-qasm-parser](https://github.com/TeamGraphix/graphix-qasm-parser): a plugin for parsing OpenQASM circuit. +- [`graphix-ibmq`](https://github.com/TeamGraphix/graphix-ibmq): pattern transpiler for IBMQ / `qiskit` +- [`graphix-perceval`](https://github.com/TeamGraphix/graphix-perceval): pattern transpiler for Quandela's `perceval` simulator and QPU +- [`graphix-pyzx`](https://github.com/thierry-martinez/graphix-pyzx): conversion between open graphs and PyZX diagrams +- [`graphix-qasm-parser`](https://github.com/TeamGraphix/graphix-qasm-parser): a plugin for parsing OpenQASM circuit. +- [`graphix-stim-backend`](https://github.com/thierry-martinez/graphix-stim-backend): `stim` backend for efficient Clifford pattern simulation - [`graphix-stim-compiler`](https://github.com/qat-inria/graphix-stim-compiler): `stim` backend for efficient compilation of Clifford maps. +- [`graphix-symbolic`](https://github.com/TeamGraphix/graphix-symbolic): parameterized patterns with symbolic simulation ## Related packages diff --git a/graphix/pyzx.py b/graphix/pyzx.py deleted file mode 100644 index bbda27e82..000000000 --- a/graphix/pyzx.py +++ /dev/null @@ -1,190 +0,0 @@ -"""Functionality for converting between OpenGraphs and :mod:`pyzx`. - -These functions are held in their own file rather than including them in the -OpenGraph class because we want :mod:`pyzx` to be an optional dependency. -""" - -from __future__ import annotations - -from fractions import Fraction -from typing import TYPE_CHECKING, SupportsFloat - -import networkx as nx -import pyzx as zx -from pyzx.graph import Graph -from pyzx.utils import EdgeType, FractionLike, VertexType - -from graphix.fundamentals import Plane -from graphix.measurements import Measurement -from graphix.opengraph import OpenGraph - -if TYPE_CHECKING: - from pyzx.graph.base import BaseGraph - - from graphix.measurements import BlochMeasurement - from graphix.parameter import ExpressionOrFloat - - -def _fraction_of_angle(angle: ExpressionOrFloat) -> Fraction: - if not isinstance(angle, SupportsFloat): - raise TypeError("Parametric angles are not supported by pyzx") - return Fraction(angle) - - -def to_pyzx_graph(og: OpenGraph[BlochMeasurement]) -> BaseGraph[int, tuple[int, int]]: - """Return a :mod:`pyzx` graph corresponding to the open graph. - - Example - ------- - >>> import networkx as nx - >>> from graphix.pyzx import to_pyzx_graph - >>> g = nx.Graph([(0, 1), (1, 2)]) - >>> inputs = [0] - >>> outputs = [2] - >>> measurements = {0: Measurement.XY(0), 1: Measurement.YZ(1)} - >>> og = OpenGraph(g, inputs, outputs, measurements) - >>> reconstructed_pyzx_graph = to_pyzx_graph(og) - """ - g = Graph() - - # Add vertices into the graph and set their type - def add_vertices(n: int, ty: VertexType) -> list[VertexType]: - verts = g.add_vertices(n) - for vert in verts: - g.set_type(vert, ty) - - return verts - - # Add input boundary nodes - in_verts = add_vertices(len(og.input_nodes), VertexType.BOUNDARY) - g.set_inputs(tuple(in_verts)) - - # Add nodes for internal Z spiders - not including the phase gadgets - body_verts = add_vertices(len(og.graph), VertexType.Z) - - # Add nodes for the phase gadgets. In OpenGraph we don't store the - # effect as a separate node, it is instead just stored in the - # "measurement" attribute of the node it measures. - x_meas = [i for i, m in og.measurements.items() if m.plane == Plane.YZ] - x_meas_verts = add_vertices(len(x_meas), VertexType.Z) - - out_verts = add_vertices(len(og.output_nodes), VertexType.BOUNDARY) - g.set_outputs(tuple(out_verts)) - - # Maps a node's ID in the Open Graph to it's corresponding node ID in - # the PyZX graph and vice versa. - map_to_og = dict(zip(body_verts, og.graph.nodes(), strict=True)) - map_to_pyzx = {v: i for i, v in map_to_og.items()} - - # Open Graph's don't have boundary nodes, so we need to connect the - # input and output Z spiders to their corresponding boundary nodes in - # pyzx. - for pyzx_index, og_index in zip(in_verts, og.input_nodes, strict=True): - g.add_edge((pyzx_index, map_to_pyzx[og_index])) - for pyzx_index, og_index in zip(out_verts, og.output_nodes, strict=True): - g.add_edge((pyzx_index, map_to_pyzx[og_index])) - - og_edges = og.graph.edges() - pyzx_edges = ((map_to_pyzx[a], map_to_pyzx[b]) for a, b in og_edges) - g.add_edges(pyzx_edges, EdgeType.HADAMARD) - - # Add the edges between the Z spiders in the graph body - for og_index, meas in og.measurements.items(): - # If it's an X measured node, then we handle it in the next loop - if meas.plane == Plane.XY: - g.set_phase(map_to_pyzx[og_index], -_fraction_of_angle(meas.angle)) - - # Connect the X measured vertices - for og_index, pyzx_index in zip(x_meas, x_meas_verts, strict=True): - g.add_edge((map_to_pyzx[og_index], pyzx_index), EdgeType.HADAMARD) - g.set_phase(pyzx_index, -_fraction_of_angle(og.measurements[og_index].angle)) - - return g - - -def _checked_float(x: FractionLike) -> float: - if not isinstance(x, SupportsFloat): - # Possibly a Poly object - raise TypeError(f"Cannot convert {x} to a float.") - return float(x) - - -def from_pyzx_graph(g: BaseGraph[int, tuple[int, int]]) -> OpenGraph[Measurement]: - """Construct an :class:`OpenGraph` from a :mod:`pyzx` graph. - - This method may add additional nodes to the graph so that it adheres - with the definition of an OpenGraph. For instance, if the final node on - a qubit is measured, it will add two nodes behind it so that no output - nodes are measured to satisfy the requirements of an open graph. - - Example - ------- - >>> import pyzx as zx - >>> from graphix.pyzx import from_pyzx_graph - >>> circ = zx.qasm("qreg q[2]; h q[1]; cx q[0], q[1]; h q[1];") - >>> g = circ.to_graph() - >>> og = from_pyzx_graph(g) - """ - zx.simplify.to_graph_like(g) - - measurements: dict[int, Measurement] = {} - inputs = list(g.inputs()) - outputs = list(g.outputs()) - - g_nx = nx.Graph(g.edges()) - - # We need to do this since the full reduce simplification can - # leave either hadamard or plain wires on the inputs and outputs - for inp in g.inputs(): - first_nbr = next(iter(g.neighbors(inp))) - et = g.edge_type((first_nbr, inp)) - - if et == EdgeType.SIMPLE: - g_nx.remove_node(inp) - inputs = [i if i != inp else first_nbr for i in inputs] - - for out in g.outputs(): - first_nbr = next(iter(g.neighbors(out))) - et = g.edge_type((first_nbr, out)) - - if et == EdgeType.SIMPLE: - g_nx.remove_node(out) - outputs = [o if o != out else first_nbr for o in outputs] - - # Turn all phase gadgets into measurements - # Since we did a full reduce, any node that isn't an input or output - # node and has only one neighbour is definitely a phase gadget. - nodes = list(g_nx.nodes()) - for v in nodes: - if v in inputs or v in outputs: - continue - - nbrs = list(g.neighbors(v)) - if len(nbrs) == 1: - measurements[nbrs[0]] = Measurement.YZ(_checked_float(g.phase(v))) - g_nx.remove_node(v) - - next_id = max(g_nx.nodes) + 1 - - # Since outputs can't be measured, we need to add an extra two nodes - # in to counter it - for out in outputs: - if g.phase(out) == 0: - continue - - g_nx.add_edges_from([(out, next_id), (next_id, next_id + 1)]) - measurements[next_id] = Measurement.X - - outputs = [o if o != out else next_id + 1 for o in outputs] - next_id += 2 - - # Add the phase to all XY measured nodes - for v in g_nx.nodes: - if v in outputs or v in measurements: - continue - - # g.phase() may be a fractions.Fraction object, but Measurement - # expects a float - measurements[v] = Measurement.XY(-_checked_float(g.phase(v))) - - return OpenGraph(g_nx, inputs, outputs, measurements) diff --git a/noxfile.py b/noxfile.py index 5379888a5..bdd1c6ee8 100644 --- a/noxfile.py +++ b/noxfile.py @@ -43,28 +43,10 @@ def tests_minimal(session: Session) -> None: run_pytest(session, mpl=True) -@nox.session(python=PYTHON_VERSIONS) -def tests_dev(session: Session) -> None: - """Run the test suite with dev dependencies.""" - session.install(".[dev]") - # We cannot run `pytest --doctest-modules` here, since some tests - # involve optional dependencies, like pyzx. - run_pytest(session, mpl=True) - - -@nox.session(python=PYTHON_VERSIONS) -def tests_extra(session: Session) -> None: - """Run the test suite with extra dependencies.""" - session.install(".[extra]") - install_pytest(session) - session.install("nox") # needed for `--doctest-modules` - run_pytest(session, doctest_modules=True) - - @nox.session(python=PYTHON_VERSIONS) def tests_all(session: Session) -> None: """Run the test suite with all dependencies.""" - session.install(".[dev,extra]") + session.install(".[dev]") # This dependency is added here to avoid circular dependencies session.install("graphix-qasm-parser>=0.1.1") run_pytest(session, doctest_modules=True, mpl=True) @@ -123,6 +105,7 @@ class ReverseDependency: ), ReverseDependency("https://github.com/TeamGraphix/graphix-ibmq", doctest_modules=False), ReverseDependency("https://github.com/qat-inria/graphix-stim-compiler", branch="ps_dim"), + ReverseDependency("https://github.com/thierry-martinez/graphix-pyzx", branch="pyzx_from_graphix"), ], ) def tests_reverse_dependencies(session: Session, package: ReverseDependency) -> None: diff --git a/pyproject.toml b/pyproject.toml index afe99d0e3..bdb4db5aa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,9 +69,6 @@ doc = [ "sphinx", "sphinx-gallery", ] -extra = [ - "pyzx>=0.10.0", -] typing = [ "numpy==2.4.2; python_version >= '3.11'", "numba==0.65.1; python_version >= '3.11'", diff --git a/tests/test_pyzx.py b/tests/test_pyzx.py deleted file mode 100644 index e4a0d868e..000000000 --- a/tests/test_pyzx.py +++ /dev/null @@ -1,131 +0,0 @@ -from __future__ import annotations - -from copy import deepcopy -from typing import TYPE_CHECKING - -import numpy as np -import pytest -from numpy.random import PCG64, Generator - -from graphix.fundamentals import ANGLE_PI -from graphix.random_objects import rand_circuit -from graphix.transpiler import Circuit - -if TYPE_CHECKING: - from graphix import Pattern - from graphix.sim.statevec import Statevec - -try: - import pyzx as zx - from pyzx.generate import cliffordT as clifford_t # noqa: N813 - - from graphix.pyzx import from_pyzx_graph, to_pyzx_graph -except ImportError: - pytestmark = pytest.mark.skip(reason="pyzx not installed") - - if TYPE_CHECKING: - import sys - - # We skip type-checking the case where there is no pyzx, since - # pyright cannot figure out that tests are skipped in this - # case. - sys.exit(1) - - -if TYPE_CHECKING: - from pyzx.graph.base import BaseGraph - - -def test_graph_equality(fx_rng: Generator) -> None: - # No default value for `byteorder` in Python 3.10 - seed = int.from_bytes(fx_rng.integers(0, 256, size=16, dtype=np.uint8).tobytes(), byteorder="big") - g = clifford_t(4, 10, 0.1, seed=seed) - - og1 = from_pyzx_graph(g) - - g_copy = deepcopy(g) - og2 = from_pyzx_graph(g_copy) - - assert og1.isclose(og2) - - -def assert_reconstructed_pyzx_graph_equal(g: BaseGraph[int, tuple[int, int]]) -> None: - """Convert a graph to and from an Open graph and then checks the resulting pyzx graph is equal to the original.""" - zx.simplify.to_graph_like(g) - - g_copy = deepcopy(g) - og = from_pyzx_graph(g_copy) - reconstructed_pyzx_graph = to_pyzx_graph(og.to_bloch()) - - # The "tensorfy" function break if the rows aren't set for some reason - for v in reconstructed_pyzx_graph.vertices(): - reconstructed_pyzx_graph.set_row(v, 2) - - for v in g.vertices(): - g.set_row(v, 2) - ten = zx.tensorfy(g) - ten_graph = zx.tensorfy(reconstructed_pyzx_graph) - assert zx.compare_tensors(ten, ten_graph) - - -# Tests that compiling from a pyzx graph to an OpenGraph returns the same -# graph. Only works with small circuits up to 4 qubits since PyZX's `tensorfy` -# function seems to consume huge amount of memory for larger qubit -def test_random_clifford_t() -> None: - for _ in range(15): - g = clifford_t(4, 10, 0.1) - assert_reconstructed_pyzx_graph_equal(g) - - -def simulate_pattern(pattern: Pattern, rng: Generator) -> Statevec: - pattern.remove_input_nodes() - pattern.perform_pauli_measurements() - pattern.minimize_space() - return pattern.simulate_pattern(rng=rng) - - -def check_round_trip(pattern: Pattern, rng: Generator, full_reduce: bool) -> bool: - opengraph = pattern.extract_opengraph() - zx_graph = to_pyzx_graph(opengraph.to_bloch()) - if full_reduce: - zx_graph.normalize() - zx.simplify.full_reduce(zx_graph) - opengraph2 = from_pyzx_graph(zx_graph) - pattern2 = opengraph2.infer_pauli_measurements().to_pattern() - state = simulate_pattern(pattern, rng) - state2 = simulate_pattern(pattern2, rng) - return state.isclose(state2) - - -@pytest.mark.parametrize("jumps", range(1, 11)) -@pytest.mark.parametrize("full_reduce", [False, True]) -def test_random_circuit(fx_bg: PCG64, jumps: int, full_reduce: bool) -> None: - rng = Generator(fx_bg.jumped(jumps)) - nqubits = 5 - depth = 5 - circuit = rand_circuit(nqubits, depth, rng, use_rzz=True) - pattern = circuit.transpile().pattern - assert check_round_trip(pattern, rng, full_reduce) - - -def test_rz(fx_rng: Generator) -> None: - circuit = Circuit(2) - circuit.rz(0, ANGLE_PI / 4) - pattern = circuit.transpile().pattern - # pyzx 0.8 does not support arithmetic expressions such as `pi / 4`. - circ = zx.qasm(f"qreg q[2]; rz({np.pi / 4}) q[0];") # type: ignore[attr-defined] - g = circ.to_graph() - og = from_pyzx_graph(g).infer_pauli_measurements() - pattern_zx = og.to_pattern() - state = pattern.simulate_pattern(rng=fx_rng) - state_zx = pattern_zx.simulate_pattern(rng=fx_rng) - assert state_zx.isclose(state) - - -@pytest.mark.parametrize("full_reduce", [False, True]) -def test_ccx(fx_rng: Generator, full_reduce: bool) -> None: - # Issue #235 - circuit = Circuit(3) - circuit.ccx(0, 1, 2) - pattern = circuit.transpile().pattern - assert check_round_trip(pattern, fx_rng, full_reduce) diff --git a/uv.lock b/uv.lock index 453fa7d58..6b34443ed 100644 --- a/uv.lock +++ b/uv.lock @@ -84,15 +84,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bd/46/d3ec57ad500f598d1554bd14ce4df615960549ab2844961bc4e1f5fbd174/ast_serialize-0.3.0-cp39-abi3-win_arm64.whl", hash = "sha256:0dd00da29985f15f50dc35728b7e1e7c84507bccfea1d9914738530f1c72238a", size = 1077165, upload-time = "2026-04-30T23:24:46.377Z" }, ] -[[package]] -name = "asttokens" -version = "3.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, -] - [[package]] name = "attrs" version = "26.1.0" @@ -277,15 +268,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6d/c1/e419ef3723a074172b68aaa89c9f3de486ed4c2399e2dbd8113a4fdcaf9e/colorlog-6.10.1-py3-none-any.whl", hash = "sha256:2d7e8348291948af66122cff006c9f8da6255d224e7cf8e37d8de2df3bad8c9c", size = 11743, upload-time = "2025-10-16T16:14:10.512Z" }, ] -[[package]] -name = "comm" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, -] - [[package]] name = "contourpy" version = "1.3.2" @@ -744,15 +726,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/68/8c/d245fd416c69d27d51f14d5ad62acc4ee5971088ee31c40ffe1cc109af68/cytoolz-1.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:047defa7f5f9a32f82373dbc3957289562e8a3fa58ae02ec8e4dca4f43a33a21", size = 916630, upload-time = "2025-10-19T00:44:54.059Z" }, ] -[[package]] -name = "decorator" -version = "5.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, -] - [[package]] name = "dependency-groups" version = "1.3.1" @@ -821,15 +794,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, ] -[[package]] -name = "executing" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, -] - [[package]] name = "filelock" version = "3.25.2" @@ -962,9 +926,6 @@ doc = [ { name = "sphinx", version = "9.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "sphinx-gallery" }, ] -extra = [ - { name = "pyzx" }, -] typing = [ { name = "numba", marker = "python_full_version >= '3.11'" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -992,7 +953,6 @@ requires-dist = [ { name = "pytest-cov", marker = "extra == 'dev'" }, { name = "pytest-mock", marker = "extra == 'dev'" }, { name = "pytest-mpl", marker = "extra == 'dev'" }, - { name = "pyzx", marker = "extra == 'extra'", specifier = ">=0.10.0" }, { name = "qiskit", marker = "extra == 'dev'", specifier = ">=1.0" }, { name = "qiskit-aer", marker = "extra == 'dev'" }, { name = "qiskit-qasm3-import", marker = "extra == 'dev'" }, @@ -1008,7 +968,7 @@ requires-dist = [ { name = "types-setuptools", marker = "extra == 'dev'" }, { name = "typing-extensions" }, ] -provides-extras = ["dev", "doc", "extra", "typing"] +provides-extras = ["dev", "doc", "typing"] [[package]] name = "humanize" @@ -1055,122 +1015,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] -[[package]] -name = "ipython" -version = "8.39.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, - { name = "decorator", marker = "python_full_version < '3.11'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, - { name = "jedi", marker = "python_full_version < '3.11'" }, - { name = "matplotlib-inline", marker = "python_full_version < '3.11'" }, - { name = "pexpect", marker = "python_full_version < '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit", marker = "python_full_version < '3.11'" }, - { name = "pygments", marker = "python_full_version < '3.11'" }, - { name = "stack-data", marker = "python_full_version < '3.11'" }, - { name = "traitlets", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/40/18/f8598d287006885e7136451fdea0755af4ebcbfe342836f24deefaed1164/ipython-8.39.0.tar.gz", hash = "sha256:4110ae96012c379b8b6db898a07e186c40a2a1ef5d57a7fa83166047d9da7624", size = 5513971, upload-time = "2026-03-27T10:02:13.94Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/56/4cc7fc9e9e3f38fd324f24f8afe0ad8bb5fa41283f37f1aaf9de0612c968/ipython-8.39.0-py3-none-any.whl", hash = "sha256:bb3c51c4fa8148ab1dea07a79584d1c854e234ea44aa1283bcb37bc75054651f", size = 831849, upload-time = "2026-03-27T10:02:07.846Z" }, -] - -[[package]] -name = "ipython" -version = "9.10.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version == '3.11.*'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version == '3.11.*' and sys_platform == 'win32'" }, - { name = "decorator", marker = "python_full_version == '3.11.*'" }, - { name = "ipython-pygments-lexers", marker = "python_full_version == '3.11.*'" }, - { name = "jedi", marker = "python_full_version == '3.11.*'" }, - { name = "matplotlib-inline", marker = "python_full_version == '3.11.*'" }, - { name = "pexpect", marker = "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit", marker = "python_full_version == '3.11.*'" }, - { name = "pygments", marker = "python_full_version == '3.11.*'" }, - { name = "stack-data", marker = "python_full_version == '3.11.*'" }, - { name = "traitlets", marker = "python_full_version == '3.11.*'" }, - { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c5/25/daae0e764047b0a2480c7bbb25d48f4f509b5818636562eeac145d06dfee/ipython-9.10.1.tar.gz", hash = "sha256:e170e9b2a44312484415bdb750492699bf329233b03f2557a9692cce6466ada4", size = 4426663, upload-time = "2026-03-27T09:53:26.244Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/01/09/ba70f8d662d5671687da55ad2cc0064cf795b15e1eea70907532202e7c97/ipython-9.10.1-py3-none-any.whl", hash = "sha256:82d18ae9fb9164ded080c71ef92a182ee35ee7db2395f67616034bebb020a232", size = 622827, upload-time = "2026-03-27T09:53:24.566Z" }, -] - -[[package]] -name = "ipython" -version = "9.12.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version >= '3.12' and sys_platform == 'win32'" }, - { name = "decorator", marker = "python_full_version >= '3.12'" }, - { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.12'" }, - { name = "jedi", marker = "python_full_version >= '3.12'" }, - { name = "matplotlib-inline", marker = "python_full_version >= '3.12'" }, - { name = "pexpect", marker = "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit", marker = "python_full_version >= '3.12'" }, - { name = "pygments", marker = "python_full_version >= '3.12'" }, - { name = "stack-data", marker = "python_full_version >= '3.12'" }, - { name = "traitlets", marker = "python_full_version >= '3.12'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3a/73/7114f80a8f9cabdb13c27732dce24af945b2923dcab80723602f7c8bc2d8/ipython-9.12.0.tar.gz", hash = "sha256:01daa83f504b693ba523b5a407246cabde4eb4513285a3c6acaff11a66735ee4", size = 4428879, upload-time = "2026-03-27T09:42:45.312Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/59/22/906c8108974c673ebef6356c506cebb6870d48cedea3c41e949e2dd556bb/ipython-9.12.0-py3-none-any.whl", hash = "sha256:0f2701e8ee86e117e37f50563205d36feaa259d2e08d4a6bc6b6d74b18ce128d", size = 625661, upload-time = "2026-03-27T09:42:42.831Z" }, -] - -[[package]] -name = "ipython-pygments-lexers" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pygments", marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, -] - -[[package]] -name = "ipywidgets" -version = "8.1.8" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "comm" }, - { name = "ipython", version = "8.39.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.10.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, - { name = "ipython", version = "9.12.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, - { name = "jupyterlab-widgets" }, - { name = "traitlets" }, - { name = "widgetsnbextension" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739, upload-time = "2025-11-01T21:18:12.393Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808, upload-time = "2025-11-01T21:18:10.956Z" }, -] - -[[package]] -name = "jedi" -version = "0.19.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "parso" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, -] - [[package]] name = "jinja2" version = "3.1.6" @@ -1192,15 +1036,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, ] -[[package]] -name = "jupyterlab-widgets" -version = "3.0.16" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423, upload-time = "2025-11-01T21:11:29.724Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926, upload-time = "2025-11-01T21:11:28.008Z" }, -] - [[package]] name = "kiwisolver" version = "1.5.0" @@ -1325,15 +1160,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0a/dd/8050c947d435c8d4bc94e3252f4d8bb8a76cfb424f043a8680be637a57f1/kiwisolver-1.5.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:59cd8683f575d96df5bb48f6add94afc055012c29e28124fcae2b63661b9efb1", size = 73558, upload-time = "2026-03-09T13:15:52.112Z" }, ] -[[package]] -name = "lark" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732, upload-time = "2025-10-27T18:25:56.653Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151, upload-time = "2025-10-27T18:25:54.882Z" }, -] - [[package]] name = "librt" version = "0.11.0" @@ -1611,18 +1437,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6f/87/afead29192170917537934c6aff4b008c805fff7b1ccea0c79120d96beda/matplotlib-3.10.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3fc0364dfbe1d07f6d15c5ebd0c5bf89e126916e5a8667dd4a7a6e84c36653d4", size = 8774002, upload-time = "2026-04-24T00:14:09.816Z" }, ] -[[package]] -name = "matplotlib-inline" -version = "0.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110, upload-time = "2025-10-23T09:00:22.126Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, -] - [[package]] name = "mpmath" version = "1.3.0" @@ -2010,15 +1824,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, ] -[[package]] -name = "parso" -version = "0.8.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/76/a1e769043c0c0c9fe391b702539d594731a4362334cdf4dc25d0c09761e7/parso-0.8.6.tar.gz", hash = "sha256:2b9a0332696df97d454fa67b81618fd69c35a7b90327cbe6ba5c92d2c68a7bfd", size = 401621, upload-time = "2026-02-09T15:45:24.425Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl", hash = "sha256:2c549f800b70a5c4952197248825584cb00f033b29c692671d3bf08bf380baff", size = 106894, upload-time = "2026-02-09T15:45:21.391Z" }, -] - [[package]] name = "pathspec" version = "1.0.4" @@ -2028,18 +1833,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, ] -[[package]] -name = "pexpect" -version = "4.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ptyprocess" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, -] - [[package]] name = "pillow" version = "12.2.0" @@ -2172,18 +1965,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/80/6e/4b28b62ecb6aae56769c34a8ff1d661473ec1e9519e2d5f8b2c150086b26/pre_commit-4.6.0-py2.py3-none-any.whl", hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b", size = 226472, upload-time = "2026-04-21T20:31:40.092Z" }, ] -[[package]] -name = "prompt-toolkit" -version = "3.0.52" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wcwidth" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, -] - [[package]] name = "psutil" version = "7.2.2" @@ -2212,24 +1993,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, ] -[[package]] -name = "ptyprocess" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, -] - -[[package]] -name = "pure-eval" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, -] - [[package]] name = "py-cpuinfo" version = "9.0.0" @@ -2257,15 +2020,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, ] -[[package]] -name = "pyperclip" -version = "1.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, -] - [[package]] name = "pyright" version = "1.1.409" @@ -2441,24 +2195,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] -[[package]] -name = "pyzx" -version = "0.10.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ipywidgets" }, - { name = "lark" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pyperclip" }, - { name = "tqdm" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9d/c0/42142c8548a5f0dc17a8cab9f019ccb753c8a0bd71b0924ddbe0cd8b7929/pyzx-0.10.2.tar.gz", hash = "sha256:080217851a4e422d020742902732d36d811a95db85522e1b4fac0a87ab7599c6", size = 507438, upload-time = "2026-05-01T10:19:42.795Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/48/ac/1c5efee9396321c23d7ea42b7a232bdc5cb80075798fe5d98b8aaabaf29d/pyzx-0.10.2-py3-none-any.whl", hash = "sha256:5fb9298910626a6c39169a35861dfff2259de5387d044a95fe119b545b9bef68", size = 543764, upload-time = "2026-05-01T10:19:40.98Z" }, -] - [[package]] name = "qiskit" version = "2.3.1" @@ -3034,20 +2770,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" }, ] -[[package]] -name = "stack-data" -version = "0.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "asttokens" }, - { name = "executing" }, - { name = "pure-eval" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, -] - [[package]] name = "stevedore" version = "5.7.0" @@ -3144,15 +2866,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, ] -[[package]] -name = "traitlets" -version = "5.14.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, -] - [[package]] name = "types-networkx" version = "3.6.1.20260513" @@ -3217,21 +2930,3 @@ sdist = { url = "https://files.pythonhosted.org/packages/60/8c/bdd9f89f89e4a787a wheels = [ { url = "https://files.pythonhosted.org/packages/95/19/bc7c4e05f42532863cf2ae7e7e847beab25835934e0410160b47eeff1e35/virtualenv-21.2.3-py3-none-any.whl", hash = "sha256:486652347ea8526d91e9807c0274583cb7ba31dd4942ff10fb5621402f0fe0d8", size = 5828329, upload-time = "2026-04-14T01:10:34.809Z" }, ] - -[[package]] -name = "wcwidth" -version = "0.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684, upload-time = "2026-02-06T19:19:40.919Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" }, -] - -[[package]] -name = "widgetsnbextension" -version = "4.0.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402, upload-time = "2025-11-01T21:15:55.178Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503, upload-time = "2025-11-01T21:15:53.565Z" }, -] From ba46e52164a897994a00a8be4ad66b404de79b42 Mon Sep 17 00:00:00 2001 From: Thierry Martinez Date: Thu, 28 May 2026 00:20:45 +0200 Subject: [PATCH 2/3] Document `extra` dependency group removal --- CHANGELOG.md | 3 ++- CONTRIBUTING.md | 2 +- README.md | 10 ++-------- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb032e9b1..f28f41e73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,7 +51,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - #507: Static method `PauliString.from_measured_node` is subsumed by the function `extraction_ps_from_corrected_node`. -- #468, #511: `pyzx` module is moved in a separate plugin: https://github.com/thierry-martinez/graphix-pyzx/ +- #468, #511: The `pyzx` module has been moved to a separate plugin: https://github.com/thierry-martinez/graphix-pyzx/ + Consequently, the `pyproject.toml` no longer defines an `extra` dependency group for the `pyzx` package. ## [0.3.5] - 2026-03-26 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c3637d607..75d732829 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,7 +15,7 @@ Once created, you'll need to clone the repository, and you can follow below to s git clone git@github.com:/graphix.git cd graphix pip install uv -uv sync --extra dev --extra extra +uv sync --extra dev ``` ## Local checks diff --git a/README.md b/README.md index 6616189a2..9a4b61cee 100644 --- a/README.md +++ b/README.md @@ -28,12 +28,6 @@ Install `graphix` with `pip`: pip install graphix ``` -Install with [extra dependencies](https://github.com/TeamGraphix/graphix/blob/master/pyproject.toml) (e.g. pyzx): - -```bash -pip install graphix[extra] -``` - ### Editable installation using pip and uv As well as support for editable installation using pip, Graphix also supports using `uv`: @@ -41,10 +35,10 @@ As well as support for editable installation using pip, Graphix also supports us ```bash git clone https://github.com/TeamGraphix/graphix.git cd graphix -uv sync --extra dev --extra extra +uv sync --extra dev ``` -This creates a virtual environment and installs all development and extra dependencies from the `pyproject.toml` and `uv` lockfile. +This creates a virtual environment and installs all development dependencies from the `pyproject.toml` and `uv` lockfile. ## Using graphix From 13671dc241ca24705536e6825c0911fd14bab5ba Mon Sep 17 00:00:00 2001 From: Thierry Martinez Date: Thu, 28 May 2026 00:22:56 +0200 Subject: [PATCH 3/3] Remove `test_pyzx.py` --- tests/test_pyzx.py | 130 --------------------------------------------- 1 file changed, 130 deletions(-) delete mode 100644 tests/test_pyzx.py diff --git a/tests/test_pyzx.py b/tests/test_pyzx.py deleted file mode 100644 index a45762e18..000000000 --- a/tests/test_pyzx.py +++ /dev/null @@ -1,130 +0,0 @@ -from __future__ import annotations - -from copy import deepcopy -from typing import TYPE_CHECKING - -import numpy as np -import pytest -from numpy.random import PCG64, Generator - -from graphix.fundamentals import ANGLE_PI -from graphix.random_objects import rand_circuit -from graphix.transpiler import Circuit - -if TYPE_CHECKING: - from graphix import Pattern - from graphix.sim.statevec import Statevec - -try: - import pyzx as zx - from pyzx.generate import cliffordT as clifford_t # noqa: N813 - - from graphix.pyzx import from_pyzx_graph, to_pyzx_graph -except ImportError: - pytestmark = pytest.mark.skip(reason="pyzx not installed") - - if TYPE_CHECKING: - import sys - - # We skip type-checking the case where there is no pyzx, since - # pyright cannot figure out that tests are skipped in this - # case. - sys.exit(1) - - -if TYPE_CHECKING: - from pyzx.graph.base import BaseGraph - - -def test_graph_equality(fx_rng: Generator) -> None: - # No default value for `byteorder` in Python 3.10 - seed = int.from_bytes(fx_rng.integers(0, 256, size=16, dtype=np.uint8).tobytes(), byteorder="big") - g = clifford_t(4, 10, 0.1, seed=seed) - - og1 = from_pyzx_graph(g) - - g_copy = deepcopy(g) - og2 = from_pyzx_graph(g_copy) - - assert og1.isclose(og2) - - -def assert_reconstructed_pyzx_graph_equal(g: BaseGraph[int, tuple[int, int]]) -> None: - """Convert a graph to and from an Open graph and then checks the resulting pyzx graph is equal to the original.""" - zx.simplify.to_graph_like(g) - - g_copy = deepcopy(g) - og = from_pyzx_graph(g_copy) - reconstructed_pyzx_graph = to_pyzx_graph(og.to_bloch()) - - # The "tensorfy" function break if the rows aren't set for some reason - for v in reconstructed_pyzx_graph.vertices(): - reconstructed_pyzx_graph.set_row(v, 2) - - for v in g.vertices(): - g.set_row(v, 2) - ten = zx.tensorfy(g) - ten_graph = zx.tensorfy(reconstructed_pyzx_graph) - assert zx.compare_tensors(ten, ten_graph) - - -# Tests that compiling from a pyzx graph to an OpenGraph returns the same -# graph. Only works with small circuits up to 4 qubits since PyZX's `tensorfy` -# function seems to consume huge amount of memory for larger qubit -def test_random_clifford_t() -> None: - for _ in range(15): - g = clifford_t(4, 10, 0.1) - assert_reconstructed_pyzx_graph_equal(g) - - -def simulate_pattern(pattern: Pattern, rng: Generator) -> Statevec: - pattern.remove_pauli_measurements() - pattern.minimize_space() - return pattern.simulate_pattern(rng=rng) - - -def check_round_trip(pattern: Pattern, rng: Generator, full_reduce: bool) -> bool: - opengraph = pattern.extract_opengraph() - zx_graph = to_pyzx_graph(opengraph.to_bloch()) - if full_reduce: - zx_graph.normalize() - zx.simplify.full_reduce(zx_graph) - opengraph2 = from_pyzx_graph(zx_graph) - pattern2 = opengraph2.infer_pauli_measurements().to_pattern() - state = simulate_pattern(pattern, rng) - state2 = simulate_pattern(pattern2, rng) - return state.isclose(state2) - - -@pytest.mark.parametrize("jumps", range(1, 11)) -@pytest.mark.parametrize("full_reduce", [False, True]) -def test_random_circuit(fx_bg: PCG64, jumps: int, full_reduce: bool) -> None: - rng = Generator(fx_bg.jumped(jumps)) - nqubits = 5 - depth = 5 - circuit = rand_circuit(nqubits, depth, rng, use_rzz=True) - pattern = circuit.transpile().pattern - assert check_round_trip(pattern, rng, full_reduce) - - -def test_rz(fx_rng: Generator) -> None: - circuit = Circuit(2) - circuit.rz(0, ANGLE_PI / 4) - pattern = circuit.transpile().pattern - # pyzx 0.8 does not support arithmetic expressions such as `pi / 4`. - circ = zx.qasm(f"qreg q[2]; rz({np.pi / 4}) q[0];") # type: ignore[attr-defined] - g = circ.to_graph() - og = from_pyzx_graph(g).infer_pauli_measurements() - pattern_zx = og.to_pattern() - state = pattern.simulate_pattern(rng=fx_rng) - state_zx = pattern_zx.simulate_pattern(rng=fx_rng) - assert state_zx.isclose(state) - - -@pytest.mark.parametrize("full_reduce", [False, True]) -def test_ccx(fx_rng: Generator, full_reduce: bool) -> None: - # Issue #235 - circuit = Circuit(3) - circuit.ccx(0, 1, 2) - pattern = circuit.transpile().pattern - assert check_round_trip(pattern, fx_rng, full_reduce)