Source code for qiskit_experiments.library.calibration.fine_frequency_cal

# 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 frequency calibration experiment."""

from typing import Dict, List, Optional, Sequence
import numpy as np

from qiskit.providers.backend import Backend
from qiskit.circuit import QuantumCircuit

from qiskit_experiments.framework import ExperimentData
from qiskit_experiments.calibration_management.update_library import BaseUpdater
from qiskit_experiments.calibration_management import (
    BaseCalibrationExperiment,
    Calibrations,
)
from qiskit_experiments.library.characterization.fine_frequency import FineFrequency


[docs] class FineFrequencyCal(BaseCalibrationExperiment, FineFrequency): """A calibration version of the fine frequency experiment. # section: example .. jupyter-execute:: :hide-code: import warnings warnings.filterwarnings("ignore", ".*Could not determine job completion time.*", UserWarning) # backend from qiskit_ibm_runtime.fake_provider import FakePerth from qiskit_aer import AerSimulator backend = AerSimulator.from_backend(FakePerth()) .. jupyter-execute:: from qiskit_experiments.calibration_management.calibrations import Calibrations from qiskit_experiments.calibration_management.basis_gate_library \ import FixedFrequencyTransmon from qiskit_experiments.library.calibration.fine_frequency_cal import FineFrequencyCal cals = Calibrations.from_backend(backend=backend, libraries=[FixedFrequencyTransmon()]) exp_cal = FineFrequencyCal((0,), cals, backend=backend, auto_update=False, gate_name="sx") cal_data=exp_cal.run().block_for_results() display(cal_data.figure(0)) cal_data.analysis_results(dataframe=True) """ def __init__( self, 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, gate_name: str = "sx", ): r"""See class :class:`.FineFrequency` for details. Note that this class implicitly assumes that the target angle of the gate is :math:`\pi/2` as seen from the default analysis options. This experiment can be seen as a calibration of a finite duration ``rz(pi/2)`` gate with any error attributed to a frequency offset in the qubit. Args: physical_qubits: Sequence containing the qubit for which to run the 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 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. """ if delay_duration is None: delay_duration = calibrations.get_schedule(gate_name, physical_qubits[0]).duration super().__init__( calibrations, physical_qubits, delay_duration=delay_duration, schedule_name=None, repetitions=repetitions, backend=backend, cal_parameter_name=cal_parameter_name, auto_update=auto_update, ) if self.backend is not None: self.set_experiment_options(dt=self._backend_data.dt) @classmethod def _default_experiment_options(cls): """default values for the fine frequency calibration experiment. Experiment Options: dt (float): The duration of the time unit ``dt`` of the delay and schedules in seconds. """ options = super()._default_experiment_options() options.dt = None return options def _metadata(self) -> Dict[str, any]: """Add metadata to the experiment data making it more self contained. The following keys are added to the experiment's metadata: cal_param_value: The value of the drive frequency parameter. This value together with the fit result will be used to find the new value of the drive frequency parameter. cal_param_name: The name of the parameter in the calibrations. cal_group: The calibration group to which the parameter belongs. delay_duration: The duration of the first delay. dt: The number of ``dt`` units of the delay. """ metadata = super()._metadata() metadata["delay_duration"] = self.experiment_options.delay_duration metadata["dt"] = self.experiment_options.dt metadata["cal_param_value"] = self._cals.get_parameter_value( self._param_name, self.physical_qubits, group=self.experiment_options.group, ) return metadata def _attach_calibrations(self, circuit: QuantumCircuit): """Adds the calibrations to the transpiled circuits.""" schedule = self._cals.get_schedule("sx", self.physical_qubits) circuit.add_calibration("sx", self.physical_qubits, schedule)
[docs] def update_calibrations(self, experiment_data: ExperimentData): r"""Update the qubit frequency based on the measured angle deviation. The frequency of the qubit is updated according to .. math:: f \to f - \frac{{\rm d}\theta}{2\pi\tau{\rm d}t} Here, :math:`{\rm d}\theta` is the measured angle error from the fit. The duration of the single qubit-gate is :math:`\tau` in samples and :math:`{\rm d}t` is the duration of a sample. This is also the duration of the time unit ``dt`` of the delay. """ result_index = self.experiment_options.result_index group = experiment_data.metadata["cal_group"] prev_freq = experiment_data.metadata["cal_param_value"] tau = experiment_data.metadata["delay_duration"] dt = experiment_data.metadata["dt"] d_theta = BaseUpdater.get_value(experiment_data, "d_theta", result_index) new_freq = prev_freq + d_theta / (2 * np.pi * tau * dt) BaseUpdater.add_parameter_value( self._cals, experiment_data, new_freq, self._param_name, self._sched_name, group )