class Sat_Qubit(Qubit): atten = Float(83.0) phi_arr = Array() pwr_arr = Array() def _default_phi_arr(self): return linspace(0.2, 0.3, 101) def _default_pwr_arr(self): return linspace(-50.0, 0, 31) gamma = Float(38.0e6) gamma_el = Float(0.750e6) gamma_phi = Float(0.0) T = Float(0.03) N_dim = Int(8) fd = Float(4.5e9) N_gamma = SProperty() @N_gamma.getter def _get_N_gamma(self, fd, T): return 1.0 / (exp(h * fd / (k * T)) - 1.0) @private_property def a_op(self): return destroy(self.N_dim) @private_property def a_dag(self): return self.a_op.dag() @private_property def tdiag(self): return Qobj(diag(range(0, 2 * self.N_dim, 2))) #dephasing operator, tdiag c_ops = SProperty().tag(sub=True) @c_ops.getter def _get_c_ops(self, gamma, N_gamma, a_op, a_dag, tdiag, gamma_phi): rate1 = gamma * (1 + N_gamma) rate2 = gamma * N_gamma return [sqrt(rate1) * a_op, sqrt(rate2) * a_dag, gamma_phi * tdiag] @private_property def nvec(self): return arange(self.N_dim) @private_property def fdvec(self): return self.nvec * self.fd @private_property def Ecvec(self): return -self.Ec * (6.0 * self.nvec**2 + 6.0 * self.nvec + 3.0) / 12.0 @private_property def pwr_lin(self): pwr_fridge = self.pwr_arr - self.atten return 0.001 * 10**(pwr_fridge / 10.0) @private_property def Omega_arr(self): pwr_fridge = self.pwr_arr - self.atten return sqrt(0.001 * 10**(pwr_fridge / 10.0) / (h * a.fd)) * sqrt(2 * self.gamma_el) @private_property def value_grid(self): value_grid = array(meshgrid(self.phi_arr, self.Omega_arr)) return zip(value_grid[0, :, :].flatten(), value_grid[1, :, :].flatten()) funcer = Callable() # @private_property # def funcer(self): # def find_expect2(vg): #phi=0.1, Omega_vec=3.0): # phi, Omega=vg#.shape # Omega_vec=-0.5j*(Omega*a.a_dag - conj(Omega)*a.a_op) # # Ej = a.Ejmax*absolute(cos(pi*phi)) #Josephson energy as function of Phi. # # wTvec = (-Ej + sqrt(8.0*Ej*a.Ec)*(a.nvec+0.5)+a.Ecvec)/h #\omega_m # # wT = wTvec-a.fdvec #rotating frame of gate drive \omega_m-m*\omega_\gate # transmon_levels = Qobj(diag(wT[range(a.N_dim)])) # H=transmon_levels +Omega_vec #- 0.5j*(Omega_true*adag - conj(Omega_true)*a) # final_state = steadystate(H, a.c_ops) #solve master equation # # return expect( a.a_op, final_state) #expectation value of relaxation operator # #Omega=\alpha\sqrt{2\Gamma_10} where |\alpha|^2=phonon flux=number of phonons per second # #Omega=2\alpha\sqrt{\gamma} where |\alpha|^2=phonon flux=number of phonons per second # # return find_expect @private_property def fexpt(self): fexpt = parallel_map(self.funcer, self.value_grid, progress_bar=True) return reshape(fexpt, (len(self.pwr_arr), len(self.phi_arr)))
class Sat_Qubit(Qubit): atten = Float(83.0) phi_arr = Array() pwr_arr = Array() frq_arr = Array() do_ls = Bool(True) harm_osc = Bool(False) Np = Int(9) f0 = Float(5.30001e9) def _default_phi_arr(self): return linspace(0.35, 0.4, 2 * 150) * pi def _default_pwr_arr(self): return linspace(-50.0, 0, 31) def _default_frq_arr(self): return linspace(3.5e9, 7.5e9, 51) gamma = Float(38.0e6) gamma_el = Float(0.750e6) gamma_phi = Float(0.0) T = Float(0.03) N_dim = Int(8) fd = Float(4.5e9) Zc = Float(50.0) Ic = Float(112.0e-9) def _get_fTvec(self, phi, gamma, Delta, fd, Psaw): C = self.Ct * (1.0 + 2.0 * Delta / fd) + self.Cc Ec = e**2 / (2 * C) Ecvec = -Ec * (6.0 * self.nvec**2 + 6.0 * self.nvec + 3.0) / 12.0 Isq = 0.0 * 4.0 * gamma * 2.0 * (self.Cc + self.Ct) * Psaw * 1 #+0.0j if Isq < self.Ic**2: Ej = self.Ejmax * absolute(cos(phi)) * sqrt( 1.0 - Isq / self.Ic**2) #Josephson energy as function of Phi. else: Ej = 0.0 #print sqrt(Isq) if self.harm_osc: return sqrt(8.0 * Ej * Ec) * (self.nvec + 0.5) / h return (-Ej + sqrt(8.0 * Ej * Ec) * (self.nvec + 0.5) + Ecvec) / h #\omega_m #return 0.0*(-Ej + 1.0*sqrt(8.0*Ej*self.Ec)*(self.nvec+0.5)+self.Ecvec)/h #\omega_m def _get_X(self, f, f0, Np): return Np * pi * (f - f0) / f0 def _get_GammaDelta(self, gamma, fd, f0, Np): if not self.do_ls: return gamma, 0.0 X = self._get_X(f=fd, f0=f0, Np=Np) Delta = gamma * (sin(2 * X) - 2 * X) / (2 * X**2) Gamma = gamma * (sin(X) / X)**2 return Gamma, Delta Gamma_C = SProperty() @Gamma_C.getter def _get_Gamma_C(self, fd, Cc, Ct, Zc): return 2 * pi * (Zc * Cc**2 * fd**2) / (4 * (Cc + Ct)) N_gamma = SProperty() @N_gamma.getter def _get_N_gamma(self, fd, T): return 1.0 / (exp(h * fd / (k * T)) - 1.0) @private_property def a_op(self): return destroy(self.N_dim) @private_property def a_dag(self): return self.a_op.dag() @private_property def tdiag(self): return Qobj(diag(range(0, 2 * self.N_dim, 2))) #dephasing operator, tdiag c_ops = SProperty().tag(sub=True) @c_ops.getter def _get_c_ops(self, gamma, N_gamma, a_op, a_dag, tdiag, gamma_phi): rate1 = gamma * (1 + N_gamma) rate2 = gamma * N_gamma return [sqrt(rate1) * a_op, sqrt(rate2) * a_dag, gamma_phi * tdiag] @private_property def nvec(self): return arange(self.N_dim) @private_property def fdvec(self): return self.nvec * self.fd @private_property def Ecvec(self): return -self.Ec * (6.0 * self.nvec**2 + 6.0 * self.nvec + 3.0) / 12.0 @private_property def pwr_lin(self): pwr_fridge = self.pwr_arr - self.atten return 0.001 * 10**(pwr_fridge / 10.0) @private_property def Omega_arr(self): return sqrt(self.pwr_lin / h * 2.0) #pwr_fridge=self.pwr_arr-self.atten #return sqrt(0.001*10**(pwr_fridge/10.0)/(h*a.fd))*sqrt(2*self.gamma_el) @private_property def value_grid(self): value_grid = array(meshgrid(self.phi_arr, self.frq_arr)) #value_grid=array(meshgrid(self.phi_arr, self.Omega_arr)) return zip(value_grid[0, :, :].flatten(), value_grid[1, :, :].flatten()) funcer = Callable() funcer2 = Callable() @private_property def value_grid2(self): value_grid = array(meshgrid(self.phi_arr, self.pwr_arr)) return zip(value_grid[0, :, :].flatten(), value_grid[1, :, :].flatten()) power_plot = Bool(True) acoustic_plot = Bool(True) @private_property def fexpt(self): self.power_plot = False fexpt = parallel_map(self.funcer, self.value_grid, progress_bar=True) return reshape(fexpt, (len(self.frq_arr), len(self.phi_arr))) @private_property def fexpt2(self): self.power_plot = True fexpt = parallel_map(self.funcer, self.value_grid2, progress_bar=True) #print shape(self.value_grid2) #print self.pwr_arr.shape #print self.phi_arr.shape #print shape(fexpt) return reshape(fexpt, (len(self.pwr_arr), len(self.phi_arr))) def find_expect(self, vg, pwr, fd): if self.power_plot: phi, pwr = vg else: phi, fd = vg pwr_fridge = pwr - self.atten lin_pwr = 0.001 * 10**(pwr_fridge / 10.0) Omega = sqrt(lin_pwr / h * 2.0) gamma, Delta = self._get_GammaDelta(fd=fd, f0=self.f0, Np=self.Np, gamma=self.gamma) g_el = self._get_Gamma_C(fd=fd) wTvec = self._get_fTvec(phi=phi, gamma=gamma, Delta=Delta, fd=fd, Psaw=lin_pwr) if self.acoustic_plot: Om = Omega * sqrt(gamma / fd) else: Om = Omega * sqrt(g_el / fd) wT = wTvec - fd * self.nvec #rotating frame of gate drive \omega_m-m*\omega_\gate transmon_levels = Qobj(diag(wT[range(self.N_dim)])) rate1 = (gamma + g_el) * (1.0 + self.N_gamma) rate2 = (gamma + g_el) * self.N_gamma c_ops = [sqrt(rate1) * self.a_op, sqrt(rate2) * self.a_dag ] #, sqrt(rate3)*self.a_op, sqrt(rate4)*self.a_dag] Omega_vec = -0.5j * (Om * self.a_dag - conj(Om) * self.a_op) H = transmon_levels + Omega_vec final_state = steadystate(H, c_ops) #solve master equation fexpt = expect(self.a_op, final_state) #expectation value of relaxation operator #return fexpt if self.acoustic_plot: return 1.0 * gamma / Om * fexpt else: return 1.0 * sqrt(g_el * gamma) / Om * fexpt
class Rho(Agent): base_name = "rho" material = Enum('LiNbYZ', 'GaAs', 'LiNb128', 'LiNbYZX', 'STquartz').tag(label="material", expression="Substrate") def _default_material(self): return 'LiNbYZ' def _observe_material(self, change): if change["type"] == "update": self.Dvv = None self.vf = None self.epsinf = None @t_property(desc="coupling strength (relative speed difference)", unit="%", tex_str=r"$\Delta v/v$", expression=r"$\Delta v/v=(v_f-v_m)/v_f$", label="piezoelectric coupling") def Dvv(self, material): return { "STquartz": 0.06e-2, 'GaAs': 0.035e-2, 'LiNbYZ': 2.4e-2, 'LiNb128': 2.7e-2, 'LiNbYZX': 0.8e-2 }[material] K2 = SProperty().tag(desc="coupling strength", unit="%", tex_str=r"K$^2$", expression=r"K$^2=2\Delta v/v$", label="piezoelectric coupling") @K2.getter def _get_K2(self, Dvv): r"""Coupling strength. K$^2=2\Delta v/v$""" return Dvv * 2.0 @K2.setter def _get_Dvv(self, K2): """other coupling strength. free speed minus metal speed all over free speed""" return K2 / 2.0 @t_property(desc="speed of SAW on free surface", unit="m/s", expression=r"$v_f$", format_str=r"{0:.4g} m/s", label="free SAW speed") def vf(self, material): return { "STquartz": 3159.0, 'GaAs': 2900.0, 'LiNbYZ': 3488.0, 'LiNb128': 3979.0, 'LiNbYZX': 3770.0 }[material] @t_property(desc="Capacitance of one finger pair per unit length", expression=r"$\epsilon_\infty=C_S$", label="SAW permittivity") def epsinf(self, material): return { "STquartz": 5.6 * eps0, 'GaAs': 1.2e-10, 'LiNbYZ': 46.0 * eps0, 'LiNb128': 56.0 * eps0, 'LiNbYZX': 46.0 * eps0 }[material] ft = Enum("double", "single").tag(desc="finger type of IDT", label="Finger type") def _observe_ft(self, change): if change["type"] == "update": self.ft_mult = None self.Ct_mult = None def _default_ft(self): return "double" @t_property(desc=r"single : 1, double : 2", label="finger type multiplier", expression=r"$c_{ft}$") def ft_mult(self, ft): return {"double": 2.0, "single": 1.0}[ft] @t_property(dictify={ "single": 1.0, "double": sqrt(2) }, label="Capacitance multiplier", expression=r"$c_c$", desc=r"single : $1$, double : $\sqrt{2}$") def Ct_mult(self, ft): return get_tag(self, "Ct_mult", "dictify")[ft] f = Float().tag(desc="what frequency is being stimulated/measured", unit="GHz", label="Operating frequency", expression=r"$f$") def _default_f(self): """default f is 1Hz off from f0""" return self.f0 #-1.0 f0 = Float(5.0000001e9).tag(unit="GHz", desc="Center frequency of IDT", reference="", expression=r"$f_0$", label="Center frequency") lbda = SProperty().tag(unit="um", desc="wavelength", reference="", label="wavelength", expression=r"$\lambda=v_f/f$") @lbda.getter def _get_lbda(self, f, vf): """wavelength relationship to speed and frequency""" return vf / f @lbda.setter def _get_f(self, lbda, vf): """frequency relationship to speed and wavelength""" return vf / lbda lbda0 = SProperty().tag(unit="um", desc="Center wavelength", reference="", label="center wavelength", expression=r"$\lambda_0=v_f/f_0$") @lbda0.getter def _get_lbda0(self, f0, vf): return vf / f0 @lbda0.setter def _get_f0(self, lbda0, vf): return vf / lbda0 k = SProperty().tag(label="Wavenumber", expression=r"$k=2\pi/\lambda$") @k.getter def _get_k(self, lbda): return 2 * pi / lbda @k.setter def _get_lbda_get_k(self, k): return 2 * pi / k k0 = SProperty().tag(label="Center wavenumber", expression=r"$k_0=2\pi/\lambda_0$") @k0.getter def _get_k0(self, lbda0): return 2 * pi / lbda0 @k0.setter def _get_lbda0_get_k0(self, k0): return 2 * pi / k0 eta = Float(0.5).tag(desc="metalization ratio", label="metallization ratio", expression=r"$\eta$") @log_func def _get_eta(self, a, g): """metalization ratio""" return a / (a + g) g = SProperty().tag(desc="gap between fingers", unit="um", label="finger gap", expression=r"$g$") @g.getter def _get_g(self, a, eta): """gap given metalization and finger width eta=a/(a+g) => a=(a+g)*eta => (1-eta)*a=g*eta => g=a*(1/eta-1)""" return a * (1.0 / eta - 1.0) @g.setter def _get_a_get_g(self, g, eta): """finger width given gap and metalization ratio eta=a/(a+g) => a=(a+g)*eta => (1-eta)*a=g*eta => a=g*eta/(1-eta)""" return g * eta / (1.0 - eta) a = SProperty().tag(desc="width of fingers", unit="um", label="finger width", expression=r"$a$") @a.getter def _get_a(self, eta, lbda0, ft_mult): """finger width from lbda0""" return eta * lbda0 / (2.0 * ft_mult) @a.setter def _get_lbda0_get_a(self, a, eta, ft_mult): return a / eta * 2.0 * ft_mult p = SProperty().tag(desc="periodicity", unit="um", label="finger periodicity", expression=r"$p=a+g$") @p.getter def _get_p(self, a, g): """periodicity from a and g""" return a + g @p.setter def _get_lbda0_get_p(self, p, ft_mult): return 2 * ft_mult * p @private_property def fixed_freq(self): return linspace(self.fixed_freq_min, self.fixed_freq_max, self.N_fixed).astype(float64) N_fixed = Int(10000) fixed_freq_max = Float() fixed_freq_min = Float(0.01) def _default_fixed_freq_max(self): return 200.0 * self.f0 def _default_fixed_freq_min(self): return 0.0001 #*self.f0 lgf1 = Typed(Legendre) #.tag(sub=True) lgf2 = Typed(Legendre) #.tag(sub=True) def _default_lgf1(self): return Legendre(x=-cos(pi * self.eta), Nmult=0) def _default_lgf2(self): return Legendre(x=cos(pi * self.eta), v=self._get_m(f=self.fixed_freq_max)) def _observe_eta(self, change): if change["type"] == "update": if self.fixed_update: self.fixed_reset() fixed_update = Bool(False).tag( desc= "if True, changing eta will trigger an update of fixed values (slow computation)" ) #@log_callable() def fixed_reset(self): self.lgf1.Pv(0.0, -cos(pi * self.eta), 0) self.lgf2.Pv(self.fixed_freq_max / (2 * self.ft_mult * self.f0), cos(pi * self.eta)) self.get_member("fixed_freq").reset(self) self.get_member("fixed_alpha").reset(self) self.get_member("surface_x").reset(self) self.get_member("surface_charge").reset(self) self.get_member("surface_voltage").reset(self) @private_property def fixed_alpha(self): return self._get_alpha(f=self.fixed_freq) @private_property def fixed_m(self): return self._get_m(f=self.fixed_freq) @private_property def fixed_s(self): return self._get_s(f=self.fixed_freq) m = SProperty().tag(desc="integer number of wavelengths") @m.getter def _get_m(self, f, f0, ft_mult): fs = self._get_fs(f=f, f0=f0, ft_mult=ft_mult) if isinstance(f, float): return int(fs) return fs.astype(int64) s = SProperty() @s.getter def _get_s(self, f, f0, ft_mult): fs = self._get_fs(f=f, f0=f0, ft_mult=ft_mult) m = self._get_m(f=f, f0=f0, ft_mult=ft_mult) return fs - m fs = SProperty() @fs.getter def _get_fs(self, f, f0, ft_mult): return f / (2.0 * ft_mult * f0) alpha = SProperty().tag(desc="single : 1.694, double : 1.247", label="mu multiplier", expression=r"$\alpha$") @alpha.getter def _get_alpha(self, f, f0, ft_mult, eta, epsinf): m = self._get_m(f=f, f0=f0, ft_mult=ft_mult) s = self._get_s(f=f, f0=f0, ft_mult=ft_mult) return 2 * sin(pi * s) / self.lgf1.Pv(-s) * self.lgf2.Pv(m) alpha0 = SProperty() @alpha0.getter def _get_alpha0(self, f0, ft_mult, eta, epsinf): return self._get_alpha(f=f0, f0=f0, ft_mult=ft_mult, eta=eta, epsinf=epsinf) @private_property def surface_x(self): fs = self._get_fs(f=self.fixed_freq) df = 1.0 / (fs[1] - fs[0]) / 2.0 #*self.lbda0/2 return linspace(-df / 2.0, df / 2.0, self.N_fixed) @private_property def surface_charge(self): return fft.fftshift(real(ifft(self.fixed_alpha * self.epsinf))) @private_property def surface_voltage(self): lbda = self._get_lbda(f=self.fixed_freq) kCs = self._get_k(lbda) #*self.epsinf return fft.fftshift(real(fft.ifft(self.fixed_alpha / kCs))).astype(float64) @private_property def view_window(self): return SurfaceChargeView(agent=self) @private_property def plot_func_dict(self): return OrderedDict([ ("legendre test", dict(code=self.lgf1.lgf_test_plot)), ("plot_alpha", dict(code=self.plot_alpha)), ("plot surface charge", dict(code=self.plot_surface_charge)), ("plot surface voltage", dict(code=self.plot_surface_voltage)), ]) def plot_alpha(self, pl=None, **kwargs): if pl is None: pl = "alpha_" + self.name f, alpha = self.fixed_freq, self.fixed_alpha pl = line(f / self.f0, alpha, plotter=pl, plot_name=self.name, color="blue", label=self.name, **kwargs) pl.xlabel = "frequency/center frequency" pl.ylabel = "element factor" pl.set_ylim(-1.0, 2.0) return pl def plot_surface_charge(self, pl=None, **kwargs): if pl is None: pl = "surface_charge_" + self.name x, charge = self.surface_x, self.surface_charge pl = line(x, charge, plotter=pl, plot_name=self.name, color="blue", label=self.name, **kwargs) pl.xlabel = "x/center wavelength" pl.ylabel = "surface charge" #pl.set_ylim(-1.0, 2.0) return pl def plot_surface_voltage(self, pl=None, **kwargs): if pl is None: pl = "surface_voltage_" + self.name x, voltage = self.surface_x, self.surface_voltage pl = line(x, voltage, plotter=pl, plot_name=self.name, color="blue", label=self.name, **kwargs) pl.xlabel = "x/center wavelength" pl.ylabel = "surface voltage" #pl.set_ylim(-1.0, 2.0) return pl
class IDT(Rho): """Theoretical description of IDT""" base_name = "IDT" Y0_type = Enum("formula", "center") df_type = Enum("formula", "center") mus_type = Enum("formula", "center") Ga_type = Enum("sinc", "giant atom", "full sum") Ba_type = Enum("hilbert", "formula") rs_type = Enum("formula", "constant") gate_type = Enum("constant", "capacitive", "transmission") #YL_type=Enum("constant", "inductor") magabs_type = Enum("S11", "S12", "S13", "S21", "S22", "S23", "S31", "S32", "S33") S_type = Enum("simple", "simpleP", "RAM") Cc = Float(1e-15).tag(desc="coupling capacitance", unit="fF") def _default_Ga_type(self): return "giant atom" def _default_fixed_freq_max(self): return 20.0 * self.f0 def _default_f(self): """default f is 0.01Hz off from f0""" return self.f0 - 0.01 Np = Float(9).tag(desc="\# of finger pairs", low=0.5, expression=r"$N_p$", label="\# of finger pairs") ef = Int(0).tag(desc="for edge effect compensation", label="\# of extra fingers", low=0) W = Float(25.0e-6).tag(desc="IDT width", unit="um", label="IDT width") Ct = SProperty().tag(unit="fF", label="IDT capacitance", expression=r"$C_t$", desc="Total capacitance of IDT", reference="Morgan page 16/145") @Ct.getter def _get_Ct(self, epsinf, Ct_mult, W, Np): """Morgan page 16, 145""" return Ct_mult * W * epsinf * Np @Ct.setter def _get_epsinf(self, Ct, Ct_mult, W, Np): """reversing capacitance to extract eps infinity""" return Ct / (Ct_mult * W * Np) Ga0_approx = SProperty().tag( desc="Conductance at center frequency", expression= r"$G_{a0} = c_{Ga0} \omega_0 \epsilon_\infty W N_p^2 \Delta v/v$", label="Center conductance") @Ga0_approx.getter def _get_Ga0_approx(self, f0, epsinf, ft, W, Dvv, Np): """Ga0 from morgan""" Ga0_mult = {"single": 2.872, "double": 3.111}[ft] return Ga0_mult * 2.0 * pi * f0 * epsinf * W * Dvv * (Np**2) X = SProperty().tag(label="relative frequency", expression=r"$X=N_p \pi (f-f_0)/f_0$") @X.getter def _get_X(self, Np, f, f0): """standard normalized frequency dependence""" return Np * pi * (f - f0) / f0 @log_func def sinc(self, f, f0, Np): X = Np * pi * (f - f0) / f0 return sin(X) / X @log_func def sinc_sq_ls(self, f, f0, Np): X = Np * pi * (f - f0) / f0 return -(sin(2.0 * X) - 2.0 * X) / (2.0 * X**2.0) @log_func def giant_atom(self, f, f0, Np): X = Np * pi * (f - f0) / f0 return 1.0 / Np * sin(X) / sin(X / Np) @log_func def giant_atom_ls(self, f, f0, Np): X = Np * pi * (f - f0) / f0 return (1.0 / Np)**2 * 2 * (Np * sin(2 * X / Np) - sin(2 * X)) / (2 * (1 - cos(2 * X / Np))) def hilbert_ls(self, cpl): return imag(hilbert(cpl)) @log_func def df_corr(self, f, f0): return sqrt(2.0) * cos(pi * f / (4 * f0)) mus = SProperty().tag(desc="Datta voltage coefficient", expression=r"$\mu=j \alpha \Delta v/v$", label="Voltage coeffecient (one finger)") @mus.getter def _get_mus(self, f, f0, ft_mult, eta, epsinf, Dvv): alpha = self._get_alpha(f=f, f0=f0, ft_mult=ft_mult, eta=eta, epsinf=epsinf) return 1.0j * alpha * Dvv mus0 = SProperty().tag( desc="Datta voltage coefficient", expression=r"$\mu_0=j \alpha_0 \Delta v/v$", label="Voltage coeffecient (center frequency, one finger)") @mus0.getter def _get_mus0(self, f0, ft_mult, eta, epsinf, Dvv): return self._get_mus(f=f0, f0=f0, ft_mult=ft_mult, eta=eta, epsinf=epsinf, Dvv=Dvv) Y0 = SProperty().tag( desc="Datta's characteristic SAW impedance", expression=r"$Y_0=\pi f W \epsilon_\infty / (\Delta v/v)$", label="Characteristic impedance") @Y0.getter def _get_Y0(self, f, Dvv, epsinf, W): return pi * f * W * epsinf / Dvv Y00 = SProperty().tag( desc="Datta's characteristic SAW impedance", expression=r"$Y_0=\pi f_0 W \epsilon_\infty / (\Delta v/v)$", label="Characteristic impedance (center frequency)") @Y00.getter def _get_Y00(self, f0, Dvv, epsinf, W): return self._get_Y0(f=f0, Dvv=Dvv, epsinf=epsinf, W=W) #pi*f0*W*epsinf/Dvv Ga = SProperty().tag(desc="Ga adjusted for frequency f") @Ga.getter def _get_Ga(self, f, f0, ft_mult, eta, epsinf, Dvv, Np, W): if self.S_type == "RAM": return self.get_fix("Ga", f) if self.Y0_type == "center": Y0 = self._get_Y00(f0=f0, Dvv=Dvv, epsinf=epsinf, W=W) else: Y0 = self._get_Y0(f=f, Dvv=Dvv, epsinf=epsinf, W=W) if self.mus_type == "center": mus = self._get_mus0(f0=f0, ft_mult=ft_mult, eta=eta, epsinf=epsinf, Dvv=Dvv) else: mus = self._get_mus(f=f, f0=f0, ft_mult=ft_mult, eta=eta, epsinf=epsinf, Dvv=Dvv) if self.df_type == "center" or self.ft == "single": df_corr = 1.0 else: df_corr = self.df_corr(f=f, f0=f0) if self.ft == "double": df_corr *= sqrt(2.0) if self.Ga_type == "sinc": cpl_form = Np * self.sinc(f=f, f0=f0, Np=Np) elif self.Ga_type == "giant atom": cpl_form = Np * self.giant_atom(f=f, f0=f0, Np=Np) if self.Ga_type == "full sum": mu = mus * self._get_Asum(f=f, f0=f0, Np=Np) else: mu = mus * df_corr * cpl_form return 2.0 * Y0 * absolute( mu )**2 #+(2*self.Ct)*(2*pi)*(self.dephasing+self.dephasing_slope*f)/1.0 Ga0 = SProperty().tag(desc="Ga (center frequency)") @Ga0.getter def _get_Ga0(self, f0, Np, W, Dvv, epsinf, eta, ft_mult): return self._get_Ga(f=f0 + 0.001, f0=f0, Np=Np, W=W, Dvv=Dvv, epsinf=epsinf, eta=eta, ft_mult=ft_mult) @private_property def fixed_Ga(self): return self.fixed_P[1] #return self._get_Ga(f=self.fixed_freq) Ba = SProperty() @Ba.getter def _get_Ba(self, f, f0, ft_mult, eta, epsinf, W, Dvv, Np): if self.Ga_type == "full sum" or self.S_type == "RAM": return self.get_fix("Ba", f) if self.Ba_type == "formula": Ga0 = self._get_Ga0(f0=f0, Np=Np, W=W, Dvv=Dvv, epsinf=epsinf, eta=eta, ft_mult=ft_mult) if self.Ga_type == "sinc": return -Ga0 * self.sinc_sq_ls( f=f, f0=f0, Np=Np) #(sin(2.0*X)-2.0*X)/(2.0*X**2.0) if self.Ga_type == "giant atom": return -Ga0 * self.giant_atom_ls( f=f, f0=f0, Np=Np ) #(1.0/Np)**2*2*(Np*sin(2*gX/Np)-sin(2*gX))/(2*(1-cos(2*gX/Np))) Ga = self._get_Ga(f=self.fixed_freq, f0=f0, Np=Np, W=W, Dvv=Dvv, epsinf=epsinf, eta=eta, ft_mult=ft_mult) yp = -imag(hilbert(Ga)) return interp(f, self.fixed_freq, yp) @private_property def fixed_Ba(self): return self.fixed_P[2] #return self._get_Ba(f=self.fixed_freq) @private_property def fixed_P(self): if self.S_type == "RAM": return self.fixed_RAM_P #_get_RAM_P() return self._get_simple_P(f=self.fixed_freq) simple_P = SProperty().tag(sub=True) @simple_P.getter def _get_simple_P(self, f, f0, ft_mult, eta, epsinf, W, Dvv, Np, Ct, dL, vf, L_IDT): Ga = self._get_Ga(f=f, f0=f0, Np=Np, W=W, Dvv=Dvv, epsinf=epsinf, eta=eta, ft_mult=ft_mult) Ba = self._get_Ba(f=f, f0=f0, Np=Np, W=W, Dvv=Dvv, epsinf=epsinf, eta=eta, ft_mult=ft_mult) w = 2 * pi * f k = 2 * pi * f / vf jkL = 1.0j * k * L_IDT P11 = P22 = 0.0 P12 = P21 = exp(-jkL) P13 = P23 = 1.0j * sqrt(Ga / 2.0) * exp(-jkL / 2.0) P31 = P32 = -2.0 * P13 P33 = Ga + 1.0j * Ba + 1.0j * w * Ct - 1.0j / w * dL return (P11, P12, P13, P21, P22, P23, P31, P32, P33), Ga, Ba coupling = SProperty().tag(desc="""Coupling adjusted by sinc sq""", unit="GHz", expression=r"$\Gamma(f)/2 \pi$", label="full qubit coupling") @coupling.getter def _get_coupling(self, f, f0, ft_mult, eta, epsinf, Dvv, Ct_mult, Np, W): if self.gate_type == "constant": Ga = self._get_Ga(f=f, f0=f0, ft_mult=ft_mult, eta=eta, epsinf=epsinf, Dvv=Dvv, Np=Np, W=W) Ct = self._get_Ct(epsinf=epsinf, Ct_mult=Ct_mult, W=W, Np=Np) return Ga / (2 * Ct) / ( 2 * pi) #+(self.dephasing+self.dephasing_slope*f)/1.0 return self.get_fix("coupling", f=f) Lamb_shift = SProperty().tag(desc="""Lamb shift""", unit="GHz", expression=r"$B_a/\omega C$", label="Lamb shift") @Lamb_shift.getter def _get_Lamb_shift(self, f, f0, ft_mult, eta, epsinf, W, Dvv, Np, Ct_mult): """returns Lamb shift""" if self.gate_type == "constant": Ba = self._get_Ba(f=f, f0=f0, Np=Np, W=W, Dvv=Dvv, epsinf=epsinf, eta=eta, ft_mult=ft_mult) Ct = self._get_Ct(epsinf=epsinf, Ct_mult=Ct_mult, W=W, Np=Np) return -Ba / (2 * Ct) / (2 * pi) return self.get_fix("Lamb_shift", f=f) Ga0_mult = SProperty().tag( desc="single: $2.87=1.694^2$, double: $3.11=(1.247 \sqrt{2})^2$", expression=r"$c_{Ga}(f)$", label=r"$G_a$ multiplier") @Ga0_mult.getter def _get_Ga0_mult(self, f0, Np, W, ft_mult, eta, epsinf, Dvv): Ga0 = self._get_Ga0(f0=f0, Np=Np, W=W, epsinf=epsinf, eta=eta, ft_mult=ft_mult) return Ga0 / (2.0 * pi * f0 * W * epsinf * Dvv * Np**2) max_coupling = SProperty().tag(desc="""Coupling at IDT center frequency""", unit="GHz", label="Coupling at center frequency", tex_str=r"$\gamma_{f0}$") @max_coupling.getter def _get_max_coupling(self, f0, ft_mult, eta, epsinf, Ct_mult, Dvv, Np, W): return self._get_coupling(f=f0 + 0.001, f0=f0, ft_mult=ft_mult, eta=eta, epsinf=epsinf, Ct_mult=Ct_mult, Dvv=Dvv, Np=Np, W=W) max_coupling_approx = SProperty().tag( unit="GHz", label="Qubit coupling ($f$) (no sinc)", expression=r"$\Gamma/2\pi \approx c_g(f) f_0 K^2 N_p$", desc="frequency dependent") @max_coupling_approx.getter def _get_max_coupling_approx(self, f0, K2, Np, ft): cpl_mult = {"single": 0.71775, "double": 0.54995}[ft] return cpl_mult * f0 * K2 * Np def fixed_reset(self): """resets fixed properties in proper order""" super(IDT, self).fixed_reset() self.get_member("fixed_P").reset(self) self.get_member("fixed_RAM_P").reset(self) self.get_member("fixed_X").reset(self) self.get_member("fixed_Asum").reset(self) self.get_member("fixed_Ga").reset(self) self.get_member("fixed_Ba").reset(self) self.get_member("fixed_coupling").reset(self) self.get_member("fixed_Lamb_shift").reset(self) self.get_member("fixed_S").reset(self) @private_property def fixed_X(self): return self._get_X(f=self.fixed_freq) @private_property def fixed_w(self): return 2.0 * pi * self.fixed_freq def get_fix(self, name, f): return interp(f, self.fixed_freq, getattr(self, "fixed_" + name)) @private_property def fixed_coupling(self): if self.gate_type == "constant": return self.fixed_Ga / (2 * self.Ct) / (2 * pi) return absolute([fp[0] for fp in self.fit_params]) * 1e9 @private_property def fixed_Lamb_shift(self): if self.gate_type == "constant": return -self.fixed_Ba / (2.0 * self.Ct) / (2.0 * pi) return self.fixed_freq - array([fp[1] for fp in self.fit_params]) * 1e9 dloss1 = Float(0.0) dloss2 = Float(0.0) #propagation_loss=SProperty() #@propagation_loss.getter #def _get_propagation_loss(self, f, f0, dloss1, dloss2): # return exp(-f/f0*dloss1-dloss2*(f/f0)**2) @t_property(sub=True) def polarity(self, Np, ft): if ft == "single": return arange(int(Np)) + 0.5 return array(sqze(zip(arange(Np) + 0.5, arange(Np) + 0.75))) @t_property(sub=True) def g_arr(self, polarity): return ones(len(polarity)) @log_func def _get_Asum(self, f, f0, Np, dloss1, dloss2, g_arr, polarity): return 1.0 / Np * array([ sum([ g_arr[i] * exp(2.0j * pi * frq / f0 * n - frq / f0 * dloss1 * n - n * dloss2 * (frq / f0)**2) for i, n in enumerate(polarity) ]) for frq in f ]) @private_property def fixed_Asum(self): return self._get_Asum(f=self.fixed_freq) dL = Float(0.0) YL = Complex(1.0 / 50.0) YS = Complex(1.0 / 50.0) Cground = Float().tag(unit="fF") @private_property def fixed_S(self): if self.S_type == "simple": return self._get_simple_S(f=self.fixed_freq) P = self.fixed_P[0] return self.PtoS(*P, YL=self.YL) simple_S = SProperty().tag(sub=True) @simple_S.getter def _get_simple_S(self, f, f0, ft_mult, eta, epsinf, W, Dvv, Np, Ct, YL, dL, vf, L_IDT, Cc, YS, Cground): Ga = self._get_Ga(f=f, f0=f0, Np=Np, W=W, Dvv=Dvv, epsinf=epsinf, eta=eta, ft_mult=ft_mult) Ba = self._get_Ba(f=f, f0=f0, Np=Np, W=W, Dvv=Dvv, epsinf=epsinf, eta=eta, ft_mult=ft_mult) w = 2 * pi * f GL = real(YL) k = 2 * pi * f / vf jkL = 1.0j * k * L_IDT P33plusYL = Ga + 1.0j * Ba + 1.0j * w * Ct - 1.0j / w * dL + YL S11 = S22 = -Ga / P33plusYL * exp(-jkL) S12 = S21 = exp(-jkL) + S11 if self.gate_type == "capacitive": Zatom = -1.0j / (w * Cc) + 1 / P33plusYL Zeff = 1.0 / (1.0j * w * Cground + 1.0 / Zatom) ZS = 1 / YS S33 = (Zatom - ZS) / (Zatom + ZS) S13 = S23 = S32 = S31 = (1.0 + S33) / 2.0 elif self.gate_type == "transmission": Zatom = -1.0j / (w * Cc) + 1 / P33plusYL ZS = 1 / YS Zeff = ZS * Zatom / (Zatom + ZS) S33 = (Zeff - ZS) / (Zeff + ZS) S13 = S23 = S32 = S31 = (1.0 + S33) / 2.0 else: S13 = S23 = S32 = S31 = 1.0j * sqrt( 2.0 * Ga * GL) / P33plusYL * exp(-jkL / 2.0) S33 = (YL - Ga + 1.0j * Ba + 1.0j * w * Ct - 1.0j / w * dL) / P33plusYL return (S11, S12, S13, S21, S22, S23, S31, S32, S33) def PtoS(self, P11, P12, P13, P21, P22, P23, P31, P32, P33, YL=1 / 50.0): YLplusP33 = YL + P33 sqrtYL = sqrt(YL) S11 = P11 - P13 * P31 / YLplusP33 S12 = P12 - P13 * P32 / YLplusP33 S13 = 2.0 * sqrtYL * P13 / YLplusP33 S21 = P21 - P23 * P31 / YLplusP33 S22 = P22 - P23 * P32 / YLplusP33 S23 = -P31 * sqrtYL * P23 / YLplusP33 S31 = -P31 * sqrtYL / YLplusP33 S32 = -P32 * sqrtYL / YL + P33 S33 = (YL - P33) / YLplusP33 return (S11, S12, S13, S21, S22, S23, S31, S32, S33) rs = Complex() def _get_rs(self, f): h = 30e-9 lbda = self._get_lbda(f=f) rs = (-1.7 / 100 - 0.24 * h / lbda) * 1.0j return array([r if absolute(r) < 0.9 else 0.9j for r in rs]) #if absolute(rs)>=1.0: # return 0.9999j #return rs ts = SProperty() @ts.getter def _get_ts(self, rs): return sqrt(1.0 - absolute(rs)**2) Gs = SProperty().tag( desc="Inglebrinsten's approximation of $\Gamma_S$ (Morgan)", expression=r"$\Gamma_S \approx (\Delta v/v)/\epsilon_\infty$") @Gs.getter def _get_Gs(self, Dvv, epsinf): return Dvv / epsinf L_IDT = SProperty().tag(desc="length of IDT", unit="um", expression=r"$L_{IDT}=N_{IDT}p$", label="IDT length") @L_IDT.getter def _get_L_IDT(self, N_IDT, p): return N_IDT * p N_IDT = SProperty().tag(desc="total number of IDT fingers", label="Total IDT fingers", expression=r"$N_{IDT}=2 c_{ft} N_p$") @N_IDT.getter def _get_N_IDT(self, ft_mult, Np): return 2 * ft_mult * Np #+ft_mult*(Np+1) def _get_RAM_P_one_f(self, f, p, N_IDT, L_IDT, f0, alpha, rs, Y0, dloss1, dloss2, W, Np, ft, vf, Dvv, epsinf): #k=2*pi*f/vf#-1.0j*(f/f0*dloss1+dloss2*(f/f0)**2) 0.19 f/1e9 + 0.88 (f/1e9)**2 dB/us*1e6/3488 *log(10.0)/20.0 k = 2 * pi * f / vf - 1.0j * ( dloss1 * f / 1e9 + dloss2 * (f / 1e9)**2) * 1e6 / 3488 * log(10.0) / 20.0 ts = sqrt(1.0 - absolute(rs)**2) A = 1.0 / ts * matrix([[exp(-1.0j * k * p), rs], [-rs, exp(1.0j * k * p)]]) #.astype(complex128) AN = A**int(N_IDT) AN11, AN12, AN21, AN22 = AN[0, 0], AN[0, 1], AN[1, 0], AN[1, 1] P11 = -AN21 / AN22 P21 = AN11 - AN12 * AN21 / AN22 P12 = 1.0 / AN22 P22 = AN12 / AN22 D = -1.0j * alpha * Dvv * sqrt(Y0) B = matrix([(1.0 - rs / ts + 1.0 / ts) * exp(-1.0j * k * p / 2.0), (1.0 + rs / ts + 1.0 / ts) * exp(1.0j * k * p / 2.0)]) I = eye(2) if ft == "single": P32_base = (inv(I - A**2) * (I - A**(2 * int(Np)))) * matrix([ [0], [1.0 / AN[1, 1] * exp(1.0j * k * (L_IDT - p) / 2.0)] ]) #geometric series else: P32_base = ( (I + A) * inv(I - A**4) * (I - A**(4 * int(Np)))) * matrix([[ 0 ], [1.0 / AN[1, 1] * exp(1.0j * k * (L_IDT - 2.0 * p) / 2.0)]]) P31 = P32 = D * (B * P32_base) P13 = P23 = -P31 / 2.0 return (P11, P12, P13, P21, P22, P23, P31, P32) @private_property def fixed_RAM_P(self): if self.Y0_type == "center": Y0 = self.Y00 else: Y0 = self._get_Y0(f=self.fixed_freq) if self.mus_type == "center": alpha = self.alpha0 else: alpha = self._get_alpha(f=self.fixed_freq) if self.rs_type == "constant": rs = self.rs else: rs = self._get_rs(f=self.fixed_freq) return self._get_RAM_P(frq=self.fixed_freq, alpha=alpha, Y0=Y0, rs=rs) # @private_property # def fixed_Y0(self): # return self._get_Y0(f=self.fixed_freq) # # @private_property # def fixed_rs(self): # return self._get_rs(f=self.fixed_freq) @log_func def _get_RAM_P(self, frq, f0, alpha, rs, Y0, dloss1, dloss2, W, Np, ft, vf, Dvv, epsinf): if Y0 is None: Y0 = pi * f0 * W * epsinf / Dvv Ct_mult = {"single": 1.0, "double": sqrt(2)}[ft] Ct = Ct_mult * W * epsinf * Np ft_mult = {"double": 2.0, "single": 1.0}[ft] lbda0 = vf / f0 p = lbda0 / (2 * ft_mult) N_IDT = 2 * ft_mult * Np L_IDT = Np * lbda0 #print alpha, rs, Y0, f0, dloss1, dloss2, W, Np, ft, vf, Dvv, epsinf, L_IDT, N_IDT if isinstance(alpha, float): alpha = ones(len(frq)) * alpha if isinstance(rs, complex): rs = ones(len(frq)) * rs if isinstance(Y0, float): Y0 = ones(len(frq)) * Y0 print "start P" P = [ self._get_RAM_P_one_f(f=f, Dvv=Dvv, epsinf=epsinf, W=W, vf=vf, rs=rs[i], Y0=Y0[i], p=p, N_IDT=N_IDT, alpha=alpha[i], ft=ft, Np=Np, f0=f0, dloss1=dloss1, dloss2=dloss2, L_IDT=L_IDT) for i, f in enumerate(frq) ] print "P_done" (P11, P12, P13, P21, P22, P23, P31, P32) = [squeeze(P_ele) for P_ele in zip(*P)] print "P_done 2" Ga = 2.0 * absolute(P13)**2 Ba = -imag(hilbert(Ga)) P33 = Ga + 1.0j * Ba + 2.0j * pi * frq * Ct return (P11, P12, P13, P21, P22, P23, P31, P32, P33), Ga, Ba, Ct @private_property def view_window(self): return IDTView(agent=self)
class IDT_S_Matrix(IDT): S11 = SProperty() @S11.getter def _get_S11(self, f): return self.get_fix("S11", f) @private_property def fixed_S11(self): return self.fixed_S[0] S12 = SProperty() @S12.getter def _get_S12(self, f): return self.get_fix("S12", f) @private_property def fixed_S12(self): return self.fixed_S[1] S13 = SProperty() @S13.getter def _get_S13(self, f): return self.get_fix("S13", f) @private_property def fixed_S13(self): return self.fixed_S[2] S21 = SProperty() @S21.getter def _get_S21(self, f): return self.get_fix("S21", f) @private_property def fixed_S21(self): return self.fixed_S[3] S22 = SProperty() @S22.getter def _get_S22(self, f): return self.get_fix("S22", f) @private_property def fixed_S22(self): return self.fixed_S[4] S23 = SProperty() @S23.getter def _get_S23(self, f): return self.get_fix("S23", f) @private_property def fixed_S23(self): return self.fixed_S[5] S31 = SProperty() @S31.getter def _get_S31(self, f): return self.get_fix("S31", f) @private_property def fixed_S31(self): return self.fixed_S[6] S32 = SProperty() @S32.getter def _get_S32(self, f): return self.get_fix("S32", f) @private_property def fixed_S32(self): return self.fixed_S[7] S33 = SProperty() @S33.getter def _get_S33(self, f): return self.get_fix("S33", f) @private_property def fixed_S33(self): return self.fixed_S[8]
class QDT(IDT, Sat_Qubit): base_name = "QDT" @private_property def view_window(self): return QDTView(agent=self) fq0 = SProperty().tag(desc="center frequency of oscillator") @fq0.getter def _get_fq0(self, f, f0, ft_mult, eta, epsinf, Ct_mult, Dvv, Np, W): ls = self._get_Lamb_shift(f=f, f0=f0, ft_mult=ft_mult, eta=eta, epsinf=epsinf, W=W, Dvv=Dvv, Np=Np, Ct_mult=Ct_mult) return sqrt(f * (f - 2.0 * ls)) @fq0.setter def _get_Ej_get_fq0(self, fq0, Ec): return (h * fq0) ^ 2 / (8.0 * Ec) fFWHM = SProperty().tag( desc="center frequency of oscillator plus half width") @fFWHM.getter def _get_fFWHM(self, f, f0, ft_mult, eta, epsinf, Ct_mult, Dvv, Np, W, dephasing, dephasing_slope): ls = self._get_Lamb_shift(f=f, f0=f0, ft_mult=ft_mult, eta=eta, epsinf=epsinf, W=W, Dvv=Dvv, Np=Np, Ct_mult=Ct_mult) gamma = self._get_coupling(f=f, f0=f0, ft_mult=ft_mult, eta=eta, epsinf=epsinf, W=W, Dvv=Dvv, Np=Np, Ct_mult=Ct_mult) fplus = sqrt(f * (f - 2.0 * ls + 2.0 * gamma)) fminus = sqrt(f * (f - 2.0 * ls - 2.0 * gamma)) return fplus, fminus, fplus - fminus + dephasing + dephasing_slope * f fluxfq0 = SProperty().tag(desc="center frequency of oscillator as voltage") @fluxfq0.getter def _get_fluxfq0(self, f, f0, ft_mult, eta, epsinf, Ct_mult, Dvv, Np, W, Ct, Ejmax): fq0 = self._get_fq0(f=f, f0=f0, ft_mult=ft_mult, eta=eta, epsinf=epsinf, Ct_mult=Ct_mult, Dvv=Dvv, Np=Np, W=W) return self._get_flux_from_fq(fq=fq0, Ct=Ct, Ejmax=Ejmax) fluxfFWHM = SProperty().tag(desc="FWHM of oscillator") @fluxfFWHM.getter def _get_fluxfFWHM(self, f, f0, ft_mult, eta, epsinf, Ct_mult, Dvv, Np, W, Ct, Ejmax): fplus, fminus, fwhm = self._get_fFWHM(f=f, f0=f0, ft_mult=ft_mult, eta=eta, epsinf=epsinf, Ct_mult=Ct_mult, Dvv=Dvv, Np=Np, W=W) Vminus = self._get_flux_from_fq(fq=fplus, Ct=Ct, Ejmax=Ejmax) Vplus = self._get_flux_from_fq(fq=fminus, Ct=Ct, Ejmax=Ejmax) return Vplus, Vminus, Vplus - Vminus Vfq0 = SProperty().tag(desc="center frequency of oscillator as voltage") @Vfq0.getter def _get_Vfq0(self, f, f0, ft_mult, eta, epsinf, Ct_mult, Dvv, Np, W, Ct, Ejmax, offset, flux_factor): fq0 = self._get_fq0(f=f, f0=f0, ft_mult=ft_mult, eta=eta, epsinf=epsinf, Ct_mult=Ct_mult, Dvv=Dvv, Np=Np, W=W) return self._get_voltage_from_flux_par(fq=fq0, Ct=Ct, Ejmax=Ejmax, offset=offset, flux_factor=flux_factor) VfFWHM = SProperty().tag(desc="FWHM of oscillator") @VfFWHM.getter def _get_VfFWHM(self, f, f0, ft_mult, eta, epsinf, Ct_mult, Dvv, Np, W, Ct, Ejmax, offset, flux_factor): fplus, fminus, fwhm = self._get_fFWHM(f=f, f0=f0, ft_mult=ft_mult, eta=eta, epsinf=epsinf, Ct_mult=Ct_mult, Dvv=Dvv, Np=Np, W=W) Vminus = self._get_voltage_from_flux_par(fq=fplus, Ct=Ct, Ejmax=Ejmax, offset=offset, flux_factor=flux_factor) Vplus = self._get_voltage_from_flux_par(fq=fminus, Ct=Ct, Ejmax=Ejmax, offset=offset, flux_factor=flux_factor) return Vplus, Vminus, Vplus - Vminus Vfq0_many = SProperty().tag(sub=True) @Vfq0_many.getter def _get_Vfq0_many(self, f, f0, ft_mult, eta, epsinf, Ct_mult, Dvv, Np, W, Ct, Ejmax, offset, flux_factor): fq0 = self._get_fq0(f=f, f0=f0, ft_mult=ft_mult, eta=eta, epsinf=epsinf, Ct_mult=Ct_mult, Dvv=Dvv, Np=Np, W=W) return self._get_voltage_from_flux_par_many(fq=fq0, Ct=Ct, Ejmax=Ejmax, offset=offset, flux_factor=flux_factor) # GL=Float(1.0) # simple_S_qdt=SProperty().tag(sub=True) # @simple_S_qdt.getter # def _get_simple_S_qdt(self, f, f0, ft_mult, eta, epsinf, Ct_mult, K2, Np, Ct, L, dL, vf, L_IDT, GL): # Ga=self._get_Ga(f=f, f0=f0, ft_mult=ft_mult, eta=eta, epsinf=epsinf, Ct_mult=Ct_mult, K2=K2, Np=Np) # Ba=self._get_Ba(f=f, f0=f0, ft_mult=ft_mult, eta=eta, epsinf=epsinf, Ct_mult=Ct_mult, K2=K2, Np=Np) # w=2*pi*f # YL=-1.0j/(w*L) # # k=2*pi*f/vf # jkL=1.0j*k*L_IDT # P33plusYL=Ga+1.0j*Ba+1.0j*w*Ct-1.0j/w*dL+YL # S11=S22=-Ga/P33plusYL*exp(-jkL) # S12=S21=exp(-jkL)+S11 # S13=S23=S32=S31=1.0j*sqrt(2.0*Ga*GL)/P33plusYL*exp(-jkL/2.0) # S33=(YL-Ga+1.0j*Ba+1.0j*w*Ct-1.0j/w*dL)/P33plusYL # return (S11, S12, S13, # S21, S22, S23, # S31, S32, S33) @private_property def fixed_fq(self): return linspace(self.fixed_fq_min, self.fixed_fq_max, self.N_fixed_fq).astype(float64) N_fixed_fq = Int(500) fixed_fq_max = Float() fixed_fq_min = Float(0.01) def _default_fixed_freq_max(self): return 2.0 * self.f0 def _default_fixed_fq_max(self): return 2.0 * self.f0 def _default_fixed_fq_min(self): return 0.0001 #*self.f0 def _default_N_fixed(self): return 400 calc_p_guess = Bool(False) fitter = Typed(LorentzianFitter, ()) #fit.gamma=0.05 flux_indices = List() def _default_flux_indices(self): return [range(len(self.fixed_fq))] @private_property def flat_flux_indices(self): return [n for ind in self.flux_indices for n in ind] fit_indices = List() #.tag(private=True) def _default_fit_indices(self): return [range(len(self.fixed_freq))] @private_property def flat_indices(self): return [n for ind in self.fit_indices for n in ind] @private_property def MagAbs(self): #(S11, S12, S13, # S21, S22, S23, # S31, S32, S33)=self.fixed_S magind = { "S11": 0, "S12": 1, "S13": 2, "S21": 3, "S22": 4, "S23": 5, "S31": 6, "S32": 7, "S33": 8 }[self.magabs_type] magcom = self.fixed_S[:, magind, :] #magcom={"S11" : S11, "S12" : S12, "S13" : S13, # "S21" : S21, "S22" : S22, "S23" : S23, # "S31" : S31, "S32" : S32, "S33" : S33}[self.magabs_type] return absolute(magcom) #if self.bgsub_type=="dB": # return 10.0**(self.MagdB/10.0) #magabs=absolute(self.Magcom) #if self.bgsub_type=="Abs": # return self.bgsub(magabs) #return magabs @private_property def fit_params(self): MagAbsSq = (self.MagAbs**2).transpose() if self.fitter.fit_params is None: self.fitter.full_fit(x=self.fixed_fq[self.flat_flux_indices] / 1e9, y=MagAbsSq, indices=self.flat_indices, gamma=self.fitter.gamma) if self.calc_p_guess: self.fitter.make_p_guess( self.fixed_fq[self.flat_flux_indices] / 1e9, y=MagAbsSq, indices=self.flat_indices, gamma=self.fitter.gamma) return self.fitter.fit_params @private_property def MagAbsFit(self): return sqrt( self.fitter.reconstruct_fit( self.fixed_fq[self.flat_flux_indices] / 1e9, self.fit_params)).transpose() # YL=SProperty() # @YL.getter # def _get_YL(self, w, L): # if self.YL_type=="constant": # return self.YL # elif self.YL_type=="inductor": # return -1.0j/(w*L) @private_property def fixed_S(self): w = 2 * pi * self.fixed_freq L_arr = self._get_L(fq=self.fixed_fq) if self.S_type == "simple": return array([ self._get_simple_S(f=self.fixed_freq, YL=-1.0j / (w * L)) for L in L_arr ]) P = self.fixed_P[0] #return self.PtoS(*P, YL=self.YL) return array([self.PtoS(*P, YL=-1.0j / (w * L)) for L in L_arr]) lamb_shifted_transmon_energy = SProperty() @lamb_shifted_transmon_energy.getter def _get_lamb_shifted_transmon_energy(self, Ej, Ec, m, f0, ft_mult, eta, epsinf, W, Dvv, Np, Ct_mult): Em = -Ej + sqrt(8.0 * Ej * Ec) * (m + 0.5) - (Ec / 12.0) * ( 6.0 * m**2 + 6.0 * m + 3.0) if m == 0: return Em Emm1 = -Ej + sqrt( 8.0 * Ej * Ec) * (m - 1 + 0.5) - (Ec / 12.0) * (6.0 * (m - 1)**2 + 6.0 * (m - 1) + 3.0) fq = (Em - Emm1) / h fls = self._get_Lamb_shift(f=fq, f0=f0, ft_mult=ft_mult, eta=eta, epsinf=epsinf, W=W, Dvv=Dvv, Np=Np, Ct_mult=Ct_mult) return Em + h * fls lamb_shifted_transmon_energy_levels = SProperty() @lamb_shifted_transmon_energy_levels.getter def _get_lamb_shifted_transmon_energy_levels(self, Ej, f0, ft_mult, eta, epsinf, W, Dvv, Np, Ct_mult, Ec, n_energy): return [ self._get_lamb_shifted_transmon_energy(Ej=Ej, Ec=Ec, m=m, f0=f0, ft_mult=ft_mult, eta=eta, epsinf=epsinf, W=W, Dvv=Dvv, Np=Np, Ct_mult=Ct_mult) for m in range(n_energy) ]
class Qubit(Agent): """Theoretical description of qubit""" base_name="qubit" Cc=Float(1e-15).tag(desc="coupling capacitance", unit="fF") qubit_type=Enum("transmon", "scb") dephasing=Float(0.0).tag(unit="GHz") dephasing_slope=Float(0.0) @private_property def view_window(self): return QubitView(agent=self) superconductor=Enum("Al").tag(label="material") def _observe_superconductor(self, change): if self.superconductor=="Al": self.Delta=Delta_Al Tc=Float(Tc_Al).tag(desc="Critical temperature of superconductor", unit="K", label="Critical temperature") Delta=SProperty().tag(label="Gap", tex_str=r"$\Delta(0)$", unit="ueV", desc="BCS superconducting gap, 200 ueV for Al", reference="BCS", expression=r"$\Delta(0)=1.764 k_B T_c$") @Delta.getter def _get_Delta(self, Tc): """BCS theory superconducting gap""" return 1.764*kB*Tc @Delta.setter def _get_Tc(self, Delta): return Delta/(1.764*kB) loop_width=Float(1.0e-6).tag(desc="loop width of SQUID", unit="um", label="loop width") loop_height=Float(1.0e-6).tag(desc="loop height of SQUID", unit="um", label="loop height") loop_area=SProperty().tag(desc="Area of SQUID loop", unit="um^2", expression="$width \times height$", comment="Loop width times loop height", label="loop area") @loop_area.getter def _get_loop_area(self, loop_width, loop_height): return loop_width*loop_height Ct=Float(1.3e-13).tag(desc="shunt capacitance", unit="fF", tex_str=r"$C_q$") Rn=Float(5.0e3).tag(desc="Normal resistance of SQUID", unit="kOhm", label="DC Junction resistance", expression=r"$R_N$") Ic=SProperty().tag(desc="critical current of SQUID, Ambegaokar Baratoff formula", unit="nA", label="Critical current", expression=r"$I_C=\pi \Delta/(2e R_N)$") @Ic.getter def _get_Ic(self, Rn, Delta): """Ic*Rn=pi*Delta/(2.0*e) #Ambegaokar Baratoff formula""" return pi*Delta/(2.0*e)/Rn @Ic.setter def _get_Rn(self, Ic, Delta): """Ic*Rn=pi*Delta/(2.0*e) #Ambegaokar Baratoff formula""" return pi*Delta/(2.0*e)/Ic Ejmax=SProperty().tag(desc="""Max Josephson Energy""", unit="hGHz", expression=r'$E_{Jmax}=\hbar I_C/2e$', label="Max Josephson energy")#, unit_factor=1.0e9*h) @Ejmax.getter def _get_Ejmax(self, Ic): """Josephson energy""" return hbar*Ic/(2.0*e) @Ejmax.setter def _get_Ic_get_Ejmax(self, Ejmax): """inverse Josephson energy""" return Ejmax*(2.0*e)/hbar Ec=SProperty().tag(desc="Charging Energy", unit="hGHz", label="Charging energy", expression=r"$E_C=e^2/2 C_t$")#, unit_factor=1.0e9*h) @Ec.getter def _get_Ec(self, Ct, Cc): """Charging energy""" return e**2/(2.0*(Ct+Cc)) @Ec.setter def _get_Ct(self, Ec, Cc): """inverse charging energy""" return e**2/(2.0*Ec)-Cc @Ec.setter def _get_Ejmax_get_Ec(self, Ec, EjmaxdivEc): return EjmaxdivEc*Ec EjmaxdivEc=SProperty().tag(desc="Maximum Ej over Ec", label=r"Maximum $E_J$ over $E_C$", expression=r"$E_{Jmax}/E_C$") @EjmaxdivEc.getter def _get_EjmaxdivEc(self, Ejmax, Ec): return Ejmax/Ec Ej=SProperty().tag(unit="hGHz", label="Josephson energy", expression=r"$E_J=E_{Jmax}|\cos(\Phi/\Phi_0)|$") @Ej.getter def _get_Ej(self, Ejmax, flux_over_flux0): return Ejmax*absolute(cos(flux_over_flux0)) #*pi @Ej.setter def _get_flux_over_flux0_get_Ej(self, Ej, Ejmax): return arccos(Ej/Ejmax)#/pi EjdivEc=SProperty().tag(desc="Ej over Ec", label="Ej over Ec", expression=r"$E_J/E_C$") @EjdivEc.getter def _get_EjdivEc(self, Ej, Ec): return Ej/Ec fq_approx_max=SProperty().tag(unit="GHz", label="fq approx max", desc="qubit frequency using approximate transmon formula", expression=r"$f_{qmax} \approx (\sqrt{8E_{Jmax} E_C}-E_C)/h$") @fq_approx_max.getter def _get_fq_approx_max(self, Ejmax, Ec): return self._get_fq_approx(Ej=Ejmax, Ec=Ec) fq_approx=SProperty().tag(unit="GHz", expression=r"$f_q \approx (\sqrt{8E_J E_C}-E_C)/h$") @fq_approx.getter def _get_fq_approx(self, Ej, Ec): return (sqrt(8.0*Ej*Ec)-Ec)/h fq_max=SProperty().tag(unit="GHz", label="fq max", expression=r"$f_{qmax}$") @fq_max.getter def _get_fq_max(self, Ejmax, Ec, qubit_type, ng, Nstates): return self._get_fq(Ej=Ejmax, Ec=Ec, qubit_type=qubit_type, ng=ng, Nstates=Nstates) fq=SProperty().tag(desc="""Operating frequency of qubit""", unit="GHz", expression=r"$f_q$") @fq.getter def _get_fq(self, Ej, Ec, qubit_type, ng, Nstates): E0, E1=self._get_energy_levels(Ej=Ej, Ec=Ec, n_energy=2, qubit_type=qubit_type, ng=ng, Nstates=Nstates) return (E1-E0)/h @fq.setter def _get_Ej_get_fq(self, fq, Ec): """h*fq=sqrt(8.0*Ej*Ec) - Ec""" return ((h*fq+Ec)**2)/(8.0*Ec) L=SProperty().tag(label="Kinetic inductance of SQUID", expression=r"$L$") @L.getter def _get_L(self, fq, Ct): return 1.0/(Ct*(2*pi*fq)**2) anharm=SProperty().tag(desc="absolute anharmonicity", unit="GHz") @anharm.getter def _get_anharm(self, Ej, Ec, qubit_type, ng, Nstates): E0, E1, E2=self._get_energy_levels(Ej=Ej, Ec=Ec, n_energy=3, qubit_type=qubit_type, ng=ng, Nstates=Nstates) return (E2-E1)/h-(E1-E0)/h fq2=SProperty().tag(desc="""20 over 2 freq""", unit="GHz", expression = r"$f_{20}/2$") @fq2.getter def _get_fq2(self, Ej, Ec, qubit_type, ng, Nstates): E0, E1, E2=self._get_energy_levels(Ej=Ej, Ec=Ec, n_energy=3, qubit_type=qubit_type, ng=ng, Nstates=Nstates) return (E2-E0)/h/2.0 voltage=Float().tag(unit="V", desc="Yoko voltage on coil") offset=Float(0.09).tag(unit="V", desc="offset of flux in volts") flux_factor=Float(0.195).tag(desc="converts voltage to flux") flux_factor_beta=Float().tag(desc="linear dependenc on magnetic field") flux_over_flux0=SProperty().tag(expression=r"$\Phi/\Phi_0=$(voltage-offset)fluxfactor", unit="pi") @flux_over_flux0.getter def _get_flux_over_flux0(self, voltage, offset, flux_factor, flux_factor_beta): return (voltage-offset)*flux_factor#/(1.0-flux_factor_beta*(voltage-offset)) #return (voltage-offset)*flux_factor @flux_over_flux0.setter def _get_voltage(self, flux_over_flux0, offset, flux_factor, flux_factor_beta): #return flux_over_flux0/flux_factor*(1-flux_factor_beta/flux_factor*flux_over_flux0)+offset #return flux_over_flux0/flux_factor+offset+sign(flux_over_flux0)*flux_factor_beta*flux_over_flux0**2 #return flux_over_flux0/flux_factor+offset+flux_factor_beta*flux_over_flux0**3 return flux_over_flux0/flux_factor+offset flux_parabola=SProperty() @flux_parabola.getter def _get_flux_parabola(self, voltage, offset, flux_factor, Ejmax, Ec, qubit_type, ng, Nstates): flx_d_flx0=self._get_flux_over_flux0(voltage=voltage, offset=offset, flux_factor=flux_factor) qEj=self._get_Ej(Ejmax=Ejmax, flux_over_flux0=flx_d_flx0) return self._get_fq(Ej=qEj, Ec=Ec, qubit_type=qubit_type, ng=ng, Nstates=Nstates)#, fq2(qEj, Ec) #freq_arr=Array().tag(desc="array of frequencies to evaluate over") f=Float(4.4e9).tag(label="Operating frequency", desc="what frequency is being stimulated/measured", unit="GHz") flux_from_fq=SProperty().tag(sub=True) @flux_from_fq.getter def _get_flux_from_fq(self, fq, Ct, Ejmax): Ec=self._get_Ec(Ct=Ct) Ej=self._get_Ej_get_fq(fq=fq, Ec=Ec) return self._get_flux_over_flux0_get_Ej(Ej=Ej, Ejmax=Ejmax) voltage_from_flux_par=SProperty().tag(sub=True) @voltage_from_flux_par.getter def _get_voltage_from_flux_par(self, fq, Ct, Ejmax, offset, flux_factor): #Ec=self._get_Ec(Ct=Ct) #Ej=self._get_Ej_get_fq(fq=fq, Ec=Ec) flux_d_flux0=self._get_flux_from_fq(fq, Ct, Ejmax) #self._get_flux_over_flux0_get_Ej(Ej=Ej, Ejmax=Ejmax) return self._get_voltage(flux_over_flux0=flux_d_flux0, offset=offset, flux_factor=flux_factor) voltage_from_flux_par_many=SProperty().tag(sub=True) @voltage_from_flux_par_many.getter def _get_voltage_from_flux_par_many(self, fq, Ct, Ejmax, offset, flux_factor, flux_factor_beta): #Ec=self._get_Ec(Ct=Ct) #Ej=self._get_Ej_get_fq(fq=f, Ec=Ec) fdf0=self._get_flux_from_fq(fq, Ct, Ejmax) #self._get_flux_over_flux0_get_Ej(Ej=Ej, Ejmax=Ejmax) #fdf0=self._get_flux_over_flux0_get_Ej(Ej=Ej, Ejmax=Ejmax) flux_d_flux0=append(fdf0, -fdf0) flux_d_flux0=append(flux_d_flux0, -fdf0+pi*(1+flux_factor_beta)) flux_d_flux0=append(flux_d_flux0, fdf0-pi*(1+flux_factor_beta)) freq=append(fq, fq) freq=append(freq, freq) return freq/1e9, self._get_voltage(flux_over_flux0=flux_d_flux0, offset=offset, flux_factor=flux_factor) def detuning(self, fq_off): return 2.0*pi*(self.fq - fq_off) def transmon_energy(self, Ej, Ec, m): return -Ej+sqrt(8.0*Ej*Ec)*(m+0.5) - (Ec/12.0)*(6.0*m**2+6.0*m+3.0) transmon_energy_levels=SProperty().tag(sub=True) @transmon_energy_levels.getter def _get_transmon_energy_levels(self, Ej, Ec, n_energy): return [self.transmon_energy(Ej, Ec, m) for m in range(n_energy)] n_energy=Int(3) ng=Float(0.5).tag(desc="charge on gate line", log=False) Nstates=Int(50).tag(desc="number of states to include in mathieu approximation. More states is better approximation") order=Int(3) EkdivEc=Array().tag(unit2="Ec", sub=True) energy_levels=SProperty().tag(sub=True) @energy_levels.getter def _get_energy_levels(self, Ej, Ec, n_energy, qubit_type, ng, Nstates): if qubit_type=="scb": return self._get_scb_energy_levels(Ej=Ej, Ec=Ec, n_energy=n_energy, ng=ng, Nstates=Nstates) return self._get_transmon_energy_levels(Ej=Ej, Ec=Ec, n_energy=n_energy) def indiv_EkdivEc(self, EjdivEc, n_energy, ng, Nstates): """calculates energies assuming float values given""" NL=2*Nstates+1 A=zeros((NL, NL)) for b in range(0,NL): A[b, b]=4.0*(b-Nstates-ng)**2 if b!=NL-1: A[b, b+1]= -EjdivEc/2.0 if b!=0: A[b, b-1]= -EjdivEc/2.0 #w,v=eig(A) w=eigvalsh(A) return w[0:n_energy] scb_energy_levels=SProperty().tag(sub=True) @scb_energy_levels.getter def _get_scb_energy_levels(self, Ej, Ec, n_energy, ng, Nstates): if isinstance(Ej, float): Ej=array([Ej]) if isinstance(ng, float): ng=[ng] EjdivEc=Ej/Ec energies=Ec*squeeze([[self.indiv_EkdivEc(EjdivEc=ejc, n_energy=n_energy, ng=ng_s, Nstates=Nstates) for ejc in EjdivEc] for ng_s in ng]) return [spl.transpose() for spl in split(energies.transpose(), n_energy)] #energies=squeeze(energies) NL=2*Nstates+1 A=zeros((NL, NL)) for b in range(0,NL): A[b, b]=4.0*Ec*(b-Nstates-ng)**2 if b!=NL-1: A[b, b+1]= -Ej/2.0 if b!=0: A[b, b-1]= -Ej/2.0 #w,v=eig(A) w=eigvalsh(A) return w[0:n_energy] def update_EkdivEc(self, ng, Ec, Ej, Nstates, order): """calculates transmon energy level with N states (more states is better approximation) effectively solves the mathieu equation but for fractional inputs (which doesn't work in scipy.special.mathieu_a)""" # if type(ng) not in (int, float): # d=zeros((order, len(ng))) # elif type(Ec) not in (int, float): # d=zeros((order, len(Ec))) # elif type(Ej) not in (int, float): # d=zeros((order, len(Ej))) if type(ng) in (int, float): ng=array([ng]) d1=[] d2=[] d3=[] Ej=Ej/Ec Ec=1.0#/4.0 for a in ng: NL=2*Nstates+1 A=zeros((NL, NL)) for b in range(0,NL): A[b, b]=4.0*Ec*(b-Nstates-a)**2 if b!=NL-1: A[b, b+1]= -Ej/2.0 if b!=0: A[b, b-1]= -Ej/2.0 #w,v=eig(A) w=eigvalsh(A) d=w[0:order] # d1.append(min(w))#/h*1e-9) # w=delete(w, w.argmin()) # d2.append(min(w))#/h*1e-9) # w=delete(w, w.argmin()) # d3.append(min(w))#/h*1e-9) return array([array(d1), array(d2), array(d3)]).transpose() def sweepEc(): Ecarr=Ej/EjoverEc E01a=sqrt(8*Ej*Ecarr)-Ecarr data=[] for Ec in Ecarr: d1, d2, d3= EkdivEc(ng=ng, Ec=Ec, Ej=Ej, N=50) E12=d3[0]-d2[0] E01=d2[0]-d1[0] anharm2=(E12-E01)#/E01 data.append(anharm2) Ctr=e**2/(2.0*Ecarr*h*1e9) return E01a, Ctr, data, d1, d2, d3
class IDT(Agent): """Theoretical description of IDT""" base_name = "IDT" #main_params=["ft", "f0", "lbda0", "a", "g", "eta", "Np", "ef", "W", "Ct", # "material", "Dvv", "K2", "vf", "epsinf"] material = Enum('LiNbYZ', 'GaAs', 'LiNb128', 'LiNbYZX', 'STquartz') def _default_material(self): return 'LiNbYZ' def _observe_material(self, change): if change["type"] == "update": self.Dvv = None self.vf = None self.epsinf = None @t_property(desc="coupling strength", unit="%", tex_str=r"$\Delta v/v$", expression=r"$(v_f-v_m))/v_f$") def Dvv(self, material): return { "STquartz": 0.06e-2, 'GaAs': 0.035e-2, 'LiNbYZ': 2.4e-2, 'LiNb128': 2.7e-2, 'LiNbYZX': 0.8e-2 }[material] @t_property(desc="speed of SAW on free surface", unit="m/s", tex_str=r"$v_f$", format_str=r"{0:.4g} m/s") def vf(self, material): return { "STquartz": 3159.0, 'GaAs': 2900.0, 'LiNbYZ': 3488.0, 'LiNb128': 3979.0, 'LiNbYZX': 3770.0 }[material] @t_property(desc="Capacitance of single finger pair per unit length", tex_str=r"$\epsilon_\infty$") def epsinf(self, material): return { "STquartz": 5.6 * eps0, 'GaAs': 1.2e-10, 'LiNbYZ': 46.0 * eps0, 'LiNb128': 56.0 * eps0, 'LiNbYZX': 46.0 * eps0 }[material] ft = Enum("double", "single").tag(desc="finger type of IDT", label="Finger type", show_value=False) def _observe_ft(self, change): if change["type"] == "update": self.ft_mult = None self.Ga0_mult = None self.Ct_mult = None self.mu_mult = None def _default_ft(self): return "double" @t_property(desc="multiplier based on finger type") def ft_mult(self, ft): return {"double": 2.0, "single": 1.0}[ft] @t_property(dictify={ "single": 1.694**2, "double": (1.247 * sqrt(2))**2 }) #{"single":2.87, "double":3.11} def Ga0_mult(self, ft): return get_tag(self, "Ga0_mult", "dictify")[ft] @t_property(dictify={"single": 1.0, "double": sqrt(2)}) def Ct_mult(self, ft): return get_tag(self, "Ct_mult", "dictify")[ft] @t_property() def mu_mult(self, ft): return {"single": 1.694, "double": 1.247}[ft] #coupling_mult_dict={"single" : 0.71775, "double" : 0.54995} couple_mult = SProperty() @couple_mult.getter def _get_couple_mult(self, Ga0_mult, Ct_mult): return Ga0_mult / (4 * Ct_mult) f = Float(4.4e9).tag( desc= "Operating frequency, e.g. what frequency is being stimulated/measured" ) Np = Float(9).tag(desc="\# of finger pairs", low=0.5, tex_str=r"$N_p$", label="\# of finger pairs") ef = Int(0).tag(desc="for edge effect compensation", label="\# of extra fingers", low=0) W = Float(25.0e-6).tag(desc="height of finger.", unit="um") eta = Float(0.5).tag(desc="metalization ratio") f0 = Float(5.0e9).tag(unit="GHz", desc="Center frequency of IDT", reference="", tex_str=r"$f_0$", label="Center frequency") C = SProperty().tag(unit="fF", desc="Total capacitance of IDT", reference="Morgan page 16/145") @C.getter def _get_C(self, epsinf, Ct_mult, W, Np): """Morgan page 16, 145""" return Ct_mult * W * epsinf * Np @C.setter def _get_epsinf(self, C, Ct_mult, W, Np): """reversing capacitance to extract eps infinity""" return C / (Ct_mult * W * Np) @log_func def _get_eta(self, a, g): """metalization ratio""" return a / (a + g) K2 = SProperty().tag(desc="coupling strength", unit="%", tex_str=r"K$^2$", expression=r"K$^2=2\Delta v/v$") @K2.getter def _get_K2(self, Dvv): r"""Coupling strength. K$^2=2\Delta v/v$""" return Dvv * 2.0 @K2.setter def _get_Dvv(self, K2): """other couplign strength. free speed minus metal speed all over free speed""" return K2 / 2.0 Ga0 = SProperty().tag(desc="Conductance at center frequency") @Ga0.getter def _get_Ga0(self, Ga0_mult, f0, epsinf, W, Dvv, Np): """Ga0 from morgan""" return Ga0_mult * 2 * pi * f0 * epsinf * W * Dvv * (Np**2) Ga0div2C = SProperty() @Ga0div2C.getter def _get_Ga0div2C(self, couple_mult, f0, K2, Np): """coupling at center frequency, in Hz (2 pi removed)""" return couple_mult * f0 * K2 * Np X = SProperty() @X.getter def _get_X(self, Np, f, f0): """standard frequency dependence""" return Np * pi * (f - f0) / f0 coupling = SProperty().tag(desc="""Coupling adjusted by sinc sq""", unit="GHz", tex_str=r"$G_f$") @coupling.getter def _get_coupling(self, f, couple_mult, f0, K2, Np): gamma0 = self._get_Ga0div2C(couple_mult=couple_mult, f0=f0, K2=K2, Np=Np) gX = self._get_X(Np=Np, f=f, f0=f0) #return gamma0*(1.0/Np*sin(gX)/sin(gX/Np))**2 return gamma0 * (sqrt(2.0) * cos(pi * f / (4 * f0)) * sin(pi * f / (2 * f0)) * (1.0 / Np) * sin(gX) / sin(gX / Np))**2 #return gamma0*(sqrt(2)*cos(pi*f/(4*f0))*(1.0/Np)*sin(gX)/sin(gX/Np))**2 #return gamma0*(sin(gX)/gX)**2.0 max_coupling = SProperty().tag(desc="""Coupling at IDT center frequency""", unit="GHz", label="Coupling at center frequency", tex_str=r"$\gamma_{f0}$") @max_coupling.getter def _get_max_coupling(self, couple_mult, f0, K2, Np): return self._get_Ga0div2C(couple_mult=couple_mult, f0=f0, K2=K2, Np=Np) Lamb_shift = SProperty().tag(desc="""Lamb shift""", unit="GHz", tex_str=r"$G_f$") @Lamb_shift.getter def _get_Lamb_shift(self, f, couple_mult, f0, K2, Np): """returns Lamb shift""" frq = linspace(-5e9, 15e9, 20000) yp = imag( hilbert( self._get_coupling(f=frq, couple_mult=couple_mult, f0=f0, K2=K2, Np=Np))) #self.get_member("Lamb_shift_hilbert").reset(self) return interp(f, frq, yp) #*self.Lamb_shift_hilbert) #gamma0=self._get_Ga0div2C(couple_mult=couple_mult, f0=f0, K2=K2, Np=Np) #gX=self._get_X(Np=Np, f=f, f0=f0) #return gamma0*(1.0/Np)**2*2*(Np*sin(2*gX/Np)-sin(2*gX))/(2*(1-cos(2*gX/Np))) #return -gamma0*(sin(2.0*gX)-2.0*gX)/(2.0*gX**2.0) #Lamb_shift_hilbert=SProperty() @private_property def Lamb_shift_hilbert(self): frq = linspace(-5e9, 15e9, 20000) return frq, imag(hilbert(self._get_coupling(f=frq))) ZL = Float(50.0) GL = Float(1 / 50.0) S11 = SProperty() @S11.getter def _get_S11(self, f, couple_mult, f0, K2, Np, C, ZL): Ga = self._get_Ga(f=f, couple_mult=couple_mult, f0=f0, K2=K2, Np=Np, C=C) Ba = self._get_Ba(f=f, couple_mult=couple_mult, f0=f0, K2=K2, Np=Np, C=C) w = 2 * pi * f return Ga / (Ga + 1j * Ba + 1j * w * C + 1.0 / ZL) S13 = SProperty() @S13.getter def _get_S13(self, f, couple_mult, f0, K2, Np, C, ZL, GL): Ga = self._get_Ga(f=f, couple_mult=couple_mult, f0=f0, K2=K2, Np=Np, C=C) Ba = self._get_Ba(f=f, couple_mult=couple_mult, f0=f0, K2=K2, Np=Np, C=C) w = 2 * pi * f return 1j * sqrt(2 * Ga * GL) / (Ga + 1j * Ba + 1j * w * C + 1.0 / ZL) Ga = SProperty().tag(desc="Ga adjusted for frequency f") @Ga.getter def _get_Ga(self, f, couple_mult, f0, K2, Np, C): return self._get_coupling( f=f, couple_mult=couple_mult, f0=f0, K2=K2, Np=Np) * 2 * C * 2 * pi Ba = SProperty() @Ba.getter def _get_Ba(self, f, couple_mult, f0, K2, Np, C): return -self._get_Lamb_shift( f=f, couple_mult=couple_mult, f0=f0, K2=K2, Np=Np) * 2 * C * 2 * pi lbda = SProperty() @lbda.getter def _get_lbda(self, f, vf): """wavelength relationship to speed and frequency""" return vf / f @lbda.setter def _get_f(self, lbda, vf): """frequency relationship to speed and wavelength""" return vf / lbda lbda0 = SProperty().tag(unit="um", desc="Center wavelength", reference="") @lbda0.getter def _get_lbda0(self, f0, vf): return self._get_lbda(f=f0, vf=vf) @lbda0.setter def _get_f0(self, lbda0, vf): return self._get_f(lbda=lbda0, vf=vf) g = SProperty().tag( desc= "gap between fingers (um). about 0.096 for double fingers at 4.5 GHz", unit="um") @g.getter def _get_g(self, a, eta): """gap given metalization and finger width eta=a/(a+g) => a=(a+g)*eta => (1-eta)*a=g*eta => g=a*(1/eta-1)""" return a * (1.0 / eta - 1.0) @g.setter def _get_a_get_(self, g, eta): """finger width given gap and metalization ratio eta=a/(a+g) => a=(a+g)*eta => (1-eta)*a=g*eta => a=g*eta/(1-eta)""" return g * eta / (1.0 - eta) a = SProperty().tag(desc="width of fingers", unit="um") @a.getter def _get_a(self, eta, lbda0, ft_mult): """finger width from lbda0""" return eta * lbda0 / (2.0 * ft_mult) @a.setter def _get_lbda0_get_(self, a, eta, ft_mult): return a / eta * 2.0 * ft_mult @private_property def view_window2(self): from enaml import imports with imports(): from taref.saw.idt_e import IDT_View return IDT_View(idt=self)
class QDT(IDT, Qubit): base_name = "QDT" #lamb_shifted_transmon_energy=SProperty() #@lamb_shifted_transmon_energy.getter def _get_lamb_shifted_transmon_energy(self, Ej, Ec, m, couple_mult, f0, K2, Np): Em = -Ej + sqrt(8.0 * Ej * Ec) * (m + 0.5) - (Ec / 12.0) * ( 6.0 * m**2 + 6.0 * m + 3.0) if m == 0: return Em Emm1 = -Ej + sqrt( 8.0 * Ej * Ec) * (m - 1 + 0.5) - (Ec / 12.0) * (6.0 * (m - 1)**2 + 6.0 * (m - 1) + 3.0) fq = (Em - Emm1) / h fls = self._get_Lamb_shift(f=fq, couple_mult=couple_mult, f0=f0, K2=K2, Np=Np) return Em + h * fls lamb_shifted_transmon_energy_levels = SProperty() @lamb_shifted_transmon_energy_levels.getter def _get_lamb_shifted_transmon_energy_levels(self, Ej, couple_mult, f0, K2, Np, Ec, n_energy): return [ self._get_lamb_shifted_transmon_energy(Ej=Ej, Ec=Ec, m=m, couple_mult=couple_mult, f0=f0, K2=K2, Np=Np) for m in range(n_energy) ] lamb_shifted_fq = SProperty().tag(desc="""Operating frequency of qubit""", unit="GHz") @lamb_shifted_fq.getter def _get_lamb_shifted_fq(self, Ej, Ec): E0, E1 = self._get_lamb_shifted_transmon_energy_levels(Ej=Ej, Ec=Ec, n_energy=2) return (E1 - E0) / h lamb_shifted_anharm = SProperty().tag(desc="absolute anharmonicity", unit="hGHz") @lamb_shifted_anharm.getter def _get_lamb_shifted_anharm(self, Ej, Ec): E0, E1, E2 = self._get_lamb_shifted_transmon_energy_levels(Ej=Ej, Ec=Ec, n_energy=3) return (E2 - E1) / h - (E1 - E0) / h lamb_shifted_fq2 = SProperty().tag(desc="""20 over 2 freq""", unit="GHz") @lamb_shifted_fq2.getter def _get_lamb_shifted_fq2(self, Ej, Ec): E0, E1, E2 = self._get_lamb_shifted_transmon_energy_levels(Ej=Ej, Ec=Ec, n_energy=3) return (E2 - E0) / h / 2.0 #@s_property(desc="shunt capacitance of QDT", unit="fF") #def Cq(self, C): # return C #@Cq.setter #def _get_C_get_Cq(self, Cq): # return Cq ls_flux_parabola = SProperty() @ls_flux_parabola.getter def _get_ls_flux_parabola(self, voltage, offset, flux_factor, Ejmax, Ec): flx_d_flx0 = self._get_flux_over_flux0(voltage=voltage, offset=offset, flux_factor=flux_factor) qEj = self._get_Ej(Ejmax=Ejmax, flux_over_flux0=flx_d_flx0) return self._get_lamb_shifted_fq(Ej=qEj, Ec=Ec) #, fq2(qEj, Ec) ls_f = SProperty().tag(sub=True) @ls_f.getter def _get_ls_f(self, f, couple_mult, f0, K2, Np): try: return array([ sqrt(qf * (qf - 2 * self._get_Lamb_shift( f=qf, couple_mult=couple_mult, f0=f0, K2=K2, Np=Np))) for qf in f ]) except TypeError: return sqrt(f * (f - 2 * self._get_Lamb_shift( f=f, couple_mult=couple_mult, f0=f0, K2=K2, Np=Np))) ls_voltage_from_flux_par = SProperty().tag(sub=True) @ls_voltage_from_flux_par.getter def _get_ls_voltage_from_flux_par(self, freq_arr, C, Ejmax, offset, flux_factor, couple_mult, f0, K2, Np): ls_f = self._get_ls_f(freq_arr=freq_arr, couple_mult=couple_mult, f0=f0, K2=K2, Np=Np) Ec = self._get_Ec(C=C) Ej = self._get_Ej_get_fq(fq=ls_f, Ec=Ec) flux_d_flux0 = self._get_flux_over_flux0_get_Ej(Ej=Ej, Ejmax=Ejmax) return ls_f / 1e9, self._get_voltage(flux_over_flux0=flux_d_flux0, offset=offset, flux_factor=flux_factor) ls_voltage_from_flux_par_many = SProperty().tag(sub=True) @ls_voltage_from_flux_par_many.getter def _get_ls_voltage_from_flux_par_many(self, f, C, Ejmax, offset, flux_factor, couple_mult, f0, K2, Np): ls_f = self._get_ls_f(f=f, couple_mult=couple_mult, f0=f0, K2=K2, Np=Np) Ec = self._get_Ec(C=C) Ej = self._get_Ej_get_fq(fq=ls_f, Ec=Ec) fdf0 = self._get_flux_over_flux0_get_Ej(Ej=Ej, Ejmax=Ejmax) flux_d_flux0 = append(fdf0, -fdf0) flux_d_flux0 = append(flux_d_flux0, -fdf0 + pi) flux_d_flux0 = append(flux_d_flux0, fdf0 - pi) freq = append(f, f) freq = append(freq, freq) return freq / 1e9, self._get_voltage(flux_over_flux0=flux_d_flux0, offset=offset, flux_factor=flux_factor) S11 = SProperty() @S11.getter def _get_S11(self, f, couple_mult, f0, K2, Np, C, L): Ga = self._get_Ga(f=f, couple_mult=couple_mult, f0=f0, K2=K2, Np=Np, C=C) Ba = self._get_Ba(f=f, couple_mult=couple_mult, f0=f0, K2=K2, Np=Np, C=C) w = 2 * pi * f try: return Ga / (Ga + 1j * Ba + 1j * w * C + 1.0 / (1j * w * L)) except ValueError: return array([ Ga / (Ga + 1j * Ba + 1j * w * C + 1.0 / (1j * w * qL)) for qL in L ]) S13 = SProperty() @S13.getter def _get_S13(self, f, couple_mult, f0, K2, Np, C, L, GL): Ga = self._get_Ga(f=f, couple_mult=couple_mult, f0=f0, K2=K2, Np=Np, C=C) Ba = self._get_Ba(f=f, couple_mult=couple_mult, f0=f0, K2=K2, Np=Np, C=C) w = 2 * pi * f return 1j * sqrt(2.0 * Ga * GL) / (Ga + 1j * Ba + 1j * w * C + 1.0 / (1j * w * L))
class Qubit(Agent): """Theoretical description of qubit""" base_name="qubit" #def _default_main_params(self): # return ["ft", "f0", "lbda0", "a", "g", "eta", "Np", "ef", "W", "Ct", # "material", "Dvv", "K2", "vf", "epsinf", # "Rn", "Ic", "Ejmax", "Ej", "Ec", "EjmaxdivEc", "EjdivEc", # "fq", "fq_max", "fq_max_full", "flux_over_flux0", "G_f0", "G_f", # "ng", "Nstates", "EkdivEc"] superconductor=Enum("Al") def _observe_superconductor(self, change): if self.superconductor=="Al": self.Delta=Delta_Al Tc=Float(Tc_Al).tag(desc="Critical temperature of superconductor", unit="K") Delta=SProperty().tag(label="Gap", tex_str=r"$\Delta(0)$", unit="ueV", desc="Superconducting gap 200 ueV for Al", reference="BCS", expression=r"$1.764 k_B T_c$") @Delta.getter def _get_Delta(self, Tc): """BCS theory superconducting gap""" return 1.764*kB*Tc @Delta.setter def _get_Tc(self, Delta): return Delta/(1.764*kB) loop_width=Float(1.0e-6).tag(desc="loop width of SQUID", unit="um", label="loop width") loop_height=Float(1.0e-6).tag(desc="loop height of SQUID", unit="um", label="loop height") loop_area=SProperty().tag(desc="Area of SQUID loop", unit="um^2", expression="$width \times height$", comment="Loop width times loop height", label="loop area") @loop_area.getter def _get_loop_area(self, loop_width, loop_height): return loop_width*loop_height C=Float(1.0e-13).tag(desc="shunt capacitance", unit="fF", tex_str=r"$C_q$") Rn=Float(10.0e3).tag(desc="Normal resistance of SQUID", unit="kOhm", label="DC Junction resistance", tex_str=r"$R_n$") Ic=SProperty().tag(desc="critical current of SQUID", unit="nA", label="Critical current", tex_str=r"$I_C$") @Ic.getter def _get_Ic(self, Rn, Delta): """Ic*Rn=pi*Delta/(2.0*e) #Ambegaokar Baratoff formula""" return pi*Delta/(2.0*e)/Rn @Ic.setter def _get_Rn(self, Ic, Delta): """Ic*Rn=pi*Delta/(2.0*e) #Ambegaokar Baratoff formula""" return pi*Delta/(2.0*e)/Ic Ejmax=SProperty().tag(desc="""Max Josephson Energy""", unit="hGHz")#, unit_factor=1.0e9*h) @Ejmax.getter def _get_Ejmax(self, Ic): """Josephson energy""" return hbar*Ic/(2.0*e) @Ejmax.setter def _get_Ic_get_Ejmax(self, Ejmax): """inverse Josephson energy""" return Ejmax*(2.0*e)/hbar Ec=SProperty().tag(desc="Charging Energy", unit="hGHz")#, unit_factor=1.0e9*h) @Ec.getter def _get_Ec(self, C): """Charging energy""" return e**2/(2.0*C) @Ec.setter def _get_C(self, Ec): """inverse charging energy""" return e**2/(2.0*Ec) @Ec.setter def _get_Ejmax_get_Ec(self, Ec, EjmaxdivEc): return EjmaxdivEc*Ec EjmaxdivEc=SProperty().tag(desc="Maximum Ej over Ec") @EjmaxdivEc.getter def _get_EjmaxdivEc(self, Ejmax, Ec): return Ejmax/Ec Ej=SProperty().tag(unit="hGHz") @Ej.getter def _get_Ej(self, Ejmax, flux_over_flux0): return Ejmax*absolute(cos(flux_over_flux0)) #*pi @Ej.setter def _get_flux_over_flux0_get_Ej(self, Ej, Ejmax): return arccos(Ej/Ejmax)#/pi EjdivEc=SProperty().tag(desc="Ej over Ec") @EjdivEc.getter def _get_EjdivEc(self, Ej, Ec): return Ej/Ec fq_approx_max=SProperty().tag(unit="GHz", label="fq max") @fq_approx_max.getter def _get_fq_approx_max(self, Ejmax, Ec): return self._get_fq_approx(Ej=Ejmax, Ec=Ec) fq_approx=SProperty() @fq_approx.getter def _get_fq_approx(self, Ej, Ec): return (sqrt(8.0*Ej*Ec)-Ec)/h fq_max=SProperty().tag(unit="hGHz", label="fq max full") @fq_approx.getter def _get_fq_max(self, Ejmax, Ec): return self._get_fq(Ej=Ejmax, Ec=Ec) fq=SProperty().tag(desc="""Operating frequency of qubit""", unit="GHz") @fq.getter def _get_fq(self, Ej, Ec): E0, E1=self._get_transmon_energy_levels(Ej=Ej, Ec=Ec, n_energy=2) return (E1-E0)/h @fq.setter def _get_Ej_get_fq(self, fq, Ec): """h*fq=sqrt(8.0*Ej*Ec) - Ec""" return ((h*fq+Ec)**2)/(8.0*Ec) L=SProperty() @L.getter def _get_L(self, fq, C): return 1.0/(C*(2*pi*fq)**2) anharm=SProperty().tag(desc="absolute anharmonicity", unit="hGHz") @anharm.getter def _get_anharm(self, Ej, Ec): E0, E1, E2=self._get_transmon_energy_levels(Ej=Ej, Ec=Ec, n_energy=3) return (E2-E1)/h-(E1-E0)/h fq2=SProperty().tag(desc="""20 over 2 freq""", unit="GHz") @fq2.getter def _get_fq2(self, Ej, Ec): E0, E1, E2=self._get_transmon_energy_levels(Ej=Ej, Ec=Ec, n_energy=3) return (E2-E0)/h/2.0 voltage=Float().tag(unit="V") offset=Float(0.09).tag(unit="V") flux_factor=Float(0.195) flux_over_flux0=SProperty() @flux_over_flux0.getter def _get_flux_over_flux0(self, voltage, offset, flux_factor): return (voltage-offset)*flux_factor @flux_over_flux0.setter def _get_voltage(self, flux_over_flux0, offset, flux_factor): return flux_over_flux0/flux_factor+offset flux_parabola=SProperty() @flux_parabola.getter def _get_flux_parabola(self, voltage, offset, flux_factor, Ejmax, Ec): flx_d_flx0=self._get_flux_over_flux0(voltage=voltage, offset=offset, flux_factor=flux_factor) qEj=self._get_Ej(Ejmax=Ejmax, flux_over_flux0=flx_d_flx0) return self._get_fq(Ej=qEj, Ec=Ec)#, fq2(qEj, Ec) #freq_arr=Array().tag(desc="array of frequencies to evaluate over") f=Float(4.4e9).tag(desc="Operating frequency, e.g. what frequency is being stimulated/measured") voltage_from_flux_par=SProperty().tag(sub=True) @voltage_from_flux_par.getter def _get_voltage_from_flux_par(self, f, C, Ejmax, offset, flux_factor): Ec=self._get_Ec(C=C) Ej=self._get_Ej_get_fq(fq=f, Ec=Ec) flux_d_flux0=self._get_flux_over_flux0_get_Ej(Ej=Ej, Ejmax=Ejmax) return f/1e9, self._get_voltage(flux_over_flux0=flux_d_flux0, offset=offset, flux_factor=flux_factor) voltage_from_flux_par_many=SProperty().tag(sub=True) @voltage_from_flux_par_many.getter def _get_voltage_from_flux_par_many(self, f, C, Ejmax, offset, flux_factor): Ec=self._get_Ec(C=C) Ej=self._get_Ej_get_fq(fq=f, Ec=Ec) fdf0=self._get_flux_over_flux0_get_Ej(Ej=Ej, Ejmax=Ejmax) flux_d_flux0=append(fdf0, -fdf0) flux_d_flux0=append(flux_d_flux0, -fdf0+pi) flux_d_flux0=append(flux_d_flux0, fdf0-pi) freq=append(f, f) freq=append(freq, freq) return freq/1e9, self._get_voltage(flux_over_flux0=flux_d_flux0, offset=offset, flux_factor=flux_factor) def detuning(self, fq_off): return 2.0*pi*(self.fq - fq_off) def transmon_energy(self, Ej, Ec, m): return -Ej+sqrt(8.0*Ej*Ec)*(m+0.5) - (Ec/12.0)*(6.0*m**2+6.0*m+3.0) transmon_energy_levels=SProperty().tag(sub=True) @transmon_energy_levels.getter def _get_transmon_energy_levels(self, Ej, Ec, n_energy): #Ej=EjdivEc*Ec return [self.transmon_energy(Ej, Ec, m) for m in range(n_energy)] n_energy=Int(3) def indiv_EkdivEc(self, ng, Ec, Ej, Nstates, order): NL=2*Nstates+1 A=zeros((NL, NL)) for b in range(0,NL): A[b, b]=4.0*Ec*(b-Nstates-a)**2 if b!=NL-1: A[b, b+1]= -Ej/2.0 if b!=0: A[b, b-1]= -Ej/2.0 w,v=eig(A) print w, v #for n in range(order): ng=Float(0.5).tag(desc="charge on gate line") Nstates=Int(50).tag(desc="number of states to include in mathieu approximation. More states is better approximation") order=Int(3) EkdivEc=Array().tag(unit2="Ec", sub=True) def update_EkdivEc(self, ng, Ec, Ej, Nstates, order): """calculates transmon energy level with N states (more states is better approximation) effectively solves the mathieu equation but for fractional inputs (which doesn't work in scipy.special.mathieu_a)""" # if type(ng) not in (int, float): # d=zeros((order, len(ng))) # elif type(Ec) not in (int, float): # d=zeros((order, len(Ec))) # elif type(Ej) not in (int, float): # d=zeros((order, len(Ej))) if type(ng) in (int, float): ng=array([ng]) d1=[] d2=[] d3=[] Ej=Ej/Ec Ec=1.0#/4.0 for a in ng: NL=2*Nstates+1 A=zeros((NL, NL)) for b in range(0,NL): A[b, b]=4.0*Ec*(b-Nstates-a)**2 if b!=NL-1: A[b, b+1]= -Ej/2.0 if b!=0: A[b, b-1]= -Ej/2.0 #w,v=eig(A) w=eigvalsh(A) d=w[0:order] # d1.append(min(w))#/h*1e-9) # w=delete(w, w.argmin()) # d2.append(min(w))#/h*1e-9) # w=delete(w, w.argmin()) # d3.append(min(w))#/h*1e-9) return array([array(d1), array(d2), array(d3)]).transpose() def sweepEc(): Ecarr=Ej/EjoverEc E01a=sqrt(8*Ej*Ecarr)-Ecarr data=[] for Ec in Ecarr: d1, d2, d3= EkdivEc(ng=ng, Ec=Ec, Ej=Ej, N=50) E12=d3[0]-d2[0] E01=d2[0]-d1[0] anharm2=(E12-E01)#/E01 data.append(anharm2) Ctr=e**2/(2.0*Ecarr*h*1e9) return E01a, Ctr, data, d1, d2, d3