From 094bd3b1435ecf32a1045611d081d9df79a8e10a Mon Sep 17 00:00:00 2001 From: andrea-pasquale Date: Tue, 6 Feb 2024 18:05:15 +0400 Subject: [PATCH 1/5] fix: Add try-except block to avoid raising errors during execution --- .../flux_dependence/qubit_flux_dependence.py | 35 +++++++----- .../resonator_flux_dependence.py | 56 +++++++++++-------- 2 files changed, 55 insertions(+), 36 deletions(-) diff --git a/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py b/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py index 6a2ec0d0b..150671d7f 100644 --- a/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py +++ b/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py @@ -203,20 +203,27 @@ def _fit(data: QubitFluxData) -> QubitFluxResults: signal, ) - popt = curve_fit( - utils.transmon_frequency, - biases, - frequencies * HZ_TO_GHZ, - bounds=utils.qubit_flux_dependence_fit_bounds( - data.qubit_frequency[qubit], qubit_data.bias - ), - maxfev=100000, - )[0] - fitted_parameters[qubit] = popt.tolist() - frequency[qubit] = popt[0] * GHZ_TO_HZ - d[qubit] = popt[1] - sweetspot[qubit] = popt[3] - matrix_element[qubit] = popt[2] + try: + popt = curve_fit( + utils.transmon_frequency, + biases, + frequencies * HZ_TO_GHZ, + bounds=utils.qubit_flux_dependence_fit_bounds( + data.qubit_frequency[qubit], qubit_data.bias + ), + maxfev=100000, + )[0] + fitted_parameters[qubit] = popt.tolist() + frequency[qubit] = popt[0] * GHZ_TO_HZ + d[qubit] = popt[1] + sweetspot[qubit] = popt[3] + matrix_element[qubit] = popt[2] + except: + fitted_parameters[qubit] = [0, 0, 0, 0] + frequency[qubit] = 0 + d[qubit] = 0 + sweetspot[qubit] = 0 + matrix_element[qubit] = 0 return QubitFluxResults( frequency=frequency, diff --git a/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py b/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py index 542f9e524..772d3774c 100644 --- a/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py +++ b/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py @@ -196,28 +196,40 @@ def _fit(data: ResonatorFluxData) -> ResonatorFluxResults: signal, ) - popt = curve_fit( - utils.transmon_readout_frequency, - biases, - frequencies * HZ_TO_GHZ, - bounds=utils.resonator_flux_dependence_fit_bounds( - data.qubit_frequency[qubit], - qubit_data.bias, - data.bare_resonator_frequency[qubit], - ), - maxfev=100000, - )[0] - fitted_parameters[qubit] = popt.tolist() - - # frequency corresponds to transmon readout frequency - # at the sweetspot popt[3] - frequency[qubit] = utils.transmon_readout_frequency(popt[3], *popt) * GHZ_TO_HZ - sweetspot[qubit] = popt[3] - d[qubit] = popt[1] - bare_frequency[qubit] = popt[4] * GHZ_TO_HZ - drive_frequency[qubit] = popt[0] * GHZ_TO_HZ - g[qubit] = popt[5] - matrix_element[qubit] = popt[2] + try: + popt = curve_fit( + utils.transmon_readout_frequency, + biases, + frequencies * HZ_TO_GHZ, + bounds=utils.resonator_flux_dependence_fit_bounds( + data.qubit_frequency[qubit], + qubit_data.bias, + data.bare_resonator_frequency[qubit], + ), + maxfev=100000, + )[0] + fitted_parameters[qubit] = popt.tolist() + + # frequency corresponds to transmon readout frequency + # at the sweetspot popt[3] + frequency[qubit] = ( + utils.transmon_readout_frequency(popt[3], *popt) * GHZ_TO_HZ + ) + sweetspot[qubit] = popt[3] + d[qubit] = popt[1] + bare_frequency[qubit] = popt[4] * GHZ_TO_HZ + drive_frequency[qubit] = popt[0] * GHZ_TO_HZ + g[qubit] = popt[5] + matrix_element[qubit] = popt[2] + except: + frequency[qubit] = 0 + sweetspot[qubit] = 0 + d[qubit] = 0 + bare_frequency[qubit] = 0 + drive_frequency[qubit] = 0 + g[qubit] = 0 + matrix_element[qubit] = 0 + fitted_parameters[qubit] = [0, 0, 0, 0, 0, 0] return ResonatorFluxResults( frequency=frequency, From 87b0fa6a285e8703d9261ff65f9e6353dbcf5d09 Mon Sep 17 00:00:00 2001 From: andrea-pasquale Date: Wed, 7 Feb 2024 11:28:24 +0400 Subject: [PATCH 2/5] refactor: Catch ValueError in flux fits and return None if fitting fails --- src/qibocal/auto/task.py | 5 +++-- .../flux_dependence/qubit_flux_dependence.py | 17 ++++++++------- .../resonator_flux_dependence.py | 21 ++++++++++--------- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/qibocal/auto/task.py b/src/qibocal/auto/task.py index bdddf0ec6..5788bf509 100644 --- a/src/qibocal/auto/task.py +++ b/src/qibocal/auto/task.py @@ -215,8 +215,9 @@ def results(self): @results.setter def results(self, results: Results): """Set and store results.""" - self._results = results - self._results.save(self.datapath) + if self._results is not None: + self._results = results + self._results.save(self.datapath) @property def data(self): diff --git a/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py b/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py index 150671d7f..ffbe7f218 100644 --- a/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py +++ b/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py @@ -12,6 +12,7 @@ from qibocal import update from qibocal.auto.operation import Data, Parameters, Qubits, Results, Routine +from qibocal.config import log from qibocal.protocols.characterization.qubit_spectroscopy_ef import ( DEFAULT_ANHARMONICITY, ) @@ -169,7 +170,7 @@ def _acquisition( return data -def _fit(data: QubitFluxData) -> QubitFluxResults: +def _fit(data: QubitFluxData) -> Optional[QubitFluxResults]: """ Post-processing for QubitFlux Experiment. See arxiv:0703002 Fit frequency as a function of current for the flux qubit spectroscopy @@ -218,12 +219,14 @@ def _fit(data: QubitFluxData) -> QubitFluxResults: d[qubit] = popt[1] sweetspot[qubit] = popt[3] matrix_element[qubit] = popt[2] - except: - fitted_parameters[qubit] = [0, 0, 0, 0] - frequency[qubit] = 0 - d[qubit] = 0 - sweetspot[qubit] = 0 - matrix_element[qubit] = 0 + except ValueError as e: + log.error( + f"Error in qubit_flux protocol fit: {e} " + "The threshold for the SNR mask is probably too high. " + "Lowering the value of `threshold` in `extract_*_feature`" + "should fix the problem." + ) + return None return QubitFluxResults( frequency=frequency, diff --git a/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py b/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py index 772d3774c..0a0ba51a2 100644 --- a/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py +++ b/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py @@ -1,4 +1,5 @@ from dataclasses import dataclass, field +from typing import Optional import numpy as np import numpy.typing as npt @@ -11,6 +12,7 @@ from qibocal import update from qibocal.auto.operation import Data, Parameters, Qubits, Results, Routine +from qibocal.config import log from ..utils import GHZ_TO_HZ, HZ_TO_GHZ, table_dict, table_html from . import utils @@ -159,7 +161,7 @@ def _acquisition( return data -def _fit(data: ResonatorFluxData) -> ResonatorFluxResults: +def _fit(data: ResonatorFluxData) -> Optional[ResonatorFluxResults]: """ Post-processing for QubitFlux Experiment. See arxiv:0703002 Fit frequency as a function of current for the flux qubit spectroscopy @@ -221,15 +223,14 @@ def _fit(data: ResonatorFluxData) -> ResonatorFluxResults: drive_frequency[qubit] = popt[0] * GHZ_TO_HZ g[qubit] = popt[5] matrix_element[qubit] = popt[2] - except: - frequency[qubit] = 0 - sweetspot[qubit] = 0 - d[qubit] = 0 - bare_frequency[qubit] = 0 - drive_frequency[qubit] = 0 - g[qubit] = 0 - matrix_element[qubit] = 0 - fitted_parameters[qubit] = [0, 0, 0, 0, 0, 0] + except ValueError as e: + log.error( + f"Error in resonator_flux protocol fit: {e} " + "The threshold for the SNR mask is probably too high. " + "Lowering the value of `threshold` in `extract_*_feature`" + "should fix the problem." + ) + return None return ResonatorFluxResults( frequency=frequency, From 08234f244352bbe4dc2b2da6e48a078e4765b67b Mon Sep 17 00:00:00 2001 From: andrea-pasquale Date: Wed, 7 Feb 2024 11:55:52 +0400 Subject: [PATCH 3/5] style: Improve readability by changing variable names --- .../flux_dependence/qubit_flux_dependence.py | 14 +++++----- .../resonator_flux_dependence.py | 28 +++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py b/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py index ffbe7f218..d0ee39eea 100644 --- a/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py +++ b/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py @@ -50,8 +50,8 @@ class QubitFluxResults(Results): """Sweetspot for each qubit.""" frequency: dict[QubitId, float] """Drive frequency for each qubit.""" - d: dict[QubitId, float] - """Asymmetry.""" + asymmetry: dict[QubitId, float] + """Asymmetry between junctions.""" fitted_parameters: dict[QubitId, dict[str, float]] """Raw fitting output.""" matrix_element: dict[QubitId, float] @@ -180,7 +180,7 @@ def _fit(data: QubitFluxData) -> Optional[QubitFluxResults]: qubits = data.qubits frequency = {} sweetspot = {} - d = {} + asymmetry = {} matrix_element = {} fitted_parameters = {} @@ -216,7 +216,7 @@ def _fit(data: QubitFluxData) -> Optional[QubitFluxResults]: )[0] fitted_parameters[qubit] = popt.tolist() frequency[qubit] = popt[0] * GHZ_TO_HZ - d[qubit] = popt[1] + asymmetry[qubit] = popt[1] sweetspot[qubit] = popt[3] matrix_element[qubit] = popt[2] except ValueError as e: @@ -231,7 +231,7 @@ def _fit(data: QubitFluxData) -> Optional[QubitFluxResults]: return QubitFluxResults( frequency=frequency, sweetspot=sweetspot, - d=d, + asymmetry=asymmetry, matrix_element=matrix_element, fitted_parameters=fitted_parameters, ) @@ -255,7 +255,7 @@ def _plot(data: QubitFluxData, fit: QubitFluxResults, qubit): [ np.round(fit.sweetspot[qubit], 4), np.round(fit.frequency[qubit], 4), - np.round(fit.d[qubit], 4), + np.round(fit.asymmetry[qubit], 4), np.round(fit.matrix_element[qubit], 4), ], ) @@ -267,7 +267,7 @@ def _plot(data: QubitFluxData, fit: QubitFluxResults, qubit): def _update(results: QubitFluxResults, platform: Platform, qubit: QubitId): update.drive_frequency(results.frequency[qubit], platform, qubit) update.sweetspot(results.sweetspot[qubit], platform, qubit) - update.asymmetry(results.d[qubit], platform, qubit) + update.asymmetry(results.asymmetry[qubit], platform, qubit) qubit_flux = Routine(_acquisition, _fit, _plot, _update) diff --git a/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py b/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py index 0a0ba51a2..ddb7107af 100644 --- a/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py +++ b/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py @@ -40,16 +40,16 @@ class ResonatorFluxResults(Results): """Readout frequency for each qubit.""" sweetspot: dict[QubitId, float] """Sweetspot for each qubit.""" - d: dict[QubitId, float] - """Asymmetry.""" + asymmetry: dict[QubitId, float] + """Asymmetry between junctions.""" bare_frequency: dict[QubitId, float] """Resonator bare frequency.""" drive_frequency: dict[QubitId, float] """Qubit frequency at sweetspot.""" fitted_parameters: dict[QubitId, dict[str, float]] """Raw fitting output.""" - g: dict[QubitId, float] - """Coupling.""" + coupling: dict[QubitId, float] + """Qubit-resonator coupling.""" matrix_element: dict[QubitId, float] """C_ii coefficient.""" @@ -171,12 +171,12 @@ def _fit(data: ResonatorFluxData) -> Optional[ResonatorFluxResults]: qubits = data.qubits frequency = {} sweetspot = {} - d = {} + asymmetry = {} bare_frequency = {} drive_frequency = {} fitted_parameters = {} matrix_element = {} - g = {} + coupling = {} for qubit in qubits: qubit_data = data[qubit] @@ -218,10 +218,10 @@ def _fit(data: ResonatorFluxData) -> Optional[ResonatorFluxResults]: utils.transmon_readout_frequency(popt[3], *popt) * GHZ_TO_HZ ) sweetspot[qubit] = popt[3] - d[qubit] = popt[1] + asymmetry[qubit] = popt[1] bare_frequency[qubit] = popt[4] * GHZ_TO_HZ drive_frequency[qubit] = popt[0] * GHZ_TO_HZ - g[qubit] = popt[5] + coupling[qubit] = popt[5] matrix_element[qubit] = popt[2] except ValueError as e: log.error( @@ -235,10 +235,10 @@ def _fit(data: ResonatorFluxData) -> Optional[ResonatorFluxResults]: return ResonatorFluxResults( frequency=frequency, sweetspot=sweetspot, - d=d, + asymmetry=asymmetry, bare_frequency=bare_frequency, drive_frequency=drive_frequency, - g=g, + coupling=coupling, matrix_element=matrix_element, fitted_parameters=fitted_parameters, ) @@ -267,8 +267,8 @@ def _plot(data: ResonatorFluxData, fit: ResonatorFluxResults, qubit): np.round(fit.bare_frequency[qubit], 4), np.round(fit.frequency[qubit], 4), np.round(fit.drive_frequency[qubit], 4), - np.round(fit.d[qubit], 4), - np.round(fit.g[qubit], 4), + np.round(fit.asymmetry[qubit], 4), + np.round(fit.coupling[qubit], 4), np.round(fit.matrix_element[qubit], 4), ], ) @@ -281,8 +281,8 @@ def _update(results: ResonatorFluxResults, platform: Platform, qubit: QubitId): update.bare_resonator_frequency(results.bare_frequency[qubit], platform, qubit) update.readout_frequency(results.frequency[qubit], platform, qubit) update.drive_frequency(results.drive_frequency[qubit], platform, qubit) - update.asymmetry(results.d[qubit], platform, qubit) - update.coupling(results.g[qubit], platform, qubit) + update.asymmetry(results.asymmetry[qubit], platform, qubit) + update.coupling(results.coupling[qubit], platform, qubit) resonator_flux = Routine(_acquisition, _fit, _plot, _update) From 70440a68d73b856c490f4b6db4d7f345c041e982 Mon Sep 17 00:00:00 2001 From: andrea-pasquale Date: Wed, 7 Feb 2024 12:39:05 +0400 Subject: [PATCH 4/5] fix: Avoid return and skip update if qubit is not in Results --- src/qibocal/auto/task.py | 14 +++++++++----- .../flux_dependence/qubit_flux_dependence.py | 1 - .../flux_dependence/resonator_flux_dependence.py | 1 - .../characterization/flux_dependence/utils.py | 6 +++++- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/qibocal/auto/task.py b/src/qibocal/auto/task.py index 5788bf509..b3aa33766 100644 --- a/src/qibocal/auto/task.py +++ b/src/qibocal/auto/task.py @@ -9,7 +9,7 @@ from qibolab.platform import Platform from qibolab.qubits import QubitId, QubitPairId -from ..config import raise_error +from ..config import log, raise_error from ..protocols.characterization import Operation from ..utils import ( allocate_qubits_pairs, @@ -215,9 +215,8 @@ def results(self): @results.setter def results(self, results: Results): """Set and store results.""" - if self._results is not None: - self._results = results - self._results.save(self.datapath) + self._results = results + self._results.save(self.datapath) @property def data(self): @@ -237,7 +236,12 @@ def update_platform(self, platform: Platform, update: bool): """Perform update on platform' parameters by looping over qubits or pairs.""" if self.task.update and update: for qubit in self.task.qubits: - self.task.operation.update(self.results, platform, qubit) + try: + self.task.operation.update(self.results, platform, qubit) + except KeyError: + log.warning( + f"Skipping update of qubit {qubit} due to error in fit." + ) def validate(self) -> tuple[Optional[TaskId], Optional[dict]]: """Check status of completed and handle Failure using handler.""" diff --git a/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py b/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py index d0ee39eea..5a589863d 100644 --- a/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py +++ b/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py @@ -226,7 +226,6 @@ def _fit(data: QubitFluxData) -> Optional[QubitFluxResults]: "Lowering the value of `threshold` in `extract_*_feature`" "should fix the problem." ) - return None return QubitFluxResults( frequency=frequency, diff --git a/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py b/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py index ddb7107af..3311ddf53 100644 --- a/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py +++ b/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py @@ -230,7 +230,6 @@ def _fit(data: ResonatorFluxData) -> Optional[ResonatorFluxResults]: "Lowering the value of `threshold` in `extract_*_feature`" "should fix the problem." ) - return None return ResonatorFluxResults( frequency=frequency, diff --git a/src/qibocal/protocols/characterization/flux_dependence/utils.py b/src/qibocal/protocols/characterization/flux_dependence/utils.py index 785667549..c816ff7df 100644 --- a/src/qibocal/protocols/characterization/flux_dependence/utils.py +++ b/src/qibocal/protocols/characterization/flux_dependence/utils.py @@ -52,7 +52,11 @@ def flux_dependence_plot(data, fit, qubit, fit_function=None): ) # TODO: This fit is for frequency, can it be reused here, do we even want the fit ? - if fit is not None and not data.__class__.__name__ == "CouplerSpectroscopyData": + if ( + fit is not None + and not data.__class__.__name__ == "CouplerSpectroscopyData" + and qubit in fit.fitted_parameters + ): params = fit.fitted_parameters[qubit] bias = np.unique(qubit_data.bias) fig.add_trace( From 78484cb19946420cea3b3e801f7071146153d2fc Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 7 Feb 2024 12:58:36 +0400 Subject: [PATCH 5/5] fix: Fix tests by removing incorrect Optional --- .../characterization/flux_dependence/qubit_flux_dependence.py | 2 +- .../flux_dependence/resonator_flux_dependence.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py b/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py index 5a589863d..c4e3737bb 100644 --- a/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py +++ b/src/qibocal/protocols/characterization/flux_dependence/qubit_flux_dependence.py @@ -170,7 +170,7 @@ def _acquisition( return data -def _fit(data: QubitFluxData) -> Optional[QubitFluxResults]: +def _fit(data: QubitFluxData) -> QubitFluxResults: """ Post-processing for QubitFlux Experiment. See arxiv:0703002 Fit frequency as a function of current for the flux qubit spectroscopy diff --git a/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py b/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py index 3311ddf53..7b1bb6534 100644 --- a/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py +++ b/src/qibocal/protocols/characterization/flux_dependence/resonator_flux_dependence.py @@ -1,5 +1,4 @@ from dataclasses import dataclass, field -from typing import Optional import numpy as np import numpy.typing as npt @@ -161,7 +160,7 @@ def _acquisition( return data -def _fit(data: ResonatorFluxData) -> Optional[ResonatorFluxResults]: +def _fit(data: ResonatorFluxData) -> ResonatorFluxResults: """ Post-processing for QubitFlux Experiment. See arxiv:0703002 Fit frequency as a function of current for the flux qubit spectroscopy