def get_simulation_parameters(L, lattice_depths, confining_depth, site_number = 100): L = np.array(L, ndmin = 1).round().astype(int) L = L[L>1] lattice_depths = np.array(lattice_depths, ndmin = 1) while lattice_depths.size < L.size: lattice_depths = np.append(lattice_depths, lattice_depths[-1]) # compute transverse tunneling and on-site overlap integral momenta, fourier_vecs, _ = mathieu_solution(confining_depth, 1, site_number) J_T = tunneling_1D(confining_depth, momenta, fourier_vecs) K_T = pair_overlap_1D(momenta, fourier_vecs) # compute everything we need along the primary axes momenta = [ None ] * L.size fourier_vecs = [ None ] * L.size energies = [ None ] * L.size J_0 = np.zeros(L.size) K_0 = np.zeros(L.size) for ii in range(L.size): momenta[ii], fourier_vecs[ii], energies[ii] = \ mathieu_solution(lattice_depths[ii], 1, site_number) energies[ii] -= np.mean(energies[ii],0) J_0[ii] = tunneling_1D(lattice_depths[ii], momenta[ii], fourier_vecs[ii]) K_0[ii] = pair_overlap_1D(momenta[ii], fourier_vecs[ii]) momenta = np.array(momenta) fourier_vecs = np.array(fourier_vecs) energies = np.array(energies) return L, J_0, J_T, K_0, K_T, momenta, fourier_vecs, energies
def get_J_O(depth, stretch): depth_S = depth * stretch**2 momenta_S, fourier_vecs_S, _ = mathieu_solution(depth_S, bands, site_number) tunneling_rate_S = tunneling_1D(depth_S, momenta_S, fourier_vecs_S) tunneling_rate = tunneling_rate_S / stretch**2 tunneling_rate *= recoil_energy_Hz rabi_ratio = abs( laser_overlap(np.pi, momenta_S, fourier_vecs_S, 1) / laser_overlap(np.pi, momenta_S, fourier_vecs_S)) return tunneling_rate, rabi_ratio * rabi_frequency
def renormalized_coupling(coupling, lattice_depths, bands=15, site_number=100, precision=10, backwards=False, harmonic=False): mean_depth = gmean(lattice_depths) # geometric mean of lattice depths w_eff = np.sqrt(2 * mean_depth / m_SR87_LU) # effective angular harmonic trap frequency # two-atom ground-state overlap integral if not harmonic: overlap_factor = 1 for depth in lattice_depths: momenta, fourier_vecs, _ = mathieu_solution( depth, bands, site_number) overlap_factor *= pair_overlap_1D(momenta, fourier_vecs) else: # determine ground-state two-body overlap integral in a harmonic oscillator HO_length = mean_depth**( -1 / 4) # harmonic oscillator length in lattice units overlap_factor = np.sqrt(2 / np.pi) / (4 * np.pi) / HO_length**3 unknown_coupling = sym.symbols("G") if not backwards: # compute an in-trap scattering length from a harmonic one E_free = coupling * overlap_factor / w_eff E_lattice = unknown_coupling * overlap_factor / w_eff else: # compute harmonic scattering length from an in-trap one E_free = unknown_coupling * overlap_factor / w_eff E_lattice = coupling * overlap_factor / w_eff # coefficients of series expansion of the right hand side of our expression c_0 = 1 c_1 = 1 - np.log(2) c_2 = -np.pi**2 / 24 - np.log(2) + 1 / 2 * np.log(2)**2 series = c_0 + c_1 * E_lattice + c_2 * E_lattice**2 null_expression = 1 / E_free - 1 / E_lattice * series return min(real_positive_roots(null_expression, unknown_coupling, precision), key=lambda x: abs(x / coupling - 1))
def U_J(depth): momenta, fourier_vecs, _ = mathieu_solution(depth, 1, site_number) J_0 = tunneling_1D(depth, momenta, fourier_vecs) K_0 = pair_overlap_1D(momenta, fourier_vecs) return g_int_LU[1] * K_0**lattice_dim * K_T**(3 - lattice_dim) / J_0
########################################################################################## # compute squeezing and make plots ########################################################################################## lattice_dim = 1 tau_vals = np.linspace(0, max_tau, time_steps) plt.figure(figsize=figsize) plt.title(r"${}$".format(title)) for ii, N in enumerate(N_vals): color = colors[ii] # determine primary lattice depth which optimally satisfies our target U_int / J_0 momenta, fourier_vecs, _ = mathieu_solution(confining_depth, 1, site_number) K_T = pair_overlap_1D(momenta, fourier_vecs) J_T = tunneling_1D(confining_depth, momenta, fourier_vecs) def U_J(depth): momenta, fourier_vecs, _ = mathieu_solution(depth, 1, site_number) J_0 = tunneling_1D(depth, momenta, fourier_vecs) K_0 = pair_overlap_1D(momenta, fourier_vecs) return g_int_LU[1] * K_0**lattice_dim * K_T**(3 - lattice_dim) / J_0 lattice_depth = minimize_scalar(lambda x: abs(U_J(x) - U_J_target), method="bounded", bounds=lattice_depth_bounds).x # get simulation parameters and on-site interaction energy L, J_0, J_T, K_0, K_T, momenta, fourier_vecs, energies = \
summary_info = False connectivity_info = False ########################################################################################## # fixed and derived lattice parameters ########################################################################################## nuclear_splitting_per_gauss = 110 # 2\pi Hz / Gauss / nuclear spin nuclear_splitting = magnetic_field * nuclear_splitting_per_gauss # lattice depths in units of "stretched" recoil energies V_P_S = V_P * stretch_P**2 V_T_S = V_T * stretch_T**2 # solve mathieu equation along primary and transverse axes momenta_P_S, fourier_vecs_P_S, _ = mathieu_solution(V_P_S, bands, site_number) momenta_T_S, fourier_vecs_T_S, _ = mathieu_solution(V_T_S, bands, site_number) # compute overlap integrals and tunneling rates in stretched recoil energies K_P_S = pair_overlap_1D(momenta_P_S, fourier_vecs_P_S) K_T_S = pair_overlap_1D(momenta_T_S, fourier_vecs_T_S) tunneling_rate_P_S = tunneling_1D(V_P_S, momenta_P_S, fourier_vecs_P_S) tunneling_rate_T_S = tunneling_1D(V_T_S, momenta_T_S, fourier_vecs_T_S) # overlap integrals and tunneling rates in regular recoil energies K_P = K_P_S / stretch_P K_T = K_T_S / stretch_T tunneling_rate_P = tunneling_rate_P_S / stretch_P**2 tunneling_rate_T = tunneling_rate_T_S / stretch_T**2 # on-site interaction energies
def energy_correction_coefficients(lattice_depths, site_number, pt_order=3, bands=13): assert (pt_order >= 1) momenta_x, fourier_vecs_x, _ = mathieu_solution(lattice_depths[0], bands, site_number) momenta_y, fourier_vecs_y, _ = mathieu_solution(lattice_depths[1], bands, site_number) momenta_z, fourier_vecs_z, _ = mathieu_solution(lattice_depths[2], bands, site_number) a_2_1 = (pair_overlap_1D(momenta_x, fourier_vecs_x) * pair_overlap_1D(momenta_y, fourier_vecs_y) * pair_overlap_1D(momenta_z, fourier_vecs_z)) momenta_list = [momenta_x, momenta_y, momenta_z] fourier_vecs_list = [fourier_vecs_x, fourier_vecs_y, fourier_vecs_z] a_prime_2_1 = momentum_coupling_overlap_3D(momenta_list, fourier_vecs_list) if pt_order == 1: return [a_2_1, a_prime_2_1] x, y, z = 0, 1, 2 # axis indices lattice_depths = np.sort(lattice_depths) # compute all 1-D band energies, spatial wavefunction overlaps, and tunneling rates band_energies = np.zeros((3, bands)) K_1D = np.zeros((3, bands, bands, bands)) if pt_order > 2: K_t_1D = np.zeros((3, bands, bands)) t_1D = np.zeros((3, bands)) for axis in range(3): # if any of the lattice depths are the same, we can recycle our calculations if axis > 0 and lattice_depths[axis] == lattice_depths[axis - 1]: band_energies[axis, :] = band_energies[axis - 1, :] K_1D[axis, :, :, :] = K_1D[axis - 1, :, :, :] if pt_order > 2: K_t_1D[axis, :, :] = K_t_1D[axis - 1, :, :] t_1D[axis, :] = t_1D[axis - 1, :] continue # otherwise compute everything for this lattice depth momenta, fourier_vecs, energies = mathieu_solution( lattice_depths[axis], bands, site_number) band_energies[axis, :] = np.mean(energies, 0) band_energies[axis, :] -= band_energies[axis, 0] for nn in range(bands): if pt_order > 2: t_1D[axis, nn] = tunneling_1D(lattice_depths[axis], momenta, fourier_vecs, nn) for mm in range(nn, bands): if pt_order > 2: K_t_1D[axis, nn, mm] = pair_overlap_1D(momenta, fourier_vecs, nn, mm, neighbors=1) K_t_1D[axis, mm, nn] = K_t_1D[axis, nn, mm] for ll in range(bands): K_1D[axis, nn, mm, ll] = pair_overlap_1D(momenta, fourier_vecs, nn, mm, ll) K_1D[axis, mm, nn, ll] = K_1D[axis, nn, mm, ll] K = np.prod(K_1D[:, 0, 0, 0]) if pt_order == 2: # collect all (even) band indices to loop over even_bands = [(n_x, n_y, n_z) for n_z in range(0, bands, 2) for n_y in range(n_z, bands, 2) for n_x in range(n_y, bands, 2)] # second order spatial overlap factor a_3_2 = 0 for n_x, n_y, n_z in even_bands[1:]: K_n = K_1D[x, n_x, 0, 0] * K_1D[y, n_y, 0, 0] * K_1D[z, n_z, 0, 0] E_n = band_energies[x, n_x] + band_energies[ y, n_y] + band_energies[z, n_z] a_3_2 += K_n**2 / E_n * len(set(permutations([n_x, n_y, n_z]))) return a_2_1, a_prime_2_1, a_3_2 # collect all band indices to loop over all_bands = ((n_x, n_y, n_z) for n_z in range(bands) for n_y in range(n_z, bands) for n_x in range(n_y, bands)) # second and third order spatial overlap factors a_3_2, a_3_3_1, a_3_3_2, a_4_3_1, a_4_3_2, a_4_3_3, a_5_3, g_2_2, g_3_2_1, g_3_2_2 \ = np.zeros(10) for n_x, n_y, n_z in all_bands: n_permutations = len(set(permutations([n_x, n_y, n_z]))) E_n = band_energies[x, n_x] + band_energies[y, n_y] + band_energies[z, n_z] K_n = K_1D[x, n_x, 0, 0] * K_1D[y, n_y, 0, 0] * K_1D[z, n_z, 0, 0] parity_x = n_x % 2 parity_y = n_y % 2 parity_z = n_z % 2 if E_n != 0 and K_n != 0: K_t_n = K_n * (K_t_1D[x, n_x, 0] / K_1D[x, n_x, 0, 0] + K_t_1D[y, n_y, 0] / K_1D[y, n_y, 0, 0] + K_t_1D[z, n_z, 0] / K_1D[z, n_z, 0, 0]) / 3 t_n = (t_1D[x, n_x] + t_1D[y, n_y] + t_1D[z, n_z]) / 3 a_3_2 += K_n**2 / E_n * n_permutations a_5_3 += K_n**2 / E_n**2 * n_permutations g_3_2_1 += K_n * K_t_n / E_n * n_permutations g_3_2_2 += t_n * K_n**2 / E_n**2 * n_permutations for m_x, m_y, m_z in cartesian_product(range(parity_x, bands, 2), range(parity_y, bands, 2), range(parity_z, bands, 2)): E_m = band_energies[x, m_x] + band_energies[ y, m_y] + band_energies[z, m_z] E_mn = E_n + E_m if E_mn == 0: continue K_m = K_1D[x, m_x, 0, 0] * K_1D[y, m_y, 0, 0] * K_1D[z, m_z, 0, 0] K_mn = K_1D[x, m_x, n_x, 0] * K_1D[y, m_y, n_y, 0] * K_1D[z, m_z, n_z, 0] if E_n != 0: a_4_3_1 += K_mn * K_m * K_n / (E_mn * E_n) * n_permutations if E_n != 0 and E_m != 0: K_m_n = K_1D[x, m_x, 0, n_x] * K_1D[y, m_y, 0, n_y] * K_1D[z, m_z, 0, n_z] a_4_3_2 += K_m * K_m_n * K_n / (E_m * E_n) * n_permutations a_4_3_3 += (K_mn / E_mn)**2 * n_permutations if K_mn == 0: continue K_t_mn = K_mn * (K_t_1D[x, n_x, n_x] / K_1D[x, m_x, n_x, 0] + K_t_1D[x, n_y, n_y] / K_1D[x, m_y, n_y, 0] + K_t_1D[x, n_z, n_z] / K_1D[x, m_z, n_z, 0]) / 3 g_2_2 += K_mn * K_t_mn / E_mn * n_permutations for l_x, l_y, l_z in cartesian_product(range(parity_x, bands, 2), range(parity_y, bands, 2), range(m_z, bands, 2)): E_l = band_energies[x, l_x] + band_energies[ y, l_y] + band_energies[z, l_z] E_ln = E_l + E_n multiplicity = n_permutations * (1 if l_z == m_z else 2) if E_ln == 0 or E_mn == 0: continue K_ln = K_1D[x, l_x, n_x, 0] * K_1D[y, l_y, n_y, 0] * K_1D[z, l_z, n_z, 0] K_l_m = K_1D[x, l_x, 0, m_x] * K_1D[y, l_y, 0, m_y] * K_1D[z, l_z, 0, m_z] a_3_3_1 += K_mn * K_l_m * K_ln / (E_mn * E_ln) * multiplicity for l_x, l_y, l_z in cartesian_product(range(0, bands, 2), repeat=3): E_l = band_energies[x, l_x] + band_energies[ y, l_y] + band_energies[z, l_z] if E_l == 0 or E_mn == 0: continue K_l = K_1D[x, l_x, 0, 0] * K_1D[y, l_y, 0, 0] * K_1D[z, l_z, 0, 0] K_lmn = K_1D[x, n_x, m_x, l_x] * K_1D[y, n_y, m_y, l_y] * K_1D[z, n_z, m_z, l_z] fac = K_l * K_mn / (E_l * E_mn) * n_permutations a_3_3_2 += fac * (K_lmn - K_l * K_mn / K) a_4_3_3 *= K a_5_3 *= K return a_2_1, a_prime_2_1, a_3_2, \ a_3_3_1, a_3_3_2, \ a_4_3_1, a_4_3_2, a_4_3_3, \ a_5_3, g_2_2, g_3_2_1, g_3_2_2
J_0 = pd.Series(data = np.zeros(shallow.size), index = shallow) K_0 = pd.Series(data = np.zeros(shallow.size), index = shallow) J_T = pd.Series(data = np.zeros(deep.size), index = deep) K_T = pd.Series(data = np.zeros(deep.size), index = deep) zero_frame = pd.DataFrame(data = np.zeros((shallow.size,deep.size)), index = shallow, columns = deep) U_int_1D = zero_frame.copy(deep = True) U_int_2D = zero_frame.copy(deep = True) phi_opt_1D = zero_frame.copy(deep = True) phi_opt_2D = zero_frame.copy(deep = True) for depth in shallow: momenta, fourier_vecs, energies = mathieu_solution(depth, bands, site_number) J_0.at[depth] = tunneling_1D(depth, momenta, fourier_vecs) K_0.at[depth] = pair_overlap_1D(momenta, fourier_vecs) for depth in deep: momenta, fourier_vecs, energies = mathieu_solution(depth, bands, site_number) J_T.at[depth] = tunneling_1D(depth, momenta, fourier_vecs) K_T.at[depth] = pair_overlap_1D(momenta, fourier_vecs) def h_std(dim,V_0,phi): return 2**(1+dim/2) * J_0.at[V_0] * np.sin(phi/2) for V_0, V_T in itertools.product(shallow, deep): U_int_1D.at[V_0,V_T] = g_int_LU[1] * K_T.at[V_T]**2 * K_0.at[V_0] U_int_2D.at[V_0,V_T] = g_int_LU[1] * K_T.at[V_T] * K_0.at[V_0]**2 def minimization_func(dim,U_int,x): return abs(h_std(dim,V_0,x)/U_int.at[V_0,V_T]/h_U_target-1)
excited_lifetime_SI = 10 # seconds; lifetime of excited state (from e --> g decay) ########################################################################################## # compute all experimental parameters ########################################################################################## N = np.product(L) eta = N / np.product(L) lattice_dim = np.array(L, ndmin=1).size print("D:", np.size(L)) print("N:", N) print() # determine primary lattice depth which optimally satisfies our target U_int / J_0 momenta, fourier_vecs, soc_energies = mathieu_solution(confining_depth, 1, site_number) K_T = pair_overlap_1D(momenta, fourier_vecs) J_T = tunneling_1D(confining_depth, momenta, fourier_vecs) def U_J(depth): momenta, fourier_vecs, _ = mathieu_solution(depth, 1, site_number) J_0 = tunneling_1D(depth, momenta, fourier_vecs) K_0 = pair_overlap_1D(momenta, fourier_vecs) U = g_int_LU[1] * K_0**lattice_dim * K_T**(3 - lattice_dim) return U / J_0 lattice_depth = minimize_scalar(lambda x: abs(U_J(x) - U_J_target), method="bounded", bounds=lattice_depth_bounds).x
[eigenvalues_4_3_C, 4, e_4_3_C]]: for aa in range(len(atom_numbers)): conversion = np.array(convert_eigenvalues( num, atom_numbers[aa])).astype(float) vals[aa, :] = (conversion @ e_X).T # compute wavefunction broadening estimates scale_factors = np.zeros((len(atom_numbers), len(lattice_depths), 3)) for dd in range(len(lattice_depths)): lattice_depth = lattice_depths[dd] print() print("lattice_depth:", lattice_depth) print("-" * 80) print() momenta, fourier_vecs, energies = mathieu_solution(lattice_depth, max_bands, site_number) kinetic_energy = kinetic_overlap_1D(momenta, fourier_vecs) pair_overlap = pair_overlap_1D(momenta, fourier_vecs) overlaps = energy_correction_coefficients(lattice_depth * np.ones(3), site_number) a_2_1, a_prime_2_1, a_3_2, a_3_3_1, a_3_3_2, a_4_3_1, a_4_3_2, a_4_3_3, a_5_3 \ = overlaps[:8] for aa in range(len(atom_numbers)): atom_number = atom_numbers[aa] print("atom number:", atom_number) # TODO: incorporate effect of renormalization and momentum-dependent coupling multi_body_energies = (