-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added visualization for qubit permutations for routed circuits (#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
- Loading branch information
1 parent
03808f4
commit 0e62198
Showing
6 changed files
with
171 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 78 additions & 0 deletions
78
cirq-core/cirq/transformers/routing/visualize_routed_circuit.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# 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, 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 _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. | ||
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} | ||
if initial_map is None: | ||
initial_map = qdict.copy() | ||
inverse_map = {v: k for k, v in initial_map.items()} | ||
|
||
def swap_print_moment() -> 'cirq.Operation': | ||
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: | ||
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] | ||
|
||
ret_circuit.append(m) | ||
if swap_in_moment: | ||
ret_circuit.append(swap_print_moment()) | ||
|
||
return ret_circuit |
89 changes: 89 additions & 0 deletions
89
cirq-core/cirq/transformers/routing/visualize_routed_circuit_test.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
# 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 pytest | ||
import cirq | ||
|
||
|
||
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)─── | ||
│ │ │ | ||
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, | ||
) | ||
|
||
# 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)───×─── | ||
│ │ | ||
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) | ||
circuit = cirq.Circuit( | ||
[ | ||
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(q[2], q[1]), cirq.SWAP(q[4], q[3]).with_tags(cirq.RoutingSwapTag()) | ||
), | ||
cirq.Moment( | ||
cirq.SWAP(q[0], q[1]).with_tags(cirq.RoutingSwapTag()), | ||
cirq.SWAP(q[3], q[2]).with_tags(cirq.RoutingSwapTag()), | ||
), | ||
cirq.Moment(cirq.CNOT(q[2], q[1])), | ||
cirq.Moment(cirq.CNOT(q[1], q[0])), | ||
] | ||
) | ||
expected_diagram = """ | ||
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) |