From 9bb60aeb139a0add3269675c5bc68a1cc590ac5f Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 17 Jun 2021 12:40:25 +0200 Subject: [PATCH 01/20] * Implemented method to setup single-qubit calibrations. --- .../calibration/calibrations_setup.py | 148 ++++++++++++++++++ test/calibration/test_setup_functions.py | 59 +++++++ 2 files changed, 207 insertions(+) create mode 100644 qiskit_experiments/calibration/calibrations_setup.py create mode 100644 test/calibration/test_setup_functions.py diff --git a/qiskit_experiments/calibration/calibrations_setup.py b/qiskit_experiments/calibration/calibrations_setup.py new file mode 100644 index 0000000000..7da891212b --- /dev/null +++ b/qiskit_experiments/calibration/calibrations_setup.py @@ -0,0 +1,148 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. + +""" +A collections of helper methods to setup Calibrations. + +Note that the set of available functions will be extended in future releases. +""" + +from typing import Optional + +from qiskit.circuit import Parameter +import qiskit.pulse as pulse +from qiskit.providers.backend import BackendV1 as Backend + +from qiskit_experiments.calibration.backend_calibrations import BackendCalibrations + + +def standard_single_qubit_gates( + backend: Backend, + calibrations: Optional[BackendCalibrations] = None, + link_amplitudes: bool = False, + link_drag: bool = False, + default_duration: int = 160, + default_amplitude: float = 0.2, + default_sigma: int = 40, + default_beta: float = 0.0, +) -> BackendCalibrations: + """Setup calibrations from a backend and populate them with single-qubit gates. + + This methods helps users setup an initial set of calibrations for single-qubit + gates. Each qubit receives six single-qubit pulses, namely xp, xm, x90p, x90m, + y90p, and y90m. Each pulse is a Drag pulse. All pulses share the same duration + and sigma. + + Args: + backend: The backend object for which to build the calibrations. + calibrations: An optional calibrations instance to which the schedules and + parameter values will be added. If this argument is not created then a + BackendCalibrations instance will be initialized from the backend object. + link_amplitudes: If set to True then the amplitudes of the x-minus pulses will + be the negative of the amplitude of the x-plus pulses. + link_drag: If set to True then all pulses will share the same Drag parameter. + default_duration: The default duration for the single-qubit gates given as + samples. This variable defaults to 160. + default_amplitude: The default amplitude for the pulses. The default value is 0.2 + for the xp pulse which gives default amplitudes of -0.2, 0.1, and -0.1 for + xm, x90p, and x90m, respectively. + default_sigma: The default standard deviation of the pulses. + default_beta: The default Drag parameter for all pulses. + + Returns: + A BackendCalibration instance populate with the schedules and default parameter values. + """ + + if calibrations is None: + calibrations = BackendCalibrations(backend) + + chan = Parameter("ch0") + duration = Parameter("duration") + sigma = Parameter("σ") + amp_xp = Parameter("amp") + amp_x90p = Parameter("amp") + beta_xp = Parameter("β") + + if link_amplitudes: + amp_xm = -amp_xp + amp_x90m = -amp_x90p + amp_y90p = 1.0j * amp_x90p + amp_y90m = -1.0j * amp_x90p + else: + amp_xm = Parameter("amp") + amp_x90m = Parameter("amp") + amp_y90p = Parameter("amp") + amp_y90m = Parameter("amp") + + if link_drag: + beta_xm = beta_xp + beta_x90p = beta_xp + beta_x90m = beta_xp + beta_y90p = beta_xp + beta_y90m = beta_xp + else: + beta_xm = Parameter("β") + beta_x90p = Parameter("β") + beta_x90m = Parameter("β") + beta_y90p = Parameter("β") + beta_y90m = Parameter("β") + + pulse_config = [ + ("xp", amp_xp, beta_xp), + ("xm", amp_xm, beta_xm), + ("x90p", amp_x90p, beta_x90p), + ("x90m", amp_x90m, beta_x90m), + ("y90p", amp_y90p, beta_y90p), + ("y90m", amp_y90m, beta_y90m), + ] + + for name, amp, beta in pulse_config: + with pulse.build(backend=backend, name=name) as schedule: + pulse.play( + pulse.Drag(duration=duration, sigma=sigma, amp=amp, beta=beta), + pulse.DriveChannel(chan), + ) + + calibrations.add_schedule(schedule) + + betas = [ + ("xp", beta_xp), + ("xm", beta_xm), + ("x90p", beta_x90p), + ("x90m", beta_x90m), + ("y90p", beta_y90p), + ("y90m", beta_y90m), + ] + + amps = [ + ("xp", 1, amp_xp), + ("xm", -1, amp_xm), + ("x90p", 0.5, amp_x90p), + ("x90m", -0.5, amp_x90m), + ("y90p", 0.5j, amp_y90p), + ("y90m", -0.5j, amp_y90m), + ] + + # Register the default parameter values common to all qubits. + for sched_name in ["xp", "xm", "x90p", "x90m"]: + calibrations.add_parameter_value(default_duration, duration, schedule=sched_name) + calibrations.add_parameter_value(default_sigma, sigma, schedule=sched_name) + + # Register parameter values for amplitude and beta for each qubit. + for qubit in range(backend.configuration().n_qubits): + for sched_name, param in betas: + calibrations.add_parameter_value(default_beta, param, qubit, sched_name) + + for sched_name, sign, param in amps: + calibrations.add_parameter_value(sign * default_amplitude, param, qubit, sched_name) + + return calibrations diff --git a/test/calibration/test_setup_functions.py b/test/calibration/test_setup_functions.py new file mode 100644 index 0000000000..920274be04 --- /dev/null +++ b/test/calibration/test_setup_functions.py @@ -0,0 +1,59 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. + +"""Class to test the calibrations setup methods.""" + +from qiskit.pulse import Drag, DriveChannel +import qiskit.pulse as pulse +from qiskit.test import QiskitTestCase +from qiskit.test.mock import FakeAthens + +from qiskit_experiments.calibration.calibrations_setup import standard_single_qubit_gates + + +class TestCalibrationsSetup(QiskitTestCase): + """Test the various setup methods.""" + + def test_standard_single_qubit_gates(self): + """Test the setup of single-qubit gates.""" + + # Test non-linked parameters + cals = standard_single_qubit_gates(FakeAthens()) + + tests = [ + ("xp", 0.2), + ("xm", -0.2), + ("x90p", 0.1), + ("x90m", -0.1), + ("y90p", 0.1j), + ("y90m", -0.1j), + ] + + for name, amp in tests: + for qubit in range(5): + with pulse.build(name=name) as sched: + pulse.play(Drag(160, amp, 40, 0.0), DriveChannel(qubit)) + + self.assertEqual(cals.get_schedule(name, (qubit,)), sched) + + xp = cals.get_template("xp", (0,)) + xm = cals.get_template("xm", (0,)) + + self.assertNotEqual(xp.parameters, xm.parameters) + + # Test linked parameters + cals = standard_single_qubit_gates(FakeAthens(), link_amplitudes=True, link_drag=True) + + xp = cals.get_template("xp", (0,)) + xm = cals.get_template("xm", (0,)) + + self.assertEqual(xp.parameters, xm.parameters) From f9735821d4f0e3184ad2119c13c80de5583b0bf3 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 21 Jul 2021 16:08:16 +0200 Subject: [PATCH 02/20] * Refactored cals setup to library style. --- .../calibration/calibrations_setup.py | 148 ------------------ .../backend_calibrations.py | 37 ++++- .../basis_gate_library.py | 126 +++++++++++++++ test/calibration/test_backend_calibrations.py | 25 +++ test/calibration/test_setup_functions.py | 59 ------- test/calibration/test_setup_library.py | 52 ++++++ 6 files changed, 237 insertions(+), 210 deletions(-) delete mode 100644 qiskit_experiments/calibration/calibrations_setup.py create mode 100644 qiskit_experiments/calibration_management/basis_gate_library.py delete mode 100644 test/calibration/test_setup_functions.py create mode 100644 test/calibration/test_setup_library.py diff --git a/qiskit_experiments/calibration/calibrations_setup.py b/qiskit_experiments/calibration/calibrations_setup.py deleted file mode 100644 index 7da891212b..0000000000 --- a/qiskit_experiments/calibration/calibrations_setup.py +++ /dev/null @@ -1,148 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. - -""" -A collections of helper methods to setup Calibrations. - -Note that the set of available functions will be extended in future releases. -""" - -from typing import Optional - -from qiskit.circuit import Parameter -import qiskit.pulse as pulse -from qiskit.providers.backend import BackendV1 as Backend - -from qiskit_experiments.calibration.backend_calibrations import BackendCalibrations - - -def standard_single_qubit_gates( - backend: Backend, - calibrations: Optional[BackendCalibrations] = None, - link_amplitudes: bool = False, - link_drag: bool = False, - default_duration: int = 160, - default_amplitude: float = 0.2, - default_sigma: int = 40, - default_beta: float = 0.0, -) -> BackendCalibrations: - """Setup calibrations from a backend and populate them with single-qubit gates. - - This methods helps users setup an initial set of calibrations for single-qubit - gates. Each qubit receives six single-qubit pulses, namely xp, xm, x90p, x90m, - y90p, and y90m. Each pulse is a Drag pulse. All pulses share the same duration - and sigma. - - Args: - backend: The backend object for which to build the calibrations. - calibrations: An optional calibrations instance to which the schedules and - parameter values will be added. If this argument is not created then a - BackendCalibrations instance will be initialized from the backend object. - link_amplitudes: If set to True then the amplitudes of the x-minus pulses will - be the negative of the amplitude of the x-plus pulses. - link_drag: If set to True then all pulses will share the same Drag parameter. - default_duration: The default duration for the single-qubit gates given as - samples. This variable defaults to 160. - default_amplitude: The default amplitude for the pulses. The default value is 0.2 - for the xp pulse which gives default amplitudes of -0.2, 0.1, and -0.1 for - xm, x90p, and x90m, respectively. - default_sigma: The default standard deviation of the pulses. - default_beta: The default Drag parameter for all pulses. - - Returns: - A BackendCalibration instance populate with the schedules and default parameter values. - """ - - if calibrations is None: - calibrations = BackendCalibrations(backend) - - chan = Parameter("ch0") - duration = Parameter("duration") - sigma = Parameter("σ") - amp_xp = Parameter("amp") - amp_x90p = Parameter("amp") - beta_xp = Parameter("β") - - if link_amplitudes: - amp_xm = -amp_xp - amp_x90m = -amp_x90p - amp_y90p = 1.0j * amp_x90p - amp_y90m = -1.0j * amp_x90p - else: - amp_xm = Parameter("amp") - amp_x90m = Parameter("amp") - amp_y90p = Parameter("amp") - amp_y90m = Parameter("amp") - - if link_drag: - beta_xm = beta_xp - beta_x90p = beta_xp - beta_x90m = beta_xp - beta_y90p = beta_xp - beta_y90m = beta_xp - else: - beta_xm = Parameter("β") - beta_x90p = Parameter("β") - beta_x90m = Parameter("β") - beta_y90p = Parameter("β") - beta_y90m = Parameter("β") - - pulse_config = [ - ("xp", amp_xp, beta_xp), - ("xm", amp_xm, beta_xm), - ("x90p", amp_x90p, beta_x90p), - ("x90m", amp_x90m, beta_x90m), - ("y90p", amp_y90p, beta_y90p), - ("y90m", amp_y90m, beta_y90m), - ] - - for name, amp, beta in pulse_config: - with pulse.build(backend=backend, name=name) as schedule: - pulse.play( - pulse.Drag(duration=duration, sigma=sigma, amp=amp, beta=beta), - pulse.DriveChannel(chan), - ) - - calibrations.add_schedule(schedule) - - betas = [ - ("xp", beta_xp), - ("xm", beta_xm), - ("x90p", beta_x90p), - ("x90m", beta_x90m), - ("y90p", beta_y90p), - ("y90m", beta_y90m), - ] - - amps = [ - ("xp", 1, amp_xp), - ("xm", -1, amp_xm), - ("x90p", 0.5, amp_x90p), - ("x90m", -0.5, amp_x90m), - ("y90p", 0.5j, amp_y90p), - ("y90m", -0.5j, amp_y90m), - ] - - # Register the default parameter values common to all qubits. - for sched_name in ["xp", "xm", "x90p", "x90m"]: - calibrations.add_parameter_value(default_duration, duration, schedule=sched_name) - calibrations.add_parameter_value(default_sigma, sigma, schedule=sched_name) - - # Register parameter values for amplitude and beta for each qubit. - for qubit in range(backend.configuration().n_qubits): - for sched_name, param in betas: - calibrations.add_parameter_value(default_beta, param, qubit, sched_name) - - for sched_name, sign, param in amps: - calibrations.add_parameter_value(sign * default_amplitude, param, qubit, sched_name) - - return calibrations diff --git a/qiskit_experiments/calibration_management/backend_calibrations.py b/qiskit_experiments/calibration_management/backend_calibrations.py index 807a250661..eb17148de5 100644 --- a/qiskit_experiments/calibration_management/backend_calibrations.py +++ b/qiskit_experiments/calibration_management/backend_calibrations.py @@ -14,13 +14,14 @@ from datetime import datetime from enum import Enum -from typing import List +from typing import Dict, List, Optional, Type import copy from qiskit.providers.backend import BackendV1 as Backend from qiskit.circuit import Parameter from qiskit_experiments.calibration_management.calibrations import Calibrations, ParameterKey from qiskit_experiments.exceptions import CalibrationError +from qiskit_experiments.calibration_management.basis_gate_library import BasisGateLibrary class FrequencyElement(Enum): @@ -43,8 +44,25 @@ class BackendCalibrations(Calibrations): __qubit_freq_parameter__ = "qubit_lo_freq" __readout_freq_parameter__ = "meas_lo_freq" - def __init__(self, backend: Backend): - """Setup an instance to manage the calibrations of a backend.""" + def __init__( + self, + backend: Backend, + basis_gates: Optional[List[str]] = None, + library: Type[BasisGateLibrary] = None, + library_options: Optional[Dict] = None, + ): + """Setup an instance to manage the calibrations of a backend. + + Args: + backend: A backend instance from which to extract the qubit and readout frequencies + (which will be added as first guesses for the corresponding parameters) as well + as the coupling map. + basis_gates: The basis gates to extract from the library and add to the calibrations. + library: A library class that will be instantiated with the library options to then + get template schedules to register as well as default parameter values. + library_options: Instantiation options for the gate library. See the init method of + :class:`BasisGateLibrary` and its subclasses. + """ if hasattr(backend.configuration(), "control_channels"): control_channels = backend.configuration().control_channels else: @@ -67,6 +85,19 @@ def __init__(self, backend: Backend): for meas, freq in enumerate(backend.defaults().meas_freq_est): self.add_parameter_value(freq, self.meas_freq, meas) + basis_gates = basis_gates or list() + + if library is not None: + library_instance = library(library_options) + + # Add the basis gates + for gate in basis_gates: + self.add_schedule(library_instance[gate]) + + # Add the default values + for param_conf in library_instance.default_values(): + self.add_parameter_value(*param_conf) + def _get_frequencies( self, element: FrequencyElement, diff --git a/qiskit_experiments/calibration_management/basis_gate_library.py b/qiskit_experiments/calibration_management/basis_gate_library.py new file mode 100644 index 0000000000..694105e1f4 --- /dev/null +++ b/qiskit_experiments/calibration_management/basis_gate_library.py @@ -0,0 +1,126 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. + +""" +A collections of libraries to setup Calibrations. + +Note that the set of available functions will be extended in future releases. +""" + +from abc import ABC, abstractmethod +from typing import Dict, List, Optional, Tuple + +from qiskit.circuit import Parameter +import qiskit.pulse as pulse +from qiskit.pulse import ScheduleBlock + +from qiskit_experiments.calibration_management.calibration_key_types import ParameterValueType + + +class BasisGateLibrary(ABC): + """A base class for libraries of basis gates to make it easier to setup Calibrations.""" + + # Location where default parameter values are stored. These may be updated at construction. + __default_values__ = dict() + + def __init__(self, default_values: Optional[Dict] = None): + """Setup the library. + + Args: + default_values: A dictionary to override library default parameter values. + """ + self._schedules = dict() + + if default_values is not None: + for key, value in default_values.items(): + self.__default_values__[key] = value + + def __getitem__(self, name: str) -> ScheduleBlock: + """Return the schedule.""" + return self._schedules[name] + + @abstractmethod + def default_values(self) -> List[Tuple[ParameterValueType, Parameter, Tuple, ScheduleBlock]]: + """Return the default values for the parameters. + + Returns + A list of tuples is returned. These tuples are structured so that instances of + :class:`Calibrations` can call :meth:`add_parameter_value` on the tuples. + """ + + +class FixedFrequencyTransmonSXRZ(BasisGateLibrary): + """A library of gates for fixed-frequency superconducting qubit architectures.""" + + __default_values__ = {"duration": 160, "amp": 0.5, "β": 0.0} + + def __init__(self, default_values: Optional[Dict] = None): + """Setup the schedules.""" + super().__init__(default_values) + + dur = Parameter("duration") + sigma = Parameter("σ") + + # X gates + sched_x = self._single_qubit_schedule("x", dur, Parameter("amp"), sigma, Parameter("β")) + + # square-root X gates + sched_sx = self._single_qubit_schedule("sx", dur, Parameter("amp"), sigma, Parameter("β")) + + for sched in [sched_x, sched_sx]: + self._schedules[sched.name] = sched + + @staticmethod + def _single_qubit_schedule( + name: str, + dur: Parameter, + amp: Parameter, + sigma: Parameter, + beta: Optional[Parameter] = None, + ) -> ScheduleBlock: + """Build a single qubit pulse.""" + + chan = pulse.DriveChannel(Parameter("ch0")) + + if beta is not None: + with pulse.build(name=name) as sched: + pulse.play(pulse.Drag(duration=dur, amp=amp, sigma=sigma, beta=beta), chan) + else: + with pulse.build(name=name) as sched: + pulse.play(pulse.Gaussian(duration=dur, amp=amp, sigma=sigma), chan) + + return sched + + def default_values(self) -> List[Tuple[ParameterValueType, Parameter, Tuple, ScheduleBlock]]: + """Return the default values for the parameters. + + Returns + A list of tuples is returned. These tuples are structured so that instances of + :class:`Calibrations` can call :meth:`add_parameter_value` on the tuples. + """ + defaults = [] + for name in ["x", "sx"]: + schedule = self._schedules[name] + for param in schedule.parameters: + if "ch" not in param.name: + + if param.name == "σ" and "σ" not in self.__default_values__: + value = self.__default_values__["duration"] / 4 + else: + value = self.__default_values__[param.name] + + if name == "sx" and param.name == "amp": + value /= 2.0 + + defaults.append((value, param.name, tuple(), name)) + + return defaults diff --git a/test/calibration/test_backend_calibrations.py b/test/calibration/test_backend_calibrations.py index bf58a4b938..296897899e 100644 --- a/test/calibration/test_backend_calibrations.py +++ b/test/calibration/test_backend_calibrations.py @@ -12,9 +12,12 @@ """Class to test the backend calibrations.""" +import qiskit.pulse as pulse from qiskit.test import QiskitTestCase from qiskit.test.mock import FakeArmonk + from qiskit_experiments.calibration_management import BackendCalibrations +from qiskit_experiments.calibration_management.basis_gate_library import FixedFrequencyTransmonSXRZ class TestBackendCalibrations(QiskitTestCase): @@ -26,3 +29,25 @@ def test_run_options(self): self.assertEqual(cals.get_meas_frequencies(), [6993370669.000001]) self.assertEqual(cals.get_qubit_frequencies(), [4971852852.405576]) + + def test_setup_withLibrary(self): + """Test that we can setup with a library.""" + + cals = BackendCalibrations( + FakeArmonk(), + basis_gates=["x", "sx"], + library=FixedFrequencyTransmonSXRZ, + library_options={"duration": 320}, + ) + + # Check the x gate + with pulse.build(name="x") as expected: + pulse.play(pulse.Drag(duration=320, amp=0.5, sigma=80, beta=0), pulse.DriveChannel(0)) + + self.assertEqual(cals.get_schedule("x", (0,)), expected) + + # Check the sx gate + with pulse.build(name="sx") as expected: + pulse.play(pulse.Drag(duration=320, amp=0.25, sigma=80, beta=0), pulse.DriveChannel(0)) + + self.assertEqual(cals.get_schedule("sx", (0,)), expected) diff --git a/test/calibration/test_setup_functions.py b/test/calibration/test_setup_functions.py deleted file mode 100644 index 920274be04..0000000000 --- a/test/calibration/test_setup_functions.py +++ /dev/null @@ -1,59 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2021. -# -# 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. - -"""Class to test the calibrations setup methods.""" - -from qiskit.pulse import Drag, DriveChannel -import qiskit.pulse as pulse -from qiskit.test import QiskitTestCase -from qiskit.test.mock import FakeAthens - -from qiskit_experiments.calibration.calibrations_setup import standard_single_qubit_gates - - -class TestCalibrationsSetup(QiskitTestCase): - """Test the various setup methods.""" - - def test_standard_single_qubit_gates(self): - """Test the setup of single-qubit gates.""" - - # Test non-linked parameters - cals = standard_single_qubit_gates(FakeAthens()) - - tests = [ - ("xp", 0.2), - ("xm", -0.2), - ("x90p", 0.1), - ("x90m", -0.1), - ("y90p", 0.1j), - ("y90m", -0.1j), - ] - - for name, amp in tests: - for qubit in range(5): - with pulse.build(name=name) as sched: - pulse.play(Drag(160, amp, 40, 0.0), DriveChannel(qubit)) - - self.assertEqual(cals.get_schedule(name, (qubit,)), sched) - - xp = cals.get_template("xp", (0,)) - xm = cals.get_template("xm", (0,)) - - self.assertNotEqual(xp.parameters, xm.parameters) - - # Test linked parameters - cals = standard_single_qubit_gates(FakeAthens(), link_amplitudes=True, link_drag=True) - - xp = cals.get_template("xp", (0,)) - xm = cals.get_template("xm", (0,)) - - self.assertEqual(xp.parameters, xm.parameters) diff --git a/test/calibration/test_setup_library.py b/test/calibration/test_setup_library.py new file mode 100644 index 0000000000..14f81804e4 --- /dev/null +++ b/test/calibration/test_setup_library.py @@ -0,0 +1,52 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021. +# +# 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. + +"""Class to test the calibrations setup methods.""" + +import qiskit.pulse as pulse +from qiskit.test import QiskitTestCase + +from qiskit_experiments.calibration_management.basis_gate_library import FixedFrequencyTransmonSXRZ + + +class TestFixedFrequencyTransmonSXRZ(QiskitTestCase): + """Test the various setup methods.""" + + def test_standard_single_qubit_gates(self): + """Test the setup of single-qubit gates.""" + + library = FixedFrequencyTransmonSXRZ({"duration": 320}) + + for gate in ["x", "sx"]: + sched = library[gate] + self.assertTrue(isinstance(sched, pulse.ScheduleBlock)) + self.assertEqual(len(sched.parameters), 5) + + sched_x = library["x"] + sched_sx = library["sx"] + + self.assertEqual(sched_x.blocks[0].pulse.duration, sched_sx.blocks[0].pulse.duration) + self.assertEqual(sched_x.blocks[0].pulse.sigma, sched_sx.blocks[0].pulse.sigma) + + expected = [ + (0.5, "amp", (), "x"), + (0.0, "β", (), "x"), + (320, "duration", (), "x"), + (40, "σ", (), "x"), + (320, "duration", (), "sx"), + (0.0, "β", (), "sx"), + (0.25, "amp", (), "sx"), + (40, "σ", (), "sx"), + ] + + for param_conf in library.default_values(): + self.assertTrue(param_conf in expected) From 262f2a4086b2b02d86d4836af03ec0a1826968c5 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 21 Jul 2021 16:24:00 +0200 Subject: [PATCH 03/20] * Fix sigma in test. --- test/calibration/test_setup_library.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/calibration/test_setup_library.py b/test/calibration/test_setup_library.py index 14f81804e4..d2690b3fb8 100644 --- a/test/calibration/test_setup_library.py +++ b/test/calibration/test_setup_library.py @@ -41,11 +41,11 @@ def test_standard_single_qubit_gates(self): (0.5, "amp", (), "x"), (0.0, "β", (), "x"), (320, "duration", (), "x"), - (40, "σ", (), "x"), + (80, "σ", (), "x"), (320, "duration", (), "sx"), (0.0, "β", (), "sx"), (0.25, "amp", (), "sx"), - (40, "σ", (), "sx"), + (80, "σ", (), "sx"), ] for param_conf in library.default_values(): From 894794ab03c97c0248f2593e0db061a904a73861 Mon Sep 17 00:00:00 2001 From: Daniel Egger <38065505+eggerdj@users.noreply.github.com> Date: Wed, 21 Jul 2021 21:00:06 +0200 Subject: [PATCH 04/20] Update qiskit_experiments/calibration_management/basis_gate_library.py Co-authored-by: Naoki Kanazawa --- .../calibration_management/basis_gate_library.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qiskit_experiments/calibration_management/basis_gate_library.py b/qiskit_experiments/calibration_management/basis_gate_library.py index 694105e1f4..2a1c327fde 100644 --- a/qiskit_experiments/calibration_management/basis_gate_library.py +++ b/qiskit_experiments/calibration_management/basis_gate_library.py @@ -41,8 +41,7 @@ def __init__(self, default_values: Optional[Dict] = None): self._schedules = dict() if default_values is not None: - for key, value in default_values.items(): - self.__default_values__[key] = value + self.__default_values__.update(default_values) def __getitem__(self, name: str) -> ScheduleBlock: """Return the schedule.""" From d10d15545096ae5e4081604ce720aaf928f09bf2 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 21 Jul 2021 21:06:39 +0200 Subject: [PATCH 05/20] * Made library options kwargs. --- .../calibration_management/backend_calibrations.py | 4 ++-- test/calibration/test_backend_calibrations.py | 2 +- test/calibration/test_setup_library.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qiskit_experiments/calibration_management/backend_calibrations.py b/qiskit_experiments/calibration_management/backend_calibrations.py index eb17148de5..a2eb7da7ad 100644 --- a/qiskit_experiments/calibration_management/backend_calibrations.py +++ b/qiskit_experiments/calibration_management/backend_calibrations.py @@ -49,7 +49,7 @@ def __init__( backend: Backend, basis_gates: Optional[List[str]] = None, library: Type[BasisGateLibrary] = None, - library_options: Optional[Dict] = None, + **library_options, ): """Setup an instance to manage the calibrations of a backend. @@ -88,7 +88,7 @@ def __init__( basis_gates = basis_gates or list() if library is not None: - library_instance = library(library_options) + library_instance = library(**library_options) # Add the basis gates for gate in basis_gates: diff --git a/test/calibration/test_backend_calibrations.py b/test/calibration/test_backend_calibrations.py index 296897899e..89c362133f 100644 --- a/test/calibration/test_backend_calibrations.py +++ b/test/calibration/test_backend_calibrations.py @@ -37,7 +37,7 @@ def test_setup_withLibrary(self): FakeArmonk(), basis_gates=["x", "sx"], library=FixedFrequencyTransmonSXRZ, - library_options={"duration": 320}, + default_values={"duration": 320}, ) # Check the x gate diff --git a/test/calibration/test_setup_library.py b/test/calibration/test_setup_library.py index d2690b3fb8..d0a9dbd105 100644 --- a/test/calibration/test_setup_library.py +++ b/test/calibration/test_setup_library.py @@ -24,7 +24,7 @@ class TestFixedFrequencyTransmonSXRZ(QiskitTestCase): def test_standard_single_qubit_gates(self): """Test the setup of single-qubit gates.""" - library = FixedFrequencyTransmonSXRZ({"duration": 320}) + library = FixedFrequencyTransmonSXRZ(default_values={"duration": 320}) for gate in ["x", "sx"]: sched = library[gate] From 59bde3e80a14878ce061d745439b3c23b5424daf Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 21 Jul 2021 21:13:08 +0200 Subject: [PATCH 06/20] * Added error raising. --- .../calibration_management/basis_gate_library.py | 4 ++++ test/calibration/test_setup_library.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/qiskit_experiments/calibration_management/basis_gate_library.py b/qiskit_experiments/calibration_management/basis_gate_library.py index 2a1c327fde..2d0c6fe1ec 100644 --- a/qiskit_experiments/calibration_management/basis_gate_library.py +++ b/qiskit_experiments/calibration_management/basis_gate_library.py @@ -24,6 +24,7 @@ from qiskit.pulse import ScheduleBlock from qiskit_experiments.calibration_management.calibration_key_types import ParameterValueType +from qiskit_experiments.exceptions import CalibrationError class BasisGateLibrary(ABC): @@ -45,6 +46,9 @@ def __init__(self, default_values: Optional[Dict] = None): def __getitem__(self, name: str) -> ScheduleBlock: """Return the schedule.""" + if name not in self._schedules: + raise CalibrationError(f"Gate {name} is not contained in {self.__class__.__name__}.") + return self._schedules[name] @abstractmethod diff --git a/test/calibration/test_setup_library.py b/test/calibration/test_setup_library.py index d0a9dbd105..1b05a577c3 100644 --- a/test/calibration/test_setup_library.py +++ b/test/calibration/test_setup_library.py @@ -16,6 +16,7 @@ from qiskit.test import QiskitTestCase from qiskit_experiments.calibration_management.basis_gate_library import FixedFrequencyTransmonSXRZ +from qiskit_experiments.exceptions import CalibrationError class TestFixedFrequencyTransmonSXRZ(QiskitTestCase): @@ -50,3 +51,6 @@ def test_standard_single_qubit_gates(self): for param_conf in library.default_values(): self.assertTrue(param_conf in expected) + + # Check that an error gets raise if the gate is not in the library. + self.assertRaises(CalibrationError, library["bswap"]) From b33f73b3919095c12cef3f21c28f21fbc9d59ee7 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 21 Jul 2021 21:19:51 +0200 Subject: [PATCH 07/20] * Added basis gates property. --- .../calibration_management/basis_gate_library.py | 5 +++++ test/calibration/test_setup_library.py | 3 +++ 2 files changed, 8 insertions(+) diff --git a/qiskit_experiments/calibration_management/basis_gate_library.py b/qiskit_experiments/calibration_management/basis_gate_library.py index 2d0c6fe1ec..175039891c 100644 --- a/qiskit_experiments/calibration_management/basis_gate_library.py +++ b/qiskit_experiments/calibration_management/basis_gate_library.py @@ -51,6 +51,11 @@ def __getitem__(self, name: str) -> ScheduleBlock: return self._schedules[name] + @property + def basis_gates(self) -> List[str]: + """Return the basis gates supported by the library.""" + return list(name for name in self._schedules) + @abstractmethod def default_values(self) -> List[Tuple[ParameterValueType, Parameter, Tuple, ScheduleBlock]]: """Return the default values for the parameters. diff --git a/test/calibration/test_setup_library.py b/test/calibration/test_setup_library.py index 1b05a577c3..cd6a6bb9b3 100644 --- a/test/calibration/test_setup_library.py +++ b/test/calibration/test_setup_library.py @@ -54,3 +54,6 @@ def test_standard_single_qubit_gates(self): # Check that an error gets raise if the gate is not in the library. self.assertRaises(CalibrationError, library["bswap"]) + + # Test the basis gates of the library. + self.assertListEqual(library.basis_gates, ["x", "sx"]) From 58acc43312b0e041b39146860a09fdc6dd1c2244 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 21 Jul 2021 21:22:16 +0200 Subject: [PATCH 08/20] * Renamed transmon library. --- .../calibration_management/basis_gate_library.py | 8 ++++++-- test/calibration/test_backend_calibrations.py | 4 ++-- test/calibration/test_setup_library.py | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/qiskit_experiments/calibration_management/basis_gate_library.py b/qiskit_experiments/calibration_management/basis_gate_library.py index 175039891c..a04a94f8c1 100644 --- a/qiskit_experiments/calibration_management/basis_gate_library.py +++ b/qiskit_experiments/calibration_management/basis_gate_library.py @@ -66,8 +66,12 @@ def default_values(self) -> List[Tuple[ParameterValueType, Parameter, Tuple, Sch """ -class FixedFrequencyTransmonSXRZ(BasisGateLibrary): - """A library of gates for fixed-frequency superconducting qubit architectures.""" +class FixedFrequencyTransmon(BasisGateLibrary): + """A library of gates for fixed-frequency superconducting qubit architectures. + + Note that for now this library supports single-qubit gates and will be extended + in the future. + """ __default_values__ = {"duration": 160, "amp": 0.5, "β": 0.0} diff --git a/test/calibration/test_backend_calibrations.py b/test/calibration/test_backend_calibrations.py index 89c362133f..9c80541331 100644 --- a/test/calibration/test_backend_calibrations.py +++ b/test/calibration/test_backend_calibrations.py @@ -17,7 +17,7 @@ from qiskit.test.mock import FakeArmonk from qiskit_experiments.calibration_management import BackendCalibrations -from qiskit_experiments.calibration_management.basis_gate_library import FixedFrequencyTransmonSXRZ +from qiskit_experiments.calibration_management.basis_gate_library import FixedFrequencyTransmon class TestBackendCalibrations(QiskitTestCase): @@ -36,7 +36,7 @@ def test_setup_withLibrary(self): cals = BackendCalibrations( FakeArmonk(), basis_gates=["x", "sx"], - library=FixedFrequencyTransmonSXRZ, + library=FixedFrequencyTransmon, default_values={"duration": 320}, ) diff --git a/test/calibration/test_setup_library.py b/test/calibration/test_setup_library.py index cd6a6bb9b3..24be23e65b 100644 --- a/test/calibration/test_setup_library.py +++ b/test/calibration/test_setup_library.py @@ -15,7 +15,7 @@ import qiskit.pulse as pulse from qiskit.test import QiskitTestCase -from qiskit_experiments.calibration_management.basis_gate_library import FixedFrequencyTransmonSXRZ +from qiskit_experiments.calibration_management.basis_gate_library import FixedFrequencyTransmon from qiskit_experiments.exceptions import CalibrationError @@ -25,7 +25,7 @@ class TestFixedFrequencyTransmonSXRZ(QiskitTestCase): def test_standard_single_qubit_gates(self): """Test the setup of single-qubit gates.""" - library = FixedFrequencyTransmonSXRZ(default_values={"duration": 320}) + library = FixedFrequencyTransmon(default_values={"duration": 320}) for gate in ["x", "sx"]: sched = library[gate] From c5962fe3a36af3f342bf22537a2cdfd8a7cfe8ac Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 21 Jul 2021 22:40:31 +0200 Subject: [PATCH 09/20] * Docs. --- .../backend_calibrations.py | 14 +++++++++++++- .../basis_gate_library.py | 19 ++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/qiskit_experiments/calibration_management/backend_calibrations.py b/qiskit_experiments/calibration_management/backend_calibrations.py index a2eb7da7ad..58aab01590 100644 --- a/qiskit_experiments/calibration_management/backend_calibrations.py +++ b/qiskit_experiments/calibration_management/backend_calibrations.py @@ -14,7 +14,7 @@ from datetime import datetime from enum import Enum -from typing import Dict, List, Optional, Type +from typing import List, Optional, Type import copy from qiskit.providers.backend import BackendV1 as Backend @@ -53,6 +53,18 @@ def __init__( ): """Setup an instance to manage the calibrations of a backend. + BackendCalibrations can be initialized from a basis gate library, i.e. a subclass of + :class:`BasisGateLibrary`. As example consider the following code: + + .. code-block:: python + + cals = BackendCalibrations( + backend, + basis_gates=["x", "sx"], + library=FixedFrequencyTransmon, + default_values={"duration": 320}, + ) + Args: backend: A backend instance from which to extract the qubit and readout frequencies (which will be added as first guesses for the corresponding parameters) as well diff --git a/qiskit_experiments/calibration_management/basis_gate_library.py b/qiskit_experiments/calibration_management/basis_gate_library.py index a04a94f8c1..1d79f829fe 100644 --- a/qiskit_experiments/calibration_management/basis_gate_library.py +++ b/qiskit_experiments/calibration_management/basis_gate_library.py @@ -28,7 +28,24 @@ class BasisGateLibrary(ABC): - """A base class for libraries of basis gates to make it easier to setup Calibrations.""" + """A base class for libraries of basis gates to make it easier to setup Calibrations. + + Calibration instances can be setup using subclasses of BasisGateLibrary. For example, + + .. code-block:: python + + cals = BackendCalibrations( + FakeArmonk(), + basis_gates=["x", "sx"], + library=FixedFrequencyTransmon, + default_values={"duration": 320}, + ) + + creates an instance of :class:`BackendCalibrations` with the X and square-root X gates. + These gates in this example are obtained from the :class:`FixedFrequencyTransmon` gate + library. Here, we also override the default schedule duration of 160 samples and set + it to 320 samples. + """ # Location where default parameter values are stored. These may be updated at construction. __default_values__ = dict() From 4ae1763b64c7d621130f4d5f5ce0a02ad8b16866 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 21 Jul 2021 22:55:26 +0200 Subject: [PATCH 10/20] * Added better Drag optionality. --- .../basis_gate_library.py | 19 +++++++++++++++---- test/calibration/test_setup_library.py | 14 ++++++++++++-- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/qiskit_experiments/calibration_management/basis_gate_library.py b/qiskit_experiments/calibration_management/basis_gate_library.py index 1d79f829fe..2b66449e04 100644 --- a/qiskit_experiments/calibration_management/basis_gate_library.py +++ b/qiskit_experiments/calibration_management/basis_gate_library.py @@ -92,18 +92,29 @@ class FixedFrequencyTransmon(BasisGateLibrary): __default_values__ = {"duration": 160, "amp": 0.5, "β": 0.0} - def __init__(self, default_values: Optional[Dict] = None): - """Setup the schedules.""" + def __init__(self, default_values: Optional[Dict] = None, use_drag: bool = True): + """Setup the schedules. + + Args: + default_values: Default values for the parameters this dictionary can contain + the following keys: "duration", "amp", "β", and "σ". If "σ" is not provided + this library will take one fourth of the pulse duration as default value. + use_drag: If set to False then Gaussian pulses will be used instead of DRAG + pulses. + """ super().__init__(default_values) dur = Parameter("duration") sigma = Parameter("σ") + def _beta(use_drag): + return Parameter("β") if use_drag else None + # X gates - sched_x = self._single_qubit_schedule("x", dur, Parameter("amp"), sigma, Parameter("β")) + sched_x = self._single_qubit_schedule("x", dur, Parameter("amp"), sigma, _beta(use_drag)) # square-root X gates - sched_sx = self._single_qubit_schedule("sx", dur, Parameter("amp"), sigma, Parameter("β")) + sched_sx = self._single_qubit_schedule("sx", dur, Parameter("amp"), sigma, _beta(use_drag)) for sched in [sched_x, sched_sx]: self._schedules[sched.name] = sched diff --git a/test/calibration/test_setup_library.py b/test/calibration/test_setup_library.py index 24be23e65b..9aa4d92db0 100644 --- a/test/calibration/test_setup_library.py +++ b/test/calibration/test_setup_library.py @@ -19,7 +19,7 @@ from qiskit_experiments.exceptions import CalibrationError -class TestFixedFrequencyTransmonSXRZ(QiskitTestCase): +class TestFixedFrequencyTransmon(QiskitTestCase): """Test the various setup methods.""" def test_standard_single_qubit_gates(self): @@ -53,7 +53,17 @@ def test_standard_single_qubit_gates(self): self.assertTrue(param_conf in expected) # Check that an error gets raise if the gate is not in the library. - self.assertRaises(CalibrationError, library["bswap"]) + with self.assertRaises(CalibrationError): + print(library["bswap"]) # Test the basis gates of the library. self.assertListEqual(library.basis_gates, ["x", "sx"]) + + def test_turn_off_drag(self): + """Test the use_drag parameter.""" + + library = FixedFrequencyTransmon(use_drag=False) + self.assertTrue(isinstance(library["x"].blocks[0].pulse, pulse.Gaussian)) + + library = FixedFrequencyTransmon() + self.assertTrue(isinstance(library["x"].blocks[0].pulse, pulse.Drag)) From 654d01783446270d0a9b367097a5f876232a93d4 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 21 Jul 2021 23:15:35 +0200 Subject: [PATCH 11/20] * Added y and sy gates to library. --- .../calibration_management/basis_gate_library.py | 15 ++++++++++++--- .../calibration_management/update_library.py | 5 ++++- test/calibration/test_setup_library.py | 10 +++++++++- test/calibration/test_update_library.py | 13 +++++++------ 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/qiskit_experiments/calibration_management/basis_gate_library.py b/qiskit_experiments/calibration_management/basis_gate_library.py index 2b66449e04..dc84d8c07e 100644 --- a/qiskit_experiments/calibration_management/basis_gate_library.py +++ b/qiskit_experiments/calibration_management/basis_gate_library.py @@ -113,10 +113,16 @@ def _beta(use_drag): # X gates sched_x = self._single_qubit_schedule("x", dur, Parameter("amp"), sigma, _beta(use_drag)) + # Y gates + sched_y = self._single_qubit_schedule("y", dur, Parameter("amp"), sigma, _beta(use_drag)) + # square-root X gates sched_sx = self._single_qubit_schedule("sx", dur, Parameter("amp"), sigma, _beta(use_drag)) - for sched in [sched_x, sched_sx]: + # square-root Y gates + sched_sy = self._single_qubit_schedule("sy", dur, Parameter("amp"), sigma, _beta(use_drag)) + + for sched in [sched_x, sched_y, sched_sx, sched_sy]: self._schedules[sched.name] = sched @staticmethod @@ -148,7 +154,7 @@ def default_values(self) -> List[Tuple[ParameterValueType, Parameter, Tuple, Sch :class:`Calibrations` can call :meth:`add_parameter_value` on the tuples. """ defaults = [] - for name in ["x", "sx"]: + for name in self.basis_gates: schedule = self._schedules[name] for param in schedule.parameters: if "ch" not in param.name: @@ -158,9 +164,12 @@ def default_values(self) -> List[Tuple[ParameterValueType, Parameter, Tuple, Sch else: value = self.__default_values__[param.name] - if name == "sx" and param.name == "amp": + if name in {"sx", "sy"} and param.name == "amp": value /= 2.0 + if "y" in name and param.name == "amp": + value *= 1.0j + defaults.append((value, param.name, tuple(), name)) return defaults diff --git a/qiskit_experiments/calibration_management/update_library.py b/qiskit_experiments/calibration_management/update_library.py index 44f6052178..36adfb01ba 100644 --- a/qiskit_experiments/calibration_management/update_library.py +++ b/qiskit_experiments/calibration_management/update_library.py @@ -209,7 +209,10 @@ def update( rate = 2 * np.pi * freq for angle, param, schedule in angles_schedules: - value = np.round(angle / rate, decimals=8) + qubits = exp_data.data(0)["metadata"]["qubits"] + prev_amp = calibrations.get_parameter_value(param, qubits, schedule, group=group) + + value = np.round(angle / rate, decimals=8) * np.exp(1.0j * np.angle(prev_amp)) cls._add_parameter_value(calibrations, exp_data, value, param, schedule, group) diff --git a/test/calibration/test_setup_library.py b/test/calibration/test_setup_library.py index 9aa4d92db0..0b4e3ae40f 100644 --- a/test/calibration/test_setup_library.py +++ b/test/calibration/test_setup_library.py @@ -47,6 +47,14 @@ def test_standard_single_qubit_gates(self): (0.0, "β", (), "sx"), (0.25, "amp", (), "sx"), (80, "σ", (), "sx"), + (0.5j, "amp", (), "y"), + (0.0, "β", (), "y"), + (320, "duration", (), "y"), + (80, "σ", (), "y"), + (320, "duration", (), "sy"), + (0.0, "β", (), "sy"), + (0.25j, "amp", (), "sy"), + (80, "σ", (), "sy"), ] for param_conf in library.default_values(): @@ -57,7 +65,7 @@ def test_standard_single_qubit_gates(self): print(library["bswap"]) # Test the basis gates of the library. - self.assertListEqual(library.basis_gates, ["x", "sx"]) + self.assertListEqual(library.basis_gates, ["x", "y", "sx", "sy"]) def test_turn_off_drag(self): """Test the use_drag parameter.""" diff --git a/test/calibration/test_update_library.py b/test/calibration/test_update_library.py index 60cfcbd576..2235554d59 100644 --- a/test/calibration/test_update_library.py +++ b/test/calibration/test_update_library.py @@ -40,20 +40,21 @@ def setUp(self): self.cals = Calibrations() self.qubit = 1 - amp = Parameter("amp") + axp = Parameter("amp") chan = Parameter("ch0") with pulse.build(name="xp") as xp: - pulse.play(pulse.Gaussian(duration=160, amp=amp, sigma=40), pulse.DriveChannel(chan)) + pulse.play(pulse.Gaussian(duration=160, amp=axp, sigma=40), pulse.DriveChannel(chan)) - amp = Parameter("amp") + ax90p = Parameter("amp") with pulse.build(name="x90p") as x90p: - pulse.play(pulse.Gaussian(duration=160, amp=amp, sigma=40), pulse.DriveChannel(chan)) + pulse.play(pulse.Gaussian(duration=160, amp=ax90p, sigma=40), pulse.DriveChannel(chan)) self.x90p = x90p self.cals.add_schedule(xp) self.cals.add_schedule(x90p) self.cals.add_parameter_value(0.2, "amp", self.qubit, "xp") + self.cals.add_parameter_value(0.1, "amp", self.qubit, "x90p") def test_amplitude(self): """Test amplitude update from Rabi.""" @@ -68,14 +69,14 @@ def test_amplitude(self): to_update = [(np.pi, "amp", "xp"), (np.pi / 2, "amp", self.x90p)] - self.assertEqual(len(self.cals.parameters_table()), 1) + self.assertEqual(len(self.cals.parameters_table()), 2) Amplitude.update(self.cals, exp_data, angles_schedules=to_update) with self.assertRaises(CalibrationError): self.cals.get_schedule("xp", qubits=0) - self.assertEqual(len(self.cals.parameters_table()), 3) + self.assertEqual(len(self.cals.parameters_table()), 4) # Now check the corresponding schedules result = exp_data.analysis_results(-1).data() From 1dadebea3da6f6fcae928eec26e30fc741e3d14a Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 21 Jul 2021 23:21:56 +0200 Subject: [PATCH 12/20] * Add check to make sure we do not add default values for gates that were not selected. --- .../calibration_management/backend_calibrations.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qiskit_experiments/calibration_management/backend_calibrations.py b/qiskit_experiments/calibration_management/backend_calibrations.py index 58aab01590..fb0d6ba461 100644 --- a/qiskit_experiments/calibration_management/backend_calibrations.py +++ b/qiskit_experiments/calibration_management/backend_calibrations.py @@ -108,7 +108,9 @@ def __init__( # Add the default values for param_conf in library_instance.default_values(): - self.add_parameter_value(*param_conf) + schedule_name = param_conf[-1] + if schedule_name in basis_gates: + self.add_parameter_value(*param_conf) def _get_frequencies( self, From 577394bf9e4712703d309a778d8a592cdf4a72b8 Mon Sep 17 00:00:00 2001 From: Daniel Egger <38065505+eggerdj@users.noreply.github.com> Date: Thu, 22 Jul 2021 08:59:58 +0200 Subject: [PATCH 13/20] Update qiskit_experiments/calibration_management/basis_gate_library.py Co-authored-by: Naoki Kanazawa --- qiskit_experiments/calibration_management/basis_gate_library.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/calibration_management/basis_gate_library.py b/qiskit_experiments/calibration_management/basis_gate_library.py index dc84d8c07e..7aecb630e3 100644 --- a/qiskit_experiments/calibration_management/basis_gate_library.py +++ b/qiskit_experiments/calibration_management/basis_gate_library.py @@ -13,7 +13,7 @@ """ A collections of libraries to setup Calibrations. -Note that the set of available functions will be extended in future releases. +Note that the set of available library will be extended in future releases. """ from abc import ABC, abstractmethod From 529d3b101caa7be1830d8c7258eb8223a896f8ef Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 22 Jul 2021 11:17:07 +0200 Subject: [PATCH 14/20] * Docs. --- .../basis_gate_library.py | 21 ++----------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/qiskit_experiments/calibration_management/basis_gate_library.py b/qiskit_experiments/calibration_management/basis_gate_library.py index dc84d8c07e..7e4e0cecfc 100644 --- a/qiskit_experiments/calibration_management/basis_gate_library.py +++ b/qiskit_experiments/calibration_management/basis_gate_library.py @@ -13,7 +13,7 @@ """ A collections of libraries to setup Calibrations. -Note that the set of available functions will be extended in future releases. +Note that the set of available libraries will be extended in future releases. """ from abc import ABC, abstractmethod @@ -28,24 +28,7 @@ class BasisGateLibrary(ABC): - """A base class for libraries of basis gates to make it easier to setup Calibrations. - - Calibration instances can be setup using subclasses of BasisGateLibrary. For example, - - .. code-block:: python - - cals = BackendCalibrations( - FakeArmonk(), - basis_gates=["x", "sx"], - library=FixedFrequencyTransmon, - default_values={"duration": 320}, - ) - - creates an instance of :class:`BackendCalibrations` with the X and square-root X gates. - These gates in this example are obtained from the :class:`FixedFrequencyTransmon` gate - library. Here, we also override the default schedule duration of 160 samples and set - it to 320 samples. - """ + """A base class for libraries of basis gates to make it easier to setup Calibrations.""" # Location where default parameter values are stored. These may be updated at construction. __default_values__ = dict() From 824bb4441b916055b4f0cc6a5ca83bcaa22c53e8 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 22 Jul 2021 11:27:07 +0200 Subject: [PATCH 15/20] * Update the way the library is passed to BackendCalibrations. --- .../calibration_management/backend_calibrations.py | 13 ++++--------- test/calibration/test_backend_calibrations.py | 3 +-- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/qiskit_experiments/calibration_management/backend_calibrations.py b/qiskit_experiments/calibration_management/backend_calibrations.py index fb0d6ba461..a024e0c84a 100644 --- a/qiskit_experiments/calibration_management/backend_calibrations.py +++ b/qiskit_experiments/calibration_management/backend_calibrations.py @@ -48,8 +48,7 @@ def __init__( self, backend: Backend, basis_gates: Optional[List[str]] = None, - library: Type[BasisGateLibrary] = None, - **library_options, + library: BasisGateLibrary = None, ): """Setup an instance to manage the calibrations of a backend. @@ -61,8 +60,7 @@ def __init__( cals = BackendCalibrations( backend, basis_gates=["x", "sx"], - library=FixedFrequencyTransmon, - default_values={"duration": 320}, + library=FixedFrequencyTransmon(default_values={duration: 320}), ) Args: @@ -72,8 +70,6 @@ def __init__( basis_gates: The basis gates to extract from the library and add to the calibrations. library: A library class that will be instantiated with the library options to then get template schedules to register as well as default parameter values. - library_options: Instantiation options for the gate library. See the init method of - :class:`BasisGateLibrary` and its subclasses. """ if hasattr(backend.configuration(), "control_channels"): control_channels = backend.configuration().control_channels @@ -100,14 +96,13 @@ def __init__( basis_gates = basis_gates or list() if library is not None: - library_instance = library(**library_options) # Add the basis gates for gate in basis_gates: - self.add_schedule(library_instance[gate]) + self.add_schedule(library[gate]) # Add the default values - for param_conf in library_instance.default_values(): + for param_conf in library.default_values(): schedule_name = param_conf[-1] if schedule_name in basis_gates: self.add_parameter_value(*param_conf) diff --git a/test/calibration/test_backend_calibrations.py b/test/calibration/test_backend_calibrations.py index 9c80541331..5e3bd11365 100644 --- a/test/calibration/test_backend_calibrations.py +++ b/test/calibration/test_backend_calibrations.py @@ -36,8 +36,7 @@ def test_setup_withLibrary(self): cals = BackendCalibrations( FakeArmonk(), basis_gates=["x", "sx"], - library=FixedFrequencyTransmon, - default_values={"duration": 320}, + library=FixedFrequencyTransmon(default_values={"duration": 320}), ) # Check the x gate From 332f8a4e1501f81942e763511e5bdf5ed1108814 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 22 Jul 2021 11:54:57 +0200 Subject: [PATCH 16/20] * Lint. --- .../calibration_management/backend_calibrations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/calibration_management/backend_calibrations.py b/qiskit_experiments/calibration_management/backend_calibrations.py index a024e0c84a..c638a6dd36 100644 --- a/qiskit_experiments/calibration_management/backend_calibrations.py +++ b/qiskit_experiments/calibration_management/backend_calibrations.py @@ -14,7 +14,7 @@ from datetime import datetime from enum import Enum -from typing import List, Optional, Type +from typing import List, Optional import copy from qiskit.providers.backend import BackendV1 as Backend From 8282ee0a84ed8f62724f441c9cafba3f73363652 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 22 Jul 2021 17:10:47 +0200 Subject: [PATCH 17/20] * Added option to link parameters. --- .../basis_gate_library.py | 37 ++++++++++---- test/calibration/test_setup_library.py | 48 +++++++++++++++---- 2 files changed, 68 insertions(+), 17 deletions(-) diff --git a/qiskit_experiments/calibration_management/basis_gate_library.py b/qiskit_experiments/calibration_management/basis_gate_library.py index 7e4e0cecfc..ec6cbc677a 100644 --- a/qiskit_experiments/calibration_management/basis_gate_library.py +++ b/qiskit_experiments/calibration_management/basis_gate_library.py @@ -75,7 +75,12 @@ class FixedFrequencyTransmon(BasisGateLibrary): __default_values__ = {"duration": 160, "amp": 0.5, "β": 0.0} - def __init__(self, default_values: Optional[Dict] = None, use_drag: bool = True): + def __init__( + self, + default_values: Optional[Dict] = None, + use_drag: bool = True, + link_parameters: bool = True + ): """Setup the schedules. Args: @@ -84,26 +89,38 @@ def __init__(self, default_values: Optional[Dict] = None, use_drag: bool = True) this library will take one fourth of the pulse duration as default value. use_drag: If set to False then Gaussian pulses will be used instead of DRAG pulses. + link_parameters: if set to True then the amplitude and DRAG parameters of the + X and Y gates will be linked as well as those of the SX and SY gates. """ super().__init__(default_values) + self._link_parameters = link_parameters dur = Parameter("duration") sigma = Parameter("σ") + # Generate the pulse parameters def _beta(use_drag): return Parameter("β") if use_drag else None - # X gates - sched_x = self._single_qubit_schedule("x", dur, Parameter("amp"), sigma, _beta(use_drag)) + x_amp, x_beta = Parameter("amp"), _beta(use_drag) - # Y gates - sched_y = self._single_qubit_schedule("y", dur, Parameter("amp"), sigma, _beta(use_drag)) + if self._link_parameters: + y_amp, y_beta = 1.0j*x_amp, x_beta + else: + y_amp, y_beta = Parameter("amp"), _beta(use_drag) + + sx_amp, sx_beta = Parameter("amp"), _beta(use_drag) - # square-root X gates - sched_sx = self._single_qubit_schedule("sx", dur, Parameter("amp"), sigma, _beta(use_drag)) + if self._link_parameters: + sy_amp, sy_beta = 1.0j * sx_amp, sx_beta + else: + sy_amp, sy_beta = Parameter("amp"), _beta(use_drag) - # square-root Y gates - sched_sy = self._single_qubit_schedule("sy", dur, Parameter("amp"), sigma, _beta(use_drag)) + # Create the schedules for the gates + sched_x = self._single_qubit_schedule("x", dur, x_amp, sigma, x_beta) + sched_y = self._single_qubit_schedule("y", dur, y_amp, sigma, y_beta) + sched_sx = self._single_qubit_schedule("sx", dur, sx_amp, sigma, sx_beta) + sched_sy = self._single_qubit_schedule("sy", dur, sy_amp, sigma, sy_beta) for sched in [sched_x, sched_y, sched_sx, sched_sy]: self._schedules[sched.name] = sched @@ -141,6 +158,8 @@ def default_values(self) -> List[Tuple[ParameterValueType, Parameter, Tuple, Sch schedule = self._schedules[name] for param in schedule.parameters: if "ch" not in param.name: + if "y" in name and self._link_parameters: + continue if param.name == "σ" and "σ" not in self.__default_values__: value = self.__default_values__["duration"] / 4 diff --git a/test/calibration/test_setup_library.py b/test/calibration/test_setup_library.py index 0b4e3ae40f..ba5a19e261 100644 --- a/test/calibration/test_setup_library.py +++ b/test/calibration/test_setup_library.py @@ -33,11 +33,16 @@ def test_standard_single_qubit_gates(self): self.assertEqual(len(sched.parameters), 5) sched_x = library["x"] + sched_y = library["y"] sched_sx = library["sx"] + sched_sy = library["sy"] self.assertEqual(sched_x.blocks[0].pulse.duration, sched_sx.blocks[0].pulse.duration) self.assertEqual(sched_x.blocks[0].pulse.sigma, sched_sx.blocks[0].pulse.sigma) + self.assertEqual(len(sched_x.parameters & sched_y.parameters), 4) + self.assertEqual(len(sched_sx.parameters & sched_sy.parameters), 4) + expected = [ (0.5, "amp", (), "x"), (0.0, "β", (), "x"), @@ -47,14 +52,6 @@ def test_standard_single_qubit_gates(self): (0.0, "β", (), "sx"), (0.25, "amp", (), "sx"), (80, "σ", (), "sx"), - (0.5j, "amp", (), "y"), - (0.0, "β", (), "y"), - (320, "duration", (), "y"), - (80, "σ", (), "y"), - (320, "duration", (), "sy"), - (0.0, "β", (), "sy"), - (0.25j, "amp", (), "sy"), - (80, "σ", (), "sy"), ] for param_conf in library.default_values(): @@ -75,3 +72,38 @@ def test_turn_off_drag(self): library = FixedFrequencyTransmon() self.assertTrue(isinstance(library["x"].blocks[0].pulse, pulse.Drag)) + + def test_unlinked_parameters(self): + """Test the we get schedules with unlinked parameters.""" + + library = FixedFrequencyTransmon(link_parameters=False) + + sched_x = library["x"] + sched_y = library["y"] + sched_sx = library["sx"] + sched_sy = library["sy"] + + # Test the number of parameters. + self.assertEqual(len(sched_x.parameters & sched_y.parameters), 2) + self.assertEqual(len(sched_sx.parameters & sched_sy.parameters), 2) + + expected = [ + (0.5, "amp", (), "x"), + (0.0, "β", (), "x"), + (320, "duration", (), "x"), + (80, "σ", (), "x"), + (320, "duration", (), "sx"), + (0.0, "β", (), "sx"), + (0.25, "amp", (), "sx"), + (80, "σ", (), "sx"), + (0.5j, "amp", (), "y"), + (0.0, "β", (), "y"), + (320, "duration", (), "y"), + (80, "σ", (), "y"), + (320, "duration", (), "sy"), + (0.0, "β", (), "sy"), + (0.25j, "amp", (), "sy"), + (80, "σ", (), "sy"), + ] + + self.assertSetEqual(set(library.default_values()), set(expected)) From d3c0b69e7c62035c1ea17f68e661e9cfb071494a Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 22 Jul 2021 20:57:07 +0200 Subject: [PATCH 18/20] * Partial generation of basis gates. --- .../backend_calibrations.py | 16 +++---- .../basis_gate_library.py | 44 +++++++++++++++---- test/calibration/test_backend_calibrations.py | 5 ++- test/calibration/test_setup_library.py | 26 +++++++---- 4 files changed, 63 insertions(+), 28 deletions(-) diff --git a/qiskit_experiments/calibration_management/backend_calibrations.py b/qiskit_experiments/calibration_management/backend_calibrations.py index c638a6dd36..062a3da2f8 100644 --- a/qiskit_experiments/calibration_management/backend_calibrations.py +++ b/qiskit_experiments/calibration_management/backend_calibrations.py @@ -14,7 +14,7 @@ from datetime import datetime from enum import Enum -from typing import List, Optional +from typing import List import copy from qiskit.providers.backend import BackendV1 as Backend @@ -47,7 +47,6 @@ class BackendCalibrations(Calibrations): def __init__( self, backend: Backend, - basis_gates: Optional[List[str]] = None, library: BasisGateLibrary = None, ): """Setup an instance to manage the calibrations of a backend. @@ -59,15 +58,16 @@ def __init__( cals = BackendCalibrations( backend, - basis_gates=["x", "sx"], - library=FixedFrequencyTransmon(default_values={duration: 320}), + library=FixedFrequencyTransmon( + basis_gates=["x", "sx"], + default_values={duration: 320} + ) ) Args: backend: A backend instance from which to extract the qubit and readout frequencies (which will be added as first guesses for the corresponding parameters) as well as the coupling map. - basis_gates: The basis gates to extract from the library and add to the calibrations. library: A library class that will be instantiated with the library options to then get template schedules to register as well as default parameter values. """ @@ -93,18 +93,16 @@ def __init__( for meas, freq in enumerate(backend.defaults().meas_freq_est): self.add_parameter_value(freq, self.meas_freq, meas) - basis_gates = basis_gates or list() - if library is not None: # Add the basis gates - for gate in basis_gates: + for gate in library.basis_gates: self.add_schedule(library[gate]) # Add the default values for param_conf in library.default_values(): schedule_name = param_conf[-1] - if schedule_name in basis_gates: + if schedule_name in library.basis_gates: self.add_parameter_value(*param_conf) def _get_frequencies( diff --git a/qiskit_experiments/calibration_management/basis_gate_library.py b/qiskit_experiments/calibration_management/basis_gate_library.py index ec6cbc677a..2cc8ac6e1f 100644 --- a/qiskit_experiments/calibration_management/basis_gate_library.py +++ b/qiskit_experiments/calibration_management/basis_gate_library.py @@ -33,16 +33,37 @@ class BasisGateLibrary(ABC): # Location where default parameter values are stored. These may be updated at construction. __default_values__ = dict() - def __init__(self, default_values: Optional[Dict] = None): + # The basis gates that this library generates. + __supported_gates__ = None + + def __init__( + self, basis_gates: Optional[List[str]] = None, default_values: Optional[Dict] = None + ): """Setup the library. Args: + basis_gates: The basis gates to generate. default_values: A dictionary to override library default parameter values. + + Raises: + CalibrationError: If on of the given basis gates is not supported by the library. """ self._schedules = dict() + # Update the default values. + self._default_values = dict(self.__default_values__) if default_values is not None: - self.__default_values__.update(default_values) + self._default_values.update(default_values) + + if basis_gates is not None: + for gate in basis_gates: + if gate not in self.__supported_gates__: + raise CalibrationError( + f"Gate {gate} is not supported by {self.__class__.__name__}. " + f"Supported gates are: {self.__supported_gates__}." + ) + + self._basis_gates = basis_gates or self.__supported_gates__ def __getitem__(self, name: str) -> ScheduleBlock: """Return the schedule.""" @@ -75,15 +96,19 @@ class FixedFrequencyTransmon(BasisGateLibrary): __default_values__ = {"duration": 160, "amp": 0.5, "β": 0.0} + __supported_gates__ = ["x", "y", "sx", "sy"] + def __init__( self, + basis_gates: Optional[List[str]] = None, default_values: Optional[Dict] = None, use_drag: bool = True, - link_parameters: bool = True + link_parameters: bool = True, ): """Setup the schedules. Args: + basis_gates: The basis gates to generate. default_values: Default values for the parameters this dictionary can contain the following keys: "duration", "amp", "β", and "σ". If "σ" is not provided this library will take one fourth of the pulse duration as default value. @@ -92,7 +117,7 @@ def __init__( link_parameters: if set to True then the amplitude and DRAG parameters of the X and Y gates will be linked as well as those of the SX and SY gates. """ - super().__init__(default_values) + super().__init__(basis_gates, default_values) self._link_parameters = link_parameters dur = Parameter("duration") @@ -105,7 +130,7 @@ def _beta(use_drag): x_amp, x_beta = Parameter("amp"), _beta(use_drag) if self._link_parameters: - y_amp, y_beta = 1.0j*x_amp, x_beta + y_amp, y_beta = 1.0j * x_amp, x_beta else: y_amp, y_beta = Parameter("amp"), _beta(use_drag) @@ -123,7 +148,8 @@ def _beta(use_drag): sched_sy = self._single_qubit_schedule("sy", dur, sy_amp, sigma, sy_beta) for sched in [sched_x, sched_y, sched_sx, sched_sy]: - self._schedules[sched.name] = sched + if sched.name in self._basis_gates: + self._schedules[sched.name] = sched @staticmethod def _single_qubit_schedule( @@ -161,10 +187,10 @@ def default_values(self) -> List[Tuple[ParameterValueType, Parameter, Tuple, Sch if "y" in name and self._link_parameters: continue - if param.name == "σ" and "σ" not in self.__default_values__: - value = self.__default_values__["duration"] / 4 + if param.name == "σ" and "σ" not in self._default_values: + value = self._default_values["duration"] / 4 else: - value = self.__default_values__[param.name] + value = self._default_values[param.name] if name in {"sx", "sy"} and param.name == "amp": value /= 2.0 diff --git a/test/calibration/test_backend_calibrations.py b/test/calibration/test_backend_calibrations.py index 5e3bd11365..c386b67d95 100644 --- a/test/calibration/test_backend_calibrations.py +++ b/test/calibration/test_backend_calibrations.py @@ -35,8 +35,9 @@ def test_setup_withLibrary(self): cals = BackendCalibrations( FakeArmonk(), - basis_gates=["x", "sx"], - library=FixedFrequencyTransmon(default_values={"duration": 320}), + library=FixedFrequencyTransmon( + basis_gates=["x", "sx"], default_values={"duration": 320} + ), ) # Check the x gate diff --git a/test/calibration/test_setup_library.py b/test/calibration/test_setup_library.py index ba5a19e261..1bfba395c4 100644 --- a/test/calibration/test_setup_library.py +++ b/test/calibration/test_setup_library.py @@ -90,20 +90,30 @@ def test_unlinked_parameters(self): expected = [ (0.5, "amp", (), "x"), (0.0, "β", (), "x"), - (320, "duration", (), "x"), - (80, "σ", (), "x"), - (320, "duration", (), "sx"), + (160, "duration", (), "x"), + (40, "σ", (), "x"), + (160, "duration", (), "sx"), (0.0, "β", (), "sx"), (0.25, "amp", (), "sx"), - (80, "σ", (), "sx"), + (40, "σ", (), "sx"), (0.5j, "amp", (), "y"), (0.0, "β", (), "y"), - (320, "duration", (), "y"), - (80, "σ", (), "y"), - (320, "duration", (), "sy"), + (160, "duration", (), "y"), + (40, "σ", (), "y"), + (160, "duration", (), "sy"), (0.0, "β", (), "sy"), (0.25j, "amp", (), "sy"), - (80, "σ", (), "sy"), + (40, "σ", (), "sy"), ] self.assertSetEqual(set(library.default_values()), set(expected)) + + def setup_partial_gates(self): + """Check that we do not setup all gates if not required.""" + + library = FixedFrequencyTransmon(basis_gates=["x", "sy"]) + + self.assertTrue("x" in library) + self.assertTrue("sy" in library) + self.assertTrue("y" not in library) + self.assertTrue("sx" not in library) From 4ca85e468bb9be7b53ccbeb9338bbcb9d963f313 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 22 Jul 2021 20:58:38 +0200 Subject: [PATCH 19/20] * Added test. --- test/calibration/test_setup_library.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/calibration/test_setup_library.py b/test/calibration/test_setup_library.py index 1bfba395c4..66ea6fd717 100644 --- a/test/calibration/test_setup_library.py +++ b/test/calibration/test_setup_library.py @@ -117,3 +117,6 @@ def setup_partial_gates(self): self.assertTrue("sy" in library) self.assertTrue("y" not in library) self.assertTrue("sx" not in library) + + with self.assertRaises(CalibrationError): + FixedFrequencyTransmon(basis_gates=["x", "bswap"]) From a457f1afcfc6906e7ab5341eb8373a58478d62b8 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 22 Jul 2021 22:06:41 +0200 Subject: [PATCH 20/20] * Fixed test issue and added in operator. --- .../calibration_management/basis_gate_library.py | 4 ++++ test/calibration/test_setup_library.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/qiskit_experiments/calibration_management/basis_gate_library.py b/qiskit_experiments/calibration_management/basis_gate_library.py index 2cc8ac6e1f..8270eba8cb 100644 --- a/qiskit_experiments/calibration_management/basis_gate_library.py +++ b/qiskit_experiments/calibration_management/basis_gate_library.py @@ -72,6 +72,10 @@ def __getitem__(self, name: str) -> ScheduleBlock: return self._schedules[name] + def __contains__(self, name: str) -> bool: + """Check if the basis gate is in the library.""" + return name in self._schedules + @property def basis_gates(self) -> List[str]: """Return the basis gates supported by the library.""" diff --git a/test/calibration/test_setup_library.py b/test/calibration/test_setup_library.py index 66ea6fd717..370d12e80a 100644 --- a/test/calibration/test_setup_library.py +++ b/test/calibration/test_setup_library.py @@ -108,7 +108,7 @@ def test_unlinked_parameters(self): self.assertSetEqual(set(library.default_values()), set(expected)) - def setup_partial_gates(self): + def test_setup_partial_gates(self): """Check that we do not setup all gates if not required.""" library = FixedFrequencyTransmon(basis_gates=["x", "sy"])