Source code for qibocal.protocols.flux_dependence.utils

import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots

from ..utils import HZ_TO_GHZ


[docs] def is_crosstalk(data): """Check if keys are tuple which corresponds to crosstalk data structure.""" return all(isinstance(key, tuple) for key in data.data.keys())
[docs] def create_data_array(freq, bias, signal, phase, dtype): """Create custom dtype array for acquired data.""" size = len(freq) * len(bias) ar = np.empty(size, dtype=dtype) frequency, biases = np.meshgrid(freq, bias) ar["freq"] = frequency.ravel() ar["bias"] = biases.ravel() ar["signal"] = signal.ravel() ar["phase"] = phase.ravel() return np.rec.array(ar)
[docs] def flux_dependence_plot(data, fit, qubit, fit_function=None): figures = [] qubit_data = data[qubit] frequencies = qubit_data.freq * HZ_TO_GHZ subplot_titles = ( "Signal [a.u.]", "Phase [rad]", ) fig = make_subplots( rows=1, cols=2, horizontal_spacing=0.1, vertical_spacing=0.1, subplot_titles=subplot_titles, ) fig.add_trace( go.Heatmap( x=qubit_data.freq * HZ_TO_GHZ, y=qubit_data.bias, z=qubit_data.signal, colorbar_x=0.46, ), row=1, col=1, ) # 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" and qubit in fit.fitted_parameters ): params = fit.fitted_parameters[qubit] bias = np.unique(qubit_data.bias) fig.add_trace( go.Scatter( x=fit_function(bias, **params), y=bias, showlegend=True, name="Fit", marker=dict(color="green"), ), 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="Sweetspot", showlegend=True, ), row=1, col=1, ) fig.update_xaxes( title_text="Frequency [GHz]", row=1, col=1, ) fig.update_yaxes(title_text="Bias [V]", row=1, col=1) fig.add_trace( go.Heatmap( x=qubit_data.freq * HZ_TO_GHZ, y=qubit_data.bias, z=qubit_data.phase, colorbar_x=1.01, ), row=1, col=2, ) fig.update_xaxes( title_text="Frequency [GHz]", row=1, col=2, ) fig.update_layout(xaxis1=dict(range=[np.min(frequencies), np.max(frequencies)])) fig.update_layout( showlegend=True, legend=dict(orientation="h"), ) figures.append(fig) return figures
[docs] def flux_crosstalk_plot(data, qubit, fit, fit_function): figures = [] fitting_report = "" all_qubit_data = { index: data_qubit for index, data_qubit in data.data.items() if index[0] == qubit } fig = make_subplots( rows=1, cols=len(all_qubit_data), horizontal_spacing=0.3 / len(all_qubit_data), vertical_spacing=0.1, subplot_titles=len(all_qubit_data) * ("Signal [a.u.]",), ) for col, (flux_qubit, qubit_data) in enumerate(all_qubit_data.items()): frequencies = qubit_data.freq * HZ_TO_GHZ fig.add_trace( go.Heatmap( x=frequencies, y=qubit_data.bias, z=qubit_data.signal, showscale=False, ), row=1, col=col + 1, ) if fit is not None: if flux_qubit[1] != qubit and flux_qubit in fit.fitted_parameters: fig.add_trace( go.Scatter( x=fit_function( xj=qubit_data.bias, **fit.fitted_parameters[flux_qubit] ), y=qubit_data.bias, showlegend=not any( isinstance(trace, go.Scatter) for trace in fig.data ), legendgroup="Fit", name="Fit", marker=dict(color="green"), ), row=1, col=col + 1, ) fig.update_xaxes( title_text="Frequency [GHz]", row=1, col=col + 1, ) fig.update_yaxes( title_text=f"Qubit {flux_qubit[1]}: Bias [V]", row=1, col=col + 1 ) fig.update_layout(xaxis1=dict(range=[np.min(frequencies), np.max(frequencies)])) fig.update_layout(xaxis2=dict(range=[np.min(frequencies), np.max(frequencies)])) fig.update_layout(xaxis3=dict(range=[np.min(frequencies), np.max(frequencies)])) fig.update_layout( showlegend=True, ) figures.append(fig) return figures, fitting_report
[docs] def G_f_d(xi, xj, offset, d, crosstalk_element, normalization): """Auxiliary function to calculate qubit frequency as a function of bias. It also determines the flux dependence of :math:`E_J`,:math:`E_J(\\phi)=E_J(0)G_f_d`. For more details see: https://arxiv.org/pdf/cond-mat/0703002.pdf Args: xi (float): bias of target qubit xj (float): bias of neighbor qubit offset (float): phase_offset [V]. 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)`. crosstalk_element(float): off-diagonal crosstalk matrix element normalization(float): diagonal crosstalk matrix element Returns: (float) """ return ( d**2 + (1 - d**2) * np.cos( np.pi * (xi * normalization + normalization * xj * crosstalk_element + offset) ) ** 2 ) ** 0.25
[docs] def transmon_frequency( xi, xj, w_max, d, normalization, offset, crosstalk_element, charging_energy ): r"""Approximation to transmon frequency. The formula holds in the transmon regime Ej / Ec >> 1. See https://arxiv.org/pdf/cond-mat/0703002.pdf for the complete formula. Args: 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} 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: (float): qubit frequency as a function of bias. """ return (w_max + charging_energy) * G_f_d( xi, xj, offset=offset, d=d, normalization=normalization, crosstalk_element=crosstalk_element, ) - charging_energy
[docs] def transmon_readout_frequency( xi, xj, w_max, d, normalization, crosstalk_element, offset, resonator_freq, g, charging_energy, ): r"""Approximation to flux dependent resonator frequency. The formula holds in the transmon regime Ej / Ec >> 1. See https://arxiv.org/pdf/cond-mat/0703002.pdf for the complete formula. Args: 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} 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. """ return resonator_freq + g**2 * G_f_d( xi, xj, offset=offset, d=d, normalization=normalization, crosstalk_element=crosstalk_element, ) / ( resonator_freq - transmon_frequency( xi=xi, xj=xj, w_max=w_max, d=d, normalization=normalization, offset=offset, crosstalk_element=crosstalk_element, charging_energy=charging_energy, ) )
[docs] def qubit_flux_dependence_fit_bounds(qubit_frequency: float): """Returns bounds for qubit flux fit.""" return ( [ qubit_frequency * HZ_TO_GHZ - 1, 0, -1, ], [ qubit_frequency * HZ_TO_GHZ + 1, np.inf, 1, ], )