From 868dd1cd3f1d6c0da9b4fe304835f970cb0f32d2 Mon Sep 17 00:00:00 2001 From: Andrea Date: Fri, 12 Jan 2024 17:56:42 +0400 Subject: [PATCH 1/8] fix: Implement proper drag pulse fit --- .../allxy/drag_pulse_tuning.py | 113 ++++++------------ 1 file changed, 34 insertions(+), 79 deletions(-) diff --git a/src/qibocal/protocols/characterization/allxy/drag_pulse_tuning.py b/src/qibocal/protocols/characterization/allxy/drag_pulse_tuning.py index 0e2e0b603..a5aa12837 100644 --- a/src/qibocal/protocols/characterization/allxy/drag_pulse_tuning.py +++ b/src/qibocal/protocols/characterization/allxy/drag_pulse_tuning.py @@ -17,6 +17,9 @@ from ..utils import table_dict, table_html from . import allxy_drag_pulse_tuning +# TODO: implement unrolling +# TODO: add errors in fitting + @dataclass class DragPulseTuningParameters(allxy_drag_pulse_tuning.AllXYDragParameters): @@ -40,7 +43,9 @@ class DragPulseTuningResults(Results): """Raw fitting output.""" -DragPulseTuningType = np.dtype([("signal", np.float64), ("beta", np.float64)]) +DragPulseTuningType = np.dtype( + [("prob", np.float64), ("error", np.float64), ("beta", np.float64)] +) @dataclass @@ -58,8 +63,7 @@ def _acquisition( ) -> DragPulseTuningData: r""" Data acquisition for drag pulse tuning experiment. - In this experiment, we apply two sequences in a given qubit: Rx(pi/2) - Ry(pi) and Ry(pi) - Rx(pi/2) for a range - of different beta parameter values. After fitting, we obtain the best coefficient value for a pi pulse with drag shape. + See https://arxiv.org/pdf/1504.06597.pdf Fig. 2 (c). """ # define the parameter to sweep and its range: # qubit drive DRAG pulse beta parameter @@ -70,80 +74,50 @@ def _acquisition( data = DragPulseTuningData() for beta_param in beta_param_range: - # create two sequences of pulses - # seq1: RX(pi/2) - RY(pi) - MZ - # seq1: RY(pi/2) - RX(pi) - MZ - ro_pulses = {} - seq1 = PulseSequence() - seq2 = PulseSequence() + sequence = PulseSequence() for qubit in qubits: - # drag pulse RX(pi/2) RX90_drag_pulse = platform.create_RX90_drag_pulse( qubit, start=0, beta=beta_param ) - # drag pulse RY(pi) - RY_drag_pulse = platform.create_RX_drag_pulse( - qubit, - start=RX90_drag_pulse.finish, - relative_phase=+np.pi / 2, - beta=beta_param, - ) - # drag pulse RY(pi/2) - RY90_drag_pulse = platform.create_RX90_drag_pulse( - qubit, start=0, relative_phase=np.pi / 2, beta=beta_param - ) - # drag pulse RX(pi) - RX_drag_pulse = platform.create_RX_drag_pulse( - qubit, start=RY90_drag_pulse.finish, beta=beta_param + + # TODO: Is this X_{-pi/2}? + RXm90_drag_pulse = platform.create_RX90_drag_pulse( + qubit, start=RX90_drag_pulse.finish, beta=beta_param ) + RXm90_drag_pulse.amplitude = -RXm90_drag_pulse.amplitude # RO pulse ro_pulses[qubit] = platform.create_qubit_readout_pulse( qubit, - start=2 - * RX90_drag_pulse.duration, # assumes all single-qubit gates have same duration + start=RXm90_drag_pulse.finish, ) - # RX(pi/2) - RY(pi) - RO - seq1.add(RX90_drag_pulse) - seq1.add(RY_drag_pulse) - seq1.add(ro_pulses[qubit]) - - # RX(pi/2) - RY(pi) - RO - seq2.add(RY90_drag_pulse) - seq2.add(RX_drag_pulse) - seq2.add(ro_pulses[qubit]) + # RX(pi/2) - RX(-pi/2) - RO + sequence.add(RX90_drag_pulse) + sequence.add(RXm90_drag_pulse) + sequence.add(ro_pulses[qubit]) # execute the pulse sequences - result1 = platform.execute_pulse_sequence( - seq1, - ExecutionParameters( - nshots=params.nshots, - relaxation_time=params.relaxation_time, - acquisition_type=AcquisitionType.INTEGRATION, - averaging_mode=AveragingMode.CYCLIC, - ), - ) - result2 = platform.execute_pulse_sequence( - seq2, + result = platform.execute_pulse_sequence( + sequence, ExecutionParameters( nshots=params.nshots, relaxation_time=params.relaxation_time, - acquisition_type=AcquisitionType.INTEGRATION, - averaging_mode=AveragingMode.CYCLIC, + acquisition_type=AcquisitionType.DISCRIMINATION, + averaging_mode=AveragingMode.SINGLESHOT, ), ) # retrieve the results for every qubit for qubit in qubits: - r1 = result1[ro_pulses[qubit].serial] - r2 = result2[ro_pulses[qubit].serial] + prob = result[qubit].probability(state=0) # store the results data.register_qubit( DragPulseTuningType, (qubit), dict( - signal=np.array([r1.magnitude - r2.magnitude]), + prob=np.array([prob]), + error=np.array([np.sqrt(prob * (1 - prob) / params.nshots)]), beta=np.array([beta_param]), ), ) @@ -151,37 +125,24 @@ def _acquisition( return data -def drag_fit(x, p0, p1, p2, p3): - # Offset : p[0] - # Amplitude : p[1] - # Period : p[2] - # Phase : p[3] - return p0 + p1 * np.cos(2 * np.pi * x / p2 + p3) +def drag_fit(x, offset, amplitude, frequency, phase): + return offset + amplitude * np.cos(2 * np.pi * x * frequency + phase) def _fit(data: DragPulseTuningData) -> DragPulseTuningResults: - r""" - Fitting routine for drag tunning. The used model is - - .. math:: - - y = p_1 cos \Big(\frac{2 \pi x}{p_2} + p_3 \Big) + p_0. - - """ qubits = data.qubits betas_optimal = {} fitted_parameters = {} for qubit in qubits: qubit_data = data[qubit] - voltages = qubit_data.signal + prob = qubit_data.prob beta_params = qubit_data.beta try: - popt, pcov = curve_fit(drag_fit, beta_params, voltages) - smooth_dataset = drag_fit(beta_params, popt[0], popt[1], popt[2], popt[3]) - min_abs_index = np.abs(smooth_dataset).argmin() - beta_optimal = beta_params[min_abs_index] + popt, _ = curve_fit(drag_fit, beta_params, prob) + predicted_prob = drag_fit(beta_params, *popt) + beta_optimal = beta_params[np.argmax(predicted_prob)] except: log.warning("drag_tuning_fit: the fitting was not succesful") popt = np.array([0, 0, 1, 0]) @@ -208,7 +169,7 @@ def _plot(data: DragPulseTuningData, qubit, fit: DragPulseTuningResults): fig.add_trace( go.Scatter( x=qubit_data.beta, - y=qubit_data.signal, + y=qubit_data.prob, mode="markers", name="Probability", showlegend=True, @@ -227,13 +188,7 @@ def _plot(data: DragPulseTuningData, qubit, fit: DragPulseTuningResults): fig.add_trace( go.Scatter( x=beta_range, - y=drag_fit( - beta_range, - float(fit.fitted_parameters[qubit][0]), - float(fit.fitted_parameters[qubit][1]), - float(fit.fitted_parameters[qubit][2]), - float(fit.fitted_parameters[qubit][3]), - ), + y=drag_fit(beta_range, *fit.fitted_parameters[qubit]), name="Fit", line=go.scatter.Line(dash="dot"), ), @@ -245,7 +200,7 @@ def _plot(data: DragPulseTuningData, qubit, fit: DragPulseTuningResults): fig.update_layout( showlegend=True, xaxis_title="Beta parameter", - yaxis_title="Signal [a.u.] [Rx(pi/2) - Ry(pi)] - [Ry(pi/2) - Rx(pi)]", + yaxis_title="Ground State Probability", ) figures.append(fig) From 57f07c4ef1ce22855ab3e454119ab0cee2fe424a Mon Sep 17 00:00:00 2001 From: Andrea Date: Fri, 15 Mar 2024 09:38:08 -0400 Subject: [PATCH 2/8] fix: Fix lint --- .../protocols/characterization/drag.py | 211 ++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 src/qibocal/protocols/characterization/drag.py diff --git a/src/qibocal/protocols/characterization/drag.py b/src/qibocal/protocols/characterization/drag.py new file mode 100644 index 000000000..9057f9a45 --- /dev/null +++ b/src/qibocal/protocols/characterization/drag.py @@ -0,0 +1,211 @@ +from dataclasses import dataclass, field + +import numpy as np +import numpy.typing as npt +import plotly.graph_objects as go +from plotly.subplots import make_subplots +from qibolab import AcquisitionType, AveragingMode, ExecutionParameters +from qibolab.platform import Platform +from qibolab.pulses import PulseSequence +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.config import log + +from .utils import table_dict, table_html + +# TODO: implement unrolling +# TODO: add errors in fitting + + +@dataclass +class DragPulseTuningParameters(Parameters): + """DragPulseTuning runcard inputs.""" + + beta_start: float + """DRAG pulse beta start sweep parameter.""" + beta_end: float + """DRAG pulse beta end sweep parameter.""" + beta_step: float + """DRAG pulse beta sweep step parameter.""" + + +@dataclass +class DragPulseTuningResults(Results): + """DragPulseTuning outputs.""" + + betas: dict[QubitId, float] + """Optimal beta paramter for each qubit.""" + fitted_parameters: dict[QubitId, dict[str, float]] + """Raw fitting output.""" + + +DragPulseTuningType = np.dtype( + [("prob", np.float64), ("error", np.float64), ("beta", np.float64)] +) + + +@dataclass +class DragPulseTuningData(Data): + """DragPulseTuning acquisition outputs.""" + + data: dict[QubitId, npt.NDArray[DragPulseTuningType]] = field(default_factory=dict) + """Raw data acquired.""" + + +def _acquisition( + params: DragPulseTuningParameters, + platform: Platform, + targets: list[QubitId], +) -> DragPulseTuningData: + r""" + Data acquisition for drag pulse tuning experiment. + See https://arxiv.org/pdf/1504.06597.pdf Fig. 2 (c). + """ + # define the parameter to sweep and its range: + # qubit drive DRAG pulse beta parameter + beta_param_range = np.arange( + params.beta_start, params.beta_end, params.beta_step + ).round(4) + + data = DragPulseTuningData() + + for beta_param in beta_param_range: + ro_pulses = {} + sequence = PulseSequence() + for qubit in targets: + RX90_drag_pulse = platform.create_RX90_drag_pulse( + qubit, start=0, beta=beta_param + ) + + # TODO: Is this X_{-pi/2}? + RXm90_drag_pulse = platform.create_RX90_drag_pulse( + qubit, start=RX90_drag_pulse.finish, beta=beta_param + ) + RXm90_drag_pulse.amplitude = -RXm90_drag_pulse.amplitude + + # RO pulse + ro_pulses[qubit] = platform.create_qubit_readout_pulse( + qubit, + start=RXm90_drag_pulse.finish, + ) + # RX(pi/2) - RX(-pi/2) - RO + sequence.add(RX90_drag_pulse) + sequence.add(RXm90_drag_pulse) + sequence.add(ro_pulses[qubit]) + + # execute the pulse sequences + result = platform.execute_pulse_sequence( + sequence, + ExecutionParameters( + nshots=params.nshots, + relaxation_time=params.relaxation_time, + acquisition_type=AcquisitionType.DISCRIMINATION, + averaging_mode=AveragingMode.SINGLESHOT, + ), + ) + + # retrieve the results for every qubit + for qubit in targets: + prob = result[qubit].probability(state=0) + # store the results + data.register_qubit( + DragPulseTuningType, + (qubit), + dict( + prob=np.array([prob]), + error=np.array([np.sqrt(prob * (1 - prob) / params.nshots)]), + beta=np.array([beta_param]), + ), + ) + + return data + + +def drag_fit(x, offset, amplitude, frequency, phase): + return offset + amplitude * np.cos(2 * np.pi * x * frequency + phase) + + +def _fit(data: DragPulseTuningData) -> DragPulseTuningResults: + qubits = data.qubits + betas_optimal = {} + fitted_parameters = {} + + for qubit in qubits: + qubit_data = data[qubit] + prob = qubit_data.prob + beta_params = qubit_data.beta + + try: + popt, _ = curve_fit(drag_fit, beta_params, prob) + predicted_prob = drag_fit(beta_params, *popt) + betas_optimal[qubit] = beta_params[np.argmax(predicted_prob)] + except Exception as e: + log.warning(f"drag_tuning_fit failed for qubit {qubit} due to {e}.") + + return DragPulseTuningResults(betas_optimal, fitted_parameters) + + +def _plot(data: DragPulseTuningData, target: QubitId, fit: DragPulseTuningResults): + """Plotting function for DragPulseTuning.""" + + figures = [] + fitting_report = "" + + fig = make_subplots( + rows=1, + cols=1, + horizontal_spacing=0.01, + vertical_spacing=0.01, + ) + qubit_data = data[target] + fig.add_trace( + go.Scatter( + x=qubit_data.beta, + y=qubit_data.prob, + mode="markers", + name="Probability", + showlegend=True, + legendgroup="group1", + ), + ) + + # add fitting traces + if fit is not None: + beta_range = np.linspace( + min(qubit_data.beta), + max(qubit_data.beta), + 20, + ) + + fig.add_trace( + go.Scatter( + x=beta_range, + y=drag_fit(beta_range, *fit.fitted_parameters[target]), + name="Fit", + line=go.scatter.Line(dash="dot"), + ), + ) + fitting_report = table_html( + table_dict(target, "Optimal Beta Param", np.round(fit.betas[target], 4)) + ) + + fig.update_layout( + showlegend=True, + xaxis_title="Beta parameter", + yaxis_title="Ground State Probability", + ) + + figures.append(fig) + + return figures, fitting_report + + +def _update(results: DragPulseTuningResults, platform: Platform, target: QubitId): + update.drag_pulse_beta(results.betas[target], platform, target) + + +drag_pulse_tuning = Routine(_acquisition, _fit, _plot, _update) +"""DragPulseTuning Routine object.""" From 2d5e6c01280222c212236f0d52a18c0147932e53 Mon Sep 17 00:00:00 2001 From: andrea-pasquale Date: Tue, 19 Mar 2024 16:47:01 +0400 Subject: [PATCH 3/8] refactor: Implement negative angle with phase --- .../protocols/characterization/drag.py | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/qibocal/protocols/characterization/drag.py b/src/qibocal/protocols/characterization/drag.py index 9057f9a45..e0092926b 100644 --- a/src/qibocal/protocols/characterization/drag.py +++ b/src/qibocal/protocols/characterization/drag.py @@ -76,24 +76,25 @@ def _acquisition( ro_pulses = {} sequence = PulseSequence() for qubit in targets: - RX90_drag_pulse = platform.create_RX90_drag_pulse( + RX_drag_pulse = platform.create_RX_drag_pulse( qubit, start=0, beta=beta_param ) - - # TODO: Is this X_{-pi/2}? - RXm90_drag_pulse = platform.create_RX90_drag_pulse( - qubit, start=RX90_drag_pulse.finish, beta=beta_param + RX_drag_pulse_minus = platform.create_RX_drag_pulse( + qubit, start=RX_drag_pulse.finish, beta=beta_param, relative_phase=np.pi ) - RXm90_drag_pulse.amplitude = -RXm90_drag_pulse.amplitude # RO pulse ro_pulses[qubit] = platform.create_qubit_readout_pulse( qubit, - start=RXm90_drag_pulse.finish, + start=RX_drag_pulse_minus.finish, ) - # RX(pi/2) - RX(-pi/2) - RO - sequence.add(RX90_drag_pulse) - sequence.add(RXm90_drag_pulse) + # RX(pi) + sequence.add(RX_drag_pulse) + + # RX(-pi) + sequence.add(RX_drag_pulse_minus) + + # RO sequence.add(ro_pulses[qubit]) # execute the pulse sequences @@ -140,6 +141,7 @@ def _fit(data: DragPulseTuningData) -> DragPulseTuningResults: try: popt, _ = curve_fit(drag_fit, beta_params, prob) + fitted_parameters[qubit] = popt.tolist() predicted_prob = drag_fit(beta_params, *popt) betas_optimal[qubit] = beta_params[np.argmax(predicted_prob)] except Exception as e: From 89619396e7c5854e013a46827eff66ee6609cb5f Mon Sep 17 00:00:00 2001 From: andrea-pasquale Date: Thu, 21 Mar 2024 23:40:20 +0400 Subject: [PATCH 4/8] refactor: Improve fit by normalizing x axis --- .../protocols/characterization/drag.py | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/src/qibocal/protocols/characterization/drag.py b/src/qibocal/protocols/characterization/drag.py index e0092926b..3e9ee7bed 100644 --- a/src/qibocal/protocols/characterization/drag.py +++ b/src/qibocal/protocols/characterization/drag.py @@ -9,6 +9,7 @@ from qibolab.pulses import PulseSequence from qibolab.qubits import QubitId from scipy.optimize import curve_fit +from scipy.signal import find_peaks from qibocal import update from qibocal.auto.operation import Data, Parameters, Results, Routine @@ -125,8 +126,8 @@ def _acquisition( return data -def drag_fit(x, offset, amplitude, frequency, phase): - return offset + amplitude * np.cos(2 * np.pi * x * frequency + phase) +def drag_fit(x, offset, amplitude, period, phase): + return offset + amplitude * np.cos(2 * np.pi * x / period + phase) def _fit(data: DragPulseTuningData) -> DragPulseTuningResults: @@ -136,13 +137,46 @@ def _fit(data: DragPulseTuningData) -> DragPulseTuningResults: for qubit in qubits: qubit_data = data[qubit] - prob = qubit_data.prob beta_params = qubit_data.beta + prob = qubit_data.prob + + beta_min = np.min(beta_params) + beta_max = np.max(beta_params) + normalized_beta = (beta_params - beta_min) / (beta_max - beta_min) + + # Guessing period using fourier transform + ft = np.fft.rfft(prob) + mags = abs(ft) + local_maxima = find_peaks(mags, threshold=1)[0] + index = local_maxima[0] if len(local_maxima) > 0 else None + # 0.5 hardcoded guess for less than one oscillation + f = ( + beta_params[index] / (beta_params[1] - beta_params[0]) + if index is not None + else 0.5 + ) + pguess = [0.5, 0.5, 1 / f, 0] try: - popt, _ = curve_fit(drag_fit, beta_params, prob) - fitted_parameters[qubit] = popt.tolist() - predicted_prob = drag_fit(beta_params, *popt) + popt, _ = curve_fit( + drag_fit, + normalized_beta, + prob, + p0=pguess, + maxfev=100000, + bounds=( + [0, 0, 0, -np.pi], + [1, 1, np.inf, np.pi], + ), + ) + translated_popt = [ + popt[0], + popt[1], + popt[2] * (beta_max - beta_min), + popt[3] - 2 * np.pi * beta_min / popt[2] / (beta_max - beta_min), + ] + fitted_parameters[qubit] = translated_popt + predicted_prob = drag_fit(beta_params, *translated_popt) betas_optimal[qubit] = beta_params[np.argmax(predicted_prob)] except Exception as e: log.warning(f"drag_tuning_fit failed for qubit {qubit} due to {e}.") From bfb26e93267e6589cb86a9a3462b1bf910d19a7e Mon Sep 17 00:00:00 2001 From: andrea-pasquale Date: Fri, 22 Mar 2024 00:26:03 +0400 Subject: [PATCH 5/8] feat: Add unrolling --- .../protocols/characterization/drag.py | 54 +++++++++++-------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/src/qibocal/protocols/characterization/drag.py b/src/qibocal/protocols/characterization/drag.py index 3e9ee7bed..c125eb5bf 100644 --- a/src/qibocal/protocols/characterization/drag.py +++ b/src/qibocal/protocols/characterization/drag.py @@ -31,6 +31,9 @@ class DragPulseTuningParameters(Parameters): """DRAG pulse beta end sweep parameter.""" beta_step: float """DRAG pulse beta sweep step parameter.""" + unrolling: bool = False + """If ``True`` it uses sequence unrolling to deploy multiple sequences in a single instrument call. + Defaults to ``False``.""" @dataclass @@ -73,9 +76,10 @@ def _acquisition( data = DragPulseTuningData() + sequences, all_ro_pulses = [], [] for beta_param in beta_param_range: - ro_pulses = {} sequence = PulseSequence() + ro_pulses = {} for qubit in targets: RX_drag_pulse = platform.create_RX_drag_pulse( qubit, start=0, beta=beta_param @@ -83,35 +87,39 @@ def _acquisition( RX_drag_pulse_minus = platform.create_RX_drag_pulse( qubit, start=RX_drag_pulse.finish, beta=beta_param, relative_phase=np.pi ) - - # RO pulse ro_pulses[qubit] = platform.create_qubit_readout_pulse( - qubit, - start=RX_drag_pulse_minus.finish, + qubit, start=RX_drag_pulse_minus.finish ) - # RX(pi) - sequence.add(RX_drag_pulse) - # RX(-pi) + sequence.add(RX_drag_pulse) sequence.add(RX_drag_pulse_minus) - - # RO sequence.add(ro_pulses[qubit]) + sequences.append(sequence) + all_ro_pulses.append(ro_pulses) + + options = ExecutionParameters( + nshots=params.nshots, + relaxation_time=params.relaxation_time, + acquisition_type=AcquisitionType.DISCRIMINATION, + averaging_mode=AveragingMode.SINGLESHOT, + ) + # execute the pulse sequence + if params.unrolling: + results = platform.execute_pulse_sequences(sequences, options) - # execute the pulse sequences - result = platform.execute_pulse_sequence( - sequence, - ExecutionParameters( - nshots=params.nshots, - relaxation_time=params.relaxation_time, - acquisition_type=AcquisitionType.DISCRIMINATION, - averaging_mode=AveragingMode.SINGLESHOT, - ), - ) + elif not params.unrolling: + results = [ + platform.execute_pulse_sequence(sequence, options) for sequence in sequences + ] - # retrieve the results for every qubit + for ig, (beta, ro_pulses) in enumerate(zip(beta_param_range, all_ro_pulses)): for qubit in targets: - prob = result[qubit].probability(state=0) + serial = ro_pulses[qubit].serial + if params.unrolling: + result = results[serial][0] + else: + result = results[ig][serial] + prob = result.probability(state=0) # store the results data.register_qubit( DragPulseTuningType, @@ -119,7 +127,7 @@ def _acquisition( dict( prob=np.array([prob]), error=np.array([np.sqrt(prob * (1 - prob) / params.nshots)]), - beta=np.array([beta_param]), + beta=np.array([beta]), ), ) From 51f5db26f19cbe9c9089c473b067b5af1a0d0c86 Mon Sep 17 00:00:00 2001 From: andrea-pasquale Date: Fri, 22 Mar 2024 18:51:12 +0400 Subject: [PATCH 6/8] feat: Add errors --- .../protocols/characterization/drag.py | 59 +++++++++++++------ 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/src/qibocal/protocols/characterization/drag.py b/src/qibocal/protocols/characterization/drag.py index c125eb5bf..07d3df350 100644 --- a/src/qibocal/protocols/characterization/drag.py +++ b/src/qibocal/protocols/characterization/drag.py @@ -1,9 +1,9 @@ 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 plotly.subplots import make_subplots from qibolab import AcquisitionType, AveragingMode, ExecutionParameters from qibolab.platform import Platform from qibolab.pulses import PulseSequence @@ -15,7 +15,7 @@ from qibocal.auto.operation import Data, Parameters, Results, Routine from qibocal.config import log -from .utils import table_dict, table_html +from .utils import COLORBAND, COLORBAND_LINE, chi2_reduced, table_dict, table_html # TODO: implement unrolling # TODO: add errors in fitting @@ -44,6 +44,8 @@ class DragPulseTuningResults(Results): """Optimal beta paramter for each qubit.""" fitted_parameters: dict[QubitId, dict[str, float]] """Raw fitting output.""" + chi2: dict[QubitId, tuple[float, Optional[float]]] = field(default_factory=dict) + """Chi2 calculation.""" DragPulseTuningType = np.dtype( @@ -142,6 +144,7 @@ def _fit(data: DragPulseTuningData) -> DragPulseTuningResults: qubits = data.qubits betas_optimal = {} fitted_parameters = {} + chi2 = {} for qubit in qubits: qubit_data = data[qubit] @@ -176,6 +179,7 @@ def _fit(data: DragPulseTuningData) -> DragPulseTuningResults: [0, 0, 0, -np.pi], [1, 1, np.inf, np.pi], ), + sigma=qubit_data.error, ) translated_popt = [ popt[0], @@ -186,10 +190,18 @@ def _fit(data: DragPulseTuningData) -> DragPulseTuningResults: fitted_parameters[qubit] = translated_popt predicted_prob = drag_fit(beta_params, *translated_popt) betas_optimal[qubit] = beta_params[np.argmax(predicted_prob)] + chi2[qubit] = ( + chi2_reduced( + prob, + predicted_prob, + qubit_data.error, + ), + np.sqrt(2 / len(prob)), + ) except Exception as e: log.warning(f"drag_tuning_fit failed for qubit {qubit} due to {e}.") - return DragPulseTuningResults(betas_optimal, fitted_parameters) + return DragPulseTuningResults(betas_optimal, fitted_parameters, chi2=chi2) def _plot(data: DragPulseTuningData, target: QubitId, fit: DragPulseTuningResults): @@ -198,22 +210,33 @@ def _plot(data: DragPulseTuningData, target: QubitId, fit: DragPulseTuningResult figures = [] fitting_report = "" - fig = make_subplots( - rows=1, - cols=1, - horizontal_spacing=0.01, - vertical_spacing=0.01, - ) qubit_data = data[target] - fig.add_trace( - go.Scatter( - x=qubit_data.beta, - y=qubit_data.prob, - mode="markers", - name="Probability", - showlegend=True, - legendgroup="group1", - ), + fig = go.Figure( + [ + go.Scatter( + x=qubit_data.beta, + y=qubit_data.prob, + opacity=1, + mode="lines", + name="Probability", + showlegend=True, + legendgroup="Probability", + ), + go.Scatter( + x=np.concatenate((qubit_data.beta, qubit_data.beta[::-1])), + y=np.concatenate( + ( + qubit_data.prob + qubit_data.error, + (qubit_data.prob - qubit_data.error)[::-1], + ) + ), + fill="toself", + fillcolor=COLORBAND, + line=dict(color=COLORBAND_LINE), + showlegend=True, + name="Errors", + ), + ] ) # add fitting traces From 2297ec43754b6c0018bd9a0177a34a2b9a5e0cb2 Mon Sep 17 00:00:00 2001 From: andrea-pasquale Date: Fri, 22 Mar 2024 19:14:04 +0400 Subject: [PATCH 7/8] feat: Add chi2 in table --- src/qibocal/auto/runcard.py | 2 +- src/qibocal/protocols/characterization/drag.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/qibocal/auto/runcard.py b/src/qibocal/auto/runcard.py index 9bb856675..f0ff10e32 100644 --- a/src/qibocal/auto/runcard.py +++ b/src/qibocal/auto/runcard.py @@ -67,7 +67,7 @@ def backend_obj(self) -> Backend: """Allocate backend.""" GlobalBackend.set_backend(self.backend, platform=self.platform) backend = GlobalBackend() - # FIXME: remove this latin abomination + # FIXME: remove transpiler lines backend.transpiler = Passes(connectivity=backend.platform.topology) backend.transpiler.passes = backend.transpiler.passes[-1:] return backend diff --git a/src/qibocal/protocols/characterization/drag.py b/src/qibocal/protocols/characterization/drag.py index 07d3df350..d0debf1c0 100644 --- a/src/qibocal/protocols/characterization/drag.py +++ b/src/qibocal/protocols/characterization/drag.py @@ -256,7 +256,12 @@ def _plot(data: DragPulseTuningData, target: QubitId, fit: DragPulseTuningResult ), ) fitting_report = table_html( - table_dict(target, "Optimal Beta Param", np.round(fit.betas[target], 4)) + table_dict( + target, + ["Optimal Beta Param", "Chi2 reduced"], + [(np.round(fit.betas[target], 4), 0), fit.chi2[target]], + display_error=True, + ) ) fig.update_layout( From f38ce29aa0f5064c0a861ae54c049c3199afd0fa Mon Sep 17 00:00:00 2001 From: andrea-pasquale Date: Thu, 4 Apr 2024 15:40:07 +0400 Subject: [PATCH 8/8] refactor: Juan suggestions --- .../protocols/characterization/__init__.py | 6 +-- .../protocols/characterization/drag.py | 41 +++++++++---------- tests/runcards/protocols.yml | 10 +---- 3 files changed, 23 insertions(+), 34 deletions(-) diff --git a/src/qibocal/protocols/characterization/__init__.py b/src/qibocal/protocols/characterization/__init__.py index 2763d0a4e..4a5178fa1 100644 --- a/src/qibocal/protocols/characterization/__init__.py +++ b/src/qibocal/protocols/characterization/__init__.py @@ -1,7 +1,6 @@ from enum import Enum from .allxy.allxy import allxy -from .allxy.allxy_drag_pulse_tuning import allxy_drag_pulse_tuning from .classification import single_shot_classification from .coherence.spin_echo import spin_echo from .coherence.spin_echo_signal import spin_echo_signal @@ -18,7 +17,7 @@ from .couplers.coupler_resonator_spectroscopy import coupler_resonator_spectroscopy from .dispersive_shift import dispersive_shift from .dispersive_shift_qutrit import dispersive_shift_qutrit -from .drag import drag_pulse_tuning +from .drag import drag_tuning from .fast_reset.fast_reset import fast_reset from .flipping import flipping from .flipping_signal import flipping_signal @@ -93,8 +92,7 @@ class Operation(Enum): spin_echo = spin_echo spin_echo_signal = spin_echo_signal allxy = allxy - allxy_drag_pulse_tuning = allxy_drag_pulse_tuning - drag_pulse_tuning = drag_pulse_tuning + drag_tuning = drag_tuning flipping = flipping dispersive_shift = dispersive_shift chevron = chevron diff --git a/src/qibocal/protocols/characterization/drag.py b/src/qibocal/protocols/characterization/drag.py index d0debf1c0..cfa6ead88 100644 --- a/src/qibocal/protocols/characterization/drag.py +++ b/src/qibocal/protocols/characterization/drag.py @@ -17,13 +17,12 @@ from .utils import COLORBAND, COLORBAND_LINE, chi2_reduced, table_dict, table_html -# TODO: implement unrolling # TODO: add errors in fitting @dataclass -class DragPulseTuningParameters(Parameters): - """DragPulseTuning runcard inputs.""" +class DragTuningParameters(Parameters): + """DragTuning runcard inputs.""" beta_start: float """DRAG pulse beta start sweep parameter.""" @@ -37,8 +36,8 @@ class DragPulseTuningParameters(Parameters): @dataclass -class DragPulseTuningResults(Results): - """DragPulseTuning outputs.""" +class DragTuningResults(Results): + """DragTuning outputs.""" betas: dict[QubitId, float] """Optimal beta paramter for each qubit.""" @@ -48,24 +47,24 @@ class DragPulseTuningResults(Results): """Chi2 calculation.""" -DragPulseTuningType = np.dtype( +DragTuningType = np.dtype( [("prob", np.float64), ("error", np.float64), ("beta", np.float64)] ) @dataclass -class DragPulseTuningData(Data): - """DragPulseTuning acquisition outputs.""" +class DragTuningData(Data): + """DragTuning acquisition outputs.""" - data: dict[QubitId, npt.NDArray[DragPulseTuningType]] = field(default_factory=dict) + data: dict[QubitId, npt.NDArray[DragTuningType]] = field(default_factory=dict) """Raw data acquired.""" def _acquisition( - params: DragPulseTuningParameters, + params: DragTuningParameters, platform: Platform, targets: list[QubitId], -) -> DragPulseTuningData: +) -> DragTuningData: r""" Data acquisition for drag pulse tuning experiment. See https://arxiv.org/pdf/1504.06597.pdf Fig. 2 (c). @@ -76,7 +75,7 @@ def _acquisition( params.beta_start, params.beta_end, params.beta_step ).round(4) - data = DragPulseTuningData() + data = DragTuningData() sequences, all_ro_pulses = [], [] for beta_param in beta_param_range: @@ -118,13 +117,13 @@ def _acquisition( for qubit in targets: serial = ro_pulses[qubit].serial if params.unrolling: - result = results[serial][0] + result = results[serial][ig] else: result = results[ig][serial] prob = result.probability(state=0) # store the results data.register_qubit( - DragPulseTuningType, + DragTuningType, (qubit), dict( prob=np.array([prob]), @@ -140,7 +139,7 @@ def drag_fit(x, offset, amplitude, period, phase): return offset + amplitude * np.cos(2 * np.pi * x / period + phase) -def _fit(data: DragPulseTuningData) -> DragPulseTuningResults: +def _fit(data: DragTuningData) -> DragTuningResults: qubits = data.qubits betas_optimal = {} fitted_parameters = {} @@ -201,11 +200,11 @@ def _fit(data: DragPulseTuningData) -> DragPulseTuningResults: except Exception as e: log.warning(f"drag_tuning_fit failed for qubit {qubit} due to {e}.") - return DragPulseTuningResults(betas_optimal, fitted_parameters, chi2=chi2) + return DragTuningResults(betas_optimal, fitted_parameters, chi2=chi2) -def _plot(data: DragPulseTuningData, target: QubitId, fit: DragPulseTuningResults): - """Plotting function for DragPulseTuning.""" +def _plot(data: DragTuningData, target: QubitId, fit: DragTuningResults): + """Plotting function for DragTuning.""" figures = [] fitting_report = "" @@ -275,9 +274,9 @@ def _plot(data: DragPulseTuningData, target: QubitId, fit: DragPulseTuningResult return figures, fitting_report -def _update(results: DragPulseTuningResults, platform: Platform, target: QubitId): +def _update(results: DragTuningResults, platform: Platform, target: QubitId): update.drag_pulse_beta(results.betas[target], platform, target) -drag_pulse_tuning = Routine(_acquisition, _fit, _plot, _update) -"""DragPulseTuning Routine object.""" +drag_tuning = Routine(_acquisition, _fit, _plot, _update) +"""DragTuning Routine object.""" diff --git a/tests/runcards/protocols.yml b/tests/runcards/protocols.yml index ba9d7d37c..be312bc69 100644 --- a/tests/runcards/protocols.yml +++ b/tests/runcards/protocols.yml @@ -490,17 +490,9 @@ actions: unrolling: True nshots: 10 - - id: allxy_drag_pulse_tuning - operation: allxy_drag_pulse_tuning - parameters: - beta_start: 0 - beta_end: 0.04 - beta_step: 0.01 - nshots: 10 - - id: drag_pulse_tuning - operation: drag_pulse_tuning + operation: drag_tuning parameters: beta_start: 0 beta_end: 0.1