From 7a5a96625c9fa0880fc6f559ad0b28edebc8f56e Mon Sep 17 00:00:00 2001 From: matulni Date: Tue, 7 Jul 2026 13:41:42 +0200 Subject: [PATCH 1/6] Change to_ methods --- docs/source/modifier.rst | 6 +-- examples/visualization.py | 2 +- graphix/circ_ext/extraction.py | 2 +- graphix/flow/core.py | 28 +++++----- graphix/opengraph.py | 70 ++++++++++++------------- graphix/optimization.py | 8 ++- graphix/pattern.py | 48 ++++++++--------- graphix/space_minimization.py | 2 +- graphix/transpiler.py | 8 +-- tests/test_circ_extraction.py | 14 ++--- tests/test_clifford.py | 2 +- tests/test_flow_core.py | 24 ++++----- tests/test_opengraph.py | 34 ++++++------ tests/test_optimization.py | 2 +- tests/test_pattern.py | 56 ++++++++++---------- tests/test_pauli_flow_extraction.py | 64 +++++++++++----------- tests/test_pretty_print.py | 23 ++++---- tests/test_remove_pauli_measurements.py | 10 ++-- tests/test_transpiler.py | 8 +-- tests/test_visualization.py | 12 ++--- 20 files changed, 205 insertions(+), 218 deletions(-) diff --git a/docs/source/modifier.rst b/docs/source/modifier.rst index 2d3b96073..5bd6679b2 100644 --- a/docs/source/modifier.rst +++ b/docs/source/modifier.rst @@ -54,11 +54,11 @@ Pattern Manipulation .. automethod:: extract_nodes - .. automethod:: extract_causal_flow + .. automethod:: to_causalflow - .. automethod:: extract_gflow + .. automethod:: to_gflow - .. automethod:: extract_opengraph + .. automethod:: to_opengraph .. automethod:: extract_measurement_commands diff --git a/examples/visualization.py b/examples/visualization.py index d6e0e68de..205b12040 100644 --- a/examples/visualization.py +++ b/examples/visualization.py @@ -88,7 +88,7 @@ 3: Measurement.YZ(0), } og = OpenGraph(graph, inputs, outputs, measurements) -cf = og.extract_gflow() +cf = og.to_gflow() cf.draw(measurement_labels=True) # %% diff --git a/graphix/circ_ext/extraction.py b/graphix/circ_ext/extraction.py index f55c0a559..5d5fa1449 100644 --- a/graphix/circ_ext/extraction.py +++ b/graphix/circ_ext/extraction.py @@ -676,7 +676,7 @@ def clifford_x_map_from_focused_flow(flow: PauliFlow[Measurement]) -> tuple[Paul og_extended, ancillary_inputs_map = extend_input(og) # Here it's crucial to not infer Pauli measurements to avoid converting measurements inadvertently. - flow_extended = og_extended.extract_pauli_flow() + flow_extended = og_extended.to_pauliflow() # `flow_extended` is guaranteed to be focused if `flow` is focused. # This function assumes that `flow` is focused and does not check it. diff --git a/graphix/flow/core.py b/graphix/flow/core.py index c473ac605..3ea981bcd 100644 --- a/graphix/flow/core.py +++ b/graphix/flow/core.py @@ -208,7 +208,7 @@ def to_pattern( pattern.reorder_output_nodes(self.og.output_nodes) return pattern - def to_causal_flow(self: XZCorrections[_PM_co]) -> CausalFlow[_PM_co]: + def to_causalflow(self: XZCorrections[_PM_co]) -> CausalFlow[_PM_co]: r"""Extract a causal flow from XZ-corrections. This method does not invoke the flow-extraction routine on the underlying open graph. @@ -268,7 +268,7 @@ def to_gflow(self: XZCorrections[_PM_co]) -> GFlow[_PM_co]: gf.check_well_formed() # Raises a `FlowError` if the partial order and the correction function are not compatible. return gf - def to_pauli_flow(self) -> PauliFlow[_AM_co]: + def to_pauliflow(self) -> PauliFlow[_AM_co]: r"""Extract a Pauli flow from XZ-corrections. This method does not invoke the flow-extraction routine on the underlying open graph. @@ -279,7 +279,7 @@ def to_pauli_flow(self) -> PauliFlow[_AM_co]: The difficulty, compared with :meth:`to_gflow`, is that the Pauli-flow correction sets may contain *anachronical corrections*: corrections targeting nodes measured in the X or Y Pauli bases that lie in the present or the past of the corrected node. - Such corrections do not appear in the pattern, because :meth:`PauliFlow.to_corrections` + Such corrections do not appear in the pattern, because :meth:`PauliFlow.to_xzcorrections` only keeps the part of each correction set lying in the future (the ``& future`` filter). They must therefore be reconstructed rather than read off the corrections. @@ -581,7 +581,7 @@ class PauliFlow(Generic[_AM_co]): ----- - See Definition 5 in Ref. [1] for a definition of Pauli flow. - - The flow's correction function defines a partial order (see Def. 2.8 and 2.9, Lemma 2.11 and Theorem 2.12 in Ref. [2]), therefore, only `og` and `correction_function` are necessary to initialize an `PauliFlow` instance (see :func:`PauliFlow.try_from_correction_matrix`). However, flow-finding algorithms generate a partial order in a layer form, which is necessary to extract the flow's XZ-corrections, so it is stored as an attribute. + - The flow's correction function defines a partial order (see Def. 2.8 and 2.9, Lemma 2.11 and Theorem 2.12 in Ref. [2]), therefore, only `og` and `correction_function` are necessary to initialize an `PauliFlow` instance (see :func:`PauliFlow.from_correction_matrix_or_none`). However, flow-finding algorithms generate a partial order in a layer form, which is necessary to extract the flow's XZ-corrections, so it is stored as an attribute. - A correct flow can only exist on an open graph with output nodes, so `layers[0]` always contains a finite set of nodes. @@ -598,7 +598,7 @@ class PauliFlow(Generic[_AM_co]): _CF_PREFIX: str = "p" # Correction function prefix for printing @classmethod - def try_from_correction_matrix(cls, correction_matrix: CorrectionMatrix[_AM_co]) -> Self | None: + def from_correction_matrix_or_none(cls, correction_matrix: CorrectionMatrix[_AM_co]) -> Self | None: """Initialize a `PauliFlow` object from a matrix encoding a correction function. Parameters @@ -626,7 +626,7 @@ def try_from_correction_matrix(cls, correction_matrix: CorrectionMatrix[_AM_co]) return cls(correction_matrix.aog.og, correction_function, partial_order_layers) - def to_corrections(self) -> XZCorrections[_AM_co]: + def to_xzcorrections(self) -> XZCorrections[_AM_co]: """Compute the X and Z corrections induced by the Pauli flow encoded in `self`. Returns @@ -975,7 +975,7 @@ def extract_circuit(self: PauliFlow[Measurement]) -> ExtractionResult: ----- - This method implements the algorithm in [1]. - - Flows are guaranteed to be focused if obtained from :func:`OpenGraph.extract_pauli_flow` or :func:`OpenGraph.extract_gflow` (see [2]). + - Flows are guaranteed to be focused if obtained from :func:`OpenGraph.to_pauliflow` or :func:`OpenGraph.to_gflow` (see [2]). References ---------- @@ -1009,7 +1009,7 @@ class GFlow(PauliFlow[_PM_co], Generic[_PM_co]): @override @classmethod - def try_from_correction_matrix(cls, correction_matrix: CorrectionMatrix[_PM_co]) -> Self | None: + def from_correction_matrix_or_none(cls, correction_matrix: CorrectionMatrix[_PM_co]) -> Self | None: """Initialize a `GFlow` object from a matrix encoding a correction function. Parameters @@ -1030,10 +1030,10 @@ def try_from_correction_matrix(cls, correction_matrix: CorrectionMatrix[_PM_co]) ---------- [1] Mitosek and Backens, 2024 (arXiv:2410.23439). """ - return super().try_from_correction_matrix(correction_matrix) + return super().from_correction_matrix_or_none(correction_matrix) @override - def to_corrections(self) -> XZCorrections[_PM_co]: + def to_xzcorrections(self) -> XZCorrections[_PM_co]: r"""Compute the XZ-corrections induced by the generalised flow encoded in `self`. Returns @@ -1187,11 +1187,11 @@ class CausalFlow(GFlow[_PM_co], Generic[_PM_co]): @override @classmethod - def try_from_correction_matrix(cls, correction_matrix: CorrectionMatrix[_PM_co]) -> None: + def from_correction_matrix_or_none(cls, correction_matrix: CorrectionMatrix[_PM_co]) -> None: raise NotImplementedError("Initialization of a causal flow from a correction matrix is not supported.") @override - def to_corrections(self) -> XZCorrections[_PM_co]: + def to_xzcorrections(self) -> XZCorrections[_PM_co]: r"""Compute the XZ-corrections induced by the causal flow encoded in `self`. Returns @@ -1458,7 +1458,7 @@ def _solve_pauli_correction_set( ) -> set[int] | None: """Reconstruct the Pauli-flow correction set of a single ``node``. - See :meth:`XZCorrections.to_pauli_flow` for the description of the GF(2) system solved here. + See :meth:`XZCorrections.to_pauliflow` for the description of the GF(2) system solved here. """ og = xz.og nodes = set(og.graph.nodes) @@ -1567,7 +1567,7 @@ def row_at(g: int) -> list[int]: def _reconstruct_pauli_correction_function(xz: XZCorrections[AbstractMeasurement]) -> dict[int, set[int]]: """Reconstruct a Pauli-flow correction function from XZ-corrections. - See :meth:`XZCorrections.to_pauli_flow`. + See :meth:`XZCorrections.to_pauliflow`. """ og = xz.og adjacency: dict[int, set[int]] = {n: og.neighbors({n}) for n in og.graph.nodes} diff --git a/graphix/opengraph.py b/graphix/opengraph.py index 3f1cba960..3618a5679 100644 --- a/graphix/opengraph.py +++ b/graphix/opengraph.py @@ -51,7 +51,7 @@ class OpenGraph(Generic[_AM_co]): measurements : Mapping[int, _AM_co] A mapping between the non-output nodes of the open graph (``key``) and their corresponding measurement label (``value``). Measurement labels can be specified as `Measurement`, `Plane` or `Axis` instances. output_cliffords: Mapping[int, Clifford] - A mapping between output nodes of the open graph (``key``) and Clifford operators. This attribute allows to preserve the semantics of (deterministic) patterns with local-clifford commands when extracting the open graph. See :meth:`Pattern.extract_opengraph`. + A mapping between output nodes of the open graph (``key``) and Clifford operators. This attribute allows to preserve the semantics of (deterministic) patterns with local-clifford commands when extracting the open graph. See :meth:`Pattern.to_opengraph`. Notes ----- @@ -134,11 +134,11 @@ def to_pattern(self: OpenGraph[Measurement], *, stacklevel: int = 1) -> Pattern: except TypeError: flow: PauliFlow[Measurement] | None = None else: - flow = bloch_case.find_causal_flow() + flow = bloch_case.to_causalflow_or_none() if flow is None: - flow = self.find_pauli_flow(stacklevel=stacklevel + 1) + flow = self.to_pauliflow_or_none(stacklevel=stacklevel + 1) if flow is not None: - return flow.to_corrections().to_pattern() + return flow.to_xzcorrections().to_pattern() raise OpenGraphError("The open graph does not have flow. It does not support a deterministic pattern.") def draw(self, **options: Unpack[DrawKwargs]) -> None: @@ -208,7 +208,7 @@ def downcast_bloch(self: OpenGraph[Measurement]) -> OpenGraph[BlochMeasurement]: the original one, but this function narrows the static type of the measurements. The returned graph can be used with functions that require all measurements are described as Bloch - measurements, such as :func:`OpenGraph.extract_causal_flow`. + measurements, such as :func:`OpenGraph.to_causalflow`. Example ------- @@ -239,7 +239,7 @@ def infer_pauli_measurements( i.e. an integer multiple of :math:`\pi/2`. This method can be used before calling methods such as - `OpenGraph.find_pauli_flow`, that deal with Pauli measurements + `OpenGraph.to_pauliflow_or_none`, that deal with Pauli measurements in a special way. Parameters @@ -368,10 +368,10 @@ def odd_neighbors(self, nodes: Collection[int]) -> set[int]: odd_neighbors_set ^= self.neighbors([node]) return odd_neighbors_set - def extract_causal_flow(self: OpenGraph[_PM_co]) -> CausalFlow[_PM_co]: + def to_causalflow(self: OpenGraph[_PM_co]) -> CausalFlow[_PM_co]: """Try to extract a causal flow on the open graph. - This method is a wrapper over :func:`OpenGraph.find_causal_flow` with a single return type. + This method is a wrapper over :func:`OpenGraph.to_causalflow_or_none` with a single return type. This method requires all measurements to be planar: to extract the causal flow from a graph that contains Pauli measurements, @@ -389,18 +389,18 @@ def extract_causal_flow(self: OpenGraph[_PM_co]) -> CausalFlow[_PM_co]: See Also -------- - :func:`OpenGraph.find_causal_flow` + :func:`OpenGraph.to_causalflow_or_none` """ - cf = self.find_causal_flow() + cf = self.to_causalflow_or_none() if cf is None: raise OpenGraphError("The open graph does not have a causal flow.") return cf - def extract_gflow(self: OpenGraph[_PM_co]) -> GFlow[_PM_co]: + def to_gflow(self: OpenGraph[_PM_co]) -> GFlow[_PM_co]: r"""Try to extract a maximally delayed generalised flow (gflow) on the open graph. - This method is a wrapper over :func:`OpenGraph.find_gflow` with a single return type. + This method is a wrapper over :func:`OpenGraph.to_gflow_or_none` with a single return type. This method requires all measurements to be planar: to extract a gflow from a graph that contains Pauli measurements, you @@ -418,18 +418,18 @@ def extract_gflow(self: OpenGraph[_PM_co]) -> GFlow[_PM_co]: See Also -------- - :func:`OpenGraph.find_gflow` + :func:`OpenGraph.to_gflow_or_none` """ - gf = self.find_gflow() + gf = self.to_gflow_or_none() if gf is None: raise OpenGraphError("The open graph does not have a gflow.") return gf - def extract_pauli_flow(self: OpenGraph[_AM_co], *, stacklevel: int = 1) -> PauliFlow[_AM_co]: + def to_pauliflow(self: OpenGraph[_AM_co], *, stacklevel: int = 1) -> PauliFlow[_AM_co]: r"""Try to extract a maximally delayed Pauli on the open graph. - This method is a wrapper over :func:`OpenGraph.find_pauli_flow` with a single return type. + This method is a wrapper over :func:`OpenGraph.to_pauliflow_or_none` with a single return type. Parameters ---------- @@ -449,14 +449,14 @@ def extract_pauli_flow(self: OpenGraph[_AM_co], *, stacklevel: int = 1) -> Pauli See Also -------- - :func:`OpenGraph.find_pauli_flow`, :func:`OpenGraph.infer_pauli_measurements` + :func:`OpenGraph.to_pauliflow_or_none`, :func:`OpenGraph.infer_pauli_measurements` """ - pf = self.find_pauli_flow(stacklevel=stacklevel + 1) + pf = self.to_pauliflow_or_none(stacklevel=stacklevel + 1) if pf is None: raise OpenGraphError("The open graph does not have a Pauli flow.") return pf - def find_causal_flow(self: OpenGraph[_PM_co]) -> CausalFlow[_PM_co] | None: + def to_causalflow_or_none(self: OpenGraph[_PM_co]) -> CausalFlow[_PM_co] | None: """Return a causal flow on the open graph if it exists. This method requires all measurements to be planar: to find @@ -470,7 +470,7 @@ def find_causal_flow(self: OpenGraph[_PM_co]) -> CausalFlow[_PM_co] | None: See Also -------- - :func:`OpenGraph.extract_causal_flow` + :func:`OpenGraph.to_causalflow` Notes ----- @@ -483,7 +483,7 @@ def find_causal_flow(self: OpenGraph[_PM_co]) -> CausalFlow[_PM_co] | None: """ return find_cflow(self) - def find_gflow(self: OpenGraph[_PM_co]) -> GFlow[_PM_co] | None: + def to_gflow_or_none(self: OpenGraph[_PM_co]) -> GFlow[_PM_co] | None: r"""Return a maximally delayed Pauli on the open graph if it exists. This method requires all measurements to be planar: to find @@ -497,11 +497,11 @@ def find_gflow(self: OpenGraph[_PM_co]) -> GFlow[_PM_co] | None: See Also -------- - :func:`OpenGraph.extract_gflow` + :func:`OpenGraph.to_gflow` Notes ----- - - The open graph instance must be of parametric type `Measurement` or `Plane` since the gflow is only defined on open graphs with planar measurements. Measurement instances with a Pauli angle (integer multiple of :math:`\pi/2`) are interpreted as `Plane` instances, in contrast with :func:`OpenGraph.find_pauli_flow`. + - The open graph instance must be of parametric type `Measurement` or `Plane` since the gflow is only defined on open graphs with planar measurements. Measurement instances with a Pauli angle (integer multiple of :math:`\pi/2`) are interpreted as `Plane` instances, in contrast with :func:`OpenGraph.to_pauliflow_or_none`. - This function implements the algorithm presented in Ref. [1] with polynomial complexity on the number of nodes, :math:`O(N^3)`. References @@ -512,11 +512,11 @@ def find_gflow(self: OpenGraph[_PM_co]) -> GFlow[_PM_co] | None: correction_matrix = compute_correction_matrix(aog) if correction_matrix is None: return None - return GFlow.try_from_correction_matrix( + return GFlow.from_correction_matrix_or_none( correction_matrix ) # The constructor returns `None` if the correction matrix is not compatible with any partial order on the open graph. - def find_pauli_flow(self: OpenGraph[_AM_co], *, stacklevel: int = 1) -> PauliFlow[_AM_co] | None: + def to_pauliflow_or_none(self: OpenGraph[_AM_co], *, stacklevel: int = 1) -> PauliFlow[_AM_co] | None: r"""Return a maximally delayed Pauli on the open graph if it exists. Parameters @@ -532,7 +532,7 @@ def find_pauli_flow(self: OpenGraph[_AM_co], *, stacklevel: int = 1) -> PauliFlo See Also -------- - :func:`OpenGraph.extract_pauli_flow`, :func:`OpenGraph.infer_pauli_measurements` + :func:`OpenGraph.to_pauliflow`, :func:`OpenGraph.infer_pauli_measurements` Notes ----- @@ -551,13 +551,13 @@ def find_pauli_flow(self: OpenGraph[_AM_co], *, stacklevel: int = 1) -> PauliFlo >>> graph = nx.Graph([(0, 1), (1, 2)]) >>> measurements = {0: Measurement.XZ(0.5), 1: Measurement.XZ(0.5)} >>> og = OpenGraph(graph, [0], [2], measurements) - >>> og.extract_pauli_flow() + >>> og.to_pauliflow() Traceback (most recent call last): ... graphix.opengraph.OpenGraphError: The open graph does not have a Pauli flow. >>> og.infer_pauli_measurements().measurements {0: Measurement.X, 1: Measurement.X} - >>> str(og.infer_pauli_measurements().extract_pauli_flow()) + >>> str(og.infer_pauli_measurements().to_pauliflow()) 'p(0) = {1}, p(1) = {2}; {0, 1} < {2}' """ self._warn_non_inferred_pauli_measurements(stacklevel=stacklevel + 1) @@ -565,11 +565,11 @@ def find_pauli_flow(self: OpenGraph[_AM_co], *, stacklevel: int = 1) -> PauliFlo correction_matrix = compute_correction_matrix(aog) if correction_matrix is None: return None - return PauliFlow.try_from_correction_matrix( + return PauliFlow.from_correction_matrix_or_none( correction_matrix ) # The constructor returns `None` if the correction matrix is not compatible with any partial order on the open graph. - def extract_circuit( + def to_circuit( self: OpenGraph[Measurement], pexp_cp: Callable[[PauliExponentialDAG, Circuit], None] | None = None, cm_cp: Callable[[CliffordMap, Circuit], None] | None = None, @@ -637,18 +637,14 @@ def extract_circuit( ... output_nodes=(2, 5, 8), ... measurements=dict.fromkeys((0, 1, 3, 4, 6, 7), Measurement.XY(angle=0)), ... ) - >>> og.extract_circuit() + >>> og.to_circuit() Circuit(width=3, instr=[H(2), H(1), CNOT(2, 1), H(1), H(1), H(0), CNOT(2, 0), CNOT(1, 0), H(2), H(1), H(0)]) >>> # The default compilation passes do not exploit the lower depth of the Pauli flow >>> # compared the gflow. - >>> og.infer_pauli_measurements().extract_circuit() + >>> og.infer_pauli_measurements().to_circuit() Circuit(width=3, instr=[H(2), H(1), CNOT(2, 1), H(1), H(1), H(0), CNOT(2, 0), CNOT(1, 0), H(2), H(1), H(0)]) """ - return ( - self.extract_pauli_flow(stacklevel=stacklevel + 1) - .extract_circuit() - .to_circuit(pexp_cp=pexp_cp, cm_cp=cm_cp) - ) + return self.to_pauliflow(stacklevel=stacklevel + 1).extract_circuit().to_circuit(pexp_cp=pexp_cp, cm_cp=cm_cp) def compose(self, other: OpenGraph[_AM_co], mapping: Mapping[int, int]) -> tuple[OpenGraph[_AM_co], dict[int, int]]: r"""Compose two open graphs by merging subsets of nodes from ``self`` and ``other``, and relabeling the nodes of ``other`` that were not merged. diff --git a/graphix/optimization.py b/graphix/optimization.py index e3981b763..e60cb9585 100644 --- a/graphix/optimization.py +++ b/graphix/optimization.py @@ -335,7 +335,7 @@ def to_space_optimal_pattern(self) -> Pattern: """ return standardized_to_space_optimal_pattern(self) - def extract_opengraph(self) -> OpenGraph[Measurement]: + def to_opengraph(self) -> OpenGraph[Measurement]: r"""Extract the underlying resource-state open graph from the pattern. Returns @@ -415,7 +415,7 @@ def process_domain(node: Node, domain: AbstractSet[Node]) -> None: return oset, *generations[::-1] return generations[::-1] - def extract_xzcorrections(self) -> XZCorrections[Measurement]: + def to_xzcorrections(self) -> XZCorrections[Measurement]: r"""Extract the XZ-corrections from the current measurement pattern. Returns @@ -443,9 +443,7 @@ def extract_xzcorrections(self) -> XZCorrections[Measurement]: for node, domain in self.z_dict.items(): _update_corrections(node, domain, z_corr) - og = ( - self.extract_opengraph() - ) # Raises a `ValueError` if `N` commands in the pattern do not represent a |+⟩ state. + og = self.to_opengraph() # Raises a `ValueError` if `N` commands in the pattern do not represent a |+⟩ state. return XZCorrections.from_measured_nodes_mapping( og, x_corr, z_corr diff --git a/graphix/pattern.py b/graphix/pattern.py index ddfebcf3d..f0d447800 100644 --- a/graphix/pattern.py +++ b/graphix/pattern.py @@ -1037,7 +1037,7 @@ def extract_partial_order_layers(self) -> tuple[frozenset[int], ...]: """ return optimization.StandardizedPattern.from_pattern(self).extract_partial_order_layers() - def extract_causal_flow(self) -> CausalFlow[BlochMeasurement]: + def to_causalflow(self) -> CausalFlow[BlochMeasurement]: r"""Extract the causal flow structure from the current measurement pattern. This method does not call the flow-extraction routine on the underlying open graph, but constructs the flow from the pattern corrections instead. @@ -1059,12 +1059,12 @@ def extract_causal_flow(self) -> CausalFlow[BlochMeasurement]: Notes ----- - - Applying the chain ``Pattern.extract_causal_flow().to_corrections().to_pattern()`` to a strongly deterministic pattern returns a new pattern implementing the same unitary transformation. This equivalence holds as long as the original pattern contains no Clifford commands, since those are discarded during open-graph extraction. + - Applying the chain ``Pattern.to_causalflow().to_xzcorrections().to_pattern()`` to a strongly deterministic pattern returns a new pattern implementing the same unitary transformation. This equivalence holds as long as the original pattern contains no Clifford commands, since those are discarded during open-graph extraction. - This method requires that all the measurements in the pattern are represented as Bloch measurements (i.e., there are no :class:`PauliMeasurement`s). Use :meth:`to_bloch()` to convert all Pauli measurements. """ - return self.extract_xzcorrections().downcast_bloch().to_causal_flow() + return self.to_xzcorrections().downcast_bloch().to_causalflow() - def extract_gflow(self) -> GFlow[BlochMeasurement]: + def to_gflow(self) -> GFlow[BlochMeasurement]: r"""Extract the generalized flow (gflow) structure from the current measurement pattern. This method does not call the flow-extraction routine on the underlying open graph, but constructs the gflow from the pattern corrections instead. @@ -1084,17 +1084,17 @@ def extract_gflow(self) -> GFlow[BlochMeasurement]: Notes ----- - The notes provided in :func:`self.extract_causal_flow` apply here as well. + The notes provided in :func:`self.to_causalflow` apply here as well. """ - return self.extract_xzcorrections().downcast_bloch().to_gflow() + return self.to_xzcorrections().downcast_bloch().to_gflow() - def extract_pauli_flow(self) -> PauliFlow[Measurement]: + def to_pauliflow(self) -> PauliFlow[Measurement]: r"""Extract the Pauli flow structure from the current measurement pattern. This method does not call the flow-extraction routine on the underlying open graph, but reconstructs the Pauli flow from the pattern corrections instead (see - :meth:`graphix.flow.core.XZCorrections.to_pauli_flow`). Contrary to - :meth:`extract_causal_flow` and :meth:`extract_gflow`, Pauli measurements are kept as + :meth:`graphix.flow.core.XZCorrections.to_pauliflow`). Contrary to + :meth:`to_causalflow` and :meth:`to_gflow`, Pauli measurements are kept as axes rather than downcast to planar Bloch measurements, so that the Pauli-basis structure is preserved. @@ -1113,11 +1113,11 @@ def extract_pauli_flow(self) -> PauliFlow[Measurement]: Notes ----- - The notes provided in :func:`self.extract_causal_flow` apply here as well. + The notes provided in :func:`self.to_causalflow` apply here as well. """ - return self.extract_xzcorrections().to_pauli_flow() + return self.to_xzcorrections().to_pauliflow() - def extract_xzcorrections(self) -> XZCorrections[Measurement]: + def to_xzcorrections(self) -> XZCorrections[Measurement]: """Extract the XZ-corrections from the current measurement pattern. Returns @@ -1134,11 +1134,11 @@ def extract_xzcorrections(self) -> XZCorrections[Measurement]: Notes ----- - To ensure that applying the chain ``Pattern.extract_xzcorrections().to_pattern()`` to a strongly deterministic pattern returns a new pattern implementing the same unitary transformation, XZ-corrections must be extracted from a standardized pattern. This requirement arises for the same reason that flow extraction also operates correctly on standardized patterns only. + To ensure that applying the chain ``Pattern.to_xzcorrections().to_pattern()`` to a strongly deterministic pattern returns a new pattern implementing the same unitary transformation, XZ-corrections must be extracted from a standardized pattern. This requirement arises for the same reason that flow extraction also operates correctly on standardized patterns only. This equivalence holds as long as the original pattern contains no Clifford commands, since those are discarded during open-graph extraction. - See docstring in :func:`optimization.StandardizedPattern.extract_gflow` for additional information. + See docstring in :func:`optimization.StandardizedPattern.to_gflow` for additional information. """ - return optimization.StandardizedPattern.from_pattern(self).extract_xzcorrections() + return optimization.StandardizedPattern.from_pattern(self).to_xzcorrections() def _measurement_order_depth(self) -> list[int]: """Obtain a measurement order which reduces the depth of a pattern. @@ -1233,7 +1233,7 @@ def extract_isolated_nodes(self) -> set[int]: graph = self.extract_graph() return {node for node, d in graph.degree if d == 0} - def extract_opengraph(self) -> OpenGraph[Measurement]: + def to_opengraph(self) -> OpenGraph[Measurement]: r"""Extract the underlying resource-state open graph from the pattern. This method standardizes the pattern first to guarantee that @@ -1245,7 +1245,7 @@ def extract_opengraph(self) -> OpenGraph[Measurement]: ------- OpenGraph[Measurement] """ - return optimization.StandardizedPattern.from_pattern(self).extract_opengraph() + return optimization.StandardizedPattern.from_pattern(self).to_opengraph() def extract_clifford(self) -> dict[int, Clifford]: """Extract Clifford commands. @@ -1542,7 +1542,7 @@ def draw( If ``flow_from_pattern==True`` but the pattern is not compatible with a gflow, an attempt to be extract the flow from the underlying open graph will be made while warning the user. """ if annotations is None: - og = self.extract_opengraph() + og = self.to_opengraph() gv = GraphVisualizer.from_opengraph(og=og, **options) else: match annotations: @@ -1551,12 +1551,12 @@ def draw( if flow_from_pattern: try: - xz_corrections = self.extract_xzcorrections().downcast_bloch() + xz_corrections = self.to_xzcorrections().downcast_bloch() except TypeError: pass else: try: - flow = xz_corrections.to_causal_flow() + flow = xz_corrections.to_causalflow() except FlowError: try: flow = xz_corrections.to_gflow() @@ -1567,15 +1567,15 @@ def draw( ) if flow is None: - og = self.extract_opengraph() + og = self.to_opengraph() try: bloch_case = og.downcast_bloch() except TypeError: pass else: - flow = bloch_case.find_causal_flow() + flow = bloch_case.to_causalflow_or_none() if flow is None: - flow = og.find_pauli_flow(stacklevel=stacklevel + 1) + flow = og.to_pauliflow_or_none(stacklevel=stacklevel + 1) if flow is None: raise PatternError( "The pattern's open graph does not have Pauli flow. Consider setting the `annotations` parameter to `None` or `DrawPatternAnnotations.XZCorrections`." @@ -1584,7 +1584,7 @@ def draw( gv = GraphVisualizer.from_flow(flow=flow, **options) case DrawPatternAnnotations.XZCorrections: - xzcorrections = self.extract_xzcorrections() + xzcorrections = self.to_xzcorrections() gv = GraphVisualizer.from_xzcorrections(xz_corr=xzcorrections, **options) gv.visualize() diff --git a/graphix/space_minimization.py b/graphix/space_minimization.py index 468d5c903..c85eb292f 100644 --- a/graphix/space_minimization.py +++ b/graphix/space_minimization.py @@ -192,7 +192,7 @@ def causal_flow(pattern: StandardizedPattern) -> SpaceMinimizationHeuristicResul This minimization heuristic is optimal but requires the pattern to have a causal flow. """ try: - cf = pattern.extract_xzcorrections().downcast_bloch().to_causal_flow() + cf = pattern.to_xzcorrections().downcast_bloch().to_causalflow() except (TypeError, FlowError): return None else: diff --git a/graphix/transpiler.py b/graphix/transpiler.py index aa49d76bb..d1fbae532 100644 --- a/graphix/transpiler.py +++ b/graphix/transpiler.py @@ -73,7 +73,7 @@ class TranspiledFlow: def to_pattern(self) -> TranspiledPattern: """Return the transpiled pattern.""" - pattern = StandardizedPattern.from_pattern(self.flow.to_corrections().to_pattern()).to_space_optimal_pattern() + pattern = StandardizedPattern.from_pattern(self.flow.to_xzcorrections().to_pattern()).to_space_optimal_pattern() pattern.extend(self.classical_outputs.values()) return TranspiledPattern(pattern, tuple(self.classical_outputs.keys())) @@ -434,7 +434,7 @@ def m(self, qubit: int, axis: Axis) -> None: self.instruction.append(instruction.M(target=qubit, axis=axis)) self.active_qubits.remove(qubit) - def transpile_to_causal_flow(self) -> TranspiledFlow: + def transpile_to_causalflow(self) -> TranspiledFlow: """Transpile a circuit via J-∧z decomposition to a causal flow. Parameters @@ -517,9 +517,9 @@ def transpile(self, *, transpile_swaps: bool = True) -> TranspiledPattern: The result of the transpilation: a pattern and classical outputs. """ if not transpile_swaps: - return self.transpile_to_causal_flow().to_pattern() + return self.transpile_to_causalflow().to_pattern() swap = _transpile_swaps(self) - result = swap.circuit.transpile_to_causal_flow().to_pattern() + result = swap.circuit.transpile_to_causalflow().to_pattern() result.pattern.reorder_output_nodes(swap.swap_output_nodes(result.pattern.output_nodes)) classical_outputs = swap.swap_classical_outputs(result.classical_outputs) return TranspiledPattern(result.pattern, classical_outputs) diff --git a/tests/test_circ_extraction.py b/tests/test_circ_extraction.py index cdd6e1549..52bfb5191 100644 --- a/tests/test_circ_extraction.py +++ b/tests/test_circ_extraction.py @@ -446,7 +446,7 @@ def test_extract_rnd_circuit(self, fx_bg: PCG64, jumps: int) -> None: circuit_ref = rand_circuit(nqubits, depth, rng, use_ccx=False) pattern = circuit_ref.transpile().pattern - circuit = pattern.extract_opengraph().extract_circuit() + circuit = pattern.to_opengraph().to_circuit() s_ref = circuit.simulate_statevector(rng=rng).statevec s_test = circuit_ref.simulate_statevector(rng=rng).statevec @@ -524,7 +524,7 @@ def test_extract_rnd_circuit(self, fx_bg: PCG64, jumps: int) -> None: ) def test_extract_og(self, test_case: OpenGraph[Measurement], fx_rng: Generator) -> None: pattern = test_case.to_pattern() - circuit = test_case.extract_circuit() + circuit = test_case.to_circuit() state = circuit.simulate_statevector(rng=fx_rng).statevec state_ref = pattern.simulate_pattern(rng=fx_rng) @@ -549,7 +549,7 @@ def test_extract_og_infer_pauli(self, infer_pauli: bool, fx_rng: Generator) -> N if infer_pauli: og = og.infer_pauli_measurements() - circuit = og.extract_circuit() + circuit = og.to_circuit() state = circuit.simulate_statevector(rng=fx_rng).statevec state_ref = pattern.simulate_pattern(rng=fx_rng) @@ -568,7 +568,7 @@ def test_extract_og_gflow(self, fx_rng: Generator) -> None: }, ) pattern = og.to_pattern() - circuit = og.extract_gflow().extract_circuit().to_circuit() + circuit = og.to_gflow().extract_circuit().to_circuit() state = circuit.simulate_statevector(rng=fx_rng).statevec state_ref = pattern.simulate_pattern(rng=fx_rng) @@ -591,13 +591,13 @@ def test_parametric_angles(self, test_case: float, fx_rng: Generator) -> None: ) # Substitute parameter at the level of the extracted circuit - qc1 = og.extract_circuit() + qc1 = og.to_circuit() s1 = qc1.subs(alpha, alpha_val).simulate_statevector(rng=fx_rng).statevec # Substitute parameter at the level of the open graph object # Calling `infer_pauli_measurements` is not necessary for the test to pass # (and it should not be), but it suppresses the warnings. - qc2 = og.subs(alpha, alpha_val).infer_pauli_measurements().extract_circuit() + qc2 = og.subs(alpha, alpha_val).infer_pauli_measurements().to_circuit() s2 = qc2.simulate_statevector(rng=fx_rng).statevec assert s1.isclose(s2) @@ -649,5 +649,5 @@ def test_extend_input() -> None: assert og_ext.isclose(og_ref) assert ancillary_inputs_map == {1: 8, 2: 7} - flow = og_ext.infer_pauli_measurements().extract_pauli_flow() + flow = og_ext.infer_pauli_measurements().to_pauliflow() assert flow.is_focused() diff --git a/tests/test_clifford.py b/tests/test_clifford.py index 06bd981bd..983843696 100644 --- a/tests/test_clifford.py +++ b/tests/test_clifford.py @@ -100,7 +100,7 @@ def test_try_from_matrix_ng(self, fx_rng: Generator) -> None: @pytest.mark.parametrize("c", Clifford) def test_to_pattern(self, fx_rng: Generator, c: Clifford) -> None: og = c.to_opengraph() - og.to_bloch().extract_causal_flow() + og.to_bloch().to_causalflow() pattern = og.to_pattern() pattern_ref = Pattern(input_nodes=[0], cmds=[Command.C(0, c)]) input_state = rand_state_vector(nqubits=1, rng=fx_rng) diff --git a/tests/test_flow_core.py b/tests/test_flow_core.py index 8e722bd93..d5fa30341 100644 --- a/tests/test_flow_core.py +++ b/tests/test_flow_core.py @@ -391,10 +391,10 @@ class TestFlowPatternConversion: """ @pytest.mark.parametrize("test_case", prepare_test_xzcorrections()) - def test_flow_to_corrections(self, test_case: XZCorrectionsTestCase) -> None: + def test_flow_to_xzcorrections(self, test_case: XZCorrectionsTestCase) -> None: flow = test_case.flow flow.check_well_formed() - corrections = flow.to_corrections() + corrections = flow.to_xzcorrections() corrections.check_well_formed() assert corrections.z_corrections == test_case.z_corr assert corrections.x_corrections == test_case.x_corr @@ -402,7 +402,7 @@ def test_flow_to_corrections(self, test_case: XZCorrectionsTestCase) -> None: @pytest.mark.parametrize("test_case", prepare_test_xzcorrections()) def test_corrections_to_pattern(self, test_case: XZCorrectionsTestCase, fx_rng: Generator) -> None: if test_case.pattern is not None: - pattern = test_case.flow.to_corrections().to_pattern() # type: ignore[misc] + pattern = test_case.flow.to_xzcorrections().to_pattern() # type: ignore[misc] n_shots = 2 for plane in {Plane.XY, Plane.XZ, Plane.YZ}: @@ -427,7 +427,7 @@ def test_subs(self) -> None: output_nodes=[1], measurements={0: Measurement.XY(alpha)}, ) - flow = og.extract_pauli_flow() + flow = og.to_pauliflow() og_ref = OpenGraph( graph=nx.Graph([(0, 1)]), @@ -435,7 +435,7 @@ def test_subs(self) -> None: output_nodes=[1], measurements={0: Measurement.XY(value)}, ) - flow_ref = og_ref.extract_pauli_flow() + flow_ref = og_ref.to_pauliflow() flow_test = flow.subs(alpha, value) @@ -455,7 +455,7 @@ def test_xreplace(self) -> None: output_nodes=[2], measurements={node: Measurement.XY(angle) for node, angle in enumerate(parametric_angles)}, ) - flow = og.extract_pauli_flow() + flow = og.to_pauliflow() og_ref = OpenGraph( graph=nx.Graph([(0, 1), (1, 2)]), @@ -463,7 +463,7 @@ def test_xreplace(self) -> None: output_nodes=[2], measurements={node: Measurement.XY(value) for node in range(2)}, ) - flow_ref = og_ref.extract_pauli_flow() + flow_ref = og_ref.to_pauliflow() flow_test = flow.xreplace(dict.fromkeys(parametric_angles, value)) @@ -498,7 +498,7 @@ class TestXZCorrections: # See `:func: generate_causal_flow_0` def test_order_0(self) -> None: - corrections = generate_causal_flow_0().to_corrections() + corrections = generate_causal_flow_0().to_xzcorrections() assert corrections.generate_total_measurement_order() == [0, 1, 2] assert corrections.is_compatible([0, 1, 2]) # Correct order @@ -706,7 +706,7 @@ def test_subs(self) -> None: output_nodes=[1], measurements={0: Measurement.XY(alpha)}, ) - xzcorr = og.extract_causal_flow().to_corrections() + xzcorr = og.to_causalflow().to_xzcorrections() og_ref = OpenGraph( graph=nx.Graph([(0, 1)]), @@ -714,7 +714,7 @@ def test_subs(self) -> None: output_nodes=[1], measurements={0: Measurement.XY(value)}, ) - xzcorr_ref = og_ref.extract_causal_flow().to_corrections() + xzcorr_ref = og_ref.to_causalflow().to_xzcorrections() xzcorr_test = xzcorr.subs(alpha, value) @@ -735,7 +735,7 @@ def test_xreplace(self) -> None: output_nodes=[2], measurements={node: Measurement.XY(angle) for node, angle in enumerate(parametric_angles)}, ) - xzcorr = og.extract_causal_flow().to_corrections() + xzcorr = og.to_causalflow().to_xzcorrections() og_ref = OpenGraph( graph=nx.Graph([(0, 1), (1, 2)]), @@ -743,7 +743,7 @@ def test_xreplace(self) -> None: output_nodes=[2], measurements={node: Measurement.XY(value) for node in range(2)}, ) - xzcorr_ref = og_ref.extract_causal_flow().to_corrections() + xzcorr_ref = og_ref.to_causalflow().to_xzcorrections() xzcorr_test = xzcorr.xreplace(dict.fromkeys(parametric_angles, value)) diff --git a/tests/test_opengraph.py b/tests/test_opengraph.py index 2e62d4eb0..e59d4be2b 100644 --- a/tests/test_opengraph.py +++ b/tests/test_opengraph.py @@ -1039,39 +1039,39 @@ def test_cflow(self, test_case: OpenGraphFlowTestCase, fx_rng: Generator) -> Non og = test_case.og.to_bloch() if test_case.has_cflow: - cf = og.extract_causal_flow() + cf = og.to_causalflow() cf.check_well_formed() - pattern = cf.to_corrections().to_pattern() + pattern = cf.to_xzcorrections().to_pattern() assert check_determinism(pattern, fx_rng) else: with pytest.raises(OpenGraphError, match=r"The open graph does not have a causal flow."): - og.extract_causal_flow() + og.to_causalflow() @pytest.mark.parametrize("test_case", OPEN_GRAPH_FLOW_TEST_CASES) def test_gflow(self, test_case: OpenGraphFlowTestCase, fx_rng: Generator) -> None: og = test_case.og.to_bloch() if test_case.has_gflow: - gf = og.extract_gflow() + gf = og.to_gflow() gf.check_well_formed() - pattern = gf.to_corrections().to_pattern() + pattern = gf.to_xzcorrections().to_pattern() assert check_determinism(pattern, fx_rng) else: with pytest.raises(OpenGraphError, match=r"The open graph does not have a gflow."): - og.extract_gflow() + og.to_gflow() @pytest.mark.parametrize("test_case", OPEN_GRAPH_FLOW_TEST_CASES) def test_pflow(self, test_case: OpenGraphFlowTestCase, fx_rng: Generator) -> None: og = test_case.og if test_case.has_pflow: - pf = og.infer_pauli_measurements().extract_pauli_flow() + pf = og.infer_pauli_measurements().to_pauliflow() pf.check_well_formed() - pattern = pf.to_corrections().to_pattern() + pattern = pf.to_xzcorrections().to_pattern() assert check_determinism(pattern, fx_rng) else: with pytest.raises(OpenGraphError, match=r"The open graph does not have a Pauli flow."): - og.extract_pauli_flow() + og.to_pauliflow() def test_large_linear_graph(self) -> None: r"""Test causal-flow extraction algorithm on large linear open graphs. @@ -1094,7 +1094,7 @@ def test_large_linear_graph(self) -> None: c_ref = {i: frozenset({i + 1}) for i in og.measurements} pol_ref = tuple(frozenset({i}) for i in reversed(range(n_nodes))) - flow = og.extract_causal_flow() + flow = og.to_causalflow() assert flow.correction_function == c_ref assert flow.partial_order_layers == pol_ref @@ -1102,19 +1102,19 @@ def test_large_linear_graph(self) -> None: def test_gflow_focused(self, test_case: OpenGraphFlowTestCase) -> None: """Test that the algebraic flow-finding algorithm generated focused gflows.""" if test_case.has_gflow: - gf = test_case.og.to_bloch().extract_gflow() + gf = test_case.og.to_bloch().to_gflow() assert gf.is_focused() @pytest.mark.parametrize("test_case", OPEN_GRAPH_FLOW_TEST_CASES) def test_pflow_focused(self, test_case: OpenGraphFlowTestCase) -> None: """Test that the algebraic flow-finding algorithm generated focused Pauli flows.""" if test_case.has_pflow: - pf = test_case.og.infer_pauli_measurements().extract_pauli_flow() + pf = test_case.og.infer_pauli_measurements().to_pauliflow() assert pf.is_focused() def test_double_entanglement(self) -> None: pattern = Pattern(input_nodes=[0, 1], cmds=[E((0, 1)), E((0, 1))]) - pattern2 = pattern.extract_opengraph().to_pattern() + pattern2 = pattern.to_opengraph().to_pattern() state = pattern.simulate_pattern() state2 = pattern2.simulate_pattern() assert state.isclose(state2) @@ -1124,9 +1124,7 @@ def test_from_to_pattern(self, fx_rng: Generator) -> None: depth = 2 circuit = rand_circuit(n_qubits, depth, fx_rng) pattern_ref = circuit.transpile().pattern - pattern = StandardizedPattern.from_pattern( - pattern_ref.extract_opengraph().to_pattern() - ).to_space_optimal_pattern() + pattern = StandardizedPattern.from_pattern(pattern_ref.to_opengraph().to_pattern()).to_space_optimal_pattern() for plane in {Plane.XY, Plane.XZ, Plane.YZ}: alpha = 2 * ANGLE_PI * fx_rng.random() @@ -1276,8 +1274,8 @@ def test_compose_clifford(self, fx_rng: Generator) -> None: mapping = {0: 0} pc, _ = p1.compose(p2, mapping) - og1 = p1.extract_opengraph() - og2 = p2.extract_opengraph() + og1 = p1.to_opengraph() + og2 = p2.to_opengraph() og_c, _ = og1.compose(og2, mapping) pc_test = og_c.to_pattern() diff --git a/tests/test_optimization.py b/tests/test_optimization.py index 5c386d8d3..bf33fe281 100644 --- a/tests/test_optimization.py +++ b/tests/test_optimization.py @@ -70,7 +70,7 @@ def test_flow_after_pauli_preprocessing(fx_bg: PCG64, jumps: int) -> None: pattern.remove_pauli_measurements() # We should convert to Bloch measurement the remaining Pauli # measurements on input nodes. - gflow = pattern.to_bloch().extract_gflow() + gflow = pattern.to_bloch().to_gflow() gflow.check_well_formed() diff --git a/tests/test_pattern.py b/tests/test_pattern.py index ac125d74f..3d83749eb 100644 --- a/tests/test_pattern.py +++ b/tests/test_pattern.py @@ -922,14 +922,14 @@ class PatternFlowTestCase(NamedTuple): # Extract causal flow from random circuits @pytest.mark.parametrize("jumps", range(1, 11)) - def test_extract_causal_flow_rnd_circuit(self, fx_bg: PCG64, jumps: int) -> None: + def test_to_causalflow_rnd_circuit(self, fx_bg: PCG64, jumps: int) -> None: """Tests the round trip Pattern -> XZCorrections -> CausalFlow -> XZCorrections -> Pattern.""" rng = Generator(fx_bg.jumped(jumps)) nqubits = 2 depth = 2 circuit_1 = rand_circuit(nqubits, depth, rng, use_ccx=False) p_ref = circuit_1.transpile().pattern - p_test = p_ref.to_bloch().extract_causal_flow().to_corrections().to_pattern().infer_pauli_measurements() + p_test = p_ref.to_bloch().to_causalflow().to_xzcorrections().to_pattern().infer_pauli_measurements() p_ref = p_ref.infer_pauli_measurements() p_test = p_test.infer_pauli_measurements() @@ -942,14 +942,14 @@ def test_extract_causal_flow_rnd_circuit(self, fx_bg: PCG64, jumps: int) -> None # Extract gflow from random circuits @pytest.mark.parametrize("jumps", range(1, 11)) - def test_extract_gflow_rnd_circuit(self, fx_bg: PCG64, jumps: int) -> None: + def test_to_gflow_rnd_circuit(self, fx_bg: PCG64, jumps: int) -> None: """Tests the round trip Pattern -> XZCorrections -> GFlow -> XZCorrections -> Pattern.""" rng = Generator(fx_bg.jumped(jumps)) nqubits = 2 depth = 2 circuit_1 = rand_circuit(nqubits, depth, rng, use_ccx=False) p_ref = circuit_1.transpile().pattern - p_test = p_ref.to_bloch().extract_gflow().to_corrections().to_pattern().infer_pauli_measurements() + p_test = p_ref.to_bloch().to_gflow().to_xzcorrections().to_pattern().infer_pauli_measurements() p_ref = p_ref.infer_pauli_measurements() p_test = p_test.infer_pauli_measurements() @@ -962,14 +962,14 @@ def test_extract_gflow_rnd_circuit(self, fx_bg: PCG64, jumps: int) -> None: # Extract Pauli flow from random circuits @pytest.mark.parametrize("jumps", range(1, 11)) - def test_extract_pauli_flow_rnd_circuit(self, fx_bg: PCG64, jumps: int) -> None: + def test_to_pauliflow_rnd_circuit(self, fx_bg: PCG64, jumps: int) -> None: """Tests the round trip Pattern -> XZCorrections -> PauliFlow -> XZCorrections -> Pattern.""" rng = Generator(fx_bg.jumped(jumps)) nqubits = 2 depth = 2 circuit_1 = rand_circuit(nqubits, depth, rng, use_ccx=False) p_ref = circuit_1.transpile().pattern - p_test = p_ref.to_bloch().extract_pauli_flow().to_corrections().to_pattern().infer_pauli_measurements() + p_test = p_ref.to_bloch().to_pauliflow().to_xzcorrections().to_pattern().infer_pauli_measurements() p_ref.remove_pauli_measurements() p_test.remove_pauli_measurements() @@ -979,36 +979,36 @@ def test_extract_pauli_flow_rnd_circuit(self, fx_bg: PCG64, jumps: int) -> None: assert s_ref.isclose(s_test) @pytest.mark.parametrize("test_case", PATTERN_FLOW_TEST_CASES) - def test_extract_causal_flow(self, fx_rng: Generator, test_case: PatternFlowTestCase) -> None: + def test_to_causalflow(self, fx_rng: Generator, test_case: PatternFlowTestCase) -> None: if test_case.has_cflow: alpha = 2 * np.pi * fx_rng.random() s_ref = test_case.pattern.simulate_pattern(input_state=PlanarState(Plane.XZ, alpha), rng=fx_rng) - p_test = test_case.pattern.to_bloch().extract_causal_flow().to_corrections().to_pattern() + p_test = test_case.pattern.to_bloch().to_causalflow().to_xzcorrections().to_pattern() s_test = p_test.simulate_pattern(input_state=PlanarState(Plane.XZ, alpha), rng=fx_rng) assert s_ref.isclose(s_test) else: with pytest.raises(FlowError): - test_case.pattern.extract_causal_flow() + test_case.pattern.to_causalflow() @pytest.mark.parametrize("test_case", PATTERN_FLOW_TEST_CASES) - def test_extract_gflow(self, fx_rng: Generator, test_case: PatternFlowTestCase) -> None: + def test_to_gflow(self, fx_rng: Generator, test_case: PatternFlowTestCase) -> None: """Tests the round trip Pattern -> XZCorrections -> GFlow -> XZCorrections -> Pattern.""" if test_case.has_gflow: alpha = 2 * np.pi * fx_rng.random() s_ref = test_case.pattern.simulate_pattern(input_state=PlanarState(Plane.XZ, alpha), rng=fx_rng) - p_test = test_case.pattern.to_bloch().extract_gflow().to_corrections().to_pattern() + p_test = test_case.pattern.to_bloch().to_gflow().to_xzcorrections().to_pattern() s_test = p_test.simulate_pattern(input_state=PlanarState(Plane.XZ, alpha), rng=fx_rng) assert s_ref.isclose(s_test) else: with pytest.raises(FlowError): - test_case.pattern.extract_gflow() + test_case.pattern.to_gflow() @pytest.mark.parametrize("test_case", PATTERN_FLOW_TEST_CASES) - def test_extract_pauli_flow(self, fx_rng: Generator, test_case: PatternFlowTestCase) -> None: + def test_to_pauliflow(self, fx_rng: Generator, test_case: PatternFlowTestCase) -> None: """Tests the round trip Pattern -> XZCorrections -> PauliFlow -> XZCorrections -> Pattern.""" # A gflow always induces a Pauli flow, so every gflow case must round-trip via the Pauli # flow. Cases without a gflow are skipped: a Pauli flow is strictly more general and may @@ -1018,7 +1018,7 @@ def test_extract_pauli_flow(self, fx_rng: Generator, test_case: PatternFlowTestC alpha = 2 * np.pi * fx_rng.random() s_ref = test_case.pattern.simulate_pattern(input_state=PlanarState(Plane.XZ, alpha), rng=fx_rng) - p_test = test_case.pattern.to_bloch().extract_pauli_flow().to_corrections().to_pattern() + p_test = test_case.pattern.to_bloch().to_pauliflow().to_xzcorrections().to_pattern() s_test = p_test.simulate_pattern(input_state=PlanarState(Plane.XZ, alpha), rng=fx_rng) assert s_ref.isclose(s_test) @@ -1038,16 +1038,16 @@ def test_extract_cflow_og(self, fx_rng: Generator) -> None: 4: Measurement.XY(0.4), }, ) - p_ref = og.extract_causal_flow().to_corrections().to_pattern() + p_ref = og.to_causalflow().to_xzcorrections().to_pattern() s_ref = p_ref.simulate_pattern(input_state=PlanarState(Plane.XZ, alpha), rng=fx_rng) - p_test = p_ref.extract_causal_flow().to_corrections().to_pattern() + p_test = p_ref.to_causalflow().to_xzcorrections().to_pattern() s_test = p_test.simulate_pattern(input_state=PlanarState(Plane.XZ, alpha), rng=fx_rng) assert s_ref.isclose(s_test) # From open graph - def test_extract_gflow_og(self, fx_rng: Generator) -> None: + def test_to_gflow_og(self, fx_rng: Generator) -> None: alpha = 2 * np.pi * fx_rng.random() og = OpenGraph( @@ -1062,10 +1062,10 @@ def test_extract_gflow_og(self, fx_rng: Generator) -> None: }, ) - p_ref = og.extract_gflow().to_corrections().to_pattern() + p_ref = og.to_gflow().to_xzcorrections().to_pattern() s_ref = p_ref.simulate_pattern(input_state=PlanarState(Plane.XZ, alpha), rng=fx_rng) - p_test = p_ref.extract_gflow().to_corrections().to_pattern() + p_test = p_ref.to_gflow().to_xzcorrections().to_pattern() s_test = p_test.simulate_pattern(input_state=PlanarState(Plane.XZ, alpha), rng=fx_rng) assert s_ref.isclose(s_test) @@ -1078,7 +1078,7 @@ def test_extract_xzc_rnd_circuit(self, fx_bg: PCG64, jumps: int) -> None: depth = 2 circuit_1 = rand_circuit(nqubits, depth, rng, use_ccx=False) p_ref = circuit_1.transpile().pattern - xzc = p_ref.extract_xzcorrections() + xzc = p_ref.to_xzcorrections() xzc.check_well_formed() p_test = xzc.to_pattern() @@ -1093,7 +1093,7 @@ def test_extract_xzc_rnd_circuit(self, fx_bg: PCG64, jumps: int) -> None: def test_extract_xzc_empty_domains(self) -> None: p = Pattern(input_nodes=[0], cmds=[N(1), E((0, 1))]) - xzc = p.extract_xzcorrections() + xzc = p.to_xzcorrections() assert xzc.x_corrections == {} assert xzc.z_corrections == {} assert xzc.partial_order_layers == (frozenset({0, 1}),) @@ -1104,9 +1104,9 @@ def test_extract_xzc_easy_example(self) -> None: cmds=[M(0), M(1), M(2, s_domain={0}, t_domain={1}), X(3, domain={2}), M(3), Z(4, domain={3})], ) - xzc = pattern.extract_xzcorrections() + xzc = pattern.to_xzcorrections() xzc_ref = XZCorrections.from_measured_nodes_mapping( - pattern.extract_opengraph(), x_corrections={0: {2}, 2: {3}}, z_corrections={1: {2}, 3: {4}} + pattern.to_opengraph(), x_corrections={0: {2}, 2: {3}}, z_corrections={1: {2}, 3: {4}} ) assert xzc.og.isclose(xzc_ref.og) assert xzc.x_corrections == xzc_ref.x_corrections @@ -1155,11 +1155,11 @@ def test_reindex(self) -> None: assert list(pattern) == list(pattern_reindexed) assert pattern.output_nodes == pattern_reindexed.output_nodes - def test_extract_opengraph_standardization(self) -> None: + def test_to_opengraph_standardization(self) -> None: p = Pattern(cmds=[N(0), C(0, Clifford.H), M(0, Measurement.XY(0.3))]) - og = p.extract_opengraph() + og = p.to_opengraph() p.standardize() - og_std = p.extract_opengraph() + og_std = p.to_opengraph() assert og.isclose(og_std) @@ -1192,8 +1192,8 @@ def test_extract_opengraph_standardization(self) -> None: ), ], ) - def test_extract_opengraph_roundtrip(self, pattern: Pattern, fx_rng: Generator) -> None: - pattern_test = pattern.extract_opengraph().to_pattern() + def test_to_opengraph_roundtrip(self, pattern: Pattern, fx_rng: Generator) -> None: + pattern_test = pattern.to_opengraph().to_pattern() sv = pattern.simulate_pattern(rng=fx_rng) sv_test = pattern_test.simulate_pattern(rng=fx_rng) diff --git a/tests/test_pauli_flow_extraction.py b/tests/test_pauli_flow_extraction.py index 80095050a..fd5e8af62 100644 --- a/tests/test_pauli_flow_extraction.py +++ b/tests/test_pauli_flow_extraction.py @@ -3,7 +3,7 @@ Correctness criterion --------------------- A reconstructed Pauli flow ``pf`` generates the original pattern if and only if -``pf.check_well_formed()`` succeeds *and* ``pf.to_corrections()`` reproduces the pattern's +``pf.check_well_formed()`` succeeds *and* ``pf.to_xzcorrections()`` reproduces the pattern's X- and Z-corrections exactly. The latter "round-trip" property is the decisive check: it guarantees that the flow generates *this* pattern (and not merely some Pauli flow of the underlying open graph, which need not be unique). The tests below verify this on the three @@ -38,18 +38,18 @@ def _norm(corrections: Mapping[int, AbstractSet[int]]) -> dict[int, frozenset[in def _assert_round_trip(pattern: Pattern) -> None: - xz = pattern.extract_xzcorrections() - pf = xz.to_pauli_flow() - # `to_pauli_flow` no longer runs `check_well_formed` in production; the + xz = pattern.to_xzcorrections() + pf = xz.to_pauliflow() + # `to_pauliflow` no longer runs `check_well_formed` in production; the # well-formedness is asserted here, in the test-suite, instead. assert pf.is_well_formed() - rt = pf.to_corrections() + rt = pf.to_xzcorrections() assert _norm(rt.x_corrections) == _norm(xz.x_corrections) assert _norm(rt.z_corrections) == _norm(xz.z_corrections) def _correction_function(pattern: Pattern) -> dict[int, set[int]]: - pf = pattern.extract_pauli_flow() + pf = pattern.to_pauliflow() return {k: set(v) for k, v in pf.correction_function.items()} @@ -82,19 +82,19 @@ def _pauli_pattern() -> Pattern: ) # fmt: skip -def test_extract_pauli_flow_causal_example() -> None: +def test_to_pauliflow_causal_example() -> None: pattern = _causal_pattern() assert _correction_function(pattern) == {0: {1}} _assert_round_trip(pattern) -def test_extract_pauli_flow_gflow_example() -> None: +def test_to_pauliflow_gflow_example() -> None: pattern = _gflow_pattern() assert _correction_function(pattern) == {0: {2, 3}, 1: {1, 2}} _assert_round_trip(pattern) -def test_extract_pauli_flow_pauli_example() -> None: +def test_to_pauliflow_pauli_example() -> None: # The flow must include the anachronical correction (node 1 in p(0)) that does not # appear in the pattern, in order to satisfy the X-axis proposition (P7). pattern = _pauli_pattern() @@ -102,7 +102,7 @@ def test_extract_pauli_flow_pauli_example() -> None: _assert_round_trip(pattern) -def test_extract_pauli_flow_pauli_opengraph() -> None: +def test_to_pauliflow_pauli_opengraph() -> None: og = OpenGraph( graph=nx.Graph([(0, 2), (2, 4), (3, 4), (4, 6), (1, 4), (1, 6), (2, 3), (3, 5), (2, 6), (3, 6)]), input_nodes=[0], @@ -118,7 +118,7 @@ def test_extract_pauli_flow_pauli_opengraph() -> None: _assert_round_trip(og.to_pattern()) -def test_extract_pauli_flow_output_zcorrection() -> None: +def test_to_pauliflow_output_zcorrection() -> None: # Regression: a Z-correction whose future target is an *output* node imposes a real GF(2) # equation in the reconstruction -- it cannot be dropped (e.g. by skipping future nodes that are # not measured in a non-Pauli plane) without silently breaking the Z-correction round-trip. @@ -149,7 +149,7 @@ def test_extract_pauli_flow_output_zcorrection() -> None: @pytest.mark.parametrize("seed", range(400)) -def test_extract_pauli_flow_randomized_round_trip(seed: int) -> None: +def test_to_pauliflow_randomized_round_trip(seed: int) -> None: # For each seed, draw a random open graph. Seeds with no edges, or whose open graph does not # admit a Pauli flow (so `to_pattern` raises `OpenGraphError`), are skipped; the rest are # converted to a pattern and round-tripped. Passing the seed as a parameter keeps each case @@ -176,20 +176,20 @@ def test_extract_pauli_flow_randomized_round_trip(seed: int) -> None: _assert_round_trip(pattern) -def test_extract_pauli_flow_pins_the_pattern_specific_flow() -> None: +def test_to_pauliflow_pins_the_pattern_specific_flow() -> None: r"""Reconstruction must return the Pauli flow implemented by *this* pattern, not just any Pauli flow of the underlying open graph. A Pauli flow on an open graph is not unique when Pauli-measured nodes admit several distinct - anachronical-correction patterns. ``OpenGraph.find_pauli_flow`` returns *some* maximally + anachronical-correction patterns. ``OpenGraph.to_pauliflow_or_none`` returns *some* maximally delayed Pauli flow (chosen by the underlying algorithm); a trivial implementation of - ``XZCorrections.to_pauli_flow`` that delegates to it -- and ignores the XZ-corrections of + ``XZCorrections.to_pauliflow`` that delegates to it -- and ignores the XZ-corrections of the pattern entirely -- would therefore pass every existing round-trip test that happens to feed it patterns whose flow already coincides with that algorithmic choice. This test pins down a small open graph that admits two well-formed Pauli flows whose - ``to_corrections()`` outputs differ, builds the XZ-corrections of the *non-default* one, + ``to_xzcorrections()`` outputs differ, builds the XZ-corrections of the *non-default* one, and asserts that the reconstruction returns the chosen flow (and not the one - ``find_pauli_flow`` would have returned on the bare open graph). + ``to_pauliflow_or_none`` would have returned on the bare open graph). """ og: OpenGraph[Measurement] = OpenGraph( graph=nx.Graph([(0, 1), (1, 2), (2, 3)]), @@ -206,35 +206,35 @@ def test_extract_pauli_flow_pins_the_pattern_specific_flow() -> None: assert pf_with_anachronical.is_well_formed() assert pf_with_future.is_well_formed() - xz_with_anachronical = pf_with_anachronical.to_corrections() - xz_with_future = pf_with_future.to_corrections() + xz_with_anachronical = pf_with_anachronical.to_xzcorrections() + xz_with_future = pf_with_future.to_xzcorrections() # The corrections genuinely differ: the future-style flow X-corrects node 3 from # node 0, the anachronical-style flow does not. assert _norm(xz_with_anachronical.x_corrections) != _norm(xz_with_future.x_corrections) - # `find_pauli_flow` is allowed to return either valid flow (or another); what matters - # is that the trivial implementation `pf = self.og.find_pauli_flow()` is independent of + # `to_pauliflow_or_none` is allowed to return either valid flow (or another); what matters + # is that the trivial implementation `pf = self.og.to_pauliflow_or_none()` is independent of # the XZ-corrections we feed in -- so it cannot get both round-trips right. - trivial_choice = og.find_pauli_flow() + trivial_choice = og.to_pauliflow_or_none() assert trivial_choice is not None trivial_cf = {k: set(v) for k, v in trivial_choice.correction_function.items()} # The real reconstruction must use the XZ-corrections to disambiguate. - rebuilt_anachronical = xz_with_anachronical.to_pauli_flow() - rebuilt_future = xz_with_future.to_pauli_flow() + rebuilt_anachronical = xz_with_anachronical.to_pauliflow() + rebuilt_future = xz_with_future.to_pauliflow() assert dict(rebuilt_anachronical.correction_function) == {0: {1}, 1: {2}, 2: {3}} assert dict(rebuilt_future.correction_function) == {0: {1, 3}, 1: {2}, 2: {3}} # And the round-trip on each must still recover the original XZ-corrections exactly. - rt_anachronical = rebuilt_anachronical.to_corrections() - rt_future = rebuilt_future.to_corrections() + rt_anachronical = rebuilt_anachronical.to_xzcorrections() + rt_future = rebuilt_future.to_xzcorrections() assert _norm(rt_anachronical.x_corrections) == _norm(xz_with_anachronical.x_corrections) assert _norm(rt_anachronical.z_corrections) == _norm(xz_with_anachronical.z_corrections) assert _norm(rt_future.x_corrections) == _norm(xz_with_future.x_corrections) assert _norm(rt_future.z_corrections) == _norm(xz_with_future.z_corrections) # Discriminator assertion: at least one of the two reconstructions must disagree with - # the trivial ``find_pauli_flow`` choice (the two flows differ from each other, so the + # the trivial ``to_pauliflow_or_none`` choice (the two flows differ from each other, so the # trivial impl -- which returns the same flow regardless -- cannot match both). assert ( dict(rebuilt_anachronical.correction_function) != trivial_cf @@ -242,12 +242,12 @@ def test_extract_pauli_flow_pins_the_pattern_specific_flow() -> None: ) -def test_to_pauli_flow_empty_pattern() -> None: +def test_to_pauliflow_empty_pattern() -> None: # Regression for the production manifestation of #531: an empty pattern has a trivial - # Pauli flow, so `to_pauli_flow` must not raise. The well-formedness sanity check is no + # Pauli flow, so `to_pauliflow` must not raise. The well-formedness sanity check is no # longer run systematically in production (it lives in the test-suite); `check_well_formed`'s # own behaviour on an empty partial order is tracked separately in #531. - pf = Pattern().extract_xzcorrections().to_pauli_flow() + pf = Pattern().to_xzcorrections().to_pauliflow() assert dict(pf.correction_function) == {} @@ -265,7 +265,7 @@ def test_to_pauli_flow_empty_pattern() -> None: ], ids=["z-input", "xz-input", "yz-input", "isolated-xy"], ) -def test_to_pauli_flow_raises_when_no_flow_exists( +def test_to_pauliflow_raises_when_no_flow_exists( measurements: dict[int, Measurement], inputs: list[int], outputs: list[int], @@ -277,7 +277,7 @@ def test_to_pauli_flow_raises_when_no_flow_exists( graph.add_nodes_from(extra_nodes) og = OpenGraph(graph=graph, input_nodes=inputs, output_nodes=outputs, measurements=measurements) with pytest.raises(FlowGenericError) as exc_info: - XZCorrections(og, {}, {}, layers).to_pauli_flow() + XZCorrections(og, {}, {}, layers).to_pauliflow() assert exc_info.value.reason == FlowGenericErrorReason.NoPauliFlow # The rendered message names the failure so it is actionable in a traceback. assert "No Pauli flow" in str(exc_info.value) diff --git a/tests/test_pretty_print.py b/tests/test_pretty_print.py index da3e47848..eb34e98cc 100644 --- a/tests/test_pretty_print.py +++ b/tests/test_pretty_print.py @@ -114,9 +114,9 @@ def test_pattern_pretty_print_random(fx_bg: PCG64, jumps: int, output: OutputFor @pytest.mark.parametrize( "flow_extractor", [ - lambda og: OpenGraph.extract_causal_flow(og.to_bloch()), - lambda og: OpenGraph.extract_gflow(og.to_bloch()), - OpenGraph.extract_pauli_flow, + lambda og: OpenGraph.to_causalflow(og.to_bloch()), + lambda og: OpenGraph.to_gflow(og.to_bloch()), + OpenGraph.to_pauliflow, ], ) def test_flow_pretty_print_random( @@ -125,7 +125,7 @@ def test_flow_pretty_print_random( flow_extractor: Callable[[OpenGraph[Measurement]], PauliFlow[Measurement]], ) -> None: rng = Generator(fx_bg.jumped(jumps)) - rand_og = rand_circuit(5, 5, rng=rng).transpile().pattern.infer_pauli_measurements().extract_opengraph() + rand_og = rand_circuit(5, 5, rng=rng).transpile().pattern.infer_pauli_measurements().to_opengraph() flow = flow_extractor(rand_og) flow.to_ascii() @@ -140,12 +140,7 @@ def test_xzcorr_pretty_print_random( ) -> None: rng = Generator(fx_bg.jumped(jumps)) xzcorr = ( - rand_circuit(5, 5, rng=rng) - .transpile() - .pattern.extract_opengraph() - .to_bloch() - .extract_causal_flow() - .to_corrections() + rand_circuit(5, 5, rng=rng).transpile().pattern.to_opengraph().to_bloch().to_causalflow().to_xzcorrections() ) xzcorr.to_ascii() @@ -168,7 +163,7 @@ def example_og() -> OpenGraph[Measurement]: def test_cflow_str() -> None: - flow = example_og().to_bloch().extract_causal_flow() + flow = example_og().to_bloch().to_causalflow() assert str(flow) == "c(3) = {5}, c(4) = {6}, c(1) = {3}, c(2) = {4}; {1, 2} < {3, 4} < {5, 6}" @@ -190,19 +185,19 @@ def test_cflow_str() -> None: def test_gflow_str() -> None: - flow = example_og().to_bloch().extract_gflow() + flow = example_og().to_bloch().to_gflow() assert str(flow) == "g(1) = {3, 6}, g(2) = {4, 5}, g(3) = {5}, g(4) = {6}; {1, 2} < {3, 4} < {5, 6}" def test_pflow_str() -> None: - flow = example_og().extract_pauli_flow() + flow = example_og().to_pauliflow() assert str(flow) == "p(1) = {3, 6}, p(2) = {4, 5}, p(3) = {5}, p(4) = {6}; {1, 2} < {3, 4} < {5, 6}" def test_xzcorr_str() -> None: - flow = example_og().to_bloch().extract_causal_flow().to_corrections() + flow = example_og().to_bloch().to_causalflow().to_xzcorrections() assert ( str(flow) diff --git a/tests/test_remove_pauli_measurements.py b/tests/test_remove_pauli_measurements.py index 10870ab36..105c97149 100644 --- a/tests/test_remove_pauli_measurements.py +++ b/tests/test_remove_pauli_measurements.py @@ -80,7 +80,7 @@ def test_local_complement(fx_rng: Generator, measured_set: AbstractSet[int]) -> remove_pauli_measurements = _RemovePauliMeasurements(cut) remove_pauli_measurements.local_complement(0) standardized_pattern2 = remove_pauli_measurements.to_standardized_pattern() - og2 = standardized_pattern2.extract_opengraph() + og2 = standardized_pattern2.to_opengraph() expected_graph: Graph = nx.Graph( [ (0, 1), @@ -91,7 +91,7 @@ def test_local_complement(fx_rng: Generator, measured_set: AbstractSet[int]) -> ) assert nx.utils.graphs_equal(og2.graph, expected_graph) pattern2 = standardized_pattern2.to_pattern() - assert pattern2.extract_gflow() + assert pattern2.to_gflow() check_pattern_equivalence(pattern, pattern2, rng=fx_rng) @@ -104,7 +104,7 @@ def test_pivot_edge(fx_rng: Generator, measured_set: AbstractSet[int]) -> None: remove_pauli_measurements = _RemovePauliMeasurements(cut) remove_pauli_measurements.pivot_edge(0, 1) standardized_pattern2 = remove_pauli_measurements.to_standardized_pattern() - og2 = standardized_pattern2.extract_opengraph() + og2 = standardized_pattern2.to_opengraph() expected_graph: Graph = nx.Graph( [ (0, 1), @@ -127,7 +127,7 @@ def test_pivot_edge(fx_rng: Generator, measured_set: AbstractSet[int]) -> None: assert nx.utils.graphs_equal(og2.graph, expected_graph) assert og2.output_nodes == tuple(0 if node == 1 else 1 if node == 0 else node for node in og.output_nodes) pattern2 = standardized_pattern2.to_pattern() - assert pattern2.extract_gflow() + assert pattern2.to_gflow() check_pattern_equivalence(pattern, pattern2, rng=fx_rng) @@ -188,7 +188,7 @@ def check_pattern(pattern: Pattern, rng: Generator) -> None: assert all_bloch_measurement_or_input_node(standardized_pattern2.input_nodes, standardized_pattern2.m_list) # Check that the pattern has a gflow - standardized_pattern2.extract_xzcorrections().to_bloch().to_gflow() + standardized_pattern2.to_xzcorrections().to_bloch().to_gflow() pattern2 = standardized_pattern2.to_pattern() check_pattern_equivalence(pattern, pattern2, rng=rng) diff --git a/tests/test_transpiler.py b/tests/test_transpiler.py index f3ae5f181..6c3c8f121 100644 --- a/tests/test_transpiler.py +++ b/tests/test_transpiler.py @@ -53,8 +53,8 @@ class TestTranspilerUnitGates: def test_instruction_flow(self, fx_rng: Generator, instruction: InstructionTestCase) -> None: circuit = Circuit(3, instr=[instruction(fx_rng)]) pattern = circuit.transpile().pattern - circuit.transpile_to_causal_flow().flow.check_well_formed() - flow = pattern.to_bloch().extract_causal_flow() + circuit.transpile_to_causalflow().flow.check_well_formed() + flow = pattern.to_bloch().to_causalflow() flow.check_well_formed() @pytest.mark.parametrize("jumps", range(1, 11)) @@ -308,7 +308,7 @@ def test_add_extend(self) -> None: def test_instruction_flow(self, fx_rng: Generator, instruction: InstructionTestCase) -> None: circuit = Circuit(3, instr=[instruction(fx_rng)]) pattern = circuit.transpile().pattern - flow = pattern.to_bloch().extract_causal_flow() + flow = pattern.to_bloch().to_causalflow() flow.check_well_formed() @pytest.mark.parametrize("jumps", range(1, 11)) @@ -393,7 +393,7 @@ def test_transpile_double_cz() -> None: circuit = Circuit(2) circuit.cz(0, 1) circuit.cz(1, 0) - cf = circuit.transpile_to_causal_flow() + cf = circuit.transpile_to_causalflow() assert len(cf.flow.og.graph.edges) == 0 diff --git a/tests/test_visualization.py b/tests/test_visualization.py index 598082bc8..9ff7f4b9f 100644 --- a/tests/test_visualization.py +++ b/tests/test_visualization.py @@ -90,10 +90,10 @@ def example_pflow(rng: Generator) -> Pattern: og = OpenGraph(graph=graph, input_nodes=inputs, output_nodes=outputs, measurements=measurements) try: - og.to_bloch().extract_gflow() + og.to_bloch().to_gflow() pytest.fail("example graph shouldn't have gflow") except OpenGraphError: - og.extract_pauli_flow() # example graph has Pauli flow + og.to_pauliflow() # example graph has Pauli flow pattern = og.to_pattern() pattern.standardize() @@ -213,7 +213,7 @@ def test_og_draw() -> Figure: @pytest.mark.mpl_image_compare def test_causal_flow_draw() -> Figure: og = example_og() - og.downcast_bloch().extract_causal_flow().draw(legend=False) + og.downcast_bloch().to_causalflow().draw(legend=False) return plt.gcf() @@ -221,7 +221,7 @@ def test_causal_flow_draw() -> Figure: @pytest.mark.mpl_image_compare def test_gflow_draw() -> Figure: og = example_og() - og.downcast_bloch().extract_gflow().draw(legend=False) + og.downcast_bloch().to_gflow().draw(legend=False) return plt.gcf() @@ -229,7 +229,7 @@ def test_gflow_draw() -> Figure: @pytest.mark.mpl_image_compare def test_pauli_flow_draw() -> Figure: og = example_og() - og.infer_pauli_measurements().extract_pauli_flow().draw(legend=False) + og.infer_pauli_measurements().to_pauliflow().draw(legend=False) return plt.gcf() @@ -237,7 +237,7 @@ def test_pauli_flow_draw() -> Figure: @pytest.mark.mpl_image_compare def test_xzcorr_draw() -> Figure: og = example_og() - og.downcast_bloch().extract_causal_flow().to_corrections().draw(legend=False) + og.downcast_bloch().to_causalflow().to_xzcorrections().draw(legend=False) return plt.gcf() From b636dea347e921d50af0fde64b7e4177728b079a Mon Sep 17 00:00:00 2001 From: matulni Date: Tue, 7 Jul 2026 13:59:56 +0200 Subject: [PATCH 2/6] Rename accesors --- benchmarks/statevec.py | 2 +- docs/source/modifier.rst | 8 +++---- docs/source/tutorial.rst | 2 +- examples/fusion_extraction.py | 2 +- examples/ghz_with_tn.py | 2 +- examples/qft_with_tn.py | 2 +- examples/tn_simulation.py | 2 +- graphix/_linalg.py | 4 ++-- graphix/flow/core.py | 4 ++-- graphix/optimization.py | 8 +++---- graphix/pattern.py | 36 ++++++++++++++-------------- graphix/remove_pauli_measurements.py | 2 +- graphix/sim/tensornet.py | 6 ++--- graphix/space_minimization.py | 6 ++--- tests/test_flow_core.py | 8 +++---- tests/test_linalg.py | 4 ++-- tests/test_pattern.py | 26 ++++++++++---------- tests/test_space_minimization.py | 2 +- tests/test_transpiler.py | 2 +- 19 files changed, 64 insertions(+), 64 deletions(-) diff --git a/benchmarks/statevec.py b/benchmarks/statevec.py index 73e1c37c6..7a29b376d 100644 --- a/benchmarks/statevec.py +++ b/benchmarks/statevec.py @@ -85,7 +85,7 @@ def simple_random_circuit(nqubit, depth): pattern = circuit.transpile() pattern.standardize() pattern.minimize_space() - nqubit = len(pattern.extract_nodes()) + nqubit = len(pattern.nodes()) start = perf_counter() pattern.simulate_pattern(max_qubit_num=30) end = perf_counter() diff --git a/docs/source/modifier.rst b/docs/source/modifier.rst index 5bd6679b2..7a4fff5ce 100644 --- a/docs/source/modifier.rst +++ b/docs/source/modifier.rst @@ -24,7 +24,7 @@ Pattern Manipulation .. automethod:: simulate_pattern - .. automethod:: compute_max_degree + .. automethod:: max_degree .. automethod:: compose @@ -50,9 +50,9 @@ Pattern Manipulation .. automethod:: is_standard - .. automethod:: extract_graph + .. automethod:: graph - .. automethod:: extract_nodes + .. automethod:: nodes .. automethod:: to_causalflow @@ -60,7 +60,7 @@ Pattern Manipulation .. automethod:: to_opengraph - .. automethod:: extract_measurement_commands + .. automethod:: measurement_commands .. automethod:: parallelize_pattern diff --git a/docs/source/tutorial.rst b/docs/source/tutorial.rst index a828719e7..5e7a44227 100644 --- a/docs/source/tutorial.rst +++ b/docs/source/tutorial.rst @@ -157,7 +157,7 @@ This reveals the graph structure of the resource state which we can inspect: .. code-block:: python import networkx as nx - graph = pattern.extract_graph() + graph = pattern.graph() pos = {0: (0, 0), 1: (0, -0.5), 2: (1, 0), 3: (4, 0), 4: (1, -0.5), 5: (2, -0.5), 6: (3, -0.5), 7: (4, -0.5)} graph_params = {'node_size': 240, 'node_color': 'w', 'edgecolors': 'k', 'with_labels': True} nx.draw(graph, pos=pos, **graph_params) diff --git a/examples/fusion_extraction.py b/examples/fusion_extraction.py index abc0a1746..7a450e42c 100644 --- a/examples/fusion_extraction.py +++ b/examples/fusion_extraction.py @@ -26,7 +26,7 @@ # %% # Here we say we want a graph state with 9 nodes and 12 edges. -# We can obtain resource graph for a measurement pattern by using :code:`pattern.extract_graph()`. +# We can obtain resource graph for a measurement pattern by using :code:`pattern.graph()`. gs = Graph() nodes = [0, 1, 2, 3, 4, 5, 6, 7, 8] edges = [(0, 1), (1, 2), (2, 3), (3, 0), (3, 4), (0, 5), (4, 5), (5, 6), (6, 7), (7, 0), (7, 8), (8, 1)] diff --git a/examples/ghz_with_tn.py b/examples/ghz_with_tn.py index 2834c4e0e..a734b54ab 100644 --- a/examples/ghz_with_tn.py +++ b/examples/ghz_with_tn.py @@ -35,7 +35,7 @@ pattern = circuit.transpile().pattern pattern.standardize() -graph = pattern.extract_graph() +graph = pattern.graph() print(f"Number of nodes: {len(graph.nodes)}") print(f"Number of edges: {len(graph.edges)}") pos = nx.spring_layout(graph) diff --git a/examples/qft_with_tn.py b/examples/qft_with_tn.py index 5cb991c2a..af8f6a43d 100644 --- a/examples/qft_with_tn.py +++ b/examples/qft_with_tn.py @@ -59,7 +59,7 @@ def qft(circuit: Circuit, n: int) -> None: pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() -graph = pattern.extract_graph() +graph = pattern.graph() print(f"Number of nodes: {len(graph.nodes)}") print(f"Number of edges: {len(graph.edges)}") diff --git a/examples/tn_simulation.py b/examples/tn_simulation.py index 84a5e8b19..873ca1e14 100644 --- a/examples/tn_simulation.py +++ b/examples/tn_simulation.py @@ -78,7 +78,7 @@ def ansatz( # %% # Print some properties of the graph. -graph = pattern.extract_graph() +graph = pattern.graph() print(f"Number of nodes: {len(graph.nodes)}") print(f"Number of edges: {len(graph.edges)}") diff --git a/graphix/_linalg.py b/graphix/_linalg.py index 094560693..11f509bb8 100644 --- a/graphix/_linalg.py +++ b/graphix/_linalg.py @@ -64,7 +64,7 @@ def mat_mul(self, other: MatGF2 | npt.NDArray[np.uint8]) -> MatGF2: return MatGF2(_mat_mul_jit(np.ascontiguousarray(self), np.ascontiguousarray(other)), copy=False) - def compute_rank(self) -> np.intp: + def rank(self) -> np.intp: """Get the rank of the matrix. Returns @@ -100,7 +100,7 @@ def right_inverse(self) -> MatGF2 | None: red = aug.row_reduction(ncols=n, copy=False) # Reduced row echelon form # Check that rank of right block is equal to the number of rows. - # We don't use `MatGF2.compute_rank()` to avoid row-reducing twice. + # We don't use `MatGF2.rank()` to avoid row-reducing twice. if m != np.count_nonzero(red[:, :n].any(axis=1)): return None rinv = np.zeros((n, m), dtype=np.uint8).view(MatGF2) diff --git a/graphix/flow/core.py b/graphix/flow/core.py index 3ea981bcd..40f92f276 100644 --- a/graphix/flow/core.py +++ b/graphix/flow/core.py @@ -351,7 +351,7 @@ def generate_total_measurement_order(self) -> TotalOrder: assert set(total_order) == set(self.og.graph.nodes) - set(self.og.output_nodes) return total_order - def extract_dag(self) -> nx.DiGraph[int]: + def dag(self) -> nx.DiGraph[int]: """Extract the directed graph induced by the XZ-corrections. Returns @@ -1319,7 +1319,7 @@ def _corrections_to_dag( Notes ----- - See :func:`XZCorrections.extract_dag`. + See :func:`XZCorrections.dag`. """ relations = ( (measured_node, corrected_node) diff --git a/graphix/optimization.py b/graphix/optimization.py index e60cb9585..f85205389 100644 --- a/graphix/optimization.py +++ b/graphix/optimization.py @@ -221,7 +221,7 @@ def from_pattern(cls, pattern: Pattern) -> Self: c_dict[cmd.node] = cmd.clifford @ c_dict.get(cmd.node, Clifford.I) return cls(pattern.input_nodes, pattern.output_nodes, n_list, e_set, m_list, z_dict, x_dict, c_dict) - def extract_graph(self) -> nx.Graph[int]: + def graph(self) -> nx.Graph[int]: """Return the graph state from the command sequence, extracted from 'N' and 'E' commands. Returns @@ -353,9 +353,9 @@ def to_opengraph(self) -> OpenGraph[Measurement]: f"Open graph construction in flow extraction requires N commands to represent a |+⟩ state. Error found in {n}." ) measurements = {m.node: m.measurement for m in self.m_list} - return OpenGraph(self.extract_graph(), self.input_nodes, self.output_nodes, measurements, self.c_dict) + return OpenGraph(self.graph(), self.input_nodes, self.output_nodes, measurements, self.c_dict) - def extract_partial_order_layers(self) -> tuple[frozenset[int], ...]: + def partial_order_layers(self) -> tuple[frozenset[int], ...]: """Extract the measurement order of the pattern in the form of layers. This method builds a directed acyclical graph (DAG) from the pattern and then performs a topological sort. @@ -617,7 +617,7 @@ def remove_local_clifford_commands(pattern: Pattern) -> Pattern: """ from graphix.pattern import Pattern # noqa: PLC0415 - nodes = pattern.extract_nodes() + nodes = pattern.nodes() if not nodes: return pattern max_node = max(nodes) diff --git a/graphix/pattern.py b/graphix/pattern.py index f0d447800..500718488 100644 --- a/graphix/pattern.py +++ b/graphix/pattern.py @@ -208,7 +208,7 @@ def node_mapping( freshly generated by the function; they do not apply to the nodes already present in ``mapping``. """ - nodes = self.extract_nodes() + nodes = self.nodes() if mapping is None: result = {} else: @@ -325,8 +325,8 @@ def compose( - Input (and, respectively, output) nodes in the returned pattern have the order of the pattern ``self`` followed by those of the pattern ``other``. Merged nodes are removed. - If ``preserve_mapping = True`` and :math:`|M_1| = |I_2| = |O_2|`, then the outputs of the returned pattern are the outputs of pattern ``self``, where the nth merged output is replaced by the output of pattern ``other`` corresponding to its nth input instead. """ - nodes_p1 = self.extract_nodes() # Results contain preprocessed Pauli nodes - nodes_p2 = other.extract_nodes() + nodes_p1 = self.nodes() # Results contain preprocessed Pauli nodes + nodes_p2 = other.nodes() if not mapping.keys() <= nodes_p2: raise PatternError("Keys of `mapping` must correspond to the nodes of `other`.") @@ -645,7 +645,7 @@ def shift_signals(self, method: str = "direct") -> dict[int, set[int]]: case "direct": return self.shift_signals_direct() case "mc": - signal_dict = self.extract_signals() + signal_dict = self.signals() target = self._find_op_to_be_moved(CommandKind.S, rev=True) while target is not None: if target == len(self.__seq) - 1: @@ -996,7 +996,7 @@ def _move_e_after_n(self) -> None: self._commute_with_preceding(target) target -= 1 - def extract_signals(self) -> dict[int, set[int]]: + def signals(self) -> dict[int, set[int]]: """Extract 't' domain of measurement commands, turn them into signal 'S' commands and add to the command sequence. This is used for shift_signals() method. @@ -1016,7 +1016,7 @@ def extract_signals(self) -> dict[int, set[int]]: pos += 1 return signal_dict - def extract_partial_order_layers(self) -> tuple[frozenset[int], ...]: + def partial_order_layers(self) -> tuple[frozenset[int], ...]: """Extract the measurement order of the pattern in the form of layers. This method standardizes the pattern, builds a directed acyclical graph (DAG) from measurement and correction domains, and then performs a topological sort. @@ -1033,9 +1033,9 @@ def extract_partial_order_layers(self) -> tuple[frozenset[int], ...]: Notes ----- - - This function wraps :func:`optimization.StandardizedPattern.extract_partial_order_layers`, and the returned object is described in the notes of this method. + - This function wraps :func:`optimization.StandardizedPattern.partial_order_layers`, and the returned object is described in the notes of this method. """ - return optimization.StandardizedPattern.from_pattern(self).extract_partial_order_layers() + return optimization.StandardizedPattern.from_pattern(self).partial_order_layers() def to_causalflow(self) -> CausalFlow[BlochMeasurement]: r"""Extract the causal flow structure from the current measurement pattern. @@ -1148,7 +1148,7 @@ def _measurement_order_depth(self) -> list[int]: list[int] optimal measurement order for parallel computing """ - partial_order_layers = self.extract_partial_order_layers() + partial_order_layers = self.partial_order_layers() return list(itertools.chain(*reversed(partial_order_layers[1:]))) def sort_measurement_commands(self, meas_order: list[int]) -> list[command.M]: @@ -1164,10 +1164,10 @@ def sort_measurement_commands(self, meas_order: list[int]) -> list[command.M]: meas_cmds: list of command sorted measurement commands """ - meas_dict = self.extract_measurement_commands() + meas_dict = self.measurement_commands() return [meas_dict[i] for i in meas_order] - def extract_measurement_commands(self) -> dict[int, command.M]: + def measurement_commands(self) -> dict[int, command.M]: """Return a dictionary mapping nodes to measurement commands. Returns @@ -1177,7 +1177,7 @@ def extract_measurement_commands(self) -> dict[int, command.M]: """ return {cmd.node: cmd for cmd in self if cmd.kind == CommandKind.M} - def compute_max_degree(self) -> int: + def max_degree(self) -> int: """Get max degree of a pattern. Returns @@ -1185,7 +1185,7 @@ def compute_max_degree(self) -> int: max_degree : int max degree of a pattern """ - graph = self.extract_graph() + graph = self.graph() degree = graph.degree() assert isinstance(degree, nx.classes.reportviews.DiDegreeView) degrees = dict(degree).values() @@ -1193,7 +1193,7 @@ def compute_max_degree(self) -> int: return 0 return int(max(degrees)) - def extract_graph(self) -> nx.Graph[int]: + def graph(self) -> nx.Graph[int]: """Return the graph state from the command sequence, extracted from ``N`` and ``E`` commands. Returns @@ -1214,7 +1214,7 @@ def extract_graph(self) -> nx.Graph[int]: graph.add_edge(u, v) return graph - def extract_nodes(self) -> set[int]: + def nodes(self) -> set[int]: """Return the set of nodes of the pattern.""" nodes = set(self.input_nodes) for cmd in self.__seq: @@ -1222,7 +1222,7 @@ def extract_nodes(self) -> set[int]: nodes.add(cmd.node) return nodes - def extract_isolated_nodes(self) -> set[int]: + def isolated_nodes(self) -> set[int]: """Get isolated nodes. Returns @@ -1230,7 +1230,7 @@ def extract_isolated_nodes(self) -> set[int]: isolated_nodes : set[int] set of the isolated nodes """ - graph = self.extract_graph() + graph = self.graph() return {node for node, d in graph.degree if d == 0} def to_opengraph(self) -> OpenGraph[Measurement]: @@ -1247,7 +1247,7 @@ def to_opengraph(self) -> OpenGraph[Measurement]: """ return optimization.StandardizedPattern.from_pattern(self).to_opengraph() - def extract_clifford(self) -> dict[int, Clifford]: + def clifford_commands(self) -> dict[int, Clifford]: """Extract Clifford commands. Returns diff --git a/graphix/remove_pauli_measurements.py b/graphix/remove_pauli_measurements.py index 5ea7b957e..fd06ebc0a 100644 --- a/graphix/remove_pauli_measurements.py +++ b/graphix/remove_pauli_measurements.py @@ -256,7 +256,7 @@ class _RemovePauliMeasurements: def __init__(self, cut: PauliPushingCut) -> None: self.cut = cut - self.graph = cut.original_pattern.extract_graph() + self.graph = cut.original_pattern.graph() self.node_specs = {node: _NodeSpec(node) for node in self.graph.nodes()} for node, domain in cut.original_pattern.x_dict.items(): self.node_specs[node].domains.s_domain = _expand_domain(cut.shifted_domains, domain) diff --git a/graphix/sim/tensornet.py b/graphix/sim/tensornet.py index f438bd0c6..df893fb77 100644 --- a/graphix/sim/tensornet.py +++ b/graphix/sim/tensornet.py @@ -639,7 +639,7 @@ def __init__( stacklevel=1, ) case "auto": - max_degree = pattern.compute_max_degree() + max_degree = pattern.max_degree() # "parallel" does not support non standard pattern graph_prep = "sequential" if max_degree > 5 or not pattern.is_standard() else "parallel" case _: @@ -648,7 +648,7 @@ def __init__( if graph_prep == "parallel": if not pattern.is_standard(): raise ValueError("parallel preparation strategy does not support not-standardized pattern") - graph = pattern.extract_graph() + graph = pattern.graph() state = MBQCTensorNet( graph_nodes=graph.nodes, graph_edges=graph.edges, @@ -659,7 +659,7 @@ def __init__( else: # graph_prep == "sequential": state = MBQCTensorNet(default_output_nodes=pattern.output_nodes, branch_selector=branch_selector) decomposed_cz = _decompose_cz() - isolated_nodes = pattern.extract_isolated_nodes() + isolated_nodes = pattern.isolated_nodes() super().__init__( state, pattern, diff --git a/graphix/space_minimization.py b/graphix/space_minimization.py index c85eb292f..35e7bdd17 100644 --- a/graphix/space_minimization.py +++ b/graphix/space_minimization.py @@ -66,7 +66,7 @@ def standardized_pattern_max_space(pattern: StandardizedPattern) -> int: pattern execution. """ initialized = set(pattern.input_nodes) - graph = pattern.extract_graph() + graph = pattern.graph() num_active = len(pattern.input_nodes) max_active = num_active @@ -113,7 +113,7 @@ def standardized_to_space_optimal_pattern(pattern: StandardizedPattern) -> Patte initialized = set(pattern.input_nodes) done: set[Node] = set() n_dict = {n.node: n for n in pattern.n_list} - graph = pattern.extract_graph() + graph = pattern.graph() def ensure_active(node: Node) -> None: """Initialize node in pattern if it has not been initialized before.""" @@ -224,7 +224,7 @@ def greedy_degree(pattern: StandardizedPattern) -> SpaceMinimizationHeuristicRes max space in some situations. """ - graph = pattern.extract_graph() + graph = pattern.graph() nodes = set(graph.nodes) not_measured = nodes - set(pattern.output_nodes) dependency = _extract_dependency(pattern) diff --git a/tests/test_flow_core.py b/tests/test_flow_core.py index d5fa30341..fa6dd97d7 100644 --- a/tests/test_flow_core.py +++ b/tests/test_flow_core.py @@ -506,7 +506,7 @@ def test_order_0(self) -> None: assert not corrections.is_compatible([1, 2]) # Incomplete order assert not corrections.is_compatible([0, 1, 2, 3]) # Contains outputs - assert nx.utils.graphs_equal(corrections.extract_dag(), nx.DiGraph([(0, 1), (0, 2), (1, 2), (2, 3), (1, 3)])) + assert nx.utils.graphs_equal(corrections.dag(), nx.DiGraph([(0, 1), (0, 2), (1, 2), (2, 3), (1, 3)])) # See `:func: generate_causal_flow_1` def test_order_1(self) -> None: @@ -532,7 +532,7 @@ def test_order_1(self) -> None: assert not corrections.is_compatible([0, 1, 2, 3, 4, 5]) # Contains outputs assert nx.utils.graphs_equal( - corrections.extract_dag(), nx.DiGraph([(0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 5), (2, 4), (3, 5)]) + corrections.dag(), nx.DiGraph([(0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 5), (2, 4), (3, 5)]) ) # Incomplete corrections @@ -555,7 +555,7 @@ def test_order_2(self) -> None: assert not corrections.is_compatible([0, 0, 1]) # Duplicates assert not corrections.is_compatible([1, 0, 2, 3]) # Contains outputs - assert nx.utils.graphs_equal(corrections.extract_dag(), nx.DiGraph([(1, 0)])) + assert nx.utils.graphs_equal(corrections.dag(), nx.DiGraph([(1, 0)])) # OG without outputs def test_order_3(self) -> None: @@ -576,7 +576,7 @@ def test_order_3(self) -> None: assert not corrections.is_compatible([2, 0, 1]) # Wrong order assert not corrections.is_compatible([0, 1]) # Incomplete order assert corrections.generate_total_measurement_order() in ([0, 1, 2], [0, 2, 1]) - assert nx.utils.graphs_equal(corrections.extract_dag(), nx.DiGraph([(0, 1), (0, 2)])) + assert nx.utils.graphs_equal(corrections.dag(), nx.DiGraph([(0, 1), (0, 2)])) # Only output nodes def test_from_measured_nodes_mapping_0(self) -> None: diff --git a/tests/test_linalg.py b/tests/test_linalg.py index a1ad1ce2f..b642c02d8 100644 --- a/tests/test_linalg.py +++ b/tests/test_linalg.py @@ -180,10 +180,10 @@ def verify_elimination(mat: MatGF2, mat_red: MatGF2, n_cols_red: int, full_reduc class TestLinAlg: @pytest.mark.parametrize("test_case", prepare_test_matrix()) - def test_compute_rank(self, test_case: LinalgTestCase) -> None: + def test_rank(self, test_case: LinalgTestCase) -> None: mat = test_case.matrix rank = test_case.rank - assert mat.compute_rank() == rank + assert mat.rank() == rank @pytest.mark.parametrize("test_case", prepare_test_matrix()) def test_right_inverse(self, benchmark: BenchmarkFixture, test_case: LinalgTestCase) -> None: diff --git a/tests/test_pattern.py b/tests/test_pattern.py index 3d83749eb..809206e84 100644 --- a/tests/test_pattern.py +++ b/tests/test_pattern.py @@ -292,7 +292,7 @@ def test_pauli_measurement(self) -> None: pattern.shift_signals(method="mc") pattern = pattern.infer_pauli_measurements() pattern_opt = pattern.remove_pauli_measurements(copy=True) - isolated_nodes = pattern_opt.extract_isolated_nodes() + isolated_nodes = pattern_opt.isolated_nodes() assert isolated_nodes == set() pattern.minimize_space() pattern_opt.minimize_space() @@ -315,7 +315,7 @@ def test_pauli_measured_against_nonmeasured(self, fx_bg: PCG64, jumps: int) -> N state1 = pattern1.simulate_pattern(rng=rng) assert state.isclose(state1) - def test_extract_measurement_commands(self) -> None: + def test_measurement_commands(self) -> None: preset_meas_plane = [ Plane.XY, Plane.XY, @@ -342,7 +342,7 @@ def test_extract_measurement_commands(self) -> None: 7: M(7, Measurement.YZ(0)), 8: M(8, Measurement.XZ(0.5)), } - meas = pattern.extract_measurement_commands() + meas = pattern.measurement_commands() assert meas == ref_meas @pytest.mark.parametrize("plane", Plane) @@ -754,7 +754,7 @@ def test_check_runnability_failures(self) -> None: pattern = Pattern(cmds=[N(0), M(0, s_domain={0})]) with pytest.raises(RunnabilityError) as exc_info: - pattern.extract_partial_order_layers() + pattern.partial_order_layers() assert exc_info.value.node == 0 assert exc_info.value.reason == RunnabilityErrorReason.DomainSelfLoop @@ -770,8 +770,8 @@ def test_check_runnability_failures(self) -> None: assert exc_info.value.node == 1 assert exc_info.value.reason == RunnabilityErrorReason.NotYetMeasured - def test_compute_max_degree_empty_pattern(self) -> None: - assert Pattern().compute_max_degree() == 0 + def test_max_degree_empty_pattern(self) -> None: + assert Pattern().max_degree() == 0 @pytest.mark.parametrize( "test_case", @@ -796,21 +796,21 @@ def test_compute_max_degree_empty_pattern(self) -> None: ), # double edge in DAG ], ) - def test_extract_partial_order_layers(self, test_case: tuple[Pattern, tuple[frozenset[int], ...]]) -> None: - assert test_case[0].extract_partial_order_layers() == test_case[1] + def test_partial_order_layers(self, test_case: tuple[Pattern, tuple[frozenset[int], ...]]) -> None: + assert test_case[0].partial_order_layers() == test_case[1] - def test_extract_partial_order_layers_results(self) -> None: + def test_partial_order_layers_results(self) -> None: c = Circuit(1) c.rz(0, 0.2) p = c.transpile().pattern p = p.infer_pauli_measurements() p.remove_pauli_measurements() - assert p.extract_partial_order_layers() == (frozenset({1}), frozenset({0})) + assert p.partial_order_layers() == (frozenset({1}), frozenset({0})) p = Pattern(cmds=[N(0), N(1), N(2), M(0), E((1, 2)), X(1, {0}), M(2, Measurement.XY(0.3))]) p = p.infer_pauli_measurements() p.remove_pauli_measurements() - assert p.extract_partial_order_layers() == (frozenset({1}), frozenset({2})) + assert p.partial_order_layers() == (frozenset({1}), frozenset({2})) class PatternFlowTestCase(NamedTuple): pattern: Pattern @@ -1237,7 +1237,7 @@ def test_no_gate(self) -> None: pattern = circuit.transpile().pattern assert len(list(iter(pattern))) == 0 - def test_extract_graph(self) -> None: + def test_graph(self) -> None: n = 3 g = nx.complete_graph(n) circuit = Circuit(n) @@ -1249,7 +1249,7 @@ def test_extract_graph(self) -> None: circuit.rx(v, ANGLE_PI / 9) pattern = circuit.transpile().pattern - graph = pattern.extract_graph() + graph = pattern.graph() graph_ref: nx.Graph[int] = nx.Graph() graph_ref.add_nodes_from(range(27)) diff --git a/tests/test_space_minimization.py b/tests/test_space_minimization.py index 88099ef6b..9e99104ec 100644 --- a/tests/test_space_minimization.py +++ b/tests/test_space_minimization.py @@ -77,7 +77,7 @@ def test_minimization_by_degree_edge_ordering() -> None: ] ) # Verify Networkx node ordering behaves as expected - graph = p.extract_graph() + graph = p.graph() assert set(graph.edges()) == {(0, 2), (1, 2)} assert set(graph.edges(2)) == {(2, 0), (2, 1)} diff --git a/tests/test_transpiler.py b/tests/test_transpiler.py index 6c3c8f121..d5048eb6d 100644 --- a/tests/test_transpiler.py +++ b/tests/test_transpiler.py @@ -253,7 +253,7 @@ def test_classical_outputs_consistency(self, fx_bg: PCG64, jumps: int, axes: lis expected_outcomes: list[Outcome] = [1 if q % 2 else 0 for q in range(n)] results_circuit: dict[int, Outcome] = dict(zip(range(n), expected_outcomes, strict=False)) m_outcomes = dict(zip(transpile_result.classical_outputs, expected_outcomes, strict=False)) - non_output_nodes = pattern.extract_nodes() - set(pattern.output_nodes) + non_output_nodes = pattern.nodes() - set(pattern.output_nodes) results_pattern: dict[int, Outcome] = {node: m_outcomes.get(node, 0) for node in non_output_nodes} input_state = rand_state_vector(width, rng=rng) measure_method = DefaultMeasureMethod() From a544b0b99ee7314a9084d2d828a00207e26d15d3 Mon Sep 17 00:00:00 2001 From: matulni Date: Tue, 7 Jul 2026 14:09:00 +0200 Subject: [PATCH 3/6] Up nox --- noxfile.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/noxfile.py b/noxfile.py index ab29a86c6..4dd431d22 100644 --- a/noxfile.py +++ b/noxfile.py @@ -95,16 +95,17 @@ class ReverseDependency: @nox.parametrize( "package", [ - ReverseDependency("https://github.com/TeamGraphix/graphix-stim-backend"), + ReverseDependency("https://github.com/matulni/graphix-stim-backend", branch="rename_methods"), ReverseDependency("https://github.com/TeamGraphix/graphix-symbolic"), ReverseDependency("https://github.com/TeamGraphix/graphix-qasm-parser"), - ReverseDependency("https://github.com/TeamGraphix/graphix-ibmq", doctest_modules=False), - ReverseDependency("https://github.com/TeamGraphix/graphix-stim-compiler"), - ReverseDependency("https://github.com/TeamGraphix/graphix-pyzx"), + ReverseDependency("https://github.com/matulni/graphix-ibmq", doctest_modules=False, branch="rename_methods"), + ReverseDependency("https://github.com/matulni/graphix-stim-compiler", branch="rename_methods"), + ReverseDependency("https://github.com/matulni/graphix-pyzx", branch="rename_methods"), ReverseDependency( - "https://github.com/qat-inria/veriphix", + "https://github.com/matulni/veriphix", doctest_modules=False, install_target=".[dev]", + branch="rename_methods", ), ReverseDependency("https://github.com/matulni/graphix-mqtbench", branch="minimal"), ], From f35a717795ee18826eb05d92f886436901ee72a2 Mon Sep 17 00:00:00 2001 From: matulni Date: Tue, 7 Jul 2026 14:29:21 +0200 Subject: [PATCH 4/6] Mod try_from --- graphix/fundamentals.py | 6 +++--- graphix/measurements.py | 22 +++++++++++----------- graphix/opengraph.py | 2 +- graphix/optimization.py | 2 +- graphix/pattern.py | 2 +- graphix/pauli.py | 2 +- tests/test_fundamentals.py | 12 ++++++------ tests/test_measurements.py | 2 +- tests/test_pattern.py | 2 +- 9 files changed, 26 insertions(+), 26 deletions(-) diff --git a/graphix/fundamentals.py b/graphix/fundamentals.py index 809482acf..617cdd872 100644 --- a/graphix/fundamentals.py +++ b/graphix/fundamentals.py @@ -166,7 +166,7 @@ class ComplexUnit(EnumReprMixin, Enum): MINUS_J = 3 @staticmethod - def try_from( + def from_or_none( value: ComplexUnit | SupportsComplexCtor, rel_tol: float = 1e-09, abs_tol: float = 0.0 ) -> ComplexUnit | None: """Return the ComplexUnit instance if the value is compatible, None otherwise. @@ -234,7 +234,7 @@ def __mul__(self, other: ComplexUnit | SupportsComplexCtor) -> ComplexUnit: if isinstance( other, (SupportsComplex, SupportsFloat, SupportsIndex, complex), - ) and (other_ := ComplexUnit.try_from(other)): + ) and (other_ := ComplexUnit.from_or_none(other)): return self.__mul__(other_) return NotImplemented @@ -329,7 +329,7 @@ def clifford(self, clifford_gate: Clifford) -> Self: -Measurement.Y >>> for pauli in PauliMeasurement: ... for clifford in Clifford: - ... assert pauli.to_bloch().clifford(clifford).try_to_pauli() == pauli.clifford(clifford) + ... assert pauli.to_bloch().clifford(clifford).to_pauli_or_none() == pauli.clifford(clifford) >>> Measurement.Y.clifford(Clifford.H).to_bloch() Measurement.XY(1.5) >>> Measurement.Y.to_bloch().clifford(Clifford.H) diff --git a/graphix/measurements.py b/graphix/measurements.py index 55307b9d4..63b297724 100644 --- a/graphix/measurements.py +++ b/graphix/measurements.py @@ -96,7 +96,7 @@ def to_bloch(self) -> BlochMeasurement: For instance, >>> from graphix.measurements import Measurement - >>> Measurement.XY(0.5).try_to_pauli() == Measurement.YZ(0.5).try_to_pauli() == Measurement.Y + >>> Measurement.XY(0.5).to_pauli_or_none() == Measurement.YZ(0.5).to_pauli_or_none() == Measurement.Y True @@ -129,7 +129,7 @@ def downcast_bloch(self) -> BlochMeasurement: """ @abstractmethod - def try_to_pauli(self, rel_tol: float = 1e-09, abs_tol: float = 0.0) -> PauliMeasurement | None: + def to_pauli_or_none(self, rel_tol: float = 1e-09, abs_tol: float = 0.0) -> PauliMeasurement | None: """Return the measurement description as a Pauli measurement if possible, or ``None`` otherwise. Parameters @@ -158,17 +158,17 @@ def try_to_pauli(self, rel_tol: float = 1e-09, abs_tol: float = 0.0) -> PauliMea Examples -------- >>> from graphix.measurements import Measurement - >>> Measurement.XY(0.5).try_to_pauli() + >>> Measurement.XY(0.5).to_pauli_or_none() Measurement.Y - >>> Measurement.Y.try_to_pauli() + >>> Measurement.Y.to_pauli_or_none() Measurement.Y - >>> Measurement.XY(0.25).try_to_pauli() is None + >>> Measurement.XY(0.25).to_pauli_or_none() is None True >>> from graphix.parameter import Placeholder >>> alpha = Placeholder("alpha") - >>> Measurement.XY(alpha).try_to_pauli() is None + >>> Measurement.XY(alpha).to_pauli_or_none() is None True - >>> Measurement.XY(alpha).subs(alpha, 0.5).try_to_pauli() + >>> Measurement.XY(alpha).subs(alpha, 0.5).to_pauli_or_none() Measurement.Y """ @@ -250,7 +250,7 @@ def downcast_bloch(self) -> BlochMeasurement: return self @override - def try_to_pauli(self, rel_tol: float = 1e-09, abs_tol: float = 0.0) -> PauliMeasurement | None: + def to_pauli_or_none(self, rel_tol: float = 1e-09, abs_tol: float = 0.0) -> PauliMeasurement | None: if not isinstance(self.angle, (int, float)): return None angle_double = 2 * self.angle @@ -264,7 +264,7 @@ def try_to_pauli(self, rel_tol: float = 1e-09, abs_tol: float = 0.0) -> PauliMea @override def to_pauli_or_bloch(self, rel_tol: float = 1e-09, abs_tol: float = 0.0) -> PauliMeasurement | BlochMeasurement: - pm = self.try_to_pauli(rel_tol=rel_tol, abs_tol=abs_tol) + pm = self.to_pauli_or_none(rel_tol=rel_tol, abs_tol=abs_tol) return self if pm is None else pm @override @@ -418,7 +418,7 @@ def to_pauli(self) -> Pauli: """Return the Pauli gate. This method returns an instance of :class:`Pauli` and should - not be confused with :meth:`try_to_pauli`, which overrides the + not be confused with :meth:`to_pauli_or_none`, which overrides the method from :class:`Measurement`, and returns ``self``. Examples @@ -453,7 +453,7 @@ def downcast_bloch(self) -> BlochMeasurement: raise TypeError("Bloch measurement expected, but Pauli measurement was found.") @override - def try_to_pauli(self, rel_tol: float = 1e-09, abs_tol: float = 0.0) -> PauliMeasurement: + def to_pauli_or_none(self, rel_tol: float = 1e-09, abs_tol: float = 0.0) -> PauliMeasurement: """Return ``self`` (overridden from :class:`Measurement`).""" return self diff --git a/graphix/opengraph.py b/graphix/opengraph.py index 3618a5679..64b210af4 100644 --- a/graphix/opengraph.py +++ b/graphix/opengraph.py @@ -826,7 +826,7 @@ def xreplace(self: OpenGraph[_M], assignment: Mapping[Parameter, ExpressionOrSup def _warn_non_inferred_pauli_measurements(self, stacklevel: int) -> None: for m in self.measurements.values(): - if isinstance(m, BlochMeasurement) and m.try_to_pauli() is not None: + if isinstance(m, BlochMeasurement) and m.to_pauli_or_none() is not None: warn("Open graph with non-inferred Pauli measurements.", stacklevel=stacklevel + 1) return diff --git a/graphix/optimization.py b/graphix/optimization.py index f85205389..e84eb46fd 100644 --- a/graphix/optimization.py +++ b/graphix/optimization.py @@ -480,7 +480,7 @@ def to_bloch(self) -> StandardizedPattern: def _warn_non_inferred_pauli_measurements(self, stacklevel: int) -> None: for m in self.m_list: - if isinstance(m.measurement, BlochMeasurement) and m.measurement.try_to_pauli() is not None: + if isinstance(m.measurement, BlochMeasurement) and m.measurement.to_pauli_or_none() is not None: warn("Pattern with non-inferred Pauli measurements.", stacklevel=stacklevel + 1) return diff --git a/graphix/pattern.py b/graphix/pattern.py index 500718488..7960db9ec 100644 --- a/graphix/pattern.py +++ b/graphix/pattern.py @@ -1502,7 +1502,7 @@ def _warn_non_inferred_pauli_measurements(self, stacklevel: int) -> None: if ( cmd.kind == CommandKind.M and isinstance(cmd.measurement, BlochMeasurement) - and cmd.measurement.try_to_pauli() is not None + and cmd.measurement.to_pauli_or_none() is not None ): warnings.warn("Pattern with non-inferred Pauli measurements.", stacklevel=stacklevel + 1) return diff --git a/graphix/pauli.py b/graphix/pauli.py index b39230c9c..c20683786 100644 --- a/graphix/pauli.py +++ b/graphix/pauli.py @@ -140,7 +140,7 @@ def __matmul__(self, other: Pauli) -> Pauli: def __mul__(self, other: ComplexUnit | SupportsComplexCtor) -> Pauli: """Return the product of two Paulis.""" - if u := ComplexUnit.try_from(other): + if u := ComplexUnit.from_or_none(other): return dataclasses.replace(self, unit=self.unit * u) return NotImplemented diff --git a/tests/test_fundamentals.py b/tests/test_fundamentals.py index 2460fad29..ec0240256 100644 --- a/tests/test_fundamentals.py +++ b/tests/test_fundamentals.py @@ -84,12 +84,12 @@ def test_int(self) -> None: class TestComplexUnit: - def test_try_from(self) -> None: - assert ComplexUnit.try_from(ComplexUnit.ONE) == ComplexUnit.ONE - assert ComplexUnit.try_from(1) == ComplexUnit.ONE - assert ComplexUnit.try_from(1.0) == ComplexUnit.ONE - assert ComplexUnit.try_from(1.0 + 0.0j) == ComplexUnit.ONE - assert ComplexUnit.try_from(3) is None + def test_from_or_none(self) -> None: + assert ComplexUnit.from_or_none(ComplexUnit.ONE) == ComplexUnit.ONE + assert ComplexUnit.from_or_none(1) == ComplexUnit.ONE + assert ComplexUnit.from_or_none(1.0) == ComplexUnit.ONE + assert ComplexUnit.from_or_none(1.0 + 0.0j) == ComplexUnit.ONE + assert ComplexUnit.from_or_none(3) is None def test_from_properties(self) -> None: assert ComplexUnit.from_properties() == ComplexUnit.ONE diff --git a/tests/test_measurements.py b/tests/test_measurements.py index 516542987..c88824425 100644 --- a/tests/test_measurements.py +++ b/tests/test_measurements.py @@ -20,7 +20,7 @@ def test_isclose(self) -> None: @pytest.mark.parametrize("pauli", PauliMeasurement) def test_pauli_to_bloch(pauli: PauliMeasurement) -> None: bloch = pauli.to_bloch() - pauli_back = bloch.try_to_pauli() + pauli_back = bloch.to_pauli_or_none() assert pauli == pauli_back diff --git a/tests/test_pattern.py b/tests/test_pattern.py index 809206e84..d8c740772 100644 --- a/tests/test_pattern.py +++ b/tests/test_pattern.py @@ -255,7 +255,7 @@ def test_pauli_measurement_random_circuit_all_paulis(self, fx_bg: PCG64, jumps: pattern.remove_pauli_measurements() input_node_set = set(pattern.input_nodes) assert not any( - cmd.measurement.try_to_pauli() is not None + cmd.measurement.to_pauli_or_none() is not None for cmd in pattern if cmd.kind == CommandKind.M and cmd.node not in input_node_set ) From 8f046536bbc65269dbe5017d93f706303ca0cb60 Mon Sep 17 00:00:00 2001 From: matulni Date: Tue, 7 Jul 2026 16:26:21 +0200 Subject: [PATCH 5/6] Up CHANGELOG --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0f40251f..b88b0dbb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -100,6 +100,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - #512: Method `Circuit.simulate_statevector` accepts a `backend: DenseStateBackend[_DenseStateT] | Literal["statevector", "densitymatrix"]` parameter. +- #557: Homogeneize namespace. See PR or commit text for detailed renaming list. Fixed the following convention: + - `.to_` for transformations that can only return `object` or raise an exception. Equivalently, we use `.from_` for constructors. + - `.to__or_none` for transformations that can return `object` or `None`. Equivalently, we use `.from__or_none` for constructors. + - accessor methods use nounds instead of verb + noun. Example: `Pattern.graph` instead of `Pattern.extract_graph`. + ## [0.3.5] - 2026-03-26 ### Added From f386b1aa32d12ed41a4ca2d00ea1acaa6dc47432 Mon Sep 17 00:00:00 2001 From: matulni Date: Tue, 7 Jul 2026 16:58:41 +0200 Subject: [PATCH 6/6] Fix docstring --- graphix/flow/core.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/graphix/flow/core.py b/graphix/flow/core.py index 63a0a4473..d31de9437 100644 --- a/graphix/flow/core.py +++ b/graphix/flow/core.py @@ -580,13 +580,7 @@ class PauliFlow(Generic[_AM_co]): ----- - See Definition 5 in Ref. [1] for a definition of Pauli flow. - <<<<<<< HEAD - - The flow's correction function defines a partial order (see Def. 2.8 and 2.9, Lemma 2.11 and Theorem 2.12 in Ref. [2]), therefore, only `og` and `correction_function` are necessary to initialize an `PauliFlow` instance (see :func:`PauliFlow.from_correction_matrix_or_none`). However, flow-finding algorithms generate a partial order in a layer form, which is necessary to extract the flow's XZ-corrections, so it is stored as an attribute. - - - A correct flow can only exist on an open graph with output nodes, so `layers[0]` always contains a finite set of nodes. - ======= - The flow's correction function defines a partial order (see Def. 2.8 and 2.9, Lemma 2.11 and Theorem 2.12 in Ref. [2]), therefore, only ``og`` and ``correction_function`` are necessary to initialize an ``PauliFlow`` instance (see :func:`PauliFlow.try_from_correction_matrix`). However, flow-finding algorithms generate a partial order in a layer form, which is necessary to extract the flow's XZ-corrections, so it is stored as an attribute. - >>>>>>> master References ----------