Skip to content

Commit eaf8dca

Browse files
committed
Fix basis_gates and coupling_map backend override in transpile()
This commit fixes an issue in the transpile() function when a user specified the `backend` argument with a BackendV2 based backend along with `basis_gates` or `coupling_map`. In this case the `transpile()` was generating the preset pass manager with a target, coupling map, and basis gates list. However, most individual passes that take basis gates and a Target will prefer to use the target if both are specified. This is generally sane behavior at the pass level because the target contains more rich data and tighter constraints that a transpiler pass will need to worry about. To fix this limitation this commit updates transpile() to not use the backend's target if either basis_gates or coupling_map are specified. Longer term this should no longer be an issue when Qiskit#9256 is implemented and we'll be relying solely on a target internally. But this fix is needed until Qiskit#9256 is started. Fixes Qiskit#9781
1 parent c2affb1 commit eaf8dca

File tree

2 files changed

+77
-1
lines changed

2 files changed

+77
-1
lines changed

qiskit/compiler/transpiler.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,11 @@ def _parse_transpile_args(
645645
timing_constraints = target.timing_constraints()
646646
if backend_properties is None:
647647
backend_properties = target_to_backend_properties(target)
648+
# If target is not specified and any hardware constraint object is
649+
# manually specified then do not use the target from the backend as
650+
# it is invalidated by a custom basis gate list or a custom coupling map
651+
elif basis_gates is None and coupling_map is None:
652+
target = _parse_target(backend, target)
648653

649654
basis_gates = _parse_basis_gates(basis_gates, backend)
650655
initial_layout = _parse_initial_layout(initial_layout, circuits)
@@ -658,7 +663,6 @@ def _parse_transpile_args(
658663
callback = _parse_callback(callback, num_circuits)
659664
durations = _parse_instruction_durations(backend, instruction_durations, dt, circuits)
660665
timing_constraints = _parse_timing_constraints(backend, timing_constraints, num_circuits)
661-
target = _parse_target(backend, target)
662666
if scheduling_method and any(d is None for d in durations):
663667
raise TranspilerError(
664668
"Transpiling a circuit with a scheduling method"

test/python/compiler/test_transpiler.py

+72
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
FakeRueschlikon,
5252
FakeBoeblingen,
5353
FakeMumbaiV2,
54+
FakeNairobiV2,
5455
)
5556
from qiskit.transpiler import Layout, CouplingMap
5657
from qiskit.transpiler import PassManager, TransformationPass
@@ -61,6 +62,7 @@
6162
from qiskit.transpiler.passmanager_config import PassManagerConfig
6263
from qiskit.transpiler.preset_passmanagers import level_0_pass_manager
6364
from qiskit.tools import parallel
65+
from qiskit.pulse import InstructionScheduleMap
6466

6567

6668
class CustomCX(Gate):
@@ -1899,3 +1901,73 @@ def run(self, dag):
18991901
for qc_test in qcs_cal_added:
19001902
added_cal = qc_test.calibrations["sx"][((0,), ())]
19011903
self.assertEqual(added_cal, ref_cal)
1904+
1905+
@data(0, 1, 2, 3)
1906+
def test_backendv2_and_basis_gates(self, opt_level):
1907+
"""Test transpile() with BackendV2 and basis_gates set."""
1908+
backend = FakeNairobiV2()
1909+
qc = QuantumCircuit(5)
1910+
qc.h(0)
1911+
qc.cz(0, 1)
1912+
qc.cz(0, 2)
1913+
qc.cz(0, 3)
1914+
qc.cz(0, 4)
1915+
qc.measure_all()
1916+
tqc = transpile(
1917+
qc,
1918+
backend=backend,
1919+
basis_gates=["u", "cz"],
1920+
optimization_level=opt_level,
1921+
seed_transpiler=12345678942,
1922+
)
1923+
op_count = set(tqc.count_ops())
1924+
self.assertEqual({"u", "cz", "measure", "barrier"}, op_count)
1925+
for inst in tqc.data:
1926+
if inst.operation.name not in {"u", "cz"}:
1927+
continue
1928+
qubits = tuple(tqc.find_bit(x).index for x in inst.qubits)
1929+
self.assertIn(qubits, backend.target.qargs)
1930+
1931+
@data(0, 1, 2, 3)
1932+
def test_backendv2_and_coupling_map(self, opt_level):
1933+
"""Test transpile() with custom coupling map."""
1934+
backend = FakeNairobiV2()
1935+
qc = QuantumCircuit(5)
1936+
qc.h(0)
1937+
qc.cz(0, 1)
1938+
qc.cz(0, 2)
1939+
qc.cz(0, 3)
1940+
qc.cz(0, 4)
1941+
qc.measure_all()
1942+
cmap = CouplingMap.from_line(5, bidirectional=False)
1943+
tqc = transpile(
1944+
qc,
1945+
backend=backend,
1946+
coupling_map=cmap,
1947+
optimization_level=opt_level,
1948+
seed_transpiler=12345678942,
1949+
)
1950+
op_count = set(tqc.count_ops())
1951+
self.assertTrue({"rz", "sx", "x", "cx", "measure", "barrier"}.issuperset(op_count))
1952+
for inst in tqc.data:
1953+
if len(inst.qubits) == 2:
1954+
qubit_0 = tqc.find_bit(inst.qubits[0]).index
1955+
qubit_1 = tqc.find_bit(inst.qubits[1]).index
1956+
self.assertEqual(qubit_1, qubit_0 + 1)
1957+
1958+
@data(0, 1, 2, 3)
1959+
def test_backend_and_custom_gate(self, opt_level):
1960+
"""Test transpile() with BackendV2, custom basis pulse gate."""
1961+
backend = FakeNairobiV2()
1962+
inst_map = InstructionScheduleMap()
1963+
inst_map.add("newgate", [0, 1], pulse.ScheduleBlock())
1964+
newgate = Gate("newgate", 2, [])
1965+
circ = QuantumCircuit(2)
1966+
circ.append(newgate, [0, 1])
1967+
tqc = transpile(
1968+
circ, backend, inst_map=inst_map, basis_gates=["newgate"], optimization_level=opt_level
1969+
)
1970+
self.assertEqual(len(tqc.data), 1)
1971+
self.assertEqual(tqc.data[0].operation, newgate)
1972+
qubits = tuple(tqc.find_bit(x).index for x in tqc.data[0].qubits)
1973+
self.assertIn(qubits, backend.target.qargs)

0 commit comments

Comments
 (0)