-
Notifications
You must be signed in to change notification settings - Fork 131
Calibrations Definition #26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
768599e
75214af
40bf8bd
2639b0d
bd0529a
c992ccc
9092094
8c4c21e
fbc2dc7
47ab364
a9bc280
f952975
ec956ae
ae24a11
f96ab98
7dd1bed
edc402d
ffe4d87
14cc378
d70acce
b919f67
aa9b1ca
b58d3cd
b08c481
a73d9e3
1d4923f
f74534e
72ce1c0
2e22059
8053c5c
75a5cfb
0862722
d35b0ed
dfc2788
70f4616
50af0e3
800d1d0
2af8cb0
e75df84
4461153
f2d680e
6019b78
0a01a7a
509aa44
9085018
d014b17
490ad63
fa27ef5
b314294
0f60d1f
9de4086
7bac467
619fea3
373cb38
38deb52
602f001
18ff956
fe07b6e
09f1cc3
9344834
d8dff58
8f8a569
174c4ec
1546e48
f4bf40d
f9e42ed
05d61eb
9194031
afaba9b
5265030
f817f5c
290e2ef
e56953f
2646227
3dfcb3d
91aa4cd
6833d1f
0c1ede5
5426c68
54db71c
c8f4fb5
37a2eaf
113150a
3006695
5d40e8c
91c239d
c4b7260
6ebdc48
b4a0f74
d06bb4f
7c40368
1b6bf14
e9d38ab
9e4ea3b
0ab664a
1803285
f7ee6b9
32892d5
3d2a052
8148d0e
13ed686
83f82a7
b83adf3
1c6d1e3
62d1728
d19dfe8
fa5edc1
5033b48
f067c7d
23402a7
4160914
84b49c9
4e9f266
d99c429
a743a9a
b6d9dec
bc11c38
62b42b6
f8132bb
0d267c2
f6f4b8a
f94b34e
a7f8a36
980f2c2
98530d4
d85f5f6
3900b82
0c519b5
987bc81
129db5b
47c0a44
80bc820
f7747c6
aaf1479
0a49a3c
320b377
f9c0490
3c56906
07d5ee1
2009af4
e9b44e7
75fadc7
54c3e81
db33772
4f04ae4
61a0ba1
fd09136
1b14366
27af03c
ced5333
7031ec1
6ada9ce
bb4fb81
bab9b9a
ba2bc1b
3166ae2
1dd07e8
da84b8a
7209231
8681523
860196a
6033672
3e844bc
dee2f58
0f3907c
dae34e1
145c190
90b0dd8
c597e52
a15b6a9
fd7db9d
abbe216
03f2d7b
4fe70a1
dd773a1
7c43741
83aeb1b
05b9582
e0c6b7f
31a11e7
c666777
33f4811
c492b35
048684d
7316a9d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| # 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. | ||
|
|
||
| r""" | ||
| Qiskit Experiments Calibration Root. | ||
|
|
||
| .. warning:: | ||
| The calibrations interface is still in active development. It may have | ||
| breaking API changes without deprecation warnings in future releases until | ||
| otherwise indicated. | ||
|
|
||
| Calibrations are managed by the Calibrations class. This class stores schedules which are | ||
| intended to be fully parameterized, including the index of the channels. This class: | ||
| - supports having different schedules share parameters | ||
| - allows default schedules for qubits that can be overridden for specific qubits. | ||
|
|
||
| The following code illustrates how a user can create a parameterized schedule, add | ||
| values to the parameters and query a schedule. | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| dur = Parameter("dur") | ||
| amp = Parameter("amp") | ||
| sigma = Parameter("σ") | ||
|
|
||
| with pulse.build(name="xp") as xp: | ||
| pulse.play(Gaussian(dur, amp, sigma), DriveChannel(Parameter("ch0"))) | ||
|
|
||
| cals = Calibrations() | ||
| cals.add_schedule(xp) | ||
|
|
||
| # add duration and sigma parameter values for all qubits. | ||
| cals.add_parameter_value(160, "dur", schedule="xp") | ||
| cals.add_parameter_value(35.5, "σ", schedule="xp") | ||
|
|
||
| # Add an amplitude for qubit 3. | ||
| cals.add_parameter_value(0.2+0.05j, "amp", (3, ), "xp") | ||
|
|
||
| # Retrieve an xp pulse with all parameters assigned | ||
| cals.get_schedule("xp", (3, )) | ||
|
|
||
| # Retrieve an xp pulse with unassigned amplitude | ||
| cals.get_schedule("xp", (3, ), free_params=["amp"]) | ||
|
|
||
| The Calibrations make a couple of assumptions which are discussed below. | ||
|
|
||
| Parametric channel naming convention | ||
| ========================= | ||
|
|
||
| Parametrized channel indices must be named according to a predefined pattern to properly | ||
| identify the channels and control channels when assigning values to the parametric | ||
| channel indices. A channel must have a name that starts with `ch` followed by an integer. | ||
| For control channels this integer can be followed by a sequence `.integer`. | ||
| Optionally, the name can end with `$integer` to specify the index of a control channel | ||
eggerdj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| for the case when a set of qubits share multiple control channels. For example, | ||
| valid channel names include "ch0", "ch1", "ch0.1", "ch0$", "ch2$3", and "ch1.0.3$2". | ||
| The "." delimiter is used to specify the different qubits when looking for control | ||
| channels. The optional $ delimiter is used to specify which control channel to use | ||
| if several control channels work together on the same qubits. For example, if the | ||
| control channel configuration is {(3,2): [ControlChannel(3), ControlChannel(12)]} | ||
| then given qubits (2, 3) the name "ch1.0$1" will resolve to ControlChannel(12) while | ||
| "ch1.0$0" will resolve to ControlChannel(3). A channel can only have one parameter. | ||
|
|
||
| Parameter naming restriction | ||
| =================== | ||
|
|
||
| Each parameter must have a unique name within each schedule. For example, it is | ||
| acceptable to have a parameter named 'amp' in the schedule 'xp' and a different | ||
| parameter instance named 'amp' in the schedule named 'xm'. It is not acceptable | ||
| to have two parameters named 'amp' in the same schedule. The naming restriction | ||
| only applies to parameters used in the immediate scope of the schedule. Schedules | ||
| called by Call instructions have their own scope for Parameter names. | ||
|
|
||
| The code block below illustrates the creation of a template schedule for a echoed cross- | ||
| resonance gate. | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| amp_cr = Parameter("amp") | ||
| amp = Parameter("amp") | ||
| d0 = DriveChannel(Parameter("ch0")) | ||
| c1 = ControlChannel(Parameter("ch0.1")) | ||
| sigma = Parameter("σ") | ||
| width = Parameter("w") | ||
| dur_xp = Parameter("duration") | ||
| dur_cr = Parameter("duration") | ||
|
|
||
| with pulse.build(name="xp") as xp: | ||
| pulse.play(Gaussian(dur_xp, amp, sigma), d0) | ||
|
|
||
| with pulse.build(name="cr") as cr: | ||
| with pulse.align_sequential(): | ||
| pulse.play(GaussianSquare(dur_cr, amp_cr, sigma, width), c1) | ||
| pulse.call(xp) | ||
| pulse.play(GaussianSquare(dur_cr, -amp_cr, sigma, width), c1) | ||
| pulse.call(xp) | ||
|
|
||
| cals = Calibrations() | ||
| cals.add_schedule(xp) | ||
| cals.add_schedule(cr) | ||
|
|
||
| Note that a registered template schedule can be retrieve by doing | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| xp = cals.get_template("xp") | ||
|
|
||
| which would return the default xp schedule block template for all qubits. | ||
| """ | ||
eggerdj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| from .calibrations import Calibrations | ||
| from .backend_calibrations import BackendCalibrations | ||
| from .exceptions import CalibrationError | ||
| from .parameter_value import ParameterValue | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| # 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. | ||
|
|
||
| """Store and manage the results of calibration experiments in the context of a backend.""" | ||
|
|
||
| from datetime import datetime | ||
| from enum import Enum | ||
| from typing import List | ||
| import copy | ||
|
|
||
| from qiskit.providers.backend import BackendV1 as Backend | ||
| from qiskit.circuit import Parameter | ||
| from qiskit_experiments.calibration.calibrations import Calibrations, ParameterKey | ||
| from qiskit_experiments.calibration.exceptions import CalibrationError | ||
|
|
||
|
|
||
| class FrequencyElement(Enum): | ||
| """An extendable enum for components that have a frequency.""" | ||
|
|
||
| QUBIT = "Qubit" | ||
| READOUT = "Readout" | ||
|
|
||
|
|
||
| class BackendCalibrations(Calibrations): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As we discussed offline, this induces tight coupling to execution chain because this class exists for generating a backend dependent runtime option though the I think the right direction should be exporting configured backend with |
||
| """ | ||
| A Calibrations class to enable a seamless interplay with backend objects. | ||
| This class enables users to export their calibrations into a backend object. | ||
| Additionally, it creates frequency parameters for qubits and readout resonators. | ||
| The parameters are named `qubit_lo_freq` and `meas_lo_freq` to be consistent | ||
| with the naming in backend.defaults(). These two parameters are not attached to | ||
| any schedule. | ||
| """ | ||
|
|
||
| def __init__(self, backend: Backend): | ||
| """Setup an instance to manage the calibrations of a backend.""" | ||
| super().__init__(backend.configuration().control_channels) | ||
|
|
||
| # Use the same naming convention as in backend.defaults() | ||
| self.qubit_freq = Parameter("qubit_lo_freq") | ||
| self.meas_freq = Parameter("meas_lo_freq") | ||
| self._register_parameter(self.qubit_freq, ()) | ||
| self._register_parameter(self.meas_freq, ()) | ||
|
|
||
| self._qubits = set(range(backend.configuration().n_qubits)) | ||
| self._backend = backend | ||
|
|
||
| def _get_frequencies( | ||
| self, | ||
| element: FrequencyElement, | ||
| group: str = "default", | ||
| cutoff_date: datetime = None, | ||
| ) -> List[float]: | ||
| """Internal helper method.""" | ||
|
|
||
| if element == FrequencyElement.READOUT: | ||
| param = self.meas_freq.name | ||
| elif element == FrequencyElement.QUBIT: | ||
| param = self.qubit_freq.name | ||
| else: | ||
| raise CalibrationError(f"Frequency element {element} is not supported.") | ||
|
|
||
| freqs = [] | ||
| for qubit in self._qubits: | ||
| if ParameterKey(None, param, (qubit,)) in self._params: | ||
| freq = self.get_parameter_value(param, (qubit,), None, True, group, cutoff_date) | ||
| else: | ||
| if element == FrequencyElement.READOUT: | ||
| freq = self._backend.defaults().meas_freq_est[qubit] | ||
| elif element == FrequencyElement.QUBIT: | ||
| freq = self._backend.defaults().qubit_freq_est[qubit] | ||
| else: | ||
| raise CalibrationError(f"Frequency element {element} is not supported.") | ||
|
|
||
| freqs.append(freq) | ||
|
|
||
| return freqs | ||
|
|
||
| def get_qubit_frequencies( | ||
eggerdj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| self, | ||
| group: str = "default", | ||
| cutoff_date: datetime = None, | ||
| ) -> List[float]: | ||
| """ | ||
| Get the most recent qubit frequencies. They can be passed to the run-time | ||
| options of :class:`BaseExperiment`. If no calibrated frequency value of a | ||
| qubit is found then the default value from the backend defaults is used. | ||
| Only valid parameter values are returned. | ||
|
|
||
| Args: | ||
| group: The calibration group from which to draw the | ||
| parameters. If not specified, this defaults to the 'default' group. | ||
| cutoff_date: Retrieve the most recent parameter up until the cutoff date. Parameters | ||
| generated after the cutoff date will be ignored. If the cutoff_date is None then | ||
| all parameters are considered. This allows users to discard more recent values | ||
| that may be erroneous. | ||
|
|
||
| Returns: | ||
| A List of qubit frequencies for all qubits of the backend. | ||
| """ | ||
| return self._get_frequencies(FrequencyElement.QUBIT, group, cutoff_date) | ||
|
|
||
| def get_meas_frequencies( | ||
| self, | ||
| group: str = "default", | ||
| cutoff_date: datetime = None, | ||
| ) -> List[float]: | ||
| """ | ||
| Get the most recent measurement frequencies. They can be passed to the run-time | ||
| options of :class:`BaseExperiment`. If no calibrated frequency value of a | ||
| measurement is found then the default value from the backend defaults is used. | ||
| Only valid parameter values are returned. | ||
|
|
||
| Args: | ||
| group: The calibration group from which to draw the | ||
| parameters. If not specified, this defaults to the 'default' group. | ||
| cutoff_date: Retrieve the most recent parameter up until the cutoff date. Parameters | ||
| generated after the cutoff date will be ignored. If the cutoff_date is None then | ||
| all parameters are considered. This allows users to discard more recent values | ||
| that may be erroneous. | ||
|
|
||
| Returns: | ||
| A List of measurement frequencies for all qubits of the backend. | ||
| """ | ||
| return self._get_frequencies(FrequencyElement.READOUT, group, cutoff_date) | ||
|
|
||
| def export_backend(self) -> Backend: | ||
| """ | ||
| Exports the calibrations to a backend object that can be used. | ||
|
|
||
| Returns: | ||
| calibrated backend: A backend with the calibrations in it. | ||
| """ | ||
| backend = copy.deepcopy(self._backend) | ||
|
|
||
| backend.defaults().qubit_freq_est = self.get_qubit_frequencies() | ||
| backend.defaults().meas_freq_est = self.get_meas_frequencies() | ||
|
|
||
| # TODO: build the instruction schedule map using the stored calibrations | ||
|
|
||
| return backend | ||
Uh oh!
There was an error while loading. Please reload this page.