From e2992f0da0b3e362f282d676b1ee776a4fd8afce Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Fri, 29 Apr 2022 14:55:30 +0200 Subject: [PATCH 01/13] * Added the restless mix-in to the init for documentation. --- qiskit_experiments/framework/__init__.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/qiskit_experiments/framework/__init__.py b/qiskit_experiments/framework/__init__.py index 57af1bc065..45bd35c22a 100644 --- a/qiskit_experiments/framework/__init__.py +++ b/qiskit_experiments/framework/__init__.py @@ -176,6 +176,11 @@ - :meth:`BaseExperiment._metadata` to add any experiment metadata to the result data. +Furthermore, some characterization and calibration experiments can be run with restless +measurements, i.e. measurements where the qubits are not reset and circuits are executed +immediately after the previous measurement. Here, the :class:`.RestlessMixin` can help +to set the appropriate run options and data processing chain. + Analysis Subclasses ******************* @@ -232,6 +237,14 @@ BaseExperiment BaseAnalysis +Mix-ins +******* + +.. autosummary:: + :toctree: ../stubs/ + + RestlessMixin + .. _create-experiment: """ from qiskit.providers.options import Options @@ -252,3 +265,4 @@ CompositeAnalysis, ) from .json import ExperimentEncoder, ExperimentDecoder +from .restless_mixin import RestlessMixin From 33d1c5010746eb01f8ce7d9bbf2dccbeb620d846 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 14 Jun 2023 17:16:23 +0200 Subject: [PATCH 02/13] * Remove special variables from the Calibrations --- .../basis_gate_library.py | 32 +++++++++++++++++-- .../calibration_management/calibrations.py | 18 +++-------- .../calibration_management/update_library.py | 7 ++-- .../library/calibration/fine_frequency_cal.py | 9 ++++-- .../library/calibration/frequency_cal.py | 5 ++- ...ams_without_schedule-20555d98875a626b.yaml | 10 ++++++ test/calibration/test_update_library.py | 7 ++-- .../calibration/test_fine_frequency.py | 4 +-- test/library/calibration/test_ramsey_xy.py | 2 +- .../calibration/test_rough_frequency.py | 4 +-- 10 files changed, 66 insertions(+), 32 deletions(-) create mode 100644 releasenotes/notes/params_without_schedule-20555d98875a626b.yaml diff --git a/qiskit_experiments/calibration_management/basis_gate_library.py b/qiskit_experiments/calibration_management/basis_gate_library.py index bf48dd7f47..cce4e236bd 100644 --- a/qiskit_experiments/calibration_management/basis_gate_library.py +++ b/qiskit_experiments/calibration_management/basis_gate_library.py @@ -36,10 +36,14 @@ class BasisGateLibrary(ABC, Mapping): # Location where default parameter values are stored. These may be updated at construction. __default_values__ = {} + # Parameters that do not belong to a schedule, set of (name: str, qubits: tuple) + __parameters_without_schedule__ = set() + def __init__( self, basis_gates: Optional[List[str]] = None, default_values: Optional[Dict] = None, + params_without_schedule: Optional[Set] = None, **extra_kwargs, ): """Setup the library. @@ -47,6 +51,8 @@ def __init__( Args: basis_gates: The basis gates to generate. default_values: A dictionary to override library default parameter values. + params_without_schedule: A set of parameters without a schedule to override the + library defaults. extra_kwargs: Extra key-word arguments of the subclasses that are saved to be able to reconstruct the library using the :meth:`__init__` method. @@ -56,9 +62,13 @@ def __init__( # Update the default values. self._extra_kwargs = extra_kwargs self._default_values = self.__default_values__.copy() + self._params_without_schedule = self.__parameters_without_schedule__.copy() if default_values is not None: self._default_values.update(default_values) + if params_without_schedule is not None: + self._params_without_schedule.update(params_without_schedule) + if basis_gates is None: basis_gates = list(self.__supported_gates__) @@ -126,6 +136,11 @@ def default_values(self) -> List[DefaultCalValue]: on the tuples. """ + @property + def parameters_without_schedule(self) -> Set: + """Return the parameters that do not have a schedule.""" + return self._params_without_schedule + @abstractmethod def _build_schedules(self, basis_gates: Set[str]) -> Dict[str, ScheduleBlock]: """Build the schedules stored in the library. @@ -146,7 +161,11 @@ def _build_schedules(self, basis_gates: Set[str]) -> Dict[str, ScheduleBlock]: def config(self) -> Dict[str, Any]: """Return the settings used to initialize the library.""" - kwargs = {"basis_gates": self.basis_gates, "default_values": self._default_values} + kwargs = { + "basis_gates": self.basis_gates, + "default_values": self._default_values, + "params_without_schedule": self._params_without_schedule, + } kwargs.update(self._extra_kwargs) return { @@ -204,16 +223,23 @@ class FixedFrequencyTransmon(BasisGateLibrary): The amplitude of the ``sx`` and ``sy`` pulses is half the provided value. - angle: The phase of the complex amplitude of the pulses. + Parameters without schedule: + - meas_freq: frequency of the measurement drives, applies to all qubits. + - drive_freq: frequency of the qubit drives, applies to all qubits. + Note that the β and amp parameters may be linked between the x and y as well as between the sx and sy pulses. All pulses share the same duration and σ parameters. """ __default_values__ = {"duration": 160, "amp": 0.5, "β": 0.0, "angle": 0.0} + __parameters_without_schedule__ = [("meas_freq", ()), ("drive_freq", ())] + def __init__( self, basis_gates: Optional[List[str]] = None, default_values: Optional[Dict] = None, + params_without_schedule: Optional[Set] = None, link_parameters: bool = True, ): """Setup the schedules. @@ -223,6 +249,8 @@ def __init__( 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. + params_without_schedule: A set of parameters without a schedule to override the + library defaults. link_parameters: If set to ``True``, then the amplitude and DRAG parameters of the :math:`X` and :math:`Y` gates will be linked as well as those of the :math:`SX` and :math:`SY` gates. @@ -231,7 +259,7 @@ def __init__( extra_kwargs = {"link_parameters": link_parameters} - super().__init__(basis_gates, default_values, **extra_kwargs) + super().__init__(basis_gates, default_values, params_without_schedule, **extra_kwargs) @property def __supported_gates__(self) -> Dict[str, int]: diff --git a/qiskit_experiments/calibration_management/calibrations.py b/qiskit_experiments/calibration_management/calibrations.py index 7ace1fba3b..4cf8082c82 100644 --- a/qiskit_experiments/calibration_management/calibrations.py +++ b/qiskit_experiments/calibration_management/calibrations.py @@ -63,12 +63,6 @@ class Calibrations: ScheduleBlock are supported. """ - # The name of the parameter under which the qubit frequencies are registered. - __drive_freq_parameter__ = "drive_freq" - - # The name of the parameter under which the readout frequencies are registered. - __readout_freq_parameter__ = "meas_freq" - def __init__( self, coupling_map: Optional[List[List[int]]] = None, @@ -163,6 +157,10 @@ def __init__( for param_conf in lib.default_values(): self.add_parameter_value(*param_conf, update_inst_map=False) + # Add the parameters that do not belong to a schedule. + for param_name, qubits in lib.parameters_without_schedule: + self._register_parameter(Parameter(param_name), qubits) + # This internal parameter is False so that if a schedule is added after the # init it will be set to True and serialization will raise an error. self._has_manually_added_schedule = False @@ -170,12 +168,6 @@ def __init__( # Instruction schedule map variables and support variables. self._inst_map = InstructionScheduleMap() - # Use the same naming convention as in backend.defaults() - self.drive_freq = Parameter(self.__drive_freq_parameter__) - self.meas_freq = Parameter(self.__readout_freq_parameter__) - self._register_parameter(self.drive_freq, ()) - self._register_parameter(self.meas_freq, ()) - # Backends with a single qubit may not have a coupling map. self._coupling_map = coupling_map if coupling_map is not None else [] @@ -237,7 +229,7 @@ def from_backend( libraries: A list of libraries from which to get template schedules to register as well as default parameter values. add_parameter_defaults: A boolean to indicate whether the default parameter values of - the given library should be used to populate the calibrations. By default this + the given library should be used to populate the calibrations. By default, this value is ``True``. Returns: diff --git a/qiskit_experiments/calibration_management/update_library.py b/qiskit_experiments/calibration_management/update_library.py index eac5e32d04..ff6009432c 100644 --- a/qiskit_experiments/calibration_management/update_library.py +++ b/qiskit_experiments/calibration_management/update_library.py @@ -149,7 +149,7 @@ def update( calibrations: Calibrations, exp_data: ExperimentData, result_index: Optional[int] = None, - parameter: str = None, + parameter: str = "drive_freq", group: str = "default", fit_parameter: Optional[str] = None, **options, @@ -163,16 +163,13 @@ def update( exp_data: The experiment data from which to update. result_index: The result index to use which defaults to -1. parameter: The name of the parameter to update. If None is given this will default - to :code:`calibrations.__qubit_freq_parameter__`. + to `drive_freq`. group: The calibrations group to update. Defaults to "default." options: Trailing options. fit_parameter: The name of the fit parameter in the analysis result. This will default to the class variable :code:`__fit_parameter__` if not given. """ - if parameter is None: - parameter = calibrations.__drive_freq_parameter__ - super().update( calibrations=calibrations, exp_data=exp_data, diff --git a/qiskit_experiments/library/calibration/fine_frequency_cal.py b/qiskit_experiments/library/calibration/fine_frequency_cal.py index 049712b7f9..66c9479918 100644 --- a/qiskit_experiments/library/calibration/fine_frequency_cal.py +++ b/qiskit_experiments/library/calibration/fine_frequency_cal.py @@ -37,6 +37,7 @@ def __init__( physical_qubits: Sequence[int], calibrations: Calibrations, backend: Optional[Backend] = None, + cal_parameter_name: Optional[str] = "drive_freq", delay_duration: Optional[int] = None, repetitions: List[int] = None, auto_update: bool = True, @@ -54,11 +55,13 @@ def __init__( fine frequency calibration. calibrations: The calibrations instance with the schedules. backend: Optional, the backend to run the experiment on. + cal_parameter_name: The name of the parameter to update in the calibrations. + This defaults to `drive_freq`. delay_duration: The duration of the delay at :math:`n=1`. If this value is not given then the duration of the gate named ``gate_name`` in the calibrations will be used. - auto_update: Whether or not to automatically update the calibrations. By - default this variable is set to True. + auto_update: Whether to automatically update the calibrations or not. By + default, this variable is set to True. gate_name: This argument is only needed if ``delay_duration`` is None. This should be the name of a valid schedule in the calibrations. """ @@ -72,7 +75,7 @@ def __init__( schedule_name=None, repetitions=repetitions, backend=backend, - cal_parameter_name=calibrations.__drive_freq_parameter__, + cal_parameter_name=cal_parameter_name, auto_update=auto_update, ) diff --git a/qiskit_experiments/library/calibration/frequency_cal.py b/qiskit_experiments/library/calibration/frequency_cal.py index 82e52c4eb2..79c3d7bc47 100644 --- a/qiskit_experiments/library/calibration/frequency_cal.py +++ b/qiskit_experiments/library/calibration/frequency_cal.py @@ -36,6 +36,7 @@ def __init__( physical_qubits: Sequence[int], calibrations: Calibrations, backend: Optional[Backend] = None, + cal_parameter_name: Optional[str] = "drive_freq", delays: Optional[List] = None, osc_freq: float = 2e6, auto_update: bool = True, @@ -46,6 +47,8 @@ def __init__( frequency calibration. calibrations: The calibrations instance with the schedules. backend: Optional, the backend to run the experiment on. + cal_parameter_name: The name of the parameter to update in the calibrations. + This defaults to `drive_freq`. delays: The list of delays that will be scanned in the experiment, in seconds. osc_freq: A frequency shift in Hz that will be applied by means of a virtual Z rotation to increase the frequency of the measured oscillation. @@ -58,7 +61,7 @@ def __init__( backend=backend, delays=delays, osc_freq=osc_freq, - cal_parameter_name=calibrations.__drive_freq_parameter__, + cal_parameter_name=cal_parameter_name, auto_update=auto_update, ) diff --git a/releasenotes/notes/params_without_schedule-20555d98875a626b.yaml b/releasenotes/notes/params_without_schedule-20555d98875a626b.yaml new file mode 100644 index 0000000000..a1aff1f844 --- /dev/null +++ b/releasenotes/notes/params_without_schedule-20555d98875a626b.yaml @@ -0,0 +1,10 @@ +--- +fixes: + - | + The variables `__drive_freq_parameter__` and `__readout_freq_parameter__` + have been removed from `Calibrations`. These variables were given special + treatment which is inconsistent with the framework. To replace them a + mechanism to define and add parameters without a schedule has been added to + the basis gate library. This has the added benefit of making the API of + frequency calibration experiments more consistent with the other calibration + experiments. diff --git a/test/calibration/test_update_library.py b/test/calibration/test_update_library.py index a9404d64fd..0cc8186dfc 100644 --- a/test/calibration/test_update_library.py +++ b/test/calibration/test_update_library.py @@ -21,6 +21,7 @@ from qiskit_experiments.library import QubitSpectroscopy from qiskit_experiments.calibration_management.calibrations import Calibrations from qiskit_experiments.calibration_management.update_library import Frequency +from qiskit_experiments.calibration_management.basis_gate_library import FixedFrequencyTransmon from qiskit_experiments.test.mock_iq_backend import MockIQBackend from qiskit_experiments.test.mock_iq_helpers import MockIQSpectroscopyHelper as SpectroscopyHelper @@ -55,7 +56,7 @@ def test_frequency(self): self.assertEqual(result.quality, "good") # Test the integration with the Calibrations - cals = Calibrations.from_backend(FakeAthensV2()) - self.assertNotEqual(cals.get_parameter_value(cals.__drive_freq_parameter__, qubit), value) + cals = Calibrations.from_backend(FakeAthensV2(), libraries=[FixedFrequencyTransmon()]) + self.assertNotEqual(cals.get_parameter_value("drive_freq", qubit), value) Frequency.update(cals, exp_data) - self.assertEqual(cals.get_parameter_value(cals.__drive_freq_parameter__, qubit), value) + self.assertEqual(cals.get_parameter_value("drive_freq", qubit), value) diff --git a/test/library/calibration/test_fine_frequency.py b/test/library/calibration/test_fine_frequency.py index 09f40471ac..b9f256dd02 100644 --- a/test/library/calibration/test_fine_frequency.py +++ b/test/library/calibration/test_fine_frequency.py @@ -80,14 +80,14 @@ def test_calibration_version(self): fine_freq = FineFrequencyCal([0], self.cals, backend) armonk_freq = BackendData(FakeArmonkV2()).drive_freqs[0] - freq_before = self.cals.get_parameter_value(self.cals.__drive_freq_parameter__, 0) + freq_before = self.cals.get_parameter_value("drive_freq", 0) self.assertAlmostEqual(freq_before, armonk_freq) expdata = fine_freq.run() self.assertExperimentDone(expdata) - freq_after = self.cals.get_parameter_value(self.cals.__drive_freq_parameter__, 0) + freq_after = self.cals.get_parameter_value("drive_freq", 0) # Test equality up to 10kHz on a 100 kHz shift self.assertAlmostEqual(freq_after, armonk_freq + exp_helper.freq_shift, delta=1e4) diff --git a/test/library/calibration/test_ramsey_xy.py b/test/library/calibration/test_ramsey_xy.py index 391700b8b6..a3c9d11252 100644 --- a/test/library/calibration/test_ramsey_xy.py +++ b/test/library/calibration/test_ramsey_xy.py @@ -76,7 +76,7 @@ def test_update_calibrations(self): tol = 1e4 # 10 kHz resolution - freq_name = self.cals.__drive_freq_parameter__ + freq_name = "drive_freq" # Check qubit frequency before running the cal f01 = self.cals.get_parameter_value(freq_name, 0) diff --git a/test/library/calibration/test_rough_frequency.py b/test/library/calibration/test_rough_frequency.py index bc4171f774..3b32ad3d26 100644 --- a/test/library/calibration/test_rough_frequency.py +++ b/test/library/calibration/test_rough_frequency.py @@ -58,7 +58,7 @@ def test_update_calibrations(self): library = FixedFrequencyTransmon() cals = Calibrations.from_backend(self.backend, libraries=[library]) - prev_freq = cals.get_parameter_value(cals.__drive_freq_parameter__, (0,)) + prev_freq = cals.get_parameter_value("drive_freq", (0,)) self.assertEqual(prev_freq, freq01) frequencies = np.linspace(freq01 - 10.0e6, freq01 + 10.0e6, 11) @@ -69,7 +69,7 @@ def test_update_calibrations(self): self.assertExperimentDone(expdata) # Check the updated frequency which should be shifted by 5MHz. - post_freq = cals.get_parameter_value(cals.__drive_freq_parameter__, (0,)) + post_freq = cals.get_parameter_value("drive_freq", (0,)) self.assertTrue(abs(post_freq - freq01 - 5e6) < 1e6) def test_experiment_config(self): From d522382ce2ba15ef13e8c56b338d35cb7cb54be6 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 14 Jun 2023 17:45:50 +0200 Subject: [PATCH 03/13] * Clean-up meas_freq and drive_freq variables. --- .../calibration_management/calibrations.py | 28 ++++++++++++++++--- ...ams_without_schedule-20555d98875a626b.yaml | 3 +- test/calibration/test_calibrations.py | 2 -- test/calibration/test_setup_library.py | 10 +++++++ 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/qiskit_experiments/calibration_management/calibrations.py b/qiskit_experiments/calibration_management/calibrations.py index 4cf8082c82..9dec9c5668 100644 --- a/qiskit_experiments/calibration_management/calibrations.py +++ b/qiskit_experiments/calibration_management/calibrations.py @@ -179,6 +179,24 @@ def __init__( # Push the schedules to the instruction schedule map. self.update_inst_map() + @property + def drive_freq(self): + """Property only here to deprecate `drive_freq`.""" + warnings.warn( + "The variable `drive_freq` has been deprecated and will be removed after " + "Qiskit Experiments 0.6 is released." + ) + return self._parameter_map.get(("drive_freq", (), None), None) + + @property + def meas_freq(self): + """Property only here to deprecate `meas_freq`.""" + warnings.warn( + "The variable `meas_freq` has been deprecated and will be removed after " + "Qiskit Experiments 0.6 is released." + ) + return self._parameter_map.get(("meas_freq", (), None), None) + def _check_consistency(self): """Check that the attributes defined in self are consistent. @@ -252,11 +270,13 @@ def from_backend( ) if add_parameter_defaults: - for qubit, freq in enumerate(backend_data.drive_freqs): - cals.add_parameter_value(freq, cals.drive_freq, qubit, update_inst_map=False) + if ("drive_freq", (), None) in cals._parameter_map: + for qubit, freq in enumerate(backend_data.drive_freqs): + cals.add_parameter_value(freq, "drive_freq", qubit, update_inst_map=False) - for meas, freq in enumerate(backend_data.meas_freqs): - cals.add_parameter_value(freq, cals.meas_freq, meas, update_inst_map=False) + if ("meas_freq", (), None) in cals._parameter_map: + for meas, freq in enumerate(backend_data.meas_freqs): + cals.add_parameter_value(freq, "meas_freq", meas, update_inst_map=False) # Update the instruction schedule map after adding all parameter values. cals.update_inst_map() diff --git a/releasenotes/notes/params_without_schedule-20555d98875a626b.yaml b/releasenotes/notes/params_without_schedule-20555d98875a626b.yaml index a1aff1f844..a43502f6be 100644 --- a/releasenotes/notes/params_without_schedule-20555d98875a626b.yaml +++ b/releasenotes/notes/params_without_schedule-20555d98875a626b.yaml @@ -7,4 +7,5 @@ fixes: mechanism to define and add parameters without a schedule has been added to the basis gate library. This has the added benefit of making the API of frequency calibration experiments more consistent with the other calibration - experiments. + experiments. Users can now add parameters to their library that are not + attached to a schedule in a meaningful way. diff --git a/test/calibration/test_calibrations.py b/test/calibration/test_calibrations.py index af70681421..e2466fa858 100644 --- a/test/calibration/test_calibrations.py +++ b/test/calibration/test_calibrations.py @@ -565,8 +565,6 @@ def test_default_schedules(self): self.sigma, self.beta, self.duration, - self.cals.drive_freq, - self.cals.meas_freq, } self.assertEqual(len(set(self.cals.parameters.keys())), len(expected)) diff --git a/test/calibration/test_setup_library.py b/test/calibration/test_setup_library.py index 04b40a95a2..81e1817f04 100644 --- a/test/calibration/test_setup_library.py +++ b/test/calibration/test_setup_library.py @@ -130,6 +130,16 @@ def test_unlinked_parameters(self): self.assertSetEqual(set(library.default_values()), set(expected)) + def test_add_parameters_without_schedule(self): + """Test that users can add parameters.""" + + library = FixedFrequencyTransmon(params_without_schedule={("new_param", ())}) + + self.assertEqual( + library.parameters_without_schedule, + {("drive_freq", ()), ("meas_freq", ()), ("new_param", ())}, + ) + def test_setup_partial_gates(self): """Check that we do not setup all gates if not required.""" From e1a7380f4042182afa14ff20b83df5a2bb038e09 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 15 Jun 2023 14:37:42 +0200 Subject: [PATCH 04/13] * Add missing cal_parameter_name in init --- qiskit_experiments/library/calibration/rough_frequency.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qiskit_experiments/library/calibration/rough_frequency.py b/qiskit_experiments/library/calibration/rough_frequency.py index a618c53749..da7a15baa9 100644 --- a/qiskit_experiments/library/calibration/rough_frequency.py +++ b/qiskit_experiments/library/calibration/rough_frequency.py @@ -40,6 +40,7 @@ def __init__( backend: Optional[Backend] = None, auto_update: bool = True, absolute: bool = True, + cal_parameter_name: Optional[str] = "drive_freq", ): """See :class:`.QubitSpectroscopy` for detailed documentation. @@ -53,6 +54,8 @@ def __init__( automatically update the frequency in the calibrations. absolute: Boolean to specify if the frequencies are absolute or relative to the qubit frequency in the backend. + cal_parameter_name: The name of the parameter to update in the calibrations. + This defaults to `drive_freq`. Raises: QiskitError: If there are less than three frequency shifts. @@ -66,6 +69,7 @@ def __init__( absolute=absolute, updater=Frequency, auto_update=auto_update, + cal_parameter_name=cal_parameter_name, ) def _attach_calibrations(self, circuit: QuantumCircuit): From d88e6b4cf5801415ae33a1bf3d3bf3a623f4ca76 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 15 Jun 2023 16:32:30 +0200 Subject: [PATCH 05/13] * Simplified parameters without schedule --- .../calibration_management/basis_gate_library.py | 8 ++++---- qiskit_experiments/calibration_management/calibrations.py | 4 ++-- test/calibration/test_setup_library.py | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/qiskit_experiments/calibration_management/basis_gate_library.py b/qiskit_experiments/calibration_management/basis_gate_library.py index cce4e236bd..9500c358da 100644 --- a/qiskit_experiments/calibration_management/basis_gate_library.py +++ b/qiskit_experiments/calibration_management/basis_gate_library.py @@ -36,7 +36,7 @@ class BasisGateLibrary(ABC, Mapping): # Location where default parameter values are stored. These may be updated at construction. __default_values__ = {} - # Parameters that do not belong to a schedule, set of (name: str, qubits: tuple) + # Parameters that do not belong to a schedule, a set of names __parameters_without_schedule__ = set() def __init__( @@ -224,8 +224,8 @@ class FixedFrequencyTransmon(BasisGateLibrary): - angle: The phase of the complex amplitude of the pulses. Parameters without schedule: - - meas_freq: frequency of the measurement drives, applies to all qubits. - - drive_freq: frequency of the qubit drives, applies to all qubits. + - meas_freq: frequency of the measurement drives. + - drive_freq: frequency of the qubit drives. Note that the β and amp parameters may be linked between the x and y as well as between the sx and sy pulses. All pulses share the same duration and σ parameters. @@ -233,7 +233,7 @@ class FixedFrequencyTransmon(BasisGateLibrary): __default_values__ = {"duration": 160, "amp": 0.5, "β": 0.0, "angle": 0.0} - __parameters_without_schedule__ = [("meas_freq", ()), ("drive_freq", ())] + __parameters_without_schedule__ = {"meas_freq", "drive_freq"} def __init__( self, diff --git a/qiskit_experiments/calibration_management/calibrations.py b/qiskit_experiments/calibration_management/calibrations.py index 9dec9c5668..52606a5808 100644 --- a/qiskit_experiments/calibration_management/calibrations.py +++ b/qiskit_experiments/calibration_management/calibrations.py @@ -158,8 +158,8 @@ def __init__( self.add_parameter_value(*param_conf, update_inst_map=False) # Add the parameters that do not belong to a schedule. - for param_name, qubits in lib.parameters_without_schedule: - self._register_parameter(Parameter(param_name), qubits) + for param_name in lib.parameters_without_schedule: + self._register_parameter(Parameter(param_name), tuple()) # This internal parameter is False so that if a schedule is added after the # init it will be set to True and serialization will raise an error. diff --git a/test/calibration/test_setup_library.py b/test/calibration/test_setup_library.py index 81e1817f04..85b733c011 100644 --- a/test/calibration/test_setup_library.py +++ b/test/calibration/test_setup_library.py @@ -92,7 +92,7 @@ def test_standard_single_qubit_gates(self): self.assertListEqual(library.basis_gates, ["x", "y", "sx", "sy"]) def test_unlinked_parameters(self): - """Test the we get schedules with unlinked parameters.""" + """Test that we get schedules with unlinked parameters.""" library = FixedFrequencyTransmon(link_parameters=False) @@ -133,11 +133,11 @@ def test_unlinked_parameters(self): def test_add_parameters_without_schedule(self): """Test that users can add parameters.""" - library = FixedFrequencyTransmon(params_without_schedule={("new_param", ())}) + library = FixedFrequencyTransmon(params_without_schedule={"new_param"}) self.assertEqual( library.parameters_without_schedule, - {("drive_freq", ()), ("meas_freq", ()), ("new_param", ())}, + {"drive_freq", "meas_freq", "new_param"}, ) def test_setup_partial_gates(self): From 10567f28ea9d777ed728506f5a3253190ac1a543 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 15 Jun 2023 16:37:00 +0200 Subject: [PATCH 06/13] * lint --- qiskit_experiments/calibration_management/calibrations.py | 3 --- test/calibration/test_setup_library.py | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/qiskit_experiments/calibration_management/calibrations.py b/qiskit_experiments/calibration_management/calibrations.py index 52606a5808..271bbb2410 100644 --- a/qiskit_experiments/calibration_management/calibrations.py +++ b/qiskit_experiments/calibration_management/calibrations.py @@ -147,7 +147,6 @@ def __init__( libraries = [libraries] for lib in libraries: - # Add the basis gates for gate in lib.basis_gates: self.add_schedule(lib[gate], num_qubits=lib.num_qubits(gate)) @@ -461,7 +460,6 @@ def _get_full_qubits_of_schedule( """ for key, circuit_inst_num_qubits in self._schedules_qubits.items(): if key.schedule == schedule_name: - if len(partial_qubits) == circuit_inst_num_qubits: return [partial_qubits] @@ -900,7 +898,6 @@ def _get_channel_index(self, qubits: Tuple[int, ...], chan: PulseChannel) -> int # Control channels name example ch1.0$1 if isinstance(chan, ControlChannel): - channel_index_parts = chan.index.name[2:].split("$") qubit_channels = channel_index_parts[0] diff --git a/test/calibration/test_setup_library.py b/test/calibration/test_setup_library.py index 85b733c011..30aff7cc70 100644 --- a/test/calibration/test_setup_library.py +++ b/test/calibration/test_setup_library.py @@ -137,7 +137,7 @@ def test_add_parameters_without_schedule(self): self.assertEqual( library.parameters_without_schedule, - {"drive_freq", "meas_freq", "new_param"}, + {"drive_freq", "meas_freq", "new_param"}, ) def test_setup_partial_gates(self): From 015ced466a86acd042586b760b779db8fc3899f3 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 15 Jun 2023 18:59:53 +0200 Subject: [PATCH 07/13] * Align tests to changes --- test/calibration/test_calibrations.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/calibration/test_calibrations.py b/test/calibration/test_calibrations.py index e2466fa858..0d8f4d78a7 100644 --- a/test/calibration/test_calibrations.py +++ b/test/calibration/test_calibrations.py @@ -217,12 +217,12 @@ def test_remove_schedule(self): self.cals.add_schedule(sched, num_qubits=1) self.assertEqual(len(self.cals.schedules()), 4) - self.assertEqual(len(self.cals.parameters), 9) + self.assertEqual(len(self.cals.parameters), 7) self.cals.remove_schedule(sched) self.assertEqual(len(self.cals.schedules()), 3) - self.assertEqual(len(self.cals.parameters), 8) + self.assertEqual(len(self.cals.parameters), 6) for param in [self.sigma, self.amp_xp, self.amp_x90p, self.amp_y90p, self.beta]: self.assertTrue(param in self.cals.parameters) @@ -323,7 +323,7 @@ def test_from_backend(self): """Test that when generating calibrations from backend the data is passed correctly""" backend = FakeBelemV2() - cals = Calibrations.from_backend(backend) + cals = Calibrations.from_backend(backend, libraries=[FixedFrequencyTransmon()]) with self.assertWarns(DeprecationWarning): config_args = cals.config()["kwargs"] control_channel_map_size = len(config_args["control_channel_map"].chan_map) From ea6a454639200e2c3e9ded2c4f66bc0b3aee8e9d Mon Sep 17 00:00:00 2001 From: "Daniel J. Egger" <38065505+eggerdj@users.noreply.github.com> Date: Mon, 19 Jun 2023 13:45:18 +0200 Subject: [PATCH 08/13] Update releasenotes/notes/params_without_schedule-20555d98875a626b.yaml Co-authored-by: Naoki Kanazawa --- .../notes/params_without_schedule-20555d98875a626b.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/notes/params_without_schedule-20555d98875a626b.yaml b/releasenotes/notes/params_without_schedule-20555d98875a626b.yaml index a43502f6be..3b60d812c4 100644 --- a/releasenotes/notes/params_without_schedule-20555d98875a626b.yaml +++ b/releasenotes/notes/params_without_schedule-20555d98875a626b.yaml @@ -7,5 +7,5 @@ fixes: mechanism to define and add parameters without a schedule has been added to the basis gate library. This has the added benefit of making the API of frequency calibration experiments more consistent with the other calibration - experiments. Users can now add parameters to their library that are not + experiments. Calibration developers can now add parameters to their library that are not attached to a schedule in a meaningful way. From 7d8a6edc8ab87ee912f47e9406eac9292658680c Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Mon, 19 Jun 2023 13:55:16 +0200 Subject: [PATCH 09/13] * Simplified interface. --- .../basis_gate_library.py | 23 ++----------------- .../calibration_management/calibrations.py | 2 +- ...ams_without_schedule-20555d98875a626b.yaml | 2 +- test/calibration/test_setup_library.py | 10 -------- 4 files changed, 4 insertions(+), 33 deletions(-) diff --git a/qiskit_experiments/calibration_management/basis_gate_library.py b/qiskit_experiments/calibration_management/basis_gate_library.py index 9500c358da..c5fd5a6781 100644 --- a/qiskit_experiments/calibration_management/basis_gate_library.py +++ b/qiskit_experiments/calibration_management/basis_gate_library.py @@ -43,7 +43,6 @@ def __init__( self, basis_gates: Optional[List[str]] = None, default_values: Optional[Dict] = None, - params_without_schedule: Optional[Set] = None, **extra_kwargs, ): """Setup the library. @@ -51,8 +50,6 @@ def __init__( Args: basis_gates: The basis gates to generate. default_values: A dictionary to override library default parameter values. - params_without_schedule: A set of parameters without a schedule to override the - library defaults. extra_kwargs: Extra key-word arguments of the subclasses that are saved to be able to reconstruct the library using the :meth:`__init__` method. @@ -62,13 +59,9 @@ def __init__( # Update the default values. self._extra_kwargs = extra_kwargs self._default_values = self.__default_values__.copy() - self._params_without_schedule = self.__parameters_without_schedule__.copy() if default_values is not None: self._default_values.update(default_values) - if params_without_schedule is not None: - self._params_without_schedule.update(params_without_schedule) - if basis_gates is None: basis_gates = list(self.__supported_gates__) @@ -136,11 +129,6 @@ def default_values(self) -> List[DefaultCalValue]: on the tuples. """ - @property - def parameters_without_schedule(self) -> Set: - """Return the parameters that do not have a schedule.""" - return self._params_without_schedule - @abstractmethod def _build_schedules(self, basis_gates: Set[str]) -> Dict[str, ScheduleBlock]: """Build the schedules stored in the library. @@ -161,11 +149,7 @@ def _build_schedules(self, basis_gates: Set[str]) -> Dict[str, ScheduleBlock]: def config(self) -> Dict[str, Any]: """Return the settings used to initialize the library.""" - kwargs = { - "basis_gates": self.basis_gates, - "default_values": self._default_values, - "params_without_schedule": self._params_without_schedule, - } + kwargs = {"basis_gates": self.basis_gates, "default_values": self._default_values} kwargs.update(self._extra_kwargs) return { @@ -239,7 +223,6 @@ def __init__( self, basis_gates: Optional[List[str]] = None, default_values: Optional[Dict] = None, - params_without_schedule: Optional[Set] = None, link_parameters: bool = True, ): """Setup the schedules. @@ -249,8 +232,6 @@ def __init__( 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. - params_without_schedule: A set of parameters without a schedule to override the - library defaults. link_parameters: If set to ``True``, then the amplitude and DRAG parameters of the :math:`X` and :math:`Y` gates will be linked as well as those of the :math:`SX` and :math:`SY` gates. @@ -259,7 +240,7 @@ def __init__( extra_kwargs = {"link_parameters": link_parameters} - super().__init__(basis_gates, default_values, params_without_schedule, **extra_kwargs) + super().__init__(basis_gates, default_values, **extra_kwargs) @property def __supported_gates__(self) -> Dict[str, int]: diff --git a/qiskit_experiments/calibration_management/calibrations.py b/qiskit_experiments/calibration_management/calibrations.py index 271bbb2410..2ff7b42d5b 100644 --- a/qiskit_experiments/calibration_management/calibrations.py +++ b/qiskit_experiments/calibration_management/calibrations.py @@ -157,7 +157,7 @@ def __init__( self.add_parameter_value(*param_conf, update_inst_map=False) # Add the parameters that do not belong to a schedule. - for param_name in lib.parameters_without_schedule: + for param_name in lib.__parameters_without_schedule__: self._register_parameter(Parameter(param_name), tuple()) # This internal parameter is False so that if a schedule is added after the diff --git a/releasenotes/notes/params_without_schedule-20555d98875a626b.yaml b/releasenotes/notes/params_without_schedule-20555d98875a626b.yaml index 3b60d812c4..5c1c019ac8 100644 --- a/releasenotes/notes/params_without_schedule-20555d98875a626b.yaml +++ b/releasenotes/notes/params_without_schedule-20555d98875a626b.yaml @@ -1,5 +1,5 @@ --- -fixes: +upgrade: - | The variables `__drive_freq_parameter__` and `__readout_freq_parameter__` have been removed from `Calibrations`. These variables were given special diff --git a/test/calibration/test_setup_library.py b/test/calibration/test_setup_library.py index 30aff7cc70..1c1307c97a 100644 --- a/test/calibration/test_setup_library.py +++ b/test/calibration/test_setup_library.py @@ -130,16 +130,6 @@ def test_unlinked_parameters(self): self.assertSetEqual(set(library.default_values()), set(expected)) - def test_add_parameters_without_schedule(self): - """Test that users can add parameters.""" - - library = FixedFrequencyTransmon(params_without_schedule={"new_param"}) - - self.assertEqual( - library.parameters_without_schedule, - {"drive_freq", "meas_freq", "new_param"}, - ) - def test_setup_partial_gates(self): """Check that we do not setup all gates if not required.""" From 48d693a55f80fb4a7b6a916809c9b5408fc72c33 Mon Sep 17 00:00:00 2001 From: "Daniel J. Egger" <38065505+eggerdj@users.noreply.github.com> Date: Tue, 20 Jun 2023 08:50:00 +0200 Subject: [PATCH 10/13] Update qiskit_experiments/calibration_management/calibrations.py Co-authored-by: Naoki Kanazawa --- .../calibration_management/calibrations.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/qiskit_experiments/calibration_management/calibrations.py b/qiskit_experiments/calibration_management/calibrations.py index 2ff7b42d5b..276be52c0f 100644 --- a/qiskit_experiments/calibration_management/calibrations.py +++ b/qiskit_experiments/calibration_management/calibrations.py @@ -179,12 +179,16 @@ def __init__( self.update_inst_map() @property + @deprecate_func( + is_property=True, + since="0.6", + package_name="qiskit-experiments", + additional_msg=( + "This attribute is moved to FixedFrequencyTransmon basis gate library." + ), + ) def drive_freq(self): - """Property only here to deprecate `drive_freq`.""" - warnings.warn( - "The variable `drive_freq` has been deprecated and will be removed after " - "Qiskit Experiments 0.6 is released." - ) + """Parameter object for qubit drive frequency.""" return self._parameter_map.get(("drive_freq", (), None), None) @property From 813ed847ce23b21dff183621fcb8e5061af2f1b9 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Tue, 20 Jun 2023 08:53:19 +0200 Subject: [PATCH 11/13] * Deprecation of meas_freq. --- .../calibration_management/calibrations.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/qiskit_experiments/calibration_management/calibrations.py b/qiskit_experiments/calibration_management/calibrations.py index 276be52c0f..4f526304f7 100644 --- a/qiskit_experiments/calibration_management/calibrations.py +++ b/qiskit_experiments/calibration_management/calibrations.py @@ -184,7 +184,7 @@ def __init__( since="0.6", package_name="qiskit-experiments", additional_msg=( - "This attribute is moved to FixedFrequencyTransmon basis gate library." + "The drive_freq is moved to FixedFrequencyTransmon basis gate library." ), ) def drive_freq(self): @@ -192,12 +192,16 @@ def drive_freq(self): return self._parameter_map.get(("drive_freq", (), None), None) @property + @deprecate_func( + is_property=True, + since="0.6", + package_name="qiskit-experiments", + additional_msg=( + "The meas_freq is moved to FixedFrequencyTransmon basis gate library." + ), + ) def meas_freq(self): """Property only here to deprecate `meas_freq`.""" - warnings.warn( - "The variable `meas_freq` has been deprecated and will be removed after " - "Qiskit Experiments 0.6 is released." - ) return self._parameter_map.get(("meas_freq", (), None), None) def _check_consistency(self): From 95ac7cd7f54cf64c23cc7ab466924bc4c8ecbd62 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Tue, 20 Jun 2023 09:02:00 +0200 Subject: [PATCH 12/13] * black --- qiskit_experiments/calibration_management/calibrations.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/qiskit_experiments/calibration_management/calibrations.py b/qiskit_experiments/calibration_management/calibrations.py index 4f526304f7..dfeddd4b62 100644 --- a/qiskit_experiments/calibration_management/calibrations.py +++ b/qiskit_experiments/calibration_management/calibrations.py @@ -183,9 +183,7 @@ def __init__( is_property=True, since="0.6", package_name="qiskit-experiments", - additional_msg=( - "The drive_freq is moved to FixedFrequencyTransmon basis gate library." - ), + additional_msg="The drive_freq is moved to FixedFrequencyTransmon basis gate library.", ) def drive_freq(self): """Parameter object for qubit drive frequency.""" @@ -196,9 +194,7 @@ def drive_freq(self): is_property=True, since="0.6", package_name="qiskit-experiments", - additional_msg=( - "The meas_freq is moved to FixedFrequencyTransmon basis gate library." - ), + additional_msg="The meas_freq is moved to FixedFrequencyTransmon basis gate library.", ) def meas_freq(self): """Property only here to deprecate `meas_freq`.""" From 395c1f83b66e299861b0e6e5905de38693885fa5 Mon Sep 17 00:00:00 2001 From: Naoki Kanazawa Date: Tue, 20 Jun 2023 18:50:52 +0900 Subject: [PATCH 13/13] Update qiskit_experiments/calibration_management/calibrations.py --- qiskit_experiments/calibration_management/calibrations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_experiments/calibration_management/calibrations.py b/qiskit_experiments/calibration_management/calibrations.py index dfeddd4b62..0ee98c707b 100644 --- a/qiskit_experiments/calibration_management/calibrations.py +++ b/qiskit_experiments/calibration_management/calibrations.py @@ -197,7 +197,7 @@ def drive_freq(self): additional_msg="The meas_freq is moved to FixedFrequencyTransmon basis gate library.", ) def meas_freq(self): - """Property only here to deprecate `meas_freq`.""" + """Parameter object for qubit measure frequency.""" return self._parameter_map.get(("meas_freq", (), None), None) def _check_consistency(self):