Ejemplo n.º 1
0
def _sesolve_list_td(H_func, psi0, tlist, e_ops, args, opt, progress_bar):
    """!
    Evolve the wave function using an ODE solver with time-dependent
    Hamiltonian.
    """

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

    if not isket(psi0):
        raise TypeError("psi0 must be a ket")

    #
    # configure time-dependent terms and setup ODE solver
    #
    if len(H_func) != 2:
        raise TypeError('Time-dependent Hamiltonian list must have two terms.')
    if (not isinstance(H_func[0], (list, np.ndarray))) or \
       (len(H_func[0]) <= 1):
        raise TypeError('Time-dependent Hamiltonians must be a list with two '
                        + 'or more terms')
    if (not isinstance(H_func[1], (list, np.ndarray))) or \
       (len(H_func[1]) != (len(H_func[0]) - 1)):
        raise TypeError('Time-dependent coefficients must be list with ' +
                        'length N-1 where N is the number of ' +
                        'Hamiltonian terms.')
    tflag = 1
    if opt.rhs_reuse and odeconfig.tdfunc is None:
        print("No previous time-dependent RHS found.")
        print("Generating one for you...")
        rhs_generate(H_func, args)
    lenh = len(H_func[0])
    if opt.tidy:
        H_func[0] = [(H_func[0][k]).tidyup() for k in range(lenh)]
    # create data arrays for time-dependent RHS function
    Hdata = [-1.0j * H_func[0][k].data.data for k in range(lenh)]
    Hinds = [H_func[0][k].data.indices for k in range(lenh)]
    Hptrs = [H_func[0][k].data.indptr for k in range(lenh)]
    # setup ode args string
    string = ""
    for k in range(lenh):
        string += ("Hdata[" + str(k) + "],Hinds[" + str(k) +
                   "],Hptrs[" + str(k) + "],")

    if args:
        td_consts = args.items()
        for elem in td_consts:
            string += str(elem[1])
            if elem != td_consts[-1]:
                string += (",")

    # run code generator
    if not opt.rhs_reuse or odeconfig.tdfunc is None:
        if opt.rhs_filename is None:
            odeconfig.tdname = "rhs" + str(odeconfig.cgen_num)
        else:
            odeconfig.tdname = opt.rhs_filename
        cgen = Codegen(h_terms=n_L_terms, h_tdterms=Lcoeff, args=args,
                       odeconfig=odeconfig)
        cgen.generate(odeconfig.tdname + ".pyx")

        code = compile('from ' + odeconfig.tdname + ' import cy_td_ode_rhs',
                       '<string>', 'exec')
        exec(code, globals())
        odeconfig.tdfunc = cy_td_ode_rhs
    #
    # setup integrator
    #
    initial_vector = psi0.full().ravel()
    r = scipy.integrate.ode(odeconfig.tdfunc)
    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(' + string + ')', '<string>', 'exec')
    exec(code)

    #
    # call generic ODE code
    #
    return _generic_ode_solve(r, psi0, tlist, e_ops, opt, progress_bar,
                              norm, dims=psi0.dims)
Ejemplo n.º 2
0
def rhs_generate(H,
                 c_ops,
                 args={},
                 options=Options(),
                 name=None,
                 cleanup=True):
    """
    Generates the Cython functions needed for solving the dynamics of a
    given system using the mesolve function inside a parfor loop.

    Parameters
    ----------
    H : qobj
        System Hamiltonian.

    c_ops : list
        ``list`` of collapse operators.

    args : dict
        Arguments for time-dependent Hamiltonian and collapse operator terms.

    options : Options
        Instance of ODE solver options.

    name: str
        Name of generated RHS

    cleanup: bool
        Whether the generated cython file should be automatically removed or
        not.

    Notes
    -----
    Using this function with any solver other than the mesolve function
    will result in an error.

    """
    config.reset()
    config.options = options

    if name:
        config.tdname = name
    else:
        config.tdname = "rhs" + str(os.getpid()) + str(config.cgen_num)

    Lconst = 0

    Ldata = []
    Linds = []
    Lptrs = []
    Lcoeff = []

    # loop over all hamiltonian terms, convert to superoperator form and
    # add the data of sparse matrix represenation to

    msg = "Incorrect specification of time-dependence: "

    for h_spec in H:
        if isinstance(h_spec, Qobj):
            h = h_spec

            if not isinstance(h, Qobj):
                raise TypeError(msg + "expected Qobj")

            if h.isoper:
                Lconst += -1j * (spre(h) - spost(h))
            elif h.issuper:
                Lconst += h
            else:
                raise TypeError(msg + "expected operator or superoperator")

        elif isinstance(h_spec, list):
            h = h_spec[0]
            h_coeff = h_spec[1]

            if not isinstance(h, Qobj):
                raise TypeError(msg + "expected Qobj")

            if h.isoper:
                L = -1j * (spre(h) - spost(h))
            elif h.issuper:
                L = h
            else:
                raise TypeError(msg + "expected operator or superoperator")

            Ldata.append(L.data.data)
            Linds.append(L.data.indices)
            Lptrs.append(L.data.indptr)
            Lcoeff.append(h_coeff)

        else:
            raise TypeError(msg + "expected string format")

    # loop over all collapse operators
    for c_spec in c_ops:
        if isinstance(c_spec, Qobj):
            c = c_spec

            if not isinstance(c, Qobj):
                raise TypeError(msg + "expected Qobj")

            if c.isoper:
                cdc = c.dag() * c
                Lconst += spre(c) * spost(c.dag()) - 0.5 * spre(cdc) \
                                                   - 0.5 * spost(cdc)
            elif c.issuper:
                Lconst += c
            else:
                raise TypeError(msg + "expected operator or superoperator")

        elif isinstance(c_spec, list):
            c = c_spec[0]
            c_coeff = c_spec[1]

            if not isinstance(c, Qobj):
                raise TypeError(msg + "expected Qobj")

            if c.isoper:
                cdc = c.dag() * c
                L = spre(c) * spost(c.dag()) - 0.5 * spre(cdc) \
                                             - 0.5 * spost(cdc)
                c_coeff = "(" + c_coeff + ")**2"
            elif c.issuper:
                L = c
            else:
                raise TypeError(msg + "expected operator or superoperator")

            Ldata.append(L.data.data)
            Linds.append(L.data.indices)
            Lptrs.append(L.data.indptr)
            Lcoeff.append(c_coeff)

        else:
            raise TypeError(msg + "expected string format")

    # add the constant part of the lagrangian
    if Lconst != 0:
        Ldata.append(Lconst.data.data)
        Linds.append(Lconst.data.indices)
        Lptrs.append(Lconst.data.indptr)
        Lcoeff.append("1.0")

    # the total number of liouvillian terms (hamiltonian terms + collapse
    # operators)
    n_L_terms = len(Ldata)

    cgen = Codegen(h_terms=n_L_terms,
                   h_tdterms=Lcoeff,
                   args=args,
                   config=config)
    cgen.generate(config.tdname + ".pyx")

    code = compile('from ' + config.tdname + ' import cy_td_ode_rhs',
                   '<string>', 'exec')
    exec(code, globals())

    config.tdfunc = cy_td_ode_rhs

    if cleanup:
        try:
            os.remove(config.tdname + ".pyx")
        except:
            pass
Ejemplo n.º 3
0
def _sesolve_list_str_td(H_list, psi0, tlist, e_ops, args, opt,
                         progress_bar):
    """
    Internal function for solving the master equation. See mesolve for usage.
    """

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

    #
    # check initial state: must be a density matrix
    #
    if not isket(psi0):
        raise TypeError("The unitary solver requires a ket as initial state")

    #
    # construct liouvillian
    #
    Ldata = []
    Linds = []
    Lptrs = []
    Lcoeff = []

    # loop over all hamiltonian terms, convert to superoperator form and
    # add the data of sparse matrix representation to h_coeff
    for h_spec in H_list:

        if isinstance(h_spec, Qobj):
            h = h_spec
            h_coeff = "1.0"

        elif isinstance(h_spec, list):
            h = h_spec[0]
            h_coeff = h_spec[1]

        else:
            raise TypeError("Incorrect specification of time-dependent " +
                            "Hamiltonian (expected string format)")

        L = -1j * h

        Ldata.append(L.data.data)
        Linds.append(L.data.indices)
        Lptrs.append(L.data.indptr)
        Lcoeff.append(h_coeff)

    # the total number of liouvillian terms (hamiltonian terms +
    # collapse operators)
    n_L_terms = len(Ldata)

    #
    # setup ode args string: we expand the list Ldata, Linds and Lptrs into
    # and explicit list of parameters
    #
    string_list = []
    for k in range(n_L_terms):
        string_list.append("Ldata[%d], Linds[%d], Lptrs[%d]" % (k, k, k))
    for name, value in args.items():
        string_list.append(str(value))
    parameter_string = ",".join(string_list)

    #
    # generate and compile new cython code if necessary
    #
    if not opt.rhs_reuse or odeconfig.tdfunc is None:
        if opt.rhs_filename is None:
            odeconfig.tdname = "rhs" + str(odeconfig.cgen_num)
        else:
            odeconfig.tdname = opt.rhs_filename
        cgen = Codegen(h_terms=n_L_terms, h_tdterms=Lcoeff, args=args,
                       odeconfig=odeconfig)
        cgen.generate(odeconfig.tdname + ".pyx")

        code = compile('from ' + odeconfig.tdname + ' import cy_td_ode_rhs',
                       '<string>', 'exec')
        exec(code, globals())
        odeconfig.tdfunc = cy_td_ode_rhs

    #
    # setup integrator
    #
    initial_vector = psi0.full().ravel()
    r = scipy.integrate.ode(odeconfig.tdfunc)
    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)

    #
    # call generic ODE code
    #
    return _generic_ode_solve(r, psi0, tlist, e_ops, opt, progress_bar,
                              norm, dims=psi0.dims)
Ejemplo n.º 4
0
def _mesolve_list_str_td(H_list, rho0, tlist, c_list, e_ops, args, opt, progress_bar):
    """
    Internal function for solving the master equation. See mesolve for usage.
    """

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

    #
    # check initial state: must be a density matrix
    #
    if isket(rho0):
        rho0 = rho0 * rho0.dag()

    #
    # construct liouvillian
    #
    Lconst = 0

    Ldata = []
    Linds = []
    Lptrs = []
    Lcoeff = []

    # loop over all hamiltonian terms, convert to superoperator form and
    # add the data of sparse matrix representation to
    for h_spec in H_list:

        if isinstance(h_spec, Qobj):
            h = h_spec

            if isoper(h):
                Lconst += -1j * (spre(h) - spost(h))
            elif issuper(h):
                Lconst += h
            else:
                raise TypeError(
                    "Incorrect specification of time-dependent "
                    + "Hamiltonian (expected operator or "
                    + "superoperator)"
                )

        elif isinstance(h_spec, list):
            h = h_spec[0]
            h_coeff = h_spec[1]

            if isoper(h):
                L = -1j * (spre(h) - spost(h))
            elif issuper(h):
                L = h
            else:
                raise TypeError(
                    "Incorrect specification of time-dependent "
                    + "Hamiltonian (expected operator or "
                    + "superoperator)"
                )

            Ldata.append(L.data.data)
            Linds.append(L.data.indices)
            Lptrs.append(L.data.indptr)
            Lcoeff.append(h_coeff)

        else:
            raise TypeError("Incorrect specification of time-dependent " + "Hamiltonian (expected string format)")

    # loop over all collapse operators
    for c_spec in c_list:

        if isinstance(c_spec, Qobj):
            c = c_spec

            if isoper(c):
                cdc = c.dag() * c
                Lconst += spre(c) * spost(c.dag()) - 0.5 * spre(cdc) - 0.5 * spost(cdc)
            elif issuper(c):
                Lconst += c
            else:
                raise TypeError(
                    "Incorrect specification of time-dependent "
                    + "Liouvillian (expected operator or "
                    + "superoperator)"
                )

        elif isinstance(c_spec, list):
            c = c_spec[0]
            c_coeff = c_spec[1]

            if isoper(c):
                cdc = c.dag() * c
                L = spre(c) * spost(c.dag()) - 0.5 * spre(cdc) - 0.5 * spost(cdc)
                c_coeff = "(" + c_coeff + ")**2"
            elif issuper(c):
                L = c
            else:
                raise TypeError(
                    "Incorrect specification of time-dependent "
                    + "Liouvillian (expected operator or "
                    + "superoperator)"
                )

            Ldata.append(L.data.data)
            Linds.append(L.data.indices)
            Lptrs.append(L.data.indptr)
            Lcoeff.append(c_coeff)

        else:
            raise TypeError(
                "Incorrect specification of time-dependent " + "collapse operators (expected string format)"
            )

    # add the constant part of the lagrangian
    if Lconst != 0:
        Ldata.append(Lconst.data.data)
        Linds.append(Lconst.data.indices)
        Lptrs.append(Lconst.data.indptr)
        Lcoeff.append("1.0")

    # the total number of liouvillian terms (hamiltonian terms +
    # collapse operators)
    n_L_terms = len(Ldata)

    #
    # setup ode args string: we expand the list Ldata, Linds and Lptrs into
    # and explicit list of parameters
    #
    string_list = []
    for k in range(n_L_terms):
        string_list.append("Ldata[%d], Linds[%d], Lptrs[%d]" % (k, k, k))
    for name, value in args.items():
        if isinstance(value, np.ndarray):
            string_list.append(name)
        else:
            string_list.append(str(value))
    parameter_string = ",".join(string_list)

    #
    # generate and compile new cython code if necessary
    #
    if not opt.rhs_reuse or config.tdfunc is None:
        if opt.rhs_filename is None:
            config.tdname = "rhs" + str(os.getpid()) + str(config.cgen_num)
        else:
            config.tdname = opt.rhs_filename
        cgen = Codegen(h_terms=n_L_terms, h_tdterms=Lcoeff, args=args, config=config)
        cgen.generate(config.tdname + ".pyx")

        code = compile("from " + config.tdname + " import cy_td_ode_rhs", "<string>", "exec")
        exec(code, globals())
        config.tdfunc = cy_td_ode_rhs

    #
    # setup integrator
    #
    initial_vector = mat2vec(rho0.full()).ravel()
    r = scipy.integrate.ode(config.tdfunc)
    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)

    #
    # call generic ODE code
    #
    return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
Ejemplo n.º 5
0
def _mesolve_list_td(H_func, rho0, tlist, c_op_list, e_ops, args, opt, progress_bar):
    """
    Evolve the density matrix using an ODE solver with time dependent
    Hamiltonian.
    """

    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:
            return _sesolve_list_td(H_func, rho0, tlist, e_ops, args, opt, progress_bar)

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

    #
    # construct liouvillian
    #
    if len(H_func) != 2:
        raise TypeError("Time-dependent Hamiltonian list must have two terms.")
    if not isinstance(H_func[0], (list, np.ndarray)) or len(H_func[0]) <= 1:
        raise TypeError("Time-dependent Hamiltonians must be a list " + "with two or more terms")
    if (not isinstance(H_func[1], (list, np.ndarray))) or (len(H_func[1]) != (len(H_func[0]) - 1)):
        raise TypeError(
            "Time-dependent coefficients must be list with "
            + "length N-1 where N is the number of "
            + "Hamiltonian terms."
        )

    if opt.rhs_reuse and config.tdfunc is None:
        rhs_generate(H_func, args)

    lenh = len(H_func[0])
    if opt.tidy:
        H_func[0] = [(H_func[0][k]).tidyup() for k in range(lenh)]
    L_func = [[liouvillian(H_func[0][0], c_op_list)], H_func[1]]
    for m in range(1, lenh):
        L_func[0].append(liouvillian(H_func[0][m], []))

    # create data arrays for time-dependent RHS function
    Ldata = [L_func[0][k].data.data for k in range(lenh)]
    Linds = [L_func[0][k].data.indices for k in range(lenh)]
    Lptrs = [L_func[0][k].data.indptr for k in range(lenh)]
    # setup ode args string
    string = ""
    for k in range(lenh):
        string += "Ldata[%d], Linds[%d], Lptrs[%d]," % (k, k, k)

    if args:
        td_consts = args.items()
        for elem in td_consts:
            string += str(elem[1])
            if elem != td_consts[-1]:
                string += ","

    # run code generator
    if not opt.rhs_reuse or config.tdfunc is None:
        if opt.rhs_filename is None:
            config.tdname = "rhs" + str(os.getpid()) + str(config.cgen_num)
        else:
            config.tdname = opt.rhs_filename
        cgen = Codegen(h_terms=n_L_terms, h_tdterms=Lcoeff, args=args, config=config)
        cgen.generate(config.tdname + ".pyx")

        code = compile("from " + config.tdname + " import cy_td_ode_rhs", "<string>", "exec")
        exec(code, globals())
        config.tdfunc = cy_td_ode_rhs

    #
    # setup integrator
    #
    initial_vector = mat2vec(rho0.full()).ravel()
    r = scipy.integrate.ode(config.tdfunc)
    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(" + string + ")", "<string>", "exec")
    exec(code)

    #
    # call generic ODE code
    #
    return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
Ejemplo n.º 6
0
def rhs_generate(H, c_ops, args={}, options=Options(), name=None,
                 cleanup=True):
    """
    Generates the Cython functions needed for solving the dynamics of a
    given system using the mesolve function inside a parfor loop.

    Parameters
    ----------
    H : qobj
        System Hamiltonian.

    c_ops : list
        ``list`` of collapse operators.

    args : dict
        Arguments for time-dependent Hamiltonian and collapse operator terms.

    options : Options
        Instance of ODE solver options.

    name: str
        Name of generated RHS

    cleanup: bool
        Whether the generated cython file should be automatically removed or
        not.

    Notes
    -----
    Using this function with any solver other than the mesolve function
    will result in an error.

    """
    config.reset()
    config.options = options

    if name:
        config.tdname = name
    else:
        config.tdname = "rhs" + str(os.getpid()) + str(config.cgen_num)

    Lconst = 0

    Ldata = []
    Linds = []
    Lptrs = []
    Lcoeff = []

    # loop over all hamiltonian terms, convert to superoperator form and
    # add the data of sparse matrix represenation to

    msg = "Incorrect specification of time-dependence: "

    for h_spec in H:
        if isinstance(h_spec, Qobj):
            h = h_spec

            if not isinstance(h, Qobj):
                raise TypeError(msg + "expected Qobj")

            if h.isoper:
                Lconst += -1j * (spre(h) - spost(h))
            elif h.issuper:
                Lconst += h
            else:
                raise TypeError(msg + "expected operator or superoperator")

        elif isinstance(h_spec, list):
            h = h_spec[0]
            h_coeff = h_spec[1]

            if not isinstance(h, Qobj):
                raise TypeError(msg + "expected Qobj")

            if h.isoper:
                L = -1j * (spre(h) - spost(h))
            elif h.issuper:
                L = h
            else:
                raise TypeError(msg + "expected operator or superoperator")

            Ldata.append(L.data.data)
            Linds.append(L.data.indices)
            Lptrs.append(L.data.indptr)
            Lcoeff.append(h_coeff)

        else:
            raise TypeError(msg + "expected string format")

    # loop over all collapse operators
    for c_spec in c_ops:
        if isinstance(c_spec, Qobj):
            c = c_spec

            if not isinstance(c, Qobj):
                raise TypeError(msg + "expected Qobj")

            if c.isoper:
                cdc = c.dag() * c
                Lconst += spre(c) * spost(c.dag()) - 0.5 * spre(cdc) \
                                                   - 0.5 * spost(cdc)
            elif c.issuper:
                Lconst += c
            else:
                raise TypeError(msg + "expected operator or superoperator")

        elif isinstance(c_spec, list):
            c = c_spec[0]
            c_coeff = c_spec[1]

            if not isinstance(c, Qobj):
                raise TypeError(msg + "expected Qobj")

            if c.isoper:
                cdc = c.dag() * c
                L = spre(c) * spost(c.dag()) - 0.5 * spre(cdc) \
                                             - 0.5 * spost(cdc)
                c_coeff = "(" + c_coeff + ")**2"
            elif c.issuper:
                L = c
            else:
                raise TypeError(msg + "expected operator or superoperator")

            Ldata.append(L.data.data)
            Linds.append(L.data.indices)
            Lptrs.append(L.data.indptr)
            Lcoeff.append(c_coeff)

        else:
            raise TypeError(msg + "expected string format")

    # add the constant part of the lagrangian
    if Lconst != 0:
        Ldata.append(Lconst.data.data)
        Linds.append(Lconst.data.indices)
        Lptrs.append(Lconst.data.indptr)
        Lcoeff.append("1.0")

    # the total number of liouvillian terms (hamiltonian terms + collapse
    # operators)
    n_L_terms = len(Ldata)

    cgen = Codegen(h_terms=n_L_terms, h_tdterms=Lcoeff, args=args,
                   config=config)
    cgen.generate(config.tdname + ".pyx")

    code = compile('from ' + config.tdname +
                   ' import cy_td_ode_rhs', '<string>', 'exec')
    exec(code, globals())

    config.tdfunc = cy_td_ode_rhs

    if cleanup:
        try:
            os.remove(config.tdname + ".pyx")
        except:
            pass
Ejemplo n.º 7
0
def _mc_data_config(H, psi0, h_stuff, c_ops, c_stuff, args, e_ops,
                    options, config):
    """Creates the appropriate data structures for the monte carlo solver
    based on the given time-dependent, or indepdendent, format.
    """

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

    config.soft_reset()

    # take care of expectation values, if any
    if any(e_ops):
        config.e_num = len(e_ops)
        for op in e_ops:
            if isinstance(op, list):
                op = op[0]
            config.e_ops_data.append(op.data.data)
            config.e_ops_ind.append(op.data.indices)
            config.e_ops_ptr.append(op.data.indptr)
            config.e_ops_isherm.append(op.isherm)

        config.e_ops_data = np.array(config.e_ops_data)
        config.e_ops_ind = np.array(config.e_ops_ind)
        config.e_ops_ptr = np.array(config.e_ops_ptr)
        config.e_ops_isherm = np.array(config.e_ops_isherm)

    # take care of collapse operators, if any
    if any(c_ops):
        config.c_num = len(c_ops)
        for c_op in c_ops:
            if isinstance(c_op, list):
                c_op = c_op[0]
            n_op = c_op.dag() * c_op
            config.c_ops_data.append(c_op.data.data)
            config.c_ops_ind.append(c_op.data.indices)
            config.c_ops_ptr.append(c_op.data.indptr)
            # norm ops
            config.n_ops_data.append(n_op.data.data)
            config.n_ops_ind.append(n_op.data.indices)
            config.n_ops_ptr.append(n_op.data.indptr)
        # to array
        config.c_ops_data = np.array(config.c_ops_data)
        config.c_ops_ind = np.array(config.c_ops_ind)
        config.c_ops_ptr = np.array(config.c_ops_ptr)

        config.n_ops_data = np.array(config.n_ops_data)
        config.n_ops_ind = np.array(config.n_ops_ind)
        config.n_ops_ptr = np.array(config.n_ops_ptr)

    if config.tflag == 0:
        # CONSTANT H & C_OPS CODE
        # -----------------------

        if config.cflag:
            config.c_const_inds = np.arange(len(c_ops))
            for c_op in c_ops:
                n_op = c_op.dag() * c_op
                H -= 0.5j * \
                    n_op  # combine Hamiltonian and collapse terms into one
        # construct Hamiltonian data structures
        if options.tidy:
            H = H.tidyup(options.atol)
        config.h_data = -1.0j * H.data.data
        config.h_ind = H.data.indices
        config.h_ptr = H.data.indptr

    elif config.tflag in [1, 10, 11]:
        # STRING BASED TIME-DEPENDENCE
        # ----------------------------

        # take care of arguments for collapse operators, if any
        if any(args):
            for item in args.items():
                config.c_args.append(item[1])
        # constant Hamiltonian / string-type collapse operators
        if config.tflag == 1:
            H_inds = np.arange(1)
            H_tdterms = 0
            len_h = 1
            C_inds = np.arange(config.c_num)
            # find inds of time-dependent terms
            C_td_inds = np.array(c_stuff[2])
            # find inds of constant terms
            C_const_inds = np.setdiff1d(C_inds, C_td_inds)
            # extract time-dependent coefficients (strings)
            C_tdterms = [c_ops[k][1] for k in C_td_inds]
            # store indicies of constant collapse terms
            config.c_const_inds = C_const_inds
            # store indicies of time-dependent collapse terms
            config.c_td_inds = C_td_inds

            for k in config.c_const_inds:
                H -= 0.5j * (c_ops[k].dag() * c_ops[k])
            if options.tidy:
                H = H.tidyup(options.atol)
            config.h_data = [H.data.data]
            config.h_ind = [H.data.indices]
            config.h_ptr = [H.data.indptr]
            for k in config.c_td_inds:
                op = c_ops[k][0].dag() * c_ops[k][0]
                config.h_data.append(-0.5j * op.data.data)
                config.h_ind.append(op.data.indices)
                config.h_ptr.append(op.data.indptr)
            config.h_data = -1.0j * np.array(config.h_data)
            config.h_ind = np.array(config.h_ind)
            config.h_ptr = np.array(config.h_ptr)

        else:
            # string-type Hamiltonian & at least one string-type
            # collapse operator
            # -----------------

            H_inds = np.arange(len(H))
            # find inds of time-dependent terms
            H_td_inds = np.array(h_stuff[2])
            # find inds of constant terms
            H_const_inds = np.setdiff1d(H_inds, H_td_inds)
            # extract time-dependent coefficients (strings or functions)
            H_tdterms = [H[k][1] for k in H_td_inds]
            # combine time-INDEPENDENT terms into one.
            H = np.array([np.sum(H[k] for k in H_const_inds)] +
                         [H[k][0] for k in H_td_inds], dtype=object)
            len_h = len(H)
            H_inds = np.arange(len_h)
            # store indicies of time-dependent Hamiltonian terms
            config.h_td_inds = np.arange(1, len_h)
            # if there are any collapse operators
            if config.c_num > 0:
                if config.tflag == 10:
                    # constant collapse operators
                    config.c_const_inds = np.arange(config.c_num)
                    for k in config.c_const_inds:
                        H[0] -= 0.5j * (c_ops[k].dag() * c_ops[k])
                    C_inds = np.arange(config.c_num)
                    C_tdterms = np.array([])
                else:
                    # some time-dependent collapse terms
                    C_inds = np.arange(config.c_num)
                    # find inds of time-dependent terms
                    C_td_inds = np.array(c_stuff[2])
                    # find inds of constant terms
                    C_const_inds = np.setdiff1d(C_inds, C_td_inds)
                    C_tdterms = [c_ops[k][1] for k in C_td_inds]
                    # extract time-dependent coefficients (strings)
                    # store indicies of constant collapse terms
                    config.c_const_inds = C_const_inds
                    # store indicies of time-dependent collapse terms
                    config.c_td_inds = C_td_inds
                    for k in config.c_const_inds:
                        H[0] -= 0.5j * (c_ops[k].dag() * c_ops[k])
            else:
                # set empty objects if no collapse operators
                C_const_inds = np.arange(config.c_num)
                config.c_const_inds = np.arange(config.c_num)
                config.c_td_inds = np.array([])
                C_tdterms = np.array([])
                C_inds = np.array([])

            # tidyup
            if options.tidy:
                H = np.array([H[k].tidyup(options.atol)
                              for k in range(len_h)], dtype=object)
            # construct data sets
            config.h_data = [H[k].data.data for k in range(len_h)]
            config.h_ind = [H[k].data.indices for k in range(len_h)]
            config.h_ptr = [H[k].data.indptr for k in range(len_h)]
            for k in config.c_td_inds:
                config.h_data.append(-0.5j * config.n_ops_data[k])
                config.h_ind.append(config.n_ops_ind[k])
                config.h_ptr.append(config.n_ops_ptr[k])
            config.h_data = -1.0j * np.array(config.h_data)
            config.h_ind = np.array(config.h_ind)
            config.h_ptr = np.array(config.h_ptr)

        # set execuatble code for collapse expectation values and spmv
        col_spmv_code = ("state = _cy_col_spmv_func(j, ODE.t, " +
                         "config.c_ops_data[j], config.c_ops_ind[j], " +
                         "config.c_ops_ptr[j], ODE.y")
        col_expect_code = ("for i in config.c_td_inds: " +
                           "n_dp.append(_cy_col_expect_func(i, ODE.t, " +
                           "config.n_ops_data[i], " +
                           "config.n_ops_ind[i], " +
                           "config.n_ops_ptr[i], ODE.y")
        for kk in range(len(config.c_args)):
            col_spmv_code += ",config.c_args[" + str(kk) + "]"
            col_expect_code += ",config.c_args[" + str(kk) + "]"
        col_spmv_code += ")"
        col_expect_code += "))"

        config.col_spmv_code = col_spmv_code
        config.col_expect_code = col_expect_code

        # setup ode args string
        config.string = ""
        data_range = range(len(config.h_data))
        for k in data_range:
            config.string += ("config.h_data[" + str(k) +
                              "], config.h_ind[" + str(k) +
                              "], config.h_ptr[" + str(k) + "]")
            if k != data_range[-1]:
                config.string += ","
        # attach args to ode args string
        if len(config.c_args) > 0:
            for kk in range(len(config.c_args)):
                config.string += "," + "config.c_args[" + str(kk) + "]"

        name = "rhs" + str(os.getpid()) + str(config.cgen_num)
        config.tdname = name
        cgen = Codegen(H_inds, H_tdterms, config.h_td_inds, args,
                       C_inds, C_tdterms, config.c_td_inds, type='mc',
                       config=config)
        cgen.generate(name + ".pyx")

    elif config.tflag in [2, 20, 22]:
        # PYTHON LIST-FUNCTION BASED TIME-DEPENDENCE
        # ------------------------------------------

        # take care of Hamiltonian
        if config.tflag == 2:
            # constant Hamiltonian, at least one function based collapse
            # operators
            H_inds = np.array([0])
            H_tdterms = 0
            len_h = 1
        else:
            # function based Hamiltonian
            H_inds = np.arange(len(H))
            H_td_inds = np.array(h_stuff[1])
            H_const_inds = np.setdiff1d(H_inds, H_td_inds)
            config.h_funcs = np.array([H[k][1] for k in H_td_inds])
            config.h_func_args = args
            Htd = np.array([H[k][0] for k in H_td_inds], dtype=object)
            config.h_td_inds = np.arange(len(Htd))
            H = np.sum(H[k] for k in H_const_inds)

        # take care of collapse operators
        C_inds = np.arange(config.c_num)
        # find inds of time-dependent terms
        C_td_inds = np.array(c_stuff[1])
        # find inds of constant terms
        C_const_inds = np.setdiff1d(C_inds, C_td_inds)
        # store indicies of constant collapse terms
        config.c_const_inds = C_const_inds
        # store indicies of time-dependent collapse terms
        config.c_td_inds = C_td_inds
        config.c_funcs = np.zeros(config.c_num, dtype=FunctionType)
        for k in config.c_td_inds:
            config.c_funcs[k] = c_ops[k][1]
        config.c_func_args = args

        # combine constant collapse terms with constant H and construct data
        for k in config.c_const_inds:
            H -= 0.5j * (c_ops[k].dag() * c_ops[k])
        if options.tidy:
            H = H.tidyup(options.atol)
            Htd = np.array([Htd[j].tidyup(options.atol)
                            for j in config.h_td_inds], dtype=object)
        # setup constant H terms data
        config.h_data = -1.0j * H.data.data
        config.h_ind = H.data.indices
        config.h_ptr = H.data.indptr

        # setup td H terms data
        config.h_td_data = np.array(
            [-1.0j * Htd[k].data.data for k in config.h_td_inds])
        config.h_td_ind = np.array(
            [Htd[k].data.indices for k in config.h_td_inds])
        config.h_td_ptr = np.array(
            [Htd[k].data.indptr for k in config.h_td_inds])

    elif config.tflag == 3:
        # PYTHON FUNCTION BASED HAMILTONIAN
        # ---------------------------------

        # take care of Hamiltonian
        config.h_funcs = H
        config.h_func_args = args

        # take care of collapse operators
        config.c_const_inds = np.arange(config.c_num)
        config.c_td_inds = np.array([])
        if len(config.c_const_inds) > 0:
            H = 0
            for k in config.c_const_inds:
                H -= 0.5j * (c_ops[k].dag() * c_ops[k])
            if options.tidy:
                H = H.tidyup(options.atol)
            config.h_data = -1.0j * H.data.data
            config.h_ind = H.data.indices
            config.h_ptr = H.data.indptr
Ejemplo n.º 8
0
def _mesolve_list_td(H_func, rho0, tlist, c_op_list, e_ops, args, opt,
                     progress_bar):
    """
    Evolve the density matrix using an ODE solver with time dependent
    Hamiltonian.
    """

    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:
            return _sesolve_list_td(H_func, rho0, tlist, e_ops, args, opt,
                                    progress_bar)

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

    #
    # construct liouvillian
    #
    if len(H_func) != 2:
        raise TypeError('Time-dependent Hamiltonian list must have two terms.')
    if not isinstance(H_func[0], (list, np.ndarray)) or len(H_func[0]) <= 1:
        raise TypeError('Time-dependent Hamiltonians must be a list ' +
                        'with two or more terms')
    if (not isinstance(H_func[1], (list, np.ndarray))) or \
       (len(H_func[1]) != (len(H_func[0]) - 1)):
        raise TypeError('Time-dependent coefficients must be list with ' +
                        'length N-1 where N is the number of ' +
                        'Hamiltonian terms.')

    if opt.rhs_reuse and config.tdfunc is None:
        rhs_generate(H_func, args)

    lenh = len(H_func[0])
    if opt.tidy:
        H_func[0] = [(H_func[0][k]).tidyup() for k in range(lenh)]
    L_func = [[liouvillian(H_func[0][0], c_op_list)], H_func[1]]
    for m in range(1, lenh):
        L_func[0].append(liouvillian(H_func[0][m], []))

    # create data arrays for time-dependent RHS function
    Ldata = [L_func[0][k].data.data for k in range(lenh)]
    Linds = [L_func[0][k].data.indices for k in range(lenh)]
    Lptrs = [L_func[0][k].data.indptr for k in range(lenh)]
    # setup ode args string
    string = ""
    for k in range(lenh):
        string += ("Ldata[%d], Linds[%d], Lptrs[%d]," % (k, k, k))

    if args:
        td_consts = args.items()
        for elem in td_consts:
            string += str(elem[1])
            if elem != td_consts[-1]:
                string += (",")

    # run code generator
    if not opt.rhs_reuse or config.tdfunc is None:
        if opt.rhs_filename is None:
            config.tdname = "rhs" + str(os.getpid()) + str(config.cgen_num)
        else:
            config.tdname = opt.rhs_filename
        cgen = Codegen(h_terms=n_L_terms,
                       h_tdterms=Lcoeff,
                       args=args,
                       config=config)
        cgen.generate(config.tdname + ".pyx")

        code = compile('from ' + config.tdname + ' import cy_td_ode_rhs',
                       '<string>', 'exec')
        exec(code, globals())
        config.tdfunc = cy_td_ode_rhs

    #
    # setup integrator
    #
    initial_vector = mat2vec(rho0.full()).ravel()
    r = scipy.integrate.ode(config.tdfunc)
    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(' + string + ')', '<string>', 'exec')
    exec(code)

    #
    # call generic ODE code
    #
    return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
Ejemplo n.º 9
0
def _mesolve_list_str_td(H_list, rho0, tlist, c_list, e_ops, args, opt,
                         progress_bar):
    """
    Internal function for solving the master equation. See mesolve for usage.
    """

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

    #
    # check initial state: must be a density matrix
    #
    if isket(rho0):
        rho0 = rho0 * rho0.dag()

    #
    # construct liouvillian
    #
    Lconst = 0

    Ldata = []
    Linds = []
    Lptrs = []
    Lcoeff = []
    Lobj = []

    # loop over all hamiltonian terms, convert to superoperator form and
    # add the data of sparse matrix representation to
    for h_spec in H_list:

        if isinstance(h_spec, Qobj):
            h = h_spec

            if isoper(h):
                Lconst += -1j * (spre(h) - spost(h))
            elif issuper(h):
                Lconst += h
            else:
                raise TypeError("Incorrect specification of time-dependent " +
                                "Hamiltonian (expected operator or " +
                                "superoperator)")

        elif isinstance(h_spec, list):
            h = h_spec[0]
            h_coeff = h_spec[1]

            if isoper(h):
                L = -1j * (spre(h) - spost(h))
            elif issuper(h):
                L = h
            else:
                raise TypeError("Incorrect specification of time-dependent " +
                                "Hamiltonian (expected operator or " +
                                "superoperator)")

            Ldata.append(L.data.data)
            Linds.append(L.data.indices)
            Lptrs.append(L.data.indptr)
            if isinstance(h_coeff, Cubic_Spline):
                Lobj.append(h_coeff.coeffs)
            Lcoeff.append(h_coeff)

        else:
            raise TypeError("Incorrect specification of time-dependent " +
                            "Hamiltonian (expected string format)")

    # loop over all collapse operators
    for c_spec in c_list:

        if isinstance(c_spec, Qobj):
            c = c_spec

            if isoper(c):
                cdc = c.dag() * c
                Lconst += spre(c) * spost(c.dag()) - 0.5 * spre(cdc) \
                                                   - 0.5 * spost(cdc)
            elif issuper(c):
                Lconst += c
            else:
                raise TypeError("Incorrect specification of time-dependent " +
                                "Liouvillian (expected operator or " +
                                "superoperator)")

        elif isinstance(c_spec, list):
            c = c_spec[0]
            c_coeff = c_spec[1]

            if isoper(c):
                cdc = c.dag() * c
                L = spre(c) * spost(c.dag()) - 0.5 * spre(cdc) \
                                             - 0.5 * spost(cdc)
                c_coeff = "(" + c_coeff + ")**2"
            elif issuper(c):
                L = c
            else:
                raise TypeError("Incorrect specification of time-dependent " +
                                "Liouvillian (expected operator or " +
                                "superoperator)")

            Ldata.append(L.data.data)
            Linds.append(L.data.indices)
            Lptrs.append(L.data.indptr)
            Lcoeff.append(c_coeff)

        else:
            raise TypeError("Incorrect specification of time-dependent " +
                            "collapse operators (expected string format)")

    # add the constant part of the lagrangian
    if Lconst != 0:
        Ldata.append(Lconst.data.data)
        Linds.append(Lconst.data.indices)
        Lptrs.append(Lconst.data.indptr)
        Lcoeff.append("1.0")

    # the total number of liouvillian terms (hamiltonian terms +
    # collapse operators)
    n_L_terms = len(Ldata)

    # Check which components should use OPENMP
    omp_components = None
    if qset.has_openmp:
        if opt.use_openmp:
            omp_components = openmp_components(Lptrs)

    #
    # setup ode args string: we expand the list Ldata, Linds and Lptrs into
    # and explicit list of parameters
    #
    string_list = []
    for k in range(n_L_terms):
        string_list.append("Ldata[%d], Linds[%d], Lptrs[%d]" % (k, k, k))
    # Add object terms to end of ode args string
    for k in range(len(Lobj)):
        string_list.append("Lobj[%d]" % k)
    for name, value in args.items():
        if isinstance(value, np.ndarray):
            string_list.append(name)
        else:
            string_list.append(str(value))
    parameter_string = ",".join(string_list)

    #
    # generate and compile new cython code if necessary
    #
    if not opt.rhs_reuse or config.tdfunc is None:
        if opt.rhs_filename is None:
            config.tdname = "rhs" + str(os.getpid()) + str(config.cgen_num)
        else:
            config.tdname = opt.rhs_filename
        cgen = Codegen(h_terms=n_L_terms,
                       h_tdterms=Lcoeff,
                       args=args,
                       config=config,
                       use_openmp=opt.use_openmp,
                       omp_components=omp_components,
                       omp_threads=opt.openmp_threads)
        cgen.generate(config.tdname + ".pyx")

        code = compile('from ' + config.tdname + ' import cy_td_ode_rhs',
                       '<string>', 'exec')
        exec(code, globals())
        config.tdfunc = cy_td_ode_rhs

    #
    # setup integrator
    #
    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])

    exec(code, locals(), args)

    #
    # call generic ODE code
    #
    return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
Ejemplo n.º 10
0
def _sesolve_list_str_td(H_list, psi0, tlist, e_ops, args, opt,
                         progress_bar):
    """
    Internal function for solving the master equation. See mesolve for usage.
    """

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

    #
    # check initial state: must be a density matrix
    #
    if not isket(psi0):
        raise TypeError("The unitary solver requires a ket as initial state")

    #
    # construct liouvillian
    #
    Ldata = []
    Linds = []
    Lptrs = []
    Lcoeff = []

    # loop over all hamiltonian terms, convert to superoperator form and
    # add the data of sparse matrix representation to h_coeff
    for h_spec in H_list:

        if isinstance(h_spec, Qobj):
            h = h_spec
            h_coeff = "1.0"

        elif isinstance(h_spec, list):
            h = h_spec[0]
            h_coeff = h_spec[1]

        else:
            raise TypeError("Incorrect specification of time-dependent " +
                            "Hamiltonian (expected string format)")

        L = -1j * h

        Ldata.append(L.data.data)
        Linds.append(L.data.indices)
        Lptrs.append(L.data.indptr)
        Lcoeff.append(h_coeff)

    # the total number of liouvillian terms (hamiltonian terms +
    # collapse operators)
    n_L_terms = len(Ldata)

    #
    # setup ode args string: we expand the list Ldata, Linds and Lptrs into
    # and explicit list of parameters
    #
    string_list = []
    for k in range(n_L_terms):
        string_list.append("Ldata[%d], Linds[%d], Lptrs[%d]" % (k, k, k))
    for name, value in args.items():
        string_list.append(str(value))
    parameter_string = ",".join(string_list)

    #
    # generate and compile new cython code if necessary
    #
    if not opt.rhs_reuse or odeconfig.tdfunc is None:
        if opt.rhs_filename is None:
            odeconfig.tdname = "rhs" + str(odeconfig.cgen_num)
        else:
            odeconfig.tdname = opt.rhs_filename
        cgen = Codegen(h_terms=n_L_terms, h_tdterms=Lcoeff, args=args,
                       odeconfig=odeconfig)
        cgen.generate(odeconfig.tdname + ".pyx")

        code = compile('from ' + odeconfig.tdname + ' import cy_td_ode_rhs',
                       '<string>', 'exec')
        exec(code, globals())
        odeconfig.tdfunc = cy_td_ode_rhs

    #
    # setup integrator
    #
    initial_vector = psi0.full().ravel()
    r = scipy.integrate.ode(odeconfig.tdfunc)
    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)

    #
    # call generic ODE code
    #
    return _generic_ode_solve(r, psi0, tlist, e_ops, opt, progress_bar,
                              norm, dims=psi0.dims)
Ejemplo n.º 11
0
def _sesolve_list_td(H_func, psi0, tlist, e_ops, args, opt, progress_bar):
    """!
    Evolve the wave function using an ODE solver with time-dependent
    Hamiltonian.
    """

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

    if not isket(psi0):
        raise TypeError("psi0 must be a ket")

    #
    # configure time-dependent terms and setup ODE solver
    #
    if len(H_func) != 2:
        raise TypeError('Time-dependent Hamiltonian list must have two terms.')
    if (not isinstance(H_func[0], (list, np.ndarray))) or \
       (len(H_func[0]) <= 1):
        raise TypeError('Time-dependent Hamiltonians must be a list with two '
                        + 'or more terms')
    if (not isinstance(H_func[1], (list, np.ndarray))) or \
       (len(H_func[1]) != (len(H_func[0]) - 1)):
        raise TypeError('Time-dependent coefficients must be list with ' +
                        'length N-1 where N is the number of ' +
                        'Hamiltonian terms.')
    tflag = 1
    if opt.rhs_reuse and odeconfig.tdfunc is None:
        print("No previous time-dependent RHS found.")
        print("Generating one for you...")
        rhs_generate(H_func, args)
    lenh = len(H_func[0])
    if opt.tidy:
        H_func[0] = [(H_func[0][k]).tidyup() for k in range(lenh)]
    # create data arrays for time-dependent RHS function
    Hdata = [-1.0j * H_func[0][k].data.data for k in range(lenh)]
    Hinds = [H_func[0][k].data.indices for k in range(lenh)]
    Hptrs = [H_func[0][k].data.indptr for k in range(lenh)]
    # setup ode args string
    string = ""
    for k in range(lenh):
        string += ("Hdata[" + str(k) + "],Hinds[" + str(k) +
                   "],Hptrs[" + str(k) + "],")

    if args:
        td_consts = args.items()
        for elem in td_consts:
            string += str(elem[1])
            if elem != td_consts[-1]:
                string += (",")

    # run code generator
    if not opt.rhs_reuse or odeconfig.tdfunc is None:
        if opt.rhs_filename is None:
            odeconfig.tdname = "rhs" + str(odeconfig.cgen_num)
        else:
            odeconfig.tdname = opt.rhs_filename
        cgen = Codegen(h_terms=n_L_terms, h_tdterms=Lcoeff, args=args,
                       odeconfig=odeconfig)
        cgen.generate(odeconfig.tdname + ".pyx")

        code = compile('from ' + odeconfig.tdname + ' import cy_td_ode_rhs',
                       '<string>', 'exec')
        exec(code, globals())
        odeconfig.tdfunc = cy_td_ode_rhs
    #
    # setup integrator
    #
    initial_vector = psi0.full().ravel()
    r = scipy.integrate.ode(odeconfig.tdfunc)
    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(' + string + ')', '<string>', 'exec')
    exec(code)

    #
    # call generic ODE code
    #
    return _generic_ode_solve(r, psi0, tlist, e_ops, opt, progress_bar,
                              norm, dims=psi0.dims)
Ejemplo n.º 12
0
def _sesolve_list_str_td(H_list, psi0, tlist, e_ops, args, opt,
                         progress_bar):
    """
    Internal function for solving the master equation. See mesolve for usage.
    """

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

    if psi0.isket:
        oper_evo = False
    elif psi0.isunitary:
        oper_evo = True
    else:
        raise TypeError("The unitary solver requires psi0 to be"
                        " a ket as initial state"
                        " or a unitary as initial operator.")

    #
    # construct dynamics generator
    #
    Ldata = []
    Linds = []
    Lptrs = []
    Lcoeff = []
    Lobj = []

    # loop over all hamiltonian terms, convert to superoperator form and
    # add the data of sparse matrix representation to h_coeff
    for h_spec in H_list:

        if isinstance(h_spec, Qobj):
            h = h_spec
            h_coeff = "1.0"

        elif isinstance(h_spec, list):
            h = h_spec[0]
            h_coeff = h_spec[1]

        else:
            raise TypeError("Incorrect specification of time-dependent " +
                            "Hamiltonian (expected string format)")

        L = -1j * h
        Ldata.append(L.data.data)
        Linds.append(L.data.indices)
        Lptrs.append(L.data.indptr)
        if isinstance(h_coeff, Cubic_Spline):
            Lobj.append(h_coeff.coeffs)
        Lcoeff.append(h_coeff)

    # the total number of Hamiltonian terms
    n_L_terms = len(Ldata)

    # Check which components should use OPENMP
    omp_components = None
    if qset.has_openmp:
        if opt.use_openmp:
            omp_components = openmp_components(Lptrs)

    #
    # setup ode args string: we expand the list Ldata, Linds and Lptrs into
    # and explicit list of parameters
    #
    string_list = []
    for k in range(n_L_terms):
        string_list.append("Ldata[%d], Linds[%d], Lptrs[%d]" % (k, k, k))
    # Add object terms to end of ode args string
    for k in range(len(Lobj)):
        string_list.append("Lobj[%d]" % k)

    for name, value in args.items():
        if isinstance(value, np.ndarray):
            string_list.append(name)
        else:
            string_list.append(str(value))
    parameter_string = ",".join(string_list)

    #
    # generate and compile new cython code if necessary
    #
    if not opt.rhs_reuse or config.tdfunc is None:
        if opt.rhs_filename is None:
            config.tdname = "rhs" + str(os.getpid()) + str(config.cgen_num)
        else:
            config.tdname = opt.rhs_filename
        cgen = Codegen(h_terms=n_L_terms, h_tdterms=Lcoeff, args=args,
                       config=config, use_openmp=opt.use_openmp,
                       omp_components=omp_components,
                       omp_threads=opt.openmp_threads)
        cgen.generate(config.tdname + ".pyx")

        code = compile('from ' + config.tdname + ' import cy_td_ode_rhs',
                       '<string>', 'exec')
        exec(code, globals())
        config.tdfunc = cy_td_ode_rhs

    #
    # setup integrator
    #
    if oper_evo:
        initial_vector = psi0.full().ravel('F')
        r = scipy.integrate.ode(_td_ode_rhs_oper)
        code = compile('r.set_f_params([' + parameter_string + '])',
                       '<string>', 'exec')
    else:
        initial_vector = psi0.full().ravel()
        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])

    exec(code, locals(), args)


    # Remove RHS cython file if necessary
    if not opt.rhs_reuse and config.tdname:
        _cython_build_cleanup(config.tdname)

    #
    # call generic ODE code
    #
    return _generic_ode_solve(r, psi0, tlist, e_ops, opt, progress_bar,
                              dims=psi0.dims)
Ejemplo n.º 13
0
def _mesolve_list_str_td(H_list, rho0, tlist, c_list, e_ops, args, opt,
                         progress_bar):
    """
    Internal function for solving the master equation. See mesolve for usage.
    """

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

    #
    # check initial state: must be a density matrix
    #
    if isket(rho0):
        rho0 = rho0 * rho0.dag()

    #
    # construct liouvillian
    #
    Lconst = 0

    Ldata = []
    Linds = []
    Lptrs = []
    Lcoeff = []
    Lobj = []
    me_cops_coeff = []
    me_cops_obj = []
    me_cops_obj_flags = []

    # loop over all hamiltonian terms, convert to superoperator form and
    # add the data of sparse matrix representation to
    n_not_const_terms = 0
    for h_spec in H_list:
        if isinstance(h_spec, Qobj):
            h = h_spec

            if isoper(h):
                Lconst += -1j * (spre(h) - spost(h))
            elif issuper(h):
                Lconst += h
            else:
                raise TypeError("Incorrect specification of time-dependent " +
                                "Hamiltonian (expected operator or " +
                                "superoperator)")

        elif isinstance(h_spec, list):
            n_not_const_terms +=1
            h = h_spec[0]
            h_coeff = h_spec[1]

            if isoper(h):
                L = -1j * (spre(h) - spost(h))
            elif issuper(h):
                L = h
            else:
                raise TypeError("Incorrect specification of time-dependent " +
                                "Hamiltonian (expected operator or " +
                                "superoperator)")

            Ldata.append(L.data.data)
            Linds.append(L.data.indices)
            Lptrs.append(L.data.indptr)
            if isinstance(h_coeff, Cubic_Spline):
                Lobj.append(h_coeff.coeffs)
            Lcoeff.append(h_coeff)

        else:
            raise TypeError("Incorrect specification of time-dependent " +
                            "Hamiltonian (expected string format)")


    
    # loop over all collapse operators
    for c_spec in c_list:
        if isinstance(c_spec, Qobj):
            c = c_spec

            if isoper(c):
                cdc = c.dag() * c
                Lconst += spre(c) * spost(c.dag()) - 0.5 * spre(cdc) \
                                                   - 0.5 * spost(cdc)
            elif issuper(c):
                Lconst += c
            else:
                raise TypeError("Incorrect specification of time-dependent " +
                                "Liouvillian (expected operator or " +
                                "superoperator)")

        elif isinstance(c_spec, list):
            n_not_const_terms +=1
            c = c_spec[0]
            c_coeff = c_spec[1]
            
            if isoper(c):
                cdc = c.dag() * c
                L = spre(c) * spost(c.dag()) - 0.5 * spre(cdc) \
                                             - 0.5 * spost(cdc)
                if isinstance(c_coeff, Cubic_Spline):
                    me_cops_obj.append(c_coeff.coeffs)
                    me_cops_obj_flags.append(n_not_const_terms)
                    me_cops_coeff.append(c_coeff)
                else:
                    c_coeff = "(" + c_coeff + ")**2"
                    Lcoeff.append(c_coeff)
            elif issuper(c):
                L = c
                if isinstance(c_coeff, Cubic_Spline):
                    me_cops_obj.append(c_coeff.coeffs)
                    me_cops_obj_flags.append(-n_not_const_terms)
                    me_cops_coeff.append(c_coeff)
                else:
                    Lcoeff.append(c_coeff)
            else:
                raise TypeError("Incorrect specification of time-dependent " +
                                "Liouvillian (expected operator or " +
                                "superoperator)")

            Ldata.append(L.data.data)
            Linds.append(L.data.indices)
            Lptrs.append(L.data.indptr)
            #Lcoeff.append(c_coeff)

        else:
            raise TypeError("Incorrect specification of time-dependent " +
                            "collapse operators (expected string format)")
    
    
    #prepend the constant part of the liouvillian
    if Lconst != 0:
       Ldata = [Lconst.data.data]+Ldata
       Linds = [Lconst.data.indices]+Linds
       Lptrs = [Lconst.data.indptr]+Lptrs
       Lcoeff = ["1.0"]+Lcoeff
       
    else:
        me_cops_obj_flags = [kk-1 for kk in me_cops_obj_flags]
    # the total number of liouvillian terms (hamiltonian terms +
    # collapse operators)
    n_L_terms = len(Ldata)
    n_td_cops = len(me_cops_obj)
    
    # Check which components should use OPENMP
    omp_components = None
    if qset.has_openmp:
        if opt.use_openmp:
            omp_components = openmp_components(Lptrs)

    #
    # setup ode args string: we expand the list Ldata, Linds and Lptrs into
    # and explicit list of parameters
    #
    string_list = []
    for k in range(n_L_terms):
        string_list.append("Ldata[%d], Linds[%d], Lptrs[%d]" % (k, k, k))
    
    # Add H object terms to ode args string
    for k in range(len(Lobj)):
        string_list.append("Lobj[%d]" % k)
        
    # Add cop object terms to end of ode args string
    for k in range(len(me_cops_obj)):
        string_list.append("me_cops_obj[%d]" % k)    
    
    for name, value in args.items():
        if isinstance(value, np.ndarray):
            string_list.append(name)
        else:
            string_list.append(str(value))
    parameter_string = ",".join(string_list)
    
    #
    # generate and compile new cython code if necessary
    #
    if not opt.rhs_reuse or config.tdfunc is None:
        if opt.rhs_filename is None:
            config.tdname = "rhs" + str(os.getpid()) + str(config.cgen_num)
        else:
            config.tdname = opt.rhs_filename
        cgen = Codegen(h_terms=len(Lcoeff), h_tdterms=Lcoeff, 
                       c_td_splines=me_cops_coeff, 
                       c_td_spline_flags=me_cops_obj_flags, args=args,
                       config=config, use_openmp=opt.use_openmp,
                       omp_components=omp_components,
                       omp_threads=opt.openmp_threads)
        cgen.generate(config.tdname + ".pyx")

        code = compile('from ' + config.tdname + ' import cy_td_ode_rhs',
                       '<string>', 'exec')
        exec(code, globals())
        config.tdfunc = cy_td_ode_rhs

    #
    # setup integrator
    #
    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])

    exec(code, locals(), args)

    #
    # call generic ODE code
    #
    return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
Ejemplo n.º 14
0
def rhs_generate(H, c_ops, args={}, options=Odeoptions(), name=None):
    """
    Generates the Cython functions needed for solving the dynamics of a
    given system using the mesolve function inside a parfor loop.

    Parameters
    ----------
    H : qobj
        System Hamiltonian.
    c_ops : list
        ``list`` of collapse operators.
    args : dict
        Arguments for time-dependent Hamiltonian and collapse operator terms.
    options : Odeoptions
        Instance of ODE solver options.
    name: str
        Name of generated RHS

    Notes
    -----
    Using this function with any solver other than the mesolve function
    will result in an error.

    """
    odeconfig.reset()
    odeconfig.options = options

    if name:
        odeconfig.tdname = name
    else:
        odeconfig.tdname = "rhs" + str(odeconfig.cgen_num)

    Lconst = 0

    Ldata = []
    Linds = []
    Lptrs = []
    Lcoeff = []

    # loop over all hamiltonian terms, convert to superoperator form and
    # add the data of sparse matrix represenation to
    for h_spec in H:
        if isinstance(h_spec, Qobj):
            h = h_spec
            Lconst += -1j * (spre(h) - spost(h))

        elif isinstance(h_spec, list):
            h = h_spec[0]
            h_coeff = h_spec[1]

            L = -1j * (spre(h) - spost(h))

            Ldata.append(L.data.data)
            Linds.append(L.data.indices)
            Lptrs.append(L.data.indptr)
            Lcoeff.append(h_coeff)

        else:
            raise TypeError("Incorrect specification of time-dependent " + "Hamiltonian (expected string format)")

    # loop over all collapse operators
    for c_spec in c_ops:
        if isinstance(c_spec, Qobj):
            c = c_spec
            cdc = c.dag() * c
            Lconst += spre(c) * spost(c.dag()) - 0.5 * spre(cdc) - 0.5 * spost(cdc)

        elif isinstance(c_spec, list):
            c = c_spec[0]
            c_coeff = c_spec[1]

            cdc = c.dag() * c
            L = spre(c) * spost(c.dag()) - 0.5 * spre(cdc) - 0.5 * spost(cdc)

            Ldata.append(L.data.data)
            Linds.append(L.data.indices)
            Lptrs.append(L.data.indptr)
            Lcoeff.append("(" + c_coeff + ")**2")

        else:
            raise TypeError(
                "Incorrect specification of time-dependent " + "collapse operators (expected string format)"
            )

    # add the constant part of the lagrangian
    if Lconst != 0:
        Ldata.append(Lconst.data.data)
        Linds.append(Lconst.data.indices)
        Lptrs.append(Lconst.data.indptr)
        Lcoeff.append("1.0")

    # the total number of liouvillian terms (hamiltonian terms + collapse
    # operators)
    n_L_terms = len(Ldata)

    cgen = Codegen(h_terms=n_L_terms, h_tdterms=Lcoeff, args=args, odeconfig=odeconfig)
    cgen.generate(odeconfig.tdname + ".pyx")

    code = compile("from " + odeconfig.tdname + " import cy_td_ode_rhs", "<string>", "exec")
    exec(code, globals())

    odeconfig.tdfunc = cy_td_ode_rhs
    try:
        os.remove(odeconfig.tdname + ".pyx")
    except:
        pass
Ejemplo n.º 15
0
def _mesolve_list_td(H_func, rho0, tlist, c_op_list, e_ops, args, opt,
                     progress_bar):
    """!
    Evolve the density matrix using an ODE solver with time dependent
    Hamiltonian.
    """

    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:
            return _sesolve_list_td(H_func, rho0, tlist, e_ops, args, opt)

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

    #
    # construct liouvillian
    #
    if len(H_func) != 2:
        raise TypeError('Time-dependent Hamiltonian list must have two terms.')
    if not isinstance(H_func[0], (list, np.ndarray)) or len(H_func[0]) <= 1:
        raise TypeError('Time-dependent Hamiltonians must be a list ' +
                        'with two or more terms')
    if (not isinstance(H_func[1], (list, np.ndarray))) or \
       (len(H_func[1]) != (len(H_func[0]) - 1)):
        raise TypeError('Time-dependent coefficients must be list with ' +
                        'length N-1 where N is the number of ' +
                        'Hamiltonian terms.')

    if opt.rhs_reuse and odeconfig.tdfunc is None:
        rhs_generate(H_func, args)

    lenh = len(H_func[0])
    if opt.tidy:
        H_func[0] = [(H_func[0][k]).tidyup() for k in range(lenh)]
    L_func = [[liouvillian_fast(H_func[0][0], c_op_list)], H_func[1]]
    for m in range(1, lenh):
        L_func[0].append(liouvillian_fast(H_func[0][m], []))

    # create data arrays for time-dependent RHS function
    Ldata = [L_func[0][k].data.data for k in range(lenh)]
    Linds = [L_func[0][k].data.indices for k in range(lenh)]
    Lptrs = [L_func[0][k].data.indptr for k in range(lenh)]
    # setup ode args string
    string = ""
    for k in range(lenh):
        string += ("Ldata[%d], Linds[%d], Lptrs[%d]," % (k, k, k))

    if args:
        for name, value in args.items():
            if isinstance(value, np.ndarray):
                globals()['var_%s'%name] = value
                string += 'var_%s,'%name
            else:
                string += str(value) + ','

    # run code generator
    if not opt.rhs_reuse or odeconfig.tdfunc is None:
        if opt.rhs_filename is None:
            odeconfig.tdname = "rhs" + str(odeconfig.cgen_num)
        else:
            odeconfig.tdname = opt.rhs_filename
        cgen = Codegen(h_terms=n_L_terms, h_tdterms=Lcoeff, args=args,
                       odeconfig=odeconfig)
        cgen.generate(odeconfig.tdname + ".pyx")

        code = compile('from ' + odeconfig.tdname + ' import cy_td_ode_rhs',
                       '<string>', 'exec')
        exec(code, globals())
        odeconfig.tdfunc = cy_td_ode_rhs

    #
    # setup integrator
    #
    initial_vector = mat2vec(rho0.full()).ravel()
    r = scipy.integrate.ode(odeconfig.tdfunc)
    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(' + string + ')', '<string>', 'exec')
    exec(code)

    #
    # call generic ODE code
    #
    return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
Ejemplo n.º 16
0
def _mc_data_config(H, psi0, h_stuff, c_ops, c_stuff, args, e_ops,
                    options, config):
    """Creates the appropriate data structures for the monte carlo solver
    based on the given time-dependent, or indepdendent, format.
    """

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

    config.soft_reset()

    # take care of expectation values, if any
    if any(e_ops):
        config.e_num = len(e_ops)
        for op in e_ops:
            if isinstance(op, list):
                op = op[0]
            config.e_ops_data.append(op.data.data)
            config.e_ops_ind.append(op.data.indices)
            config.e_ops_ptr.append(op.data.indptr)
            config.e_ops_isherm.append(op.isherm)

        config.e_ops_data = np.array(config.e_ops_data)
        config.e_ops_ind = np.array(config.e_ops_ind)
        config.e_ops_ptr = np.array(config.e_ops_ptr)
        config.e_ops_isherm = np.array(config.e_ops_isherm)

    # take care of collapse operators, if any
    if any(c_ops):
        config.c_num = len(c_ops)
        for c_op in c_ops:
            if isinstance(c_op, list):
                c_op = c_op[0]
            n_op = c_op.dag() * c_op
            config.c_ops_data.append(c_op.data.data)
            config.c_ops_ind.append(c_op.data.indices)
            config.c_ops_ptr.append(c_op.data.indptr)
            # norm ops
            config.n_ops_data.append(n_op.data.data)
            config.n_ops_ind.append(n_op.data.indices)
            config.n_ops_ptr.append(n_op.data.indptr)
        # to array
        config.c_ops_data = np.array(config.c_ops_data)
        config.c_ops_ind = np.array(config.c_ops_ind)
        config.c_ops_ptr = np.array(config.c_ops_ptr)

        config.n_ops_data = np.array(config.n_ops_data)
        config.n_ops_ind = np.array(config.n_ops_ind)
        config.n_ops_ptr = np.array(config.n_ops_ptr)

    if config.tflag == 0:
        # CONSTANT H & C_OPS CODE
        # -----------------------

        if config.cflag:
            config.c_const_inds = np.arange(len(c_ops))
            for c_op in c_ops:
                n_op = c_op.dag() * c_op
                H -= 0.5j * \
                    n_op  # combine Hamiltonian and collapse terms into one
        # construct Hamiltonian data structures
        if options.tidy:
            H = H.tidyup(options.atol)
        config.h_data = -1.0j * H.data.data
        config.h_ind = H.data.indices
        config.h_ptr = H.data.indptr

    elif config.tflag in [1, 10, 11]:
        # STRING BASED TIME-DEPENDENCE
        # ----------------------------

        # take care of arguments for collapse operators, if any
        if any(args):
            for item in args.items():
                config.c_args.append(item[1])
        # constant Hamiltonian / string-type collapse operators
        if config.tflag == 1:
            H_inds = np.arange(1)
            H_tdterms = 0
            len_h = 1
            C_inds = np.arange(config.c_num)
            # find inds of time-dependent terms
            C_td_inds = np.array(c_stuff[2])
            # find inds of constant terms
            C_const_inds = np.setdiff1d(C_inds, C_td_inds)
            # extract time-dependent coefficients (strings)
            C_tdterms = [c_ops[k][1] for k in C_td_inds]
            # store indicies of constant collapse terms
            config.c_const_inds = C_const_inds
            # store indicies of time-dependent collapse terms
            config.c_td_inds = C_td_inds

            for k in config.c_const_inds:
                H -= 0.5j * (c_ops[k].dag() * c_ops[k])
            if options.tidy:
                H = H.tidyup(options.atol)
            config.h_data = [H.data.data]
            config.h_ind = [H.data.indices]
            config.h_ptr = [H.data.indptr]
            for k in config.c_td_inds:
                op = c_ops[k][0].dag() * c_ops[k][0]
                config.h_data.append(-0.5j * op.data.data)
                config.h_ind.append(op.data.indices)
                config.h_ptr.append(op.data.indptr)
            config.h_data = -1.0j * np.array(config.h_data)
            config.h_ind = np.array(config.h_ind)
            config.h_ptr = np.array(config.h_ptr)

        else:
            # string-type Hamiltonian & at least one string-type
            # collapse operator
            # -----------------

            H_inds = np.arange(len(H))
            # find inds of time-dependent terms
            H_td_inds = np.array(h_stuff[2])
            # find inds of constant terms
            H_const_inds = np.setdiff1d(H_inds, H_td_inds)
            # extract time-dependent coefficients (strings or functions)
            H_tdterms = [H[k][1] for k in H_td_inds]
            # combine time-INDEPENDENT terms into one.
            H = np.array([np.sum(H[k] for k in H_const_inds)] +
                         [H[k][0] for k in H_td_inds], dtype=object)
            len_h = len(H)
            H_inds = np.arange(len_h)
            # store indicies of time-dependent Hamiltonian terms
            config.h_td_inds = np.arange(1, len_h)
            # if there are any collapse operators
            if config.c_num > 0:
                if config.tflag == 10:
                    # constant collapse operators
                    config.c_const_inds = np.arange(config.c_num)
                    for k in config.c_const_inds:
                        H[0] -= 0.5j * (c_ops[k].dag() * c_ops[k])
                    C_inds = np.arange(config.c_num)
                    C_tdterms = np.array([])
                else:
                    # some time-dependent collapse terms
                    C_inds = np.arange(config.c_num)
                    # find inds of time-dependent terms
                    C_td_inds = np.array(c_stuff[2])
                    # find inds of constant terms
                    C_const_inds = np.setdiff1d(C_inds, C_td_inds)
                    C_tdterms = [c_ops[k][1] for k in C_td_inds]
                    # extract time-dependent coefficients (strings)
                    # store indicies of constant collapse terms
                    config.c_const_inds = C_const_inds
                    # store indicies of time-dependent collapse terms
                    config.c_td_inds = C_td_inds
                    for k in config.c_const_inds:
                        H[0] -= 0.5j * (c_ops[k].dag() * c_ops[k])
            else:
                # set empty objects if no collapse operators
                C_const_inds = np.arange(config.c_num)
                config.c_const_inds = np.arange(config.c_num)
                config.c_td_inds = np.array([])
                C_tdterms = np.array([])
                C_inds = np.array([])

            # tidyup
            if options.tidy:
                H = np.array([H[k].tidyup(options.atol)
                              for k in range(len_h)], dtype=object)
            # construct data sets
            config.h_data = [H[k].data.data for k in range(len_h)]
            config.h_ind = [H[k].data.indices for k in range(len_h)]
            config.h_ptr = [H[k].data.indptr for k in range(len_h)]
            for k in config.c_td_inds:
                config.h_data.append(-0.5j * config.n_ops_data[k])
                config.h_ind.append(config.n_ops_ind[k])
                config.h_ptr.append(config.n_ops_ptr[k])
            config.h_data = -1.0j * np.array(config.h_data)
            config.h_ind = np.array(config.h_ind)
            config.h_ptr = np.array(config.h_ptr)

        # set execuatble code for collapse expectation values and spmv
        col_spmv_code = ("state = _cy_col_spmv_func(j, ODE.t, " +
                         "config.c_ops_data[j], config.c_ops_ind[j], " +
                         "config.c_ops_ptr[j], ODE.y")
        col_expect_code = ("for i in config.c_td_inds: " +
                           "n_dp.append(_cy_col_expect_func(i, ODE.t, " +
                           "config.n_ops_data[i], " +
                           "config.n_ops_ind[i], " +
                           "config.n_ops_ptr[i], ODE.y")
        for kk in range(len(config.c_args)):
            col_spmv_code += ",config.c_args[" + str(kk) + "]"
            col_expect_code += ",config.c_args[" + str(kk) + "]"
        col_spmv_code += ")"
        col_expect_code += "))"

        config.col_spmv_code = col_spmv_code
        config.col_expect_code = col_expect_code

        # setup ode args string
        config.string = ""
        data_range = range(len(config.h_data))
        for k in data_range:
            config.string += ("config.h_data[" + str(k) +
                              "], config.h_ind[" + str(k) +
                              "], config.h_ptr[" + str(k) + "]")
            if k != data_range[-1]:
                config.string += ","
        # attach args to ode args string
        if len(config.c_args) > 0:
            for kk in range(len(config.c_args)):
                config.string += "," + "config.c_args[" + str(kk) + "]"

        name = "rhs" + str(os.getpid()) + str(config.cgen_num)
        config.tdname = name
        cgen = Codegen(H_inds, H_tdterms, config.h_td_inds, args,
                       C_inds, C_tdterms, config.c_td_inds, type='mc',
                       config=config)
        cgen.generate(name + ".pyx")

    elif config.tflag in [2, 20, 22]:
        # PYTHON LIST-FUNCTION BASED TIME-DEPENDENCE
        # ------------------------------------------

        # take care of Hamiltonian
        if config.tflag == 2:
            # constant Hamiltonian, at least one function based collapse
            # operators
            H_inds = np.array([0])
            H_tdterms = 0
            len_h = 1
        else:
            # function based Hamiltonian
            H_inds = np.arange(len(H))
            H_td_inds = np.array(h_stuff[1])
            H_const_inds = np.setdiff1d(H_inds, H_td_inds)
            config.h_funcs = np.array([H[k][1] for k in H_td_inds])
            config.h_func_args = args
            Htd = np.array([H[k][0] for k in H_td_inds], dtype=object)
            config.h_td_inds = np.arange(len(Htd))
            H = np.sum(H[k] for k in H_const_inds)

        # take care of collapse operators
        C_inds = np.arange(config.c_num)
        # find inds of time-dependent terms
        C_td_inds = np.array(c_stuff[1])
        # find inds of constant terms
        C_const_inds = np.setdiff1d(C_inds, C_td_inds)
        # store indicies of constant collapse terms
        config.c_const_inds = C_const_inds
        # store indicies of time-dependent collapse terms
        config.c_td_inds = C_td_inds
        config.c_funcs = np.zeros(config.c_num, dtype=FunctionType)
        for k in config.c_td_inds:
            config.c_funcs[k] = c_ops[k][1]
        config.c_func_args = args

        # combine constant collapse terms with constant H and construct data
        for k in config.c_const_inds:
            H -= 0.5j * (c_ops[k].dag() * c_ops[k])
        if options.tidy:
            H = H.tidyup(options.atol)
            Htd = np.array([Htd[j].tidyup(options.atol)
                            for j in config.h_td_inds], dtype=object)
        # setup constant H terms data
        config.h_data = -1.0j * H.data.data
        config.h_ind = H.data.indices
        config.h_ptr = H.data.indptr

        # setup td H terms data
        config.h_td_data = np.array(
            [-1.0j * Htd[k].data.data for k in config.h_td_inds])
        config.h_td_ind = np.array(
            [Htd[k].data.indices for k in config.h_td_inds])
        config.h_td_ptr = np.array(
            [Htd[k].data.indptr for k in config.h_td_inds])

    elif config.tflag == 3:
        # PYTHON FUNCTION BASED HAMILTONIAN
        # ---------------------------------

        # take care of Hamiltonian
        config.h_funcs = H
        config.h_func_args = args

        # take care of collapse operators
        config.c_const_inds = np.arange(config.c_num)
        config.c_td_inds = np.array([])
        if len(config.c_const_inds) > 0:
            H = 0
            for k in config.c_const_inds:
                H -= 0.5j * (c_ops[k].dag() * c_ops[k])
            if options.tidy:
                H = H.tidyup(options.atol)
            config.h_data = -1.0j * H.data.data
            config.h_ind = H.data.indices
            config.h_ptr = H.data.indptr