Repository: qutip/qutip

Issue:
mesolve: allow superoperator as initial condition
Hi all,

There are quite a few applications when it is convenient to solve the Lindblad master equation for a superoperator, i.e.,

![equation](https://dl.dropboxusercontent.com/u/6042643/Tex2Img_1445353534.jpg)

where the last equation shows how the density matrix is found from the time-propagator (i.e., the superoperator solution to the Lindblad equation). The initial condition to the first equation is typically the identity superoperator.

As an example, I've typically come across the need for this when dealing with non-Markovian problems. Certain non-Markovian problems can be dealt with using time-propagators of the above type where the Lindblad evolution is in an "enlarged" Hilbert space of some sort. In fact, to give some motivation for implementing this feature, I am currently working on a sub-package for qutip with a few solvers for non-Markovian problems, one of them being the so-called "Transfer Tensor Method" (TTM) [PRL 112, 110401 (2014)], which takes as input a list of precomputed dynamical maps in the form of superoperators. A typical usecase could be to generate these with a (costly) Lindblad equation in a large Hilbert space, and use the TTM to extrapolate to larger times. My code is not quite ready for a pull request yet (due to this issue amongst other things), but when it is I would also like to discuss the structure of such a package. I guess it is best to leave that to when I have a pull request ready though.

For now I have a local modified version of the `_mesolve_const` function that allows a superoperator in place of `rho0`. The necessary modification is actually only to the function implementing the "right hand side" (RHS) of the differential equation, as this now needs to implement a matrix-matrix multiplication instead of a matrix-vector multiplication. For illustration purposes I will attach a code for a modified `_mesolve_const` below, that does the job, but the RHS is not implemented in cython.

I think implementing this feature in qutip should be relatively straight forward, but possibly a bit tedious since it there are so many different dispatch functions from `mesolve` depending on the different types of time-dependence etc. One would also need cython implementations of the RHS functions for superoperator evolutions.

Please let me know what you think about implementing this in qutip.

Here is the code for a function that propagates the Lindblad equation with a superoperator as initial value (written as a standalone module for illustration purposes). As you will notice in the code, there is a subtle issue with needing to transpose the Lindbladian to get the correct result. I'm not sure why this is the case.. must be something with the superoperator representation. Perhaps @cgranade knows?

``` python
import scipy

from qutip.mesolve import _generic_ode_solve
from qutip.superoperator import liouvillian, mat2vec
from qutip.settings import debug

if debug:
    import inspect


def _mesolve_const_super(H, E0, tlist, c_op_list, e_ops, args, opt,
                   progress_bar):
    """
    Evolve the super-operator `E0` using an ODE solver, for constant 
    Liouvillian
    """

    if debug:
        print(inspect.stack()[0][3])

    """
    #
    # check initial state
    #
    if isket(rho0):
        # if initial state is a ket and no collapse operator where given,
        # fall back on the unitary schrodinger equation solver
        if len(c_op_list) == 0 and isoper(H):
            return _sesolve_const(H, rho0, tlist, e_ops, args, opt,
                                  progress_bar)

        # Got a wave function as initial state: convert to density matrix.
        rho0 = ket2dm(rho0)
    """

    #
    # check initial value
    #
    if not E0.issuper:
        raise TypeError("Argument 'E0' should be a super-operator")

    #
    # construct liouvillian
    #
    if opt.tidy:
        H = H.tidyup(opt.atol)

    L = liouvillian(H, c_op_list)

    #
    # setup integrator
    #
    initial_vector = mat2vec(E0.full()).ravel()
    # r = scipy.integrate.ode(cy_ode_rhs)
    r = scipy.integrate.ode(_rhs)
    # r.set_f_params(L.data.data, L.data.indices, L.data.indptr)
    # not sure why I need to transpose L here:
    r.set_f_params(L.trans().data)
    r.set_integrator('zvode', method=opt.method, order=opt.order,
                     atol=opt.atol, rtol=opt.rtol, nsteps=opt.nsteps,
                     first_step=opt.first_step, min_step=opt.min_step,
                     max_step=opt.max_step)
    r.set_initial_value(initial_vector, tlist[0])

    #
    # call generic ODE code
    #
    return _generic_ode_solve(r, E0, tlist, e_ops, opt, progress_bar)


def _rhs(t,y,data):
    ym = y.reshape(data.shape)
    return (ym*data).ravel()
```



Discussion:

Resolving patch:
diff --git a/qutip/mesolve.py b/qutip/mesolve.py
--- a/qutip/mesolve.py
+++ b/qutip/mesolve.py
@@ -221,6 +221,12 @@ def mesolve(H, rho0, tlist, c_ops, e_ops, args={}, options=None,
     else:
         e_ops_dict = None
 
+    # check if rho0 is a superoperator, in which case e_ops argument should
+    # be empty, i.e., e_ops = []
+    if issuper(rho0) and not e_ops == []:
+        raise TypeError("Must have e_ops = [] when initial condition rho0 is" +
+                " a superoperator.")
+
     # convert array based time-dependence to string format
     H, c_ops, args = _td_wrap_array_str(H, c_ops, args, tlist)
 
@@ -246,7 +252,8 @@ def mesolve(H, rho0, tlist, c_ops, e_ops, args={}, options=None,
             isinstance(H[0], Qobj) and issuper(H[0]))):
 
         #
-        # we have collapse operators
+        # we have collapse operators, or rho0 is not a ket,
+        # or H is a Liouvillian
         #
 
         #
@@ -414,11 +421,17 @@ def _mesolve_list_func_td(H_list, rho0, tlist, c_list, e_ops, args, opt,
     #
     # setup integrator
     #
-    initial_vector = mat2vec(rho0.full()).ravel()
-    if opt.rhs_with_state:
-        r = scipy.integrate.ode(drho_list_td_with_state)
+    initial_vector = mat2vec(rho0.full()).ravel('F')
+    if issuper(rho0):
+        if opt.rhs_with_state:
+            r = scipy.integrate.ode(dsuper_list_td_with_state)
+        else:
+            r = scipy.integrate.ode(dsuper_list_td)
     else:
-        r = scipy.integrate.ode(drho_list_td)
+        if opt.rhs_with_state:
+            r = scipy.integrate.ode(drho_list_td_with_state)
+        else:
+            r = scipy.integrate.ode(drho_list_td)
     r.set_integrator('zvode', method=opt.method, order=opt.order,
                      atol=opt.atol, rtol=opt.rtol, nsteps=opt.nsteps,
                      first_step=opt.first_step, min_step=opt.min_step,
@@ -467,6 +480,39 @@ def drho_list_td_with_state(t, rho, L_list, args):
 
     return L * rho
 
+#
+# evaluate dE(t)/dt according to the master equation using the
+# [Qobj, function] style time dependence API, where E is a superoperator
+#
+def dsuper_list_td(t, y, L_list, args):
+
+    L = L_list[0][0] * L_list[0][1](t, args)
+    for n in range(1, len(L_list)):
+        #
+        # L_args[n][0] = the sparse data for a Qobj in super-operator form
+        # L_args[n][1] = function callback giving the coefficient
+        #
+        if L_list[n][2]:
+            L = L + L_list[n][0] * (L_list[n][1](t, args)) ** 2
+        else:
+            L = L + L_list[n][0] * L_list[n][1](t, args)
+
+    return _ode_super_func(t, y, L)
+
+def dsuper_list_td_with_state(t, y, L_list, args):
+
+    L = L_list[0][0] * L_list[0][1](t, y, args)
+    for n in range(1, len(L_list)):
+        #
+        # L_args[n][0] = the sparse data for a Qobj in super-operator form
+        # L_args[n][1] = function callback giving the coefficient
+        #
+        if L_list[n][2]:
+            L = L + L_list[n][0] * (L_list[n][1](t, y, args)) ** 2
+        else:
+            L = L + L_list[n][0] * L_list[n][1](t, y, args)
+
+    return _ode_super_func(t, y, L)
 
 # -----------------------------------------------------------------------------
 # A time-dependent dissipative master equation on the list-string format for
@@ -622,15 +668,20 @@ def _mesolve_list_str_td(H_list, rho0, tlist, c_list, e_ops, args, opt,
     #
     # setup integrator
     #
-    initial_vector = mat2vec(rho0.full()).ravel()
-    r = scipy.integrate.ode(config.tdfunc)
+    initial_vector = mat2vec(rho0.full()).ravel('F')
+    if issuper(rho0):
+        r = scipy.integrate.ode(_td_ode_rhs_super)
+        code = compile('r.set_f_params([' + parameter_string + '])',
+                       '<string>', 'exec')
+    else:
+        r = scipy.integrate.ode(config.tdfunc)
+        code = compile('r.set_f_params(' + parameter_string + ')',
+                       '<string>', 'exec')
     r.set_integrator('zvode', method=opt.method, order=opt.order,
                      atol=opt.atol, rtol=opt.rtol, nsteps=opt.nsteps,
                      first_step=opt.first_step, min_step=opt.min_step,
                      max_step=opt.max_step)
     r.set_initial_value(initial_vector, tlist[0])
-    code = compile('r.set_f_params(' + parameter_string + ')',
-                   '<string>', 'exec')
 
     exec(code, locals(), args)
 
@@ -639,6 +690,14 @@ def _mesolve_list_str_td(H_list, rho0, tlist, c_list, e_ops, args, opt,
     #
     return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
 
+def _td_ode_rhs_super(t, y, arglist):
+    N = int(np.sqrt(len(y)))
+    out = np.zeros(N, dtype=complex)
+    y2 = np.zeros(len(y), dtype=complex)
+    for i in range(N):
+        out = cy_td_ode_rhs(t, y[i*N:(i+1)*N], *arglist)
+        y2[i*N:(i+1)*N] = out
+    return y2
 
 # -----------------------------------------------------------------------------
 # Master equation solver
@@ -677,9 +736,15 @@ def _mesolve_const(H, rho0, tlist, c_op_list, e_ops, args, opt,
     #
     # setup integrator
     #
-    initial_vector = mat2vec(rho0.full()).ravel()
-    r = scipy.integrate.ode(cy_ode_rhs)
-    r.set_f_params(L.data.data, L.data.indices, L.data.indptr)
+    initial_vector = mat2vec(rho0.full()).ravel('F')
+    if issuper(rho0):
+        r = scipy.integrate.ode(_ode_super_func)
+        r.set_f_params(L.data)
+    else:
+        r = scipy.integrate.ode(cy_ode_rhs)
+        r.set_f_params(L.data.data, L.data.indices, L.data.indptr)
+        # r = scipy.integrate.ode(_ode_rho_test)
+        # r.set_f_params(L.data)
     r.set_integrator('zvode', method=opt.method, order=opt.order,
                      atol=opt.atol, rtol=opt.rtol, nsteps=opt.nsteps,
                      first_step=opt.first_step, min_step=opt.min_step,
@@ -699,6 +764,16 @@ def _mesolve_const(H, rho0, tlist, c_op_list, e_ops, args, opt,
 def _ode_rho_func(t, rho, L):
     return L * rho
 
+def _ode_rho_test(t, rho, data):
+    # for performance comparison of cython code
+    return data*(np.transpose(rho))
+#
+# Evaluate d E(t)/dt for E a super-operator
+#
+
+def _ode_super_func(t, y, data):
+    ym = vec2mat(y)
+    return (data*ym).ravel('F')
 
 # -----------------------------------------------------------------------------
 # Master equation solver for python-function time-dependence.
@@ -728,7 +803,10 @@ def _mesolve_func_td(L_func, rho0, tlist, c_op_list, e_ops, args, opt,
         L_data = liouvillian(None, c_op_list).data
     else:
         n, m = rho0.shape
-        L_data = sp.csr_matrix((n ** 2, m ** 2), dtype=complex)
+        if issuper(rho0):
+            L_data = sp.csr_matrix((n, m), dtype=complex)
+        else:
+            L_data = sp.csr_matrix((n ** 2, m ** 2), dtype=complex)
 
     if type(args) is dict:
         new_args = {}
@@ -767,11 +845,17 @@ def _mesolve_func_td(L_func, rho0, tlist, c_op_list, e_ops, args, opt,
     #
     # setup integrator
     #
-    initial_vector = mat2vec(rho0.full()).ravel()
-    if not opt.rhs_with_state:
-        r = scipy.integrate.ode(cy_ode_rho_func_td)
+    initial_vector = mat2vec(rho0.full()).ravel('F')
+    if issuper(rho0):
+        if not opt.rhs_with_state:
+            r = scipy.integrate.ode(_ode_super_func_td)
+        else:
+            r = scipy.integrate.ode(_ode_super_func_td_with_state)
     else:
-        r = scipy.integrate.ode(_ode_rho_func_td_with_state)
+        if not opt.rhs_with_state:
+            r = scipy.integrate.ode(cy_ode_rho_func_td)
+        else:
+            r = scipy.integrate.ode(_ode_rho_func_td_with_state)
     r.set_integrator('zvode', method=opt.method, order=opt.order,
                      atol=opt.atol, rtol=opt.rtol, nsteps=opt.nsteps,
                      first_step=opt.first_step, min_step=opt.min_step,
@@ -800,6 +884,23 @@ def _ode_rho_func_td_with_state(t, rho, L0, L_func, args):
     L = L0 + L_func(t, rho, args)
     return L * rho
 
+#
+# evaluate dE(t)/dt according to the master equation, where E is a 
+# superoperator
+#
+def _ode_super_func_td(t, y, L0, L_func, args):
+    L = L0 + L_func(t, args)
+    return _ode_super_func(t, y, L)
+
+#
+# evaluate dE(t)/dt according to the master equation, where E is a 
+# superoperator
+#
+def _ode_super_func_td_with_state(t, y, L0, L_func, args):
+    L = L0 + L_func(t, y, args)
+    return _ode_super_func(t, y, L)
+
+
 
 # -----------------------------------------------------------------------------
 # Generic ODE solver: shared code among the various ODE solver
diff --git a/qutip/rhs_generate.py b/qutip/rhs_generate.py
--- a/qutip/rhs_generate.py
+++ b/qutip/rhs_generate.py
@@ -375,7 +375,7 @@ def _td_wrap_array_str(H, c_ops, args, times):
             if isinstance(Hk, list) and isinstance(Hk[1], np.ndarray):
                 H_op, H_td = Hk
                 td_array_name = "_td_array_%d" % n
-                H_td_str = '(0 if (t > %f) else %s[round(%d * (t/%f))])' %\
+                H_td_str = '(0 if (t > %f) else %s[int(round(%d * (t/%f)))])' %\
                     (times[-1], td_array_name, len(times) - 1, times[-1])
                 args_new[td_array_name] = H_td
                 H_new.append([H_op, H_td_str])
@@ -390,7 +390,7 @@ def _td_wrap_array_str(H, c_ops, args, times):
             if isinstance(ck, list) and isinstance(ck[1], np.ndarray):
                 c_op, c_td = ck
                 td_array_name = "_td_array_%d" % n
-                c_td_str = '(0 if (t > %f) else %s[round(%d * (t/%f))])' %\
+                c_td_str = '(0 if (t > %f) else %s[int(round(%d * (t/%f)))])' %\
                     (times[-1], td_array_name, len(times) - 1, times[-1])
                 args_new[td_array_name] = c_td
                 c_ops_new.append([c_op, c_td_str])


Test patch:
diff --git a/qutip/tests/test_mesolve.py b/qutip/tests/test_mesolve.py
--- a/qutip/tests/test_mesolve.py
+++ b/qutip/tests/test_mesolve.py
@@ -38,10 +38,13 @@
 
 # disable the MC progress bar
 import os
-os.environ['QUTIP_GRAPHICS'] = "NO"
 
 from qutip import (sigmax, sigmay, sigmaz, sigmam, mesolve, tensor, destroy,
-                   identity, steadystate, expect, basis, num)
+                   identity, steadystate, expect, basis, num, qeye, sprepost,
+                   operator_to_vector, vector_to_operator, fidelity, ket2dm,
+                   liouvillian)
+
+os.environ['QUTIP_GRAPHICS'] = "NO"
 
 
 class TestJCModelEvolution:
@@ -403,6 +406,7 @@ def sqrt_kappa(t, args):
     def testMETDDecayAsPartFuncList(self):
         "mesolve: time-dependence as partial function list"
 
+        me_error = 1e-5
         N = 10
         a = destroy(N)
         H = num(N)
@@ -451,5 +455,214 @@ def testMETDDecayAsArray(self):
         avg_diff = np.mean(abs(actual_answer - expt) / actual_answer)
         assert_(avg_diff < 100 * me_error)
 
+    def testMETDDecayAsFunc(self):
+        "mesolve: time-dependent Liouvillian as single function"
+
+        N = 10  # number of basis states to consider
+        a = destroy(N)
+        H = a.dag() * a
+        rho0 = ket2dm(basis(N, 9))  # initial state
+        kappa = 0.2  # coupling to oscillator
+
+        def Liouvillian_func(t, args):
+            c = np.sqrt(kappa * np.exp(-t))*a
+            return liouvillian(H, [c]).data
+
+        tlist = np.linspace(0, 10, 100)
+        args = {'kappa': kappa}
+        out1 = mesolve(Liouvillian_func, rho0, tlist, [], [], args=args)
+        expt = expect(a.dag()*a, out1.states)
+        actual_answer = 9.0 * np.exp(-kappa * (1.0 - np.exp(-tlist)))
+        avg_diff = np.mean(abs(actual_answer - expt) / actual_answer)
+        assert_(avg_diff < me_error)
+
+
+# average error for failure
+# me_error = 1e-6
+
+class TestMESolveSuperInit:
+    """
+    A test class comparing mesolve run with an identity super-operator and
+    a density matrix as initial conditions, respectively.
+    """
+
+    def fidelitycheck(self, out1, out2, rho0vec):
+        fid = np.zeros(len(out1.states))
+        for i, E in enumerate(out2.states):
+            rhot = vector_to_operator(E*rho0vec)
+            fid[i] = fidelity(out1.states[i], rhot)
+        return fid
+
+    def jc_integrate(self, N, wc, wa, g, kappa, gamma,
+                     pump, psi0, use_rwa, tlist):
+        # Hamiltonian
+        a = tensor(destroy(N), identity(2))
+        sm = tensor(identity(N), destroy(2))
+
+        # Identity super-operator
+        E0 = sprepost(tensor(qeye(N), qeye(2)), tensor(qeye(N), qeye(2)))
+
+        if use_rwa:
+            # use the rotating wave approxiation
+            H = wc * a.dag() * a + wa * sm.dag() * sm + g * (
+                a.dag() * sm + a * sm.dag())
+        else:
+            H = wc * a.dag() * a + wa * sm.dag() * sm + g * (
+                a.dag() + a) * (sm + sm.dag())
+
+        # collapse operators
+        c_op_list = []
+
+        n_th_a = 0.0  # zero temperature
+
+        rate = kappa * (1 + n_th_a)
+        c_op_list.append(np.sqrt(rate) * a)
+
+        rate = kappa * n_th_a
+        if rate > 0.0:
+            c_op_list.append(np.sqrt(rate) * a.dag())
+
+        rate = gamma
+        if rate > 0.0:
+            c_op_list.append(np.sqrt(rate) * sm)
+
+        rate = pump
+        if rate > 0.0:
+            c_op_list.append(np.sqrt(rate) * sm.dag())
+
+        # evolve and calculate expectation values
+        output1 = mesolve(H, psi0, tlist, c_op_list, [])
+        output2 = mesolve(H, E0, tlist, c_op_list, [])
+        return output1, output2
+
+    def testSuperJC(self):
+        "mesolve: super vs. density matrix as initial condition"
+        me_error = 1e-6
+
+        use_rwa = True
+        N = 4           # number of cavity fock states
+        wc = 2 * np.pi * 1.0   # cavity frequency
+        wa = 2 * np.pi * 1.0   # atom frequency
+        g = 2 * np.pi * 0.1   # coupling strength
+        kappa = 0.05    # cavity dissipation rate
+        gamma = 0.001   # atom dissipation rate
+        pump = 0.25    # atom pump rate
+
+        # start with an excited atom and maximum number of photons
+        n = N - 2
+        psi0 = tensor(basis(N, n), basis(2, 1))
+        rho0vec = operator_to_vector(psi0*psi0.dag())
+        tlist = np.linspace(0, 100, 50)
+
+        out1, out2 = self.jc_integrate(
+            N, wc, wa, g, kappa, gamma, pump, psi0, use_rwa, tlist)
+
+        fid = self.fidelitycheck(out1, out2, rho0vec)
+        assert_(max(abs(1.0-fid)) < me_error, True)
+
+    def testMETDDecayAsFuncList(self):
+        "mesolve: time-dependence as function list with super as init cond"
+        me_error = 1e-6
+
+        N = 10  # number of basis states to consider
+        a = destroy(N)
+        H = a.dag() * a
+        psi0 = basis(N, 9)  # initial state
+        rho0vec = operator_to_vector(psi0*psi0.dag())
+        E0 = sprepost(qeye(N), qeye(N))
+        kappa = 0.2  # coupling to oscillator
+
+        def sqrt_kappa(t, args):
+            return np.sqrt(kappa * np.exp(-t))
+        c_op_list = [[a, sqrt_kappa]]
+        tlist = np.linspace(0, 10, 100)
+        out1 = mesolve(H, psi0, tlist, c_op_list, [])
+        out2 = mesolve(H, E0, tlist, c_op_list, [])
+
+        fid = self.fidelitycheck(out1, out2, rho0vec)
+        assert_(max(abs(1.0-fid)) < me_error, True)
+
+    def testMETDDecayAsPartFuncList(self):
+        "mesolve: time-dep. as partial function list with super as init cond"
+        me_error = 1e-5
+
+        N = 10
+        a = destroy(N)
+        H = num(N)
+        psi0 = basis(N, 9)
+        rho0vec = operator_to_vector(psi0*psi0.dag())
+        E0 = sprepost(qeye(N), qeye(N))
+        tlist = np.linspace(0, 10, 100)
+        c_ops = [[[a, partial(lambda t, args, k:
+                              np.sqrt(k * np.exp(-t)), k=kappa)]]
+                 for kappa in [0.05, 0.1, 0.2]]
+
+        for idx, kappa in enumerate([0.05, 0.1, 0.2]):
+            out1 = mesolve(H, psi0, tlist, c_ops[idx], [])
+            out2 = mesolve(H, E0, tlist, c_ops[idx], [])
+            fid = self.fidelitycheck(out1, out2, rho0vec)
+            assert_(max(abs(1.0-fid)) < me_error, True)
+
+    def testMETDDecayAsStrList(self):
+        "mesolve: time-dependence as string list with super as init cond"
+        me_error = 1e-6
+
+        N = 10  # number of basis states to consider
+        a = destroy(N)
+        H = a.dag() * a
+        psi0 = basis(N, 9)  # initial state
+        rho0vec = operator_to_vector(psi0*psi0.dag())
+        E0 = sprepost(qeye(N), qeye(N))
+        kappa = 0.2  # coupling to oscillator
+        c_op_list = [[a, 'sqrt(k*exp(-t))']]
+        args = {'k': kappa}
+        tlist = np.linspace(0, 10, 100)
+        out1 = mesolve(H, psi0, tlist, c_op_list, [], args=args)
+        out2 = mesolve(H, E0, tlist, c_op_list, [], args=args)
+        fid = self.fidelitycheck(out1, out2, rho0vec)
+        assert_(max(abs(1.0-fid)) < me_error, True)
+
+    def testMETDDecayAsArray(self):
+        "mesolve: time-dependence as array with super as init cond"
+        me_error = 1e-5
+
+        N = 10
+        a = destroy(N)
+        H = a.dag() * a
+        psi0 = basis(N, 9)
+        rho0vec = operator_to_vector(psi0*psi0.dag())
+        E0 = sprepost(qeye(N), qeye(N))
+        kappa = 0.2
+        tlist = np.linspace(0, 10, 1000)
+        c_op_list = [[a, np.sqrt(kappa * np.exp(-tlist))]]
+        out1 = mesolve(H, psi0, tlist, c_op_list, [])
+        out2 = mesolve(H, E0, tlist, c_op_list, [])
+        fid = self.fidelitycheck(out1, out2, rho0vec)
+        assert_(max(abs(1.0-fid)) < me_error, True)
+
+    def testMETDDecayAsFunc(self):
+        "mesolve: time-dependence as function with super as init cond"
+
+        N = 10  # number of basis states to consider
+        a = destroy(N)
+        H = a.dag() * a
+        rho0 = ket2dm(basis(N, 9))  # initial state
+        rho0vec = operator_to_vector(rho0)
+        E0 = sprepost(qeye(N), qeye(N))
+        kappa = 0.2  # coupling to oscillator
+
+        def Liouvillian_func(t, args):
+            c = np.sqrt(kappa * np.exp(-t))*a
+            data = liouvillian(H, [c]).data
+            return data
+
+        tlist = np.linspace(0, 10, 100)
+        args = {'kappa': kappa}
+        out1 = mesolve(Liouvillian_func, rho0, tlist, [], [], args=args)
+        out2 = mesolve(Liouvillian_func, E0, tlist, [], [], args=args)
+
+        fid = self.fidelitycheck(out1, out2, rho0vec)
+        assert_(max(abs(1.0-fid)) < me_error, True)
+
 if __name__ == "__main__":
     run_module_suite()