diff --git a/src/qibocal/protocols/characterization/coherence/spin_echo.py b/src/qibocal/protocols/characterization/coherence/spin_echo.py index 95d7d4595..1723e9e99 100644 --- a/src/qibocal/protocols/characterization/coherence/spin_echo.py +++ b/src/qibocal/protocols/characterization/coherence/spin_echo.py @@ -9,37 +9,23 @@ from qibolab.pulses import PulseSequence from qibolab.qubits import QubitId -from qibocal import update -from qibocal.auto.operation import Parameters, Results, Routine +from qibocal.auto.operation import Routine from ..utils import chi2_reduced, table_dict, table_html from . import t1 +from .spin_echo_signal import SpinEchoSignalParameters, SpinEchoSignalResults, _update from .utils import exp_decay, exponential_fit_probability @dataclass -class SpinEchoParameters(Parameters): +class SpinEchoParameters(SpinEchoSignalParameters): """SpinEcho 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].""" - unrolling: bool = False - """If ``True`` it uses sequence unrolling to deploy multiple sequences in a single instrument call. - Defaults to ``False``.""" - @dataclass -class SpinEchoResults(Results): +class SpinEchoResults(SpinEchoSignalResults): """SpinEcho outputs.""" - t2_spin_echo: dict[QubitId, float] - """T2 echo for each qubit.""" - fitted_parameters: dict[QubitId, dict[str, float]] - """Raw fitting output.""" chi2: Optional[dict[QubitId, tuple[float, Optional[float]]]] = field( default_factory=dict ) @@ -228,9 +214,5 @@ def _plot(data: SpinEchoData, target: QubitId, fit: SpinEchoResults = None): return figures, fitting_report -def _update(results: SpinEchoResults, platform: Platform, target: QubitId): - update.t2_spin_echo(results.t2_spin_echo[target], platform, target) - - spin_echo = Routine(_acquisition, _fit, _plot, _update) """SpinEcho Routine object.""" diff --git a/src/qibocal/protocols/characterization/coherence/spin_echo_signal.py b/src/qibocal/protocols/characterization/coherence/spin_echo_signal.py index 76ffd023c..dbe8ba39c 100644 --- a/src/qibocal/protocols/characterization/coherence/spin_echo_signal.py +++ b/src/qibocal/protocols/characterization/coherence/spin_echo_signal.py @@ -9,23 +9,37 @@ from qibolab.qubits import QubitId from qibocal import update -from qibocal.auto.operation import Routine +from qibocal.auto.operation import Parameters, Results, Routine from ..utils import table_dict, table_html -from . import spin_echo from .t1_signal import CoherenceType, T1SignalData from .utils import exp_decay, exponential_fit @dataclass -class SpinEchoSignalParameters(spin_echo.SpinEchoParameters): +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].""" + unrolling: bool = False + """If ``True`` it uses sequence unrolling to deploy multiple sequences in a single instrument call. + Defaults to ``False``.""" + @dataclass -class SpinEchoSignalResults(spin_echo.SpinEchoResults): +class SpinEchoSignalResults(Results): """SpinEchoSignal outputs.""" + t2_spin_echo: dict[QubitId, float] + """T2 echo for each qubit.""" + fitted_parameters: dict[QubitId, dict[str, float]] + """Raw fitting output.""" + class SpinEchoSignalData(T1SignalData): """SpinEcho acquisition outputs.""" diff --git a/src/qibocal/protocols/characterization/coherence/t1.py b/src/qibocal/protocols/characterization/coherence/t1.py index 7bb2b2119..7a4d61c67 100644 --- a/src/qibocal/protocols/characterization/coherence/t1.py +++ b/src/qibocal/protocols/characterization/coherence/t1.py @@ -10,36 +10,24 @@ from qibolab.qubits import QubitId from qibolab.sweeper import Parameter, Sweeper, SweeperType -from qibocal import update -from qibocal.auto.operation import Data, Parameters, Results, Routine +from qibocal.auto.operation import Data, Routine from ..utils import chi2_reduced, table_dict, table_html -from . import utils +from . import t1_signal, utils COLORBAND = "rgba(0,100,80,0.2)" COLORBAND_LINE = "rgba(255,255,255,0)" @dataclass -class T1Parameters(Parameters): +class T1Parameters(t1_signal.T1SignalParameters): """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].""" - @dataclass -class T1Results(Results): +class T1Results(t1_signal.T1SignalResults): """T1 outputs.""" - t1: dict[QubitId, tuple[float]] - """T1 for each qubit.""" - fitted_parameters: dict[QubitId, dict[str, float]] - """Raw fitting output.""" chi2: Optional[dict[QubitId, tuple[float, Optional[float]]]] = field( default_factory=dict ) @@ -232,9 +220,5 @@ def _plot(data: T1Data, target: QubitId, fit: T1Results = None): return figures, fitting_report -def _update(results: T1Results, platform: Platform, target: QubitId): - update.t1(results.t1[target], platform, target) - - -t1 = Routine(_acquisition, _fit, _plot, _update) +t1 = Routine(_acquisition, _fit, _plot, t1_signal._update) """T1 Routine object.""" diff --git a/src/qibocal/protocols/characterization/coherence/t1_sequences.py b/src/qibocal/protocols/characterization/coherence/t1_sequences.py index f665946fa..48c51a7e9 100644 --- a/src/qibocal/protocols/characterization/coherence/t1_sequences.py +++ b/src/qibocal/protocols/characterization/coherence/t1_sequences.py @@ -6,7 +6,7 @@ from qibocal.auto.operation import Routine -from . import t1, t1_signal +from . import t1_signal def _acquisition( @@ -83,5 +83,5 @@ def _acquisition( return data -t1_sequences = Routine(_acquisition, t1_signal._fit, t1_signal._plot, t1._update) +t1_sequences = Routine(_acquisition, t1_signal._fit, t1_signal._plot, t1_signal._update) """T1 Routine object.""" diff --git a/src/qibocal/protocols/characterization/coherence/t1_signal.py b/src/qibocal/protocols/characterization/coherence/t1_signal.py index 0c8074968..0ef893f4a 100644 --- a/src/qibocal/protocols/characterization/coherence/t1_signal.py +++ b/src/qibocal/protocols/characterization/coherence/t1_signal.py @@ -9,21 +9,34 @@ from qibolab.qubits import QubitId from qibolab.sweeper import Parameter, Sweeper, SweeperType -from qibocal.auto.operation import Data, Routine +from qibocal import update +from qibocal.auto.operation import Data, Parameters, Results, Routine from ..utils import table_dict, table_html -from . import t1, utils +from . import utils @dataclass -class T1SignalParameters(t1.T1Parameters): - """T1 Signal runcard inputs.""" +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].""" @dataclass -class T1SignalResults(t1.T1Results): +class T1SignalResults(Results): """T1 Signal outputs.""" + t1: dict[QubitId, tuple[float]] + """T1 for each qubit.""" + fitted_parameters: dict[QubitId, dict[str, float]] + """Raw fitting output.""" + CoherenceType = np.dtype( [("wait", np.float64), ("signal", np.float64), ("phase", np.float64)] @@ -180,5 +193,9 @@ def _plot(data: T1SignalData, target: QubitId, fit: T1SignalResults = None): return figures, fitting_report -t1_signal = Routine(_acquisition, _fit, _plot, t1._update) +def _update(results: T1SignalResults, platform: Platform, target: QubitId): + update.t1(results.t1[target], platform, target) + + +t1_signal = Routine(_acquisition, _fit, _plot, _update) """T1 Signal Routine object.""" diff --git a/src/qibocal/protocols/characterization/coherence/t2.py b/src/qibocal/protocols/characterization/coherence/t2.py index 372179836..b6ba62034 100644 --- a/src/qibocal/protocols/characterization/coherence/t2.py +++ b/src/qibocal/protocols/characterization/coherence/t2.py @@ -9,15 +9,14 @@ from qibolab.qubits import QubitId from qibolab.sweeper import Parameter, Sweeper, SweeperType -from qibocal import update -from qibocal.auto.operation import Parameters, Results, Routine +from qibocal.auto.operation import Routine from ..utils import chi2_reduced, table_dict, table_html -from . import t1, utils +from . import t1, t2_signal, utils @dataclass -class T2Parameters(Parameters): +class T2Parameters(t2_signal.T2SignalParameters): """T2 runcard inputs.""" delay_between_pulses_start: int @@ -29,13 +28,9 @@ class T2Parameters(Parameters): @dataclass -class T2Results(Results): +class T2Results(t2_signal.T2SignalResults): """T2 outputs.""" - t2: dict[QubitId, float] - """T2 for each qubit [ns].""" - fitted_parameters: dict[QubitId, dict[str, float]] - """Raw fitting output.""" chi2: Optional[dict[QubitId, tuple[float, Optional[float]]]] = field( default_factory=dict ) @@ -205,9 +200,5 @@ def _plot(data: T2Data, target: QubitId, fit: T2Results = None): return figures, fitting_report -def _update(results: T2Results, platform: Platform, target: QubitId): - update.t2(results.t2[target], platform, target) - - -t2 = Routine(_acquisition, _fit, _plot, _update) +t2 = Routine(_acquisition, _fit, _plot, t2_signal._update) """T2 Routine object.""" diff --git a/src/qibocal/protocols/characterization/coherence/t2_signal.py b/src/qibocal/protocols/characterization/coherence/t2_signal.py index ca11af9da..2c0b74c58 100644 --- a/src/qibocal/protocols/characterization/coherence/t2_signal.py +++ b/src/qibocal/protocols/characterization/coherence/t2_signal.py @@ -9,25 +9,42 @@ from qibolab.sweeper import Parameter, Sweeper, SweeperType from qibocal import update -from qibocal.auto.operation import Routine +from qibocal.auto.operation import Parameters, Results, Routine from ..utils import table_dict, table_html from . import t1_signal, t2, utils @dataclass -class T2SignalParameters(t2.T2Parameters): +class T2SignalParameters(Parameters): """T2Signal runcard inputs.""" + delay_between_pulses_start: int + """Initial delay between RX(pi/2) pulses in ns.""" + delay_between_pulses_end: int + """Final delay between RX(pi/2) pulses in ns.""" + delay_between_pulses_step: int + """Step delay between RX(pi/2) pulses in ns.""" + @dataclass -class T2SignalResults(t2.T2Results): +class T2SignalResults(Results): """T2Signal outputs.""" + t2: dict[QubitId, float] + """T2 for each qubit [ns].""" + fitted_parameters: dict[QubitId, dict[str, float]] + """Raw fitting output.""" + class T2SignalData(t1_signal.T1SignalData): """T2Signal acquisition outputs.""" + t2: dict[QubitId, float] + """T2 for each qubit [ns].""" + fitted_parameters: dict[QubitId, dict[str, float]] + """Raw fitting output.""" + def _acquisition( params: T2SignalParameters, diff --git a/src/qibocal/protocols/characterization/coherence/zeno.py b/src/qibocal/protocols/characterization/coherence/zeno.py index f608be372..9799623c3 100644 --- a/src/qibocal/protocols/characterization/coherence/zeno.py +++ b/src/qibocal/protocols/characterization/coherence/zeno.py @@ -8,20 +8,17 @@ from qibolab.pulses import PulseSequence from qibolab.qubits import QubitId -from qibocal import update -from qibocal.auto.operation import Parameters, Results, Routine +from qibocal.auto.operation import Routine from ..utils import chi2_reduced, table_dict, table_html from . import t1, utils +from .zeno_signal import ZenoSignalParameters, ZenoSignalResults, _update @dataclass -class ZenoParameters(Parameters): +class ZenoParameters(ZenoSignalParameters): """Zeno runcard inputs.""" - readouts: int - "Number of readout pulses" - @dataclass class ZenoData(t1.T1Data): @@ -30,13 +27,9 @@ class ZenoData(t1.T1Data): @dataclass -class ZenoResults(Results): +class ZenoResults(ZenoSignalResults): """Zeno outputs.""" - zeno_t1: dict[QubitId, int] - """T1 for each qubit.""" - fitted_parameters: dict[QubitId, dict[str, float]] - """Raw fitting output.""" chi2: dict[QubitId, tuple[float, Optional[float]]] """Chi squared estimate mean value and error.""" @@ -202,8 +195,4 @@ def _plot(data: ZenoData, fit: ZenoResults, target: QubitId): return figures, fitting_report -def _update(results: ZenoResults, platform: Platform, target: QubitId): - update.t1(results.zeno_t1[target], platform, target) - - zeno = Routine(_acquisition, _fit, _plot, _update) diff --git a/src/qibocal/protocols/characterization/coherence/zeno_signal.py b/src/qibocal/protocols/characterization/coherence/zeno_signal.py index 7cd2209b1..ca934410c 100644 --- a/src/qibocal/protocols/characterization/coherence/zeno_signal.py +++ b/src/qibocal/protocols/characterization/coherence/zeno_signal.py @@ -1,5 +1,4 @@ from dataclasses import dataclass, field -from typing import Optional import numpy as np import numpy.typing as npt @@ -17,23 +16,20 @@ @dataclass -class ZenoParameters(Parameters): +class ZenoSignalParameters(Parameters): """Zeno runcard inputs.""" readouts: int "Number of readout pulses" - nshots: Optional[int] = None - """Number of shots.""" - relaxation_time: Optional[int] = None - """Relaxation time [ns].""" -ZenoType = np.dtype([("signal", np.float64), ("phase", np.float64)]) +ZenoSignalType = np.dtype([("signal", np.float64), ("phase", np.float64)]) """Custom dtype for Zeno.""" @dataclass -class ZenoData(Data): +class ZenoSignalData(Data): + readout_duration: dict[QubitId, float] = field(default_factory=dict) """Readout durations for each qubit""" data: dict[QubitId, npt.NDArray] = field(default_factory=dict) @@ -41,7 +37,7 @@ class ZenoData(Data): def register_qubit(self, qubit, signal, phase): """Store output for single qubit.""" - ar = np.empty((1,), dtype=ZenoType) + ar = np.empty((1,), dtype=ZenoSignalType) ar["signal"] = signal ar["phase"] = phase if qubit in self.data: @@ -51,7 +47,7 @@ def register_qubit(self, qubit, signal, phase): @dataclass -class ZenoResults(Results): +class ZenoSignalResults(Results): """Zeno outputs.""" zeno_t1: dict[QubitId, int] @@ -61,10 +57,10 @@ class ZenoResults(Results): def _acquisition( - params: ZenoParameters, + params: ZenoSignalParameters, platform: Platform, targets: list[QubitId], -) -> ZenoData: +) -> ZenoSignalData: """ In a T1_Zeno experiment, we measure an excited qubit repeatedly. Due to decoherence processes, it is possible that, at the time of measurement, the qubit will not be excited anymore. @@ -92,7 +88,7 @@ def _acquisition( ro_pulse_duration[qubit] = ro_pulse.duration # create a DataUnits object to store the results - data = ZenoData(readout_duration=ro_pulse_duration) + data = ZenoSignalData(readout_duration=ro_pulse_duration) # execute the first pulse sequence results = platform.execute_pulse_sequence( @@ -115,7 +111,7 @@ def _acquisition( return data -def _fit(data: ZenoData) -> ZenoResults: +def _fit(data: ZenoSignalData) -> ZenoSignalResults: """ Fitting routine for T1 experiment. The used model is @@ -126,10 +122,10 @@ def _fit(data: ZenoData) -> ZenoResults: t1s, fitted_parameters = utils.exponential_fit(data, zeno=True) - return ZenoResults(t1s, fitted_parameters) + return ZenoSignalResults(t1s, fitted_parameters) -def _plot(data: ZenoData, fit: ZenoResults, target: QubitId): +def _plot(data: ZenoSignalData, fit: ZenoSignalResults, target: QubitId): """Plotting function for T1 experiment.""" figures = [] fig = go.Figure() @@ -189,7 +185,7 @@ def _plot(data: ZenoData, fit: ZenoResults, target: QubitId): return figures, fitting_report -def _update(results: ZenoResults, platform: Platform, qubit: QubitId): +def _update(results: ZenoSignalResults, platform: Platform, qubit: QubitId): update.t1(results.zeno_t1[qubit], platform, qubit) diff --git a/src/qibocal/protocols/characterization/flipping.py b/src/qibocal/protocols/characterization/flipping.py index ca2ee4a87..82257846a 100644 --- a/src/qibocal/protocols/characterization/flipping.py +++ b/src/qibocal/protocols/characterization/flipping.py @@ -10,16 +10,22 @@ from qibolab.qubits import QubitId from scipy.optimize import curve_fit -from qibocal import update -from qibocal.auto.operation import Data, Parameters, Results, Routine +from qibocal.auto.operation import Routine from qibocal.config import log from qibocal.protocols.characterization.utils import table_dict, table_html +from .flipping_signal import ( + FlippingSignalData, + FlippingSignalParameters, + FlippingSignalResults, + _update, + flipping_fit, +) from .utils import COLORBAND, COLORBAND_LINE, chi2_reduced @dataclass -class FlippingParameters(Parameters): +class FlippingParameters(FlippingSignalParameters): """Flipping runcard inputs.""" nflips_max: int @@ -32,15 +38,9 @@ class FlippingParameters(Parameters): @dataclass -class FlippingResults(Results): +class FlippingResults(FlippingSignalResults): """Flipping outputs.""" - amplitude: dict[QubitId, tuple[float, Optional[float]]] - """Drive amplitude for each qubit.""" - amplitude_factors: dict[QubitId, tuple[float, Optional[float]]] - """Drive amplitude correction factor for each qubit.""" - fitted_parameters: dict[QubitId, dict[str, float]] - """Raw fitting output.""" chi2: dict[QubitId, tuple[float, Optional[float]]] = field(default_factory=dict) """Chi squared estimate mean value and error. """ @@ -51,12 +51,9 @@ class FlippingResults(Results): @dataclass -class FlippingData(Data): +class FlippingData(FlippingSignalData): """Flipping acquisition outputs.""" - resonator_type: str - """Resonator type.""" - pi_pulse_amplitudes: dict[QubitId, float] """Pi pulse amplitudes for each qubit.""" data: dict[QubitId, npt.NDArray[FlippingType]] = field(default_factory=dict) """Raw data acquired.""" @@ -154,10 +151,6 @@ def _acquisition( return data -def flipping_fit(x, offset, amplitude, omega, phase, gamma): - return np.sin(x * omega + phase) * amplitude * np.exp(-x * gamma) + offset - - def _fit(data: FlippingData) -> FlippingResults: r"""Post-processing function for Flipping. @@ -318,9 +311,5 @@ def _plot(data: FlippingData, target: QubitId, fit: FlippingResults = None): return figures, fitting_report -def _update(results: FlippingResults, platform: Platform, target: QubitId): - update.drive_amplitude(results.amplitude[target], platform, target) - - flipping = Routine(_acquisition, _fit, _plot, _update) """Flipping Routine object.""" diff --git a/src/qibocal/protocols/characterization/flipping_signal.py b/src/qibocal/protocols/characterization/flipping_signal.py index 86be36dd2..4b0f1505d 100644 --- a/src/qibocal/protocols/characterization/flipping_signal.py +++ b/src/qibocal/protocols/characterization/flipping_signal.py @@ -1,4 +1,5 @@ from dataclasses import dataclass, field +from typing import Optional import numpy as np import numpy.typing as npt @@ -11,34 +12,47 @@ from scipy.signal import find_peaks from qibocal import update -from qibocal.auto.operation import Routine +from qibocal.auto.operation import Data, Parameters, Results, Routine from qibocal.config import log -from qibocal.protocols.characterization.flipping import ( - FlippingData, - FlippingParameters, - FlippingResults, - flipping_fit, -) from qibocal.protocols.characterization.utils import table_dict, table_html @dataclass -class FlippingSignalParameters(FlippingParameters): +class FlippingSignalParameters(Parameters): """Flipping runcard inputs.""" + nflips_max: int + """Maximum number of flips ([RX(pi) - RX(pi)] sequences). """ + nflips_step: int + """Flip step.""" + unrolling: bool = False + """If ``True`` it uses sequence unrolling to deploy multiple sequences in a single instrument call. + Defaults to ``False``.""" + @dataclass -class FlippingSignalResults(FlippingResults): +class FlippingSignalResults(Results): """Flipping outputs.""" + amplitude: dict[QubitId, tuple[float, Optional[float]]] + """Drive amplitude for each qubit.""" + amplitude_factors: dict[QubitId, tuple[float, Optional[float]]] + """Drive amplitude correction factor for each qubit.""" + fitted_parameters: dict[QubitId, dict[str, float]] + """Raw fitting output.""" + FlippingType = np.dtype([("flips", np.float64), ("signal", np.float64)]) @dataclass -class FlippingSignalData(FlippingData): +class FlippingSignalData(Data): """Flipping acquisition outputs.""" + resonator_type: str + """Resonator type.""" + pi_pulse_amplitudes: dict[QubitId, float] + """Pi pulse amplitudes for each qubit.""" data: dict[QubitId, npt.NDArray[FlippingType]] = field(default_factory=dict) """Raw data acquired.""" @@ -132,6 +146,10 @@ def _acquisition( return data +def flipping_fit(x, offset, amplitude, omega, phase, gamma): + return np.sin(x * omega + phase) * amplitude * np.exp(-x * gamma) + offset + + def _fit(data: FlippingSignalData) -> FlippingSignalResults: r"""Post-processing function for Flipping. diff --git a/src/qibocal/protocols/characterization/qubit_spectroscopy.py b/src/qibocal/protocols/characterization/qubit_spectroscopy.py index e7db7844a..4b63e5e82 100644 --- a/src/qibocal/protocols/characterization/qubit_spectroscopy.py +++ b/src/qibocal/protocols/characterization/qubit_spectroscopy.py @@ -140,7 +140,7 @@ def _fit(data: QubitSpectroscopyData) -> QubitSpectroscopyResults: ) chi2[qubit] = ( chi2_reduced( - data[qubit].freq, + data[qubit].signal, lorentzian(data[qubit].freq, *fitted_parameters[qubit]), data[qubit].error_signal, ), diff --git a/src/qibocal/protocols/characterization/rabi/amplitude.py b/src/qibocal/protocols/characterization/rabi/amplitude.py index 0e92ab21d..8759c7183 100644 --- a/src/qibocal/protocols/characterization/rabi/amplitude.py +++ b/src/qibocal/protocols/characterization/rabi/amplitude.py @@ -12,37 +12,23 @@ from scipy.signal import find_peaks from qibocal import update -from qibocal.auto.operation import Data, Parameters, Results, Routine +from qibocal.auto.operation import Data, Routine from qibocal.config import log from ..utils import chi2_reduced from . import utils +from .amplitude_signal import RabiAmplitudeVoltParameters, RabiAmplitudeVoltResults @dataclass -class RabiAmplitudeParameters(Parameters): +class RabiAmplitudeParameters(RabiAmplitudeVoltParameters): """RabiAmplitude runcard inputs.""" - min_amp_factor: float - """Minimum amplitude multiplicative factor.""" - max_amp_factor: float - """Maximum amplitude multiplicative factor.""" - step_amp_factor: float - """Step amplitude multiplicative factor.""" - pulse_length: Optional[float] - """RX pulse duration [ns].""" - @dataclass -class RabiAmplitudeResults(Results): +class RabiAmplitudeResults(RabiAmplitudeVoltResults): """RabiAmplitude outputs.""" - amplitude: dict[QubitId, tuple[float, Optional[float]]] - """Drive amplitude for each qubit.""" - length: dict[QubitId, tuple[float, Optional[float]]] - """Drive pulse duration. Same for all qubits.""" - fitted_parameters: dict[QubitId, dict[str, float]] - """Raw fitted parameters.""" chi2: dict[QubitId, tuple[float, Optional[float]]] = field(default_factory=dict) @@ -182,7 +168,6 @@ def _fit(data: RabiAmplitudeData) -> RabiAmplitudeResults: except Exception as e: log.warning(f"Rabi fit failed for qubit {qubit} due to {e}.") - return RabiAmplitudeResults(pi_pulse_amplitudes, durations, fitted_parameters, chi2) diff --git a/src/qibocal/protocols/characterization/rabi/amplitude_signal.py b/src/qibocal/protocols/characterization/rabi/amplitude_signal.py index 3b1f557e8..4a8c58298 100644 --- a/src/qibocal/protocols/characterization/rabi/amplitude_signal.py +++ b/src/qibocal/protocols/characterization/rabi/amplitude_signal.py @@ -1,6 +1,8 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field +from typing import Optional import numpy as np +import numpy.typing as npt from qibolab import AcquisitionType, AveragingMode, ExecutionParameters from qibolab.platform import Platform from qibolab.pulses import PulseSequence @@ -10,26 +12,37 @@ from scipy.signal import find_peaks from qibocal import update -from qibocal.auto.operation import Routine +from qibocal.auto.operation import Data, Parameters, Results, Routine from qibocal.config import log -from qibocal.protocols.characterization.rabi.amplitude import ( - RabiAmplitudeData, - RabiAmplitudeParameters, - RabiAmplitudeResults, -) from . import utils @dataclass -class RabiAmplitudeVoltParameters(RabiAmplitudeParameters): +class RabiAmplitudeVoltParameters(Parameters): """RabiAmplitude runcard inputs.""" + min_amp_factor: float + """Minimum amplitude multiplicative factor.""" + max_amp_factor: float + """Maximum amplitude multiplicative factor.""" + step_amp_factor: float + """Step amplitude multiplicative factor.""" + pulse_length: Optional[float] + """RX pulse duration [ns].""" + @dataclass -class RabiAmplitudeVoltResults(RabiAmplitudeResults): +class RabiAmplitudeVoltResults(Results): """RabiAmplitude outputs.""" + amplitude: dict[QubitId, tuple[float, Optional[float]]] + """Drive amplitude for each qubit.""" + length: dict[QubitId, tuple[float, Optional[float]]] + """Drive pulse duration. Same for all qubits.""" + fitted_parameters: dict[QubitId, dict[str, float]] + """Raw fitted parameters.""" + RabiAmpVoltType = np.dtype( [("amp", np.float64), ("signal", np.float64), ("phase", np.float64)] @@ -38,8 +51,13 @@ class RabiAmplitudeVoltResults(RabiAmplitudeResults): @dataclass -class RabiAmplitudeVoltData(RabiAmplitudeData): - """RabiAmplitude data acquisition.""" +class RabiAmplitudeVoltData(Data): + """RabiAmplitudeVolt data acquisition.""" + + durations: dict[QubitId, float] = field(default_factory=dict) + """Pulse durations provided by the user.""" + data: dict[QubitId, npt.NDArray[RabiAmpVoltType]] = field(default_factory=dict) + """Raw data acquired.""" def _acquisition( diff --git a/src/qibocal/protocols/characterization/ramsey/ramsey.py b/src/qibocal/protocols/characterization/ramsey/ramsey.py index 6d2ae6dfb..734b11b09 100644 --- a/src/qibocal/protocols/characterization/ramsey/ramsey.py +++ b/src/qibocal/protocols/characterization/ramsey/ramsey.py @@ -10,11 +10,16 @@ from qibolab.qubits import QubitId from qibolab.sweeper import Parameter, Sweeper, SweeperType -from qibocal import update -from qibocal.auto.operation import Data, Parameters, Results, Routine +from qibocal.auto.operation import Routine from qibocal.config import log from ..utils import GHZ_TO_HZ, chi2_reduced, table_dict, table_html +from .ramsey_signal import ( + RamseySignalData, + RamseySignalParameters, + RamseySignalResults, + _update, +) from .utils import fitting, ramsey_fit, ramsey_sequence COLORBAND = "rgba(0,100,80,0.2)" @@ -22,38 +27,14 @@ @dataclass -class RamseyParameters(Parameters): +class RamseyParameters(RamseySignalParameters): """Ramsey runcard inputs.""" - delay_between_pulses_start: int - """Initial delay between RX(pi/2) pulses in ns.""" - delay_between_pulses_end: int - """Final delay between RX(pi/2) pulses in ns.""" - delay_between_pulses_step: int - """Step delay between RX(pi/2) pulses in ns.""" - detuning: Optional[int] = 0 - """Frequency detuning [Hz] (optional). - If 0 standard Ramsey experiment is performed.""" - unrolling: bool = False - """If ``True`` it uses sequence unrolling to deploy multiple sequences in a single instrument call. - Defaults to ``False``.""" - @dataclass -class RamseyResults(Results): +class RamseyResults(RamseySignalResults): """Ramsey outputs.""" - frequency: dict[QubitId, tuple[float, Optional[float]]] - """Drive frequency [Hz] for each qubit.""" - t2: dict[QubitId, tuple[float, Optional[float]]] - """T2 for each qubit [ns].""" - delta_phys: dict[QubitId, tuple[float, Optional[float]]] - """Drive frequency [Hz] correction for each qubit.""" - delta_fitting: dict[QubitId, tuple[float, Optional[float]]] - """Raw drive frequency [Hz] correction for each qubit. - including the detuning.""" - fitted_parameters: dict[QubitId, list[float]] - """Raw fitting output.""" chi2: dict[QubitId, tuple[float, Optional[float]]] """Chi squared estimate mean value and error. """ @@ -65,24 +46,12 @@ class RamseyResults(Results): @dataclass -class RamseyData(Data): +class RamseyData(RamseySignalData): """Ramsey acquisition outputs.""" - detuning: int - """Frequency detuning [Hz].""" - qubit_freqs: dict[QubitId, float] = field(default_factory=dict) - """Qubit freqs for each qubit.""" - data: dict[QubitId, npt.NDArray] = field(default_factory=dict) + data: dict[QubitId, npt.NDArray[RamseyType]] = field(default_factory=dict) """Raw data acquired.""" - @property - def waits(self): - """ - Return a list with the waiting times without repetitions. - """ - qubit = next(iter(self.data)) - return np.unique(self.data[qubit].wait) - def _acquisition( params: RamseyParameters, @@ -346,9 +315,5 @@ def _plot(data: RamseyData, target: QubitId, fit: RamseyResults = None): return figures, fitting_report -def _update(results: RamseyResults, platform: Platform, target: QubitId): - update.drive_frequency(results.frequency[target][0], platform, target) - - ramsey = Routine(_acquisition, _fit, _plot, _update) """Ramsey Routine object.""" diff --git a/src/qibocal/protocols/characterization/ramsey/ramsey_signal.py b/src/qibocal/protocols/characterization/ramsey/ramsey_signal.py index 72db2c2fd..a1bfb0c2f 100644 --- a/src/qibocal/protocols/characterization/ramsey/ramsey_signal.py +++ b/src/qibocal/protocols/characterization/ramsey/ramsey_signal.py @@ -1,7 +1,8 @@ -from dataclasses import dataclass +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, ExecutionParameters from qibolab.platform import Platform @@ -9,18 +10,31 @@ from qibolab.qubits import QubitId from qibolab.sweeper import Parameter, Sweeper, SweeperType -from qibocal.auto.operation import Results, Routine +from qibocal import update +from qibocal.auto.operation import Data, Parameters, Results, Routine from qibocal.config import log from ..utils import GHZ_TO_HZ, table_dict, table_html -from .ramsey import RamseyData, RamseyParameters, _update from .utils import fitting, ramsey_fit, ramsey_sequence @dataclass -class RamseySignalParameters(RamseyParameters): +class RamseySignalParameters(Parameters): """Ramsey runcard inputs.""" + delay_between_pulses_start: int + """Initial delay between RX(pi/2) pulses in ns.""" + delay_between_pulses_end: int + """Final delay between RX(pi/2) pulses in ns.""" + delay_between_pulses_step: int + """Step delay between RX(pi/2) pulses in ns.""" + detuning: Optional[int] = 0 + """Frequency detuning [Hz] (optional). + If 0 standard Ramsey experiment is performed.""" + unrolling: bool = False + """If ``True`` it uses sequence unrolling to deploy multiple sequences in a single instrument call. + Defaults to ``False``.""" + @dataclass class RamseySignalResults(Results): @@ -44,20 +58,23 @@ class RamseySignalResults(Results): @dataclass -class RamseySignalData(RamseyData): +class RamseySignalData(Data): """Ramsey acquisition outputs.""" - def register_qubit(self, qubit, wait, signal): - """Store output for single qubit.""" - # to be able to handle the non-sweeper case - shape = (1,) if np.isscalar(signal) else signal.shape - ar = np.empty(shape, dtype=RamseySignalType) - ar["wait"] = wait - ar["signal"] = signal - if qubit in self.data: - self.data[qubit] = np.rec.array(np.concatenate((self.data[qubit], ar))) - else: - self.data[qubit] = np.rec.array(ar) + detuning: int + """Frequency detuning [Hz].""" + qubit_freqs: dict[QubitId, float] = field(default_factory=dict) + """Qubit freqs for each qubit.""" + data: dict[QubitId, npt.NDArray[RamseySignalType]] = field(default_factory=dict) + """Raw data acquired.""" + + @property + def waits(self): + """ + Return a list with the waiting times without repetitions. + """ + qubit = next(iter(self.data)) + return np.unique(self.data[qubit].wait) def _acquisition( @@ -116,9 +133,12 @@ def _acquisition( result = results[sequence.get_qubit_pulses(qubit).ro_pulses[0].serial] # The probability errors are the standard errors of the binomial distribution data.register_qubit( - qubit, - wait=waits, - signal=result.magnitude, + RamseySignalType, + (qubit), + dict( + wait=waits, + signal=result.magnitude, + ), ) else: @@ -144,9 +164,12 @@ def _acquisition( else: result = results[ig][serial] data.register_qubit( - qubit, - wait=wait, - signal=result.magnitude, + RamseySignalType, + (qubit), + dict( + wait=np.array([wait]), + signal=np.array([result.magnitude]), + ), ) return data @@ -271,5 +294,9 @@ def _plot(data: RamseySignalData, target: QubitId, fit: RamseySignalResults = No return figures, fitting_report +def _update(results: RamseySignalResults, platform: Platform, target: QubitId): + update.drive_frequency(results.frequency[target][0], platform, target) + + ramsey_signal = Routine(_acquisition, _fit, _plot, _update) """Ramsey Routine object.""" diff --git a/src/qibocal/protocols/characterization/resonator_spectroscopy.py b/src/qibocal/protocols/characterization/resonator_spectroscopy.py index cb250a862..f3bae440e 100644 --- a/src/qibocal/protocols/characterization/resonator_spectroscopy.py +++ b/src/qibocal/protocols/characterization/resonator_spectroscopy.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass, field +from dataclasses import dataclass, field, fields from typing import Optional, Union import numpy as np @@ -82,6 +82,14 @@ class ResonatorSpectroscopyResults(Results): ) """Readout attenuation [dB] for each qubit.""" + def __contains__(self, key: QubitId): + return all( + key in getattr(self, field.name) + for field in fields(self) + if isinstance(getattr(self, field.name), dict) + and field.name != "bare_frequency" + ) + @dataclass class ResonatorSpectroscopyData(Data): @@ -211,7 +219,7 @@ def _fit( bare_frequency[qubit] = frequency[qubit] chi2[qubit] = ( chi2_reduced( - data[qubit].freq, + data[qubit].signal, lorentzian(data[qubit].freq, *fitted_parameters[qubit]), data[qubit].error_signal, ),