Skip to content

Commit

Permalink
Merge pull request #1021 from qiboteam/0.2-cal
Browse files Browse the repository at this point in the history
Adding calibration parameters to qibocal
  • Loading branch information
stavros11 authored Nov 7, 2024
2 parents d35ab6b + f4b7049 commit a698464
Show file tree
Hide file tree
Showing 22 changed files with 1,612 additions and 921 deletions.
1,976 changes: 1,127 additions & 849 deletions poetry.lock

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions src/qibocal/auto/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
from typing import Optional, Union

from qibo.backends import construct_backend
from qibolab import Platform, create_platform

from qibocal import protocols
from qibocal.config import log

from ..calibration import CalibrationPlatform, create_calibration_platform
from .history import History
from .mode import AUTOCALIBRATION, ExecutionMode
from .operation import Routine
Expand Down Expand Up @@ -71,7 +71,7 @@ class Executor:
"""The execution history, with results and exit states."""
targets: Targets
"""Qubits/Qubit Pairs to be calibrated."""
platform: Platform
platform: CalibrationPlatform
"""Qubits' platform."""
update: bool = True
"""Runcard update mechanism."""
Expand All @@ -98,12 +98,12 @@ def __post_init__(self):
_register(self.name, self)

@classmethod
def create(cls, name: str, platform: Union[Platform, str, None] = None):
def create(cls, name: str, platform: Union[CalibrationPlatform, str, None] = None):
"""Load list of protocols."""
platform = (
platform
if isinstance(platform, Platform)
else create_platform(
if isinstance(platform, CalibrationPlatform)
else create_calibration_platform(
platform
if platform is not None
else os.environ.get("QIBO_PLATFORM", "dummy")
Expand Down Expand Up @@ -251,7 +251,7 @@ def init(
self,
path: os.PathLike,
force: bool = False,
platform: Union[Platform, str, None] = None,
platform: Union[CalibrationPlatform, str, None] = None,
update: Optional[bool] = None,
targets: Optional[Targets] = None,
):
Expand All @@ -261,7 +261,7 @@ def init(

backend = construct_backend(backend="qibolab", platform=platform)
platform = self.platform = backend.platform
assert isinstance(platform, Platform)
assert isinstance(platform, CalibrationPlatform)

if update is not None:
self.update = update
Expand Down Expand Up @@ -310,7 +310,7 @@ def open(
name: str,
path: os.PathLike,
force: bool = False,
platform: Union[Platform, str, None] = None,
platform: Union[CalibrationPlatform, str, None] = None,
update: Optional[bool] = None,
targets: Optional[Targets] = None,
):
Expand Down
4 changes: 1 addition & 3 deletions src/qibocal/auto/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@

from qibocal.config import log

from ..calibration.calibration import QubitId, QubitPairId
from .serialize import deserialize, load, serialize

QubitId = Union[str, int]
QubitPairId = tuple[QubitId, QubitId]

OperationId = NewType("OperationId", str)
"""Identifier for a calibration routine."""
ParameterValue = Union[float, int]
Expand Down
8 changes: 4 additions & 4 deletions src/qibocal/auto/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from typing import Optional

from qibo.backends import construct_backend
from qibolab import Platform

from ..calibration import CalibrationPlatform
from ..config import log
from ..version import __version__
from .history import History
Expand Down Expand Up @@ -154,7 +154,7 @@ class Output:

history: History
meta: Metadata
platform: Optional[Platform] = None
platform: Optional[CalibrationPlatform] = None

@classmethod
def load(cls, path: Path):
Expand Down Expand Up @@ -201,7 +201,7 @@ def dump(self, path: Path):
self.update_platform(self.platform, path)

@staticmethod
def update_platform(platform: Platform, path: Path):
def update_platform(platform: CalibrationPlatform, path: Path):
"""Dump platform used.
If the original one is not defined, use the current one as the
Expand All @@ -212,7 +212,7 @@ def update_platform(platform: Platform, path: Path):
platpath = path / UPDATED_PLATFORM

platpath.mkdir(parents=True, exist_ok=True)
# dump_platform(platform, platpath)
platform.dump(platpath)

def _export_stats(self):
"""Export task statistics.
Expand Down
1 change: 1 addition & 0 deletions src/qibocal/calibration/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .platform import CalibrationPlatform, create_calibration_platform
171 changes: 171 additions & 0 deletions src/qibocal/calibration/calibration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
from pathlib import Path
from typing import Annotated, Optional, Union

from pydantic import BaseModel, BeforeValidator, ConfigDict, Field, PlainSerializer
from qibolab._core.serialize import NdArray

QubitId = Annotated[Union[int, str], Field(union_mode="left_to_right")]
"""Qubit name."""

QubitPairId = Annotated[
tuple[QubitId, QubitId],
BeforeValidator(lambda p: tuple(p.split("-")) if isinstance(p, str) else p),
PlainSerializer(lambda p: f"{p[0]}-{p[1]}"),
]
"""Qubit pair name."""

CALIBRATION = "calibration.json"
"""Calibration file."""

Measure = list[float]
"""Measured is represented as two values: mean and error."""


class Model(BaseModel):
"""Global model, holding common configurations."""

model_config = ConfigDict(extra="forbid")


class Resonator(Model):
"""Representation of resonator parameters."""

bare_frequency: Optional[float] = None
"""Bare resonator frequency [Hz]."""
dressed_frequency: Optional[float] = None
"""Dressed resonator frequency [Hz]."""
depletion_time: Optional[int] = None
"""Depletion time [ns]."""

@property
def dispersive_shift(self):
"""Dispersive shift."""
return self.bare_frequency - self.dressed_frequency

# TODO: Add setter for dispersive shift as well
# TODO: Add something related to resonator calibration


class Qubit(Model):
"""Representation of Qubit parameters"""

frequency_01: Optional[float] = None
""""0->1 transition frequency [Hz]."""
frequency_12: Optional[float] = None
"""1->2 transition frequency [Hz]."""
maximum_frequency: Optional[float] = None
"""Maximum transition frequency [Hz]."""
asymmetry: Optional[float] = None
"""Junctions asymmetry."""
sweetspot: Optional[float] = None
"""Qubit sweetspot [V]."""

@property
def anharmonicity(self):
"""Anharmonicity of the qubit [Hz]."""
return self.frequency_12 - self.frequency_01

@property
def charging_energy(self):
"""Charging energy Ec [Hz]."""
return -self.anharmonicity

@property
def josephson_energy(self):
"""Josephson energy [Hz].
The following formula is the inversion of the maximum frequency
obtained from the flux dependence protoco.
"""
return (
(self.maximum_frequency + self.charging_energy) ** 2
/ 8
/ self.charging_energy
)


class Readout(Model):
"""Readout parameters."""

fidelity: Optional[float] = None
"""Readout fidelity."""
coupling: Optional[float] = None
"""Readout coupling [Hz]."""
effective_temperature: Optional[float] = None
"""Qubit effective temperature."""
ground_state: list[float] = Field(default_factory=list)
"""Ground state position in IQ plane."""
excited_state: list[float] = Field(default_factory=list)
"""Excited state position in IQ plane."""

@property
def assignment_fidelity(self):
"""Assignment fidelity."""
return (1 + self.fidelity) / 2


class QubitCalibration(Model):
"""Container for calibration of single qubit."""

resonator: Resonator = Field(default_factory=Resonator)
"""Resonator calibration."""
qubit: Qubit = Field(default_factory=Qubit)
"""Qubit calibration."""
readout: Readout = Field(default_factory=Readout)
"""Readout information."""
t1: Optional[Measure] = None
"""Relaxation time [ns]."""
t2: Optional[Measure] = None
"""T2 of the qubit [ns]."""
t2_spin_echo: Optional[Measure] = None
"""T2 hanh echo [ns]."""
rb_fidelity: Optional[Measure] = None
"""Standard rb pulse fidelity."""


class TwoQubitCalibration(Model):
"""Container for calibration of qubit pair."""

rb_fidelity: Optional[Measure] = None
"""Two qubit standard rb fidelity."""
cz_fidelity: Optional[Measure] = None
"""CZ interleaved rb fidelity."""
coupling: Optional[float] = None
"""Qubit-qubit coupling."""


class Calibration(Model):
"""Calibration container."""

single_qubits: dict[QubitId, QubitCalibration] = Field(default_factory=dict)
"""Dict with single qubit calibration."""
two_qubits: dict[QubitPairId, TwoQubitCalibration] = Field(default_factory=dict)
"""Dict with qubit pairs calibration."""
readout_mitigation_matrix: Optional[NdArray] = None
"""Readout mitigation matrix."""
flux_crosstalk_matrix: Optional[NdArray] = None
"""Crosstalk flux matrix."""

def dump(self, path: Path):
"""Dump calibration model."""
(path / CALIBRATION).write_text(self.model_dump_json(indent=4))

@property
def qubits(self) -> list:
"""List of qubits available in the model."""
return list(self.single_qubits)

@property
def nqubits(self) -> int:
"""Number of qubits available."""
return len(self.qubits)

# TODO: add crosstalk object where I can do this
def get_crosstalk_element(self, qubit1: QubitId, qubit2: QubitId):
a, b = self.qubits.index(qubit1), self.qubits.index(qubit2)
return self.flux_crosstalk_matrix[a, b]

def set_crosstalk_element(self, qubit1: QubitId, qubit2: QubitId, value: float):
a, b = self.qubits.index(qubit1), self.qubits.index(qubit2)
self.flux_crosstalk_matrix[a, b] = value
Loading

0 comments on commit a698464

Please sign in to comment.