Source code for qiskit_metal.analyses.quantization.lumped_capacitive

# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2021.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""Handles the capacitive simulation of a transmon pocket model.

Convert capacitance matrices extracted from Q3D into Hamiltonian parameters
using the Duffing model. Typical input is the capacitance matrix calculated from Q3D.

Each function prints out the parameters and outputs a dictionary.
"""
# pylint: disable=invalid-name

import io
import re
from pathlib import Path
from typing import List, Union

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import scipy.optimize as opt
from pint import UnitRegistry

from pyEPR.calcs.convert import Convert
from .constants import (e, h, hbar, phinot, phi0)

__all__ = [
    'Ic_from_Lj', 'Ic_from_Ej', 'Cs_from_Ec', 'transmon_props', 'chi',
    'extract_transmon_coupled_Noscillator', 'levels_vs_ng_real_units',
    'get_C_and_Ic', 'cos_to_mega_and_delta', 'chargeline_T1',
    'readin_q3d_matrix', 'readin_q3d_matrix_m', 'load_q3d_capacitance_matrix',
    'df_cmat_style_print', 'move_index_to', 'df_reorder_matrix_basis',
    'lumped_oscillator_from_path'
]


[docs] def Ic_from_Lj(Lj: float) -> float: """Critical current In SI units. Args: Lj (float): In Henries Returns: float: In Amps """ return phi0 / Lj
[docs] def Ic_from_Ej(Ej: float) -> float: """Critical current In SI units. Args: Ej (float): In Joules Returns: float: In Amps """ return Ej / phi0
[docs] def Cs_from_Ec(Ec: float) -> float: """Get total shunt capacitance from charging energy In SI units. Args: Ec (float): In Joules Returns: float: In farads """ return e**2 / (2 * Ec)
[docs] def transmon_props(Ic: float, Cq: float): """Properties of a transmon qubit. Calculate LJ, EJ, EC, wq, eps from Ic,Cq. Args: Ic (float): Junction Ic (in A) Cq (float): Junction capacitance (in F) Returns: tuple: [LJ, EJ, Zqp, EC, wq, wq0, eps1] -- Inductance """ LJ = phi0 / Ic EJ = phi0**2 / LJ / hbar Zqp = np.sqrt(LJ / Cq) EC = e**2 / 2 / Cq / hbar wq0 = 1 / np.sqrt(LJ * Cq) wq = 1 / np.sqrt(LJ * Cq) - EC # charge dispersion eps1 = EC * 2**9 * (np.sqrt(2/np.pi)) * \ (EJ/2/EC)**(1.25) * np.exp(-np.sqrt(8*EJ/EC)) return LJ, EJ, Zqp, EC, wq, wq0, eps1
# TODO: Move to a more generic file
[docs] def chi(g: float, wr: float, w01: float, w12: float): r""" Calculate the dispersive shift $\chi$, where $2*\chi$ is the `|0> --> |1>` splitting). Accounts for push on the i-th transmon level due to the j-th transmon level, mediated by cavity. All args need to be in the same units. Args: g (float): Qubit-cavity linear coupling. wr (float): Frequency of resonator. w01 (float): Qubit 01 transition frequency w12 (float): Qubit 12 transition frequency Returns: float: Calculated chi value """ # Push on the i-th transmon level due to the j-th transmon level # mediated by cavity # shift of the zero state # In this case i=0 and j=1. chibus_0 = -2 * g**2 * w01 / (w01**2 - wr**2) # Koch Eq. (3.10) # shift of the 1 state # the g of the levels scales rougly as sqrt(n), so the 2 for the 2 / (w12 - wr) chibus_1 = g**2 * (1 / (w01 - wr) - 2 / (w12 - wr) + 1 / (w01 + wr) - 2 / (w12 + wr)) return (chibus_1 - chibus_0) / 2 # Koch Eq. (3.9)
[docs] def extract_transmon_coupled_Noscillator(capMatrix, Ic: float, CJ: float, N: int, fb: List[float], fr: float, res_L4_corr: float = None, g_scale: float = 1.0, print_info: bool = False): """Primary analysis function called by the user for lumped-element mode (LOM) analysis. Uses a Maxwell capacitance matrix generated by a capacitive extractor, such as Q3D. Additionally takes more values to determine many parameters of the Hamiltonian of the system. The capMatrix can be imported using `readin_q3d_matrix` Calculate the χ The full dispersive splitting using analytical approximations, i.e., return the `|0> --> |1>` splitting. Args: capMatrix (np.ndarray): Order of the capacitance matrix must be bus1...busN-1, ground, Qubit_pad1, Qubit_pad2, readout. (in F) If not in the correct order, use df_reorder_matrix_basis() to put it in the proper order. It is advised that the user follow a naming scheme in QiskitMetal or Q3D which results in the necessary order by default (eg. alphabetical) Ic (float): Junction Ic (in A) Cj (float): Junction capacitance (in F) N (int): Coupling pads (1 readout, N-1 bus) fb (float): Coupling bus and readout frequencies (in GHz). fb can be a list with the order the order they appear in the capMatrix. fr (float): Coupling bus and readout frequencies (in GHz). fr can be a list with the order the order they appear in the capMatrix. res_L4_corr (list): Correction factor is the resonators are L/4 if none it ignores, otherwise this is a list of length N in the form [1,0,1,...]. Defaults to None. g_scale (float): Scale factor print_info (bool): Print transmon and coupling properties. Defaults to False. Returns: dict: `ham_dict` is a dictionary of all the calculated values Raises: ValueError: If N is not positive ValueError: If the capacitance matrix is the wrong size """ # Error checks if N < 0: raise ValueError('N must positive') if len(capMatrix) != (N + 3): raise ValueError('Capacitance matrix is not the right size') # make list of angular frequencies of resonators wr = np.zeros(N) # angular freq of resonators (GHz-rad) for ii in range(N): if ii == 0: # readout resonator wr[ii] = 2 * np.pi * fr * 1e9 else: if isinstance(fb, (int, float)): # just a single one wr[ii] = 2 * np.pi * fb * 1e9 else: wr[ii] = 2 * np.pi * fb[ii - 1] * 1e9 # offset index by ######################################################## #### Transmission line properties # Initial values Zbus = 50 Cr = 0.5 * np.pi / (wr * Zbus) Lr = 1 / wr**2 / Cr # L/4 resonators have an effective capacitance that is # half the L/2 case (and likewise the Lr is twice) at the # same resonance frequency # DCM (I think from numerics) if not res_L4_corr is None: for i in range(len(res_L4_corr)): if res_L4_corr[i]: Cr[i] /= 2.0 Lr[i] *= 2.0 ######################################################## # Capacitance matrix parsing ground_index = max([0, N - 1]) qubit_index = [ground_index + 1, ground_index + 2] bus_index = np.zeros(N, dtype=int) for ii in range(N): if ii == 0: bus_index[ii] = len(capMatrix) - 1 else: bus_index[ii] = ii - 1 # Cg list of qubit pads to ground Cg = [ -capMatrix[qubit_index[0], ground_index], -capMatrix[qubit_index[1], ground_index] ] # Cs qubit pads to each other Cs = -capMatrix[qubit_index[0], qubit_index[1]] # Cbus (qubit pads to coupling pads) # index is ordered as [readout,bus1,...] Cbus = np.zeros([2, N]) for ii in range(2): for jj in range(N): Cbus[ii, jj] = -capMatrix[qubit_index[ii], bus_index[jj]] # crosspad capacitance Cbusbus = np.zeros([N, N]) for ii in range(N): for jj in range(N): if ii != jj: Cbusbus[ii, jj] = -capMatrix[bus_index[ii], bus_index[jj]] # sum of capacitances from each pad to ground # this assumes the bus couplers are at "ground" C1S = Cg[0] + np.sum(Cbus[ 0, ]) C2S = Cg[1] + np.sum(Cbus[ 1, ]) # total capacitance between pads tCSq = Cs + C1S * C2S / (C1S + C2S) # Key equation # total capacitance of each pad to ground? # Note the + in the squared term below !!! tCSbus = np.zeros(N) for ii in range(N): tCSbus[ii] = Cr[ii] - (Cbus[0, ii]+Cbus[1, ii])**2 / \ (C1S+C2S) + np.sum(Cbus[:, ii]) + np.sum(Cbusbus[ii, :]) # qubit to coupling pad capacitance tCqbus = (C2S * Cbus[ 0, ] - Cbus[ 1, ] * C1S) / (C1S + C2S) # coupling pad to coupling pad capacitance tCqbusbus = np.zeros([N, N]) for ii in range(N): for jj in range(N): tCqbusbus[ii, jj] = Cbusbus[ii, jj] + \ (Cbus[0, ii]+Cbus[1, ii])*(Cbus[0, jj]+Cbus[1, jj])/(C1S+C2S) # voltage division ratio bbus = (C2S * Cbus[ 0, ] - Cbus[ 1, ] * C1S) / ((C1S + C2S) * Cs + C1S * C2S) # total qubit capacitance (including junction capacitance) Cq = tCSq + CJ ######################################################## ##### Transmon qubit & bus quantum properties # get transmon properties given Ic and Cq LJ, EJ, Zqp, EC, wq, wq0, eps1 = transmon_props(Ic, Cq) # get numerical properties fq, alpha, disp, tphi_ms = levels_vs_ng_real_units(Cq / 1e-15, Ic / 1e-9, N=51) wq = 2 * np.pi * fq * 1e9 # effective impedances of the coupling pads (?) Zbus = np.sqrt(Lr / tCSbus) # matrix of the qubit and couplings with a single ground node # Cm = np.zeros([N+1,N+1]) # Cm = np.array([[Cq, -tCqr1, -tCqr2], # [-tCqr1, tCSr1, -tCr1r2], # [-tCqr2, -tCr1r2, tCSr2]]) # # Cminv = np.linalg.inv(Cm) # # #readout g # gr0 = 0.5*Cminv[0,1]/np.sqrt(Zqp*Zr1p) # print('gr0 = %f [MHz]'%(gr0/2/np.pi/1e6)) # g's from the qubit gqbus = 0.5 * wr * bbus * np.sqrt(Zbus / Zqp) * g_scale #gbus = bbus*wr*np.sqrt(Zbus)*e*(EJ/8/EC)**(1/4)/np.sqrt(hbar) gbus_in_MHz = gqbus / 1e6 / 2 / np.pi # g's between pads gbusbus = np.zeros([N, N]) for ii in range(N): for jj in range(N): gbusbus[ii, jj] = (0.01) * tCqbusbus[ii, jj] / (tCSbus[ii] * tCSbus[jj]) ######################################################## ##### Purcell, Qs, dissipative # guesses for the Q's Qreadout = 1e4 Qcouplingbus = 1e5 Qbus = np.zeros(N) for ii in range(N): if ii == 0: Qbus[ii] = Qreadout else: Qbus[ii] = Qcouplingbus # loss tangent kbus = wr / Qbus # purcell due to each coupling bus T1bus = (wr**2 - wq**2)**2 / (4 * kbus * gqbus**2 * wq**2) # total T1 if N > 0: T1 = 1 / (np.sum(1 / T1bus)) else: T1 = 100 ######################################################## ##### Transmon properties and final summary # chi's #d = -EC d = alpha * 2 * np.pi * 1e6 Chi_in_MHz = 2 * chi(gqbus, wr, wq, d + wq) / 2 / np.pi / 1e6 # Total chi in MHz ham_dict = {} ham_dict['fQ'] = wq / 2 / np.pi / 1E9 ham_dict['EC'] = EC / 2 / np.pi / 1E6 ham_dict['EJ'] = EJ / 2 / np.pi / 1E9 # correction to the anharmonicity (from eq 52 of solgun et al.) #ham_dict['alpha'] = ((wq0/wq)**2*ham_dict['EC']) ham_dict['alpha'] = alpha ham_dict['dispersion'] = disp / 1e3 ham_dict['gbus'] = gbus_in_MHz ham_dict['chi_in_MHz'] = Chi_in_MHz if print_info: print(qubit_index, bus_index) print('Predicted Values') print('') print('Transmon Properties') print('f_Q %f [GHz]' % ham_dict['fQ']) print('EC %f [MHz]' % ham_dict['EC']) print('EJ %f [GHz]' % ham_dict['EJ']) print('alpha %f [MHz]' % ham_dict['alpha']) print('dispersion %f [KHz]' % ham_dict['dispersion']) print('Lq %f [nH]' % (phi0**2 / (hbar * EJ) / 1e-9)) print('Cq %f [fF]' % (Cq / 1e-15)) print('T1 %f [us]' % (T1 / (1e-6))) print('') print('**Coupling Properties**') for ii in range(N): print('\ntCqbus%d %f [fF]' % (ii + 1, tCqbus[ii] / (1e-15))) print('gbus%d_in_MHz %f [MHz]' % (ii + 1, ham_dict['gbus'][ii])) print('χ_bus%d %f [MHz]' % (ii + 1, Chi_in_MHz[ii])) print('1/T1bus%d %f [Hz]' % (ii + 1, 1 / T1bus[ii] / (2 * np.pi))) print('T1bus%d %f [us]' % (ii + 1, T1bus[ii] / (1e-6))) print('Bus-Bus Couplings') for ii in range(N): for jj in range(ii + 1, N): print('gbus%d_%d %f [MHz]' % (ii + 1, jj + 1, gbusbus[ii, jj] / (2 * np.pi * 1e6))) return ham_dict
[docs] def levels_vs_ng_real_units(Cq, IC, N=301, do_disp=0, do_plots=0): """This numerically computes the exact transmon levels given C and IC as a function of the ng ration -- it subtracts the vacuum flucations so that the ground state is set to zero energy. Args: C (float): In fF Ic (float): In nA N (int): Number of charge values to use (needs to be odd) do_disp (int): Will print out the values do_plots (int): Will plot the data Returns: tuple: fqubitGHz, anharMHz, disp, tphi_ms Raises: ValueError: If the matrix is not Hermitian """ C = Cq * 1e-15 IC = IC * 1e-9 Ec = e**2 / 2 / C nmax = 40 dim = 2 * nmax + 1 nmat = np.zeros([dim, dim]) V = nmat charge = np.linspace(-1., 1., N) # KE for ii in range(dim): nmat[ii, ii] = ii - nmax # PE V = np.diag(-0.5 * np.ones([dim - 1]), 1) V = (V + np.transpose(np.conj(V))) varphi = hbar / 2 / e EJ = IC * varphi elvls = np.zeros([dim, N]) for iindex in range(N): ng = charge[iindex] H = 4 * Ec * (nmat - ng * np.eye(dim))**2 + EJ * V if (not np.array_equal(H, np.transpose(np.conj(H)))): raise ValueError('Matrix is not Hermitian') [d, v] = np.linalg.eig(H) sortIX = np.argsort(d) sorted_d = d[sortIX] elvls[:, iindex] = (sorted_d - sorted_d[0]) if do_plots: # plot using matplotlib (might need to clean this up) plt.figure() plt.subplot(1, 2, 1) plt.plot(charge, elvls[ 0, ] / h / 1e9, 'k', charge, elvls[ 1, ] / h / 1e9, 'b', charge, elvls[ 2, ] / h / 1e9, 'r', charge, elvls[ 3, ] / h / 1e9, 'g', linewidth=2) plt.xlabel('Gate charge, n_g [2e]') plt.ylabel('Energy, E_n [GHz]') plt.subplot(1, 2, 2) plt.plot(charge, (-elvls[1, :] / h + elvls[1, 0] / h) / 1e3, 'k') plt.xlabel('Gate charge, n_g [2e]') plt.ylabel('Energy [kHz], ') plt.show() plt.figure(2) plt.subplot(1, 2, 1) plt.plot( charge, 1000 * (elvls[ 2, ] / h / 1e9 - elvls[ 0, ] / h / 1e9 - 2 * elvls[ 1, ] / h / 1e9 - elvls[ 0, ] / h / 1e9), charge, -charge * 0 - 1000 * Ec / h / 1e9) plt.xlabel('Gate charge, n_g [2e]') plt.ylabel('delta [MHZ] green theory, blue numerics ') plt.subplot(1, 2, 2) plt.plot(charge, elvls[ 1, ] / h / 1e9 - elvls[ 0, ] / h / 1e9, charge, charge * 0 + (np.sqrt(8 * EJ * Ec) - Ec) / h / 1e9) plt.xlabel('Gate charge, n_g [2e]') plt.ylabel('F01 [GHZ] green theory, blue numerics ') plt.show() fqubitGHz = np.mean(elvls[ 1, ] / h / 1e9) anharMHz = np.mean(1000 * (elvls[ 2, ] / h / 1e9 - elvls[ 0, ] / h / 1e9 - 2 * elvls[ 1, ] / h / 1e9 - elvls[ 0, ] / h / 1e9)) disp = np.max(-elvls[ 1, ] / h + elvls[1, 0] / h) tphi_ms = 2 / (2 * np.pi * disp * np.pi * 1e-4 * 1e-3) if do_disp: print('Mean Frequency %f [GHz]' % fqubitGHz) print('Anharmonicity %f [MHz]' % anharMHz) print('EC %f [GHz]' % (Ec / h / 1e9)) print('Charge Dispersion %f [kHz]' % (disp / 1e3)) print('Dephasing Time %f [ms]' % tphi_ms) return fqubitGHz, anharMHz, disp, tphi_ms
[docs] def get_C_and_Ic(Cin_est, Icin_est, f01, f02on2): """Get the capacitance and critical current for a transmon of a certain frequency and anharmonicity. Args: Cin_est (float): Initial guess for capacitance (in fF) Icin_est (float): Initial guess for critical current (in nA) f01 (float): Transmon frequency (in GHz) f02on2 (float): 02/2 frequency (in GHz) Returns: tuple: [C,Ic] from levels_vs_ng_real_units that gives the specified frequency and anharmonicity """ xrr = opt.minimize(lambda x: cos_to_mega_and_delta(x[0], x[1], f01, f02on2), [Cin_est, Icin_est], tol=1e-4, options={ 'maxiter': 100, 'disp': True }) return xrr.x
######################################################################## # Utility functions for reporting and loading - Zlatko # Cost function for calculating C and IC # given a C and IC calculate f and f02/2 from 'levels_vs_ng_real_units' # and least square with measured f01,f02on2
[docs] def cos_to_mega_and_delta(Cin, ICin, f01, f02on2): """Cost function for calculating C and IC given a C and IC calculate f and f02/2 from 'levels_vs_ng_real_units' and least square with measured f01,f02on2. Args: Cin (float): Cin ICin (float): ICin f01 (float): f01 f02on2 (float): f02on2 Returns: float: Calculated value """ fqubitGHz, anharMHz, disp, tphi_ms = levels_vs_ng_real_units(Cin, ICin, N=51) return ((fqubitGHz - f01)**2 + (fqubitGHz + anharMHz / 2. / 1e3 - f02on2)**2)**0.5
# TODO: Move to a more generic file
[docs] def chargeline_T1(Ccharge, Cq, f01): """Calculate the charge line `T1`. Args: Cchare (float): Ccharge Cq (float): Cq f01 (float): f01 Returns: float: Calculated chargeline T1 """ return Cq / (Ccharge**2 * 50. * (2 * np.pi * f01)**2)
# TODO: Move to a more generic file
[docs] def readin_q3d_matrix(path: str, delim_whitespace=True): """Read in the txt file created from q3d export as CSV and output the capacitance matrix file exported by Ansys Q3D. When exporting pick "save as type: data table" Args: path (str): Path to file Returns: tuple: df_cmat, units, design_variation, df_cond Example file: :: DesignVariation:$BBoxL='650um' $boxH='750um' $boxL='2mm' $QubitGap='30um' $QubitH='90um' $QubitL='450um' Lj_1='13nH' Setup1:LastAdaptive Problem Type:C C Units:farad, G Units:mSie Reduce Matrix:Original Frequency: 5.5E+09 Hz Capacitance Matrix ground_plane Q1_bus_Q0_connector_pad Q1_bus_Q2_connector_pad Q1_pad_bot Q1_pad_top1 Q1_readout_connector_pad ground_plane 2.8829E-13 -3.254E-14 -3.1978E-14 -4.0063E-14 -4.3842E-14 -3.0053E-14 Q1_bus_Q0_connector_pad -3.254E-14 4.7257E-14 -2.2765E-16 -1.269E-14 -1.3351E-15 -1.451E-16 Q1_bus_Q2_connector_pad -3.1978E-14 -2.2765E-16 4.5327E-14 -1.218E-15 -1.1552E-14 -5.0414E-17 Q1_pad_bot -4.0063E-14 -1.269E-14 -1.218E-15 9.5831E-14 -3.2415E-14 -8.3665E-15 Q1_pad_top1 -4.3842E-14 -1.3351E-15 -1.1552E-14 -3.2415E-14 9.132E-14 -1.0199E-15 Q1_readout_connector_pad -3.0053E-14 -1.451E-16 -5.0414E-17 -8.3665E-15 -1.0199E-15 3.9884E-14 Conductance Matrix ground_plane Q1_bus_Q0_connector_pad Q1_bus_Q2_connector_pad Q1_pad_bot Q1_pad_top1 Q1_readout_connector_pad ground_plane 0 0 0 0 0 0 Q1_bus_Q0_connector_pad 0 0 0 0 0 0 Q1_bus_Q2_connector_pad 0 0 0 0 0 0 Q1_pad_bot 0 0 0 0 0 0 Q1_pad_top1 0 0 0 0 0 0 Q1_readout_connector_pad 0 0 0 0 0 0 """ text = Path(path).read_text() s1 = text.split('Capacitance Matrix') assert len(s1) == 2, "Copuld not split text to `Capacitance Matrix`" s2 = s1[1].split('Conductance Matrix') df_cmat = pd.read_csv(io.StringIO(s2[0].strip()), delim_whitespace=delim_whitespace, skipinitialspace=True, index_col=0) if len(s2) > 1: df_cond = pd.read_csv(io.StringIO(s2[1].strip()), delim_whitespace=delim_whitespace, skipinitialspace=True, index_col=0) else: df_cond = None if delim_whitespace == False and len(df_cmat.columns): df_cmat = df_cmat.drop(df_cmat.columns[-1], axis=1) units = re.findall(r'C Units:(.*?),', text)[0] design_variation = re.findall(r'DesignVariation:(.*?)\n', text) if len(design_variation) == 0: design_variation = re.findall(r'Design Variation:(.*?)\n', text) if design_variation: design_variation = design_variation[0] else: design_variation = '' else: design_variation = design_variation[0] return df_cmat, units, design_variation, df_cond
[docs] def readin_q3d_matrix_m(path: str) -> pd.DataFrame: """Read in Q3D cap matrix from a .m file exported by Ansys Q3d. Args: path (str): Path to .m file Returns: pd.DataFrame of cap matrix, with no names of columns. """ text = Path(path).read_text() match = re.findall(r'capMatrix (.*?)]', text, re.DOTALL) if match: match = match[0].strip('= [').strip(']').strip('\n') dfC = pd.read_csv(io.StringIO(match), skipinitialspace=True, header=None) return dfC
# TODO: Move to a more generic file
[docs] def load_q3d_capacitance_matrix(path, user_units='fF', _disp=True): """Load Q3D capcitance file exported as Maxwell matrix. Do not export conductance. Units are read in automatically and converted to user units. Args: path (str): Path to file. user_units (str): Units. Defaults to 'fF'. _disp (bool): whehter or not to display messages. Defaults to True. Returns: tupe: df_cmat, user_units, design_variation, df_cond """ df_cmat, Cunits, design_variation, df_cond = readin_q3d_matrix(path) # Unit convert ureg = UnitRegistry() q = ureg.parse_expression(Cunits).to(user_units) df_cmat = df_cmat * q.magnitude # scale to user units # Report if _disp: print( "Imported capacitance matrix with UNITS: [%s] now converted to USER UNITS:[%s] \ from file:\n\t%s" % (Cunits, user_units, path)) df_cmat_style_print(df_cmat) return df_cmat, user_units, design_variation, df_cond
[docs] def lumped_oscillator_from_path(path: str, Lj_nH: float, Cj_fF: float, N: int, fr: Union[list, float], fb: Union[list, float]) -> pd.DataFrame: """Obtain a single result dataframe from a Q3D capacitance file pointed to by path. Similar to member method lumped_oscillator_vs_passes() of QQ3DRenderer but for a user provided capacitance matrix file. Args: path (str): Path to file. Lj_nH (float): Junction inductance (in nH) Cj_fF (float): Junction capacitance (in fF) N (int): Coupling pads (1 readout, N - 1 bus) fr (Union[list, float]):Readout frequencies (in GHz). fr can be a list with the order they appear in the capMatrix. fb (Union[list, float]): Coupling bus frequencies (in GHz). fb can be a list with the order they appear in the capMatrix. g_scale (float, optional): Scale factor. Defaults to 1.. Returns: dict: A single dataframe corresponding to a single capacitance matrix """ ureg = UnitRegistry() IC_Amps = Convert.Ic_from_Lj(Lj_nH, 'nH', 'A') CJ = ureg(f'{Cj_fF} fF').to('farad').magnitude fr = ureg(f'{fr} GHz').to('GHz').magnitude fb = [ureg(f'{freq} GHz').to('GHz').magnitude for freq in fb] df_cmat, user_units, _, _, = load_q3d_capacitance_matrix(path) c_units = ureg(user_units).to('farads').magnitude RES = extract_transmon_coupled_Noscillator(df_cmat.values * c_units, IC_Amps, CJ, N, fb, fr, g_scale=1, print_info=True) RES = pd.DataFrame([RES]) RES['χr MHz'] = abs(RES['chi_in_MHz'].apply(lambda x: x[0])) RES['gr MHz'] = abs(RES['gbus'].apply(lambda x: x[0])) return RES
[docs] def df_cmat_style_print(df_cmat: pd.DataFrame): """Display the dataframe in the cmat style. Args: df_cmat (dataframe): Dataframe to display """ from IPython.display import display display(df_cmat.style.format("{:.2f}").bar(color='#5fba7d', width=100))
######################################################################## # Utility functions - Zlatko
[docs] def move_index_to(i_from: List[int], i_to: List[int], len_): """Utility function to swap index. Args: i_from (int): Data frame to swap index i_to (int): Data frame to index len_ (int): Length of array Returns: list: list of indecies, such as array([1, 2, 3, 4, 0, 5]) """ idxs = np.arange(0, len_) idxs = np.delete(idxs, i_from) return np.insert(idxs, i_to, i_from)
# TODO: Move to a more generic file
[docs] def df_reorder_matrix_basis(df, i_from, i_to): """Data frame handle reording of matrix basis. Args: df (DataFrame): Data frame to swap i_from (int): Index to move from i_to (int): Index to move to Returns: DataFrame: The changed index DataFrame """ arr = df.values idx = move_index_to(i_from, i_to, len(arr)) arr = arr[np.ix_(idx, idx)] # Maybe make copy return pd.DataFrame(arr, columns=df.columns[idx], index=df.index[idx])