def _smesolve_single_trajectory(L, dt, tlist, N_store, N_substeps, rho_t, A_ops, e_ops, data, rhs, d1, d2): """ Internal function. See smesolve. """ dW = np.sqrt(dt) * scipy.randn(len(A_ops), N_store, N_substeps) states_list = [] for t_idx, t in enumerate(tlist): if e_ops: for e_idx, e in enumerate(e_ops): # XXX: need to keep hilbert space structure data.expect[e_idx, t_idx] += expect(e, Qobj(vec2mat(rho_t))) else: states_list.append(Qobj(rho_t)) # dito for j in range(N_substeps): drho_t = spmv(L.data.data, L.data.indices, L.data.indptr, rho_t) * dt for a_idx, A in enumerate(A_ops): drho_t += rhs(L.data, rho_t, A, dt, dW[a_idx, t_idx, j], d1, d2) rho_t += drho_t return states_list
def _ssesolve_single_trajectory(H, dt, tlist, N_store, N_substeps, psi_t, A_ops, e_ops, data, rhs, d1, d2): """ Internal function. See ssesolve. """ #if debug: print inspect.stack()[0][3] dW = np.sqrt(dt) * scipy.randn(len(A_ops), N_store, N_substeps) states_list = [] for t_idx, t in enumerate(tlist): if e_ops: for e_idx, e in enumerate(e_ops): data.expect[e_idx, t_idx] += expect(e, Qobj(psi_t)) else: states_list.append(Qobj(psi_t)) for j in range(N_substeps): dpsi_t = (-1.0j * dt) * (H.data * psi_t) for a_idx, A in enumerate(A_ops): dpsi_t += rhs(H.data, psi_t, A, dt, dW[a_idx, t_idx, j], d1, d2) # increment and renormalize the wave function psi_t += dpsi_t psi_t /= norm(psi_t, 2) return states_list
def swap(mask=None): """Quantum object representing the SWAP gate. Returns ------- swap_gate : qobj Quantum object representation of SWAP gate Examples -------- >>> swap() 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.+0.j 1.+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 1.+0.j]] """ if mask is None: uu = qstate('uu') ud = qstate('ud') du = qstate('du') dd = qstate('dd') Q = uu * uu.dag() + ud * du.dag() + du * ud.dag() + dd * dd.dag() return Qobj(Q) else: if sum(mask) != 2: raise ValueError("mask must only have two ones, rest zeros") dims = [2] * len(mask) idx, = where(mask) N = prod(dims) data = sp.lil_matrix((N, N)) for s1 in state_number_enumerate(dims): i1 = state_number_index(dims, s1) if s1[idx[0]] == s1[idx[1]]: i2 = i1 else: s2 = array(s1).copy() s2[idx[0]], s2[idx[1]] = s2[idx[1]], s2[idx[0]] i2 = state_number_index(dims, s2) data[i1, i2] = 1 return Qobj(data, dims=[dims, dims], shape=[N, N])
def ket2dm(Q): """Takes input ket or bra vector and returns density matrix formed by outer product. Parameters ---------- Q : qobj Ket or bra type quantum object. Returns ------- dm : qobj Density matrix formed by outer product of `Q`. Examples -------- >>> x=basis(3,2) >>> ket2dm(x) Quantum object: dims = [[3], [3]], shape = [3, 3], type = oper, isHerm = True Qobj data = [[ 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 Q.type=='ket': out=Q*Q.dag() elif Q.type=='bra': out=Q.dag()*Q else: raise TypeError("Input is not a ket or bra vector.") return Qobj(out)
def thermal_dm(N, n): """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. 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.50000+0.j 0.00000+0.j 0.00000+0.j 0.00000+0.j 0.00000+0.j] [ 0.00000+0.j 0.25000+0.j 0.00000+0.j 0.00000+0.j 0.00000+0.j] [ 0.00000+0.j 0.00000+0.j 0.12500+0.j 0.00000+0.j 0.00000+0.j] [ 0.00000+0.j 0.00000+0.j 0.00000+0.j 0.06250+0.j 0.00000+0.j] [ 0.00000+0.j 0.00000+0.j 0.00000+0.j 0.00000+0.j 0.03125+0.j]] """ i=arange(N) rm = sp.spdiags((1.0+n)**(-1.0)*(n/(1.0+n))**(i),0,N,N,format='csr') #populates diagonal terms (the only nonzero terms in matrix) return Qobj(rm)
def num(N): """Quantum object for number operator. Parameters ---------- N : int The dimension of the Hilbert space. Returns ------- oper: qobj Qobj for number operator. Examples -------- >>> num(4) Quantum object: dims = [[4], [4]], shape = [4, 4], type = oper, isHerm = True Qobj data = [[0 0 0 0] [0 1 0 0] [0 0 2 0] [0 0 0 3]] """ data = sp.spdiags(np.arange(N), 0, N, N, format='csr') return Qobj(data)
def qeye(N): """Identity operator Parameters ---------- N : int Dimension of Hilbert space. Returns ------- oper : qobj Identity operator Qobj. Examples -------- >>> qeye(3) Quantum object: dims = [[3], [3]], shape = [3, 3], type = oper, isHerm = True Qobj data = [[ 1. 0. 0.] [ 0. 1. 0.] [ 0. 0. 1.]] """ N = int(N) if (not isinstance(N, int)) or N < 0: #check if N is int and N>0 raise ValueError("N must be integer N>=0") return Qobj(sp.eye(N, N, dtype=complex, format='csr'))
def create(N): '''Creation (raising) operator. Parameters ---------- N : int Dimension of Hilbert space. Returns ------- oper : qobj Qobj for raising operator. Examples -------- >>> create(4) Quantum object: dims = [[4], [4]], shape = [4, 4], type = oper, isHerm = False Qobj data = [[ 0.00000000+0.j 0.00000000+0.j 0.00000000+0.j 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]] ''' if not isinstance(N, int): #raise error if N not integer raise ValueError("Hilbert space dimension must be integer value") qo = destroy(N) #create operator using destroy function qo.data = qo.data.T #transpsoe data in Qobj return Qobj(qo)
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 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 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)) N = int(np.sqrt(len(evals))) evecs = evecs.T rho = Qobj(vec2mat(evecs[ev_idx])) rho = rho * (1.0/rho.tr()) rho = 0.5*(rho+rho.dag()) #make sure rho is herm return rho
def phasegate(theta): """ Returns quantum object representing the phase shift gate. Parameters ---------- theta : float Phase rotation angle. Returns ------- phase_gate : qobj Quantum object representation of phase shift gate. Examples -------- >>> phasegate(pi/4) Quantum object: dims = [[2], [2]], shape = [2, 2], type = oper, isHerm = False Qobj data = [[ 1.00000000+0.j 0.00000000+0.j ] [ 0.00000000+0.j 0.70710678+0.70710678j]] """ u = qstate('u') d = qstate('d') Q = d * d.dag() + (exp(1.0j * theta) * u * u.dag()) return Qobj(Q)
def destroy(N): '''Destruction (lowering) operator. Parameters ---------- N : int Dimension of Hilbert space. 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): #raise error if N not integer raise ValueError("Hilbert space dimension must be integer value") return Qobj(sp.spdiags(np.sqrt(range(0, N)), 1, N, N, format='csr'))
def cnot(): """ Quantum object representing the CNOT gate. Returns ------- cnot_gate : qobj Quantum object representation of CNOT gate Examples -------- >>> cnot() 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 1.+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 1.+0.j 0.+0.j]] """ uu = qstate('uu') ud = qstate('ud') du = qstate('du') dd = qstate('dd') Q = dd * dd.dag() + du * du.dag() + uu * ud.dag() + ud * uu.dag() return Qobj(Q)
def iswap(mask=None): """Quantum object representing the iSWAP gate. Returns ------- iswap_gate : qobj Quantum object representation of iSWAP gate Examples -------- >>> iswap() Quantum object: dims = [[2, 2], [2, 2]], shape = [4, 4], type = oper, isHerm = False Qobj data = [[ 1.+0.j 0.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 0.+1.j 0.+0.j] [ 0.+0.j 0.+1.j 0.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 0.+0.j 1.+0.j]] """ if mask is None: return Qobj(array([[1, 0, 0, 0], [0, 0, 1j, 0], [0, 1j, 0, 0], [0, 0, 0, 1]]), dims=[[2, 2], [2, 2]]) else: if sum(mask) != 2: raise ValueError("mask must only have two ones, rest zeros") dims = [2] * len(mask) idx, = where(mask) N = prod(dims) data = sp.lil_matrix((N, N), dtype=complex) for s1 in state_number_enumerate(dims): i1 = state_number_index(dims, s1) if s1[idx[0]] == s1[idx[1]]: i2 = i1 val = 1.0 else: s2 = s1.copy() s2[idx[0]], s2[idx[1]] = s2[idx[1]], s2[idx[0]] i2 = state_number_index(dims, s2) val = 1.0j data[i1, i2] = val return Qobj(data, dims=[dims, dims], shape=[N, N])
def essolve(H, rho0, tlist, c_op_list, expt_op_list): """ 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 (`expt_op_list`). Parameters ---------- H : qobj/function_type System Hamiltonian. rho0 : qobj Initial state density matrix. tlist : list/array ``list`` of times for :math:`t`. c_op_list : list ``list`` of ``qobj`` collapse operators. expt_op_list : list ``list`` of ``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(expt_op_list) n_tsteps = len(tlist) # Calculate the Liouvillian if c_op_list == None or len(c_op_list) == 0: L = H else: L = liouvillian(H, c_op_list) es = ode2es(L, rho0) # evaluate the expectation values if n_expt_op == 0: result_list = [Qobj() for k in range(n_tsteps)] else: result_list = np.zeros([n_expt_op, n_tsteps], dtype=complex) for n in range(0, n_expt_op): result_list[n,:] = esval(expect(expt_op_list[n],es),tlist) return result_list
def basis(N,*args): """Generates the vector representation of a Fock state. Parameters ---------- N : int Number of Fock states in Hilbert space. args : int ``int`` corresponding to desired number state, defaults to 0 if omitted. Returns ------- state : qobj Qobj representing the requested number state ``|args>``. Examples -------- >>> basis(5,2) Quantum object: dims = [[5], [1]], shape = [5, 1], type = ket Qobj data = [[ 0.+0.j] [ 0.+0.j] [ 1.+0.j] [ 0.+0.j] [ 0.+0.j]] Notes ----- A subtle incompability with the quantum optics toolbox: In QuTiP:: basis(N, 0) = ground state but in qotoolbox:: basis(N, 1) = ground state """ if (not isinstance(N,int)) or N<0: raise ValueError("N must be integer N>=0") if not any(args):#if no args then assume vacuum state args=0 if not isinstance(args,int):#if input arg!=0 if not isinstance(args[0],int): raise ValueError("need integer for basis vector index") args=args[0] if args<0 or args>(N-1): #check if args is within bounds raise ValueError("basis vector index need to be in 0=<indx<=N-1") bas=sp.lil_matrix((N,1)) #column vector of zeros bas[args,0]=1 # 1 located at position args bas=bas.tocsr() return Qobj(bas)
def sqrtiswap(): """Quantum object representing the square root iSWAP gate. Returns ------- sqrtiswap_gate : qobj Quantum object representation of square root iSWAP gate Examples -------- >>> sqrtiswap() Quantum object: dims = [[2, 2], [2, 2]], shape = [4, 4], type = oper, isHerm = False Qobj data = [[ 1.00000000+0.j 0.00000000+0.j 0.00000000+0.j 0.00000000+0.j] [ 0.00000000+0.j 0.70710678+0.j 0.00000000-0.70710678j 0.00000000+0.j] [ 0.00000000+0.j 0.00000000-0.70710678j 0.70710678+0.j 0.00000000+0.j] [ 0.00000000+0.j 0.00000000+0.j 0.00000000+0.j 1.00000000+0.j]] """ return Qobj(array([[1,0,0,0], [0, 1/sqrt(2), -1j/sqrt(2), 0], [0, -1j/sqrt(2), 1/sqrt(2), 0], [0, 0, 0, 1]]), dims=[[2, 2], [2, 2]])
def iswap(): """Quantum object representing the iSWAP gate. Returns ------- iswap_gate : qobj Quantum object representation of iSWAP gate Examples -------- >>> iswap() Quantum object: dims = [[2, 2], [2, 2]], shape = [4, 4], type = oper, isHerm = False Qobj data = [[ 1.+0.j 0.+0.j 0.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 0.+1.j 0.+0.j] [ 0.+0.j 0.+1.j 0.+0.j 0.+0.j] [ 0.+0.j 0.+0.j 0.+0.j 1.+0.j]] """ return Qobj(array([[1,0,0,0], [0,0,1j,0], [0,1j,0,0], [0,0,0,1]]), dims=[[2, 2], [2, 2]])
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 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/list ``qobj`` for requested spin operator(s). Examples -------- >>> jmat(1) [ 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 (scipy.fix(2 * j) != 2 * j) or (j < 0): raise TypeError('j must be a non-negative integer or half-integer') if not args: a1 = Qobj(0.5 * (jplus(j) + jplus(j).conj().T)) a2 = Qobj(0.5 * 1j * (jplus(j) - jplus(j).conj().T)) a3 = Qobj(jz(j)) return np.array([a1, a2, a3]) if args[0] == '+': A = jplus(j) elif args[0] == '-': A = jplus(j).conj().T elif args[0] == 'x': A = 0.5 * (jplus(j) + jplus(j).conj().T) elif args[0] == 'y': A = -0.5 * 1j * (jplus(j) - jplus(j).conj().T) elif args[0] == 'z': A = jz(j) else: raise TypeError('Invlaid type') return Qobj(A.tocsr())
def tensor(*args): """Calculates the tensor product of input operators. Parameters ---------- args : array_like ``list`` or ``array`` of quantum objects for tensor product. Returns -------- obj : qobj A composite quantum object. Examples -------- >>> tensor([sigmax(), sigmax()]) Quantum object: dims = [[2, 2], [2, 2]], shape = [4, 4], type = oper, isHerm = True Qobj data = [[ 0.+0.j 0.+0.j 0.+0.j 1.+0.j] [ 0.+0.j 0.+0.j 1.+0.j 0.+0.j] [ 0.+0.j 1.+0.j 0.+0.j 0.+0.j] [ 1.+0.j 0.+0.j 0.+0.j 0.+0.j]] """ if not args: raise TypeError("Requires at least one input argument") num_args = len(args) step = 0 for n in range(num_args): if isinstance(args[n], Qobj): qos = args[n] if step == 0: dat = qos.data dim = qos.dims shp = qos.shape step = 1 else: dat = sp.kron(dat, qos.data, format='csr') #sparse Kronecker product dim = [dim[0] + qos.dims[0], dim[1] + qos.dims[1]] #append dimensions of Qobjs shp = [dat.shape[0], dat.shape[1]] #new shape of matrix elif isinstance( args[n], (list, ndarray)): #checks if input is list/array of Qobjs qos = args[n] items = len(qos) #number of inputs if not all([ isinstance(k, Qobj) for k in qos ]): #raise error if one of the inputs is not a quantum object raise TypeError("One of inputs is not a quantum object") if items == 1: # if only one Qobj, do nothing if step == 0: dat = qos[0].data dim = qos[0].dims shp = qos[0].shape step = 1 else: dat = sp.kron(dat, qos[0].data, format='csr') #sparse Kronecker product dim = [dim[0] + qos[0].dims[0], dim[1] + qos[0].dims[1] ] #append dimensions of Qobjs shp = [dat.shape[0], dat.shape[1]] #new shape of matrix elif items != 1: if step == 0: dat = qos[0].data dim = qos[0].dims shp = qos[0].shape step = 1 for k in range(items - 1): #cycle over all items dat = sp.kron(dat, qos[k + 1].data, format='csr') #sparse Kronecker product dim = [ dim[0] + qos[k + 1].dims[0], dim[1] + qos[k + 1].dims[1] ] #append dimensions of Qobjs shp = [dat.shape[0], dat.shape[1]] #new shape of matrix out = Qobj() out.data = dat out.dims = dim out.shape = shp if qutip.settings.auto_tidyup: return Qobj(out).tidyup() #returns tidy Qobj else: return Qobj(out)
def propagator(H, t, c_op_list, H_args=None, opt=None): """ Calculate the propagator U(t) for the density matrix or wave function such that :math:`\psi(t) = U(t)\psi(0)` or :math:`\\rho_{\mathrm vec}(t) = U(t) \\rho_{\mathrm vec}(0)` where :math:`\\rho_{\mathrm vec}` is the vector representation of the density matrix. Parameters ---------- H : qobj or list Hamiltonian as a Qobj instance of a nested list of Qobjs and coefficients in the list-string or list-function format for time-dependent Hamiltonians (see description in :func:`qutip.mesolve`). t : float or array-like Time or list of times for which to evaluate the propagator. c_op_list : list List of qobj collapse operators. H_args : list/array/dictionary Parameters to callback functions for time-dependent Hamiltonians. Returns ------- a : qobj Instance representing the propagator :math:`U(t)`. """ if opt == None: opt = Odeoptions() opt.rhs_reuse = True tlist = [0, t] if isinstance(t,(int,float,np.int64,np.float64)) else t if len(c_op_list) == 0: # calculate propagator for the wave function if isinstance(H, types.FunctionType): H0 = H(0.0, H_args) N = H0.shape[0] elif isinstance(H, list): if isinstance(H[0], list): H0 = H[0][0] N = H0.shape[0] else: H0 = H[0] N = H0.shape[0] else: N = H.shape[0] u = np.zeros([N, N, len(tlist)], dtype=complex) for n in range(0, N): psi0 = basis(N, n) output = mesolve(H, psi0, tlist, [], [], H_args, opt) for k, t in enumerate(tlist): u[:,n,k] = output.states[k].full().T # todo: evolving a batch of wave functions: #psi_0_list = [basis(N, n) for n in range(N)] #psi_t_list = mesolve(H, psi_0_list, [0, t], [], [], H_args, opt) #for n in range(0, N): # u[:,n] = psi_t_list[n][1].full().T else: # calculate the propagator for the vector representation of the # density matrix if isinstance(H, types.FunctionType): H0 = H(0.0, H_args) N = H0.shape[0] elif isinstance(H, list): if isinstance(H[0], list): H0 = H[0][0] N = H0.shape[0] else: H0 = H[0] N = H0.shape[0] else: N = H.shape[0] u = np.zeros([N*N, N*N, len(tlist)], dtype=complex) for n in range(0, N*N): psi0 = basis(N*N, n) rho0 = Qobj(vec2mat(psi0.full())) output = mesolve(H, rho0, tlist, c_op_list, [], H_args, opt) for k, t in enumerate(tlist): u[:,n,k] = mat2vec(output.states[k].full()).T if len(tlist) == 2: return Qobj(u[:,:,1]) else: return [Qobj(u[:,:,k]) for k in range(len(tlist))]
def floquet_markov_mesolve(R, ekets, rho0, tlist, e_ops, options=None): """ Solve the dynamics for the system using the Floquet-Markov master equation. """ if options == None: opt = Odeoptions() 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 = Odedata() 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: 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 and the e_ops opterators to the # eigenbasis: from computational basis to the floquet basis # if ekets != None: rho0 = rho0.transform(ekets, True) if isinstance(e_ops, list): for n in np.arange(len(e_ops)): # not working e_ops[n] = e_ops[n].transform(ekets) # # # setup integrator # initial_vector = mat2vec(rho0.full()) r = scipy.integrate.ode(cyq_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.data = vec2mat(r.y) if expt_callback: # use callback method e_ops(t, Qobj(rho)) else: # calculate all the expectation values, or output rho if no operators if n_expt_op == 0: output.states.append(Qobj(rho)) # copy psi/rho else: for m in range(0, n_expt_op): output.expect[m][t_idx] = expect(e_ops[m], rho) # basis OK? r.integrate(r.t + dt) t_idx += 1 return output
def floquet_modes(H, T, args=None, sort=False): """ Calculate the initial Floquet modes Phi_alpha(0) for a driven system with period T. Returns a list of :class:`qutip.Qobj` instances representing the Floquet modes and a list of corresponding quasienergies, sorted by increasing quasienergy in the interval [-pi/T, pi/T]. The optional parameter `sort` decides if the output is to be sorted in increasing quasienergies or not. Parameters ---------- H : :class:`qutip.Qobj` system Hamiltonian, time-dependent with period `T` args : dictionary dictionary with variables required to evaluate H T : float The period of the time-dependence of the hamiltonian. The default value 'None' indicates that the 'tlist' spans a single period of the driving. Returns ------- output : list of kets, list of quasi energies Two lists: the Floquet modes as kets and the quasi energies. """ # get the unitary propagator U = propagator(H, T, [], args) # find the eigenstates for the propagator evals, evecs = la.eig(U.full()) eargs = angle(evals) # make sure that the phase is in the interval [-pi, pi], so that the # quasi energy is in the interval [-pi/T, pi/T] where T is the period of the # driving. #eargs += (eargs <= -2*pi) * (2*pi) + (eargs > 0) * (-2*pi) eargs += (eargs <= -pi) * (2 * pi) + (eargs > pi) * (-2 * pi) e_quasi = -eargs / T # sort by the quasi energy if sort == True: order = np.argsort(-e_quasi) else: order = list(range(len(evals))) # prepare a list of kets for the floquet states new_dims = [U.dims[0], [1] * len(U.dims[0])] new_shape = [U.shape[0], 1] kets_order = [ Qobj(np.matrix(evecs[:, o]).T, dims=new_dims, shape=new_shape) for o in order ] return kets_order, e_quasi[order]
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`. Parameters ---------- L : qobj Liouvillian of the system. rho0 : qobj Initial state vector or density matrix. Returns ------- ode_series : 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() w, v = L.eigenstates() v=np.hstack([ket.full() for ket in v]) # w[i] = eigenvalue i # v[:,i] = eigenvector i rlen = 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.') w, v = L.eigenstates() v=np.hstack([ket.full() for ket in v]) # w[i] = eigenvalue i # v[:,i] = eigenvector i rlen = 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(matrix(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 _generic_ode_solve(r, psi0, tlist, expt_ops, opt, state_vectorize, state_norm_func=None): """ Internal function for solving ODEs. """ # # prepare output array # n_tsteps = len(tlist) dt = tlist[1] - tlist[0] output = Odedata() output.solver = "mesolve" output.times = tlist if isinstance(expt_ops, types.FunctionType): n_expt_op = 0 expt_callback = True elif isinstance(expt_ops, list): n_expt_op = len(expt_ops) expt_callback = False if n_expt_op == 0: output.states = [] else: output.expect = [] output.num_expect = n_expt_op for op in expt_ops: if op.isherm and psi0.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") # # start evolution # psi = Qobj(psi0) t_idx = 0 for t in tlist: if not r.successful(): break if state_norm_func: psi.data = state_vectorize(r.y) state_norm = state_norm_func(psi.data) psi.data = psi.data / state_norm r.set_initial_value(r.y / state_norm, r.t) else: psi.data = state_vectorize(r.y) if expt_callback: # use callback method expt_ops(t, Qobj(psi)) else: # calculate all the expectation values, or output rho if no operators if n_expt_op == 0: output.states.append(Qobj(psi)) # copy psi/rho else: for m in range(0, n_expt_op): output.expect[m][t_idx] = expect(expt_ops[m], psi) r.integrate(r.t + dt) t_idx += 1 if not opt.rhs_reuse and odeconfig.tdname != None: try: os.remove(odeconfig.tdname + ".pyx") except: pass return output
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.Odedata` An instance of the class :class:`qutip.Odedata`, which contains either an *array* of expectation values for the times specified by `tlist`. """ if options == None: options = Odeoptions() options.nsteps = 2500 # 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_e_ops = len(e_ops) n_tsteps = len(tlist) dt = tlist[1] - tlist[0] if n_e_ops == 0: result_list = [] else: result_list = [] for op in e_ops: if op.isherm and rho0.isherm: result_list.append(np.zeros(n_tsteps)) else: result_list.append(np.zeros(n_tsteps, dtype=complex)) # # transform the initial density matrix and the e_ops opterators to the # eigenbasis # if ekets != None: rho0 = rho0.transform(ekets) for n in arange(len(e_ops)): e_ops[n] = e_ops[n].transform(ekets, False) # # setup integrator # initial_vector = mat2vec(rho0.full()) r = scipy.integrate.ode(cyq_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 # rho = Qobj(rho0) t_idx = 0 for t in tlist: if not r.successful(): break rho.data = vec2mat(r.y) # calculate all the expectation values, or output rho if no operators if n_e_ops == 0: result_list.append(Qobj(rho)) else: for m in range(0, n_e_ops): result_list[m][t_idx] = expect(e_ops[m], rho) r.integrate(r.t + dt) t_idx += 1 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 NORMALIZED. """ 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) rm = sp.spdiags( diags, 0, N, N, format='csr' ) #populates diagonal terms using truncated operator expression elif method == 'analytic': rm = sp.spdiags( (1.0 + n)**(-1.0) * (n / (1.0 + n))**(i), 0, N, N, format='csr') #populates diagonal terms using analytic values else: raise ValueError( "'method' keyword argument must be 'operator' or 'analytic'") return Qobj(rm)
def __init__(self, q=np.array([]), s=np.array([])): if isinstance(s, (int, float, complex)): s = np.array([s]) if (not np.any(q)) and (len(s) == 0): self.ampl = np.array([]) self.rates = np.array([]) self.dims = [[1, 1]] self.shape = [1, 1] if np.any(q) and (len(s) == 0): if isinstance(q, eseries): self.ampl = q.ampl self.rates = q.rates self.dims = q.dims self.shape = q.shape elif isinstance(q, (np.ndarray, list)): ind = np.shape(q) num = ind[0] #number of elements in q sh = np.array([Qobj(x).shape for x in q]) if any(sh != sh[0]): raise TypeError('All amplitudes must have same dimension.') self.ampl = np.array([x for x in q]) self.rates = np.zeros(ind) self.dims = self.ampl[0].dims self.shape = self.ampl[0].shape elif isinstance(q, Qobj): qo = Qobj(q) self.ampl = np.array([qo]) self.rates = np.array([0]) self.dims = qo.dims self.shape = qo.shape else: self.ampl = np.array([q]) self.rates = np.array([0]) self.dims = [[1, 1]] self.shape = [1, 1] if np.any(q) and len(s) != 0: if isinstance(q, (np.ndarray, list)): ind = np.shape(q) num = ind[0] sh = np.array([Qobj(q[x]).shape for x in range(0, num)]) if np.any(sh != sh[0]): raise TypeError('All amplitudes must have same dimension.') self.ampl = np.array([Qobj(q[x]) for x in range(0, num)]) self.dims = self.ampl[0].dims self.shape = self.ampl[0].shape else: num = 1 self.ampl = np.array([Qobj(q)]) self.dims = self.ampl[0].dims self.shape = self.ampl[0].shape if isinstance(s, (int, complex, float)): if num != 1: raise TypeError( 'Number of rates must match number of members in object array.' ) self.rates = np.array([s]) elif isinstance(s, (np.ndarray, list)): if len(s) != num: raise TypeError( 'Number of rates must match number of members in object array.' ) self.rates = np.array(s) if len(self.ampl) != 0: zipped = list( zip(self.rates, self.ampl )) #combine arrays so that they can be sorted together zipped.sort() #sort rates from lowest to highest rates, ampl = list(zip(*zipped)) #get back rates and ampl self.ampl = np.array(ampl) self.rates = np.array(rates)