Source code for qibocal.protocols.flux_dependence.flux_gate

"""FluxGate experiment, implementation of Z gate using flux pulse."""

from dataclasses import dataclass, field

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

from qibocal.auto.operation import Data, Parameters, QubitId, Results, Routine

from ...result import probability
from ..ramsey.utils import fitting, ramsey_fit
from ..utils import COLORBAND, COLORBAND_LINE, GHZ_TO_HZ, table_dict, table_html

__all__ = ["flux_gate"]


[docs] @dataclass class FluxGateParameters(Parameters): """FluxGate runcard inputs.""" duration_min: float """Minimum flux pulse duration.""" duration_max: float """Maximum flux duration start.""" duration_step: float """Flux pulse duration step.""" flux_pulse_amplitude: float """Flux pulse amplitude."""
@dataclass class FluxGateResults(Results): """FluxGate outputs.""" detuning: dict[QubitId, float] = field(default_factory=dict) """Detuning for every qubit.""" fitted_parameters: dict[QubitId, list[float]] = field(default_factory=dict) """Fitted parameters for every qubit.""" FluxGateType = np.dtype( [("duration", float), ("prob_1", np.float64), ("error", np.float64)] ) """Custom dtype for FluxGate.""" @dataclass class FluxGateData(Data): """FluxGate acquisition outputs.""" flux_pulse_amplitude: float """Flux pulse amplitude.""" data: dict[tuple[QubitId, str], npt.NDArray[FluxGateType]] = field( default_factory=dict ) def _acquisition( params: FluxGateParameters, platform: Platform, targets: list[QubitId], ) -> FluxGateData: data = FluxGateData( flux_pulse_amplitude=params.flux_pulse_amplitude, ) duration_range = np.arange( params.duration_min, params.duration_max, params.duration_step ) sequence = PulseSequence() flux_pulses = {} delays = [] for qubit in targets: qubit_sequence = PulseSequence() native = platform.natives.single_qubit[qubit] drive_channel, rx90 = native.R(theta=np.pi / 2)[0] ro_channel, ro_pulse = native.MZ()[0] flux_channel = platform.qubits[qubit].flux flux_pulses[qubit] = Pulse( duration=params.duration_max, amplitude=params.flux_pulse_amplitude, envelope=Rectangular(), ) drive_delay = Delay(duration=flux_pulses[qubit].duration) ro_delay = Delay(duration=flux_pulses[qubit].duration) qubit_sequence.extend( [ (drive_channel, rx90), (flux_channel, Delay(duration=rx90.duration)), (flux_channel, flux_pulses[qubit]), (drive_channel, drive_delay), (drive_channel, rx90), (ro_channel, ro_delay), (ro_channel, Delay(duration=2 * rx90.duration)), (ro_channel, ro_pulse), ] ) delays += [drive_delay, ro_delay] sequence += qubit_sequence sweeper = Sweeper( parameter=Parameter.duration, values=duration_range, pulses=list(flux_pulses.values()) + delays, ) options = dict( nshots=params.nshots, acquisition_type=AcquisitionType.DISCRIMINATION, averaging_mode=AveragingMode.SINGLESHOT, ) results = platform.execute([sequence], [[sweeper]], **options) for qubit in targets: ro_pulse = list(sequence.channel(platform.qubits[qubit].acquisition))[-1] prob = probability(results[ro_pulse.id], state=1) data.register_qubit( FluxGateType, (qubit), dict( duration=duration_range, prob_1=prob, error=np.sqrt(prob * (1 - prob) / params.nshots), ), ) return data def _fit(data: FluxGateData) -> FluxGateResults: fitted_parameters = {} detuning = {} for qubit in data.qubits: qubit_data = data[qubit] x = qubit_data.duration y = qubit_data.prob_1 popt, _ = fitting(x, y) fitted_parameters[qubit] = popt detuning[qubit] = popt[2] / (2 * np.pi) * GHZ_TO_HZ return FluxGateResults(detuning=detuning, fitted_parameters=fitted_parameters) def _plot(data: FluxGateData, fit: FluxGateResults, target: QubitId): """FluxGate plots.""" fig = go.Figure() fitting_report = "" qubit_data = data[target] duration = qubit_data.duration prob = qubit_data.prob_1 error = qubit_data.error fig.add_trace( go.Scatter( x=qubit_data.duration, y=qubit_data.prob_1, name="Data", ) ) fig.add_trace( go.Scatter( x=np.concatenate((duration, duration[::-1])), y=np.concatenate((prob + error, (prob - error)[::-1])), fill="toself", fillcolor=COLORBAND, line=dict(color=COLORBAND_LINE), showlegend=True, name="Errors", ) ) if fit is not None: x = np.linspace(np.min(qubit_data.duration), np.max(qubit_data.duration), 100) fig.add_trace( go.Scatter( x=x, y=ramsey_fit(x, *fit.fitted_parameters[target]), name="Fit", ) ) fitting_report = table_html( table_dict( target, ["Flux pulse amplitude", "Detuning [Hz]"], [data.flux_pulse_amplitude, fit.detuning[target]], ) ) fig.update_layout( showlegend=True, xaxis_title="Time [ns]", yaxis_title="Excited state probability", ) return [fig], fitting_report flux_gate = Routine(_acquisition, _fit, _plot)