def propagator_steadystate(U): """Find the steady state for successive applications of the propagator :math:`U`. Parameters ---------- U : qobj Operator representing the propagator. Returns ------- a : qobj Instance representing the steady-state density matrix. """ evals, evecs = la.eig(U.full()) ev_min, ev_idx = _get_min_and_index(abs(evals - 1.0)) evecs = evecs.T rho = Qobj(vec2mat(evecs[ev_idx]), dims=U.dims[0]) rho = rho * (1.0 / rho.tr()) rho = 0.5 * (rho + rho.dag()) # make sure rho is herm return rho
def test_QobjFull(): "Qobj full" data = np.random.random( (15, 15)) + 1j * np.random.random((15, 15)) - (0.5 + 0.5j) A = Qobj(data) b = A.full() assert_(np.all(b - data == 0))
def _init_ptrace_stuff(sel): psi0 = Qobj(config.psi0, dims=config.psi0_dims, shape=config.psi0_shape) qtf90.qutraj_run.init_ptrace_stuff(config.psi0_dims[0], np.array(sel) + 1, psi0.ptrace(sel).shape[0])
def test_QobjExpmExplicitlySparse(): "Qobj expm (sparse)" data = np.random.random( (15, 15)) + 1j * np.random.random((15, 15)) - (0.5 + 0.5j) A = Qobj(data) B = A.expm(method='sparse') assert_((B.data.todense() - np.matrix(la.expm(data)) < 1e-10).all())
def test_QobjHerm(): "Qobj Hermicity" N = 10 data = np.random.random( (N, N)) + 1j * np.random.random((N, N)) - (0.5 + 0.5j) q = Qobj(data) assert_equal(q.isherm, False) data = data + data.conj().T q = Qobj(data) assert_(q.isherm) q_a = destroy(5) assert_(not q_a.isherm) q_ad = create(5) assert_(not q_ad.isherm) # test addition of two nonhermitian operators adding up to a hermitian one q_x = q_a + q_ad assert_(q_x.isherm) # isherm use the _isherm cache from q_a + q_ad q_x._isherm = None # reset _isherm cache assert_(q_x.isherm) # recalculate _isherm # test addition of one hermitan and one nonhermitian operator q = q_x + q_a assert_(not q.isherm) q._isherm = None assert_(not q.isherm) # test addition of two hermitan operators q = q_x + q_x assert_(q.isherm) q._isherm = None assert_(q.isherm)
def _steadystate_svd_dense(L, ss_args): """ Find the steady state(s) of an open quantum system by solving for the nullspace of the Liouvillian. """ ss_args['info'].pop('weight', None) atol = 1e-12 rtol = 1e-12 if settings.debug: logger.debug('Starting SVD solver.') _svd_start = time.time() u, s, vh = svd(L.full(), full_matrices=False) tol = max(atol, rtol * s[0]) nnz = (s >= tol).sum() ns = vh[nnz:].conj().T _svd_end = time.time() ss_args['info']['solution_time'] = _svd_end-_svd_start if ss_args['all_states']: rhoss_list = [] for n in range(ns.shape[1]): rhoss = Qobj(vec2mat(ns[:, n]), dims=L.dims[0]) rhoss_list.append(rhoss / rhoss.tr()) if ss_args['return_info']: return rhoss_list, ss_args['info'] else: if ss_args['return_info']: return rhoss_list, ss_args['info'] else: return rhoss_list else: rhoss = Qobj(vec2mat(ns[:, 0]), dims=L.dims[0]) return rhoss / rhoss.tr()
def floquet_master_equation_tensor(Alist, f_energies): """ Construct a tensor that represents the master equation in the floquet basis (with constant Hamiltonian and collapse operators). Simplest RWA approximation [Grifoni et al, Phys.Rep. 304 229 (1998)] Parameters ---------- Alist : list A list of Floquet-Markov master equation rate matrices. f_energies : array The Floquet energies. Returns ------- output : array The Floquet-Markov master equation tensor `R`. """ if isinstance(Alist, list): # Alist can be a list of rate matrices corresponding # to different operators that couple to the environment N, M = np.shape(Alist[0]) else: # or a simple rate matrix, in which case we put it in a list Alist = [Alist] N, M = np.shape(Alist[0]) R = Qobj(scipy.sparse.csr_matrix((N * N, N * N)), [[N, N], [N, N]], [N * N, N * N]) R.data = R.data.tolil() for I in range(N * N): a, b = vec2mat_index(N, I) for J in range(N * N): c, d = vec2mat_index(N, J) R.data[I, J] = -1.0j * (f_energies[a] - f_energies[b]) * \ (a == c) * (b == d) for A in Alist: s1 = s2 = 0 for n in range(N): s1 += A[a, n] * (n == c) * (n == d) - A[n, a] * \ (a == c) * (a == d) s2 += (A[n, a] + A[n, b]) * (a == c) * (b == d) dR = (a == b) * s1 - 0.5 * (1 - (a == b)) * s2 if dR != 0.0: R.data[I, J] += dR R.data = R.data.tocsr() return R
def test_QobjExpm(): "Qobj expm (dense)" data = np.random.random( (15, 15)) + 1j * np.random.random((15, 15)) - (0.5 + 0.5j) A = Qobj(data) B = A.expm() assert_((B.data.todense() - np.matrix(la.expm(data)) < 1e-10).all())
def _steadystate_eigen(L, ss_args): """ Internal function for solving the steady state problem by finding the eigenvector corresponding to the zero eigenvalue of the Liouvillian using ARPACK. """ if settings.debug: print('Starting Eigen solver...') dims = L.dims[0] shape = prod(dims[0]) L = L.data.tocsc() if ss_args['use_rcm']: if settings.debug: old_band = sp_bandwidth(L)[0] print('Original bandwidth:', old_band) perm = reverse_cuthill_mckee(L) rev_perm = np.argsort(perm) L = sp_permute(L, perm, perm, 'csc') if settings.debug: rcm_band = sp_bandwidth(L)[0] print('RCM bandwidth:', rcm_band) print('Bandwidth reduction factor:', round(old_band/rcm_band, 1)) eigval, eigvec = eigs(L, k=1, sigma=1e-15, tol=ss_args['tol'], which='LM', maxiter=ss_args['maxiter']) if ss_args['use_rcm']: eigvec = eigvec[np.ix_(rev_perm,)] data = vec2mat(eigvec) data = 0.5 * (data + data.conj().T) out = Qobj(data, dims=dims, isherm=True) return out/out.tr()
def _steadystate_svd_dense(L, atol=1e-12, rtol=0, all_steadystates=False, verbose=False): """ Find the steady state(s) of an open quantum system by solving for the nullspace of the Liouvillian. """ if verbose: print('Starting SVD solver...') start_time = time.time() u, s, vh = svd(L.full(), full_matrices=False) tol = max(atol, rtol * s[0]) nnz = (s >= tol).sum() ns = vh[nnz:].conj().T if verbose: print('SVD solver time: ', time.time() - start_time) if all_steadystates: rhoss_list = [] for n in range(ns.shape[1]): rhoss = Qobj(vec2mat(ns[:, n]), dims=L.dims[0]) rhoss_list.append(rhoss / rhoss.tr()) return rhoss_list else: rhoss = Qobj(vec2mat(ns[:, 0]), dims=L.dims[0]) return rhoss / rhoss.tr()
def test_QobjDiagonals(): "Qobj diagonals" data = np.random.random( (5, 5)) + 1j * np.random.random((5, 5)) - (0.5 + 0.5j) A = Qobj(data) b = A.diag() assert_(np.all(b - np.diag(data) == 0))
def test_Qobj_sqrtm(): "Qobj sqrtm" data = np.random.random( (5, 5)) + 1j * np.random.random((5, 5)) - (0.5 + 0.5j) A = Qobj(data) B = A.sqrtm() assert_(A == B * B)
def _steadystate_svd_dense(L, ss_args): """ Find the steady state(s) of an open quantum system by solving for the nullspace of the Liouvillian. """ atol = 1e-12 rtol = 1e-12 if settings.debug: print('Starting SVD solver...') u, s, vh = svd(L.full(), full_matrices=False) tol = max(atol, rtol * s[0]) nnz = (s >= tol).sum() ns = vh[nnz:].conj().T if ss_args['all_states']: rhoss_list = [] for n in range(ns.shape[1]): rhoss = Qobj(vec2mat(ns[:, n]), dims=L.dims[0]) rhoss_list.append(rhoss / rhoss.tr()) return rhoss_list else: rhoss = Qobj(vec2mat(ns[:, 0]), dims=L.dims[0]) return rhoss / rhoss.tr()
def test_QobjMulNonsquareDims(): """ Qobj: multiplication w/ non-square qobj.dims Checks for regression of #331. """ data = np.array([[0, 1], [1, 0]]) q1 = Qobj(data) q1.dims[0].append(1) q2 = Qobj(data) assert_equal((q1 * q2).dims, [[2, 1], [2]]) assert_equal((q2 * q1.dag()).dims, [[2], [2, 1]]) # Note that this is [[2], [2]] instead of [[2, 1], [2, 1]], # as matching dimensions of 1 are implicitly partial traced out. # (See #331.) assert_equal((q1 * q2 * q1.dag()).dims, [[2], [2]]) # Because of the above, we also need to check for extra indices # that aren't of length 1. q1 = Qobj([[ 1.+0.j, 0.+0.j], [ 0.+0.j, 1.+0.j], [ 0.+0.j, 1.+0.j], [ 1.+0.j, 0.+0.j], [ 0.+0.j, 0.-1.j], [ 0.+1.j, 0.+0.j], [ 1.+0.j, 0.+0.j], [ 0.+0.j, -1.+0.j] ], dims=[[4, 2], [2]]) assert_equal((q1 * q2 * q1.dag()).dims, [[4, 2], [4, 2]])
def rand_ket(N, density=1, dims=None): """Creates a random Nx1 sparse ket vector. Parameters ---------- N : int Number of rows for output quantum operator. density : float Density between [0,1] of output ket state. dims : list Left-dimensions of quantum object. Used for specifying tensor structure. Default is dims=[[N]]. Returns ------- oper : qobj Nx1 ket state quantum operator. """ if dims: _check_ket_dims(dims, N) X = sp.rand(N, 1, density, format="csr") X.data = X.data - 0.5 Y = X.copy() Y.data = 1.0j * (np.random.random(len(X.data)) - 0.5) X = X + Y X.sort_indices() X = Qobj(X) if dims: return Qobj(X / X.norm(), dims=[dims, [1]], shape=[N, 1]) else: return Qobj(X / X.norm())
def operator_to_vector(op): """ Create a vector representation of a quantum operator given the matrix representation. """ q = Qobj() q.dims = [op.dims, [1]] q.data = sp_reshape(op.data.T, (np.prod(op.shape), 1)) return q
def vector_to_operator(op): """ Create a matrix representation given a quantum operator in vector form. """ q = Qobj() q.dims = op.dims[0] q.data = sp_reshape(op.data.T, q.shape).T return q
def test_QobjDagger(): "Qobj adjoint (dagger)" data = np.random.random((5, 5)) + 1j * np.random.random((5, 5)) - (0.5 + 0.5j) A = Qobj(data) B = A.dag() assert_(np.all(B.data.todense() - np.matrix(data.conj().T) == 0)) assert_equal(A.isherm, B.isherm) assert_equal(A.type, B.type) assert_equal(A.superrep, B.superrep)
def test_QobjConjugate(): "Qobj conjugate" data = np.random.random((5, 5)) + 1j * np.random.random((5, 5)) - (0.5 + 0.5j) A = Qobj(data) B = A.conj() assert_(np.all(B.data.todense() - np.matrix(data.conj()) == 0)) assert_equal(A.isherm, B.isherm) assert_equal(A.type, B.type) assert_equal(A.superrep, B.superrep)
def vector_to_operator(op): """ Create a matrix representation given a quantum operator in vector form. """ q = Qobj() q.dims = op.dims[0] n = int(np.sqrt(op.shape[0])) q.data = sp_reshape(op.data.T, (n, n)).T return q
def test_QobjEigenStates(): "Qobj eigenstates" data = np.eye(5) A = Qobj(data) b, c = A.eigenstates() assert_(np.all(b - np.ones(5) == 0)) kets = [basis(5, k) for k in range(5)] for k in range(5): assert_(c[k] == kets[k])
def rand_unitary_haar(N=2, dims=None): """ Returns a Haar random unitary matrix of dimension ``dim``, using the algorithm of [Mez07]_. Parameters ---------- N : int Dimension of the unitary to be returned. dims : list of lists of int, or None Dimensions of quantum object. Used for specifying tensor structure. Default is dims=[[N],[N]]. Returns ------- U : Qobj Unitary of dims ``[[dim], [dim]]`` drawn from the Haar measure. """ if dims is not None: _check_dims(dims, N, N) else: dims = [[N], [N]] # Mez01 STEP 1: Generate an N × N matrix Z of complex standard # normal random variates. Z = randnz((N, N)) # Mez01 STEP 2: Find a QR decomposition Z = Q · R. Q, R = la.qr(Z) # Mez01 STEP 3: Create a diagonal matrix Lambda by rescaling # the diagonal elements of R. Lambda = np.diag(R).copy() Lambda /= np.abs(Lambda) # Mez01 STEP 4: Note that R' := Λ¯¹ · R has real and # strictly positive elements, such that # Q' = Q · Λ is Haar distributed. # NOTE: Λ is a diagonal matrix, represented as a vector # of the diagonal entries. Thus, the matrix dot product # is represented nicely by the NumPy broadcasting of # the *scalar* multiplication. In particular, # Q · Λ = Q_ij Λ_jk = Q_ij δ_jk λ_k = Q_ij λ_j. # As NumPy arrays, Q has shape (N, N) and # Lambda has shape (N, ), such that the broadcasting # represents precisely Q_ij λ_j. U = Qobj(Q * Lambda) U.dims = dims return U
def steadystate_nonlinear(L_func, rho0, args={}, maxiter=10, random_initial_state=False, tol=1e-6, itertol=1e-5, use_umfpack=True, verbose=False): """ Steady state for the evolution subject to the nonlinear Liouvillian (which depends on the density matrix). .. note:: Experimental. Not at all certain that the inverse power method works for state-dependent Liouvillian operators. """ use_solver(assumeSortedIndices=True, useUmfpack=use_umfpack) if random_initial_state: rhoss = rand_dm(rho0.shape[0], 1.0, dims=rho0.dims) elif isket(rho0): rhoss = ket2dm(rho0) else: rhoss = Qobj(rho0) v = mat2vec(rhoss.full()) n = prod(rhoss.shape) tr_vec = sp.eye(rhoss.shape[0], rhoss.shape[0], format='coo') tr_vec = tr_vec.reshape((1, n)) it = 0 while it < maxiter: L = L_func(rhoss, args) L = L.data.tocsc() - (tol ** 2) * sp.eye(n, n, format='csc') L.sort_indices() v = spsolve(L, v, use_umfpack=use_umfpack) v = v / la.norm(v, np.inf) data = v / sum(tr_vec.dot(v)) data = reshape(data, (rhoss.shape[0], rhoss.shape[1])).T rhoss.data = sp.csr_matrix(data) it += 1 if la.norm(L * v, np.inf) <= tol: break if it >= maxiter: raise ValueError('Failed to find steady state after ' + str(maxiter) + ' iterations') rhoss = 0.5 * (rhoss + rhoss.dag()) return rhoss.tidyup() if qset.auto_tidyup else rhoss
def test_QobjEigenEnergies(): "Qobj eigenenergies" data = np.eye(5) A = Qobj(data) b = A.eigenenergies() assert_(np.all(b - np.ones(5) == 0)) data = np.diag(np.arange(10)) A = Qobj(data) b = A.eigenenergies() assert_(np.all(b - np.arange(10) == 0)) data = np.diag(np.arange(10)) A = 5 * Qobj(data) b = A.eigenenergies() assert_(np.all(b - 5 * np.arange(10) == 0))
def _steadystate_eigen(L, ss_args): """ Internal function for solving the steady state problem by finding the eigenvector corresponding to the zero eigenvalue of the Liouvillian using ARPACK. """ ss_args['info'].pop('weight', None) if settings.debug: logger.debug('Starting Eigen solver.') dims = L.dims[0] L = L.data.tocsc() if ss_args['use_rcm']: ss_args['info']['perm'].append('rcm') if settings.debug: old_band = sp_bandwidth(L)[0] logger.debug('Original bandwidth: %i' % old_band) perm = reverse_cuthill_mckee(L) rev_perm = np.argsort(perm) L = sp_permute(L, perm, perm, 'csc') if settings.debug: rcm_band = sp_bandwidth(L)[0] logger.debug('RCM bandwidth: %i' % rcm_band) logger.debug('Bandwidth reduction factor: %f' % (old_band/rcm_band)) _eigen_start = time.time() eigval, eigvec = eigs(L, k=1, sigma=1e-15, tol=ss_args['tol'], which='LM', maxiter=ss_args['maxiter']) _eigen_end = time.time() ss_args['info']['solution_time'] = _eigen_end - _eigen_start if ss_args['return_info']: ss_args['info']['residual_norm'] = la.norm(L*eigvec, np.inf) if ss_args['use_rcm']: eigvec = eigvec[np.ix_(rev_perm,)] _temp = vec2mat(eigvec) data = dense2D_to_fastcsr_fmode(_temp, _temp.shape[0], _temp.shape[1]) data = 0.5 * (data + data.H) out = Qobj(data, dims=dims, isherm=True) if ss_args['return_info']: return out/out.tr(), ss_args['info'] else: return out/out.tr()
def _steadystate_power(L, ss_args): """ Inverse power method for steady state solving. """ if settings.debug: print('Starting iterative power method Solver...') tol=ss_args['tol'] maxiter=ss_args['maxiter'] use_solver(assumeSortedIndices=True) rhoss = Qobj() sflag = issuper(L) if sflag: rhoss.dims = L.dims[0] else: rhoss.dims = [L.dims[0], 1] n = prod(rhoss.shape) L = L.data.tocsc() - (tol ** 2) * sp.eye(n, n, format='csc') L.sort_indices() v = mat2vec(rand_dm(rhoss.shape[0], 0.5 / rhoss.shape[0] + 0.5).full()) it = 0 while (la.norm(L * v, np.inf) > tol) and (it < maxiter): v = spsolve(L, v) v = v / la.norm(v, np.inf) it += 1 if it >= maxiter: raise Exception('Failed to find steady state after ' + str(maxiter) + ' iterations') # normalise according to type of problem if sflag: trow = sp.eye(rhoss.shape[0], rhoss.shape[0], format='coo') trow = sp_reshape(trow, (1, n)) data = v / sum(trow.dot(v)) else: data = data / la.norm(v) data = sp.csr_matrix(vec2mat(data)) rhoss.data = 0.5 * (data + data.conj().T) rhoss.isherm = True return rhoss
def rand_herm(N, density=0.75, dims=None): """Creates a random NxN sparse Hermitian quantum object. Uses :math:`H=X+X^{+}` where :math:`X` is a randomly generated quantum operator with a given `density`. Parameters ---------- N : int Shape of output quantum operator. density : float Density between [0,1] of output Hermitian operator. dims : list Dimensions of quantum object. Used for specifying tensor structure. Default is dims=[[N],[N]]. Returns ------- oper : qobj NxN Hermitian quantum operator. """ if dims: _check_dims(dims, N, N) # to get appropriate density of output # Hermitian operator must convert via: herm_density = 2.0 * arcsin(density) / pi X = sp.rand(N, N, herm_density, format='csr') X.data = X.data - 0.5 Y = X.copy() Y.data = 1.0j * np.random.random(len(X.data)) - (0.5 + 0.5j) X = X + Y X.sort_indices() X = Qobj(X) if dims: return Qobj((X + X.dag()) / 2.0, dims=dims, shape=[N, N]) else: return Qobj((X + X.dag()) / 2.0)
def spre(A): """Superoperator formed from pre-multiplication by operator A. Parameters ---------- A : qobj Quantum operator for pre-multiplication. Returns -------- super :qobj Superoperator formed from input quantum object. """ if not isinstance(A, Qobj): raise TypeError('Input is not a quantum object') if not A.isoper: raise TypeError('Input is not a quantum operator') S = Qobj(isherm=A.isherm, superrep='super') S.dims = [[A.dims[0], A.dims[1]], [A.dims[0], A.dims[1]]] S.data = sp.kron(sp.identity(np.prod(A.shape[1])), A.data, format='csr') return S
def _steadystate_power(L, maxiter=10, tol=1e-6, itertol=1e-5, verbose=False): """ Inverse power method for steady state solving. """ if verbose: print('Starting iterative power method Solver...') use_solver(assumeSortedIndices=True) rhoss = Qobj() sflag = issuper(L) if sflag: rhoss.dims = L.dims[0] rhoss.shape = [prod(rhoss.dims[0]), prod(rhoss.dims[1])] else: rhoss.dims = [L.dims[0], 1] rhoss.shape = [prod(rhoss.dims[0]), 1] n = prod(rhoss.shape) L = L.data.tocsc() - (tol ** 2) * sp.eye(n, n, format='csc') L.sort_indices() v = mat2vec(rand_dm(rhoss.shape[0], 0.5 / rhoss.shape[0] + 0.5).full()) if verbose: start_time = time.time() it = 0 while (la.norm(L * v, np.inf) > tol) and (it < maxiter): v = spsolve(L, v) v = v / la.norm(v, np.inf) it += 1 if it >= maxiter: raise Exception('Failed to find steady state after ' + str(maxiter) + ' iterations') # normalise according to type of problem if sflag: trow = sp.eye(rhoss.shape[0], rhoss.shape[0], format='coo') trow = sp_reshape(trow, (1, n)) data = v / sum(trow.dot(v)) else: data = data / la.norm(v) data = sp.csr_matrix(vec2mat(data)) rhoss.data = 0.5 * (data + data.conj().T) rhoss.isherm = True if verbose: print('Power solver time: ', time.time() - start_time) if qset.auto_tidyup: return rhoss.tidyup() else: return rhoss
def spost(A): """Superoperator formed from post-multiplication by operator A Parameters ---------- A : qobj Quantum operator for post multiplication. Returns ------- super : qobj Superoperator formed from input qauntum object. """ if not isinstance(A, Qobj): raise TypeError('Input is not a quantum object') if not A.isoper: raise TypeError('Input is not a quantum operator') S = Qobj(isherm=A.isherm, superrep='super') S.dims = [[A.dims[0], A.dims[1]], [A.dims[0], A.dims[1]]] S.data = zcsr_kron(A.data.T, fast_identity(np.prod(A.shape[0]))) return S
def sprepost(A, B): """Superoperator formed from pre-multiplication by operator A and post- multiplication of operator B. Parameters ---------- A : Qobj Quantum operator for pre-multiplication. B : Qobj Quantum operator for post-multiplication. Returns -------- super : Qobj Superoperator formed from input quantum objects. """ dims = [[_drop_projected_dims(A.dims[0]), _drop_projected_dims(B.dims[1])], [_drop_projected_dims(A.dims[1]), _drop_projected_dims(B.dims[0])]] data = zcsr_kron(B.data.T, A.data) return Qobj(data, dims=dims, superrep='super')
def fredkin(N=None, control=0, targets=[1, 2]): """Quantum object representing the Fredkin gate. Returns ------- fredkin_gate : qobj Quantum object representation of Fredkin gate. Examples -------- >>> fredkin() # doctest: +SKIP Quantum object: dims = [[2, 2, 2], [2, 2, 2]], \ shape = [8, 8], type = oper, isHerm = True Qobj data = [[ 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j]] """ if [control, targets[0], targets[1]] != [0, 1, 2] and N is None: N = 3 if N is not None: return gate_expand_3toN(fredkin(), N, [control, targets[0]], targets[1]) return Qobj([[1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1]], dims=[[2, 2, 2], [2, 2, 2]])
def toffoli(N=None, controls=[0, 1], target=2): """Quantum object representing the Toffoli gate. Returns ------- toff_gate : qobj Quantum object representation of Toffoli gate. Examples -------- >>> toffoli() # doctest: +SKIP Quantum object: dims = [[2, 2, 2], [2, 2, 2]], \ shape = [8, 8], type = oper, isHerm = True Qobj data = [[ 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j] [ 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j]] """ if [controls[0], controls[1], target] != [0, 1, 2] and N is None: N = 3 if N is not None: return gate_expand_3toN(toffoli(), N, controls, target) return Qobj([[1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 1, 0]], dims=[[2, 2, 2], [2, 2, 2]])
def snot(N=None, target=0): """Quantum object representing the SNOT (Hadamard) gate. Returns ------- snot_gate : qobj Quantum object representation of SNOT gate. Examples -------- >>> snot() Quantum object: dims = [[2], [2]], \ shape = [2, 2], type = oper, isHerm = True Qobj data = [[ 0.70710678+0.j 0.70710678+0.j] [ 0.70710678+0.j -0.70710678+0.j]] """ if N is not None: return gate_expand_1toN(snot(), N, target) else: return 1 / np.sqrt(2.0) * Qobj([[1, 1], [1, -1]])
def chi_to_choi(q_oper): """ Converts a Choi matrix to a Chi matrix in the Pauli basis. NOTE: this is only supported for qubits right now. Need to extend to Heisenberg-Weyl for other subsystem dimensions. """ nq = _nq(q_oper.dims) B = _pauli_basis(nq) # Force the basis change to match the dimensions of # the input. B.dims = q_oper.dims # We normally should not multiply objects of different # superreps, so Qobj warns about that. Here, however, we're actively # converting between, so the superrep of B is irrelevant. # To suppress warnings, we pretend that B is also a chi. B.superrep = 'chi' # The Chi matrix has tr(chi) == d², so we need to divide out # by that to get back to the Choi form. return Qobj((B * q_oper * B.dag()) / q_oper.shape[0], superrep='choi')
def _add_common_result_attribs(self, result, st_time, end_time): """ Update the result object attributes which are common to all optimisers and outcomes """ dyn = self.dynamics result.num_iter = self.num_iter result.num_fid_func_calls = self.num_fid_func_calls result.wall_time = end_time - st_time result.fid_err = dyn.fid_computer.get_fid_err() result.grad_norm_final = dyn.fid_computer.grad_norm result.final_amps = dyn.ctrl_amps final_evo = dyn.full_evo if isinstance(final_evo, Qobj): result.evo_full_final = final_evo else: result.evo_full_final = Qobj(final_evo, dims=dyn.sys_dims) # *** update stats *** if self.stats is not None: self.stats.wall_time_optim_end = end_time self.stats.calculate() result.stats = copy.copy(self.stats)
def get_states(self, nstep, ntraj): if debug: print(inspect.stack()[0][3]) from scipy.sparse import csr_matrix if (odeconfig.options.average_states): states = np.array([Qobj()] * nstep) if (self.sparse_dms): # averaged sparse density matrices for i in range(nstep): qtf90.qutraj_run.get_rho_sparse(i + 1) val = qtf90.qutraj_run.csr_val col = qtf90.qutraj_run.csr_col - 1 ptr = qtf90.qutraj_run.csr_ptr - 1 m = qtf90.qutraj_run.csr_nrows k = qtf90.qutraj_run.csr_ncols states[i] = Qobj(csr_matrix((val, col, ptr), (m, k)).toarray(), dims=self.dm_dims, shape=self.dm_shape) else: # averaged dense density matrices for i in range(nstep): states[i] = Qobj(qtf90.qutraj_run.sol[0, i, :, :], dims=self.dm_dims, shape=self.dm_shape) else: # all trajectories as kets if (ntraj == 1): states = np.array([Qobj()] * nstep) for i in range(nstep): states[i] = Qobj(numpy.matrix( qtf90.qutraj_run.sol[0, 0, i, :]).transpose(), dims=self.psi0_dims, shape=self.psi0_shape) else: states = np.array([np.array([Qobj()] * nstep)] * ntraj) for traj in range(ntraj): for i in range(nstep): states[traj][i] = Qobj(numpy.matrix( qtf90.qutraj_run.sol[0, traj, i, :]).transpose(), dims=self.psi0_dims, shape=self.psi0_shape) return states
def _steadystate_direct_dense(L): """ Direct solver that use numpy dense matrices. Suitable for small system, with a few states. """ if settings.debug: print('Starting direct dense solver...') dims = L.dims[0] n = prod(L.dims[0][0]) b = np.zeros(n**2) b[0] = ss_args['weight'] L = L.data.todense() L[0, :] = np.diag(ss_args['weight'] * np.ones(n)).reshape((1, n**2)) _dense_start = time.time() v = np.linalg.solve(L, b) _dense_end = time.time() ss_args['info']['solution_time'] = _dense_end - _dense_start data = vec2mat(v) data = 0.5 * (data + data.conj().T) return Qobj(data, dims=dims, isherm=True)
def snot(): """Quantum object representing the SNOT (Hadamard) gate. Returns ------- snot_gate : qobj Quantum object representation of SNOT (Hadamard) gate. Examples -------- >>> snot() Quantum object: dims = [[2], [2]], \ shape = [2, 2], type = oper, isHerm = True Qobj data = [[ 0.70710678+0.j 0.70710678+0.j] [ 0.70710678+0.j -0.70710678+0.j]] """ u = qstate('u') d = qstate('d') Q = 1.0 / sqrt(2.0) * (d * d.dag() + u * d.dag() + d * u.dag() - u * u.dag()) return Qobj(Q)
def _steadystate_power(L, maxiter=10, tol=1e-6, itertol=1e-5): """ Inverse power method for steady state solving. """ if settings.debug: print('Starting iterative power method Solver...') use_solver(assumeSortedIndices=True) rhoss = Qobj() sflag = issuper(L) if sflag: rhoss.dims = L.dims[0] else: rhoss.dims = [L.dims[0], 1] n = prod(rhoss.shape) L = L.data.tocsc() - (tol**2) * sp.eye(n, n, format='csc') L.sort_indices() v = mat2vec(rand_dm(rhoss.shape[0], 0.5 / rhoss.shape[0] + 0.5).full()) it = 0 while (la.norm(L * v, np.inf) > tol) and (it < maxiter): v = spsolve(L, v) v = v / la.norm(v, np.inf) it += 1 if it >= maxiter: raise Exception('Failed to find steady state after ' + str(maxiter) + ' iterations') # normalise according to type of problem if sflag: trow = sp.eye(rhoss.shape[0], rhoss.shape[0], format='coo') trow = sp_reshape(trow, (1, n)) data = v / sum(trow.dot(v)) else: data = data / la.norm(v) data = sp.csr_matrix(vec2mat(data)) rhoss.data = 0.5 * (data + data.conj().T) rhoss.isherm = True return rhoss.tidyup() if settings.auto_tidyup else rhoss
def destroy_x_1(states): """ Destruction (lowering) operator for atomic excitations. Parameters ---------- states : array List of states of the Hilbert space. Returns ------- oper : qobj Qobj for destruction operator for atomic excitations. """ nstates = len(states) sigma = np.eye(nstates) for i in range(nstates): for j in range(nstates): sigma[i, j] = _destroy_excitons_1(states[i, 0], states[i, 1], states[i, 2], states[i, 3], states[j, 0], states[j, 1], states[j, 2], states[j, 3]) return Qobj(sp.csr_matrix(sigma, dtype=complex), isherm=False)
def qzero(dimensions): """ Zero operator. Parameters ---------- dimensions : (int) or (list of int) or (list of list of int) Dimension of Hilbert space. If provided as a list of ints, then the dimension is the product over this list, but the ``dims`` property of the new Qobj are set to this list. This can produce either `oper` or `super` depending on the passed `dimensions`. Returns ------- qzero : qobj Zero operator Qobj. """ size, dimensions = _implicit_tensor_dimensions(dimensions) # A sparse matrix with no data is equal to a zero matrix. return Qobj(fast_csr_matrix(shape=(size, size), dtype=complex), dims=dimensions, isherm=True)
def _partial_transpose_reference(rho, mask): """ This is a reference implementation that explicitly loops over all states and performs the transpose. It's slow but easy to understand and useful for testing. """ A_pt = np.zeros(rho.shape, dtype=complex) for psi_A in state_number_enumerate(rho.dims[0]): m = state_number_index(rho.dims[0], psi_A) for psi_B in state_number_enumerate(rho.dims[1]): n = state_number_index(rho.dims[1], psi_B) m_pt = state_number_index( rho.dims[1], np.choose(mask, [psi_A, psi_B])) n_pt = state_number_index( rho.dims[0], np.choose(mask, [psi_B, psi_A])) A_pt[m_pt, n_pt] = rho.data[m, n] return Qobj(A_pt, dims=rho.dims)
def swapalpha(alpha, N=None, targets=[0, 1]): """ Quantum object representing the SWAPalpha gate. Returns ------- swapalpha_gate : qobj Quantum object representation of SWAPalpha gate Examples -------- >>> swapalpha(alpha) Quantum object: dims = [[2, 2], [2, 2]], \ shape = [4, 4], type = oper, isHerm = True Qobj data = [[ 1.+0.j 0.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 0.5*(1 + exp(j*pi*alpha) 0.5*(1 - exp(j*pi*alpha) 0.+0.j] [ 0.+0.j 0.5*(1 - exp(j*pi*alpha) 0.5*(1 + exp(j*pi*alpha) 0.+0.j] [ 0.+0.j 0.+0.j 0.+0.j 1.+0.j]] """ if (targets[0] == 1 and targets[1] == 0) and N is None: N = 2 if N is not None: return gate_expand_2toN(cnot(), N, targets=targets) else: return Qobj([[1, 0, 0, 0], [ 0, 0.5 * (1 + np.exp(1.0j * np.pi * alpha)), 0.5 * (1 - np.exp(1.0j * np.pi * alpha)), 0 ], [ 0, 0.5 * (1 - np.exp(1.0j * np.pi * alpha)), 0.5 * (1 + np.exp(1.0j * np.pi * alpha)), 0 ], [0, 0, 0, 1]], dims=[[2, 2], [2, 2]])
def toffoli(): """Quantum object representing the Toffoli gate. Returns ------- toff_gate : qobj Quantum object representation of Toffoli gate. Examples -------- >>> toffoli() Quantum object: dims = [[2, 2, 2], [2, 2, 2]], \ shape = [8, 8], type = oper, isHerm = True Qobj data = [[ 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j] [ 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j]] """ uuu = qstate('uuu') uud = qstate('uud') udu = qstate('udu') udd = qstate('udd') duu = qstate('duu') dud = qstate('dud') ddu = qstate('ddu') ddd = qstate('ddd') Q = ddd * ddd.dag() + ddu * ddu.dag() + dud * dud.dag() + \ duu * duu.dag() + udd * udd.dag() + udu * udu.dag() + \ uuu * uud.dag() + uud * uuu.dag() return Qobj(Q)
def destroy(N, offset=0): '''Destruction (lowering) operator. Parameters ---------- N : int Dimension of Hilbert space. offset : int (default 0) The lowest number state that is included in the finite number state representation of the operator. Returns ------- oper : qobj Qobj for lowering operator. Examples -------- >>> destroy(4) Quantum object: dims = [[4], [4]], \ shape = [4, 4], type = oper, isHerm = False Qobj data = [[ 0.00000000+0.j 1.00000000+0.j 0.00000000+0.j 0.00000000+0.j] [ 0.00000000+0.j 0.00000000+0.j 1.41421356+0.j 0.00000000+0.j] [ 0.00000000+0.j 0.00000000+0.j 0.00000000+0.j 1.73205081+0.j] [ 0.00000000+0.j 0.00000000+0.j 0.00000000+0.j 0.00000000+0.j]] ''' if not isinstance(N, (int, np.integer)): # raise error if N not integer raise ValueError("Hilbert space dimension must be integer value") return Qobj(sp.spdiags(np.sqrt(range(offset, N + offset)), 1, N, N, format='csr'), isherm=False)
def qzero(N): """ Zero operator Parameters ---------- N : int or list of ints Dimension of Hilbert space. If provided as a list of ints, then the dimension is the product over this list, but the ``dims`` property of the new Qobj are set to this list. Returns ------- qzero : qobj Zero operator Qobj. """ if isinstance(N, list): return tensor(*[qzero(n) for n in N]) N = int(N) if (not isinstance(N, (int, np.integer))) or N < 0: raise ValueError("N must be integer N>=0") return Qobj(sp.csr_matrix((N, N), dtype=complex), isherm=True)
def rand_dm_ginibre(N=2, rank=None, dims=None): """ Returns a Ginibre random density operator of dimension ``dim`` and rank ``rank`` by using the algorithm of [BCSZ08]_. If ``rank`` is `None`, a full-rank (Hilbert-Schmidt ensemble) random density operator will be returned. Parameters ---------- N : int Dimension of the density operator to be returned. dims : list Dimensions of quantum object. Used for specifying tensor structure. Default is dims=[[N],[N]]. rank : int or None Rank of the sampled density operator. If None, a full-rank density operator is generated. Returns ------- rho : Qobj An N × N density operator sampled from the Ginibre or Hilbert-Schmidt distribution. """ if rank is None: rank = N if rank > N: raise ValueError("Rank cannot exceed dimension.") X = randnz((N, rank), norm='ginibre') rho = np.dot(X, X.T.conj()) rho /= np.trace(rho) return Qobj(rho, dims=dims)
def controlled_f(co, N=2, control=0, target=1, control_value=1): """ Create an N-qubit controlled gate from f gate with the given control and target qubits. Parameters ---------- N : integer The number of qubits in the target space. control : integer The index of the first control qubit. target : integer The index of the target qubit. control_value : integer (1) The state of the control qubit that activates the gate U. Returns ------- result : qobj Quantum object representing the controlled-s gate. """ U0 = Qobj([[1.0065, 0.2425], [0.2425, 0.9935]]) U = (co * 1j * np.pi * U0).expm() if [N, control, target] == [2, 0, 1]: return (tensor(fock_dm(2, control_value), U) + tensor(fock_dm(2, 1 - control_value), identity(2))) else: U2 = controlled_gate(U, control_value=control_value) return gate_expand_2toN(U2, N=N, control=control, target=target)
def destroy(N, offset=0): '''Destruction (lowering) operator. Parameters ---------- N : int Dimension of Hilbert space. offset : int (default 0) The lowest number state that is included in the finite number state representation of the operator. Returns ------- oper : qobj Qobj for lowering operator. Examples -------- >>> destroy(4) # doctest: +SKIP Quantum object: dims = [[4], [4]], \ shape = [4, 4], type = oper, isHerm = False Qobj data = [[ 0.00000000+0.j 1.00000000+0.j 0.00000000+0.j 0.00000000+0.j] [ 0.00000000+0.j 0.00000000+0.j 1.41421356+0.j 0.00000000+0.j] [ 0.00000000+0.j 0.00000000+0.j 0.00000000+0.j 1.73205081+0.j] [ 0.00000000+0.j 0.00000000+0.j 0.00000000+0.j 0.00000000+0.j]] ''' if not isinstance(N, (int, np.integer)): # raise error if N not integer raise ValueError("Hilbert space dimension must be integer value") data = np.sqrt(np.arange(offset + 1, N + offset, dtype=complex)) ind = np.arange(1, N, dtype=np.int32) ptr = np.arange(N + 1, dtype=np.int32) ptr[-1] = N - 1 return Qobj(fast_csr_matrix((data, ind, ptr), shape=(N, N)), isherm=False)
def testPulseConstructor(self): """ Test for creating empty Pulse, Pulse with constant coefficients etc. """ coeff = np.array([0.1, 0.2, 0.3, 0.4]) tlist = np.array([0., 1., 2., 3.]) ham = sigmaz() # Special ways of initializing pulse pulse2 = Pulse(sigmax(), 0, tlist, True) assert_allclose( pulse2.get_ideal_qobjevo(2).ops[0].qobj, tensor(sigmax(), identity(2))) pulse3 = Pulse(sigmay(), 0) assert_allclose(pulse3.get_ideal_qobjevo(2).cte.norm(), 0.) pulse4 = Pulse(None, None) # Dummy empty ham assert_allclose(pulse4.get_ideal_qobjevo(2).cte.norm(), 0.) tlist_noise = np.array([1., 2.5, 3.]) coeff_noise = np.array([0.5, 0.1, 0.5]) tlist_noise2 = np.array([0.5, 2, 3.]) coeff_noise2 = np.array([0.1, 0.2, 0.3]) # Pulse with different dims random_qobj = Qobj(np.random.random((3, 3))) pulse5 = Pulse(sigmaz(), 1, tlist, True) pulse5.add_coherent_noise(sigmay(), 1, tlist_noise, coeff_noise) pulse5.add_lindblad_noise(random_qobj, 0, tlist=tlist_noise2, coeff=coeff_noise2) qu, c_ops = pulse5.get_noisy_qobjevo(dims=[3, 2]) assert_allclose(qu.ops[0].qobj, tensor([identity(3), sigmaz()])) assert_allclose(qu.ops[1].qobj, tensor([identity(3), sigmay()])) assert_allclose(c_ops[0].ops[0].qobj, tensor([random_qobj, identity(2)]))
def _steadystate_svd_dense(L, atol=1e-12, rtol=0, all_steadystates=False): """ Find the steady state(s) of an open quantum system by solving for the nullspace of the Liouvillian. """ if settings.debug: print('Starting SVD solver...') u, s, vh = svd(L.full(), full_matrices=False) tol = max(atol, rtol * s[0]) nnz = (s >= tol).sum() ns = vh[nnz:].conj().T if all_steadystates: rhoss_list = [] for n in range(ns.shape[1]): rhoss = Qobj(vec2mat(ns[:, n]), dims=L.dims[0]) rhoss_list.append(rhoss / rhoss.tr()) return rhoss_list else: rhoss = Qobj(vec2mat(ns[:, 0]), dims=L.dims[0]) return rhoss / rhoss.tr()
def bloch_redfield_solve(R, ekets, rho0, tlist, e_ops=[], options=None): """ Evolve the ODEs defined by Bloch-Redfield master equation. The Bloch-Redfield tensor can be calculated by the function :func:`bloch_redfield_tensor`. Parameters ---------- R : :class:`qutip.qobj` Bloch-Redfield tensor. ekets : array of :class:`qutip.qobj` Array of kets that make up a basis tranformation for the eigenbasis. rho0 : :class:`qutip.qobj` Initial density matrix. tlist : *list* / *array* List of times for :math:`t`. e_ops : list of :class:`qutip.qobj` / callback function List of operators for which to evaluate expectation values. options : :class:`qutip.Qdeoptions` Options for the ODE solver. Returns ------- output: :class:`qutip.solver` An instance of the class :class:`qutip.solver`, which contains either an *array* of expectation values for the times specified by `tlist`. """ if options is None: options = Options() if options.tidy: R.tidyup() # # check initial state # if isket(rho0): # Got a wave function as initial state: convert to density matrix. rho0 = rho0 * rho0.dag() # # prepare output array # n_tsteps = len(tlist) dt = tlist[1] - tlist[0] result_list = [] # # transform the initial density matrix and the e_ops opterators to the # eigenbasis # rho_eb = rho0.transform(ekets) e_eb_ops = [e.transform(ekets) for e in e_ops] for e_eb in e_eb_ops: result_list.append(np.zeros(n_tsteps, dtype=complex)) # # setup integrator # initial_vector = mat2vec(rho_eb.full()) r = scipy.integrate.ode(cy_ode_rhs) r.set_f_params(R.data.data, R.data.indices, R.data.indptr) r.set_integrator('zvode', method=options.method, order=options.order, atol=options.atol, rtol=options.rtol, nsteps=options.nsteps, first_step=options.first_step, min_step=options.min_step, max_step=options.max_step) r.set_initial_value(initial_vector, tlist[0]) # # start evolution # dt = np.diff(tlist) for t_idx, _ in enumerate(tlist): if not r.successful(): break rho_eb.data = vec2mat(r.y) # calculate all the expectation values, or output rho_eb if no # expectation value operators are given if e_ops: rho_eb_tmp = Qobj(rho_eb) for m, e in enumerate(e_eb_ops): result_list[m][t_idx] = expect(e, rho_eb_tmp) else: result_list.append(rho_eb.transform(ekets, True)) if t_idx < n_tsteps - 1: r.integrate(r.t + dt[t_idx]) return result_list
def thermal_dm(N, n, method='operator'): """Density matrix for a thermal state of n particles Parameters ---------- N : int Number of basis states in Hilbert space. n : float Expectation value for number of particles in thermal state. method : string {'operator', 'analytic'} ``string`` that sets the method used to generate the thermal state probabilities Returns ------- dm : qobj Thermal state density matrix. Examples -------- >>> thermal_dm(5, 1) Quantum object: dims = [[5], [5]], \ shape = [5, 5], type = oper, isHerm = True Qobj data = [[ 0.51612903 0. 0. 0. 0. ] [ 0. 0.25806452 0. 0. 0. ] [ 0. 0. 0.12903226 0. 0. ] [ 0. 0. 0. 0.06451613 0. ] [ 0. 0. 0. 0. 0.03225806]] >>> thermal_dm(5, 1, 'analytic') Quantum object: dims = [[5], [5]], \ shape = [5, 5], type = oper, isHerm = True Qobj data = [[ 0.5 0. 0. 0. 0. ] [ 0. 0.25 0. 0. 0. ] [ 0. 0. 0.125 0. 0. ] [ 0. 0. 0. 0.0625 0. ] [ 0. 0. 0. 0. 0.03125]] Notes ----- The 'operator' method (default) generates the thermal state using the truncated number operator ``num(N)``. This is the method that should be used in computations. The 'analytic' method uses the analytic coefficients derived in an infinite Hilbert space. The analytic form is not necessarily normalized, if truncated too aggressively. """ if n == 0: return fock_dm(N, 0) else: i = arange(N) if method == 'operator': beta = np.log(1.0 / n + 1.0) diags = np.exp(-beta * i) diags = diags / np.sum(diags) # populates diagonal terms using truncated operator expression rm = sp.spdiags(diags, 0, N, N, format='csr') elif method == 'analytic': # populates diagonal terms using analytic values rm = sp.spdiags((1.0 + n) ** (-1.0) * (n / (1.0 + n)) ** (i), 0, N, N, format='csr') else: raise ValueError( "'method' keyword argument must be 'operator' or 'analytic'") return Qobj(rm)
def coherent(N, alpha, offset=0, method='operator'): """Generates a coherent state with eigenvalue alpha. Constructed using displacement operator on vacuum state. Parameters ---------- N : int Number of Fock states in Hilbert space. alpha : float/complex Eigenvalue of coherent state. offset : int (default 0) The lowest number state that is included in the finite number state representation of the state. Using a non-zero offset will make the default method 'analytic'. method : string {'operator', 'analytic'} Method for generating coherent state. Returns ------- state : qobj Qobj quantum object for coherent state Examples -------- >>> coherent(5,0.25j) Quantum object: dims = [[5], [1]], shape = [5, 1], type = ket Qobj data = [[ 9.69233235e-01+0.j ] [ 0.00000000e+00+0.24230831j] [ -4.28344935e-02+0.j ] [ 0.00000000e+00-0.00618204j] [ 7.80904967e-04+0.j ]] Notes ----- Select method 'operator' (default) or 'analytic'. With the 'operator' method, the coherent state is generated by displacing the vacuum state using the displacement operator defined in the truncated Hilbert space of size 'N'. This method guarantees that the resulting state is normalized. With 'analytic' method the coherent state is generated using the analytical formula for the coherent state coefficients in the Fock basis. This method does not guarantee that the state is normalized if truncated to a small number of Fock states, but would in that case give more accurate coefficients. """ if method == "operator" and offset == 0: x = basis(N, 0) a = destroy(N) D = (alpha * a.dag() - conj(alpha) * a).expm() return D * x elif method == "analytic" or offset > 0: sqrtn = np.sqrt(np.arange(offset, offset+N, dtype=complex)) sqrtn[0] = 1 # Get rid of divide by zero warning data = alpha/sqrtn if offset == 0: data[0] = np.exp(-abs(alpha)**2 / 2.0) else: s = np.prod(np.sqrt(np.arange(1, offset + 1))) # sqrt factorial data[0] = np.exp(-abs(alpha)**2 / 2.0) * alpha**(offset) / s np.cumprod(data, out=sqrtn) # Reuse sqrtn array return Qobj(sqrtn) else: raise TypeError( "The method option can only take values 'operator' or 'analytic'")
def jmat(j, *args): """Higher-order spin operators: Parameters ---------- j : float Spin of operator args : str Which operator to return 'x','y','z','+','-'. If no args given, then output is ['x','y','z'] Returns ------- jmat : qobj / ndarray ``qobj`` for requested spin operator(s). Examples -------- >>> jmat(1) # doctest: +SKIP [ Quantum object: dims = [[3], [3]], \ shape = [3, 3], type = oper, isHerm = True Qobj data = [[ 0. 0.70710678 0. ] [ 0.70710678 0. 0.70710678] [ 0. 0.70710678 0. ]] Quantum object: dims = [[3], [3]], \ shape = [3, 3], type = oper, isHerm = True Qobj data = [[ 0.+0.j 0.-0.70710678j 0.+0.j ] [ 0.+0.70710678j 0.+0.j 0.-0.70710678j] [ 0.+0.j 0.+0.70710678j 0.+0.j ]] Quantum object: dims = [[3], [3]], \ shape = [3, 3], type = oper, isHerm = True Qobj data = [[ 1. 0. 0.] [ 0. 0. 0.] [ 0. 0. -1.]]] Notes ----- If no 'args' input, then returns array of ['x','y','z'] operators. """ if (np.fix(2 * j) != 2 * j) or (j < 0): raise TypeError('j must be a non-negative integer or half-integer') if not args: return jmat(j, 'x'), jmat(j, 'y'), jmat(j, 'z') if args[0] == '+': A = _jplus(j) elif args[0] == '-': A = _jplus(j).getH() elif args[0] == 'x': A = 0.5 * (_jplus(j) + _jplus(j).getH()) elif args[0] == 'y': A = -0.5 * 1j * (_jplus(j) - _jplus(j).getH()) elif args[0] == 'z': A = _jz(j) else: raise TypeError('Invalid type') return Qobj(A)
def essolve(H, rho0, tlist, c_op_list, e_ops): """ Evolution of a state vector or density matrix (`rho0`) for a given Hamiltonian (`H`) and set of collapse operators (`c_op_list`), by expressing the ODE as an exponential series. The output is either the state vector at arbitrary points in time (`tlist`), or the expectation values of the supplied operators (`e_ops`). .. deprecated:: 4.6.0 :obj:`~essolev` will be removed in QuTiP 5. Please use :obj:`~sesolve` or :obj:`~mesolve` for general-purpose integration of the Schroedinger/Lindblad master equation. This will likely be faster than :obj:`~essolve` for you. Parameters ---------- H : qobj/function_type System Hamiltonian. rho0 : :class:`qutip.qobj` Initial state density matrix. tlist : list/array ``list`` of times for :math:`t`. c_op_list : list of :class:`qutip.qobj` ``list`` of :class:`qutip.qobj` collapse operators. e_ops : list of :class:`qutip.qobj` ``list`` of :class:`qutip.qobj` operators for which to evaluate expectation values. Returns ------- expt_array : array Expectation values of wavefunctions/density matrices for the times specified in ``tlist``. .. note:: This solver does not support time-dependent Hamiltonians. """ n_expt_op = len(e_ops) n_tsteps = len(tlist) # Calculate the Liouvillian if (c_op_list is None or len(c_op_list) == 0) and isket(rho0): L = H else: L = liouvillian(H, c_op_list) es = ode2es(L, rho0) # evaluate the expectation values if n_expt_op == 0: results = [Qobj()] * n_tsteps else: results = np.zeros([n_expt_op, n_tsteps], dtype=complex) for n, e in enumerate(e_ops): results[n, :] = expect(e, esval(es, tlist)) data = Result() data.solver = "essolve" data.times = tlist data.expect = [ np.real(results[n, :]) if e.isherm else results[n, :] for n, e in enumerate(e_ops) ] return data
def ode2es(L, rho0): """Creates an exponential series that describes the time evolution for the initial density matrix (or state vector) `rho0`, given the Liouvillian (or Hamiltonian) `L`. .. deprecated:: 4.6.0 :obj:`~ode2es` will be removed in QuTiP 5. Please use :obj:`Qobj.eigenstates` to get the eigenstates and -values, and use :obj:`~QobjEvo` for general time-dependence. Parameters ---------- L : qobj Liouvillian of the system. rho0 : qobj Initial state vector or density matrix. Returns ------- eseries : :class:`qutip.eseries` ``eseries`` represention of the system dynamics. """ if issuper(L): # check initial state if isket(rho0): # Got a wave function as initial state: convert to density matrix. rho0 = rho0 * rho0.dag() # check if state is below error threshold if abs(rho0.full()).sum() < 1e-10 + 1e-24: # enforce zero operator return eseries(qzero(rho0.dims[0])) w, v = L.eigenstates() v = np.hstack([ket.full() for ket in v]) # w[i] = eigenvalue i # v[:,i] = eigenvector i rlen = np.prod(rho0.shape) r0 = mat2vec(rho0.full()) v0 = la.solve(v, r0) vv = v * sp.spdiags(v0.T, 0, rlen, rlen) out = None for i in range(rlen): qo = Qobj(vec2mat(vv[:, i]), dims=rho0.dims, shape=rho0.shape) if out: out += eseries(qo, w[i]) else: out = eseries(qo, w[i]) elif isoper(L): if not isket(rho0): raise TypeError('Second argument must be a ket if first' + 'is a Hamiltonian.') # check if state is below error threshold if abs(rho0.full()).sum() < 1e-5 + 1e-20: # enforce zero operator dims = rho0.dims return eseries( Qobj(sp.csr_matrix((dims[0][0], dims[1][0]), dtype=complex))) w, v = L.eigenstates() v = np.hstack([ket.full() for ket in v]) # w[i] = eigenvalue i # v[:,i] = eigenvector i rlen = np.prod(rho0.shape) r0 = rho0.full() v0 = la.solve(v, r0) vv = v * sp.spdiags(v0.T, 0, rlen, rlen) out = None for i in range(rlen): qo = Qobj(np.array(vv[:, i]).T, dims=rho0.dims, shape=rho0.shape) if out: out += eseries(qo, -1.0j * w[i]) else: out = eseries(qo, -1.0j * w[i]) else: raise TypeError('First argument must be a Hamiltonian or Liouvillian.') return estidy(out)
def floquet_markov_mesolve(R, ekets, rho0, tlist, e_ops, f_modes_table=None, options=None, floquet_basis=True): """ Solve the dynamics for the system using the Floquet-Markov master equation. """ if options is None: opt = Options() else: opt = options if opt.tidy: R.tidyup() # # check initial state # if isket(rho0): # Got a wave function as initial state: convert to density matrix. rho0 = ket2dm(rho0) # # prepare output array # n_tsteps = len(tlist) dt = tlist[1] - tlist[0] output = Result() output.solver = "fmmesolve" output.times = tlist if isinstance(e_ops, FunctionType): n_expt_op = 0 expt_callback = True elif isinstance(e_ops, list): n_expt_op = len(e_ops) expt_callback = False if n_expt_op == 0: output.states = [] else: if not f_modes_table: raise TypeError("The Floquet mode table has to be provided " + "when requesting expectation values.") output.expect = [] output.num_expect = n_expt_op for op in e_ops: if op.isherm: output.expect.append(np.zeros(n_tsteps)) else: output.expect.append(np.zeros(n_tsteps, dtype=complex)) else: raise TypeError("Expectation parameter must be a list or a function") # # transform the initial density matrix to the eigenbasis: from # computational basis to the floquet basis # if ekets is not None: rho0 = rho0.transform(ekets) # # setup integrator # initial_vector = mat2vec(rho0.full()) r = scipy.integrate.ode(cy_ode_rhs) r.set_f_params(R.data.data, R.data.indices, R.data.indptr) r.set_integrator('zvode', method=opt.method, order=opt.order, atol=opt.atol, rtol=opt.rtol, max_step=opt.max_step) r.set_initial_value(initial_vector, tlist[0]) # # start evolution # rho = Qobj(rho0) t_idx = 0 for t in tlist: if not r.successful(): break rho = Qobj(vec2mat(r.y), rho0.dims, rho0.shape) if expt_callback: # use callback method if floquet_basis: e_ops(t, Qobj(rho)) else: f_modes_table_t, T = f_modes_table f_modes_t = floquet_modes_t_lookup(f_modes_table_t, t, T) e_ops(t, Qobj(rho).transform(f_modes_t, True)) else: # calculate all the expectation values, or output rho if # no operators if n_expt_op == 0: if floquet_basis: output.states.append(Qobj(rho)) else: f_modes_table_t, T = f_modes_table f_modes_t = floquet_modes_t_lookup(f_modes_table_t, t, T) output.states.append(Qobj(rho).transform(f_modes_t, True)) else: f_modes_table_t, T = f_modes_table f_modes_t = floquet_modes_t_lookup(f_modes_table_t, t, T) for m in range(0, n_expt_op): output.expect[m][t_idx] = \ expect(e_ops[m], rho.transform(f_modes_t, False)) r.integrate(r.t + dt) t_idx += 1 return output