From 2d91a9bcea839be73551074922729fc469240485 Mon Sep 17 00:00:00 2001 From: jevillegasd Date: Fri, 15 May 2026 18:47:49 +0400 Subject: [PATCH 01/10] modified: src/qibocal/protocols/__init__.py new file: src/qibocal/protocols/flipping_amplitude.py --- src/qibocal/protocols/__init__.py | 3 +++ src/qibocal/protocols/flipping_amplitude.py | 1 + 2 files changed, 4 insertions(+) create mode 100644 src/qibocal/protocols/flipping_amplitude.py diff --git a/src/qibocal/protocols/__init__.py b/src/qibocal/protocols/__init__.py index f279bd4640..15acc70d4f 100644 --- a/src/qibocal/protocols/__init__.py +++ b/src/qibocal/protocols/__init__.py @@ -5,6 +5,7 @@ dispersive_shift, drag, flipping, + flipping_amplitude, flux_dependence, qubit_spectroscopies, rabi, @@ -24,6 +25,7 @@ from .dispersive_shift import * from .drag import * from .flipping import * +from .flipping_amplitude import * from .flux_dependence import * from .qubit_spectroscopies import * from .rabi import * @@ -62,6 +64,7 @@ __all__ += ["classification"] __all__ += ["drag"] __all__ += ["flipping"] +__all__ += ["flipping_amplitude"] __all__ += ["readout"] __all__ += ["tomographies"] __all__ += ["resonator_spectroscopies"] diff --git a/src/qibocal/protocols/flipping_amplitude.py b/src/qibocal/protocols/flipping_amplitude.py new file mode 100644 index 0000000000..c3bceee261 --- /dev/null +++ b/src/qibocal/protocols/flipping_amplitude.py @@ -0,0 +1 @@ +"""Flipping experiment sweeping number of flips and pulse amplitude.""" From acc99dd98838978e3e7d70f854fbba098c527327 Mon Sep 17 00:00:00 2001 From: jevillegasd Date: Fri, 15 May 2026 18:50:04 +0400 Subject: [PATCH 02/10] Parameters and Types --- src/qibocal/protocols/flipping_amplitude.py | 107 ++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/src/qibocal/protocols/flipping_amplitude.py b/src/qibocal/protocols/flipping_amplitude.py index c3bceee261..09ce3139fe 100644 --- a/src/qibocal/protocols/flipping_amplitude.py +++ b/src/qibocal/protocols/flipping_amplitude.py @@ -1 +1,108 @@ """Flipping experiment sweeping number of flips and pulse amplitude.""" + +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, PulseSequence, Readout + +from qibocal import update +from qibocal.auto.operation import Data, Parameters, QubitId, Results, Routine +from qibocal.calibration import CalibrationPlatform +from qibocal.config import log +from qibocal.protocols.utils import table_dict, table_html + +from .flipping import flipping_sequence + +__all__ = ["flipping_amplitude"] + + +@dataclass +class FlippingAmplitudeParameters(Parameters): + """FlippingAmplitude runcard inputs.""" + + nflips_max: int = 21 + """Maximum number of flips ([RX(pi) - RX(pi)] sequences).""" + nflips_step: int = 1 + """Step size for the number of consecutive flips.""" + delta_amplitude_min: float = -0.05 + """Minimum amplitude delta relative to the native pulse amplitude.""" + delta_amplitude_max: float = 0.05 + """Maximum amplitude delta relative to the native pulse amplitude.""" + delta_amplitude_step: float = 0.001 + """Amplitude delta step.""" + rx90: bool = False + """Calibration of native pi pulse, if true calibrates pi/2 pulse.""" + + + +@dataclass +class FlippingAmplitudeResults(Results): + """FlippingAmplitude outputs.""" + + amplitude: dict[QubitId, float | list[float]] + """Best drive amplitude for each qubit.""" + delta_amplitude: dict[QubitId, float | list[float]] + """Difference in amplitude between native value and best fit.""" + rx90: bool + """Pi or Pi_half calibration.""" + + +FlippingAmplitudeType = np.dtype( + [ + ("flips", np.float64), + ("amplitude", np.float64), + ("prob", np.float64), + ("error", np.float64), + ] +) +"""Custom dtype for flipping amplitude sweep.""" + + +@dataclass +class FlippingAmplitudeData(Data): + """FlippingAmplitude acquisition outputs.""" + + resonator_type: str + """Resonator type.""" + pulse_amplitudes: dict[QubitId, float] + """Native pulse amplitudes for each qubit.""" + rx90: bool + """Pi or Pi_half calibration.""" + data: dict[QubitId, npt.NDArray[FlippingAmplitudeType]] = field( + default_factory=dict + ) + """Raw data acquired.""" + + +def _acquisition( + params: FlippingAmplitudeParameters, + platform: CalibrationPlatform, + targets: list[QubitId], +) -> FlippingAmplitudeData: + return None + + +def _fit(data: FlippingAmplitudeData) -> FlippingAmplitudeResults: + return None + + +def _plot( + data: FlippingAmplitudeData, + target: QubitId, + fit: FlippingAmplitudeResults | None = None, +): + return None + + +def _update( + results: FlippingAmplitudeResults, + platform: CalibrationPlatform, + qubit: QubitId, +): + update.drive_amplitude(results.amplitude[qubit], results.rx90, platform, qubit) + + +flipping_amplitude = Routine(_acquisition, _fit, _plot, _update) +"""FlippingAmplitude Routine object.""" From ac58fcffe3590ed19a007046d6bff8db20a3e5db Mon Sep 17 00:00:00 2001 From: jevillegasd Date: Fri, 15 May 2026 18:50:30 +0400 Subject: [PATCH 03/10] acquisition --- src/qibocal/protocols/flipping_amplitude.py | 89 ++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/src/qibocal/protocols/flipping_amplitude.py b/src/qibocal/protocols/flipping_amplitude.py index 09ce3139fe..abbe1bd04f 100644 --- a/src/qibocal/protocols/flipping_amplitude.py +++ b/src/qibocal/protocols/flipping_amplitude.py @@ -35,6 +35,21 @@ class FlippingAmplitudeParameters(Parameters): rx90: bool = False """Calibration of native pi pulse, if true calibrates pi/2 pulse.""" + def __post_init__(self): + if not isinstance(self.nflips_max, int): + raise TypeError( + f"nflips_max must be int, got {type(self.nflips_max).__name__}" + ) + if not isinstance(self.nflips_step, int): + raise TypeError( + f"nflips_step must be int, got {type(self.nflips_step).__name__}" + ) + if not isinstance(self.rx90, bool): + raise TypeError(f"rx90 must be boolean, got {type(self.rx90).__name__}") + if self.nflips_max <= 0: + raise ValueError("nflips_max must be greater than 0.") + if self.nflips_step <= 0: + raise ValueError("nflips_step must be greater than 0.") @dataclass @@ -81,7 +96,79 @@ def _acquisition( platform: CalibrationPlatform, targets: list[QubitId], ) -> FlippingAmplitudeData: - return None + r"""Data acquisition for flipping with amplitude sweep. + + For each combination of (flips, delta_amplitude) a sequence is built and + executed. The amplitude values are stored as absolute amplitudes + (native + delta). The resulting 2D map allows identifying the correct + drive amplitude: at the true pi-pulse amplitude the excited-state + probability should remain flat regardless of the number of flips. + """ + + data = FlippingAmplitudeData( + resonator_type=platform.resonator_type, + pulse_amplitudes={ + qubit: getattr( + platform.natives.single_qubit[qubit], "RX90" if params.rx90 else "RX" + )[0][1].amplitude + for qubit in targets + }, + rx90=params.rx90, + ) + + flips_sweep = range(0, params.nflips_max, params.nflips_step) + delta_amplitude_sweep = np.arange( + params.delta_amplitude_min, + params.delta_amplitude_max, + params.delta_amplitude_step, + ) + + sequences: list[PulseSequence] = [] + sweep_params: list[tuple[int, float]] = [] + + for flips in flips_sweep: + for delta_amp in delta_amplitude_sweep: + sequence = PulseSequence() + for qubit in targets: + sequence += flipping_sequence( + platform=platform, + qubit=qubit, + delta_amplitude=float(delta_amp), + flips=flips, + rx90=params.rx90, + ) + sequences.append(sequence) + sweep_params.append((flips, float(delta_amp))) + + results = platform.execute( + sequences, + acquisition_type=AcquisitionType.DISCRIMINATION, + averaging_mode=AveragingMode.CYCLIC, + nshots=params.nshots, + relaxation_time=params.relaxation_time, + ) + + for (flips, delta_amp), sequence in zip(sweep_params, sequences): + for qubit in targets: + acq_channel = platform.qubits[qubit].acquisition + assert acq_channel is not None + ro_pulse = list(sequence.channel(acq_channel))[-1] + assert isinstance(ro_pulse, Readout) + prob = results[ro_pulse.id] + error = np.sqrt(prob * (1 - prob) / params.nshots) + native_amp = data.pulse_amplitudes[qubit] + data.register_qubit( + FlippingAmplitudeType, + qubit, + { + "flips": np.array([flips]), + "amplitude": np.array([native_amp + delta_amp]), + "prob": np.array([prob]), + "error": np.array([error]), + }, + ) + + return data def _fit(data: FlippingAmplitudeData) -> FlippingAmplitudeResults: From 46d7cfc8ab5e0a69afa0d28fd9cd7729a6beebe3 Mon Sep 17 00:00:00 2001 From: jevillegasd Date: Fri, 15 May 2026 19:00:46 +0400 Subject: [PATCH 04/10] acquisition and fit --- src/qibocal/protocols/flipping_amplitude.py | 91 ++++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/src/qibocal/protocols/flipping_amplitude.py b/src/qibocal/protocols/flipping_amplitude.py index abbe1bd04f..daee8f372b 100644 --- a/src/qibocal/protocols/flipping_amplitude.py +++ b/src/qibocal/protocols/flipping_amplitude.py @@ -172,7 +172,37 @@ def _acquisition( def _fit(data: FlippingAmplitudeData) -> FlippingAmplitudeResults: - return None + """Find the best amplitude by minimising the variance of P(|1>) vs flips. + TODO: Use the same fit as in the flipping protocol to extract the rabi amp + per input delta detiuning, these should all bve the same so the mean value + can be used as the best amplitude. + """ + + best_amplitudes: dict[QubitId, list[float]] = {} + delta_amplitudes: dict[QubitId, list[float]] = {} + + for qubit in data.qubits: + qubit_data = data[qubit] + amplitudes = np.unique(qubit_data["amplitude"]) + variances = [] + + for amp in amplitudes: + mask = qubit_data["amplitude"] == amp + probs = qubit_data["prob"][mask] + variances.append(float(np.var(probs))) + + best_idx = int(np.argmin(variances)) + best_amp = float(amplitudes[best_idx]) + native_amp = data.pulse_amplitudes[qubit] + + best_amplitudes[qubit] = [best_amp, 0.0] + delta_amplitudes[qubit] = [best_amp - native_amp, 0.0] + + return FlippingAmplitudeResults( + amplitude=best_amplitudes, + delta_amplitude=delta_amplitudes, + rx90=data.rx90, + ) def _plot( @@ -180,7 +210,64 @@ def _plot( target: QubitId, fit: FlippingAmplitudeResults | None = None, ): - return None + """Plotting function for FlippingAmplitude. + + Produces a heatmap of excited-state probability as a function of flips + (x-axis) and pulse amplitude (y-axis). When fit results are available a + dashed horizontal line marks the best amplitude. + """ + + qubit_data = data[target] + amplitudes = np.unique(qubit_data["amplitude"]) + flips_vals = np.unique(qubit_data["flips"]) + + # Build 2D probability matrix: rows = amplitude, cols = flips + z = np.full((len(amplitudes), len(flips_vals)), np.nan) + amp_index = {amp: i for i, amp in enumerate(amplitudes)} + flip_index = {fl: j for j, fl in enumerate(flips_vals)} + + for row in qubit_data: + i = amp_index[row["amplitude"]] + j = flip_index[row["flips"]] + z[i, j] = row["prob"] + + fig = go.Figure( + go.Heatmap( + x=flips_vals, + y=amplitudes, + z=z, + colorscale="RdBu", + zmid=0.5, + colorbar=dict(title="Excited State Probability"), + ) + ) + + if fit is not None and target in fit.amplitude: + best_amp = fit.amplitude[target][0] + fig.add_hline( + y=best_amp, + line=dict(color="black", dash="dash", width=2), + annotation_text=f"Best amp: {best_amp:.4f}", + annotation_position="right", + ) + + fig.update_layout( + xaxis_title="Flips", + yaxis_title="Amplitude [a.u.]", + ) + + fitting_report = "" + if fit is not None and target in fit.amplitude: + fitting_report = table_html( + table_dict( + target, + ["Best amplitude [a.u.]", "Delta amplitude [a.u.]"], + [fit.amplitude[target], fit.delta_amplitude[target]], + display_error=True, + ) + ) + + return [fig], fitting_report def _update( From 64578d83e276ebfe976a7fdcefe4a22405178919 Mon Sep 17 00:00:00 2001 From: jevillegasd Date: Mon, 18 May 2026 07:32:11 +0400 Subject: [PATCH 05/10] remove unused import and channge plots to --- src/qibocal/protocols/flipping_amplitude.py | 24 ++++++++++++--------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/qibocal/protocols/flipping_amplitude.py b/src/qibocal/protocols/flipping_amplitude.py index daee8f372b..d7aa7dcd98 100644 --- a/src/qibocal/protocols/flipping_amplitude.py +++ b/src/qibocal/protocols/flipping_amplitude.py @@ -10,7 +10,6 @@ from qibocal import update from qibocal.auto.operation import Data, Parameters, QubitId, Results, Routine from qibocal.calibration import CalibrationPlatform -from qibocal.config import log from qibocal.protocols.utils import table_dict, table_html from .flipping import flipping_sequence @@ -116,8 +115,8 @@ def _acquisition( rx90=params.rx90, ) - flips_sweep = range(0, params.nflips_max, params.nflips_step) - delta_amplitude_sweep = np.arange( + flips_range = range(0, params.nflips_max, params.nflips_step) + delta_amplitude_range = np.arange( params.delta_amplitude_min, params.delta_amplitude_max, params.delta_amplitude_step, @@ -126,8 +125,9 @@ def _acquisition( sequences: list[PulseSequence] = [] sweep_params: list[tuple[int, float]] = [] - for flips in flips_sweep: - for delta_amp in delta_amplitude_sweep: + for flips in flips_range: + # TODO: The inner loop can be improved using a sweeper + for delta_amp in delta_amplitude_range: sequence = PulseSequence() for qubit in targets: sequence += flipping_sequence( @@ -174,7 +174,7 @@ def _acquisition( def _fit(data: FlippingAmplitudeData) -> FlippingAmplitudeResults: """Find the best amplitude by minimising the variance of P(|1>) vs flips. TODO: Use the same fit as in the flipping protocol to extract the rabi amp - per input delta detiuning, these should all bve the same so the mean value + per input delta detiuning, these should all be the same and the mean value can be used as the best amplitude. """ @@ -217,6 +217,10 @@ def _plot( dashed horizontal line marks the best amplitude. """ + def ev(prob: np.ndarray) -> np.ndarray: + """Helper function to calculate the expectation value.""" + return 2 * prob - 1 + qubit_data = data[target] amplitudes = np.unique(qubit_data["amplitude"]) flips_vals = np.unique(qubit_data["flips"]) @@ -229,16 +233,16 @@ def _plot( for row in qubit_data: i = amp_index[row["amplitude"]] j = flip_index[row["flips"]] - z[i, j] = row["prob"] + z[i, j] = ev(row["prob"]) fig = go.Figure( go.Heatmap( x=flips_vals, y=amplitudes, z=z, - colorscale="RdBu", - zmid=0.5, - colorbar=dict(title="Excited State Probability"), + colorscale="viridis", + zmid=0.0, + colorbar=dict(title="Expected value of Z"), ) ) From 42aea449a92f8fb89de0019990727bbf66139d1e Mon Sep 17 00:00:00 2001 From: jevillegasd Date: Tue, 19 May 2026 13:01:47 +0400 Subject: [PATCH 06/10] change the flip experiment sequence --- src/qibocal/protocols/flipping.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qibocal/protocols/flipping.py b/src/qibocal/protocols/flipping.py index 3b5b5a321e..164cb652d7 100644 --- a/src/qibocal/protocols/flipping.py +++ b/src/qibocal/protocols/flipping.py @@ -46,12 +46,13 @@ def flipping_sequence( qd_pulse, amplitude=qd_pulse.amplitude + delta_amplitude ) sequence.append((qd_channel, qd_detuned)) - sequence.append((qd_channel, qd_detuned)) + # sequence.append((qd_channel, qd_detuned)) if rx90: sequence.append((qd_channel, qd_detuned)) sequence.append((qd_channel, qd_detuned)) + sequence |= natives.R(theta=np.pi / 2, phi = 0.0 if flips%2 == 0 else np.pi) sequence |= natives.MZ() return sequence From 64715513a4a9459d41ebbe18067152a355cd5c97 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 09:02:11 +0000 Subject: [PATCH 07/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibocal/protocols/flipping.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibocal/protocols/flipping.py b/src/qibocal/protocols/flipping.py index 164cb652d7..c408dc7a32 100644 --- a/src/qibocal/protocols/flipping.py +++ b/src/qibocal/protocols/flipping.py @@ -52,7 +52,7 @@ def flipping_sequence( sequence.append((qd_channel, qd_detuned)) sequence.append((qd_channel, qd_detuned)) - sequence |= natives.R(theta=np.pi / 2, phi = 0.0 if flips%2 == 0 else np.pi) + sequence |= natives.R(theta=np.pi / 2, phi=0.0 if flips % 2 == 0 else np.pi) sequence |= natives.MZ() return sequence From a8799495c6792dd40a7ddf8c6c34157e4e410002 Mon Sep 17 00:00:00 2001 From: Paul Tan Date: Tue, 19 May 2026 22:43:41 +0800 Subject: [PATCH 08/10] feat(flipping): add sweeper support for amp protocol --- src/qibocal/protocols/flipping_amplitude.py | 85 +++++++++++++-------- 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/src/qibocal/protocols/flipping_amplitude.py b/src/qibocal/protocols/flipping_amplitude.py index d7aa7dcd98..23d5dfcae3 100644 --- a/src/qibocal/protocols/flipping_amplitude.py +++ b/src/qibocal/protocols/flipping_amplitude.py @@ -1,11 +1,20 @@ """Flipping experiment sweeping number of flips and pulse amplitude.""" +from collections import defaultdict 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, PulseSequence, Readout +from qibolab import ( + AcquisitionType, + AveragingMode, + Parameter, + Pulse, + PulseSequence, + Readout, + Sweeper, +) from qibocal import update from qibocal.auto.operation import Data, Parameters, QubitId, Results, Routine @@ -123,50 +132,66 @@ def _acquisition( ) sequences: list[PulseSequence] = [] - sweep_params: list[tuple[int, float]] = [] + pulses_to_sweep: defaultdict[QubitId, list[Pulse]] = defaultdict(list) + parallel_sweepers: list[Sweeper] = [] for flips in flips_range: - # TODO: The inner loop can be improved using a sweeper - for delta_amp in delta_amplitude_range: - sequence = PulseSequence() - for qubit in targets: - sequence += flipping_sequence( - platform=platform, - qubit=qubit, - delta_amplitude=float(delta_amp), - flips=flips, - rx90=params.rx90, - ) - sequences.append(sequence) - sweep_params.append((flips, float(delta_amp))) + sequence = PulseSequence() + for qubit in targets: + seq = flipping_sequence( + platform=platform, + qubit=qubit, + delta_amplitude=0, + flips=flips, + rx90=params.rx90, + ) + # First two pulses are the pi/2 pulse and unused alignment and the last pulse is a readout alignment + # So we add every pulse in-between + # Technically we can reduce the number of pulses by filtering by unique UUID + pulses_to_sweep[qubit].extend( + list(seq.channel(platform.qubits[qubit].drive))[2:-1] + ) + sequence += seq + sequences.append(sequence) + + for qubit in targets: + parallel_sweepers.append( + Sweeper( + parameter=Parameter.amplitude, + values=data.pulse_amplitudes[qubit] + delta_amplitude_range, + pulses=pulses_to_sweep[qubit], + ) + ) results = platform.execute( sequences, + sweepers=[parallel_sweepers], acquisition_type=AcquisitionType.DISCRIMINATION, averaging_mode=AveragingMode.CYCLIC, nshots=params.nshots, relaxation_time=params.relaxation_time, ) - for (flips, delta_amp), sequence in zip(sweep_params, sequences): - for qubit in targets: + for flips, sequence in zip(flips_range, sequences): + for qubit, sweeper in zip(targets, parallel_sweepers): acq_channel = platform.qubits[qubit].acquisition assert acq_channel is not None ro_pulse = list(sequence.channel(acq_channel))[-1] assert isinstance(ro_pulse, Readout) - prob = results[ro_pulse.id] - error = np.sqrt(prob * (1 - prob) / params.nshots) - native_amp = data.pulse_amplitudes[qubit] - data.register_qubit( - FlippingAmplitudeType, - qubit, - { - "flips": np.array([flips]), - "amplitude": np.array([native_amp + delta_amp]), - "prob": np.array([prob]), - "error": np.array([error]), - }, - ) + prob_array = results[ro_pulse.id] + assert len(prob_array) == len(sweeper.values) + for amp, prob in zip(sweeper.values, prob_array): + error = np.sqrt(prob * (1 - prob) / params.nshots) + data.register_qubit( + FlippingAmplitudeType, + qubit, + { + "flips": np.array([flips]), + "amplitude": np.array([amp]), + "prob": np.array([prob]), + "error": np.array([error]), + }, + ) return data From 001c80c990c77761491aff673f59aa14a5b46bc5 Mon Sep 17 00:00:00 2001 From: Paul Tan Date: Wed, 20 May 2026 15:52:59 +0800 Subject: [PATCH 09/10] style(flipping): update type hint --- src/qibocal/protocols/flipping_amplitude.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qibocal/protocols/flipping_amplitude.py b/src/qibocal/protocols/flipping_amplitude.py index 23d5dfcae3..34a15feb7f 100644 --- a/src/qibocal/protocols/flipping_amplitude.py +++ b/src/qibocal/protocols/flipping_amplitude.py @@ -9,6 +9,7 @@ from qibolab import ( AcquisitionType, AveragingMode, + ParallelSweepers, Parameter, Pulse, PulseSequence, @@ -133,7 +134,7 @@ def _acquisition( sequences: list[PulseSequence] = [] pulses_to_sweep: defaultdict[QubitId, list[Pulse]] = defaultdict(list) - parallel_sweepers: list[Sweeper] = [] + parallel_sweepers: ParallelSweepers = [] for flips in flips_range: sequence = PulseSequence() From 776a94df770ec5ae3cee500e56524c1a0cab87ce Mon Sep 17 00:00:00 2001 From: Paul Tan Date: Wed, 20 May 2026 21:41:46 +0800 Subject: [PATCH 10/10] perf(flipping): optimize sequence generation and pulse-sweeper assignment --- src/qibocal/protocols/flipping_amplitude.py | 48 ++++++++++----------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/src/qibocal/protocols/flipping_amplitude.py b/src/qibocal/protocols/flipping_amplitude.py index 34a15feb7f..9d9e95ab60 100644 --- a/src/qibocal/protocols/flipping_amplitude.py +++ b/src/qibocal/protocols/flipping_amplitude.py @@ -1,6 +1,5 @@ """Flipping experiment sweeping number of flips and pulse amplitude.""" -from collections import defaultdict from dataclasses import dataclass, field import numpy as np @@ -22,8 +21,6 @@ from qibocal.calibration import CalibrationPlatform from qibocal.protocols.utils import table_dict, table_html -from .flipping import flipping_sequence - __all__ = ["flipping_amplitude"] @@ -133,36 +130,35 @@ def _acquisition( ) sequences: list[PulseSequence] = [] - pulses_to_sweep: defaultdict[QubitId, list[Pulse]] = defaultdict(list) - parallel_sweepers: ParallelSweepers = [] + pulse_to_sweep: dict[QubitId, Pulse] = {} + pulses_store: dict[QubitId, tuple(PulseSequence, PulseSequence)] = {} + + for qubit in targets: + pulses_store[qubit] = ( + platform.natives.single_qubit[qubit].R(np.pi / 2), + platform.natives.single_qubit[qubit].RX90() * 4 + if params.rx90 + else platform.natives.single_qubit[qubit].RX() * 2, + ) + pulse_to_sweep[qubit] = pulses_store[qubit][1][0][1] for flips in flips_range: sequence = PulseSequence() for qubit in targets: - seq = flipping_sequence( - platform=platform, - qubit=qubit, - delta_amplitude=0, - flips=flips, - rx90=params.rx90, - ) - # First two pulses are the pi/2 pulse and unused alignment and the last pulse is a readout alignment - # So we add every pulse in-between - # Technically we can reduce the number of pulses by filtering by unique UUID - pulses_to_sweep[qubit].extend( - list(seq.channel(platform.qubits[qubit].drive))[2:-1] - ) - sequence += seq + rx90, qd_seq = pulses_store[qubit] + sequence += (rx90 + qd_seq * flips) | platform.natives.single_qubit[ + qubit + ].MZ() sequences.append(sequence) - for qubit in targets: - parallel_sweepers.append( - Sweeper( - parameter=Parameter.amplitude, - values=data.pulse_amplitudes[qubit] + delta_amplitude_range, - pulses=pulses_to_sweep[qubit], - ) + parallel_sweepers: ParallelSweepers = [ + Sweeper( + parameter=Parameter.amplitude, + values=data.pulse_amplitudes[qubit] + delta_amplitude_range, + pulses=[pulse_to_sweep[qubit]], ) + for qubit in targets + ] results = platform.execute( sequences,