Source code for qibocal.protocols.coherence.t1_signal

from dataclasses import dataclass, field
from typing import Union

import numpy as np
import numpy.typing as npt
import plotly.graph_objects as go
from qibolab import (
    AcquisitionType,
    AveragingMode,
    Delay,
    Parameter,
    PulseSequence,
    Sweeper,
)

from qibocal.auto.operation import Data, Parameters, QubitId, Results, Routine
from qibocal.calibration import CalibrationPlatform
from qibocal.result import magnitude, phase

from ... import update
from ..utils import readout_frequency, table_dict, table_html
from . import utils


[docs]@dataclass class T1SignalParameters(Parameters): """T1 runcard inputs.""" delay_before_readout_start: int """Initial delay before readout [ns].""" delay_before_readout_end: int """Final delay before readout [ns].""" delay_before_readout_step: int """Step delay before readout [ns].""" single_shot: bool = False """If ``True`` save single shot signal data."""
[docs]@dataclass class T1SignalResults(Results): """T1 Signal outputs.""" t1: dict[QubitId, Union[float, list[float]]] """T1 for each qubit.""" fitted_parameters: dict[QubitId, dict[str, float]] """Raw fitting output.""" pcov: dict[QubitId, list[float]] """Approximate covariance of fitted parameters."""
[docs]@dataclass class T1SignalData(Data): """T1 acquisition outputs.""" data: dict[QubitId, npt.NDArray] = field(default_factory=dict) """Raw data acquired.""" @property def average(self): if len(next(iter(self.data.values())).shape) > 1: return utils.average_single_shots(self.__class__, self.data) return self
[docs]def t1_sequence(platform: CalibrationPlatform, targets: list[QubitId]): """Create sequence for T1 experiment with a given optional delay.""" sequence = PulseSequence() ro_pulses, delays = {}, {} for q in targets: natives = platform.natives.single_qubit[q] qd_channel, qd_pulse = natives.RX()[0] ro_channel, ro_pulse = natives.MZ()[0] ro_pulses[q] = ro_pulse delays[q] = Delay(duration=0) sequence.append((qd_channel, qd_pulse)) sequence.append((ro_channel, Delay(duration=qd_pulse.duration))) sequence.append((ro_channel, delays[q])) sequence.append((ro_channel, ro_pulse)) return sequence, ro_pulses, delays
[docs]def _acquisition( params: T1SignalParameters, platform: CalibrationPlatform, targets: list[QubitId] ) -> T1SignalData: """Data acquisition for T1 experiment. In this protocol the y axis is the magnitude of signal in the IQ plane.""" sequence, ro_pulses, delays = t1_sequence(platform, 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], ) results = platform.execute( [sequence], [[sweeper]], updates=[ {platform.qubits[q].probe: {"frequency": readout_frequency(q, platform)}} for q in targets ], nshots=params.nshots, relaxation_time=params.relaxation_time, acquisition_type=AcquisitionType.INTEGRATION, averaging_mode=( AveragingMode.SINGLESHOT if params.single_shot else AveragingMode.CYCLIC ), ) data = T1SignalData() for q in targets: result = results[ro_pulses[q].id] signal = magnitude(result) if params.single_shot: _waits = np.array(len(signal) * [ro_wait_range]) else: _waits = ro_wait_range data.register_qubit( utils.CoherenceType, (q), dict(wait=_waits, signal=signal, phase=phase(result)), ) return data
[docs]def _fit(data: T1SignalData) -> T1SignalResults: """ Fitting routine for T1 experiment. The used model is .. math:: y = p_0-p_1 e^{-x p_2}. """ data = data.average t1s, fitted_parameters, pcovs = utils.exponential_fit(data) return T1SignalResults(t1s, fitted_parameters, pcovs)
[docs]def _plot(data: T1SignalData, target: QubitId, fit: T1SignalResults = None): """Plotting function for T1 experiment.""" data = data.average figures = [] fig = go.Figure() fitting_report = None qubit_data = data[target] waits = qubit_data.wait fig.add_trace( go.Scatter( x=waits, y=qubit_data.signal, opacity=1, name="Signal", showlegend=True, legendgroup="Signal", ) ) 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]"], [np.round(fit.t1[target])], display_error=True ) ) # last part fig.update_layout( showlegend=True, xaxis_title="Time [ns]", yaxis_title="Signal [a.u.]", ) figures.append(fig) return figures, fitting_report
[docs]def _update(results: T1SignalResults, platform: CalibrationPlatform, target: QubitId): update.t1(results.t1[target], platform, target)
t1_signal = Routine(_acquisition, _fit, _plot, _update) """T1 Signal Routine object."""