Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def calibrations(self) -> Calibrations:

@classmethod
def _default_experiment_options(cls):
"""Default values for the fine amplitude calibration experiment.
"""Default values for a calibration experiment.

Experiment Options:
result_index (int): The index of the result from which to update the calibrations.
Expand Down
6 changes: 4 additions & 2 deletions qiskit_experiments/library/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@
~characterization.FineSXAmplitude
~characterization.Rabi
~characterization.EFRabi
~characterization.RamseyXY


.. _calibration:

Expand All @@ -93,7 +95,6 @@ class instance to manage parameters and pulse schedules.
~calibration.FineAmplitudeCal
~calibration.FineXAmplitudeCal
~calibration.FineSXAmplitudeCal
~calibration.RamseyXY
~calibration.RoughAmplitudeCal
~calibration.RoughXSXAmplitudeCal
~calibration.EFRoughXSXAmplitudeCal
Expand All @@ -111,7 +112,7 @@ class instance to manage parameters and pulse schedules.
FineXAmplitudeCal,
FineSXAmplitudeCal,
RoughFrequencyCal,
RamseyXY,
FrequencyCal,
)
from .characterization import (
T1,
Expand All @@ -127,6 +128,7 @@ class instance to manage parameters and pulse schedules.
FineAmplitude,
FineXAmplitude,
FineSXAmplitude,
RamseyXY,
)
from .randomized_benchmarking import StandardRB, InterleavedRB
from .tomography import StateTomography, ProcessTomography
Expand Down
4 changes: 2 additions & 2 deletions qiskit_experiments/library/calibration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@
:template: autosummary/experiment.rst

RoughFrequencyCal
FrequencyCal
RoughDragCal
FineDrag
FineXDrag
FineSXDrag
FineAmplitudeCal
FineXAmplitudeCal
FineSXAmplitudeCal
RamseyXY
RoughAmplitudeCal
RoughXSXAmplitudeCal
EFRoughXSXAmplitudeCal
Expand All @@ -74,7 +74,7 @@
from .fine_drag import FineDrag, FineXDrag, FineSXDrag
from .rough_amplitude_cal import RoughAmplitudeCal, RoughXSXAmplitudeCal, EFRoughXSXAmplitudeCal
from .fine_amplitude import FineAmplitudeCal, FineXAmplitudeCal, FineSXAmplitudeCal
from .ramsey_xy import RamseyXY
from .frequency_cal import FrequencyCal

from .analysis.drag_analysis import DragCalAnalysis
from .analysis.fine_drag_analysis import FineDragAnalysis
Expand Down
2 changes: 0 additions & 2 deletions qiskit_experiments/library/calibration/fine_amplitude.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,6 @@ def _add_cal_metadata(self, circuits: List[QuantumCircuit]):
circuit.metadata["target_angle"] = self.experiment_options.target_angle
circuit.metadata["cal_group"] = self.experiment_options.group

return circuits

def update_calibrations(self, experiment_data: ExperimentData):
r"""Update the amplitude of the pulse in the calibrations.

Expand Down
110 changes: 110 additions & 0 deletions qiskit_experiments/library/calibration/frequency_cal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# 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.

"""Ramsey XY frequency calibration experiment."""

from typing import List, Optional

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

from qiskit_experiments.framework import ExperimentData, fix_class_docs
from qiskit_experiments.library.characterization.ramsey_xy import RamseyXY
from qiskit_experiments.calibration_management.backend_calibrations import BackendCalibrations
from qiskit_experiments.calibration_management.update_library import BaseUpdater
from qiskit_experiments.calibration_management.base_calibration_experiment import (
BaseCalibrationExperiment,
)


@fix_class_docs
class FrequencyCal(BaseCalibrationExperiment, RamseyXY):
"""A qubit frequency calibration experiment based on the Ramsey XY experiment.

# section: see_also
qiskit_experiments.library.characterization.ramsey_xy.RamseyXY
"""

def __init__(
self,
qubit: int,
calibrations: BackendCalibrations,
backend: Optional[Backend] = None,
delays: Optional[List] = None,
unit: str = "s",
osc_freq: float = 2e6,
auto_update: bool = True,
):
"""
Args:
qubit: The qubit on which to run the frequency calibration.
calibrations: The calibrations instance with the schedules.
backend: Optional, the backend to run the experiment on.
delays: The list of delays that will be scanned in the experiment.
unit: The unit of the delays. Accepted values are dt, i.e. the
duration of a single sample on the backend, seconds, and sub-units,
e.g. ms, us, ns.
osc_freq: A frequency shift in Hz that will be applied by means of
a virtual Z rotation to increase the frequency of the measured oscillation.
auto_update: If set to True, which is the default, then the experiment will
automatically update the frequency in the calibrations.
"""
super().__init__(
calibrations,
qubit,
backend=backend,
delays=delays,
unit=unit,
osc_freq=osc_freq,
cal_parameter_name="qubit_lo_freq",
auto_update=auto_update,
)

# Instruction schedule map to bring in the calibrations for the sx gate.
self.set_transpile_options(inst_map=calibrations.default_inst_map)

def _add_cal_metadata(self, circuits: List[QuantumCircuit]):
"""Add the oscillation frequency of the experiment to the metadata."""

param_val = self._cals.get_parameter_value(
self._param_name,
self.physical_qubits,
group=self.experiment_options.group,
)

for circuit in circuits:
circuit.metadata["cal_param_value"] = param_val
circuit.metadata["cal_group"] = self.experiment_options.group
circuit.metadata["osc_freq"] = self.experiment_options.osc_freq

def update_calibrations(self, experiment_data: ExperimentData):
"""Update the frequency using the reported frequency less the imparted oscillation."""

data = experiment_data.data()

# No data -> no update
if len(data) > 0:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the analysis_callback, are there still cases where data is empty?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely sure. In any case its good to protect against empty data to avoid things like IndexErrors.

result_index = self.experiment_options.result_index
osc_freq = data[0]["metadata"]["osc_freq"]
group = data[0]["metadata"]["cal_group"]
old_freq = data[0]["metadata"]["cal_param_value"]

fit_freq = BaseUpdater.get_value(experiment_data, "freq", result_index)
new_freq = old_freq + fit_freq - osc_freq
Comment thread
nkanazawa1989 marked this conversation as resolved.

BaseUpdater.add_parameter_value(
self._cals,
experiment_data,
new_freq,
self._param_name,
group=group,
)
2 changes: 2 additions & 0 deletions qiskit_experiments/library/characterization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
FineAmplitude
FineXAmplitude
FineSXAmplitude
RamseyXY
RoughDrag


Expand Down Expand Up @@ -61,4 +62,5 @@
from .rabi import Rabi, EFRabi
from .half_angle import HalfAngle
from .fine_amplitude import FineAmplitude, FineXAmplitude, FineSXAmplitude
from .ramsey_xy import RamseyXY
from .drag import RoughDrag
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Ramsey XY frequency calibration experiment."""
"""Ramsey XY frequency characterization experiment."""

from typing import List, Optional
import numpy as np
Expand Down Expand Up @@ -88,7 +88,6 @@ def _default_experiment_options(cls):
"""Default values for the Ramsey XY experiment.

Experiment Options:
schedule (ScheduleBlock): The schedule for the sx gate.
delays (list): The list of delays that will be scanned in the experiment.
unit (str): The unit of the delays. Accepted values are dt, i.e. the
duration of a single sample on the backend, seconds, and sub-units,
Expand All @@ -97,7 +96,6 @@ def _default_experiment_options(cls):
a virtual Z rotation to increase the frequency of the measured oscillation.
"""
options = super()._default_experiment_options()
options.schedule = None
options.delays = np.linspace(0, 1.0e-6, 51)
options.unit = "s"
options.osc_freq = 2e6
Expand Down Expand Up @@ -130,13 +128,13 @@ def __init__(
def _pre_circuit(self) -> QuantumCircuit:
"""Return a preparation circuit.

This method can be overridden by subclasses e.g. to calibrate schedules on
transitions other than the 0 <-> 1 transition.
This method can be overridden by subclasses e.g. to run on transitions other
than the 0 <-> 1 transition.
"""
return QuantumCircuit(1)

def circuits(self) -> List[QuantumCircuit]:
"""Create the circuits for the Ramsey XY calibration experiment.
"""Create the circuits for the Ramsey XY characterization experiment.

Returns:
A list of circuits with a variable delay.
Expand Down Expand Up @@ -187,12 +185,6 @@ def circuits(self) -> List[QuantumCircuit]:
ram_y.measure_active()
ram_y.metadata = metadata.copy()

# Add the schedule if any.
schedule = self.experiment_options.schedule
if schedule is not None:
for circ in [ram_x, ram_y]:
circ.add_calibration("sx", self.physical_qubits, schedule)

circs = []
for delay in self.experiment_options.delays:

Expand Down
42 changes: 38 additions & 4 deletions test/calibration/experiments/test_ramsey_xy.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,27 @@
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Test Ramsey XY calibration experiment."""
"""Test Ramsey XY experiments."""

from qiskit.test import QiskitTestCase
import qiskit.pulse as pulse
from qiskit.test.mock import FakeArmonk

from qiskit_experiments.library import RamseyXY
from qiskit_experiments.calibration_management.backend_calibrations import BackendCalibrations
from qiskit_experiments.calibration_management.basis_gate_library import FixedFrequencyTransmon
from qiskit_experiments.library import RamseyXY, FrequencyCal
from qiskit_experiments.test.mock_iq_backend import MockRamseyXY


class TestRamseyXY(QiskitTestCase):
"""Tests for the Ramsey XY experiment."""

def setUp(self):
"""Initialize some cals."""
super().setUp()

library = FixedFrequencyTransmon()
self.cals = BackendCalibrations(FakeArmonk(), library)

def test_end_to_end(self):
"""Test that we can run on a mock backend and perform a fit.

Expand All @@ -31,17 +40,42 @@ def test_end_to_end(self):
test_tol = 0.01

ramsey = RamseyXY(0)
ramsey.set_experiment_options(schedule=pulse.ScheduleBlock())

for freq_shift in [2e6, -3e6]:
test_data = ramsey.run(MockRamseyXY(freq_shift=freq_shift)).block_for_results()
meas_shift = test_data.analysis_results(1).value.value
self.assertTrue((meas_shift - freq_shift) < abs(test_tol * freq_shift))

def test_update_calibrations(self):
"""Test that the calibration version of the experiment updates the cals."""

tol = 1e4 # 10 kHz resolution

# Check qubit frequency before running the cal
f01 = self.cals.get_parameter_value("qubit_lo_freq", 0)
self.assertTrue(len(self.cals.parameters_table(parameters=["qubit_lo_freq"])["data"]), 1)
self.assertEqual(f01, FakeArmonk().defaults().qubit_freq_est[0])

freq_shift = 4e6
osc_shift = 2e6
backend = MockRamseyXY(freq_shift=freq_shift + osc_shift) # oscillation with 6 MHz
FrequencyCal(0, self.cals, backend, osc_freq=osc_shift).run().block_for_results()

# Check that qubit frequency after running the cal is shifted by freq_shift, i.e. 4 MHz.
f01 = self.cals.get_parameter_value("qubit_lo_freq", 0)
self.assertTrue(len(self.cals.parameters_table(parameters=["qubit_lo_freq"])["data"]), 2)
self.assertTrue(abs(f01 - (freq_shift + FakeArmonk().defaults().qubit_freq_est[0])) < tol)

def test_experiment_config(self):
"""Test converting to and from config works"""
exp = RamseyXY(0)
config = exp.config
loaded_exp = RamseyXY.from_config(config)
self.assertNotEqual(exp, loaded_exp)
self.assertEqual(config, loaded_exp.config)

exp = FrequencyCal(0, self.cals)
config = exp.config
loaded_exp = FrequencyCal.from_config(config)
self.assertNotEqual(exp, loaded_exp)
self.assertEqual(config, loaded_exp.config)