Skip to content

Commit

Permalink
Fix basis_gates and coupling_map backend override in transpile() (#9789)
Browse files Browse the repository at this point in the history
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 #9256 is implemented
and we'll be relying solely on a target internally. But this fix is
needed until #9256 is started.

Fixes #9781

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
mtreinish and mergify[bot] authored Mar 14, 2023
1 parent 648da26 commit bf3bb96
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 1 deletion.
6 changes: 5 additions & 1 deletion qiskit/compiler/transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,11 @@ def _parse_transpile_args(
timing_constraints = target.timing_constraints()
if backend_properties is None:
backend_properties = target_to_backend_properties(target)
# If target is not specified and any hardware constraint object is
# manually specified then do not use the target from the backend as
# it is invalidated by a custom basis gate list or a custom coupling map
elif basis_gates is None and coupling_map is None:
target = _parse_target(backend, target)

basis_gates = _parse_basis_gates(basis_gates, backend)
initial_layout = _parse_initial_layout(initial_layout, circuits)
Expand All @@ -658,7 +663,6 @@ def _parse_transpile_args(
callback = _parse_callback(callback, num_circuits)
durations = _parse_instruction_durations(backend, instruction_durations, dt, circuits)
timing_constraints = _parse_timing_constraints(backend, timing_constraints, num_circuits)
target = _parse_target(backend, target)
if scheduling_method and any(d is None for d in durations):
raise TranspilerError(
"Transpiling a circuit with a scheduling method"
Expand Down
72 changes: 72 additions & 0 deletions test/python/compiler/test_transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
FakeRueschlikon,
FakeBoeblingen,
FakeMumbaiV2,
FakeNairobiV2,
)
from qiskit.transpiler import Layout, CouplingMap
from qiskit.transpiler import PassManager, TransformationPass
Expand All @@ -61,6 +62,7 @@
from qiskit.transpiler.passmanager_config import PassManagerConfig
from qiskit.transpiler.preset_passmanagers import level_0_pass_manager
from qiskit.tools import parallel
from qiskit.pulse import InstructionScheduleMap


class CustomCX(Gate):
Expand Down Expand Up @@ -1899,3 +1901,73 @@ def run(self, dag):
for qc_test in qcs_cal_added:
added_cal = qc_test.calibrations["sx"][((0,), ())]
self.assertEqual(added_cal, ref_cal)

@data(0, 1, 2, 3)
def test_backendv2_and_basis_gates(self, opt_level):
"""Test transpile() with BackendV2 and basis_gates set."""
backend = FakeNairobiV2()
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."""
backend = FakeNairobiV2()
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=backend,
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)

@data(0, 1, 2, 3)
def test_backend_and_custom_gate(self, opt_level):
"""Test transpile() with BackendV2, custom basis pulse gate."""
backend = FakeNairobiV2()
inst_map = InstructionScheduleMap()
inst_map.add("newgate", [0, 1], pulse.ScheduleBlock())
newgate = Gate("newgate", 2, [])
circ = QuantumCircuit(2)
circ.append(newgate, [0, 1])
tqc = transpile(
circ, backend, inst_map=inst_map, basis_gates=["newgate"], optimization_level=opt_level
)
self.assertEqual(len(tqc.data), 1)
self.assertEqual(tqc.data[0].operation, newgate)
qubits = tuple(tqc.find_bit(x).index for x in tqc.data[0].qubits)
self.assertIn(qubits, backend.target.qargs)

0 comments on commit bf3bb96

Please sign in to comment.