def _hamiltonian_expected(cells, periodic, sites_per_cell, freedom): """Expected Hamiltonian for the simple 1D lattice model""" # Within the cell, adjacent sites hop to each other, and it's always # non-periodic. cell = qutip.qdiags([-np.ones(sites_per_cell - 1)]*2, [1, -1]) # To hop to the cell one to the left, then you have to drop from the lowest # in-cell location to the highest in the other. hop_left = qutip.qdiags(np.ones(cells - 1), 1) if periodic and cells > 2: # If cells <= 2 then all cells border each other anyway. hop_left += qutip.qdiags([1], -(cells - 1)) drop_site = -qutip.projection(sites_per_cell, sites_per_cell-1, 0) if np.prod(freedom) != 1: # Degenerate degrees of freedom per lattice site are unchanged. identity = qutip.qeye(freedom) cell = qutip.tensor(cell, identity) drop_site = qutip.tensor(drop_site, identity) out = (qutip.tensor(qutip.qeye(cells), cell) + qutip.tensor(hop_left, drop_site) + qutip.tensor(hop_left, drop_site).dag()) # Contract scalar spaces. dims = [x for x in [cells, sites_per_cell] + freedom if x != 1] dims = dims or [1] out.dims = [dims, dims] return out
def du_dphi(self, time: float, phase: float) -> qutip.Qobj: """du_dphi(time: float in s, phase: float in rad) -> operator Get the matrix form of the partial derivative of the time-evolution operator with respect to phase.""" self.__update_if_required(time, phase) eg = -1j*self.__eg_pre*self.__sin[:self.__off_diag_len]*self.__phase_tot return qutip.tensor(_proj_eg, qutip.qdiags(eg, -self.order))\ + qutip.tensor(_proj_ge, qutip.qdiags(-np.conj(eg), self.order))
def split_diagonal_hamiltonian(H0, n_blocks): """Split a diagonal Hamiltonian into blocks""" diag_data = H0.diag() assert (H0 - qdiags(diag_data, 0)).data.nnz == 0 res = [] for i in range(n_blocks): res.append(diag_data.copy()) for j in range(n_blocks): if i == j: continue res[-1][split_list(range(len(diag_data)), n_blocks)[j]] = 0 res[-1] = qdiags(res[-1], 0) return res
def test_simdiag_degen(): N = 10 U = qutip.rand_unitary(N) commuting_matrices = [ U * qutip.qdiags([0, 0, 0, 1, 1, 1, 2, 2, 3, 4], 0) * U.dag(), U * qutip.qdiags([0, 0, 0, 1, 2, 2, 2, 2, 2, 2], 0) * U.dag(), U * qutip.qdiags([0, 0, 2, 1, 1, 2, 2, 3, 3, 4], 0) * U.dag(), ] all_evals, evecs = qutip.simdiag(commuting_matrices) for matrix, evals in zip(commuting_matrices, all_evals): for eval, evec in zip(evals, evecs): np.testing.assert_allclose((matrix * evec).full(), (evec * eval).full())
def _crow_lattice(coupling, phase_delay, cells=4, boundary="periodic", sites_per_cell=1, freedom=[2]): r""" Return a `qutip.Lattice1d` of a "Coupled Resonator Optical Waveguide" (CROW) with the given coupling strength and phase delay. See for example: https://www.doi.org/10.1103/PhysRevB.99.224201 where `coupling` is $J$ and `phase_delay` is $\eta$. """ cell_hamiltonian = coupling * np.sin(phase_delay) * qutip.sigmax() phase_term = np.exp(1j * phase_delay) hopping = [ 0.5 * coupling * qutip.qdiags([phase_term, phase_term.conj()], 0), 0.5 * coupling * qutip.sigmax(), ] return qutip.Lattice1d(num_cell=cells, boundary=boundary, cell_num_site=sites_per_cell, cell_site_dof=freedom, Hamiltonian_of_cell=cell_hamiltonian, inter_hop=hopping)
def du_dt(self, time: float, phase: float) -> qutip.Qobj: """du_dt(time : float in s, phase : float in rad) -> operator Get the matrix form of the partial derivative of the time-evolution operator with respect to time.""" self.__update_if_required(time, phase) ee = self.__ee_du_dt_pre * self.__phase_time * self.__sin eg = 0.5 * self.__eg_pre * self.__phase_tot * (\ self.__rabi_mod * self.__cos - 1j * self.detuning * self.__sin\ )[:self.__off_diag_len] self.__diag[self.__const_ind[0]:self.__const_ind[1]] = 0.0 self.__diag[self.__ee_ind[0]:self.__ee_ind[1]] =\ ee[:self.__ee_ind[1] - self.__ee_ind[0]] self.__diag[self.__gg_ind[0]:self.__gg_ind[1]] =\ np.conj(ee[:self.__gg_ind[1] - self.__gg_ind[0]]) return qutip.tensor(_proj_ee, qutip.qdiags(self.__diag[0:self.ns], 0))\ + qutip.tensor(_proj_gg, qutip.qdiags(self.__diag[self.ns:], 0))\ + qutip.tensor(_proj_eg, qutip.qdiags(eg, -self.order))\ + qutip.tensor(_proj_ge, qutip.qdiags(-np.conj(eg), self.order))
def u(self, time: float, phase: float) -> qutip.Qobj: """u(time: float in s, phase: float in rad) -> operator Get the matrix form of the time-evolution operator corresponding to this transition. The result is ordered such that it should be applied to a vector [|e0>, |e1>, ..., |g0>, |g1>, ...].""" self.__update_if_required(time, phase) ee = self.__cos + 1j * self.detuning * self.__sin / self.__rabi_mod ee = ee * self.__phase_time self.__diag[self.__const_ind[0]:self.__const_ind[1]] = 1.0 self.__diag[self.__ee_ind[0]:self.__ee_ind[1]] =\ ee[:self.__ee_ind[1] - self.__ee_ind[0]] self.__diag[self.__gg_ind[0]:self.__gg_ind[1]] =\ np.conj(ee[:self.__gg_ind[1] - self.__gg_ind[0]]) eg = self.__sin[:self.__off_diag_len] * self.__eg_pre * self.__phase_tot ge = -np.conj(eg) return qutip.tensor(_proj_ee, qutip.qdiags(self.__diag[:self.ns], 0))\ + qutip.tensor(_proj_gg, qutip.qdiags(self.__diag[self.ns:], 0))\ + qutip.tensor(_proj_eg, qutip.qdiags(eg, -self.order))\ + qutip.tensor(_proj_ge, qutip.qdiags(ge, self.order))
def test_simdiag_degen_large(): N = 20 U = qutip.rand_unitary(N) commuting_matrices = [ U * qutip.qdiags(np.random.randint(0, 3, N), 0) * U.dag() for _ in range(5) ] all_evals, evecs = qutip.simdiag(commuting_matrices, tol=1e-12) for matrix, evals in zip(commuting_matrices, all_evals): for eval, evec in zip(evals, evecs): np.testing.assert_allclose((matrix * evec).full(), (evec * eval).full())
def test_simdiag(num_mat): N = 10 U = qutip.rand_unitary(N) commuting_matrices = [ U * qutip.qdiags(np.random.rand(N), 0) * U.dag() for _ in range(num_mat) ] all_evals, evecs = qutip.simdiag(commuting_matrices) for matrix, evals in zip(commuting_matrices, all_evals): for eval, evec in zip(evals, evecs): assert matrix * evec == evec * eval
def test_simdiag_no_evals(num_mat): N = 10 U = qutip.rand_unitary(N) commuting_matrices = [ U * qutip.qdiags(np.random.rand(N), 0) * U.dag() for _ in range(num_mat) ] evecs = qutip.simdiag(commuting_matrices, evals=False) for matrix in commuting_matrices: for evec in evecs: Mvec = matrix * evec eval = Mvec.norm() / evec.norm() assert matrix * evec == evec * eval
def build_pot_op_dft(dim, sym_op): N = 2 * dim - 1 # Number of points where the potential is evaluated. The reasoning behind this is that the exponential operator exp(i*n*x) is nonzero in our subspace for 2*dim-1 different values of n. Thus to characterize all frequencies we need 2*dim-1 different points. x = linspace(0, 2 * pi * (N - 1) / N, N) x[x > pi] = x[x > pi] - 2 * pi u = lambdify(sym_op.free_symbols, sym_op)(x) u_fft = rfft(u) E_n = qdiags(ones(dim - 1), -1) # Operator representation of exp(i*n*x) E_1 = E_n # Operator representing exp(i*x) for n in range(len(u_fft)): if n == 0: op = u_fft[n] * qeye(dim) else: op += u_fft[n] * E_n op += conj(u_fft[n]) * E_n.dag() E_n *= E_1 return op / N
def test_fixed_ssh(self): intra, inter = -0.5, -0.6 cells = 5 lattice = _ssh_lattice(intra, inter, cells=cells, sites_per_cell=2, freedom=[1]) Hin = qutip.Qobj([[0, intra], [intra, 0]]) Ht = qutip.Qobj([[0, 0], [inter, 0]]) D = qutip.qeye(cells) T = qutip.qdiags([np.ones(cells-1), [1]], [1, 1-cells]) hopping = qutip.tensor(T, Ht) expected_hamiltonian = qutip.tensor(D, Hin) + hopping + hopping.dag() assert lattice.Hamiltonian() == expected_hamiltonian test_k, test_energies = lattice.get_dispersion() band = np.array([0.35297281, 0.89185772, 1.1, 0.89185772, 0.35297281]) _assert_angles_close(test_k, _k_expected(cells), atol=1e-12) np.testing.assert_allclose(test_energies, np.array([-band, band]), atol=1e-8)
def hamiltonian_int_i(V): """ This function gives the Hamiltonian of 2 interacting electrons with interaction strength V. Parameters ---------- V: float The interaction strength between electrons in units of meV. Returns ------- H_site: Qobj This returns a 9 x 9 matrix representing a single interaction between electrons. """ V = float(V) return qdiags([ V, V / sqrt(2), V / sqrt(5), V / sqrt(2), V, V / sqrt(2), V / sqrt(5), V / sqrt(2), V ], 0)
def quantize_potential_dft(U, xmin = None, xmax = None): if xmin == None: xmin = -np.pi*np.ones(U.ndim) if xmax == None: xmax = np.pi*np.ones(U.ndim) UF = np.fft.fftn(U) N = U.shape s = [2*np.pi*(1-1/Nn)/(xmaxn-xminn) for Nn,xmaxn,xminn in zip(N,xmax,xmin)] theta = [-sn*xminn for sn,xminn in zip(s,xmin)] Nq = [int((Nn-1)/2) if Nn%2 else int(Nn/2) for Nn in N] Ep = [] for D in range(len(Nq)): Ep.append(qt.tensor([np.exp(1j*thetan)*qt.qdiags(np.ones(2*Nqn),-1) if D==d else qt.qeye(2*Nqn+1) for d, (thetan,Nqn) in enumerate(zip(theta,Nq))])) I = qt.tensor([qt.qeye(2*Nqn +1) for Nqn in Nq]) ops = [I for i in range(len(Nq))] k_last = np.zeros(np.shape(Nq), dtype=int) out = 0 for k in np.ndindex(*[Nqn+1 for Nqn in Nq]): k_diff = np.array(list(k))-k_last for d in range(len(Nq)): if not k_diff[d] == 0: if k[d] == 0: ops[d] = I else: ops[d] = Ep[d]*ops[d] if k[d] == N[d]/2: ops[d] = ops[d] / 2 # Because otherwise nyquist freq is counted double pm_iter = [[1] if ki == 0 else [1,-1] for ki in k] for dir in itertools.product(*pm_iter): ck = UF[tuple(np.array(list(dir))*k)]/np.prod(N) out += ck*np.prod([ops[d] if sgn == 1 else ops[d].dag() for d,sgn in enumerate(dir)]) k_last = np.array(list(k)) return out, Nq
def build_sin_charge(dim): op = 1j * (qdiags(ones(dim - 1), 1) - qdiags(ones(dim - 1), -1)) / 2 return op
def test_binary(self, p): dm = qutip.qdiags([p, 1 - p], 0) expected = 0 if p in [0, 1 ] else p * np.log2(p) + (1 - p) * np.log2(1 - p) assert abs(-qutip.entropy_vn(dm, 2) - expected) < 1e-12
def makeBlochOperators(gsEnergies, esEnergies, osc_strengths=1, T1_opt=-1, br_ratio=0, decay_rates=None, coh_decay_rates=0): """Construct a parameterised Hamiltonian and collapse operators for driven transitions between a ground state manifold with energy splittings @gsEnergies and excited state splittings. Returns in the format needed by the setAtomParameters function (H0, H1, Hlst) @esEnergies. Ground and excited states are coupled by a Hamiltonian H1 according to the values Args: osc_strengths: A detuning between ground and excited states is given y the hamiltonian H2. T1_opt: Decays from the excited state manifold to ground occur at the rate 1/T1_opt. br_ratio: Ratios for second order transitions to make osc_strengths (if not given explicity) Returns: H0, (Esym,H1), Hlst, c_opL """ Ngs = len(gsEnergies) Nes = len(esEnergies) Nstates = Ngs + Nes gsL = [q.basis(Nstates, n) for n in range(Ngs)] esL = [q.basis(Nstates, Ngs + n) for n in range(Nes)] H1 = None try: Nbrs = len(br_ratio) except TypeError: br_ratio = [k * br_ratio**k for k in range(4)] Nbrs = len(br_ratio) if not np.iterable(osc_strengths): strngth = osc_strengths osc_strengths = np.zeros((Ngs, Nes), dtype='O') osc_strengths[np.diag_indices(min(Ngs, Nes))] = 1. for k in range(Ngs): for i in range(Nes): df = int(abs(k - i)) if df == 0: # Like-to-like transition osc_strengths[k, i] = strngth if df < len(br_ratio) and df > 0: # Second order or further... osc_strengths[k, i] = strngth * br_ratio[df] # -------------------------------------------------------- # Add field interaction operators (oscillator strengths) Esym = sm.symbols('Ef', complex=True) for k, es in enumerate(esL): for i, gs in enumerate(gsL): term = Esym * toDense( es * gs.dag()) + sm.conjugate(Esym) * toDense(gs * es.dag()) if H1 is None: H1 = 0.5 * sm.sqrt(osc_strengths[i, k]) * term else: H1 += 0.5 * sm.sqrt(osc_strengths[i, k]) * term # ---------------------------------------------- # H0 Evolution H0 = q.zero_ket(Nstates) * q.zero_ket(Nstates).dag() Hdet = H0.copy() H0 = toDense(H0) for freq, ket in zip(gsEnergies, gsL): H0 += 2 * pi * -freq * toDense(ket * ket.dag()) for freq, ket in zip(esEnergies, esL): H0 += 2 * pi * -freq * toDense(ket * ket.dag()) Hdet = toDense( q.qdiags(np.hstack([np.zeros(Ngs), np.ones(Nes)]), offsets=0)) # ------------------------------------------------- # Decay. If decay_rate is not given, assume it scales # with oscillator strength. Otherwise, T1_opt does nothing gamma = 1. / T1_opt if decay_rates is None: decay_rates = [[gamma * osc_strengths[l][k] for k in range(len(esL))] for l in range(len(gsL))] c_opL = [] for k, es in enumerate(esL): for i, gs in enumerate(gsL): #c_opL.append(sm.sqrt(gamma*osc_strengths[l][k]) * toDense(gs * es.dag())) c_opL.append(sm.sqrt(decay_rates[i][k]) * toDense(gs * es.dag())) # T2s if coh_decay_rates != 0: if not isiterable(coh_decay_rates): rate = coh_decay_rates coh_decay_rates = [[rate for k in range(len(esL))] for i in range(len(gsL))] for k, es in enumerate(esL): for i, gs in enumerate(gsL): c_opL.append( sm.sqrt(coh_decay_rates[i][k]) * toDense(1j * gs * es.dag() - 1j * es * gs.dag())) # An attempt to compensate for 'edge' transitions having different rates if T1_opt != -1 and 0: gamma_tot = 1. / T1_opt # = gamma_1 + 2*br*gamma_1 # gamma_tot = gamma_1 = gamma_tot / (1 + 2 * br_ratio) gamma_2 = br_ratio * gamma_1 # Make collapse operators c_opL = [ sm.sqrt(gamma_1) * toDense(gs * es.dag()) for gs, es in zip(gsL, esL) ] c_opL += [ sm.sqrt(gamma_2) * toDense(gs * es.dag()) for gs, es in zip(gsL[1:], esL) ] c_opL += [ sm.sqrt(gamma_2) * toDense(gs * es.dag()) for gs, es in zip(gsL, esL[1:]) ] print("N c_ops: {}".format(len(c_opL))) print(c_opL) delt = sm.symbols('Delta', real=True) # Hlst=[ return H0, (Esym, H1), (delt, Hdet), c_opL
def test_known_cases(self): r1 = qdiags(np.array([0.5, 0.5, 0, 0]), 0) r2 = qdiags(np.array([0, 0, 0.5, 0.5]), 0) assert hilbert_dist(r1, r2) == pytest.approx(1, abs=1e-6)
def build_cos_charge(dim): op = (qdiags(ones(dim - 1), 1) + qdiags(ones(dim - 1), -1)) / 2 return op
_l_adc_system = 0.1 * (2*qutip.tensor(_sm, _sp.dag()) - qutip.tensor(_project_0, _si) - qutip.tensor(_si, _project_0.dag())) _l_adc_controls = [1j * (qutip.tensor(_si, _sz) - qutip.tensor(_sz, _si)), 1j * (qutip.tensor(_si, _sx) - qutip.tensor(_sx, _si))] _l_adc_kwargs = {'num_tslots': 10, 'evo_time': 5, 'init_pulse_type': 'LIN', 'max_iter': 200, 'fid_err_targ': 1e-1, 'gen_stats': True} l_adc = _System(system=_l_adc_system, controls=_l_adc_controls, initial=qutip.identity([2, 2]), target=hadamard_transform(2), kwargs=_l_adc_kwargs) # Two coupled oscillators with symplectic dynamics. _g1, _g2 = 1.0, 0.2 _A_rotate = qutip.qdiags([[1, 1, 0, 0]], [0]) _A_squeeze = 0.4 * qutip.qdiags([[1, -1, 0, 0]], [0]) _A_target = qutip.qdiags([[1, 1], [1, 1]], [2, -2]) _Omega = qutip.Qobj(qutip.control.symplectic.calc_omega(2)) _sympl_system = qutip.qdiags([[1, 1, 1, 1], [_g1, _g2], [_g1, _g2]], [0, 2, -2]) _sympl_target = (-0.5 * _A_target * _Omega * np.pi).expm() _sympl_kwargs = {'num_tslots': 20, 'evo_time': 10, 'fid_err_targ': 1e-3, 'max_iter': 200, 'dyn_type': 'SYMPL', 'init_pulse_type': 'ZERO', 'gen_stats': True} symplectic = _System(system=_sympl_system, controls=[_A_rotate, _A_squeeze], initial=qutip.identity(4), target=_sympl_target, kwargs=_sympl_kwargs)
def test_qdiags_type(): "Operator CSR Type: qdiags" op = qdiags(np.sqrt(range(1, 4)), 1) assert_equal(isspmatrix_csr(op.data), True)
ini_coeff = [0, 1e-9, 0, 1e-9, 1, 0, 0, 0, 0] # 11 ini_state = ini_coeff[0]*qt.tensor(ket(Nq,0), ket(Nq,0)) \ + ini_coeff[1]*qt.tensor(ket(Nq,0), ket(Nq,1)) \ + ini_coeff[2]*qt.tensor(ket(Nq,0), ket(Nq,2)) \ + ini_coeff[3]*qt.tensor(ket(Nq,1), ket(Nq,0)) \ + ini_coeff[4]*qt.tensor(ket(Nq,1), ket(Nq,1)) \ + ini_coeff[5]*qt.tensor(ket(Nq,1), ket(Nq,2)) \ + ini_coeff[6]*qt.tensor(ket(Nq,2), ket(Nq,0)) \ + ini_coeff[7]*qt.tensor(ket(Nq,2), ket(Nq,1)) \ + ini_coeff[8]*qt.tensor(ket(Nq,2), ket(Nq,2)) Iq1 = qt.qeye(Nq) Hq1_lab = QQ.Hq1 * (2 * pi) rot2 = Hq(Q2.Nq, 0, abs(Q2.anh * (2 * pi))) q2Freqs = qt.qdiags(np.arange(0, Q2.Nq, 1), 0) Hq2_t_ind = qt.tensor(Iq1, rot2) #Hq2_rot(constant term) Hq2_t_dep = qt.tensor(Iq1, q2Freqs) #Hq2_rot(modulation term) Hint = QQ.Hint12 * (2 * pi) H_rot = [Hq1_lab + Hq2_t_ind + Hint, [Hq2_t_dep, MW_shaped]] tgstart = 48 tgend = 48 for gt in range(tgstart, tgend + 1): pulsesystem = CZpulse(Q1, Q2, g, the_f=0.88, lambda2=0.13, gatetime=gt) tg = pulsesystem.tg t_list, adiabaticpulse = pulsesystem.netzeropulse() degeneracy_freq = pulsesystem.qFreq20 args = {'mwamp': 1.0, 'shape': adiabaticpulse}
multiplied by a unit complex number. """ vec = vec.flatten() nonzero = vec != 0 if not np.any(nonzero): return vec return vec / vec[np.argmax(nonzero)] # Random diagonal Hamiltonian. _diagonal_dimension = 10 _diagonal_eigenvalues = np.sort(np.random.rand(_diagonal_dimension)) _diagonal_eigenstates = np.array([[0] * n + [1] + [0] * (_diagonal_dimension - n - 1) for n in range(_diagonal_dimension)]) _diagonal_hamiltonian = qutip.qdiags(_diagonal_eigenvalues, 0) # Arbitrary known non-diagonal complex Hamiltonian. _nondiagonal_hamiltonian = qutip.Qobj( np.array([[0.16252356, 0.27696416 + 0.0405202j, 0.19577420 + 0.07815636j], [0.27696416 - 0.0405202j, 0.45859633, 0.36222915 + 0.17372725j], [0.19577420 - 0.07815636j, 0.36222915 - 0.17372725j, 0.44149665]])) _nondiagonal_eigenvalues = np.array( [-0.022062710138316392, 0.08888141616526818, 0.995797833973048]) _nondiagonal_eigenstates = np.array( [[ -0.737511505546763, 0.5270680510449308 - 0.29398599661318j, 0.009793118179759598 + 0.3029065489313791j ], [