Skip to content
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

Cross resonance Rabi experiment and rough amplitude calibration #1199

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions qiskit_experiments/library/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
~characterization.FineSXAmplitude
~characterization.Rabi
~characterization.EFRabi
~characterization.CrossResRabi
~characterization.RamseyXY
~characterization.FineFrequency
~characterization.ReadoutAngle
Expand Down Expand Up @@ -138,6 +139,7 @@ class instance to manage parameters and pulse schedules.
~calibration.RoughAmplitudeCal
~calibration.RoughXSXAmplitudeCal
~calibration.EFRoughXSXAmplitudeCal
~calibration.CrossResRoughAmplitudeCal

"""
from .calibration import (
Expand All @@ -148,6 +150,7 @@ class instance to manage parameters and pulse schedules.
RoughAmplitudeCal,
RoughXSXAmplitudeCal,
EFRoughXSXAmplitudeCal,
CrossResRoughAmplitudeCal,
FineAmplitudeCal,
FineXAmplitudeCal,
FineSXAmplitudeCal,
Expand All @@ -172,6 +175,7 @@ class instance to manage parameters and pulse schedules.
FineSXDrag,
Rabi,
EFRabi,
CrossResRabi,
HalfAngle,
FineAmplitude,
FineXAmplitude,
Expand Down
8 changes: 7 additions & 1 deletion qiskit_experiments/library/calibration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
RoughAmplitudeCal
RoughXSXAmplitudeCal
EFRoughXSXAmplitudeCal
CrossResRoughAmplitudeCal

Calibrations management
=======================
Expand All @@ -57,7 +58,12 @@

from .rough_frequency import RoughFrequencyCal, RoughEFFrequencyCal
from .rough_drag_cal import RoughDragCal
from .rough_amplitude_cal import RoughAmplitudeCal, RoughXSXAmplitudeCal, EFRoughXSXAmplitudeCal
from .rough_amplitude_cal import (
RoughAmplitudeCal,
RoughXSXAmplitudeCal,
EFRoughXSXAmplitudeCal,
CrossResRoughAmplitudeCal,
)
from .fine_amplitude import FineAmplitudeCal, FineXAmplitudeCal, FineSXAmplitudeCal
from .fine_drag_cal import FineDragCal, FineXDragCal, FineSXDragCal
from .frequency_cal import FrequencyCal
Expand Down
92 changes: 91 additions & 1 deletion qiskit_experiments/library/calibration/rough_amplitude_cal.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

from qiskit_experiments.framework import ExperimentData
from qiskit_experiments.calibration_management import BaseCalibrationExperiment, Calibrations
from qiskit_experiments.library.characterization import Rabi
from qiskit_experiments.library.characterization import Rabi, CrossResRabi
from qiskit_experiments.calibration_management.update_library import BaseUpdater
from qiskit_experiments.warnings import qubit_deprecate

Expand Down Expand Up @@ -288,3 +288,93 @@ def _attach_calibrations(self, circuit: QuantumCircuit):
if self._cals.has_template("x", self.physical_qubits):
schedule = self._cals.get_schedule("x", self.physical_qubits)
circuit.add_calibration("x", self.physical_qubits, schedule)


class CrossResRoughAmplitudeCal(BaseCalibrationExperiment, CrossResRabi):
"""A calibration version of the CrossResRabi experiment."""

def __init__(
self,
physical_qubits: Sequence[int],
calibrations: Calibrations,
schedule_name: str = "ecr",
cal_parameter_name: str = "amp",
auto_update: bool = True,
group: str = "default",
backend: Optional[Backend] = None,
):
"""Create new experiment.

Args:
physical_qubits: Two element sequence of control and target qubit index.
calibrations: The calibrations instance with the schedules.
schedule_name: The name of the schedule to calibrate.
cal_parameter_name: The name of the parameter in the schedule to update.
auto_update: Whether or not to automatically update the calibrations.
group: The group of calibration parameters to use.
backend: Optional, the backend to run the experiment on.
"""
schedule = calibrations.get_schedule(
name=schedule_name,
qubits=tuple(physical_qubits),
assign_params={cal_parameter_name: CrossResRabi.parameter},
group=group,
)
super().__init__(
calibrations,
physical_qubits,
schedule=schedule,
backend=backend,
cal_parameter_name=cal_parameter_name,
auto_update=auto_update,
)
self.set_experiment_options(
angle_schedules=[
AnglesSchedules(
target_angle=np.pi / 2,
parameter=cal_parameter_name,
schedule=schedule_name,
previous_value=None,
),
]
)

@classmethod
def _default_experiment_options(cls):
"""Default experiment options.

Experiment Options:
angle_schedules (list[AngleSchedules]): A list of parameter value
information. Each entry of the list is a tuple of
the target angle, the name of parameter to update,
the name of associated schedule to update, and previous parameter value.
"""
options = super()._default_experiment_options()
options.update_options(angle_schedules=None)
return options

def _metadata(self):
metadata = super()._metadata()
metadata["angles_schedules"] = self.experiment_options.angle_schedules

return metadata

def _attach_calibrations(self, circuit: QuantumCircuit):
pass

def update_calibrations(self, experiment_data: ExperimentData):
rabi_rate_o1 = 2 * np.pi * BaseUpdater.get_value(experiment_data, "cross_res_rabi_rate_o1")
rabi_rate_o3 = 2 * np.pi * BaseUpdater.get_value(experiment_data, "cross_res_rabi_rate_o3")
group = experiment_data.metadata["cal_group"]

for angle, param, schedule, _ in experiment_data.metadata["angles_schedules"]:
candidates = np.roots([rabi_rate_o3, 0.0, rabi_rate_o1, -angle])
value = min(candidates[candidates > 0])
BaseUpdater.add_parameter_value(
self._cals,
experiment_data,
value,
param,
schedule,
group,
)
5 changes: 4 additions & 1 deletion qiskit_experiments/library/characterization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
EchoedCrossResonanceHamiltonian
Rabi
EFRabi
CrossResRabi
HalfAngle
FineAmplitude
FineXAmplitude
Expand Down Expand Up @@ -64,6 +65,7 @@
T2RamseyAnalysis
T2HahnAnalysis
TphiAnalysis
CrossResRabiAnalysis
CrossResonanceHamiltonianAnalysis
DragCalAnalysis
FineAmplitudeAnalysis
Expand All @@ -86,6 +88,7 @@
T1KerneledAnalysis,
T2HahnAnalysis,
TphiAnalysis,
CrossResRabiAnalysis,
CrossResonanceHamiltonianAnalysis,
ReadoutAngleAnalysis,
ResonatorSpectroscopyAnalysis,
Expand All @@ -102,7 +105,7 @@
from .t2hahn import T2Hahn
from .tphi import Tphi
from .cr_hamiltonian import CrossResonanceHamiltonian, EchoedCrossResonanceHamiltonian
from .rabi import Rabi, EFRabi
from .rabi import Rabi, EFRabi, CrossResRabi
from .half_angle import HalfAngle
from .fine_amplitude import FineAmplitude, FineXAmplitude, FineSXAmplitude, FineZXAmplitude
from .ramsey_xy import RamseyXY, StarkRamseyXY
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from .t1_analysis import T1Analysis
from .t1_analysis import T1KerneledAnalysis
from .tphi_analysis import TphiAnalysis
from .cr_rabi_analysis import CrossResRabiAnalysis
from .cr_hamiltonian_analysis import CrossResonanceHamiltonianAnalysis
from .readout_angle_analysis import ReadoutAngleAnalysis
from .local_readout_error_analysis import LocalReadoutErrorAnalysis
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2023.
#
# 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.

"""Analyze oscillating data of cross resonance Rabi."""


from typing import List, Union

import lmfit
import numpy as np

from qiskit_experiments.framework import Options
import qiskit_experiments.curve_analysis as curve


class CrossResRabiAnalysis(curve.CurveAnalysis):
r"""Cross resonance Rabi oscillation analysis class with nonlinear frequency.

# section: fit_model

Under the perturbation approximation, the amplitude dependence of
the controlled rotation term, i.e. :math:`ZX` term,
in the cross resonance Hamiltonian might be fit by [1]

.. math::

y = {\rm amp} \cos\left(
2 \pi\cdot \left( {\rm freq}^{o1} \cdot x + {\rm freq}^{o3} \cdot x^3 \right) + \pi
\right) + {\rm base}

This approximation is valid as long as the tone amplitude is sufficiently weaker
than the breakdown point at :math:`\Omega/\Delta \ll 1` where
:math:`\Omega` is the tone amplitude and :math:`\Delta` is the qubit-qubit detuning.

# section: fit_parameters
defpar \rm amp:
desc: Amplitude of the oscillation.
init_guess: Calculated by :func:`~qiskit_experiments.curve_analysis.guess.max_height`.
bounds: [0, 1]

defpar \rm base:
desc: Base line.
init_guess: Calculated by :func:`~qiskit_experiments.curve_analysis.\
guess.constant_sinusoidal_offset`.
bounds: [-1, 1]

defpar \rm freq_o1:
desc: Frequency of the oscillation in the first order.
This is the fit parameter of interest.
init_guess: Calculated by :func:`~qiskit_experiments.curve_analysis.guess.frequency`.
bounds: [0, inf].

defpar \rm freq_o3:
desc: Frequency of the oscillation in the third order.
This is the fit parameter of interest.
init_guess: 0.0.
bounds: [0, inf].

# section: reference
.. ref_arxiv:: 1 1804.04073

"""

def __init__(self):
super().__init__(
models=[
lmfit.models.ExpressionModel(
expr="amp * cos(2 * pi * (freq_o1 * x + freq_o3 * x**3) + pi) + offset",
name="cos",
)
],
)

@classmethod
def _default_options(cls) -> Options:
options = super()._default_options()
options.update_options(
result_parameters=[
curve.ParameterRepr("freq_o1", "cross_res_rabi_rate_o1"),
curve.ParameterRepr("freq_o3", "cross_res_rabi_rate_o3"),
],
outcome="1",
)
options.plotter.set_figure_options(xlabel="Amplitude", ylabel="Target P(1)")
return options

def _generate_fit_guesses(
self,
user_opt: curve.FitOptions,
curve_data: curve.CurveData,
) -> Union[curve.FitOptions, List[curve.FitOptions]]:
"""Create algorithmic initial fit guess from analysis options and curve data.

Args:
user_opt: Fit options filled with user provided guess and bounds.
curve_data: Formatted data collection to fit.

Returns:
List of fit options that are passed to the fitter function.
"""
y_offset = curve.guess.constant_sinusoidal_offset(curve_data.y)

user_opt.bounds.set_if_empty(
amp=(0, 1),
freq_o1=(0, np.inf),
freq_o3=(-np.inf, np.inf),
offset=[-1, 1],
)
user_opt.p0.set_if_empty(
offset=y_offset,
)
user_opt.p0.set_if_empty(
freq_o1=curve.guess.frequency(curve_data.x, curve_data.y - user_opt.p0["offset"]),
freq_o3=0.0,
amp=curve.guess.max_height(curve_data.y - user_opt.p0["offset"], absolute=True)[0],
)

return user_opt

def _evaluate_quality(self, fit_data: curve.CurveFitResult) -> Union[str, None]:
"""Algorithmic criteria for whether the fit is good or bad.

A good fit has:
- more than a quarter of a full period,
- an error on the fit frequency lower than the fit frequency.
"""
fit_freq_o1 = fit_data.ufloat_params["freq_o1"]
fit_freq_o3 = fit_data.ufloat_params["freq_o3"]

criteria = [
curve.utils.is_error_not_significant(fit_freq_o1),
curve.utils.is_error_not_significant(fit_freq_o3),
]

if all(criteria):
return "good"

return "bad"
Loading