From 32f2e9fbfec71ee10f6485c9e047ac71b6a2698a Mon Sep 17 00:00:00 2001 From: Ammar Eltigani Date: Wed, 31 Aug 2022 10:38:15 -0700 Subject: [PATCH 1/8] added circuit visualization code --- cirq-core/cirq/__init__.py | 1 + cirq-core/cirq/transformers/__init__.py | 1 + .../cirq/transformers/routing/__init__.py | 1 + .../routing/visualize_routed_circuit.py | 72 +++++++++++++++++++ .../routing/visualize_routed_circuit_test.py | 38 ++++++++++ 5 files changed, 113 insertions(+) create mode 100644 cirq-core/cirq/transformers/routing/visualize_routed_circuit.py create mode 100644 cirq-core/cirq/transformers/routing/visualize_routed_circuit_test.py diff --git a/cirq-core/cirq/__init__.py b/cirq-core/cirq/__init__.py index a2ebf156136..b6d4a4a0b5f 100644 --- a/cirq-core/cirq/__init__.py +++ b/cirq-core/cirq/__init__.py @@ -366,6 +366,7 @@ parameterized_2q_op_to_sqrt_iswap_operations, prepare_two_qubit_state_using_cz, prepare_two_qubit_state_using_sqrt_iswap, + routed_circuit_with_mapping, SqrtIswapTargetGateset, single_qubit_matrix_to_gates, single_qubit_matrix_to_pauli_rotations, diff --git a/cirq-core/cirq/transformers/__init__.py b/cirq-core/cirq/transformers/__init__.py index f45aa8c872e..77e39e2fe20 100644 --- a/cirq-core/cirq/transformers/__init__.py +++ b/cirq-core/cirq/transformers/__init__.py @@ -50,6 +50,7 @@ HardCodedInitialMapper, LineInitialMapper, MappingManager, + routed_circuit_with_mapping, ) from cirq.transformers.target_gatesets import ( diff --git a/cirq-core/cirq/transformers/routing/__init__.py b/cirq-core/cirq/transformers/routing/__init__.py index ba1962b28d0..8770452ae3d 100644 --- a/cirq-core/cirq/transformers/routing/__init__.py +++ b/cirq-core/cirq/transformers/routing/__init__.py @@ -17,3 +17,4 @@ from cirq.transformers.routing.initial_mapper import AbstractInitialMapper, HardCodedInitialMapper from cirq.transformers.routing.mapping_manager import MappingManager from cirq.transformers.routing.line_initial_mapper import LineInitialMapper +from cirq.transformers.routing.visualize_routed_circuit import routed_circuit_with_mapping diff --git a/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py b/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py new file mode 100644 index 00000000000..fe70b8384ef --- /dev/null +++ b/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py @@ -0,0 +1,72 @@ +# Copyright 2022 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Dict, Optional, Sequence, Tuple, TYPE_CHECKING +from cirq import circuits, ops + +if TYPE_CHECKING: + import cirq + + +class SwapPrintGate(ops.Gate): + """A gate that displays the string representation of each qubits on the circuit.""" + def __init__(self, qubits: Tuple[Tuple['cirq.Qid', 'cirq.Qid'], ...]) -> None: + self.qubits = qubits + + def num_qubits(self): + return len(self.qubits) + + def _decompose_(self, qubits: Sequence['cirq.Qid']) -> 'cirq.OP_TREE': + return ops.I.on_each(*qubits) + + def _circuit_diagram_info_(self, args: 'cirq.CircuitDiagramInfoArgs') -> Tuple[str, ...]: + return tuple(f'{str(q[1])}' for q in self.qubits) + + +def routed_circuit_with_mapping( + routed_circuit: 'cirq.AbstractCircuit', + initial_map: Optional[Dict['cirq.Qid', 'cirq.Qid']] = None, + ) -> 'cirq.AbstractCircuit': + """Returns the same circuits with information about the permutation of qubits after each swap. + + Args: + routed_circuit: a routed circuit that potentially has inserted swaps tagged with a + RoutingSwapTag. + initial_map: the initial mapping from logical to physical qubits. If this is not specified + then the identity mapping of the qubits in routed_circuit will be used as initial_map. + """ + all_qubits = sorted(routed_circuit.all_qubits()) + qdict = {q : q for q in all_qubits} + if initial_map is None: + initial_map = qdict.copy() + inverse_map = {v : k for k, v in initial_map.items()} + + ret_circuit = circuits.Circuit( + SwapPrintGate( + tuple(zip(qdict.values(),[inverse_map[x] for x in qdict.values()])) + ).on(*all_qubits) + ) + for m in routed_circuit: + # Find the mapping at after this moment + for op in m: + if ops.RoutingSwapTag() in op.tags: + q1, q2 = op.qubits + qdict[q1], qdict[q2] = qdict[q2], qdict[q1] + ret_circuit.append(m) + ret_circuit.append( + SwapPrintGate( + tuple(zip(qdict.values(),[inverse_map[x] for x in qdict.values()])) + ).on(*all_qubits) + ) + return ret_circuit diff --git a/cirq-core/cirq/transformers/routing/visualize_routed_circuit_test.py b/cirq-core/cirq/transformers/routing/visualize_routed_circuit_test.py new file mode 100644 index 00000000000..722a5cd438a --- /dev/null +++ b/cirq-core/cirq/transformers/routing/visualize_routed_circuit_test.py @@ -0,0 +1,38 @@ +# Copyright 2022 The Cirq Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import cirq + +def test_routed_circuit_with_mapping(): + q = cirq.LineQubit.range(2) + circuit = cirq.Circuit([ + cirq.Moment(cirq.SWAP(q[0], q[1]).with_tags(cirq.RoutingSwapTag())) + ]) + expected_diagram = """0: ───q(0)───×[cirq.RoutingSwapTag()]───q(1)─── + │ │ │ +1: ───q(1)───×──────────────────────────q(0)───""" + cirq.testing.assert_has_diagram( + cirq.routed_circuit_with_mapping(circuit), + expected_diagram) + + expected_diagram_with_initial_mapping = """0: ───a───×[cirq.RoutingSwapTag()]───b─── + │ │ │ +1: ───b───×──────────────────────────a───""" + cirq.testing.assert_has_diagram( + cirq.routed_circuit_with_mapping( + circuit, + {cirq.NamedQubit("a"): q[0], cirq.NamedQubit("b"): q[1]} + ), + expected_diagram_with_initial_mapping + ) From 53526f5e19731a6bb88b745a98381b35b57c1925 Mon Sep 17 00:00:00 2001 From: Ammar Eltigani Date: Wed, 31 Aug 2022 11:21:14 -0700 Subject: [PATCH 2/8] made print gate private and added more tests --- .../routing/visualize_routed_circuit.py | 24 +++---- .../routing/visualize_routed_circuit_test.py | 72 +++++++++++++++---- 2 files changed, 71 insertions(+), 25 deletions(-) diff --git a/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py b/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py index fe70b8384ef..ea3d4443ec2 100644 --- a/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py +++ b/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py @@ -19,17 +19,15 @@ import cirq -class SwapPrintGate(ops.Gate): +class _SwapPrintGate(ops.Gate): """A gate that displays the string representation of each qubits on the circuit.""" + def __init__(self, qubits: Tuple[Tuple['cirq.Qid', 'cirq.Qid'], ...]) -> None: self.qubits = qubits def num_qubits(self): return len(self.qubits) - def _decompose_(self, qubits: Sequence['cirq.Qid']) -> 'cirq.OP_TREE': - return ops.I.on_each(*qubits) - def _circuit_diagram_info_(self, args: 'cirq.CircuitDiagramInfoArgs') -> Tuple[str, ...]: return tuple(f'{str(q[1])}' for q in self.qubits) @@ -37,7 +35,7 @@ def _circuit_diagram_info_(self, args: 'cirq.CircuitDiagramInfoArgs') -> Tuple[s def routed_circuit_with_mapping( routed_circuit: 'cirq.AbstractCircuit', initial_map: Optional[Dict['cirq.Qid', 'cirq.Qid']] = None, - ) -> 'cirq.AbstractCircuit': +) -> 'cirq.AbstractCircuit': """Returns the same circuits with information about the permutation of qubits after each swap. Args: @@ -47,16 +45,16 @@ def routed_circuit_with_mapping( then the identity mapping of the qubits in routed_circuit will be used as initial_map. """ all_qubits = sorted(routed_circuit.all_qubits()) - qdict = {q : q for q in all_qubits} + qdict = {q: q for q in all_qubits} if initial_map is None: initial_map = qdict.copy() - inverse_map = {v : k for k, v in initial_map.items()} + inverse_map = {v: k for k, v in initial_map.items()} ret_circuit = circuits.Circuit( - SwapPrintGate( - tuple(zip(qdict.values(),[inverse_map[x] for x in qdict.values()])) - ).on(*all_qubits) + _SwapPrintGate(tuple(zip(qdict.values(), [inverse_map[x] for x in qdict.values()]))).on( + *all_qubits ) + ) for m in routed_circuit: # Find the mapping at after this moment for op in m: @@ -65,8 +63,8 @@ def routed_circuit_with_mapping( qdict[q1], qdict[q2] = qdict[q2], qdict[q1] ret_circuit.append(m) ret_circuit.append( - SwapPrintGate( - tuple(zip(qdict.values(),[inverse_map[x] for x in qdict.values()])) - ).on(*all_qubits) + _SwapPrintGate(tuple(zip(qdict.values(), [inverse_map[x] for x in qdict.values()]))).on( + *all_qubits ) + ) return ret_circuit diff --git a/cirq-core/cirq/transformers/routing/visualize_routed_circuit_test.py b/cirq-core/cirq/transformers/routing/visualize_routed_circuit_test.py index 722a5cd438a..8f14247821e 100644 --- a/cirq-core/cirq/transformers/routing/visualize_routed_circuit_test.py +++ b/cirq-core/cirq/transformers/routing/visualize_routed_circuit_test.py @@ -14,25 +14,73 @@ import cirq -def test_routed_circuit_with_mapping(): + +def test_routed_circuit_with_mapping_simple(): q = cirq.LineQubit.range(2) - circuit = cirq.Circuit([ - cirq.Moment(cirq.SWAP(q[0], q[1]).with_tags(cirq.RoutingSwapTag())) - ]) - expected_diagram = """0: ───q(0)───×[cirq.RoutingSwapTag()]───q(1)─── + circuit = cirq.Circuit([cirq.Moment(cirq.SWAP(q[0], q[1]).with_tags(cirq.RoutingSwapTag()))]) + expected_diagram = """ +0: ───q(0)───×[cirq.RoutingSwapTag()]───q(1)─── │ │ │ 1: ───q(1)───×──────────────────────────q(0)───""" - cirq.testing.assert_has_diagram( - cirq.routed_circuit_with_mapping(circuit), - expected_diagram) + cirq.testing.assert_has_diagram(cirq.routed_circuit_with_mapping(circuit), expected_diagram) - expected_diagram_with_initial_mapping = """0: ───a───×[cirq.RoutingSwapTag()]───b─── + expected_diagram_with_initial_mapping = """ +0: ───a───×[cirq.RoutingSwapTag()]───b─── │ │ │ 1: ───b───×──────────────────────────a───""" cirq.testing.assert_has_diagram( cirq.routed_circuit_with_mapping( - circuit, - {cirq.NamedQubit("a"): q[0], cirq.NamedQubit("b"): q[1]} + circuit, {cirq.NamedQubit("a"): q[0], cirq.NamedQubit("b"): q[1]} ), - expected_diagram_with_initial_mapping + expected_diagram_with_initial_mapping, + ) + + # if swap is untagged should not affect the mapping + circuit = cirq.Circuit([cirq.Moment(cirq.SWAP(q[0], q[1]))]) + expected_diagram = """ +0: ───q(0)───×───q(0)─── + │ │ │ +1: ───q(1)───×───q(1)───""" + cirq.testing.assert_has_diagram(cirq.routed_circuit_with_mapping(circuit), expected_diagram) + + +def test_routed_circuit_with_mapping_multi_swaps(): + circuit = cirq.Circuit( + [ + cirq.Moment(cirq.CNOT(cirq.GridQubit(6, 4), cirq.GridQubit(7, 4))), + cirq.Moment( + cirq.CNOT(cirq.GridQubit(8, 4), cirq.GridQubit(7, 4)), + cirq.CNOT(cirq.GridQubit(5, 4), cirq.GridQubit(6, 4)), + ), + cirq.Moment( + cirq.CNOT(cirq.GridQubit(5, 4), cirq.GridQubit(4, 4)), + cirq.SWAP(cirq.GridQubit(7, 4), cirq.GridQubit(6, 4)).with_tags( + cirq.RoutingSwapTag() + ), + ), + cirq.Moment( + cirq.SWAP(cirq.GridQubit(3, 4), cirq.GridQubit(4, 4)).with_tags( + cirq.RoutingSwapTag() + ), + cirq.SWAP(cirq.GridQubit(6, 4), cirq.GridQubit(5, 4)).with_tags( + cirq.RoutingSwapTag() + ), + ), + cirq.Moment(cirq.CNOT(cirq.GridQubit(5, 4), cirq.GridQubit(4, 4))), + cirq.Moment(cirq.CNOT(cirq.GridQubit(4, 4), cirq.GridQubit(3, 4))), + ] ) + expected_diagram = """ +(3, 4): ───q(3, 4)───────q(3, 4)───────q(3, 4)──────────────────────────────q(3, 4)───×[cirq.RoutingSwapTag()]───q(4, 4)───────q(4, 4)───X───q(4, 4)─── + │ │ │ │ │ │ │ │ │ +(4, 4): ───q(4, 4)───────q(4, 4)───────q(4, 4)───X──────────────────────────q(4, 4)───×──────────────────────────q(3, 4)───X───q(3, 4)───@───q(3, 4)─── + │ │ │ │ │ │ │ │ │ +(5, 4): ───q(5, 4)───────q(5, 4)───@───q(5, 4)───@──────────────────────────q(5, 4)───×──────────────────────────q(7, 4)───@───q(7, 4)───────q(7, 4)─── + │ │ │ │ │ │ │ │ │ +(6, 4): ───q(6, 4)───@───q(6, 4)───X───q(6, 4)───×──────────────────────────q(7, 4)───×[cirq.RoutingSwapTag()]───q(5, 4)───────q(5, 4)───────q(5, 4)─── + │ │ │ │ │ │ │ │ │ +(7, 4): ───q(7, 4)───X───q(7, 4)───X───q(7, 4)───×[cirq.RoutingSwapTag()]───q(6, 4)──────────────────────────────q(6, 4)───────q(6, 4)───────q(6, 4)─── + │ │ │ │ │ │ │ │ +(8, 4): ───q(8, 4)───────q(8, 4)───@───q(8, 4)──────────────────────────────q(8, 4)──────────────────────────────q(8, 4)───────q(8, 4)───────q(8, 4)─── +""" + cirq.testing.assert_has_diagram(cirq.routed_circuit_with_mapping(circuit), expected_diagram) From af704220d6f001b87c8e61e1bb1380b14440d6fc Mon Sep 17 00:00:00 2001 From: Ammar Eltigani Date: Wed, 31 Aug 2022 11:39:14 -0700 Subject: [PATCH 3/8] removed unused import and added gate to private list --- cirq-core/cirq/ops/gate_operation_test.py | 1 + cirq-core/cirq/transformers/routing/visualize_routed_circuit.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cirq-core/cirq/ops/gate_operation_test.py b/cirq-core/cirq/ops/gate_operation_test.py index f2f888c8902..bfd5e1d156d 100644 --- a/cirq-core/cirq/ops/gate_operation_test.py +++ b/cirq-core/cirq/ops/gate_operation_test.py @@ -494,6 +494,7 @@ def all_subclasses(cls): cirq.Pauli, # Private gates. cirq.transformers.analytical_decompositions.two_qubit_to_fsim._BGate, + cirq.transformers.routing.visualize_routed_circuit._SwapPrintGate, cirq.ops.raw_types._InverseCompositeGate, cirq.circuits.qasm_output.QasmTwoQubitGate, cirq.ops.MSGate, diff --git a/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py b/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py index ea3d4443ec2..011bd4512c7 100644 --- a/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py +++ b/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict, Optional, Sequence, Tuple, TYPE_CHECKING +from typing import Dict, Optional, Tuple, TYPE_CHECKING from cirq import circuits, ops if TYPE_CHECKING: From c74e045239da803fbe2c10d95a60caa9d6628aaa Mon Sep 17 00:00:00 2001 From: Ammar Eltigani Date: Wed, 31 Aug 2022 15:26:18 -0700 Subject: [PATCH 4/8] addressed comments --- .../routing/visualize_routed_circuit.py | 16 +++--- .../routing/visualize_routed_circuit_test.py | 53 ++++++++----------- 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py b/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py index 011bd4512c7..241778521d3 100644 --- a/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py +++ b/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py @@ -56,15 +56,19 @@ def routed_circuit_with_mapping( ) ) for m in routed_circuit: - # Find the mapping at after this moment + swap_in_moment = False for op in m: - if ops.RoutingSwapTag() in op.tags: + if ops.RoutingSwapTag() in op.tags and type(op.gate) == ops.swap_gates.SwapPowGate: + swap_in_moment = True q1, q2 = op.qubits qdict[q1], qdict[q2] = qdict[q2], qdict[q1] + ret_circuit.append(m) - ret_circuit.append( - _SwapPrintGate(tuple(zip(qdict.values(), [inverse_map[x] for x in qdict.values()]))).on( - *all_qubits + if swap_in_moment: + ret_circuit.append( + _SwapPrintGate( + tuple(zip(qdict.values(), [inverse_map[x] for x in qdict.values()])) + ).on(*all_qubits) ) - ) + return ret_circuit diff --git a/cirq-core/cirq/transformers/routing/visualize_routed_circuit_test.py b/cirq-core/cirq/transformers/routing/visualize_routed_circuit_test.py index 8f14247821e..1a96035cdf6 100644 --- a/cirq-core/cirq/transformers/routing/visualize_routed_circuit_test.py +++ b/cirq-core/cirq/transformers/routing/visualize_routed_circuit_test.py @@ -38,49 +38,40 @@ def test_routed_circuit_with_mapping_simple(): # if swap is untagged should not affect the mapping circuit = cirq.Circuit([cirq.Moment(cirq.SWAP(q[0], q[1]))]) expected_diagram = """ -0: ───q(0)───×───q(0)─── - │ │ │ -1: ───q(1)───×───q(1)───""" +0: ───q(0)───×─── + │ │ +1: ───q(1)───×───""" cirq.testing.assert_has_diagram(cirq.routed_circuit_with_mapping(circuit), expected_diagram) def test_routed_circuit_with_mapping_multi_swaps(): + q = cirq.LineQubit.range(6) circuit = cirq.Circuit( [ - cirq.Moment(cirq.CNOT(cirq.GridQubit(6, 4), cirq.GridQubit(7, 4))), + cirq.Moment(cirq.CNOT(q[3], q[4])), + cirq.Moment(cirq.CNOT(q[5], q[4]), cirq.CNOT(q[2], q[3])), cirq.Moment( - cirq.CNOT(cirq.GridQubit(8, 4), cirq.GridQubit(7, 4)), - cirq.CNOT(cirq.GridQubit(5, 4), cirq.GridQubit(6, 4)), + cirq.CNOT(q[2], q[1]), cirq.SWAP(q[4], q[3]).with_tags(cirq.RoutingSwapTag()) ), cirq.Moment( - cirq.CNOT(cirq.GridQubit(5, 4), cirq.GridQubit(4, 4)), - cirq.SWAP(cirq.GridQubit(7, 4), cirq.GridQubit(6, 4)).with_tags( - cirq.RoutingSwapTag() - ), + cirq.SWAP(q[0], q[1]).with_tags(cirq.RoutingSwapTag()), + cirq.SWAP(q[3], q[2]).with_tags(cirq.RoutingSwapTag()), ), - cirq.Moment( - cirq.SWAP(cirq.GridQubit(3, 4), cirq.GridQubit(4, 4)).with_tags( - cirq.RoutingSwapTag() - ), - cirq.SWAP(cirq.GridQubit(6, 4), cirq.GridQubit(5, 4)).with_tags( - cirq.RoutingSwapTag() - ), - ), - cirq.Moment(cirq.CNOT(cirq.GridQubit(5, 4), cirq.GridQubit(4, 4))), - cirq.Moment(cirq.CNOT(cirq.GridQubit(4, 4), cirq.GridQubit(3, 4))), + cirq.Moment(cirq.CNOT(q[2], q[1])), + cirq.Moment(cirq.CNOT(q[1], q[0])), ] ) expected_diagram = """ -(3, 4): ───q(3, 4)───────q(3, 4)───────q(3, 4)──────────────────────────────q(3, 4)───×[cirq.RoutingSwapTag()]───q(4, 4)───────q(4, 4)───X───q(4, 4)─── - │ │ │ │ │ │ │ │ │ -(4, 4): ───q(4, 4)───────q(4, 4)───────q(4, 4)───X──────────────────────────q(4, 4)───×──────────────────────────q(3, 4)───X───q(3, 4)───@───q(3, 4)─── - │ │ │ │ │ │ │ │ │ -(5, 4): ───q(5, 4)───────q(5, 4)───@───q(5, 4)───@──────────────────────────q(5, 4)───×──────────────────────────q(7, 4)───@───q(7, 4)───────q(7, 4)─── - │ │ │ │ │ │ │ │ │ -(6, 4): ───q(6, 4)───@───q(6, 4)───X───q(6, 4)───×──────────────────────────q(7, 4)───×[cirq.RoutingSwapTag()]───q(5, 4)───────q(5, 4)───────q(5, 4)─── - │ │ │ │ │ │ │ │ │ -(7, 4): ───q(7, 4)───X───q(7, 4)───X───q(7, 4)───×[cirq.RoutingSwapTag()]───q(6, 4)──────────────────────────────q(6, 4)───────q(6, 4)───────q(6, 4)─── - │ │ │ │ │ │ │ │ -(8, 4): ───q(8, 4)───────q(8, 4)───@───q(8, 4)──────────────────────────────q(8, 4)──────────────────────────────q(8, 4)───────q(8, 4)───────q(8, 4)─── +0: ───q(0)──────────────────────────────────────q(0)───×[cirq.RoutingSwapTag()]───q(1)───────X─── + │ │ │ │ │ +1: ───q(1)───────────X──────────────────────────q(1)───×──────────────────────────q(0)───X───@─── + │ │ │ │ │ +2: ───q(2)───────@───@──────────────────────────q(2)───×──────────────────────────q(4)───@─────── + │ │ │ │ │ +3: ───q(3)───@───X───×──────────────────────────q(4)───×[cirq.RoutingSwapTag()]───q(2)─────────── + │ │ │ │ │ +4: ───q(4)───X───X───×[cirq.RoutingSwapTag()]───q(3)──────────────────────────────q(3)─────────── + │ │ │ │ +5: ───q(5)───────@──────────────────────────────q(5)──────────────────────────────q(5)─────────── """ cirq.testing.assert_has_diagram(cirq.routed_circuit_with_mapping(circuit), expected_diagram) From 2bf5a0cd9d92d75a664ab869af0410919f52f4b9 Mon Sep 17 00:00:00 2001 From: Ammar Eltigani Date: Wed, 31 Aug 2022 15:48:07 -0700 Subject: [PATCH 5/8] added safeguard for wrong use of RoutingSwapTag --- .../transformers/routing/visualize_routed_circuit.py | 9 ++++++++- .../routing/visualize_routed_circuit_test.py | 12 ++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py b/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py index 241778521d3..4b23f5b6367 100644 --- a/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py +++ b/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py @@ -43,6 +43,9 @@ def routed_circuit_with_mapping( RoutingSwapTag. initial_map: the initial mapping from logical to physical qubits. If this is not specified then the identity mapping of the qubits in routed_circuit will be used as initial_map. + + Raises: + ValueError: if a non-SWAP gate is tagged with a RoutingSwapTag. """ all_qubits = sorted(routed_circuit.all_qubits()) qdict = {q: q for q in all_qubits} @@ -58,7 +61,11 @@ def routed_circuit_with_mapping( for m in routed_circuit: swap_in_moment = False for op in m: - if ops.RoutingSwapTag() in op.tags and type(op.gate) == ops.swap_gates.SwapPowGate: + if ops.RoutingSwapTag() in op.tags: + if type(op.gate) != ops.swap_gates.SwapPowGate: + raise ValueError( + "Invalid circuit. A non-SWAP gate cannot be tagged a RoutingSwapTag." + ) swap_in_moment = True q1, q2 = op.qubits qdict[q1], qdict[q2] = qdict[q2], qdict[q1] diff --git a/cirq-core/cirq/transformers/routing/visualize_routed_circuit_test.py b/cirq-core/cirq/transformers/routing/visualize_routed_circuit_test.py index 1a96035cdf6..7ab797ed10c 100644 --- a/cirq-core/cirq/transformers/routing/visualize_routed_circuit_test.py +++ b/cirq-core/cirq/transformers/routing/visualize_routed_circuit_test.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import pytest import cirq @@ -43,6 +44,17 @@ def test_routed_circuit_with_mapping_simple(): 1: ───q(1)───×───""" cirq.testing.assert_has_diagram(cirq.routed_circuit_with_mapping(circuit), expected_diagram) + circuit = cirq.Circuit( + [ + cirq.Moment(cirq.X(q[0]).with_tags(cirq.RoutingSwapTag())), + cirq.Moment(cirq.SWAP(q[0], q[1])), + ] + ) + with pytest.raises( + ValueError, match="Invalid circuit. A non-SWAP gate cannot be tagged a RoutingSwapTag." + ): + cirq.routed_circuit_with_mapping(circuit) + def test_routed_circuit_with_mapping_multi_swaps(): q = cirq.LineQubit.range(6) From 7db4cd801e3fe37844e0c302b368b8895eaaed49 Mon Sep 17 00:00:00 2001 From: ammareltigani Date: Mon, 12 Sep 2022 18:44:27 +0100 Subject: [PATCH 6/8] addressed nits --- cirq-core/cirq/ops/gate_operation_test.py | 1 - .../routing/visualize_routed_circuit.py | 17 +++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/cirq-core/cirq/ops/gate_operation_test.py b/cirq-core/cirq/ops/gate_operation_test.py index bfd5e1d156d..f2f888c8902 100644 --- a/cirq-core/cirq/ops/gate_operation_test.py +++ b/cirq-core/cirq/ops/gate_operation_test.py @@ -494,7 +494,6 @@ def all_subclasses(cls): cirq.Pauli, # Private gates. cirq.transformers.analytical_decompositions.two_qubit_to_fsim._BGate, - cirq.transformers.routing.visualize_routed_circuit._SwapPrintGate, cirq.ops.raw_types._InverseCompositeGate, cirq.circuits.qasm_output.QasmTwoQubitGate, cirq.ops.MSGate, diff --git a/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py b/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py index 4b23f5b6367..6cc0f38730e 100644 --- a/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py +++ b/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py @@ -53,11 +53,12 @@ def routed_circuit_with_mapping( initial_map = qdict.copy() inverse_map = {v: k for k, v in initial_map.items()} - ret_circuit = circuits.Circuit( - _SwapPrintGate(tuple(zip(qdict.values(), [inverse_map[x] for x in qdict.values()]))).on( - *all_qubits - ) - ) + def swap_print_moment(): + return _SwapPrintGate( + tuple(zip(qdict.values(), [inverse_map[x] for x in qdict.values()])) + ).on(*all_qubits) + + ret_circuit = circuits.Circuit(swap_print_moment()) for m in routed_circuit: swap_in_moment = False for op in m: @@ -72,10 +73,6 @@ def routed_circuit_with_mapping( ret_circuit.append(m) if swap_in_moment: - ret_circuit.append( - _SwapPrintGate( - tuple(zip(qdict.values(), [inverse_map[x] for x in qdict.values()])) - ).on(*all_qubits) - ) + ret_circuit.append(swap_print_moment()) return ret_circuit From 3e2fd9e84d9b930e82519d6782485959ee2d92a9 Mon Sep 17 00:00:00 2001 From: ammareltigani Date: Mon, 12 Sep 2022 18:46:51 +0100 Subject: [PATCH 7/8] added return type for --- cirq-core/cirq/transformers/routing/visualize_routed_circuit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py b/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py index 6cc0f38730e..700b00d4d17 100644 --- a/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py +++ b/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py @@ -53,7 +53,7 @@ def routed_circuit_with_mapping( initial_map = qdict.copy() inverse_map = {v: k for k, v in initial_map.items()} - def swap_print_moment(): + def swap_print_moment() -> _SwapPrintGate: return _SwapPrintGate( tuple(zip(qdict.values(), [inverse_map[x] for x in qdict.values()])) ).on(*all_qubits) From ae1ef4b256e7583d55de6f7ce98f0081d2e7e186 Mon Sep 17 00:00:00 2001 From: ammareltigani Date: Mon, 12 Sep 2022 19:18:47 +0100 Subject: [PATCH 8/8] added _SwapPrintGate again to and fixed type issue --- cirq-core/cirq/ops/gate_operation_test.py | 1 + cirq-core/cirq/transformers/routing/visualize_routed_circuit.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cirq-core/cirq/ops/gate_operation_test.py b/cirq-core/cirq/ops/gate_operation_test.py index f2f888c8902..bfd5e1d156d 100644 --- a/cirq-core/cirq/ops/gate_operation_test.py +++ b/cirq-core/cirq/ops/gate_operation_test.py @@ -494,6 +494,7 @@ def all_subclasses(cls): cirq.Pauli, # Private gates. cirq.transformers.analytical_decompositions.two_qubit_to_fsim._BGate, + cirq.transformers.routing.visualize_routed_circuit._SwapPrintGate, cirq.ops.raw_types._InverseCompositeGate, cirq.circuits.qasm_output.QasmTwoQubitGate, cirq.ops.MSGate, diff --git a/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py b/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py index 700b00d4d17..05a3ba71323 100644 --- a/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py +++ b/cirq-core/cirq/transformers/routing/visualize_routed_circuit.py @@ -53,7 +53,7 @@ def routed_circuit_with_mapping( initial_map = qdict.copy() inverse_map = {v: k for k, v in initial_map.items()} - def swap_print_moment() -> _SwapPrintGate: + def swap_print_moment() -> 'cirq.Operation': return _SwapPrintGate( tuple(zip(qdict.values(), [inverse_map[x] for x in qdict.values()])) ).on(*all_qubits)