def correlation(H, state0, tlist, taulist, c_ops, a_op, b_op, solver="me", reverse=False, args={}, options=Options(ntraj=[20, 100])): """ Calculate the two-operator two-time correlation function: :math:`\left<A(t+\\tau)B(t)\\right>` along two time axes using the quantum regression theorem and the evolution solver indicated by the `solver` parameter. Parameters ---------- H : :class:`qutip.qobj.Qobj` system Hamiltonian. state0 : :class:`qutip.qobj.Qobj` Initial state density matrix :math:`\\rho(t_0)` or state vector :math:`\\psi(t_0)`. If 'state0' is 'None', then the steady state will be used as the initial state. The 'steady-state' is only implemented for the `me` and `es` solvers. tlist : *list* / *array* list of times for :math:`t`. tlist must be positive and contain the element `0`. When taking steady-steady correlations only one tlist value is necessary, i.e. :math:`t \rightarrow \infty`; here tlist is automatically set, ignoring user input. taulist : *list* / *array* list of times for :math:`\\tau`. taulist must be positive and contain the element `0`. c_ops : list of :class:`qutip.qobj.Qobj` list of collapse operators. a_op : :class:`qutip.qobj.Qobj` operator A. b_op : :class:`qutip.qobj.Qobj` operator B. reverse : bool If `True`, calculate :math:`\left<A(t)B(t+\\tau)\\right>` instead of :math:`\left<A(t+\\tau)B(t)\\right>`. solver : str choice of solver (`me` for master-equation, `mc` for Monte Carlo, and `es` for exponential series) options : :class:`qutip.solver.Options` solver options class. `ntraj` is taken as a two-element list because the `mc` correlator calls `mcsolve()` recursively; by default, `ntraj=[20, 100]`. `mc_corr_eps` prevents divide-by-zero errors in the `mc` correlator; by default, `mc_corr_eps=1e-10`. Returns ------- corr_mat: *array* An 2-dimensional *array* (matrix) of correlation values for the times specified by `tlist` (first index) and `taulist` (second index). If `tlist` is `None`, then a 1-dimensional *array* of correlation values is returned instead. References ---------- See, Gardiner, Quantum Noise, Section 5.2. """ warn("correlation() now legacy, please use correlation_2op_2t()", FutureWarning) if debug: print(inspect.stack()[0][3]) return correlation_2op_2t(H, state0, tlist, taulist, c_ops, a_op, b_op, solver=solver, reverse=reverse, args=args, options=options)
def coherence_function_g2(H, state0, taulist, c_ops, a_op, solver="me", args={}, options=Options(ntraj=[20, 100])): """ Calculate the normalized second-order quantum coherence function: .. math:: g^{(2)}(\\tau) = \\frac{\\langle A^\\dagger(0)A^\\dagger(\\tau)A(\\tau)A(0)\\rangle} {\\langle A^\\dagger(\\tau)A(\\tau)\\rangle \\langle A^\\dagger(0)A(0)\\rangle} using the quantum regression theorem and the evolution solver indicated by the `solver` parameter. Parameters ---------- H : Qobj system Hamiltonian, may be time-dependent for solver choice of `me` or `mc`. state0 : Qobj Initial state density matrix :math:`\\rho(t_0)` or state vector :math:`\\psi(t_0)`. If 'state0' is 'None', then the steady state will be used as the initial state. The 'steady-state' is only implemented for the `me` and `es` solvers. taulist : array_like list of times for :math:`\\tau`. taulist must be positive and contain the element `0`. c_ops : list list of collapse operators, may be time-dependent for solver choice of `me` or `mc`. a_op : Qobj operator A. solver : str choice of solver (`me` for master-equation and `es` for exponential series). options : Options solver options class. `ntraj` is taken as a two-element list because the `mc` correlator calls `mcsolve()` recursively; by default, `ntraj=[20, 100]`. `mc_corr_eps` prevents divide-by-zero errors in the `mc` correlator; by default, `mc_corr_eps=1e-10`. Returns ------- g2, G2 : tuple The normalized and unnormalized second-order coherence function. """ # first calculate the photon number if state0 is None: state0 = steadystate(H, c_ops) n = np.array([expect(state0, a_op.dag() * a_op)]) else: n = mesolve(H, state0, taulist, c_ops, [a_op.dag() * a_op]).expect[0] # calculate the correlation function G2 and normalize with n to obtain g2 G2 = correlation_3op_1t(H, state0, taulist, c_ops, a_op.dag(), a_op.dag()*a_op, a_op, solver=solver, args=args, options=options) g2 = G2 / (n[0] * n) return g2, G2
def correlation_2op_1t(H, state0, taulist, c_ops, a_op, b_op, solver="me", reverse=False, args={}, options=Options(ntraj=[20, 100])): """ Calculate the two-operator two-time correlation function: :math:`\left<A(t+\\tau)B(t)\\right>` along one time axis using the quantum regression theorem and the evolution solver indicated by the `solver` parameter. Parameters ---------- H : Qobj system Hamiltonian, may be time-dependent for solver choice of `me` or `mc`. state0 : Qobj Initial state density matrix :math:`\\rho(t_0)` or state vector :math:`\\psi(t_0)`. If 'state0' is 'None', then the steady state will be used as the initial state. The 'steady-state' is only implemented for the `me` and `es` solvers. taulist : array_like list of times for :math:`\\tau`. taulist must be positive and contain the element `0`. c_ops : list list of collapse operators, may be time-dependent for solver choice of `me` or `mc`. a_op : Qobj operator A. b_op : Qobj operator B. reverse : bool {False, True} If `True`, calculate :math:`\left<A(t)B(t+\\tau)\\right>` instead of :math:`\left<A(t+\\tau)B(t)\\right>`. solver : str {'me', 'mc', 'es'} choice of solver (`me` for master-equation, `mc` for Monte Carlo, and `es` for exponential series). options : Options Solver options class. `ntraj` is taken as a two-element list because the `mc` correlator calls `mcsolve()` recursively; by default, `ntraj=[20, 100]`. `mc_corr_eps` prevents divide-by-zero errors in the `mc` correlator; by default, `mc_corr_eps=1e-10`. Returns ------- corr_vec : ndarray An array of correlation values for the times specified by `tlist`. References ---------- See, Gardiner, Quantum Noise, Section 5.2. """ if debug: print(inspect.stack()[0][3]) if reverse: A_op = a_op B_op = b_op C_op = 1 else: A_op = 1 B_op = a_op C_op = b_op return _correlation_2t(H, state0, [0], taulist, c_ops, A_op, B_op, C_op, solver=solver, args=args, options=options)[0]
Exps = [ MagnetizationX(N), MagnetizationZ(N), MagnetizationY(N), sigmaz(0, N), sigmaz(N - 1, N), upup(0, N), sigmap(0, N), sigmam(0, N), downdown(0, N) ] Commutatorlist = [] Anticommutatorlist = [] opts = Options(store_states=True, store_final_state=True) result_t1 = mesolve(H0(omega, J, N), productstateZ(0, N - 1, N), t1, [], Exps, options=opts) result_t1t2 = mesolve(H0(omega, J, N), result_t1.states[timesteps - 1], t2, [], Exps, options=opts) Perturb = MagnetizationX(N) Measure = MagnetizationY(N)
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. """ 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 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 # 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() 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 mcsolve(H, psi0, tlist, c_ops=[], e_ops=[], ntraj=0, args={}, options=None, progress_bar=True, map_func=parallel_map, map_kwargs={}, _safe_mode=True): """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`, ``list`` 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 : :class:`qutip.Qobj`, ``list`` single collapse operator or a ``list`` of collapse operators. e_ops : :class:`qutip.Qobj`, ``list`` single operator as Qobj or ``list`` or equivalent of Qobj 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 isinstance(c_ops, (Qobj, QobjEvo)): c_ops = [c_ops] if options is None: options = Options() if options.rhs_reuse and not isinstance(H, SolverSystem): # TODO: deprecate when going to class based solver. if "mcsolve" in solver_safe: # print(" ") H = solver_safe["mcsolve"] else: pass # raise Exception("Could not find the Hamiltonian to reuse.") if not ntraj: ntraj = options.ntraj if len(c_ops) == 0 and not options.rhs_reuse: warn("No c_ops, using sesolve") return sesolve(H, psi0, tlist, e_ops=e_ops, args=args, options=options, progress_bar=progress_bar, _safe_mode=_safe_mode) try: num_traj = int(ntraj) except: num_traj = max(ntraj) # set the physics if not psi0.isket: raise Exception("Initial state must be a state vector.") # load monte carlo class mc = _MC(options) if isinstance(H, SolverSystem): mc.ss = H else: mc.make_system(H, c_ops, tlist, args, options) mc.reset(tlist[0], psi0) mc.set_e_ops(e_ops) if options.seeds is not None: mc.seed(num_traj, options.seeds) if _safe_mode: mc.run_test() # Run the simulation mc.run(num_traj=num_traj, tlist=tlist, progress_bar=progress_bar, map_func=map_func, map_kwargs=map_kwargs) return mc.get_result(ntraj)
def brmesolve(H, psi0, tlist, a_ops, e_ops=[], spectra_cb=[], args={}, options=Options()): """ Solve the dynamics for the system using the Bloch-Redfeild master equation. .. note:: This solver does not currently support time-dependent Hamiltonian or collapse operators. 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. args : *dictionary* Dictionary of parameters for time-dependent Hamiltonians and collapse operators. options : :class:`qutip.Qdeoptions` Options for the ODE solver. Returns ------- output: :class:`qutip.solver` An instance of the class :class:`qutip.solver`, which contains either a list 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) 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
import matplotlib.pyplot as plt # Extract virtual photon population from HEOM wq = 1. delta = 0. coup_strength, bath_broad, bath_freq = 0.2, 0.05, 1. Q = sigmax() tlist = np.linspace(0, 200, 1000) Nc = 9 Hsys = 0.5 * wq * sigmaz() + 0.5 * delta * sigmax() initial_ket = basis(2, 1) rho0 = initial_ket*initial_ket.dag() omega = np.sqrt(bath_freq**2 - (bath_broad/2.)**2) options = Options(nsteps=1500, store_states=True, atol=1e-12, rtol=1e-12) # zero temperature case, renormalized coupling strength beta = np.inf lam_renorm = coup_strength**2/(2*(omega)) ck1, vk1 = nonmatsubara_exponents(coup_strength, bath_broad, bath_freq, beta) # Ignore Matsubara hsolver_no_matsubara = HeomUB(Hsys, Q, lam_renorm, ck1, -vk1, ncut=Nc) output_no_matsubara = hsolver_no_matsubara.solve(rho0, tlist, options) # Add zero temperature Matsubara coefficients mats_data_zero = matsubara_zero_analytical(coup_strength, bath_broad, bath_freq, tlist) ck20, vk20 = biexp_fit(tlist, mats_data_zero)
def __init__(self, H=None, state0=None, times=None, c_ops=[], sc_ops=[], e_ops=[], m_ops=None, args=None, ntraj=1, nsubsteps=1, d1=None, d2=None, d2_len=1, dW_factors=None, rhs=None, generate_A_ops=None, generate_noise=None, homogeneous=True, solver=None, method=None, distribution='normal', store_measurement=False, noise=None, normalize=True, options=None, progress_bar=None, map_func=None, map_kwargs=None): if options is None: options = Options() if progress_bar is None: progress_bar = TextProgressBar() self.H = H self.d1 = d1 self.d2 = d2 self.d2_len = d2_len self.dW_factors = dW_factors# if dW_factors else np.ones(d2_len) self.state0 = state0 self.times = times self.c_ops = c_ops self.sc_ops = sc_ops self.e_ops = e_ops #if m_ops is None: # self.m_ops = [[c for _ in range(d2_len)] for c in sc_ops] #else: # self.m_ops = m_ops self.m_ops = m_ops self.ntraj = ntraj self.nsubsteps = nsubsteps self.solver = solver self.method = method self.distribution = distribution self.homogeneous = homogeneous self.rhs = rhs self.options = options self.progress_bar = progress_bar self.store_measurement = store_measurement self.store_states = options.store_states self.noise = noise self.args = args self.normalize = normalize self.generate_noise = generate_noise self.generate_A_ops = generate_A_ops if self.ntraj > 1 and map_func: self.map_func = map_func else: self.map_func = serial_map self.map_kwargs = map_kwargs if map_kwargs is not None else {} #Does any operator depend on time? self.td = False if not isinstance(H, Qobj): self.td = True for ops in c_ops: if not isinstance(ops, Qobj): self.td = True for ops in sc_ops: if not isinstance(ops, Qobj): self.td = True
def sesolve(H, psi0, tlist, e_ops=None, args=None, 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`, :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 : None / 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 : None / *dictionary* dictionary of parameters for time-dependent Hamiltonians options : None / :class:`qutip.Qdeoptions` with options for the ODE solver. progress_bar : None / 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 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: # print(" ") H = solver_safe["sesolve"] else: pass # raise Exception("Could not find the Hamiltonian to reuse.") 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, 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 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 propagator(H, t, c_op_list, args=None, options=None, sparse=False): """ Calculate the propagator U(t) for the density matrix or wave function such that :math:`\psi(t) = U(t)\psi(0)` or :math:`\\rho_{\mathrm vec}(t) = U(t) \\rho_{\mathrm vec}(0)` where :math:`\\rho_{\mathrm vec}` is the vector representation of the density matrix. Parameters ---------- H : qobj or list Hamiltonian as a Qobj instance of a nested list of Qobjs and coefficients in the list-string or list-function format for time-dependent Hamiltonians (see description in :func:`qutip.mesolve`). t : float or array-like Time or list of times for which to evaluate the propagator. c_op_list : list List of qobj collapse operators. args : list/array/dictionary Parameters to callback functions for time-dependent Hamiltonians and collapse operators. options : :class:`qutip.Options` with options for the ODE solver. Returns ------- a : qobj Instance representing the propagator :math:`U(t)`. """ if options is None: options = Options() options.rhs_reuse = True rhs_clear() if isinstance(t, (int, float, np.integer, np.floating)): tlist = [0, t] else: tlist = t if isinstance( H, (types.FunctionType, types.BuiltinFunctionType, functools.partial)): H0 = H(0.0, args) elif isinstance(H, list): H0 = H[0][0] if isinstance(H[0], list) else H[0] else: H0 = H if len(c_op_list) == 0 and H0.isoper: # calculate propagator for the wave function N = H0.shape[0] dims = H0.dims u = np.zeros([N, N, len(tlist)], dtype=complex) for n in range(0, N): psi0 = basis(N, n) output = sesolve(H, psi0, tlist, [], args, options) for k, t in enumerate(tlist): u[:, n, k] = output.states[k].full().T # todo: evolving a batch of wave functions: # psi_0_list = [basis(N, n) for n in range(N)] # psi_t_list = mesolve(H, psi_0_list, [0, t], [], [], args, options) # for n in range(0, N): # u[:,n] = psi_t_list[n][1].full().T elif len(c_op_list) == 0 and H0.issuper: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) N = H0.shape[0] dims = H0.dims u = np.zeros([N, N, len(tlist)], dtype=complex) for n in range(0, N): psi0 = basis(N, n) rho0 = Qobj(vec2mat(psi0.full())) output = mesolve(H, rho0, tlist, [], [], args, options) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T else: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) N = H0.shape[0] dims = [H0.dims, H0.dims] u = np.zeros([N * N, N * N, len(tlist)], dtype=complex) if sparse: for n in range(N * N): psi0 = basis(N * N, n) psi0.dims = [dims[0], 1] rho0 = vector_to_operator(psi0) output = mesolve(H, rho0, tlist, c_op_list, [], args, options) for k, t in enumerate(tlist): u[:, n, k] = operator_to_vector( output.states[k]).full(squeeze=True) else: for n in range(N * N): psi0 = basis(N * N, n) rho0 = Qobj(vec2mat(psi0.full())) output = mesolve(H, rho0, tlist, c_op_list, [], args, options) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T if len(tlist) == 2: return Qobj(u[:, :, 1], dims=dims) else: return [Qobj(u[:, :, k], dims=dims) for k in range(len(tlist))]
def test_pure_dephasing(self): """ HSolverDL: Compare with pure-dephasing analytical assert that the analytical result and HEOM produce the same time dephasing evoltion. """ resid_tol = 1e-4 def spectral_density(omega, lam_c, omega_c): return 2.0 * lam_c * omega * omega_c / (omega_c**2 + omega**2) def integrand(omega, lam_c, omega_c, Temp, t): J = spectral_density(omega, lam_c, omega_c) return (-4.0 * J * (1.0 - cos(omega * t)) / (tanh(omega / (2.0 * Temp)) * omega**2)) cut_freq = 0.05 coup_strength = 0.025 temperature = 1.0 / 0.95 tlist = np.linspace(0, 10, 21) # Calculate the analytical results by numerical integration lam_c = coup_strength / pi PEG_DL = [ 0.5 * np.exp( quad(integrand, 0, np.inf, args=(lam_c, cut_freq, temperature, t))[0]) for t in tlist ] H_sys = Qobj(np.zeros((2, 2))) Q = sigmaz() initial_state = 0.5 * Qobj(np.ones((2, 2))) P12p = basis(2, 0) * basis(2, 1).dag() integ_options = Options(nsteps=15000, store_states=True) test_desc = "renorm, bnd_cut_approx, and stats" hsolver = HSolverDL(H_sys, Q, coup_strength, temperature, 20, 2, cut_freq, renorm=True, bnd_cut_approx=True, options=integ_options, stats=True) result = hsolver.run(initial_state, tlist) P12_result1 = expect(result.states, P12p) resid = abs(real(P12_result1 - PEG_DL)) max_resid = max(resid) assert_( max_resid < resid_tol, "Max residual {} outside tolerence {}, " "for hsolve with {}".format(max_resid, resid_tol, test_desc)) resid_tol = 1e-3 test_desc = "renorm" hsolver.configure(H_sys, Q, coup_strength, temperature, 20, 2, cut_freq, renorm=True, bnd_cut_approx=False, options=integ_options, stats=False) assert_(hsolver.stats == None, "Failed to unset stats") result = hsolver.run(initial_state, tlist) P12_result1 = expect(result.states, P12p) resid = abs(real(P12_result1 - PEG_DL)) max_resid = max(resid) assert_( max_resid < resid_tol, "Max residual {} outside tolerence {}, " "for hsolve with {}".format(max_resid, resid_tol, test_desc)) resid_tol = 1e-4 test_desc = "bnd_cut_approx" hsolver.configure(H_sys, Q, coup_strength, temperature, 20, 2, cut_freq, renorm=False, bnd_cut_approx=True, options=integ_options, stats=False) assert_(hsolver.stats == None, "Failed to unset stats") result = hsolver.run(initial_state, tlist) P12_result1 = expect(result.states, P12p) resid = abs(real(P12_result1 - PEG_DL)) max_resid = max(resid) assert_( max_resid < resid_tol, "Max residual {} outside tolerence {}, " "for hsolve with {}".format(max_resid, resid_tol, test_desc))
def correlation_4op_1t(H, state0, taulist, c_ops, a_op, b_op, c_op, d_op, solver="me", args={}, options=Options(ntraj=[20, 100])): """ Calculate the four-operator two-time correlation function: :math:`\left<A(t)B(t+\\tau)C(t+\\tau)D(t)\\right>` along one time axis using the quantum regression theorem and the evolution solver indicated by the `solver` parameter. Note: it is not possibly to calculate a physically meaningful correlation of this form where :math:`\\tau<0`. Parameters ---------- H : :class:`qutip.qobj.Qobj` system Hamiltonian. rho0 : :class:`qutip.qobj.Qobj` Initial state density matrix :math:`\\rho(t_0)` or state vector :math:`\\psi(t_0)`. If 'state0' is 'None', then the steady state will be used as the initial state. The 'steady-state' is only implemented for the `me` and `es` solvers. taulist : *list* / *array* list of times for :math:`\\tau`. taulist must be positive and contain the element `0`. c_ops : list of :class:`qutip.qobj.Qobj` list of collapse operators. a_op : :class:`qutip.qobj.Qobj` operator A. b_op : :class:`qutip.qobj.Qobj` operator B. c_op : :class:`qutip.qobj.Qobj` operator C. d_op : :class:`qutip.qobj.Qobj` operator D. solver : str choice of solver (`me` for master-equation, `mc` for Monte Carlo, and `es` for exponential series) options : :class:`qutip.solver.Options` solver options class. `ntraj` is taken as a two-element list because the `mc` correlator calls `mcsolve()` recursively; by default, `ntraj=[20, 100]`. `mc_corr_eps` prevents divide-by-zero errors in the `mc` correlator; by default, `mc_corr_eps=1e-10`. Returns ------- corr_vec: *array* An *array* of correlation values for the times specified by `taulist` References ---------- See, Gardiner, Quantum Noise, Section 5.2. """ warn("correlation_4op_1t() now legacy, please use correlation_3op_1t()", FutureWarning) warn("the reverse argument has been removed as it did not contain any" + "new physical information", DeprecationWarning) if debug: print(inspect.stack()[0][3]) return correlation_3op_1t(H, state0, taulist, c_ops, a_op, b_op * c_op, d_op, solver=solver, args=args, options=options)
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) else: res = _sesolve_const(H, psi0, 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 propagator(H, t, c_op_list=[], args={}, options=None, unitary_mode='batch', parallel=False, progress_bar=None, **kwargs): """ Calculate the propagator U(t) for the density matrix or wave function such that :math:`\psi(t) = U(t)\psi(0)` or :math:`\\rho_{\mathrm vec}(t) = U(t) \\rho_{\mathrm vec}(0)` where :math:`\\rho_{\mathrm vec}` is the vector representation of the density matrix. Parameters ---------- H : qobj or list Hamiltonian as a Qobj instance of a nested list of Qobjs and coefficients in the list-string or list-function format for time-dependent Hamiltonians (see description in :func:`qutip.mesolve`). t : float or array-like Time or list of times for which to evaluate the propagator. c_op_list : list List of qobj collapse operators. args : list/array/dictionary Parameters to callback functions for time-dependent Hamiltonians and collapse operators. options : :class:`qutip.Options` with options for the ODE solver. unitary_mode = str ('batch', 'single') Solve all basis vectors simulaneously ('batch') or individually ('single'). parallel : bool {False, True} Run the propagator in parallel mode. This will override the unitary_mode settings if set to True. progress_bar: BaseProgressBar Optional instance of BaseProgressBar, or a subclass thereof, for showing the progress of the simulation. By default no progress bar is used, and if set to True a TextProgressBar will be used. Returns ------- a : qobj Instance representing the propagator :math:`U(t)`. """ kw = _default_kwargs() if 'num_cpus' in kwargs: num_cpus = kwargs['num_cpus'] else: num_cpus = kw['num_cpus'] if progress_bar is None: progress_bar = BaseProgressBar() elif progress_bar is True: progress_bar = TextProgressBar() if options is None: options = Options() options.rhs_reuse = True rhs_clear() if isinstance(t, (int, float, np.integer, np.floating)): tlist = [0, t] else: tlist = t td_type = _td_format_check(H, c_op_list, solver='me') if isinstance( H, (types.FunctionType, types.BuiltinFunctionType, functools.partial)): H0 = H(0.0, args) elif isinstance(H, list): H0 = H[0][0] if isinstance(H[0], list) else H[0] else: H0 = H if len(c_op_list) == 0 and H0.isoper: # calculate propagator for the wave function N = H0.shape[0] dims = H0.dims if parallel: unitary_mode = 'single' u = np.zeros([N, N, len(tlist)], dtype=complex) output = parallel_map(_parallel_sesolve, range(N), task_args=(N, H, tlist, args, options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N): for k, t in enumerate(tlist): u[:, n, k] = output[n].states[k].full().T else: if unitary_mode == 'single': u = np.zeros([N, N, len(tlist)], dtype=complex) progress_bar.start(N) for n in range(0, N): progress_bar.update(n) psi0 = basis(N, n) output = sesolve(H, psi0, tlist, [], args, options, _safe_mode=False) for k, t in enumerate(tlist): u[:, n, k] = output.states[k].full().T progress_bar.finished() elif unitary_mode == 'batch': u = np.zeros(len(tlist), dtype=object) _rows = np.array([(N + 1) * m for m in range(N)]) _cols = np.zeros_like(_rows) _data = np.ones_like(_rows, dtype=complex) psi0 = Qobj(sp.coo_matrix((_data, (_rows, _cols))).tocsr()) if td_type[1] > 0 or td_type[2] > 0: H2 = [] for k in range(len(H)): if isinstance(H[k], list): H2.append([tensor(qeye(N), H[k][0]), H[k][1]]) else: H2.append(tensor(qeye(N), H[k])) else: H2 = tensor(qeye(N), H) output = sesolve(H2, psi0, tlist, [], args=args, _safe_mode=False, options=Options(normalize_output=False)) for k, t in enumerate(tlist): u[k] = sp_reshape(output.states[k].data, (N, N)) unit_row_norm(u[k].data, u[k].indptr, u[k].shape[0]) u[k] = u[k].T.tocsr() else: raise Exception('Invalid unitary mode.') elif len(c_op_list) == 0 and H0.issuper: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) unitary_mode = 'single' N = H0.shape[0] sqrt_N = int(np.sqrt(N)) dims = H0.dims u = np.zeros([N, N, len(tlist)], dtype=complex) if parallel: output = parallel_map(_parallel_mesolve, range(N * N), task_args=(sqrt_N, H, tlist, c_op_list, args, options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N * N): for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output[n].states[k].full()).T else: progress_bar.start(N) for n in range(0, N): progress_bar.update(n) col_idx, row_idx = np.unravel_index(n, (sqrt_N, sqrt_N)) rho0 = Qobj( sp.csr_matrix(([1], ([row_idx], [col_idx])), shape=(sqrt_N, sqrt_N), dtype=complex)) output = mesolve(H, rho0, tlist, [], [], args, options, _safe_mode=False) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T progress_bar.finished() else: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) unitary_mode = 'single' N = H0.shape[0] dims = [H0.dims, H0.dims] u = np.zeros([N * N, N * N, len(tlist)], dtype=complex) if parallel: output = parallel_map(_parallel_mesolve, range(N * N), task_args=(N, H, tlist, c_op_list, args, options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N * N): for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output[n].states[k].full()).T else: progress_bar.start(N * N) for n in range(N * N): progress_bar.update(n) col_idx, row_idx = np.unravel_index(n, (N, N)) rho0 = Qobj( sp.csr_matrix(([1], ([row_idx], [col_idx])), shape=(N, N), dtype=complex)) output = mesolve(H, rho0, tlist, c_op_list, [], args, options, _safe_mode=False) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T progress_bar.finished() if len(tlist) == 2: if unitary_mode == 'batch': return Qobj(u[-1], dims=dims) else: return Qobj(u[:, :, 1], dims=dims) else: if unitary_mode == 'batch': return np.array([Qobj(u[k], dims=dims) for k in range(len(tlist))], dtype=object) else: return np.array( [Qobj(u[:, :, k], dims=dims) for k in range(len(tlist))], dtype=object)
def mesolve(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.solver.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.solver.Result` An instance of the class :class:`qutip.solver.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 bloch_redfield_solve(R, ekets, rho0, tlist, e_ops=[], options=None): """ Evolve the ODEs defined by Bloch-Redfield master equation. The Bloch-Redfield tensor can be calculated by the function :func:`bloch_redfield_tensor`. Parameters ---------- R : :class:`qutip.qobj` Bloch-Redfield tensor. ekets : array of :class:`qutip.qobj` Array of kets that make up a basis tranformation for the eigenbasis. rho0 : :class:`qutip.qobj` Initial density matrix. tlist : *list* / *array* List of times for :math:`t`. e_ops : list of :class:`qutip.qobj` / callback function List of operators for which to evaluate expectation values. options : :class:`qutip.Qdeoptions` Options for the ODE solver. Returns ------- output: :class:`qutip.solver` An instance of the class :class:`qutip.solver`, which contains either an *array* of expectation values for the times specified by `tlist`. """ if options is None: options = Options() if options.tidy: R.tidyup() # # check initial state # if isket(rho0): # Got a wave function as initial state: convert to density matrix. rho0 = rho0 * rho0.dag() # # prepare output array # n_tsteps = len(tlist) dt = tlist[1] - tlist[0] result_list = [] # # transform the initial density matrix and the e_ops opterators to the # eigenbasis # rho_eb = rho0.transform(ekets) e_eb_ops = [e.transform(ekets) for e in e_ops] for e_eb in e_eb_ops: result_list.append(np.zeros(n_tsteps, dtype=complex)) # # setup integrator # initial_vector = mat2vec(rho_eb.full()) r = scipy.integrate.ode(cy_ode_rhs) r.set_f_params(R.data.data, R.data.indices, R.data.indptr) r.set_integrator('zvode', method=options.method, order=options.order, atol=options.atol, rtol=options.rtol, nsteps=options.nsteps, first_step=options.first_step, min_step=options.min_step, max_step=options.max_step) r.set_initial_value(initial_vector, tlist[0]) # # start evolution # dt = np.diff(tlist) for t_idx, _ in enumerate(tlist): if not r.successful(): break rho_eb.data = vec2mat(r.y) # calculate all the expectation values, or output rho_eb if no # expectation value operators are given if e_ops: rho_eb_tmp = Qobj(rho_eb) for m, e in enumerate(e_eb_ops): result_list[m][t_idx] = expect(e, rho_eb_tmp) else: result_list.append(rho_eb.transform(ekets, True)) if t_idx < n_tsteps - 1: r.integrate(r.t + dt[t_idx]) return result_list
def rhs_generate(H, c_ops, args={}, options=Options(), name=None, cleanup=True): """ Generates the Cython functions needed for solving the dynamics of a given system using the mesolve function inside a parfor loop. Parameters ---------- H : qobj System Hamiltonian. c_ops : list ``list`` of collapse operators. args : dict Arguments for time-dependent Hamiltonian and collapse operator terms. options : Options Instance of ODE solver options. name: str Name of generated RHS cleanup: bool Whether the generated cython file should be automatically removed or not. Notes ----- Using this function with any solver other than the mesolve function will result in an error. """ config.reset() config.options = options if name: config.tdname = name else: config.tdname = "rhs" + str(os.getpid()) + str(config.cgen_num) Lconst = 0 Ldata = [] Linds = [] Lptrs = [] Lcoeff = [] # loop over all hamiltonian terms, convert to superoperator form and # add the data of sparse matrix represenation to msg = "Incorrect specification of time-dependence: " for h_spec in H: if isinstance(h_spec, Qobj): h = h_spec if not isinstance(h, Qobj): raise TypeError(msg + "expected Qobj") if h.isoper: Lconst += -1j * (spre(h) - spost(h)) elif h.issuper: Lconst += h else: raise TypeError(msg + "expected operator or superoperator") elif isinstance(h_spec, list): h = h_spec[0] h_coeff = h_spec[1] if not isinstance(h, Qobj): raise TypeError(msg + "expected Qobj") if h.isoper: L = -1j * (spre(h) - spost(h)) elif h.issuper: L = h else: raise TypeError(msg + "expected operator or superoperator") Ldata.append(L.data.data) Linds.append(L.data.indices) Lptrs.append(L.data.indptr) Lcoeff.append(h_coeff) else: raise TypeError(msg + "expected string format") # loop over all collapse operators for c_spec in c_ops: if isinstance(c_spec, Qobj): c = c_spec if not isinstance(c, Qobj): raise TypeError(msg + "expected Qobj") if c.isoper: cdc = c.dag() * c Lconst += spre(c) * spost(c.dag()) - 0.5 * spre(cdc) \ - 0.5 * spost(cdc) elif c.issuper: Lconst += c else: raise TypeError(msg + "expected operator or superoperator") elif isinstance(c_spec, list): c = c_spec[0] c_coeff = c_spec[1] if not isinstance(c, Qobj): raise TypeError(msg + "expected Qobj") if c.isoper: cdc = c.dag() * c L = spre(c) * spost(c.dag()) - 0.5 * spre(cdc) \ - 0.5 * spost(cdc) c_coeff = "(" + c_coeff + ")**2" elif c.issuper: L = c else: raise TypeError(msg + "expected operator or superoperator") Ldata.append(L.data.data) Linds.append(L.data.indices) Lptrs.append(L.data.indptr) Lcoeff.append(c_coeff) else: raise TypeError(msg + "expected string format") # add the constant part of the lagrangian if Lconst != 0: Ldata.append(Lconst.data.data) Linds.append(Lconst.data.indices) Lptrs.append(Lconst.data.indptr) Lcoeff.append("1.0") # the total number of liouvillian terms (hamiltonian terms + collapse # operators) n_L_terms = len(Ldata) cgen = Codegen(h_terms=n_L_terms, h_tdterms=Lcoeff, args=args, config=config) cgen.generate(config.tdname + ".pyx") code = compile('from ' + config.tdname + ' import cy_td_ode_rhs', '<string>', 'exec') exec(code, globals()) config.tdfunc = cy_td_ode_rhs if cleanup: try: os.remove(config.tdname + ".pyx") except: pass
def butter_bandpass(lowcut, highcut, fs, order=5): nyq = 0.5 * fs low = lowcut / nyq high = highcut / nyq b, a = butter(order, [low, high], btype='band') return b, a def butter_bandpass_filter(data, lowcut, highcut, fs, order=5): b, a = butter_bandpass(lowcut, highcut, fs, order=order) y = lfilter(b, a, data) return y opts = Options(store_states=True, store_final_state=True, ntraj=200) def threebasis(): return np.array([basis(3, 0), basis(3, 1), basis(3, 2)], dtype=object) def productstateZ(up_atom, down_atom, N): ancilla, up, down = threebasis() oplist = np.empty(N, dtype=object) oplist = [down for _ in oplist] oplist[up_atom] = Qobj(up) oplist[down_atom] = Qobj(down) return tensor(oplist)
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 odesolve(H, rho0, tlist, c_op_list, e_ops, args=None, options=None): """ Master equation evolution of a density matrix for a given Hamiltonian. Evolution of a state vector or density matrix (`rho0`) for a given Hamiltonian (`H`) and set of collapse operators (`c_op_list`), 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`). For problems with time-dependent Hamiltonians, `H` can be a callback function that takes two arguments, time and `args`, and returns the Hamiltonian at that point in time. `args` is a list of parameters that is passed to the callback function `H` (only used for time-dependent Hamiltonians). 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`. c_op_list : list of :class:`qutip.qobj` list of collapse operators. e_ops : list of :class:`qutip.qobj` / callback function 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 ODE solver. Returns ------- output :array Expectation values of wavefunctions/density matrices for the times specified by `tlist`. Notes ----- On using callback function: odesolve 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. odesolve 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 to scipy who will raise an NotImplemented exception. Deprecated in QuTiP 2.0.0. Use :func:`mesolve` instead. """ warnings.warn("odesolve is deprecated since 2.0.0. Use mesolve instead.", DeprecationWarning) if debug: print(inspect.stack()[0][3]) if options is None: options = Options() if (c_op_list and len(c_op_list) > 0) or not isket(rho0): if isinstance(H, list): output = _mesolve_list_td(H, rho0, tlist, c_op_list, e_ops, args, options, BaseProgressBar()) if isinstance( H, (types.FunctionType, types.BuiltinFunctionType, partial)): output = _mesolve_func_td(H, rho0, tlist, c_op_list, e_ops, args, options, BaseProgressBar()) else: output = _mesolve_const(H, rho0, tlist, c_op_list, e_ops, args, options, BaseProgressBar()) else: if isinstance(H, list): output = _sesolve_list_td(H, rho0, tlist, e_ops, args, options, BaseProgressBar()) if isinstance( H, (types.FunctionType, types.BuiltinFunctionType, partial)): output = _sesolve_func_td(H, rho0, tlist, e_ops, args, options, BaseProgressBar()) else: output = _sesolve_const(H, rho0, tlist, e_ops, args, options, BaseProgressBar()) if len(e_ops) > 0: return output.expect else: return output.states
def fmmesolve(H, rho0, tlist, c_ops, e_ops=[], spectra_cb=[], T=None, args={}, options=Options(), floquet_basis=True, kmax=5): """ Solve the dynamics for the system using the Floquet-Markov master equation. .. note:: This solver currently does not support multiple collapse operators. 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`. c_ops : list of :class:`qutip.qobj` list of collapse operators. e_ops : list of :class:`qutip.qobj` / callback function list of operators for which to evaluate expectation values. spectra_cb : list callback functions List of callback functions that compute the noise power spectrum as a function of frequency for the collapse operators in `c_ops`. T : float The period of the time-dependence of the hamiltonian. The default value 'None' indicates that the 'tlist' spans a single period of the driving. args : *dictionary* dictionary of parameters for time-dependent Hamiltonians and collapse operators. This dictionary should also contain an entry 'w_th', which is the temperature of the environment (if finite) in the energy/frequency units of the Hamiltonian. For example, if the Hamiltonian written in units of 2pi GHz, and the temperature is given in K, use the following conversion >>> temperature = 25e-3 # unit K >>> h = 6.626e-34 >>> kB = 1.38e-23 >>> args['w_th'] = temperature * (kB / h) * 2 * pi * 1e-9 options : :class:`qutip.solver` options for the ODE solver. k_max : int The truncation of the number of sidebands (default 5). Returns ------- output : :class:`qutip.solver` An instance of the class :class:`qutip.solver`, which contains either an *array* of expectation values for the times specified by `tlist`. """ if T is None: T = max(tlist) if len(spectra_cb) == 0: # add white noise callbacks if absent spectra_cb = [lambda w: 1.0] * len(c_ops) f_modes_0, f_energies = floquet_modes(H, T, args) f_modes_table_t = floquet_modes_table(f_modes_0, f_energies, np.linspace(0, T, 500 + 1), H, T, args) # get w_th from args if it exists if 'w_th' in args: w_th = args['w_th'] else: w_th = 0 # TODO: loop over input c_ops and spectra_cb, calculate one R for each set # calculate the rate-matrices for the floquet-markov master equation Delta, X, Gamma, Amat = floquet_master_equation_rates( f_modes_0, f_energies, c_ops[0], H, T, args, spectra_cb[0], w_th, kmax, f_modes_table_t) # the floquet-markov master equation tensor R = floquet_master_equation_tensor(Amat, f_energies) return floquet_markov_mesolve(R, f_modes_0, rho0, tlist, e_ops, f_modes_table=(f_modes_table_t, T), options=options, floquet_basis=floquet_basis)
def _correlation_mc_2t(H, state0, tlist, taulist, c_ops, a_op, b_op, c_op, args={}, options=Options()): """ Internal function for calculating the three-operator two-time correlation function: <A(t)B(t+tau)C(t)> using a Monte Carlo solver. """ if not c_ops: raise TypeError("If no collapse operators are required, use the `me`" + "or `es` solvers") # the solvers only work for positive time differences and the correlators # require positive tau if state0 is None: raise NotImplementedError("steady state not implemented for " + "mc solver, please use `es` or `me`") elif not isket(state0): raise TypeError("state0 must be a state vector.") psi0 = state0 if debug: print(inspect.stack()[0][3]) psi_t_mat = mcsolve( H, psi0, tlist, c_ops, [], args=args, ntraj=options.ntraj[0], options=options, progress_bar=None ).states corr_mat = np.zeros([np.size(tlist), np.size(taulist)], dtype=complex) H_shifted, c_ops_shifted, _args = _transform_L_t_shift(H, c_ops, args) rhs_clear() # calculation of <A(t)B(t+tau)C(t)> from only knowledge of psi0 requires # averaging over both t and tau for t_idx in range(np.size(tlist)): if not isinstance(H, Qobj): _args["_t0"] = tlist[t_idx] for trial_idx in range(options.ntraj[0]): if isinstance(a_op, Qobj) and isinstance(c_op, Qobj): if a_op.dag() == c_op: # A shortcut here, requires only 1/4 the trials chi_0 = (options.mc_corr_eps + c_op) * \ psi_t_mat[trial_idx, t_idx] # evolve these states and calculate expectation value of B c_tau = chi_0.norm()**2 * mcsolve( H_shifted, chi_0/chi_0.norm(), taulist, c_ops_shifted, [b_op], args=_args, ntraj=options.ntraj[1], options=options, progress_bar=None ).expect[0] # final correlation vector computed by combining the # averages corr_mat[t_idx, :] += c_tau/options.ntraj[1] else: # otherwise, need four trial wavefunctions # (Ad+C)*psi_t, (Ad+iC)*psi_t, (Ad-C)*psi_t, (Ad-iC)*psi_t if isinstance(a_op, Qobj): a_op_dag = a_op.dag() else: # assume this is a number, ex. i.e. a_op = 1 # if this is not correct, the over-loaded addition # operation will raise errors a_op_dag = a_op chi_0 = [(options.mc_corr_eps + a_op_dag + np.exp(1j*x*np.pi/2)*c_op) * psi_t_mat[trial_idx, t_idx] for x in range(4)] # evolve these states and calculate expectation value of B c_tau = [ chi.norm()**2 * mcsolve( H_shifted, chi/chi.norm(), taulist, c_ops_shifted, [b_op], args=_args, ntraj=options.ntraj[1], options=options, progress_bar=None ).expect[0] for chi in chi_0 ] # final correlation vector computed by combining the averages corr_mat_add = np.asarray( 1.0 / (4*options.ntraj[0]) * (c_tau[0] - c_tau[2] - 1j*c_tau[1] + 1j*c_tau[3]), dtype=corr_mat.dtype ) corr_mat[t_idx, :] += corr_mat_add if t_idx == 1: options.rhs_reuse = True rhs_clear() return corr_mat
def propagator(H, t, c_op_list=[], args={}, options=None, parallel=False, progress_bar=None, **kwargs): """ Calculate the propagator U(t) for the density matrix or wave function such that :math:`\psi(t) = U(t)\psi(0)` or :math:`\\rho_{\mathrm vec}(t) = U(t) \\rho_{\mathrm vec}(0)` where :math:`\\rho_{\mathrm vec}` is the vector representation of the density matrix. Parameters ---------- H : qobj or list Hamiltonian as a Qobj instance of a nested list of Qobjs and coefficients in the list-string or list-function format for time-dependent Hamiltonians (see description in :func:`qutip.mesolve`). t : float or array-like Time or list of times for which to evaluate the propagator. c_op_list : list List of qobj collapse operators. args : list/array/dictionary Parameters to callback functions for time-dependent Hamiltonians and collapse operators. options : :class:`qutip.Options` with options for the ODE solver. parallel : bool {False, True} Run the propagator in parallel mode. progress_bar: BaseProgressBar Optional instance of BaseProgressBar, or a subclass thereof, for showing the progress of the simulation. By default no progress bar is used, and if set to True a TextProgressBar will be used. Returns ------- a : qobj Instance representing the propagator :math:`U(t)`. """ kw = _default_kwargs() if 'num_cpus' in kwargs: num_cpus = kwargs['num_cpus'] else: num_cpus = kw['num_cpus'] if progress_bar is None: progress_bar = BaseProgressBar() elif progress_bar is True: progress_bar = TextProgressBar() if options is None: options = Options() options.rhs_reuse = True rhs_clear() if isinstance(t, (int, float, np.integer, np.floating)): tlist = [0, t] else: tlist = t td_type = _td_format_check(H, c_op_list, solver='me')[2] if td_type > 0: rhs_generate(H, c_op_list, args=args, options=options) if isinstance( H, (types.FunctionType, types.BuiltinFunctionType, functools.partial)): H0 = H(0.0, args) elif isinstance(H, list): H0 = H[0][0] if isinstance(H[0], list) else H[0] else: H0 = H if len(c_op_list) == 0 and H0.isoper: # calculate propagator for the wave function N = H0.shape[0] dims = H0.dims u = np.zeros([N, N, len(tlist)], dtype=complex) if parallel: output = parallel_map(_parallel_sesolve, range(N), task_args=(N, H, tlist, args, options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N): for k, t in enumerate(tlist): u[:, n, k] = output[n].states[k].full().T else: progress_bar.start(N) for n in range(0, N): progress_bar.update(n) psi0 = basis(N, n) output = sesolve(H, psi0, tlist, [], args, options, _safe_mode=False) for k, t in enumerate(tlist): u[:, n, k] = output.states[k].full().T progress_bar.finished() # todo: evolving a batch of wave functions: # psi_0_list = [basis(N, n) for n in range(N)] # psi_t_list = mesolve(H, psi_0_list, [0, t], [], [], args, options) # for n in range(0, N): # u[:,n] = psi_t_list[n][1].full().T elif len(c_op_list) == 0 and H0.issuper: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) N = H0.shape[0] sqrt_N = int(np.sqrt(N)) dims = H0.dims u = np.zeros([N, N, len(tlist)], dtype=complex) if parallel: output = parallel_map(_parallel_mesolve, range(N * N), task_args=(sqrt_N, H, tlist, c_op_list, args, options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N * N): for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output[n].states[k].full()).T else: progress_bar.start(N) for n in range(0, N): progress_bar.update(n) col_idx, row_idx = np.unravel_index(n, (sqrt_N, sqrt_N)) rho0 = Qobj( sp.csr_matrix(([1], ([row_idx], [col_idx])), shape=(sqrt_N, sqrt_N), dtype=complex)) output = mesolve(H, rho0, tlist, [], [], args, options, _safe_mode=False) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T progress_bar.finished() else: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) N = H0.shape[0] dims = [H0.dims, H0.dims] u = np.zeros([N * N, N * N, len(tlist)], dtype=complex) if parallel: output = parallel_map(_parallel_mesolve, range(N * N), task_args=(N, H, tlist, c_op_list, args, options), progress_bar=progress_bar, num_cpus=num_cpus) for n in range(N * N): for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output[n].states[k].full()).T else: progress_bar.start(N * N) for n in range(N * N): progress_bar.update(n) col_idx, row_idx = np.unravel_index(n, (N, N)) rho0 = Qobj( sp.csr_matrix(([1], ([row_idx], [col_idx])), shape=(N, N), dtype=complex)) output = mesolve(H, rho0, tlist, c_op_list, [], args, options, _safe_mode=False) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T progress_bar.finished() if len(tlist) == 2: return Qobj(u[:, :, 1], dims=dims) else: return np.array( [Qobj(u[:, :, k], dims=dims) for k in range(len(tlist))], dtype=object)
def correlation_ss(H, taulist, c_ops, a_op, b_op, solver="me", reverse=False, args={}, options=Options(ntraj=[20, 100])): """ Calculate the two-operator two-time correlation function: .. math:: \lim_{t \\to \\infty} \left<A(t+\\tau)B(t)\\right> along one time axis (given steady-state initial conditions) using the quantum regression theorem and the evolution solver indicated by the `solver` parameter. Parameters ---------- H : Qobj system Hamiltonian. taulist : array_like list of times for :math:`\\tau`. taulist must be positive and contain the element `0`. c_ops : list list of collapse operators. a_op : Qobj operator A. b_op : Qobj operator B. reverse : *bool* If `True`, calculate :math:`\lim_{t \\to \\infty} \left<A(t)B(t+\\tau)\\right>` instead of :math:`\lim_{t \\to \\infty} \left<A(t+\\tau)B(t)\\right>`. solver : str choice of solver (`me` for master-equation and `es` for exponential series). options : Options solver options class. `ntraj` is taken as a two-element list because the `mc` correlator calls `mcsolve()` recursively; by default, `ntraj=[20, 100]`. `mc_corr_eps` prevents divide-by-zero errors in the `mc` correlator; by default, `mc_corr_eps=1e-10`. Returns ------- corr_vec : array An array of correlation values for the times specified by `tlist`. References ---------- See, Gardiner, Quantum Noise, Section 5.2. """ warn("correlation_ss() now legacy, please use correlation_2op_1t() with" + "initial state as None", FutureWarning) if debug: print(inspect.stack()[0][3]) return correlation_2op_1t(H, None, taulist, c_ops, a_op, b_op, solver=solver, reverse=reverse, args=args, options=options)
def sesolve(H, rho0, tlist, e_ops, args={}, options=None, progress_bar=BaseProgressBar()): """ 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 # 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() 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 correlation_4op_2t(H, state0, tlist, taulist, c_ops, a_op, b_op, c_op, d_op, solver="me", args={}, options=Options(ntraj=[20, 100])): """ Calculate the four-operator two-time correlation function: :math:`\left<A(t)B(t+\\tau)C(t+\\tau)D(t)\\right>` along two time axes using the quantum regression theorem and the evolution solver indicated by the `solver` parameter. Note: it is not possibly to calculate a physically meaningful correlation of this form where :math:`\\tau<0`. Parameters ---------- H : Qobj system Hamiltonian, may be time-dependent for solver choice of `me` or `mc`. rho0 : Qobj Initial state density matrix :math:`\\rho_0` or state vector :math:`\\psi_0`. If 'state0' is 'None', then the steady state will be used as the initial state. The 'steady-state' is only implemented for the `me` and `es` solvers. tlist : array_like list of times for :math:`t`. tlist must be positive and contain the element `0`. When taking steady-steady correlations only one tlist value is necessary, i.e. when :math:`t \\rightarrow \\infty`; here tlist is automatically set, ignoring user input. taulist : array_like list of times for :math:`\\tau`. taulist must be positive and contain the element `0`. c_ops : list list of collapse operators, may be time-dependent for solver choice of `me` or `mc`. a_op : Qobj operator A. b_op : Qobj operator B. c_op : Qobj operator C. d_op : Qobj operator D. solver : str choice of solver (`me` for master-equation, `mc` for Monte Carlo, and `es` for exponential series). options : Options solver options class. `ntraj` is taken as a two-element list because the `mc` correlator calls `mcsolve()` recursively; by default, `ntraj=[20, 100]`. `mc_corr_eps` prevents divide-by-zero errors in the `mc` correlator; by default, `mc_corr_eps=1e-10`. Returns ------- corr_mat : array An 2-dimensional array (matrix) of correlation values for the times specified by `tlist` (first index) and `taulist` (second index). If `tlist` is `None`, then a 1-dimensional array of correlation values is returned instead. References ---------- See, Gardiner, Quantum Noise, Section 5.2. """ warn("correlation_4op_2t() now legacy, please use correlation_3op_2t()", FutureWarning) warn("the reverse argument has been removed as it did not contain any" + "new physical information", DeprecationWarning) if debug: print(inspect.stack()[0][3]) return correlation_3op_2t(H, state0, tlist, taulist, c_ops, a_op, b_op * c_op, d_op, solver=solver, args=args, options=options)
def coherence_function_g2(H, taulist, c_ops, a_op, solver="me", args={}, options=Options(ntraj=[20, 100])): """ Calculate the normalized second-order quantum coherence function: .. math:: g^{(2)}(\\tau) = \lim_{t \to \infty} \\frac{\\langle a^\\dagger(t)a^\\dagger(t+\\tau) a(t+\\tau)a(t)\\rangle} {\\langle a^\\dagger(t)a(t)\\rangle^2} using the quantum regression theorem and the evolution solver indicated by the `solver` parameter. Note: g2 is only defined for stationary statistics (uses steady state rho0). Parameters ---------- H : :class:`qutip.qobj.Qobj` system Hamiltonian. taulist : *list* / *array* list of times for :math:`\\tau`. taulist must be positive and contain the element `0`. c_ops : list of :class:`qutip.qobj.Qobj` list of collapse operators. a_op : :class:`qutip.qobj.Qobj` The annihilation operator of the mode. solver : str choice of solver (`me` for master-equation and `es` for exponential series) options : :class:`qutip.solver.Options` solver options class. `ntraj` is taken as a two-element list because the `mc` correlator calls `mcsolve()` recursively; by default, `ntraj=[20, 100]`. `mc_corr_eps` prevents divide-by-zero errors in the `mc` correlator; by default, `mc_corr_eps=1e-10`. Returns ------- g2: *array* The normalized second-order coherence function. """ # first calculate the the steady state photon number rho0 = steadystate(H, c_ops) n = np.array([expect(rho0, a_op.dag() * a_op)]) # calculate the correlation function G2 and normalize with n to obtain g2 G2 = correlation_3op_1t(H, None, taulist, c_ops, a_op.dag(), a_op.dag() * a_op, a_op, solver=solver, args=args, options=options) g2 = G2 / n**2 return g2