Source code for qibocal.protocols.coherence.spin_echo_signal

from dataclasses import dataclass
from typing import Union

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

from qibocal.auto.operation import 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 .t1_signal import T1SignalData
from .utils import (
    CoherenceType,
    dynamical_decoupling_sequence,
    exp_decay,
    exponential_fit,
)


[docs]@dataclass class SpinEchoSignalParameters(Parameters): """SpinEcho Signal runcard inputs.""" delay_between_pulses_start: int """Initial delay between pulses [ns].""" delay_between_pulses_end: int """Final delay between pulses [ns].""" delay_between_pulses_step: int """Step delay between pulses [ns].""" single_shot: bool = False
[docs]@dataclass class SpinEchoSignalResults(Results): """SpinEchoSignal outputs.""" t2: dict[QubitId, Union[float, list[float]]] """T2 echo for each qubit.""" fitted_parameters: dict[QubitId, dict[str, float]] """Raw fitting output.""" pcov: dict[QubitId, list[float]] """Approximate covariance of fitted parameters."""
[docs]class SpinEchoSignalData(T1SignalData): """SpinEcho acquisition outputs."""
[docs]def _acquisition( params: SpinEchoSignalParameters, platform: CalibrationPlatform, targets: list[QubitId], ) -> SpinEchoSignalData: """Data acquisition for SpinEcho""" # create a sequence of pulses for the experiment: sequence, delays = dynamical_decoupling_sequence(platform, targets, kind="CP") # define the parameter to sweep and its range: # delay between pulses wait_range = np.arange( params.delay_between_pulses_start, params.delay_between_pulses_end, params.delay_between_pulses_step, ) durations = [] for q in targets: # this is assuming that RX and RX90 have the same duration duration = platform.natives.single_qubit[q].RX()[0][1].duration durations.append(duration) assert (params.delay_between_pulses_start - duration) / 2 >= 0, ( f"Initial delay too short for qubit {q}, minimum delay should be {duration}" ) assert len(set(durations)) == 1, ( "Cannot run on mulitple qubit with different RX duration." ) sweeper = Sweeper( parameter=Parameter.duration, values=(wait_range - durations[0]) / 2, pulses=delays, ) 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 = SpinEchoSignalData() for qubit in targets: ro_pulse = list(sequence.channel(platform.qubits[qubit].acquisition))[-1] result = results[ro_pulse.id] signal = magnitude(result) if params.single_shot: _wait = np.array(len(signal) * [wait_range]) else: _wait = wait_range data.register_qubit( CoherenceType, (qubit), dict( wait=_wait, signal=signal, phase=phase(result), ), ) return data
[docs]def _fit(data: SpinEchoSignalData) -> SpinEchoSignalResults: """Post-processing for SpinEcho.""" data = data.average t2echos, fitted_parameters, pcov = exponential_fit(data) return SpinEchoSignalResults(t2echos, fitted_parameters, pcov)
[docs]def _plot(data: SpinEchoSignalData, target: QubitId, fit: SpinEchoSignalResults = None): """Plotting for SpinEcho""" data = data.average figures = [] fig = go.Figure() # iterate over multiple data folders 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: # add fitting trace waitrange = np.linspace( min(waits), max(waits), 2 * len(qubit_data), ) params = fit.fitted_parameters[target] fig.add_trace( go.Scatter( x=waitrange, y=exp_decay(waitrange, *params), name="Fit", line=go.scatter.Line(dash="dot"), ), ) fitting_report = table_html( table_dict( target, ["T2 Spin Echo [ns]"], [np.round(fit.t2[target])], display_error=True, ) ) 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: SpinEchoSignalResults, platform: CalibrationPlatform, target: QubitId ): update.t2_spin_echo(results.t2[target], platform, target)
spin_echo_signal = Routine(_acquisition, _fit, _plot, _update) """SpinEcho Routine object."""