Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
9bb60ae
* Implemented method to setup single-qubit calibrations.
eggerdj Jun 17, 2021
6b83656
Merge branch 'main' into calibrations_setup
eggerdj Jun 21, 2021
9b16df9
Merge branch 'main' into calibrations_setup
eggerdj Jul 21, 2021
f973582
* Refactored cals setup to library style.
eggerdj Jul 21, 2021
262f2a4
* Fix sigma in test.
eggerdj Jul 21, 2021
319de19
Merge branch 'main' into calibrations_setup
eggerdj Jul 21, 2021
894794a
Update qiskit_experiments/calibration_management/basis_gate_library.py
eggerdj Jul 21, 2021
97524de
Merge branch 'main' into calibrations_setup
eggerdj Jul 21, 2021
d10d155
* Made library options kwargs.
eggerdj Jul 21, 2021
59bde3e
* Added error raising.
eggerdj Jul 21, 2021
b33f73b
* Added basis gates property.
eggerdj Jul 21, 2021
58acc43
* Renamed transmon library.
eggerdj Jul 21, 2021
c5962fe
* Docs.
eggerdj Jul 21, 2021
4ae1763
* Added better Drag optionality.
eggerdj Jul 21, 2021
654d017
* Added y and sy gates to library.
eggerdj Jul 21, 2021
1dadebe
* Add check to make sure we do not add default values for gates that …
eggerdj Jul 21, 2021
577394b
Update qiskit_experiments/calibration_management/basis_gate_library.py
eggerdj Jul 22, 2021
84ee237
Merge branch 'main' into calibrations_setup
eggerdj Jul 22, 2021
529d3b1
* Docs.
eggerdj Jul 22, 2021
977122c
Merge branch 'calibrations_setup' of github.com:eggerdj/qiskit-experi…
eggerdj Jul 22, 2021
824bb44
* Update the way the library is passed to BackendCalibrations.
eggerdj Jul 22, 2021
332f8a4
* Lint.
eggerdj Jul 22, 2021
fd4b286
Merge branch 'main' into calibrations_setup
eggerdj Jul 22, 2021
8282ee0
* Added option to link parameters.
eggerdj Jul 22, 2021
86c3e9a
Merge branch 'calibrations_setup' of github.com:eggerdj/qiskit-experi…
eggerdj Jul 22, 2021
d3c0b69
* Partial generation of basis gates.
eggerdj Jul 22, 2021
4ca85e4
* Added test.
eggerdj Jul 22, 2021
58fad06
Merge branch 'main' into calibrations_setup
eggerdj Jul 22, 2021
a457f1a
* Fixed test issue and added in operator.
eggerdj Jul 22, 2021
3ac1e08
Merge branch 'calibrations_setup' of github.com:eggerdj/qiskit-experi…
eggerdj Jul 22, 2021
9e478b2
Merge branch 'main' into calibrations_setup
eggerdj Jul 23, 2021
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
46 changes: 43 additions & 3 deletions qiskit_experiments/calibration_management/backend_calibrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@

from datetime import datetime
from enum import Enum
from typing import List
from typing import List, Optional
import copy

from qiskit.providers.backend import BackendV1 as Backend
from qiskit.circuit import Parameter
from qiskit_experiments.calibration_management.calibrations import Calibrations, ParameterKey
from qiskit_experiments.exceptions import CalibrationError
from qiskit_experiments.calibration_management.basis_gate_library import BasisGateLibrary


class FrequencyElement(Enum):
Expand All @@ -43,8 +44,33 @@ class BackendCalibrations(Calibrations):
__qubit_freq_parameter__ = "qubit_lo_freq"
__readout_freq_parameter__ = "meas_lo_freq"

def __init__(self, backend: Backend):
"""Setup an instance to manage the calibrations of a backend."""
def __init__(
self,
backend: Backend,
basis_gates: Optional[List[str]] = None,
library: BasisGateLibrary = None,
Comment thread
eggerdj marked this conversation as resolved.
Outdated
):
"""Setup an instance to manage the calibrations of a backend.

BackendCalibrations can be initialized from a basis gate library, i.e. a subclass of
:class:`BasisGateLibrary`. As example consider the following code:

.. code-block:: python

cals = BackendCalibrations(
backend,
basis_gates=["x", "sx"],
library=FixedFrequencyTransmon(default_values={duration: 320}),
)

Args:
backend: A backend instance from which to extract the qubit and readout frequencies
(which will be added as first guesses for the corresponding parameters) as well
as the coupling map.
basis_gates: The basis gates to extract from the library and add to the calibrations.
library: A library class that will be instantiated with the library options to then
get template schedules to register as well as default parameter values.
"""
if hasattr(backend.configuration(), "control_channels"):
control_channels = backend.configuration().control_channels
else:
Expand All @@ -67,6 +93,20 @@ def __init__(self, backend: Backend):
for meas, freq in enumerate(backend.defaults().meas_freq_est):
self.add_parameter_value(freq, self.meas_freq, meas)

basis_gates = basis_gates or list()

if library is not None:

# Add the basis gates
for gate in basis_gates:
Comment thread
eggerdj marked this conversation as resolved.
Outdated
self.add_schedule(library[gate])

# Add the default values
for param_conf in library.default_values():
schedule_name = param_conf[-1]
if schedule_name in basis_gates:
self.add_parameter_value(*param_conf)

def _get_frequencies(
self,
element: FrequencyElement,
Expand Down
158 changes: 158 additions & 0 deletions qiskit_experiments/calibration_management/basis_gate_library.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# 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.

"""
A collections of libraries to setup Calibrations.

Note that the set of available libraries will be extended in future releases.
"""

from abc import ABC, abstractmethod
from typing import Dict, List, Optional, Tuple

from qiskit.circuit import Parameter
import qiskit.pulse as pulse
from qiskit.pulse import ScheduleBlock

from qiskit_experiments.calibration_management.calibration_key_types import ParameterValueType
from qiskit_experiments.exceptions import CalibrationError


class BasisGateLibrary(ABC):
Comment thread
eggerdj marked this conversation as resolved.
"""A base class for libraries of basis gates to make it easier to setup Calibrations."""
Comment thread
eggerdj marked this conversation as resolved.

# Location where default parameter values are stored. These may be updated at construction.
__default_values__ = dict()

def __init__(self, default_values: Optional[Dict] = None):
"""Setup the library.

Args:
default_values: A dictionary to override library default parameter values.
"""
self._schedules = dict()

if default_values is not None:
self.__default_values__.update(default_values)

def __getitem__(self, name: str) -> ScheduleBlock:
"""Return the schedule."""
if name not in self._schedules:
raise CalibrationError(f"Gate {name} is not contained in {self.__class__.__name__}.")
Comment thread
eggerdj marked this conversation as resolved.

return self._schedules[name]
Comment thread
eggerdj marked this conversation as resolved.

@property
def basis_gates(self) -> List[str]:
"""Return the basis gates supported by the library."""
return list(name for name in self._schedules)
Comment thread
eggerdj marked this conversation as resolved.

@abstractmethod
def default_values(self) -> List[Tuple[ParameterValueType, Parameter, Tuple, ScheduleBlock]]:
"""Return the default values for the parameters.

Returns
A list of tuples is returned. These tuples are structured so that instances of
:class:`Calibrations` can call :meth:`add_parameter_value` on the tuples.
"""


class FixedFrequencyTransmon(BasisGateLibrary):
"""A library of gates for fixed-frequency superconducting qubit architectures.

Note that for now this library supports single-qubit gates and will be extended
in the future.
"""

__default_values__ = {"duration": 160, "amp": 0.5, "β": 0.0}

def __init__(self, default_values: Optional[Dict] = None, use_drag: bool = True):

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.

perhaps you can explicitly write duration_default, amp_default, beta_default here.

"""Setup the schedules.

Args:
default_values: Default values for the parameters this dictionary can contain
the following keys: "duration", "amp", "β", and "σ". If "σ" is not provided
this library will take one fourth of the pulse duration as default value.
use_drag: If set to False then Gaussian pulses will be used instead of DRAG
pulses.
"""
super().__init__(default_values)

dur = Parameter("duration")
sigma = Parameter("σ")

def _beta(use_drag):
return Parameter("β") if use_drag else None

# X gates
sched_x = self._single_qubit_schedule("x", dur, Parameter("amp"), sigma, _beta(use_drag))

# Y gates
sched_y = self._single_qubit_schedule("y", dur, Parameter("amp"), sigma, _beta(use_drag))

# square-root X gates
sched_sx = self._single_qubit_schedule("sx", dur, Parameter("amp"), sigma, _beta(use_drag))

# square-root Y gates
sched_sy = self._single_qubit_schedule("sy", dur, Parameter("amp"), sigma, _beta(use_drag))
Comment thread
eggerdj marked this conversation as resolved.
Outdated

for sched in [sched_x, sched_y, sched_sx, sched_sy]:
self._schedules[sched.name] = sched
Comment thread
eggerdj marked this conversation as resolved.
Outdated

@staticmethod
def _single_qubit_schedule(
name: str,
dur: Parameter,
amp: Parameter,
sigma: Parameter,
beta: Optional[Parameter] = None,
) -> ScheduleBlock:
"""Build a single qubit pulse."""

chan = pulse.DriveChannel(Parameter("ch0"))

if beta is not None:
Comment thread
eggerdj marked this conversation as resolved.
with pulse.build(name=name) as sched:
pulse.play(pulse.Drag(duration=dur, amp=amp, sigma=sigma, beta=beta), chan)
else:
with pulse.build(name=name) as sched:
pulse.play(pulse.Gaussian(duration=dur, amp=amp, sigma=sigma), chan)

return sched

def default_values(self) -> List[Tuple[ParameterValueType, Parameter, Tuple, ScheduleBlock]]:
"""Return the default values for the parameters.

Returns
A list of tuples is returned. These tuples are structured so that instances of
:class:`Calibrations` can call :meth:`add_parameter_value` on the tuples.
"""
defaults = []
for name in self.basis_gates:
schedule = self._schedules[name]
for param in schedule.parameters:
if "ch" not in param.name:

if param.name == "σ" and "σ" not in self.__default_values__:
value = self.__default_values__["duration"] / 4
else:
value = self.__default_values__[param.name]

if name in {"sx", "sy"} and param.name == "amp":
value /= 2.0

if "y" in name and param.name == "amp":
value *= 1.0j

defaults.append((value, param.name, tuple(), name))

return defaults
5 changes: 4 additions & 1 deletion qiskit_experiments/calibration_management/update_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,10 @@ def update(
rate = 2 * np.pi * freq

for angle, param, schedule in angles_schedules:
value = np.round(angle / rate, decimals=8)
qubits = exp_data.data(0)["metadata"]["qubits"]
prev_amp = calibrations.get_parameter_value(param, qubits, schedule, group=group)

value = np.round(angle / rate, decimals=8) * np.exp(1.0j * np.angle(prev_amp))

cls._add_parameter_value(calibrations, exp_data, value, param, schedule, group)

Expand Down
24 changes: 24 additions & 0 deletions test/calibration/test_backend_calibrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@

"""Class to test the backend calibrations."""

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

from qiskit_experiments.calibration_management import BackendCalibrations
from qiskit_experiments.calibration_management.basis_gate_library import FixedFrequencyTransmon


class TestBackendCalibrations(QiskitTestCase):
Expand All @@ -26,3 +29,24 @@ def test_run_options(self):

self.assertEqual(cals.get_meas_frequencies(), [6993370669.000001])
self.assertEqual(cals.get_qubit_frequencies(), [4971852852.405576])

def test_setup_withLibrary(self):
"""Test that we can setup with a library."""

cals = BackendCalibrations(
FakeArmonk(),
basis_gates=["x", "sx"],
library=FixedFrequencyTransmon(default_values={"duration": 320}),
)

# Check the x gate
with pulse.build(name="x") as expected:
pulse.play(pulse.Drag(duration=320, amp=0.5, sigma=80, beta=0), pulse.DriveChannel(0))

self.assertEqual(cals.get_schedule("x", (0,)), expected)

# Check the sx gate
with pulse.build(name="sx") as expected:
pulse.play(pulse.Drag(duration=320, amp=0.25, sigma=80, beta=0), pulse.DriveChannel(0))

self.assertEqual(cals.get_schedule("sx", (0,)), expected)
77 changes: 77 additions & 0 deletions test/calibration/test_setup_library.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# 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.

"""Class to test the calibrations setup methods."""

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

from qiskit_experiments.calibration_management.basis_gate_library import FixedFrequencyTransmon
from qiskit_experiments.exceptions import CalibrationError


class TestFixedFrequencyTransmon(QiskitTestCase):
"""Test the various setup methods."""

def test_standard_single_qubit_gates(self):
"""Test the setup of single-qubit gates."""

library = FixedFrequencyTransmon(default_values={"duration": 320})

for gate in ["x", "sx"]:
sched = library[gate]
self.assertTrue(isinstance(sched, pulse.ScheduleBlock))
self.assertEqual(len(sched.parameters), 5)

sched_x = library["x"]
sched_sx = library["sx"]

self.assertEqual(sched_x.blocks[0].pulse.duration, sched_sx.blocks[0].pulse.duration)
self.assertEqual(sched_x.blocks[0].pulse.sigma, sched_sx.blocks[0].pulse.sigma)

expected = [
(0.5, "amp", (), "x"),
(0.0, "β", (), "x"),
(320, "duration", (), "x"),
(80, "σ", (), "x"),
(320, "duration", (), "sx"),
(0.0, "β", (), "sx"),
(0.25, "amp", (), "sx"),
(80, "σ", (), "sx"),
(0.5j, "amp", (), "y"),
(0.0, "β", (), "y"),
(320, "duration", (), "y"),
(80, "σ", (), "y"),
(320, "duration", (), "sy"),
(0.0, "β", (), "sy"),
(0.25j, "amp", (), "sy"),
(80, "σ", (), "sy"),
]

for param_conf in library.default_values():
self.assertTrue(param_conf in expected)

# Check that an error gets raise if the gate is not in the library.
with self.assertRaises(CalibrationError):
print(library["bswap"])

# Test the basis gates of the library.
self.assertListEqual(library.basis_gates, ["x", "y", "sx", "sy"])

def test_turn_off_drag(self):
"""Test the use_drag parameter."""

library = FixedFrequencyTransmon(use_drag=False)
self.assertTrue(isinstance(library["x"].blocks[0].pulse, pulse.Gaussian))

library = FixedFrequencyTransmon()
self.assertTrue(isinstance(library["x"].blocks[0].pulse, pulse.Drag))
Loading