-
Notifications
You must be signed in to change notification settings - Fork 134
Half angle #418
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
Half angle #418
Changes from all commits
ef5dc84
348235c
0be733d
6f6cfee
c8c5371
a08b0e7
d3c600d
1c78d0a
493ec5a
ab469e7
d2579e9
79239b4
b185ed6
6dffacb
d63a46e
c6cf2ac
c840ddf
79bb283
0f38958
6a894bd
b5c8d36
920baf8
9d3722d
5f9b775
1572085
38c7b04
5f654ee
d3a1724
843ec67
c0a7af2
a8bd795
d6c10b0
086d6c1
009534e
bf3cd3e
dd12058
ff24a78
4ab1352
dedf7a8
af71a42
756251a
7f72917
fcbe7ea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| # 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. | ||
|
|
||
| """Fine half angle calibration analysis.""" | ||
|
|
||
| from qiskit_experiments.curve_analysis import ErrorAmplificationAnalysis | ||
|
|
||
|
|
||
| class FineHalfAngleAnalysis(ErrorAmplificationAnalysis): | ||
| r"""Analysis class for the HalfAngle experiment to define the fixed parameters. | ||
|
|
||
| # section: note | ||
|
|
||
| The following parameters are held fixed during fitting. | ||
|
|
||
| * :math:`{\rm apg}` The angle per gate is set by the user, for example pi for a pi-pulse. | ||
| * :math:`{\rm phase\_offset}` The phase offset in the cosine oscillation. | ||
| * :math:`{\rm amp}` The amplitude of the oscillation. | ||
| """ | ||
|
|
||
| __fixed_parameters__ = ["angle_per_gate", "phase_offset", "amp"] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. | ||
|
|
||
| """Half angle calibration.""" | ||
|
|
||
| from typing import List, Optional | ||
| import numpy as np | ||
|
|
||
| from qiskit import QuantumCircuit | ||
| from qiskit.providers.backend import Backend | ||
|
|
||
| from qiskit_experiments.framework import ExperimentData, fix_class_docs | ||
| from qiskit_experiments.calibration_management import ( | ||
| BaseCalibrationExperiment, | ||
| BackendCalibrations, | ||
| ) | ||
| from qiskit_experiments.library.characterization import HalfAngle | ||
| from qiskit_experiments.calibration_management.update_library import BaseUpdater | ||
|
|
||
|
|
||
| @fix_class_docs | ||
| class HalfAngleCal(BaseCalibrationExperiment, HalfAngle): | ||
| """Calibration version of the half-angle experiment.""" | ||
|
|
||
| def __init__( | ||
| self, | ||
| qubit, | ||
| calibrations: BackendCalibrations, | ||
| backend: Optional[Backend] = None, | ||
| schedule_name: str = "sx", | ||
| cal_parameter_name: Optional[str] = "amp", | ||
| auto_update: bool = True, | ||
| ): | ||
| """see class :class:`HalfAngle` for details. | ||
|
|
||
| Args: | ||
| qubit: The qubit for which to run the half-angle calibration. | ||
| calibrations: The calibrations instance with the schedules. | ||
| backend: Optional, the backend to run the experiment on. | ||
| schedule_name: The name of the schedule to calibrate which defaults to sx. | ||
| cal_parameter_name: The name of the parameter in the schedule to update. This will | ||
| default to amp since the complex amplitude contains the phase of the pulse. | ||
| auto_update: Whether or not to automatically update the calibrations. By | ||
| default this variable is set to True. | ||
| """ | ||
| super().__init__( | ||
| calibrations, | ||
| qubit, | ||
| backend=backend, | ||
| schedule_name=schedule_name, | ||
| cal_parameter_name=cal_parameter_name, | ||
| auto_update=auto_update, | ||
| ) | ||
|
|
||
| self.set_transpile_options(inst_map=calibrations.default_inst_map) | ||
|
|
||
| @classmethod | ||
| def _default_experiment_options(cls): | ||
| """Default values for the half angle calibration experiment. | ||
|
|
||
| Experiment Options: | ||
| result_index (int): The index of the result from which to update the calibrations. | ||
| group (str): The calibration group to which the parameter belongs. This will default | ||
| to the value "default". | ||
|
|
||
| """ | ||
| options = super()._default_experiment_options() | ||
|
|
||
| options.result_index = -1 | ||
|
wshanks marked this conversation as resolved.
|
||
| options.group = "default" | ||
|
wshanks marked this conversation as resolved.
|
||
|
|
||
| return options | ||
|
|
||
| def _add_cal_metadata(self, circuits: List[QuantumCircuit]): | ||
| """Add metadata to the circuit to make the experiment data more self contained. | ||
|
|
||
| The following keys are added to each circuit's metadata: | ||
| cal_param_value: The value of the pulse amplitude. This value together with | ||
| the fit result will be used to find the new value of the pulse amplitude. | ||
| cal_param_name: The name of the parameter in the calibrations. | ||
| cal_schedule: The name of the schedule in the calibrations. | ||
| cal_group: The calibration group to which the parameter belongs. | ||
| """ | ||
|
|
||
| param_val = self._cals.get_parameter_value( | ||
| self._param_name, | ||
| self._physical_qubits, | ||
| self._sched_name, | ||
| group=self.experiment_options.group, | ||
| ) | ||
|
|
||
| for circuit in circuits: | ||
| circuit.metadata["cal_param_value"] = param_val | ||
| circuit.metadata["cal_param_name"] = self._param_name | ||
| circuit.metadata["cal_schedule"] = self._sched_name | ||
| circuit.metadata["cal_group"] = self.experiment_options.group | ||
|
|
||
| return circuits | ||
|
|
||
| def update_calibrations(self, experiment_data: ExperimentData): | ||
| r"""Update the value of the parameter in the calibrations. | ||
|
|
||
| The parameter that is updated is the phase of the sx pulse. This phase is contained | ||
| in the complex amplitude of the pulse. The update rule for the half angle calibration is | ||
| therefore: | ||
|
|
||
| ..math:: | ||
|
|
||
| A \to A \cdot e^{-i{\rm d}\theta_\text{hac}/2} | ||
|
|
||
| where :math:`A` is the complex amplitude of the sx pulse which has an angle which might be | ||
| different from the angle of the x pulse due to the non-linearity in the mixer's skew. The | ||
| angle :math:`{\rm d}\theta_\text{hac}` is the angle deviation measured through the error | ||
| amplifying pulse sequence. | ||
|
|
||
| Args: | ||
| experiment_data: The experiment data from which to extract the measured over/under | ||
| rotation used to adjust the amplitude. | ||
| """ | ||
|
|
||
| data = experiment_data.data() | ||
|
|
||
| # No data -> no update | ||
| if len(data) > 0: | ||
| result_index = self.experiment_options.result_index | ||
| group = data[0]["metadata"]["cal_group"] | ||
| prev_amp = data[0]["metadata"]["cal_param_value"] | ||
|
|
||
| d_theta = BaseUpdater.get_value(experiment_data, "d_hac", result_index) | ||
| new_amp = prev_amp * np.exp(-1.0j * d_theta / 2) | ||
|
|
||
| BaseUpdater.add_parameter_value( | ||
| self._cals, | ||
| experiment_data, | ||
| new_amp, | ||
| self._param_name, | ||
| self._sched_name, | ||
| group, | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| # 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. | ||
|
|
||
| """Half angle characterization.""" | ||
|
|
||
| from typing import List, Optional | ||
| import numpy as np | ||
|
|
||
| from qiskit import QuantumCircuit | ||
| from qiskit.providers import Backend | ||
|
|
||
| from qiskit_experiments.framework import BaseExperiment, Options, fix_class_docs | ||
| from qiskit_experiments.library.calibration.analysis import FineHalfAngleAnalysis | ||
| from qiskit_experiments.curve_analysis import ParameterRepr | ||
|
|
||
|
|
||
|
eggerdj marked this conversation as resolved.
|
||
| @fix_class_docs | ||
| class HalfAngle(BaseExperiment): | ||
| r"""An experiment class to measure the amount by which sx and x are not parallel. | ||
|
|
||
| # section: overview | ||
|
|
||
| This experiment runs circuits that repeat blocks of :code:`sx - sx - y` gates | ||
| inserted in a Ramsey type experiment, i.e. the full gate sequence is thus | ||
| :code:`Ry(π/2) - [sx - sx - y] ^ n - sx` where :code:`n` is varied. | ||
|
|
||
| .. parsed-literal:: | ||
|
|
||
| ┌─────────┐┌────┐┌────┐┌───┐ ┌────┐┌────┐┌───┐┌────┐ ░ ┌─┐ | ||
| q_0: ┤ Ry(π/2) ├┤ sx ├┤ sx ├┤ y ├...┤ sx ├┤ sx ├┤ y ├┤ sx ├─░─┤M├ | ||
| └─────────┘└────┘└────┘└───┘ └────┘└────┘└───┘└────┘ ░ └╥┘ | ||
| meas: 1/════════════════════════════...═══════════════════════════╩═ | ||
| 0 | ||
|
|
||
| This sequence measures angle errors where the axis of the :code:`sx` and :code:`x` | ||
| rotation are not parallel. A similar experiment is described in Ref.~[1] where the | ||
| gate sequence :code:`x - y` is repeated to amplify errors caused by non-orthogonal | ||
| :code:`x` and :code:`y` rotation axes. Such errors can occur due to phase errors. | ||
| For example, the non-linearities in the mixer's skew for :math:`\pi/2` pulses may | ||
| be different from the :math:`\pi` pulse. | ||
|
|
||
| # section: reference | ||
| .. ref_arxiv:: 1 1504.06597 | ||
| """ | ||
|
|
||
| __analysis_class__ = FineHalfAngleAnalysis | ||
|
|
||
| @classmethod | ||
| def _default_experiment_options(cls) -> Options: | ||
| r"""Default values for the half angle experiment. | ||
|
|
||
| Experiment Options: | ||
| repetitions (List[int]): A list of the number of times that the gate | ||
| sequence :code:`[sx sx y]` is repeated. | ||
| """ | ||
| options = super()._default_experiment_options() | ||
|
nkanazawa1989 marked this conversation as resolved.
|
||
| options.repetitions = list(range(15)) | ||
| return options | ||
|
|
||
| @classmethod | ||
| def _default_transpile_options(cls) -> Options: | ||
| """Default transpile options. | ||
|
|
||
| The basis gates option should not be changed since it will affect the gates and | ||
| the pulses that are run on the hardware. | ||
| """ | ||
| options = super()._default_transpile_options() | ||
| options.basis_gates = ["sx", "rz", "y"] | ||
|
eggerdj marked this conversation as resolved.
|
||
| options.inst_map = None | ||
| return options | ||
|
|
||
| @classmethod | ||
| def _default_analysis_options(cls) -> Options: | ||
| r"""Default analysis options. | ||
|
|
||
| If the rotation error is very small the fit may chose a d_theta close to | ||
| :math:`\pm\pi`. To prevent this we impose bounds on d_theta. Note that the | ||
| options angle per gate, phase offset and amp are not intended to be changed. | ||
| """ | ||
| options = super()._default_analysis_options() | ||
| options.result_parameters = [ParameterRepr("d_theta", "d_hac", "rad")] | ||
| options.normalization = True | ||
| options.angle_per_gate = np.pi | ||
|
eggerdj marked this conversation as resolved.
|
||
| options.phase_offset = -np.pi / 2 | ||
|
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. Is this -pi/2? I updated offset sign in probability PR
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. I think we are okay with -pi/2. I believe we might have some freedom between -pi/2 or +pi/2: the choice of + or - will change how we handle the rest of the update rule which needs to be consistent. When I ran it yesterday it gave the correct result, see the NB in the PR. In this NB a |
||
| options.amp = 1.0 | ||
| options.bounds.update({"d_theta": (-np.pi / 2, np.pi / 2)}) | ||
|
|
||
| return options | ||
|
|
||
| def __init__(self, qubit: int, backend: Optional[Backend] = None): | ||
| """Setup a half angle experiment on the given qubit. | ||
|
|
||
| Args: | ||
| qubit: The qubit on which to run the fine amplitude calibration experiment. | ||
| backend: Optional, the backend to run the experiment on. | ||
| """ | ||
| super().__init__([qubit], backend=backend) | ||
|
|
||
| @staticmethod | ||
| def _pre_circuit() -> QuantumCircuit: | ||
| """Return the preparation circuit for the experiment.""" | ||
| return QuantumCircuit(1) | ||
|
|
||
| def circuits(self) -> List[QuantumCircuit]: | ||
| """Create the circuits for the half angle calibration experiment.""" | ||
|
|
||
| circuits = [] | ||
|
|
||
| for repetition in self.experiment_options.repetitions: | ||
| circuit = self._pre_circuit() | ||
|
|
||
| # First ry gate | ||
| circuit.rz(np.pi / 2, 0) | ||
| circuit.sx(0) | ||
| circuit.rz(-np.pi / 2, 0) | ||
|
eggerdj marked this conversation as resolved.
|
||
|
|
||
| # Error amplifying sequence | ||
| for _ in range(repetition): | ||
| circuit.sx(0) | ||
| circuit.sx(0) | ||
| circuit.y(0) | ||
|
eggerdj marked this conversation as resolved.
|
||
|
|
||
| circuit.sx(0) | ||
| circuit.measure_all() | ||
|
|
||
| circuit.metadata = { | ||
| "experiment_type": self._type, | ||
| "qubits": self.physical_qubits, | ||
| "xval": repetition, | ||
| "unit": "repetition number", | ||
| } | ||
|
|
||
| circuits.append(circuit) | ||
|
|
||
| return circuits | ||
Uh oh!
There was an error while loading. Please reload this page.