Skip to content

Commit 7d2dfad

Browse files
Added visualization for qubit permutations for routed circuits (quantumlib#5848)
* added circuit visualization code * made print gate private and added more tests * removed unused import and added gate to private list * addressed comments * added safeguard for wrong use of RoutingSwapTag * addressed nits * added return type for * added _SwapPrintGate again to and fixed type issue
1 parent 1da8914 commit 7d2dfad

File tree

6 files changed

+171
-0
lines changed

6 files changed

+171
-0
lines changed

cirq/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@
367367
prepare_two_qubit_state_using_cz,
368368
prepare_two_qubit_state_using_sqrt_iswap,
369369
RouteCQC,
370+
routed_circuit_with_mapping,
370371
SqrtIswapTargetGateset,
371372
single_qubit_matrix_to_gates,
372373
single_qubit_matrix_to_pauli_rotations,

cirq/ops/gate_operation_test.py

+1
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,7 @@ def all_subclasses(cls):
494494
cirq.Pauli,
495495
# Private gates.
496496
cirq.transformers.analytical_decompositions.two_qubit_to_fsim._BGate,
497+
cirq.transformers.routing.visualize_routed_circuit._SwapPrintGate,
497498
cirq.ops.raw_types._InverseCompositeGate,
498499
cirq.circuits.qasm_output.QasmTwoQubitGate,
499500
cirq.ops.MSGate,

cirq/transformers/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
LineInitialMapper,
5151
MappingManager,
5252
RouteCQC,
53+
routed_circuit_with_mapping,
5354
)
5455

5556
from cirq.transformers.target_gatesets import (

cirq/transformers/routing/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@
1818
from cirq.transformers.routing.mapping_manager import MappingManager
1919
from cirq.transformers.routing.line_initial_mapper import LineInitialMapper
2020
from cirq.transformers.routing.route_circuit_cqc import RouteCQC
21+
from cirq.transformers.routing.visualize_routed_circuit import routed_circuit_with_mapping
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Copyright 2022 The Cirq Developers
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from typing import Dict, Optional, Tuple, TYPE_CHECKING
16+
from cirq import circuits, ops
17+
18+
if TYPE_CHECKING:
19+
import cirq
20+
21+
22+
class _SwapPrintGate(ops.Gate):
23+
"""A gate that displays the string representation of each qubits on the circuit."""
24+
25+
def __init__(self, qubits: Tuple[Tuple['cirq.Qid', 'cirq.Qid'], ...]) -> None:
26+
self.qubits = qubits
27+
28+
def num_qubits(self):
29+
return len(self.qubits)
30+
31+
def _circuit_diagram_info_(self, args: 'cirq.CircuitDiagramInfoArgs') -> Tuple[str, ...]:
32+
return tuple(f'{str(q[1])}' for q in self.qubits)
33+
34+
35+
def routed_circuit_with_mapping(
36+
routed_circuit: 'cirq.AbstractCircuit',
37+
initial_map: Optional[Dict['cirq.Qid', 'cirq.Qid']] = None,
38+
) -> 'cirq.AbstractCircuit':
39+
"""Returns the same circuits with information about the permutation of qubits after each swap.
40+
41+
Args:
42+
routed_circuit: a routed circuit that potentially has inserted swaps tagged with a
43+
RoutingSwapTag.
44+
initial_map: the initial mapping from logical to physical qubits. If this is not specified
45+
then the identity mapping of the qubits in routed_circuit will be used as initial_map.
46+
47+
Raises:
48+
ValueError: if a non-SWAP gate is tagged with a RoutingSwapTag.
49+
"""
50+
all_qubits = sorted(routed_circuit.all_qubits())
51+
qdict = {q: q for q in all_qubits}
52+
if initial_map is None:
53+
initial_map = qdict.copy()
54+
inverse_map = {v: k for k, v in initial_map.items()}
55+
56+
def swap_print_moment() -> 'cirq.Operation':
57+
return _SwapPrintGate(
58+
tuple(zip(qdict.values(), [inverse_map[x] for x in qdict.values()]))
59+
).on(*all_qubits)
60+
61+
ret_circuit = circuits.Circuit(swap_print_moment())
62+
for m in routed_circuit:
63+
swap_in_moment = False
64+
for op in m:
65+
if ops.RoutingSwapTag() in op.tags:
66+
if type(op.gate) != ops.swap_gates.SwapPowGate:
67+
raise ValueError(
68+
"Invalid circuit. A non-SWAP gate cannot be tagged a RoutingSwapTag."
69+
)
70+
swap_in_moment = True
71+
q1, q2 = op.qubits
72+
qdict[q1], qdict[q2] = qdict[q2], qdict[q1]
73+
74+
ret_circuit.append(m)
75+
if swap_in_moment:
76+
ret_circuit.append(swap_print_moment())
77+
78+
return ret_circuit
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Copyright 2022 The Cirq Developers
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import pytest
16+
import cirq
17+
18+
19+
def test_routed_circuit_with_mapping_simple():
20+
q = cirq.LineQubit.range(2)
21+
circuit = cirq.Circuit([cirq.Moment(cirq.SWAP(q[0], q[1]).with_tags(cirq.RoutingSwapTag()))])
22+
expected_diagram = """
23+
0: ───q(0)───×[cirq.RoutingSwapTag()]───q(1)───
24+
│ │ │
25+
1: ───q(1)───×──────────────────────────q(0)───"""
26+
cirq.testing.assert_has_diagram(cirq.routed_circuit_with_mapping(circuit), expected_diagram)
27+
28+
expected_diagram_with_initial_mapping = """
29+
0: ───a───×[cirq.RoutingSwapTag()]───b───
30+
│ │ │
31+
1: ───b───×──────────────────────────a───"""
32+
cirq.testing.assert_has_diagram(
33+
cirq.routed_circuit_with_mapping(
34+
circuit, {cirq.NamedQubit("a"): q[0], cirq.NamedQubit("b"): q[1]}
35+
),
36+
expected_diagram_with_initial_mapping,
37+
)
38+
39+
# if swap is untagged should not affect the mapping
40+
circuit = cirq.Circuit([cirq.Moment(cirq.SWAP(q[0], q[1]))])
41+
expected_diagram = """
42+
0: ───q(0)───×───
43+
│ │
44+
1: ───q(1)───×───"""
45+
cirq.testing.assert_has_diagram(cirq.routed_circuit_with_mapping(circuit), expected_diagram)
46+
47+
circuit = cirq.Circuit(
48+
[
49+
cirq.Moment(cirq.X(q[0]).with_tags(cirq.RoutingSwapTag())),
50+
cirq.Moment(cirq.SWAP(q[0], q[1])),
51+
]
52+
)
53+
with pytest.raises(
54+
ValueError, match="Invalid circuit. A non-SWAP gate cannot be tagged a RoutingSwapTag."
55+
):
56+
cirq.routed_circuit_with_mapping(circuit)
57+
58+
59+
def test_routed_circuit_with_mapping_multi_swaps():
60+
q = cirq.LineQubit.range(6)
61+
circuit = cirq.Circuit(
62+
[
63+
cirq.Moment(cirq.CNOT(q[3], q[4])),
64+
cirq.Moment(cirq.CNOT(q[5], q[4]), cirq.CNOT(q[2], q[3])),
65+
cirq.Moment(
66+
cirq.CNOT(q[2], q[1]), cirq.SWAP(q[4], q[3]).with_tags(cirq.RoutingSwapTag())
67+
),
68+
cirq.Moment(
69+
cirq.SWAP(q[0], q[1]).with_tags(cirq.RoutingSwapTag()),
70+
cirq.SWAP(q[3], q[2]).with_tags(cirq.RoutingSwapTag()),
71+
),
72+
cirq.Moment(cirq.CNOT(q[2], q[1])),
73+
cirq.Moment(cirq.CNOT(q[1], q[0])),
74+
]
75+
)
76+
expected_diagram = """
77+
0: ───q(0)──────────────────────────────────────q(0)───×[cirq.RoutingSwapTag()]───q(1)───────X───
78+
│ │ │ │ │
79+
1: ───q(1)───────────X──────────────────────────q(1)───×──────────────────────────q(0)───X───@───
80+
│ │ │ │ │
81+
2: ───q(2)───────@───@──────────────────────────q(2)───×──────────────────────────q(4)───@───────
82+
│ │ │ │ │
83+
3: ───q(3)───@───X───×──────────────────────────q(4)───×[cirq.RoutingSwapTag()]───q(2)───────────
84+
│ │ │ │ │
85+
4: ───q(4)───X───X───×[cirq.RoutingSwapTag()]───q(3)──────────────────────────────q(3)───────────
86+
│ │ │ │
87+
5: ───q(5)───────@──────────────────────────────q(5)──────────────────────────────q(5)───────────
88+
"""
89+
cirq.testing.assert_has_diagram(cirq.routed_circuit_with_mapping(circuit), expected_diagram)

0 commit comments

Comments
 (0)