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 e87983f05..d784eb35a 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 @@ -178,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): @@ -218,10 +226,10 @@ def fit_function(x, w_max, normalization, offset): "charging_energy": data.charging_energy[qubit] * HZ_TO_GHZ, } frequency[qubit] = popt[0] * GHZ_TO_HZ - # 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] + 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] matrix_element[qubit] = popt[1] except ValueError as e: log.error( @@ -249,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/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) diff --git a/src/qibocal/protocols/flux_dependence/utils.py b/src/qibocal/protocols/flux_dependence/utils.py index 673a71275..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]", @@ -205,11 +225,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 +256,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: @@ -279,13 +298,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.