Skip to content

Commit ac26da0

Browse files
authored
Fixed calibration experiments failing on backends with no coupling map (#1117)
On backends with no coupling map (such as one could configure with `DynamicsBackend` from qiskit-dynamics), calibration experiments failed because the `BaseCalibrationExperiment` tried to pass the backend's `coupling_map` (run through `BackendData`) to terra's `CouplingMap` which acceprts `None`. The transpiler pass `FullAncillaAllocation` behaves differently when passed `None` than when passed `CouplingMap(None)`.
1 parent 7a1fb78 commit ac26da0

File tree

4 files changed

+73
-6
lines changed

4 files changed

+73
-6
lines changed

qiskit_experiments/calibration_management/base_calibration_experiment.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,10 +316,14 @@ def _map_to_physical_qubits(self, circuit: QuantumCircuit) -> QuantumCircuit:
316316
"""
317317
initial_layout = Layout.from_intlist(list(self.physical_qubits), *circuit.qregs)
318318

319+
coupling_map = self._backend_data.coupling_map
320+
if coupling_map is not None:
321+
coupling_map = CouplingMap(self._backend_data.coupling_map)
322+
319323
layout = PassManager(
320324
[
321325
SetLayout(initial_layout),
322-
FullAncillaAllocation(CouplingMap(self._backend_data.coupling_map)),
326+
FullAncillaAllocation(coupling_map),
323327
EnlargeWithAncilla(),
324328
ApplyLayout(),
325329
]
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
fixes:
3+
- |
4+
Fixed error generating circuits for :class:`.BaseCalibrationExperiment`
5+
subclasses when the backend instance had no coupling map. Fixed `#1116
6+
<https://github.com/Qiskit/qiskit-experiments/issues/1116>`_.

test/calibration/test_base_calibration_experiment.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,18 @@
1414

1515
from test.base import QiskitExperimentsTestCase
1616

17+
from qiskit import QuantumCircuit
1718
from qiskit.circuit import Parameter
1819
from qiskit.pulse import Play, Constant, DriveChannel, ScheduleBlock
1920

20-
from qiskit_experiments.library import QubitSpectroscopy
2121
from qiskit_experiments.calibration_management.base_calibration_experiment import (
2222
BaseCalibrationExperiment,
2323
Calibrations,
2424
)
2525
from qiskit_experiments.framework.composite import ParallelExperiment, BatchExperiment
26+
from qiskit_experiments.library import QubitSpectroscopy
2627
from qiskit_experiments.test.fake_backend import FakeBackend
28+
2729
from .utils import MockCalExperiment, DoNothingAnalysis
2830

2931

@@ -324,3 +326,36 @@ def test_update_calibration_parallel(self):
324326
new_schedule2 = cals.get_schedule("test2", (1,))
325327
ref_schedule2 = schedule2.assign_parameters({param2: ref_new_value2}, inplace=False)
326328
self.assertEqual(new_schedule2, ref_schedule2)
329+
330+
def test_transpiled_circuits_no_coupling_map(self):
331+
"""Test transpilation of calibration experiment with no coupling map"""
332+
# This test was added to catch errors found when running calibration
333+
# experiments against DynamicsBackend from qiskit-dynamics for which
334+
# the coupling map could be None. Previously, this led to
335+
# BaseCalibrationExperiment's custom pass manager failing.
336+
backend = FakeBackend(num_qubits=2)
337+
# If the following fails, it should be reassessed if this test is still
338+
# useful
339+
self.assertTrue(backend.coupling_map is None)
340+
341+
cals = Calibrations()
342+
343+
# Build a circuit to be passed through transpilation pipeline
344+
qc = QuantumCircuit(1, 1)
345+
qc.x(0)
346+
qc.measure(0, 0)
347+
348+
exp = MockCalExperiment(
349+
physical_qubits=(1,),
350+
calibrations=cals,
351+
new_value=0.2,
352+
param_name="amp",
353+
sched_name="x",
354+
backend=backend,
355+
circuits=[qc],
356+
)
357+
transpiled = exp._transpiled_circuits()[0]
358+
# Make sure circuit was expanded with the ancilla on qubit 0
359+
self.assertEqual(len(transpiled.qubits), 2)
360+
# Make sure instructions were unchanged
361+
self.assertDictEqual(transpiled.count_ops(), qc.count_ops())

test/calibration/utils.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
"""Utility to test calibration module."""
1414

1515
import datetime
16-
from typing import Sequence
16+
from typing import Optional, Sequence
17+
18+
from qiskit import QuantumCircuit
19+
from qiskit.providers import Backend
1720

1821
from qiskit_experiments.calibration_management import (
1922
BaseCalibrationExperiment,
@@ -46,12 +49,25 @@ def _run_analysis(
4649
class DoNothingExperiment(BaseExperiment):
4750
"""Experiment doesn't provide any circuit to run."""
4851

49-
def __init__(self, physical_qubits: Sequence[int], return_value: float):
50-
super().__init__(physical_qubits=physical_qubits, analysis=DoNothingAnalysis())
52+
def __init__(
53+
self,
54+
physical_qubits: Sequence[int],
55+
return_value: float,
56+
circuits: Optional[Sequence[QuantumCircuit]] = None,
57+
backend: Optional[Backend] = None,
58+
):
59+
super().__init__(
60+
physical_qubits=physical_qubits, analysis=DoNothingAnalysis(), backend=backend
61+
)
5162
self.analysis.set_options(return_value=return_value)
5263

64+
if circuits is not None:
65+
self._circuits = circuits
66+
else:
67+
self._circuits = []
68+
5369
def circuits(self):
54-
return []
70+
return self._circuits
5571

5672

5773
class MockCalExperiment(BaseCalibrationExperiment, DoNothingExperiment):
@@ -69,6 +85,8 @@ def __init__(
6985
new_value: float,
7086
param_name: str,
7187
sched_name: str,
88+
circuits: Optional[Sequence[QuantumCircuit]] = None,
89+
backend: Optional[Backend] = None,
7290
):
7391
"""Create mock calibration experiment.
7492
@@ -78,11 +96,15 @@ def __init__(
7896
new_value: New parameter value obtained by the calibration experiment.
7997
param_name: Name of parameter to update.
8098
sched_name: Name of schedule to update.
99+
circuits: List of QuantumCircuits for the circuits() method
100+
backend: Backend to set on experiment
81101
"""
82102
super().__init__(
83103
physical_qubits=physical_qubits,
84104
calibrations=calibrations,
85105
return_value=new_value,
106+
circuits=circuits,
107+
backend=backend,
86108
)
87109
self.to_update = {
88110
"param": param_name,

0 commit comments

Comments
 (0)