Source code for qibocal.protocols.coherence.t2_flux

from dataclasses import dataclass, field

import numpy as np
import plotly.graph_objects as go
from qibolab import AcquisitionType, AveragingMode, Parameter, Pulse, Sweeper

from qibocal.auto.operation import QubitId, Results, Routine
from qibocal.calibration import CalibrationPlatform

from ...config import log
from ..ramsey.utils import fitting, ramsey_sequence
from ..utils import COLORBAND, COLORBAND_LINE, HZ_TO_GHZ
from .t1_flux import T1FluxData, T1FluxParameters


[docs] @dataclass class T2FluxParameters(T1FluxParameters): """T2 flux runcard inputs."""
[docs] @dataclass class T2FluxResults(Results): """T2 flux outputs.""" t2: dict[QubitId, list[float]] = field(default_factory=dict) """List of T2 value for each detuning value."""
[docs] @dataclass class T2FluxData(T1FluxData): """T2 flux acquisition outputs."""
[docs] def _acquisition( params: T2FluxParameters, platform: CalibrationPlatform, targets: list[QubitId] ) -> T2FluxData: """Data acquisition for T2 flux experiment.""" sequence, pulses = ramsey_sequence( platform=platform, targets=targets, flux_pulse_amplitude=0.5 ) for qubit in targets: assert ( platform.calibration.single_qubits[qubit].qubit.flux_coefficients is not None ), f"Qubit {qubit} flux coefficients not set in calibration." sweeper_delay = Sweeper( parameter=Parameter.duration, values=params.delay_range, pulses=pulses, ) sweeper_amplitude = Sweeper( parameter=Parameter.amplitude, values=params.flux_range, pulses=[pulse for pulse in pulses if isinstance(pulse, Pulse)], ) _data = {} results = platform.execute( [sequence], [[sweeper_amplitude], [sweeper_delay]], nshots=params.nshots, relaxation_time=params.relaxation_time, acquisition_type=AcquisitionType.DISCRIMINATION, averaging_mode=AveragingMode.SINGLESHOT, ) for qubit in targets: ro_pulse = list(sequence.channel(platform.qubits[qubit].acquisition))[-1] _data[qubit] = results[ro_pulse.id] data = T2FluxData( data=_data, wait_range=params.delay_range.tolist(), flux_range=params.flux_range.tolist(), detuning={ qubit: ( platform.config(platform.qubits[qubit].drive).frequency * HZ_TO_GHZ + platform.calibration.single_qubits[qubit].qubit.detuning( params.flux_range ) ).tolist() for qubit in targets }, ) return data
[docs] def _fit(data: T2FluxData) -> T2FluxResults: """T2 flux fitting function. For each detuning value we compute the T2.""" t2s = {qubit: [] for qubit in data.qubits} for qubit in data.qubits: prob = data.probability(qubit) error = data.error(qubit) for i in range(len(data.detuning[qubit])): try: popt, perr = fitting(np.array(data.wait_range), prob[i], error[i]) t2 = 1 / popt[4] t2s[qubit].append([t2, perr[4] * (t2**2)]) except Exception as e: log.warning( f"T2 fitting failed for qubit {qubit} and amplitude {data.flux_range[i]} due to {e}." ) t2s[qubit].append(np.nan) return T2FluxResults(t2=t2s)
[docs] def _plot(data: T2FluxData, target: QubitId, fit: T2FluxResults = None): """Plotting function for T2 experiment.""" fig = go.Figure() if fit is not None: indices = list(set(np.where(np.array(fit.t2[target]) != np.nan)[0])) t2s = np.array([fit.t2[target][i][0] for i in indices]) error = np.array([fit.t2[target][i][1] for i in indices]) detuning = np.array(data.detuning[target])[indices] fig.add_traces( [ go.Scatter( x=detuning, y=t2s, opacity=1, name="T2", showlegend=True, legendgroup="T2", mode="lines", ), go.Scatter( x=np.concatenate((detuning, detuning[::-1])), y=np.concatenate((t2s + error, (t2s - error)[::-1])), fill="toself", fillcolor=COLORBAND, line=dict(color=COLORBAND_LINE), showlegend=True, name="Errors", ), ] ) fig.update_layout( xaxis_title="Frequency [GHz]", yaxis_title="T1 [ns]", yaxis=dict(range=[0, max(t2s) * 1.2]), xaxis=dict(range=[min(data.detuning[target]), max(data.detuning[target])]), ) return [fig], ""
t2_flux = Routine(_acquisition, _fit, _plot) """T2 Routine object."""