From 5f8e6531888ebe1d19d69f1a91c6c1675475759f Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Tue, 9 Dec 2025 17:05:58 +0100 Subject: [PATCH 01/18] Removed inputs from Pauli presimulation --- examples/visualization.py | 2 +- graphix/pattern.py | 25 ++++++--------------- tests/test_pattern.py | 43 ------------------------------------- tests/test_visualization.py | 2 +- 4 files changed, 9 insertions(+), 63 deletions(-) diff --git a/examples/visualization.py b/examples/visualization.py index 8721f5492..87b02500f 100644 --- a/examples/visualization.py +++ b/examples/visualization.py @@ -40,7 +40,7 @@ # %% # next, show the gflow: -pattern.perform_pauli_measurements(leave_input=True) +pattern.perform_pauli_measurements() pattern.draw_graph(flow_from_pattern=False, show_measurement_planes=True, node_distance=(1, 0.6)) diff --git a/graphix/pattern.py b/graphix/pattern.py index dbc312553..b48efbbc5 100644 --- a/graphix/pattern.py +++ b/graphix/pattern.py @@ -1377,14 +1377,11 @@ def simulate_pattern( sim.run(input_state, rng=rng) return sim.backend.state - def perform_pauli_measurements(self, leave_input: bool = False, ignore_pauli_with_deps: bool = False) -> None: + def perform_pauli_measurements(self, ignore_pauli_with_deps: bool = False) -> None: """Perform Pauli measurements in the pattern using efficient stabilizer simulator. Parameters ---------- - leave_input : bool - Optional (*False* by default). - If *True*, measurements on input nodes are preserved as-is in the pattern. ignore_pauli_with_deps : bool Optional (*False* by default). If *True*, Pauli measurements with domains depending on other measures are preserved as-is in the pattern. @@ -1395,7 +1392,7 @@ def perform_pauli_measurements(self, leave_input: bool = False, ignore_pauli_wit """ # if not ignore_pauli_with_deps: # self.move_pauli_measurements_to_the_front() - measure_pauli(self, leave_input, copy=False, ignore_pauli_with_deps=ignore_pauli_with_deps) + measure_pauli(self, copy=False, ignore_pauli_with_deps=ignore_pauli_with_deps) def draw_graph( self, @@ -1627,9 +1624,7 @@ def __str__(self) -> str: assert_never(self.reason) -def measure_pauli( - pattern: Pattern, leave_input: bool, *, copy: bool = False, ignore_pauli_with_deps: bool = False -) -> Pattern: +def measure_pauli(pattern: Pattern, *, copy: bool = False, ignore_pauli_with_deps: bool = False) -> Pattern: """Perform Pauli measurement of a pattern by fast graph state simulator. Uses the decorated-graph method implemented in graphix.graphsim to perform @@ -1641,9 +1636,6 @@ def measure_pauli( Parameters ---------- pattern : graphix.pattern.Pattern object - leave_input : bool - True: input nodes will not be removed - False: all the nodes measured in Pauli bases will be removed copy : bool True: changes will be applied to new copied object and will be returned False: changes will be applied to the supplied Pattern object @@ -1668,8 +1660,8 @@ def measure_pauli( graph = standardized_pattern.extract_graph() graph_state = GraphState(nodes=graph.nodes, edges=graph.edges, vops=standardized_pattern.c_dict) results: dict[int, Outcome] = {} - to_measure, non_pauli_meas = pauli_nodes(standardized_pattern, leave_input) - if not leave_input and len(list(set(pattern.input_nodes) & {i[0].node for i in to_measure})) > 0: + to_measure, non_pauli_meas = pauli_nodes(standardized_pattern) + if len(list(set(pattern.input_nodes) & {i[0].node for i in to_measure})) > 0: new_inputs = [] else: new_inputs = pattern.input_nodes @@ -1744,15 +1736,12 @@ def measure_pauli( return pat -def pauli_nodes( - pattern: optimization.StandardizedPattern, leave_input: bool -) -> tuple[list[tuple[command.M, PauliMeasurement]], set[int]]: +def pauli_nodes(pattern: optimization.StandardizedPattern) -> tuple[list[tuple[command.M, PauliMeasurement]], set[int]]: """Return the list of measurement commands that are in Pauli bases and that are not dependent on any non-Pauli measurements. Parameters ---------- pattern : optimization.StandardizedPattern - leave_input : bool Returns ------- @@ -1765,7 +1754,7 @@ def pauli_nodes( non_pauli_node: set[int] = set() for cmd in pattern.m_list: pm = PauliMeasurement.try_from(cmd.plane, cmd.angle) # None returned if the measurement is not in Pauli basis - if pm is not None and (cmd.node not in pattern.input_nodes or not leave_input): + if pm is not None: # Pauli measurement to be removed if pm.axis == Axis.X: if cmd.t_domain & non_pauli_node: # cmd depend on non-Pauli measurement diff --git a/tests/test_pattern.py b/tests/test_pattern.py index fc4a61b5a..6a06ce953 100644 --- a/tests/test_pattern.py +++ b/tests/test_pattern.py @@ -216,21 +216,6 @@ def test_pauli_measurement_single(self, plane: Plane, angle: float) -> None: state_ref = pattern_ref.simulate_pattern(branch_selector=ConstBranchSelector(0)) assert np.abs(np.dot(state.flatten().conjugate(), state_ref.flatten())) == pytest.approx(1) - @pytest.mark.parametrize("jumps", range(1, 11)) - def test_pauli_measurement_leave_input_random_circuit(self, fx_bg: PCG64, jumps: int) -> None: - rng = Generator(fx_bg.jumped(jumps)) - nqubits = 3 - depth = 3 - circuit = rand_circuit(nqubits, depth, rng) - pattern = circuit.transpile().pattern - pattern.standardize() - pattern.shift_signals(method="mc") - pattern.perform_pauli_measurements(leave_input=True) - pattern.minimize_space() - state = circuit.simulate_statevector().statevec - state_mbqc = pattern.simulate_pattern(rng=rng) - assert np.abs(np.dot(state_mbqc.flatten().conjugate(), state.flatten())) == pytest.approx(1) - def test_pauli_measurement(self) -> None: # test pattern is obtained from 3-qubit QFT with pauli measurement circuit = Circuit(3) @@ -259,34 +244,6 @@ def test_pauli_measurement(self) -> None: assert isolated_nodes == isolated_nodes_ref - def test_pauli_measurement_leave_input(self) -> None: - # test pattern is obtained from 3-qubit QFT with pauli measurement - circuit = Circuit(3) - for i in range(3): - circuit.h(i) - circuit.x(1) - circuit.x(2) - - # QFT - circuit.h(2) - cp(circuit, np.pi / 4, 0, 2) - cp(circuit, np.pi / 2, 1, 2) - circuit.h(1) - cp(circuit, np.pi / 2, 0, 1) - circuit.h(0) - swap(circuit, 0, 2) - - pattern = circuit.transpile().pattern - pattern.standardize() - pattern.shift_signals(method="mc") - pattern.perform_pauli_measurements(leave_input=True) - - isolated_nodes = pattern.extract_isolated_nodes() - # There is no isolated node. - isolated_nodes_ref: set[int] = set() - - assert isolated_nodes == isolated_nodes_ref - def test_get_meas_plane(self) -> None: preset_meas_plane = [ Plane.XY, diff --git a/tests/test_visualization.py b/tests/test_visualization.py index e0a9e2120..835299ae8 100644 --- a/tests/test_visualization.py +++ b/tests/test_visualization.py @@ -240,6 +240,6 @@ def test_draw_graph_reference(flow_from_pattern: bool) -> Figure: circuit.x(2) circuit.cnot(2, 1) pattern = circuit.transpile().pattern - pattern.perform_pauli_measurements(leave_input=True) + pattern.perform_pauli_measurements() pattern.draw_graph(flow_from_pattern=flow_from_pattern, node_distance=(0.7, 0.6)) return plt.gcf() From 79ef0351a6e24d609d96116c43f0e03c9d956c38 Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Tue, 9 Dec 2025 17:41:36 +0100 Subject: [PATCH 02/18] Fixed mpl baseline images for testing. --- .../test_draw_graph_reference_False.png | Bin 10964 -> 3371 bytes .../test_draw_graph_reference_True.png | Bin 9990 -> 3371 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/baseline/test_draw_graph_reference_False.png b/tests/baseline/test_draw_graph_reference_False.png index 22999333f6e5eb31d91183309bd867ba8b2b1088..78d0d86ca9a99676affa03829ec6faddf6b83f38 100644 GIT binary patch literal 3371 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 Date: Tue, 9 Dec 2025 18:02:21 +0100 Subject: [PATCH 03/18] regenerated mpi tests and changed input circuit to ensure non-trivial plot. --- .../test_draw_graph_reference_False.png | Bin 3371 -> 21955 bytes .../test_draw_graph_reference_True.png | Bin 3371 -> 25107 bytes tests/test_visualization.py | 6 ++---- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/baseline/test_draw_graph_reference_False.png b/tests/baseline/test_draw_graph_reference_False.png index 78d0d86ca9a99676affa03829ec6faddf6b83f38..b2dab1ab202b02ef5f9c7a127004b692545020cf 100644 GIT binary patch literal 21955 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 literal 3371 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%0D*1Cm&1kA)%$i^(wIQm(? zdu~gD;pExhBGb}P&8h8(Z72PmIu{eRH z77fz!H9$#hO3I4LNQbmBnq3{bx=$$wp(rwS`vRm8cSbZvEDk3wlY({L7@FMw_H!#N zyas!F`Y? z+SF&!#1Bs@LpVuygBBU3N3ozvxSUH|>aJltr<<6lFE>Bzt)3qCqQJh01-AyJpOpcU zVT(^go*(`O6t!4(hCJ6NtN)IieHIL=n-{5^>#u1vUaz#Jhc5q54|6@gbBs?`6_+{s zpXVR=Yl8twYxl6+OxRaXpJ{x6rBs@}TFVY}_}4yv@5KA`g*8DxMRd=MB4<^@x2o=q zR9*bUjauhF%bhje9eW+s1}@X+h`iF+s8C1&Q&_CEL5>|VP%Fijv#+SfFKD}U^QKT4 zJXpNT{-=y&__oqV(9w+75i|EUK5RYF0)@BixO3Z5_g=%``Zz38NJ Re&s>2)Xl^7%;MF%e+Sxm@6!MP literal 3371 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 None: def test_draw_graph_reference(flow_from_pattern: bool) -> Figure: circuit = Circuit(3) circuit.cnot(0, 1) - circuit.cnot(2, 1) - circuit.rx(0, pi / 3) - circuit.x(2) - circuit.cnot(2, 1) + circuit.ccx(0, 1, 2) pattern = circuit.transpile().pattern pattern.perform_pauli_measurements() + pattern.standardize() pattern.draw_graph(flow_from_pattern=flow_from_pattern, node_distance=(0.7, 0.6)) return plt.gcf() From 4f2243b3050967a8d10a46bb0ea7c4720b814263 Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Wed, 10 Dec 2025 10:24:20 +0100 Subject: [PATCH 04/18] Fix linter for CI --- tests/test_visualization.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_visualization.py b/tests/test_visualization.py index b2e221a06..f63fea4d3 100644 --- a/tests/test_visualization.py +++ b/tests/test_visualization.py @@ -1,6 +1,5 @@ from __future__ import annotations -from math import pi from pathlib import Path from tempfile import TemporaryDirectory from typing import TYPE_CHECKING From c6ab6a2961148b9dd84d1cfc9af98f50e80eac08 Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Thu, 11 Dec 2025 11:06:50 +0100 Subject: [PATCH 05/18] added method and test. --- graphix/pattern.py | 19 +++++++----- tests/test_optimization.py | 3 ++ tests/test_parameter.py | 1 + tests/test_pattern.py | 60 ++++++++++++++++--------------------- tests/test_tnsim.py | 3 ++ tests/test_visualization.py | 8 ++++- 6 files changed, 50 insertions(+), 44 deletions(-) diff --git a/graphix/pattern.py b/graphix/pattern.py index b48efbbc5..ad3f22a3c 100644 --- a/graphix/pattern.py +++ b/graphix/pattern.py @@ -1377,6 +1377,13 @@ def simulate_pattern( sim.run(input_state, rng=rng) return sim.backend.state + def remove_input_nodes(self) -> None: + """Remove the input nodes from the pattern.""" + for node in reversed(self.input_nodes): + self.__seq.insert(0, command.N(node=node)) + empty_nodes: list[int] = [] + self.__input_nodes = empty_nodes + def perform_pauli_measurements(self, ignore_pauli_with_deps: bool = False) -> None: """Perform Pauli measurements in the pattern using efficient stabilizer simulator. @@ -1390,8 +1397,8 @@ def perform_pauli_measurements(self, ignore_pauli_with_deps: bool = False) -> No .. seealso:: :func:`measure_pauli` """ - # if not ignore_pauli_with_deps: - # self.move_pauli_measurements_to_the_front() + if self.input_nodes: + raise ValueError("Remove inputs with `self.remove_input_nodes()` before performing Pauli presimulation.") measure_pauli(self, copy=False, ignore_pauli_with_deps=ignore_pauli_with_deps) def draw_graph( @@ -1661,10 +1668,6 @@ def measure_pauli(pattern: Pattern, *, copy: bool = False, ignore_pauli_with_dep graph_state = GraphState(nodes=graph.nodes, edges=graph.edges, vops=standardized_pattern.c_dict) results: dict[int, Outcome] = {} to_measure, non_pauli_meas = pauli_nodes(standardized_pattern) - if len(list(set(pattern.input_nodes) & {i[0].node for i in to_measure})) > 0: - new_inputs = [] - else: - new_inputs = pattern.input_nodes for cmd in to_measure: pattern_cmd = cmd[0] measurement_basis = cmd[1] @@ -1713,7 +1716,7 @@ def measure_pauli(pattern: Pattern, *, copy: bool = False, ignore_pauli_with_dep # update command sequence vops = graph_state.get_vops() new_seq: list[Command] = [] - new_seq.extend(command.N(node=index) for index in set(graph_state.nodes) - set(new_inputs)) + new_seq.extend(command.N(node=index) for index in set(graph_state.nodes)) new_seq.extend(command.E(nodes=edge) for edge in graph_state.edges) new_seq.extend( cmd.clifford(Clifford(vops[cmd.node])) for cmd in standardized_pattern.m_list if cmd.node in graph_state.nodes @@ -1728,7 +1731,7 @@ def measure_pauli(pattern: Pattern, *, copy: bool = False, ignore_pauli_with_dep pat = Pattern() if copy else pattern - pat.replace(new_seq, input_nodes=new_inputs) + pat.replace(new_seq, input_nodes=[]) pat.reorder_output_nodes(standardized_pattern.output_nodes) assert pat.n_node == len(graph_state.nodes) pat.results = results diff --git a/tests/test_optimization.py b/tests/test_optimization.py index 7693845df..c25afbca2 100644 --- a/tests/test_optimization.py +++ b/tests/test_optimization.py @@ -62,6 +62,7 @@ def test_incorporate_pauli_results(fx_bg: PCG64, jumps: int) -> None: pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() + pattern.remove_input_nodes() pattern.perform_pauli_measurements() pattern2 = incorporate_pauli_results(pattern) state = pattern.simulate_pattern(rng=rng) @@ -79,6 +80,7 @@ def test_flow_after_pauli_preprocessing(fx_bg: PCG64, jumps: int) -> None: pattern.standardize() pattern.shift_signals() # pattern.move_pauli_measurements_to_the_front() + pattern.remove_input_nodes() pattern.perform_pauli_measurements() pattern2 = incorporate_pauli_results(pattern) pattern2.standardize() @@ -95,6 +97,7 @@ def test_remove_useless_domains(fx_bg: PCG64, jumps: int) -> None: pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() + pattern.remove_input_nodes() pattern.perform_pauli_measurements() pattern2 = remove_useless_domains(pattern) state = pattern.simulate_pattern(rng=rng) diff --git a/tests/test_parameter.py b/tests/test_parameter.py index b57f9bba4..8b701f2ce 100644 --- a/tests/test_parameter.py +++ b/tests/test_parameter.py @@ -158,6 +158,7 @@ def test_random_circuit_with_parameters(fx_bg: PCG64, jumps: int, use_xreplace: pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() + pattern.remove_input_nodes() pattern.perform_pauli_measurements() pattern.minimize_space() assignment: dict[Parameter, float] = {alpha: rng.uniform(high=2), beta: rng.uniform(high=2)} diff --git a/tests/test_pattern.py b/tests/test_pattern.py index 6a06ce953..412cca1b5 100644 --- a/tests/test_pattern.py +++ b/tests/test_pattern.py @@ -92,6 +92,7 @@ def test_pauli_non_contiguous(self) -> None: M(node=0, plane=Plane.XY, angle=0.0, s_domain=set(), t_domain=set()), ] ) + pattern.remove_input_nodes() pattern.perform_pauli_measurements() @pytest.mark.parametrize("jumps", range(1, 11)) @@ -104,6 +105,7 @@ def test_minimize_space_with_gflow(self, fx_bg: PCG64, jumps: int) -> None: pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals(method="mc") + pattern.remove_input_nodes() pattern.perform_pauli_measurements() pattern.minimize_space() state = circuit.simulate_statevector().statevec @@ -181,6 +183,7 @@ def test_pauli_measurement_random_circuit(self, fx_bg: PCG64, jumps: int, backen pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals(method="mc") + pattern.remove_input_nodes() pattern.perform_pauli_measurements() pattern.minimize_space() state = circuit.simulate_statevector().statevec @@ -199,6 +202,7 @@ def test_pauli_measurement_random_circuit_all_paulis( pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals(method="mc") + pattern.remove_input_nodes() pattern.perform_pauli_measurements(ignore_pauli_with_deps=ignore_pauli_with_deps) assert ignore_pauli_with_deps or not any( PauliMeasurement.try_from(cmd.plane, cmd.angle) for cmd in pattern if cmd.kind == CommandKind.M @@ -211,6 +215,7 @@ def test_pauli_measurement_single(self, plane: Plane, angle: float) -> None: pattern.add(E(nodes=(0, 1))) pattern.add(M(node=0, plane=plane, angle=angle)) pattern_ref = pattern.copy() + pattern.remove_input_nodes() pattern.perform_pauli_measurements() state = pattern.simulate_pattern() state_ref = pattern_ref.simulate_pattern(branch_selector=ConstBranchSelector(0)) @@ -236,6 +241,7 @@ def test_pauli_measurement(self) -> None: pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals(method="mc") + pattern.remove_input_nodes() pattern.perform_pauli_measurements() isolated_nodes = pattern.extract_isolated_nodes() @@ -244,6 +250,22 @@ def test_pauli_measurement(self) -> None: assert isolated_nodes == isolated_nodes_ref + @pytest.mark.parametrize("jumps", range(1, 6)) + @pytest.mark.parametrize("ignore_pauli_with_deps", [False, True]) + def test_pauli_measured_against_nonmeasured(self, fx_bg: PCG64, jumps: int, ignore_pauli_with_deps: bool) -> None: + rng = Generator(fx_bg.jumped(jumps)) + nqubits = 2 + depth = 2 + circuit = rand_circuit(nqubits, depth, rng) + pattern = circuit.transpile().pattern + pattern.standardize() + pattern1 = copy.deepcopy(pattern) + pattern1.remove_input_nodes() + pattern1.perform_pauli_measurements(ignore_pauli_with_deps=ignore_pauli_with_deps) + state = pattern.simulate_pattern(rng=rng) + state1 = pattern1.simulate_pattern(rng=rng) + assert np.abs(np.dot(state.flatten().conjugate(), state1.flatten())) == pytest.approx(1) + def test_get_meas_plane(self) -> None: preset_meas_plane = [ Plane.XY, @@ -334,6 +356,7 @@ def test_pauli_measurement_then_standardize(self, fx_bg: PCG64, jumps: int) -> N depth = 3 circuit = rand_circuit(nqubits, depth, rng) pattern = circuit.transpile().pattern + pattern.remove_input_nodes() pattern.perform_pauli_measurements() pattern.standardize() pattern.minimize_space() @@ -546,41 +569,6 @@ def test_compose_3(self) -> None: assert p_1 == pc_1 assert p_2 == pc_2 - # Pattern composition with Pauli preprocessing - def test_compose_4(self, fx_rng: Generator) -> None: - alpha = fx_rng.random() - i1 = [0] - o1 = [2] - cmds1: list[Command] = [N(1), N(2), E((0, 1)), E((1, 2)), M(0, angle=-alpha), M(1), X(2, {1}), Z(2, {0})] - p1 = Pattern(cmds=cmds1, input_nodes=i1, output_nodes=o1) - p2 = Pattern(cmds=cmds1, input_nodes=i1, output_nodes=o1) - - p1.perform_pauli_measurements() - p2.perform_pauli_measurements() - - mapping = {0: 2, 1: 3, 2: 4} - pc, mapping_complete = p1.compose(p2, mapping=mapping) - - i = [0] - o = [4] - cmds: list[Command] = [ - N(2), - E((0, 2)), - M(0, plane=Plane.YZ, angle=alpha), - Z(2, {0}), - X(2, {1}), - N(4), - E((2, 4)), - M(2, plane=Plane.YZ, angle=alpha), - Z(4, {2}), - X(4, {3}), - ] - p = Pattern(cmds=cmds, input_nodes=i, output_nodes=o) - p.results = {1: 0, 3: 0} - - assert p == pc - assert mapping_complete == mapping - # Equivalence between pattern and circuit composition def test_compose_5(self, fx_rng: Generator) -> None: circuit_1 = Circuit(1) @@ -631,6 +619,7 @@ def test_compose_7(self, fx_rng: Generator) -> None: circuit_1.h(0) circuit_1.rz(0, alpha) p1 = circuit_1.transpile().pattern + p1.remove_input_nodes() p1.perform_pauli_measurements() circuit_2 = Circuit(1) @@ -902,6 +891,7 @@ def test_pauli_measurement_end_with_measure(self) -> None: p = Pattern(input_nodes=[0]) p.add(N(node=1)) p.add(M(node=1, plane=Plane.XY)) + p.remove_input_nodes() p.perform_pauli_measurements() @pytest.mark.parametrize("backend", ["statevector", "densitymatrix"]) diff --git a/tests/test_tnsim.py b/tests/test_tnsim.py index dd5ac3906..adb778d66 100644 --- a/tests/test_tnsim.py +++ b/tests/test_tnsim.py @@ -333,6 +333,7 @@ def test_with_graphtrans(self, fx_bg: PCG64, jumps: int, fx_rng: Generator) -> N pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() + pattern.remove_input_nodes() pattern.perform_pauli_measurements() state = circuit.simulate_statevector().statevec tn_mbqc = pattern.simulate_pattern(backend="tensornetwork", rng=fx_rng) @@ -351,6 +352,7 @@ def test_with_graphtrans_sequential(self, fx_bg: PCG64, jumps: int, fx_rng: Gene pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() + pattern.remove_input_nodes() pattern.perform_pauli_measurements() state = circuit.simulate_statevector().statevec tn_mbqc = pattern.simulate_pattern(backend="tensornetwork", graph_prep="sequential", rng=fx_rng) @@ -400,6 +402,7 @@ def test_evolve(self, fx_bg: PCG64, jumps: int, fx_rng: Generator) -> None: pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() + pattern.remove_input_nodes() pattern.perform_pauli_measurements() state = circuit.simulate_statevector().statevec tn_mbqc = pattern.simulate_pattern(backend="tensornetwork", rng=fx_rng) diff --git a/tests/test_visualization.py b/tests/test_visualization.py index f63fea4d3..2a83e2b1c 100644 --- a/tests/test_visualization.py +++ b/tests/test_visualization.py @@ -1,5 +1,6 @@ from __future__ import annotations +from math import pi from pathlib import Path from tempfile import TemporaryDirectory from typing import TYPE_CHECKING @@ -132,6 +133,7 @@ def example_hadamard() -> Pattern: def example_local_clifford() -> Pattern: pattern = example_hadamard() + pattern.remove_input_nodes() pattern.perform_pauli_measurements() return pattern @@ -234,8 +236,12 @@ def test_empty_pattern() -> None: def test_draw_graph_reference(flow_from_pattern: bool) -> Figure: circuit = Circuit(3) circuit.cnot(0, 1) - circuit.ccx(0, 1, 2) + circuit.cnot(2, 1) + circuit.rx(0, pi / 3) + circuit.x(2) + circuit.cnot(2, 1) pattern = circuit.transpile().pattern + pattern.remove_input_nodes() pattern.perform_pauli_measurements() pattern.standardize() pattern.draw_graph(flow_from_pattern=flow_from_pattern, node_distance=(0.7, 0.6)) From 698e55c526416310ce27b62c656ef1faa09742d0 Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Thu, 11 Dec 2025 11:57:18 +0100 Subject: [PATCH 06/18] fixed testing modules --- .../test_draw_graph_reference_False.png | Bin 21955 -> 3371 bytes .../test_draw_graph_reference_True.png | Bin 25107 -> 20239 bytes tests/test_visualization.py | 11 ++++++----- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/baseline/test_draw_graph_reference_False.png b/tests/baseline/test_draw_graph_reference_False.png index b2dab1ab202b02ef5f9c7a127004b692545020cf..78d0d86ca9a99676affa03829ec6faddf6b83f38 100644 GIT binary patch literal 3371 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 diff --git a/tests/baseline/test_draw_graph_reference_True.png b/tests/baseline/test_draw_graph_reference_True.png index 2a1194f9387e161b89067f80ea6362eac1ad0f14..aeb2d3ca63aadf06366ceb41e51c2e4350ee0ad1 100644 GIT binary patch literal 20239 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

GXp-&)NCwwVQ?~yGuvX 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 literal 25107 zcmc$`c|6zM`!)JOA(bdfGN%%fLOPr zRK_Brvv&9Q{yop{JkN7p=lpeE_p8Zmc)$0)_I0hbu66BTt)oZQGO#mHD3rCTDvH_^ z3Y8Xq3DeW!BhmEZmiS}Uc@?7z6v{et@{1}{?!Fy`V&0^xcu3dn?zgw@#U>4C9P=`b+%X;WzMF2G}l)QpC8Ps6=2SG zpoqFnbj{Ws|G$1wq=yb&nDWu1jC_22X6EKpvWY`IPHX{ohpDenHgDd1{^G@eq9Uir z)5n9U*_oNIs77v=a9tB%9`%IV=l7N14LkPP(MAg-ZQQt#GWcCoNJz*(|ML3=e|ma) zt^S4U*RO}g$1~ugBO@c?V-Kv47o9Vj7W5b>)>iCme)DDGi(Y`wM7;P%r=*dIg!uUD z`0lD>hjp}rsZTh)auRnPmyZC#lx@~g8Br-sTk$vp4P~$h5pG?93%aSjBS)FZN9BrY0}fvH93_8 zv!e{6i>b4t@w?y#eR%68LoBTUYF@y7a3vs8(S>fd3mYo>+2tS{1DXE*5+9s%%A8~VQhHvq~#-Ds@Of2 zw0PM&qFEM&2tkb+lUmQsH#Y57Ha6za(9n1{GD3gp(xp<3g6}Y z*Zw^#R<5k?=wMu4UOw;ad}eIk*^jw zB{URz|HH-=&MWaaY?9}BJUu<%efdIn?%cQL5K~8T9=683EHJfv9K@`mrq*Z+%e&e!~I2;lT%am zFOn6^9UVi9EL%R54|zwH9{ARoN_X|@)v(yupik{F`yaow7tK|$4luV`&&uk5$d9(Z zt80D1shWn#_l@RucENY=a$NKGPsW4u^74+nsn~n{#*NN$N7LD_uQt%oF{X;ah6MDK;2SN;6nW2M)wUHf}^abl!5C&ucY({OWrPY+Ao@`6ylX)L{Dl;#4q3SIYn$#$is({{QUeBH|cv_ zQ^PGnODk5a5Pwk~$w@Mz%DK7kZ$cE#+)&ROe^p|$&c)SrkJ%$%weH^D z-uELTK_er!{?-mEe`AXkha@VB1(9#C_^UB+U6J>aM}L36+3ZNCM83_Jj)aHaOHvz! z4Jlq;OM;0fPoCVmZQDxfEAr;AwlMsC&QG1GN0@>YZ4npI&z+r}MK66f9vK_6=+4lT zxR-qIzy;cqQ!pD%a>D=k|>KmzbUle-j!n_yL~&YkbbUjWaI|@yc2rE7sVwcsN}aTY`3;aV$j#s zy|!h)LsDjDW>%_-QuK7Tw!VDXv|40z%Pc>mdVLWF4gL7M?E`Bxq zR{ZDp%sRZ3ufyBQ`k^82u`!9nDm|8t>xZ+S8dB+r78&0B@WCRe=|e^cAM3xVO-Qrv z;QnK5{zaW@lHyxeCnsW3c@5#uD<{Xj9$MkS}; zKfAL(R?1aS#I)Afj%)AU5<&m+4~L=!U230_;^`_VB-Hq0{AFjIkI%KhKn4Ewyt{U- zrJNt_+GbqolGP@bs(h@88!>?qUfg zb@ohDWTao+pXDvg%*+ubPreP54^Mw;?=P`Ame5{q*G*gQ@HWEZa?Ea1<%<{h)&8Dt zUwG^wPGU!A74!R!1Zhd94=Zq17*Pz?a7wY{=jV$`Ncblta3VbIze*aN;tQv*x-=1x zkYEydX+7UAQqIH4LBi<>xOsC;Y+RgqU#=0GjNAG2#c*U!k_8VdDC}{cw(>tG&CkE0 zwY4?q`gQGosV(e(Lr%tLnIrE+S;NiEd(A8@w~n6qZgTYKQKX(jW@bDnfc{(uJ$f!z z;lU5%19sag zT;n>vvAU+Fv+9!L{K7(8&Ap6AudfKkW@Ky^78ce`VHQm~;jXt+2)_#ztLey4$)7yA z1*bY7I+`^@^G?9h;+z3{gCRTKesON1rPQw5?A*CqSm%?UXm69MuuOAL#{DX%^hL9! zg&zzj?<40f{1^}H>e428?9{1K56{1+zINC=uYDB?#KZICL&&%^r=P|9KY5}Oy+w=B z3=uU`&&P)>@(%f2MMcH8wpP~I*jQe%cW@v|3H2)DSfYrXuH3oT;%{obw#ZG zBOc`Ew*dtmoeik15l-(dEiA$k64pvfS4jmCDpE zA#7Mk0jhDTnP}OFdiGjIr zTj}aoRaKRC3&_-iIF=Mx;b@GjW9m{+juc zdf`ilsF;}cNM(hw$mfoP8@vhuJhHM}d-m+CD39Ks7rp%RY;1hIMQg;i$;nCim-!}CNzLD^t*y;F?jCs3)~4;oW&MaD z^J=gn9}6pM>zlOHECE4B6_udFLuy$aKCVNx<4E`s9S2xcZ|9Ep(wRxYkXC4!S2hYBF$c#kk=kLE#c6r`X@yL->y&Qdp z=V=W62emSkv1CU=IO>+pe z1IULf5d|3`%ZHvkf8G+e*XI1ftlh`YpHnW+eK)N2Jg|Vcv5MO$VoVR1AZ${}^61a4 zlwYPz3<5g-hQi9&dn9T9FV7QGv8MB1OP;@|^}2k%!r02%`u&Hd8?lm3e9Lonf6TG4 zc!I#nN~sUS!$wqj_i=JQJi0>j(D@TXPfw4i+oajUii-M%h7{Ec2~w^M_m3ttnEEXJ zs6VI~vq>mPW1GLf|K#lKW8fK*Cvs1eF(gP|$`^kgFJb?p;)0FvscJSVDk>xu(Ff-S z0+0|82DQNvK9Y#Ou!xAOJ9RS5MqVVp=3Q$a*EU0u0)vUNwI8RAxx^N1ayN+l{P z%IxEla00usbnc5fe|&Q0OG!~teZEOmq2rJ&(hqH-*U!zLpT_wuFD;f(XH$?}`YSHn zKm6K3P)%N*g8YwjuqVB(w+Cd0|Msqv0jO5*un;4Fbhlf@nkD%TDK1_rMB992 z6xHNK{>OPI%RivfW|z6?<4a;?y`-wEtIvOY!klYdvHsD(;{E34=IDEU2GtEO>wNyK z`^+lq9u*z^9_c;^Osy?i$lT2AMNfEIvCF8ot-by5;zV7HyODx|f}4kj{=kbcWrczEN10|z9XKhlF>X&D;Q17fgj+LWV6RRe5} zGQiBiA#C8Ns;sOWD{6lA&A{C3?6v#%ZPM)kMs@GWHKb9mU%#H7vW=f#+%-xHsZvl_ zm_J#ozvpfW$jThF**Y>rCW6uhd?=$YpsES!P&Q!tiU?*o}Mzx%ZrX#9S*v7LkSw*f>v-_D(@5K4jZ?3ce}NFi}ke6bn4U>6;~0GszXe^g0J zPv;dBRLkF&h-AEaqp)vk>QUKI_E$B4QulLHd9KtV}(KS3iI zDl;r56NCTc%nT@9TVB=#uZlw4cX!WB;QdvUm9(^dlF!rCW9TU>SFF$i>7wERv|<;x zWui(tc=@Mpd|gtKymP1v4o9hF3uFAgbL?4JS)ECT{8WTDZeRv7yF*V=Qc{xmXrLR5 zJ+1`*(P^<2RaI354N}I@JUb@POSl7rz*YhpH-xsN>fAr7w^&hAa}d!HhM>Vqv~_fB z=Sz9`@Zp)&wsv+qm5v-SFd~VEo`M|-2^y5mJ%9c@&*jUPo4k2fwzsvVBqi~%?|0zP zyt7Ypn(mzf-^LrG2+Zvu{=jkeW0HS=rpWuv!MC>TCv1#O;tWge($4|4Z{Ls}!AaH% zo7Qq*`{Lu{ZBbA2#)qfedM}C}pAs|!u_Js!h<$1YKfl1_mfl1|;bwfF!ZW>sz|zBG zy*X@qPp{c`wm+#j=1bWDw@IFSZ#aw{MFTC`5<&17QK{C4*%A z-Em1lKa&O_LnEWjVq#*tpCi&UsxvY&=HCZ$PfpbR`2dN9=|&U7fxX6c-UlH^fHE4S zf4iTTx9j_x3YshOJUV>=Nf{Xdj~>YjpD0zy|F9~vuju^TcN2U*Cytvun9LoikTx@$ zB#ne?hj|tG!mWO9ap#nAV-hmRe~cpX?%Ow$PZzKc5CW`DpH5Ma5s|#~odd)rIWKQ5 zga9?pXo_!0$Wgz2lllgKCVqQ({#lwseyIoDxFdC4?biec@69)Y9Z6%|sa+KpzKB}4 zu{^A-w7^g9?a-OZIEo;pyqg{ERylSorc|@{#S8fx8+T2%aQh@xS95IGarE&fQ_thW z`)>TBMcn;1R3ka{bSr@C%;e3CQEuPh?8}dlauSSzkL&xZS3*z z6eX!yeZ2B$!uuXhq8j?#lUa0czn8E4PGjCa0$Qp?pg1?sj}z=?^{# zT$lpn#QkQ&JlNSVa31}tRfixr=(HR^fYj^yVdmFJU*5^+eenYi&NUMHH9M+9AGODgDHOR0y{SvPqoVx)~)9~gE2Vm?KAmYQ*`yLPgg&loNC}^3zLAt$P z4gCLu7eRzF8~y99B^Ji5w+{$r#KE!Q9BML`y|H1#H9H$ z_#eJ2>&5Ozw@%;iG{RCNL5fu&kTJSy(|zmq+q}DXuRE3~+cew~I`y%2gN*lYHlS$a(#g6%O9k~0NQyA^{*~SlBcjVHl*EAj+3s$U%aHa= z+Ol~KB(wKO8EgkV#3?*mx2AUd&{;eDq;7(wk?D`C4j(>DU;#Hb_Zlvlhg}1*#ote! zJ9iG}MmgKvUfXHl(UT|iZ>ug9%o?exu4-#N&k_a%Jm5i@J+mFnv16;j(A=<$T} zOLId-7rq=tXs19fI23Y{D|E+RB;%S{A1*`_|rc=o*kk} zb{OpsRtT%KYuG5zEq2%aUQYxK1PS=uT0E9Uym(fd9~WN(7$2cY>E7vL&`7ZTG1VIf zvh{>ek68Qq`U)RBP_DZ8js0Mq3jbBq6G;w63rkDZ1;_0R453&rDpZim6s;CqNAd;6ctzenxBU_bVqIWIzNgE z$brkRY3tV&7k2DAQ5w{IbF+ejgM+wrTa@y|w!`i=5kdiE7kq8J}zpu+a;+j}6vEkGz61)&xk*s6`y z-nL_hj+K>Fs>57wjzLeg`-ONhYqeAdgK)s0%;sBC8LHTrb$9liyNXv%Oynlw^rlUl z-aK?lJvCkf2>>|cMUKJlT%!_Ni=u~ZGLD1QS3*JaL_0zYPStte zLeT~&mlt4B^Lk#EWq0OG3XsG6$H-%65<-K{zk5WLmX@Yl$QW5 z7W_f<1*Ub)?J>K<@7%fZ=8ZAZ)CaJi7rnhofDzD%USOYo1MWh#Z9p=la&&Zz3-E%j zMZadv<6`S}9u@|tr3Kg4!g7d>IGbnAoYB(Jp$ToFiD?J1+x4Q-)gH205}=~n{3I{Y zmKr8R?*I6)qKwx9%jeIZ@rKuO za_sZgb_2~GO^{j%?v3>Us#BW2SA(;fX6RgDd|r}+i5VH07H1DhoMq!ipc)GHs~&+) zR&~?vQ@V@%#s&tP0s;bT`*JsfD&D}iOGfG#>B-t47H~y51*-PY-?m$ZG58Y^xD?Tj#@#(iJ9tk#6wbr?NA>4Ch{#UdhVZ z38>EBl!814nVhC z6Vd*r+I>TCaInhJqs?-Ee*5|SSu$|mciPO1g7ny$bAwaL#TV)U5v=!}d)+gyu8Vw& zAi!CA)Y3wS<<6RQN985-)z_EOS_%k$wPl!h&mQKviJ=yxJzmm3xlm#3GBL;*5)$&C z{>gDjnRxfD`}c2x7_8Ip9em?PBiaAScZVPeAaAY$6&gWv;8cx=KLm#Nz*;Km>W#>) zWC>;5CMk+5Anb(7F3>YFGM;yFd5j9T2KWR?py#!B3Dgt0-;*?iN$D1#TXE&l0}k>14Gd!r_Z2`#t4wl>{${hKqdva+%g1rtU4PD8WkCI){j zkuumf2!23+zhT<_`!sF?>3443T1&D&asnVjV_O>oz;o1?_bH`QfR-Suq_v}>s(J`V zF6Xy}tj8>UqR;XHhr#M#aIt!DAkg#%kp9jTrPZyT|925reK$_@MCQ1*3Az-LILbA zwqEU9KGE$n-MRy<$>uO#g={Z6Qy!*AFJ3SqLLUQ*P^!<#4;EP-A#etZ&L(Om?{{;x zslgP>ji$*TKbmlo`4dWX@AHNX{Goc1Ch}!Ue(84v4uABW=Z(>4w7DNmLXAQeJ zJz}K^G0};#67QKgk3zP2^-XM8Ec2oi?CHnhl!k^~Zm!>}TFJErLmnnh$ z+TT6K-15ywySwyW#4WM1#q9Pw`yEHH#j~HEZHc?x-rAZhw>^YrrCDJ2z;dczX$iZ<|U(NPaC;iNr}W~t6V&oP z%fEI&t=K^EnEIe}YXHs0WQcMRckXQQnt8V01s|+9ayjbKkUki7+UC8d!*1W+h_V%fEm}Zo?z$mg3#o-QBHfYMQ8_skw@RmPaTwq4dEaq{}p5 z=NBON^pw%Dv91_XZ=+B1QQIaUFW_q;vDTxYJkdW#kq&KtO@Au(QstAn@OceY5}Jrw zr%rMCU%U3=+NuquUuIXDG89szx+Bic zVt^*`{HFM7(3@k|0s=lj#(44K#Ur%#t|Gc_vP-Ca|LG1Efh|?Aut@n>?1{5mcIp9I z)u5b79j+H7If&?npV)%j_kMg_C{E*>>{ zg|6fJ8nL2O5crzEzAD*c--=?yqH&hiQi=XRA|I;kUGk&*wdEA!j#qrihpfQRCzpR(b> zwo_L4f`9+``YIBYeOtvf!JRvKxk8WNxSlJ_?OocoDOk}3K!9D&TZRNI!1e)Y>F#?6 zFQXA_;OsaKEIT_lpG+;W`$fhakP1kYM*LUZT{xeIrqyevEYb;UIu+sEm1inwU5S*cf&9uCe7H8aPC! z;xDLjXWGQw29c2j+ z*Y%>t)JW%6V7c3Au{}R-TzKEGva7c@or;E5H~tGsaH-R<@&Yh5Y3cr0mhH;iX^)x+ zX->U3wC>mEEw9m1CQEX0Vo(zW-0I93H4er=Xp>o)nL(&{N`#FS-vY;Fpr=nl;Zk$R z`ueuYAp0aAD;SR*l3Q9%jye|?7t8wfd^;Z_>xrZ7Lef)y{!x;iP+wnwUXQ}wQ0nkj zY5@W{&OR0sQtt?YcsqrKlXF+^&ytc7RjfoA&6>9_wJ1h;W&`;!l8pm+U#KY zUQn;1MOLb-sR_WT)V0AoSxW^{7(WF>mBjtGp)~_-O|rBJcLEEpLOUTd|ru; z0ku4F1-P92U_!#i>4PFE9UFI@c!2_~3DTPcJhy>T3I3Nb0LJfQH(2cs9CyT6BnGM| zK^NIG>nxN8Vd&7tH(o_dT*skhGc!Yx3!`WuWn0CWR9y^*zrsn9V*xbH4qeyBpPXB} zx+1n8yJ!6E;$w?FIOO1wZ)_oquJEn>vwS&j+05V)2VP#kEkpR91^kLf+D35!JkPpa z0~f_DNEXf;k2@8NBJ85#}{9?Zn!_xv7aX4r|^6ZH7kcj9HqDxp-LvP{mJ{Lr-8`QpXx;On)uwb7PF{P+r!@v_}i&$nzv|d(U4{zQwKt z&{D8h2U<#8Y^)aEa0D+NUA*t{=F48_Pbb0%HvZdhvKF+u3qhIlRmbROo}q9CN=PT_ zcW~qTFqn#VovM!I!WsYe8=~u0iVL&?kA>NtXz*>I#G~oCG}*Wgb>;ZElbu+#6R7H& z+1b^iT@ivSe4)o5###K^vkPKrs!&S-)gzhIP#-JdqnhNMd2BMKUurV33Q#W#pAZt9JNEgtZ zE!3h?IBlf7e@mVls!dq4X3dkQPj8|Y<{4KMH|K(3r^7@Oj#osbt8C6ik3<{}3bk?O zU`6u7+e#A$=N5k0FZ}wFNDd1a=n<$v`42@iwxd{uBBaXwjN1jKwm3- zp%t8YTP|U~QdgN(`Yb?54}{gM7$HGHKIneb^5CQ_TwF&FCx@ZAh-R`v?_i_Q?%nq% zC(k1(^H4qx4{xndBp<;;9)~SY1rWh|`Ik-Hy^h-5`}Z46-_Y0Td);$8dgM<)L}oAw zJNUJjs3;F7CnwZ_lc_^5qgoKN^zyeHDK!NdUP2K0;F9ajzn|}Q^6kEhU;7Uo^qA$L zpuMhC_j`IZ(o8&*COhCcakvD#Uc3mq8%{7fIkFU$lPB*mtX^$@kP# zR5?QSu4(Sl!XPH#D|TIJsxGK+t#kQL>@6)7pk0A%D}uphPwm*Vha9NZ=4R#m>RjKp zOpCy^+_KeeI_>cvKYco0=e-p4{ELpfyzkQ(4=f5)b@Fd~c%;F9HAx_3gCU?6_ch?S z9!1jA-ejO;lQ-_I2k?l^HF5sjm2V;)&CYGp7_c_2pkQ}z^{pC5@JMKb zBEXsQ0-)ziLY>9Ck}*cb-Mb6UI6g7>tt<1(+kj}lXU^!`#R?01rbJM4s6=j5Tn7>;`ugI9*!Li?BL_*kLTorv&m?YcEUVo`>8G31-}Gm z7Ut)f0LTC$b?~hKTT*`BhA&_hX9faosBdK}p)Yr2?P zF!eg;Movym(5(y*(LTewcuGb2X9^R|j~~B4PB}Gd16?L9FHaL%p2TT4pAi#2K1gQH z7cQj2q}}OQx1B)8WtRA?UTsfSSUrooz5ytWx5H=j}IBkXvzB~p-#{3_#nkp(VVC))$qZTxx z8+J@#yRgLDxAljAeri48!~t3N`puhZK(1XN2ZGN|7CGY6Tl@QCw`;`8|5+URGXj%G z2I|R%#OL?79tlyoK8#dNHXRfdIs zS)Ze{u=}*u)YM=n4bFtRe}8j;S{^{+4!=D}3ei|1Xg?CvSvD7|JGwUNQpsS&g}~CX zvew?-ve(=7|Fck?ommnJt}Ta5+)eAeO-8-u=6ujs)*0U;4Ksw8xOf_@$E~HP^bz^_ zBBbfU@a)7}r^#iS?b6cH*qvk?AAPjj9p0v(Km+k_g=wR;6f1cb-7W$gBJ89ue&wN* zqSK)^r+Dt{*(V(x`F*)Z& z3%Kfeh_f!ET`Q5jVkPW1Mnpt}MMiGj%%A_j@+V|xHYpbYkOh&r(-sz2QK3Ho5(&T# zY1lUgNnd4I8fH6aIuXf6gG+1vXAg9Q9lXSTk&!y-_nKjIq+7juwW6Y;Zkx2%g3IHk zrd}aOOYI731`6qz1B<~q)d>fxDFYC4qP;-kXn;Me&mFnt9}mJi z4{sVX$Sz_wUg#U1s&=~qnymZl>EIw2)Nj4w&tJZ%2XxQBSi>#LfoDW6z6!4ZY_V5> zHZ6hZ#U=(AnwpwkynGpmjtw9yyv%O|SE}_5^N5M9$Ho%VF1&9+0OgIXt*eN2Q9s|< z$fgf!=b1li%gV}*!`}yC;pM|zzEGog-<<3%EVL~*xhYF7x5{uvr z_%kPxM0AE8%L%1V=WGQBxJOXj!*AaX!0CM-D!2Ry=8fZURoHcBw7Mv>LBPbf(YPyF z)ZtF7+-M}n#>Orv(oxV9XhgpzBNx^Qiq^TrWZ)OKA7jy09DGWr^-d#6kx7S+M7dB1 zqGsq$3kV2={gT8?N+XVY3dBx$93&x*c`yAi;NP)h81WOaY;FCv92(nWJhAAR9swdM zLENkwZ+9^XdF?yEe4itzb?!8uOod1K38H$e*`|Xmhz;v zmC9Gm^0c09XUfXynHj9u_RSx)(4#``>d_)Fg(R& za+pgv?Buiu#q|7#$E!Fb&r{F?iWRq8j|Na7<{+|)JL2}zVF8=aktW7M?7n4N)XoUG zOxLAF!7)jQc<2pWDJyd=OO*4RXCkqTh5~VXy*t;^N69uGqX}bqcHLbN%>|h2m9~YP zj6O)?Wr`LT+Rl|1zX|>jLlGhVo05_X>5J-RMa9KewFDGTpFW*_rQ3>}^SBb~Fym&- z%jo~g{lMcnr(IB)i+9CrOypA?W8<61A-tHRdHwpesJM8pYf?G33Yf7szWeFZ+`gg~ zQ#4@UbzqnKGUbLAN{;K637=2~(m=Bx>a&7UZCG}O73P;I2-YeY244p*O(@kJL$8nc zMj_uv4^VKTV8~qID^=2Q@8A~vaN<=)`KO~0GYGKNbu95A1B3FuPpqySx*X*&nnNW* z#(kO~y$J@7{!+Whk88?4th?NFeqbWIe|a%A8cd~ju+H+IZtbf>w-%Q{XvO#c8 zukkw%3Q2z&ZV1d6H9?e5%FhoW84=J-+u8(FY~$`zH(?ii1S1VV`lIWtLX>JwkBWWQ zr5Mi$x(c{Jw9; zJneQqDk}6K?Av0tbY)6*=X@7CabntPr7!fOB($M1fl@I23YZs7^#Zl-?Dk5}U(q$l7$_=Olv)ZA)CUAdKl+^>>GL+hfPdsn)eE$CEfq(mEJEQ zlf>MK>B-4QP#@5o-B$7RpwF_*_raPQX#e!Z{a8}BM7e|MQXY-Z>_8D2u7bAR!MS(@ zEB1A@4ONT!kltUrqto!|bvceXctSz(AT009iYqp4ic#wxmtq`zb{RH<6?Al#qh0B- zaz3?bl6qe+xQF<`E<*;eFu$Osle2U@ASJ}_`tUxCid?*S5k3OLYPUV4V}15jae5o~ zBm**h6P$HqK%jE9v%sc`bM6jE2Ex&Nd>dfF6WkpZSUIQWQ~{! zN*FrCfDXNGubMtN!=$v;pbsnk9<32KC=Qf&=<&HhC8ChtEhvFuk;O{9@--lM%jMs{ zP^=%rfA=Ew$T|(!5i@eTKI?5QAt(7cY$P;;c!bst6Z-Y%9Pr^efwPCX-^+# zs^D!cA@6Mq$_um6j;_AG?H~YSeR*3zmTKGXNEUO^K~Vy+;UDPk=}AKaTpP6$I_M3; z&c~hH{w#a31IcaPvLzCpCf9GTqtSLsXKy(b-Xh#N;Dzdc7%j{mAj%Vq(Qb2V(^HRx zMqtaPet81n5cF6HX`{^KEwrbec!Rh>#SctNe_rm7>POBox3|B6BR2~+*8B^~H?hQE zD5BlPROiI|_wUi%DcBn*&Mq_#ebE2w*Rz>9IgP+N2*iNAyu38c;Xswmp?)|&q@M|3 z0MsWea;iEAx~1Bu5~qFh~HbyuHX^W-5`Ey@UnF$g65 zF{W2>swfxwM1wCC4bR3K+t$X8=M)v!di-EOYww}k6d##%%E`Hb=5c$kzP6sgfYV>| zB!swn(8uE_L3oR;2M%n;lq0c7L$%XpQs6dt`I>h&@Evwc)0-m*0y*Z7AVb`x{1xwDTTTW4 zCQ1WH*zF+Q@z`w(b=80=c?NPx6MqT^W7&gprE^mpo6uwSKWjSZ&6D2AWaKWx`g$U0$`N7d0;#4;>B-5!(aP-^Y(1b+hD- z=^Z!?2aP|3By_`xxB#HSsx4#WleAW)kMpO)v5!9v@(6j%U377Ya=*LTPF%5%h}ALe z=|CrK5*)jjI}xoTFm_<}Mj)$&>kI?xeh>V6s`Y2#mMnwW-?zMxYX4`a^W%NH@VOi9 z?CpuRrx`1H1<}8KwCX{1^<6*)HKK$wZ&fBL+qU-XhCPdLBA!4By2UPWeR+O(ooeXp zk6kIO2fNe<{A7ia#3F10^=|s7rl%{xtrcPK83%y{Ky>2)DJeCRAT+EY)th7t4sPKa zM=m9l4^({wIYsOfO!6*BiC_g>M7e|}Y6mLl0=B0HyQ%$6C#6i092lqnEKT^7+C1li zXt={SXU=7S;C6x17)^5L;r6Dfjyj@FX7EGU`+sItyCp{TREW z`l#X6(I+29@HTRc%0jIhxe`Pz4~8 z@1T4f8BrlqqwyQ9PoIu}v_;xZZ{EJ$Rx|uEV`qp_O8ne87!G!sR7R0TJKi}@OzT6H zJ2<2*S{|4~F#M8v7h*^F&9q5&xE)SztoGVqh5w6LrAO$_zWe^25uz@HlE%Ki zjr#ifFS@#rfL5VLLVQI*LG%E9^~e!8wGuxsXEGQGRUTSIi(?No1+dp6U03`|Z{E7~ z{=)}q`1H`G=7p0H{4gpDi?WfO{W_QkPB0ZLdGjNwp`!3~wVqVE{-zuRiwrk|k;y9p z23`vdJyv$i4~GguoIjZac=l{9ijk}LCTpqF$L>mA0fkD1lLLkmC7=`gXI039NI%>l zF7WwW179VBqDaS47y?HX#oh)52NT~HRN1koTQ+XgPanGW`?sgRr<}j-7=y@KrKP6e z(9j(y6=3STA~7ekI(I{Q+OlN}QU2%W-73nHGkL{W;>yy}u8~Fr#TJ||TBLr<8I@3? z=40n}Zr!S2p6Xqjs3*KpUHRec^mGcE@(Z9KtK{=6SGHC_w-YFTU!BW+JUIaevR)5+9VoF*%ooTxo@b(Qm5Ht7O9A4}we0(K<`t-15Ebgx63jzkj+kbA zAf7!?4#O###)X}q9vBa9-G=F}q~W}-ZVxYd{|2>!py^+pWJVlFBuYa_KtLW1di108#w$^K0s%J& zL?)~gsfrP{GBQU+rurb4fCRsL_bw^yD;{AU?WiL7Eucz}G(Z1yz>#^=rX8DQ$#5Z? zm=%3)UY@zNwVut9=C<#EaIj?mpj$fOs9<^dyBri(D!d!fW*`K6!?x&A*H-~M0Ed(d zJy=+tt1Nn+YAegO17f>PzM}$k5*g*MfR_@kQq&k?$4Y3LK^A@TiJ32bZFa+fPE3jP>6h?+{VUeQZYar+!Gk55lS038kyQLfZO&o$y2rR+l z)=nQ?^jZ0j2OCz-Zys-l?AM@c3*y}jFH9lEX5h`Sgc)C=V>c81B+5omlLdrx26!x@ zWnXr8E1-vnCIYMT={`*Rv_^hIKWhaotp(1~c_$}K;F{$v<>`|}MT_+j5GwrkT%mMm zO=DK(J@jM@kTpWGPl5>*&I=1nQRqp{yP_s8N(*!s$t#j^J7kP-+!g@m2${Q#V2qF6 znWJX+A<$6!c*cDP?s)vn_=A`CQ6(Y2>v~u$g92lxSz!@Sn9exXbPT7*4oNxU(ikS@ zS5a_(gHiG?XcQkg?HnB^qy`lGG9EsR#ei}a_GE=GMpbr-iiSarV2w4bKu}9z#Nk=o z-ayjgMOnb8=e7%5y{uC-e)wW!8vS1kATa@ApoVloQ$tWG5!VRT2#pXWxV%%qQ~>Sx zz75qSV(RoL_$}!|BX{aT?{N21K8`hPefu^6F7R`M)kfdikMoD||Npu9N3?WKtMn;#w6RTX%Tk}v{~+dlep9Yn2)%F6W=tX;$#ZZGh9be7TZ z5hH&%ITiJ64&jzNiubd!w^u)QPbS=>0@`{v+ELp$gKu(5--Zoh@{n>EOw6{Qr^h| zI{R#uxG^e>xHH#$dr@w2z66ODJudo#ZjIikW8+M-%b2|Y%*@ObpxYq<77H>ttgfzrRy+`k)!=sAxq=yETpkl- z-WWhEMq~y!-FYSRh*9$ROwrNSB`j9`v}AR#)k_YFvkSQ z+ysV~l$Uo4Lcn$(P2Jv*lfZNX78DA)zSyJ{Xhvr*y#Wq@VZH?_TR42UnD3Rk@da)S z%(!QN3)!`` zJwkqIP-dW1lDbhJN4DaZ{sXckI{*qE950;*;`-bJTO z?nYP-d{qyjZjaBhC%KmaUYo+=Vrt6cmKI23TOicZ#kA+S9wfeFG$4`k1oVnNIakbo zubKct4?*Ay>P@{VcjSh<_$EHu052l2{&NmillAyy?YDEe zA-LZIbBmCpI@8rR5zPhGdNN#s`#aEZL}=5%y@4BWFrQc5_d~*QkOQ*bZJ`@{nO#Lm zxE^a2);0mR5Kxl(cX*O!Q~&et|I&xhNK44X(8NSMaS{O`uhsK!3ksH!5?u4}w5pcd7OW zIL?2Owyh@%!pJ-43evy^{k0c+lXK1JleGF7#GZ4uUc;fR0R5dmID=R$`Vm_}) zg_#ZWofyMGb;dm-J(8i=LGacbjUm)9itgg8MWRii0-SJU6}>5jTW)UZqPHFjz%7rV z_$LbxMbIo6lej6`D83JvB?|^n8Z-!SwMdjkJDeS>${mODFVrHh@Cpg7CYpRT^X81s zB6YWw8-f+#dB1|jM#8O*=qsh}(0XyX6uBP+6Rv`@Rd7gRn3r%`5Rm*!`qs&6?$HwV zhK6i7>Vlf>&!0P{i1*r#F{-8~Yj9@veFFO@ws{YDTy&0vUl(>yO-cDXP%a?eNCw2^ z)6HeGXLu(DGBW<&Wmc%}rhMj~>t*U8;h)Dawtwyn&N&fQ(F>>Bv15mSZlpQ;YN4c? z8oz)4CIfBgSNBu}4L)?DfrL8pwrW3QLDr}+bM|%QlMp6TuqLKrSd83tgVbo{Ew+1o zFcTA#FFFVfm@om?yZYe4MVf7=V*?s7jSllrJTID%rd~f-Nx~%gSw^N>E%eC51_sg_ zYJoCWh;eIjw#oD`0=-YNezS6YzmA)-2pT_RGz1QsLaWyG538!kWIk8~MmPKs&uCub zI=8F1@(Vl6!^cOB-9aTfg4$K}r8QFqL$)4HnALzu1ba=&RctI9F@JPfAH@6{2>koc zp93&{AW+?C6<{ZbB*_5v@_%z<%d7FYr*yc2ZDn^~A8rInNVDH({gTQ3m!~Xbp_IZO zdS;6c-e+p-TQ}g66{m_fQi0QY+yC3mZ$DY?{V%uH{r8Ik?=|dQ)6IhcV$y6tqYD!s zFtC01vtReL16Qt~69MoTk?bCwAoxiyJ=gclCq3OhD0wJ5bl{HH;4y)?g`0(FF#I|) zI+_fk9J|-%Xx^}W*7}RI*%&Of200^BBYGBYxYq8UtM5(@<&un#q64}}w_(Eu(xHC) zmJ<~?W;QtN-n|gqv&A8OaSae`VM&Q!U0t2pTrjR0!0m@ZA|lP0G9)&COcC;kiZUVM zdKp(+*x1m$J6B)~m&765t%5rm$RI2NKFca50IU4JzZ64W{ z*dzo(9tjCn=o9s5<6Cpx3~B#lG5xW%A6Ml7M(we9Opj!`U+Y6aJ$Xtoz{brm1Qfc@ zoW{_bIqs7i8LE>b4Rq8}y(+AqjVOvg=@XM%^!~ZN>Lzx92SW!?hzx5z-G{xyJ^o() z7e~GYj4vd8;ZnUqw0~q^671u8h>2ekrQ-H|Yv2ZW1g?ky;2@wCJ{$njNyC6DuFwlY z3EuyqJd69{I$W_Ahk5E1(DupL0@|j~x#`hRBi&(ic$Lc9F|&}2bVo!7P%a9&=7r3S zGI4Q*0E=0m&=YwaExX*p2Wzt|RW$#- ze@g9+D~=C_M8hk-W#G~*2)2Q6{*w_UXc6>l*DAqKPU>LXb2tuArZEYpADnE8tjwl- z2JF>8H8u6=u{SXo&V%Eg~iz31Q4;iZUN0Hf3Vj&Z7~hYM8ja4NSu6OAX8Xle&+*6@Bq9C8*;$vk z+k5x#55&X?v;`Wz2n`?bMl!7jM5`Y!2eEN%Y)ll|oXYor#~puvX5#Z0nRo66P}&$6 z7$&DbsbfM^msr&)W)0U?!Aa79mnOjfJxuwJ$t)0n2r&x~J!!gqw@_Zezqhyjx2Z$5 zn)k;;GW z1AOurejOOw5xIIJ9twvKhuyuq#>dCUKfj#!??r_gz~Th6f=s-rbXDgauZBe&uF^g5 zjzzO?{d+y96|6deP>$;d2C4>?W(x^@fKR3e3CfCGWzkbz9hmo{QF z&AN7nAnxS@S4VGiEnF}2n0L{0Fgx%!a@0{o$v6{XEEu-%Mav2FA4C0y*;^} z9DzKHw_v^W;&0=^O+~xx1)lF$An9&`G7S$g!^s_ijaffioP>5y_Ecak&)*Is>`g5=a-lY=dlKk})hfy%w!Vb6Z?Dy}5mBYHcO!P26aNguNupS!h|JB@?$7EfA zas0(Jnn9kd@<0?d1QsBNjuJ&crt<~l3T&9DV4zH6k;gPd1csuB_@b!63y8@Js2p=7 zpqzm(34|{+W9R~zrX~^|2x@(vS~IuS)_?sE@9{gH-}60R3I&8*6@wWJ5k@D6DAz=E zNT~|oj!xj>1b95|yO9imAZ>d`9U@lxxEDy}*J6Amdrna^d$nPJLzgU7~dXCIZuB95hf2hPWD5#5CU{erT-^Jm# zK&A6{3|cEzW3&OW4E8rn>xY`G#!#K;)A37e*`6c*XkWPK)~#G(GFDB_DrS7UM@I!O zTZ8LRyaskd?{xl(@yIXfjiqlzhN(0MTnDVxc*yMheGLxCc3%^XFXEuIsK%(XF zc>z0{=Nq0l9oiOJ2^9?#(6eGus<9HTT@Gt70-nq3~mkx|RMTP|(sTgBEaezX;y}PPj5}Be7Z&zO*1aDWhwi+= z@qnH&1gVTVr3u_Q;4nL^ga`Ks1xX5@unRN?A*#>Jv?wP-%WLGAih9EaB@u8$0Ne_$ zUS3{>O)u+@_aA;(ND^3IqBB^1M!tW<;!&hh^`tSyRP+owbozh(I#Uccq66sKH(bou z`3ByI!k!={FN9>G?ynDNv?%md_uWFXXgU2tr z+0`C!j-*eAM?sX+!*ET35z|E424nDrv`@;(8_;GKl^}&Z*jf?3@M>EdjN8EvT-FYG z8nq6=0}Kw`J!?ZM5I{n8t)6n~(rzMcNPyL>6O4{mo|2;Epd{9kO6*#D>Gr?ZFj>bp1*Q5dzGWR@7<1 zMQWeLtXZ#~D_tN8k)4z$P8RcO+|>TV2lLU(&J`6ovsCryr#jcqIzG2D#5@mGC}?mnSDDCj?lg?KpD}h0czdz~k`V^~%Wv9;A|IL&eiB-)&vO zC*;rXnPRiXRdqf|nf^%JuWXk;!Iy~;h(1O=tpHmhTe%0D*1Cm&1kA)%$i^(wIQm(? zdu~gD;pExhBGb}P&8h8(Z72PmIu{eRH z77fz!H9$#hO3I4LNQbmBnq3{bx=$$wp(rwS`vRm8cSbZvEDk3wlY({L7@FMw_H!#N zyas!F`Y? z+SF&!#1Bs@LpVuygBBU3N3ozvxSUH|>aJltr<<6lFE>Bzt)3qCqQJh01-AyJpOpcU zVT(^go*(`O6t!4(hCJ6NtN)IieHIL=n-{5^>#u1vUaz#Jhc5q54|6@gbBs?`6_+{s zpXVR=Yl8twYxl6+OxRaXpJ{x6rBs@}TFVY}_}4yv@5KA`g*8DxMRd=MB4<^@x2o=q zR9*bUjauhF%bhje9eW+s1}@X+h`iF+s8C1&Q&_CEL5>|VP%Fijv#+SfFKD}U^QKT4 zJXpNT{-=y&__oqV(9w+75i|EUK5RYF0)@BixO3Z5_g=%``Zz38NJ Re&s>2)Xl^7%;MF%e+Sxm@6!MP diff --git a/tests/test_visualization.py b/tests/test_visualization.py index 2a83e2b1c..e4300999f 100644 --- a/tests/test_visualization.py +++ b/tests/test_visualization.py @@ -231,9 +231,9 @@ def test_empty_pattern() -> None: # Compare with baseline/test_draw_graph_reference.png # Update baseline by running: pytest --mpl-generate-path=tests/baseline @pytest.mark.usefixtures("mock_plot") -@pytest.mark.parametrize("flow_from_pattern", [False, True]) +@pytest.mark.parametrize("flow_and_not_pauli_presimulate", [False, True]) @pytest.mark.mpl_image_compare -def test_draw_graph_reference(flow_from_pattern: bool) -> Figure: +def test_draw_graph_reference(flow_and_not_pauli_presimulate: bool) -> Figure: circuit = Circuit(3) circuit.cnot(0, 1) circuit.cnot(2, 1) @@ -241,8 +241,9 @@ def test_draw_graph_reference(flow_from_pattern: bool) -> Figure: circuit.x(2) circuit.cnot(2, 1) pattern = circuit.transpile().pattern - pattern.remove_input_nodes() - pattern.perform_pauli_measurements() + if not flow_and_not_pauli_presimulate: + pattern.remove_input_nodes() + pattern.perform_pauli_measurements() pattern.standardize() - pattern.draw_graph(flow_from_pattern=flow_from_pattern, node_distance=(0.7, 0.6)) + pattern.draw_graph(flow_from_pattern=flow_and_not_pauli_presimulate, node_distance=(0.7, 0.6)) return plt.gcf() From 14564b44c44402a7d880e60f71b018b806354f42 Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Thu, 11 Dec 2025 15:20:36 +0100 Subject: [PATCH 07/18] udpated docs and tests --- examples/deutsch_jozsa.py | 1 + examples/mbqc_vqe.py | 1 + examples/qaoa.py | 1 + examples/qft_with_tn.py | 1 + examples/tn_simulation.py | 3 ++- examples/visualization.py | 2 +- tests/test_gflow.py | 1 + tests/test_pyzx.py | 2 ++ 8 files changed, 10 insertions(+), 2 deletions(-) diff --git a/examples/deutsch_jozsa.py b/examples/deutsch_jozsa.py index 771c8ca50..31dd37b7b 100644 --- a/examples/deutsch_jozsa.py +++ b/examples/deutsch_jozsa.py @@ -73,6 +73,7 @@ # %% # Now we preprocess all Pauli measurements +pattern.remove_input_nodes() pattern.perform_pauli_measurements() print( pattern.to_ascii( diff --git a/examples/mbqc_vqe.py b/examples/mbqc_vqe.py index 269fb1ce6..ae75a7f7f 100644 --- a/examples/mbqc_vqe.py +++ b/examples/mbqc_vqe.py @@ -89,6 +89,7 @@ def build_mbqc_pattern(self, params: Iterable[Angle]) -> Pattern: pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() + pattern.remove_input_nodes() pattern.perform_pauli_measurements() # Perform Pauli measurements return pattern diff --git a/examples/qaoa.py b/examples/qaoa.py index 51355b05c..57b2cafc9 100644 --- a/examples/qaoa.py +++ b/examples/qaoa.py @@ -40,6 +40,7 @@ # %% # perform Pauli measurements and plot the new (minimal) graph to perform the same quantum computation +pattern.remove_input_nodes() pattern.perform_pauli_measurements() pattern.draw_graph(flow_from_pattern=False) diff --git a/examples/qft_with_tn.py b/examples/qft_with_tn.py index c8f9722c2..c1e064dc8 100644 --- a/examples/qft_with_tn.py +++ b/examples/qft_with_tn.py @@ -63,6 +63,7 @@ def qft(circuit, n): # %% # Using efficient graph state simulator `graphix.graphsim`, we can classically preprocess Pauli measurements. # We are currently improving the speed of this process by using rust-based graph manipulation backend. +pattern.remove_input_nodes() pattern.perform_pauli_measurements() diff --git a/examples/tn_simulation.py b/examples/tn_simulation.py index 6c8425875..61b43a2a6 100644 --- a/examples/tn_simulation.py +++ b/examples/tn_simulation.py @@ -69,7 +69,7 @@ def ansatz(circuit, n, gamma, beta, iterations): # %% # Optimizing by performing Pauli measurements in the pattern using efficient stabilizer simulator. - +pattern.remove_input_nodes() pattern.perform_pauli_measurements() # %% @@ -180,6 +180,7 @@ def cost(params, n, ham, quantum_iter, slice_index, opt=None): pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals() + pattern.remove_input_nodes() pattern.perform_pauli_measurements() mbqc_tn = pattern.simulate_pattern(backend="tensornetwork", graph_prep="parallel") exp_val = 0 diff --git a/examples/visualization.py b/examples/visualization.py index 87b02500f..47c88ab5f 100644 --- a/examples/visualization.py +++ b/examples/visualization.py @@ -39,7 +39,7 @@ # %% # next, show the gflow: - +pattern.remove_input_nodes() pattern.perform_pauli_measurements() pattern.draw_graph(flow_from_pattern=False, show_measurement_planes=True, node_distance=(1, 0.6)) diff --git a/tests/test_gflow.py b/tests/test_gflow.py index 87fbae81a..0d938be87 100644 --- a/tests/test_gflow.py +++ b/tests/test_gflow.py @@ -538,6 +538,7 @@ def test_rand_circ_gflow(self, fx_rng: Generator) -> None: pattern = circ.transpile().pattern pattern.standardize() pattern.shift_signals() + pattern.remove_input_nodes() pattern.perform_pauli_measurements() graph = pattern.extract_graph() input_ = set() diff --git a/tests/test_pyzx.py b/tests/test_pyzx.py index a468cca0f..644a33e30 100644 --- a/tests/test_pyzx.py +++ b/tests/test_pyzx.py @@ -84,9 +84,11 @@ def test_random_circuit(fx_bg: PCG64, jumps: int) -> None: zx_graph = to_pyzx_graph(opengraph) opengraph2 = from_pyzx_graph(zx_graph) pattern2 = opengraph2.to_pattern() + pattern.remove_input_nodes() pattern.perform_pauli_measurements() pattern.minimize_space() state = pattern.simulate_pattern() + pattern2.remove_input_nodes() pattern2.perform_pauli_measurements() pattern2.minimize_space() state2 = pattern2.simulate_pattern() From 535d5c3f58cf3b746966932b0ce24bab42d21e0b Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Thu, 11 Dec 2025 17:50:23 +0100 Subject: [PATCH 08/18] Fixed issue #397 --- graphix/pattern.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/graphix/pattern.py b/graphix/pattern.py index ad3f22a3c..243f31247 100644 --- a/graphix/pattern.py +++ b/graphix/pattern.py @@ -1660,6 +1660,9 @@ def measure_pauli(pattern: Pattern, *, copy: bool = False, ignore_pauli_with_dep .. seealso:: :class:`graphix.graphsim.GraphState` """ + pat = Pattern() if copy else pattern + if pat._pauli_preprocessed is True: + return pat standardized_pattern = optimization.StandardizedPattern.from_pattern(pattern) if not ignore_pauli_with_deps: standardized_pattern = standardized_pattern.perform_pauli_pushing() @@ -1668,6 +1671,8 @@ def measure_pauli(pattern: Pattern, *, copy: bool = False, ignore_pauli_with_dep graph_state = GraphState(nodes=graph.nodes, edges=graph.edges, vops=standardized_pattern.c_dict) results: dict[int, Outcome] = {} to_measure, non_pauli_meas = pauli_nodes(standardized_pattern) + if not to_measure: + return pattern for cmd in to_measure: pattern_cmd = cmd[0] measurement_basis = cmd[1] @@ -1728,9 +1733,6 @@ def measure_pauli(pattern: Pattern, *, copy: bool = False, ignore_pauli_with_dep ) new_seq.extend(command.Z(node=node, domain=set(domain)) for node, domain in standardized_pattern.z_dict.items()) new_seq.extend(command.X(node=node, domain=set(domain)) for node, domain in standardized_pattern.x_dict.items()) - - pat = Pattern() if copy else pattern - pat.replace(new_seq, input_nodes=[]) pat.reorder_output_nodes(standardized_pattern.output_nodes) assert pat.n_node == len(graph_state.nodes) From f9b364803ca5fc22374f25ac841f80cfbc2c3465 Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Fri, 12 Dec 2025 15:16:20 +0100 Subject: [PATCH 09/18] corrected issues with and added test. --- graphix/pattern.py | 7 ++++++- tests/test_pattern.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/graphix/pattern.py b/graphix/pattern.py index 243f31247..50cb4b4fb 100644 --- a/graphix/pattern.py +++ b/graphix/pattern.py @@ -123,6 +123,7 @@ def add(self, cmd: Command) -> None: cmd : :class:`graphix.command.Command` MBQC command. """ + self._pauli_preprocessed = False if cmd.kind == CommandKind.N: self.__n_node += 1 self.__output_nodes.append(cmd.node) @@ -136,6 +137,7 @@ def extend(self, *cmds: Command | Iterable[Command]) -> None: :param cmds: sequences of commands """ + self._pauli_preprocessed = False for item in cmds: if isinstance(item, Iterable): for cmd in item: @@ -145,6 +147,7 @@ def extend(self, *cmds: Command | Iterable[Command]) -> None: def clear(self) -> None: """Clear the sequence of pattern commands.""" + self._pauli_preprocessed = False self.__n_node = len(self.__input_nodes) self.__seq = [] self.__output_nodes = list(self.__input_nodes) @@ -156,6 +159,7 @@ def replace(self, cmds: list[Command], input_nodes: list[int] | None = None) -> :param input_nodes: optional, list of input qubits (by default, keep the same input nodes as before) """ + self._pauli_preprocessed = False if input_nodes is not None: self.__input_nodes = list(input_nodes) self.clear() @@ -201,6 +205,7 @@ 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. """ + self._pauli_preprocessed = False nodes_p1 = self.extract_nodes() | self.results.keys() # Results contain preprocessed Pauli nodes nodes_p2 = other.extract_nodes() | other.results.keys() @@ -1669,7 +1674,7 @@ def measure_pauli(pattern: Pattern, *, copy: bool = False, ignore_pauli_with_dep output_nodes = set(pattern.output_nodes) graph = standardized_pattern.extract_graph() graph_state = GraphState(nodes=graph.nodes, edges=graph.edges, vops=standardized_pattern.c_dict) - results: dict[int, Outcome] = {} + results: dict[int, Outcome] = pat.results to_measure, non_pauli_meas = pauli_nodes(standardized_pattern) if not to_measure: return pattern diff --git a/tests/test_pattern.py b/tests/test_pattern.py index 412cca1b5..b4336cc3f 100644 --- a/tests/test_pattern.py +++ b/tests/test_pattern.py @@ -266,6 +266,41 @@ def test_pauli_measured_against_nonmeasured(self, fx_bg: PCG64, jumps: int, igno state1 = pattern1.simulate_pattern(rng=rng) assert np.abs(np.dot(state.flatten().conjugate(), state1.flatten())) == pytest.approx(1) + @pytest.mark.parametrize("jumps", range(1, 4)) + def test_pauli_repeated_measurement(self, fx_bg: PCG64, jumps: int) -> None: + rng = Generator(fx_bg.jumped(jumps)) + nqubits = 2 + depth = 2 + circuit = rand_circuit(nqubits, depth, rng, use_ccx=False) + pattern = circuit.transpile().pattern + pattern.remove_input_nodes() + assert not pattern.results + pattern.perform_pauli_measurements() + assert pattern.results + pattern.perform_pauli_measurements() + assert pattern.results + + @pytest.mark.parametrize("jumps", range(1, 4)) + def test_pauli_repeated_measurement_compose(self, fx_bg: PCG64, jumps: int) -> None: + rng = Generator(fx_bg.jumped(jumps)) + nqubits = 2 + depth = 2 + circuit = rand_circuit(nqubits, depth, rng, use_ccx=False) + circuit1 = rand_circuit(nqubits, depth, rng, use_ccx=False) + pattern = circuit.transpile().pattern + pattern1 = circuit1.transpile().pattern + pattern.remove_input_nodes() + assert not pattern.results + assert not pattern1.results + pattern.perform_pauli_measurements() + assert pattern.results + composed_pattern, _ = pattern.compose( + pattern1, mapping=dict(zip(pattern1.input_nodes, pattern.output_nodes, strict=True)), preserve_mapping=True + ) + composed_pattern.remove_input_nodes() + composed_pattern.perform_pauli_measurements() + assert len(composed_pattern.results) > len(pattern.results) + def test_get_meas_plane(self) -> None: preset_meas_plane = [ Plane.XY, From 5a73584ef382de4c7fdd3df77c8290afbd5a0e78 Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Mon, 15 Dec 2025 11:38:45 +0100 Subject: [PATCH 10/18] simplified function in . --- tests/test_pattern.py | 32 ++++++-------------------------- 1 file changed, 6 insertions(+), 26 deletions(-) diff --git a/tests/test_pattern.py b/tests/test_pattern.py index b4336cc3f..c34410eb5 100644 --- a/tests/test_pattern.py +++ b/tests/test_pattern.py @@ -228,26 +228,22 @@ def test_pauli_measurement(self) -> None: circuit.h(i) circuit.x(1) circuit.x(2) - # QFT circuit.h(2) - cp(circuit, np.pi / 4, 0, 2) - cp(circuit, np.pi / 2, 1, 2) + circuit.rzz(1, 2, np.pi / 4) + circuit.rzz(0, 2, np.pi / 8) circuit.h(1) - cp(circuit, np.pi / 2, 0, 1) + circuit.rzz(0, 1, np.pi / 4) circuit.h(0) - swap(circuit, 0, 2) - + circuit.swap(0, 2) pattern = circuit.transpile().pattern pattern.standardize() pattern.shift_signals(method="mc") pattern.remove_input_nodes() pattern.perform_pauli_measurements() - isolated_nodes = pattern.extract_isolated_nodes() - # 48-node is the isolated and output node. - isolated_nodes_ref = {48} - + # 30-node is the isolated and output node. + isolated_nodes_ref = {30} assert isolated_nodes == isolated_nodes_ref @pytest.mark.parametrize("jumps", range(1, 6)) @@ -752,22 +748,6 @@ def test_compute_max_degree_empty_pattern(self) -> None: assert Pattern().compute_max_degree() == 0 -def cp(circuit: Circuit, theta: float, control: int, target: int) -> None: - """Controlled rotation gate, decomposed.""" # noqa: D401 - circuit.rz(control, theta / 2) - circuit.rz(target, theta / 2) - circuit.cnot(control, target) - circuit.rz(target, -1 * theta / 2) - circuit.cnot(control, target) - - -def swap(circuit: Circuit, a: int, b: int) -> None: - """Swap gate, decomposed.""" - circuit.cnot(a, b) - circuit.cnot(b, a) - circuit.cnot(a, b) - - class TestMCOps: @pytest.mark.parametrize( "test", From 1545dbd5bd46b579b4175f7364457e544a490525 Mon Sep 17 00:00:00 2001 From: Emlyn <42484330+emlynsg@users.noreply.github.com> Date: Tue, 16 Dec 2025 09:33:00 +0100 Subject: [PATCH 11/18] Update graphix/pattern.py Co-authored-by: thierry-martinez --- graphix/pattern.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/graphix/pattern.py b/graphix/pattern.py index 50cb4b4fb..d7c3be916 100644 --- a/graphix/pattern.py +++ b/graphix/pattern.py @@ -1384,8 +1384,7 @@ def simulate_pattern( def remove_input_nodes(self) -> None: """Remove the input nodes from the pattern.""" - for node in reversed(self.input_nodes): - self.__seq.insert(0, command.N(node=node)) + self.__seq[0:0] = [command.N(node=node) for node in self.input_nodes] empty_nodes: list[int] = [] self.__input_nodes = empty_nodes From 0608999af9f42a9dfd6cf21595ae9a6ef201d6c9 Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Tue, 16 Dec 2025 10:01:08 +0100 Subject: [PATCH 12/18] updated tutorial and result handling --- docs/source/tutorial.rst | 5 +++-- graphix/pattern.py | 14 ++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/source/tutorial.rst b/docs/source/tutorial.rst index 3d7ba2093..c27b695f2 100644 --- a/docs/source/tutorial.rst +++ b/docs/source/tutorial.rst @@ -175,12 +175,13 @@ It is known that quantum circuit consisting of Pauli basis states, Clifford gate `_; e.g. the graph state simulator runs in :math:`\mathcal{O}(n \log n)` time). The Pauli measurement part of the MBQC is exactly this, and they can be preprocessed by our graph state simulator :class:`~graphix.graphsim.GraphState` - see :doc:`lc-mbqc` for more detailed description. -We can call this in a line by calling :meth:`~graphix.pattern.Pattern.perform_pauli_measurements()` of :class:`~graphix.pattern.Pattern` object, which acts as the optimization routine of the measurement pattern. +We can call this in a line by calling :meth:`~graphix.pattern.Pattern.remove_input_nodes` followed by ` :meth:`~graphix.pattern.Pattern.perform_pauli_measurements()` (both methods of the :class:`~graphix.pattern.Pattern` object). The first method removes the input nodes, while the second method optimizes the measurement pattern. We get an updated measurement pattern without Pauli measurements as follows: +>>> pattern.remove_input_nodes() >>> pattern.perform_pauli_measurements() >>> pattern -Pattern(input_nodes=[0, 1], cmds=[N(3), N(7), E((0, 3)), E((1, 3)), E((1, 7)), M(0, Plane.YZ, 0.2907266109187514), M(1, Plane.YZ, 0.01258854060311348), C(3, Clifford.I), C(7, Clifford.I), Z(3, {0, 1, 5}), Z(7, {1, 5}), X(3, {2}), X(7, {2, 4, 6})], output_nodes=[3, 7]) +Pattern(input_nodes=[], cmds=[N(0), N(1), N(3), N(7), E((0, 3)), E((1, 3)), E((1, 7)), M(0, Plane.YZ, 0.2907266109187514), M(1, Plane.YZ, 0.01258854060311348), C(3, Clifford.I), C(7, Clifford.I), Z(3, {0, 1, 5}), Z(7, {1, 5}), X(3, {2}), X(7, {2, 4, 6})], output_nodes=[3, 7]) Notice that all measurements with angle=0 (Pauli X measurements) disappeared - this means that a part of quantum computation was *classically* (and efficiently) preprocessed such that we only need much smaller quantum resource. diff --git a/graphix/pattern.py b/graphix/pattern.py index d7c3be916..513ef98e6 100644 --- a/graphix/pattern.py +++ b/graphix/pattern.py @@ -1383,7 +1383,11 @@ def simulate_pattern( return sim.backend.state def remove_input_nodes(self) -> None: - """Remove the input nodes from the pattern.""" + """Remove the input nodes from the pattern and replace them with N commands. + + This removes the possibility of choosing the input state, fixing the input state to the plus state. + .. seealso:: :class:`graphix.command.N` + """ self.__seq[0:0] = [command.N(node=node) for node in self.input_nodes] empty_nodes: list[int] = [] self.__input_nodes = empty_nodes @@ -1638,9 +1642,8 @@ def __str__(self) -> str: def measure_pauli(pattern: Pattern, *, copy: bool = False, ignore_pauli_with_deps: bool = False) -> Pattern: """Perform Pauli measurement of a pattern by fast graph state simulator. - Uses the decorated-graph method implemented in graphix.graphsim to perform - the measurements in Pauli bases, and then sort remaining nodes back into - pattern together with Clifford commands. + Uses the decorated-graph method implemented in graphix.graphsim to perform the measurements in Pauli bases, and then sort remaining nodes back into + pattern together with Clifford commands. Users are required to ensure there are no input nodes with :func:`graphix.pattern.Pattern.remove_input_nodes` before using this function. TODO: non-XY plane measurements in original pattern @@ -1662,11 +1665,10 @@ def measure_pauli(pattern: Pattern, *, copy: bool = False, ignore_pauli_with_dep only returned if copy argument is True. + .. seealso:: :class:`graphix.pattern.Pattern.remove_input_nodes` .. seealso:: :class:`graphix.graphsim.GraphState` """ pat = Pattern() if copy else pattern - if pat._pauli_preprocessed is True: - return pat standardized_pattern = optimization.StandardizedPattern.from_pattern(pattern) if not ignore_pauli_with_deps: standardized_pattern = standardized_pattern.perform_pauli_pushing() From d11386ddce90265ec3d9e33a8530d989a8dde956 Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Tue, 16 Dec 2025 14:02:43 +0100 Subject: [PATCH 13/18] fixed changelog and tutorial --- CHANGELOG.md | 4 ++++ docs/source/tutorial.rst | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3396d617..58a1eba65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- #392: Added `graphix.pattern.Pattern.remove_input_nodes` method which removes the input nodes from the pattern and replaces them with N commands. + - #385 - Introduced `graphix.flow.core.XZCorrections.check_well_formed` which verifies the correctness of an XZ-corrections instance and raises an exception if incorrect. - Added XZ-correction exceptions to module `graphix.flow.core.exceptions`. @@ -27,6 +29,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- #392: `Pattern.remove_input_nodes` is required before the `Pattern.perform_pauli_measurements` method to ensure input nodes are removed and fixed in the |+> state. + - #379: Removed unnecessary `meas_index` from API for rotation instructions `RZ`, `RY` and `RX`. - #347: Adapted existing method `graphix.opengraph.OpenGraph.isclose` to the new API introduced in #358. diff --git a/docs/source/tutorial.rst b/docs/source/tutorial.rst index c27b695f2..7364e4be6 100644 --- a/docs/source/tutorial.rst +++ b/docs/source/tutorial.rst @@ -175,7 +175,7 @@ It is known that quantum circuit consisting of Pauli basis states, Clifford gate `_; e.g. the graph state simulator runs in :math:`\mathcal{O}(n \log n)` time). The Pauli measurement part of the MBQC is exactly this, and they can be preprocessed by our graph state simulator :class:`~graphix.graphsim.GraphState` - see :doc:`lc-mbqc` for more detailed description. -We can call this in a line by calling :meth:`~graphix.pattern.Pattern.remove_input_nodes` followed by ` :meth:`~graphix.pattern.Pattern.perform_pauli_measurements()` (both methods of the :class:`~graphix.pattern.Pattern` object). The first method removes the input nodes, while the second method optimizes the measurement pattern. +We can call this in a line by calling :meth:`~graphix.pattern.Pattern.remove_input_nodes` followed by :meth:`~graphix.pattern.Pattern.perform_pauli_measurements()` (both methods of the :class:`~graphix.pattern.Pattern` object). The first method removes the input nodes, while the second method optimizes the measurement pattern. We get an updated measurement pattern without Pauli measurements as follows: >>> pattern.remove_input_nodes() @@ -211,7 +211,7 @@ We can simply call :meth:`~graphix.pattern.Pattern.minimize_space()` to reduce t >>> pattern.minimize_space() >>> pattern -Pattern(input_nodes=[0, 1], cmds=[N(3), E((0, 3)), M(0, Plane.YZ, 0.11120090987081546), E((1, 3)), N(7), E((1, 7)), M(1, Plane.YZ, 0.230565199664617), C(3, Clifford.I), C(7, Clifford.I), Z(3, {0, 1, 5}), Z(7, {1, 5}), X(3, {2}), X(7, {2, 4, 6})], output_nodes=[3, 7]) +Pattern(input_nodes=[], cmds=[N(0), N(1), N(3), E((0, 3)), M(0, Plane.YZ, 0.11120090987081546), E((1, 3)), N(7), E((1, 7)), M(1, Plane.YZ, 0.230565199664617), C(3, Clifford.I), C(7, Clifford.I), Z(3, {0, 1, 5}), Z(7, {1, 5}), X(3, {2}), X(7, {2, 4, 6})], output_nodes=[3, 7]) With the original measurement pattern, the simulation should have proceeded as follows, with maximum of four qubits on the memory. From 91982b7f496d7225556c659bc0111131f652eb60 Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Tue, 16 Dec 2025 14:26:30 +0100 Subject: [PATCH 14/18] fixing CI --- tests/test_pattern.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/tests/test_pattern.py b/tests/test_pattern.py index c34410eb5..8404906b2 100644 --- a/tests/test_pattern.py +++ b/tests/test_pattern.py @@ -246,6 +246,15 @@ def test_pauli_measurement(self) -> None: isolated_nodes_ref = {30} assert isolated_nodes == isolated_nodes_ref + def test_pauli_measurement_error(self, fx_rng: Generator) -> None: + nqubits = 2 + depth = 1 + circuit = rand_circuit(nqubits, depth, fx_rng) + pattern = circuit.transpile().pattern + pattern.standardize() + with pytest.raises(ValueError): + pattern.perform_pauli_measurements() + @pytest.mark.parametrize("jumps", range(1, 6)) @pytest.mark.parametrize("ignore_pauli_with_deps", [False, True]) def test_pauli_measured_against_nonmeasured(self, fx_bg: PCG64, jumps: int, ignore_pauli_with_deps: bool) -> None: @@ -285,17 +294,18 @@ def test_pauli_repeated_measurement_compose(self, fx_bg: PCG64, jumps: int) -> N circuit1 = rand_circuit(nqubits, depth, rng, use_ccx=False) pattern = circuit.transpile().pattern pattern1 = circuit1.transpile().pattern + composed_pattern, _ = pattern.compose( + pattern1, mapping=dict(zip(pattern1.input_nodes, pattern.output_nodes, strict=True)), preserve_mapping=True + ) pattern.remove_input_nodes() + pattern1.remove_input_nodes() assert not pattern.results assert not pattern1.results pattern.perform_pauli_measurements() - assert pattern.results - composed_pattern, _ = pattern.compose( - pattern1, mapping=dict(zip(pattern1.input_nodes, pattern.output_nodes, strict=True)), preserve_mapping=True - ) + pattern1.perform_pauli_measurements() composed_pattern.remove_input_nodes() composed_pattern.perform_pauli_measurements() - assert len(composed_pattern.results) > len(pattern.results) + assert abs(len(composed_pattern.results) - len(pattern.results) - len(pattern1.results)) <= 2 def test_get_meas_plane(self) -> None: preset_meas_plane = [ From 63385d88981663b40770f66ec7e15d751a4803d8 Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Tue, 16 Dec 2025 16:55:56 +0100 Subject: [PATCH 15/18] Pushing again to check CI --- examples/deutsch_jozsa.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/deutsch_jozsa.py b/examples/deutsch_jozsa.py index 31dd37b7b..4db0f055a 100644 --- a/examples/deutsch_jozsa.py +++ b/examples/deutsch_jozsa.py @@ -71,7 +71,7 @@ print(pattern.to_ascii(left_to_right=True, limit=15)) # %% -# Now we preprocess all Pauli measurements +# Now we preprocess all Pauli measurements, which requires that we move inputs to N commands pattern.remove_input_nodes() pattern.perform_pauli_measurements() From e9eeafdefa20b3be1bf9bf8e0d33d0e9a0cbba39 Mon Sep 17 00:00:00 2001 From: Emlyn Graham Date: Tue, 16 Dec 2025 17:13:41 +0100 Subject: [PATCH 16/18] Disabled qasm parser CI tests causing Graphix CI to fail. --- noxfile.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/noxfile.py b/noxfile.py index 16574550d..ccad346b7 100644 --- a/noxfile.py +++ b/noxfile.py @@ -74,18 +74,18 @@ def tests_symbolic(session: Session) -> None: run_pytest(session, doctest_modules=True) -@nox.session(python=["3.10", "3.11", "3.12", "3.13"]) -def tests_qasm_parser(session: Session) -> None: - """Run the test suite of graphix-qasm-parser.""" - session.install(".") - install_pytest(session) - session.install("nox") # needed for `--doctest-modules` - # Use `session.cd` as a context manager to ensure that the - # working directory is restored afterward. This is important - # because Windows cannot delete a temporary directory while it - # is the working directory. - with TemporaryDirectory() as tmpdir, session.cd(tmpdir): - session.run("git", "clone", "https://github.com/TeamGraphix/graphix-qasm-parser") - with session.cd("graphix-qasm-parser"): - session.install(".") - run_pytest(session, doctest_modules=True) +# @nox.session(python=["3.10", "3.11", "3.12", "3.13"]) +# def tests_qasm_parser(session: Session) -> None: +# """Run the test suite of graphix-qasm-parser.""" +# session.install(".") +# install_pytest(session) +# session.install("nox") # needed for `--doctest-modules` +# # Use `session.cd` as a context manager to ensure that the +# # working directory is restored afterward. This is important +# # because Windows cannot delete a temporary directory while it +# # is the working directory. +# with TemporaryDirectory() as tmpdir, session.cd(tmpdir): +# session.run("git", "clone", "https://github.com/TeamGraphix/graphix-qasm-parser") +# with session.cd("graphix-qasm-parser"): +# session.install(".") +# run_pytest(session, doctest_modules=True) From 6baec4c64bdc8195d65f37f0c0ef3e7127c0f631 Mon Sep 17 00:00:00 2001 From: emlynsg Date: Tue, 6 Jan 2026 11:12:31 +0900 Subject: [PATCH 17/18] Added qasm test back in. --- noxfile.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/noxfile.py b/noxfile.py index ccad346b7..16574550d 100644 --- a/noxfile.py +++ b/noxfile.py @@ -74,18 +74,18 @@ def tests_symbolic(session: Session) -> None: run_pytest(session, doctest_modules=True) -# @nox.session(python=["3.10", "3.11", "3.12", "3.13"]) -# def tests_qasm_parser(session: Session) -> None: -# """Run the test suite of graphix-qasm-parser.""" -# session.install(".") -# install_pytest(session) -# session.install("nox") # needed for `--doctest-modules` -# # Use `session.cd` as a context manager to ensure that the -# # working directory is restored afterward. This is important -# # because Windows cannot delete a temporary directory while it -# # is the working directory. -# with TemporaryDirectory() as tmpdir, session.cd(tmpdir): -# session.run("git", "clone", "https://github.com/TeamGraphix/graphix-qasm-parser") -# with session.cd("graphix-qasm-parser"): -# session.install(".") -# run_pytest(session, doctest_modules=True) +@nox.session(python=["3.10", "3.11", "3.12", "3.13"]) +def tests_qasm_parser(session: Session) -> None: + """Run the test suite of graphix-qasm-parser.""" + session.install(".") + install_pytest(session) + session.install("nox") # needed for `--doctest-modules` + # Use `session.cd` as a context manager to ensure that the + # working directory is restored afterward. This is important + # because Windows cannot delete a temporary directory while it + # is the working directory. + with TemporaryDirectory() as tmpdir, session.cd(tmpdir): + session.run("git", "clone", "https://github.com/TeamGraphix/graphix-qasm-parser") + with session.cd("graphix-qasm-parser"): + session.install(".") + run_pytest(session, doctest_modules=True) From 28397fa87202d21d073c8fe3bcdc1fe6e84632c1 Mon Sep 17 00:00:00 2001 From: emlynsg Date: Wed, 7 Jan 2026 11:57:48 +0900 Subject: [PATCH 18/18] fixing CI --- tests/test_pattern.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/test_pattern.py b/tests/test_pattern.py index c3934da01..0b282cac9 100644 --- a/tests/test_pattern.py +++ b/tests/test_pattern.py @@ -247,8 +247,8 @@ def test_pauli_measurement(self) -> None: pattern.remove_input_nodes() pattern.perform_pauli_measurements() isolated_nodes = pattern.extract_isolated_nodes() - # 30-node is the isolated and output node. - isolated_nodes_ref = {30} + # 42-node is the isolated and output node. + isolated_nodes_ref = {42} assert isolated_nodes == isolated_nodes_ref def test_pauli_measurement_error(self, fx_rng: Generator) -> None: @@ -813,6 +813,7 @@ def test_extract_partial_order_layers_results(self) -> None: c = Circuit(1) c.rz(0, 0.2) p = c.transpile().pattern + p.remove_input_nodes() p.perform_pauli_measurements() assert p.extract_partial_order_layers() == (frozenset({2}), frozenset({0})) @@ -938,6 +939,8 @@ def test_extract_causal_flow_rnd_circuit(self, fx_bg: PCG64, jumps: int) -> None p_ref = circuit_1.transpile().pattern p_test = p_ref.extract_causal_flow().to_corrections().to_pattern() + p_ref.remove_input_nodes() + p_test.remove_input_nodes() p_ref.perform_pauli_measurements() p_test.perform_pauli_measurements() @@ -954,7 +957,8 @@ def test_extract_gflow_rnd_circuit(self, fx_bg: PCG64, jumps: int) -> None: circuit_1 = rand_circuit(nqubits, depth, rng, use_ccx=False) p_ref = circuit_1.transpile().pattern p_test = p_ref.extract_gflow().to_corrections().to_pattern() - + p_ref.remove_input_nodes() + p_test.remove_input_nodes() p_ref.perform_pauli_measurements() p_test.perform_pauli_measurements()