From a39f39a31b979af9b9f9472a5488ed0e00923321 Mon Sep 17 00:00:00 2001 From: DavidSarlle Date: Mon, 23 Oct 2023 23:42:08 +0400 Subject: [PATCH 01/14] initial commit --- .../protocols/characterization/__init__.py | 2 + .../twpa_calibration/frequency_power.py | 198 ++++++++++++++++++ 2 files changed, 200 insertions(+) create mode 100644 src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py diff --git a/src/qibocal/protocols/characterization/__init__.py b/src/qibocal/protocols/characterization/__init__.py index e4664477a..8b77c371a 100644 --- a/src/qibocal/protocols/characterization/__init__.py +++ b/src/qibocal/protocols/characterization/__init__.py @@ -38,6 +38,7 @@ from .readout_optimization.resonator_frequency import resonator_frequency from .readout_optimization.twpa_calibration.frequency import twpa_frequency from .readout_optimization.twpa_calibration.power import twpa_power +from .readout_optimization.twpa_calibration.frequency_power import twpa_frequency_power from .resonator_punchout import resonator_punchout from .resonator_punchout_attenuation import resonator_punchout_attenuation from .resonator_spectroscopy import resonator_spectroscopy @@ -90,6 +91,7 @@ class Operation(Enum): readout_mitigation_matrix = readout_mitigation_matrix twpa_frequency = twpa_frequency twpa_power = twpa_power + twpa_frequency_power = twpa_frequency_power rabi_amplitude_ef = rabi_amplitude_ef qubit_spectroscopy_ef = qubit_spectroscopy_ef resonator_amplitude = resonator_amplitude diff --git a/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py b/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py new file mode 100644 index 000000000..ed0aa9817 --- /dev/null +++ b/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py @@ -0,0 +1,198 @@ +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.platform import Platform +from qibolab.qubits import QubitId +from sklearn.model_selection import train_test_split + +from qibocal.auto.operation import Data, Parameters, Qubits, Results, Routine +from qibocal.fitting.classifier.qubit_fit import QubitFit +from qibocal.fitting.classifier.run import benchmarking +from qibocal.protocols.characterization import classification +from qibocal.protocols.characterization.utils import HZ_TO_GHZ + + +@dataclass +class TwpaFrequencyPowerParameters(Parameters): + """Twpa Frequency Power runcard inputs.""" + + frequency_width: float + frequency_step: float + power_width: float + """Power total width.""" + power_step: float + """Power step to be probed.""" + + nshots: Optional[int] = None + """Number of shots.""" + relaxation_time: Optional[int] = None + """Relaxation time (ns).""" + + +@dataclass +class TwpaFrequencyPowerData(Data): + """Twpa Frequency Power acquisition outputs.""" + + data: dict[ + tuple[QubitId, float, float], npt.NDArray[classification.ClassificationType] + ] = field(default_factory=dict) + """Raw data acquired.""" + + def register_freq_pow( + self, + qubit: QubitId, + freq: float, + pow: float, + classification_data: npt.NDArray[classification.ClassificationType], + ): + self.data[qubit, freq, pow] = classification_data[qubit] + + +@dataclass +class TwpaFrequencyPowerResults(Results): + """Twpa Frequency Power outputs.""" + + fidelities: dict[QubitId, float, float] = field(default_factory=dict) + + def __getitem__(self, qubit: QubitId): + return { + index: value + for index, value in self.fidelities.items() + if index[0] == qubit + } + + +def _acquisition( + params: TwpaFrequencyPowerParameters, + platform: Platform, + qubits: Qubits, +) -> TwpaFrequencyPowerData: + r""" + Data acquisition for TWPA power optmization. + This protocol perform a classification protocol for twpa frequencies + in the range [twpa_frequency - frequency_width / 2, twpa_frequency + frequency_width / 2] + with step frequency_step. + + Args: + params (:class:`TwpaFrequencyParameters`): input parameters + platform (:class:`Platform`): Qibolab's platform + qubits (dict): dict of target :class:`Qubit` objects to be characterized + + Returns: + data (:class:`TwpaFrequencyData`) + """ + + data = TwpaFrequencyPowerData() + + freq_range = np.arange( + -params.frequency_width / 2, params.frequency_width / 2, params.frequency_step + ).astype(int) + power_range = np.arange( + -params.power_width / 2, params.power_width / 2, params.power_step + ) + data = TwpaFrequencyPowerData() + + initial_twpa_freq = {} + initial_twpa_power = {} + for qubit in qubits: + initial_twpa_freq[qubit] = platform.qubits[ + qubit + ].twpa.local_oscillator.frequency + initial_twpa_power[qubit] = platform.qubits[qubit].twpa.local_oscillator.power + + for freq in freq_range: + for qubit in qubits: + platform.qubits[qubit].twpa.local_oscillator.frequency = ( + initial_twpa_freq[qubit] + freq + ) + + for power in power_range: + for qubit in qubits: + platform.qubits[qubit].twpa.local_oscillator.power = ( + initial_twpa_power[qubit] + power + ) + + # classification_data = classification._acquisition( + # classification.SingleShotClassificationParameters(nshots=params.nshots), + # platform, + # qubits, + # ) + + classification_data = classification._acquisition( + classification.SingleShotClassificationParameters.load( + {"nshots": params.nshots} + ), + platform, + qubits, + ) + + for qubit in qubits: + data.register_freq_pow( + qubit, + platform.qubits[qubit].twpa.local_oscillator.frequency, + platform.qubits[qubit].twpa.local_oscillator.power, + classification_data, + ) + + return data + + +def _fit(data: TwpaFrequencyPowerData) -> TwpaFrequencyPowerResults: + """Extract fidelity for each configuration qubit / param. + Where param can be either frequency or power.""" + fidelities = {} + for qubit, freq, pow in data.data: + qubit_data = data.data[qubit, freq, pow] + x_train, x_test, y_train, y_test = train_test_split( + np.array(qubit_data[["i", "q"]].tolist())[:, :], + np.array(qubit_data[["state"]].tolist())[:, 0], + test_size=0.25, + random_state=0, + shuffle=True, + ) + + model = QubitFit() + results, y_pred, model, fit_info = benchmarking( + model, x_train, y_train, x_test, y_test + ) + fidelities[qubit, freq, pow] = model.assignment_fidelity + + return TwpaFrequencyPowerResults(fidelities=fidelities) + + +def _plot(data: TwpaFrequencyPowerData, fit: TwpaFrequencyPowerResults, qubit): + """Plotting function that shows the assignment fidelity + for different values of the twpa frequency and power for a single qubit""" + + figures = [] + fitting_report = "No fitting data" + qubit_fit = fit[qubit] + freqs = [] + pows = [] + fidelities = [] + for _, freq, pow in qubit_fit: + freqs.append(freq * HZ_TO_GHZ) + pows.append(pow) + fidelities.append(qubit_fit[qubit, freq, pow]) + + fitting_report = f"{qubit} | Best assignment fidelity: {np.max(fidelities):.3f}
" + # fitting_report += f"{qubit} | TWPA Frequency: {int(freqs[np.argmax(fidelities)]*GHZ_TO_HZ)} Hz
" + + fig = go.Figure([go.Heatmap(x=freqs, y=pows, z=fidelities, name="Fidelity")]) + figures.append(fig) + + fig.update_layout( + showlegend=True, + uirevision="0", # ``uirevision`` allows zooming while live plotting + xaxis_title="TWPA Frequency [GHz]", + yaxis_title="TWPA Power [dBm]", + ) + + return figures, fitting_report + + +twpa_frequency_power = Routine(_acquisition, _fit, _plot) +"""Twpa frequency Routine object.""" \ No newline at end of file From 2770a2a9359ab76f249284e4a90cc66b69dd4ede Mon Sep 17 00:00:00 2001 From: andrea-pasquale Date: Tue, 24 Oct 2023 10:51:29 +0400 Subject: [PATCH 02/14] First implementation of qutrit dispersive shift --- .../protocols/characterization/__init__.py | 2 + .../dispersive_shift_qutrit.py | 308 ++++++++++++++++++ tests/runcards/protocols.yml | 8 + 3 files changed, 318 insertions(+) create mode 100644 src/qibocal/protocols/characterization/dispersive_shift_qutrit.py diff --git a/src/qibocal/protocols/characterization/__init__.py b/src/qibocal/protocols/characterization/__init__.py index e4664477a..3269d82ce 100644 --- a/src/qibocal/protocols/characterization/__init__.py +++ b/src/qibocal/protocols/characterization/__init__.py @@ -15,6 +15,7 @@ from .coherence.zeno import zeno from .coherence.zeno_msr import zeno_msr from .dispersive_shift import dispersive_shift +from .dispersive_shift_qutrit import dispersive_shift_qutrit from .fast_reset.fast_reset import fast_reset from .flipping import flipping from .flux_dependence.qubit_flux_dependence import qubit_crosstalk, qubit_flux @@ -93,3 +94,4 @@ class Operation(Enum): rabi_amplitude_ef = rabi_amplitude_ef qubit_spectroscopy_ef = qubit_spectroscopy_ef resonator_amplitude = resonator_amplitude + dispersive_shift_qutrit = dispersive_shift_qutrit diff --git a/src/qibocal/protocols/characterization/dispersive_shift_qutrit.py b/src/qibocal/protocols/characterization/dispersive_shift_qutrit.py new file mode 100644 index 000000000..82bc748a4 --- /dev/null +++ b/src/qibocal/protocols/characterization/dispersive_shift_qutrit.py @@ -0,0 +1,308 @@ +from copy import deepcopy +from dataclasses import asdict, dataclass + +import numpy as np +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 qibolab.sweeper import Parameter, Sweeper, SweeperType + +from qibocal.auto.operation import Qubits, Results, Routine +from qibocal.protocols.characterization.utils import ( + GHZ_TO_HZ, + HZ_TO_GHZ, + V_TO_UV, + lorentzian, + lorentzian_fit, + table_dict, + table_html, +) + +from .dispersive_shift import DispersiveShiftData, DispersiveShiftParameters + + +@dataclass +class DispersiveShiftQutritParameters(DispersiveShiftParameters): + """Dispersive shift inputs.""" + + +@dataclass +class DispersiveShiftQutritResults(Results): + """Dispersive shift outputs.""" + + frequency_state_zero: dict[QubitId, float] + """State zero frequency.""" + frequency_state_one: dict[QubitId, float] + """State one frequency.""" + frequency_state_two: dict[QubitId, float] + """State two frequency.""" + fitted_parameters_state_zero: dict[QubitId, dict[str, float]] + """Fitted parameters state zero.""" + fitted_parameters_state_one: dict[QubitId, dict[str, float]] + """Fitted parameters state one.""" + fitted_parameters_state_two: dict[QubitId, dict[str, float]] + """Fitted parameters state one.""" + + @property + def state_zero(self): + return {key: value for key, value in asdict(self).items() if "zero" in key} + + @property + def state_one(self): + return {key: value for key, value in asdict(self).items() if "one" in key} + + @property + def state_two(self): + return {key: value for key, value in asdict(self).items() if "two" in key} + + +DispersiveShiftQutritType = np.dtype( + [ + ("freq", np.float64), + ("msr", np.float64), + ("phase", np.float64), + ] +) +"""Custom dtype for rabi amplitude.""" + + +@dataclass +class DispersiveShiftQutritData(DispersiveShiftData): + """Dipsersive shift acquisition outputs.""" + + +def _acquisition( + params: DispersiveShiftParameters, platform: Platform, qubits: Qubits +) -> DispersiveShiftQutritData: + r""" + Data acquisition for dispersive shift experiment. + Perform spectroscopy on the readout resonator, with the qubit in ground and excited state, showing + the resonator shift produced by the coupling between the resonator and the qubit. + + Args: + params (DispersiveShiftParameters): experiment's parameters + platform (Platform): Qibolab platform object + qubits (dict): list of target qubits to perform the action + + """ + + # create 3 sequences of pulses for the experiment: + # sequence_0: I - MZ + # sequence_1: RX - MZ + # sequence_2: RX - RX12 - MZ + + # taking advantage of multiplexing, apply the same set of gates to all qubits in parallel + sequence_0 = PulseSequence() + sequence_1 = PulseSequence() + sequence_2 = PulseSequence() + + for qubit in platform.qubits: + rx_pulse = platform.create_RX_pulse(qubit, start=0) + rx_12_pulse = platform.create_RX12_pulse(qubit, start=rx_pulse.finish) + ro_pulse = platform.create_qubit_readout_pulse(qubit, start=0) + sequence_1.add(rx_pulse) + sequence_2.add(rx_pulse, rx_12_pulse) + for sequence in [sequence_0, sequence_1, sequence_2]: + readout_pulse = deepcopy(ro_pulse) + readout_pulse.start = sequence.qd_pulses.finish + sequence.add(readout_pulse) + + # define the parameter to sweep and its range: + delta_frequency_range = np.arange( + -params.freq_width // 2, params.freq_width // 2, params.freq_step + ) + + # create a DataUnits objects to store the results + data = DispersiveShiftQutritData(resonator_type=platform.resonator_type) + + for state, sequence in enumerate([sequence_0, sequence_1, sequence_2]): + sweeper = Sweeper( + Parameter.frequency, + delta_frequency_range, + pulses=list(sequence.ro_pulses), + type=SweeperType.OFFSET, + ) + + results = platform.sweep( + sequence, + ExecutionParameters( + nshots=params.nshots, + relaxation_time=params.relaxation_time, + acquisition_type=AcquisitionType.INTEGRATION, + averaging_mode=AveragingMode.CYCLIC, + ), + sweeper, + ) + + for qubit in qubits: + # average msr, phase, i and q over the number of shots defined in the runcard + result = results[sequence.ro_pulses[qubit].serial] + # store the results + data.register_qubit( + DispersiveShiftQutritType, + (qubit, state), + dict( + freq=sequence.ro_pulses[qubit].frequency + delta_frequency_range, + msr=result.magnitude, + phase=result.phase, + ), + ) + + return data + + +def _fit(data: DispersiveShiftQutritData) -> DispersiveShiftQutritResults: + """Post-Processing for dispersive shift""" + qubits = data.qubits + + frequency_0 = {} + frequency_1 = {} + frequency_2 = {} + fitted_parameters_0 = {} + fitted_parameters_1 = {} + fitted_parameters_2 = {} + + for i in range(3): + for qubit in qubits: + data_i = data[qubit, i] + freq, fitted_params = lorentzian_fit( + data_i, resonator_type=data.resonator_type, fit="resonator" + ) + if i == 0: + frequency_0[qubit] = freq + fitted_parameters_0[qubit] = fitted_params + elif i == 1: + frequency_1[qubit] = freq + fitted_parameters_1[qubit] = fitted_params + else: + frequency_2[qubit] = freq + fitted_parameters_2[qubit] = fitted_params + + return DispersiveShiftQutritResults( + frequency_state_zero=frequency_0, + frequency_state_one=frequency_1, + frequency_state_two=frequency_2, + fitted_parameters_state_one=fitted_parameters_1, + fitted_parameters_state_zero=fitted_parameters_0, + fitted_parameters_state_two=fitted_parameters_2, + ) + + +def _plot(data: DispersiveShiftQutritData, qubit, fit: DispersiveShiftQutritResults): + """Plotting function for dispersive shift.""" + figures = [] + fig = make_subplots( + rows=1, + cols=2, + horizontal_spacing=0.1, + vertical_spacing=0.1, + subplot_titles=( + "MSR (uV)", + "phase (rad)", + ), + ) + # iterate over multiple data folders + + fitting_report = "" + + data_0 = data[qubit, 0] + data_1 = data[qubit, 1] + data_2 = data[qubit, 2] + fit_data_0 = fit.state_zero if fit is not None else None + fit_data_1 = fit.state_one if fit is not None else None + fit_data_2 = fit.state_two if fit is not None else None + for i, label, q_data, data_fit in list( + zip( + (0, 1, 2), + ("State 0", "State 1", "State 2"), + (data_0, data_1, data_2), + (fit_data_0, fit_data_1, fit_data_2), + ) + ): + opacity = 1 + frequencies = q_data.freq * HZ_TO_GHZ + fig.add_trace( + go.Scatter( + x=frequencies, + y=q_data.msr * V_TO_UV, + opacity=opacity, + name=f"{label}", + showlegend=True, + legendgroup=f"{label}", + ), + row=1, + col=1, + ) + fig.add_trace( + go.Scatter( + x=frequencies, + y=q_data.phase, + opacity=opacity, + showlegend=False, + legendgroup=f"{label}", + ), + row=1, + col=2, + ) + + if fit is not None: + freqrange = np.linspace( + min(frequencies), + max(frequencies), + 2 * len(q_data), + ) + params = data_fit[ + "fitted_parameters_state_zero" + if i == 0 + else ( + "fitted_parameters_state_one" + if i == 1 + else "fitted_parameters_state_two" + ) + ][qubit] + fig.add_trace( + go.Scatter( + x=freqrange, + y=lorentzian(freqrange, **params), + name=f"{label} Fit", + line=go.scatter.Line(dash="dot"), + ), + row=1, + col=1, + ) + + if fit is not None: + fitting_report = table_html( + table_dict( + qubit, + [ + "State Zero Frequency [Hz]", + "State One Frequency [Hz]", + "State Two Frequency [Hz]", + ], + np.round( + [ + fit_data_0["frequency_state_zero"][qubit] * GHZ_TO_HZ, + fit_data_1["frequency_state_one"][qubit] * GHZ_TO_HZ, + fit_data_2["frequency_state_two"][qubit] * GHZ_TO_HZ, + ] + ), + ) + ) + fig.update_layout( + showlegend=True, + xaxis_title="Frequency (GHz)", + yaxis_title="MSR (uV)", + xaxis2_title="Frequency (GHz)", + yaxis2_title="Phase (rad)", + ) + + figures.append(fig) + + return figures, fitting_report + + +dispersive_shift_qutrit = Routine(_acquisition, fit=_fit, report=_plot) diff --git a/tests/runcards/protocols.yml b/tests/runcards/protocols.yml index c390063b5..28d1a8e25 100644 --- a/tests/runcards/protocols.yml +++ b/tests/runcards/protocols.yml @@ -416,6 +416,14 @@ actions: freq_step: 100_000 nshots: 10 + - id: dispersive shift qutrit + priority: 0 + operation: dispersive_shift_qutrit + parameters: + freq_width: 10_000_000 + freq_step: 100_000 + nshots: 10 + - id: standard rb no error priority: 0 operation: standard_rb From 6407bd0f222545520d54a9fc7cb0d817173c095a Mon Sep 17 00:00:00 2001 From: andrea-pasquale Date: Tue, 24 Oct 2023 11:10:38 +0400 Subject: [PATCH 03/14] Fix tests --- tests/runcards/protocols.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/runcards/protocols.yml b/tests/runcards/protocols.yml index 28d1a8e25..50b6c83f4 100644 --- a/tests/runcards/protocols.yml +++ b/tests/runcards/protocols.yml @@ -419,6 +419,8 @@ actions: - id: dispersive shift qutrit priority: 0 operation: dispersive_shift_qutrit + #FIXME: add qubit 4 with new release of Qibolab + qubits: [0, 1, 2, 3] parameters: freq_width: 10_000_000 freq_step: 100_000 From 7ae98fb917f23ea5a9d8baca9a44846ba488e9a2 Mon Sep 17 00:00:00 2001 From: andrea-pasquale Date: Tue, 24 Oct 2023 11:57:25 +0400 Subject: [PATCH 04/14] Fix tests by using correct qubits --- .../protocols/characterization/dispersive_shift_qutrit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibocal/protocols/characterization/dispersive_shift_qutrit.py b/src/qibocal/protocols/characterization/dispersive_shift_qutrit.py index 82bc748a4..26e93a8a0 100644 --- a/src/qibocal/protocols/characterization/dispersive_shift_qutrit.py +++ b/src/qibocal/protocols/characterization/dispersive_shift_qutrit.py @@ -99,7 +99,7 @@ def _acquisition( sequence_1 = PulseSequence() sequence_2 = PulseSequence() - for qubit in platform.qubits: + for qubit in qubits: rx_pulse = platform.create_RX_pulse(qubit, start=0) rx_12_pulse = platform.create_RX12_pulse(qubit, start=rx_pulse.finish) ro_pulse = platform.create_qubit_readout_pulse(qubit, start=0) From afef337239cb99155877e1aae10922938b6cf74c Mon Sep 17 00:00:00 2001 From: DavidSarlle Date: Tue, 24 Oct 2023 22:23:56 +0400 Subject: [PATCH 05/14] all tested and working --- .../twpa_calibration/frequency_power.py | 174 ++++++++++-------- 1 file changed, 99 insertions(+), 75 deletions(-) diff --git a/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py b/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py index ed0aa9817..df545cd0a 100644 --- a/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py +++ b/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py @@ -8,11 +8,12 @@ from qibolab.qubits import QubitId from sklearn.model_selection import train_test_split +from qibocal import update from qibocal.auto.operation import Data, Parameters, Qubits, Results, Routine from qibocal.fitting.classifier.qubit_fit import QubitFit from qibocal.fitting.classifier.run import benchmarking from qibocal.protocols.characterization import classification -from qibocal.protocols.characterization.utils import HZ_TO_GHZ +from qibocal.protocols.characterization.utils import HZ_TO_GHZ, table_dict, table_html @dataclass @@ -20,17 +21,25 @@ class TwpaFrequencyPowerParameters(Parameters): """Twpa Frequency Power runcard inputs.""" frequency_width: float + """Frequency total width.""" frequency_step: float + """Frequency step to be probed.""" power_width: float """Power total width.""" power_step: float """Power step to be probed.""" - nshots: Optional[int] = None """Number of shots.""" relaxation_time: Optional[int] = None """Relaxation time (ns).""" +TwpaFrequencyPowerType = np.dtype( + [ + ("freq", np.float64), + ("power", np.float64), + ("assignment_fidelity", np.float64), + ] +) @dataclass class TwpaFrequencyPowerData(Data): @@ -40,29 +49,18 @@ class TwpaFrequencyPowerData(Data): tuple[QubitId, float, float], npt.NDArray[classification.ClassificationType] ] = field(default_factory=dict) """Raw data acquired.""" - - def register_freq_pow( - self, - qubit: QubitId, - freq: float, - pow: float, - classification_data: npt.NDArray[classification.ClassificationType], - ): - self.data[qubit, freq, pow] = classification_data[qubit] - + frequencies: dict[QubitId, float] = field(default_factory=dict) + """Frequencies for each qubit.""" + powers: dict[QubitId, float] = field(default_factory=dict) + """Frequencies for each qubit.""" @dataclass class TwpaFrequencyPowerResults(Results): """Twpa Frequency Power outputs.""" - fidelities: dict[QubitId, float, float] = field(default_factory=dict) - - def __getitem__(self, qubit: QubitId): - return { - index: value - for index, value in self.fidelities.items() - if index[0] == qubit - } + best_freqs: dict[QubitId, float] = field(default_factory=dict) + best_powers: dict[QubitId, float] = field(default_factory=dict) + best_fidelities: dict[QubitId, float] = field(default_factory=dict) def _acquisition( @@ -115,12 +113,6 @@ def _acquisition( initial_twpa_power[qubit] + power ) - # classification_data = classification._acquisition( - # classification.SingleShotClassificationParameters(nshots=params.nshots), - # platform, - # qubits, - # ) - classification_data = classification._acquisition( classification.SingleShotClassificationParameters.load( {"nshots": params.nshots} @@ -129,70 +121,102 @@ def _acquisition( qubits, ) + classification_result = classification._fit(classification_data) for qubit in qubits: - data.register_freq_pow( - qubit, - platform.qubits[qubit].twpa.local_oscillator.frequency, - platform.qubits[qubit].twpa.local_oscillator.power, - classification_data, + data.register_qubit( + TwpaFrequencyPowerType, + (qubit), + dict( + freq=np.array( + [platform.qubits[qubit].twpa.local_oscillator.frequency], + dtype=np.float64, + ), + power=np.array( + [platform.qubits[qubit].twpa.local_oscillator.power], + dtype=np.float64, + ), + assignment_fidelity=np.array( + [classification_result.assignment_fidelity[qubit]], + ), + ), ) - return data def _fit(data: TwpaFrequencyPowerData) -> TwpaFrequencyPowerResults: """Extract fidelity for each configuration qubit / param. Where param can be either frequency or power.""" - fidelities = {} - for qubit, freq, pow in data.data: - qubit_data = data.data[qubit, freq, pow] - x_train, x_test, y_train, y_test = train_test_split( - np.array(qubit_data[["i", "q"]].tolist())[:, :], - np.array(qubit_data[["state"]].tolist())[:, 0], - test_size=0.25, - random_state=0, - shuffle=True, - ) - - model = QubitFit() - results, y_pred, model, fit_info = benchmarking( - model, x_train, y_train, x_test, y_test - ) - fidelities[qubit, freq, pow] = model.assignment_fidelity - - return TwpaFrequencyPowerResults(fidelities=fidelities) - + + best_freq = {} + best_power = {} + best_fidelity = {} + qubits = data.qubits + + for qubit in qubits: + data_qubit = data[qubit] + index_best_err = np.argmax(data_qubit["assignment_fidelity"]) + best_fidelity[qubit] = data_qubit["assignment_fidelity"][index_best_err] + best_freq[qubit] = data_qubit["freq"][index_best_err] + best_power[qubit] = data_qubit["power"][index_best_err] + + return TwpaFrequencyPowerResults(best_freq, best_power, best_fidelity) + + # for qubit, freq, pow in data.data: + # qubit_data = data.data[qubit, freq, pow] + # x_train, x_test, y_train, y_test = train_test_split( + # np.array(qubit_data[["i", "q"]].tolist())[:, :], + # np.array(qubit_data[["state"]].tolist())[:, 0], + # test_size=0.25, + # random_state=0, + # shuffle=True, + # ) + + # model = QubitFit() + # results, y_pred, model, fit_info = benchmarking( + # model, x_train, y_train, x_test, y_test + # ) + # fidelities[qubit, freq, pow] = model.assignment_fidelity + + # return TwpaFrequencyPowerResults(fidelities=fidelities) def _plot(data: TwpaFrequencyPowerData, fit: TwpaFrequencyPowerResults, qubit): """Plotting function that shows the assignment fidelity - for different values of the twpa frequency and power for a single qubit""" + for different values of the twpa frequency for a single qubit""" figures = [] - fitting_report = "No fitting data" - qubit_fit = fit[qubit] - freqs = [] - pows = [] - fidelities = [] - for _, freq, pow in qubit_fit: - freqs.append(freq * HZ_TO_GHZ) - pows.append(pow) - fidelities.append(qubit_fit[qubit, freq, pow]) - - fitting_report = f"{qubit} | Best assignment fidelity: {np.max(fidelities):.3f}
" - # fitting_report += f"{qubit} | TWPA Frequency: {int(freqs[np.argmax(fidelities)]*GHZ_TO_HZ)} Hz
" - - fig = go.Figure([go.Heatmap(x=freqs, y=pows, z=fidelities, name="Fidelity")]) - figures.append(fig) - - fig.update_layout( - showlegend=True, - uirevision="0", # ``uirevision`` allows zooming while live plotting - xaxis_title="TWPA Frequency [GHz]", - yaxis_title="TWPA Power [dBm]", - ) + fitting_report = "" + if fit is not None: + qubit_data = data.data[qubit] + fidelities = qubit_data["assignment_fidelity"] + frequencies = qubit_data["freq"] + powers = qubit_data["power"] + fitting_report = table_html( + table_dict( + qubit, + ["Best assignment fidelity", "TWPA Frequency [Hz]", "TWPA Power [dBm]"], + [ + np.round(fit.best_fidelities[qubit], 3), + fit.best_freqs[qubit], + np.round(fit.best_powers[qubit], 3), + ], + ) + ) + + fig = go.Figure([go.Heatmap(x=frequencies * HZ_TO_GHZ, y=powers, z=fidelities, name="Fidelity")]) + + fig.update_layout( + showlegend=True, + xaxis_title="TWPA Frequency [GHz]", + yaxis_title="TWPA Power [dBm]", + ) + + figures.append(fig) return figures, fitting_report +def _update(results: TwpaFrequencyPowerResults, platform: Platform, qubit: QubitId): + update.twpa_frequency(results.best_freqs[qubit], platform, qubit) + update.twpa_power(results.best_powers[qubit], platform, qubit) -twpa_frequency_power = Routine(_acquisition, _fit, _plot) +twpa_frequency_power = Routine(_acquisition, _fit, _plot, _update) """Twpa frequency Routine object.""" \ No newline at end of file From 472e5acd0266447c980c6d83526074548f174ac8 Mon Sep 17 00:00:00 2001 From: DavidSarlle Date: Tue, 24 Oct 2023 22:24:47 +0400 Subject: [PATCH 06/14] delete comments --- .../twpa_calibration/frequency_power.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py b/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py index df545cd0a..94ef554d9 100644 --- a/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py +++ b/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py @@ -161,24 +161,6 @@ def _fit(data: TwpaFrequencyPowerData) -> TwpaFrequencyPowerResults: return TwpaFrequencyPowerResults(best_freq, best_power, best_fidelity) - # for qubit, freq, pow in data.data: - # qubit_data = data.data[qubit, freq, pow] - # x_train, x_test, y_train, y_test = train_test_split( - # np.array(qubit_data[["i", "q"]].tolist())[:, :], - # np.array(qubit_data[["state"]].tolist())[:, 0], - # test_size=0.25, - # random_state=0, - # shuffle=True, - # ) - - # model = QubitFit() - # results, y_pred, model, fit_info = benchmarking( - # model, x_train, y_train, x_test, y_test - # ) - # fidelities[qubit, freq, pow] = model.assignment_fidelity - - # return TwpaFrequencyPowerResults(fidelities=fidelities) - def _plot(data: TwpaFrequencyPowerData, fit: TwpaFrequencyPowerResults, qubit): """Plotting function that shows the assignment fidelity for different values of the twpa frequency for a single qubit""" From 39afa1f048525f0a72d50e5683164400e74be5b8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 24 Oct 2023 18:26:40 +0000 Subject: [PATCH 07/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../protocols/characterization/__init__.py | 2 +- .../twpa_calibration/frequency_power.py | 25 +++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/qibocal/protocols/characterization/__init__.py b/src/qibocal/protocols/characterization/__init__.py index 8b77c371a..4534f16c3 100644 --- a/src/qibocal/protocols/characterization/__init__.py +++ b/src/qibocal/protocols/characterization/__init__.py @@ -37,8 +37,8 @@ from .readout_optimization.resonator_amplitude import resonator_amplitude from .readout_optimization.resonator_frequency import resonator_frequency from .readout_optimization.twpa_calibration.frequency import twpa_frequency -from .readout_optimization.twpa_calibration.power import twpa_power from .readout_optimization.twpa_calibration.frequency_power import twpa_frequency_power +from .readout_optimization.twpa_calibration.power import twpa_power from .resonator_punchout import resonator_punchout from .resonator_punchout_attenuation import resonator_punchout_attenuation from .resonator_spectroscopy import resonator_spectroscopy diff --git a/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py b/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py index 94ef554d9..15ad7a375 100644 --- a/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py +++ b/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py @@ -6,12 +6,9 @@ import plotly.graph_objects as go from qibolab.platform import Platform from qibolab.qubits import QubitId -from sklearn.model_selection import train_test_split from qibocal import update from qibocal.auto.operation import Data, Parameters, Qubits, Results, Routine -from qibocal.fitting.classifier.qubit_fit import QubitFit -from qibocal.fitting.classifier.run import benchmarking from qibocal.protocols.characterization import classification from qibocal.protocols.characterization.utils import HZ_TO_GHZ, table_dict, table_html @@ -33,6 +30,7 @@ class TwpaFrequencyPowerParameters(Parameters): relaxation_time: Optional[int] = None """Relaxation time (ns).""" + TwpaFrequencyPowerType = np.dtype( [ ("freq", np.float64), @@ -41,6 +39,7 @@ class TwpaFrequencyPowerParameters(Parameters): ] ) + @dataclass class TwpaFrequencyPowerData(Data): """Twpa Frequency Power acquisition outputs.""" @@ -54,6 +53,7 @@ class TwpaFrequencyPowerData(Data): powers: dict[QubitId, float] = field(default_factory=dict) """Frequencies for each qubit.""" + @dataclass class TwpaFrequencyPowerResults(Results): """Twpa Frequency Power outputs.""" @@ -146,12 +146,12 @@ def _acquisition( def _fit(data: TwpaFrequencyPowerData) -> TwpaFrequencyPowerResults: """Extract fidelity for each configuration qubit / param. Where param can be either frequency or power.""" - + best_freq = {} best_power = {} best_fidelity = {} qubits = data.qubits - + for qubit in qubits: data_qubit = data[qubit] index_best_err = np.argmax(data_qubit["assignment_fidelity"]) @@ -161,6 +161,7 @@ def _fit(data: TwpaFrequencyPowerData) -> TwpaFrequencyPowerResults: return TwpaFrequencyPowerResults(best_freq, best_power, best_fidelity) + def _plot(data: TwpaFrequencyPowerData, fit: TwpaFrequencyPowerResults, qubit): """Plotting function that shows the assignment fidelity for different values of the twpa frequency for a single qubit""" @@ -184,8 +185,14 @@ def _plot(data: TwpaFrequencyPowerData, fit: TwpaFrequencyPowerResults, qubit): ) ) - fig = go.Figure([go.Heatmap(x=frequencies * HZ_TO_GHZ, y=powers, z=fidelities, name="Fidelity")]) - + fig = go.Figure( + [ + go.Heatmap( + x=frequencies * HZ_TO_GHZ, y=powers, z=fidelities, name="Fidelity" + ) + ] + ) + fig.update_layout( showlegend=True, xaxis_title="TWPA Frequency [GHz]", @@ -196,9 +203,11 @@ def _plot(data: TwpaFrequencyPowerData, fit: TwpaFrequencyPowerResults, qubit): return figures, fitting_report + def _update(results: TwpaFrequencyPowerResults, platform: Platform, qubit: QubitId): update.twpa_frequency(results.best_freqs[qubit], platform, qubit) update.twpa_power(results.best_powers[qubit], platform, qubit) + twpa_frequency_power = Routine(_acquisition, _fit, _plot, _update) -"""Twpa frequency Routine object.""" \ No newline at end of file +"""Twpa frequency Routine object.""" From 3191ff91d00978443598a812ee047692916de863 Mon Sep 17 00:00:00 2001 From: DavidSarlle Date: Wed, 25 Oct 2023 15:13:35 +0400 Subject: [PATCH 08/14] adressing andreas review comments --- .../twpa_calibration/frequency_power.py | 45 +++++++++---------- tests/runcards/protocols.yml | 10 +++++ 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py b/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py index 15ad7a375..31f17b6c7 100644 --- a/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py +++ b/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py @@ -25,10 +25,6 @@ class TwpaFrequencyPowerParameters(Parameters): """Power total width.""" power_step: float """Power step to be probed.""" - nshots: Optional[int] = None - """Number of shots.""" - relaxation_time: Optional[int] = None - """Relaxation time (ns).""" TwpaFrequencyPowerType = np.dtype( @@ -51,7 +47,7 @@ class TwpaFrequencyPowerData(Data): frequencies: dict[QubitId, float] = field(default_factory=dict) """Frequencies for each qubit.""" powers: dict[QubitId, float] = field(default_factory=dict) - """Frequencies for each qubit.""" + """Powers for each qubit.""" @dataclass @@ -69,18 +65,18 @@ def _acquisition( qubits: Qubits, ) -> TwpaFrequencyPowerData: r""" - Data acquisition for TWPA power optmization. + Data acquisition for TWPA frequency vs. power optmization. This protocol perform a classification protocol for twpa frequencies in the range [twpa_frequency - frequency_width / 2, twpa_frequency + frequency_width / 2] - with step frequency_step. + with step frequency_step and powers in the range [twpa_power - power_width / 2, twpa_power + power_width / 2] Args: - params (:class:`TwpaFrequencyParameters`): input parameters + params (:class:`TwpaFrequencyPowerParameters`): input parameters platform (:class:`Platform`): Qibolab's platform qubits (dict): dict of target :class:`Qubit` objects to be characterized Returns: - data (:class:`TwpaFrequencyData`) + data (:class:`TwpaFrequencyPowerData`) """ data = TwpaFrequencyPowerData() @@ -101,28 +97,27 @@ def _acquisition( ].twpa.local_oscillator.frequency initial_twpa_power[qubit] = platform.qubits[qubit].twpa.local_oscillator.power - for freq in freq_range: - for qubit in qubits: + for freq in freq_range: platform.qubits[qubit].twpa.local_oscillator.frequency = ( initial_twpa_freq[qubit] + freq ) - for power in power_range: - for qubit in qubits: - platform.qubits[qubit].twpa.local_oscillator.power = ( - initial_twpa_power[qubit] + power - ) + for power in power_range: + for qubit in qubits: + platform.qubits[qubit].twpa.local_oscillator.power = ( + initial_twpa_power[qubit] + power + ) - classification_data = classification._acquisition( - classification.SingleShotClassificationParameters.load( - {"nshots": params.nshots} - ), - platform, - qubits, - ) + classification_data = classification._acquisition( + classification.SingleShotClassificationParameters.load( + {"nshots": params.nshots} + ), + platform, + qubits, + ) - classification_result = classification._fit(classification_data) - for qubit in qubits: + classification_result = classification._fit(classification_data) + data.register_qubit( TwpaFrequencyPowerType, (qubit), diff --git a/tests/runcards/protocols.yml b/tests/runcards/protocols.yml index c390063b5..972021d97 100644 --- a/tests/runcards/protocols.yml +++ b/tests/runcards/protocols.yml @@ -558,6 +558,16 @@ actions: power_width: 10 power_step: 1 + - id: twpa frequency power + priority: 0 + operation: twpa_frequency_power + parameters: + frequency_width: 1_000_000 + frequency_step: 100_000 + power_width: 1 + power_step: 0.1 + nshots: 10 + - id: resoantor_amplitude priority: 0 operation: resonator_amplitude From 94b5fec04e5d6150b09f07b5a4e9ea0bb9d23c61 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 25 Oct 2023 11:13:57 +0000 Subject: [PATCH 09/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../readout_optimization/twpa_calibration/frequency_power.py | 3 +-- tests/runcards/protocols.yml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py b/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py index 31f17b6c7..0af5806fb 100644 --- a/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py +++ b/src/qibocal/protocols/characterization/readout_optimization/twpa_calibration/frequency_power.py @@ -1,5 +1,4 @@ from dataclasses import dataclass, field -from typing import Optional import numpy as np import numpy.typing as npt @@ -117,7 +116,7 @@ def _acquisition( ) classification_result = classification._fit(classification_data) - + data.register_qubit( TwpaFrequencyPowerType, (qubit), diff --git a/tests/runcards/protocols.yml b/tests/runcards/protocols.yml index 972021d97..981bca4dd 100644 --- a/tests/runcards/protocols.yml +++ b/tests/runcards/protocols.yml @@ -566,7 +566,7 @@ actions: frequency_step: 100_000 power_width: 1 power_step: 0.1 - nshots: 10 + nshots: 10 - id: resoantor_amplitude priority: 0 From 60e81e5744a2fd668b9bad6ee931e47cf2c60a9b Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 25 Oct 2023 15:36:50 +0400 Subject: [PATCH 10/14] Avoid dtype duplication --- .../characterization/dispersive_shift_qutrit.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/qibocal/protocols/characterization/dispersive_shift_qutrit.py b/src/qibocal/protocols/characterization/dispersive_shift_qutrit.py index 26e93a8a0..2163f2033 100644 --- a/src/qibocal/protocols/characterization/dispersive_shift_qutrit.py +++ b/src/qibocal/protocols/characterization/dispersive_shift_qutrit.py @@ -22,6 +22,7 @@ ) from .dispersive_shift import DispersiveShiftData, DispersiveShiftParameters +from .resonator_spectroscopy import ResSpecType @dataclass @@ -59,13 +60,6 @@ def state_two(self): return {key: value for key, value in asdict(self).items() if "two" in key} -DispersiveShiftQutritType = np.dtype( - [ - ("freq", np.float64), - ("msr", np.float64), - ("phase", np.float64), - ] -) """Custom dtype for rabi amplitude.""" @@ -115,7 +109,6 @@ def _acquisition( -params.freq_width // 2, params.freq_width // 2, params.freq_step ) - # create a DataUnits objects to store the results data = DispersiveShiftQutritData(resonator_type=platform.resonator_type) for state, sequence in enumerate([sequence_0, sequence_1, sequence_2]): @@ -142,7 +135,7 @@ def _acquisition( result = results[sequence.ro_pulses[qubit].serial] # store the results data.register_qubit( - DispersiveShiftQutritType, + ResSpecType, (qubit, state), dict( freq=sequence.ro_pulses[qubit].frequency + delta_frequency_range, From c6d5c4e60a97c4fdb5828d0897cb645dec2d420c Mon Sep 17 00:00:00 2001 From: andrea-pasquale Date: Tue, 31 Oct 2023 12:52:38 +0400 Subject: [PATCH 11/14] Minor fix --- .../protocols/characterization/dispersive_shift_qutrit.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qibocal/protocols/characterization/dispersive_shift_qutrit.py b/src/qibocal/protocols/characterization/dispersive_shift_qutrit.py index 2163f2033..1b388d34c 100644 --- a/src/qibocal/protocols/characterization/dispersive_shift_qutrit.py +++ b/src/qibocal/protocols/characterization/dispersive_shift_qutrit.py @@ -131,14 +131,14 @@ def _acquisition( ) for qubit in qubits: - # average msr, phase, i and q over the number of shots defined in the runcard - result = results[sequence.ro_pulses[qubit].serial] + result = results[qubit] # store the results data.register_qubit( ResSpecType, (qubit, state), dict( - freq=sequence.ro_pulses[qubit].frequency + delta_frequency_range, + freq=sequence.get_qubit_pulses(qubit).ro_pulses[0].frequency + + delta_frequency_range, msr=result.magnitude, phase=result.phase, ), From 1d59a6af1476e7cd5813ec185b5750da851438ef Mon Sep 17 00:00:00 2001 From: DavidSarlle Date: Wed, 1 Nov 2023 15:31:24 +0400 Subject: [PATCH 12/14] fixing test not passed --- tests/runcards/protocols.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/runcards/protocols.yml b/tests/runcards/protocols.yml index 981bca4dd..79fa95bc6 100644 --- a/tests/runcards/protocols.yml +++ b/tests/runcards/protocols.yml @@ -566,7 +566,6 @@ actions: frequency_step: 100_000 power_width: 1 power_step: 0.1 - nshots: 10 - id: resoantor_amplitude priority: 0 From e0a1efe51549f0a1e0761d48678c8c37fa7a487e Mon Sep 17 00:00:00 2001 From: DavidSarlle Date: Wed, 1 Nov 2023 16:11:54 +0400 Subject: [PATCH 13/14] modifiying runcard --- tests/runcards/protocols.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/runcards/protocols.yml b/tests/runcards/protocols.yml index 79fa95bc6..68459be95 100644 --- a/tests/runcards/protocols.yml +++ b/tests/runcards/protocols.yml @@ -564,8 +564,8 @@ actions: parameters: frequency_width: 1_000_000 frequency_step: 100_000 - power_width: 1 - power_step: 0.1 + power_width: 10 + power_step: 1 - id: resoantor_amplitude priority: 0 From 98f5d6afb0007ea88132bdfa7fde1c92449c2b04 Mon Sep 17 00:00:00 2001 From: DavidSarlle Date: Wed, 1 Nov 2023 16:28:08 +0400 Subject: [PATCH 14/14] modifiying protocols runcard --- tests/runcards/protocols.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/runcards/protocols.yml b/tests/runcards/protocols.yml index 68459be95..041486ce3 100644 --- a/tests/runcards/protocols.yml +++ b/tests/runcards/protocols.yml @@ -561,6 +561,7 @@ actions: - id: twpa frequency power priority: 0 operation: twpa_frequency_power + qubits: [0] parameters: frequency_width: 1_000_000 frequency_step: 100_000