Source code for qibocal.protocols.flux_dependence.utils

from dataclasses import dataclass

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

from ...auto.operation import Parameters
from ..utils import HZ_TO_GHZ


[docs] @dataclass(kw_only=True) class FluxFrequencySweepParameters(Parameters): """Parameters to define flux DC sweep.""" freq_width: int """Width for frequency sweep relative to the readout frequency [Hz].""" freq_step: int """Frequency step for sweep [Hz].""" bias_width: float """Width for bias sweep [V].""" bias_step: float """Bias step for sweep [V].""" @property def frequency_range(self) -> np.ndarray: return np.arange(-self.freq_width / 2, self.freq_width / 2, self.freq_step) @property def bias_range(self) -> np.ndarray: return np.arange(-self.bias_width / 2, self.bias_width / 2, self.bias_step)
[docs] def create_data_array(freq, bias, signal, 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() 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 filtered_freq, filtered_bias = data.filtered_data(qubit) fig = go.Figure() fig.add_trace( go.Heatmap( x=qubit_data.freq * HZ_TO_GHZ, y=qubit_data.bias, z=qubit_data.signal, colorbar=dict(title="Signal [a.u.]"), colorscale="Viridis", ), ) fig.add_trace( go.Scatter( x=filtered_freq * HZ_TO_GHZ, y=filtered_bias, name="Estimated points", mode="markers", marker=dict(color="rgb(248, 248, 248)"), ) ) # TODO: This fit is for frequency, can it be reused here, do we even want the fit ? if ( fit is not None and fit_function 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="rgb(248, 248, 248)"), ), ) fig.add_trace( go.Scatter( x=[ fit.frequency[qubit] * HZ_TO_GHZ, ], y=[ fit.sweetspot[qubit], ], mode="markers", marker=dict( size=8, color="red", ), name="Sweetspot", showlegend=True, ), ) fig.update_xaxes( title_text="Frequency [GHz]", ) fig.update_yaxes(title_text="Bias [V]") 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. """ qubit_frequency = transmon_frequency( xi=xi, xj=xj, w_max=w_max, d=d, normalization=normalization, offset=offset, crosstalk_element=crosstalk_element, charging_energy=charging_energy, ) return resonator_freq + g**2 * ( 1 / (resonator_freq - qubit_frequency) - 1 / (resonator_freq - qubit_frequency + 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, ], )