Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed calibration experiments failing on backends with no coupling map #1117

Merged
merged 3 commits into from
Apr 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -316,10 +316,14 @@ def _map_to_physical_qubits(self, circuit: QuantumCircuit) -> QuantumCircuit:
"""
initial_layout = Layout.from_intlist(list(self.physical_qubits), *circuit.qregs)

coupling_map = self._backend_data.coupling_map
if coupling_map is not None:
coupling_map = CouplingMap(self._backend_data.coupling_map)

layout = PassManager(
[
SetLayout(initial_layout),
FullAncillaAllocation(CouplingMap(self._backend_data.coupling_map)),
FullAncillaAllocation(coupling_map),
EnlargeWithAncilla(),
ApplyLayout(),
]
Expand Down
6 changes: 6 additions & 0 deletions releasenotes/notes/cals-no-coupling-map-5114ae9faa2f9e69.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
fixes:
- |
Fixed error generating circuits for :class:`.BaseCalibrationExperiment`
subclasses when the backend instance had no coupling map. Fixed `#1116
<https://github.com/Qiskit/qiskit-experiments/issues/1116>`_.
37 changes: 36 additions & 1 deletion test/calibration/test_base_calibration_experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@

from test.base import QiskitExperimentsTestCase

from qiskit import QuantumCircuit
from qiskit.circuit import Parameter
from qiskit.pulse import Play, Constant, DriveChannel, ScheduleBlock

from qiskit_experiments.library import QubitSpectroscopy
from qiskit_experiments.calibration_management.base_calibration_experiment import (
BaseCalibrationExperiment,
Calibrations,
)
from qiskit_experiments.framework.composite import ParallelExperiment, BatchExperiment
from qiskit_experiments.library import QubitSpectroscopy
from qiskit_experiments.test.fake_backend import FakeBackend

from .utils import MockCalExperiment, DoNothingAnalysis


Expand Down Expand Up @@ -324,3 +326,36 @@ def test_update_calibration_parallel(self):
new_schedule2 = cals.get_schedule("test2", (1,))
ref_schedule2 = schedule2.assign_parameters({param2: ref_new_value2}, inplace=False)
self.assertEqual(new_schedule2, ref_schedule2)

def test_transpiled_circuits_no_coupling_map(self):
"""Test transpilation of calibration experiment with no coupling map"""
# This test was added to catch errors found when running calibration
# experiments against DynamicsBackend from qiskit-dynamics for which
# the coupling map could be None. Previously, this led to
# BaseCalibrationExperiment's custom pass manager failing.
backend = FakeBackend(num_qubits=2)
# If the following fails, it should be reassessed if this test is still
# useful
self.assertTrue(backend.coupling_map is None)

cals = Calibrations()

# Build a circuit to be passed through transpilation pipeline
qc = QuantumCircuit(1, 1)
qc.x(0)
qc.measure(0, 0)

exp = MockCalExperiment(
physical_qubits=(1,),
calibrations=cals,
new_value=0.2,
param_name="amp",
sched_name="x",
backend=backend,
circuits=[qc],
)
transpiled = exp._transpiled_circuits()[0]
# Make sure circuit was expanded with the ancilla on qubit 0
self.assertEqual(len(transpiled.qubits), 2)
# Make sure instructions were unchanged
self.assertDictEqual(transpiled.count_ops(), qc.count_ops())
30 changes: 26 additions & 4 deletions test/calibration/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
"""Utility to test calibration module."""

import datetime
from typing import Sequence
from typing import Optional, Sequence

from qiskit import QuantumCircuit
from qiskit.providers import Backend

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

def __init__(self, physical_qubits: Sequence[int], return_value: float):
super().__init__(physical_qubits=physical_qubits, analysis=DoNothingAnalysis())
def __init__(
self,
physical_qubits: Sequence[int],
return_value: float,
circuits: Optional[Sequence[QuantumCircuit]] = None,
backend: Optional[Backend] = None,
):
super().__init__(
physical_qubits=physical_qubits, analysis=DoNothingAnalysis(), backend=backend
)
self.analysis.set_options(return_value=return_value)

if circuits is not None:
self._circuits = circuits
else:
self._circuits = []

def circuits(self):
return []
return self._circuits


class MockCalExperiment(BaseCalibrationExperiment, DoNothingExperiment):
Expand All @@ -69,6 +85,8 @@ def __init__(
new_value: float,
param_name: str,
sched_name: str,
circuits: Optional[Sequence[QuantumCircuit]] = None,
backend: Optional[Backend] = None,
):
"""Create mock calibration experiment.

Expand All @@ -78,11 +96,15 @@ def __init__(
new_value: New parameter value obtained by the calibration experiment.
param_name: Name of parameter to update.
sched_name: Name of schedule to update.
circuits: List of QuantumCircuits for the circuits() method
backend: Backend to set on experiment
"""
super().__init__(
physical_qubits=physical_qubits,
calibrations=calibrations,
return_value=new_value,
circuits=circuits,
backend=backend,
)
self.to_update = {
"param": param_name,
Expand Down