diff --git a/qiskit_experiments/calibration_management/__init__.py b/qiskit_experiments/calibration_management/__init__.py index e2ed834877..fceb3b33d6 100644 --- a/qiskit_experiments/calibration_management/__init__.py +++ b/qiskit_experiments/calibration_management/__init__.py @@ -38,7 +38,6 @@ BackendCalibrations Calibrations - Frequency Managing Calibration Data ========================= @@ -147,5 +146,4 @@ from .calibrations import Calibrations from .backend_calibrations import BackendCalibrations from .base_calibration_experiment import BaseCalibrationExperiment - -from .update_library import Frequency, FineDragUpdater +from .basis_gate_library import FixedFrequencyTransmon diff --git a/qiskit_experiments/calibration_management/update_library.py b/qiskit_experiments/calibration_management/update_library.py index 7fbd09452a..adee36f306 100644 --- a/qiskit_experiments/calibration_management/update_library.py +++ b/qiskit_experiments/calibration_management/update_library.py @@ -15,10 +15,9 @@ from abc import ABC from datetime import datetime, timezone from typing import Optional, Union -import numpy as np from qiskit.circuit import Parameter -from qiskit.pulse import ScheduleBlock, Play +from qiskit.pulse import ScheduleBlock from qiskit_experiments.framework.experiment_data import ExperimentData from qiskit_experiments.calibration_management.backend_calibrations import BackendCalibrations @@ -182,60 +181,3 @@ def update( group=group, fit_parameter=fit_parameter, ) - - -class FineDragUpdater(BaseUpdater): - """Updater for the fine drag calibration.""" - - # pylint: disable=arguments-differ,unused-argument - @classmethod - def update( - cls, - calibrations: Calibrations, - exp_data: ExperimentData, - parameter: str, - schedule: Union[ScheduleBlock, str], - result_index: Optional[int] = -1, - group: str = "default", - target_angle: float = np.pi, - **options, - ): - """Update the value of a drag parameter measured by the FineDrag experiment. - - Args: - calibrations: The calibrations to update. - exp_data: The experiment data from which to update. - parameter: The name of the parameter in the calibrations to update. - schedule: The ScheduleBlock instance or the name of the instance to which the parameter - is attached. - result_index: The result index to use. By default search entry by name. - group: The calibrations group to update. Defaults to "default." - target_angle: The target rotation angle of the pulse. - options: Trailing options. - - Raises: - CalibrationError: If we cannot get the pulse's standard deviation from the schedule. - """ - qubits = exp_data.metadata["physical_qubits"] - - if isinstance(schedule, str): - schedule = calibrations.get_schedule(schedule, qubits) - - # Obtain sigma as it is needed for the fine DRAG update rule. - sigma = None - for block in schedule.blocks: - if isinstance(block, Play) and hasattr(block.pulse, "sigma"): - sigma = getattr(block.pulse, "sigma") - - if sigma is None: - raise CalibrationError(f"Could not infer sigma from {schedule}.") - - d_theta = BaseUpdater.get_value(exp_data, "d_theta", result_index) - - # See the documentation in fine_drag.py for the derivation of this rule. - d_beta = -np.sqrt(np.pi) * d_theta * sigma / target_angle ** 2 - - old_beta = calibrations.get_parameter_value(parameter, qubits, schedule, group=group) - new_beta = old_beta + d_beta - - cls.add_parameter_value(calibrations, exp_data, new_beta, parameter, schedule, group) diff --git a/qiskit_experiments/library/__init__.py b/qiskit_experiments/library/__init__.py index b23ab5e9ef..5eb0ff5150 100644 --- a/qiskit_experiments/library/__init__.py +++ b/qiskit_experiments/library/__init__.py @@ -63,6 +63,9 @@ ~characterization.CrossResonanceHamiltonian ~characterization.EchoedCrossResonanceHamiltonian ~characterization.RoughDrag + ~characterization.FineDrag + ~characterization.FineXDrag + ~characterization.FineSXDrag ~characterization.HalfAngle ~characterization.FineAmplitude ~characterization.FineXAmplitude @@ -89,9 +92,9 @@ class instance to manage parameters and pulse schedules. ~calibration.RoughFrequencyCal ~calibration.RoughDragCal - ~calibration.FineDrag - ~calibration.FineXDrag - ~calibration.FineSXDrag + ~calibration.FineXDragCal + ~calibration.FineSXDragCal + ~calibration.FineDragCal ~calibration.FineAmplitudeCal ~calibration.FineXAmplitudeCal ~calibration.FineSXAmplitudeCal @@ -102,9 +105,9 @@ class instance to manage parameters and pulse schedules. """ from .calibration import ( RoughDragCal, - FineDrag, - FineXDrag, - FineSXDrag, + FineDragCal, + FineXDragCal, + FineSXDragCal, RoughAmplitudeCal, RoughXSXAmplitudeCal, EFRoughXSXAmplitudeCal, @@ -122,6 +125,9 @@ class instance to manage parameters and pulse schedules. CrossResonanceHamiltonian, EchoedCrossResonanceHamiltonian, RoughDrag, + FineDrag, + FineXDrag, + FineSXDrag, Rabi, EFRabi, HalfAngle, diff --git a/qiskit_experiments/library/calibration/__init__.py b/qiskit_experiments/library/calibration/__init__.py index c56d1b23b2..7b7a350a81 100644 --- a/qiskit_experiments/library/calibration/__init__.py +++ b/qiskit_experiments/library/calibration/__init__.py @@ -42,9 +42,9 @@ RoughFrequencyCal FrequencyCal RoughDragCal - FineDrag - FineXDrag - FineSXDrag + FineDragCal + FineXDragCal + FineSXDragCal FineAmplitudeCal FineXAmplitudeCal FineSXAmplitudeCal @@ -71,9 +71,9 @@ from .rough_frequency import RoughFrequencyCal from .rough_drag_cal import RoughDragCal -from .fine_drag import FineDrag, FineXDrag, FineSXDrag from .rough_amplitude_cal import RoughAmplitudeCal, RoughXSXAmplitudeCal, EFRoughXSXAmplitudeCal from .fine_amplitude import FineAmplitudeCal, FineXAmplitudeCal, FineSXAmplitudeCal +from .fine_drag_cal import FineDragCal, FineXDragCal, FineSXDragCal from .frequency_cal import FrequencyCal from .analysis.drag_analysis import DragCalAnalysis diff --git a/qiskit_experiments/library/calibration/fine_amplitude.py b/qiskit_experiments/library/calibration/fine_amplitude.py index b01e0bb703..5d619ff01e 100644 --- a/qiskit_experiments/library/calibration/fine_amplitude.py +++ b/qiskit_experiments/library/calibration/fine_amplitude.py @@ -69,18 +69,14 @@ def __init__( auto_update=auto_update, ) - self.transpile_options.inst_map = calibrations.default_inst_map + self.set_transpile_options(inst_map=calibrations.default_inst_map) @classmethod def _default_experiment_options(cls): """Default values for the fine amplitude calibration experiment. Experiment Options: - result_index (int): The index of the result from which to update the calibrations. target_angle (float): The target angle of the pulse. - group (str): The calibration group to which the parameter belongs. This will default - to the value "default". - """ options = super()._default_experiment_options() options.target_angle = np.pi diff --git a/qiskit_experiments/library/calibration/fine_drag_cal.py b/qiskit_experiments/library/calibration/fine_drag_cal.py new file mode 100644 index 0000000000..6961aef7ea --- /dev/null +++ b/qiskit_experiments/library/calibration/fine_drag_cal.py @@ -0,0 +1,221 @@ +# 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 drag calibration experiment.""" + +from typing import List, Optional +import numpy as np + +from qiskit.circuit import Gate, QuantumCircuit +from qiskit.providers.backend import Backend +from qiskit.pulse import Play + +from qiskit_experiments.exceptions import CalibrationError +from qiskit_experiments.framework import ExperimentData, fix_class_docs, Options +from qiskit_experiments.calibration_management import ( + BaseCalibrationExperiment, + BackendCalibrations, +) +from qiskit_experiments.calibration_management.update_library import BaseUpdater +from qiskit_experiments.library.characterization.fine_drag import FineDrag + + +@fix_class_docs +class FineDragCal(BaseCalibrationExperiment, FineDrag): + """A calibration version of the fine drag experiment.""" + + def __init__( + self, + qubit: int, + calibrations: BackendCalibrations, + schedule_name: str, + backend: Optional[Backend] = None, + cal_parameter_name: Optional[str] = "β", + auto_update: bool = True, + ): + r"""see class :class:`FineDrag` for details. + + Note that this class implicitly assumes that the target angle of the gate + is :math:`\pi` as seen from the default experiment options. + + Args: + qubit: The qubit for which to run the fine drag calibration. + calibrations: The calibrations instance with the schedules. + schedule_name: The name of the schedule to calibrate. + backend: Optional, the backend to run the experiment on. + cal_parameter_name: The name of the parameter in the schedule to update. + auto_update: Whether or not to automatically update the calibrations. By + default this variable is set to True. + """ + super().__init__( + calibrations, + qubit, + Gate(name=schedule_name, num_qubits=1, params=[]), + schedule_name=schedule_name, + backend=backend, + cal_parameter_name=cal_parameter_name, + auto_update=auto_update, + ) + + self.set_transpile_options( + inst_map=calibrations.default_inst_map, + basis_gates=["sx", schedule_name, "rz"], + ) + + @classmethod + def _default_experiment_options(cls) -> Options: + """Default experiment options. + + Experiment Options: + target_angle (float): The target rotation angle of the gate being calibrated. + This value is needed for the update rule. + """ + options = super()._default_experiment_options() + options.target_angle = np.pi + 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 drag parameter. This value together with + the fit result will be used to find the new value of the drag parameter. + cal_param_name: The name of the parameter in the calibrations. + cal_schedule: The name of the schedule in the calibrations. + target_angle: The target angle of the gate. + 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["target_angle"] = self.experiment_options.target_angle + circuit.metadata["cal_group"] = self.experiment_options.group + + def update_calibrations(self, experiment_data: ExperimentData): + """Update the drag parameter of the pulse in the calibrations.""" + + 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"] + target_angle = data[0]["metadata"]["target_angle"] + qubits = experiment_data.metadata["physical_qubits"] + + schedule = self._cals.get_schedule(self._sched_name, qubits) + + # Obtain sigma as it is needed for the fine DRAG update rule. + sigmas = [] + for block in schedule.blocks: + if isinstance(block, Play) and hasattr(block.pulse, "sigma"): + sigmas.append(getattr(block.pulse, "sigma")) + + if len(set(sigmas)) != 1: + raise CalibrationError( + "Cannot run fine Drag calibration on a schedule with multiple values of sigma." + ) + + if len(sigmas) == 0: + raise CalibrationError(f"Could not infer sigma from {schedule}.") + + d_theta = BaseUpdater.get_value(experiment_data, "d_theta", result_index) + + # See the documentation in fine_drag.py for the derivation of this rule. + d_beta = -np.sqrt(np.pi) * d_theta * sigmas[0] / target_angle ** 2 + old_beta = data[0]["metadata"]["cal_param_value"] + new_beta = old_beta + d_beta + + BaseUpdater.add_parameter_value( + self._cals, experiment_data, new_beta, self._param_name, schedule, group + ) + + +@fix_class_docs +class FineXDragCal(FineDragCal): + """Fine drag calibration of X gate.""" + + def __init__( + self, + qubit: int, + calibrations: BackendCalibrations, + backend: Optional[Backend] = None, + cal_parameter_name: Optional[str] = "β", + auto_update: bool = True, + ): + r"""see class :class:`FineDrag` for details. + + Args: + qubit: The qubit for which to run the fine drag 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 in the schedule to update. + auto_update: Whether or not to automatically update the calibrations. By + default this variable is set to True. + """ + super().__init__( + qubit, + calibrations, + schedule_name="x", + backend=backend, + cal_parameter_name=cal_parameter_name, + auto_update=auto_update, + ) + + +@fix_class_docs +class FineSXDragCal(FineDragCal): + """Fine drag calibration of X gate.""" + + def __init__( + self, + qubit: int, + calibrations: BackendCalibrations, + backend: Optional[Backend] = None, + cal_parameter_name: Optional[str] = "β", + auto_update: bool = True, + ): + r"""see class :class:`FineDrag` for details. + + Args: + qubit: The qubit for which to run the fine drag 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 in the schedule to update. + auto_update: Whether or not to automatically update the calibrations. By + default this variable is set to True. + """ + super().__init__( + qubit, + calibrations, + schedule_name="sx", + backend=backend, + cal_parameter_name=cal_parameter_name, + auto_update=auto_update, + ) + + @classmethod + def _default_experiment_options(cls) -> Options: + """Default experiment options.""" + options = super()._default_experiment_options() + options.target_angle = np.pi / 2 + return options diff --git a/qiskit_experiments/library/characterization/__init__.py b/qiskit_experiments/library/characterization/__init__.py index c583f03dd3..f791617ae2 100644 --- a/qiskit_experiments/library/characterization/__init__.py +++ b/qiskit_experiments/library/characterization/__init__.py @@ -36,6 +36,9 @@ FineSXAmplitude RamseyXY RoughDrag + FineDrag + FineXDrag + FineSXDrag Analysis @@ -64,3 +67,4 @@ from .fine_amplitude import FineAmplitude, FineXAmplitude, FineSXAmplitude from .ramsey_xy import RamseyXY from .drag import RoughDrag +from .fine_drag import FineDrag, FineXDrag, FineSXDrag diff --git a/qiskit_experiments/library/calibration/fine_drag.py b/qiskit_experiments/library/characterization/fine_drag.py similarity index 91% rename from qiskit_experiments/library/calibration/fine_drag.py rename to qiskit_experiments/library/characterization/fine_drag.py index a6dacb9e5d..313e3c6bab 100644 --- a/qiskit_experiments/library/calibration/fine_drag.py +++ b/qiskit_experiments/library/characterization/fine_drag.py @@ -144,13 +144,12 @@ def _default_experiment_options(cls) -> Options: repetitions (List[int]): A list of the number of times that Rp - Rm gate sequence is repeated. schedule (ScheduleBlock): The schedule for the plus rotation. - gate_type (Type[Gate]): This is a gate class such as XGate, so that one can obtain a gate - by doing :code:`options.gate_type()`. + gate (Gate): This is the gate such as XGate() that will be in the circuits. """ options = super()._default_experiment_options() options.repetitions = list(range(20)) options.schedule = None - options.gate_type = None + options.gate = None return options @@ -165,14 +164,16 @@ def _default_analysis_options(cls) -> Options: return options - def __init__(self, qubit: int, backend: Optional[Backend] = None): + def __init__(self, qubit: int, gate: Gate, backend: Optional[Backend] = None): """Setup a fine amplitude experiment on the given qubit. Args: qubit: The qubit on which to run the fine amplitude calibration experiment. + gate: The gate that will be repeated. backend: Optional, the backend to run the experiment on. """ super().__init__([qubit], backend=backend) + self.set_experiment_options(gate=gate) @staticmethod def _pre_circuit() -> QuantumCircuit: @@ -182,8 +183,11 @@ def _pre_circuit() -> QuantumCircuit: @staticmethod def _post_circuit() -> QuantumCircuit: """Return the quantum circuit to apply after repeating the Rp - Rz - Rp - Rz gates.""" + + # Map unwanted Z rotations to qubit population. circ = QuantumCircuit(1) - circ.ry(np.pi / 2, 0) # Maps unwanted Z rotations to qubit population. + circ.rz(-np.pi / 2, 0) + circ.sx(0) return circ def circuits(self) -> List[QuantumCircuit]: @@ -195,18 +199,13 @@ def circuits(self) -> List[QuantumCircuit]: """ schedule, circuits = self.experiment_options.schedule, [] - if schedule is None: - gate = self.experiment_options.gate_type() - else: - gate = Gate(name=schedule.name, num_qubits=1, params=[]) - for repetition in self.experiment_options.repetitions: circuit = self._pre_circuit() for _ in range(repetition): - circuit.append(gate, (0,)) + circuit.append(self.experiment_options.gate, (0,)) circuit.rz(np.pi, 0) - circuit.append(gate, (0,)) + circuit.append(self.experiment_options.gate, (0,)) circuit.rz(np.pi, 0) circuit.compose(self._post_circuit(), inplace=True) @@ -236,15 +235,19 @@ class FineXDrag(FineDrag): qiskit_experiments.library.calibration.fine_drag.FineDrag """ + def __init__(self, qubit: int, backend: Optional[Backend] = None): + """Initialize the experiment.""" + super().__init__(qubit, XGate(), backend=backend) + @classmethod def _default_experiment_options(cls) -> Options: r"""Default values for the FineXDrag experiment. Experiment Options: - gate_type (Type): FineXDrag calibrates an XGate. + gate (Gate): FineXDrag calibrates an XGate. """ options = super()._default_experiment_options() - options.gate_type = XGate + options.gate = XGate() return options @@ -261,15 +264,19 @@ class FineSXDrag(FineDrag): qiskit_experiments.library.calibration.fine_drag.FineDrag """ + def __init__(self, qubit: int, backend: Optional[Backend] = None): + """Initialize the experiment.""" + super().__init__(qubit, SXGate(), backend=backend) + @classmethod def _default_experiment_options(cls) -> Options: r"""Default values for the FineSXDrag experiment. Experiment Options: - gate_type (Type): FineSXDrag calibrates an SXGate. + gate (Gate): FineSXDrag calibrates an SXGate. """ options = super()._default_experiment_options() - options.gate_type = SXGate + options.gate = SXGate() return options diff --git a/test/calibration/experiments/test_fine_drag.py b/test/calibration/experiments/test_fine_drag.py index ae57053623..8b5f46bdc4 100644 --- a/test/calibration/experiments/test_fine_drag.py +++ b/test/calibration/experiments/test_fine_drag.py @@ -12,15 +12,19 @@ """Test fine drag calibration experiment.""" +import copy import numpy as np -from qiskit import QuantumCircuit +from qiskit import transpile +from qiskit.circuit import QuantumCircuit, Gate from qiskit.test import QiskitTestCase from qiskit.test.mock import FakeArmonk import qiskit.pulse as pulse -from qiskit_experiments.library import FineDrag, FineXDrag +from qiskit_experiments.library import FineDrag, FineXDrag, FineDragCal from qiskit_experiments.test.mock_iq_backend import DragBackend +from qiskit_experiments.calibration_management import BackendCalibrations +from qiskit_experiments.calibration_management.basis_gate_library import FixedFrequencyTransmon class FineDragTestBackend(DragBackend): @@ -48,7 +52,7 @@ def setUp(self): def test_circuits(self): """Test the circuits of the experiment.""" - drag = FineDrag(0) + drag = FineDrag(0, Gate("Drag", num_qubits=1, params=[])) drag.set_experiment_options(schedule=self.schedule) drag.backend = FakeArmonk() for circuit in drag.circuits()[1:]: @@ -58,9 +62,9 @@ def test_circuits(self): def test_end_to_end(self): """A simple test to check if the experiment will run and fit data.""" - drag = FineDrag(0) + drag = FineDrag(0, Gate("Drag", num_qubits=1, params=[])) drag.set_experiment_options(schedule=self.schedule) - drag.set_transpile_options(basis_gates=["rz", "Drag", "ry"]) + drag.set_transpile_options(basis_gates=["rz", "Drag", "sx"]) exp_data = drag.run(FineDragTestBackend()).block_for_results() self.assertEqual(exp_data.analysis_results(0).quality, "good") @@ -74,8 +78,67 @@ def test_end_to_end_no_schedule(self): def test_experiment_config(self): """Test converting to and from config works""" - exp = FineDrag(0) + exp = FineDrag(0, Gate("Drag", num_qubits=1, params=[])) config = exp.config loaded_exp = FineDrag.from_config(config) self.assertNotEqual(exp, loaded_exp) self.assertEqual(config, loaded_exp.config) + + +class TestFineDragCal(QiskitTestCase): + """Test the calibration version of the fine drag experiment.""" + + def setUp(self): + """Setup the test.""" + super().setUp() + + library = FixedFrequencyTransmon() + + self.backend = FineDragTestBackend() + self.cals = BackendCalibrations(self.backend, library) + + def test_experiment_config(self): + """Test converting to and from config works""" + exp = FineDragCal(0, self.cals, schedule_name="x") + config = exp.config + loaded_exp = FineDragCal.from_config(config) + self.assertNotEqual(exp, loaded_exp) + self.assertEqual(config, loaded_exp.config) + + def test_update_cals(self): + """Test that the calibrations are updated.""" + + init_beta = 0.0 + + drag_cal = FineDragCal(0, self.cals, "x", self.backend) + + transpile_opts = copy.copy(drag_cal.transpile_options.__dict__) + transpile_opts["initial_layout"] = list(drag_cal.physical_qubits) + circs = transpile(drag_cal.circuits(), **transpile_opts) + + with pulse.build(name="x") as expected_x: + pulse.play(pulse.Drag(160, 0.5, 40, 0), pulse.DriveChannel(0)) + + with pulse.build(name="sx") as expected_sx: + pulse.play(pulse.Drag(160, 0.25, 40, 0), pulse.DriveChannel(0)) + + self.assertEqual(circs[5].calibrations["x"][((0,), ())], expected_x) + self.assertEqual(circs[5].calibrations["sx"][((0,), ())], expected_sx) + + # run the calibration experiment. This should update the beta parameter of x which we test. + exp_data = drag_cal.run(self.backend).block_for_results() + d_theta = exp_data.analysis_results(1).value.value + sigma = 40 + target_angle = np.pi + new_beta = -np.sqrt(np.pi) * d_theta * sigma / target_angle ** 2 + + transpile_opts = copy.copy(drag_cal.transpile_options.__dict__) + transpile_opts["initial_layout"] = list(drag_cal.physical_qubits) + circs = transpile(drag_cal.circuits(), **transpile_opts) + + x_cal = circs[5].calibrations["x"][((0,), ())] + + # Requires allclose due to numerical precision. + self.assertTrue(np.allclose(x_cal.blocks[0].pulse.beta, new_beta)) + self.assertFalse(np.allclose(x_cal.blocks[0].pulse.beta, init_beta)) + self.assertEqual(circs[5].calibrations["sx"][((0,), ())], expected_sx) diff --git a/test/calibration/test_update_library.py b/test/calibration/test_update_library.py index 3db5d0e7da..855402d6ed 100644 --- a/test/calibration/test_update_library.py +++ b/test/calibration/test_update_library.py @@ -21,14 +21,10 @@ import qiskit.pulse as pulse from qiskit.test.mock import FakeAthens -from qiskit_experiments.library import FineXDrag, QubitSpectroscopy +from qiskit_experiments.library import QubitSpectroscopy from qiskit_experiments.calibration_management.calibrations import Calibrations -from qiskit_experiments.calibration_management.update_library import ( - Frequency, - FineDragUpdater, -) +from qiskit_experiments.calibration_management.update_library import Frequency from qiskit_experiments.calibration_management.backend_calibrations import BackendCalibrations -from .experiments.test_fine_drag import FineDragTestBackend class TestAmplitudeUpdate(QiskitTestCase): @@ -84,67 +80,3 @@ def test_frequency(self): self.assertNotEqual(cals.get_qubit_frequencies()[qubit], value) Frequency.update(cals, exp_data) self.assertEqual(cals.get_qubit_frequencies()[qubit], value) - - -class TestFineDragUpdate(QiskitTestCase): - """A class to test fine DRAG updates.""" - - def test_fine_drag(self): - """Test that we can update from a fine DRAG experiment.""" - - d_theta = 0.03 # rotation error per single gate. - backend = FineDragTestBackend(error=d_theta) - - qubit = 0 - test_tol = 0.005 - beta = Parameter("β") - chan = Parameter("ch0") - - with pulse.build(backend=backend, name="xp") as x_plus: - pulse.play( - pulse.Drag(duration=160, amp=0.208519, sigma=40, beta=beta), - pulse.DriveChannel(chan), - ) - - # Setup the calibrations - cals = BackendCalibrations(backend) - - cals.add_schedule(x_plus, num_qubits=1) - - old_beta = 0.2 - cals.add_parameter_value(old_beta, "β", qubit, x_plus) - cals.inst_map_add("xp", (qubit,)) - - # Check that the inst_map has the default beta - beta_val = cals.default_inst_map.get("xp", (qubit,)).blocks[0].pulse.beta - self.assertEqual(beta_val, old_beta) - - # Run a Drag calibration experiment. - drag = FineXDrag(qubit) - drag.set_experiment_options(schedule=cals.get_schedule("xp", qubit)) - drag.set_transpile_options(basis_gates=["rz", "xp", "ry"]) - exp_data = drag.run(backend).block_for_results() - - result = exp_data.analysis_results(1) - - # Test the fit for good measure. - self.assertTrue(abs(result.value.value - d_theta) < test_tol) - self.assertEqual(result.quality, "good") - - # Check schedules pre-update - expected = x_plus.assign_parameters({beta: 0.2, chan: qubit}, inplace=False) - self.assertEqual(cals.get_schedule("xp", qubit), expected) - - FineDragUpdater.update(cals, exp_data, parameter="β", schedule="xp") - - # Check schedules post-update. Here the FineDragTestBackend has a leakage - # of 0.03 per gate so the DRAG update rule - # -np.sqrt(np.pi) * d_theta * sigma / target_angle ** 2 should give a new beta of - # 0.2 - np.sqrt(np.pi) * 0.03 * 40 / (np.pi ** 2) - new_beta = old_beta - np.sqrt(np.pi) * result.value.value * 40 / np.pi ** 2 - expected = x_plus.assign_parameters({beta: new_beta, chan: qubit}, inplace=False) - self.assertEqual(cals.get_schedule("xp", qubit), expected) - - # Check the inst map post update - beta_val = cals.default_inst_map.get("xp", (qubit,)).blocks[0].pulse.beta - self.assertTrue(np.allclose(beta_val, new_beta))