예제 #1
0
파일: parallel.py 프로젝트: yipk2/qutip
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
예제 #2
0
파일: prob5.py 프로젝트: physicaone/qutip
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
예제 #3
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
예제 #4
0
파일: prob3.py 프로젝트: physicaone/qutip
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
예제 #5
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