from dataclasses import dataclass, field
from typing import Optional
import numpy as np
import numpy.typing as npt
import plotly.graph_objects as go
from qibolab import AcquisitionType, AveragingMode, Parameter, Sweeper
from qibocal.auto.operation import Data, QubitId, Routine
from qibocal.calibration import CalibrationPlatform
from qibocal.result import probability
from ..utils import table_dict, table_html
from . import t1_signal, utils
[docs]@dataclass
class T1Parameters(t1_signal.T1SignalParameters):
"""T1 runcard inputs."""
[docs]@dataclass
class T1Results(t1_signal.T1SignalResults):
"""T1 outputs."""
chi2: Optional[dict[QubitId, list[float]]] = field(default_factory=dict)
"""Chi squared estimate mean value and error."""
CoherenceProbType = np.dtype(
[("wait", np.float64), ("prob", np.float64), ("error", np.float64)]
)
"""Custom dtype for coherence routines."""
[docs]@dataclass
class T1Data(Data):
"""T1 acquisition outputs."""
data: dict[QubitId, npt.NDArray] = field(default_factory=dict)
"""Raw data acquired."""
[docs]def _acquisition(
params: T1Parameters, platform: CalibrationPlatform, targets: list[QubitId]
) -> T1Data:
"""Data acquisition for T1 experiment."""
sequence, ro_pulses, delays = t1_signal.t1_sequence(
platform=platform, targets=targets
)
ro_wait_range = np.arange(
params.delay_before_readout_start,
params.delay_before_readout_end,
params.delay_before_readout_step,
)
sweeper = Sweeper(
parameter=Parameter.duration,
values=ro_wait_range,
pulses=[delays[q] for q in targets],
)
data = T1Data()
results = platform.execute(
[sequence],
[[sweeper]],
nshots=params.nshots,
relaxation_time=params.relaxation_time,
acquisition_type=AcquisitionType.DISCRIMINATION,
averaging_mode=AveragingMode.SINGLESHOT,
)
for qubit in targets:
probs = probability(results[ro_pulses[qubit].id], state=1)
errors = np.sqrt(probs * (1 - probs) / params.nshots)
data.register_qubit(
CoherenceProbType,
(qubit),
dict(wait=ro_wait_range, prob=probs, error=errors),
)
return data
[docs]def _fit(data: T1Data) -> T1Results:
"""
Fitting routine for T1 experiment. The used model is
.. math::
y = p_0-p_1 e^{-x p_2}.
"""
t1s, fitted_parameters, pcovs, chi2 = utils.exponential_fit_probability(data)
return T1Results(t1s, fitted_parameters, pcovs, chi2)
[docs]def _plot(data: T1Data, target: QubitId, fit: T1Results = None):
"""Plotting function for T1 experiment."""
figures = []
fitting_report = ""
qubit_data = data[target]
waits = qubit_data.wait
probs = qubit_data.prob
error_bars = qubit_data.error
fig = go.Figure(
[
go.Scatter(
x=waits,
y=probs,
opacity=1,
name="Probability of 1",
showlegend=True,
legendgroup="Probability of 1",
mode="lines",
),
go.Scatter(
x=np.concatenate((waits, waits[::-1])),
y=np.concatenate((probs + error_bars, (probs - error_bars)[::-1])),
fill="toself",
fillcolor=utils.COLORBAND,
line=dict(color=utils.COLORBAND_LINE),
showlegend=True,
name="Errors",
),
]
)
if fit is not None:
waitrange = np.linspace(
min(waits),
max(waits),
2 * len(qubit_data),
)
params = fit.fitted_parameters[target]
fig.add_trace(
go.Scatter(
x=waitrange,
y=utils.exp_decay(waitrange, *params),
name="Fit",
line=go.scatter.Line(dash="dot"),
)
)
fitting_report = table_html(
table_dict(
target,
[
"T1 [ns]",
"chi2 reduced",
],
[fit.t1[target], fit.chi2[target]],
display_error=True,
)
)
# last part
fig.update_layout(
showlegend=True,
xaxis_title="Time [ns]",
yaxis_title="Probability of State 1",
)
figures.append(fig)
return figures, fitting_report
t1 = Routine(_acquisition, _fit, _plot, t1_signal._update)
"""T1 Routine object."""