def mesolve(H, rho0, tlist, c_ops=None, e_ops=None, args=None, options=None, progress_bar=None, _safe_mode=True): """ Master equation evolution of a density matrix for a given Hamiltonian and set of collapse operators, or a Liouvillian. Evolve the state vector or density matrix (`rho0`) using a given Hamiltonian or Liouvillian (`H`) and an optional set of collapse operators (`c_ops`), by integrating the set of ordinary differential equations that define the system. In the absence of collapse operators the system is evolved according to the unitary evolution of the Hamiltonian. The output is either the state vector at arbitrary points in time (`tlist`), or the expectation values of the supplied operators (`e_ops`). If e_ops is a callback function, it is invoked for each time in `tlist` with time and the state as arguments, and the function does not use any return values. If either `H` or the Qobj elements in `c_ops` are superoperators, they will be treated as direct contributions to the total system Liouvillian. This allows the solution of master equations that are not in standard Lindblad form. **Time-dependent operators** For time-dependent problems, `H` and `c_ops` can be a specified in a nested-list format where each element in the list is a list of length 2, containing an operator (:class:`qutip.qobj`) at the first element and where the second element is either a string (*list string format*), a callback function (*list callback format*) that evaluates to the time-dependent coefficient for the corresponding operator, or a NumPy array (*list array format*) which specifies the value of the coefficient to the corresponding operator for each value of t in `tlist`. Alternatively, `H` (but not `c_ops`) can be a callback function with the signature `f(t, args) -> Qobj` (*callback format*), which can return the Hamiltonian or Liouvillian superoperator at any point in time. If the equation cannot be put in standard Lindblad form, then this time-dependence format must be used. *Examples* H = [[H0, 'sin(w*t)'], [H1, 'sin(2*w*t)']] H = [[H0, f0_t], [H1, f1_t]] where f0_t and f1_t are python functions with signature f_t(t, args). H = [[H0, np.sin(w*tlist)], [H1, np.sin(2*w*tlist)]] In the *list string format* and *list callback format*, the string expression and the callback function must evaluate to a real or complex number (coefficient for the corresponding operator). In all cases of time-dependent operators, `args` is a dictionary of parameters that is used when evaluating operators. It is passed to the callback functions as their second argument. **Additional options** Additional options to mesolve can be set via the `options` argument, which should be an instance of :class:`qutip.solver.Options`. Many ODE integration options can be set this way, and the `store_states` and `store_final_state` options can be used to store states even though expectation values are requested via the `e_ops` argument. .. note:: If an element in the list-specification of the Hamiltonian or the list of collapse operators are in superoperator form it will be added to the total Liouvillian of the problem without further transformation. This allows for using mesolve for solving master equations that are not in standard Lindblad form. .. note:: On using callback functions: mesolve transforms all :class:`qutip.Qobj` objects to sparse matrices before handing the problem to the integrator function. In order for your callback function to work correctly, pass all :class:`qutip.Qobj` objects that are used in constructing the Hamiltonian via `args`. mesolve will check for :class:`qutip.Qobj` in `args` and handle the conversion to sparse matrices. All other :class:`qutip.Qobj` objects that are not passed via `args` will be passed on to the integrator in scipy which will raise a NotImplemented exception. Parameters ---------- H : :class:`qutip.Qobj` System Hamiltonian, or a callback function for time-dependent Hamiltonians, or alternatively a system Liouvillian. rho0 : :class:`qutip.Qobj` initial density matrix or state vector (ket). tlist : *list* / *array* list of times for :math:`t`. c_ops : None / list of :class:`qutip.Qobj` single collapse operator, or list of collapse operators, or a list of Liouvillian superoperators. e_ops : None / list / callback function, optional A list of operators as `Qobj` and/or callable functions (can be mixed) or a single callable function. For operators, the result's expect will be computed by :func:`qutip.expect`. For callable functions, they are called as ``f(t, state)`` and return the expectation value. A single callback's expectation value can be any type, but a callback part of a list must return a number as the expectation value. args : None / *dictionary* dictionary of parameters for time-dependent Hamiltonians and collapse operators. options : None / :class:`qutip.Options` with options for the solver. progress_bar : None / BaseProgressBar Optional instance of BaseProgressBar, or a subclass thereof, for showing the progress of the simulation. Returns ------- result: :class:`qutip.Result` An instance of the class :class:`qutip.Result`, which contains either an *array* `result.expect` of expectation values for the times specified by `tlist`, or an *array* `result.states` of state vectors or density matrices corresponding to the times in `tlist` [if `e_ops` is an empty list], or nothing if a callback function was given in place of operators for which to calculate the expectation values. """ if c_ops is None: c_ops = [] if isinstance(c_ops, (Qobj, QobjEvo)): c_ops = [c_ops] if e_ops is None: e_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 progress_bar is None: progress_bar = BaseProgressBar() if progress_bar is True: progress_bar = TextProgressBar() # check if rho0 is a superoperator, in which case e_ops argument should # be empty, i.e., e_ops = [] # TODO: e_ops for superoperator if issuper(rho0) and not e_ops == []: raise TypeError("Must have e_ops = [] when initial condition rho0 is" + " a superoperator.") if options is None: options = Options() if options.rhs_reuse and not isinstance(H, SolverSystem): # TODO: deprecate when going to class based solver. if "mesolve" in solver_safe: # print(" ") H = solver_safe["mesolve"] else: pass # raise Exception("Could not find the Hamiltonian to reuse.") if args is None: args = {} check_use_openmp(options) use_mesolve = ((c_ops and len(c_ops) > 0) or (not isket(rho0)) or (isinstance(H, Qobj) and issuper(H)) or (isinstance(H, QobjEvo) and issuper(H.cte)) or (isinstance(H, list) and isinstance(H[0], Qobj) and issuper(H[0])) or (not isinstance(H, (Qobj, QobjEvo)) and callable(H) and not options.rhs_with_state and issuper(H(0., args))) or (not isinstance(H, (Qobj, QobjEvo)) and callable(H) and options.rhs_with_state)) if not use_mesolve: return sesolve(H, rho0, tlist, e_ops=e_ops, args=args, options=options, progress_bar=progress_bar, _safe_mode=_safe_mode) if isket(rho0): rho0 = ket2dm(rho0) if (not (rho0.isoper or rho0.issuper)) or (rho0.dims[0] != rho0.dims[1]): raise ValueError( "input state must be a pure state vector, square density matrix, " "or superoperator") if isinstance(H, SolverSystem): ss = H elif isinstance(H, (list, Qobj, QobjEvo)): ss = _mesolve_QobjEvo(H, c_ops, tlist, args, options) elif callable(H): ss = _mesolve_func_td(H, c_ops, rho0, tlist, args, options) else: raise Exception("Invalid H type") func, ode_args = ss.makefunc(ss, rho0, args, e_ops, options) if _safe_mode: # This is to test safety of the function before starting the loop. v = rho0.full().ravel('F') func(0., v, *ode_args) + v res = _generic_ode_solve(func, ode_args, rho0, tlist, e_ops, options, progress_bar, dims=rho0.dims) res.num_collapse = len(c_ops) if e_ops_dict: res.expect = { e: res.expect[n] for n, e in enumerate(e_ops_dict.keys()) } return res
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 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 sesolve(H, rho0, tlist, e_ops=[], args={}, options=None, progress_bar=BaseProgressBar(), _safe_mode=True): """ Schrodinger equation evolution of a state vector for a given Hamiltonian. Evolve the state vector or density matrix (`rho0`) using a given Hamiltonian (`H`), by integrating the set of ordinary differential equations that define the system. The output is either the state vector at arbitrary points in time (`tlist`), or the expectation values of the supplied operators (`e_ops`). If e_ops is a callback function, it is invoked for each time in `tlist` with time and the state as arguments, and the function does not use any return values. Parameters ---------- H : :class:`qutip.qobj` system Hamiltonian, or a callback function for time-dependent Hamiltonians. rho0 : :class:`qutip.qobj` initial density matrix or state vector (ket). tlist : *list* / *array* list of times for :math:`t`. e_ops : list of :class:`qutip.qobj` / callback function single single operator or list of operators for which to evaluate expectation values. args : *dictionary* dictionary of parameters for time-dependent Hamiltonians and collapse operators. options : :class:`qutip.Qdeoptions` with options for the ODE solver. Returns ------- output: :class:`qutip.solver` An instance of the class :class:`qutip.solver`, which contains either an *array* of expectation values for the times specified by `tlist`, or an *array* or state vectors or density matrices corresponding to the times in `tlist` [if `e_ops` is an empty list], or nothing if a callback function was given inplace of operators for which to calculate the expectation values. """ 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 _safe_mode: _solver_safety_check(H, rho0, c_ops=[], e_ops=e_ops, args=args) # convert array based time-dependence to string format H, _, args = _td_wrap_array_str(H, [], args, tlist) # check for type (if any) of time-dependent inputs n_const, n_func, n_str = _td_format_check(H, []) if options is None: options = Options() if (not options.rhs_reuse) or (not config.tdfunc): # reset config time-dependence flags to default values config.reset() #check if should use OPENMP check_use_openmp(options) if n_func > 0: res = _sesolve_list_func_td(H, rho0, tlist, e_ops, args, options, progress_bar) elif n_str > 0: res = _sesolve_list_str_td(H, rho0, tlist, e_ops, args, options, progress_bar) elif isinstance(H, (types.FunctionType, types.BuiltinFunctionType, partial)): res = _sesolve_func_td(H, rho0, tlist, e_ops, args, options, progress_bar) else: res = _sesolve_const(H, rho0, tlist, e_ops, args, options, progress_bar) if e_ops_dict: res.expect = { e: res.expect[n] for n, e in enumerate(e_ops_dict.keys()) } return res
def mesolve(H, rho0, tlist, c_ops=[], e_ops=[], args={}, options=None, progress_bar=None, _safe_mode=True): """ Master equation evolution of a density matrix for a given Hamiltonian and set of collapse operators, or a Liouvillian. Evolve the state vector or density matrix (`rho0`) using a given Hamiltonian (`H`) and an [optional] set of collapse operators (`c_ops`), by integrating the set of ordinary differential equations that define the system. In the absence of collapse operators the system is evolved according to the unitary evolution of the Hamiltonian. The output is either the state vector at arbitrary points in time (`tlist`), or the expectation values of the supplied operators (`e_ops`). If e_ops is a callback function, it is invoked for each time in `tlist` with time and the state as arguments, and the function does not use any return values. If either `H` or the Qobj elements in `c_ops` are superoperators, they will be treated as direct contributions to the total system Liouvillian. This allows to solve master equations that are not on standard Lindblad form by passing a custom Liouvillian in place of either the `H` or `c_ops` elements. **Time-dependent operators** For time-dependent problems, `H` and `c_ops` can be callback functions that takes two arguments, time and `args`, and returns the Hamiltonian or Liouvillian for the system at that point in time (*callback format*). Alternatively, `H` and `c_ops` can be a specified in a nested-list format where each element in the list is a list of length 2, containing an operator (:class:`qutip.qobj`) at the first element and where the second element is either a string (*list string format*), a callback function (*list callback format*) that evaluates to the time-dependent coefficient for the corresponding operator, or a NumPy array (*list array format*) which specifies the value of the coefficient to the corresponding operator for each value of t in tlist. *Examples* H = [[H0, 'sin(w*t)'], [H1, 'sin(2*w*t)']] H = [[H0, f0_t], [H1, f1_t]] where f0_t and f1_t are python functions with signature f_t(t, args). H = [[H0, np.sin(w*tlist)], [H1, np.sin(2*w*tlist)]] In the *list string format* and *list callback format*, the string expression and the callback function must evaluate to a real or complex number (coefficient for the corresponding operator). In all cases of time-dependent operators, `args` is a dictionary of parameters that is used when evaluating operators. It is passed to the callback functions as second argument. **Additional options** Additional options to mesolve can be set via the `options` argument, which should be an instance of :class:`qutip.solver.Options`. Many ODE integration options can be set this way, and the `store_states` and `store_final_state` options can be used to store states even though expectation values are requested via the `e_ops` argument. .. note:: If an element in the list-specification of the Hamiltonian or the list of collapse operators are in superoperator form it will be added to the total Liouvillian of the problem with out further transformation. This allows for using mesolve for solving master equations that are not on standard Lindblad form. .. note:: On using callback function: mesolve transforms all :class:`qutip.qobj` objects to sparse matrices before handing the problem to the integrator function. In order for your callback function to work correctly, pass all :class:`qutip.qobj` objects that are used in constructing the Hamiltonian via args. mesolve will check for :class:`qutip.qobj` in `args` and handle the conversion to sparse matrices. All other :class:`qutip.qobj` objects that are not passed via `args` will be passed on to the integrator in scipy which will raise an NotImplemented exception. Parameters ---------- H : :class:`qutip.Qobj` System Hamiltonian, or a callback function for time-dependent Hamiltonians, or alternatively a system Liouvillian. rho0 : :class:`qutip.Qobj` initial density matrix or state vector (ket). tlist : *list* / *array* list of times for :math:`t`. c_ops : list of :class:`qutip.Qobj` single collapse operator, or list of collapse operators, or a list of Liouvillian superoperators. e_ops : list of :class:`qutip.Qobj` / callback function single single operator or list of operators for which to evaluate expectation values. args : *dictionary* dictionary of parameters for time-dependent Hamiltonians and collapse operators. options : :class:`qutip.Options` with 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.Result` An instance of the class :class:`qutip.Result`, which contains either an *array* `result.expect` of expectation values for the times specified by `tlist`, or an *array* `result.states` of state vectors or density matrices corresponding to the times in `tlist` [if `e_ops` is an empty list], or nothing if a callback function was given in place of operators for which to calculate the expectation values. """ # check whether c_ops or e_ops is is a single operator # if so convert it to a list containing only that operator 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 _safe_mode: _solver_safety_check(H, rho0, c_ops, e_ops, args) if progress_bar is None: progress_bar = BaseProgressBar() elif progress_bar is True: progress_bar = TextProgressBar() # check if rho0 is a superoperator, in which case e_ops argument should # be empty, i.e., e_ops = [] if issuper(rho0) and not e_ops == []: raise TypeError("Must have e_ops = [] when initial condition rho0 is" + " a superoperator.") # convert array based time-dependence to string format H, c_ops, args = _td_wrap_array_str(H, c_ops, args, tlist) # check for type (if any) of time-dependent inputs _, n_func, n_str = _td_format_check(H, c_ops) 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) res = None # # dispatch the appropriate solver # if ((c_ops and len(c_ops) > 0) or (not isket(rho0)) or (isinstance(H, Qobj) and issuper(H)) or (isinstance(H, list) and isinstance(H[0], Qobj) and issuper(H[0]))): # # we have collapse operators, or rho0 is not a ket, # or H is a Liouvillian # # # find out if we are dealing with all-constant hamiltonian and # collapse operators or if we have at least one time-dependent # operator. Then delegate to appropriate solver... # if isinstance(H, Qobj): # constant hamiltonian if n_func == 0 and n_str == 0: # constant collapse operators res = _mesolve_const(H, rho0, tlist, c_ops, e_ops, args, options, progress_bar) elif n_str > 0: # constant hamiltonian but time-dependent collapse # operators in list string format res = _mesolve_list_str_td([H], rho0, tlist, c_ops, e_ops, args, options, progress_bar) elif n_func > 0: # constant hamiltonian but time-dependent collapse # operators in list function format res = _mesolve_list_func_td([H], rho0, tlist, c_ops, e_ops, args, options, progress_bar) elif isinstance( H, (types.FunctionType, types.BuiltinFunctionType, partial)): # function-callback style time-dependence: must have constant # collapse operators if n_str > 0: # or n_func > 0: raise TypeError("Incorrect format: function-format " + "Hamiltonian cannot be mixed with " + "time-dependent collapse operators.") else: res = _mesolve_func_td(H, rho0, tlist, c_ops, e_ops, args, options, progress_bar) elif isinstance(H, list): # determine if we are dealing with list of [Qobj, string] or # [Qobj, function] style time-dependencies (for pure python and # cython, respectively) if n_func > 0: res = _mesolve_list_func_td(H, rho0, tlist, c_ops, e_ops, args, options, progress_bar) else: res = _mesolve_list_str_td(H, rho0, tlist, c_ops, e_ops, args, options, progress_bar) else: raise TypeError("Incorrect specification of Hamiltonian " + "or collapse operators.") else: # # no collapse operators: unitary dynamics # if n_func > 0: res = _sesolve_list_func_td(H, rho0, tlist, e_ops, args, options, progress_bar) elif n_str > 0: res = _sesolve_list_str_td(H, rho0, tlist, e_ops, args, options, progress_bar) elif isinstance( H, (types.FunctionType, types.BuiltinFunctionType, partial)): res = _sesolve_func_td(H, rho0, tlist, e_ops, args, options, progress_bar) else: res = _sesolve_const(H, rho0, tlist, e_ops, args, options, progress_bar) if e_ops_dict: res.expect = { e: res.expect[n] for n, e in enumerate(e_ops_dict.keys()) } return res
def mesolve(H, rho0, tlist, c_ops=[], e_ops=[], args={}, options=None, progress_bar=None, _safe_mode=True): """ Master equation evolution of a density matrix for a given Hamiltonian and set of collapse operators, or a Liouvillian. Evolve the state vector or density matrix (`rho0`) using a given Hamiltonian (`H`) and an [optional] set of collapse operators (`c_ops`), by integrating the set of ordinary differential equations that define the system. In the absence of collapse operators the system is evolved according to the unitary evolution of the Hamiltonian. The output is either the state vector at arbitrary points in time (`tlist`), or the expectation values of the supplied operators (`e_ops`). If e_ops is a callback function, it is invoked for each time in `tlist` with time and the state as arguments, and the function does not use any return values. If either `H` or the Qobj elements in `c_ops` are superoperators, they will be treated as direct contributions to the total system Liouvillian. This allows to solve master equations that are not on standard Lindblad form by passing a custom Liouvillian in place of either the `H` or `c_ops` elements. **Time-dependent operators** For time-dependent problems, `H` and `c_ops` can be callback functions that takes two arguments, time and `args`, and returns the Hamiltonian or Liouvillian for the system at that point in time (*callback format*). Alternatively, `H` and `c_ops` can be a specified in a nested-list format where each element in the list is a list of length 2, containing an operator (:class:`qutip.qobj`) at the first element and where the second element is either a string (*list string format*), a callback function (*list callback format*) that evaluates to the time-dependent coefficient for the corresponding operator, or a NumPy array (*list array format*) which specifies the value of the coefficient to the corresponding operator for each value of t in tlist. *Examples* H = [[H0, 'sin(w*t)'], [H1, 'sin(2*w*t)']] H = [[H0, f0_t], [H1, f1_t]] where f0_t and f1_t are python functions with signature f_t(t, args). H = [[H0, np.sin(w*tlist)], [H1, np.sin(2*w*tlist)]] In the *list string format* and *list callback format*, the string expression and the callback function must evaluate to a real or complex number (coefficient for the corresponding operator). In all cases of time-dependent operators, `args` is a dictionary of parameters that is used when evaluating operators. It is passed to the callback functions as second argument. **Additional options** Additional options to mesolve can be set via the `options` argument, which should be an instance of :class:`qutip.solver.Options`. Many ODE integration options can be set this way, and the `store_states` and `store_final_state` options can be used to store states even though expectation values are requested via the `e_ops` argument. .. note:: If an element in the list-specification of the Hamiltonian or the list of collapse operators are in superoperator form it will be added to the total Liouvillian of the problem with out further transformation. This allows for using mesolve for solving master equations that are not on standard Lindblad form. .. note:: On using callback function: mesolve transforms all :class:`qutip.qobj` objects to sparse matrices before handing the problem to the integrator function. In order for your callback function to work correctly, pass all :class:`qutip.qobj` objects that are used in constructing the Hamiltonian via args. mesolve will check for :class:`qutip.qobj` in `args` and handle the conversion to sparse matrices. All other :class:`qutip.qobj` objects that are not passed via `args` will be passed on to the integrator in scipy which will raise an NotImplemented exception. Parameters ---------- H : :class:`qutip.Qobj` System Hamiltonian, or a callback function for time-dependent Hamiltonians, or alternatively a system Liouvillian. rho0 : :class:`qutip.Qobj` initial density matrix or state vector (ket). tlist : *list* / *array* list of times for :math:`t`. c_ops : list of :class:`qutip.Qobj` single collapse operator, or list of collapse operators, or a list of Liouvillian superoperators. e_ops : list of :class:`qutip.Qobj` / callback function single single operator or list of operators for which to evaluate expectation values. args : *dictionary* dictionary of parameters for time-dependent Hamiltonians and collapse operators. options : :class:`qutip.Options` with 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.Result` An instance of the class :class:`qutip.Result`, which contains either an *array* `result.expect` of expectation values for the times specified by `tlist`, or an *array* `result.states` of state vectors or density matrices corresponding to the times in `tlist` [if `e_ops` is an empty list], or nothing if a callback function was given in place of operators for which to calculate the expectation values. """ # check whether c_ops or e_ops is is a single operator # if so convert it to a list containing only that operator 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 _safe_mode: _solver_safety_check(H, rho0, c_ops, e_ops, args) if progress_bar is None: progress_bar = BaseProgressBar() elif progress_bar is True: progress_bar = TextProgressBar() # check if rho0 is a superoperator, in which case e_ops argument should # be empty, i.e., e_ops = [] if issuper(rho0) and not e_ops == []: raise TypeError("Must have e_ops = [] when initial condition rho0 is" + " a superoperator.") # convert array based time-dependence to string format H, c_ops, args = _td_wrap_array_str(H, c_ops, args, tlist) # check for type (if any) of time-dependent inputs _, n_func, n_str = _td_format_check(H, c_ops) 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) res = None # # dispatch the appropriate solver # if ((c_ops and len(c_ops) > 0) or (not isket(rho0)) or (isinstance(H, Qobj) and issuper(H)) or (isinstance(H, list) and isinstance(H[0], Qobj) and issuper(H[0]))): # # we have collapse operators, or rho0 is not a ket, # or H is a Liouvillian # # # find out if we are dealing with all-constant hamiltonian and # collapse operators or if we have at least one time-dependent # operator. Then delegate to appropriate solver... # if isinstance(H, Qobj): # constant hamiltonian if n_func == 0 and n_str == 0: # constant collapse operators res = _mesolve_const(H, rho0, tlist, c_ops, e_ops, args, options, progress_bar) elif n_str > 0: # constant hamiltonian but time-dependent collapse # operators in list string format res = _mesolve_list_str_td([H], rho0, tlist, c_ops, e_ops, args, options, progress_bar) elif n_func > 0: # constant hamiltonian but time-dependent collapse # operators in list function format res = _mesolve_list_func_td([H], rho0, tlist, c_ops, e_ops, args, options, progress_bar) elif isinstance(H, (types.FunctionType, types.BuiltinFunctionType, partial)): # function-callback style time-dependence: must have constant # collapse operators if n_str > 0: # or n_func > 0: raise TypeError("Incorrect format: function-format " + "Hamiltonian cannot be mixed with " + "time-dependent collapse operators.") else: res = _mesolve_func_td(H, rho0, tlist, c_ops, e_ops, args, options, progress_bar) elif isinstance(H, list): # determine if we are dealing with list of [Qobj, string] or # [Qobj, function] style time-dependencies (for pure python and # cython, respectively) if n_func > 0: res = _mesolve_list_func_td(H, rho0, tlist, c_ops, e_ops, args, options, progress_bar) else: res = _mesolve_list_str_td(H, rho0, tlist, c_ops, e_ops, args, options, progress_bar) else: raise TypeError("Incorrect specification of Hamiltonian " + "or collapse operators.") else: # # no collapse operators: unitary dynamics # if n_func > 0: res = _sesolve_list_func_td(H, rho0, tlist, e_ops, args, options, progress_bar) elif n_str > 0: res = _sesolve_list_str_td(H, rho0, tlist, e_ops, args, options, progress_bar) elif isinstance(H, (types.FunctionType, types.BuiltinFunctionType, partial)): res = _sesolve_func_td(H, rho0, tlist, e_ops, args, options, progress_bar) else: res = _sesolve_const(H, rho0, tlist, e_ops, args, options, progress_bar) if e_ops_dict: res.expect = {e: res.expect[n] for n, e in enumerate(e_ops_dict.keys())} return res
def sesolve(H, psi0, tlist, e_ops=[], args={}, options=None, progress_bar=None, _safe_mode=True): """ Schrodinger equation evolution of a state vector or unitary matrix for a given Hamiltonian. Evolve the state vector (`psi0`) using a given Hamiltonian (`H`), by integrating the set of ordinary differential equations that define the system. Alternatively evolve a unitary matrix in solving the Schrodinger operator equation. The output is either the state vector or unitary matrix at arbitrary points in time (`tlist`), or the expectation values of the supplied operators (`e_ops`). If e_ops is a callback function, it is invoked for each time in `tlist` with time and the state as arguments, and the function does not use any return values. e_ops cannot be used in conjunction with solving the Schrodinger operator equation Parameters ---------- H : :class:`qutip.qobj` system Hamiltonian, or a callback function for time-dependent Hamiltonians. psi0 : :class:`qutip.qobj` initial state vector (ket) or initial unitary operator `psi0 = U` tlist : *list* / *array* list of times for :math:`t`. e_ops : list of :class:`qutip.qobj` / callback function single single operator or list of operators for which to evaluate expectation values. Must be empty list operator evolution args : *dictionary* dictionary of parameters for time-dependent Hamiltonians options : :class:`qutip.Qdeoptions` with options for the ODE solver. progress_bar : BaseProgressBar Optional instance of BaseProgressBar, or a subclass thereof, for showing the progress of the simulation. Returns ------- output: :class:`qutip.solver` An instance of the class :class:`qutip.solver`, which contains either an *array* of expectation values for the times specified by `tlist`, or an *array* or state vectors corresponding to the times in `tlist` [if `e_ops` is an empty list], or nothing if a callback function was given inplace of operators for which to calculate the expectation values. """ # check initial state: must be a state vector if _safe_mode: if not isinstance(psi0, Qobj): raise TypeError("psi0 must be Qobj") if psi0.isket: pass elif psi0.isunitary: if not e_ops == []: raise TypeError("Must have e_ops = [] when initial condition" " psi0 is a unitary operator.") else: raise TypeError("The unitary solver requires psi0 to be" " a ket as initial state" " or a unitary as initial operator.") _solver_safety_check(H, psi0, c_ops=[], e_ops=e_ops, args=args) 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 progress_bar is None: progress_bar = BaseProgressBar() elif progress_bar is True: progress_bar = TextProgressBar() # convert array based time-dependence to string format H, _, args = _td_wrap_array_str(H, [], args, tlist) # check for type (if any) of time-dependent inputs n_const, n_func, n_str = _td_format_check(H, []) if options is None: options = Options() if (not options.rhs_reuse) or (not config.tdfunc): # reset config time-dependence flags to default values config.reset() #check if should use OPENMP check_use_openmp(options) if n_func > 0: res = _sesolve_list_func_td(H, psi0, tlist, e_ops, args, options, progress_bar) elif n_str > 0: res = _sesolve_list_str_td(H, psi0, tlist, e_ops, args, options, progress_bar) elif isinstance(H, (types.FunctionType, types.BuiltinFunctionType, partial)): res = _sesolve_func_td(H, psi0, tlist, e_ops, args, options, progress_bar) elif isinstance(H, Qobj): res = _sesolve_const(H, psi0, tlist, e_ops, args, options, progress_bar) else: raise TypeError("Invalid Hamiltonian specification") if e_ops_dict: res.expect = {e: res.expect[n] for n, e in enumerate(e_ops_dict.keys())} return res
def sesolve(H, psi0, tlist, e_ops=None, args=None, options=None, progress_bar=None, _safe_mode=True): """ Schrödinger equation evolution of a state vector or unitary matrix for a given Hamiltonian. Evolve the state vector (``psi0``) using a given Hamiltonian (``H``), by integrating the set of ordinary differential equations that define the system. Alternatively evolve a unitary matrix in solving the Schrodinger operator equation. The output is either the state vector or unitary matrix at arbitrary points in time (``tlist``), or the expectation values of the supplied operators (``e_ops``). If ``e_ops`` is a callback function, it is invoked for each time in ``tlist`` with time and the state as arguments, and the function does not use any return values. ``e_ops`` cannot be used in conjunction with solving the Schrodinger operator equation Parameters ---------- H : :class:`~Qobj`, :class:`~QobjEvo`, list, or callable System Hamiltonian as a :obj:`~Qobj , list of :obj:`Qobj` and coefficient, :obj:`~QObjEvo`, or a callback function for time-dependent Hamiltonians. List format and options can be found in QobjEvo's description. psi0 : :class:`~Qobj` Initial state vector (ket) or initial unitary operator ``psi0 = U``. tlist : array_like of float List of times for :math:`t`. e_ops : None / list / callback function, optional A list of operators as `Qobj` and/or callable functions (can be mixed) or a single callable function. For callable functions, they are called as ``f(t, state)`` and return the expectation value. A single callback's expectation value can be any type, but a callback part of a list must return a number as the expectation value. For operators, the result's expect will be computed by :func:`qutip.expect` when the state is a ``ket``. For operator evolution, the overlap is computed by: :: (e_ops[i].dag() * op(t)).tr() args : dict, optional Dictionary of scope parameters for time-dependent Hamiltonians. options : :obj:`~solver.Options`, optional Options for the ODE solver. progress_bar : :obj:`~BaseProgressBar`, optional Optional instance of :obj:`~BaseProgressBar`, or a subclass thereof, for showing the progress of the simulation. Returns ------- output: :class:`~solver.Result` An instance of the class :class:`~solver.Result`, which contains either an array of expectation values for the times specified by ``tlist``, or an array or state vectors corresponding to the times in ``tlist`` (if ``e_ops`` is an empty list), or nothing if a callback function was given inplace of operators for which to calculate the expectation values. """ if e_ops is None: e_ops = [] if isinstance(e_ops, Qobj): e_ops = [e_ops] elif isinstance(e_ops, dict): e_ops_dict = e_ops e_ops = [e for e in e_ops.values()] else: e_ops_dict = None if progress_bar is None: progress_bar = BaseProgressBar() if progress_bar is True: progress_bar = TextProgressBar() if not (psi0.isket or psi0.isunitary): raise TypeError("The unitary solver requires psi0 to be" " a ket as initial state" " or a unitary as initial operator.") if options is None: options = Options() if options.rhs_reuse and not isinstance(H, SolverSystem): # TODO: deprecate when going to class based solver. if "sesolve" in solver_safe: H = solver_safe["sesolve"] if args is None: args = {} check_use_openmp(options) if isinstance(H, SolverSystem): ss = H elif isinstance(H, (list, Qobj, QobjEvo)): ss = _sesolve_QobjEvo(H, tlist, args, options) elif callable(H): ss = _sesolve_func_td(H, args, options) else: raise Exception("Invalid H type") func, ode_args = ss.makefunc(ss, psi0, args, e_ops, options) if _safe_mode: v = psi0.full().ravel('F') func(0., v, *ode_args) + v res = _generic_ode_solve(func, ode_args, psi0, tlist, e_ops, options, progress_bar, dims=psi0.dims) if e_ops_dict: res.expect = {e: res.expect[n] for n, e in enumerate(e_ops_dict.keys())} return res
def sesolve(H, psi0, tlist, e_ops=[], args={}, options=Options(), progress_bar=BaseProgressBar(), _safe_mode=True): """ Schrodinger equation evolution of a state vector or unitary matrix for a given Hamiltonian. Evolve the state vector (`psi0`) using a given Hamiltonian (`H`), by integrating the set of ordinary differential equations that define the system. Alternatively evolve a unitary matrix in solving the Schrodinger operator equation. The output is either the state vector or unitary matrix at arbitrary points in time (`tlist`), or the expectation values of the supplied operators (`e_ops`). If e_ops is a callback function, it is invoked for each time in `tlist` with time and the state as arguments, and the function does not use any return values. e_ops cannot be used in conjunction with solving the Schrodinger operator equation Parameters ---------- H : :class:`qutip.qobj`, :class:`qutip.qobjevo`, *list*, *callable* system Hamiltonian as a Qobj, list of Qobj and coefficient, QobjEvo, or a callback function for time-dependent Hamiltonians. list format and options can be found in QobjEvo's description. psi0 : :class:`qutip.qobj` initial state vector (ket) or initial unitary operator `psi0 = U` tlist : *list* / *array* list of times for :math:`t`. e_ops : list of :class:`qutip.qobj` / callback function single operator or list of operators for which to evaluate expectation values. For list operator evolution, the overlapse is computed: tr(e_ops[i].dag()*op(t)) args : *dictionary* dictionary of parameters for time-dependent Hamiltonians options : :class:`qutip.Qdeoptions` with options for the ODE solver. progress_bar : BaseProgressBar Optional instance of BaseProgressBar, or a subclass thereof, for showing the progress of the simulation. Returns ------- output: :class:`qutip.solver` An instance of the class :class:`qutip.solver`, which contains either an *array* of expectation values for the times specified by `tlist`, or an *array* or state vectors corresponding to the times in `tlist` [if `e_ops` is an empty list], or nothing if a callback function was given inplace of operators for which to calculate the expectation values. """ if isinstance(e_ops, Qobj): e_ops = [e_ops] elif isinstance(e_ops, dict): e_ops_dict = e_ops e_ops = [e for e in e_ops.values()] else: e_ops_dict = None if progress_bar is True: progress_bar = TextProgressBar() if not (psi0.isket or psi0.isunitary): raise TypeError("The unitary solver requires psi0 to be" " a ket as initial state" " or a unitary as initial operator.") if options.rhs_reuse and not isinstance(H, SolverSystem): # TODO: deprecate when going to class based solver. if "sesolve" in solver_safe: # print(" ") H = solver_safe["sesolve"] else: pass # raise Exception("Could not find the Hamiltonian to reuse.") #check if should use OPENMP check_use_openmp(options) if isinstance(H, SolverSystem): ss = H elif isinstance(H, (list, Qobj, QobjEvo)): ss = _sesolve_QobjEvo(H, tlist, args, options) elif callable(H): ss = _sesolve_func_td(H, args, options) else: raise Exception("Invalid H type") func, ode_args = ss.makefunc(ss, psi0, args, options) if _safe_mode: v = psi0.full().ravel('F') func(0., v, *ode_args) + v res = _generic_ode_solve(func, ode_args, psi0, tlist, e_ops, options, progress_bar, dims=psi0.dims) if e_ops_dict: res.expect = { e: res.expect[n] for n, e in enumerate(e_ops_dict.keys()) } res.SolverSystem = ss return res
def sesolve(H, rho0, tlist, e_ops=[], args={}, options=None, progress_bar=BaseProgressBar(), _safe_mode=True): """ Schrodinger equation evolution of a state vector for a given Hamiltonian. Evolve the state vector or density matrix (`rho0`) using a given Hamiltonian (`H`), by integrating the set of ordinary differential equations that define the system. The output is either the state vector at arbitrary points in time (`tlist`), or the expectation values of the supplied operators (`e_ops`). If e_ops is a callback function, it is invoked for each time in `tlist` with time and the state as arguments, and the function does not use any return values. Parameters ---------- H : :class:`qutip.qobj` system Hamiltonian, or a callback function for time-dependent Hamiltonians. rho0 : :class:`qutip.qobj` initial density matrix or state vector (ket). tlist : *list* / *array* list of times for :math:`t`. e_ops : list of :class:`qutip.qobj` / callback function single single operator or list of operators for which to evaluate expectation values. args : *dictionary* dictionary of parameters for time-dependent Hamiltonians and collapse operators. options : :class:`qutip.Qdeoptions` with options for the ODE solver. Returns ------- output: :class:`qutip.solver` An instance of the class :class:`qutip.solver`, which contains either an *array* of expectation values for the times specified by `tlist`, or an *array* or state vectors or density matrices corresponding to the times in `tlist` [if `e_ops` is an empty list], or nothing if a callback function was given inplace of operators for which to calculate the expectation values. """ 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 _safe_mode: _solver_safety_check(H, rho0, c_ops=[], e_ops=e_ops, args=args) # convert array based time-dependence to string format H, _, args = _td_wrap_array_str(H, [], args, tlist) # check for type (if any) of time-dependent inputs n_const, n_func, n_str = _td_format_check(H, []) if options is None: options = Options() if (not options.rhs_reuse) or (not config.tdfunc): # reset config time-dependence flags to default values config.reset() #check if should use OPENMP check_use_openmp(options) if n_func > 0: res = _sesolve_list_func_td(H, rho0, tlist, e_ops, args, options, progress_bar) elif n_str > 0: res = _sesolve_list_str_td(H, rho0, tlist, e_ops, args, options, progress_bar) elif isinstance(H, (types.FunctionType, types.BuiltinFunctionType, partial)): res = _sesolve_func_td(H, rho0, tlist, e_ops, args, options, progress_bar) else: res = _sesolve_const(H, rho0, tlist, e_ops, args, options, progress_bar) if e_ops_dict: res.expect = {e: res.expect[n] for n, e in enumerate(e_ops_dict.keys())} return res