From 12bcfead265bc0715b7d4eb303b55cb9009cdeff Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Thu, 23 Jan 2025 15:30:43 +0400 Subject: [PATCH 1/7] fix: evaluate the sweetspot closest to the center of the swept frequencies --- .../protocols/flux_dependence/qubit_flux_dependence.py | 5 ++++- src/qibocal/protocols/flux_dependence/utils.py | 7 ++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py index e87983f05..e2a464285 100644 --- a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py +++ b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py @@ -218,10 +218,13 @@ def fit_function(x, w_max, normalization, offset): "charging_energy": data.charging_energy[qubit] * HZ_TO_GHZ, } frequency[qubit] = popt[0] * GHZ_TO_HZ + middle_freq = np.median(frequencies) # solution to x*popt[1] + popt[2] = k # such that x is close to 0 # to avoid errors due to periodicity - sweetspot[qubit] = (np.round(popt[2]) - popt[2]) / popt[1] + sweetspot[qubit] = ( + np.round(popt[1] * middle_freq + popt[2]) - popt[2] + ) / popt[1] matrix_element[qubit] = popt[1] except ValueError as e: log.error( diff --git a/src/qibocal/protocols/flux_dependence/utils.py b/src/qibocal/protocols/flux_dependence/utils.py index 673a71275..9978be07e 100644 --- a/src/qibocal/protocols/flux_dependence/utils.py +++ b/src/qibocal/protocols/flux_dependence/utils.py @@ -279,13 +279,14 @@ def transmon_readout_frequency( xi (float): bias of target qubit xj (float): bias of neighbor qubit w_max (float): maximum frequency :math:`w_{max} = \sqrt{8 E_j E_c} - sweetspot (float): sweetspot [V]. - matrix_element(float): diagonal crosstalk matrix element - crosstalk_element(float): off-diagonal crosstalk matrix element d (float): asymmetry between the two junctions of the transmon. Typically denoted as :math:`d`. :math:`d = (E_J^1 - E_J^2) / (E_J^1 + E_J^2)`. + normalization(float): diagonal crosstalk matrix element + offset (float): phase_offset [V]. + crosstalk_element(float): off-diagonal crosstalk matrix element resonator_freq (float): bare resonator frequency [GHz] g (float): readout coupling. + charging_energy (float): Ec / h (GHz) Returns: (float): resonator frequency as a function of bias. From a23d7ae81e90ee0ce5e870906d42d373d91ad995 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Thu, 23 Jan 2025 15:46:20 +0400 Subject: [PATCH 2/7] fix: mismatch freq and biases --- .../protocols/flux_dependence/qubit_flux_dependence.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py index e2a464285..1cebfaeea 100644 --- a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py +++ b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py @@ -218,12 +218,12 @@ def fit_function(x, w_max, normalization, offset): "charging_energy": data.charging_energy[qubit] * HZ_TO_GHZ, } frequency[qubit] = popt[0] * GHZ_TO_HZ - middle_freq = np.median(frequencies) + middle_bias = np.median(biases) # solution to x*popt[1] + popt[2] = k # such that x is close to 0 # to avoid errors due to periodicity sweetspot[qubit] = ( - np.round(popt[1] * middle_freq + popt[2]) - popt[2] + np.round(popt[1] * middle_bias + popt[2]) - popt[2] ) / popt[1] matrix_element[qubit] = popt[1] except ValueError as e: From 051abd5837360070b78e680f8d5c8f4ada1dcff5 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Thu, 23 Jan 2025 16:54:17 +0400 Subject: [PATCH 3/7] doc: improve documentation of qubit flux dependency fit --- doc/source/protocols/flux/single.rst | 13 ++++++++++--- .../flux_dependence/qubit_flux_dependence.py | 14 ++++++++------ src/qibocal/protocols/flux_dependence/utils.py | 11 +++++------ 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/doc/source/protocols/flux/single.rst b/doc/source/protocols/flux/single.rst index 07ae5ca47..9e1746722 100644 --- a/doc/source/protocols/flux/single.rst +++ b/doc/source/protocols/flux/single.rst @@ -16,9 +16,8 @@ of GHz, which leads to several applications including quantum logical gates. The transmon frequency as a function of the external flux can be expressed as :cite:p:`Barrett_2023` -.. math:: - - f_q(\Phi) = \Bigg( f_q^{\text{max}} + \frac{E_C}{h} \Bigg) \sqrt[4]{d^2 + (1-d^2)\cos^2\Big( \pi \frac{\Phi}{\Phi_0}\Big)} - \frac{E_C}{h} \, +.. math:: f_q(\Phi) = \Bigg( f_q^{\text{max}} + \frac{E_C}{h} \Bigg) \sqrt[4]{d^2 + (1-d^2)\cos^2\Big( \pi \frac{\Phi}{\Phi_0}\Big)} - \frac{E_C}{h} \, + :label: transmon where :math:`f_{\text{max}} = ( \sqrt{8 E_C E_J} - E_C) / h` is the maximum qubit frequency, :math:`d` is the junctions asymmetry, :math:`E_C` is the charging energy, @@ -76,6 +75,14 @@ The expected output is the following: From the acquired data this protocol estimates the flux insensitive point "sweetspot", which corresponds to the flux value where the frequency is maximed, as well as the drive frequency and the diagonal crosstalk coefficient :math:`V_{ii}`. + +.. note:: + + From the cosinusoidal term in the transmon equation :math:numref:`transmon`, it is clear that the + sweetspot is not unique. + In this protocol, Qibocal returns the sweetspot that is closest to the bias + that is in the middle of the swept interval. + Both the sweetspot and the :math:`C_{ii}` can be understood by writing the full expression for the flux felt by qubit :math:`i` :cite:p:`Barrett_2023`: diff --git a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py index 1cebfaeea..c3b3dd913 100644 --- a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py +++ b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py @@ -165,9 +165,14 @@ def _acquisition( 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 - data (QubitFluxData): data object with information on the feature response at each current point. + Post-processing for QubitFlux Experiment. See `arXiv:0703002 `_. + Fit frequency as a function of current for the flux qubit spectroscopy data. + All possible sweetspots :math:`x` are evaluated by the function + :math:`x p_1 + p_2 = k`, for integers :math:`k`, where :math:`p_1` and :math:`p_2` + are respectively the normalization and the offset, as defined in + :mod:`qibocal.protocols.flux_dependence.utils.transmon_frequency`. + The code returns the sweetspot that is closest to the bias + in the middle of the swept interval. """ qubits = data.qubits @@ -219,9 +224,6 @@ def fit_function(x, w_max, normalization, offset): } frequency[qubit] = popt[0] * GHZ_TO_HZ middle_bias = np.median(biases) - # solution to x*popt[1] + popt[2] = k - # such that x is close to 0 - # to avoid errors due to periodicity sweetspot[qubit] = ( np.round(popt[1] * middle_bias + popt[2]) - popt[2] ) / popt[1] diff --git a/src/qibocal/protocols/flux_dependence/utils.py b/src/qibocal/protocols/flux_dependence/utils.py index 9978be07e..c45888f8e 100644 --- a/src/qibocal/protocols/flux_dependence/utils.py +++ b/src/qibocal/protocols/flux_dependence/utils.py @@ -205,11 +205,10 @@ def G_f_d(xi, xj, offset, d, crosstalk_element, normalization): xi (float): bias of target qubit xj (float): bias of neighbor qubit offset (float): phase_offset [V]. - matrix_element(float): diagonal crosstalk matrix element - crosstalk_element(float): off-diagonal crosstalk matrix element d (float): asymmetry between the two junctions of the transmon. Typically denoted as :math:`d`. :math:`d = (E_J^1 - E_J^2) / (E_J^1 + E_J^2)`. - normalization (float): Normalize diagonal element to 1 + crosstalk_element(float): off-diagonal crosstalk matrix element + normalization(float): diagonal crosstalk matrix element Returns: (float) """ @@ -237,11 +236,11 @@ def transmon_frequency( xi (float): bias of target qubit xj (float): bias of neighbor qubit w_max (float): maximum frequency :math:`w_{max} = \sqrt{8 E_j E_c} - sweetspot (float): sweetspot [V]. - matrix_element(float): diagonal crosstalk matrix element - crosstalk_element(float): off-diagonal crosstalk matrix element d (float): asymmetry between the two junctions of the transmon. Typically denoted as :math:`d`. :math:`d = (E_J^1 - E_J^2) / (E_J^1 + E_J^2)`. + normalization(float): diagonal crosstalk matrix element + offset (float): phase_offset [V]. + crosstalk_element(float): off-diagonal crosstalk matrix element charging_energy (float): Ec / h (GHz) Returns: From 0f4b58cc2533ec62a673ae3bea3e37677509026a Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Thu, 23 Jan 2025 17:14:29 +0400 Subject: [PATCH 4/7] refactor: change method to evaluate middle point --- src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py index c3b3dd913..37a296df0 100644 --- a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py +++ b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py @@ -223,7 +223,7 @@ def fit_function(x, w_max, normalization, offset): "charging_energy": data.charging_energy[qubit] * HZ_TO_GHZ, } frequency[qubit] = popt[0] * GHZ_TO_HZ - middle_bias = np.median(biases) + middle_bias = (np.max(biases) + np.min(biases)) / 2 sweetspot[qubit] = ( np.round(popt[1] * middle_bias + popt[2]) - popt[2] ) / popt[1] From 79b0cf452c6ec6a624478b6fe7ecc1970c9ffa36 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Thu, 23 Jan 2025 17:28:47 +0400 Subject: [PATCH 5/7] fix: remove variables overwrite --- .../flux_dependence/qubit_flux_dependence.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py index 37a296df0..ed32aab06 100644 --- a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py +++ b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py @@ -183,12 +183,15 @@ def _fit(data: QubitFluxData) -> QubitFluxResults: for qubit in qubits: qubit_data = data[qubit] - biases = qubit_data.bias - frequencies = qubit_data.freq + interval_biases = qubit_data.bias + interval_frequencies = qubit_data.freq signal = qubit_data.signal frequencies, biases = extract_feature( - frequencies, biases, signal, "max" if data.resonator_type == "2D" else "min" + interval_frequencies, + interval_biases, + signal, + "max" if data.resonator_type == "2D" else "min", ) def fit_function(x, w_max, normalization, offset): @@ -223,7 +226,7 @@ def fit_function(x, w_max, normalization, offset): "charging_energy": data.charging_energy[qubit] * HZ_TO_GHZ, } frequency[qubit] = popt[0] * GHZ_TO_HZ - middle_bias = (np.max(biases) + np.min(biases)) / 2 + middle_bias = (np.max(interval_biases) + np.min(interval_biases)) / 2 sweetspot[qubit] = ( np.round(popt[1] * middle_bias + popt[2]) - popt[2] ) / popt[1] From bce5dfb9881ed39e8ec0d27395c80569baa51d43 Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Thu, 23 Jan 2025 17:54:38 +0400 Subject: [PATCH 6/7] feat: plot sweetspot --- .../flux_dependence/qubit_flux_dependence.py | 1 + .../protocols/flux_dependence/utils.py | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py index ed32aab06..d784eb35a 100644 --- a/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py +++ b/src/qibocal/protocols/flux_dependence/qubit_flux_dependence.py @@ -257,6 +257,7 @@ def _plot(data: QubitFluxData, fit: QubitFluxResults, target: QubitId): fit_function=utils.transmon_frequency, ) if fit is not None: + fitting_report = table_html( table_dict( target, diff --git a/src/qibocal/protocols/flux_dependence/utils.py b/src/qibocal/protocols/flux_dependence/utils.py index c45888f8e..5e56f7733 100644 --- a/src/qibocal/protocols/flux_dependence/utils.py +++ b/src/qibocal/protocols/flux_dependence/utils.py @@ -71,6 +71,26 @@ def flux_dependence_plot(data, fit, qubit, fit_function=None): row=1, col=1, ) + fig.add_trace( + go.Scatter( + x=[ + fit.frequency[qubit] * HZ_TO_GHZ, + ], + y=[ + fit.sweetspot[qubit], + ], + mode="markers", + marker=dict( + size=8, + color="black", + symbol="cross", + ), + name=f"Sweetspot", + showlegend=True, + ), + row=1, + col=1, + ) fig.update_xaxes( title_text=f"Frequency [GHz]", From dd0339f8cc9e5d325928eb561740528218dffc1f Mon Sep 17 00:00:00 2001 From: Edoardo-Pedicillo Date: Fri, 24 Jan 2025 12:18:57 +0400 Subject: [PATCH 7/7] fix: rename attribute in Resonator Flux dependence results --- .../flux_dependence/resonator_crosstalk.py | 8 +++----- .../flux_dependence/resonator_flux_dependence.py | 15 ++++++--------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/qibocal/protocols/flux_dependence/resonator_crosstalk.py b/src/qibocal/protocols/flux_dependence/resonator_crosstalk.py index 7a16456fa..790d61a55 100644 --- a/src/qibocal/protocols/flux_dependence/resonator_crosstalk.py +++ b/src/qibocal/protocols/flux_dependence/resonator_crosstalk.py @@ -242,9 +242,7 @@ def _fit(data: ResCrosstalkData) -> ResCrosstalkResults: diagonal.matrix_element[qubit] if condition else data.matrix_element[qubit] ) resonator_frequency[qubit] = ( - diagonal.resonator_freq[qubit] - if condition - else data.resonator_frequency[qubit] + diagonal.frequency[qubit] if condition else data.resonator_frequency[qubit] ) offset[qubit] = ( diagonal.fitted_parameters[qubit]["offset"] @@ -328,7 +326,7 @@ def fit_function(x, crosstalk_element): crosstalk_matrix[target_qubit][flux_qubit] = matrix_element[qubit] return ResCrosstalkResults( - resonator_freq=resonator_frequency, + frequency=resonator_frequency, asymmetry=asymmetry, resonator_frequency_bias_point=resonator_frequency_bias_point, coupling=coupling, @@ -350,7 +348,7 @@ def _plot(data: ResCrosstalkData, fit: ResCrosstalkResults, target: QubitId): "Resonator Frequency at Bias point [Hz]", ] values = [ - np.round(fit.resonator_freq[target], 4), + np.round(fit.frequency[target], 4), np.round(fit.coupling[target] * 1e3, 2), np.round(fit.asymmetry[target], 2), np.round(fit.resonator_frequency_bias_point[target], 4), diff --git a/src/qibocal/protocols/flux_dependence/resonator_flux_dependence.py b/src/qibocal/protocols/flux_dependence/resonator_flux_dependence.py index f39abd054..a95223c08 100644 --- a/src/qibocal/protocols/flux_dependence/resonator_flux_dependence.py +++ b/src/qibocal/protocols/flux_dependence/resonator_flux_dependence.py @@ -35,7 +35,7 @@ class ResonatorFluxParameters(Parameters): class ResonatorFluxResults(Results): """ResonatoFlux outputs.""" - resonator_freq: dict[QubitId, float] = field(default_factory=dict) + frequency: dict[QubitId, float] = field(default_factory=dict) """Readout frequency.""" coupling: dict[QubitId, float] = field(default_factory=dict) """Qubit-resonator coupling.""" @@ -156,7 +156,7 @@ def _fit(data: ResonatorFluxData) -> ResonatorFluxResults: """PostProcessing for resonator_flux protocol. After applying a mask on the 2D data, the signal is fitted using - the expected resonator_freq vs flux behavior. + the expected frequency vs flux behavior. The fitting procedure requires the knowledge of the bare resonator frequency, the charging energy Ec and the maximum qubit frequency which is assumed to be the frequency at which the qubit is placed. @@ -235,7 +235,7 @@ def fit_function( "should fix the problem." ) return ResonatorFluxResults( - resonator_freq=resonator_freq, + frequency=resonator_freq, coupling=coupling, matrix_element=matrix_element, sweetspot=sweetspot, @@ -264,15 +264,12 @@ def _plot(data: ResonatorFluxData, fit: ResonatorFluxResults, target: QubitId): ], [ np.round(fit.coupling[target] * 1e3, 2), - np.round(fit.resonator_freq[target], 6), + np.round(fit.frequency[target], 6), np.round(fit.asymmetry[target], 3), np.round(fit.sweetspot[target], 4), np.round(fit.matrix_element[target], 4), np.round( - ( - data.bare_resonator_frequency[target] - - fit.resonator_freq[target] - ) + (data.bare_resonator_frequency[target] - fit.frequency[target]) * 1e-6, 2, ), @@ -284,7 +281,7 @@ def _plot(data: ResonatorFluxData, fit: ResonatorFluxResults, target: QubitId): def _update(results: ResonatorFluxResults, platform: Platform, qubit: QubitId): - update.readout_frequency(results.resonator_freq[qubit], platform, qubit) + update.readout_frequency(results.frequency[qubit], platform, qubit) update.coupling(results.coupling[qubit], platform, qubit) update.asymmetry(results.coupling[qubit], platform, qubit) update.sweetspot(results.sweetspot[qubit], platform, qubit)