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 _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 solve(self, rho0, tlist, options=None, progress=None): """ Solve the Hierarchy equations of motion for the given initial density matrix and time. """ if options is None: options = Options() output = Result() output.solver = "hsolve" output.times = tlist output.states = [] output.states.append(Qobj(rho0)) dt = np.diff(tlist) rho_he = np.zeros(self.hshape, dtype=np.complex) rho_he[0] = rho0.full().ravel("F") rho_he = rho_he.flatten() self.rhs() L_helems = self.L_helems.asformat("csr") r = ode(cy_ode_rhs) r.set_f_params(L_helems.data, L_helems.indices, L_helems.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(rho_he, tlist[0]) dt = np.diff(tlist) n_tsteps = len(tlist) if progress: bar = progress(total=n_tsteps - 1) for t_idx, t in enumerate(tlist): if t_idx < n_tsteps - 1: r.integrate(r.t + dt[t_idx]) r1 = r.y.reshape(self.hshape) r0 = r1[0].reshape(self.N, self.N).T output.states.append(Qobj(r0)) r_heom = r.y.reshape(self.hshape) self.full_hierarchy.append(r_heom) if progress: bar.update() return output
def run(self, rho0, tlist): """ Function to solve for an open quantum system using the HEOM model. Parameters ---------- rho0 : Qobj Initial state (density matrix) of the system. tlist : list Time over which system evolves. Returns ------- results : :class:`qutip.solver.Result` Object storing all results from the simulation. """ sup_dim = self._sup_dim solver = self._ode if not self._configured: raise RuntimeError("Solver must be configured before it is run") output = Result() output.solver = "hsolve" output.times = tlist output.states = [] output.states.append(Qobj(rho0)) rho0_flat = rho0.full().ravel('F') rho0_he = np.zeros([sup_dim*self._N_he], dtype=complex) rho0_he[:sup_dim] = rho0_flat solver.set_initial_value(rho0_he, tlist[0]) dt = np.diff(tlist) n_tsteps = len(tlist) for t_idx, t in enumerate(tlist): if t_idx < n_tsteps - 1: solver.integrate(solver.t + dt[t_idx]) rho = Qobj(solver.y[:sup_dim].reshape(rho0.shape,order='F'), dims=rho0.dims) output.states.append(rho) return output
def get_result(self, ntraj=[]): # Store results in the Result object if not ntraj: ntraj = [self.num_traj] elif not isinstance(ntraj, list): ntraj = [ntraj] output = Result() output.solver = 'mcsolve' output.seeds = self.seeds options = self.options output.options = options if options.steady_state_average: output.states = self.steady_state elif options.average_states and options.store_states: output.states = self.states elif options.store_states: output.states = self.runs_states if options.store_final_state: if options.average_states: output.final_state = self.final_state else: output.final_state = self.runs_final_states if options.average_expect: output.expect = [self.expect_traj_avg(n) for n in ntraj] if len(output.expect) == 1: output.expect = output.expect[0] else: output.expect = self.runs_expect # simulation parameters output.times = self.tlist output.num_expect = self.e_ops.e_num output.num_collapse = len(self.ss.td_c_ops) output.ntraj = self.num_traj output.col_times = self.collapse_times output.col_which = self.collapse_which return output
def solve(self, rho0, tlist, options=None): """ Solve the ODE for the evolution of diagonal states and Hamiltonians. """ if options is None: options = Options() output = Result() output.solver = "pisolve" output.times = tlist output.states = [] output.states.append(Qobj(rho0)) rhs_generate = lambda y, tt, M: M.dot(y) rho0_flat = np.diag(np.real(rho0.full())) L = self.coefficient_matrix() rho_t = odeint(rhs_generate, rho0_flat, tlist, args=(L,)) for r in rho_t[1:]: diag = np.diag(r) output.states.append(Qobj(diag)) return output
def solve(self, rho0, tlist, options=None): """ Solve the ODE for the evolution of diagonal states and Hamiltonians. """ if options is None: options = Options() output = Result() output.solver = "pisolve" output.times = tlist output.states = [] output.states.append(Qobj(rho0)) rhs_generate = lambda y, tt, M: M.dot(y) rho0_flat = np.diag(np.real(rho0.full())) L = self.coefficient_matrix() rho_t = odeint(rhs_generate, rho0_flat, tlist, args=(L, )) for r in rho_t[1:]: diag = np.diag(r) output.states.append(Qobj(diag)) return output
def essolve(H, rho0, tlist, c_op_list, e_ops): """ Evolution of a state vector or density matrix (`rho0`) for a given Hamiltonian (`H`) and set of collapse operators (`c_op_list`), by expressing the ODE as an exponential series. The output is either the state vector at arbitrary points in time (`tlist`), or the expectation values of the supplied operators (`e_ops`). .. deprecated:: 4.6.0 :obj:`~essolev` will be removed in QuTiP 5. Please use :obj:`~sesolve` or :obj:`~mesolve` for general-purpose integration of the Schroedinger/Lindblad master equation. This will likely be faster than :obj:`~essolve` for you. Parameters ---------- H : qobj/function_type System Hamiltonian. rho0 : :class:`qutip.qobj` Initial state density matrix. tlist : list/array ``list`` of times for :math:`t`. c_op_list : list of :class:`qutip.qobj` ``list`` of :class:`qutip.qobj` collapse operators. e_ops : list of :class:`qutip.qobj` ``list`` of :class:`qutip.qobj` operators for which to evaluate expectation values. Returns ------- expt_array : array Expectation values of wavefunctions/density matrices for the times specified in ``tlist``. .. note:: This solver does not support time-dependent Hamiltonians. """ n_expt_op = len(e_ops) n_tsteps = len(tlist) # Calculate the Liouvillian if (c_op_list is None or len(c_op_list) == 0) and isket(rho0): L = H else: L = liouvillian(H, c_op_list) es = ode2es(L, rho0) # evaluate the expectation values if n_expt_op == 0: results = [Qobj()] * n_tsteps else: results = np.zeros([n_expt_op, n_tsteps], dtype=complex) for n, e in enumerate(e_ops): results[n, :] = expect(e, esval(es, tlist)) data = Result() data.solver = "essolve" data.times = tlist data.expect = [ np.real(results[n, :]) if e.isherm else results[n, :] for n, e in enumerate(e_ops) ] return data
def fsesolve(H, psi0, tlist, e_ops=[], T=None, args={}, Tsteps=100): """ Solve the Schrodinger equation using the Floquet formalism. Parameters ---------- H : :class:`qutip.qobj.Qobj` System Hamiltonian, time-dependent with period `T`. psi0 : :class:`qutip.qobj` Initial state vector (ket). 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. If this list is empty, the state vectors for each time in `tlist` will be returned instead of expectation values. T : float The period of the time-dependence of the hamiltonian. args : dictionary Dictionary with variables required to evaluate H. Tsteps : integer The number of time steps in one driving period for which to precalculate the Floquet modes. `Tsteps` should be an even number. 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`. """ if not T: # assume that tlist span exactly one period of the driving T = tlist[-1] # find the floquet modes for the time-dependent hamiltonian f_modes_0, f_energies = floquet_modes(H, T, args) # calculate the wavefunctions using the from the floquet modes f_modes_table_t = floquet_modes_table(f_modes_0, f_energies, np.linspace(0, T, Tsteps + 1), H, T, args) # setup Result for storing the results output = Result() output.times = tlist output.solver = "fsesolve" if isinstance(e_ops, FunctionType): output.num_expect = 0 expt_callback = True elif isinstance(e_ops, list): output.num_expect = len(e_ops) expt_callback = False if output.num_expect == 0: output.states = [] else: output.expect = [] for op in e_ops: if op.isherm: output.expect.append(np.zeros(len(tlist))) else: output.expect.append(np.zeros(len(tlist), dtype=complex)) else: raise TypeError("e_ops must be a list Qobj or a callback function") psi0_fb = psi0.transform(f_modes_0) for t_idx, t in enumerate(tlist): f_modes_t = floquet_modes_t_lookup(f_modes_table_t, t, T) f_states_t = floquet_states(f_modes_t, f_energies, t) psi_t = psi0_fb.transform(f_states_t, True) if expt_callback: # use callback method e_ops(t, psi_t) else: # calculate all the expectation values, or output psi if # no expectation value operators where defined if output.num_expect == 0: output.states.append(Qobj(psi_t)) else: for e_idx, e in enumerate(e_ops): output.expect[e_idx][t_idx] = expect(e, psi_t) return output
def _generic_ode_solve(func, ode_args, rho0, tlist, e_ops, opt, progress_bar, dims=None): """ Internal function for solving ME. Calculate the required expectation values or invoke callback function at each time step. """ # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # This function is made similar to sesolve's one for futur merging in a # solver class # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # prepare output array n_tsteps = len(tlist) output = Result() output.solver = "mesolve" output.times = tlist size = rho0.shape[0] initial_vector = rho0.full().ravel('F') r = scipy.integrate.ode(func) 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) if ode_args: r.set_f_params(*ode_args) r.set_initial_value(initial_vector, tlist[0]) e_ops_data = [] output.expect = [] if callable(e_ops): n_expt_op = 0 expt_callback = True output.num_expect = 1 elif isinstance(e_ops, list): n_expt_op = len(e_ops) expt_callback = False output.num_expect = n_expt_op if n_expt_op == 0: # fall back on storing states opt.store_states = True else: for op in e_ops: if not isinstance(op, Qobj) and callable(op): output.expect.append(np.zeros(n_tsteps, dtype=complex)) continue if op.dims != rho0.dims: raise TypeError(f"e_ops dims ({op.dims}) are not " f"compatible with the state's " f"({rho0.dims})") e_ops_data.append(spre(op).data) if op.isherm and rho0.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") if opt.store_states: output.states = [] def get_curr_state_data(r): return vec2mat(r.y) # # start evolution # dt = np.diff(tlist) cdata = None progress_bar.start(n_tsteps) for t_idx, t in enumerate(tlist): progress_bar.update(t_idx) if not r.successful(): raise Exception("ODE integration error: Try to increase " "the allowed number of substeps by increasing " "the nsteps parameter in the Options class.") if opt.store_states or expt_callback: cdata = get_curr_state_data(r) fdata = dense2D_to_fastcsr_fmode(cdata, size, size) # Try to guess if there is a fast path for rho_t if issuper(rho0) or not rho0.isherm: rho_t = Qobj(fdata, dims=dims) else: rho_t = Qobj(fdata, dims=dims, fast="mc-dm") if opt.store_states: output.states.append(rho_t) if expt_callback: # use callback method output.expect.append(e_ops(t, rho_t)) for m in range(n_expt_op): if not isinstance(e_ops[m], Qobj) and callable(e_ops[m]): output.expect[m][t_idx] = e_ops[m](t, rho_t) continue output.expect[m][t_idx] = expect_rho_vec( e_ops_data[m], r.y, e_ops[m].isherm and rho0.isherm) if t_idx < n_tsteps - 1: r.integrate(r.t + dt[t_idx]) progress_bar.finished() if opt.store_final_state: cdata = get_curr_state_data(r) output.final_state = Qobj(cdata, dims=dims, isherm=rho0.isherm or None) return output
def ttmsolve(dynmaps, rho0, times, e_ops=[], learningtimes=None, tensors=None, **kwargs): """ Solve time-evolution using the Transfer Tensor Method, based on a set of precomputed dynamical maps. Parameters ---------- dynmaps : list of :class:`qutip.Qobj` List of precomputed dynamical maps (superoperators), or a callback function that returns the superoperator at a given time. rho0 : :class:`qutip.Qobj` Initial density matrix or state vector (ket). times : array_like list of times :math:`t_n` at which to compute :math:`\\rho(t_n)`. Must be uniformily spaced. e_ops : list of :class:`qutip.Qobj` / callback function single operator or list of operators for which to evaluate expectation values. learningtimes : array_like list of times :math:`t_k` for which we have knowledge of the dynamical maps :math:`E(t_k)`. tensors : array_like optional list of precomputed tensors :math:`T_k` kwargs : dictionary Optional keyword arguments. See :class:`qutip.nonmarkov.ttm.TTMSolverOptions`. Returns ------- output: :class:`qutip.solver.Result` An instance of the class :class:`qutip.solver.Result`. """ opt = TTMSolverOptions(dynmaps=dynmaps, times=times, learningtimes=learningtimes, **kwargs) diff = None if isket(rho0): rho0 = ket2dm(rho0) output = Result() e_sops_data = [] if callable(e_ops): n_expt_op = 0 expt_callback = True else: try: tmp = e_ops[:] del tmp n_expt_op = len(e_ops) expt_callback = False if n_expt_op == 0: # fall back on storing states opt.store_states = True for op in e_ops: e_sops_data.append(spre(op).data) if op.isherm and rho0.isherm: output.expect.append(np.zeros(len(times))) else: output.expect.append(np.zeros(len(times), dtype=complex)) except TypeError: raise TypeError("Argument 'e_ops' should be a callable or" + "list-like.") if tensors is None: tensors, diff = _generatetensors(dynmaps, learningtimes, opt=opt) if rho0.isoper: # vectorize density matrix rho0vec = operator_to_vector(rho0) else: # rho0 might be a super in which case we should not vectorize rho0vec = rho0 K = len(tensors) states = [rho0vec] for n in range(1, len(times)): states.append(None) for k in range(n): if n-k < K: states[-1] += tensors[n-k]*states[k] for i, r in enumerate(states): if opt.store_states or expt_callback: if r.type == 'operator-ket': states[i] = vector_to_operator(r) else: states[i] = r if expt_callback: # use callback method e_ops(times[i], states[i]) for m in range(n_expt_op): if output.expect[m].dtype == complex: output.expect[m][i] = expect_rho_vec(e_sops_data[m], r, 0) else: output.expect[m][i] = expect_rho_vec(e_sops_data[m], r, 1) output.solver = "ttmsolve" output.times = times output.ttmconvergence = diff if opt.store_states: output.states = states return output
def brmesolve(H, psi0, tlist, a_ops=[], e_ops=[], c_ops=[], args={}, use_secular=True, sec_cutoff = 0.1, tol=qset.atol, spectra_cb=None, options=None, progress_bar=None, _safe_mode=True, verbose=False): """ Solves for the dynamics of a system using the Bloch-Redfield master equation, given an input Hamiltonian, Hermitian bath-coupling terms and their associated spectrum functions, as well as possible Lindblad collapse operators. For time-independent systems, the Hamiltonian must be given as a Qobj, whereas the bath-coupling terms (a_ops), must be written as a nested list of operator - spectrum function pairs, where the frequency is specified by the `w` variable. *Example* a_ops = [[a+a.dag(),lambda w: 0.2*(w>=0)]] For time-dependent systems, the Hamiltonian, a_ops, and Lindblad collapse operators (c_ops), can be specified in the QuTiP string-based time-dependent format. For the a_op spectra, the frequency variable must be `w`, and the string cannot contain any other variables other than the possibility of having a time-dependence through the time variable `t`: *Example* a_ops = [[a+a.dag(), '0.2*exp(-t)*(w>=0)']] It is also possible to use Cubic_Spline objects for time-dependence. In the case of a_ops, Cubic_Splines must be passed as a tuple: *Example* a_ops = [ [a+a.dag(), ( f(w), g(t)] ] where f(w) and g(t) are strings or Cubic_spline objects for the bath spectrum and time-dependence, respectively. Finally, if one has bath-couplimg terms of the form H = f(t)*a + conj[f(t)]*a.dag(), then the correct input format is *Example* a_ops = [ [(a,a.dag()), (f(w), g1(t), g2(t))],... ] where f(w) is the spectrum of the operators while g1(t) and g2(t) are the time-dependence of the operators `a` and `a.dag()`, respectively Parameters ---------- H : Qobj / list System Hamiltonian given as a Qobj or nested list in string-based format. psi0: Qobj Initial density matrix or state vector (ket). tlist : array_like List of times for evaluating evolution a_ops : list Nested list of Hermitian system operators that couple to the bath degrees of freedom, along with their associated spectra. e_ops : list List of operators for which to evaluate expectation values. c_ops : list List of system collapse operators, or nested list in string-based format. args : dict Placeholder for future implementation, kept for API consistency. use_secular : bool {True} Use secular approximation when evaluating bath-coupling terms. sec_cutoff : float {0.1} Cutoff for secular approximation. tol : float {qutip.setttings.atol} Tolerance used for removing small values after basis transformation. spectra_cb : list DEPRECIATED. Do not use. options : :class:`qutip.solver.Options` Options for the solver. progress_bar : BaseProgressBar Optional instance of BaseProgressBar, or a subclass thereof, for showing the progress of the simulation. Returns ------- result: :class:`qutip.solver.Result` An instance of the class :class:`qutip.solver.Result`, which contains either an array of expectation values, for operators given in e_ops, or a list of states for the times specified by `tlist`. """ _prep_time = time.time() #This allows for passing a list of time-independent Qobj #as allowed by mesolve if isinstance(H, list): if np.all([isinstance(h,Qobj) for h in H]): H = sum(H) if isinstance(c_ops, Qobj): c_ops = [c_ops] if isinstance(e_ops, Qobj): e_ops = [e_ops] if isinstance(e_ops, dict): e_ops_dict = e_ops e_ops = [e for e in e_ops.values()] else: e_ops_dict = None if not (spectra_cb is None): warnings.warn("The use of spectra_cb is depreciated.", DeprecationWarning) _a_ops = [] for kk, a in enumerate(a_ops): _a_ops.append([a,spectra_cb[kk]]) a_ops = _a_ops if _safe_mode: _solver_safety_check(H, psi0, a_ops+c_ops, e_ops, args) # check for type (if any) of time-dependent inputs _, n_func, n_str = _td_format_check(H, a_ops+c_ops) if progress_bar is None: progress_bar = BaseProgressBar() elif progress_bar is True: progress_bar = TextProgressBar() if options is None: options = Options() if (not options.rhs_reuse) or (not config.tdfunc): # reset config collapse and time-dependence flags to default values config.reset() #check if should use OPENMP check_use_openmp(options) if n_str == 0: R, ekets = bloch_redfield_tensor(H, a_ops, spectra_cb=None, c_ops=c_ops, use_secular=use_secular, sec_cutoff=sec_cutoff) output = Result() output.solver = "brmesolve" output.times = tlist results = bloch_redfield_solve(R, ekets, psi0, tlist, e_ops, options, progress_bar=progress_bar) if e_ops: output.expect = results else: output.states = results return output elif n_str != 0 and n_func == 0: output = _td_brmesolve(H, psi0, tlist, a_ops=a_ops, e_ops=e_ops, c_ops=c_ops, args=args, use_secular=use_secular, sec_cutoff=sec_cutoff, tol=tol, options=options, progress_bar=progress_bar, _safe_mode=_safe_mode, verbose=verbose, _prep_time=_prep_time) return output else: raise Exception('Cannot mix func and str formats.')
def essolve(H, rho0, tlist, c_op_list, e_ops): """ Evolution of a state vector or density matrix (`rho0`) for a given Hamiltonian (`H`) and set of collapse operators (`c_op_list`), by expressing the ODE as an exponential series. The output is either the state vector at arbitrary points in time (`tlist`), or the expectation values of the supplied operators (`e_ops`). Parameters ---------- H : qobj/function_type System Hamiltonian. rho0 : :class:`qutip.qobj` Initial state density matrix. tlist : list/array ``list`` of times for :math:`t`. c_op_list : list of :class:`qutip.qobj` ``list`` of :class:`qutip.qobj` collapse operators. e_ops : list of :class:`qutip.qobj` ``list`` of :class:`qutip.qobj` operators for which to evaluate expectation values. Returns ------- expt_array : array Expectation values of wavefunctions/density matrices for the times specified in ``tlist``. .. note:: This solver does not support time-dependent Hamiltonians. """ n_expt_op = len(e_ops) n_tsteps = len(tlist) # Calculate the Liouvillian if (c_op_list is None or len(c_op_list) == 0) and isket(rho0): L = H else: L = liouvillian(H, c_op_list) es = ode2es(L, rho0) # evaluate the expectation values if n_expt_op == 0: results = [Qobj()] * n_tsteps else: results = np.zeros([n_expt_op, n_tsteps], dtype=complex) for n, e in enumerate(e_ops): results[n, :] = expect(e, esval(es, tlist)) data = Result() data.solver = "essolve" data.times = tlist data.expect = [np.real(results[n, :]) if e.isherm else results[n, :] for n, e in enumerate(e_ops)] return data
def _generic_ode_solve(func, ode_args, psi0, tlist, e_ops, opt, progress_bar, dims=None): """ Internal function for solving ODEs. """ # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # This function is made similar to mesolve's one for futur merging in a # solver class # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% # prepare output array n_tsteps = len(tlist) output = Result() output.solver = "sesolve" output.times = tlist if psi0.isunitary: initial_vector = psi0.full().ravel('F') oper_evo = True size = psi0.shape[0] # oper_n = dims[0][0] # norm_dim_factor = np.sqrt(oper_n) elif psi0.isket: initial_vector = psi0.full().ravel() oper_evo = False # norm_dim_factor = 1.0 r = scipy.integrate.ode(func) 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) if ode_args: r.set_f_params(*ode_args) r.set_initial_value(initial_vector, tlist[0]) e_ops_data = [] output.expect = [] if callable(e_ops): n_expt_op = 0 expt_callback = True output.num_expect = 1 elif isinstance(e_ops, list): n_expt_op = len(e_ops) expt_callback = False output.num_expect = n_expt_op if n_expt_op == 0: # fallback on storing states opt.store_states = True else: for op in e_ops: if not isinstance(op, Qobj) and callable(op): output.expect.append(np.zeros(n_tsteps, dtype=complex)) continue if op.isherm: output.expect.append(np.zeros(n_tsteps)) else: output.expect.append(np.zeros(n_tsteps, dtype=complex)) if oper_evo: for e in e_ops: if isinstance(e, Qobj): e_ops_data.append(e.dag().data) continue e_ops_data.append(e) else: for e in e_ops: if isinstance(e, Qobj): e_ops_data.append(e.data) continue e_ops_data.append(e) else: raise TypeError("Expectation parameter must be a list or a function") if opt.store_states: output.states = [] if oper_evo: def get_curr_state_data(r): return vec2mat(r.y) else: def get_curr_state_data(r): return r.y # # start evolution # dt = np.diff(tlist) cdata = None progress_bar.start(n_tsteps) for t_idx, t in enumerate(tlist): progress_bar.update(t_idx) if not r.successful(): raise Exception("ODE integration error: Try to increase " "the allowed number of substeps by increasing " "the nsteps parameter in the Options class.") # get the current state / oper data if needed if opt.store_states or opt.normalize_output \ or n_expt_op > 0 or expt_callback: cdata = get_curr_state_data(r) if opt.normalize_output: # normalize per column if oper_evo: cdata /= la_norm(cdata, axis=0) #cdata *= norm_dim_factor / la_norm(cdata) r.set_initial_value(cdata.ravel('F'), r.t) else: #cdata /= la_norm(cdata) norm = normalize_inplace(cdata) if norm > 1e-12: # only reset the solver if state changed r.set_initial_value(cdata, r.t) else: r._y = cdata if opt.store_states: if oper_evo: fdata = dense2D_to_fastcsr_fmode(cdata, size, size) output.states.append(Qobj(fdata, dims=dims)) else: fdata = dense1D_to_fastcsr_ket(cdata) output.states.append(Qobj(fdata, dims=dims, fast='mc')) if expt_callback: # use callback method output.expect.append(e_ops(t, Qobj(cdata, dims=dims))) if oper_evo: for m in range(n_expt_op): if callable(e_ops_data[m]): func = e_ops_data[m] output.expect[m][t_idx] = func(t, Qobj(cdata, dims=dims)) continue output.expect[m][t_idx] = (e_ops_data[m] * cdata).trace() else: for m in range(n_expt_op): if callable(e_ops_data[m]): func = e_ops_data[m] output.expect[m][t_idx] = func(t, Qobj(cdata, dims=dims)) continue output.expect[m][t_idx] = cy_expect_psi(e_ops_data[m], cdata, e_ops[m].isherm) if t_idx < n_tsteps - 1: r.integrate(r.t + dt[t_idx]) progress_bar.finished() if opt.store_final_state: cdata = get_curr_state_data(r) if opt.normalize_output: cdata /= la_norm(cdata, axis=0) # cdata *= norm_dim_factor / la_norm(cdata) output.final_state = Qobj(cdata, dims=dims) return output
def _generic_ode_solve(r, psi0, tlist, e_ops, opt, progress_bar, state_norm_func=None, dims=None): """ Internal function for solving ODEs. """ # # prepare output array # n_tsteps = len(tlist) output = Result() output.solver = "sesolve" output.times = tlist if opt.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: # fallback on storing states output.states = [] opt.store_states = True 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") # # start evolution # progress_bar.start(n_tsteps) dt = np.diff(tlist) for t_idx, t in enumerate(tlist): progress_bar.update(t_idx) if not r.successful(): break if state_norm_func: data = r.y / state_norm_func(r.y) r.set_initial_value(data, r.t) if opt.store_states: output.states.append(Qobj(r.y, dims=dims)) if expt_callback: # use callback method e_ops(t, Qobj(r.y, dims=psi0.dims)) for m in range(n_expt_op): output.expect[m][t_idx] = cy_expect_psi(e_ops[m].data, r.y, e_ops[m].isherm) if t_idx < n_tsteps - 1: r.integrate(r.t + dt[t_idx]) progress_bar.finished() if not opt.rhs_reuse and config.tdname is not None: try: os.remove(config.tdname + ".pyx") except: pass if opt.store_final_state: output.final_state = Qobj(r.y) return output
def generic_ode_solve_checkpoint(r, rho0, tlist, e_ops, opt, progress_bar, save, subdir): """ Internal function for solving ME. Solve an ODE which solver parameters already setup (r). Calculate the required expectation values or invoke callback function at each time step. """ # # prepare output array # n_tsteps = len(tlist) e_sops_data = [] output = Result() output.solver = "mesolve" output.times = tlist if opt.store_states: output.states = [] e_ops_dict = e_ops e_ops = [e for e in e_ops_dict.values()] headings = [key for key in e_ops_dict.keys()] 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 = [] opt.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 and rho0.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") results_row = np.zeros(n_expt_op) # # start evolution # progress_bar.start(n_tsteps) rho = Qobj(rho0) dims = rho.dims dt = np.diff(tlist) end_time = tlist[-1] for t_idx, t in tqdm(enumerate(tlist)): progress_bar.update(t_idx) if not r.successful(): raise Exception("ODE integration error: Try to increase " "the allowed number of substeps by increasing " "the nsteps parameter in the Options class.") if opt.store_states or expt_callback: rho.data = vec2mat(r.y) if opt.store_states: output.states.append(Qobj(rho)) 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], r.y, 0) results_row[m] = output.expect[m][t_idx] else: output.expect[m][t_idx] = expect_rho_vec( e_sops_data[m], r.y, 1) results_row[m] = output.expect[m][t_idx] results = pd.DataFrame(results_row).T results.columns = headings results.index = [t] results.index.name = 'times' if t == 0: first_row = True else: first_row = False if save: rho_checkpoint = Qobj(vec2mat(r.y)) rho_checkpoint.dims = dims if t_idx % 200 == 0: rho_c = rho_checkpoint.ptrace(0) with open('./cavity_states.pkl', 'ab') as f: pickle.dump(rho_c, f) with open('./results.csv', 'a') as file: results.to_csv(file, header=first_row, float_format='%.15f') qsave(rho_checkpoint, './state_checkpoint') save = True if t_idx < n_tsteps - 1: r.integrate(r.t + dt[t_idx]) progress_bar.finished() if not opt.rhs_reuse and config.tdname is not None: _cython_build_cleanup(config.tdname) return output
def mcsolve(H, psi0, tlist, c_ops, e_ops, ntraj=None, args={}, options=Options()): """Monte-Carlo evolution of a state vector :math:`|\psi \\rangle` for a given Hamiltonian and sets of collapse operators, and possibly, operators for calculating expectation values. Options for the underlying ODE solver are given by the Options class. mcsolve supports time-dependent Hamiltonians and collapse operators using either Python functions of strings to represent time-dependent coefficients. Note that, the system Hamiltonian MUST have at least one constant term. As an example of a time-dependent problem, consider a Hamiltonian with two terms ``H0`` and ``H1``, where ``H1`` is time-dependent with coefficient ``sin(w*t)``, and collapse operators ``C0`` and ``C1``, where ``C1`` is time-dependent with coeffcient ``exp(-a*t)``. Here, w and a are constant arguments with values ``W`` and ``A``. Using the Python function time-dependent format requires two Python functions, one for each collapse coefficient. Therefore, this problem could be expressed as:: def H1_coeff(t,args): return sin(args['w']*t) def C1_coeff(t,args): return exp(-args['a']*t) H=[H0,[H1,H1_coeff]] c_op_list=[C0,[C1,C1_coeff]] args={'a':A,'w':W} or in String (Cython) format we could write:: H=[H0,[H1,'sin(w*t)']] c_op_list=[C0,[C1,'exp(-a*t)']] args={'a':A,'w':W} Constant terms are preferably placed first in the Hamiltonian and collapse operator lists. Parameters ---------- H : qobj System Hamiltonian. psi0 : qobj Initial state vector tlist : array_like Times at which results are recorded. ntraj : int Number of trajectories to run. c_ops : array_like single collapse operator or ``list`` or ``array`` of collapse operators. e_ops : array_like single operator or ``list`` or ``array`` of operators for calculating expectation values. args : dict Arguments for time-dependent Hamiltonian and collapse operator terms. options : Options Instance of ODE solver options. Returns ------- results : Result Object storing all results from simulation. """ if debug: print(inspect.stack()[0][3]) if ntraj is None: ntraj = options.ntraj if not psi0.isket: raise Exception("Initial state must be a state vector.") if isinstance(c_ops, Qobj): c_ops = [c_ops] if isinstance(e_ops, Qobj): e_ops = [e_ops] if isinstance(e_ops, dict): e_ops_dict = e_ops e_ops = [e for e in e_ops.values()] else: e_ops_dict = None config.options = options if isinstance(ntraj, list): config.progress_bar = TextProgressBar(max(ntraj)) else: config.progress_bar = TextProgressBar(ntraj) # set num_cpus to the value given in qutip.settings if none in Options if not config.options.num_cpus: config.options.num_cpus = qutip.settings.num_cpus # set initial value data if options.tidy: config.psi0 = psi0.tidyup(options.atol).full().ravel() else: config.psi0 = psi0.full().ravel() config.psi0_dims = psi0.dims config.psi0_shape = psi0.shape # set options on ouput states if config.options.steady_state_average: config.options.average_states = True # set general items config.tlist = tlist if isinstance(ntraj, (list, ndarray)): config.ntraj = sort(ntraj)[-1] else: config.ntraj = ntraj # set norm finding constants config.norm_tol = options.norm_tol config.norm_steps = options.norm_steps # convert array based time-dependence to string format H, c_ops, args = _td_wrap_array_str(H, c_ops, args, tlist) # ---------------------------------------------- # SETUP ODE DATA IF NONE EXISTS OR NOT REUSING # ---------------------------------------------- if (not options.rhs_reuse) or (not config.tdfunc): # reset config collapse and time-dependence flags to default values config.soft_reset() # check for type of time-dependence (if any) time_type, h_stuff, c_stuff = _td_format_check(H, c_ops, "mc") h_terms = len(h_stuff[0]) + len(h_stuff[1]) + len(h_stuff[2]) c_terms = len(c_stuff[0]) + len(c_stuff[1]) + len(c_stuff[2]) # set time_type for use in multiprocessing config.tflag = time_type # check for collapse operators if c_terms > 0: config.cflag = 1 else: config.cflag = 0 # Configure data _mc_data_config(H, psi0, h_stuff, c_ops, c_stuff, args, e_ops, options, config) # compile and load cython functions if necessary _mc_func_load(config) else: # setup args for new parameters when rhs_reuse=True and tdfunc is given # string based if config.tflag in array([1, 10, 11]): if any(args): config.c_args = [] arg_items = args.items() for k in range(len(args)): config.c_args.append(arg_items[k][1]) # function based elif config.tflag in array([2, 3, 20, 22]): config.h_func_args = args # load monte-carlo class mc = _MC_class(config) # RUN THE SIMULATION mc.run() # remove RHS cython file if necessary if not options.rhs_reuse and config.tdname: try: os.remove(config.tdname + ".pyx") except: pass # AFTER MCSOLVER IS DONE -------------------------------------- # ------- COLLECT AND RETURN OUTPUT DATA IN ODEDATA OBJECT -------------- output = Result() output.solver = "mcsolve" # state vectors if mc.psi_out is not None and config.options.average_states and config.cflag and ntraj != 1: output.states = parfor(_mc_dm_avg, mc.psi_out.T) elif mc.psi_out is not None: output.states = mc.psi_out # expectation values elif mc.expect_out is not None and config.cflag and config.options.average_expect: # averaging if multiple trajectories if isinstance(ntraj, int): output.expect = [mean([mc.expect_out[nt][op] for nt in range(ntraj)], axis=0) for op in range(config.e_num)] elif isinstance(ntraj, (list, ndarray)): output.expect = [] for num in ntraj: expt_data = mean(mc.expect_out[:num], axis=0) data_list = [] if any([not op.isherm for op in e_ops]): for k in range(len(e_ops)): if e_ops[k].isherm: data_list.append(np.real(expt_data[k])) else: data_list.append(expt_data[k]) else: data_list = [data for data in expt_data] output.expect.append(data_list) else: # no averaging for single trajectory or if average_states flag # (Options) is off if mc.expect_out is not None: output.expect = mc.expect_out # simulation parameters output.times = config.tlist output.num_expect = config.e_num output.num_collapse = config.c_num output.ntraj = config.ntraj output.col_times = mc.collapse_times_out output.col_which = mc.which_op_out if e_ops_dict: output.expect = {e: output.expect[n] for n, e in enumerate(e_ops_dict.keys())} return output
def mcsolve_f90(H, psi0, tlist, c_ops, e_ops, ntraj=None, options=Options(), sparse_dms=True, serial=False, ptrace_sel=[], calc_entropy=False): """ Monte-Carlo wave function solver with fortran 90 backend. Usage is identical to qutip.mcsolve, for problems without explicit time-dependence, and with some optional input: Parameters ---------- H : qobj System Hamiltonian. psi0 : qobj Initial state vector tlist : array_like Times at which results are recorded. ntraj : int Number of trajectories to run. c_ops : array_like ``list`` or ``array`` of collapse operators. e_ops : array_like ``list`` or ``array`` of operators for calculating expectation values. options : Options Instance of solver options. sparse_dms : boolean If averaged density matrices are returned, they will be stored as sparse (Compressed Row Format) matrices during computation if sparse_dms = True (default), and dense matrices otherwise. Dense matrices might be preferable for smaller systems. serial : boolean If True (default is False) the solver will not make use of the multiprocessing module, and simply run in serial. ptrace_sel: list This optional argument specifies a list of components to keep when returning a partially traced density matrix. This can be convenient for large systems where memory becomes a problem, but you are only interested in parts of the density matrix. calc_entropy : boolean If ptrace_sel is specified, calc_entropy=True will have the solver return the averaged entropy over trajectories in results.entropy. This can be interpreted as a measure of entanglement. See Phys. Rev. Lett. 93, 120408 (2004), Phys. Rev. A 86, 022310 (2012). Returns ------- results : Result Object storing all results from simulation. """ if ntraj is None: ntraj = options.ntraj if psi0.type != 'ket': raise Exception("Initial state must be a state vector.") config.options = options # set num_cpus to the value given in qutip.settings # if none in Options if not config.options.num_cpus: config.options.num_cpus = qutip.settings.num_cpus # set initial value data if options.tidy: config.psi0 = psi0.tidyup(options.atol).full() else: config.psi0 = psi0.full() config.psi0_dims = psi0.dims config.psi0_shape = psi0.shape # set general items config.tlist = tlist if isinstance(ntraj, (list, np.ndarray)): raise Exception("ntraj as list argument is not supported.") else: config.ntraj = ntraj # ntraj_list = [ntraj] # set norm finding constants config.norm_tol = options.norm_tol config.norm_steps = options.norm_steps if not options.rhs_reuse: config.soft_reset() # no time dependence config.tflag = 0 # check for collapse operators if len(c_ops) > 0: config.cflag = 1 else: config.cflag = 0 # Configure data _mc_data_config(H, psi0, [], c_ops, [], [], e_ops, options, config) # Load Monte Carlo class mc = _MC_class() # Set solver type if (options.method == 'adams'): mc.mf = 10 elif (options.method == 'bdf'): mc.mf = 22 else: if debug: print('Unrecognized method for ode solver, using "adams".') mc.mf = 10 # store ket and density matrix dims and shape for convenience mc.psi0_dims = psi0.dims mc.psi0_shape = psi0.shape mc.dm_dims = (psi0 * psi0.dag()).dims mc.dm_shape = (psi0 * psi0.dag()).shape # use sparse density matrices during computation? mc.sparse_dms = sparse_dms # run in serial? mc.serial_run = serial or (ntraj == 1) # are we doing a partial trace for returned states? mc.ptrace_sel = ptrace_sel if (ptrace_sel != []): if debug: print("ptrace_sel set to " + str(ptrace_sel)) print("We are using dense density matrices during computation " + "when performing partial trace. Setting sparse_dms = False") print("This feature is experimental.") mc.sparse_dms = False mc.dm_dims = psi0.ptrace(ptrace_sel).dims mc.dm_shape = psi0.ptrace(ptrace_sel).shape if (calc_entropy): if (ptrace_sel == []): if debug: print("calc_entropy = True, but ptrace_sel = []. Please set " + "a list of components to keep when calculating average" + " entropy of reduced density matrix in ptrace_sel. " + "Setting calc_entropy = False.") calc_entropy = False mc.calc_entropy = calc_entropy # construct output Result object output = Result() # Run mc.run() output.states = mc.sol.states output.expect = mc.sol.expect output.col_times = mc.sol.col_times output.col_which = mc.sol.col_which if (hasattr(mc.sol, 'entropy')): output.entropy = mc.sol.entropy output.solver = 'Fortran 90 Monte Carlo solver' # simulation parameters output.times = config.tlist output.num_expect = config.e_num output.num_collapse = config.c_num output.ntraj = config.ntraj return output
def _ssepdpsolve_generic(sso, options, progress_bar): """ For internal use. See ssepdpsolve. """ 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 = "sepdpsolve" data.times = sso.tlist data.expect = np.zeros((len(sso.e_ops), N_store), dtype=complex) data.ss = np.zeros((len(sso.e_ops), N_store), dtype=complex) data.jump_times = [] data.jump_op_idx = [] # effective hamiltonian for deterministic part Heff = sso.H for c in sso.c_ops: Heff += -0.5j * c.dag() * c progress_bar.start(sso.ntraj) for n in range(sso.ntraj): progress_bar.update(n) psi_t = sso.state0.full().ravel() states_list, jump_times, jump_op_idx = \ _ssepdpsolve_single_trajectory(data, Heff, dt, sso.times, N_store, N_substeps, psi_t, sso.state0.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 / 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(sso.e_ops)] return data
def mcsolve(H, psi0, tlist, c_ops, e_ops, ntraj=None, args={}, options=None, progress_bar=True, map_func=None, map_kwargs=None): """Monte Carlo evolution of a state vector :math:`|\psi \\rangle` for a given Hamiltonian and sets of collapse operators, and possibly, operators for calculating expectation values. Options for the underlying ODE solver are given by the Options class. mcsolve supports time-dependent Hamiltonians and collapse operators using either Python functions of strings to represent time-dependent coefficients. Note that, the system Hamiltonian MUST have at least one constant term. As an example of a time-dependent problem, consider a Hamiltonian with two terms ``H0`` and ``H1``, where ``H1`` is time-dependent with coefficient ``sin(w*t)``, and collapse operators ``C0`` and ``C1``, where ``C1`` is time-dependent with coeffcient ``exp(-a*t)``. Here, w and a are constant arguments with values ``W`` and ``A``. Using the Python function time-dependent format requires two Python functions, one for each collapse coefficient. Therefore, this problem could be expressed as:: def H1_coeff(t,args): return sin(args['w']*t) def C1_coeff(t,args): return exp(-args['a']*t) H = [H0, [H1, H1_coeff]] c_ops = [C0, [C1, C1_coeff]] args={'a': A, 'w': W} or in String (Cython) format we could write:: H = [H0, [H1, 'sin(w*t)']] c_ops = [C0, [C1, 'exp(-a*t)']] args={'a': A, 'w': W} Constant terms are preferably placed first in the Hamiltonian and collapse operator lists. Parameters ---------- H : :class:`qutip.Qobj` System Hamiltonian. psi0 : :class:`qutip.Qobj` Initial state vector tlist : array_like Times at which results are recorded. ntraj : int Number of trajectories to run. c_ops : array_like single collapse operator or ``list`` or ``array`` of collapse operators. e_ops : array_like single operator or ``list`` or ``array`` of operators for calculating expectation values. args : dict Arguments for time-dependent Hamiltonian and collapse operator terms. options : Options Instance of ODE solver options. progress_bar: BaseProgressBar Optional instance of BaseProgressBar, or a subclass thereof, for showing the progress of the simulation. Set to None to disable the progress bar. map_func: function A map function for managing the calls to the single-trajactory solver. map_kwargs: dictionary Optional keyword arguments to the map_func function. Returns ------- results : :class:`qutip.solver.Result` Object storing all results from the simulation. .. note:: It is possible to reuse the random number seeds from a previous run of the mcsolver by passing the output Result object seeds via the Options class, i.e. Options(seeds=prev_result.seeds). """ if debug: print(inspect.stack()[0][3]) if options is None: options = Options() if ntraj is None: ntraj = options.ntraj config.map_func = map_func if map_func is not None else parallel_map config.map_kwargs = map_kwargs if map_kwargs is not None else {} if not psi0.isket: raise Exception("Initial state must be a state vector.") if isinstance(c_ops, Qobj): c_ops = [c_ops] if isinstance(e_ops, Qobj): e_ops = [e_ops] if isinstance(e_ops, dict): e_ops_dict = e_ops e_ops = [e for e in e_ops.values()] else: e_ops_dict = None config.options = options if progress_bar: if progress_bar is True: config.progress_bar = TextProgressBar() else: config.progress_bar = progress_bar else: config.progress_bar = BaseProgressBar() # set num_cpus to the value given in qutip.settings if none in Options if not config.options.num_cpus: config.options.num_cpus = qutip.settings.num_cpus if config.options.num_cpus == 1: # fallback on serial_map if num_cpu == 1, since there is no # benefit of starting multiprocessing in this case config.map_func = serial_map # set initial value data if options.tidy: config.psi0 = psi0.tidyup(options.atol).full().ravel() else: config.psi0 = psi0.full().ravel() config.psi0_dims = psi0.dims config.psi0_shape = psi0.shape # set options on ouput states if config.options.steady_state_average: config.options.average_states = True # set general items config.tlist = tlist if isinstance(ntraj, (list, np.ndarray)): config.ntraj = np.sort(ntraj)[-1] else: config.ntraj = ntraj # set norm finding constants config.norm_tol = options.norm_tol config.norm_steps = options.norm_steps # convert array based time-dependence to string format H, c_ops, args = _td_wrap_array_str(H, c_ops, args, tlist) # SETUP ODE DATA IF NONE EXISTS OR NOT REUSING # -------------------------------------------- if not options.rhs_reuse or not config.tdfunc: # reset config collapse and time-dependence flags to default values config.soft_reset() # check for type of time-dependence (if any) time_type, h_stuff, c_stuff = _td_format_check(H, c_ops, 'mc') c_terms = len(c_stuff[0]) + len(c_stuff[1]) + len(c_stuff[2]) # set time_type for use in multiprocessing config.tflag = time_type # check for collapse operators if c_terms > 0: config.cflag = 1 else: config.cflag = 0 # Configure data _mc_data_config(H, psi0, h_stuff, c_ops, c_stuff, args, e_ops, options, config) # compile and load cython functions if necessary _mc_func_load(config) else: # setup args for new parameters when rhs_reuse=True and tdfunc is given # string based if config.tflag in [1, 10, 11]: if any(args): config.c_args = [] arg_items = list(args.items()) for k in range(len(arg_items)): config.c_args.append(arg_items[k][1]) # function based elif config.tflag in [2, 3, 20, 22]: config.h_func_args = args # load monte carlo class mc = _MC(config) # Run the simulation mc.run() # Remove RHS cython file if necessary if not options.rhs_reuse and config.tdname: _cython_build_cleanup(config.tdname) # AFTER MCSOLVER IS DONE # ---------------------- # Store results in the Result object output = Result() output.solver = 'mcsolve' output.seeds = config.options.seeds # state vectors if (mc.psi_out is not None and config.options.average_states and config.cflag and ntraj != 1): output.states = parfor(_mc_dm_avg, mc.psi_out.T) elif mc.psi_out is not None: output.states = mc.psi_out # expectation values if (mc.expect_out is not None and config.cflag and config.options.average_expect): # averaging if multiple trajectories if isinstance(ntraj, int): output.expect = [np.mean(np.array([mc.expect_out[nt][op] for nt in range(ntraj)], dtype=object), axis=0) for op in range(config.e_num)] elif isinstance(ntraj, (list, np.ndarray)): output.expect = [] for num in ntraj: expt_data = np.mean(mc.expect_out[:num], axis=0) data_list = [] if any([not op.isherm for op in e_ops]): for k in range(len(e_ops)): if e_ops[k].isherm: data_list.append(np.real(expt_data[k])) else: data_list.append(expt_data[k]) else: data_list = [data for data in expt_data] output.expect.append(data_list) else: # no averaging for single trajectory or if average_expect flag # (Options) is off if mc.expect_out is not None: output.expect = mc.expect_out # simulation parameters output.times = config.tlist output.num_expect = config.e_num output.num_collapse = config.c_num output.ntraj = config.ntraj output.col_times = mc.collapse_times_out output.col_which = mc.which_op_out if e_ops_dict: output.expect = {e: output.expect[n] for n, e in enumerate(e_ops_dict.keys())} return output
def _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar): """ Internal function for solving ME. Solve an ODE which solver parameters already setup (r). Calculate the required expectation values or invoke callback function at each time step. """ # # prepare output array # n_tsteps = len(tlist) e_sops_data = [] output = Result() output.solver = "mesolve" output.times = tlist if opt.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 = [] opt.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 and rho0.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 r.successful(): raise Exception("ODE integration error: Try to increase " "the allowed number of substeps by increasing " "the nsteps parameter in the Options class.") if opt.store_states or expt_callback: rho.data = dense2D_to_fastcsr_fmode(vec2mat(r.y), rho.shape[0], rho.shape[1]) if opt.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], r.y, 0) else: output.expect[m][t_idx] = expect_rho_vec(e_sops_data[m], r.y, 1) if t_idx < n_tsteps - 1: r.integrate(r.t + dt[t_idx]) progress_bar.finished() if (not opt.rhs_reuse) and (config.tdname is not None): _cython_build_cleanup(config.tdname) if opt.store_final_state: rho.data = dense2D_to_fastcsr_fmode(vec2mat(r.y), rho.shape[0], rho.shape[1]) output.final_state = Qobj(rho, dims=rho0.dims, isherm=True) return output
def _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar): """ Internal function for solving ME. Solve an ODE which solver parameters already setup (r). Calculate the required expectation values or invoke callback function at each time step. """ # # prepare output array # n_tsteps = len(tlist) e_sops_data = [] output = Result() output.solver = "mesolve" output.times = tlist if opt.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 = [] opt.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 and rho0.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 r.successful(): raise Exception("ODE integration error: Try to increase " "the allowed number of substeps by increasing " "the nsteps parameter in the Options class.") if opt.store_states or expt_callback: rho.data = dense2D_to_fastcsr_fmode(vec2mat(r.y), rho.shape[0], rho.shape[1]) if opt.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], r.y, 0) else: output.expect[m][t_idx] = expect_rho_vec( e_sops_data[m], r.y, 1) if t_idx < n_tsteps - 1: r.integrate(r.t + dt[t_idx]) progress_bar.finished() if (not opt.rhs_reuse) and (config.tdname is not None): _cython_build_cleanup(config.tdname) if opt.store_final_state: rho.data = dense2D_to_fastcsr_fmode(vec2mat(r.y), rho.shape[0], rho.shape[1]) output.final_state = Qobj(rho, dims=rho0.dims, isherm=True) return output
def brmesolve(H, psi0, tlist, a_ops, e_ops=[], spectra_cb=[], c_ops=[], args={}, options=Options()): """ Solve the dynamics for a system using the Bloch-Redfield master equation. .. note:: This solver does not currently support time-dependent Hamiltonians. Parameters ---------- H : :class:`qutip.Qobj` System Hamiltonian. rho0 / psi0: :class:`qutip.Qobj` Initial density matrix or state vector (ket). tlist : *list* / *array* List of times for :math:`t`. a_ops : list of :class:`qutip.qobj` List of system operators that couple to bath degrees of freedom. e_ops : list of :class:`qutip.qobj` / callback function List of operators for which to evaluate expectation values. c_ops : list of :class:`qutip.qobj` List of system collapse operators. args : *dictionary* Placeholder for future implementation, kept for API consistency. options : :class:`qutip.solver.Options` Options for the solver. Returns ------- result: :class:`qutip.solver.Result` An instance of the class :class:`qutip.solver.Result`, which contains either an array of expectation values, for operators given in e_ops, or a list of states for the times specified by `tlist`. """ if not spectra_cb: # default to infinite temperature white noise spectra_cb = [lambda w: 1.0 for _ in a_ops] R, ekets = bloch_redfield_tensor(H, a_ops, spectra_cb, c_ops) output = Result() output.solver = "brmesolve" output.times = tlist results = bloch_redfield_solve(R, ekets, psi0, tlist, e_ops, options) if e_ops: output.expect = results else: output.states = results 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 run(self, rho0, tlist, e_ops=None, ado_init=False, ado_return=False): """ Solve for the time evolution of the system. Parameters ---------- rho0 : Qobj or HierarchyADOsState or numpy.array Initial state (:obj:`~Qobj` density matrix) of the system if ``ado_init`` is ``False``. If ``ado_init`` is ``True``, then ``rho0`` should be an instance of :obj:`~HierarchyADOsState` or a numpy array giving the initial state of all ADOs. Usually the state of the ADOs would be determine from a previous call to ``.run(..., ado_return=True)``. For example, ``result = solver.run(..., ado_return=True)`` could be followed by ``solver.run(result.ado_states[-1], tlist, ado_init=True)``. If a numpy array is passed its shape must be ``(number_of_ados, n, n)`` where ``(n, n)`` is the system shape (i.e. shape of the system density matrix) and the ADOs must be in the same order as in ``.ados.labels``. tlist : list An ordered list of times at which to return the value of the state. e_ops : Qobj / callable / list / dict / None, optional A list or dictionary of operators as `Qobj` and/or callable functions (they can be mixed) or a single operator or callable function. For an operator ``op``, the result will be computed using ``(state * op).tr()`` and the state at each time ``t``. For callable functions, ``f``, the result is computed using ``f(t, ado_state)``. The values are stored in ``expect`` on (see the return section below). ado_init: bool, default False Indicates if initial condition is just the system state, or a numpy array including all ADOs. ado_return: bool, default True Whether to also return as output the full state of all ADOs. Returns ------- :class:`qutip.solver.Result` The results of the simulation run, with the following attributes: * ``times``: the times ``t`` (i.e. the ``tlist``). * ``states``: the system state at each time ``t`` (only available if ``e_ops`` was ``None`` or if the solver option ``store_states`` was set to ``True``). * ``ado_states``: the full ADO state at each time (only available if ``ado_return`` was set to ``True``). Each element is an instance of :class:`HierarchyADOsState`. . The state of a particular ADO may be extracted from ``result.ado_states[i]`` by calling :meth:`.extract`. * ``expect``: the value of each ``e_ops`` at time ``t`` (only available if ``e_ops`` were given). If ``e_ops`` was passed as a dictionary, then ``expect`` will be a dictionary with the same keys as ``e_ops`` and values giving the list of outcomes for the corresponding key. """ e_ops, expected = self._convert_e_ops(e_ops) e_ops_callables = any(not isinstance(op, Qobj) for op in e_ops.values()) n = self._sys_shape rho_shape = (n, n) rho_dims = self._sys_dims hierarchy_shape = (self._n_ados, n, n) output = Result() output.solver = "HEOMSolver" output.times = tlist if e_ops: output.expect = expected if not e_ops or self.options.store_states: output.states = [] if ado_init: if isinstance(rho0, HierarchyADOsState): rho0_he = rho0._ado_state else: rho0_he = rho0 if rho0_he.shape != hierarchy_shape: raise ValueError( f"ADOs passed with ado_init have shape {rho0_he.shape}" f"but the solver hierarchy shape is {hierarchy_shape}") rho0_he = rho0_he.reshape(n**2 * self._n_ados) else: rho0_he = np.zeros([n**2 * self._n_ados], dtype=complex) rho0_he[:n**2] = rho0.full().ravel('F') if ado_return: output.ado_states = [] solver = self._ode solver.set_initial_value(rho0_he, tlist[0]) self.progress_bar.start(len(tlist)) for t_idx, t in enumerate(tlist): self.progress_bar.update(t_idx) if t_idx != 0: solver.integrate(t) if not solver.successful(): raise RuntimeError( "HEOMSolver ODE integration error. Try increasing" " the nsteps given in the HEOMSolver options" " (which increases the allowed substeps in each" " step between times given in tlist).") rho = Qobj( solver.y[:n**2].reshape(rho_shape, order='F'), dims=rho_dims, ) if self.options.store_states: output.states.append(rho) if ado_return or e_ops_callables: ado_state = HierarchyADOsState( rho, self.ados, solver.y.reshape(hierarchy_shape)) if ado_return: output.ado_states.append(ado_state) for e_key, e_op in e_ops.items(): if isinstance(e_op, Qobj): e_result = (rho * e_op).tr() else: e_result = e_op(t, ado_state) output.expect[e_key].append(e_result) self.progress_bar.finished() return output
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 _generic_ode_solve(r, psi0, tlist, e_ops, opt, progress_bar, dims=None): """ Internal function for solving ODEs. """ # # prepare output array # n_tsteps = len(tlist) output = Result() output.solver = "sesolve" output.times = tlist if psi0.isunitary: oper_evo = True oper_n = dims[0][0] norm_dim_factor = np.sqrt(oper_n) else: oper_evo = False norm_dim_factor = 1.0 if opt.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: # fallback on storing states output.states = [] opt.store_states = True 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") def get_curr_state_data(): if oper_evo: return vec2mat(r.y) else: return r.y # # start evolution # progress_bar.start(n_tsteps) dt = np.diff(tlist) for t_idx, t in enumerate(tlist): progress_bar.update(t_idx) if not r.successful(): raise Exception("ODE integration error: Try to increase " "the allowed number of substeps by increasing " "the nsteps parameter in the Options class.") # get the current state / oper data if needed cdata = None if opt.store_states or opt.normalize_output or n_expt_op > 0: cdata = get_curr_state_data() if opt.normalize_output: # cdata *= _get_norm_factor(cdata, oper_evo) cdata *= norm_dim_factor / la_norm(cdata) if oper_evo: r.set_initial_value(cdata.ravel('F'), r.t) else: r.set_initial_value(cdata, r.t) if opt.store_states: output.states.append(Qobj(cdata, dims=dims)) if expt_callback: # use callback method e_ops(t, Qobj(cdata, dims=dims)) for m in range(n_expt_op): output.expect[m][t_idx] = cy_expect_psi(e_ops[m].data, cdata, e_ops[m].isherm) if t_idx < n_tsteps - 1: r.integrate(r.t + dt[t_idx]) progress_bar.finished() if not opt.rhs_reuse and config.tdname is not None: try: os.remove(config.tdname + ".pyx") except: pass if opt.store_final_state: cdata = get_curr_state_data() if opt.normalize_output: cdata *= norm_dim_factor / la_norm(cdata) output.final_state = Qobj(cdata, dims=dims) return output
def ttmsolve(dynmaps, rho0, times, e_ops=[], learningtimes=None, tensors=None, **kwargs): """ Solve time-evolution using the Transfer Tensor Method, based on a set of precomputed dynamical maps. Parameters ---------- dynmaps : list of :class:`qutip.Qobj` List of precomputed dynamical maps (superoperators), or a callback function that returns the superoperator at a given time. rho0 : :class:`qutip.Qobj` Initial density matrix or state vector (ket). times : array_like list of times :math:`t_n` at which to compute :math:`\\rho(t_n)`. Must be uniformily spaced. e_ops : list of :class:`qutip.Qobj` / callback function single operator or list of operators for which to evaluate expectation values. learningtimes : array_like list of times :math:`t_k` for which we have knowledge of the dynamical maps :math:`E(t_k)`. tensors : array_like optional list of precomputed tensors :math:`T_k` kwargs : dictionary Optional keyword arguments. See :class:`qutip.nonmarkov.ttm.TTMSolverOptions`. Returns ------- output: :class:`qutip.solver.Result` An instance of the class :class:`qutip.solver.Result`. """ opt = TTMSolverOptions(dynmaps=dynmaps, times=times, learningtimes=learningtimes, **kwargs) diff = None if isket(rho0): rho0 = ket2dm(rho0) output = Result() e_sops_data = [] if callable(e_ops): n_expt_op = 0 expt_callback = True else: try: tmp = e_ops[:] del tmp n_expt_op = len(e_ops) expt_callback = False if n_expt_op == 0: # fall back on storing states opt.store_states = True for op in e_ops: e_sops_data.append(spre(op).data) if op.isherm and rho0.isherm: output.expect.append(np.zeros(len(times))) else: output.expect.append(np.zeros(len(times), dtype=complex)) except TypeError: raise TypeError("Argument 'e_ops' should be a callable or" + "list-like.") if tensors is None: tensors, diff = _generatetensors(dynmaps, learningtimes, opt=opt) rho0vec = operator_to_vector(rho0) K = len(tensors) states = [rho0vec] for n in range(1, len(times)): states.append(None) for k in range(n): if n-k < K: states[-1] += tensors[n-k]*states[k] for i, r in enumerate(states): if opt.store_states or expt_callback: if r.type == 'operator-ket': states[i] = vector_to_operator(r) else: states[i] = r if expt_callback: # use callback method e_ops(times[i], states[i]) for m in range(n_expt_op): if output.expect[m].dtype == complex: output.expect[m][i] = expect_rho_vec(e_sops_data[m], r, 0) else: output.expect[m][i] = expect_rho_vec(e_sops_data[m], r, 1) output.solver = "ttmsolve" output.times = times output.ttmconvergence = diff if opt.store_states: output.states = states return output
def _ssepdpsolve_generic(sso, options, progress_bar): """ For internal use. See ssepdpsolve. """ 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 = "sepdpsolve" data.times = sso.tlist data.expect = np.zeros((len(sso.e_ops), N_store), dtype=complex) data.ss = np.zeros((len(sso.e_ops), N_store), dtype=complex) data.jump_times = [] data.jump_op_idx = [] # effective hamiltonian for deterministic part Heff = sso.H for c in sso.c_ops: Heff += -0.5j * c.dag() * c progress_bar.start(sso.ntraj) for n in range(sso.ntraj): progress_bar.update(n) psi_t = sso.state0.full().ravel() states_list, jump_times, jump_op_idx = \ _ssepdpsolve_single_trajectory(data, Heff, dt, sso.times, N_store, N_substeps, psi_t, sso.state0.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 / 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(sso.e_ops) ] return data
def _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar): """ Internal function for solving ME. Solve an ODE which solver parameters already setup (r). Calculate the required expectation values or invoke callback function at each time step. """ # # prepare output array # n_tsteps = len(tlist) e_sops_data = [] output = Result() output.solver = "mesolve" output.times = tlist if opt.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 = [] opt.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 and rho0.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 r.successful(): break if opt.store_states or expt_callback: rho.data = vec2mat(r.y) if opt.store_states: output.states.append(Qobj(rho)) 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], r.y, 0) else: output.expect[m][t_idx] = expect_rho_vec(e_sops_data[m], r.y, 1) if t_idx < n_tsteps - 1: r.integrate(r.t + dt[t_idx]) progress_bar.finished() if not opt.rhs_reuse and config.tdname is not None: try: os.remove(config.tdname + ".pyx") except: pass if opt.store_final_state: rho.data = vec2mat(r.y) output.final_state = Qobj(rho) return output
def run(self, rho0, tlist): """ Function to solve for an open quantum system using the HEOM model. Parameters ---------- rho0 : Qobj Initial state (density matrix) of the system. tlist : list Time over which system evolves. Returns ------- results : :class:`qutip.solver.Result` Object storing all results from the simulation. """ start_run = timeit.default_timer() sup_dim = self._sup_dim stats = self.stats r = self._ode if not self._configured: raise RuntimeError("Solver must be configured before it is run") if stats: ss_conf = stats.sections.get('config') if ss_conf is None: raise RuntimeError("No config section for solver stats") ss_run = stats.sections.get('run') if ss_run is None: ss_run = stats.add_section('run') # Set up terms of the matsubara and tanimura boundaries output = Result() output.solver = "hsolve" output.times = tlist output.states = [] if stats: start_init = timeit.default_timer() output.states.append(Qobj(rho0)) rho0_flat = rho0.full().ravel('F') # Using 'F' effectively transposes rho0_he = np.zeros([sup_dim*self._N_he], dtype=complex) rho0_he[:sup_dim] = rho0_flat r.set_initial_value(rho0_he, tlist[0]) if stats: stats.add_timing('initialize', timeit.default_timer() - start_init, ss_run) start_integ = timeit.default_timer() dt = np.diff(tlist) n_tsteps = len(tlist) for t_idx, t in enumerate(tlist): if t_idx < n_tsteps - 1: r.integrate(r.t + dt[t_idx]) rho = Qobj(r.y[:sup_dim].reshape(rho0.shape), dims=rho0.dims) output.states.append(rho) if stats: time_now = timeit.default_timer() stats.add_timing('integrate', time_now - start_integ, ss_run) if ss_run.total_time is None: ss_run.total_time = time_now - start_run else: ss_run.total_time += time_now - start_run stats.total_time = ss_conf.total_time + ss_run.total_time return output
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 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
def brmesolve(H, psi0, tlist, a_ops=[], e_ops=[], c_ops=[], args={}, use_secular=True, tol=qset.atol, spectra_cb=None, options=None, progress_bar=None, _safe_mode=True): """ Solves for the dynamics of a system using the Bloch-Redfield master equation, given an input Hamiltonian, Hermitian bath-coupling terms and their associated spectrum functions, as well as possible Lindblad collapse operators. For time-independent systems, the Hamiltonian must be given as a Qobj, whereas the bath-coupling terms (a_ops), must be written as a nested list of operator - spectrum function pairs, where the frequency is specified by the `w` variable. *Example* a_ops = [[a+a.dag(),lambda w: 0.2*(w>=0)]] For time-dependent systems, the Hamiltonian, a_ops, and Lindblad collapse operators (c_ops), can be specified in the QuTiP string-based time-dependent format. For the a_op spectra, the frequency variable must be `w`, and the string cannot contain any other variables other than the possibility of having a time-dependence through the time variable `t`: *Example* a_ops = [[a+a.dag(), '0.2*exp(-t)*(w>=0)']] Parameters ---------- H : Qobj / list System Hamiltonian given as a Qobj or nested list in string-based format. psi0: Qobj Initial density matrix or state vector (ket). tlist : array_like List of times for evaluating evolution a_ops : list Nested list of Hermitian system operators that couple to the bath degrees of freedom, along with their associated spectra. e_ops : list List of operators for which to evaluate expectation values. c_ops : list List of system collapse operators, or nested list in string-based format. args : dict (not implimented) Placeholder for future implementation, kept for API consistency. use_secular : bool {True} Use secular approximation when evaluating bath-coupling terms. tol : float {qutip.setttings.atol} Tolerance used for removing small values after basis transformation. spectra_cb : list DEPRECIATED. Do not use. options : :class:`qutip.solver.Options` Options for the solver. progress_bar : BaseProgressBar Optional instance of BaseProgressBar, or a subclass thereof, for showing the progress of the simulation. Returns ------- result: :class:`qutip.solver.Result` An instance of the class :class:`qutip.solver.Result`, which contains either an array of expectation values, for operators given in e_ops, or a list of states for the times specified by `tlist`. """ if isinstance(c_ops, Qobj): c_ops = [c_ops] if isinstance(e_ops, Qobj): e_ops = [e_ops] if isinstance(e_ops, dict): e_ops_dict = e_ops e_ops = [e for e in e_ops.values()] else: e_ops_dict = None if not (spectra_cb is None): warnings.warn("The use of spectra_cb is depreciated.", DeprecationWarning) _a_ops = [] for kk, a in enumerate(a_ops): _a_ops.append([a, spectra_cb[kk]]) a_ops = _a_ops if _safe_mode: _solver_safety_check(H, psi0, a_ops + c_ops, e_ops, args) # check for type (if any) of time-dependent inputs _, n_func, n_str = _td_format_check(H, a_ops + c_ops) if progress_bar is None: progress_bar = BaseProgressBar() elif progress_bar is True: progress_bar = TextProgressBar() if options is None: options = Options() if (not options.rhs_reuse) or (not config.tdfunc): # reset config collapse and time-dependence flags to default values config.reset() #check if should use OPENMP check_use_openmp(options) if n_str == 0: R, ekets = bloch_redfield_tensor(H, a_ops, spectra_cb=None, c_ops=c_ops) output = Result() output.solver = "brmesolve" output.times = tlist results = bloch_redfield_solve(R, ekets, psi0, tlist, e_ops, options, progress_bar=progress_bar) if e_ops: output.expect = results else: output.states = results return output elif n_str != 0 and n_func == 0: output = _td_brmesolve(H, psi0, tlist, a_ops=a_ops, e_ops=e_ops, c_ops=c_ops, use_secular=use_secular, tol=tol, options=options, progress_bar=progress_bar, _safe_mode=_safe_mode) return output else: raise Exception('Cannot mix func and str formats.')
def _generic_ode_solve(r, psi0, tlist, e_ops, opt, progress_bar, dims=None): """ Internal function for solving ODEs. """ if opt.normalize_output: state_norm_func = norm else: state_norm_func = None # # prepare output array # n_tsteps = len(tlist) output = Result() output.solver = "sesolve" output.times = tlist if opt.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: # fallback on storing states output.states = [] opt.store_states = True 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") # # start evolution # progress_bar.start(n_tsteps) dt = np.diff(tlist) for t_idx, t in enumerate(tlist): progress_bar.update(t_idx) if not r.successful(): raise Exception("ODE integration error: Try to increase " "the allowed number of substeps by increasing " "the nsteps parameter in the Options class.") if state_norm_func: data = r.y / state_norm_func(r.y) r.set_initial_value(data, r.t) if opt.store_states: output.states.append(Qobj(r.y, dims=dims)) if expt_callback: # use callback method e_ops(t, Qobj(r.y, dims=psi0.dims)) for m in range(n_expt_op): output.expect[m][t_idx] = cy_expect_psi(e_ops[m].data, r.y, e_ops[m].isherm) if t_idx < n_tsteps - 1: r.integrate(r.t + dt[t_idx]) progress_bar.finished() if not opt.rhs_reuse and config.tdname is not None: try: os.remove(config.tdname + ".pyx") except: pass if opt.store_final_state: output.final_state = Qobj(r.y, dims=dims) return output
def run(self, rho0, tlist): """ Function to solve for an open quantum system using the HEOM model. Parameters ---------- rho0 : Qobj Initial state (density matrix) of the system. tlist : list Time over which system evolves. Returns ------- results : :class:`qutip.solver.Result` Object storing all results from the simulation. """ start_run = timeit.default_timer() sup_dim = self._sup_dim stats = self.stats r = self._ode if not self._configured: raise RuntimeError("Solver must be configured before it is run") if stats: ss_conf = stats.sections.get('config') if ss_conf is None: raise RuntimeError("No config section for solver stats") ss_run = stats.sections.get('run') if ss_run is None: ss_run = stats.add_section('run') # Set up terms of the matsubara and tanimura boundaries output = Result() output.solver = "hsolve" output.times = tlist output.states = [] if stats: start_init = timeit.default_timer() output.states.append(Qobj(rho0)) rho0_flat = rho0.full().ravel('F') # Using 'F' effectively transposes rho0_he = np.zeros([sup_dim * self._N_he], dtype=complex) rho0_he[:sup_dim] = rho0_flat r.set_initial_value(rho0_he, tlist[0]) if stats: stats.add_timing('initialize', timeit.default_timer() - start_init, ss_run) start_integ = timeit.default_timer() dt = np.diff(tlist) n_tsteps = len(tlist) for t_idx, t in enumerate(tlist): if t_idx < n_tsteps - 1: r.integrate(r.t + dt[t_idx]) rho = Qobj(r.y[:sup_dim].reshape(rho0.shape), dims=rho0.dims) output.states.append(rho) if stats: time_now = timeit.default_timer() stats.add_timing('integrate', time_now - start_integ, ss_run) if ss_run.total_time is None: ss_run.total_time = time_now - start_run else: ss_run.total_time += time_now - start_run stats.total_time = ss_conf.total_time + ss_run.total_time return output
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 propagate(self, tlist, *, propagator, rho0=None, e_ops=None): """Propagates the system of the objective over the entire time grid Solve the dynamics for the `H` and `c_ops` of the objective. If `rho0` is not given, the `initial_state` will be propagated. This is similar to the :meth:`mesolve` method, but instead of using :func:`qutip.mesolve.mesolve`, the `propagate` function is used to go between points on the time grid. This function is the same as what is passed to :func:`.optimize_pulses`. The crucial difference between this and :meth:`mesolve` is in the time discretization convention. While :meth:`mesolve` uses piecewise-constant controls centered around the values in `tlist` (the control field switches in the middle between two points in `tlist`), :meth:`propagate` uses piecewise-constant controls on the intervals of `tlist` (the control field switches on the points in `tlist`) Comparing the result of :meth:`mesolve` and :meth:`propagate` allows to estimate the "time discretization error". If the error is significant, a shorter time step shoud be used. Returns: qutip.solver.Result: Result of the propagation, using the same structure as :meth:`mesolve`. """ if e_ops is None: e_ops = [] result = QutipSolverResult() result.solver = propagator.__name__ result.times = copy.copy(tlist) result.states = [] result.expect = [] result.num_expect = len(e_ops) result.num_collapse = len(self.c_ops) for _ in e_ops: result.expect.append([]) state = rho0 if state is None: state = self.initial_state if len(e_ops) == 0: result.states.append(state) else: for (i, oper) in enumerate(e_ops): result.expect[i].append(qutip.expect(oper, state)) controls = extract_controls([self]) pulses_mapping = extract_controls_mapping([self], controls) mapping = pulses_mapping[0] # "first objective" (dummy structure) pulses = [ # defined on the tlist intervals control_onto_interval(discretize(control, tlist)) for control in controls ] for time_index in range(len(tlist) - 1): # index over intervals H = plug_in_pulse_values(self.H, pulses, mapping[0], time_index) c_ops = [ plug_in_pulse_values(c_op, pulses, mapping[ic + 1], time_index) for (ic, c_op) in enumerate(self.c_ops) ] dt = tlist[time_index + 1] - tlist[time_index] state = propagator(H, state, dt, c_ops) if len(e_ops) == 0: result.states.append(state) else: for (i, oper) in enumerate(e_ops): result.expect[i].append(qutip.expect(oper, state)) return result
def _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar): """ Internal function for solving ME. Solve an ODE which solver parameters already setup (r). Calculate the required expectation values or invoke callback function at each time step. """ # # prepare output array # n_tsteps = len(tlist) e_sops_data = [] output = Result() output.solver = "mesolve" output.times = tlist if opt.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 = [] opt.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 and rho0.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 r.successful(): break if opt.store_states or expt_callback: rho.data = vec2mat(r.y) if opt.store_states: output.states.append(Qobj(rho)) 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], r.y, 0) else: output.expect[m][t_idx] = expect_rho_vec( e_sops_data[m], r.y, 1) if t_idx < n_tsteps - 1: r.integrate(r.t + dt[t_idx]) progress_bar.finished() if not opt.rhs_reuse and config.tdname is not None: try: os.remove(config.tdname + ".pyx") except: pass if opt.store_final_state: rho.data = vec2mat(r.y) output.final_state = Qobj(rho) return output