qiskit/test/python/compiler/test_transpiler.py

3943 lines
143 KiB
Python

# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2024.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""Tests basic functionality of the transpile function"""
import copy
import io
import math
import os
import sys
from logging import StreamHandler, getLogger
from unittest.mock import patch
import numpy as np
import rustworkx as rx
from ddt import data, ddt, unpack
from qiskit import (
ClassicalRegister,
QuantumCircuit,
QuantumRegister,
pulse,
qasm3,
qpy,
)
from qiskit.circuit import (
Clbit,
ControlFlowOp,
ForLoopOp,
Gate,
IfElseOp,
Parameter,
Qubit,
SwitchCaseOp,
WhileLoopOp,
)
from qiskit.circuit.classical import expr, types
from qiskit.circuit.annotated_operation import (
AnnotatedOperation,
InverseModifier,
ControlModifier,
PowerModifier,
)
from qiskit.circuit.delay import Delay
from qiskit.circuit.measure import Measure
from qiskit.circuit.reset import Reset
from qiskit.circuit.library import (
CXGate,
CZGate,
ECRGate,
HGate,
RXGate,
RYGate,
RZGate,
SGate,
SXGate,
SXdgGate,
SdgGate,
U2Gate,
UGate,
XGate,
ZGate,
)
from qiskit.compiler import transpile
from qiskit.converters import circuit_to_dag
from qiskit.dagcircuit import DAGOpNode, DAGOutNode
from qiskit.exceptions import QiskitError
from qiskit.providers.backend import BackendV2
from qiskit.providers.backend_compat import BackendV2Converter
from qiskit.providers.fake_provider import Fake20QV1, Fake27QPulseV1, GenericBackendV2
from qiskit.providers.basic_provider import BasicSimulator
from qiskit.providers.options import Options
from qiskit.pulse import InstructionScheduleMap
from qiskit.quantum_info import Operator, random_unitary
from qiskit.utils import parallel
from qiskit.transpiler import CouplingMap, Layout, PassManager
from qiskit.transpiler.exceptions import TranspilerError, CircuitTooWideForTarget
from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements, GateDirection, VF2PostLayout
from qiskit.transpiler.passmanager_config import PassManagerConfig
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager, level_0_pass_manager
from qiskit.transpiler.target import (
InstructionProperties,
Target,
InstructionDurations,
)
from test import QiskitTestCase, combine, slow_test # pylint: disable=wrong-import-order
from ..legacy_cmaps import MELBOURNE_CMAP, RUESCHLIKON_CMAP, TOKYO_CMAP
class CustomCX(Gate):
"""Custom CX gate representation."""
def __init__(self):
super().__init__("custom_cx", 2, [])
def _define(self):
self._definition = QuantumCircuit(2)
self._definition.cx(0, 1)
def connected_qubits(physical: int, coupling_map: CouplingMap) -> set:
"""Get the physical qubits that have a connection to this one in the coupling map."""
for component in coupling_map.connected_components():
if physical in (qubits := set(component.graph.nodes())):
return qubits
raise ValueError(f"physical qubit {physical} is not in the coupling map")
@ddt
class TestTranspile(QiskitTestCase):
"""Test transpile function."""
def test_empty_transpilation(self):
"""Test that transpiling an empty list is a no-op. Regression test of gh-7287."""
self.assertEqual(transpile([], seed_transpiler=42), [])
def test_pass_manager_none(self):
"""Test passing the default (None) pass manager to the transpiler.
It should perform the default qiskit flow:
unroll, swap_mapper, cx_direction, cx_cancellation, optimize_1q_gates
and should be equivalent to using tools.compile
"""
qr = QuantumRegister(2, "qr")
circuit = QuantumCircuit(qr)
circuit.h(qr[0])
circuit.h(qr[0])
circuit.cx(qr[0], qr[1])
circuit.cx(qr[1], qr[0])
circuit.cx(qr[0], qr[1])
circuit.cx(qr[1], qr[0])
coupling_map = [[1, 0]]
basis_gates = ["u1", "u2", "u3", "cx", "id"]
backend = BasicSimulator()
circuit2 = transpile(
circuit,
backend=backend,
coupling_map=coupling_map,
basis_gates=basis_gates,
seed_transpiler=42,
)
circuit3 = transpile(
circuit,
backend=backend,
coupling_map=coupling_map,
basis_gates=basis_gates,
seed_transpiler=42,
)
self.assertEqual(circuit2, circuit3)
@data(0, 1, 2, 3)
def test_num_processes_kwarg_concurrent_default(self, num_processes):
"""Test that num_processes kwarg works when the system default parallel is false"""
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
target = GenericBackendV2(num_qubits=27, seed=42).target
res = transpile([qc] * 3, target=target, num_processes=num_processes)
self.assertIsInstance(res, list)
for circ in res:
self.assertIsInstance(circ, QuantumCircuit)
def test_transpile_basis_gates_no_backend_no_coupling_map(self):
"""Verify transpile() works with no coupling_map or backend."""
qr = QuantumRegister(2, "qr")
circuit = QuantumCircuit(qr)
circuit.h(qr[0])
circuit.h(qr[0])
circuit.cx(qr[0], qr[1])
circuit.cx(qr[0], qr[1])
circuit.cx(qr[0], qr[1])
circuit.cx(qr[0], qr[1])
basis_gates = ["u1", "u2", "u3", "cx", "id"]
circuit2 = transpile(
circuit, basis_gates=basis_gates, optimization_level=0, seed_transpiler=42
)
resources_after = circuit2.count_ops()
self.assertEqual({"u2": 2, "cx": 4}, resources_after)
def test_transpile_non_adjacent_layout(self):
"""Transpile pipeline can handle manual layout on non-adjacent qubits.
circuit:
.. code-block:: text
┌───┐
qr_0: ┤ H ├──■──────────── -> 1
└───┘┌─┴─┐
qr_1: ─────┤ X ├──■─────── -> 2
└───┘┌─┴─┐
qr_2: ──────────┤ X ├──■── -> 3
└───┘┌─┴─┐
qr_3: ───────────────┤ X ├ -> 5
└───┘
device:
0 - 1 - 2 - 3 - 4 - 5 - 6
| | | | | |
13 - 12 - 11 - 10 - 9 - 8 - 7
"""
cmap = [
[0, 1],
[0, 14],
[1, 0],
[1, 2],
[1, 13],
[2, 1],
[2, 3],
[2, 12],
[3, 2],
[3, 4],
[3, 11],
[4, 3],
[4, 5],
[4, 10],
[5, 4],
[5, 6],
[5, 9],
[6, 5],
[6, 8],
[7, 8],
[8, 6],
[8, 7],
[8, 9],
[9, 5],
[9, 8],
[9, 10],
[10, 4],
[10, 9],
[10, 11],
[11, 3],
[11, 10],
[11, 12],
[12, 2],
[12, 11],
[12, 13],
[13, 1],
[13, 12],
[13, 14],
[14, 0],
[14, 13],
]
qr = QuantumRegister(4, "qr")
circuit = QuantumCircuit(qr)
circuit.h(qr[0])
circuit.cx(qr[0], qr[1])
circuit.cx(qr[1], qr[2])
circuit.cx(qr[2], qr[3])
backend = GenericBackendV2(
num_qubits=15, basis_gates=["ecr", "id", "rz", "sx", "x"], coupling_map=cmap, seed=42
)
initial_layout = [None, qr[0], qr[1], qr[2], None, qr[3]]
new_circuit = transpile(
circuit,
basis_gates=backend.operation_names,
coupling_map=backend.coupling_map,
initial_layout=initial_layout,
seed_transpiler=42,
)
qubit_indices = {bit: idx for idx, bit in enumerate(new_circuit.qubits)}
for instruction in new_circuit.data:
if isinstance(instruction.operation, CXGate):
self.assertIn([qubit_indices[x] for x in instruction.qubits], backend.coupling_map)
def test_transpile_qft_grid(self):
"""Transpile pipeline can handle 8-qubit QFT on 14-qubit grid."""
basis_gates = ["cx", "id", "rz", "sx", "x"]
qr = QuantumRegister(8)
circuit = QuantumCircuit(qr)
for i, q in enumerate(qr):
for j in range(i):
circuit.cp(math.pi / float(2 ** (i - j)), q, qr[j])
circuit.h(q)
new_circuit = transpile(
circuit, basis_gates=basis_gates, coupling_map=MELBOURNE_CMAP, seed_transpiler=42
)
qubit_indices = {bit: idx for idx, bit in enumerate(new_circuit.qubits)}
for instruction in new_circuit.data:
if isinstance(instruction.operation, CXGate):
self.assertIn([qubit_indices[x] for x in instruction.qubits], MELBOURNE_CMAP)
def test_already_mapped_1(self):
"""Circuit not remapped if matches topology.
See: https://github.com/Qiskit/qiskit-terra/issues/342
"""
backend = GenericBackendV2(num_qubits=16, coupling_map=RUESCHLIKON_CMAP, seed=42)
coupling_map = backend.coupling_map
basis_gates = backend.operation_names
qr = QuantumRegister(16, "qr")
cr = ClassicalRegister(16, "cr")
qc = QuantumCircuit(qr, cr)
qc.cx(qr[3], qr[14])
qc.cx(qr[5], qr[4])
qc.h(qr[9])
qc.cx(qr[9], qr[8])
qc.x(qr[11])
qc.cx(qr[3], qr[4])
qc.cx(qr[12], qr[11])
qc.cx(qr[13], qr[4])
qc.measure(qr, cr)
new_qc = transpile(
qc,
coupling_map=coupling_map,
basis_gates=basis_gates,
initial_layout=Layout.generate_trivial_layout(qr),
seed_transpiler=42,
)
qubit_indices = {bit: idx for idx, bit in enumerate(new_qc.qubits)}
cx_qubits = [instr.qubits for instr in new_qc.data if instr.operation.name == "cx"]
cx_qubits_physical = [
[qubit_indices[ctrl], qubit_indices[tgt]] for [ctrl, tgt] in cx_qubits
]
self.assertEqual(
sorted(cx_qubits_physical), [[3, 4], [3, 14], [5, 4], [9, 8], [12, 11], [13, 4]]
)
def test_already_mapped_via_layout(self):
"""Test that a manual layout that satisfies a coupling map does not get altered.
See: https://github.com/Qiskit/qiskit-terra/issues/2036
circuit:
.. code-block:: text
┌───┐ ┌───┐ ░ ┌─┐
qn_0: ┤ H ├──■────────────■──┤ H ├─░─┤M├─── -> 9
└───┘ │ │ └───┘ ░ └╥┘
qn_1: ───────┼────────────┼────────░──╫──── -> 6
│ │ ░ ║
qn_2: ───────┼────────────┼────────░──╫──── -> 5
│ │ ░ ║
qn_3: ───────┼────────────┼────────░──╫──── -> 0
│ │ ░ ║
qn_4: ───────┼────────────┼────────░──╫──── -> 1
┌───┐┌─┴─┐┌──────┐┌─┴─┐┌───┐ ░ ║ ┌─┐
qn_5: ┤ H ├┤ X ├┤ P(2) ├┤ X ├┤ H ├─░──╫─┤M├ -> 4
└───┘└───┘└──────┘└───┘└───┘ ░ ║ └╥┘
cn: 2/════════════════════════════════╩══╩═
0 1
device:
0 -- 1 -- 2 -- 3 -- 4
| |
5 -- 6 -- 7 -- 8 -- 9
| |
10 - 11 - 12 - 13 - 14
| |
15 - 16 - 17 - 18 - 19
"""
basis_gates = ["u1", "u2", "u3", "cx", "id"]
coupling_map = [
[0, 1],
[0, 5],
[1, 0],
[1, 2],
[2, 1],
[2, 3],
[3, 2],
[3, 4],
[4, 3],
[4, 9],
[5, 0],
[5, 6],
[5, 10],
[6, 5],
[6, 7],
[7, 6],
[7, 8],
[7, 12],
[8, 7],
[8, 9],
[9, 4],
[9, 8],
[9, 14],
[10, 5],
[10, 11],
[10, 15],
[11, 10],
[11, 12],
[12, 7],
[12, 11],
[12, 13],
[13, 12],
[13, 14],
[14, 9],
[14, 13],
[14, 19],
[15, 10],
[15, 16],
[16, 15],
[16, 17],
[17, 16],
[17, 18],
[18, 17],
[18, 19],
[19, 14],
[19, 18],
]
q = QuantumRegister(6, name="qn")
c = ClassicalRegister(2, name="cn")
qc = QuantumCircuit(q, c)
qc.h(q[0])
qc.h(q[5])
qc.cx(q[0], q[5])
qc.p(2, q[5])
qc.cx(q[0], q[5])
qc.h(q[0])
qc.h(q[5])
qc.barrier(q)
qc.measure(q[0], c[0])
qc.measure(q[5], c[1])
initial_layout = [
q[3],
q[4],
None,
None,
q[5],
q[2],
q[1],
None,
None,
q[0],
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
]
new_qc = transpile(
qc,
coupling_map=coupling_map,
basis_gates=basis_gates,
initial_layout=initial_layout,
seed_transpiler=42,
)
qubit_indices = {bit: idx for idx, bit in enumerate(new_qc.qubits)}
cx_qubits = [instr.qubits for instr in new_qc.data if instr.operation.name == "cx"]
cx_qubits_physical = [
[qubit_indices[ctrl], qubit_indices[tgt]] for [ctrl, tgt] in cx_qubits
]
self.assertEqual(sorted(cx_qubits_physical), [[9, 4], [9, 4]])
def test_transpile_bell(self):
"""Test Transpile Bell.
If all correct some should exists.
"""
backend = BasicSimulator()
qubit_reg = QuantumRegister(2, name="q")
clbit_reg = ClassicalRegister(2, name="c")
qc = QuantumCircuit(qubit_reg, clbit_reg, name="bell")
qc.h(qubit_reg[0])
qc.cx(qubit_reg[0], qubit_reg[1])
qc.measure(qubit_reg, clbit_reg)
circuits = transpile(qc, backend, seed_transpiler=42)
self.assertIsInstance(circuits, QuantumCircuit)
def test_transpile_bell_discrete_basis(self):
"""Test that it's possible to transpile a very simple circuit to a discrete stabilizer-like
basis. In general, we do not make any guarantees about the possibility or quality of
transpilation in these situations, but this is at least useful as a check that stuff that
_could_ be possible remains so."""
target = Target(num_qubits=2)
for one_q in [XGate(), SXGate(), SXdgGate(), SGate(), SdgGate(), ZGate()]:
target.add_instruction(one_q, {(0,): None, (1,): None})
# This is only in one direction, and not the direction we're going to attempt to lay it out
# onto, so we can test the basis translation.
target.add_instruction(ECRGate(), {(1, 0): None})
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
# Try with the initial layout in both directions to ensure we're dealing with the basis
# having only a single direction.
# Use optimization level=1 because the synthesis that runs as part of optimization at
# higher optimization levels will create intermediate gates that the transpiler currently
# lacks logic to translate to a discrete basis.
self.assertIsInstance(
transpile(
qc, target=target, initial_layout=[0, 1], seed_transpiler=42, optimization_level=1
),
QuantumCircuit,
)
self.assertIsInstance(
transpile(
qc, target=target, initial_layout=[1, 0], seed_transpiler=42, optimization_level=1
),
QuantumCircuit,
)
def test_transpile_one(self):
"""Test transpile a single circuit.
Check that the top-level `transpile` function returns
a single circuit."""
backend = BasicSimulator()
qubit_reg = QuantumRegister(2)
clbit_reg = ClassicalRegister(2)
qc = QuantumCircuit(qubit_reg, clbit_reg, name="bell")
qc.h(qubit_reg[0])
qc.cx(qubit_reg[0], qubit_reg[1])
qc.measure(qubit_reg, clbit_reg)
circuit = transpile(qc, backend, seed_transpiler=42)
self.assertIsInstance(circuit, QuantumCircuit)
def test_transpile_two(self):
"""Test transpile two circuits.
Check that the transpiler returns a list of two circuits.
"""
backend = BasicSimulator()
qubit_reg = QuantumRegister(2)
clbit_reg = ClassicalRegister(2)
qubit_reg2 = QuantumRegister(2)
clbit_reg2 = ClassicalRegister(2)
qc = QuantumCircuit(qubit_reg, clbit_reg, name="bell")
qc.h(qubit_reg[0])
qc.cx(qubit_reg[0], qubit_reg[1])
qc.measure(qubit_reg, clbit_reg)
qc_extra = QuantumCircuit(qubit_reg, qubit_reg2, clbit_reg, clbit_reg2, name="extra")
qc_extra.measure(qubit_reg, clbit_reg)
circuits = transpile([qc, qc_extra], backend, seed_transpiler=42)
self.assertIsInstance(circuits, list)
self.assertEqual(len(circuits), 2)
for circuit in circuits:
self.assertIsInstance(circuit, QuantumCircuit)
def test_transpile_singleton(self):
"""Test transpile a single-element list with a circuit.
Check that `transpile` returns a single-element list.
See https://github.com/Qiskit/qiskit-terra/issues/5260
"""
backend = BasicSimulator()
qubit_reg = QuantumRegister(2)
clbit_reg = ClassicalRegister(2)
qc = QuantumCircuit(qubit_reg, clbit_reg, name="bell")
qc.h(qubit_reg[0])
qc.cx(qubit_reg[0], qubit_reg[1])
qc.measure(qubit_reg, clbit_reg)
circuits = transpile([qc], backend, seed_transpiler=42)
self.assertIsInstance(circuits, list)
self.assertEqual(len(circuits), 1)
self.assertIsInstance(circuits[0], QuantumCircuit)
def test_mapping_correction(self):
"""Test mapping works in previous failed case."""
backend = GenericBackendV2(num_qubits=12, seed=42)
qr = QuantumRegister(name="qr", size=11)
cr = ClassicalRegister(name="qc", size=11)
circuit = QuantumCircuit(qr, cr)
circuit.u(1.564784764685993, -1.2378965763410095, 2.9746763177861713, qr[3])
circuit.u(1.2269835563676523, 1.1932982847014162, -1.5597357740824318, qr[5])
circuit.cx(qr[5], qr[3])
circuit.p(0.856768317675967, qr[3])
circuit.u(-3.3911273825190915, 0.0, 0.0, qr[5])
circuit.cx(qr[3], qr[5])
circuit.u(2.159209321625547, 0.0, 0.0, qr[5])
circuit.cx(qr[5], qr[3])
circuit.u(0.30949966910232335, 1.1706201763833217, 1.738408691990081, qr[3])
circuit.u(1.9630571407274755, -0.6818742967975088, 1.8336534616728195, qr[5])
circuit.u(1.330181833806101, 0.6003162754946363, -3.181264980452862, qr[7])
circuit.u(0.4885914820775024, 3.133297443244865, -2.794457469189904, qr[8])
circuit.cx(qr[8], qr[7])
circuit.p(2.2196187596178616, qr[7])
circuit.u(-3.152367609631023, 0.0, 0.0, qr[8])
circuit.cx(qr[7], qr[8])
circuit.u(1.2646005789809263, 0.0, 0.0, qr[8])
circuit.cx(qr[8], qr[7])
circuit.u(0.7517780502091939, 1.2828514296564781, 1.6781179605443775, qr[7])
circuit.u(0.9267400575390405, 2.0526277839695153, 2.034202361069533, qr[8])
circuit.u(2.550304293455634, 3.8250017126569698, -2.1351609599720054, qr[1])
circuit.u(0.9566260876600556, -1.1147561503064538, 2.0571590492298797, qr[4])
circuit.cx(qr[4], qr[1])
circuit.p(2.1899329069137394, qr[1])
circuit.u(-1.8371715243173294, 0.0, 0.0, qr[4])
circuit.cx(qr[1], qr[4])
circuit.u(0.4717053496327104, 0.0, 0.0, qr[4])
circuit.cx(qr[4], qr[1])
circuit.u(2.3167620677708145, -1.2337330260253256, -0.5671322899563955, qr[1])
circuit.u(1.0468499525240678, 0.8680750644809365, -1.4083720073192485, qr[4])
circuit.u(2.4204244021892807, -2.211701932616922, 3.8297006565735883, qr[10])
circuit.u(0.36660280497727255, 3.273119149343493, -1.8003362351299388, qr[6])
circuit.cx(qr[6], qr[10])
circuit.p(1.067395863586385, qr[10])
circuit.u(-0.7044917541291232, 0.0, 0.0, qr[6])
circuit.cx(qr[10], qr[6])
circuit.u(2.1830003849921527, 0.0, 0.0, qr[6])
circuit.cx(qr[6], qr[10])
circuit.u(2.1538343756723917, 2.2653381826084606, -3.550087952059485, qr[10])
circuit.u(1.307627685019188, -0.44686656993522567, -2.3238098554327418, qr[6])
circuit.u(2.2046797998462906, 0.9732961754855436, 1.8527865921467421, qr[9])
circuit.u(2.1665254613904126, -1.281337664694577, -1.2424905413631209, qr[0])
circuit.cx(qr[0], qr[9])
circuit.p(2.6209599970201007, qr[9])
circuit.u(0.04680566321901303, 0.0, 0.0, qr[0])
circuit.cx(qr[9], qr[0])
circuit.u(1.7728411151289603, 0.0, 0.0, qr[0])
circuit.cx(qr[0], qr[9])
circuit.u(2.4866395967434443, 0.48684511243566697, -3.0069186877854728, qr[9])
circuit.u(1.7369112924273789, -4.239660866163805, 1.0623389015296005, qr[0])
circuit.barrier(qr)
circuit.measure(qr, cr)
circuits = transpile(circuit, backend, seed_transpiler=42)
self.assertIsInstance(circuits, QuantumCircuit)
def test_transpiler_layout_from_intlist(self):
"""A list of ints gives layout to correctly map circuit.
virtual physical
q1_0 - 4 ---[H]---
q2_0 - 5
q2_1 - 6 ---[H]---
q3_0 - 8
q3_1 - 9
q3_2 - 10 ---[H]---
"""
qr1 = QuantumRegister(1, "qr1")
qr2 = QuantumRegister(2, "qr2")
qr3 = QuantumRegister(3, "qr3")
qc = QuantumCircuit(qr1, qr2, qr3)
qc.h(qr1[0])
qc.h(qr2[1])
qc.h(qr3[2])
layout = [4, 5, 6, 8, 9, 10]
cmap = [
[1, 0],
[1, 2],
[2, 3],
[4, 3],
[4, 10],
[5, 4],
[5, 6],
[5, 9],
[6, 8],
[7, 8],
[9, 8],
[9, 10],
[11, 3],
[11, 10],
[11, 12],
[12, 2],
[13, 1],
[13, 12],
]
new_circ = transpile(
qc,
backend=None,
coupling_map=cmap,
basis_gates=["u2"],
initial_layout=layout,
seed_transpiler=42,
)
qubit_indices = {bit: idx for idx, bit in enumerate(new_circ.qubits)}
mapped_qubits = []
for instruction in new_circ.data:
mapped_qubits.append(qubit_indices[instruction.qubits[0]])
self.assertEqual(mapped_qubits, [4, 6, 10])
def test_mapping_multi_qreg(self):
"""Test mapping works for multiple qregs."""
backend = GenericBackendV2(num_qubits=8, seed=42)
qr = QuantumRegister(3, name="qr")
qr2 = QuantumRegister(1, name="qr2")
qr3 = QuantumRegister(4, name="qr3")
cr = ClassicalRegister(3, name="cr")
qc = QuantumCircuit(qr, qr2, qr3, cr)
qc.h(qr[0])
qc.cx(qr[0], qr2[0])
qc.cx(qr[1], qr3[2])
qc.measure(qr, cr)
circuits = transpile(qc, backend, seed_transpiler=42)
self.assertIsInstance(circuits, QuantumCircuit)
def test_transpile_circuits_diff_registers(self):
"""Transpile list of circuits with different qreg names."""
backend = GenericBackendV2(num_qubits=4, seed=42)
circuits = []
for _ in range(2):
qr = QuantumRegister(2)
cr = ClassicalRegister(2)
circuit = QuantumCircuit(qr, cr)
circuit.h(qr[0])
circuit.cx(qr[0], qr[1])
circuit.measure(qr, cr)
circuits.append(circuit)
circuits = transpile(circuits, backend)
self.assertIsInstance(circuits[0], QuantumCircuit)
def test_wrong_initial_layout(self):
"""Test transpile with a bad initial layout."""
backend = GenericBackendV2(num_qubits=4, seed=42)
qubit_reg = QuantumRegister(2, name="q")
clbit_reg = ClassicalRegister(2, name="c")
qc = QuantumCircuit(qubit_reg, clbit_reg, name="bell")
qc.h(qubit_reg[0])
qc.cx(qubit_reg[0], qubit_reg[1])
qc.measure(qubit_reg, clbit_reg)
bad_initial_layout = [
QuantumRegister(3, "q")[0],
QuantumRegister(3, "q")[1],
QuantumRegister(3, "q")[2],
]
with self.assertRaises(TranspilerError):
transpile(qc, backend, initial_layout=bad_initial_layout)
def test_parameterized_circuit_for_simulator(self):
"""Verify that a parameterized circuit can be transpiled for a simulator backend."""
qr = QuantumRegister(2, name="qr")
qc = QuantumCircuit(qr)
theta = Parameter("theta")
qc.rz(theta, qr[0])
transpiled_qc = transpile(qc, backend=BasicSimulator())
expected_qc = QuantumCircuit(qr)
expected_qc.append(RZGate(theta), [qr[0]])
self.assertEqual(expected_qc, transpiled_qc)
def test_parameterized_circuit_for_device(self):
"""Verify that a parameterized circuit can be transpiled for a device backend."""
qr = QuantumRegister(2, name="qr")
qc = QuantumCircuit(qr)
theta = Parameter("theta")
qc.p(theta, qr[0])
backend = GenericBackendV2(num_qubits=4, seed=42)
transpiled_qc = transpile(
qc,
backend=backend,
initial_layout=Layout.generate_trivial_layout(qr),
)
qr = QuantumRegister(backend.num_qubits, "q")
expected_qc = QuantumCircuit(qr, global_phase=theta / 2.0)
expected_qc.append(RZGate(theta), [qr[0]])
self.assertEqual(expected_qc, transpiled_qc)
def test_parameter_expression_circuit_for_simulator(self):
"""Verify that a circuit including expressions of parameters can be
transpiled for a simulator backend."""
qr = QuantumRegister(2, name="qr")
qc = QuantumCircuit(qr)
theta = Parameter("theta")
square = theta * theta
qc.rz(square, qr[0])
transpiled_qc = transpile(qc, backend=BasicSimulator())
expected_qc = QuantumCircuit(qr)
expected_qc.append(RZGate(square), [qr[0]])
self.assertEqual(expected_qc, transpiled_qc)
def test_parameter_expression_circuit_for_device(self):
"""Verify that a circuit including expressions of parameters can be
transpiled for a device backend."""
qr = QuantumRegister(2, name="qr")
qc = QuantumCircuit(qr)
theta = Parameter("theta")
square = theta * theta
qc.rz(square, qr[0])
backend = GenericBackendV2(num_qubits=4, seed=42)
transpiled_qc = transpile(
qc,
backend=backend,
initial_layout=Layout.generate_trivial_layout(qr),
)
qr = QuantumRegister(backend.num_qubits, "q")
expected_qc = QuantumCircuit(qr)
expected_qc.append(RZGate(square), [qr[0]])
self.assertEqual(expected_qc, transpiled_qc)
def test_final_measurement_barrier_for_devices(self):
"""Verify BarrierBeforeFinalMeasurements pass is called in default pipeline for devices."""
qasm_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "qasm")
circ = QuantumCircuit.from_qasm_file(os.path.join(qasm_dir, "example.qasm"))
layout = Layout.generate_trivial_layout(*circ.qregs)
orig_pass = BarrierBeforeFinalMeasurements()
with patch.object(BarrierBeforeFinalMeasurements, "run", wraps=orig_pass.run) as mock_pass:
transpile(
circ,
coupling_map=RUESCHLIKON_CMAP,
initial_layout=layout,
)
self.assertTrue(mock_pass.called)
def test_do_not_run_gatedirection_with_symmetric_cm(self):
"""When the coupling map is symmetric, do not run GateDirection."""
qasm_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "qasm")
circ = QuantumCircuit.from_qasm_file(os.path.join(qasm_dir, "example.qasm"))
layout = Layout.generate_trivial_layout(*circ.qregs)
coupling_map = []
for node1, node2 in GenericBackendV2(num_qubits=16, seed=42).coupling_map:
coupling_map.append([node1, node2])
coupling_map.append([node2, node1])
orig_pass = GateDirection(CouplingMap(coupling_map))
with patch.object(GateDirection, "run", wraps=orig_pass.run) as mock_pass:
transpile(circ, coupling_map=coupling_map, initial_layout=layout)
self.assertFalse(mock_pass.called)
def test_do_not_run_elide_permutations_no_routing(self):
"""Test the ElidePermutations pass doesn't run if we disable routing
See https://github.com/Qiskit/qiskit/issues/13144 for the details and
reproduce in this test
"""
circuit_routed = QuantumCircuit(4)
circuit_routed.cx(0, 1)
circuit_routed.h(1)
circuit_routed.swap(1, 2)
circuit_routed.cx(2, 3)
pm = generate_preset_pass_manager(
basis_gates=["cx", "sx", "rz"], routing_method="none", optimization_level=2
)
circuit_basis = pm.run(circuit_routed)
cx_gate_qubits = []
for instruction in circuit_basis.data:
if instruction.name == "cx":
cx_gate_qubits.append(instruction.qubits)
# If we did not Elide the existing swaps then the swap should be
# decomposed into 3 cx between 1 and 2 and there are no gates between
# 1 and 3
self.assertIn((circuit_basis.qubits[1], circuit_basis.qubits[2]), cx_gate_qubits)
self.assertIn((circuit_basis.qubits[2], circuit_basis.qubits[1]), cx_gate_qubits)
self.assertNotIn((circuit_basis.qubits[1], circuit_basis.qubits[3]), cx_gate_qubits)
self.assertNotIn((circuit_basis.qubits[3], circuit_basis.qubits[1]), cx_gate_qubits)
def test_optimize_to_nothing(self):
"""Optimize gates up to fixed point in the default pipeline
See https://github.com/Qiskit/qiskit-terra/issues/2035
"""
# ┌───┐ ┌───┐┌───┐┌───┐ ┌───┐
# q0_0: ┤ H ├──■──┤ X ├┤ Y ├┤ Z ├──■──┤ H ├──■────■──
# └───┘┌─┴─┐└───┘└───┘└───┘┌─┴─┐└───┘┌─┴─┐┌─┴─┐
# q0_1: ─────┤ X ├───────────────┤ X ├─────┤ X ├┤ X ├
# └───┘ └───┘ └───┘└───┘
qr = QuantumRegister(2)
circ = QuantumCircuit(qr)
circ.h(qr[0])
circ.cx(qr[0], qr[1])
circ.x(qr[0])
circ.y(qr[0])
circ.z(qr[0])
circ.cx(qr[0], qr[1])
circ.h(qr[0])
circ.cx(qr[0], qr[1])
circ.cx(qr[0], qr[1])
after = transpile(circ, coupling_map=[[0, 1], [1, 0]], basis_gates=["u3", "u2", "u1", "cx"])
expected = QuantumCircuit(QuantumRegister(2, "q"), global_phase=-np.pi / 2)
msg = f"after:\n{after}\nexpected:\n{expected}"
self.assertEqual(after, expected, msg=msg)
def test_pass_manager_empty(self):
"""Test passing an empty PassManager() to the transpiler.
It should perform no transformations on the circuit.
"""
qr = QuantumRegister(2)
circuit = QuantumCircuit(qr)
circuit.h(qr[0])
circuit.h(qr[0])
circuit.cx(qr[0], qr[1])
circuit.cx(qr[0], qr[1])
circuit.cx(qr[0], qr[1])
circuit.cx(qr[0], qr[1])
resources_before = circuit.count_ops()
pass_manager = PassManager()
out_circuit = pass_manager.run(circuit)
resources_after = out_circuit.count_ops()
self.assertDictEqual(resources_before, resources_after)
def test_move_measurements(self):
"""Measurements applied AFTER swap mapping."""
cmap = GenericBackendV2(num_qubits=16, seed=42).coupling_map
qasm_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "qasm")
circ = QuantumCircuit.from_qasm_file(os.path.join(qasm_dir, "move_measurements.qasm"))
lay = [0, 1, 15, 2, 14, 3, 13, 4, 12, 5, 11, 6]
with self.assertWarns(DeprecationWarning):
out = transpile(
circ, initial_layout=lay, coupling_map=cmap, routing_method="stochastic"
)
out_dag = circuit_to_dag(out)
meas_nodes = out_dag.named_nodes("measure")
for meas_node in meas_nodes:
is_last_measure = all(
isinstance(after_measure, DAGOutNode)
for after_measure in out_dag.quantum_successors(meas_node)
)
self.assertTrue(is_last_measure)
@data(0, 1, 2, 3)
def test_init_resets_kept_preset_passmanagers(self, optimization_level):
"""Test initial resets kept at all preset transpilation levels"""
num_qubits = 5
qc = QuantumCircuit(num_qubits)
qc.reset(range(num_qubits))
qc.h(range(num_qubits))
num_resets = transpile(qc, optimization_level=optimization_level).count_ops()["reset"]
self.assertEqual(num_resets, num_qubits)
@data(0, 1, 2, 3)
def test_initialize_reset_is_not_removed(self, optimization_level):
"""The reset in front of initializer should NOT be removed at beginning"""
qr = QuantumRegister(1, "qr")
qc = QuantumCircuit(qr)
qc.initialize([1.0 / math.sqrt(2), 1.0 / math.sqrt(2)], [qr[0]])
qc.initialize([1.0 / math.sqrt(2), -1.0 / math.sqrt(2)], [qr[0]])
after = transpile(qc, basis_gates=["reset", "u3"], optimization_level=optimization_level)
self.assertEqual(after.count_ops()["reset"], 2, msg=f"{after}\n does not have 2 resets.")
def test_initialize_FakeMelbourne(self):
"""Test that the zero-state resets are remove in a device not supporting them."""
desired_vector = [1 / math.sqrt(2), 0, 0, 0, 0, 0, 0, 1 / math.sqrt(2)]
qr = QuantumRegister(3, "qr")
qc = QuantumCircuit(qr)
qc.initialize(desired_vector, [qr[0], qr[1], qr[2]])
out = transpile(qc, backend=GenericBackendV2(num_qubits=4, seed=42))
out_dag = circuit_to_dag(out)
reset_nodes = out_dag.named_nodes("reset")
self.assertEqual(len(reset_nodes), 3)
def test_non_standard_basis(self):
"""Test a transpilation with a non-standard basis"""
qr1 = QuantumRegister(1, "q1")
qr2 = QuantumRegister(2, "q2")
qr3 = QuantumRegister(3, "q3")
qc = QuantumCircuit(qr1, qr2, qr3)
qc.h(qr1[0])
qc.h(qr2[1])
qc.h(qr3[2])
layout = [4, 5, 6, 8, 9, 10]
cmap = [
[1, 0],
[1, 2],
[2, 3],
[4, 3],
[4, 10],
[5, 4],
[5, 6],
[5, 9],
[6, 8],
[7, 8],
[9, 8],
[9, 10],
[11, 3],
[11, 10],
[11, 12],
[12, 2],
[13, 1],
[13, 12],
]
circuit = transpile(
qc, backend=None, coupling_map=cmap, basis_gates=["h"], initial_layout=layout
)
dag_circuit = circuit_to_dag(circuit)
resources_after = dag_circuit.count_ops()
self.assertEqual({"h": 3}, resources_after)
def test_hadamard_to_rot_gates(self):
"""Test a transpilation from H to Rx, Ry gates"""
qr = QuantumRegister(1)
qc = QuantumCircuit(qr)
qc.h(0)
expected = QuantumCircuit(qr, global_phase=np.pi / 2)
expected.append(RYGate(theta=np.pi / 2), [0])
expected.append(RXGate(theta=np.pi), [0])
circuit = transpile(qc, basis_gates=["rx", "ry"], optimization_level=0)
self.assertEqual(circuit, expected)
def test_basis_subset(self):
"""Test a transpilation with a basis subset of the standard basis"""
qr = QuantumRegister(1, "q1")
qc = QuantumCircuit(qr)
qc.h(qr[0])
qc.x(qr[0])
qc.t(qr[0])
layout = [4]
cmap = [
[1, 0],
[1, 2],
[2, 3],
[4, 3],
[4, 10],
[5, 4],
[5, 6],
[5, 9],
[6, 8],
[7, 8],
[9, 8],
[9, 10],
[11, 3],
[11, 10],
[11, 12],
[12, 2],
[13, 1],
[13, 12],
]
circuit = transpile(
qc, backend=None, coupling_map=cmap, basis_gates=["u3"], initial_layout=layout
)
dag_circuit = circuit_to_dag(circuit)
resources_after = dag_circuit.count_ops()
self.assertEqual({"u3": 1}, resources_after)
def test_check_circuit_width(self):
"""Verify transpilation of circuit with virtual qubits greater than
physical qubits raises error"""
cmap = [
[1, 0],
[1, 2],
[2, 3],
[4, 3],
[4, 10],
[5, 4],
[5, 6],
[5, 9],
[6, 8],
[7, 8],
[9, 8],
[9, 10],
[11, 3],
[11, 10],
[11, 12],
[12, 2],
[13, 1],
[13, 12],
]
qc = QuantumCircuit(15, 15)
with self.assertRaises(CircuitTooWideForTarget):
transpile(qc, coupling_map=cmap)
@data(0, 1, 2, 3)
def test_ccx_routing_method_none(self, optimization_level):
"""CCX without routing method."""
qc = QuantumCircuit(3)
qc.cx(0, 1)
qc.cx(1, 2)
out = transpile(
qc,
routing_method="none",
basis_gates=["u", "cx"],
initial_layout=[0, 1, 2],
seed_transpiler=0,
coupling_map=[[0, 1], [1, 2]],
optimization_level=optimization_level,
)
self.assertTrue(Operator(qc).equiv(out))
@data(0, 1, 2, 3)
def test_ccx_routing_method_none_failed(self, optimization_level):
"""CCX without routing method cannot be routed."""
qc = QuantumCircuit(3)
qc.ccx(0, 1, 2)
with self.assertRaises(TranspilerError):
transpile(
qc,
routing_method="none",
basis_gates=["u", "cx"],
initial_layout=[0, 1, 2],
seed_transpiler=0,
coupling_map=[[0, 1], [1, 2]],
optimization_level=optimization_level,
)
@data(0, 1, 2, 3)
def test_ms_unrolls_to_cx(self, optimization_level):
"""Verify a Rx,Ry,Rxx circuit transpile to a U3,CX target."""
qc = QuantumCircuit(2)
qc.rx(math.pi / 2, 0)
qc.ry(math.pi / 4, 1)
qc.rxx(math.pi / 4, 0, 1)
out = transpile(
qc, basis_gates=["u3", "cx"], optimization_level=optimization_level, seed_transpiler=42
)
self.assertTrue(Operator(qc).equiv(out))
@data(0, 1, 2, 3)
def test_ms_can_target_ms(self, optimization_level):
"""Verify a Rx,Ry,Rxx circuit can transpile to an Rx,Ry,Rxx target."""
qc = QuantumCircuit(2)
qc.rx(math.pi / 2, 0)
qc.ry(math.pi / 4, 1)
qc.rxx(math.pi / 4, 0, 1)
out = transpile(
qc,
basis_gates=["rx", "ry", "rxx"],
optimization_level=optimization_level,
seed_transpiler=42,
)
self.assertTrue(Operator(qc).equiv(out))
@data(0, 1, 2, 3)
def test_cx_can_target_ms(self, optimization_level):
"""Verify a U3,CX circuit can transpiler to a Rx,Ry,Rxx target."""
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.rz(math.pi / 4, [0, 1])
out = transpile(
qc,
basis_gates=["rx", "ry", "rxx"],
optimization_level=optimization_level,
seed_transpiler=42,
)
self.assertTrue(Operator(qc).equiv(out))
@data(0, 1, 2, 3)
def test_measure_doesnt_unroll_ms(self, optimization_level):
"""Verify a measure doesn't cause an Rx,Ry,Rxx circuit to unroll to U3,CX."""
qc = QuantumCircuit(2, 2)
qc.rx(math.pi / 2, 0)
qc.ry(math.pi / 4, 1)
qc.rxx(math.pi / 4, 0, 1)
qc.measure([0, 1], [0, 1])
out = transpile(
qc,
basis_gates=["rx", "ry", "rxx"],
optimization_level=optimization_level,
seed_transpiler=42,
)
self.assertEqual(qc, out)
@data(
["cx", "u3"],
["cz", "u3"],
["cz", "rx", "rz"],
["rxx", "rx", "ry"],
["iswap", "rx", "rz"],
)
def test_block_collection_runs_for_non_cx_bases(self, basis_gates):
"""Verify block collection is run when a single two qubit gate is in the basis."""
twoq_gate, *_ = basis_gates
qc = QuantumCircuit(2)
qc.cx(0, 1)
qc.cx(1, 0)
qc.cx(0, 1)
qc.cx(0, 1)
out = transpile(qc, basis_gates=basis_gates, optimization_level=3, seed_transpiler=42)
self.assertLessEqual(out.count_ops()[twoq_gate], 2)
@unpack
@data(
(["u3", "cx"], {"u3": 1, "cx": 1}),
(["rx", "rz", "iswap"], {"rx": 6, "rz": 12, "iswap": 2}),
(["rx", "ry", "rxx"], {"rx": 6, "ry": 5, "rxx": 1}),
)
def test_block_collection_reduces_1q_gate(self, basis_gates, gate_counts):
"""For synthesis to non-U3 bases, verify we minimize 1q gates."""
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
out = transpile(qc, basis_gates=basis_gates, optimization_level=3, seed_transpiler=42)
self.assertTrue(Operator(out).equiv(qc))
self.assertTrue(set(out.count_ops()).issubset(basis_gates))
for basis_gate in basis_gates:
self.assertLessEqual(out.count_ops()[basis_gate], gate_counts[basis_gate])
@combine(
optimization_level=[0, 1, 2, 3],
basis_gates=[
["u3", "cx"],
["rx", "rz", "iswap"],
["ry", "rz", "rxx"],
],
)
def test_translation_method_synthesis(self, optimization_level, basis_gates):
"""Verify translation_method='synthesis' gets to the basis."""
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
out = transpile(
qc,
translation_method="synthesis",
basis_gates=basis_gates,
optimization_level=optimization_level,
seed_transpiler=42,
)
self.assertTrue(Operator(out).equiv(qc))
self.assertTrue(set(out.count_ops()).issubset(basis_gates))
def test_transpiled_custom_gates_calibration(self):
"""Test if transpiled calibrations is equal to custom gates circuit calibrations."""
custom_180 = Gate("mycustom", 1, [3.14])
custom_90 = Gate("mycustom", 1, [1.57])
circ = QuantumCircuit(2)
circ.append(custom_180, [0])
circ.append(custom_90, [1])
with self.assertWarns(DeprecationWarning):
with pulse.build() as q0_x180:
pulse.play(pulse.library.Gaussian(20, 1.0, 3.0), pulse.DriveChannel(0))
with pulse.build() as q1_y90:
pulse.play(pulse.library.Gaussian(20, -1.0, 3.0), pulse.DriveChannel(1))
# Add calibration
circ.add_calibration(custom_180, [0], q0_x180)
circ.add_calibration(custom_90, [1], q1_y90)
transpiled_circuit = transpile(
circ,
backend=GenericBackendV2(num_qubits=4, seed=42),
layout_method="trivial",
seed_transpiler=42,
)
with self.assertWarns(DeprecationWarning):
self.assertEqual(transpiled_circuit.calibrations, circ.calibrations)
self.assertEqual(list(transpiled_circuit.count_ops().keys()), ["mycustom"])
self.assertEqual(list(transpiled_circuit.count_ops().values()), [2])
def test_transpiled_basis_gates_calibrations(self):
"""Test if the transpiled calibrations is equal to basis gates circuit calibrations."""
circ = QuantumCircuit(2)
circ.h(0)
with self.assertWarns(DeprecationWarning):
with pulse.build() as q0_x180:
pulse.play(pulse.library.Gaussian(20, 1.0, 3.0), pulse.DriveChannel(0))
# Add calibration
circ.add_calibration("h", [0], q0_x180)
transpiled_circuit = transpile(
circ, backend=GenericBackendV2(num_qubits=4, seed=42), seed_transpiler=42
)
with self.assertWarns(DeprecationWarning):
self.assertEqual(transpiled_circuit.calibrations, circ.calibrations)
def test_transpile_calibrated_custom_gate_on_diff_qubit(self):
"""Test if the custom, non calibrated gate raises QiskitError."""
custom_180 = Gate("mycustom", 1, [3.14])
circ = QuantumCircuit(2)
circ.append(custom_180, [0])
with self.assertWarns(DeprecationWarning):
with pulse.build() as q0_x180:
pulse.play(pulse.library.Gaussian(20, 1.0, 3.0), pulse.DriveChannel(0))
# Add calibration
circ.add_calibration(custom_180, [1], q0_x180)
with self.assertRaises(QiskitError):
transpile(
circ,
backend=GenericBackendV2(num_qubits=4, seed=42),
layout_method="trivial",
seed_transpiler=42,
optimization_level=1,
)
def test_transpile_calibrated_nonbasis_gate_on_diff_qubit(self):
"""Test if the non-basis gates are transpiled if they are on different qubit that
is not calibrated."""
circ = QuantumCircuit(2)
circ.h(0)
circ.h(1)
with self.assertWarns(DeprecationWarning):
with pulse.build() as q0_x180:
pulse.play(pulse.library.Gaussian(20, 1.0, 3.0), pulse.DriveChannel(0))
# Add calibration
circ.add_calibration("h", [1], q0_x180)
transpiled_circuit = transpile(
circ, backend=GenericBackendV2(num_qubits=4), seed_transpiler=42, optimization_level=1
)
with self.assertWarns(DeprecationWarning):
self.assertEqual(transpiled_circuit.calibrations, circ.calibrations)
self.assertEqual(set(transpiled_circuit.count_ops().keys()), {"rz", "sx", "h"})
def test_transpile_subset_of_calibrated_gates(self):
"""Test transpiling a circuit with both basis gate (not-calibrated) and
a calibrated gate on different qubits."""
x_180 = Gate("mycustom", 1, [3.14])
circ = QuantumCircuit(2)
circ.h(0)
circ.append(x_180, [0])
circ.h(1)
with self.assertWarns(DeprecationWarning):
with pulse.build() as q0_x180:
pulse.play(pulse.library.Gaussian(20, 1.0, 3.0), pulse.DriveChannel(0))
circ.add_calibration(x_180, [0], q0_x180)
circ.add_calibration("h", [1], q0_x180) # 'h' is calibrated on qubit 1
transpiled_circ = transpile(
circ,
backend=GenericBackendV2(num_qubits=4, seed=42),
layout_method="trivial",
seed_transpiler=42,
)
self.assertEqual(set(transpiled_circ.count_ops().keys()), {"rz", "sx", "mycustom", "h"})
def test_parameterized_calibrations_transpile(self):
"""Check that gates can be matched to their calibrations before and after parameter
assignment."""
tau = Parameter("tau")
circ = QuantumCircuit(3, 3)
circ.append(Gate("rxt", 1, [2 * 3.14 * tau]), [0])
def q0_rxt(tau):
with self.assertWarns(DeprecationWarning):
with pulse.build() as q0_rxt:
pulse.play(pulse.library.Gaussian(20, 0.4 * tau, 3.0), pulse.DriveChannel(0))
return q0_rxt
with self.assertWarns(DeprecationWarning):
circ.add_calibration("rxt", [0], q0_rxt(tau), [2 * 3.14 * tau])
transpiled_circ = transpile(
circ,
backend=GenericBackendV2(num_qubits=4, seed=42),
layout_method="trivial",
seed_transpiler=42,
)
self.assertEqual(set(transpiled_circ.count_ops().keys()), {"rxt"})
circ = circ.assign_parameters({tau: 1})
transpiled_circ = transpile(
circ,
backend=GenericBackendV2(num_qubits=4),
layout_method="trivial",
seed_transpiler=42,
)
self.assertEqual(set(transpiled_circ.count_ops().keys()), {"rxt"})
def test_inst_durations_from_calibrations(self):
"""Test that circuit calibrations can be used instead of explicitly
supplying inst_durations.
"""
qc = QuantumCircuit(2)
qc.append(Gate("custom", 1, []), [0])
with self.assertWarns(DeprecationWarning):
with pulse.build() as cal:
pulse.play(pulse.library.Gaussian(20, 1.0, 3.0), pulse.DriveChannel(0))
qc.add_calibration("custom", [0], cal)
out = transpile(qc, scheduling_method="alap", seed_transpiler=42)
with self.assertWarns(DeprecationWarning):
self.assertEqual(out.duration, cal.duration)
@data(0, 1, 2, 3)
def test_circuit_with_delay(self, optimization_level):
"""Verify a circuit with delay can transpile to a scheduled circuit."""
qc = QuantumCircuit(2)
qc.h(0)
qc.delay(500, 1)
qc.cx(0, 1)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="The `target` parameter should be used instead",
):
out = transpile(
qc,
scheduling_method="alap",
basis_gates=["h", "cx"],
instruction_durations=[("h", 0, 200), ("cx", [0, 1], 700)],
optimization_level=optimization_level,
seed_transpiler=42,
)
self.assertEqual(out.duration, 1200)
def test_delay_converts_to_dt(self):
"""Test that a delay instruction is converted to units of dt given a backend."""
qc = QuantumCircuit(2)
qc.delay(1000, [0], unit="us")
backend = GenericBackendV2(num_qubits=4)
backend.target.dt = 0.5e-6
out = transpile([qc, qc], backend, seed_transpiler=42)
self.assertEqual(out[0].data[0].operation.unit, "dt")
self.assertEqual(out[1].data[0].operation.unit, "dt")
out = transpile(qc, dt=1e-9, seed_transpiler=42)
self.assertEqual(out.data[0].operation.unit, "dt")
def test_scheduling_backend_v2(self):
"""Test that scheduling method works with Backendv2."""
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
out = transpile(
[qc, qc],
backend=GenericBackendV2(num_qubits=4),
scheduling_method="alap",
seed_transpiler=42,
)
self.assertIn("delay", out[0].count_ops())
self.assertIn("delay", out[1].count_ops())
def test_scheduling_instruction_constraints_backend(self):
"""Test that scheduling-related loose transpile constraints
work with both BackendV1 and BackendV2."""
with self.assertWarns(DeprecationWarning):
backend_v1 = Fake27QPulseV1()
backend_v2 = BackendV2Converter(backend_v1)
qc = QuantumCircuit(2)
qc.h(0)
qc.delay(500, 1, "dt")
qc.cx(0, 1)
# update durations
durations = InstructionDurations.from_backend(backend_v1)
durations.update([("cx", [0, 1], 1000, "dt")])
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="The `transpile` function will stop supporting inputs of type `BackendV1` ",
):
scheduled = transpile(
qc,
backend=backend_v1,
scheduling_method="alap",
instruction_durations=durations,
layout_method="trivial",
)
self.assertEqual(scheduled.duration, 1500)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="The `target` parameter should be used instead",
):
scheduled = transpile(
qc,
backend=backend_v2,
scheduling_method="alap",
instruction_durations=durations,
layout_method="trivial",
)
self.assertEqual(scheduled.duration, 1500)
def test_scheduling_instruction_constraints(self):
"""Test that scheduling-related loose transpile constraints work with target."""
target = GenericBackendV2(
2,
coupling_map=[[0, 1]],
basis_gates=["cx", "h"],
seed=42,
).target
qc = QuantumCircuit(2)
qc.h(0)
qc.delay(0.000001, 1, "s")
qc.cx(0, 1)
# update cx to 2 seconds
target.update_instruction_properties("cx", (0, 1), InstructionProperties(0.000001))
scheduled = transpile(
qc,
target=target,
scheduling_method="alap",
layout_method="trivial",
)
self.assertEqual(scheduled.duration, 9010)
def test_scheduling_dt_constraints(self):
"""Test that scheduling-related loose transpile constraints
work with both BackendV1 and BackendV2."""
with self.assertWarns(DeprecationWarning):
backend_v1 = Fake27QPulseV1()
backend_v2 = BackendV2Converter(backend_v1)
qc = QuantumCircuit(1, 1)
qc.x(0)
qc.measure(0, 0)
original_dt = 2.2222222222222221e-10
original_duration = 3504
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="The `transpile` function will stop supporting inputs of type `BackendV1` ",
):
# halve dt in sec = double duration in dt
scheduled = transpile(
qc, backend=backend_v1, scheduling_method="asap", dt=original_dt / 2
)
self.assertEqual(scheduled.duration, original_duration * 2)
# halve dt in sec = double duration in dt
scheduled = transpile(qc, backend=backend_v2, scheduling_method="asap", dt=original_dt / 2)
self.assertEqual(scheduled.duration, original_duration * 2)
@data(1, 2, 3)
def test_no_infinite_loop(self, optimization_level):
"""Verify circuit cost always descends and optimization does not flip flop indefinitely."""
qc = QuantumCircuit(1)
qc.ry(0.2, 0)
out = transpile(
qc,
basis_gates=["id", "p", "sx", "cx"],
optimization_level=optimization_level,
seed_transpiler=42,
)
if optimization_level == 1:
# Expect a -pi/2 global phase for the U3 to RZ/SX conversion, and
# a -0.5 * theta phase for RZ to P twice, once at theta, and once at 3 pi
# for the second and third RZ gates in the U3 decomposition.
expected = QuantumCircuit(
1, global_phase=-np.pi / 2 - 0.5 * (-0.2 + np.pi) - 0.5 * 3 * np.pi
)
expected.p(-np.pi, 0)
expected.sx(0)
expected.p(np.pi - 0.2, 0)
expected.sx(0)
else:
expected = QuantumCircuit(1, global_phase=(15 * np.pi - 1) / 10)
expected.sx(0)
expected.p(1.0 / 5.0 + np.pi, 0)
expected.sx(0)
expected.p(3 * np.pi, 0)
error_message = (
f"\nOutput circuit:\n{out!s}\n{Operator(out).data}\n"
f"Expected circuit:\n{expected!s}\n{Operator(expected).data}"
)
self.assertEqual(Operator(qc), Operator(out))
self.assertEqual(out, expected, error_message)
@data(0, 1, 2, 3)
def test_transpile_preserves_circuit_metadata(self, optimization_level):
"""Verify that transpile preserves circuit metadata in the output."""
circuit = QuantumCircuit(2, metadata={"experiment_id": "1234", "execution_number": 4})
circuit.h(0)
circuit.cx(0, 1)
cmap = [
[1, 0],
[1, 2],
[2, 3],
[4, 3],
[4, 10],
[5, 4],
[5, 6],
[5, 9],
[6, 8],
[7, 8],
[9, 8],
[9, 10],
[11, 3],
[11, 10],
[11, 12],
[12, 2],
[13, 1],
[13, 12],
]
res = transpile(
circuit,
basis_gates=["id", "p", "sx", "cx"],
coupling_map=cmap,
optimization_level=optimization_level,
seed_transpiler=42,
)
self.assertEqual(circuit.metadata, res.metadata)
@data(0, 1, 2, 3)
def test_transpile_optional_registers(self, optimization_level):
"""Verify transpile accepts circuits without registers end-to-end."""
qubits = [Qubit() for _ in range(3)]
clbits = [Clbit() for _ in range(3)]
qc = QuantumCircuit(qubits, clbits)
qc.h(0)
qc.cx(0, 1)
qc.cx(1, 2)
qc.measure(qubits, clbits)
backend = GenericBackendV2(num_qubits=4)
out = transpile(
qc, backend=backend, optimization_level=optimization_level, seed_transpiler=42
)
self.assertEqual(len(out.qubits), backend.num_qubits)
self.assertEqual(len(out.clbits), len(clbits))
@data(0, 1, 2, 3)
def test_translate_ecr_basis(self, optimization_level):
"""Verify that rewriting in ECR basis is efficient."""
circuit = QuantumCircuit(2)
circuit.append(random_unitary(4, seed=1), [0, 1])
circuit.barrier()
circuit.cx(0, 1)
circuit.barrier()
circuit.swap(0, 1)
circuit.barrier()
circuit.iswap(0, 1)
res = transpile(
circuit,
basis_gates=["u", "ecr"],
optimization_level=optimization_level,
seed_transpiler=42,
)
# Swap gates get optimized away in opt. level 2, 3
expected_num_ecr_gates = 6 if optimization_level in (2, 3) else 9
self.assertEqual(res.count_ops()["ecr"], expected_num_ecr_gates)
self.assertEqual(Operator(circuit), Operator.from_circuit(res))
def test_optimize_ecr_basis(self):
"""Test highest optimization level can optimize over ECR."""
circuit = QuantumCircuit(2)
circuit.swap(1, 0)
circuit.iswap(0, 1)
res = transpile(circuit, basis_gates=["u", "ecr"], optimization_level=3, seed_transpiler=42)
# an iswap gate is equivalent to (swap, CZ) up to single-qubit rotations. Normally, the swap gate
# in the circuit would cancel with the swap gate of the (swap, CZ), leaving a single CZ gate that
# can be realized via one ECR gate. However, with the introduction of ElideSwap, the swap gate
# cancellation can not occur anymore, thus requiring two ECR gates for the iswap gate.
self.assertEqual(res.count_ops()["ecr"], 2)
self.assertEqual(Operator(circuit), Operator.from_circuit(res))
def test_approximation_degree_invalid(self):
"""Test invalid approximation degree raises."""
circuit = QuantumCircuit(2)
circuit.swap(0, 1)
with self.assertRaises(QiskitError):
transpile(
circuit, basis_gates=["u", "cz"], approximation_degree=1.1, seed_transpiler=42
)
def test_approximation_degree(self):
"""Test more approximation can give lower-cost circuit."""
circuit = QuantumCircuit(2)
circuit.swap(0, 1)
circuit.h(0)
circ_10 = transpile(
circuit,
basis_gates=["u", "cx"],
translation_method="synthesis",
approximation_degree=0.1,
seed_transpiler=42,
optimization_level=1,
)
circ_90 = transpile(
circuit,
basis_gates=["u", "cx"],
translation_method="synthesis",
approximation_degree=0.9,
seed_transpiler=42,
optimization_level=1,
)
self.assertLess(circ_10.depth(), circ_90.depth())
@data(0, 1, 2, 3)
def test_synthesis_translation_method_with_single_qubit_gates(self, optimization_level):
"""Test that synthesis basis translation works for solely 1q circuit"""
qc = QuantumCircuit(3)
qc.h(0)
qc.h(1)
qc.h(2)
res = transpile(
qc,
basis_gates=["id", "rz", "x", "sx", "cx"],
translation_method="synthesis",
optimization_level=optimization_level,
seed_transpiler=42,
)
expected = QuantumCircuit(3, global_phase=3 * np.pi / 4)
expected.rz(np.pi / 2, 0)
expected.rz(np.pi / 2, 1)
expected.rz(np.pi / 2, 2)
expected.sx(0)
expected.sx(1)
expected.sx(2)
expected.rz(np.pi / 2, 0)
expected.rz(np.pi / 2, 1)
expected.rz(np.pi / 2, 2)
self.assertEqual(res, expected)
@data(0, 1, 2, 3)
def test_synthesis_translation_method_with_gates_outside_basis(self, optimization_level):
"""Test that synthesis translation works for circuits with single gates outside basis"""
qc = QuantumCircuit(2)
qc.swap(0, 1)
res = transpile(
qc,
basis_gates=["id", "rz", "x", "sx", "cx"],
translation_method="synthesis",
optimization_level=optimization_level,
seed_transpiler=42,
)
if optimization_level not in {2, 3}:
self.assertTrue(Operator(qc).equiv(res))
self.assertNotIn("swap", res.count_ops())
else:
# Optimization level 2 and 3 eliminates the swap by permuting the
# qubits
self.assertEqual(res, QuantumCircuit(2))
@data(0, 1, 2, 3)
def test_target_ideal_gates(self, opt_level):
"""Test that transpile() with a custom ideal sim target works."""
theta = Parameter("θ")
phi = Parameter("ϕ")
lam = Parameter("λ")
target = Target(num_qubits=2)
target.add_instruction(UGate(theta, phi, lam), {(0,): None, (1,): None})
target.add_instruction(CXGate(), {(0, 1): None})
target.add_instruction(Measure(), {(0,): None, (1,): None})
qubit_reg = QuantumRegister(2, name="q")
clbit_reg = ClassicalRegister(2, name="c")
qc = QuantumCircuit(qubit_reg, clbit_reg, name="bell")
qc.h(qubit_reg[0])
qc.cx(qubit_reg[0], qubit_reg[1])
result = transpile(qc, target=target, optimization_level=opt_level, seed_transpiler=42)
self.assertEqual(Operator.from_circuit(result), Operator.from_circuit(qc))
@data(0, 1, 2, 3)
def test_transpile_control_flow_no_backend(self, opt_level):
"""Test `transpile` with control flow and no specified hardware constraints."""
qc = QuantumCircuit(QuantumRegister(1, "q"), ClassicalRegister(1, "c"))
qc.h(0)
qc.measure(0, 0)
with qc.if_test((qc.clbits[0], False)):
qc.x(0)
with qc.while_loop((qc.clbits[0], True)):
qc.x(0)
with qc.for_loop(range(2)):
qc.x(0)
with qc.switch(qc.cregs[0]) as case:
with case(case.DEFAULT):
qc.x(0)
qc.measure(0, 0)
transpiled = transpile(qc, optimization_level=opt_level)
# There's nothing that can be optimized here.
self.assertEqual(qc, transpiled)
@data(0, 1, 2, 3)
def test_transpile_with_custom_control_flow_target(self, opt_level):
"""Test transpile() with a target and control flow ops."""
target = GenericBackendV2(num_qubits=8, control_flow=True).target
circuit = QuantumCircuit(6, 1)
circuit.h(0)
circuit.measure(0, 0)
circuit.cx(0, 1)
circuit.cz(0, 2)
circuit.append(CustomCX(), [1, 2], [])
with circuit.for_loop((1,)):
circuit.cx(0, 1)
circuit.cz(0, 2)
circuit.append(CustomCX(), [1, 2], [])
with circuit.if_test((circuit.clbits[0], True)) as else_:
circuit.cx(0, 1)
circuit.cz(0, 2)
circuit.append(CustomCX(), [1, 2], [])
with else_:
circuit.cx(3, 4)
circuit.cz(3, 5)
circuit.append(CustomCX(), [4, 5], [])
with circuit.while_loop((circuit.clbits[0], True)):
circuit.cx(3, 4)
circuit.cz(3, 5)
circuit.append(CustomCX(), [4, 5], [])
with circuit.switch(circuit.cregs[0]) as case_:
with case_(0):
circuit.cx(0, 1)
circuit.cz(0, 2)
circuit.append(CustomCX(), [1, 2], [])
with case_(1):
circuit.cx(1, 2)
circuit.cz(1, 3)
circuit.append(CustomCX(), [2, 3], [])
transpiled = transpile(
circuit, optimization_level=opt_level, target=target, seed_transpiler=12434
)
# Tests of the complete validity of a circuit are mostly done at the individual pass level;
# here we're just checking that various passes do appear to have run.
self.assertIsInstance(transpiled, QuantumCircuit)
# Assert layout ran.
self.assertIsNot(getattr(transpiled, "_layout", None), None)
def _visit_block(circuit, qubit_mapping=None):
for instruction in circuit:
qargs = tuple(qubit_mapping[x] for x in instruction.qubits)
self.assertTrue(target.instruction_supported(instruction.operation.name, qargs))
if isinstance(instruction.operation, ControlFlowOp):
for block in instruction.operation.blocks:
new_mapping = {
inner: qubit_mapping[outer]
for outer, inner in zip(instruction.qubits, block.qubits)
}
_visit_block(block, new_mapping)
# Assert unrolling ran.
self.assertNotIsInstance(instruction.operation, CustomCX)
# Assert translation ran.
self.assertNotIsInstance(instruction.operation, CZGate)
# Assert routing ran.
_visit_block(
transpiled,
qubit_mapping={qubit: index for index, qubit in enumerate(transpiled.qubits)},
)
@data(1, 2, 3)
def test_transpile_identity_circuit_no_target(self, opt_level):
"""Test circuit equivalent to identity is optimized away for all optimization levels >0.
Reproduce taken from https://github.com/Qiskit/qiskit-terra/issues/9217
"""
qr1 = QuantumRegister(3, "state")
qr2 = QuantumRegister(2, "ancilla")
cr = ClassicalRegister(2, "c")
qc = QuantumCircuit(qr1, qr2, cr)
qc.h(qr1[0])
qc.cx(qr1[0], qr1[1])
qc.cx(qr1[1], qr1[2])
qc.cx(qr1[1], qr1[2])
qc.cx(qr1[0], qr1[1])
qc.h(qr1[0])
empty_qc = QuantumCircuit(qr1, qr2, cr)
result = transpile(qc, optimization_level=opt_level, seed_transpiler=42)
self.assertEqual(empty_qc, result)
@data(0, 1, 2, 3)
def test_initial_layout_with_loose_qubits(self, opt_level):
"""Regression test of gh-10125."""
qc = QuantumCircuit([Qubit(), Qubit()])
qc.cx(0, 1)
transpiled = transpile(
qc, initial_layout=[1, 0], optimization_level=opt_level, seed_transpiler=42
)
self.assertIsNotNone(transpiled.layout)
self.assertEqual(
transpiled.layout.initial_layout, Layout({0: qc.qubits[1], 1: qc.qubits[0]})
)
@data(0, 1, 2, 3)
def test_initial_layout_with_overlapping_qubits(self, opt_level):
"""Regression test of gh-10125."""
qr1 = QuantumRegister(2, "qr1")
qr2 = QuantumRegister(bits=qr1[:])
qc = QuantumCircuit(qr1, qr2)
qc.cx(0, 1)
transpiled = transpile(
qc, initial_layout=[1, 0], optimization_level=opt_level, seed_transpiler=42
)
self.assertIsNotNone(transpiled.layout)
self.assertEqual(
transpiled.layout.initial_layout, Layout({0: qc.qubits[1], 1: qc.qubits[0]})
)
@combine(opt_level=[0, 1, 2, 3], basis=[["rz", "x"], ["rx", "z"], ["rz", "y"], ["ry", "x"]])
def test_paulis_to_constrained_1q_basis(self, opt_level, basis):
"""Test that Pauli-gate circuits can be transpiled to constrained 1q bases that do not
contain any root-Pauli gates."""
qc = QuantumCircuit(1)
qc.x(0)
qc.barrier()
qc.y(0)
qc.barrier()
qc.z(0)
transpiled = transpile(
qc, basis_gates=basis, optimization_level=opt_level, seed_transpiler=42
)
self.assertGreaterEqual(set(basis) | {"barrier"}, transpiled.count_ops().keys())
self.assertEqual(Operator(qc), Operator(transpiled))
@data(0, 1, 2, 3)
def test_barrier_not_output(self, opt_level):
"""Test that barriers added as part internal transpiler operations do not leak out."""
qc = QuantumCircuit(2, 2)
qc.cx(0, 1)
qc.measure(range(2), range(2))
tqc = transpile(
qc,
initial_layout=[1, 4],
coupling_map=[[1, 2], [2, 3], [3, 4]],
optimization_level=opt_level,
)
self.assertNotIn("barrier", tqc.count_ops())
@data(0, 1, 2, 3)
def test_barrier_not_output_input_preservered(self, opt_level):
"""Test that barriers added as part internal transpiler operations do not leak out."""
qc = QuantumCircuit(2, 2)
qc.cx(0, 1)
qc.measure_all()
tqc = transpile(
qc,
initial_layout=[1, 4],
coupling_map=[[0, 1], [1, 2], [2, 3], [3, 4]],
optimization_level=opt_level,
)
op_counts = tqc.count_ops()
self.assertEqual(op_counts["barrier"], 1)
for inst in tqc.data:
if inst.operation.name == "barrier":
self.assertEqual(len(inst.qubits), 2)
@combine(opt_level=[0, 1, 2, 3])
def test_transpile_annotated_ops(self, opt_level):
"""Test transpilation of circuits with annotated operations."""
qc = QuantumCircuit(3)
qc.append(AnnotatedOperation(SGate(), InverseModifier()), [0])
qc.append(AnnotatedOperation(XGate(), ControlModifier(1)), [1, 2])
qc.append(AnnotatedOperation(HGate(), PowerModifier(3)), [2])
expected = QuantumCircuit(3)
expected.sdg(0)
expected.cx(1, 2)
expected.h(2)
transpiled = transpile(qc, optimization_level=opt_level, seed_transpiler=42)
self.assertNotIn("annotated", transpiled.count_ops().keys())
self.assertEqual(Operator(qc), Operator(transpiled))
self.assertEqual(Operator(qc), Operator(expected))
@combine(opt_level=[0, 1, 2, 3])
def test_transpile_annotated_ops_with_backend_v1(self, opt_level):
"""Test transpilation of circuits with annotated operations given a backend.
Remove once Fake20QV1 is removed."""
qc = QuantumCircuit(3)
qc.append(AnnotatedOperation(SGate(), InverseModifier()), [0])
qc.append(AnnotatedOperation(XGate(), ControlModifier(1)), [1, 2])
qc.append(AnnotatedOperation(HGate(), PowerModifier(3)), [2])
with self.assertWarns(DeprecationWarning):
backend = Fake20QV1()
transpiled = transpile(
qc, optimization_level=opt_level, backend=backend, seed_transpiler=42
)
self.assertLessEqual(set(transpiled.count_ops().keys()), {"u1", "u2", "u3", "cx"})
@combine(opt_level=[0, 1, 2, 3])
def test_transpile_annotated_ops_with_backend(self, opt_level):
"""Test transpilation of circuits with annotated operations given a backend."""
qc = QuantumCircuit(3)
qc.append(AnnotatedOperation(SGate(), InverseModifier()), [0])
qc.append(AnnotatedOperation(XGate(), ControlModifier(1)), [1, 2])
qc.append(AnnotatedOperation(HGate(), PowerModifier(3)), [2])
backend = GenericBackendV2(
num_qubits=20,
coupling_map=TOKYO_CMAP,
basis_gates=["id", "u1", "u2", "u3", "cx"],
)
transpiled = transpile(
qc, optimization_level=opt_level, backend=backend, seed_transpiler=42
)
self.assertLessEqual(set(transpiled.count_ops().keys()), {"u1", "u2", "u3", "cx"})
@ddt
class TestPostTranspileIntegration(QiskitTestCase):
"""Test that the output of `transpile` is usable in various other integration contexts."""
def _regular_circuit(self):
a = Parameter("a")
regs = [
QuantumRegister(2, name="q0"),
QuantumRegister(3, name="q1"),
ClassicalRegister(2, name="c0"),
]
bits = [Qubit(), Qubit(), Clbit()]
base = QuantumCircuit(*regs, bits)
base.h(0)
base.measure(0, 0)
base.cx(0, 1)
base.cz(0, 2)
base.cz(0, 3)
base.cz(1, 4)
base.cx(1, 5)
base.measure(1, 1)
base.append(CustomCX(), [3, 6])
base.append(CustomCX(), [5, 4])
base.append(CustomCX(), [5, 3])
with self.assertWarns(DeprecationWarning):
base.append(CustomCX(), [2, 4]).c_if(base.cregs[0], 3)
base.ry(a, 4)
base.measure(4, 2)
return base
def _control_flow_circuit(self):
a = Parameter("a")
regs = [
QuantumRegister(2, name="q0"),
QuantumRegister(3, name="q1"),
ClassicalRegister(2, name="c0"),
]
bits = [Qubit(), Qubit(), Clbit()]
base = QuantumCircuit(*regs, bits)
base.h(0)
base.measure(0, 0)
with base.if_test((base.cregs[0], 1)) as else_:
base.cx(0, 1)
base.cz(0, 2)
base.cz(0, 3)
with else_:
base.cz(1, 4)
with base.for_loop((1, 2)):
base.cx(1, 5)
base.measure(2, 2)
with base.while_loop((2, False)):
base.append(CustomCX(), [3, 6])
base.append(CustomCX(), [5, 4])
base.append(CustomCX(), [5, 3])
base.append(CustomCX(), [2, 4])
base.ry(a, 4)
base.measure(4, 2)
with base.switch(base.cregs[0]) as case_:
with case_(0, 1):
base.cz(3, 5)
with case_(case_.DEFAULT):
base.cz(1, 4)
base.append(CustomCX(), [2, 4])
base.append(CustomCX(), [3, 4])
return base
def _control_flow_expr_circuit(self):
a = Parameter("a")
regs = [
QuantumRegister(2, name="q0"),
QuantumRegister(3, name="q1"),
ClassicalRegister(2, name="c0"),
]
bits = [Qubit(), Qubit(), Clbit()]
base = QuantumCircuit(*regs, bits)
base.h(0)
base.measure(0, 0)
with base.if_test(expr.equal(base.cregs[0], 1)) as else_:
base.cx(0, 1)
base.cz(0, 2)
base.cz(0, 3)
with else_:
base.cz(1, 4)
with base.for_loop((1, 2)):
base.cx(1, 5)
base.measure(2, 2)
with base.while_loop(expr.logic_not(bits[2])):
base.append(CustomCX(), [3, 6])
base.append(CustomCX(), [5, 4])
base.append(CustomCX(), [5, 3])
base.append(CustomCX(), [2, 4])
base.ry(a, 4)
base.measure(4, 2)
with base.switch(expr.bit_and(base.cregs[0], 2)) as case_:
with case_(0, 1):
base.cz(3, 5)
with case_(case_.DEFAULT):
base.cz(1, 4)
base.append(CustomCX(), [2, 4])
base.append(CustomCX(), [3, 4])
return base
def _standalone_var_circuit(self):
a = expr.Var.new("a", types.Bool())
b = expr.Var.new("b", types.Uint(8))
c = expr.Var.new("c", types.Uint(8))
qc = QuantumCircuit(5, 5, inputs=[a])
qc.add_var(b, 12)
qc.h(0)
qc.cx(0, 1)
qc.measure([0, 1], [0, 1])
qc.store(a, expr.bit_xor(qc.clbits[0], qc.clbits[1]))
with qc.if_test(a) as else_:
qc.cx(2, 3)
qc.cx(3, 4)
qc.cx(4, 2)
with else_:
qc.add_var(c, 12)
with qc.while_loop(a):
with qc.while_loop(a):
qc.add_var(c, 12)
qc.cz(1, 0)
qc.cz(4, 1)
qc.store(a, False)
with qc.switch(expr.bit_and(b, 7)) as case:
with case(0):
qc.cz(0, 1)
qc.cx(1, 2)
qc.cy(2, 0)
with case(case.DEFAULT):
qc.store(b, expr.bit_and(b, 7))
return qc
@data(0, 1, 2, 3)
def test_qpy_roundtrip(self, optimization_level):
"""Test that the output of a transpiled circuit can be round-tripped through QPY."""
transpiled = transpile(
self._regular_circuit(),
backend=GenericBackendV2(num_qubits=8),
optimization_level=optimization_level,
seed_transpiler=2022_10_17,
)
# Round-tripping the layout is out-of-scope for QPY while it's a private attribute.
transpiled._layout = None
buffer = io.BytesIO()
qpy.dump(transpiled, buffer)
buffer.seek(0)
round_tripped = qpy.load(buffer)[0]
self.assertEqual(round_tripped, transpiled)
@data(0, 1, 2, 3)
def test_qpy_roundtrip_backendv2(self, optimization_level):
"""Test that the output of a transpiled circuit can be round-tripped through QPY."""
transpiled = transpile(
self._regular_circuit(),
backend=GenericBackendV2(num_qubits=8),
optimization_level=optimization_level,
seed_transpiler=2022_10_17,
)
# Round-tripping the layout is out-of-scope for QPY while it's a private attribute.
transpiled._layout = None
buffer = io.BytesIO()
qpy.dump(transpiled, buffer)
buffer.seek(0)
round_tripped = qpy.load(buffer)[0]
self.assertEqual(round_tripped, transpiled)
@data(0, 1, 2, 3)
def test_qpy_roundtrip_control_flow(self, optimization_level):
"""Test that the output of a transpiled circuit with control flow can be round-tripped
through QPY."""
if optimization_level == 3 and sys.platform == "win32":
self.skipTest(
"This test case triggers a bug in the eigensolver routine on windows. "
"See #10345 for more details."
)
backend = GenericBackendV2(num_qubits=8, control_flow=True)
transpiled = transpile(
self._control_flow_circuit(),
backend=backend,
basis_gates=backend.operation_names,
optimization_level=optimization_level,
seed_transpiler=2022_10_17,
)
# Round-tripping the layout is out-of-scope for QPY while it's a private attribute.
transpiled._layout = None
buffer = io.BytesIO()
qpy.dump(transpiled, buffer)
buffer.seek(0)
round_tripped = qpy.load(buffer)[0]
self.assertEqual(round_tripped, transpiled)
@data(0, 1, 2, 3)
def test_qpy_roundtrip_control_flow_backendv2(self, optimization_level):
"""Test that the output of a transpiled circuit with control flow can be round-tripped
through QPY."""
transpiled = transpile(
self._control_flow_circuit(),
backend=GenericBackendV2(num_qubits=8, control_flow=True),
optimization_level=optimization_level,
seed_transpiler=2022_10_17,
)
# Round-tripping the layout is out-of-scope for QPY while it's a private attribute.
transpiled._layout = None
buffer = io.BytesIO()
qpy.dump(transpiled, buffer)
buffer.seek(0)
round_tripped = qpy.load(buffer)[0]
self.assertEqual(round_tripped, transpiled)
@data(0, 1, 2, 3)
def test_qpy_roundtrip_control_flow_expr(self, optimization_level):
"""Test that the output of a transpiled circuit with control flow including `Expr` nodes can
be round-tripped through QPY."""
if optimization_level == 3 and sys.platform == "win32":
self.skipTest(
"This test case triggers a bug in the eigensolver routine on windows. "
"See #10345 for more details."
)
backend = GenericBackendV2(num_qubits=16)
transpiled = transpile(
self._control_flow_expr_circuit(),
backend=backend,
basis_gates=backend.operation_names
+ ["if_else", "for_loop", "while_loop", "switch_case"],
optimization_level=optimization_level,
seed_transpiler=2023_07_26,
)
buffer = io.BytesIO()
qpy.dump(transpiled, buffer)
buffer.seek(0)
round_tripped = qpy.load(buffer)[0]
self.assertEqual(round_tripped, transpiled)
@data(0, 1, 2, 3)
def test_qpy_roundtrip_control_flow_expr_backendv2(self, optimization_level):
"""Test that the output of a transpiled circuit with control flow including `Expr` nodes can
be round-tripped through QPY."""
backend = GenericBackendV2(num_qubits=27)
backend.target.add_instruction(IfElseOp, name="if_else")
backend.target.add_instruction(ForLoopOp, name="for_loop")
backend.target.add_instruction(WhileLoopOp, name="while_loop")
backend.target.add_instruction(SwitchCaseOp, name="switch_case")
transpiled = transpile(
self._control_flow_circuit(),
backend=backend,
optimization_level=optimization_level,
seed_transpiler=2023_07_26,
)
buffer = io.BytesIO()
qpy.dump(transpiled, buffer)
buffer.seek(0)
round_tripped = qpy.load(buffer)[0]
self.assertEqual(round_tripped, transpiled)
@data(0, 1, 2, 3)
def test_qpy_roundtrip_standalone_var(self, optimization_level):
"""Test that the output of a transpiled circuit with control flow including standalone `Var`
nodes can be round-tripped through QPY."""
backend = GenericBackendV2(num_qubits=7)
transpiled = transpile(
self._standalone_var_circuit(),
backend=backend,
basis_gates=backend.operation_names
+ ["if_else", "for_loop", "while_loop", "switch_case"],
optimization_level=optimization_level,
seed_transpiler=2024_05_01,
)
buffer = io.BytesIO()
qpy.dump(transpiled, buffer)
buffer.seek(0)
round_tripped = qpy.load(buffer)[0]
self.assertEqual(round_tripped, transpiled)
@data(0, 1, 2, 3)
def test_qpy_roundtrip_standalone_var_target(self, optimization_level):
"""Test that the output of a transpiled circuit with control flow including standalone `Var`
nodes can be round-tripped through QPY."""
backend = GenericBackendV2(num_qubits=11)
backend.target.add_instruction(IfElseOp, name="if_else")
backend.target.add_instruction(ForLoopOp, name="for_loop")
backend.target.add_instruction(WhileLoopOp, name="while_loop")
backend.target.add_instruction(SwitchCaseOp, name="switch_case")
transpiled = transpile(
self._standalone_var_circuit(),
backend=backend,
optimization_level=optimization_level,
seed_transpiler=2024_05_01,
)
buffer = io.BytesIO()
qpy.dump(transpiled, buffer)
buffer.seek(0)
round_tripped = qpy.load(buffer)[0]
self.assertEqual(round_tripped, transpiled)
@data(0, 1, 2, 3)
def test_qasm3_output(self, optimization_level):
"""Test that the output of a transpiled circuit can be dumped into OpenQASM 3."""
backend = GenericBackendV2(
num_qubits=20,
coupling_map=TOKYO_CMAP,
basis_gates=["id", "u1", "u2", "u3", "cx"],
)
transpiled = transpile(
self._regular_circuit(),
backend=backend,
optimization_level=optimization_level,
seed_transpiler=2022_10_17,
)
# TODO: There's not a huge amount we can sensibly test for the output here until we can
# round-trip the OpenQASM 3 back into a Terra circuit. Mostly we're concerned that the dump
# itself doesn't throw an error, though.
self.assertIsInstance(qasm3.dumps(transpiled).strip(), str)
@data(0, 1, 2, 3)
def test_qasm3_output_v1(self, optimization_level):
"""Test that the output of a transpiled circuit can be dumped into OpenQASM 3 (backend V1)."""
with self.assertWarns(DeprecationWarning):
backend = Fake20QV1()
transpiled = transpile(
self._regular_circuit(),
backend=backend,
optimization_level=optimization_level,
seed_transpiler=2022_10_17,
)
# TODO: There's not a huge amount we can sensibly test for the output here until we can
# round-trip the OpenQASM 3 back into a Terra circuit. Mostly we're concerned that the dump
# itself doesn't throw an error, though.
self.assertIsInstance(qasm3.dumps(transpiled).strip(), str)
@data(0, 1, 2, 3)
def test_qasm3_output_control_flow(self, optimization_level):
"""Test that the output of a transpiled circuit with control flow can be dumped into
OpenQASM 3."""
transpiled = transpile(
self._control_flow_circuit(),
backend=GenericBackendV2(num_qubits=8, control_flow=True),
optimization_level=optimization_level,
seed_transpiler=2022_10_17,
)
# TODO: There's not a huge amount we can sensibly test for the output here until we can
# round-trip the OpenQASM 3 back into a Terra circuit. Mostly we're concerned that the dump
# itself doesn't throw an error, though.
self.assertIsInstance(
qasm3.dumps(transpiled, experimental=qasm3.ExperimentalFeatures.SWITCH_CASE_V1).strip(),
str,
)
@data(0, 1, 2, 3)
def test_qasm3_output_control_flow_expr(self, optimization_level):
"""Test that the output of a transpiled circuit with control flow and `Expr` nodes can be
dumped into OpenQASM 3."""
transpiled = transpile(
self._control_flow_circuit(),
backend=GenericBackendV2(num_qubits=27, control_flow=True),
optimization_level=optimization_level,
seed_transpiler=2023_07_26,
)
# TODO: There's not a huge amount we can sensibly test for the output here until we can
# round-trip the OpenQASM 3 back into a Terra circuit. Mostly we're concerned that the dump
# itself doesn't throw an error, though.
self.assertIsInstance(
qasm3.dumps(transpiled, experimental=qasm3.ExperimentalFeatures.SWITCH_CASE_V1).strip(),
str,
)
@data(0, 1, 2, 3)
def test_qasm3_output_standalone_var(self, optimization_level):
"""Test that the output of a transpiled circuit with control flow and standalone `Var` nodes
can be dumped into OpenQASM 3."""
transpiled = transpile(
self._standalone_var_circuit(),
backend=GenericBackendV2(num_qubits=13, control_flow=True),
optimization_level=optimization_level,
seed_transpiler=2024_05_01,
)
# TODO: There's not a huge amount we can sensibly test for the output here until we can
# round-trip the OpenQASM 3 back into a Terra circuit. Mostly we're concerned that the dump
# itself doesn't throw an error, though.
self.assertIsInstance(qasm3.dumps(transpiled), str)
@data(0, 1, 2, 3)
def test_transpile_target_no_measurement_error(self, opt_level):
"""Test that transpile with a target which contains ideal measurement works
Reproduce from https://github.com/Qiskit/qiskit-terra/issues/8969
"""
target = Target()
target.add_instruction(Measure(), {(0,): None})
qc = QuantumCircuit(1, 1)
qc.measure(0, 0)
res = transpile(qc, target=target, optimization_level=opt_level, seed_transpiler=42)
self.assertEqual(qc, res)
def test_transpile_final_layout_updated_with_post_layout(self):
"""Test that the final layout is correctly set when vf2postlayout runs.
Reproduce from #10457
"""
def _get_index_layout(transpiled_circuit: QuantumCircuit, num_source_qubits: int):
"""Return the index layout of a transpiled circuit"""
layout = transpiled_circuit.layout
if layout is None:
return list(range(num_source_qubits))
pos_to_virt = {v: k for k, v in layout.input_qubit_mapping.items()}
qubit_indices = []
for index in range(num_source_qubits):
qubit_idx = layout.initial_layout[pos_to_virt[index]]
if layout.final_layout is not None:
qubit_idx = layout.final_layout[transpiled_circuit.qubits[qubit_idx]]
qubit_indices.append(qubit_idx)
return qubit_indices
vf2_post_layout_called = False
def callback(**kwargs):
nonlocal vf2_post_layout_called
if isinstance(kwargs["pass_"], VF2PostLayout):
vf2_post_layout_called = True
self.assertIsNotNone(kwargs["property_set"]["post_layout"])
coupling_map = [[0, 1], [1, 0], [1, 2], [1, 3], [2, 1], [3, 1], [3, 4], [4, 3]]
backend = GenericBackendV2(
num_qubits=5,
basis_gates=["id", "sx", "x", "cx", "rz"],
coupling_map=coupling_map,
seed=0,
)
qubits = 3
qc = QuantumCircuit(qubits)
for i in range(5):
qc.cx(i % qubits, int(i + qubits / 2) % qubits)
tqc = transpile(qc, backend=backend, seed_transpiler=4242, callback=callback)
self.assertTrue(vf2_post_layout_called)
self.assertEqual([2, 1, 0], _get_index_layout(tqc, qubits))
class StreamHandlerRaiseException(StreamHandler):
"""Handler class that will raise an exception on formatting errors."""
def handleError(self, record):
raise sys.exc_info()
class TestLogTranspile(QiskitTestCase):
"""Testing the log_transpile option."""
def setUp(self):
super().setUp()
logger = getLogger()
self.addCleanup(logger.setLevel, logger.level)
logger.setLevel("DEBUG")
self.output = io.StringIO()
logger.addHandler(StreamHandlerRaiseException(self.output))
self.circuit = QuantumCircuit(QuantumRegister(1))
def assertTranspileLog(self, log_msg):
"""Runs the transpiler and check for logs containing specified message"""
transpile(self.circuit, seed_transpiler=42)
self.output.seek(0)
# Filter unrelated log lines
output_lines = self.output.readlines()
transpile_log_lines = [x for x in output_lines if log_msg in x]
self.assertTrue(len(transpile_log_lines) > 0)
def test_transpile_log_time(self):
"""Check Total Transpile Time is logged"""
self.assertTranspileLog("Total Transpile Time")
class TestTranspileCustomPM(QiskitTestCase):
"""Test transpile function with custom pass manager"""
def test_custom_multiple_circuits(self):
"""Test transpiling with custom pass manager and multiple circuits.
This tests created a deadlock, so it needs to be monitored for timeout.
See: https://github.com/Qiskit/qiskit-terra/issues/3925
"""
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
pm_conf = PassManagerConfig(
initial_layout=None,
basis_gates=["u1", "u2", "u3", "cx"],
coupling_map=CouplingMap([[0, 1]]),
seed_transpiler=1,
)
passmanager = level_0_pass_manager(pm_conf)
transpiled = passmanager.run([qc, qc])
expected = QuantumCircuit(QuantumRegister(2, "q"))
expected.append(U2Gate(0, 3.141592653589793), [0])
expected.cx(0, 1)
self.assertEqual(len(transpiled), 2)
self.assertEqual(transpiled[0], expected)
self.assertEqual(transpiled[1], expected)
@ddt
class TestTranspileParallel(QiskitTestCase):
"""Test transpile() in parallel."""
def setUp(self):
super().setUp()
# Force parallel execution to True to test multiprocessing for this class
original_val = parallel.PARALLEL_DEFAULT
def restore_default():
parallel.PARALLEL_DEFAULT = original_val
self.addCleanup(restore_default)
parallel.PARALLEL_DEFAULT = True
@data(0, 1, 2, 3)
def test_parallel_multiprocessing(self, opt_level):
"""Test parallel dispatch works with multiprocessing."""
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
pm = generate_preset_pass_manager(opt_level, backend=GenericBackendV2(num_qubits=4))
res = pm.run([qc, qc])
for circ in res:
self.assertIsInstance(circ, QuantumCircuit)
@data(0, 1, 2, 3)
def test_parallel_with_target(self, opt_level):
"""Test that parallel dispatch works with a manual target."""
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
target = GenericBackendV2(num_qubits=4).target
res = transpile([qc] * 3, target=target, optimization_level=opt_level, seed_transpiler=42)
self.assertIsInstance(res, list)
for circ in res:
self.assertIsInstance(circ, QuantumCircuit)
@data(0, 1, 2, 3)
def test_parallel_num_processes_kwarg(self, num_processes):
"""Test that num_processes kwarg works when the system default parallel is true"""
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
target = GenericBackendV2(num_qubits=27).target
res = transpile([qc] * 3, target=target, num_processes=num_processes)
self.assertIsInstance(res, list)
for circ in res:
self.assertIsInstance(circ, QuantumCircuit)
@data(0, 1, 2, 3)
def test_parallel_dispatch(self, opt_level):
"""Test that transpile in parallel works for all optimization levels."""
backend = GenericBackendV2(num_qubits=5, basis_gates=["cx", "id", "rz", "sx", "x"], seed=42)
qr = QuantumRegister(5)
cr = ClassicalRegister(5)
qc = QuantumCircuit(qr, cr)
qc.h(qr[0])
for k in range(1, 4):
qc.cx(qr[0], qr[k])
qc.measure(qr, cr)
qlist = [qc for k in range(15)]
tqc = transpile(
qlist, backend=backend, optimization_level=opt_level, seed_transpiler=424242
)
result = backend.run(tqc, seed_simulator=4242424242, shots=1000).result()
counts = result.get_counts()
for count in counts:
self.assertTrue(math.isclose(count["00000"], 500, rel_tol=0.1))
self.assertTrue(math.isclose(count["01111"], 500, rel_tol=0.1))
@data(0, 1, 2, 3)
def test_parallel_singleton_conditional_gate(self, opt_level):
"""Test that singleton mutable instance doesn't lose state in parallel."""
backend = GenericBackendV2(num_qubits=27)
circ = QuantumCircuit(2, 1)
circ.h(0)
circ.measure(0, circ.clbits[0])
with self.assertWarns(DeprecationWarning):
circ.z(1).c_if(circ.clbits[0], 1)
res = transpile(
[circ, circ], backend, optimization_level=opt_level, seed_transpiler=123456769
)
self.assertTrue(res[0].data[-1].operation.mutable)
with self.assertWarns(DeprecationWarning):
self.assertEqual(res[0].data[-1].operation.condition, (res[0].clbits[0], 1))
@data(0, 1, 2, 3)
def test_backendv2_and_basis_gates(self, opt_level):
"""Test transpile() with BackendV2 and basis_gates set."""
backend = GenericBackendV2(num_qubits=6)
qc = QuantumCircuit(5)
qc.h(0)
qc.cz(0, 1)
qc.cz(0, 2)
qc.cz(0, 3)
qc.cz(0, 4)
qc.measure_all()
tqc = transpile(
qc,
backend=backend,
basis_gates=["u", "cz"],
optimization_level=opt_level,
seed_transpiler=12345678942,
)
op_count = set(tqc.count_ops())
self.assertEqual({"u", "cz", "measure", "barrier"}, op_count)
for inst in tqc.data:
if inst.operation.name not in {"u", "cz"}:
continue
qubits = tuple(tqc.find_bit(x).index for x in inst.qubits)
self.assertIn(qubits, backend.target.qargs)
@data(0, 1, 2, 3)
def test_backendv2_and_coupling_map(self, opt_level):
"""Test transpile() with custom coupling map."""
qc = QuantumCircuit(5)
qc.h(0)
qc.cz(0, 1)
qc.cz(0, 2)
qc.cz(0, 3)
qc.cz(0, 4)
qc.measure_all()
cmap = CouplingMap.from_line(5, bidirectional=False)
tqc = transpile(
qc,
backend=GenericBackendV2(num_qubits=6),
coupling_map=cmap,
optimization_level=opt_level,
seed_transpiler=12345678942,
)
op_count = set(tqc.count_ops())
self.assertTrue({"rz", "sx", "x", "cx", "measure", "barrier"}.issuperset(op_count))
for inst in tqc.data:
if len(inst.qubits) == 2:
qubit_0 = tqc.find_bit(inst.qubits[0]).index
qubit_1 = tqc.find_bit(inst.qubits[1]).index
self.assertEqual(qubit_1, qubit_0 + 1)
def test_transpile_with_multiple_coupling_maps(self):
"""Test passing a different coupling map for every circuit"""
backend = GenericBackendV2(num_qubits=4)
qc = QuantumCircuit(3)
qc.cx(0, 2)
# Add a connection between 0 and 2 so that transpile does not change
# the gates
cmap = CouplingMap.from_line(7)
cmap.add_edge(0, 2)
with self.assertRaisesRegex(TranspilerError, "Only a single input coupling"):
# Initial layout needed to prevent transpiler from relabeling
# qubits to avoid doing the swap
transpile(
[qc] * 2,
backend,
coupling_map=[backend.coupling_map, cmap],
initial_layout=(0, 1, 2),
seed_transpiler=42,
)
@data(0, 1, 2, 3)
def test_backend_and_custom_gate(self, opt_level):
"""Test transpile() with BackendV2, custom basis pulse gate."""
backend = GenericBackendV2(
num_qubits=5,
coupling_map=[[0, 1], [1, 0], [1, 2], [1, 3], [2, 1], [3, 1], [3, 4], [4, 3]],
seed=42,
)
with self.assertWarns(DeprecationWarning):
inst_map = InstructionScheduleMap()
inst_map.add("newgate", [0, 1], pulse.ScheduleBlock())
newgate = Gate("newgate", 2, [])
circ = QuantumCircuit(2)
circ.append(newgate, [0, 1])
with self.assertWarns(DeprecationWarning):
tqc = transpile(
circ,
backend,
inst_map=inst_map,
basis_gates=["newgate"],
optimization_level=opt_level,
seed_transpiler=42,
)
self.assertEqual(len(tqc.data), 1)
self.assertEqual(tqc.data[0].operation, newgate)
for x in tqc.data[0].qubits:
self.assertIn((tqc.find_bit(x).index,), backend.target.qargs)
@ddt
class TestTranspileMultiChipTarget(QiskitTestCase):
"""Test transpile() with a disjoint coupling map."""
def setUp(self):
super().setUp()
class FakeMultiChip(BackendV2):
"""Fake multi chip backend."""
def __init__(self):
super().__init__()
graph = rx.generators.directed_heavy_hex_graph(3)
num_qubits = len(graph) * 3
rng = np.random.default_rng(seed=12345678942)
rz_props = {}
x_props = {}
sx_props = {}
measure_props = {}
delay_props = {}
self._target = Target("Fake multi-chip backend", num_qubits=num_qubits)
for i in range(num_qubits):
qarg = (i,)
rz_props[qarg] = InstructionProperties(error=0.0, duration=0.0)
x_props[qarg] = InstructionProperties(
error=rng.uniform(1e-6, 1e-4), duration=rng.uniform(1e-8, 9e-7)
)
sx_props[qarg] = InstructionProperties(
error=rng.uniform(1e-6, 1e-4), duration=rng.uniform(1e-8, 9e-7)
)
measure_props[qarg] = InstructionProperties(
error=rng.uniform(1e-3, 1e-1), duration=rng.uniform(1e-8, 9e-7)
)
delay_props[qarg] = None
self._target.add_instruction(XGate(), x_props)
self._target.add_instruction(SXGate(), sx_props)
self._target.add_instruction(RZGate(Parameter("theta")), rz_props)
self._target.add_instruction(Measure(), measure_props)
self._target.add_instruction(Delay(Parameter("t")), delay_props)
cz_props = {}
for i in range(3):
for root_edge in graph.edge_list():
offset = i * len(graph)
edge = (root_edge[0] + offset, root_edge[1] + offset)
cz_props[edge] = InstructionProperties(
error=rng.uniform(1e-5, 5e-3), duration=rng.uniform(1e-8, 9e-7)
)
self._target.add_instruction(CZGate(), cz_props)
@property
def target(self):
return self._target
@property
def max_circuits(self):
return None
@classmethod
def _default_options(cls):
return Options(shots=1024)
def run(self, circuit, **kwargs): # pylint:disable=arguments-renamed
raise NotImplementedError
self.backend = FakeMultiChip()
@data(0, 1, 2, 3)
def test_basic_connected_circuit(self, opt_level):
"""Test basic connected circuit on disjoint backend"""
qc = QuantumCircuit(5)
qc.h(0)
qc.cx(0, 1)
qc.cx(0, 2)
qc.cx(0, 3)
qc.cx(0, 4)
qc.measure_all()
tqc = transpile(qc, self.backend, optimization_level=opt_level, seed_transpiler=42)
for inst in tqc.data:
qubits = tuple(tqc.find_bit(x).index for x in inst.qubits)
op_name = inst.operation.name
if op_name == "barrier":
continue
self.assertIn(qubits, self.backend.target[op_name])
@data(0, 1, 2, 3)
def test_triple_circuit(self, opt_level):
"""Test a split circuit with one circuit component per chip."""
qc = QuantumCircuit(30)
qc.h(0)
qc.h(10)
qc.h(20)
qc.cx(0, 1)
qc.cx(0, 2)
qc.cx(0, 3)
qc.cx(0, 4)
qc.cx(0, 5)
qc.cx(0, 6)
qc.cx(0, 7)
qc.cx(0, 8)
qc.cx(0, 9)
qc.ecr(10, 11)
qc.ecr(10, 12)
qc.ecr(10, 13)
qc.ecr(10, 14)
qc.ecr(10, 15)
qc.ecr(10, 16)
qc.ecr(10, 17)
qc.ecr(10, 18)
qc.ecr(10, 19)
qc.cy(20, 21)
qc.cy(20, 22)
qc.cy(20, 23)
qc.cy(20, 24)
qc.cy(20, 25)
qc.cy(20, 26)
qc.cy(20, 27)
qc.cy(20, 28)
qc.cy(20, 29)
qc.measure_all()
if opt_level == 0:
with self.assertRaises(TranspilerError):
tqc = transpile(qc, self.backend, optimization_level=opt_level, seed_transpiler=42)
return
tqc = transpile(qc, self.backend, optimization_level=opt_level, seed_transpiler=42)
for inst in tqc.data:
qubits = tuple(tqc.find_bit(x).index for x in inst.qubits)
op_name = inst.operation.name
if op_name == "barrier":
continue
self.assertIn(qubits, self.backend.target[op_name])
def test_disjoint_control_flow(self):
"""Test control flow circuit on disjoint coupling map."""
qc = QuantumCircuit(6, 1)
qc.h(0)
qc.ecr(0, 1)
qc.cx(0, 2)
qc.measure(0, 0)
with qc.if_test((qc.clbits[0], True)):
qc.reset(0)
qc.cz(1, 0)
qc.h(3)
qc.cz(3, 4)
qc.cz(3, 5)
target = self.backend.target
target.add_instruction(Reset(), {(i,): None for i in range(target.num_qubits)})
target.add_instruction(IfElseOp, name="if_else")
tqc = transpile(qc, target=target, seed_transpiler=42)
edges = set(target.build_coupling_map().graph.edge_list())
def _visit_block(circuit, qubit_mapping=None):
for instruction in circuit:
if instruction.operation.name == "barrier":
continue
qargs = tuple(qubit_mapping[x] for x in instruction.qubits)
self.assertTrue(target.instruction_supported(instruction.operation.name, qargs))
if isinstance(instruction.operation, ControlFlowOp):
for block in instruction.operation.blocks:
new_mapping = {
inner: qubit_mapping[outer]
for outer, inner in zip(instruction.qubits, block.qubits)
}
_visit_block(block, new_mapping)
elif len(qargs) == 2:
self.assertIn(qargs, edges)
self.assertIn(instruction.operation.name, target)
_visit_block(
tqc,
qubit_mapping={qubit: index for index, qubit in enumerate(tqc.qubits)},
)
def test_disjoint_control_flow_shared_classical(self):
"""Test circuit with classical data dependency between connected components."""
creg = ClassicalRegister(19)
qc = QuantumCircuit(25)
qc.add_register(creg)
qc.h(0)
for i in range(18):
qc.cx(0, i + 1)
for i in range(18):
qc.measure(i, creg[i])
with qc.if_test((creg, 0)):
qc.h(20)
qc.ecr(20, 21)
qc.ecr(20, 22)
qc.ecr(20, 23)
qc.ecr(20, 24)
target = self.backend.target
target.add_instruction(Reset(), {(i,): None for i in range(target.num_qubits)})
target.add_instruction(IfElseOp, name="if_else")
tqc = transpile(qc, target=target, seed_transpiler=42)
def _visit_block(circuit, qubit_mapping=None):
for instruction in circuit:
if instruction.operation.name == "barrier":
continue
qargs = tuple(qubit_mapping[x] for x in instruction.qubits)
self.assertTrue(target.instruction_supported(instruction.operation.name, qargs))
if isinstance(instruction.operation, ControlFlowOp):
for block in instruction.operation.blocks:
new_mapping = {
inner: qubit_mapping[outer]
for outer, inner in zip(instruction.qubits, block.qubits)
}
_visit_block(block, new_mapping)
_visit_block(
tqc,
qubit_mapping={qubit: index for index, qubit in enumerate(tqc.qubits)},
)
@slow_test
@data(2, 3)
def test_six_component_circuit(self, opt_level):
"""Test input circuit with more than 1 component per backend component."""
qc = QuantumCircuit(42)
qc.h(0)
qc.h(10)
qc.h(20)
qc.cx(0, 1)
qc.cx(0, 2)
qc.cx(0, 3)
qc.cx(0, 4)
qc.cx(0, 5)
qc.cx(0, 6)
qc.cx(0, 7)
qc.cx(0, 8)
qc.cx(0, 9)
qc.ecr(10, 11)
qc.ecr(10, 12)
qc.ecr(10, 13)
qc.ecr(10, 14)
qc.ecr(10, 15)
qc.ecr(10, 16)
qc.ecr(10, 17)
qc.ecr(10, 18)
qc.ecr(10, 19)
qc.cy(20, 21)
qc.cy(20, 22)
qc.cy(20, 23)
qc.cy(20, 24)
qc.cy(20, 25)
qc.cy(20, 26)
qc.cy(20, 27)
qc.cy(20, 28)
qc.cy(20, 29)
qc.h(30)
qc.cx(30, 31)
qc.cx(30, 32)
qc.cx(30, 33)
qc.h(34)
qc.cx(34, 35)
qc.cx(34, 36)
qc.cx(34, 37)
qc.h(38)
qc.cx(38, 39)
qc.cx(39, 40)
qc.cx(39, 41)
qc.measure_all()
tqc = transpile(qc, self.backend, optimization_level=opt_level, seed_transpiler=42)
for inst in tqc.data:
qubits = tuple(tqc.find_bit(x).index for x in inst.qubits)
op_name = inst.operation.name
if op_name == "barrier":
continue
self.assertIn(qubits, self.backend.target[op_name])
def test_six_component_circuit_level_1(self):
"""Test input circuit with more than 1 component per backend component."""
opt_level = 1
qc = QuantumCircuit(42)
qc.h(0)
qc.h(10)
qc.h(20)
qc.cx(0, 1)
qc.cx(0, 2)
qc.cx(0, 3)
qc.cx(0, 4)
qc.cx(0, 5)
qc.cx(0, 6)
qc.cx(0, 7)
qc.cx(0, 8)
qc.cx(0, 9)
qc.ecr(10, 11)
qc.ecr(10, 12)
qc.ecr(10, 13)
qc.ecr(10, 14)
qc.ecr(10, 15)
qc.ecr(10, 16)
qc.ecr(10, 17)
qc.ecr(10, 18)
qc.ecr(10, 19)
qc.cy(20, 21)
qc.cy(20, 22)
qc.cy(20, 23)
qc.cy(20, 24)
qc.cy(20, 25)
qc.cy(20, 26)
qc.cy(20, 27)
qc.cy(20, 28)
qc.cy(20, 29)
qc.h(30)
qc.cx(30, 31)
qc.cx(30, 32)
qc.cx(30, 33)
qc.h(34)
qc.cx(34, 35)
qc.cx(34, 36)
qc.cx(34, 37)
qc.h(38)
qc.cx(38, 39)
qc.cx(39, 40)
qc.cx(39, 41)
qc.measure_all()
tqc = transpile(qc, self.backend, optimization_level=opt_level, seed_transpiler=42)
for inst in tqc.data:
qubits = tuple(tqc.find_bit(x).index for x in inst.qubits)
op_name = inst.operation.name
if op_name == "barrier":
continue
self.assertIn(qubits, self.backend.target[op_name])
@data(0, 1, 2, 3)
def test_shared_classical_between_components_condition(self, opt_level):
"""Test a condition sharing classical bits between components."""
creg = ClassicalRegister(19)
qc = QuantumCircuit(25)
qc.add_register(creg)
qc.h(0)
for i in range(18):
qc.cx(0, i + 1)
for i in range(18):
qc.measure(i, creg[i])
with self.assertWarns(DeprecationWarning):
qc.ecr(20, 21).c_if(creg, 0)
tqc = transpile(qc, self.backend, optimization_level=opt_level, seed_transpiler=42)
def _visit_block(circuit, qubit_mapping=None):
for instruction in circuit:
if instruction.operation.name == "barrier":
continue
qargs = tuple(qubit_mapping[x] for x in instruction.qubits)
self.assertTrue(
self.backend.target.instruction_supported(instruction.operation.name, qargs)
)
if isinstance(instruction.operation, ControlFlowOp):
for block in instruction.operation.blocks:
new_mapping = {
inner: qubit_mapping[outer]
for outer, inner in zip(instruction.qubits, block.qubits)
}
_visit_block(block, new_mapping)
_visit_block(
tqc,
qubit_mapping={qubit: index for index, qubit in enumerate(tqc.qubits)},
)
@data(0, 1, 2, 3)
def test_shared_classical_between_components_condition_large_to_small(self, opt_level):
"""Test a condition sharing classical bits between components."""
creg = ClassicalRegister(2)
qc = QuantumCircuit(25)
qc.add_register(creg)
# Component 0
qc.h(24)
qc.cx(24, 23)
qc.measure(24, creg[0])
qc.measure(23, creg[1])
# Component 1
with self.assertWarns(DeprecationWarning):
qc.h(0).c_if(creg, 0)
for i in range(18):
with self.assertWarns(DeprecationWarning):
qc.ecr(0, i + 1).c_if(creg, 0)
tqc = transpile(qc, self.backend, optimization_level=opt_level, seed_transpiler=123456789)
def _visit_block(circuit, qubit_mapping=None):
for instruction in circuit:
if instruction.operation.name == "barrier":
continue
qargs = tuple(qubit_mapping[x] for x in instruction.qubits)
self.assertTrue(
self.backend.target.instruction_supported(instruction.operation.name, qargs)
)
if isinstance(instruction.operation, ControlFlowOp):
for block in instruction.operation.blocks:
new_mapping = {
inner: qubit_mapping[outer]
for outer, inner in zip(instruction.qubits, block.qubits)
}
_visit_block(block, new_mapping)
_visit_block(
tqc,
qubit_mapping={qubit: index for index, qubit in enumerate(tqc.qubits)},
)
# Check that virtual qubits that interact with each other via quantum links are placed into
# the same component of the coupling map.
initial_layout = tqc.layout.initial_layout
coupling_map = self.backend.target.build_coupling_map()
components = [
connected_qubits(initial_layout[qc.qubits[23]], coupling_map),
connected_qubits(initial_layout[qc.qubits[0]], coupling_map),
]
self.assertLessEqual({initial_layout[qc.qubits[i]] for i in [23, 24]}, components[0])
self.assertLessEqual({initial_layout[qc.qubits[i]] for i in range(19)}, components[1])
# Check clbits are in order.
# Traverse the output dag over the sole clbit, checking that the qubits of the ops
# go in order between the components. This is a sanity check to ensure that routing
# doesn't reorder a classical data dependency between components. Inside a component
# we have the dag ordering so nothing should be out of order within a component.
tqc_dag = circuit_to_dag(tqc)
qubit_map = {qubit: index for index, qubit in enumerate(tqc_dag.qubits)}
input_node = tqc_dag.input_map[tqc_dag.clbits[0]]
first_meas_node = tqc_dag._find_successors_by_edge(
input_node._node_id, lambda edge_data: isinstance(edge_data, Clbit)
)[0]
# The first node should be a measurement
self.assertIsInstance(first_meas_node.op, Measure)
# This should be in the first component
self.assertIn(qubit_map[first_meas_node.qargs[0]], components[0])
op_node = tqc_dag._find_successors_by_edge(
first_meas_node._node_id, lambda edge_data: isinstance(edge_data, Clbit)
)[0]
while isinstance(op_node, DAGOpNode):
self.assertIn(qubit_map[op_node.qargs[0]], components[1])
op_node = tqc_dag._find_successors_by_edge(
op_node._node_id, lambda edge_data: isinstance(edge_data, Clbit)
)[0]
@data(1, 2, 3)
def test_shared_classical_between_components_condition_large_to_small_reverse_index(
self, opt_level
):
"""Test a condition sharing classical bits between components."""
creg = ClassicalRegister(2)
qc = QuantumCircuit(25)
qc.add_register(creg)
# Component 0
qc.h(0)
qc.cx(0, 1)
qc.measure(0, creg[0])
qc.measure(1, creg[1])
# Component 1
with self.assertWarns(DeprecationWarning):
qc.h(24).c_if(creg, 0)
for i in range(23, 5, -1):
with self.assertWarns(DeprecationWarning):
qc.ecr(24, i).c_if(creg, 0)
tqc = transpile(qc, self.backend, optimization_level=opt_level, seed_transpiler=2023)
def _visit_block(circuit, qubit_mapping=None):
for instruction in circuit:
if instruction.operation.name == "barrier":
continue
qargs = tuple(qubit_mapping[x] for x in instruction.qubits)
self.assertTrue(
self.backend.target.instruction_supported(instruction.operation.name, qargs)
)
if isinstance(instruction.operation, ControlFlowOp):
for block in instruction.operation.blocks:
new_mapping = {
inner: qubit_mapping[outer]
for outer, inner in zip(instruction.qubits, block.qubits)
}
_visit_block(block, new_mapping)
_visit_block(
tqc,
qubit_mapping={qubit: index for index, qubit in enumerate(tqc.qubits)},
)
# Check that virtual qubits that interact with each other via quantum links are placed into
# the same component of the coupling map.
initial_layout = tqc.layout.initial_layout
coupling_map = self.backend.target.build_coupling_map()
components = [
connected_qubits(initial_layout[qc.qubits[0]], coupling_map),
connected_qubits(initial_layout[qc.qubits[6]], coupling_map),
]
self.assertLessEqual({initial_layout[qc.qubits[i]] for i in range(2)}, components[0])
self.assertLessEqual({initial_layout[qc.qubits[i]] for i in range(6, 25)}, components[1])
# Check clbits are in order.
# Traverse the output dag over the sole clbit, checking that the qubits of the ops
# go in order between the components. This is a sanity check to ensure that routing
# doesn't reorder a classical data dependency between components. Inside a component
# we have the dag ordering so nothing should be out of order within a component.
tqc_dag = circuit_to_dag(tqc)
qubit_map = {qubit: index for index, qubit in enumerate(tqc_dag.qubits)}
input_node = tqc_dag.input_map[tqc_dag.clbits[0]]
first_meas_node = tqc_dag._find_successors_by_edge(
input_node._node_id, lambda edge_data: isinstance(edge_data, Clbit)
)[0]
# The first node should be a measurement
self.assertIsInstance(first_meas_node.op, Measure)
# This should be in the first component
self.assertIn(qubit_map[first_meas_node.qargs[0]], components[0])
op_node = tqc_dag._find_successors_by_edge(
first_meas_node._node_id, lambda edge_data: isinstance(edge_data, Clbit)
)[0]
while isinstance(op_node, DAGOpNode):
self.assertIn(qubit_map[op_node.qargs[0]], components[1])
op_node = tqc_dag._find_successors_by_edge(
op_node._node_id, lambda edge_data: isinstance(edge_data, Clbit)
)[0]
@data(1, 2, 3)
def test_chained_data_dependency(self, opt_level):
"""Test 3 component circuit with shared clbits between each component."""
creg = ClassicalRegister(1)
qc = QuantumCircuit(30)
qc.add_register(creg)
# Component 0
qc.h(0)
for i in range(9):
qc.cx(0, i + 1)
measure_op = Measure()
qc.append(measure_op, [9], [creg[0]])
# Component 1
with self.assertWarns(DeprecationWarning):
qc.h(10).c_if(creg, 0)
for i in range(11, 20):
with self.assertWarns(DeprecationWarning):
qc.ecr(10, i).c_if(creg, 0)
measure_op = Measure()
qc.append(measure_op, [19], [creg[0]])
# Component 2
with self.assertWarns(DeprecationWarning):
qc.h(20).c_if(creg, 0)
for i in range(21, 30):
with self.assertWarns(DeprecationWarning):
qc.cz(20, i).c_if(creg, 0)
measure_op = Measure()
qc.append(measure_op, [29], [creg[0]])
tqc = transpile(qc, self.backend, optimization_level=opt_level, seed_transpiler=2023)
def _visit_block(circuit, qubit_mapping=None):
for instruction in circuit:
if instruction.operation.name == "barrier":
continue
qargs = tuple(qubit_mapping[x] for x in instruction.qubits)
self.assertTrue(
self.backend.target.instruction_supported(instruction.operation.name, qargs)
)
if isinstance(instruction.operation, ControlFlowOp):
for block in instruction.operation.blocks:
new_mapping = {
inner: qubit_mapping[outer]
for outer, inner in zip(instruction.qubits, block.qubits)
}
_visit_block(block, new_mapping)
_visit_block(
tqc,
qubit_mapping={qubit: index for index, qubit in enumerate(tqc.qubits)},
)
# Check that virtual qubits that interact with each other via quantum links are placed into
# the same component of the coupling map.
initial_layout = tqc.layout.initial_layout
coupling_map = self.backend.target.build_coupling_map()
components = [
connected_qubits(initial_layout[qc.qubits[0]], coupling_map),
connected_qubits(initial_layout[qc.qubits[10]], coupling_map),
connected_qubits(initial_layout[qc.qubits[20]], coupling_map),
]
self.assertLessEqual({initial_layout[qc.qubits[i]] for i in range(10)}, components[0])
self.assertLessEqual({initial_layout[qc.qubits[i]] for i in range(10, 20)}, components[1])
self.assertLessEqual({initial_layout[qc.qubits[i]] for i in range(20, 30)}, components[2])
# Check clbits are in order.
# Traverse the output dag over the sole clbit, checking that the qubits of the ops
# go in order between the components. This is a sanity check to ensure that routing
# doesn't reorder a classical data dependency between components. Inside a component
# we have the dag ordering so nothing should be out of order within a component.
tqc_dag = circuit_to_dag(tqc)
qubit_map = {qubit: index for index, qubit in enumerate(tqc_dag.qubits)}
input_node = tqc_dag.input_map[tqc_dag.clbits[0]]
first_meas_node = tqc_dag._find_successors_by_edge(
input_node._node_id, lambda edge_data: isinstance(edge_data, Clbit)
)[0]
self.assertIsInstance(first_meas_node.op, Measure)
self.assertIn(qubit_map[first_meas_node.qargs[0]], components[0])
op_node = tqc_dag._find_successors_by_edge(
first_meas_node._node_id, lambda edge_data: isinstance(edge_data, Clbit)
)[0]
while not isinstance(op_node.op, Measure):
self.assertIn(qubit_map[op_node.qargs[0]], components[1])
op_node = tqc_dag._find_successors_by_edge(
op_node._node_id, lambda edge_data: isinstance(edge_data, Clbit)
)[0]
self.assertIn(qubit_map[op_node.qargs[0]], components[1])
op_node = tqc_dag._find_successors_by_edge(
op_node._node_id, lambda edge_data: isinstance(edge_data, Clbit)
)[0]
while not isinstance(op_node.op, Measure):
self.assertIn(qubit_map[op_node.qargs[0]], components[2])
op_node = tqc_dag._find_successors_by_edge(
op_node._node_id, lambda edge_data: isinstance(edge_data, Clbit)
)[0]
self.assertIn(qubit_map[op_node.qargs[0]], components[2])
@data("sabre", "basic", "lookahead")
def test_basic_connected_circuit_dense_layout(self, routing_method):
"""Test basic connected circuit on disjoint backend"""
qc = QuantumCircuit(5)
qc.h(0)
qc.cx(0, 1)
qc.cx(0, 2)
qc.cx(0, 3)
qc.cx(0, 4)
qc.measure_all()
tqc = transpile(
qc,
self.backend,
layout_method="dense",
routing_method=routing_method,
seed_transpiler=42,
)
for inst in tqc.data:
qubits = tuple(tqc.find_bit(x).index for x in inst.qubits)
op_name = inst.operation.name
if op_name == "barrier":
continue
self.assertIn(qubits, self.backend.target[op_name])
@data("stochastic")
def test_basic_connected_circuit_dense_layout_stochastic(self, routing_method):
"""Test basic connected circuit on disjoint backend for deprecated stochastic swap"""
# TODO: Remove when StochasticSwap is removed
qc = QuantumCircuit(5)
qc.h(0)
qc.cx(0, 1)
qc.cx(0, 2)
qc.cx(0, 3)
qc.cx(0, 4)
qc.measure_all()
with self.assertWarns(DeprecationWarning):
tqc = transpile(
qc,
self.backend,
layout_method="dense",
routing_method=routing_method,
seed_transpiler=42,
)
for inst in tqc.data:
qubits = tuple(tqc.find_bit(x).index for x in inst.qubits)
op_name = inst.operation.name
if op_name == "barrier":
continue
self.assertIn(qubits, self.backend.target[op_name])
# Lookahead swap skipped for performance
@data("sabre", "basic")
def test_triple_circuit_dense_layout(self, routing_method):
"""Test a split circuit with one circuit component per chip."""
qc = QuantumCircuit(30)
qc.h(0)
qc.h(10)
qc.h(20)
qc.cx(0, 1)
qc.cx(0, 2)
qc.cx(0, 3)
qc.cx(0, 4)
qc.cx(0, 5)
qc.cx(0, 6)
qc.cx(0, 7)
qc.cx(0, 8)
qc.cx(0, 9)
qc.ecr(10, 11)
qc.ecr(10, 12)
qc.ecr(10, 13)
qc.ecr(10, 14)
qc.ecr(10, 15)
qc.ecr(10, 16)
qc.ecr(10, 17)
qc.ecr(10, 18)
qc.ecr(10, 19)
qc.cy(20, 21)
qc.cy(20, 22)
qc.cy(20, 23)
qc.cy(20, 24)
qc.cy(20, 25)
qc.cy(20, 26)
qc.cy(20, 27)
qc.cy(20, 28)
qc.cy(20, 29)
qc.measure_all()
tqc = transpile(
qc,
self.backend,
layout_method="dense",
routing_method=routing_method,
seed_transpiler=42,
)
for inst in tqc.data:
qubits = tuple(tqc.find_bit(x).index for x in inst.qubits)
op_name = inst.operation.name
if op_name == "barrier":
continue
self.assertIn(qubits, self.backend.target[op_name])
@data("stochastic")
def test_triple_circuit_dense_layout_stochastic(self, routing_method):
"""Test a split circuit with one circuit component per chip for deprecated StochasticSwap."""
# TODO: Remove when StochasticSwap is removed
qc = QuantumCircuit(30)
qc.h(0)
qc.h(10)
qc.h(20)
qc.cx(0, 1)
qc.cx(0, 2)
qc.cx(0, 3)
qc.cx(0, 4)
qc.cx(0, 5)
qc.cx(0, 6)
qc.cx(0, 7)
qc.cx(0, 8)
qc.cx(0, 9)
qc.ecr(10, 11)
qc.ecr(10, 12)
qc.ecr(10, 13)
qc.ecr(10, 14)
qc.ecr(10, 15)
qc.ecr(10, 16)
qc.ecr(10, 17)
qc.ecr(10, 18)
qc.ecr(10, 19)
qc.cy(20, 21)
qc.cy(20, 22)
qc.cy(20, 23)
qc.cy(20, 24)
qc.cy(20, 25)
qc.cy(20, 26)
qc.cy(20, 27)
qc.cy(20, 28)
qc.cy(20, 29)
qc.measure_all()
with self.assertWarns(DeprecationWarning):
tqc = transpile(
qc,
self.backend,
layout_method="dense",
routing_method=routing_method,
seed_transpiler=42,
)
for inst in tqc.data:
qubits = tuple(tqc.find_bit(x).index for x in inst.qubits)
op_name = inst.operation.name
if op_name == "barrier":
continue
self.assertIn(qubits, self.backend.target[op_name])
@data("sabre", "basic", "lookahead")
def test_triple_circuit_invalid_layout(self, routing_method):
"""Test a split circuit with one circuit component per chip."""
qc = QuantumCircuit(30)
qc.h(0)
qc.h(10)
qc.h(20)
qc.cx(0, 1)
qc.cx(0, 2)
qc.cx(0, 3)
qc.cx(0, 4)
qc.cx(0, 5)
qc.cx(0, 6)
qc.cx(0, 7)
qc.cx(0, 8)
qc.cx(0, 9)
qc.ecr(10, 11)
qc.ecr(10, 12)
qc.ecr(10, 13)
qc.ecr(10, 14)
qc.ecr(10, 15)
qc.ecr(10, 16)
qc.ecr(10, 17)
qc.ecr(10, 18)
qc.ecr(10, 19)
qc.cy(20, 21)
qc.cy(20, 22)
qc.cy(20, 23)
qc.cy(20, 24)
qc.cy(20, 25)
qc.cy(20, 26)
qc.cy(20, 27)
qc.cy(20, 28)
qc.cy(20, 29)
qc.measure_all()
with self.assertRaises(TranspilerError):
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="The `target` parameter should be used instead",
):
if routing_method == "stochastic":
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="The StochasticSwap transpilation pass is a suboptimal",
):
transpile(
qc,
self.backend,
layout_method="trivial",
routing_method=routing_method,
seed_transpiler=42,
)
else:
transpile(
qc,
self.backend,
layout_method="trivial",
routing_method=routing_method,
seed_transpiler=42,
)
@data("stochastic")
def test_triple_circuit_invalid_layout_stochastic(self, routing_method):
"""Test a split circuit with one circuit component per chip for deprecated ``StochasticSwap``"""
# TODO: Remove when StochasticSwap is removed
qc = QuantumCircuit(30)
qc.h(0)
qc.h(10)
qc.h(20)
qc.cx(0, 1)
qc.cx(0, 2)
qc.cx(0, 3)
qc.cx(0, 4)
qc.cx(0, 5)
qc.cx(0, 6)
qc.cx(0, 7)
qc.cx(0, 8)
qc.cx(0, 9)
qc.ecr(10, 11)
qc.ecr(10, 12)
qc.ecr(10, 13)
qc.ecr(10, 14)
qc.ecr(10, 15)
qc.ecr(10, 16)
qc.ecr(10, 17)
qc.ecr(10, 18)
qc.ecr(10, 19)
qc.cy(20, 21)
qc.cy(20, 22)
qc.cy(20, 23)
qc.cy(20, 24)
qc.cy(20, 25)
qc.cy(20, 26)
qc.cy(20, 27)
qc.cy(20, 28)
qc.cy(20, 29)
qc.measure_all()
with self.assertRaises(TranspilerError):
if routing_method == "stochastic":
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="The StochasticSwap transpilation pass is a suboptimal",
):
transpile(
qc,
self.backend,
layout_method="trivial",
routing_method=routing_method,
seed_transpiler=42,
)
else:
transpile(
qc,
self.backend,
layout_method="trivial",
routing_method=routing_method,
seed_transpiler=42,
)
# Lookahead swap skipped for performance reasons, stochastic moved to new test due to deprecation
@data("sabre", "basic")
def test_six_component_circuit_dense_layout(self, routing_method):
"""Test input circuit with more than 1 component per backend component."""
qc = QuantumCircuit(42)
qc.h(0)
qc.h(10)
qc.h(20)
qc.cx(0, 1)
qc.cx(0, 2)
qc.cx(0, 3)
qc.cx(0, 4)
qc.cx(0, 5)
qc.cx(0, 6)
qc.cx(0, 7)
qc.cx(0, 8)
qc.cx(0, 9)
qc.ecr(10, 11)
qc.ecr(10, 12)
qc.ecr(10, 13)
qc.ecr(10, 14)
qc.ecr(10, 15)
qc.ecr(10, 16)
qc.ecr(10, 17)
qc.ecr(10, 18)
qc.ecr(10, 19)
qc.cy(20, 21)
qc.cy(20, 22)
qc.cy(20, 23)
qc.cy(20, 24)
qc.cy(20, 25)
qc.cy(20, 26)
qc.cy(20, 27)
qc.cy(20, 28)
qc.cy(20, 29)
qc.h(30)
qc.cx(30, 31)
qc.cx(30, 32)
qc.cx(30, 33)
qc.h(34)
qc.cx(34, 35)
qc.cx(34, 36)
qc.cx(34, 37)
qc.h(38)
qc.cx(38, 39)
qc.cx(39, 40)
qc.cx(39, 41)
qc.measure_all()
tqc = transpile(
qc,
self.backend,
layout_method="dense",
routing_method=routing_method,
seed_transpiler=42,
)
for inst in tqc.data:
qubits = tuple(tqc.find_bit(x).index for x in inst.qubits)
op_name = inst.operation.name
if op_name == "barrier":
continue
self.assertIn(qubits, self.backend.target[op_name])
# Lookahead swap skipped for performance reasons
@data("stochastic")
def test_six_component_circuit_dense_layout_stochastic(self, routing_method):
"""Test input circuit with more than 1 component per backend component
for deprecated ``StochasticSwap``."""
# TODO: Remove when StochasticSwap is removed
qc = QuantumCircuit(42)
qc.h(0)
qc.h(10)
qc.h(20)
qc.cx(0, 1)
qc.cx(0, 2)
qc.cx(0, 3)
qc.cx(0, 4)
qc.cx(0, 5)
qc.cx(0, 6)
qc.cx(0, 7)
qc.cx(0, 8)
qc.cx(0, 9)
qc.ecr(10, 11)
qc.ecr(10, 12)
qc.ecr(10, 13)
qc.ecr(10, 14)
qc.ecr(10, 15)
qc.ecr(10, 16)
qc.ecr(10, 17)
qc.ecr(10, 18)
qc.ecr(10, 19)
qc.cy(20, 21)
qc.cy(20, 22)
qc.cy(20, 23)
qc.cy(20, 24)
qc.cy(20, 25)
qc.cy(20, 26)
qc.cy(20, 27)
qc.cy(20, 28)
qc.cy(20, 29)
qc.h(30)
qc.cx(30, 31)
qc.cx(30, 32)
qc.cx(30, 33)
qc.h(34)
qc.cx(34, 35)
qc.cx(34, 36)
qc.cx(34, 37)
qc.h(38)
qc.cx(38, 39)
qc.cx(39, 40)
qc.cx(39, 41)
qc.measure_all()
with self.assertWarns(DeprecationWarning):
tqc = transpile(
qc,
self.backend,
layout_method="dense",
routing_method=routing_method,
seed_transpiler=42,
)
for inst in tqc.data:
qubits = tuple(tqc.find_bit(x).index for x in inst.qubits)
op_name = inst.operation.name
if op_name == "barrier":
continue
self.assertIn(qubits, self.backend.target[op_name])
@data(0, 1, 2, 3)
def test_transpile_target_with_qubits_without_ops(self, opt_level):
"""Test qubits without operations aren't ever used."""
target = Target(num_qubits=5)
target.add_instruction(XGate(), {(i,): InstructionProperties(error=0.5) for i in range(3)})
target.add_instruction(HGate(), {(i,): InstructionProperties(error=0.5) for i in range(3)})
target.add_instruction(
CXGate(), {edge: InstructionProperties(error=0.5) for edge in [(0, 1), (1, 2), (2, 0)]}
)
qc = QuantumCircuit(3)
qc.x(0)
qc.cx(0, 1)
qc.cx(0, 2)
tqc = transpile(qc, target=target, optimization_level=opt_level, seed_transpiler=42)
invalid_qubits = {3, 4}
self.assertEqual(tqc.num_qubits, 5)
for inst in tqc.data:
for bit in inst.qubits:
self.assertNotIn(tqc.find_bit(bit).index, invalid_qubits)
@data(0, 1, 2, 3)
def test_transpile_target_with_qubits_without_ops_with_routing(self, opt_level):
"""Test qubits without operations aren't ever used."""
target = Target(num_qubits=5)
target.add_instruction(XGate(), {(i,): InstructionProperties(error=0.5) for i in range(4)})
target.add_instruction(HGate(), {(i,): InstructionProperties(error=0.5) for i in range(4)})
target.add_instruction(
CXGate(),
{edge: InstructionProperties(error=0.5) for edge in [(0, 1), (1, 2), (2, 0), (2, 3)]},
)
qc = QuantumCircuit(4)
qc.x(0)
qc.cx(0, 1)
qc.cx(0, 2)
qc.cx(1, 3)
qc.cx(0, 3)
tqc = transpile(qc, target=target, optimization_level=opt_level, seed_transpiler=42)
invalid_qubits = {
4,
}
self.assertEqual(tqc.num_qubits, 5)
for inst in tqc.data:
for bit in inst.qubits:
self.assertNotIn(tqc.find_bit(bit).index, invalid_qubits)
@data(0, 1, 2, 3)
def test_transpile_target_with_qubits_without_ops_circuit_too_large(self, opt_level):
"""Test qubits without operations aren't ever used and error if circuit needs them."""
target = Target(num_qubits=5)
target.add_instruction(XGate(), {(i,): InstructionProperties(error=0.5) for i in range(3)})
target.add_instruction(HGate(), {(i,): InstructionProperties(error=0.5) for i in range(3)})
target.add_instruction(
CXGate(), {edge: InstructionProperties(error=0.5) for edge in [(0, 1), (1, 2), (2, 0)]}
)
qc = QuantumCircuit(4)
qc.x(0)
qc.cx(0, 1)
qc.cx(0, 2)
qc.cx(0, 3)
with self.assertRaises(TranspilerError):
transpile(qc, target=target, optimization_level=opt_level, seed_transpiler=42)
@data(0, 1, 2, 3)
def test_transpile_target_with_qubits_without_ops_circuit_too_large_disconnected(
self, opt_level
):
"""Test qubits without operations aren't ever used if a disconnected circuit needs them."""
target = Target(num_qubits=5)
target.add_instruction(XGate(), {(i,): InstructionProperties(error=0.5) for i in range(3)})
target.add_instruction(HGate(), {(i,): InstructionProperties(error=0.5) for i in range(3)})
target.add_instruction(
CXGate(), {edge: InstructionProperties(error=0.5) for edge in [(0, 1), (1, 2), (2, 0)]}
)
qc = QuantumCircuit(5)
qc.x(0)
qc.x(1)
qc.x(3)
qc.x(4)
with self.assertRaises(TranspilerError):
transpile(qc, target=target, optimization_level=opt_level, seed_transpiler=42)
@data(0, 1, 2, 3)
def test_barrier_no_leak_disjoint_connectivity(self, opt_level):
"""Test that we don't leak an internal labelled barrier from disjoint layout processing."""
cmap = CouplingMap([(0, 1), (1, 2), (3, 4)])
qc = QuantumCircuit(cmap.size(), cmap.size())
qc.cx(0, 1)
qc.cx(1, 2)
qc.cx(2, 0)
qc.cx(3, 4)
qc.measure(qc.qubits, qc.clbits)
out = transpile(qc, coupling_map=cmap, optimization_level=opt_level)
self.assertNotIn("barrier", out.count_ops())
@data(0, 1, 2, 3)
def test_transpile_does_not_affect_backend_coupling(self, opt_level):
"""Test that transpiliation of a circuit does not mutate the `CouplingMap` stored by a V2
backend. Regression test of gh-9997."""
qc = QuantumCircuit(127)
for i in range(1, 127):
qc.ecr(0, i)
backend = GenericBackendV2(num_qubits=130)
original_map = copy.deepcopy(backend.coupling_map)
transpile(qc, backend, optimization_level=opt_level, seed_transpiler=42)
self.assertEqual(original_map, backend.coupling_map)
@combine(
optimization_level=[0, 1, 2, 3],
scheduling_method=["asap", "alap"],
)
def test_transpile_target_with_qubits_without_delays_with_scheduling(
self, optimization_level, scheduling_method
):
"""Test qubits without operations aren't ever used."""
no_delay_qubits = [1, 3, 4]
target = Target(num_qubits=5, dt=1)
target.add_instruction(
XGate(), {(i,): InstructionProperties(duration=160) for i in range(4)}
)
target.add_instruction(
HGate(), {(i,): InstructionProperties(duration=160) for i in range(4)}
)
target.add_instruction(
CXGate(),
{
edge: InstructionProperties(duration=800)
for edge in [(0, 1), (1, 2), (2, 0), (2, 3)]
},
)
target.add_instruction(
Delay(Parameter("t")), {(i,): None for i in range(4) if i not in no_delay_qubits}
)
qc = QuantumCircuit(4)
qc.x(0)
qc.cx(0, 1)
qc.cx(0, 2)
qc.cx(1, 3)
qc.cx(0, 3)
tqc = transpile(
qc,
target=target,
optimization_level=optimization_level,
scheduling_method=scheduling_method,
seed_transpiler=42,
)
invalid_qubits = {
4,
}
self.assertEqual(tqc.num_qubits, 5)
for inst in tqc.data:
for bit in inst.qubits:
self.assertNotIn(tqc.find_bit(bit).index, invalid_qubits)
if isinstance(inst.operation, Delay):
self.assertNotIn(tqc.find_bit(bit).index, no_delay_qubits)