def test_ComplexSuperApply(self): """ Superoperator: Efficient numerics and reference return same result, acting on non-composite system """ rho_list = list(map(rand_dm, [2, 3, 2, 3, 2])) rho_input = tensor(rho_list) superop = kraus_to_super(rand_kraus_map(3)) analytic_result = rho_list analytic_result[1] = Qobj(vec2mat(superop.data.todense() * mat2vec(analytic_result[1].data.todense()))) analytic_result[3] = Qobj(vec2mat(superop.data.todense() * mat2vec(analytic_result[3].data.todense()))) analytic_result = tensor(analytic_result) naive_result = subsystem_apply(rho_input, superop, [False, True, False, True, False], reference=True) naive_diff = (analytic_result - naive_result).data.todense() assert_(norm(naive_diff) < 1e-12) efficient_result = subsystem_apply(rho_input, superop, [False, True, False, True, False]) efficient_diff = (efficient_result - analytic_result).data.todense() assert_(norm(efficient_diff) < 1e-12)
def qpt(U, op_basis_list): """ Calculate the quantum process tomography chi matrix for a given (possibly nonunitary) transformation matrix U, which transforms a density matrix in vector form according to: vec(rho) = U * vec(rho0) or rho = vec2mat(U * mat2vec(rho0)) U can be calculated for an open quantum system using the QuTiP propagator function. """ E_ops = [] # loop over all index permutations for inds in index_permutations([len(op_list) for op_list in op_basis_list]): # loop over all composite systems E_op_list = [op_basis_list[k][inds[k]] for k in range(len(op_basis_list))] E_ops.append(tensor(E_op_list)) EE_ops = [spre(E1) * spost(E2.dag()) for E1 in E_ops for E2 in E_ops] M = hstack([mat2vec(EE.full()) for EE in EE_ops]) Uvec = mat2vec(U.full()) chi_vec = la.solve(M, Uvec) return vec2mat(chi_vec)
def test_ComplexSuperApply(self): """ Superoperator: Efficient numerics and reference return same result, acting on non-composite system """ tol = 1e-10 rho_list = list(map(rand_dm, [2, 3, 2, 3, 2])) rho_input = tensor(rho_list) superop = kraus_to_super(rand_kraus_map(3)) analytic_result = rho_list analytic_result[1] = Qobj( vec2mat(superop.full() @ mat2vec(analytic_result[1].full()))) analytic_result[3] = Qobj( vec2mat(superop.full() @ mat2vec(analytic_result[3].full()))) analytic_result = tensor(analytic_result) naive_result = subsystem_apply(rho_input, superop, [False, True, False, True, False], reference=True) naive_diff = (analytic_result - naive_result).full() naive_diff_norm = norm(naive_diff) assert_(naive_diff_norm < tol, msg="ComplexSuper: naive_diff_norm {} " "is beyond tolerance {}".format(naive_diff_norm, tol)) efficient_result = subsystem_apply(rho_input, superop, [False, True, False, True, False]) efficient_diff = (efficient_result - analytic_result).full() efficient_diff_norm = norm(efficient_diff) assert_(efficient_diff_norm < tol, msg="ComplexSuper: efficient_diff_norm {} " "is beyond tolerance {}".format(efficient_diff_norm, tol))
def test_ComplexSuperApply(self): """ Superoperator: Efficient numerics and reference return same result, acting on non-composite system """ rho_list = list(map(rand_dm, [2, 3, 2, 3, 2])) rho_input = tensor(rho_list) superop = kraus_to_super(rand_kraus_map(3)) analytic_result = rho_list analytic_result[1] = Qobj( vec2mat(superop.data.todense() * mat2vec(analytic_result[1].data.todense()))) analytic_result[3] = Qobj( vec2mat(superop.data.todense() * mat2vec(analytic_result[3].data.todense()))) analytic_result = tensor(analytic_result) naive_result = subsystem_apply(rho_input, superop, [False, True, False, True, False], reference=True) naive_diff = (analytic_result - naive_result).data.todense() assert_(norm(naive_diff) < 1e-12) efficient_result = subsystem_apply(rho_input, superop, [False, True, False, True, False]) efficient_diff = (efficient_result - analytic_result).data.todense() assert_(norm(efficient_diff) < 1e-12)
def test_vec_to_eigbasis(): "BR Tools : vector to eigenbasis" N = 10 for kk in range(50): H = rand_herm(N,0.5) h = H.full('F') R = rand_dm(N,0.5) r = mat2vec(R.full()).ravel() ans = mat2vec(R.transform(H.eigenstates()[1]).full()).ravel() out = _test_vec_to_eigbasis(h, r) assert_(np.allclose(ans,out))
def test_vec_to_eigbasis(): "BR Tools : vector to eigenbasis" N = 10 for kk in range(50): H = rand_herm(N, 0.5) h = H.full('F') R = rand_dm(N, 0.5) r = mat2vec(R.full()).ravel() ans = mat2vec(R.transform(H.eigenstates()[1]).full()).ravel() out = _test_vec_to_eigbasis(h, r) assert_(np.allclose(ans, out))
def test_eigvec_to_fockbasis(): "BR Tools : eigvector to fockbasis" N = 10 for kk in range(50): H = rand_herm(N,0.5) h = H.full('F') R = rand_dm(N,0.5) r = mat2vec(R.full()).ravel() eigvals = np.zeros(N,dtype=float) Z = _test_zheevr(H.full('F'), eigvals) eig_vec = mat2vec(R.transform(H.eigenstates()[1]).full()).ravel() out = _test_eigvec_to_fockbasis(eig_vec, Z, N) assert_(np.allclose(r,out))
def test_eigvec_to_fockbasis(): "BR Tools : eigvector to fockbasis" N = 10 for kk in range(50): H = rand_herm(N, 0.5) h = H.full('F') R = rand_dm(N, 0.5) r = mat2vec(R.full()).ravel() eigvals = np.zeros(N, dtype=float) Z = _test_zheevr(H.full('F'), eigvals) eig_vec = mat2vec(R.transform(H.eigenstates()[1]).full()).ravel() out = _test_eigvec_to_fockbasis(eig_vec, Z, N) assert_(np.allclose(r, out))
def smepdpsolve_generic(ssdata, options, progress_bar): """ For internal use. .. note:: Experimental. """ if debug: print(inspect.stack()[0][3]) N_store = len(ssdata.tlist) N_substeps = ssdata.nsubsteps N = N_store * N_substeps dt = (ssdata.tlist[1] - ssdata.tlist[0]) / N_substeps NT = ssdata.ntraj data = Odedata() data.solver = "smepdpsolve" data.times = ssdata.tlist data.expect = np.zeros((len(ssdata.e_ops), N_store), dtype=complex) data.jump_times = [] data.jump_op_idx = [] # Liouvillian for the deterministic part. # needs to be modified for TD systems L = liouvillian_fast(ssdata.H, ssdata.c_ops) progress_bar.start(ssdata.ntraj) for n in range(ssdata.ntraj): progress_bar.update(n) rho_t = mat2vec(ssdata.rho0.full()).ravel() states_list, jump_times, jump_op_idx = \ _smepdpsolve_single_trajectory(data, L, dt, ssdata.tlist, N_store, N_substeps, rho_t, ssdata.c_ops, ssdata.e_ops) data.states.append(states_list) data.jump_times.append(jump_times) data.jump_op_idx.append(jump_op_idx) progress_bar.finished() # average density matrices if options.average_states and np.any(data.states): data.states = [sum(state_list).unit() for state_list in data.states] # average data.expect = data.expect / ssdata.ntraj # standard error if NT > 1: data.se = (data.ss - NT * (data.expect ** 2)) / (NT * (NT - 1)) else: data.se = None return data
def _initialize_data(self, L, rho, dt, c_ops, backwards): L_list = [] control_indices = [] if not (c_ops is None or len(c_ops) == 0): # in principle, we could convert c_ops to a Lindbladian, here raise NotImplementedError("c_ops not implemented") for (i, spec) in enumerate(L): if isinstance(spec, qutip.Qobj): l_op = spec l_coeff = 1 elif isinstance(spec, list) and isinstance(spec[0], qutip.Qobj): l_op = spec[0] l_coeff = spec[1] control_indices.append(i) else: raise ValueError( "Incorrect specification of time-dependent Liouvillian") if l_op.type == 'super': L_list.append([l_op.data, l_coeff, False]) else: raise ValueError( "Incorrect specification of time-dependent Liouvillian") self._L_list = L_list self._control_indices = control_indices if rho.type == 'oper': self._y = mat2vec(rho.full()).ravel('F') # initial state else: raise ValueError("rho must be a density matrix")
def smesolve_generic(H, rho0, tlist, c_ops, sc_ops, e_ops, rhs, d1, d2, d2_len, ntraj, nsubsteps, options, progress_bar): """ internal .. note:: Experimental. """ if debug: print(inspect.stack()[0][3]) N_store = len(tlist) N_substeps = nsubsteps N = N_store * N_substeps dt = (tlist[1] - tlist[0]) / N_substeps data = Odedata() data.solver = "smesolve" data.times = tlist data.expect = np.zeros((len(e_ops), N_store), dtype=complex) # pre-compute collapse operator combinations that are commonly needed # when evaluating the RHS of stochastic master equations A_ops = [] for c_idx, c in enumerate(sc_ops): # xxx: precompute useful operator expressions... cdc = c.dag() * c Ldt = spre(c) * spost(c.dag()) - 0.5 * spre(cdc) - 0.5 * spost(cdc) LdW = spre(c) + spost(c.dag()) Lm = spre(c) + spost(c.dag()) # currently same as LdW A_ops.append([Ldt.data, LdW.data, Lm.data]) # Liouvillian for the deterministic part L = liouvillian_fast(H, c_ops) # needs to be modified for TD systems progress_bar.start(ntraj) for n in range(ntraj): progress_bar.update(n) rho_t = mat2vec(rho0.full()) states_list = _smesolve_single_trajectory( L, dt, tlist, N_store, N_substeps, rho_t, A_ops, e_ops, data, rhs, d1, d2, d2_len) # if average -> average... data.states.append(states_list) progress_bar.finished() # average data.expect = data.expect / ntraj return data
def smesolve_generic(H, rho0, tlist, c_ops, e_ops, rhs, d1, d2, ntraj, nsubsteps): """ internal .. note:: Experimental. """ if debug: print(inspect.stack()[0][3]) N_store = len(tlist) N_substeps = nsubsteps N = N_store * N_substeps dt = (tlist[1] - tlist[0]) / N_substeps print("N = %d. dt=%.2e" % (N, dt)) data = Odedata() data.expect = np.zeros((len(e_ops), N_store), dtype=complex) # pre-compute collapse operator combinations that are commonly needed # when evaluating the RHS of stochastic master equations A_ops = [] for c_idx, c in enumerate(c_ops): # xxx: precompute useful operator expressions... cdc = c.dag() * c Ldt = spre(c) * spost(c.dag()) - 0.5 * spre(cdc) - 0.5 * spost(cdc) LdW = spre(c) + spost(c.dag()) Lm = spre(c) + spost(c.dag()) # currently same as LdW A_ops.append([Ldt.data, LdW.data, Lm.data]) # Liouvillian for the unitary part L = -1.0j * (spre(H) - spost(H)) # XXX: should we split the ME in stochastic # and deterministic collapse operators here? progress_acc = 0.0 for n in range(ntraj): if debug and (100 * float(n) / ntraj) >= progress_acc: print("Progress: %.2f" % (100 * float(n) / ntraj)) progress_acc += 10.0 rho_t = mat2vec(rho0.full()) states_list = _smesolve_single_trajectory( L, dt, tlist, N_store, N_substeps, rho_t, A_ops, e_ops, data, rhs, d1, d2 ) # if average -> average... data.states.append(states_list) # average data.expect = data.expect / ntraj return data
def _mesolve_const(H, rho0, tlist, c_op_list, e_ops, args, opt, progress_bar): """ Evolve the density matrix using an ODE solver, for constant hamiltonian and collapse operators. """ if debug: print(inspect.stack()[0][3]) # # check initial state # if isket(rho0): # if initial state is a ket and no collapse operator where given, # fall back on the unitary schrodinger equation solver if len(c_op_list) == 0 and isoper(H): return _sesolve_const(H, rho0, tlist, e_ops, args, opt, progress_bar) # Got a wave function as initial state: convert to density matrix. rho0 = ket2dm(rho0) # # construct liouvillian # if opt.tidy: H = H.tidyup(opt.atol) L = liouvillian(H, c_op_list) # # setup integrator # initial_vector = mat2vec(rho0.full()).ravel('F') if issuper(rho0): r = scipy.integrate.ode(_ode_super_func) r.set_f_params(L.data) else: if opt.use_openmp and L.data.nnz >= qset.openmp_thresh: r = scipy.integrate.ode(cy_ode_rhs_openmp) r.set_f_params(L.data.data, L.data.indices, L.data.indptr, opt.openmp_threads) else: r = scipy.integrate.ode(cy_ode_rhs) r.set_f_params(L.data.data, L.data.indices, L.data.indptr) # r = scipy.integrate.ode(_ode_rho_test) # r.set_f_params(L.data) r.set_integrator('zvode', method=opt.method, order=opt.order, atol=opt.atol, rtol=opt.rtol, nsteps=opt.nsteps, first_step=opt.first_step, min_step=opt.min_step, max_step=opt.max_step) r.set_initial_value(initial_vector, tlist[0]) # # call generic ODE code # return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
def _smepdpsolve_generic(sso, options, progress_bar): """ For internal use. See smepdpsolve. """ if debug: logger.debug(inspect.stack()[0][3]) N_store = len(sso.times) N_substeps = sso.nsubsteps dt = (sso.times[1] - sso.times[0]) / N_substeps nt = sso.ntraj data = Result() data.solver = "smepdpsolve" data.times = sso.times data.expect = np.zeros((len(sso.e_ops), N_store), dtype=complex) data.jump_times = [] data.jump_op_idx = [] # Liouvillian for the deterministic part. # needs to be modified for TD systems L = liouvillian(sso.H, sso.c_ops) progress_bar.start(sso.ntraj) for n in range(sso.ntraj): progress_bar.update(n) rho_t = mat2vec(sso.rho0.full()).ravel() states_list, jump_times, jump_op_idx = \ _smepdpsolve_single_trajectory(data, L, dt, sso.times, N_store, N_substeps, rho_t, sso.rho0.dims, sso.c_ops, sso.e_ops) data.states.append(states_list) data.jump_times.append(jump_times) data.jump_op_idx.append(jump_op_idx) progress_bar.finished() # average density matrices if options.average_states and np.any(data.states): data.states = [ sum([data.states[m][n] for m in range(nt)]).unit() for n in range(len(data.times)) ] # average data.expect = data.expect / sso.ntraj # standard error if nt > 1: data.se = (data.ss - nt * (data.expect**2)) / (nt * (nt - 1)) else: data.se = None return data
def _mesolve_const(H, rho0, tlist, c_op_list, e_ops, args, opt, progress_bar): """ Evolve the density matrix using an ODE solver, for constant hamiltonian and collapse operators. """ if debug: print(inspect.stack()[0][3]) # # check initial state # if isket(rho0): # if initial state is a ket and no collapse operator where given, # fall back on the unitary schrodinger equation solver if len(c_op_list) == 0 and isoper(H): return _sesolve_const(H, rho0, tlist, e_ops, args, opt, progress_bar) # Got a wave function as initial state: convert to density matrix. rho0 = ket2dm(rho0) # # construct liouvillian # if opt.tidy: H = H.tidyup(opt.atol) L = liouvillian(H, c_op_list) # # setup integrator # initial_vector = mat2vec(rho0.full()).ravel('F') if issuper(rho0): r = scipy.integrate.ode(_ode_super_func) r.set_f_params(L.data) else: r = scipy.integrate.ode(cy_ode_rhs) r.set_f_params(L.data.data, L.data.indices, L.data.indptr) # r = scipy.integrate.ode(_ode_rho_test) # r.set_f_params(L.data) r.set_integrator('zvode', method=opt.method, order=opt.order, atol=opt.atol, rtol=opt.rtol, nsteps=opt.nsteps, first_step=opt.first_step, min_step=opt.min_step, max_step=opt.max_step) r.set_initial_value(initial_vector, tlist[0]) # # call generic ODE code # return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
def _smepdpsolve_generic(sso, options, progress_bar): """ For internal use. See smepdpsolve. """ if debug: logger.debug(inspect.stack()[0][3]) N_store = len(sso.times) N_substeps = sso.nsubsteps dt = (sso.times[1] - sso.times[0]) / N_substeps nt = sso.ntraj data = Result() data.solver = "smepdpsolve" data.times = sso.times data.expect = np.zeros((len(sso.e_ops), N_store), dtype=complex) data.jump_times = [] data.jump_op_idx = [] # Liouvillian for the deterministic part. # needs to be modified for TD systems L = liouvillian(sso.H, sso.c_ops) progress_bar.start(sso.ntraj) for n in range(sso.ntraj): progress_bar.update(n) rho_t = mat2vec(sso.rho0.full()).ravel() states_list, jump_times, jump_op_idx = \ _smepdpsolve_single_trajectory(data, L, dt, sso.times, N_store, N_substeps, rho_t, sso.rho0.dims, sso.c_ops, sso.e_ops) data.states.append(states_list) data.jump_times.append(jump_times) data.jump_op_idx.append(jump_op_idx) progress_bar.finished() # average density matrices if options.average_states and np.any(data.states): data.states = [sum([data.states[m][n] for m in range(nt)]).unit() for n in range(len(data.times))] # average data.expect = data.expect / sso.ntraj # standard error if nt > 1: data.se = (data.ss - nt * (data.expect ** 2)) / (nt * (nt - 1)) else: data.se = None return data
def test_vector_roundtrip(): "BR Tools : vector roundtrip transform" N = 10 for kk in range(50): H = rand_herm(N, 0.5) h = H.full('F') R = rand_dm(N, 0.5) r = mat2vec(R.full()).ravel() out = _test_vector_roundtrip(h, r) assert_(np.allclose(r, out))
def test_vector_roundtrip(): "BR Tools : vector roundtrip transform" N = 10 for kk in range(50): H = rand_herm(N,0.5) h = H.full('F') R = rand_dm(N,0.5) r = mat2vec(R.full()).ravel() out = _test_vector_roundtrip(h,r) assert_(np.allclose(r,out))
def countstat_current(L, c_ops=None, rhoss=None, J_ops=None): """ Calculate the current corresponding a system Liouvillian `L` and a list of current collapse operators `c_ops` or current superoperators `J_ops` (either must be specified). Optionally the steadystate density matrix `rhoss` and a list of current superoperators `J_ops` can be specified. If either of these are omitted they are computed internally. Parameters ---------- L : :class:`qutip.Qobj` Qobj representing the system Liouvillian. c_ops : array / list (optional) List of current collapse operators. rhoss : :class:`qutip.Qobj` (optional) The steadystate density matrix corresponding the system Liouvillian `L`. J_ops : array / list (optional) List of current superoperators. Returns -------- I : array The currents `I` corresponding to each current collapse operator `c_ops` (or, equivalently, each current superopeator `J_ops`). """ if J_ops is None: if c_ops is None: raise ValueError("c_ops must be given if J_ops is not") J_ops = [sprepost(c, c.dag()) for c in c_ops] if rhoss is None: if c_ops is None: raise ValueError("c_ops must be given if rhoss is not") rhoss = steadystate(L, c_ops) rhoss_vec = mat2vec(rhoss.full()).ravel() N = len(J_ops) I = np.zeros(N) for i, Ji in enumerate(J_ops): I[i] = expect_rho_vec(Ji.data, rhoss_vec, 1) return I
def _spectrum_pi(H, wlist, c_ops, a_op, b_op, use_pinv=False): """ Internal function for calculating the spectrum of the correlation function :math:`\left<A(\\tau)B(0)\\right>`. """ L = H if issuper(H) else liouvillian(H, c_ops) tr_mat = tensor([qeye(n) for n in L.dims[0][0]]) N = np.prod(L.dims[0][0]) A = L.full() b = spre(b_op).full() a = spre(a_op).full() tr_vec = np.transpose(mat2vec(tr_mat.full())) rho_ss = steadystate(L) rho = np.transpose(mat2vec(rho_ss.full())) I = np.identity(N * N) P = np.kron(np.transpose(rho), tr_vec) Q = I - P spectrum = np.zeros(len(wlist)) for idx, w in enumerate(wlist): if use_pinv: MMR = np.linalg.pinv(-1.0j * w * I + A) else: MMR = np.dot(Q, np.linalg.solve(-1.0j * w * I + A, Q)) s = np.dot(tr_vec, np.dot(a, np.dot(MMR, np.dot(b, np.transpose(rho))))) spectrum[idx] = -2 * np.real(s[0, 0]) return spectrum
def test_SimpleSuperApply(self): """ Non-composite system, operator on Liouville space. """ rho_3 = rand_dm(3) superop = kraus_to_super(rand_kraus_map(3)) analytic_result = vec2mat(superop.data.todense() * mat2vec(rho_3.data.todense())) naive_result = subsystem_apply(rho_3, superop, [True], reference=True) naive_diff = (analytic_result - naive_result).data.todense() assert_(norm(naive_diff) < 1e-12) efficient_result = subsystem_apply(rho_3, superop, [True]) efficient_diff = (efficient_result - analytic_result).data.todense() assert_(norm(efficient_diff) < 1e-12)
def mesolve_const_checkpoint(H, rho0, tlist, c_op_list, e_ops, args, opt, progress_bar, save, subdir): """ Evolve the density matrix using an ODE solver, for constant hamiltonian and collapse operators. """ if debug: print(inspect.stack()[0][3]) # # check initial state # if isket(rho0): # Got a wave function as initial state: convert to density matrix. rho0 = ket2dm(rho0) # # construct liouvillian # if opt.tidy: H = H.tidyup(opt.atol) L = liouvillian(H, c_op_list) # # setup integrator # initial_vector = mat2vec(rho0.full()).ravel() r = scipy.integrate.ode(cy_ode_rhs) r.set_f_params(L.data.data, L.data.indices, L.data.indptr) r.set_integrator('zvode', method=opt.method, order=opt.order, atol=opt.atol, rtol=opt.rtol, nsteps=opt.nsteps, first_step=opt.first_step, min_step=opt.min_step, max_step=opt.max_step) r.set_initial_value(initial_vector, tlist[0]) # # call generic ODE code # return generic_ode_solve_checkpoint(r, rho0, tlist, e_ops, opt, progress_bar, save, subdir)
def propagation_multiple(self): ''' This function will save all the propators during the evolution, which may be memory consuming. ''' if len(self.Hamiltonian_derivative) < 2: raise TypeError( 'this is a multiparameter scenario, the length of dH has to be larger than 1!' ) num = len(self.times) dim = self.freeHamiltonian.dims[0][0] dt = self.times[1] - self.times[0] dL = [ Qobj(liouvillian(self.Hamiltonian_derivative[i]).full()) for i in range(0, len(self.Hamiltonian_derivative)) ] D = [[[] for i in range(0, num + 1)] for k in range(0, num + 1)] rhovec = [[] for i in range(0, num)] drhovec = [[[] for k in range(0, len(self.Hamiltonian_derivative))] for i in range(0, num)] rhovec[0] = Qobj(mat2vec(self.rho_initial.full())) for para_i in range(0, len(self.Hamiltonian_derivative)): drhovec[0][para_i] = dt * dL[para_i] * rhovec[0] D[0][0] = self.evolution(0) D[1][0] = qeye(dim**2) for di in range(1, num): tnow = self.times[0] + dt * di D[di + 1][di] = qeye(dim**2) D[di][di] = self.evolution(tnow) D[0][di] = D[di][di] * D[0][di - 1] rhovec[di] = D[di][di] * rhovec[di - 1] for para_i in range(0, len(self.Hamiltonian_derivative)): drho_temp = dt * dL[para_i] * rhovec[di] for dj in range(1, di): D[di - dj][di] = D[di - dj + 1][di] * D[di - dj][di - dj] drho_temp += dt * D[di - dj + 1][di] * dL[para_i] * rhovec[di - dj] drhovec[di][para_i] = drho_temp self.rho = rhovec self.rho_derivative = drhovec self.propagator = D
def test_SimpleSuperApply(self): """ Non-composite system, operator on Liouville space. """ tol = 1e-12 rho_3 = rand_dm(3) superop = kraus_to_super(rand_kraus_map(3)) analytic_result = vec2mat(superop.full() @ mat2vec(rho_3.full())) naive_result = subsystem_apply(rho_3, superop, [True], reference=True) naive_diff = (analytic_result - naive_result).full() naive_diff_norm = norm(naive_diff) assert_(naive_diff_norm < tol, msg="SimpleSuper: naive_diff_norm {} " "is beyond tolerance {}".format(naive_diff_norm, tol)) efficient_result = subsystem_apply(rho_3, superop, [True]) efficient_diff = (efficient_result - analytic_result).full() efficient_diff_norm = norm(efficient_diff) assert_(efficient_diff_norm < tol, msg="SimpleSuper: efficient_diff_norm {} " "is beyond tolerance {}".format(efficient_diff_norm, tol))
def test_SimpleSuperApply(self): """ Non-composite system, operator on Liouville space. """ tol = 1e-12 rho_3 = rand_dm(3) superop = kraus_to_super(rand_kraus_map(3)) analytic_result = vec2mat(superop.data.todense() * mat2vec(rho_3.data.todense())) naive_result = subsystem_apply(rho_3, superop, [True], reference=True) naive_diff = (analytic_result - naive_result).data.todense() naive_diff_norm = norm(naive_diff) assert_( naive_diff_norm < tol, msg="SimpleSuper: naive_diff_norm {} " "is beyond tolerance {}".format(naive_diff_norm, tol), ) efficient_result = subsystem_apply(rho_3, superop, [True]) efficient_diff = (efficient_result - analytic_result).data.todense() efficient_diff_norm = norm(efficient_diff) assert_( efficient_diff_norm < tol, msg="SimpleSuper: efficient_diff_norm {} " "is beyond tolerance {}".format(efficient_diff_norm, tol), )
def _mesolve_list_func_td(H_list, rho0, tlist, c_list, e_ops, args, opt, progress_bar): """ Internal function for solving the master equation. See mesolve for usage. """ if debug: print(inspect.stack()[0][3]) # # check initial state # if isket(rho0): rho0 = rho0 * rho0.dag() # # construct liouvillian in list-function format # L_list = [] if opt.rhs_with_state: constant_func = lambda x, y, z: 1.0 else: constant_func = lambda x, y: 1.0 # add all hamitonian terms to the lagrangian list for h_spec in H_list: if isinstance(h_spec, Qobj): h = h_spec h_coeff = constant_func elif isinstance(h_spec, list) and isinstance(h_spec[0], Qobj): h = h_spec[0] h_coeff = h_spec[1] else: raise TypeError("Incorrect specification of time-dependent " + "Hamiltonian (expected callback function)") if isoper(h): L_list.append([(-1j * (spre(h) - spost(h))).data, h_coeff, False]) elif issuper(h): L_list.append([h.data, h_coeff, False]) else: raise TypeError("Incorrect specification of time-dependent " + "Hamiltonian (expected operator or superoperator)") # add all collapse operators to the lagrangian list for c_spec in c_list: if isinstance(c_spec, Qobj): c = c_spec c_coeff = constant_func c_square = False elif isinstance(c_spec, list) and isinstance(c_spec[0], Qobj): c = c_spec[0] c_coeff = c_spec[1] c_square = True else: raise TypeError("Incorrect specification of time-dependent " + "collapse operators (expected callback function)") if isoper(c): cdc = c.dag() * c L_list.append([ liouvillian_fast(None, [c], data_only=True), c_coeff, c_square ]) elif issuper(c): L_list.append([c.data, c_coeff, c_square]) else: raise TypeError("Incorrect specification of time-dependent " + "collapse operators (expected operator or " + "superoperator)") # # setup integrator # initial_vector = mat2vec(rho0.full()).ravel() if opt.rhs_with_state: r = scipy.integrate.ode(drho_list_td_with_state) else: r = scipy.integrate.ode(drho_list_td) r.set_integrator('zvode', method=opt.method, order=opt.order, atol=opt.atol, rtol=opt.rtol, nsteps=opt.nsteps, first_step=opt.first_step, min_step=opt.min_step, max_step=opt.max_step) r.set_initial_value(initial_vector, tlist[0]) r.set_f_params(L_list, args) # # call generic ODE code # return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
def propagator(H, t, c_op_list=[], args={}, options=None, parallel=False, progress_bar=None, **kwargs): """ 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. args : list/array/dictionary Parameters to callback functions for time-dependent Hamiltonians and collapse operators. options : :class:`qutip.Options` with options for the ODE solver. parallel : bool {False, True} Run the propagator in parallel mode. progress_bar: BaseProgressBar Optional instance of BaseProgressBar, or a subclass thereof, for showing the progress of the simulation. By default no progress bar is used, and if set to True a TextProgressBar will be used. Returns ------- a : qobj Instance representing the propagator :math:`U(t)`. """ kw = _default_kwargs() if 'num_cpus' in kwargs: num_cpus = kwargs['num_cpus'] else: num_cpus = kw['num_cpus'] if progress_bar is None: progress_bar = BaseProgressBar() elif progress_bar is True: progress_bar = TextProgressBar() if options is None: options = Options() options.rhs_reuse = True rhs_clear() if isinstance(t, (int, float, np.integer, np.floating)): tlist = [0, t] else: tlist = t td_type = _td_format_check(H, c_op_list, solver='me')[2] if td_type > 0: rhs_generate(H, c_op_list, args=args, options=options) if isinstance(H, (types.FunctionType, types.BuiltinFunctionType, functools.partial)): H0 = H(0.0, args) elif isinstance(H, list): H0 = H[0][0] if isinstance(H[0], list) else H[0] else: H0 = H if len(c_op_list) == 0 and H0.isoper: # calculate propagator for the wave function N = H0.shape[0] dims = H0.dims u = np.zeros([N, N, len(tlist)], dtype=complex) if parallel: output = parallel_map(_parallel_sesolve,range(N), task_args=(N,H,tlist,args,options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N): for k, t in enumerate(tlist): u[:, n, k] = output[n].states[k].full().T else: progress_bar.start(N) for n in range(0, N): progress_bar.update(n) psi0 = basis(N, n) output = sesolve(H, psi0, tlist, [], args, options) for k, t in enumerate(tlist): u[:, n, k] = output.states[k].full().T progress_bar.finished() # 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], [], [], args, options) # for n in range(0, N): # u[:,n] = psi_t_list[n][1].full().T elif len(c_op_list) == 0 and H0.issuper: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) N = H0.shape[0] sqrt_N = int(np.sqrt(N)) dims = H0.dims u = np.zeros([N, N, len(tlist)], dtype=complex) if parallel: output = parallel_map(_parallel_mesolve,range(N * N), task_args=(sqrt_N,H,tlist,c_op_list,args,options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N * N): for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output[n].states[k].full()).T else: progress_bar.start(N) for n in range(0, N): progress_bar.update(n) col_idx, row_idx = np.unravel_index(n,(sqrt_N,sqrt_N)) rho0 = Qobj(sp.csr_matrix(([1],([row_idx],[col_idx])), shape=(sqrt_N,sqrt_N), dtype=complex)) output = mesolve(H, rho0, tlist, [], [], args, options) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T progress_bar.finished() else: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) N = H0.shape[0] dims = [H0.dims, H0.dims] u = np.zeros([N * N, N * N, len(tlist)], dtype=complex) if parallel: output = parallel_map(_parallel_mesolve,range(N * N), task_args=(N,H,tlist,c_op_list,args,options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N * N): for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output[n].states[k].full()).T else: progress_bar.start(N * N) for n in range(N * N): progress_bar.update(n) col_idx, row_idx = np.unravel_index(n,(N,N)) rho0 = Qobj(sp.csr_matrix(([1],([row_idx],[col_idx])), shape=(N,N), dtype=complex)) output = mesolve(H, rho0, tlist, c_op_list, [], args, options) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T progress_bar.finished() if len(tlist) == 2: return Qobj(u[:, :, 1], dims=dims) else: return np.array([Qobj(u[:, :, k], dims=dims) for k in range(len(tlist))], dtype=object)
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.data = vec2mat(r.y) 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
def _mesolve_func_td(L_func, rho0, tlist, c_op_list, e_ops, args, opt, progress_bar): """ Evolve the density matrix using an ODE solver with time dependent Hamiltonian. """ if debug: print(inspect.stack()[0][3]) # # check initial state # if isket(rho0): rho0 = ket2dm(rho0) # # construct liouvillian # new_args = None if len(c_op_list) > 0: L_data = liouvillian(None, c_op_list).data else: n, m = rho0.shape L_data = sp.csr_matrix((n ** 2, m ** 2), dtype=complex) if type(args) is dict: new_args = {} for key in args: if isinstance(args[key], Qobj): if isoper(args[key]): new_args[key] = (-1j * (spre(args[key]) - spost(args[key]))).data else: new_args[key] = args[key].data else: new_args[key] = args[key] elif type(args) is list or type(args) is tuple: new_args = [] for arg in args: if isinstance(arg, Qobj): if isoper(arg): new_args.append((-1j * (spre(arg) - spost(arg))).data) else: new_args.append(arg.data) else: new_args.append(arg) if type(args) is tuple: new_args = tuple(new_args) else: if isinstance(args, Qobj): if isoper(args): new_args = (-1j * (spre(args) - spost(args))).data else: new_args = args.data else: new_args = args # # setup integrator # initial_vector = mat2vec(rho0.full()).ravel() if not opt.rhs_with_state: r = scipy.integrate.ode(cy_ode_rho_func_td) else: r = scipy.integrate.ode(_ode_rho_func_td_with_state) r.set_integrator( "zvode", method=opt.method, order=opt.order, atol=opt.atol, rtol=opt.rtol, nsteps=opt.nsteps, first_step=opt.first_step, min_step=opt.min_step, max_step=opt.max_step, ) r.set_initial_value(initial_vector, tlist[0]) r.set_f_params(L_data, L_func, new_args) # # call generic ODE code # return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
def _mesolve_list_func_td(H_list, rho0, tlist, c_list, e_ops, args, opt, progress_bar): """ Internal function for solving the master equation. See mesolve for usage. """ if debug: print(inspect.stack()[0][3]) # # check initial state # if isket(rho0): rho0 = rho0 * rho0.dag() # # construct liouvillian in list-function format # L_list = [] if opt.rhs_with_state: constant_func = lambda x, y, z: 1.0 else: constant_func = lambda x, y: 1.0 # add all hamitonian terms to the lagrangian list for h_spec in H_list: if isinstance(h_spec, Qobj): h = h_spec h_coeff = constant_func elif isinstance(h_spec, list) and isinstance(h_spec[0], Qobj): h = h_spec[0] h_coeff = h_spec[1] else: raise TypeError("Incorrect specification of time-dependent " + "Hamiltonian (expected callback function)") if isoper(h): L_list.append([(-1j * (spre(h) - spost(h))).data, h_coeff, False]) elif issuper(h): L_list.append([h.data, h_coeff, False]) else: raise TypeError( "Incorrect specification of time-dependent " + "Hamiltonian (expected operator or superoperator)" ) # add all collapse operators to the liouvillian list for c_spec in c_list: if isinstance(c_spec, Qobj): c = c_spec c_coeff = constant_func c_square = False elif isinstance(c_spec, list) and isinstance(c_spec[0], Qobj): c = c_spec[0] c_coeff = c_spec[1] c_square = True else: raise TypeError( "Incorrect specification of time-dependent " + "collapse operators (expected callback function)" ) if isoper(c): L_list.append([liouvillian(None, [c], data_only=True), c_coeff, c_square]) elif issuper(c): L_list.append([c.data, c_coeff, c_square]) else: raise TypeError( "Incorrect specification of time-dependent " + "collapse operators (expected operator or " + "superoperator)" ) # # setup integrator # initial_vector = mat2vec(rho0.full()).ravel() if opt.rhs_with_state: r = scipy.integrate.ode(drho_list_td_with_state) else: r = scipy.integrate.ode(drho_list_td) r.set_integrator( "zvode", method=opt.method, order=opt.order, atol=opt.atol, rtol=opt.rtol, nsteps=opt.nsteps, first_step=opt.first_step, min_step=opt.min_step, max_step=opt.max_step, ) r.set_initial_value(initial_vector, tlist[0]) r.set_f_params(L_list, args) # # call generic ODE code # return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
def _mesolve_func_td(L_func, rho0, tlist, c_op_list, e_ops, args, opt, progress_bar): """! Evolve the density matrix using an ODE solver with time dependent Hamiltonian. """ if debug: print(inspect.stack()[0][3]) # # check initial state # if isket(rho0): rho0 = ket2dm(rho0) # # construct liouvillian # new_args = None if len(c_op_list) > 0: L_data = liouvillian_fast(None, c_op_list).data else: n, m = rho0.shape L_data = sp.csr_matrix((n**2, m**2), dtype=complex) if type(args) is dict: new_args = {} for key in args: if isinstance(args[key], Qobj): if isoper(args[key]): new_args[key] = (-1j * (spre(args[key]) - spost(args[key]))).data else: new_args[key] = args[key].data else: new_args[key] = args[key] elif type(args) is list: new_args = [] for arg in args: if isinstance(arg, Qobj): if isoper(arg): new_args.append((-1j * (spre(arg) - spost(arg))).data) else: new_args.append(arg.data) else: new_args.append(arg) else: if isinstance(args, Qobj): if isoper(args): new_args = (-1j * (spre(args) - spost(args))).data else: new_args = args.data else: new_args = args # # setup integrator # initial_vector = mat2vec(rho0.full()).ravel() if not opt.rhs_with_state: r = scipy.integrate.ode(cy_ode_rho_func_td) else: r = scipy.integrate.ode(_ode_rho_func_td_with_state) r.set_integrator('zvode', method=opt.method, order=opt.order, atol=opt.atol, rtol=opt.rtol, nsteps=opt.nsteps, first_step=opt.first_step, min_step=opt.min_step, max_step=opt.max_step) r.set_initial_value(initial_vector, tlist[0]) r.set_f_params(L_data, L_func, new_args) # # call generic ODE code # return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
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 propagator(H, t, c_op_list=[], args={}, options=None, unitary_mode='batch', parallel=False, progress_bar=None, _safe_mode=True, **kwargs): r""" 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. args : list/array/dictionary Parameters to callback functions for time-dependent Hamiltonians and collapse operators. options : :class:`qutip.Options` with options for the ODE solver. unitary_mode = str ('batch', 'single') Solve all basis vectors simulaneously ('batch') or individually ('single'). parallel : bool {False, True} Run the propagator in parallel mode. This will override the unitary_mode settings if set to True. progress_bar: BaseProgressBar Optional instance of BaseProgressBar, or a subclass thereof, for showing the progress of the simulation. By default no progress bar is used, and if set to True a TextProgressBar will be used. Returns ------- a : qobj Instance representing the propagator :math:`U(t)`. """ kw = _default_kwargs() if 'num_cpus' in kwargs: num_cpus = kwargs['num_cpus'] else: num_cpus = kw['num_cpus'] if progress_bar is None: progress_bar = BaseProgressBar() elif progress_bar is True: progress_bar = TextProgressBar() if options is None: options = Options() options.rhs_reuse = True rhs_clear() if isinstance(t, (int, float, np.integer, np.floating)): tlist = [0, t] else: tlist = t if _safe_mode: _solver_safety_check(H, None, c_ops=c_op_list, e_ops=[], args=args) td_type = _td_format_check(H, c_op_list, solver='me') if isinstance( H, (types.FunctionType, types.BuiltinFunctionType, functools.partial)): H0 = H(0.0, args) if unitary_mode == 'batch': # batch don't work with function Hamiltonian unitary_mode = 'single' elif isinstance(H, list): H0 = H[0][0] if isinstance(H[0], list) else H[0] else: H0 = H if len(c_op_list) == 0 and H0.isoper: # calculate propagator for the wave function N = H0.shape[0] dims = H0.dims if parallel: unitary_mode = 'single' u = np.zeros([N, N, len(tlist)], dtype=complex) output = parallel_map(_parallel_sesolve, range(N), task_args=(N, H, tlist, args, options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N): for k, t in enumerate(tlist): u[:, n, k] = output[n].states[k].full().T else: if unitary_mode == 'single': output = sesolve(H, qeye(dims[0]), tlist, [], args, options, _safe_mode=False) if len(tlist) == 2: return output.states[-1] else: return output.states elif unitary_mode == 'batch': u = np.zeros(len(tlist), dtype=object) _rows = np.array([(N + 1) * m for m in range(N)]) _cols = np.zeros_like(_rows) _data = np.ones_like(_rows, dtype=complex) psi0 = Qobj(sp.coo_matrix((_data, (_rows, _cols))).tocsr()) if td_type[1] > 0 or td_type[2] > 0: H2 = [] for k in range(len(H)): if isinstance(H[k], list): H2.append([tensor(qeye(N), H[k][0]), H[k][1]]) else: H2.append(tensor(qeye(N), H[k])) else: H2 = tensor(qeye(N), H) options.normalize_output = False output = sesolve(H2, psi0, tlist, [], args=args, options=options, _safe_mode=False) for k, t in enumerate(tlist): u[k] = sp_reshape(output.states[k].data, (N, N)) unit_row_norm(u[k].data, u[k].indptr, u[k].shape[0]) u[k] = u[k].T.tocsr() else: raise Exception('Invalid unitary mode.') elif len(c_op_list) == 0 and H0.issuper: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) unitary_mode = 'single' N = H0.shape[0] sqrt_N = int(np.sqrt(N)) dims = H0.dims u = np.zeros([N, N, len(tlist)], dtype=complex) if parallel: output = parallel_map(_parallel_mesolve, range(N * N), task_args=(sqrt_N, H, tlist, c_op_list, args, options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N * N): for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output[n].states[k].full()).T else: rho0 = qeye(N, N) rho0.dims = [[sqrt_N, sqrt_N], [sqrt_N, sqrt_N]] output = mesolve(H, psi0, tlist, [], args, options, _safe_mode=False) if len(tlist) == 2: return output.states[-1] else: return output.states else: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) unitary_mode = 'single' N = H0.shape[0] dims = [H0.dims, H0.dims] u = np.zeros([N * N, N * N, len(tlist)], dtype=complex) if parallel: output = parallel_map(_parallel_mesolve, range(N * N), task_args=(N, H, tlist, c_op_list, args, options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N * N): for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output[n].states[k].full()).T else: progress_bar.start(N * N) for n in range(N * N): progress_bar.update(n) col_idx, row_idx = np.unravel_index(n, (N, N)) rho0 = Qobj( sp.csr_matrix(([1], ([row_idx], [col_idx])), shape=(N, N), dtype=complex)) output = mesolve(H, rho0, tlist, c_op_list, [], args, options, _safe_mode=False) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T progress_bar.finished() if len(tlist) == 2: if unitary_mode == 'batch': return Qobj(u[-1], dims=dims) else: return Qobj(u[:, :, 1], dims=dims) else: if unitary_mode == 'batch': return np.array([Qobj(u[k], dims=dims) for k in range(len(tlist))], dtype=object) else: return np.array( [Qobj(u[:, :, k], dims=dims) for k in range(len(tlist))], dtype=object)
def _mesolve_func_td(L_func, rho0, tlist, c_op_list, expt_ops, args, opt): """! Evolve the density matrix using an ODE solver with time dependent Hamiltonian. """ if debug: print(inspect.stack()[0][3]) # # check initial state # if isket(rho0): rho0 = ket2dm(rho0) # # construct liouvillian # if len(c_op_list) > 0: L = 0 for c in c_op_list: cdc = c.dag() * c L += spre(c) * spost(c.dag()) - 0.5 * spre(cdc) - 0.5 * spost(cdc) L_func_and_args = [L_func, L.data] else: n, m = rho0.shape L_func_and_args = [L_func, sp.lil_matrix((n**2, m**2)).tocsr()] for arg in args: if isinstance(arg, Qobj): if isoper(arg): L_func_and_args.append((-1j * (spre(arg) - spost(arg))).data) else: L_func_and_args.append(arg.data) else: L_func_and_args.append(arg) # # setup integrator # initial_vector = mat2vec(rho0.full()) r = scipy.integrate.ode(_ode_rho_func_td) r.set_integrator('zvode', method=opt.method, order=opt.order, atol=opt.atol, rtol=opt.rtol, nsteps=opt.nsteps, first_step=opt.first_step, min_step=opt.min_step, max_step=opt.max_step) r.set_initial_value(initial_vector, tlist[0]) r.set_f_params(L_func_and_args) # # call generic ODE code # return _generic_ode_solve(r, rho0, tlist, expt_ops, opt, vec2mat)
def _mesolve_list_str_td(H_list, rho0, tlist, c_list, e_ops, args, opt, progress_bar): """ Internal function for solving the master equation. See mesolve for usage. """ if debug: print(inspect.stack()[0][3]) # # check initial state: must be a density matrix # if isket(rho0): rho0 = rho0 * rho0.dag() # # construct liouvillian # Lconst = 0 Ldata = [] Linds = [] Lptrs = [] Lcoeff = [] Lobj = [] me_cops_coeff = [] me_cops_obj = [] me_cops_obj_flags = [] # loop over all hamiltonian terms, convert to superoperator form and # add the data of sparse matrix representation to n_not_const_terms = 0 for h_spec in H_list: if isinstance(h_spec, Qobj): h = h_spec if isoper(h): Lconst += -1j * (spre(h) - spost(h)) elif issuper(h): Lconst += h else: raise TypeError("Incorrect specification of time-dependent " + "Hamiltonian (expected operator or " + "superoperator)") elif isinstance(h_spec, list): n_not_const_terms +=1 h = h_spec[0] h_coeff = h_spec[1] if isoper(h): L = -1j * (spre(h) - spost(h)) elif issuper(h): L = h else: raise TypeError("Incorrect specification of time-dependent " + "Hamiltonian (expected operator or " + "superoperator)") Ldata.append(L.data.data) Linds.append(L.data.indices) Lptrs.append(L.data.indptr) if isinstance(h_coeff, Cubic_Spline): Lobj.append(h_coeff.coeffs) Lcoeff.append(h_coeff) else: raise TypeError("Incorrect specification of time-dependent " + "Hamiltonian (expected string format)") # loop over all collapse operators for c_spec in c_list: if isinstance(c_spec, Qobj): c = c_spec if isoper(c): cdc = c.dag() * c Lconst += spre(c) * spost(c.dag()) - 0.5 * spre(cdc) \ - 0.5 * spost(cdc) elif issuper(c): Lconst += c else: raise TypeError("Incorrect specification of time-dependent " + "Liouvillian (expected operator or " + "superoperator)") elif isinstance(c_spec, list): n_not_const_terms +=1 c = c_spec[0] c_coeff = c_spec[1] if isoper(c): cdc = c.dag() * c L = spre(c) * spost(c.dag()) - 0.5 * spre(cdc) \ - 0.5 * spost(cdc) if isinstance(c_coeff, Cubic_Spline): me_cops_obj.append(c_coeff.coeffs) me_cops_obj_flags.append(n_not_const_terms) me_cops_coeff.append(c_coeff) else: c_coeff = "(" + c_coeff + ")**2" Lcoeff.append(c_coeff) elif issuper(c): L = c if isinstance(c_coeff, Cubic_Spline): me_cops_obj.append(c_coeff.coeffs) me_cops_obj_flags.append(-n_not_const_terms) me_cops_coeff.append(c_coeff) else: Lcoeff.append(c_coeff) else: raise TypeError("Incorrect specification of time-dependent " + "Liouvillian (expected operator or " + "superoperator)") Ldata.append(L.data.data) Linds.append(L.data.indices) Lptrs.append(L.data.indptr) #Lcoeff.append(c_coeff) else: raise TypeError("Incorrect specification of time-dependent " + "collapse operators (expected string format)") #prepend the constant part of the liouvillian if Lconst != 0: Ldata = [Lconst.data.data]+Ldata Linds = [Lconst.data.indices]+Linds Lptrs = [Lconst.data.indptr]+Lptrs Lcoeff = ["1.0"]+Lcoeff else: me_cops_obj_flags = [kk-1 for kk in me_cops_obj_flags] # the total number of liouvillian terms (hamiltonian terms + # collapse operators) n_L_terms = len(Ldata) n_td_cops = len(me_cops_obj) # Check which components should use OPENMP omp_components = None if qset.has_openmp: if opt.use_openmp: omp_components = openmp_components(Lptrs) # # setup ode args string: we expand the list Ldata, Linds and Lptrs into # and explicit list of parameters # string_list = [] for k in range(n_L_terms): string_list.append("Ldata[%d], Linds[%d], Lptrs[%d]" % (k, k, k)) # Add H object terms to ode args string for k in range(len(Lobj)): string_list.append("Lobj[%d]" % k) # Add cop object terms to end of ode args string for k in range(len(me_cops_obj)): string_list.append("me_cops_obj[%d]" % k) for name, value in args.items(): if isinstance(value, np.ndarray): string_list.append(name) else: string_list.append(str(value)) parameter_string = ",".join(string_list) # # generate and compile new cython code if necessary # if not opt.rhs_reuse or config.tdfunc is None: if opt.rhs_filename is None: config.tdname = "rhs" + str(os.getpid()) + str(config.cgen_num) else: config.tdname = opt.rhs_filename cgen = Codegen(h_terms=len(Lcoeff), h_tdterms=Lcoeff, c_td_splines=me_cops_coeff, c_td_spline_flags=me_cops_obj_flags, args=args, config=config, use_openmp=opt.use_openmp, omp_components=omp_components, omp_threads=opt.openmp_threads) cgen.generate(config.tdname + ".pyx") code = compile('from ' + config.tdname + ' import cy_td_ode_rhs', '<string>', 'exec') exec(code, globals()) config.tdfunc = cy_td_ode_rhs # # setup integrator # initial_vector = mat2vec(rho0.full()).ravel('F') if issuper(rho0): r = scipy.integrate.ode(_td_ode_rhs_super) code = compile('r.set_f_params([' + parameter_string + '])', '<string>', 'exec') else: r = scipy.integrate.ode(config.tdfunc) code = compile('r.set_f_params(' + parameter_string + ')', '<string>', 'exec') r.set_integrator('zvode', method=opt.method, order=opt.order, atol=opt.atol, rtol=opt.rtol, nsteps=opt.nsteps, first_step=opt.first_step, min_step=opt.min_step, max_step=opt.max_step) r.set_initial_value(initial_vector, tlist[0]) exec(code, locals(), args) # # call generic ODE code # return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
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 ------- 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() 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.") 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.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 smesolve_generic(ssdata, options, progress_bar): """ internal .. note:: Experimental. """ if debug: print(inspect.stack()[0][3]) N_store = len(ssdata.tlist) N_substeps = ssdata.nsubsteps N = N_store * N_substeps dt = (ssdata.tlist[1] - ssdata.tlist[0]) / N_substeps NT = ssdata.ntraj data = Odedata() data.solver = "smesolve" data.times = ssdata.tlist data.expect = np.zeros((len(ssdata.e_ops), N_store), dtype=complex) data.ss = np.zeros((len(ssdata.e_ops), N_store), dtype=complex) data.noise = [] data.measurement = [] # pre-compute suporoperator operator combinations that are commonly needed # when evaluating the RHS of stochastic master equations A_ops = [] for c_idx, c in enumerate(ssdata.sc_ops): n = c.dag() * c A_ops.append([spre(c).data, spost(c).data, spre(c.dag()).data, spost(c.dag()).data, spre(n).data, spost(n).data, (spre(c) * spost(c.dag())).data, lindblad_dissipator(c, data_only=True)]) s_e_ops = [spre(e) for e in ssdata.e_ops] # Liouvillian for the deterministic part. # needs to be modified for TD systems L = liouvillian_fast(ssdata.H, ssdata.c_ops) progress_bar.start(ssdata.ntraj) for n in range(ssdata.ntraj): progress_bar.update(n) rho_t = mat2vec(ssdata.state0.full()).ravel() noise = ssdata.noise[n] if ssdata.noise else None states_list, dW, m = _smesolve_single_trajectory( L, dt, ssdata.tlist, N_store, N_substeps, rho_t, A_ops, s_e_ops, data, ssdata.rhs, ssdata.d1, ssdata.d2, ssdata.d2_len, ssdata.homogeneous, ssdata.distribution, ssdata.args, store_measurement=ssdata.store_measurement, store_states=ssdata.store_states, noise=noise) data.states.append(states_list) data.noise.append(dW) data.measurement.append(m) progress_bar.finished() # average density matrices if options.average_states and np.any(data.states): data.states = [sum(state_list).unit() for state_list in data.states] # average data.expect = data.expect / NT # standard error if NT > 1: data.se = (data.ss - NT * (data.expect ** 2)) / (NT * (NT - 1)) else: data.se = None # convert complex data to real if hermitian data.expect = [np.real(data.expect[n,:]) if e.isherm else data.expect[n,:] for n, e in enumerate(ssdata.e_ops)] return data
def _mesolve_list_str_td(H_list, rho0, tlist, c_list, e_ops, args, opt, progress_bar): """ Internal function for solving the master equation. See mesolve for usage. """ if debug: print(inspect.stack()[0][3]) # # check initial state: must be a density matrix # if isket(rho0): rho0 = rho0 * rho0.dag() # # construct liouvillian # Lconst = 0 Ldata = [] Linds = [] Lptrs = [] Lcoeff = [] # loop over all hamiltonian terms, convert to superoperator form and # add the data of sparse matrix representation to for h_spec in H_list: if isinstance(h_spec, Qobj): h = h_spec if isoper(h): Lconst += -1j * (spre(h) - spost(h)) elif issuper(h): Lconst += h else: raise TypeError("Incorrect specification of time-dependent " + "Hamiltonian (expected operator or " + "superoperator)") elif isinstance(h_spec, list): h = h_spec[0] h_coeff = h_spec[1] if isoper(h): L = -1j * (spre(h) - spost(h)) elif issuper(h): L = h else: raise TypeError("Incorrect specification of time-dependent " + "Hamiltonian (expected operator or " + "superoperator)") Ldata.append(L.data.data) Linds.append(L.data.indices) Lptrs.append(L.data.indptr) Lcoeff.append(h_coeff) else: raise TypeError("Incorrect specification of time-dependent " + "Hamiltonian (expected string format)") # loop over all collapse operators for c_spec in c_list: if isinstance(c_spec, Qobj): c = c_spec if isoper(c): cdc = c.dag() * c Lconst += spre(c) * spost(c.dag()) - 0.5 * spre(cdc) \ - 0.5 * spost(cdc) elif issuper(c): Lconst += c else: raise TypeError("Incorrect specification of time-dependent " + "Liouvillian (expected operator or " + "superoperator)") elif isinstance(c_spec, list): c = c_spec[0] c_coeff = c_spec[1] if isoper(c): cdc = c.dag() * c L = spre(c) * spost(c.dag()) - 0.5 * spre(cdc) \ - 0.5 * spost(cdc) c_coeff = "(" + c_coeff + ")**2" elif issuper(c): L = c else: raise TypeError("Incorrect specification of time-dependent " + "Liouvillian (expected operator or " + "superoperator)") Ldata.append(L.data.data) Linds.append(L.data.indices) Lptrs.append(L.data.indptr) Lcoeff.append(c_coeff) else: raise TypeError("Incorrect specification of time-dependent " + "collapse operators (expected string format)") # add the constant part of the lagrangian if Lconst != 0: Ldata.append(Lconst.data.data) Linds.append(Lconst.data.indices) Lptrs.append(Lconst.data.indptr) Lcoeff.append("1.0") # the total number of liouvillian terms (hamiltonian terms + # collapse operators) n_L_terms = len(Ldata) # # setup ode args string: we expand the list Ldata, Linds and Lptrs into # and explicit list of parameters # string_list = [] for k in range(n_L_terms): string_list.append("Ldata[%d], Linds[%d], Lptrs[%d]" % (k, k, k)) for name, value in args.items(): string_list.append(str(value)) parameter_string = ",".join(string_list) # # generate and compile new cython code if necessary # if not opt.rhs_reuse or odeconfig.tdfunc is None: if opt.rhs_filename is None: odeconfig.tdname = "rhs" + str(odeconfig.cgen_num) else: odeconfig.tdname = opt.rhs_filename cgen = Codegen(h_terms=n_L_terms, h_tdterms=Lcoeff, args=args, odeconfig=odeconfig) cgen.generate(odeconfig.tdname + ".pyx") code = compile('from ' + odeconfig.tdname + ' import cyq_td_ode_rhs', '<string>', 'exec') exec(code, globals()) odeconfig.tdfunc = cyq_td_ode_rhs # # setup integrator # initial_vector = mat2vec(rho0.full()).ravel() r = scipy.integrate.ode(odeconfig.tdfunc) r.set_integrator('zvode', method=opt.method, order=opt.order, atol=opt.atol, rtol=opt.rtol, nsteps=opt.nsteps, first_step=opt.first_step, min_step=opt.min_step, max_step=opt.max_step) r.set_initial_value(initial_vector, tlist[0]) code = compile('r.set_f_params(' + parameter_string + ')', '<string>', 'exec') exec(code) # # call generic ODE code # return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
def _mesolve_list_td(H_func, rho0, tlist, c_op_list, e_ops, args, opt, progress_bar): """! Evolve the density matrix using an ODE solver with time dependent Hamiltonian. """ if debug: print(inspect.stack()[0][3]) # # check initial state # if isket(rho0): # if initial state is a ket and no collapse operator where given, # fall back on the unitary schrodinger equation solver if len(c_op_list) == 0: return _sesolve_list_td(H_func, rho0, tlist, e_ops, args, opt) # Got a wave function as initial state: convert to density matrix. rho0 = ket2dm(rho0) # # construct liouvillian # if len(H_func) != 2: raise TypeError('Time-dependent Hamiltonian list must have two terms.') if not isinstance(H_func[0], (list, np.ndarray)) or len(H_func[0]) <= 1: raise TypeError('Time-dependent Hamiltonians must be a list ' + 'with two or more terms') if (not isinstance(H_func[1], (list, np.ndarray))) or \ (len(H_func[1]) != (len(H_func[0]) - 1)): raise TypeError('Time-dependent coefficients must be list with ' + 'length N-1 where N is the number of ' + 'Hamiltonian terms.') if opt.rhs_reuse and odeconfig.tdfunc is None: rhs_generate(H_func, args) lenh = len(H_func[0]) if opt.tidy: H_func[0] = [(H_func[0][k]).tidyup() for k in range(lenh)] L_func = [[liouvillian_fast(H_func[0][0], c_op_list)], H_func[1]] for m in range(1, lenh): L_func[0].append(liouvillian_fast(H_func[0][m], [])) # create data arrays for time-dependent RHS function Ldata = [L_func[0][k].data.data for k in range(lenh)] Linds = [L_func[0][k].data.indices for k in range(lenh)] Lptrs = [L_func[0][k].data.indptr for k in range(lenh)] # setup ode args string string = "" for k in range(lenh): string += ("Ldata[%d], Linds[%d], Lptrs[%d]," % (k, k, k)) if args: td_consts = args.items() for elem in td_consts: string += str(elem[1]) if elem != td_consts[-1]: string += (",") # run code generator if not opt.rhs_reuse or odeconfig.tdfunc is None: if opt.rhs_filename is None: odeconfig.tdname = "rhs" + str(odeconfig.cgen_num) else: odeconfig.tdname = opt.rhs_filename cgen = Codegen(h_terms=n_L_terms, h_tdterms=Lcoeff, args=args, odeconfig=odeconfig) cgen.generate(odeconfig.tdname + ".pyx") code = compile('from ' + odeconfig.tdname + ' import cyq_td_ode_rhs', '<string>', 'exec') exec(code, globals()) odeconfig.tdfunc = cyq_td_ode_rhs # # setup integrator # initial_vector = mat2vec(rho0.full()).ravel() r = scipy.integrate.ode(odeconfig.tdfunc) r.set_integrator('zvode', method=opt.method, order=opt.order, atol=opt.atol, rtol=opt.rtol, nsteps=opt.nsteps, first_step=opt.first_step, min_step=opt.min_step, max_step=opt.max_step) r.set_initial_value(initial_vector, tlist[0]) code = compile('r.set_f_params(' + string + ')', '<string>', 'exec') exec(code) # # call generic ODE code # return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
def countstat_current_noise(L, c_ops, wlist=None, rhoss=None, J_ops=None, sparse=True, method='direct'): """ Compute the cross-current noise spectrum for a list of collapse operators `c_ops` corresponding to monitored currents, given the system Liouvillian `L`. The current collapse operators `c_ops` should be part of the dissipative processes in `L`, but the `c_ops` given here does not necessarily need to be all collapse operators contributing to dissipation in the Liouvillian. Optionally, the steadystate density matrix `rhoss` and the current operators `J_ops` correpsonding to the current collapse operators `c_ops` can also be specified. If either of `rhoss` and `J_ops` are omitted, they will be computed internally. 'wlist' is an optional list of frequencies at which to evaluate the noise spectrum. Note: The default method is a direct solution using dense matrices, as sparse matrix methods fail for some examples of small systems. For larger systems it is reccomended to use the sparse solver with the direct method, as it avoids explicit calculation of the pseudo-inverse, as described in page 67 of "Electrons in nanostructures" C. Flindt, PhD Thesis, available online: http://orbit.dtu.dk/fedora/objects/orbit:82314/datastreams/file_4732600/content Parameters ---------- L : :class:`qutip.Qobj` Qobj representing the system Liouvillian. c_ops : array / list List of current collapse operators. rhoss : :class:`qutip.Qobj` (optional) The steadystate density matrix corresponding the system Liouvillian `L`. wlist : array / list (optional) List of frequencies at which to evaluate (if none are given, evaluates at zero frequency) J_ops : array / list (optional) List of current superoperators. sparse : bool Flag that indicates whether to use sparse or dense matrix methods when computing the pseudo inverse. Default is false, as sparse solvers can fail for small systems. For larger systems the sparse solvers are reccomended. Returns -------- I, S : tuple of arrays The currents `I` corresponding to each current collapse operator `c_ops` (or, equivalently, each current superopeator `J_ops`) and the zero-frequency cross-current correlation `S`. """ if rhoss is None: rhoss = steadystate(L, c_ops) if J_ops is None: J_ops = [sprepost(c, c.dag()) for c in c_ops] N = len(J_ops) I = np.zeros(N) if wlist is None: S = np.zeros((N, N,1)) wlist=[0.] else: S = np.zeros((N, N,len(wlist))) if sparse == False: rhoss_vec = mat2vec(rhoss.full()).ravel() for k,w in enumerate(wlist): R = pseudo_inverse(L, rhoss=rhoss, w= w, sparse = sparse, method=method) for i, Ji in enumerate(J_ops): for j, Jj in enumerate(J_ops): if i == j: I[i] = expect_rho_vec(Ji.data, rhoss_vec, 1) S[i, j,k] = I[i] S[i, j,k] -= expect_rho_vec((Ji * R * Jj + Jj * R * Ji).data, rhoss_vec, 1) else: if method == "direct": N = np.prod(L.dims[0][0]) rhoss_vec = operator_to_vector(rhoss) tr_op = tensor([identity(n) for n in L.dims[0][0]]) tr_op_vec = operator_to_vector(tr_op) Pop = sp.kron(rhoss_vec.data, tr_op_vec.data.T, format='csr') Iop = sp.eye(N*N, N*N, format='csr') Q = Iop - Pop for k,w in enumerate(wlist): if w != 0.0: L_temp = 1.0j*w*spre(tr_op) + L else: #At zero frequency some solvers fail for small systems. #Adding a small finite frequency of order 1e-15 #helps prevent the solvers from throwing an exception. L_temp = 1.0j*(1e-15)*spre(tr_op) + L if not settings.has_mkl: A = L_temp.data.tocsc() else: A = L_temp.data.tocsr() A.sort_indices() rhoss_vec = mat2vec(rhoss.full()).ravel() for j, Jj in enumerate(J_ops): Qj = Q.dot( Jj.data.dot( rhoss_vec)) try: if settings.has_mkl: X_rho_vec_j = mkl_spsolve(A,Qj) else: X_rho_vec_j = sp.linalg.splu(A, permc_spec ='COLAMD').solve(Qj) except: X_rho_vec_j = sp.linalg.lsqr(A,Qj)[0] for i, Ji in enumerate(J_ops): Qi = Q.dot( Ji.data.dot(rhoss_vec)) try: if settings.has_mkl: X_rho_vec_i = mkl_spsolve(A,Qi) else: X_rho_vec_i = sp.linalg.splu(A, permc_spec ='COLAMD').solve(Qi) except: X_rho_vec_i = sp.linalg.lsqr(A,Qi)[0] if i == j: I[i] = expect_rho_vec(Ji.data, rhoss_vec, 1) S[j, i, k] = I[i] S[j, i, k] -= (expect_rho_vec(Jj.data * Q, X_rho_vec_i, 1) + expect_rho_vec(Ji.data * Q, X_rho_vec_j, 1)) else: rhoss_vec = mat2vec(rhoss.full()).ravel() for k,w in enumerate(wlist): R = pseudo_inverse(L,rhoss=rhoss, w= w, sparse = sparse, method=method) for i, Ji in enumerate(J_ops): for j, Jj in enumerate(J_ops): if i == j: I[i] = expect_rho_vec(Ji.data, rhoss_vec, 1) S[i, j, k] = I[i] S[i, j, k] -= expect_rho_vec((Ji * R * Jj + Jj * R * Ji).data, rhoss_vec, 1) return I, S
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 propagator(H, t, c_op_list=[], args={}, options=None, unitary_mode='batch', parallel=False, progress_bar=None, **kwargs): """ 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. args : list/array/dictionary Parameters to callback functions for time-dependent Hamiltonians and collapse operators. options : :class:`qutip.Options` with options for the ODE solver. unitary_mode = str ('batch', 'single') Solve all basis vectors simulaneously ('batch') or individually ('single'). parallel : bool {False, True} Run the propagator in parallel mode. This will override the unitary_mode settings if set to True. progress_bar: BaseProgressBar Optional instance of BaseProgressBar, or a subclass thereof, for showing the progress of the simulation. By default no progress bar is used, and if set to True a TextProgressBar will be used. Returns ------- a : qobj Instance representing the propagator :math:`U(t)`. """ kw = _default_kwargs() if 'num_cpus' in kwargs: num_cpus = kwargs['num_cpus'] else: num_cpus = kw['num_cpus'] if progress_bar is None: progress_bar = BaseProgressBar() elif progress_bar is True: progress_bar = TextProgressBar() if options is None: options = Options() options.rhs_reuse = True rhs_clear() if isinstance(t, (int, float, np.integer, np.floating)): tlist = [0, t] else: tlist = t td_type = _td_format_check(H, c_op_list, solver='me') if isinstance(H, (types.FunctionType, types.BuiltinFunctionType, functools.partial)): H0 = H(0.0, args) elif isinstance(H, list): H0 = H[0][0] if isinstance(H[0], list) else H[0] else: H0 = H if len(c_op_list) == 0 and H0.isoper: # calculate propagator for the wave function N = H0.shape[0] dims = H0.dims if parallel: unitary_mode = 'single' u = np.zeros([N, N, len(tlist)], dtype=complex) output = parallel_map(_parallel_sesolve,range(N), task_args=(N,H, tlist,args,options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N): for k, t in enumerate(tlist): u[:, n, k] = output[n].states[k].full().T else: if unitary_mode == 'single': u = np.zeros([N, N, len(tlist)], dtype=complex) progress_bar.start(N) for n in range(0, N): progress_bar.update(n) psi0 = basis(N, n) output = sesolve(H, psi0, tlist, [], args, options, _safe_mode=False) for k, t in enumerate(tlist): u[:, n, k] = output.states[k].full().T progress_bar.finished() elif unitary_mode =='batch': u = np.zeros(len(tlist), dtype=object) _rows = np.array([(N+1)*m for m in range(N)]) _cols = np.zeros_like(_rows) _data = np.ones_like(_rows,dtype=complex) psi0 = Qobj(sp.coo_matrix((_data,(_rows,_cols))).tocsr()) if td_type[1] > 0 or td_type[2] > 0: H2 = [] for k in range(len(H)): if isinstance(H[k], list): H2.append([tensor(qeye(N), H[k][0]), H[k][1]]) else: H2.append(tensor(qeye(N), H[k])) else: H2 = tensor(qeye(N), H) output = sesolve(H2, psi0, tlist, [] , args = args, _safe_mode=False, options=Options(normalize_output=False)) for k, t in enumerate(tlist): u[k] = sp_reshape(output.states[k].data, (N, N)) unit_row_norm(u[k].data, u[k].indptr, u[k].shape[0]) u[k] = u[k].T.tocsr() else: raise Exception('Invalid unitary mode.') elif len(c_op_list) == 0 and H0.issuper: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) unitary_mode = 'single' N = H0.shape[0] sqrt_N = int(np.sqrt(N)) dims = H0.dims u = np.zeros([N, N, len(tlist)], dtype=complex) if parallel: output = parallel_map(_parallel_mesolve,range(N * N), task_args=(sqrt_N,H,tlist,c_op_list,args,options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N * N): for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output[n].states[k].full()).T else: progress_bar.start(N) for n in range(0, N): progress_bar.update(n) col_idx, row_idx = np.unravel_index(n,(sqrt_N,sqrt_N)) rho0 = Qobj(sp.csr_matrix(([1],([row_idx],[col_idx])), shape=(sqrt_N,sqrt_N), dtype=complex)) output = mesolve(H, rho0, tlist, [], [], args, options, _safe_mode=False) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T progress_bar.finished() else: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) unitary_mode = 'single' N = H0.shape[0] dims = [H0.dims, H0.dims] u = np.zeros([N * N, N * N, len(tlist)], dtype=complex) if parallel: output = parallel_map(_parallel_mesolve,range(N * N), task_args=(N,H,tlist,c_op_list,args,options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N * N): for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output[n].states[k].full()).T else: progress_bar.start(N * N) for n in range(N * N): progress_bar.update(n) col_idx, row_idx = np.unravel_index(n,(N,N)) rho0 = Qobj(sp.csr_matrix(([1],([row_idx],[col_idx])), shape=(N,N), dtype=complex)) output = mesolve(H, rho0, tlist, c_op_list, [], args, options, _safe_mode=False) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T progress_bar.finished() if len(tlist) == 2: if unitary_mode == 'batch': return Qobj(u[-1], dims=dims) else: return Qobj(u[:, :, 1], dims=dims) else: if unitary_mode == 'batch': return np.array([Qobj(u[k], dims=dims) for k in range(len(tlist))], dtype=object) else: return np.array([Qobj(u[:, :, k], dims=dims) for k in range(len(tlist))], dtype=object)
def _mesolve_list_str_td(H_list, rho0, tlist, c_list, e_ops, args, opt, progress_bar): """ Internal function for solving the master equation. See mesolve for usage. """ if debug: print(inspect.stack()[0][3]) # # check initial state: must be a density matrix # if isket(rho0): rho0 = rho0 * rho0.dag() # # construct liouvillian # Lconst = 0 Ldata = [] Linds = [] Lptrs = [] Lcoeff = [] # loop over all hamiltonian terms, convert to superoperator form and # add the data of sparse matrix representation to for h_spec in H_list: if isinstance(h_spec, Qobj): h = h_spec if isoper(h): Lconst += -1j * (spre(h) - spost(h)) elif issuper(h): Lconst += h else: raise TypeError( "Incorrect specification of time-dependent " + "Hamiltonian (expected operator or " + "superoperator)" ) elif isinstance(h_spec, list): h = h_spec[0] h_coeff = h_spec[1] if isoper(h): L = -1j * (spre(h) - spost(h)) elif issuper(h): L = h else: raise TypeError( "Incorrect specification of time-dependent " + "Hamiltonian (expected operator or " + "superoperator)" ) Ldata.append(L.data.data) Linds.append(L.data.indices) Lptrs.append(L.data.indptr) Lcoeff.append(h_coeff) else: raise TypeError("Incorrect specification of time-dependent " + "Hamiltonian (expected string format)") # loop over all collapse operators for c_spec in c_list: if isinstance(c_spec, Qobj): c = c_spec if isoper(c): cdc = c.dag() * c Lconst += spre(c) * spost(c.dag()) - 0.5 * spre(cdc) - 0.5 * spost(cdc) elif issuper(c): Lconst += c else: raise TypeError( "Incorrect specification of time-dependent " + "Liouvillian (expected operator or " + "superoperator)" ) elif isinstance(c_spec, list): c = c_spec[0] c_coeff = c_spec[1] if isoper(c): cdc = c.dag() * c L = spre(c) * spost(c.dag()) - 0.5 * spre(cdc) - 0.5 * spost(cdc) c_coeff = "(" + c_coeff + ")**2" elif issuper(c): L = c else: raise TypeError( "Incorrect specification of time-dependent " + "Liouvillian (expected operator or " + "superoperator)" ) Ldata.append(L.data.data) Linds.append(L.data.indices) Lptrs.append(L.data.indptr) Lcoeff.append(c_coeff) else: raise TypeError( "Incorrect specification of time-dependent " + "collapse operators (expected string format)" ) # add the constant part of the lagrangian if Lconst != 0: Ldata.append(Lconst.data.data) Linds.append(Lconst.data.indices) Lptrs.append(Lconst.data.indptr) Lcoeff.append("1.0") # the total number of liouvillian terms (hamiltonian terms + # collapse operators) n_L_terms = len(Ldata) # # setup ode args string: we expand the list Ldata, Linds and Lptrs into # and explicit list of parameters # string_list = [] for k in range(n_L_terms): string_list.append("Ldata[%d], Linds[%d], Lptrs[%d]" % (k, k, k)) for name, value in args.items(): if isinstance(value, np.ndarray): string_list.append(name) else: string_list.append(str(value)) parameter_string = ",".join(string_list) # # generate and compile new cython code if necessary # if not opt.rhs_reuse or config.tdfunc is None: if opt.rhs_filename is None: config.tdname = "rhs" + str(os.getpid()) + str(config.cgen_num) else: config.tdname = opt.rhs_filename cgen = Codegen(h_terms=n_L_terms, h_tdterms=Lcoeff, args=args, config=config) cgen.generate(config.tdname + ".pyx") code = compile("from " + config.tdname + " import cy_td_ode_rhs", "<string>", "exec") exec(code, globals()) config.tdfunc = cy_td_ode_rhs # # setup integrator # initial_vector = mat2vec(rho0.full()).ravel() r = scipy.integrate.ode(config.tdfunc) r.set_integrator( "zvode", method=opt.method, order=opt.order, atol=opt.atol, rtol=opt.rtol, nsteps=opt.nsteps, first_step=opt.first_step, min_step=opt.min_step, max_step=opt.max_step, ) r.set_initial_value(initial_vector, tlist[0]) code = compile("r.set_f_params(" + parameter_string + ")", "<string>", "exec") exec(code, locals(), args) # # call generic ODE code # return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
def propagator(H, t, c_op_list, args=None, options=None, sparse=False, progress_bar=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. args : list/array/dictionary Parameters to callback functions for time-dependent Hamiltonians and collapse operators. options : :class:`qutip.Options` with options for the ODE solver. progress_bar: BaseProgressBar Optional instance of BaseProgressBar, or a subclass thereof, for showing the progress of the simulation. By default no progress bar is used, and if set to True a TextProgressBar will be used. Returns ------- a : qobj Instance representing the propagator :math:`U(t)`. """ if progress_bar is None: progress_bar = BaseProgressBar() elif progress_bar is True: progress_bar = TextProgressBar() if options is None: options = Options() options.rhs_reuse = True rhs_clear() if isinstance(t, (int, float, np.integer, np.floating)): tlist = [0, t] else: tlist = t if isinstance( H, (types.FunctionType, types.BuiltinFunctionType, functools.partial)): H0 = H(0.0, args) elif isinstance(H, list): H0 = H[0][0] if isinstance(H[0], list) else H[0] else: H0 = H if len(c_op_list) == 0 and H0.isoper: # calculate propagator for the wave function N = H0.shape[0] dims = H0.dims u = np.zeros([N, N, len(tlist)], dtype=complex) progress_bar.start(N) for n in range(0, N): progress_bar.update(n) psi0 = basis(N, n) output = sesolve(H, psi0, tlist, [], args, options) for k, t in enumerate(tlist): u[:, n, k] = output.states[k].full().T progress_bar.finished() # 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], [], [], args, options) # for n in range(0, N): # u[:,n] = psi_t_list[n][1].full().T elif len(c_op_list) == 0 and H0.issuper: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) N = H0.shape[0] dims = H0.dims u = np.zeros([N, N, len(tlist)], dtype=complex) progress_bar.start(N) for n in range(0, N): progress_bar.update(n) psi0 = basis(N, n) rho0 = Qobj(vec2mat(psi0.full())) output = mesolve(H, rho0, tlist, [], [], args, options) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T progress_bar.finished() else: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) N = H0.shape[0] dims = [H0.dims, H0.dims] u = np.zeros([N * N, N * N, len(tlist)], dtype=complex) if sparse: progress_bar.start(N * N) for n in range(N * N): progress_bar.update(n) psi0 = basis(N * N, n) psi0.dims = [dims[0], 1] rho0 = vector_to_operator(psi0) output = mesolve(H, rho0, tlist, c_op_list, [], args, options) for k, t in enumerate(tlist): u[:, n, k] = operator_to_vector( output.states[k]).full(squeeze=True) progress_bar.finished() else: progress_bar.start(N * N) for n in range(N * N): progress_bar.update(n) psi0 = basis(N * N, n) rho0 = Qobj(vec2mat(psi0.full())) output = mesolve(H, rho0, tlist, c_op_list, [], args, options) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T progress_bar.finished() if len(tlist) == 2: return Qobj(u[:, :, 1], dims=dims) else: return [Qobj(u[:, :, k], dims=dims) for k in range(len(tlist))]
def _mesolve_list_td(H_func, rho0, tlist, c_op_list, e_ops, args, opt, progress_bar): """ Evolve the density matrix using an ODE solver with time dependent Hamiltonian. """ if debug: print(inspect.stack()[0][3]) # # check initial state # if isket(rho0): # if initial state is a ket and no collapse operator where given, # fall back on the unitary schrodinger equation solver if len(c_op_list) == 0: return _sesolve_list_td(H_func, rho0, tlist, e_ops, args, opt, progress_bar) # Got a wave function as initial state: convert to density matrix. rho0 = ket2dm(rho0) # # construct liouvillian # if len(H_func) != 2: raise TypeError("Time-dependent Hamiltonian list must have two terms.") if not isinstance(H_func[0], (list, np.ndarray)) or len(H_func[0]) <= 1: raise TypeError("Time-dependent Hamiltonians must be a list " + "with two or more terms") if (not isinstance(H_func[1], (list, np.ndarray))) or (len(H_func[1]) != (len(H_func[0]) - 1)): raise TypeError( "Time-dependent coefficients must be list with " + "length N-1 where N is the number of " + "Hamiltonian terms." ) if opt.rhs_reuse and config.tdfunc is None: rhs_generate(H_func, args) lenh = len(H_func[0]) if opt.tidy: H_func[0] = [(H_func[0][k]).tidyup() for k in range(lenh)] L_func = [[liouvillian(H_func[0][0], c_op_list)], H_func[1]] for m in range(1, lenh): L_func[0].append(liouvillian(H_func[0][m], [])) # create data arrays for time-dependent RHS function Ldata = [L_func[0][k].data.data for k in range(lenh)] Linds = [L_func[0][k].data.indices for k in range(lenh)] Lptrs = [L_func[0][k].data.indptr for k in range(lenh)] # setup ode args string string = "" for k in range(lenh): string += "Ldata[%d], Linds[%d], Lptrs[%d]," % (k, k, k) if args: td_consts = args.items() for elem in td_consts: string += str(elem[1]) if elem != td_consts[-1]: string += "," # run code generator if not opt.rhs_reuse or config.tdfunc is None: if opt.rhs_filename is None: config.tdname = "rhs" + str(os.getpid()) + str(config.cgen_num) else: config.tdname = opt.rhs_filename cgen = Codegen(h_terms=n_L_terms, h_tdterms=Lcoeff, args=args, config=config) cgen.generate(config.tdname + ".pyx") code = compile("from " + config.tdname + " import cy_td_ode_rhs", "<string>", "exec") exec(code, globals()) config.tdfunc = cy_td_ode_rhs # # setup integrator # initial_vector = mat2vec(rho0.full()).ravel() r = scipy.integrate.ode(config.tdfunc) r.set_integrator( "zvode", method=opt.method, order=opt.order, atol=opt.atol, rtol=opt.rtol, nsteps=opt.nsteps, first_step=opt.first_step, min_step=opt.min_step, max_step=opt.max_step, ) r.set_initial_value(initial_vector, tlist[0]) code = compile("r.set_f_params(" + string + ")", "<string>", "exec") exec(code) # # call generic ODE code # return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
def propagator(H, t, c_op_list, args=None, options=None, sparse=False): """ 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. args : list/array/dictionary Parameters to callback functions for time-dependent Hamiltonians and collapse operators. options : :class:`qutip.Options` with options for the ODE solver. Returns ------- a : qobj Instance representing the propagator :math:`U(t)`. """ if options is None: options = Options() options.rhs_reuse = True rhs_clear() if isinstance(t, (int, float, np.integer, np.floating)): tlist = [0, t] else: tlist = t if isinstance(H, (types.FunctionType, types.BuiltinFunctionType, functools.partial)): H0 = H(0.0, args) elif isinstance(H, list): H0 = H[0][0] if isinstance(H[0], list) else H[0] else: H0 = H if len(c_op_list) == 0 and H0.isoper: # calculate propagator for the wave function N = H0.shape[0] dims = H0.dims u = np.zeros([N, N, len(tlist)], dtype=complex) for n in range(0, N): psi0 = basis(N, n) output = sesolve(H, psi0, tlist, [], args, options) 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], [], [], args, options) # for n in range(0, N): # u[:,n] = psi_t_list[n][1].full().T elif len(c_op_list) == 0 and H0.issuper: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) N = H0.shape[0] dims = H0.dims u = np.zeros([N, N, len(tlist)], dtype=complex) for n in range(0, N): psi0 = basis(N, n) rho0 = Qobj(vec2mat(psi0.full())) output = mesolve(H, rho0, tlist, [], [], args, options) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T else: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) N = H0.shape[0] dims = [H0.dims, H0.dims] u = np.zeros([N * N, N * N, len(tlist)], dtype=complex) if sparse: for n in range(N * N): psi0 = basis(N * N, n) psi0.dims = [dims[0], 1] rho0 = vector_to_operator(psi0) output = mesolve(H, rho0, tlist, c_op_list, [], args, options) for k, t in enumerate(tlist): u[:, n, k] = operator_to_vector( output.states[k]).full(squeeze=True) else: for n in range(N * N): psi0 = basis(N * N, n) rho0 = Qobj(vec2mat(psi0.full())) output = mesolve(H, rho0, tlist, c_op_list, [], args, options) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T if len(tlist) == 2: return Qobj(u[:, :, 1], dims=dims) else: return [Qobj(u[:, :, k], dims=dims) for k in range(len(tlist))]
def _td_brmesolve(H, psi0, tlist, a_ops=[], e_ops=[], c_ops=[], use_secular=True, tol=qset.atol, options=None, progress_bar=None, _safe_mode=True): if isket(psi0): rho0 = ket2dm(psi0) else: rho0 = psi0 nrows = rho0.shape[0] H_terms = [] H_td_terms = [] H_obj = [] A_terms = [] A_td_terms = [] C_terms = [] C_td_terms = [] C_obj = [] spline_count = [0, 0] if isinstance(H, Qobj): H_terms.append(H.full('f')) H_td_terms.append('1') else: for kk, h in enumerate(H): if isinstance(h, Qobj): H_terms.append(h.full('f')) H_td_terms.append('1') elif isinstance(h, list): H_terms.append(h[0].full('f')) if isinstance(h[1], Cubic_Spline): H_obj.append(h[1].coeffs) spline_count[0] += 1 H_td_terms.append(h[1]) else: raise Exception('Invalid Hamiltonian specifiction.') for kk, c in enumerate(c_ops): if isinstance(c, Qobj): C_terms.append(c.full('f')) C_td_terms.append('1') elif isinstance(c, list): C_terms.append(c[0].full('f')) if isinstance(c[1], Cubic_Spline): C_obj.append(c[1].coeffs) spline_count[0] += 1 C_td_terms.append(c[1]) else: raise Exception('Invalid collape operator specifiction.') for kk, a in enumerate(a_ops): if isinstance(a, list): A_terms.append(a[0].full('f')) A_td_terms.append(a[1]) if isinstance(a[1], tuple): if not len(a[1]) == 2: raise Exception('Tuple must be len=2.') if isinstance(a[1][0], Cubic_Spline): spline_count[1] += 1 if isinstance(a[1][1], Cubic_Spline): spline_count[1] += 1 else: raise Exception('Invalid bath-coupling specifiction.') string_list = [] for kk, _ in enumerate(H_td_terms): string_list.append("H_terms[{0}]".format(kk)) for kk, _ in enumerate(H_obj): string_list.append("H_obj[{0}]".format(kk)) for kk, _ in enumerate(C_td_terms): string_list.append("C_terms[{0}]".format(kk)) for kk, _ in enumerate(C_obj): string_list.append("C_obj[{0}]".format(kk)) for kk, _ in enumerate(A_td_terms): string_list.append("A_terms[{0}]".format(kk)) #Add nrows to parameters string_list.append('nrows') parameter_string = ",".join(string_list) # # generate and compile new cython code if necessary # if not options.rhs_reuse or config.tdfunc is None: if options.rhs_filename is None: config.tdname = "rhs" + str(os.getpid()) + str(config.cgen_num) else: config.tdname = opt.rhs_filename cgen = BR_Codegen( h_terms=len(H_terms), h_td_terms=H_td_terms, h_obj=H_obj, c_terms=len(C_terms), c_td_terms=C_td_terms, c_obj=C_obj, a_terms=len(A_terms), a_td_terms=A_td_terms, spline_count=spline_count, config=config, sparse=False, use_secular=use_secular, use_openmp=options.use_openmp, omp_thresh=qset.openmp_thresh if qset.has_openmp else None, omp_threads=options.num_cpus, atol=tol) cgen.generate(config.tdname + ".pyx") code = compile('from ' + config.tdname + ' import cy_td_ode_rhs', '<string>', 'exec') exec(code, globals()) config.tdfunc = cy_td_ode_rhs initial_vector = mat2vec(rho0.full()).ravel() _ode = scipy.integrate.ode(config.tdfunc) code = compile('_ode.set_f_params(' + parameter_string + ')', '<string>', 'exec') _ode.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) _ode.set_initial_value(initial_vector, tlist[0]) exec(code, locals()) # # prepare output array # n_tsteps = len(tlist) e_sops_data = [] output = Result() output.solver = "brmesolve" output.times = tlist if options.store_states: output.states = [] if isinstance(e_ops, types.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: # fall back on storing states output.states = [] options.store_states = True else: output.expect = [] output.num_expect = n_expt_op for op in e_ops: e_sops_data.append(spre(op).data) 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") # # start evolution # progress_bar.start(n_tsteps) rho = Qobj(rho0) dt = np.diff(tlist) for t_idx, t in enumerate(tlist): progress_bar.update(t_idx) if not _ode.successful(): raise Exception("ODE integration error: Try to increase " "the allowed number of substeps by increasing " "the nsteps parameter in the Options class.") if options.store_states or expt_callback: rho.data = dense2D_to_fastcsr_fmode(vec2mat(_ode.y), rho.shape[0], rho.shape[1]) if options.store_states: output.states.append(Qobj(rho, isherm=True)) if expt_callback: # use callback method e_ops(t, rho) for m in range(n_expt_op): if output.expect[m].dtype == complex: output.expect[m][t_idx] = expect_rho_vec( e_sops_data[m], _ode.y, 0) else: output.expect[m][t_idx] = expect_rho_vec( e_sops_data[m], _ode.y, 1) if t_idx < n_tsteps - 1: _ode.integrate(_ode.t + dt[t_idx]) progress_bar.finished() if (not options.rhs_reuse) and (config.tdname is not None): _cython_build_cleanup(config.tdname) if options.store_final_state: rho.data = dense2D_to_fastcsr_fmode(vec2mat(_ode.y), rho.shape[0], rho.shape[1]) output.final_state = Qobj(rho, dims=rho0.dims, isherm=True) return output
def smesolve_generic(H, rho0, tlist, c_ops, e_ops, rhs, d1, d2, ntraj, nsubsteps): """ internal .. note:: Experimental. """ if debug: print(inspect.stack()[0][3]) N_store = len(tlist) N_substeps = nsubsteps N = N_store * N_substeps dt = (tlist[1] - tlist[0]) / N_substeps print("N = %d. dt=%.2e" % (N, dt)) data = Odedata() data.expect = np.zeros((len(e_ops), N_store), dtype=complex) # pre-compute collapse operator combinations that are commonly needed # when evaluating the RHS of stochastic master equations A_ops = [] for c_idx, c in enumerate(c_ops): # xxx: precompute useful operator expressions... cdc = c.dag() * c Ldt = spre(c) * spost(c.dag()) - 0.5 * spre(cdc) - 0.5 * spost(cdc) LdW = spre(c) + spost(c.dag()) Lm = spre(c) + spost(c.dag()) # currently same as LdW A_ops.append([Ldt.data, LdW.data, Lm.data]) # Liouvillian for the unitary part L = -1.0j * (spre(H) - spost(H)) # XXX: should we split the ME in stochastic # and deterministic collapse operators here? progress_acc = 0.0 for n in range(ntraj): if debug and (100 * float(n) / ntraj) >= progress_acc: print("Progress: %.2f" % (100 * float(n) / ntraj)) progress_acc += 10.0 rho_t = mat2vec(rho0.full()) states_list = _smesolve_single_trajectory( L, dt, tlist, N_store, N_substeps, rho_t, A_ops, e_ops, data, rhs, d1, d2) # if average -> average... data.states.append(states_list) # average data.expect = data.expect / ntraj return data
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 is 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] dims = H0.dims elif isinstance(H, list): H0 = H[0][0] if isinstance(H[0], list) else H[0] N = H0.shape[0] dims = H0.dims else: N = H.shape[0] dims = H.dims 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 (a superoperator propagator) if isinstance(H, types.FunctionType): H0 = H(0.0, H_args) N = H0.shape[0] dims = [H0.dims, H0.dims] elif isinstance(H, list): H0 = H[0][0] if isinstance(H[0], list) else H[0] N = H0.shape[0] dims = [H0.dims, H0.dims] else: N = H.shape[0] dims = [H.dims, H.dims] 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], dims=dims) else: return [Qobj(u[:, :, k], dims=dims) for k in range(len(tlist))]
def propagator(H, t, c_op_list, args=None, options=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. args : list/array/dictionary Parameters to callback functions for time-dependent Hamiltonians and collapse operators. options : :class:`qutip.Odeoptions` with options for the ODE solver. Returns ------- a : qobj Instance representing the propagator :math:`U(t)`. """ if options is None: options = Odeoptions() options.rhs_reuse = True rhs_clear() elif options.rhs_reuse: msg = ("propagator is using previously defined rhs " + "function (options.rhs_reuse = True)") warnings.warn(msg) 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, args) N = H0.shape[0] dims = H0.dims elif isinstance(H, list): H0 = H[0][0] if isinstance(H[0], list) else H[0] N = H0.shape[0] dims = H0.dims else: N = H.shape[0] dims = H.dims u = np.zeros([N, N, len(tlist)], dtype=complex) for n in range(0, N): psi0 = basis(N, n) output = sesolve(H, psi0, tlist, [], args, options) 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], [], [], args, options) # 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 (a superoperator propagator) if isinstance(H, types.FunctionType): H0 = H(0.0, args) N = H0.shape[0] dims = [H0.dims, H0.dims] elif isinstance(H, list): H0 = H[0][0] if isinstance(H[0], list) else H[0] N = H0.shape[0] dims = [H0.dims, H0.dims] else: N = H.shape[0] dims = [H.dims, H.dims] 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, [], args, options) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T if len(tlist) == 2: return Qobj(u[:, :, 1], dims=dims) else: return [Qobj(u[:, :, k], dims=dims) for k in range(len(tlist))]
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
def countstat_current_noise(L, c_ops, wlist=None, rhoss=None, J_ops=None, sparse=True, method='direct'): """ Compute the cross-current noise spectrum for a list of collapse operators `c_ops` corresponding to monitored currents, given the system Liouvillian `L`. The current collapse operators `c_ops` should be part of the dissipative processes in `L`, but the `c_ops` given here does not necessarily need to be all collapse operators contributing to dissipation in the Liouvillian. Optionally, the steadystate density matrix `rhoss` and the current operators `J_ops` correpsonding to the current collapse operators `c_ops` can also be specified. If either of `rhoss` and `J_ops` are omitted, they will be computed internally. 'wlist' is an optional list of frequencies at which to evaluate the noise spectrum. Note: The default method is a direct solution using dense matrices, as sparse matrix methods fail for some examples of small systems. For larger systems it is reccomended to use the sparse solver with the direct method, as it avoids explicit calculation of the pseudo-inverse, as described in page 67 of "Electrons in nanostructures" C. Flindt, PhD Thesis, available online: http://orbit.dtu.dk/fedora/objects/orbit:82314/datastreams/file_4732600/content Parameters ---------- L : :class:`qutip.Qobj` Qobj representing the system Liouvillian. c_ops : array / list List of current collapse operators. rhoss : :class:`qutip.Qobj` (optional) The steadystate density matrix corresponding the system Liouvillian `L`. wlist : array / list (optional) List of frequencies at which to evaluate (if none are given, evaluates at zero frequency) J_ops : array / list (optional) List of current superoperators. sparse : bool Flag that indicates whether to use sparse or dense matrix methods when computing the pseudo inverse. Default is false, as sparse solvers can fail for small systems. For larger systems the sparse solvers are reccomended. Returns -------- I, S : tuple of arrays The currents `I` corresponding to each current collapse operator `c_ops` (or, equivalently, each current superopeator `J_ops`) and the zero-frequency cross-current correlation `S`. """ if rhoss is None: rhoss = steadystate(L, c_ops) if J_ops is None: J_ops = [sprepost(c, c.dag()) for c in c_ops] N = len(J_ops) I = np.zeros(N) if wlist is None: S = np.zeros((N, N, 1)) wlist = [0.] else: S = np.zeros((N, N, len(wlist))) if sparse == False: rhoss_vec = mat2vec(rhoss.full()).ravel() for k, w in enumerate(wlist): R = pseudo_inverse(L, rhoss=rhoss, w=w, sparse=sparse, method=method) for i, Ji in enumerate(J_ops): for j, Jj in enumerate(J_ops): if i == j: I[i] = expect_rho_vec(Ji.data, rhoss_vec, 1) S[i, j, k] = I[i] S[i, j, k] -= expect_rho_vec( (Ji * R * Jj + Jj * R * Ji).data, rhoss_vec, 1) else: if method == "direct": N = np.prod(L.dims[0][0]) rhoss_vec = operator_to_vector(rhoss) tr_op = tensor([identity(n) for n in L.dims[0][0]]) tr_op_vec = operator_to_vector(tr_op) Pop = sp.kron(rhoss_vec.data, tr_op_vec.data.T, format='csr') Iop = sp.eye(N * N, N * N, format='csr') Q = Iop - Pop for k, w in enumerate(wlist): if w != 0.0: L_temp = 1.0j * w * spre(tr_op) + L else: #At zero frequency some solvers fail for small systems. #Adding a small finite frequency of order 1e-15 #helps prevent the solvers from throwing an exception. L_temp = 1.0j * (1e-15) * spre(tr_op) + L if not settings.has_mkl: A = L_temp.data.tocsc() else: A = L_temp.data.tocsr() A.sort_indices() rhoss_vec = mat2vec(rhoss.full()).ravel() for j, Jj in enumerate(J_ops): Qj = Q.dot(Jj.data.dot(rhoss_vec)) try: if settings.has_mkl: X_rho_vec_j = mkl_spsolve(A, Qj) else: X_rho_vec_j = sp.linalg.splu( A, permc_spec='COLAMD').solve(Qj) except: X_rho_vec_j = sp.linalg.lsqr(A, Qj)[0] for i, Ji in enumerate(J_ops): Qi = Q.dot(Ji.data.dot(rhoss_vec)) try: if settings.has_mkl: X_rho_vec_i = mkl_spsolve(A, Qi) else: X_rho_vec_i = sp.linalg.splu( A, permc_spec='COLAMD').solve(Qi) except: X_rho_vec_i = sp.linalg.lsqr(A, Qi)[0] if i == j: I[i] = expect_rho_vec(Ji.data, rhoss_vec, 1) S[j, i, k] = I[i] S[j, i, k] -= (expect_rho_vec(Jj.data * Q, X_rho_vec_i, 1) + expect_rho_vec(Ji.data * Q, X_rho_vec_j, 1)) else: rhoss_vec = mat2vec(rhoss.full()).ravel() for k, w in enumerate(wlist): R = pseudo_inverse(L, rhoss=rhoss, w=w, sparse=sparse, method=method) for i, Ji in enumerate(J_ops): for j, Jj in enumerate(J_ops): if i == j: I[i] = expect_rho_vec(Ji.data, rhoss_vec, 1) S[i, j, k] = I[i] S[i, j, k] -= expect_rho_vec( (Ji * R * Jj + Jj * R * Ji).data, rhoss_vec, 1) return I, S
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 _td_brmesolve(H, psi0, tlist, a_ops=[], e_ops=[], c_ops=[], args={}, use_secular=True, sec_cutoff=0.1, tol=qset.atol, options=None, progress_bar=None,_safe_mode=True, verbose=False, _prep_time=0): if isket(psi0): rho0 = ket2dm(psi0) else: rho0 = psi0 nrows = rho0.shape[0] H_terms = [] H_td_terms = [] H_obj = [] A_terms = [] A_td_terms = [] C_terms = [] C_td_terms = [] CA_obj = [] spline_count = [0,0] coupled_ops = [] coupled_lengths = [] coupled_spectra = [] if isinstance(H, Qobj): H_terms.append(H.full('f')) H_td_terms.append('1') else: for kk, h in enumerate(H): if isinstance(h, Qobj): H_terms.append(h.full('f')) H_td_terms.append('1') elif isinstance(h, list): H_terms.append(h[0].full('f')) if isinstance(h[1], Cubic_Spline): H_obj.append(h[1].coeffs) spline_count[0] += 1 H_td_terms.append(h[1]) else: raise Exception('Invalid Hamiltonian specification.') for kk, c in enumerate(c_ops): if isinstance(c, Qobj): C_terms.append(c.full('f')) C_td_terms.append('1') elif isinstance(c, list): C_terms.append(c[0].full('f')) if isinstance(c[1], Cubic_Spline): CA_obj.append(c[1].coeffs) spline_count[0] += 1 C_td_terms.append(c[1]) else: raise Exception('Invalid collapse operator specification.') coupled_offset = 0 for kk, a in enumerate(a_ops): if isinstance(a, list): if isinstance(a[0], Qobj): A_terms.append(a[0].full('f')) A_td_terms.append(a[1]) if isinstance(a[1], tuple): if not len(a[1])==2: raise Exception('Tuple must be len=2.') if isinstance(a[1][0],Cubic_Spline): spline_count[1] += 1 if isinstance(a[1][1],Cubic_Spline): spline_count[1] += 1 elif isinstance(a[0], tuple): if not isinstance(a[1], tuple): raise Exception('Invalid bath-coupling specification.') if (len(a[0])+1) != len(a[1]): raise Exception('BR a_ops tuple lengths not compatible.') coupled_ops.append(kk+coupled_offset) coupled_lengths.append(len(a[0])) coupled_spectra.append(a[1][0]) coupled_offset += len(a[0])-1 if isinstance(a[1][0],Cubic_Spline): spline_count[1] += 1 for nn, _a in enumerate(a[0]): A_terms.append(_a.full('f')) A_td_terms.append(a[1][nn+1]) if isinstance(a[1][nn+1],Cubic_Spline): CA_obj.append(a[1][nn+1].coeffs) spline_count[1] += 1 else: raise Exception('Invalid bath-coupling specification.') string_list = [] for kk,_ in enumerate(H_td_terms): string_list.append("H_terms[{0}]".format(kk)) for kk,_ in enumerate(H_obj): string_list.append("H_obj[{0}]".format(kk)) for kk,_ in enumerate(C_td_terms): string_list.append("C_terms[{0}]".format(kk)) for kk,_ in enumerate(CA_obj): string_list.append("CA_obj[{0}]".format(kk)) for kk,_ in enumerate(A_td_terms): string_list.append("A_terms[{0}]".format(kk)) #Add nrows to parameters string_list.append('nrows') for name, value in args.items(): if isinstance(value, np.ndarray): raise TypeError('NumPy arrays not valid args for BR solver.') else: string_list.append(str(value)) parameter_string = ",".join(string_list) if verbose: print('BR prep time:', time.time()-_prep_time) # # generate and compile new cython code if necessary # if not options.rhs_reuse or config.tdfunc is None: if options.rhs_filename is None: config.tdname = "rhs" + str(os.getpid()) + str(config.cgen_num) else: config.tdname = opt.rhs_filename if verbose: _st = time.time() cgen = BR_Codegen(h_terms=len(H_terms), h_td_terms=H_td_terms, h_obj=H_obj, c_terms=len(C_terms), c_td_terms=C_td_terms, c_obj=CA_obj, a_terms=len(A_terms), a_td_terms=A_td_terms, spline_count=spline_count, coupled_ops = coupled_ops, coupled_lengths = coupled_lengths, coupled_spectra = coupled_spectra, config=config, sparse=False, use_secular = use_secular, sec_cutoff = sec_cutoff, args=args, use_openmp=options.use_openmp, omp_thresh=qset.openmp_thresh if qset.has_openmp else None, omp_threads=options.num_cpus, atol=tol) cgen.generate(config.tdname + ".pyx") code = compile('from ' + config.tdname + ' import cy_td_ode_rhs', '<string>', 'exec') exec(code, globals()) config.tdfunc = cy_td_ode_rhs if verbose: print('BR compile time:', time.time()-_st) initial_vector = mat2vec(rho0.full()).ravel() _ode = scipy.integrate.ode(config.tdfunc) code = compile('_ode.set_f_params(' + parameter_string + ')', '<string>', 'exec') _ode.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) _ode.set_initial_value(initial_vector, tlist[0]) exec(code, locals()) # # prepare output array # n_tsteps = len(tlist) e_sops_data = [] output = Result() output.solver = "brmesolve" output.times = tlist if options.store_states: output.states = [] if isinstance(e_ops, types.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: # fall back on storing states output.states = [] options.store_states = True else: output.expect = [] output.num_expect = n_expt_op for op in e_ops: e_sops_data.append(spre(op).data) 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") # # start evolution # if type(progress_bar)==BaseProgressBar and verbose: _run_time = time.time() progress_bar.start(n_tsteps) rho = Qobj(rho0) dt = np.diff(tlist) for t_idx, t in enumerate(tlist): progress_bar.update(t_idx) if not _ode.successful(): raise Exception("ODE integration error: Try to increase " "the allowed number of substeps by increasing " "the nsteps parameter in the Options class.") if options.store_states or expt_callback: rho.data = dense2D_to_fastcsr_fmode(vec2mat(_ode.y), rho.shape[0], rho.shape[1]) if options.store_states: output.states.append(Qobj(rho, isherm=True)) if expt_callback: # use callback method e_ops(t, rho) for m in range(n_expt_op): if output.expect[m].dtype == complex: output.expect[m][t_idx] = expect_rho_vec(e_sops_data[m], _ode.y, 0) else: output.expect[m][t_idx] = expect_rho_vec(e_sops_data[m], _ode.y, 1) if t_idx < n_tsteps - 1: _ode.integrate(_ode.t + dt[t_idx]) progress_bar.finished() if type(progress_bar)==BaseProgressBar and verbose: print('BR runtime:', time.time()-_run_time) if (not options.rhs_reuse) and (config.tdname is not None): _cython_build_cleanup(config.tdname) if options.store_final_state: rho.data = dense2D_to_fastcsr_fmode(vec2mat(_ode.y), rho.shape[0], rho.shape[1]) output.final_state = Qobj(rho, dims=rho0.dims, isherm=True) return output
def floquet_markov_mesolve( R, rho0, tlist, e_ops, options=None, floquet_basis=True, f_modes_0=None, f_modes_table_t=None, f_energies=None, T=None, ): """ Solve the dynamics for the system using the Floquet-Markov master equation. .. note:: It is important to understand in which frame and basis the results are returned here. Parameters ---------- R : array The Floquet-Markov master equation tensor `R`. rho0 : :class:`qutip.qobj` Initial density matrix. If ``f_modes_0`` is not passed, this density matrix is assumed to be in the Floquet picture. 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.solver.Options` options for the ODE solver. floquet_basis: bool, True If ``True``, states and expectation values will be returned in the Floquet basis. If ``False``, a transformation will be made to the computational basis; this will be in the lab frame if ``f_modes_table``, ``T` and ``f_energies`` are all supplied, or the interaction picture (defined purely be f_modes_0) if they are not. f_modes_0 : list of :class:`qutip.qobj` (kets), optional A list of initial Floquet modes, used to transform the given starting density matrix into the Floquet basis. If this is not passed, it is assumed that ``rho`` is already in the Floquet basis. f_modes_table_t : nested list of :class:`qutip.qobj` (kets), optional A lookup-table of Floquet modes at times precalculated by :func:`qutip.floquet.floquet_modes_table`. Necessary if ``floquet_basis`` is ``False`` and the transformation should be made back to the lab frame. f_energies : array_like of float, optional The precalculated Floquet quasienergies. Necessary if ``floquet_basis`` is ``False`` and the transformation should be made back to the lab frame. T : float, optional The time period of driving. Necessary if ``floquet_basis`` is ``False`` and the transformation should be made back to the lab frame. Returns ------- output : :class:`qutip.solver.Result` An instance of the class :class:`qutip.solver.Result`, which contains either an *array* of expectation values or an array of state vectors, for the times specified by `tlist`. """ opt = options or Options() if opt.tidy: R.tidyup() rho0 = rho0.proj() if rho0.isket else rho0 # Prepare output object. dt = tlist[1] - tlist[0] output = Result() output.solver = "fmmesolve" output.times = tlist if isinstance(e_ops, FunctionType): expt_callback = True store_states = opt.store_states or False else: expt_callback = False try: e_ops = list(e_ops) except TypeError: raise TypeError("`e_ops` must be iterable or a function") from None n_expt_op = len(e_ops) if n_expt_op == 0: store_states = True else: output.expect = [] output.num_expect = n_expt_op for op in e_ops: dtype = np.float64 if op.isherm else np.complex128 output.expect.append(np.zeros(len(tlist), dtype=dtype)) store_states = opt.store_states or (n_expt_op == 0) if store_states: output.states = [] # Choose which frame transformations should be done on the initial and # evolved states. lab_lookup = [f_modes_table_t, f_energies, T] if (any(x is None for x in lab_lookup) and not all(x is None for x in lab_lookup)): warnings.warn( "if transformation back to the computational basis in the lab" "frame is desired, all of `f_modes_t`, `f_energies` and `T` must" "be supplied.") f_modes_table_t = f_energies = T = None # Initial state. if f_modes_0 is not None: rho0 = rho0.transform(f_modes_0) # Evolved states. if floquet_basis: def transform(rho, t): return rho elif f_modes_table_t is not None: # Lab frame, computational basis. def transform(rho, t): f_modes_t = floquet_modes_t_lookup(f_modes_table_t, t, T) f_states_t = floquet_states(f_modes_t, f_energies, t) return rho.transform(f_states_t, True) elif f_modes_0 is not None: # Interaction picture, computational basis. def transform(rho, t): return rho.transform(f_modes_0, False) else: raise ValueError( "cannot transform out of the Floquet basis without some knowledge " "of the Floquet modes. Pass `f_modes_0`, or all of `f_modes_t`, " "`f_energies` and `T`.") # 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]) # Main evolution loop. for t_idx, t in enumerate(tlist): if not r.successful(): break rho = transform(Qobj(vec2mat(r.y), rho0.dims, rho0.shape), t) if expt_callback: e_ops(t, rho) else: for m, e_op in enumerate(e_ops): output.expect[m][t_idx] = expect(e_op, rho) if store_states: output.states.append(rho) r.integrate(r.t + dt) return output