-
Notifications
You must be signed in to change notification settings - Fork 134
Spectroscopy and calibrations integration V3 #88
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
19f8899
a19c821
71553c7
4904e42
9c6ef7f
299c959
43a00cf
383fc08
61c0920
c1e29db
4f2f40f
c7a4b71
f276c7b
991f8ea
9a76125
f6f2d83
3fea5a4
722ba59
4e31847
ae4a7c9
f032b5d
0d8fc79
ff9d254
9f72426
8cd5c89
e0e3bfd
db00f3b
a9a38b9
c3f5907
468af33
d9d47df
864f3af
744bb17
b818297
8e65eef
23b3a04
d4c792d
fac68ec
c2b4109
90b5617
762ded0
ce9b9fc
cac9706
e1e5a72
4bb5861
1262246
206baa1
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,23 @@ | ||
| # 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. | ||
|
|
||
| """Types used by the calibration module.""" | ||
|
|
||
| from typing import Union | ||
| from collections import namedtuple | ||
|
|
||
| from qiskit.circuit import ParameterExpression | ||
|
|
||
|
|
||
| ParameterKey = namedtuple("ParameterKey", ["parameter", "qubits", "schedule"]) | ||
| ScheduleKey = namedtuple("ScheduleKey", ["schedule", "qubits"]) | ||
| ParameterValueType = Union[ParameterExpression, float, int, complex] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,183 @@ | ||
| # 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 library of experiment calibrations.""" | ||
|
|
||
| from abc import ABC, abstractmethod | ||
| from datetime import datetime | ||
| from typing import List, Tuple, Union | ||
| import numpy as np | ||
|
|
||
| from qiskit.circuit import Parameter | ||
| from qiskit.pulse import ScheduleBlock | ||
|
|
||
| from qiskit_experiments.experiment_data import ExperimentData | ||
| from qiskit_experiments.calibration.backend_calibrations import BackendCalibrations | ||
| from qiskit_experiments.calibration.calibrations import Calibrations | ||
| from qiskit_experiments.calibration.parameter_value import ParameterValue | ||
| from qiskit_experiments.calibration.exceptions import CalibrationError | ||
| from qiskit_experiments.calibration.calibration_key_types import ParameterValueType | ||
|
|
||
|
|
||
| class BaseUpdater(ABC): | ||
| """A base class to update calibrations.""" | ||
|
|
||
| def __init__(self): | ||
| """Updaters are not meant to be instantiated.""" | ||
| raise CalibrationError( | ||
| "Calibration updaters are not meant to be instantiated. The intended usage" | ||
| "is Updater.update(calibrations, exp_data, ...)." | ||
| ) | ||
|
|
||
| @staticmethod | ||
| def _time_stamp(exp_data: ExperimentData) -> datetime: | ||
| """Helper method to extract the datetime.""" | ||
| all_times = exp_data.completion_times.values() | ||
| if all_times: | ||
| return max(all_times) | ||
|
|
||
| return datetime.now() | ||
|
|
||
| @classmethod | ||
| def _add_parameter_value( | ||
|
eggerdj marked this conversation as resolved.
|
||
| cls, | ||
| cal: Calibrations, | ||
| exp_data: ExperimentData, | ||
| value: ParameterValueType, | ||
| param: Union[Parameter, str], | ||
| schedule: Union[ScheduleBlock, str] = None, | ||
| group: str = "default", | ||
| ): | ||
| """Update the calibrations with the given value. | ||
|
|
||
| Args: | ||
| cal: The Calibrations instance to update. | ||
| exp_data: The ExperimentData instance that contains the result and the experiment data. | ||
| value: The value extracted by the subclasses in the :meth:`update` method. | ||
| param: The name of the parameter, or the parameter instance, which will receive an | ||
| updated value. | ||
| schedule: The ScheduleBlock instance or the name of the instance to which the parameter | ||
| is attached. | ||
| group: The calibrations group to update. | ||
| """ | ||
|
|
||
| qubits = exp_data.data(0)["metadata"]["qubits"] | ||
|
|
||
| param_value = ParameterValue( | ||
| value=value, | ||
| date_time=cls._time_stamp(exp_data), | ||
| group=group, | ||
| exp_id=exp_data.experiment_id, | ||
| ) | ||
|
|
||
| cal.add_parameter_value(param_value, param, qubits, schedule) | ||
|
|
||
| @classmethod | ||
| @abstractmethod | ||
| def update(cls, calibrations: BackendCalibrations, exp_data: ExperimentData, **options): | ||
| """Update the calibrations based on the data. | ||
|
|
||
| Child update classes must implement this function. This function defines how the data | ||
| is extracted from an experiment and then used to update the values of one or more | ||
| parameters in the calibrations. | ||
| """ | ||
|
|
||
|
|
||
| class Frequency(BaseUpdater): | ||
| """Update frequencies.""" | ||
|
|
||
| # pylint: disable=arguments-differ | ||
| @classmethod | ||
| def update( | ||
| cls, | ||
| calibrations: BackendCalibrations, | ||
| exp_data: ExperimentData, | ||
| result_index: int = -1, | ||
| group: str = "default", | ||
| parameter: str = BackendCalibrations.__qubit_freq_parameter__, | ||
| ): | ||
| """Update a qubit frequency from, e.g., QubitSpectroscopy. | ||
|
|
||
| Args: | ||
| calibrations: The calibrations to update. | ||
| exp_data: The experiment data from which to update. | ||
| result_index: The result index to use, defaults to -1. | ||
| group: The calibrations group to update. Defaults to "default." | ||
| parameter: The name of the parameter to update. If it is not specified | ||
| this will default to the qubit frequency. | ||
|
|
||
| Raises: | ||
| CalibrationError: If the analysis result does not contain a frequency variable. | ||
| """ | ||
|
|
||
| from qiskit_experiments.characterization.qubit_spectroscopy import SpectroscopyAnalysis | ||
|
|
||
| result = exp_data.analysis_result(result_index) | ||
|
|
||
| if "freq" not in result["popt_keys"]: | ||
| raise CalibrationError( | ||
| f"{cls.__name__} updates from analysis classes such as " | ||
| f'{type(SpectroscopyAnalysis.__name__)} which report "freq" in popt.' | ||
| ) | ||
|
|
||
| param = parameter | ||
| value = result["popt"][result["popt_keys"].index("freq")] | ||
|
|
||
| cls._add_parameter_value(calibrations, exp_data, value, param, schedule=None, group=group) | ||
|
|
||
|
|
||
| class Amplitude(BaseUpdater): | ||
| """Update pulse amplitudes.""" | ||
|
|
||
| # pylint: disable=arguments-differ | ||
| @classmethod | ||
| def update( | ||
| cls, | ||
| calibrations: Calibrations, | ||
| exp_data: ExperimentData, | ||
| result_index: int = -1, | ||
| group: str = "default", | ||
| angles_schedules: List[Tuple[float, str, Union[str, ScheduleBlock]]] = None, | ||
| ): | ||
| """Update the amplitude of pulses. | ||
|
|
||
| Args: | ||
| calibrations: The calibrations to update. | ||
| exp_data: The experiment data from which to update. | ||
| result_index: The result index to use, defaults to -1. | ||
| group: The calibrations group to update. Defaults to "default." | ||
| angles_schedules: A list of tuples specifying which angle to update for which | ||
| pulse schedule. Each tuple is of the form: (angle, parameter_name, | ||
| schedule). Here, angle is the rotation angle for which to extract the amplitude, | ||
| parameter_name is the name of the parameter whose value is to be updated, and | ||
| schedule is the schedule or its name that contains the parameter. | ||
|
|
||
| Raises: | ||
| CalibrationError: If the experiment is not of the supported type. | ||
| """ | ||
| from qiskit_experiments.calibration.experiments.rabi import Rabi | ||
|
|
||
| if angles_schedules is None: | ||
| angles_schedules = [(np.pi, "amp", "xp")] | ||
|
|
||
| if isinstance(exp_data.experiment, Rabi): | ||
| result = exp_data.analysis_result(result_index) | ||
|
|
||
| freq = result["popt"][result["popt_keys"].index("freq")] | ||
| rate = 2 * np.pi * freq | ||
|
|
||
| for angle, param, schedule in angles_schedules: | ||
| value = np.round(angle / rate, decimals=8) | ||
|
|
||
| cls._add_parameter_value(calibrations, exp_data, value, param, schedule, group) | ||
| else: | ||
| raise CalibrationError(f"{cls.__name__} updates from {type(Rabi.__name__)}.") | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ | |
| import os | ||
| import uuid | ||
| from collections import OrderedDict | ||
| from datetime import datetime | ||
|
|
||
| from qiskit.result import Result | ||
| from qiskit.providers import Backend | ||
|
|
@@ -116,6 +117,16 @@ def job_ids(self) -> List[str]: | |
| """ | ||
| return list(self._jobs.keys()) | ||
|
|
||
| @property | ||
| def completion_times(self) -> Dict[str, datetime]: | ||
|
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. Once
Contributor
Author
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. Thanks for pointing this out @yaelbh . Which PR is
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. |
||
| """Returns the completion times of the jobs.""" | ||
| job_times = {} | ||
| for job_id, job in self._jobs.items(): | ||
| if job is not None and "COMPLETED" in job.time_per_step(): | ||
| job_times[job_id] = job.time_per_step().get("COMPLETED") | ||
|
|
||
| return job_times | ||
|
|
||
| @property | ||
| def backend(self) -> Backend: | ||
| """Return backend. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.