Beispiel #1
0
    def __init__(
        self,
        H_sys,
        bath,
        max_depth,
        options=None,
        progress_bar=None,
    ):
        self.H_sys = self._convert_h_sys(H_sys)
        self.options = Options() if options is None else options
        self._is_timedep = isinstance(self.H_sys, QobjEvo)
        self._H0 = self.H_sys.to_list()[0] if self._is_timedep else self.H_sys
        self._is_hamiltonian = self._H0.type == "oper"
        self._L0 = liouvillian(self._H0) if self._is_hamiltonian else self._H0

        self._sys_shape = (self._H0.shape[0] if self._is_hamiltonian else int(
            np.sqrt(self._H0.shape[0])))
        self._sup_shape = self._L0.shape[0]
        self._sys_dims = (self._H0.dims
                          if self._is_hamiltonian else self._H0.dims[0])

        self.ados = HierarchyADOs(
            self._combine_bath_exponents(bath),
            max_depth,
        )
        self._n_ados = len(self.ados.labels)
        self._n_exponents = len(self.ados.exponents)

        # pre-calculate identity matrix required by _grad_n
        self._sId = fast_identity(self._sup_shape)

        # pre-calculate superoperators required by _grad_prev and _grad_next:
        Qs = [exp.Q for exp in self.ados.exponents]
        self._spreQ = [spre(op).data for op in Qs]
        self._spostQ = [spost(op).data for op in Qs]
        self._s_pre_minus_post_Q = [
            self._spreQ[k] - self._spostQ[k] for k in range(self._n_exponents)
        ]
        self._s_pre_plus_post_Q = [
            self._spreQ[k] + self._spostQ[k] for k in range(self._n_exponents)
        ]
        self._spreQdag = [spre(op.dag()).data for op in Qs]
        self._spostQdag = [spost(op.dag()).data for op in Qs]
        self._s_pre_minus_post_Qdag = [
            self._spreQdag[k] - self._spostQdag[k]
            for k in range(self._n_exponents)
        ]
        self._s_pre_plus_post_Qdag = [
            self._spreQdag[k] + self._spostQdag[k]
            for k in range(self._n_exponents)
        ]

        if progress_bar is None:
            self.progress_bar = BaseProgressBar()
        if progress_bar is True:
            self.progress_bar = TextProgressBar()

        self._configure_solver()
Beispiel #2
0
def serial_map(task, values, task_args=tuple(), task_kwargs={}, **kwargs):
    """
    Serial mapping function with the same call signature as parallel_map, for
    easy switching between serial and parallel execution. This
    is functionally equivalent to:

        result = [task(value, *task_args, **task_kwargs) for value in values]

    This function work as a drop-in replacement of :func:`qutip.parallel_map`.

    Parameters
    ----------

    task: a Python function
        The function that is to be called for each value in ``task_vec``.

    values: array / list
        The list or array of values for which the ``task`` function is to be
        evaluated.

    task_args: list / dictionary
        The optional additional argument to the ``task`` function.

    task_kwargs: list / dictionary
        The optional additional keyword argument to the ``task`` function.

    progress_bar: ProgressBar
        Progress bar class instance for showing progress.

    Returns
    --------
    result : list
        The result list contains the value of
        ``task(value, *task_args, **task_kwargs)`` for each
        value in ``values``.
    """
    try:
        progress_bar = kwargs['progress_bar']
        if progress_bar is True:
            progress_bar = TextProgressBar()
    except:
        progress_bar = BaseProgressBar()

    progress_bar.start(len(values))
    results = []
    for n, value in enumerate(values):
        progress_bar.update(n)
        result = task(value, *task_args, **task_kwargs)
        results.append(result)
    progress_bar.finished()

    return results
Beispiel #3
0
    def configure(self,
                  H_sys,
                  coup_op,
                  coup_strength,
                  temperature,
                  N_cut,
                  N_exp,
                  planck=None,
                  boltzmann=None,
                  renorm=None,
                  bnd_cut_approx=None,
                  options=None,
                  progress_bar=None,
                  stats=None):
        """
        Configure the solver using the passed parameters
        The parameters are described in the class attributes, unless there
        is some specific behaviour

        Parameters
        ----------
        options : :class:`qutip.solver.Options`
            Generic solver options.
            If set to None the default options will be used

        progress_bar: BaseProgressBar
            Optional instance of BaseProgressBar, or a subclass thereof, for
            showing the progress of the simulation.
            If set to None, then the default progress bar will be used
            Set to False for no progress bar

        stats: :class:`qutip.solver.Stats`
            Optional instance of solver.Stats, or a subclass thereof, for
            storing performance statistics for the solver
            If set to True, then the default Stats for this class will be used
            Set to False for no stats
        """

        self.H_sys = H_sys
        self.coup_op = coup_op
        self.coup_strength = coup_strength
        self.temperature = temperature
        self.N_cut = N_cut
        self.N_exp = N_exp
        if planck: self.planck = planck
        if boltzmann: self.boltzmann = boltzmann
        if isinstance(options, Options): self.options = options
        if isinstance(progress_bar, BaseProgressBar):
            self.progress_bar = progress_bar
        elif progress_bar == True:
            self.progress_bar = TextProgressBar()
        elif progress_bar == False:
            self.progress_bar = None
        if isinstance(stats, Stats):
            self.stats = stats
        elif stats == True:
            self.stats = self.create_new_stats()
        elif stats == False:
            self.stats = None
Beispiel #4
0
def freqCalc(rho0, frelist, tlist, target):
    wmw1 = 0
    wmw2 = 0
    wmw3 = 0

    rmat = np.zeros((len(frelist), len(tlist)))
    pbar = ProgressBar(len(frelist))
    c_ops = []

    if g1 > 0.0:
        c_ops.append(np.sqrt(g1) * sigmam())

    if g2 > 0.0:
        c_ops.append(np.sqrt(g2) * sigmaz())

    sx = sigmax()
    sz = sigmaz()
    idm = qeye(2)

    for i in range(len(frelist)):
        pbar.update(i)

        if target == 1:
            e_ops = [tensor(sz, idm, idm)]
            wmw1 = frelist[i]

        elif target == 2:
            e_ops = [tensor(idm, sz, idm)]
            wmw2 = frelist[i]

        else:
            e_ops = [tensor(idm, idm, sz)]
            wmw3 = frelist[i]

        H0 = (w01 - wmw1) / 2 * tensor(sz, idm, idm) + (
            w02 - wmw2) / 2 * tensor(idm, sz, idm) + (w03 - wmw3) / 2 * tensor(
                idm, idm, sz) + J12 * tensor(sz, sz, idm) + J23 * tensor(
                    idm, sz, sz)
        Hint = (Bac1 / 4) * tensor(sx, idm, idm) + (Bac2 / 4) * tensor(
            idm, sx, idm) + (Bac3 / 4) * tensor(idm, idm, sx)
        H = H0 + Hint

        output = mesolve(H, rho0, tlist, c_ops, e_ops, options=options)
        rmat[i] = output.expect[0]
    return rmat
Beispiel #5
0
def _check_progress_bar(progress_bar):
    """
    Check instance of progress_bar and return the object.
    """
    if progress_bar is None:
        pbar = BaseProgressBar()
    if progress_bar is True:
        pbar = TextProgressBar()
    return pbar
Beispiel #6
0
def calculate():
    rmat = np.zeros((len(frelist), len(tlist)))
    pbar = ProgressBar(len(frelist))
    c_ops = []

    if g1 > 0.0:
        c_ops.append(np.sqrt(g1) * sigmam())

    if g2 > 0.0:
        c_ops.append(np.sqrt(g2) * sigmaz())

    e_ops = [sigmax(), sigmay(), sigmaz()]

    for i in range(len(frelist)):
        pbar.update(i)
        wmw = frelist[i]
        args = {'wmw': wmw}
        #  H0=(w01/2.0)*tensor(sigmaz(),qeye(2))+(w02/2.0)*tensor(qeye(2),sigmaz())+(J12/2.0)*tensor(sigmaz(),sigmaz())
        #  H1=Bac*tensor(sigmax(),qeye(2))
        #  H=[H0,[H1,'np.cos(wmw*t)']]
        H0 = (w01 - wmw) / 2 * tensor(
            sigmaz(), qeye(2)) + (w02 - wmw) / 2 * tensor(
                qeye(2), sigmaz()) + J12 / 2 * tensor(sigmaz(), sigmaz())
        Hint = (Bac1 / 2) * tensor(sigmax(), qeye(2)) + (Bac2 / 2) * tensor(
            qeye(2), sigmax())
        H = H0 + Hint
        output = mesolve(H,
                         rho0,
                         tlist,
                         c_ops, [
                             tensor(sigmax(), qeye(2)),
                             tensor(sigmay(), qeye(2)),
                             tensor(sigmaz(), qeye(2))
                         ],
                         args,
                         options=options)
        rmat[i] = output.expect[2]
    return rmat
Beispiel #7
0
def freqFind(rho0):
    rmat=np.zeros((len(frelist),len(tlist)))
    pbar=ProgressBar(len(frelist))
    c_ops=[]

    if g1>0.0:
        c_ops.append(np.sqrt(g1)*sigmam())

    if g2>0.0:
        c_ops.append(np.sqrt(g2)*sigmaz())

    #  H0=(w01/2.0)*tensor(sigmaz(),qeye(2))+(w02/2.0)*tensor(qeye(2),sigmaz())+(J12/2.0)*tensor(sigmaz(),sigmaz())
    #  H1=Bac*tensor(sigmax(),qeye(2))
    #  H=[H0,[H1,'np.cos(wmw*t)']]
    for i in range(len(frelist)):
        pbar.update(i)
        wmw2=frelist[i]
        H0=(w01-wmw1)/2*tensor(sigmaz(),qeye(2))+(w02-wmw2)/2*tensor(qeye(2),sigmaz())+J12*tensor(sigmaz(),sigmaz())
        Hint=(Bac1/4)*tensor(sigmax(),qeye(2))+(Bac2/4)*tensor(qeye(2),sigmax())
        H=H0+Hint
        output=mesolve(H,rho0,tlist,c_ops,[tensor(qeye(2),sigmaz())],options=options)
        rmat[i]=output.expect[0]
    return rmat
    def test_create_progress_bar(self):
        Q = sigmaz()
        H = sigmax()
        bath = Bath([
            BathExponent("R", None, Q=Q, ck=1.1, vk=2.1),
        ])

        hsolver = HEOMSolver(H, bath, 2)
        assert isinstance(hsolver.progress_bar, BaseProgressBar)

        hsolver = HEOMSolver(H, bath, 2, progress_bar=True)
        assert isinstance(hsolver.progress_bar, TextProgressBar)

        hsolver = HEOMSolver(H, bath, 2, progress_bar=TextProgressBar())
        assert isinstance(hsolver.progress_bar, BaseProgressBar)

        with pytest.raises(TypeError):
            HEOMSolver(H, bath, 2, progress_bar=False)
Beispiel #9
0
    def __init__(self,
                 H_sys,
                 coup_op,
                 coup_strength,
                 temperature,
                 N_cut,
                 N_exp,
                 cut_freq,
                 planck=1.0,
                 boltzmann=1.0,
                 renorm=True,
                 bnd_cut_approx=True,
                 options=None,
                 progress_bar=None,
                 stats=None):

        self.reset()

        if options is None:
            self.options = Options()
        else:
            self.options = options

        self.progress_bar = False
        if progress_bar is None:
            self.progress_bar = BaseProgressBar()
        elif progress_bar == True:
            self.progress_bar = TextProgressBar()

        # the other attributes will be set in the configure method
        self.configure(H_sys,
                       coup_op,
                       coup_strength,
                       temperature,
                       N_cut,
                       N_exp,
                       cut_freq,
                       planck=planck,
                       boltzmann=boltzmann,
                       renorm=renorm,
                       bnd_cut_approx=bnd_cut_approx,
                       stats=stats)
def transmission_calc_array(queue):
    args = []
    for index, value in enumerate(queue.wd_points):
        args.append([value, queue.params[index]])
    steady_states = parallel_map(transmission_calc,
                                 args,
                                 num_cpus=10,
                                 progress_bar=TextProgressBar())
    transmissions = np.array(
        [steady_state[0] for steady_state in steady_states])
    edge_occupations_c = np.array(
        [steady_state[1] for steady_state in steady_states])
    edge_occupations_c = np.absolute(edge_occupations_c)
    edge_occupations_t = np.array(
        [steady_state[2] for steady_state in steady_states])
    edge_occupations_t = np.absolute(edge_occupations_t)
    results = Results(queue.params, queue.wd_points, transmissions,
                      edge_occupations_c, edge_occupations_t)
    abs_transmissions = np.absolute(transmissions)
    return results
Beispiel #11
0
if False :    
    "Calculates the coefficient of the hamiltonian time-dependant terms"
    def coeff_rot(t):
        return(np.exp(1j*2*delta*(t-T1)*(t>=T1 and t<=T2)))
        
    H_NOT=0*a
    #H=-1j*(a**2*eps_2-a.dag()**2*np.conjugate(eps_2))
    cops1=[k1**0.5*a]
    cops2_bis=convert_time_dependant_cops_2(k2**0.5*a**2,coeff_rot,-k2**0.5*alpha_inf_abs**2*Ia)
    cops_bis=cops1+cops2_bis
    cops_noNOT=[k1**0.5*a,k2**0.5*(a**2-alpha_inf_abs**2*Ia)]

    "Resolution of the equation over time with mesolve"
    init_state=C_alpha_plus#initial state
    tlist = np.linspace(0, T_final, n_t)
    res_NOT = qt.mesolve(H_NOT, init_state, tlist, cops, progress_bar=TextProgressBar())
    
    "Resolution of the equation without NOT" #to have the theoretical fidelity (no analytical formula)
    res_free= qt.mesolve(H_NOT, init_state, tlist, cops_noNOT,  progress_bar=TextProgressBar())
    
    
    #Wigner
    res_NOT_Wigner = compute_Wigner([-6, 6, 51], nbWignerPlot,nbCols, n_t,-1)
    res_NOT_Wigner.draw_Wigner(res_NOT.states, title='Simulations NOT')
    res_free_Wigner= compute_Wigner([-6,6, 51], nbWignerPlot,nbCols, n_t,-1)
    res_free_Wigner.draw_Wigner(res_free.states, title='Simulations without NOT')
    

  
#  
#    
    def coef_eps_conj(t, args):
        return (np.conjugate(coef_eps(t, args)))

    H_NOT = [[a**2, coef_eps], [a.dag()**2, coef_eps_conj]]
    #H=-1j*(a**2*eps_2-a.dag()**2*np.conjugate(eps_2))
    cops = [k1**0.5 * a, k2**0.5 * a**2]

    "Resolution of the equation over time with mesolve"
    init_state = qt.coherent(Na, alpha)  #initial state
    tlist = np.linspace(0, T_final, n_t)
    res_NOT = qt.mesolve(H_NOT,
                         init_state,
                         tlist,
                         cops,
                         progress_bar=TextProgressBar())

    ##Wigner
    res_NOT_Wigner = compute_Wigner([-6, 6, 51], nbWignerPlot, nbCols, n_t, -1)
    res_NOT_Wigner.draw_Wigner(res_NOT.states, title='Simulations with NOT')
    #    res_free_Wigner= compute_Wigner([-6,6, 51], nbWignerPlot,nbCols, n_t,-1)
    #    res_free_Wigner.draw_Wigner(res_free.states, title='Simulations without NOT')

    "Plot the evolution of fidelity over time"
    target_res = []  #to check the Wigner
    fidelity_NOT_list = []
    for ii, t in enumerate(tlist):
        if t <= T1:
            current_theta = 0
        elif (t > T1) and (t < T2):
            current_theta = delta * (t - T1)
Beispiel #13
0
    def run(self, num_traj=0, psi0=None, tlist=None,
            args={}, e_ops=None, options=None,
            progress_bar=True,
            map_func=parallel_map, map_kwargs={}):
        # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        # 4 situation for run:
        # - first run
        # - change parameters
        # - add  trajectories
        #       (self.add_traj)      Not Implemented
        # - continue from the last time and states
        #       (self.continue_runs) Not Implemented
        # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        options = options if options is not None else self.options

        if self.ran and tlist[0] == self.t:
            # psi0 is ignored since we restart from a
            # different states for each trajectories
            self.continue_runs(num_traj, tlist, args, e_ops, options,
                               progress_bar, map_func, map_kwargs)
            return

        if args and args != self.ss.args:
            self.ss.set_args(self.ss, args)
            self.reset()

        if e_ops and e_ops != self.e_ops:
            self.set_e_ops(e_ops)
            self.reset()

        if psi0 is not None and psi0 != self.psi0:
            self.psi0 = psi0
            self.reset()

        tlist = np.array(tlist)
        if tlist is not None and np.all(tlist != self.tlist):
            self.tlist = tlist
            self.reset()

        if self.ran:
            if options.store_states and self._psi_out[0].shape[0] == 1:
                self.reset()
            else:
                # if not reset here, add trajectories
                self.add_traj(num_traj, progress_bar, map_func, map_kwargs)
                return

        if not num_traj:
            num_traj = options.ntraj

        if options.num_cpus == 1 or num_traj == 1:
            map_func = serial_map

        if len(self.seeds) != num_traj:
            self.seed(num_traj, self.seeds)

        if not progress_bar:
            progress_bar = BaseProgressBar()
        elif progress_bar is True:
            progress_bar = TextProgressBar()

        # set arguments for input to monte carlo
        map_kwargs_ = {'progress_bar': progress_bar,
                      'num_cpus': options.num_cpus}
        map_kwargs_.update(map_kwargs)
        map_kwargs = map_kwargs_

        if self.e_ops is None:
            self.set_e_ops()

        if self.ss.type == "Diagonal":
            results = map_func(self._single_traj_diag, list(range(num_traj)), **map_kwargs)
        else:
            results = map_func(self._single_traj, list(range(num_traj)), **map_kwargs)

        self.t = self.tlist[-1]
        self.num_traj = num_traj
        self.ran = True

        for result in results:
            state_out, ss_out, expect, collapse = result
            self._psi_out.append(state_out)
            self._ss_out.append(ss_out)
            self._expect_out.append(expect)
            self._collapse.append(collapse)
        self._psi_out = np.stack(self._psi_out)
        self._ss_out = np.stack(self._ss_out)
def time_dependant_c_ops(t,args):
    return B**2-1/2*alpha*(A+alpha)+1/2*alpha*np.exp(2j*np.pi/T*t)*(A-alpha)


H=0*A

#H=-1j*(a**2*eps_2-a.dag()**2*np.conjugate(eps_2))
cops=[k2**0.5*(A**2-alpha**2),
      time_dependant_c_ops]

"Resolution of the equation over time with mesolve"
init_state= C_alpha_plus #initial state
n_t = 1001
T=np.pi /delta #total time of simulation
tlist = np.linspace(0, 2*T, n_t)
res = qt.mesolve(H, init_state, tlist, cops, progress_bar=TextProgressBar())

#Wigner
test_Wigner = compute_Wigner([-4, 4, 51], nbWignerPlot,nbCols, n_t,-1)
test_Wigner.draw_Wigner(res)

"Plot the evolution of fidelity over time"
fidelity_list=[]
for ii,t in enumerate(tlist):
    if t<T/2:
        fidelity_list.append(qt.fidelity(res.states[ii],C_alpha_plus))
    elif t>3*T/2:
        fidelity_list.append(qt.fidelity(res.states[ii],C_alpha_plus))
    else:
        theta=np.exp(-1j*delta*(t-T/2))
        C_alpha_plus_rot=qt.coherent(Na, alpha*theta)+qt.coherent(Na, -alpha*theta)
Beispiel #15
0
def sesolve(H,
            psi0,
            tlist,
            e_ops=[],
            args={},
            options=Options(),
            progress_bar=BaseProgressBar(),
            _safe_mode=True):
    """
    Schrodinger equation evolution of a state vector or unitary matrix
    for a given Hamiltonian.

    Evolve the state vector (`psi0`) using a given
    Hamiltonian (`H`), by integrating the set of ordinary differential
    equations that define the system. Alternatively evolve a unitary matrix in
    solving the Schrodinger operator equation.

    The output is either the state vector or unitary matrix at arbitrary points
    in time (`tlist`), or the expectation values of the supplied operators
    (`e_ops`). If e_ops is a callback function, it is invoked for each
    time in `tlist` with time and the state as arguments, and the function
    does not use any return values. e_ops cannot be used in conjunction
    with solving the Schrodinger operator equation

    Parameters
    ----------

    H : :class:`qutip.qobj`, :class:`qutip.qobjevo`, *list*, *callable*
        system Hamiltonian as a Qobj, list of Qobj and coefficient, QobjEvo,
        or a callback function for time-dependent Hamiltonians.
        list format and options can be found in QobjEvo's description.

    psi0 : :class:`qutip.qobj`
        initial state vector (ket)
        or initial unitary operator `psi0 = U`

    tlist : *list* / *array*
        list of times for :math:`t`.

    e_ops : list of :class:`qutip.qobj` / callback function
        single operator or list of operators for which to evaluate
        expectation values.
        For list operator evolution, the overlapse is computed:
            tr(e_ops[i].dag()*op(t))

    args : *dictionary*
        dictionary of parameters for time-dependent Hamiltonians

    options : :class:`qutip.Qdeoptions`
        with options for the ODE solver.

    progress_bar : BaseProgressBar
        Optional instance of BaseProgressBar, or a subclass thereof, for
        showing the progress of the simulation.

    Returns
    -------

    output: :class:`qutip.solver`

        An instance of the class :class:`qutip.solver`, which contains either
        an *array* of expectation values for the times specified by `tlist`, or
        an *array* or state vectors corresponding to the
        times in `tlist` [if `e_ops` is an empty list], or
        nothing if a callback function was given inplace of operators for
        which to calculate the expectation values.

    """
    if isinstance(e_ops, Qobj):
        e_ops = [e_ops]
    elif isinstance(e_ops, dict):
        e_ops_dict = e_ops
        e_ops = [e for e in e_ops.values()]
    else:
        e_ops_dict = None

    if progress_bar is True:
        progress_bar = TextProgressBar()

    if not (psi0.isket or psi0.isunitary):
        raise TypeError("The unitary solver requires psi0 to be"
                        " a ket as initial state"
                        " or a unitary as initial operator.")

    if options.rhs_reuse and not isinstance(H, SolverSystem):
        # TODO: deprecate when going to class based solver.
        if "sesolve" in solver_safe:
            # print(" ")
            H = solver_safe["sesolve"]
        else:
            pass
            # raise Exception("Could not find the Hamiltonian to reuse.")

    #check if should use OPENMP
    check_use_openmp(options)

    if isinstance(H, SolverSystem):
        ss = H
    elif isinstance(H, (list, Qobj, QobjEvo)):
        ss = _sesolve_QobjEvo(H, tlist, args, options)
    elif callable(H):
        ss = _sesolve_func_td(H, args, options)
    else:
        raise Exception("Invalid H type")

    func, ode_args = ss.makefunc(ss, psi0, args, options)

    if _safe_mode:
        v = psi0.full().ravel('F')
        func(0., v, *ode_args) + v

    res = _generic_ode_solve(func,
                             ode_args,
                             psi0,
                             tlist,
                             e_ops,
                             options,
                             progress_bar,
                             dims=psi0.dims)
    if e_ops_dict:
        res.expect = {
            e: res.expect[n]
            for n, e in enumerate(e_ops_dict.keys())
        }
    res.SolverSystem = ss
    return res
Beispiel #16
0
def sesolve(H, psi0, tlist, e_ops=[], args={}, options=None,
            progress_bar=None,
            _safe_mode=True):
    """
    Schrodinger equation evolution of a state vector or unitary matrix
    for a given Hamiltonian.

    Evolve the state vector (`psi0`) using a given
    Hamiltonian (`H`), by integrating the set of ordinary differential
    equations that define the system. Alternatively evolve a unitary matrix in
    solving the Schrodinger operator equation.

    The output is either the state vector or unitary matrix at arbitrary points
    in time (`tlist`), or the expectation values of the supplied operators
    (`e_ops`). If e_ops is a callback function, it is invoked for each
    time in `tlist` with time and the state as arguments, and the function
    does not use any return values. e_ops cannot be used in conjunction
    with solving the Schrodinger operator equation

    Parameters
    ----------

    H : :class:`qutip.qobj`
        system Hamiltonian, or a callback function for time-dependent
        Hamiltonians.

    psi0 : :class:`qutip.qobj`
        initial state vector (ket)
        or initial unitary operator `psi0 = U`

    tlist : *list* / *array*
        list of times for :math:`t`.

    e_ops : list of :class:`qutip.qobj` / callback function single
        single operator or list of operators for which to evaluate
        expectation values.
        Must be empty list operator evolution

    args : *dictionary*
        dictionary of parameters for time-dependent Hamiltonians

    options : :class:`qutip.Qdeoptions`
        with options for the ODE solver.

    progress_bar : BaseProgressBar
        Optional instance of BaseProgressBar, or a subclass thereof, for
        showing the progress of the simulation.

    Returns
    -------

    output: :class:`qutip.solver`

        An instance of the class :class:`qutip.solver`, which contains either
        an *array* of expectation values for the times specified by `tlist`, or
        an *array* or state vectors corresponding to the
        times in `tlist` [if `e_ops` is an empty list], or
        nothing if a callback function was given inplace of operators for
        which to calculate the expectation values.

    """
    # check initial state: must be a state vector


    if _safe_mode:
        if not isinstance(psi0, Qobj):
            raise TypeError("psi0 must be Qobj")
        if psi0.isket:
            pass
        elif psi0.isunitary:
            if not e_ops == []:
                raise TypeError("Must have e_ops = [] when initial condition"
                                " psi0 is a unitary operator.")
        else:
            raise TypeError("The unitary solver requires psi0 to be"
                            " a ket as initial state"
                            " or a unitary as initial operator.")
        _solver_safety_check(H, psi0, c_ops=[], e_ops=e_ops, args=args)


    if isinstance(e_ops, Qobj):
        e_ops = [e_ops]

    if isinstance(e_ops, dict):
        e_ops_dict = e_ops
        e_ops = [e for e in e_ops.values()]
    else:
        e_ops_dict = None

    if progress_bar is None:
        progress_bar = BaseProgressBar()
    elif progress_bar is True:
        progress_bar = TextProgressBar()

    # convert array based time-dependence to string format
    H, _, args = _td_wrap_array_str(H, [], args, tlist)
    # check for type (if any) of time-dependent inputs
    n_const, n_func, n_str = _td_format_check(H, [])

    if options is None:
        options = Options()

    if (not options.rhs_reuse) or (not config.tdfunc):
        # reset config time-dependence flags to default values
        config.reset()

    #check if should use OPENMP
    check_use_openmp(options)

    if n_func > 0:
        res = _sesolve_list_func_td(H, psi0, tlist, e_ops, args, options,
                                    progress_bar)

    elif n_str > 0:
        res = _sesolve_list_str_td(H, psi0, tlist, e_ops, args, options,
                                   progress_bar)

    elif isinstance(H, (types.FunctionType,
                        types.BuiltinFunctionType,
                        partial)):
        res = _sesolve_func_td(H, psi0, tlist, e_ops, args, options,
                               progress_bar)

    elif isinstance(H, Qobj):
        res = _sesolve_const(H, psi0, tlist, e_ops, args, options,
                             progress_bar)
    else:
        raise TypeError("Invalid Hamiltonian specification")

    if e_ops_dict:
        res.expect = {e: res.expect[n]
                      for n, e in enumerate(e_ops_dict.keys())}

    return res
Beispiel #17
0
def brmesolve(H,
              psi0,
              tlist,
              a_ops=[],
              e_ops=[],
              c_ops=[],
              args={},
              use_secular=True,
              tol=qset.atol,
              spectra_cb=None,
              options=None,
              progress_bar=None,
              _safe_mode=True):
    """
    Solves for the dynamics of a system using the Bloch-Redfield master equation,
    given an input Hamiltonian, Hermitian bath-coupling terms and their associated 
    spectrum functions, as well as possible Lindblad collapse operators.
              
    For time-independent systems, the Hamiltonian must be given as a Qobj,
    whereas the bath-coupling terms (a_ops), must be written as a nested list
    of operator - spectrum function pairs, where the frequency is specified by
    the `w` variable.
              
    *Example*

        a_ops = [[a+a.dag(),lambda w: 0.2*(w>=0)]] 
              
    For time-dependent systems, the Hamiltonian, a_ops, and Lindblad collapse
    operators (c_ops), can be specified in the QuTiP string-based time-dependent
    format.  For the a_op spectra, the frequency variable must be `w`, and the 
    string cannot contain any other variables other than the possibility of having
    a time-dependence through the time variable `t`:
              
              
    *Example*

        a_ops = [[a+a.dag(), '0.2*exp(-t)*(w>=0)']]

    
    Parameters
    ----------
    H : Qobj / list
        System Hamiltonian given as a Qobj or
        nested list in string-based format.

    psi0: Qobj
        Initial density matrix or state vector (ket).

    tlist : array_like
        List of times for evaluating evolution

    a_ops : list
        Nested list of Hermitian system operators that couple to 
        the bath degrees of freedom, along with their associated
        spectra.

    e_ops : list
        List of operators for which to evaluate expectation values.

    c_ops : list
        List of system collapse operators, or nested list in
        string-based format.

    args : dict (not implimented)
        Placeholder for future implementation, kept for API consistency.

    use_secular : bool {True}
        Use secular approximation when evaluating bath-coupling terms.
    
    tol : float {qutip.setttings.atol}
        Tolerance used for removing small values after 
        basis transformation.
              
    spectra_cb : list
        DEPRECIATED. Do not use.
    
    options : :class:`qutip.solver.Options`
        Options for the solver.
              
    progress_bar : BaseProgressBar
        Optional instance of BaseProgressBar, or a subclass thereof, for
        showing the progress of the simulation.

    Returns
    -------
    result: :class:`qutip.solver.Result`

        An instance of the class :class:`qutip.solver.Result`, which contains
        either an array of expectation values, for operators given in e_ops,
        or a list of states for the times specified by `tlist`.
    """
    if isinstance(c_ops, Qobj):
        c_ops = [c_ops]

    if isinstance(e_ops, Qobj):
        e_ops = [e_ops]

    if isinstance(e_ops, dict):
        e_ops_dict = e_ops
        e_ops = [e for e in e_ops.values()]
    else:
        e_ops_dict = None

    if not (spectra_cb is None):
        warnings.warn("The use of spectra_cb is depreciated.",
                      DeprecationWarning)
        _a_ops = []
        for kk, a in enumerate(a_ops):
            _a_ops.append([a, spectra_cb[kk]])
        a_ops = _a_ops

    if _safe_mode:
        _solver_safety_check(H, psi0, a_ops + c_ops, e_ops, args)

    # check for type (if any) of time-dependent inputs
    _, n_func, n_str = _td_format_check(H, a_ops + c_ops)

    if progress_bar is None:
        progress_bar = BaseProgressBar()
    elif progress_bar is True:
        progress_bar = TextProgressBar()

    if options is None:
        options = Options()

    if (not options.rhs_reuse) or (not config.tdfunc):
        # reset config collapse and time-dependence flags to default values
        config.reset()

    #check if should use OPENMP
    check_use_openmp(options)

    if n_str == 0:

        R, ekets = bloch_redfield_tensor(H,
                                         a_ops,
                                         spectra_cb=None,
                                         c_ops=c_ops)

        output = Result()
        output.solver = "brmesolve"
        output.times = tlist

        results = bloch_redfield_solve(R,
                                       ekets,
                                       psi0,
                                       tlist,
                                       e_ops,
                                       options,
                                       progress_bar=progress_bar)

        if e_ops:
            output.expect = results
        else:
            output.states = results

        return output

    elif n_str != 0 and n_func == 0:
        output = _td_brmesolve(H,
                               psi0,
                               tlist,
                               a_ops=a_ops,
                               e_ops=e_ops,
                               c_ops=c_ops,
                               use_secular=use_secular,
                               tol=tol,
                               options=options,
                               progress_bar=progress_bar,
                               _safe_mode=_safe_mode)

        return output

    else:
        raise Exception('Cannot mix func and str formats.')
Beispiel #18
0
def propagator(H,
               t,
               c_op_list=[],
               args={},
               options=None,
               unitary_mode='batch',
               parallel=False,
               progress_bar=None,
               _safe_mode=True,
               **kwargs):
    r"""
    Calculate the propagator U(t) for the density matrix or wave function such
    that :math:`\psi(t) = U(t)\psi(0)` or
    :math:`\rho_{\mathrm vec}(t) = U(t) \rho_{\mathrm vec}(0)`
    where :math:`\rho_{\mathrm vec}` is the vector representation of the
    density matrix.

    Parameters
    ----------
    H : qobj or list
        Hamiltonian as a Qobj instance of a nested list of Qobjs and
        coefficients in the list-string or list-function format for
        time-dependent Hamiltonians (see description in :func:`qutip.mesolve`).

    t : float or array-like
        Time or list of times for which to evaluate the propagator.

    c_op_list : list
        List of qobj collapse operators.

    args : list/array/dictionary
        Parameters to callback functions for time-dependent Hamiltonians and
        collapse operators.

    options : :class:`qutip.Options`
        with options for the ODE solver.

    unitary_mode = str ('batch', 'single')
        Solve all basis vectors simulaneously ('batch') or individually
        ('single').

    parallel : bool {False, True}
        Run the propagator in parallel mode. This will override the
        unitary_mode settings if set to True.

    progress_bar: BaseProgressBar
        Optional instance of BaseProgressBar, or a subclass thereof, for
        showing the progress of the simulation. By default no progress bar
        is used, and if set to True a TextProgressBar will be used.

    Returns
    -------
     a : qobj
        Instance representing the propagator :math:`U(t)`.

    """
    kw = _default_kwargs()
    if 'num_cpus' in kwargs:
        num_cpus = kwargs['num_cpus']
    else:
        num_cpus = kw['num_cpus']

    if progress_bar is None:
        progress_bar = BaseProgressBar()
    elif progress_bar is True:
        progress_bar = TextProgressBar()

    if options is None:
        options = Options()
        options.rhs_reuse = True
        rhs_clear()

    if isinstance(t, (int, float, np.integer, np.floating)):
        tlist = [0, t]
    else:
        tlist = t

    if _safe_mode:
        _solver_safety_check(H, None, c_ops=c_op_list, e_ops=[], args=args)

    td_type = _td_format_check(H, c_op_list, solver='me')

    if isinstance(
            H,
        (types.FunctionType, types.BuiltinFunctionType, functools.partial)):
        H0 = H(0.0, args)
        if unitary_mode == 'batch':
            # batch don't work with function Hamiltonian
            unitary_mode = 'single'
    elif isinstance(H, list):
        H0 = H[0][0] if isinstance(H[0], list) else H[0]
    else:
        H0 = H

    if len(c_op_list) == 0 and H0.isoper:
        # calculate propagator for the wave function

        N = H0.shape[0]
        dims = H0.dims

        if parallel:
            unitary_mode = 'single'
            u = np.zeros([N, N, len(tlist)], dtype=complex)
            output = parallel_map(_parallel_sesolve,
                                  range(N),
                                  task_args=(N, H, tlist, args, options),
                                  progress_bar=progress_bar,
                                  num_cpus=num_cpus)
            for n in range(N):
                for k, t in enumerate(tlist):
                    u[:, n, k] = output[n].states[k].full().T
        else:
            if unitary_mode == 'single':
                output = sesolve(H,
                                 qeye(dims[0]),
                                 tlist, [],
                                 args,
                                 options,
                                 _safe_mode=False)
                if len(tlist) == 2:
                    return output.states[-1]
                else:
                    return output.states

            elif unitary_mode == 'batch':
                u = np.zeros(len(tlist), dtype=object)
                _rows = np.array([(N + 1) * m for m in range(N)])
                _cols = np.zeros_like(_rows)
                _data = np.ones_like(_rows, dtype=complex)
                psi0 = Qobj(sp.coo_matrix((_data, (_rows, _cols))).tocsr())
                if td_type[1] > 0 or td_type[2] > 0:
                    H2 = []
                    for k in range(len(H)):
                        if isinstance(H[k], list):
                            H2.append([tensor(qeye(N), H[k][0]), H[k][1]])
                        else:
                            H2.append(tensor(qeye(N), H[k]))
                else:
                    H2 = tensor(qeye(N), H)
                options.normalize_output = False
                output = sesolve(H2,
                                 psi0,
                                 tlist, [],
                                 args=args,
                                 options=options,
                                 _safe_mode=False)
                for k, t in enumerate(tlist):
                    u[k] = sp_reshape(output.states[k].data, (N, N))
                    unit_row_norm(u[k].data, u[k].indptr, u[k].shape[0])
                    u[k] = u[k].T.tocsr()

            else:
                raise Exception('Invalid unitary mode.')

    elif len(c_op_list) == 0 and H0.issuper:
        # calculate the propagator for the vector representation of the
        # density matrix (a superoperator propagator)
        unitary_mode = 'single'
        N = H0.shape[0]
        sqrt_N = int(np.sqrt(N))
        dims = H0.dims

        u = np.zeros([N, N, len(tlist)], dtype=complex)

        if parallel:
            output = parallel_map(_parallel_mesolve,
                                  range(N * N),
                                  task_args=(sqrt_N, H, tlist, c_op_list, args,
                                             options),
                                  progress_bar=progress_bar,
                                  num_cpus=num_cpus)
            for n in range(N * N):
                for k, t in enumerate(tlist):
                    u[:, n, k] = mat2vec(output[n].states[k].full()).T
        else:
            rho0 = qeye(N, N)
            rho0.dims = [[sqrt_N, sqrt_N], [sqrt_N, sqrt_N]]
            output = mesolve(H,
                             psi0,
                             tlist, [],
                             args,
                             options,
                             _safe_mode=False)
            if len(tlist) == 2:
                return output.states[-1]
            else:
                return output.states

    else:
        # calculate the propagator for the vector representation of the
        # density matrix (a superoperator propagator)
        unitary_mode = 'single'
        N = H0.shape[0]
        dims = [H0.dims, H0.dims]

        u = np.zeros([N * N, N * N, len(tlist)], dtype=complex)

        if parallel:
            output = parallel_map(_parallel_mesolve,
                                  range(N * N),
                                  task_args=(N, H, tlist, c_op_list, args,
                                             options),
                                  progress_bar=progress_bar,
                                  num_cpus=num_cpus)
            for n in range(N * N):
                for k, t in enumerate(tlist):
                    u[:, n, k] = mat2vec(output[n].states[k].full()).T
        else:
            progress_bar.start(N * N)
            for n in range(N * N):
                progress_bar.update(n)
                col_idx, row_idx = np.unravel_index(n, (N, N))
                rho0 = Qobj(
                    sp.csr_matrix(([1], ([row_idx], [col_idx])),
                                  shape=(N, N),
                                  dtype=complex))
                output = mesolve(H,
                                 rho0,
                                 tlist,
                                 c_op_list, [],
                                 args,
                                 options,
                                 _safe_mode=False)
                for k, t in enumerate(tlist):
                    u[:, n, k] = mat2vec(output.states[k].full()).T
            progress_bar.finished()

    if len(tlist) == 2:
        if unitary_mode == 'batch':
            return Qobj(u[-1], dims=dims)
        else:
            return Qobj(u[:, :, 1], dims=dims)
    else:
        if unitary_mode == 'batch':
            return np.array([Qobj(u[k], dims=dims) for k in range(len(tlist))],
                            dtype=object)
        else:
            return np.array(
                [Qobj(u[:, :, k], dims=dims) for k in range(len(tlist))],
                dtype=object)
def parallel_map(
    task,
    values,
    task_args=None,
    task_kwargs=None,
    num_cpus=None,
    progress_bar=None,
):
    """Map function `task` onto `values`, in parallel.

    This function's interface is identical to
    :func:`qutip.parallel.parallel_map` as of QuTiP 4.5.0, but has the option
    of using :mod:`loky` as a backend (see :func:`set_parallelization`). It
    also eliminates internal threads, according to
    :obj:`USE_THREADPOOL_LIMITS`.
    """
    # TODO: if QuTiP's parallel_map catches up, we can remove this function,
    # and put QuTiP's parallel_map into __all__ to maintain krotov's interface.
    if task_args is None:
        task_args = ()
    if task_kwargs is None:
        task_kwargs = {}

    if num_cpus is None:
        num_cpus = multiprocessing.cpu_count()

    if progress_bar is None:
        progress_bar = BaseProgressBar()
    if progress_bar is True:
        progress_bar = TextProgressBar()

    progress_bar.start(len(values))
    nfinished = [0]

    def _update_progress_bar(x):
        nfinished[0] += 1
        progress_bar.update(nfinished[0])

    if USE_LOKY:
        Executor = LokyReusableExecutor
        if USE_THREADPOOL_LIMITS:
            Executor = partial(
                LokyReusableExecutor,
                initializer=_process_threadpool_limits_initializier,
            )
    else:
        Executor = ProcessPoolExecutor

    _threadpool_limits = _no_threadpool_limits
    if USE_THREADPOOL_LIMITS:
        _threadpool_limits = threadpool_limits

    with _threadpool_limits(limits=1):
        with Executor(max_workers=num_cpus) as executor:
            jobs = []
            try:
                for value in values:
                    args = (value, ) + tuple(task_args)
                    job = executor.submit(task, *args, **task_kwargs)
                    job.add_done_callback(_update_progress_bar)
                    jobs.append(job)
                res = [job.result() for job in jobs]
            except KeyboardInterrupt as e:
                raise e

    progress_bar.finished()
    return res
Beispiel #20
0
def sesolve(H, psi0, tlist, e_ops=None, args=None, options=None,
            progress_bar=None, _safe_mode=True):
    """
    Schrödinger equation evolution of a state vector or unitary matrix for a
    given Hamiltonian.

    Evolve the state vector (``psi0``) using a given Hamiltonian (``H``), by
    integrating the set of ordinary differential equations that define the
    system. Alternatively evolve a unitary matrix in solving the Schrodinger
    operator equation.

    The output is either the state vector or unitary matrix at arbitrary points
    in time (``tlist``), or the expectation values of the supplied operators
    (``e_ops``). If ``e_ops`` is a callback function, it is invoked for each
    time in ``tlist`` with time and the state as arguments, and the function
    does not use any return values. ``e_ops`` cannot be used in conjunction
    with solving the Schrodinger operator equation

    Parameters
    ----------

    H : :class:`~Qobj`, :class:`~QobjEvo`, list, or callable
        System Hamiltonian as a :obj:`~Qobj , list of :obj:`Qobj` and
        coefficient, :obj:`~QObjEvo`, or a callback function for time-dependent
        Hamiltonians.  List format and options can be found in QobjEvo's
        description.

    psi0 : :class:`~Qobj`
        Initial state vector (ket) or initial unitary operator ``psi0 = U``.

    tlist : array_like of float
        List of times for :math:`t`.

    e_ops : None / list / callback function, optional
        A list of operators as `Qobj` and/or callable functions (can be mixed)
        or a single callable function. For callable functions, they are called
        as ``f(t, state)`` and return the expectation value. A single
        callback's expectation value can be any type, but a callback part of a
        list must return a number as the expectation value. For operators, the
        result's expect will be computed by :func:`qutip.expect` when the state
        is a ``ket``. For operator evolution, the overlap is computed by: ::

            (e_ops[i].dag() * op(t)).tr()

    args : dict, optional
        Dictionary of scope parameters for time-dependent Hamiltonians.

    options : :obj:`~solver.Options`, optional
        Options for the ODE solver.

    progress_bar : :obj:`~BaseProgressBar`, optional
        Optional instance of :obj:`~BaseProgressBar`, or a subclass thereof,
        for showing the progress of the simulation.

    Returns
    -------

    output: :class:`~solver.Result`
        An instance of the class :class:`~solver.Result`, which contains either
        an array of expectation values for the times specified by ``tlist``, or
        an array or state vectors corresponding to the times in ``tlist`` (if
        ``e_ops`` is an empty list), or nothing if a callback function was
        given inplace of operators for which to calculate the expectation
        values.
    """
    if e_ops is None:
        e_ops = []
    if isinstance(e_ops, Qobj):
        e_ops = [e_ops]
    elif isinstance(e_ops, dict):
        e_ops_dict = e_ops
        e_ops = [e for e in e_ops.values()]
    else:
        e_ops_dict = None

    if progress_bar is None:
        progress_bar = BaseProgressBar()
    if progress_bar is True:
        progress_bar = TextProgressBar()

    if not (psi0.isket or psi0.isunitary):
        raise TypeError("The unitary solver requires psi0 to be"
                        " a ket as initial state"
                        " or a unitary as initial operator.")

    if options is None:
        options = Options()
    if options.rhs_reuse and not isinstance(H, SolverSystem):
        # TODO: deprecate when going to class based solver.
        if "sesolve" in solver_safe:
            H = solver_safe["sesolve"]

    if args is None:
        args = {}

    check_use_openmp(options)

    if isinstance(H, SolverSystem):
        ss = H
    elif isinstance(H, (list, Qobj, QobjEvo)):
        ss = _sesolve_QobjEvo(H, tlist, args, options)
    elif callable(H):
        ss = _sesolve_func_td(H, args, options)
    else:
        raise Exception("Invalid H type")

    func, ode_args = ss.makefunc(ss, psi0, args, e_ops, options)

    if _safe_mode:
        v = psi0.full().ravel('F')
        func(0., v, *ode_args) + v

    res = _generic_ode_solve(func, ode_args, psi0, tlist, e_ops, options,
                             progress_bar, dims=psi0.dims)
    if e_ops_dict:
        res.expect = {e: res.expect[n]
                      for n, e in enumerate(e_ops_dict.keys())}
    return res
Beispiel #21
0
def propagator(H,
               t,
               c_op_list,
               args=None,
               options=None,
               sparse=False,
               progress_bar=None):
    """
    Calculate the propagator U(t) for the density matrix or wave function such
    that :math:`\psi(t) = U(t)\psi(0)` or
    :math:`\\rho_{\mathrm vec}(t) = U(t) \\rho_{\mathrm vec}(0)`
    where :math:`\\rho_{\mathrm vec}` is the vector representation of the
    density matrix.

    Parameters
    ----------
    H : qobj or list
        Hamiltonian as a Qobj instance of a nested list of Qobjs and
        coefficients in the list-string or list-function format for
        time-dependent Hamiltonians (see description in :func:`qutip.mesolve`).

    t : float or array-like
        Time or list of times for which to evaluate the propagator.

    c_op_list : list
        List of qobj collapse operators.

    args : list/array/dictionary
        Parameters to callback functions for time-dependent Hamiltonians and
        collapse operators.

    options : :class:`qutip.Options`
        with options for the ODE solver.

    progress_bar: BaseProgressBar
        Optional instance of BaseProgressBar, or a subclass thereof, for
        showing the progress of the simulation. By default no progress bar
        is used, and if set to True a TextProgressBar will be used.

    Returns
    -------
     a : qobj
        Instance representing the propagator :math:`U(t)`.

    """

    if progress_bar is None:
        progress_bar = BaseProgressBar()
    elif progress_bar is True:
        progress_bar = TextProgressBar()

    if options is None:
        options = Options()
        options.rhs_reuse = True
        rhs_clear()

    if isinstance(t, (int, float, np.integer, np.floating)):
        tlist = [0, t]
    else:
        tlist = t

    if isinstance(
            H,
        (types.FunctionType, types.BuiltinFunctionType, functools.partial)):
        H0 = H(0.0, args)
    elif isinstance(H, list):
        H0 = H[0][0] if isinstance(H[0], list) else H[0]
    else:
        H0 = H

    if len(c_op_list) == 0 and H0.isoper:
        # calculate propagator for the wave function

        N = H0.shape[0]
        dims = H0.dims
        u = np.zeros([N, N, len(tlist)], dtype=complex)

        progress_bar.start(N)
        for n in range(0, N):
            progress_bar.update(n)
            psi0 = basis(N, n)
            output = sesolve(H, psi0, tlist, [], args, options)
            for k, t in enumerate(tlist):
                u[:, n, k] = output.states[k].full().T
        progress_bar.finished()

        # todo: evolving a batch of wave functions:
        # psi_0_list = [basis(N, n) for n in range(N)]
        # psi_t_list = mesolve(H, psi_0_list, [0, t], [], [], args, options)
        # for n in range(0, N):
        #    u[:,n] = psi_t_list[n][1].full().T

    elif len(c_op_list) == 0 and H0.issuper:
        # calculate the propagator for the vector representation of the
        # density matrix (a superoperator propagator)

        N = H0.shape[0]
        dims = H0.dims

        u = np.zeros([N, N, len(tlist)], dtype=complex)

        progress_bar.start(N)
        for n in range(0, N):
            progress_bar.update(n)
            psi0 = basis(N, n)
            rho0 = Qobj(vec2mat(psi0.full()))
            output = mesolve(H, rho0, tlist, [], [], args, options)
            for k, t in enumerate(tlist):
                u[:, n, k] = mat2vec(output.states[k].full()).T
        progress_bar.finished()

    else:
        # calculate the propagator for the vector representation of the
        # density matrix (a superoperator propagator)

        N = H0.shape[0]
        dims = [H0.dims, H0.dims]

        u = np.zeros([N * N, N * N, len(tlist)], dtype=complex)

        if sparse:
            progress_bar.start(N * N)
            for n in range(N * N):
                progress_bar.update(n)
                psi0 = basis(N * N, n)
                psi0.dims = [dims[0], 1]
                rho0 = vector_to_operator(psi0)
                output = mesolve(H, rho0, tlist, c_op_list, [], args, options)
                for k, t in enumerate(tlist):
                    u[:, n, k] = operator_to_vector(
                        output.states[k]).full(squeeze=True)
            progress_bar.finished()

        else:
            progress_bar.start(N * N)
            for n in range(N * N):
                progress_bar.update(n)
                psi0 = basis(N * N, n)
                rho0 = Qobj(vec2mat(psi0.full()))
                output = mesolve(H, rho0, tlist, c_op_list, [], args, options)
                for k, t in enumerate(tlist):
                    u[:, n, k] = mat2vec(output.states[k].full()).T
            progress_bar.finished()

    if len(tlist) == 2:
        return Qobj(u[:, :, 1], dims=dims)
    else:
        return [Qobj(u[:, :, k], dims=dims) for k in range(len(tlist))]
        return val**0.5
    
    def funcApiB(t, args=0):
        val = complex(i(t))
        return val**0.5
    
    A =k2**0.5*(b_tensor**2-1./2*alpha_inf_abs*(a_tensor+alpha_inf_abs*I_tensor))
    B =k2**0.5*(1./2*alpha_inf_abs*(a_tensor-alpha_inf_abs*I_tensor))
    
    cops = [k1**0.5*a_tensor,k1**0.5*b_tensor]
    cops.append(k2**0.5*(a_tensor**2-alpha_inf_abs**2*I_tensor)) #stabilization control cat
    
    cops.append([A, funcA])
    cops.append([B, funcB])
    cops.append([A+B, funcApB])
    cops.append([A+1j*B, funcApiB])
    
    init_state_C=C_alpha_plus
    control_on=True
    init_state_T=C_alpha_plus
    init_state_tensor=qt.tensor(init_state_C,init_state_T)#initial state
    
    tlist = np.linspace(0,T_final, n_t)
    res_CNOT = qt.mesolve(0*a_tensor, init_state_tensor, tlist, cops,progress_bar=TextProgressBar())#,progress_bar=TextProgressBar())
    
    #np.save(path+'tab_values',res_CNOT.states)
    # cannot save the data because it is an array of Quantum objects
    name='density_with_loss_k2_over_%d'%(ratio)  
    qt.qsave(res_CNOT,path+name)

Beispiel #23
0
    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
Beispiel #24
0
def parallel_map(task, values, task_args=tuple(), task_kwargs={}, **kwargs):
    """
    Parallel execution of a mapping of `values` to the function `task`. This
    is functionally equivalent to::

        result = [task(value, *task_args, **task_kwargs) for value in values]

    Parameters
    ----------
    task : a Python function
        The function that is to be called for each value in ``task_vec``.
    values : array / list
        The list or array of values for which the ``task`` function is to be
        evaluated.
    task_args : list / dictionary
        The optional additional argument to the ``task`` function.
    task_kwargs : list / dictionary
        The optional additional keyword argument to the ``task`` function.
    progress_bar : ProgressBar
        Progress bar class instance for showing progress.

    Returns
    --------
    result : list
        The result list contains the value of 
        ``task(value, *task_args, **task_kwargs)`` for 
        each value in ``values``.

    """
    kw = _default_kwargs()
    if 'num_cpus' in kwargs:
        kw['num_cpus'] = kwargs['num_cpus']

    try:
        progress_bar = kwargs['progress_bar']
        if progress_bar is True:
            progress_bar = TextProgressBar()
    except:
        progress_bar = BaseProgressBar()

    progress_bar.start(len(values))
    nfinished = [0]

    def _update_progress_bar(x):
        nfinished[0] += 1
        progress_bar.update(nfinished[0])

    try:
        pool = Pool(processes=kw['num_cpus'])

        async_res = [pool.apply_async(task, (value,) + task_args, task_kwargs,
                                      _update_progress_bar)
                     for value in values]

        while not all([ar.ready() for ar in async_res]):
            for ar in async_res:
                ar.wait(timeout=0.1)

        pool.terminate()
        pool.join()

    except KeyboardInterrupt as e:
        pool.terminate()
        pool.join()
        raise e

    progress_bar.finished()

    return [ar.get() for ar in async_res]
psi_plus = psi_plus/psi_plus.norm() # even cat /!\ normalisation

def dissipative_operator(t, args):
    return k2**0.5*(a**2-(alpha*np.exp(1j*np.pi*t*delta))**2)

H = 0*a

# see Wigner : it seems that the pumping is not rotating

# if one does not want to display wigners of the time evolution and 
# only evolution of average quantities, one can put a list of operator
# in the following list 
eops = [parity_a, 
        a,
        n_a,
        ]
c_ops = []

tlist = np.linspace(0, T_gate, n_t)
res = qt.mesolve(dissipative_operator, psi_plus, tlist, c_ops,progress_bar=TextProgressBar())

test_Wigner = compute_Wigner([-4, 4, 51], 10, n_t,-1)
test_Wigner.draw_Wigner(res)
number_t = []
for ii, t in enumerate(tlist):
    number_t.append(qt.expect(n_a, res.states[ii]))
#fig, ax = plt.subplots(3)
#plt.plot(tlist, number_t)
#ax[1].plot(tlist, na_t)
#ax[2].plot(tlist, np.real(a_t))
#ax[2].plot(tlist, np.imag(a_t), linestyle='dashed')
Beispiel #26
0
def bloch_redfield_solve(R,
                         ekets,
                         rho0,
                         tlist,
                         e_ops=[],
                         options=None,
                         progress_bar=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()

    if progress_bar is None:
        progress_bar = BaseProgressBar()
    elif progress_bar is True:
        progress_bar = TextProgressBar()

    #
    # 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:
        if e_eb.isherm:
            result_list.append(np.zeros(n_tsteps, dtype=float))
        else:
            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)
    progress_bar.start(n_tsteps)
    for t_idx, _ in enumerate(tlist):
        progress_bar.update(t_idx)
        if not r.successful():
            break

        rho_eb.data = dense2D_to_fastcsr_fmode(vec2mat(r.y), rho0.shape[0],
                                               rho0.shape[1])

        # 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])
    progress_bar.finished()
    return result_list
Beispiel #27
0
class HEOMSolver:
    """
    HEOM solver that supports multiple baths.

    The baths must be all either bosonic or fermionic baths.

    Parameters
    ----------
    H_sys : QObj, QobjEvo or a list
        The system Hamiltonian or Liouvillian specified as either a
        :obj:`Qobj`, a :obj:`QobjEvo`, or a list of elements that may
        be converted to a :obj:`ObjEvo`.

    bath : Bath or list of Bath
        A :obj:`Bath` containing the exponents of the expansion of the
        bath correlation funcion and their associated coefficients
        and coupling operators, or a list of baths.

        If multiple baths are given, they must all be either fermionic
        or bosonic baths.

    max_depth : int
        The maximum depth of the heirarchy (i.e. the maximum number of bath
        exponent "excitations" to retain).

    options : :class:`qutip.solver.Options`
        Generic solver options. If set to None the default options will be
        used.

    progress_bar : None, True or :class:`BaseProgressBar`
        Optional instance of BaseProgressBar, or a subclass thereof, for
        showing the progress of the solver. If True, an instance of
        :class:`TextProgressBar` is used instead.

    Attributes
    ----------
    ados : :obj:`HierarchyADOs`
        The description of the hierarchy constructed from the given bath
        and maximum depth.
    """
    def __init__(
        self,
        H_sys,
        bath,
        max_depth,
        options=None,
        progress_bar=None,
    ):
        self.H_sys = self._convert_h_sys(H_sys)
        self.options = Options() if options is None else options
        self._is_timedep = isinstance(self.H_sys, QobjEvo)
        self._H0 = self.H_sys.to_list()[0] if self._is_timedep else self.H_sys
        self._is_hamiltonian = self._H0.type == "oper"
        self._L0 = liouvillian(self._H0) if self._is_hamiltonian else self._H0

        self._sys_shape = (self._H0.shape[0] if self._is_hamiltonian else int(
            np.sqrt(self._H0.shape[0])))
        self._sup_shape = self._L0.shape[0]
        self._sys_dims = (self._H0.dims
                          if self._is_hamiltonian else self._H0.dims[0])

        self.ados = HierarchyADOs(
            self._combine_bath_exponents(bath),
            max_depth,
        )
        self._n_ados = len(self.ados.labels)
        self._n_exponents = len(self.ados.exponents)

        # pre-calculate identity matrix required by _grad_n
        self._sId = fast_identity(self._sup_shape)

        # pre-calculate superoperators required by _grad_prev and _grad_next:
        Qs = [exp.Q for exp in self.ados.exponents]
        self._spreQ = [spre(op).data for op in Qs]
        self._spostQ = [spost(op).data for op in Qs]
        self._s_pre_minus_post_Q = [
            self._spreQ[k] - self._spostQ[k] for k in range(self._n_exponents)
        ]
        self._s_pre_plus_post_Q = [
            self._spreQ[k] + self._spostQ[k] for k in range(self._n_exponents)
        ]
        self._spreQdag = [spre(op.dag()).data for op in Qs]
        self._spostQdag = [spost(op.dag()).data for op in Qs]
        self._s_pre_minus_post_Qdag = [
            self._spreQdag[k] - self._spostQdag[k]
            for k in range(self._n_exponents)
        ]
        self._s_pre_plus_post_Qdag = [
            self._spreQdag[k] + self._spostQdag[k]
            for k in range(self._n_exponents)
        ]

        if progress_bar is None:
            self.progress_bar = BaseProgressBar()
        if progress_bar is True:
            self.progress_bar = TextProgressBar()

        self._configure_solver()

    def _convert_h_sys(self, H_sys):
        """ Process input system Hamiltonian, converting and raising as needed.
        """
        if isinstance(H_sys, (Qobj, QobjEvo)):
            pass
        elif isinstance(H_sys, list):
            try:
                H_sys = QobjEvo(H_sys)
            except Exception as err:
                raise ValueError(
                    "Hamiltonian (H_sys) of type list cannot be converted to"
                    " QObjEvo") from err
        else:
            raise TypeError(
                f"Hamiltonian (H_sys) has unsupported type: {type(H_sys)!r}")
        return H_sys

    def _combine_bath_exponents(self, bath):
        """ Combine the exponents for the specified baths. """
        if not isinstance(bath, (list, tuple)):
            exponents = bath.exponents
        else:
            exponents = []
            for b in bath:
                exponents.extend(b.exponents)
        all_bosonic = all(exp.type in (exp.types.R, exp.types.I, exp.types.RI)
                          for exp in exponents)
        all_fermionic = all(exp.type in (exp.types["+"], exp.types["-"])
                            for exp in exponents)
        if not (all_bosonic or all_fermionic):
            raise ValueError(
                "Bath exponents are currently restricted to being either"
                " all bosonic or all fermionic, but a mixture of bath"
                " exponents was given.")
        if not all(exp.Q.dims == exponents[0].Q.dims for exp in exponents):
            raise ValueError(
                "All bath exponents must have system coupling operators"
                " with the same dimensions but a mixture of dimensions"
                " was given.")
        return exponents

    def _dsuper_list_td(self, t, y, L_list):
        """ Auxiliary function for the time-dependent integration. Called every
            time step.
        """
        L = L_list[0][0]
        for n in range(1, len(L_list)):
            L = L + L_list[n][0] * L_list[n][1](t)
        return L * y

    def _grad_n(self, L, he_n):
        """ Get the gradient for the hierarchy ADO at level n. """
        vk = self.ados.vk
        vk_sum = sum(he_n[i] * vk[i] for i in range(len(vk)))
        op = L - vk_sum * self._sId
        return op

    def _grad_prev(self, he_n, k):
        """ Get the previous gradient. """
        if self.ados.exponents[k].type in (BathExponent.types.R,
                                           BathExponent.types.I,
                                           BathExponent.types.RI):
            return self._grad_prev_bosonic(he_n, k)
        elif self.ados.exponents[k].type in (BathExponent.types["+"],
                                             BathExponent.types["-"]):
            return self._grad_prev_fermionic(he_n, k)
        else:
            raise ValueError(
                f"Mode {k} has unsupported type {self.ados.exponents[k].type}")

    def _grad_prev_bosonic(self, he_n, k):
        if self.ados.exponents[k].type == BathExponent.types.R:
            op = (-1j * he_n[k] *
                  self.ados.ck[k]) * (self._s_pre_minus_post_Q[k])
        elif self.ados.exponents[k].type == BathExponent.types.I:
            op = (-1j * he_n[k] * 1j *
                  self.ados.ck[k]) * (self._s_pre_plus_post_Q[k])
        elif self.ados.exponents[k].type == BathExponent.types.RI:
            term1 = (he_n[k] * -1j *
                     self.ados.ck[k]) * (self._s_pre_minus_post_Q[k])
            term2 = (he_n[k] * self.ados.ck2[k]) * self._s_pre_plus_post_Q[k]
            op = term1 + term2
        else:
            raise ValueError(f"Unsupported type {self.ados.exponents[k].type}"
                             f" for exponent {k}")
        return op

    def _grad_prev_fermionic(self, he_n, k):
        ck = self.ados.ck

        n_excite = sum(he_n)
        sign1 = (-1)**(n_excite + 1)

        n_excite_before_m = sum(he_n[:k])
        sign2 = (-1)**(n_excite_before_m)

        sigma_bar_k = k + self.ados.sigma_bar_k_offset[k]

        if self.ados.exponents[k].type == BathExponent.types["+"]:
            op = -1j * sign2 * (
                (ck[k] * self._spreQdag[k]) -
                (sign1 * np.conj(ck[sigma_bar_k]) * self._spostQdag[k]))
        elif self.ados.exponents[k].type == BathExponent.types["-"]:
            op = -1j * sign2 * (
                (ck[k] * self._spreQ[k]) -
                (sign1 * np.conj(ck[sigma_bar_k]) * self._spostQ[k]))
        else:
            raise ValueError(f"Unsupported type {self.ados.exponents[k].type}"
                             f" for exponent {k}")
        return op

    def _grad_next(self, he_n, k):
        """ Get the previous gradient. """
        if self.ados.exponents[k].type in (BathExponent.types.R,
                                           BathExponent.types.I,
                                           BathExponent.types.RI):
            return self._grad_next_bosonic(he_n, k)
        elif self.ados.exponents[k].type in (BathExponent.types["+"],
                                             BathExponent.types["-"]):
            return self._grad_next_fermionic(he_n, k)
        else:
            raise ValueError(
                f"Mode {k} has unsupported type {self.ados.exponents[k].type}")

    def _grad_next_bosonic(self, he_n, k):
        op = -1j * self._s_pre_minus_post_Q[k]
        return op

    def _grad_next_fermionic(self, he_n, k):
        n_excite = sum(he_n)
        sign1 = (-1)**(n_excite + 1)

        n_excite_before_m = sum(he_n[:k])
        sign2 = (-1)**(n_excite_before_m)

        if self.ados.exponents[k].type == BathExponent.types["+"]:
            if sign1 == -1:
                op = (-1j * sign2) * self._s_pre_minus_post_Q[k]
            else:
                op = (-1j * sign2) * self._s_pre_plus_post_Q[k]
        elif self.ados.exponents[k].type == BathExponent.types["-"]:
            if sign1 == -1:
                op = (-1j * sign2) * self._s_pre_minus_post_Qdag[k]
            else:
                op = (-1j * sign2) * self._s_pre_plus_post_Qdag[k]
        else:
            raise ValueError(f"Unsupported type {self.ados.exponents[k].type}"
                             f" for exponent {k}")
        return op

    def _rhs(self, L):
        """ Make the RHS for the HEOM. """
        ops = _GatherHEOMRHS(self.ados.idx, block=L.shape[0], nhe=self._n_ados)

        for he_n in self.ados.labels:
            op = self._grad_n(L, he_n)
            ops.add_op(he_n, he_n, op)
            for k in range(len(self.ados.dims)):
                next_he = self.ados.next(he_n, k)
                if next_he is not None:
                    op = self._grad_next(he_n, k)
                    ops.add_op(he_n, next_he, op)
                prev_he = self.ados.prev(he_n, k)
                if prev_he is not None:
                    op = self._grad_prev(he_n, k)
                    ops.add_op(he_n, prev_he, op)

        return ops.gather()

    def _configure_solver(self):
        """ Set up the solver. """
        RHSmat = self._rhs(self._L0.data)
        assert isinstance(RHSmat, sp.csr_matrix)

        if self._is_timedep:
            # In the time dependent case, we construct the parameters
            # for the ODE gradient function _dsuper_list_td under the
            # assumption that RHSmat(t) = RHSmat + time dependent terms
            # that only affect the diagonal blocks of the RHS matrix.
            # This assumption holds because only _grad_n dependents on
            # the system Liovillian (and not _grad_prev or _grad_next).

            h_identity_mat = sp.identity(self._n_ados, format="csr")
            H_list = self.H_sys.to_list()

            solver_params = [[RHSmat]]
            for idx in range(1, len(H_list)):
                temp_mat = sp.kron(h_identity_mat, liouvillian(H_list[idx][0]))
                solver_params.append([temp_mat, H_list[idx][1]])

            solver = scipy.integrate.ode(self._dsuper_list_td)
            solver.set_f_params(solver_params)
        else:
            solver = scipy.integrate.ode(cy_ode_rhs)
            solver.set_f_params(RHSmat.data, RHSmat.indices, RHSmat.indptr)

        solver.set_integrator(
            "zvode",
            method=self.options.method,
            order=self.options.order,
            atol=self.options.atol,
            rtol=self.options.rtol,
            nsteps=self.options.nsteps,
            first_step=self.options.first_step,
            min_step=self.options.min_step,
            max_step=self.options.max_step,
        )

        self._ode = solver
        self.RHSmat = RHSmat

    def steady_state(self,
                     use_mkl=True,
                     mkl_max_iter_refine=100,
                     mkl_weighted_matching=False):
        """
        Compute the steady state of the system.

        Parameters
        ----------
        use_mkl : bool, default=False
            Whether to use mkl or not. If mkl is not installed or if
            this is false, use the scipy splu solver instead.

        mkl_max_iter_refine : int
            Specifies the the maximum number of iterative refinement steps that
            the MKL PARDISO solver performs.

            For a complete description, see iparm(8) in
            http://cali2.unilim.fr/intel-xe/mkl/mklman/GUID-264E311E-ACED-4D56-AC31-E9D3B11D1CBF.htm.

        mkl_weighted_matching : bool
            MKL PARDISO can use a maximum weighted matching algorithm to
            permute large elements close the diagonal. This strategy adds an
            additional level of reliability to the factorization methods.

            For a complete description, see iparm(13) in
            http://cali2.unilim.fr/intel-xe/mkl/mklman/GUID-264E311E-ACED-4D56-AC31-E9D3B11D1CBF.htm.

        Returns
        -------
        steady_state : Qobj
            The steady state density matrix of the system.

        steady_ados : :class:`HierarchyADOsState`
            The steady state of the full ADO hierarchy. A particular ADO may be
            extracted from the full state by calling :meth:`.extract`.
        """
        n = self._sys_shape

        b_mat = np.zeros(n**2 * self._n_ados, dtype=complex)
        b_mat[0] = 1.0

        L = deepcopy(self.RHSmat)
        L = L.tolil()
        L[0, 0:n**2 * self._n_ados] = 0.0
        L = L.tocsr()
        L += sp.csr_matrix(
            (np.ones(n), (np.zeros(n), [num * (n + 1) for num in range(n)])),
            shape=(n**2 * self._n_ados, n**2 * self._n_ados))

        if mkl_spsolve is not None and use_mkl:
            L.sort_indices()
            solution = mkl_spsolve(
                L,
                b_mat,
                perm=None,
                verbose=False,
                max_iter_refine=mkl_max_iter_refine,
                scaling_vectors=True,
                weighted_matching=mkl_weighted_matching,
            )
        else:
            L = L.tocsc()
            solution = spsolve(L, b_mat)

        data = dense2D_to_fastcsr_fmode(vec2mat(solution[:n**2]), n, n)
        data = 0.5 * (data + data.H)
        steady_state = Qobj(data, dims=self._sys_dims)

        solution = solution.reshape((self._n_ados, n, n))
        steady_ados = HierarchyADOsState(steady_state, self.ados, solution)

        return steady_state, steady_ados

    def _convert_e_ops(self, e_ops):
        """
        Parse and convert a dictionary or list of e_ops.

        Returns
        -------
        e_ops, expected : tuple
            If the input ``e_ops`` was a list or scalar, ``expected`` is a list
            with one item for each element of the original e_ops.

            If the input ``e_ops`` was a dictionary, ``expected`` is a
            dictionary with the same keys.

            The output ``e_ops`` is always a dictionary. Its keys are the
            keys or indexes for ``expected`` and its elements are the e_ops
            functions or callables.
        """
        if isinstance(e_ops, (list, dict)):
            pass
        elif e_ops is None:
            e_ops = []
        elif isinstance(e_ops, Qobj):
            e_ops = [e_ops]
        elif callable(e_ops):
            e_ops = [e_ops]
        else:
            try:
                e_ops = list(e_ops)
            except Exception as err:
                raise TypeError(
                    "e_ops must be an iterable, Qobj or function") from err

        if isinstance(e_ops, dict):
            expected = {k: [] for k in e_ops}
        else:
            expected = [[] for _ in e_ops]
            e_ops = {i: op for i, op in enumerate(e_ops)}

        if not all(
                callable(op) or isinstance(op, Qobj) for op in e_ops.values()):
            raise TypeError("e_ops must only contain Qobj or functions")

        return e_ops, expected

    def run(self, rho0, tlist, e_ops=None, ado_init=False, ado_return=False):
        """
        Solve for the time evolution of the system.

        Parameters
        ----------
        rho0 : Qobj or HierarchyADOsState or numpy.array
            Initial state (:obj:`~Qobj` density matrix) of the system
            if ``ado_init`` is ``False``.

            If ``ado_init`` is ``True``, then ``rho0`` should be an
            instance of :obj:`~HierarchyADOsState` or a numpy array
            giving the initial state of all ADOs. Usually
            the state of the ADOs would be determine from a previous call
            to ``.run(..., ado_return=True)``. For example,
            ``result = solver.run(..., ado_return=True)`` could be followed
            by ``solver.run(result.ado_states[-1], tlist, ado_init=True)``.

            If a numpy array is passed its shape must be
            ``(number_of_ados, n, n)`` where ``(n, n)`` is the system shape
            (i.e. shape of the system density matrix) and the ADOs must be
            in the same order as in ``.ados.labels``.

        tlist : list
            An ordered list of times at which to return the value of the state.

        e_ops : Qobj / callable / list / dict / None, optional
            A list or dictionary of operators as `Qobj` and/or callable
            functions (they can be mixed) or a single operator or callable
            function. For an operator ``op``, the result will be computed
            using ``(state * op).tr()`` and the state at each time ``t``. For
            callable functions, ``f``, the result is computed using
            ``f(t, ado_state)``. The values are stored in ``expect`` on
            (see the return section below).

        ado_init: bool, default False
            Indicates if initial condition is just the system state, or a
            numpy array including all ADOs.

        ado_return: bool, default True
            Whether to also return as output the full state of all ADOs.

        Returns
        -------
        :class:`qutip.solver.Result`
            The results of the simulation run, with the following attributes:

            * ``times``: the times ``t`` (i.e. the ``tlist``).

            * ``states``: the system state at each time ``t`` (only available
              if ``e_ops`` was ``None`` or if the solver option
              ``store_states`` was set to ``True``).

            * ``ado_states``: the full ADO state at each time (only available
              if ``ado_return`` was set to ``True``). Each element is an
              instance of :class:`HierarchyADOsState`.            .
              The state of a particular ADO may be extracted from
              ``result.ado_states[i]`` by calling :meth:`.extract`.

            * ``expect``: the value of each ``e_ops`` at time ``t`` (only
              available if ``e_ops`` were given). If ``e_ops`` was passed
              as a dictionary, then ``expect`` will be a dictionary with
              the same keys as ``e_ops`` and values giving the list of
              outcomes for the corresponding key.
        """
        e_ops, expected = self._convert_e_ops(e_ops)
        e_ops_callables = any(not isinstance(op, Qobj)
                              for op in e_ops.values())

        n = self._sys_shape
        rho_shape = (n, n)
        rho_dims = self._sys_dims
        hierarchy_shape = (self._n_ados, n, n)

        output = Result()
        output.solver = "HEOMSolver"
        output.times = tlist
        if e_ops:
            output.expect = expected
        if not e_ops or self.options.store_states:
            output.states = []

        if ado_init:
            if isinstance(rho0, HierarchyADOsState):
                rho0_he = rho0._ado_state
            else:
                rho0_he = rho0
            if rho0_he.shape != hierarchy_shape:
                raise ValueError(
                    f"ADOs passed with ado_init have shape {rho0_he.shape}"
                    f"but the solver hierarchy shape is {hierarchy_shape}")
            rho0_he = rho0_he.reshape(n**2 * self._n_ados)
        else:
            rho0_he = np.zeros([n**2 * self._n_ados], dtype=complex)
            rho0_he[:n**2] = rho0.full().ravel('F')

        if ado_return:
            output.ado_states = []

        solver = self._ode
        solver.set_initial_value(rho0_he, tlist[0])

        self.progress_bar.start(len(tlist))
        for t_idx, t in enumerate(tlist):
            self.progress_bar.update(t_idx)
            if t_idx != 0:
                solver.integrate(t)
                if not solver.successful():
                    raise RuntimeError(
                        "HEOMSolver ODE integration error. Try increasing"
                        " the nsteps given in the HEOMSolver options"
                        " (which increases the allowed substeps in each"
                        " step between times given in tlist).")

            rho = Qobj(
                solver.y[:n**2].reshape(rho_shape, order='F'),
                dims=rho_dims,
            )
            if self.options.store_states:
                output.states.append(rho)
            if ado_return or e_ops_callables:
                ado_state = HierarchyADOsState(
                    rho, self.ados, solver.y.reshape(hierarchy_shape))
            if ado_return:
                output.ado_states.append(ado_state)
            for e_key, e_op in e_ops.items():
                if isinstance(e_op, Qobj):
                    e_result = (rho * e_op).tr()
                else:
                    e_result = e_op(t, ado_state)
                output.expect[e_key].append(e_result)

        self.progress_bar.finished()
        return output
Beispiel #28
0
def mesolve(H,
            rho0,
            tlist,
            c_ops=None,
            e_ops=None,
            args=None,
            options=None,
            progress_bar=None,
            _safe_mode=True):
    """
    Master equation evolution of a density matrix for a given Hamiltonian and
    set of collapse operators, or a Liouvillian.

    Evolve the state vector or density matrix (`rho0`) using a given
    Hamiltonian or Liouvillian (`H`) and an optional set of collapse operators
    (`c_ops`), by integrating the set of ordinary differential equations
    that define the system. In the absence of collapse operators the system is
    evolved according to the unitary evolution of the Hamiltonian.

    The output is either the state vector at arbitrary points in time
    (`tlist`), or the expectation values of the supplied operators
    (`e_ops`). If e_ops is a callback function, it is invoked for each
    time in `tlist` with time and the state as arguments, and the function
    does not use any return values.

    If either `H` or the Qobj elements in `c_ops` are superoperators, they
    will be treated as direct contributions to the total system Liouvillian.
    This allows the solution of master equations that are not in standard
    Lindblad form.

    **Time-dependent operators**

    For time-dependent problems, `H` and `c_ops` can be a specified in a
    nested-list format where each element in the list is a list of length 2,
    containing an operator (:class:`qutip.qobj`) at the first element and where
    the second element is either a string (*list string format*), a callback
    function (*list callback format*) that evaluates to the time-dependent
    coefficient for the corresponding operator, or a NumPy array (*list
    array format*) which specifies the value of the coefficient to the
    corresponding operator for each value of t in `tlist`.

    Alternatively, `H` (but not `c_ops`) can be a callback function with the
    signature `f(t, args) -> Qobj` (*callback format*), which can return the
    Hamiltonian or Liouvillian superoperator at any point in time.  If the
    equation cannot be put in standard Lindblad form, then this time-dependence
    format must be used.

    *Examples*

        H = [[H0, 'sin(w*t)'], [H1, 'sin(2*w*t)']]

        H = [[H0, f0_t], [H1, f1_t]]

        where f0_t and f1_t are python functions with signature f_t(t, args).

        H = [[H0, np.sin(w*tlist)], [H1, np.sin(2*w*tlist)]]

    In the *list string format* and *list callback format*, the string
    expression and the callback function must evaluate to a real or complex
    number (coefficient for the corresponding operator).

    In all cases of time-dependent operators, `args` is a dictionary of
    parameters that is used when evaluating operators. It is passed to the
    callback functions as their second argument.

    **Additional options**

    Additional options to mesolve can be set via the `options` argument, which
    should be an instance of :class:`qutip.solver.Options`. Many ODE
    integration options can be set this way, and the `store_states` and
    `store_final_state` options can be used to store states even though
    expectation values are requested via the `e_ops` argument.

    .. note::

        If an element in the list-specification of the Hamiltonian or
        the list of collapse operators are in superoperator form it will be
        added to the total Liouvillian of the problem without further
        transformation. This allows for using mesolve for solving master
        equations that are not in standard Lindblad form.

    .. note::

        On using callback functions: mesolve transforms all :class:`qutip.Qobj`
        objects to sparse matrices before handing the problem to the integrator
        function. In order for your callback function to work correctly, pass
        all :class:`qutip.Qobj` objects that are used in constructing the
        Hamiltonian via `args`. mesolve will check for :class:`qutip.Qobj` in
        `args` and handle the conversion to sparse matrices. All other
        :class:`qutip.Qobj` objects that are not passed via `args` will be
        passed on to the integrator in scipy which will raise a NotImplemented
        exception.

    Parameters
    ----------

    H : :class:`qutip.Qobj`
        System Hamiltonian, or a callback function for time-dependent
        Hamiltonians, or alternatively a system Liouvillian.

    rho0 : :class:`qutip.Qobj`
        initial density matrix or state vector (ket).

    tlist : *list* / *array*
        list of times for :math:`t`.

    c_ops : None / list of :class:`qutip.Qobj`
        single collapse operator, or list of collapse operators, or a list
        of Liouvillian superoperators.

    e_ops : None / list / callback function, optional
        A list of operators as `Qobj` and/or callable functions (can be mixed)
        or a single callable function. For operators, the result's expect will
        be  computed by :func:`qutip.expect`. For callable functions, they are
        called as ``f(t, state)`` and return the expectation value.
        A single callback's expectation value can be any type, but a callback
        part of a list must return a number as the expectation value.

    args : None / *dictionary*
        dictionary of parameters for time-dependent Hamiltonians and
        collapse operators.

    options : None / :class:`qutip.Options`
        with options for the solver.

    progress_bar : None / BaseProgressBar
        Optional instance of BaseProgressBar, or a subclass thereof, for
        showing the progress of the simulation.

    Returns
    -------
    result: :class:`qutip.Result`

        An instance of the class :class:`qutip.Result`, which contains
        either an *array* `result.expect` of expectation values for the times
        specified by `tlist`, or an *array* `result.states` of state vectors or
        density matrices corresponding to the times in `tlist` [if `e_ops` is
        an empty list], or nothing if a callback function was given in place of
        operators for which to calculate the expectation values.

    """
    if c_ops is None:
        c_ops = []
    if isinstance(c_ops, (Qobj, QobjEvo)):
        c_ops = [c_ops]

    if e_ops is None:
        e_ops = []
    if isinstance(e_ops, Qobj):
        e_ops = [e_ops]

    if isinstance(e_ops, dict):
        e_ops_dict = e_ops
        e_ops = [e for e in e_ops.values()]
    else:
        e_ops_dict = None

    if progress_bar is None:
        progress_bar = BaseProgressBar()
    if progress_bar is True:
        progress_bar = TextProgressBar()

    # check if rho0 is a superoperator, in which case e_ops argument should
    # be empty, i.e., e_ops = []
    # TODO: e_ops for superoperator
    if issuper(rho0) and not e_ops == []:
        raise TypeError("Must have e_ops = [] when initial condition rho0 is" +
                        " a superoperator.")

    if options is None:
        options = Options()
    if options.rhs_reuse and not isinstance(H, SolverSystem):
        # TODO: deprecate when going to class based solver.
        if "mesolve" in solver_safe:
            # print(" ")
            H = solver_safe["mesolve"]
        else:
            pass
            # raise Exception("Could not find the Hamiltonian to reuse.")

    if args is None:
        args = {}

    check_use_openmp(options)

    use_mesolve = ((c_ops and len(c_ops) > 0) or (not isket(rho0))
                   or (isinstance(H, Qobj) and issuper(H))
                   or (isinstance(H, QobjEvo) and issuper(H.cte))
                   or (isinstance(H, list) and isinstance(H[0], Qobj)
                       and issuper(H[0]))
                   or (not isinstance(H, (Qobj, QobjEvo)) and callable(H)
                       and not options.rhs_with_state and issuper(H(0., args)))
                   or (not isinstance(H, (Qobj, QobjEvo)) and callable(H)
                       and options.rhs_with_state))

    if not use_mesolve:
        return sesolve(H,
                       rho0,
                       tlist,
                       e_ops=e_ops,
                       args=args,
                       options=options,
                       progress_bar=progress_bar,
                       _safe_mode=_safe_mode)

    if isket(rho0):
        rho0 = ket2dm(rho0)
    if (not (rho0.isoper or rho0.issuper)) or (rho0.dims[0] != rho0.dims[1]):
        raise ValueError(
            "input state must be a pure state vector, square density matrix, "
            "or superoperator")

    if isinstance(H, SolverSystem):
        ss = H
    elif isinstance(H, (list, Qobj, QobjEvo)):
        ss = _mesolve_QobjEvo(H, c_ops, tlist, args, options)
    elif callable(H):
        ss = _mesolve_func_td(H, c_ops, rho0, tlist, args, options)
    else:
        raise Exception("Invalid H type")

    func, ode_args = ss.makefunc(ss, rho0, args, e_ops, options)

    if _safe_mode:
        # This is to test safety of the function before starting the loop.
        v = rho0.full().ravel('F')
        func(0., v, *ode_args) + v

    res = _generic_ode_solve(func,
                             ode_args,
                             rho0,
                             tlist,
                             e_ops,
                             options,
                             progress_bar,
                             dims=rho0.dims)
    res.num_collapse = len(c_ops)

    if e_ops_dict:
        res.expect = {
            e: res.expect[n]
            for n, e in enumerate(e_ops_dict.keys())
        }

    return res
Beispiel #29
0
def parallel_map(task, values, task_args=tuple(), task_kwargs={}, **kwargs):
    """
    Parallel execution of a mapping of `values` to the function `task`. This
    is functionally equivalent to::

        result = [task(value, *task_args, **task_kwargs) for value in values]

    Parameters
    ----------
    task : a Python function
        The function that is to be called for each value in ``task_vec``.
    values : array / list
        The list or array of values for which the ``task`` function is to be
        evaluated.
    task_args : list / dictionary
        The optional additional argument to the ``task`` function.
    task_kwargs : list / dictionary
        The optional additional keyword argument to the ``task`` function.
    progress_bar : ProgressBar
        Progress bar class instance for showing progress.

    Returns
    --------
    result : list
        The result list contains the value of
        ``task(value, *task_args, **task_kwargs)`` for
        each value in ``values``.

    """
    os.environ['QUTIP_IN_PARALLEL'] = 'TRUE'
    kw = _default_kwargs()
    if 'num_cpus' in kwargs:
        kw['num_cpus'] = kwargs['num_cpus']

    try:
        progress_bar = kwargs['progress_bar']
        if progress_bar is True:
            progress_bar = TextProgressBar()
    except:
        progress_bar = BaseProgressBar()

    progress_bar.start(len(values))
    nfinished = [0]

    def _update_progress_bar(x):
        nfinished[0] += 1
        progress_bar.update(nfinished[0])

    try:
        pool = Pool(processes=kw['num_cpus'])

        async_res = [pool.apply_async(task, (value,) + task_args, task_kwargs,
                                      _update_progress_bar)
                     for value in values]

        while not all([ar.ready() for ar in async_res]):
            for ar in async_res:
                ar.wait(timeout=0.1)

        pool.terminate()
        pool.join()

    except KeyboardInterrupt as e:
        os.environ['QUTIP_IN_PARALLEL'] = 'FALSE'
        pool.terminate()
        pool.join()
        raise e

    progress_bar.finished()
    os.environ['QUTIP_IN_PARALLEL'] = 'FALSE'
    return [ar.get() for ar in async_res]
# in the following list 
eops = [n_a_tensor, 
        n_b_tensor,
        ]

n_t = 101
tlist = np.linspace(0, Tp, n_t)

# return the average value evolution of given operators
##print(H_pcc.shape)
#print(H_inter.shape)
#print(storage_0.shape)
#print(PCC_0.shape)
#print(qt.tensor(storage_0,PCC_0).shape)

res = qt.mesolve([chi0*H_inter+H_storage+H_pcc,], qt.tensor(storage_0,PCC_0), tlist,cops,progress_bar=TextProgressBar())
print("solved")

print("Wigner storage")
test_Wigner1 = compute_Wigner([-4, 4, 51], 10, n_t, 0)
test_Wigner1.draw_Wigner(res)
print("Wigner PCC")
test_Wigner2 = compute_Wigner([-4, 4, 51], 10, n_t, 1)
test_Wigner2.draw_Wigner(res)
proba_PCC = [] 
proba_storage = [] 
fidelity_PCC = []
fidelity_storage = []

for ii, t in enumerate(tlist):
    rho_PCC = res.states[ii].ptrace(1)
Beispiel #31
0
def mesolve(H,
            rho0,
            tlist,
            c_ops=[],
            e_ops=[],
            args={},
            options=None,
            progress_bar=None,
            _safe_mode=True):
    """
    Master equation evolution of a density matrix for a given Hamiltonian and
    set of collapse operators, or a Liouvillian.

    Evolve the state vector or density matrix (`rho0`) using a given
    Hamiltonian (`H`) and an [optional] set of collapse operators
    (`c_ops`), by integrating the set of ordinary differential equations
    that define the system. In the absence of collapse operators the system is
    evolved according to the unitary evolution of the Hamiltonian.

    The output is either the state vector at arbitrary points in time
    (`tlist`), or the expectation values of the supplied operators
    (`e_ops`). If e_ops is a callback function, it is invoked for each
    time in `tlist` with time and the state as arguments, and the function
    does not use any return values.

    If either `H` or the Qobj elements in `c_ops` are superoperators, they
    will be treated as direct contributions to the total system Liouvillian.
    This allows to solve master equations that are not on standard Lindblad
    form by passing a custom Liouvillian in place of either the `H` or `c_ops`
    elements.

    **Time-dependent operators**

    For time-dependent problems, `H` and `c_ops` can be callback
    functions that takes two arguments, time and `args`, and returns the
    Hamiltonian or Liouvillian for the system at that point in time
    (*callback format*).

    Alternatively, `H` and `c_ops` can be a specified in a nested-list format
    where each element in the list is a list of length 2, containing an
    operator (:class:`qutip.qobj`) at the first element and where the
    second element is either a string (*list string format*), a callback
    function (*list callback format*) that evaluates to the time-dependent
    coefficient for the corresponding operator, or a NumPy array (*list
    array format*) which specifies the value of the coefficient to the
    corresponding operator for each value of t in tlist.

    *Examples*

        H = [[H0, 'sin(w*t)'], [H1, 'sin(2*w*t)']]

        H = [[H0, f0_t], [H1, f1_t]]

        where f0_t and f1_t are python functions with signature f_t(t, args).

        H = [[H0, np.sin(w*tlist)], [H1, np.sin(2*w*tlist)]]

    In the *list string format* and *list callback format*, the string
    expression and the callback function must evaluate to a real or complex
    number (coefficient for the corresponding operator).

    In all cases of time-dependent operators, `args` is a dictionary of
    parameters that is used when evaluating operators. It is passed to the
    callback functions as second argument.

    **Additional options**

    Additional options to mesolve can be set via the `options` argument, which
    should be an instance of :class:`qutip.solver.Options`. Many ODE
    integration options can be set this way, and the `store_states` and
    `store_final_state` options can be used to store states even though
    expectation values are requested via the `e_ops` argument.

    .. note::

        If an element in the list-specification of the Hamiltonian or
        the list of collapse operators are in superoperator form it will be
        added to the total Liouvillian of the problem with out further
        transformation. This allows for using mesolve for solving master
        equations that are not on standard Lindblad form.

    .. note::

        On using callback function: mesolve transforms all :class:`qutip.qobj`
        objects to sparse matrices before handing the problem to the integrator
        function. In order for your callback function to work correctly, pass
        all :class:`qutip.qobj` objects that are used in constructing the
        Hamiltonian via args. mesolve will check for :class:`qutip.qobj` in
        `args` and handle the conversion to sparse matrices. All other
        :class:`qutip.qobj` objects that are not passed via `args` will be
        passed on to the integrator in scipy which will raise an NotImplemented
        exception.

    Parameters
    ----------

    H : :class:`qutip.Qobj`
        System Hamiltonian, or a callback function for time-dependent
        Hamiltonians, or alternatively a system Liouvillian.

    rho0 : :class:`qutip.Qobj`
        initial density matrix or state vector (ket).

    tlist : *list* / *array*
        list of times for :math:`t`.

    c_ops : list of :class:`qutip.Qobj`
        single collapse operator, or list of collapse operators, or a list
        of Liouvillian superoperators.

    e_ops : list of :class:`qutip.Qobj` / callback function single
        single operator or list of operators for which to evaluate
        expectation values.

    args : *dictionary*
        dictionary of parameters for time-dependent Hamiltonians and
        collapse operators.

    options : :class:`qutip.Options`
        with options for the solver.

    progress_bar : BaseProgressBar
        Optional instance of BaseProgressBar, or a subclass thereof, for
        showing the progress of the simulation.

    Returns
    -------
    result: :class:`qutip.Result`

        An instance of the class :class:`qutip.Result`, which contains
        either an *array* `result.expect` of expectation values for the times
        specified by `tlist`, or an *array* `result.states` of state vectors or
        density matrices corresponding to the times in `tlist` [if `e_ops` is
        an empty list], or nothing if a callback function was given in place of
        operators for which to calculate the expectation values.

    """
    # check whether c_ops or e_ops is is a single operator
    # if so convert it to a list containing only that operator
    if isinstance(c_ops, Qobj):
        c_ops = [c_ops]

    if isinstance(e_ops, Qobj):
        e_ops = [e_ops]

    if isinstance(e_ops, dict):
        e_ops_dict = e_ops
        e_ops = [e for e in e_ops.values()]
    else:
        e_ops_dict = None

    if _safe_mode:
        _solver_safety_check(H, rho0, c_ops, e_ops, args)

    if progress_bar is None:
        progress_bar = BaseProgressBar()
    elif progress_bar is True:
        progress_bar = TextProgressBar()

    # check if rho0 is a superoperator, in which case e_ops argument should
    # be empty, i.e., e_ops = []
    if issuper(rho0) and not e_ops == []:
        raise TypeError("Must have e_ops = [] when initial condition rho0 is" +
                        " a superoperator.")

    # convert array based time-dependence to string format
    H, c_ops, args = _td_wrap_array_str(H, c_ops, args, tlist)

    # check for type (if any) of time-dependent inputs
    _, n_func, n_str = _td_format_check(H, c_ops)

    if options is None:
        options = Options()

    if (not options.rhs_reuse) or (not config.tdfunc):
        # reset config collapse and time-dependence flags to default values
        config.reset()

    #check if should use OPENMP
    check_use_openmp(options)

    res = None

    #
    # dispatch the appropriate solver
    #
    if ((c_ops and len(c_ops) > 0) or (not isket(rho0))
            or (isinstance(H, Qobj) and issuper(H)) or
        (isinstance(H, list) and isinstance(H[0], Qobj) and issuper(H[0]))):

        #
        # we have collapse operators, or rho0 is not a ket,
        # or H is a Liouvillian
        #

        #
        # find out if we are dealing with all-constant hamiltonian and
        # collapse operators or if we have at least one time-dependent
        # operator. Then delegate to appropriate solver...
        #

        if isinstance(H, Qobj):
            # constant hamiltonian
            if n_func == 0 and n_str == 0:
                # constant collapse operators
                res = _mesolve_const(H, rho0, tlist, c_ops, e_ops, args,
                                     options, progress_bar)
            elif n_str > 0:
                # constant hamiltonian but time-dependent collapse
                # operators in list string format
                res = _mesolve_list_str_td([H], rho0, tlist, c_ops, e_ops,
                                           args, options, progress_bar)
            elif n_func > 0:
                # constant hamiltonian but time-dependent collapse
                # operators in list function format
                res = _mesolve_list_func_td([H], rho0, tlist, c_ops, e_ops,
                                            args, options, progress_bar)

        elif isinstance(
                H, (types.FunctionType, types.BuiltinFunctionType, partial)):
            # function-callback style time-dependence: must have constant
            # collapse operators
            if n_str > 0:  # or n_func > 0:
                raise TypeError("Incorrect format: function-format " +
                                "Hamiltonian cannot be mixed with " +
                                "time-dependent collapse operators.")
            else:
                res = _mesolve_func_td(H, rho0, tlist, c_ops, e_ops, args,
                                       options, progress_bar)

        elif isinstance(H, list):
            # determine if we are dealing with list of [Qobj, string] or
            # [Qobj, function] style time-dependencies (for pure python and
            # cython, respectively)
            if n_func > 0:
                res = _mesolve_list_func_td(H, rho0, tlist, c_ops, e_ops, args,
                                            options, progress_bar)
            else:
                res = _mesolve_list_str_td(H, rho0, tlist, c_ops, e_ops, args,
                                           options, progress_bar)

        else:
            raise TypeError("Incorrect specification of Hamiltonian " +
                            "or collapse operators.")

    else:
        #
        # no collapse operators: unitary dynamics
        #
        if n_func > 0:
            res = _sesolve_list_func_td(H, rho0, tlist, e_ops, args, options,
                                        progress_bar)
        elif n_str > 0:
            res = _sesolve_list_str_td(H, rho0, tlist, e_ops, args, options,
                                       progress_bar)
        elif isinstance(
                H, (types.FunctionType, types.BuiltinFunctionType, partial)):
            res = _sesolve_func_td(H, rho0, tlist, e_ops, args, options,
                                   progress_bar)
        else:
            res = _sesolve_const(H, rho0, tlist, e_ops, args, options,
                                 progress_bar)

    if e_ops_dict:
        res.expect = {
            e: res.expect[n]
            for n, e in enumerate(e_ops_dict.keys())
        }

    return res