def qfunc(state, xvec, yvec, g=sqrt(2)): """Q-function of a given state vector or density matrix at points `xvec + i * yvec`. Parameters ---------- state : qobj A state vector or density matrix. xvec : array_like x-coordinates at which to calculate the Wigner function. yvec : array_like y-coordinates at which to calculate the Wigner function. g : float Scaling factor for `a = 0.5 * g * (x + iy)`, default `g = sqrt(2)`. The value of `g` is related to the value of `hbar` in the commutation relation `[x, y] = 1j * hbar` via `hbar=2/g^2` giving the default value `hbar=1`. Returns -------- Q : array Values representing the Q-function calculated over the specified range [xvec,yvec]. """ X, Y = meshgrid(xvec, yvec) amat = 0.5 * g * (X + Y * 1j) if not (isoper(state) or isket(state)): raise TypeError('Invalid state operand to qfunc.') qmat = zeros(size(amat)) if isket(state): qmat = _qfunc_pure(state, amat) elif isoper(state): d, v = la.eig(state.full()) # d[i] = eigenvalue i # v[:,i] = eigenvector i qmat = zeros(np.shape(amat)) for k in arange(0, len(d)): qmat1 = _qfunc_pure(v[:, k], amat) qmat += real(d[k] * qmat1) qmat = 0.25 * qmat * g**2 return qmat
def _single_qobj_expect(oper, state): """ Private function used by expect to calculate expectation values of Qobjs. """ if isoper(oper): if state.type == 'oper': # calculates expectation value via TR(op*rho) #prod = oper.data * state.data #tr = prod.diagonal().sum() #if oper.isherm and state.isherm: # return float(np.real(tr)) #else: # return tr return cy_spmm_tr(oper.data, state.data, oper.isherm and state.isherm) elif state.type == 'ket': # calculates expectation value via <psi|op|psi> #prod = state.data.conj().T.dot(oper.data * state.data) #if oper.isherm: # return float(np.real(prod[0, 0])) #else: # return prod[0, 0] return cy_expect_psi(oper.data, state.full(squeeze=True), oper.isherm) else: raise TypeError('Invalid operand types')
def _single_eseries_expect(oper, state): """ Private function used by expect to calculate expectation values for eseries. """ out = eseries() if isoper(state.ampl[0]): out.rates = state.rates out.ampl = np.array([expect(oper, a) for a in state.ampl]) else: out.rates = np.array([]) out.ampl = np.array([]) for m in range(len(state.rates)): op_m = state.ampl[m].data.conj().T * oper.data for n in range(len(state.rates)): a = op_m * state.ampl[n].data if isinstance(a, sp.spmatrix): a = a.todense() out.rates = np.append(out.rates, state.rates[n] - state.rates[m]) out.ampl = np.append(out.ampl, a) return out
def _subsystem_apply_reference(state, channel, mask): if isket(state): state = ket2dm(state) if isoper(channel): full_oper = tensor([ channel if mask[j] else qeye(state.dims[0][j]) for j in range(len(state.dims[0])) ]) return full_oper * state * full_oper.dag() else: # Go to Choi, then Kraus # chan_mat = array(channel.data.todense()) choi_matrix = super_to_choi(channel) vals, vecs = eig(choi_matrix.full()) vecs = list(map(array, zip(*vecs))) kraus_list = [ sqrt(vals[j]) * vec2mat(vecs[j]) for j in range(len(vals)) ] # Kraus operators to be padded with identities: k_qubit_kraus_list = product(kraus_list, repeat=sum(mask)) rho_out = Qobj(inpt=zeros(state.shape), dims=state.dims) for operator_iter in k_qubit_kraus_list: operator_iter = iter(operator_iter) op_iter_list = [ next(operator_iter) if mask[j] else qeye(state.dims[0][j]) for j in range(len(state.dims[0])) ] full_oper = tensor(list(map(Qobj, op_iter_list))) rho_out = rho_out + full_oper * state * full_oper.dag() return Qobj(rho_out)
def _subsystem_apply_reference(state, channel, mask): if isket(state): state = ket2dm(state) if isoper(channel): full_oper = tensor([channel if mask[j] else qeye(state.dims[0][j]) for j in range(len(state.dims[0]))]) return full_oper * state * full_oper.dag() else: # Go to Choi, then Kraus # chan_mat = array(channel.data.todense()) choi_matrix = super_to_choi(channel) vals, vecs = eig(choi_matrix.full()) vecs = list(map(array, zip(*vecs))) kraus_list = [sqrt(vals[j]) * vec2mat(vecs[j]) for j in range(len(vals))] # Kraus operators to be padded with identities: k_qubit_kraus_list = product(kraus_list, repeat=sum(mask)) rho_out = Qobj(inpt=zeros(state.shape), dims=state.dims) for operator_iter in k_qubit_kraus_list: operator_iter = iter(operator_iter) op_iter_list = [next(operator_iter).conj().T if mask[j] else qeye(state.dims[0][j]) for j in range(len(state.dims[0]))] full_oper = tensor(list(map(Qobj, op_iter_list))) rho_out = rho_out + full_oper * state * full_oper.dag() return Qobj(rho_out)
def qfunc(state, xvec, yvec, g=sqrt(2)): """Q-function of a given state vector or density matrix at points `xvec + i * yvec`. Parameters ---------- state : qobj A state vector or density matrix. xvec : array_like x-coordinates at which to calculate the Wigner function. yvec : array_like y-coordinates at which to calculate the Wigner function. g : float Scaling factor for `a = 0.5 * g * (x + iy)`, default `g = sqrt(2)`. Returns -------- Q : array Values representing the Q-function calculated over the specified range [xvec,yvec]. """ X, Y = meshgrid(xvec, yvec) amat = 0.5 * g * (X + Y * 1j) if not (isoper(state) or isket(state)): raise TypeError('Invalid state operand to qfunc.') qmat = zeros(size(amat)) if isket(state): qmat = _qfunc_pure(state, amat) elif isoper(state): d, v = la.eig(state.full()) # d[i] = eigenvalue i # v[:,i] = eigenvector i qmat = zeros(np.shape(amat)) for k in arange(0, len(d)): qmat1 = _qfunc_pure(v[:, k], amat) qmat += real(d[k] * qmat1) qmat = 0.25 * qmat * g ** 2 return qmat
def qfunc(state, xvec, yvec, g=sqrt(2)): """Q-function of a given state vector or density matrix at points `xvec + i * yvec`. Parameters ---------- state : qobj A state vector or density matrix. xvec : array_like x-coordinates at which to calculate the Wigner function. yvec : array_like y-coordinates at which to calculate the Wigner function. g : float Scaling factor for `a = 0.5 * g * (x + iy)`, default `g = sqrt(2)`. Returns -------- Q : array Values representing the Q-function calculated over the specified range [xvec,yvec]. """ X, Y = meshgrid(xvec, yvec) avec = np.reshape(0.5 * g * (X + Y * 1j), [len(xvec) * len(yvec), 1]) if not (isoper(state) or isket(state)): raise TypeError('Invalid state operand to qfunc.') qvec = zeros(size(avec)) if isket(state): qvec = _qfunc_pure(state, avec) elif isoper(state): d, v = la.eig(state.full()) # d[i] = eigenvalue i # v[:,i] = eigenvector i qvec = zeros(np.shape(avec)) for k in arange(0, len(d)): qvec1 = _qfunc_pure(v[:, k], avec) qvec += real(d[k] * qvec1) qvec = 0.25 * qvec * g**2 return np.reshape(qvec, X.shape)
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 _mesolve_const(H, rho0, tlist, c_op_list, expt_ops, args, opt): """! 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, # fallback on the unitary schrodinger equation solver if len(c_op_list) == 0 and isoper(H): return _sesolve_const(H, rho0, tlist, expt_ops, args, opt) # Got a wave function as initial state: convert to density matrix. rho0 = rho0 * rho0.dag() # # construct liouvillian # if opt.tidy: H = H.tidyup(opt.atol) if issuper(H): L = H + liouvillian(None, c_op_list) else: L = liouvillian(H, c_op_list) # # setup integrator # initial_vector = mat2vec(rho0.full()) 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(r, rho0, tlist, expt_ops, opt, vec2mat)
def _steadystate_setup(A, c_op_list): """Build Liouvillian (if necessary) and check input. """ if isoper(A): if len(c_op_list) > 0: return liouvillian(A, c_op_list) raise TypeError('Cannot calculate the steady state for a ' + 'non-dissipative system ' + '(no collapse operators given)') elif issuper(A): return A else: raise TypeError('Solving for steady states requires ' + 'Liouvillian (super) operators')
def _single_qobj_expect(oper, state): """ Private function used by expect to calculate expectation values of Qobjs. """ if isoper(oper): if oper.dims[1] != state.dims[0]: raise Exception('Operator and state do not have same tensor structure.') if state.type == 'oper': # calculates expectation value via TR(op*rho) return cy_spmm_tr(oper.data, state.data, oper.isherm and state.isherm) elif state.type == 'ket': # calculates expectation value via <psi|op|psi> return cy_expect_psi(oper.data, state.full(squeeze=True), oper.isherm) else: raise TypeError('Invalid operand types')
def _single_qobj_expect(oper, state): """ Private function used by expect to calculate expectation values of Qobjs. """ if isoper(oper): if state.type == 'oper': # calculates expectation value via TR(op*rho) prod = oper.data * state.data tr = prod.diagonal().sum() if oper.isherm and state.isherm: return float(np.real(tr)) else: return tr elif state.type == 'ket': # calculates expectation value via <psi|op|psi> prod = state.data.conj().T.dot(oper.data * state.data) if oper.isherm: return float(np.real(prod[0, 0])) else: return prod[0, 0] else: raise TypeError('Invalid operand types')
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 qfunc(state, xvec, yvec, g=sqrt(2), precompute=None): """Q-function of a given state vector or density matrix at points `xvec + i * yvec`. Parameters ---------- state : qobj A state vector or density matrix. xvec : array_like x-coordinates at which to calculate the Wigner function. yvec : array_like y-coordinates at which to calculate the Wigner function. g : float Scaling factor for `a = 0.5 * g * (x + iy)`, default `g = sqrt(2)`. precompute : None (default), bool or array Use precomputation to speed up the Husimi Q func. None (default): True for density matrices, False for bras/kets True / False: Always/Never use precomputation array: The result of the precomputation can be given explicitly. Useful if qfunc is called many times with the same xvec, yvec and dimensions. The precomputed array is returned by qfunc_precompute(xvec, yvec, n, g), where xvec, yvec and g need to be the same as for qfunc, and n is the dim of the chosen system. Returns -------- Q : array Values representing the Q-function calculated over the specified range [xvec,yvec]. """ X, Y = meshgrid(xvec, yvec) amat = 0.5 * g * (X + Y * 1j) if not (isoper(state) or isket(state)): raise TypeError('Invalid state operand to qfunc.') qmat = zeros(size(amat)) if precompute is None: # Decide whether precomputation should be used, if not already set precompute = isoper(state) memory = len(xvec) * len(yvec) * state.shape[0] * 16 / 1024**2 if (precompute is True) and (memory > 1024): # If too much memory would be used, do not precompute warnings.warn( f"Precomputation uses {memory} MB memory, with a max of " + f"1024 MB. Falling back to iterative Husimi function") precompute = False if precompute is True: # If precomputation should be used but result not given, do it now precompute = qfunc_precompute(xvec, yvec, state.shape[0], g) if isket(state): qmat = _qfunc_pure(state, amat, precompute) elif isoper(state): d, v = la.eig(state.full()) # d[i] = eigenvalue i # v[:,i] = eigenvector i qmat = zeros(np.shape(amat)) for k in arange(0, len(d)): qmat1 = _qfunc_pure(v[:, k], amat, precompute) qmat += real(d[k] * qmat1) qmat = 0.25 * qmat * g**2 return qmat
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 steadystate( A, c_op_list=[], method='direct', sparse=True, use_umfpack=True, maxiter=5000, tol=1e-5, use_precond=True, perm_method='AUTO', drop_tol=1e-1, diag_pivot_thresh=0.33, verbose=False): """Calculates the steady state for the evolution subject to the supplied Hamiltonian or Liouvillian operator and (if given a Hamiltonian) a list of collapse operators. If the user passes a Hamiltonian then it, along with the list of collapse operators, will be converted into a Liouvillian operator in Lindblad form. Parameters ---------- A : qobj A Hamiltonian or Liouvillian operator. c_op_list : list A list of collapse operators. method : str {'direct', 'iterative', 'iterative-bicg', 'lu', 'svd', 'power'} Method for solving the underlying linear equation. Direct solver 'direct' (default), iterative LGMRES method 'iterative', iterative method BICG 'iterative-bicg', LU decomposition 'lu', SVD 'svd' (dense), or inverse-power method 'power'. sparse : bool default=True Solve for the steady state using sparse algorithms. If set to False, the underlying Liouvillian operator will be converted into a dense matrix. Use only for 'smaller' systems. use_umfpack : bool optional, default = True Use the UMFpack backend for the direct solver 'direct' or Power method 'power. If 'False', the solver uses the SuperLU backend. This option does not affect the other methods. Used only when sparse=True. maxiter : int optional Maximum number of iterations to perform if using an iterative method such as 'iterative' (default=5000), or 'power' (default=10). tol : float optional, default=1e-5 Tolerance used for terminating solver solution when using iterative solvers. use_precond : bool optional, default = True ITERATIVE ONLY. Use an incomplete sparse LU decomposition as a preconditioner for the 'iterative' LGMRES and BICG solvers. Speeds up convergence time by orders of magnitude in many cases. perm_method : str {'AUTO', 'AUTO-BREAK', 'COLAMD', 'MMD_ATA', 'NATURAL'} ITERATIVE ONLY. Sets the method for column ordering the incomplete LU preconditioner used by the 'iterative' method. When set to 'AUTO' (default), the solver will attempt to precondition the system using 'COLAMD'. If this fails, the solver will use no preconditioner. Using 'AUTO-BREAK' will cause the solver to issue an exception and stop if the 'COLAMD' method fails. drop_tol : float default=1e-1 ITERATIVE ONLY. Sets the threshold for the magnitude of preconditioner elements that should be dropped. diag_pivot_thresh : float default=0.33 ITERATIVE ONLY. Sets the threshold for which diagonal elements are considered acceptable pivot points when using a preconditioner. verbose : bool default=False Flag for printing out detailed information on the steady state solver. Returns ------- dm : qobj Steady state density matrix. Notes ----- The SVD method works only for dense operators (i.e. small systems). Setting use_umfpack=True (default) may result in 'out of memory' errors if your system size becomes to large. """ n_op = len(c_op_list) if isoper(A): if n_op == 0: raise TypeError('Cannot calculate the steady state for a ' + 'non-dissipative system ' + '(no collapse operators given)') else: A = liouvillian_fast(A, c_op_list) if not issuper(A): raise TypeError('Solving for steady states requires ' + 'Liouvillian (super) operators') if method == 'direct': if sparse: return _steadystate_direct_sparse(A, use_umfpack=use_umfpack, verbose=verbose) else: return _steadystate_direct_dense(A, verbose=verbose) elif method == 'iterative': return _steadystate_iterative(A, tol=tol, use_precond=use_precond, maxiter=maxiter, perm_method=perm_method, drop_tol=drop_tol, verbose=verbose, diag_pivot_thresh=diag_pivot_thresh) elif method == 'iterative-bicg': return _steadystate_iterative_bicg(A, tol=tol, use_precond=use_precond, maxiter=maxiter, perm_method=perm_method, drop_tol=drop_tol, verbose=verbose, diag_pivot_thresh=diag_pivot_thresh) elif method == 'lu': return _steadystate_lu(A, verbose=verbose) elif method == 'svd': return _steadystate_svd_dense(A, atol=1e-12, rtol=0, all_steadystates=False, verbose=verbose) elif method == 'power': return _steadystate_power(A, maxiter=10, tol=tol, itertol=tol, use_umfpack=use_umfpack, verbose=verbose) else: raise ValueError('Invalid method argument for steadystate.')
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 _block_apply(block, channel): if isoper(channel): block = _top_apply_U(block, channel) elif issuper(channel): block = _top_apply_S(block, channel) return block
def steadystate(A, c_op_list=[], **kwargs): """Calculates the steady state for quantum evolution subject to the supplied Hamiltonian or Liouvillian operator and (if given a Hamiltonian) a list of collapse operators. If the user passes a Hamiltonian then it, along with the list of collapse operators, will be converted into a Liouvillian operator in Lindblad form. Parameters ---------- A : qobj A Hamiltonian or Liouvillian operator. c_op_list : list A list of collapse operators. method : str {'direct', 'iterative', 'iterative-bicg', 'svd', 'power'} Method for solving the underlying linear equation. Direct solver 'direct' (default), iterative GMRES method 'iterative', iterative method BICGSTAB 'iterative-bicg', SVD 'svd' (dense), or inverse-power method 'power'. sparse : bool, {True, False} Solve for the steady state using sparse algorithms. If set to False, the underlying Liouvillian operator will be converted into a dense matrix. Use only for 'smaller' systems. use_rcm : bool, {True, False} Use reverse Cuthill-Mckee reordering to minimize fill-in in the LU factorization of the Liouvillian. use_umfpack : bool {False, True} Use umfpack solver instead of SuperLU. For SciPy 0.14+, this option requires installing scikits.umfpack. maxiter : int, optional Maximum number of iterations to perform if using an iterative method such as 'iterative' (default=1000), or 'power' (default=10). tol : float, optional, default=1e-5 Tolerance used for terminating solver solution when using iterative solvers. use_precond : bool optional, default = True ITERATIVE ONLY. Use an incomplete sparse LU decomposition as a preconditioner for the 'iterative' GMRES and BICG solvers. Speeds up convergence time by orders of magnitude in many cases. M : {sparse matrix, dense matrix, LinearOperator}, optional Preconditioner for A. The preconditioner should approximate the inverse of A. Effective preconditioning dramatically improves the rate of convergence, for iterative methods. Does not affect other solvers. fill_factor : float, default=12 ITERATIVE ONLY. Specifies the fill ratio upper bound (>=1) of the iLU preconditioner. Lower values save memory at the cost of longer execution times and a possible singular factorization. drop_tol : float, default=1e-3 ITERATIVE ONLY. Sets the threshold for the magnitude of preconditioner elements that should be dropped. Can be reduced for a courser factorization at the cost of an increased number of iterations, and a possible singular factorization. diag_pivot_thresh : float, default=None ITERATIVE ONLY. Sets the threshold between [0,1] for which diagonal elements are considered acceptable pivot points when using a preconditioner. A value of zero forces the pivot to be the diagonal element. Returns ------- dm : qobj Steady state density matrix. Notes ----- The SVD method works only for dense operators (i.e. small systems). """ ss_args = _default_steadystate_args() for key in kwargs.keys(): if key in ss_args.keys(): ss_args[key]=kwargs[key] else: raise Exception('Invalid keyword argument passed to steadystate.') n_op = len(c_op_list) if isoper(A): if n_op == 0: raise TypeError('Cannot calculate the steady state for a ' + 'non-dissipative system ' + '(no collapse operators given)') else: A = liouvillian(A, c_op_list) if not issuper(A): raise TypeError('Solving for steady states requires ' + 'Liouvillian (super) operators') if ss_args['method'] == 'direct': if ss_args['sparse']: return _steadystate_direct_sparse(A, ss_args) else: return _steadystate_direct_dense(A) elif ss_args['method'] == 'iterative': return _steadystate_iterative(A, ss_args) elif ss_args['method'] == 'iterative-bicg': return _steadystate_iterative_bicg(A, ss_args) elif ss_args['method'] == 'svd': return _steadystate_svd_dense(A, ss_args) elif ss_args['method'] == 'power': return _steadystate_power(A, ss_args) else: raise ValueError('Invalid method argument for steadystate.')
def steadystate(A, c_op_list=[], method='direct', sparse=True, use_rcm=True, sym=False, use_precond=True, M=None, drop_tol=1e-3, fill_factor=12, diag_pivot_thresh=None, maxiter=1000, tol=1e-5, use_umfpack=False): """Calculates the steady state for quantum evolution subject to the supplied Hamiltonian or Liouvillian operator and (if given a Hamiltonian) a list of collapse operators. If the user passes a Hamiltonian then it, along with the list of collapse operators, will be converted into a Liouvillian operator in Lindblad form. Parameters ---------- A : qobj A Hamiltonian or Liouvillian operator. c_op_list : list A list of collapse operators. method : str {'direct', 'iterative', 'iterative-bicg', 'lu', 'svd', 'power'} Method for solving the underlying linear equation. Direct solver 'direct' (default), iterative GMRES method 'iterative', iterative method BICGSTAB 'iterative-bicg', LU decomposition 'lu', SVD 'svd' (dense), or inverse-power method 'power'. sparse : bool, default = True Solve for the steady state using sparse algorithms. If set to False, the underlying Liouvillian operator will be converted into a dense matrix. Use only for 'smaller' systems. use_rcm : bool, default = True Use reverse Cuthill-Mckee reordering to minimize fill-in in the LU factorization of the Liouvillian. maxiter : int, optional Maximum number of iterations to perform if using an iterative method such as 'iterative' (default=1000), or 'power' (default=10). tol : float, optional, default=1e-5 Tolerance used for terminating solver solution when using iterative solvers. use_precond : bool optional, default = True ITERATIVE ONLY. Use an incomplete sparse LU decomposition as a preconditioner for the 'iterative' LGMRES and BICG solvers. Speeds up convergence time by orders of magnitude in many cases. M : {sparse matrix, dense matrix, LinearOperator} Preconditioner for A. The preconditioner should approximate the inverse of A. Effective preconditioning dramatically improves the rate of convergence, for iterative methods. Does not affect other solvers. fill_factor : float, default=12 ITERATIVE ONLY. Specifies the fill ratio upper bound (>=1) of the iLU preconditioner. Lower values save memory at the cost of longer execution times and a possible singular factorization. drop_tol : float, default=1e-3 ITERATIVE ONLY. Sets the threshold for the magnitude of preconditioner elements that should be dropped. Can be reduced for a courser factorization at the cost of an increased number of iterations, and a possible singular factorization. diag_pivot_thresh : float, default=None ITERATIVE ONLY. Sets the threshold between [0,1] for which diagonal elements are considered acceptable pivot points when using a preconditioner. A value of zero forces the pivot to be the diagonal element. Returns ------- dm : qobj Steady state density matrix. Notes ----- The SVD method works only for dense operators (i.e. small systems). """ n_op = len(c_op_list) if isoper(A): if n_op == 0: raise TypeError('Cannot calculate the steady state for a ' + 'non-dissipative system ' + '(no collapse operators given)') else: A = liouvillian_fast(A, c_op_list) if not issuper(A): raise TypeError('Solving for steady states requires ' + 'Liouvillian (super) operators') if use_umfpack: warnings.warn("The use of use_umfpack is deprecated.") if method == 'direct': if sparse: return _steadystate_direct_sparse(A, use_rcm=use_rcm, use_umfpack=use_umfpack) else: return _steadystate_direct_dense(A) elif method == 'iterative': return _steadystate_iterative(A, tol=tol, use_precond=use_precond, M=M, use_rcm=use_rcm, sym=sym, maxiter=maxiter, fill_factor=fill_factor, drop_tol=drop_tol, diag_pivot_thresh=diag_pivot_thresh) elif method == 'iterative-bicg': return _steadystate_iterative_bicg(A, tol=tol, use_precond=use_precond, M=M, use_rcm=use_rcm, maxiter=maxiter, fill_factor=fill_factor, drop_tol=drop_tol, diag_pivot_thresh=diag_pivot_thresh) elif method == 'lu': return _steadystate_lu(A, use_umfpack=use_umfpack) elif method == 'svd': return _steadystate_svd_dense(A, atol=1e-12, rtol=0, all_steadystates=False) elif method == 'power': return _steadystate_power(A, maxiter=10, tol=tol, itertol=tol) else: raise ValueError('Invalid method argument for steadystate.')
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_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 steadystate(A, c_op_list=[], **kwargs): """Calculates the steady state for quantum evolution subject to the supplied Hamiltonian or Liouvillian operator and (if given a Hamiltonian) a list of collapse operators. If the user passes a Hamiltonian then it, along with the list of collapse operators, will be converted into a Liouvillian operator in Lindblad form. Parameters ---------- A : qobj A Hamiltonian or Liouvillian operator. c_op_list : list A list of collapse operators. method : str {'direct', 'eigen', 'iterative-bicg', 'iterative-gmres', 'svd', 'power'} Method for solving the underlying linear equation. Direct LU solver 'direct' (default), sparse eigenvalue problem 'eigen', iterative GMRES method 'iterative-gmres', iterative LGMRES method 'iterative-lgmres', SVD 'svd' (dense), or inverse-power method 'power'. sparse : bool, optional, default=True Solve for the steady state using sparse algorithms. If set to False, the underlying Liouvillian operator will be converted into a dense matrix. Use only for 'smaller' systems. use_rcm : bool, optional, default=True Use reverse Cuthill-Mckee reordering to minimize fill-in in the LU factorization of the Liouvillian. use_wbm : bool, optional, default=False Use Weighted Bipartite Matching reordering to make the Liouvillian diagonally dominant. This is useful for iterative preconditioners only, and is set to ``True`` by default when finding a preconditioner. weight : float, optional Sets the size of the elements used for adding the unity trace condition to the linear solvers. This is set to the average abs value of the Liouvillian elements if not specified by the user. use_umfpack : bool {False, True} Use umfpack solver instead of SuperLU. For SciPy 0.14+, this option requires installing scikits.umfpack. maxiter : int, optional, default=10000 Maximum number of iterations to perform if using an iterative method. tol : float, optional, default=1e-9 Tolerance used for terminating solver solution when using iterative solvers. permc_spec : str, optional, default='COLAMD' Column ordering used internally by superLU for the 'direct' LU decomposition method. Options include 'COLAMD' and 'NATURAL'. If using RCM then this is set to 'NATURAL' automatically unless explicitly specified. use_precond : bool optional, default = True ITERATIVE ONLY. Use an incomplete sparse LU decomposition as a preconditioner for the 'iterative' GMRES and BICG solvers. Speeds up convergence time by orders of magnitude in many cases. M : {sparse matrix, dense matrix, LinearOperator}, optional Preconditioner for A. The preconditioner should approximate the inverse of A. Effective preconditioning dramatically improves the rate of convergence, for iterative methods only . If no preconditioner is given and ``use_precond=True``, then one is generated automatically. fill_factor : float, optional, default=10 ITERATIVE ONLY. Specifies the fill ratio upper bound (>=1) of the iLU preconditioner. Lower values save memory at the cost of longer execution times and a possible singular factorization. drop_tol : float, optional, default=1e-3 ITERATIVE ONLY. Sets the threshold for the magnitude of preconditioner elements that should be dropped. Can be reduced for a courser factorization at the cost of an increased number of iterations, and a possible singular factorization. diag_pivot_thresh : float, optional, default=None ITERATIVE ONLY. Sets the threshold between [0,1] for which diagonal elements are considered acceptable pivot points when using a preconditioner. A value of zero forces the pivot to be the diagonal element. ILU_MILU : str, optional, default='smilu_2' Selects the incomplete LU decomposition method algoithm used in creating the preconditoner. Should only be used by advanced users. Returns ------- dm : qobj Steady state density matrix. Notes ----- The SVD method works only for dense operators (i.e. small systems). """ ss_args = _default_steadystate_args() for key in kwargs.keys(): if key in ss_args.keys(): ss_args[key] = kwargs[key] else: raise Exception( "Invalid keyword argument '"+key+"' passed to steadystate.") # Set column perm to NATURAL if using RCM and not specified by user if ss_args['use_rcm'] and ('permc_spec' not in kwargs.keys()): ss_args['permc_spec'] = 'NATURAL' # Set use_wbm=True if using iterative solver with preconditioner and # not explicitly set to False by user if (ss_args['method'] in ['iterative-lgmres', 'iterative-gmres']) \ and ('use_wbm' not in kwargs.keys()): ss_args['use_wbm'] = True n_op = len(c_op_list) if isoper(A): if n_op == 0: raise TypeError('Cannot calculate the steady state for a ' + 'non-dissipative system ' + '(no collapse operators given)') else: A = liouvillian(A, c_op_list) if not issuper(A): raise TypeError('Solving for steady states requires ' + 'Liouvillian (super) operators') # Set weight parameter to avg abs val in L if not set explicitly if 'weight' not in kwargs.keys(): ss_args['weight'] = np.mean(np.abs(A.data.data.max())) if ss_args['method'] == 'direct': if ss_args['sparse']: return _steadystate_direct_sparse(A, ss_args) else: return _steadystate_direct_dense(A) elif ss_args['method'] == 'eigen': return _steadystate_eigen(A, ss_args) elif ss_args['method'] in ['iterative-gmres', 'iterative-lgmres']: return _steadystate_iterative(A, ss_args) elif ss_args['method'] == 'svd': return _steadystate_svd_dense(A, ss_args) elif ss_args['method'] == 'power': return _steadystate_power(A, ss_args) else: raise ValueError('Invalid method argument for steadystate.')
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 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 steadystate(A, c_op_list=[], method='direct', sparse=True, use_rcm=True, sym=False, use_precond=True, M=None, drop_tol=1e-3, fill_factor=12, diag_pivot_thresh=None, maxiter=1000, tol=1e-5, verbose=False): """Calculates the steady state for quantum evolution subject to the supplied Hamiltonian or Liouvillian operator and (if given a Hamiltonian) a list of collapse operators. If the user passes a Hamiltonian then it, along with the list of collapse operators, will be converted into a Liouvillian operator in Lindblad form. Parameters ---------- A : qobj A Hamiltonian or Liouvillian operator. c_op_list : list A list of collapse operators. method : str {'direct', 'iterative', 'iterative-bicg', 'lu', 'svd', 'power'} Method for solving the underlying linear equation. Direct solver 'direct' (default), iterative GMRES method 'iterative', iterative method BICGSTAB 'iterative-bicg', LU decomposition 'lu', SVD 'svd' (dense), or inverse-power method 'power'. sparse : bool, default=True Solve for the steady state using sparse algorithms. If set to False, the underlying Liouvillian operator will be converted into a dense matrix. Use only for 'smaller' systems. maxiter : int, optional Maximum number of iterations to perform if using an iterative method such as 'iterative' (default=1000), or 'power' (default=10). tol : float, optional, default=1e-5 Tolerance used for terminating solver solution when using iterative solvers. use_precond : bool optional, default = True ITERATIVE ONLY. Use an incomplete sparse LU decomposition as a preconditioner for the 'iterative' LGMRES and BICG solvers. Speeds up convergence time by orders of magnitude in many cases. M : {sparse matrix, dense matrix, LinearOperator} Preconditioner for A. The preconditioner should approximate the inverse of A. Effective preconditioning dramatically improves the rate of convergence, for iterative methods. Does not affect other solvers. fill_factor : float, default=12 ITERATIVE ONLY. Specifies the fill ratio upper bound (>=1) of the iLU preconditioner. Lower values save memory at the cost of longer execution times and a possible singular factorization. drop_tol : float, default=1e-3 ITERATIVE ONLY. Sets the threshold for the magnitude of preconditioner elements that should be dropped. Can be reduced for a courser factorization at the cost of an increased number of iterations, and a possible singular factorization. diag_pivot_thresh : float, default=None ITERATIVE ONLY. Sets the threshold between [0,1] for which diagonal elements are considered acceptable pivot points when using a preconditioner. A value of zero forces the pivot to be the diagonal element. verbose : bool default=False Flag for printing out detailed information on the steady state solver. Returns ------- dm : qobj Steady state density matrix. Notes ----- The SVD method works only for dense operators (i.e. small systems). """ n_op = len(c_op_list) if isoper(A): if n_op == 0: raise TypeError('Cannot calculate the steady state for a ' + 'non-dissipative system ' + '(no collapse operators given)') else: A = liouvillian_fast(A, c_op_list) if not issuper(A): raise TypeError('Solving for steady states requires ' + 'Liouvillian (super) operators') if method == 'direct': if sparse: return _steadystate_direct_sparse(A, verbose=verbose) else: return _steadystate_direct_dense(A, verbose=verbose) elif method == 'iterative': return _steadystate_iterative(A, tol=tol, use_precond=use_precond, M=M, use_rcm=use_rcm, sym=sym, maxiter=maxiter, fill_factor=fill_factor, drop_tol=drop_tol, diag_pivot_thresh=diag_pivot_thresh, verbose=verbose) elif method == 'iterative-bicg': return _steadystate_iterative_bicg(A, tol=tol, use_precond=use_precond, M=M, use_rcm=use_rcm, maxiter=maxiter, fill_factor=fill_factor, drop_tol=drop_tol, diag_pivot_thresh=diag_pivot_thresh, verbose=verbose) elif method == 'lu': return _steadystate_lu(A, verbose=verbose) elif method == 'svd': return _steadystate_svd_dense(A, atol=1e-12, rtol=0, all_steadystates=False, verbose=verbose) elif method == 'power': return _steadystate_power(A, maxiter=10, tol=tol, itertol=tol, verbose=verbose) else: raise ValueError('Invalid method argument for steadystate.')
def steadystate(A, c_op_list=[], method='direct', sparse=True, use_umfpack=True, maxiter=5000, tol=1e-5, use_precond=True, perm_method='AUTO', drop_tol=1e-1, diag_pivot_thresh=0.33, verbose=False): """Calculates the steady state for the evolution subject to the supplied Hamiltonian or Liouvillian operator and (if given a Hamiltonian) a list of collapse operators. If the user passes a Hamiltonian then it, along with the list of collapse operators, will be converted into a Liouvillian operator in Lindblad form. Parameters ---------- A : qobj A Hamiltonian or Liouvillian operator. c_op_list : list A list of collapse operators. method : str {'direct', 'iterative', 'iterative-bicg', 'lu', 'svd', 'power'} Method for solving the underlying linear equation. Direct solver 'direct' (default), iterative LGMRES method 'iterative', iterative method BICG 'iterative-bicg', LU decomposition 'lu', SVD 'svd' (dense), or inverse-power method 'power'. sparse : bool default=True Solve for the steady state using sparse algorithms. If set to False, the underlying Liouvillian operator will be converted into a dense matrix. Use only for 'smaller' systems. use_umfpack : bool optional, default = True Use the UMFpack backend for the direct solver 'direct' or Power method 'power. If 'False', the solver uses the SuperLU backend. This option does not affect the other methods. Used only when sparse=True. maxiter : int optional Maximum number of iterations to perform if using an iterative method such as 'iterative' (default=5000), or 'power' (default=10). tol : float optional, default=1e-5 Tolerance used for terminating solver solution when using iterative solvers. use_precond : bool optional, default = True ITERATIVE ONLY. Use an incomplete sparse LU decomposition as a preconditioner for the 'iterative' LGMRES and BICG solvers. Speeds up convergence time by orders of magnitude in many cases. perm_method : str {'AUTO', 'AUTO-BREAK', 'COLAMD', 'MMD_ATA', 'NATURAL'} ITERATIVE ONLY. Sets the method for column ordering the incomplete LU preconditioner used by the 'iterative' method. When set to 'AUTO' (default), the solver will attempt to precondition the system using 'COLAMD'. If this fails, the solver will use no preconditioner. Using 'AUTO-BREAK' will cause the solver to issue an exception and stop if the 'COLAMD' method fails. drop_tol : float default=1e-1 ITERATIVE ONLY. Sets the threshold for the magnitude of preconditioner elements that should be dropped. diag_pivot_thresh : float default=0.33 ITERATIVE ONLY. Sets the threshold for which diagonal elements are considered acceptable pivot points when using a preconditioner. verbose : bool default=False Flag for printing out detailed information on the steady state solver. Returns ------- dm : qobj Steady state density matrix. Notes ----- The SVD method works only for dense operators (i.e. small systems). Setting use_umfpack=True (default) may result in 'out of memory' errors if your system size becomes to large. """ n_op = len(c_op_list) if isoper(A): if n_op == 0: raise TypeError('Cannot calculate the steady state for a ' + 'non-dissipative system ' + '(no collapse operators given)') else: A = liouvillian_fast(A, c_op_list) if not issuper(A): raise TypeError('Solving for steady states requires ' + 'Liouvillian (super) operators') if method == 'direct': if sparse: return _steadystate_direct_sparse(A, use_umfpack=use_umfpack, verbose=verbose) else: return _steadystate_direct_dense(A, verbose=verbose) elif method == 'iterative': return _steadystate_iterative(A, tol=tol, use_precond=use_precond, maxiter=maxiter, perm_method=perm_method, drop_tol=drop_tol, verbose=verbose, diag_pivot_thresh=diag_pivot_thresh) elif method == 'iterative-bicg': return _steadystate_iterative_bicg(A, tol=tol, use_precond=use_precond, maxiter=maxiter, perm_method=perm_method, drop_tol=drop_tol, verbose=verbose, diag_pivot_thresh=diag_pivot_thresh) elif method == 'lu': return _steadystate_lu(A, verbose=verbose) elif method == 'svd': return _steadystate_svd_dense(A, atol=1e-12, rtol=0, all_steadystates=False, verbose=verbose) elif method == 'power': return _steadystate_power(A, maxiter=10, tol=tol, itertol=tol, use_umfpack=use_umfpack, verbose=verbose) else: raise ValueError('Invalid method argument for steadystate.')
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 _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 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 _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)