Esempio n. 1
0
    def gradient_CFI(self, M):
        '''
        Input:
        1) M:
            TYPE: list of Qobj (matrix)
            DESCRIPTION: merasurement. It takes the form [M1,M2,...], where M1, M2 ...
            are matrices and satisfies M1+M2+... = identity matrix.
        Output:
        1) updated values of self.control_coefficients.
        Notice: To run this function, the function 'propagation_single()' has to run first.
        '''
        num = len(self.times)
        dH0 = 1.j * Qobj(liouvillian(self.Hamiltonian_derivative[0]).full())

        dim = self.freeHamiltonian.dims[0][0]
        dt = self.times[1] - self.times[0]
        Hc_coeff = self.control_coefficients
        D = self.propagator

        rhoT_vec = self.rho[num-1]
        drhoT_vec = self.rho_derivative[num-1]
        rhoT = Qobj(vec2mat(rhoT_vec.full()))
        drhoT = Qobj(vec2mat(drhoT_vec.full()))

        L1 = Qobj(np.array([[0. for i in range(0,dim)] for k in range(0,dim)]))
        L2 = Qobj(np.array([[0. for i in range(0,dim)] for k in range(0,dim)]))
        for mi in range(0, len(M)):
            ptemp = (rhoT * M[mi]).tr()
            dptemp = (drhoT * M[mi]).tr()
            if ptemp != 0:
                L1 += (dptemp / ptemp) * M[mi]
                L2 += ((dptemp / ptemp)**2) * M[mi]

        for ki in range(0, len(self.control_Hamiltonian)):
            Hk = 1.j * Qobj(liouvillian(self.control_Hamiltonian[ki]).full())
            Hc_ki = Hc_coeff[ki]
            for ti in range(0,num):
                Mj1 = 1.j * D[ti+1][num-1] * Hk * self.rho[ti]

                Mj2 = Qobj(np.zeros((dim*dim, 1)))
                for ri in range(0, ti+1):
                    Mj2 += D[ti+1][num-1] * Hk * D[ri+1][ti] * dH0 * self.rho[ri]

                Mj3 = Qobj(np.zeros((dim*dim, 1)))
                for ri in range(ti+1, num):
                    Mj3 += D[ri+1][num-1] * dH0 * D[ti+1][ri] * Hk * self.rho[ti]
                Mj1mat = Qobj(vec2mat(Mj1.full()))
                Mj2mat = Qobj(vec2mat(Mj2.full()))
                Mj3mat = Qobj(vec2mat(Mj3.full()))

                term1 = dt * (L2 * Mj1mat).tr()
                term2 = -2 * (dt * dt) * (L1 * (Mj2mat+Mj3mat)).tr()
                delta = np.real(term1 + term2)

                Hc_ki[ti] += Hc_ki[ti] + self.epsilon * delta

            Hc_coeff[ki] = Hc_ki

        self.control_coefficients = Hc_coeff
Esempio n. 2
0
def _mesolve_const(H, rho0, tlist, c_op_list, expt_ops, args, opt):
    """!
    Evolve the density matrix using an ODE solver, for constant hamiltonian
    and collapse operators.
    """

    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,
        # fallback on the unitary schrodinger equation solver
        if len(c_op_list) == 0 and isoper(H):
            return _sesolve_const(H, rho0, tlist, expt_ops, args, opt)

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

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

    if issuper(H):
        L = H + liouvillian(None, c_op_list)
    else:
        L = liouvillian(H, c_op_list)

    #
    # setup integrator
    #
    initial_vector = mat2vec(rho0.full())
    r = scipy.integrate.ode(cy_ode_rhs)
    r.set_f_params(L.data.data, L.data.indices, L.data.indptr)
    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, rho0, tlist, expt_ops, opt, vec2mat)
Esempio n. 3
0
    def __init__(self,
                 hamiltonian,
                 coupling,
                 coup_strength,
                 ck,
                 vk,
                 ncut,
                 beta=np.inf):
        self.hamiltonian = hamiltonian
        self.coupling = coupling
        self.ck, self.vk = ck, vk
        self.ncut = ncut
        self.kcut = len(ck)
        nhe, he2idx, idx2he = _heom_state_dictionaries([ncut + 1] * (len(ck)),
                                                       ncut)

        self.nhe = nhe
        self.he2idx = he2idx
        self.idx2he = idx2he
        self.N = self.hamiltonian.shape[0]

        total_nhe = int(
            factorial(self.ncut + self.kcut) /
            (factorial(self.ncut) * factorial(self.kcut)))
        self.total_nhe = total_nhe
        self.hshape = (total_nhe, self.N**2)
        self.L = liouvillian(self.hamiltonian, []).data
        self.grad_shape = (self.N**2, self.N**2)
        self.spreQ = spre(coupling).data
        self.spostQ = spost(coupling).data
        self.L_helems = lil_matrix(
            (total_nhe * self.N**2, total_nhe * self.N**2), dtype=np.complex)
        self.lam = coup_strength
        self.full_hierarchy = []
Esempio n. 4
0
def _spectrum_es(H, wlist, c_ops, a_op, b_op):
    """
    Internal function for calculating the spectrum of the correlation function
    :math:`\left<A(\\tau)B(0)\\right>`.
    """
    if debug:
        print(inspect.stack()[0][3])

    # construct the Liouvillian
    L = liouvillian(H, c_ops)

    # find the steady state density matrix and a_op and b_op expecation values
    rho0 = steadystate(L)

    a_op_ss = expect(a_op, rho0)
    b_op_ss = expect(b_op, rho0)

    # eseries solution for (b * rho0)(t)
    es = ode2es(L, b_op * rho0)

    # correlation
    corr_es = expect(a_op, es)

    # covariance
    cov_es = corr_es - np.real(np.conjugate(a_op_ss) * b_op_ss)

    # spectrum
    spectrum = esspec(cov_es, wlist)

    return spectrum
Esempio n. 5
0
def _correlation_es_2t(H, state0, tlist, taulist, c_ops, a_op, b_op, c_op):
    """
    Internal function for calculating the three-operator two-time
    correlation function:
    <A(t)B(t+tau)C(t)>
    using an exponential series solver.
    """

    # the solvers only work for positive time differences and the correlators
    # require positive tau
    if state0 is None:
        rho0 = steadystate(H, c_ops)
        tlist = [0]
    elif isket(state0):
        rho0 = ket2dm(state0)
    else:
        rho0 = state0

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

    # contruct the Liouvillian
    L = liouvillian(H, c_ops)

    corr_mat = np.zeros([np.size(tlist), np.size(taulist)], dtype=complex)
    solES_t = ode2es(L, rho0)

    # evaluate the correlation function
    for t_idx in range(len(tlist)):
        rho_t = esval(solES_t, [tlist[t_idx]])
        solES_tau = ode2es(L, c_op * rho_t * a_op)
        corr_mat[t_idx, :] = esval(expect(b_op, solES_tau), taulist)

    return corr_mat
Esempio n. 6
0
def _jc_liouvillian(N):
    from qutip.tensor import tensor
    from qutip.operators import destroy, qeye
    from qutip.superoperator import liouvillian
    wc = 1.0  * 2 * np.pi  # cavity frequency
    wa = 1.0  * 2 * np.pi  # atom frequency
    g  = 0.05 * 2 * np.pi  # coupling strength
    kappa = 0.005          # cavity dissipation rate
    gamma = 0.05           # atom dissipation rate
    n_th_a = 1           # temperature in frequency units
    use_rwa = 0
    # operators
    a  = tensor(destroy(N), qeye(2))
    sm = tensor(qeye(N), destroy(2))
    # Hamiltonian
    if use_rwa:
        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())
    c_op_list = []

    rate = kappa * (1 + n_th_a)
    if rate > 0.0:
        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)

    return liouvillian(H, c_op_list)
Esempio n. 7
0
def _mesolve_QobjEvo(H, c_ops, tlist, args, opt):
    """
    Prepare the system for the solver, H can be an QobjEvo.
    """
    H_td = QobjEvo(H, args, tlist=tlist)
    if not issuper(H_td.cte):
        L_td = liouvillian(H_td)
    else:
        L_td = H_td
    for op in c_ops:
        op_td = QobjEvo(op, args, tlist=tlist)
        if not issuper(op_td.cte):
            op_td = lindblad_dissipator(op_td)
        L_td += op_td

    if opt.rhs_with_state:
        L_td._check_old_with_state()

    nthread = opt.openmp_threads if opt.use_openmp else 0
    L_td.compile(omp=nthread)

    ss = SolverSystem()
    ss.H = L_td
    ss.makefunc = _qobjevo_set
    solver_safe["mesolve"] = ss
    return ss
Esempio n. 8
0
def _opto_liouvillian(N):
    from qutip.tensor import tensor
    from qutip.operators import destroy, qeye
    from qutip.superoperator import liouvillian
    Nc = 5  # Number of cavity states
    Nm = N  # Number of mech states
    kappa = 0.3  # Cavity damping rate
    E = 0.1  # Driving Amplitude
    g0 = 2.4 * kappa  # Coupling strength
    Qm = 1e4  # Mech quality factor
    gamma = 1 / Qm  # Mech damping rate
    n_th = 1  # Mech bath temperature
    delta = -0.43  # Detuning
    a = tensor(destroy(Nc), qeye(Nm))
    b = tensor(qeye(Nc), destroy(Nm))
    num_b = b.dag() * b
    num_a = a.dag() * a
    H = -delta * (num_a) + num_b + g0 * (b.dag() + b) * num_a + E * (a.dag() +
                                                                     a)
    cc = np.sqrt(kappa) * a
    cm = np.sqrt(gamma * (1.0 + n_th)) * b
    cp = np.sqrt(gamma * n_th) * b.dag()
    c_ops = [cc, cm, cp]

    return liouvillian(H, c_ops)
Esempio n. 9
0
def _spectrum_es(H, wlist, c_ops, a_op, b_op):
    """
    Internal function for calculating the spectrum of the correlation function
    :math:`\left<A(\\tau)B(0)\\right>`.
    """
    if debug:
        print(inspect.stack()[0][3])

    # construct the Liouvillian
    L = liouvillian(H, c_ops)

    # find the steady state density matrix and a_op and b_op expecation values
    rho0 = steadystate(L)

    a_op_ss = expect(a_op, rho0)
    b_op_ss = expect(b_op, rho0)

    # eseries solution for (b * rho0)(t)
    es = ode2es(L, b_op * rho0)

    # correlation
    corr_es = expect(a_op, es)

    # covariance
    cov_es = corr_es - a_op_ss * b_op_ss
    # tidy up covariance (to combine, e.g., zero-frequency components that cancel)
    cov_es.tidyup()

    # spectrum
    spectrum = esspec(cov_es, wlist)

    return spectrum
Esempio n. 10
0
def _mesolve_const(H, rho0, tlist, c_op_list, e_ops, args, opt,
                   progress_bar):
    """
    Evolve the density matrix using an ODE solver, for constant hamiltonian
    and collapse operators.
    """

    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)

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

    L = liouvillian(H, c_op_list)
    

    #
    # setup integrator
    #
    initial_vector = mat2vec(rho0.full()).ravel('F')
    if issuper(rho0):
        r = scipy.integrate.ode(_ode_super_func)
        r.set_f_params(L.data)
    else:
        if opt.use_openmp and L.data.nnz >= qset.openmp_thresh:
            r = scipy.integrate.ode(cy_ode_rhs_openmp)
            r.set_f_params(L.data.data, L.data.indices, L.data.indptr, 
                            opt.openmp_threads)
        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,
                     max_step=opt.max_step)
    r.set_initial_value(initial_vector, tlist[0])

    #
    # call generic ODE code
    #
    return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
Esempio n. 11
0
    def __init__(
        self,
        H_sys,
        bath,
        max_depth,
        options=None,
        progress_bar=None,
    ):
        self.H_sys = self._convert_h_sys(H_sys)
        self.options = Options() if options is None else options
        self._is_timedep = isinstance(self.H_sys, QobjEvo)
        self._H0 = self.H_sys.to_list()[0] if self._is_timedep else self.H_sys
        self._is_hamiltonian = self._H0.type == "oper"
        self._L0 = liouvillian(self._H0) if self._is_hamiltonian else self._H0

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

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

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

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

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

        self._configure_solver()
Esempio n. 12
0
def _smepdpsolve_generic(sso, options, progress_bar):
    """
    For internal use. See smepdpsolve.
    """
    if debug:
        logger.debug(inspect.stack()[0][3])

    N_store = len(sso.times)
    N_substeps = sso.nsubsteps
    dt = (sso.times[1] - sso.times[0]) / N_substeps
    nt = sso.ntraj

    data = Result()
    data.solver = "smepdpsolve"
    data.times = sso.times
    data.expect = np.zeros((len(sso.e_ops), N_store), dtype=complex)
    data.jump_times = []
    data.jump_op_idx = []

    # Liouvillian for the deterministic part.
    # needs to be modified for TD systems
    L = liouvillian(sso.H, sso.c_ops)

    progress_bar.start(sso.ntraj)

    for n in range(sso.ntraj):
        progress_bar.update(n)
        rho_t = mat2vec(sso.rho0.full()).ravel()

        states_list, jump_times, jump_op_idx = \
            _smepdpsolve_single_trajectory(data, L, dt, sso.times,
                                           N_store, N_substeps,
                                           rho_t, sso.rho0.dims,
                                           sso.c_ops, sso.e_ops)

        data.states.append(states_list)
        data.jump_times.append(jump_times)
        data.jump_op_idx.append(jump_op_idx)

    progress_bar.finished()

    # average density matrices
    if options.average_states and np.any(data.states):
        data.states = [
            sum([data.states[m][n] for m in range(nt)]).unit()
            for n in range(len(data.times))
        ]

    # average
    data.expect = data.expect / sso.ntraj

    # standard error
    if nt > 1:
        data.se = (data.ss - nt * (data.expect**2)) / (nt * (nt - 1))
    else:
        data.se = None

    return data
Esempio n. 13
0
    def gradient_QFI(self):
        '''
          Output:
          1) updated values of self.control_coefficients
          Notice: To run this function, the function 'propagation_single()' has to run first.
          '''
        num = len(self.times)
        dH0 = 1.j * Qobj(liouvillian(self.Hamiltonian_derivative[0]).full())
        rhomat_final = Qobj(vec2mat(self.rho[num - 1].full()))
        drhomat_final = Qobj(vec2mat(self.rho_derivative[num - 1].full()))
        SLD_final = CR.SLD(rhomat_final, drhomat_final)

        dim = self.freeHamiltonian.dims[0][0]
        dt = self.times[1] - self.times[0]
        Hc_coeff = self.control_coefficients
        D = self.propagator

        for ki in range(0, len(self.control_Hamiltonian)):
            Hk = 1.j * Qobj(liouvillian(self.control_Hamiltonian[ki]).full())
            Hc_ki = Hc_coeff[ki]
            for ti in range(0, num):
                Mj1 = 1.j * D[ti + 1][num - 1] * Hk * self.rho[ti]

                Mj2 = Qobj(np.zeros((dim * dim, 1)))
                for ri in range(0, ti + 1):
                    Mj2 += D[ti + 1][num -
                                     1] * Hk * D[ri +
                                                 1][ti] * dH0 * self.rho[ri]

                Mj3 = Qobj(np.zeros((dim * dim, 1)))
                for ri in range(ti + 1, num):
                    Mj3 += D[ri + 1][num -
                                     1] * dH0 * D[ti +
                                                  1][ri] * Hk * self.rho[ti]
                Mj1mat = Qobj(vec2mat(Mj1.full()))
                Mj2mat = Qobj(vec2mat(Mj2.full()))
                Mj3mat = Qobj(vec2mat(Mj3.full()))

                term1 = dt * (SLD_final * SLD_final * Mj1mat).tr()
                term2 = -2 * (dt * dt) * (SLD_final * (Mj2mat + Mj3mat)).tr()
                delta = np.real(term1 + term2)
                Hc_ki[ti] += self.epsilon * delta
            Hc_coeff[ki] = Hc_ki

        self.control_coefficients = Hc_coeff
Esempio n. 14
0
def _smepdpsolve_generic(sso, options, progress_bar):
    """
    For internal use. See smepdpsolve.
    """
    if debug:
        logger.debug(inspect.stack()[0][3])

    N_store = len(sso.times)
    N_substeps = sso.nsubsteps
    dt = (sso.times[1] - sso.times[0]) / N_substeps
    nt = sso.ntraj

    data = Result()
    data.solver = "smepdpsolve"
    data.times = sso.times
    data.expect = np.zeros((len(sso.e_ops), N_store), dtype=complex)
    data.jump_times = []
    data.jump_op_idx = []

    # Liouvillian for the deterministic part.
    # needs to be modified for TD systems
    L = liouvillian(sso.H, sso.c_ops)

    progress_bar.start(sso.ntraj)

    for n in range(sso.ntraj):
        progress_bar.update(n)
        rho_t = mat2vec(sso.rho0.full()).ravel()

        states_list, jump_times, jump_op_idx = \
            _smepdpsolve_single_trajectory(data, L, dt, sso.times,
                                           N_store, N_substeps,
                                           rho_t, sso.rho0.dims,
                                           sso.c_ops, sso.e_ops)

        data.states.append(states_list)
        data.jump_times.append(jump_times)
        data.jump_op_idx.append(jump_op_idx)

    progress_bar.finished()

    # average density matrices
    if options.average_states and np.any(data.states):
        data.states = [sum([data.states[m][n] for m in range(nt)]).unit()
                       for n in range(len(data.times))]

    # average
    data.expect = data.expect / sso.ntraj

    # standard error
    if nt > 1:
        data.se = (data.ss - nt * (data.expect ** 2)) / (nt * (nt - 1))
    else:
        data.se = None

    return data
Esempio n. 15
0
    def __init__(
        self,
        H_sys,
        coup_op,
        coup_strength,
        temperature,
        N_cut,
        N_exp,
        cut_freq,
        bnd_cut_approx=False,
        options=None,
        progress_bar=None,
        combine=True,
    ):
        bath = DrudeLorentzBath(
            Q=coup_op,
            lam=coup_strength,
            gamma=cut_freq,
            Nk=N_exp - 1,
            T=temperature,
            combine=combine,
        )

        if bnd_cut_approx:
            # upgrade H_sys to a Liouvillian if needed and add the
            # bath terminator
            H_sys = self._convert_h_sys(H_sys)
            is_timedep = isinstance(H_sys, QobjEvo)
            H0 = H_sys.to_list()[0] if is_timedep else H_sys
            is_hamiltonian = H0.type == "oper"
            if is_hamiltonian:
                H_sys = liouvillian(H_sys)
            _, terminator = bath.terminator()
            H_sys = H_sys + terminator

        super().__init__(
            H_sys,
            bath=bath,
            max_depth=N_cut,
            options=options,
            progress_bar=progress_bar,
        )

        # store input parameters as attributes for politeness and compatibility
        # with HSolverDL in QuTiP 4.6 and below.
        self.coup_strength = coup_strength
        self.cut_freq = cut_freq
        self.temperature = temperature
        self.N_exp = N_exp
        self.bnd_cut_approx = bnd_cut_approx
Esempio n. 16
0
def add_photon_noise(rho0, gamma, tlist):
    """
    """
    n = rho0.shape[0]
    a = destroy(n)
    c_ops = [
        gamma * a,
    ]
    H = -0 * (a.dag() + a)
    opts = Options(atol=1e-20, store_states=True, nsteps=1500)
    L = liouvillian(H, c_ops=c_ops)
    states = mesolve(H, rho0, tlist, c_ops=c_ops)

    return states.states
Esempio n. 17
0
    def evolution(self, t):
        '''
        internal function
        '''
        H0 = self.freeHamiltonian
        tj = (t-self.times[0])/(self.times[1]-self.times[0])
        dt = self.times[1]-self.times[0]

        for hn in range(0, len(self.control_Hamiltonian)):
            Hc_temp = self.control_coefficients[hn]
            H0 += self.control_Hamiltonian[hn] * Hc_temp[tj]

        Ld = dt * Qobj(liouvillian(H0, c_ops=self.Liouville_operator).full())
        return Ld.expm()
Esempio n. 18
0
 def test_liouvillian_td(self):
     "Superoperator: liouvillian, time-dependent"
     assert_(liouvillian(self.t1)(0.5) == liouvillian(self.t1(0.5)))
     assert_(liouvillian(None, [self.t2])(0.5) ==
             liouvillian(None, [self.t2(0.5)]))
     assert_(liouvillian(self.t1, [self.t2, self.q1, self.t3],
                         chi=[1,2,3])(0.5) ==
             liouvillian(self.t1(0.5), [self.t2(0.5), self.q1, self.t3(0.5)],
                         chi=[1,2,3]))
Esempio n. 19
0
 def test_liouvillian_td(self):
     "Superoperator: liouvillian, time-dependent"
     assert_(liouvillian(self.t1)(0.5) == liouvillian(self.t1(0.5)))
     assert_(liouvillian(None, [self.t2])(0.5) ==
             liouvillian(None, [self.t2(0.5)]))
     assert_(liouvillian(self.t1, [self.t2, self.q1, self.t3],
                         chi=[1,2,3])(0.5) ==
             liouvillian(self.t1(0.5), [self.t2(0.5), self.q1, self.t3(0.5)],
                         chi=[1,2,3]))
Esempio n. 20
0
def _steadystate_setup(A, c_op_list):
    """Build Liouvillian (if necessary) and check input.
    """
    if isoper(A):
        if len(c_op_list) > 0:
            return liouvillian(A, c_op_list)

        raise TypeError('Cannot calculate the steady state for a ' +
                        'non-dissipative system ' +
                        '(no collapse operators given)')
    elif issuper(A):
        return A
    else:
        raise TypeError('Solving for steady states requires ' +
                        'Liouvillian (super) operators')
Esempio n. 21
0
def _steadystate_setup(A, c_op_list):
    """Build Liouvillian (if necessary) and check input.
    """
    if isoper(A):
        if len(c_op_list) > 0:
            return liouvillian(A, c_op_list)

        raise TypeError('Cannot calculate the steady state for a ' +
                        'non-dissipative system ' +
                        '(no collapse operators given)')
    elif issuper(A):
        return A
    else:
        raise TypeError('Solving for steady states requires ' +
                        'Liouvillian (super) operators')
def mesolve_const_checkpoint(H, rho0, tlist, c_op_list, e_ops, args, opt,
                             progress_bar, save, subdir):
    """
    Evolve the density matrix using an ODE solver, for constant hamiltonian
    and collapse operators.
    """

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

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

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

    L = liouvillian(H, c_op_list)

    #
    # 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)
    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_checkpoint(r, rho0, tlist, e_ops, opt,
                                        progress_bar, save, subdir)
Esempio n. 23
0
    def propagation_multiple(self):
        '''
          This function will save all the propators during the evolution, which may be memory consuming.
          '''
        if len(self.Hamiltonian_derivative) < 2:
            raise TypeError(
                'this is a multiparameter scenario, the length of dH has to be larger than 1!'
            )

        num = len(self.times)
        dim = self.freeHamiltonian.dims[0][0]
        dt = self.times[1] - self.times[0]
        dL = [
            Qobj(liouvillian(self.Hamiltonian_derivative[i]).full())
            for i in range(0, len(self.Hamiltonian_derivative))
        ]
        D = [[[] for i in range(0, num + 1)] for k in range(0, num + 1)]

        rhovec = [[] for i in range(0, num)]
        drhovec = [[[] for k in range(0, len(self.Hamiltonian_derivative))]
                   for i in range(0, num)]
        rhovec[0] = Qobj(mat2vec(self.rho_initial.full()))

        for para_i in range(0, len(self.Hamiltonian_derivative)):
            drhovec[0][para_i] = dt * dL[para_i] * rhovec[0]

        D[0][0] = self.evolution(0)
        D[1][0] = qeye(dim**2)
        for di in range(1, num):
            tnow = self.times[0] + dt * di
            D[di + 1][di] = qeye(dim**2)
            D[di][di] = self.evolution(tnow)
            D[0][di] = D[di][di] * D[0][di - 1]
            rhovec[di] = D[di][di] * rhovec[di - 1]

            for para_i in range(0, len(self.Hamiltonian_derivative)):
                drho_temp = dt * dL[para_i] * rhovec[di]
                for dj in range(1, di):
                    D[di - dj][di] = D[di - dj + 1][di] * D[di - dj][di - dj]
                    drho_temp += dt * D[di - dj +
                                        1][di] * dL[para_i] * rhovec[di - dj]
                drhovec[di][para_i] = drho_temp

        self.rho = rhovec
        self.rho_derivative = drhovec
        self.propagator = D
Esempio n. 24
0
    def testLiouvillianImplem(self):
        """
        Superoperator: Randomized comparison of standard and reference
        Liouvillian functions.
        """
        N1 = 3
        N2 = 4
        N3 = 5

        a1 = tensor(rand_dm(N1, density=0.75), identity(N2), identity(N3))
        a2 = tensor(identity(N1), rand_dm(N2, density=0.75), identity(N3))
        a3 = tensor(identity(N1), identity(N2), rand_dm(N3, density=0.75))
        H = a1.dag() * a1 + a2.dag() * a2 + a3.dag() * a3

        c_ops = [np.sqrt(0.01) * a1, np.sqrt(0.025) * a2, np.sqrt(0.05) * a3]

        L1 = liouvillian(H, c_ops)
        L2 = liouvillian_ref(H, c_ops)

        assert_((L1 - L2).norm('max') < 1e-8)
Esempio n. 25
0
    def testLiouvillianImplem(self):
        """
        Superoperator: Randomized comparison of standard and reference
        Liouvillian functions.
        """
        N1 = 3
        N2 = 4
        N3 = 5

        a1 = tensor(rand_dm(N1, density=0.75), identity(N2), identity(N3))
        a2 = tensor(identity(N1), rand_dm(N2, density=0.75), identity(N3))
        a3 = tensor(identity(N1), identity(N2), rand_dm(N3, density=0.75))
        H = a1.dag() * a1 + a2.dag() * a2 + a3.dag() * a3

        c_ops = [np.sqrt(0.01) * a1, np.sqrt(0.025) * a2, np.sqrt(0.05) * a3]

        L1 = liouvillian(H, c_ops)
        L2 = liouvillian_ref(H, c_ops)

        assert_((L1 - L2).norm('max') < 1e-8)
Esempio n. 26
0
    def _configure_solver(self):
        """ Set up the solver. """
        RHSmat = self._rhs(self._L0.data)
        assert isinstance(RHSmat, sp.csr_matrix)

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

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

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

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

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

        self._ode = solver
        self.RHSmat = RHSmat
Esempio n. 27
0
def _spectrum_pi(H, wlist, c_ops, a_op, b_op, use_pinv=False):
    """
    Internal function for calculating the spectrum of the correlation function
    :math:`\left<A(\\tau)B(0)\\right>`.
    """

    L = H if issuper(H) else liouvillian(H, c_ops)

    tr_mat = tensor([qeye(n) for n in L.dims[0][0]])
    N = np.prod(L.dims[0][0])

    A = L.full()
    b = spre(b_op).full()
    a = spre(a_op).full()

    tr_vec = np.transpose(mat2vec(tr_mat.full()))

    rho_ss = steadystate(L)
    rho = np.transpose(mat2vec(rho_ss.full()))

    I = np.identity(N * N)
    P = np.kron(np.transpose(rho), tr_vec)
    Q = I - P

    spectrum = np.zeros(len(wlist))

    for idx, w in enumerate(wlist):
        if use_pinv:
            MMR = np.linalg.pinv(-1.0j * w * I + A)
        else:
            MMR = np.dot(Q, np.linalg.solve(-1.0j * w * I + A, Q))

        s = np.dot(tr_vec,
                   np.dot(a, np.dot(MMR, np.dot(b, np.transpose(rho)))))
        spectrum[idx] = -2 * np.real(s[0, 0])

    return spectrum
Esempio n. 28
0
def _opto_liouvillian(N):
    from qutip.tensor import tensor
    from qutip.operators import destroy, qeye
    from qutip.superoperator import liouvillian
    Nc = 5                      # Number of cavity states
    Nm = N                     # Number of mech states
    kappa = 0.3                 # Cavity damping rate
    E = 0.1                     # Driving Amplitude         
    g0 = 2.4*kappa              # Coupling strength
    Qm = 1e4                    # Mech quality factor
    gamma = 1/Qm                # Mech damping rate
    n_th = 1                    # Mech bath temperature
    delta = -0.43               # Detuning
    a = tensor(destroy(Nc), qeye(Nm))
    b = tensor(qeye(Nc), destroy(Nm))
    num_b = b.dag()*b
    num_a = a.dag()*a
    H = -delta*(num_a)+num_b+g0*(b.dag()+b)*num_a+E*(a.dag()+a)
    cc = np.sqrt(kappa)*a
    cm = np.sqrt(gamma*(1.0 + n_th))*b
    cp = np.sqrt(gamma*n_th)*b.dag()
    c_ops = [cc,cm,cp]

    return liouvillian(H, c_ops)
Esempio n. 29
0
def _jc_liouvillian(N):
    from qutip.tensor import tensor
    from qutip.operators import destroy, qeye
    from qutip.superoperator import liouvillian
    wc = 1.0 * 2 * np.pi  # cavity frequency
    wa = 1.0 * 2 * np.pi  # atom frequency
    g = 0.05 * 2 * np.pi  # coupling strength
    kappa = 0.005  # cavity dissipation rate
    gamma = 0.05  # atom dissipation rate
    n_th_a = 1  # temperature in frequency units
    use_rwa = 0
    # operators
    a = tensor(destroy(N), qeye(2))
    sm = tensor(qeye(N), destroy(2))
    # Hamiltonian
    if use_rwa:
        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())
    c_op_list = []

    rate = kappa * (1 + n_th_a)
    if rate > 0.0:
        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)

    return liouvillian(H, c_op_list)
Esempio n. 30
0
def _mesolve_QobjEvo(H, c_ops, tlist, args, opt):
    """
    Prepare the system for the solver, H can be an QobjEvo.
    """
    H_td = QobjEvo(H, args, tlist=tlist)
    if not issuper(H_td.cte):
        L_td = liouvillian(H_td)
    else:
        L_td = H_td
    for op in c_ops:
        # We want to avoid passing tlist where it isn't necessary, to allow a
        # Hamiltonian/Liouvillian which already _has_ time-dependence not equal
        # to the mesolve evaluation times to be used in conjunction with
        # time-independent c_ops.  If we _always_ pass it, it may appear to
        # QobjEvo that there is a tlist mismatch, even though it is not used.
        if isinstance(op, Qobj):
            op_td = QobjEvo(op)
        elif isinstance(op, QobjEvo):
            op_td = QobjEvo(op, args)
        else:
            op_td = QobjEvo(op, args, tlist=tlist)
        if not issuper(op_td.cte):
            op_td = lindblad_dissipator(op_td)
        L_td += op_td

    if opt.rhs_with_state:
        L_td._check_old_with_state()

    nthread = opt.openmp_threads if opt.use_openmp else 0
    L_td.compile(omp=nthread)

    ss = SolverSystem()
    ss.H = L_td
    ss.makefunc = _qobjevo_set
    solver_safe["mesolve"] = ss
    return ss
Esempio n. 31
0
 def liouvillian_c(c_ops, chi):
     return liouvillian(None, c_ops=[c_ops], chi=chi)
Esempio n. 32
0
def bloch_redfield_tensor(H, a_ops, spectra_cb=None, c_ops=[], use_secular=True, sec_cutoff=0.1):
    """
    Calculate the Bloch-Redfield tensor for a system given a set of operators
    and corresponding spectral functions that describes the system's coupling
    to its environment.

    .. note::

        This tensor generation requires a time-independent Hamiltonian.

    Parameters
    ----------

    H : :class:`qutip.qobj`
        System Hamiltonian.

    a_ops : list of :class:`qutip.qobj`
        List of system operators that couple to the environment.

    spectra_cb : list of callback functions
        List of callback functions that evaluate the noise power spectrum
        at a given frequency.

    c_ops : list of :class:`qutip.qobj`
        List of system collapse operators.

    use_secular : bool
        Flag (True of False) that indicates if the secular approximation should
        be used.
    
    sec_cutoff : float {0.1}
        Threshold for secular approximation.

    Returns
    -------

    R, kets: :class:`qutip.Qobj`, list of :class:`qutip.Qobj`

        R is the Bloch-Redfield tensor and kets is a list eigenstates of the
        Hamiltonian.

    """
    
    if not (spectra_cb is None):
        warnings.warn("The use of spectra_cb is depreciated.", DeprecationWarning)
        _a_ops = []
        for kk, a in enumerate(a_ops):
            _a_ops.append([a,spectra_cb[kk]])
        a_ops = _a_ops
    
    # Sanity checks for input parameters
    if not isinstance(H, Qobj):
        raise TypeError("H must be an instance of Qobj")

    for a in a_ops:
        if not isinstance(a[0], Qobj) or not a[0].isherm:
            raise TypeError("Operators in a_ops must be Hermitian Qobj.")

    if c_ops is None:
        c_ops = []

    # use the eigenbasis
    evals, ekets = H.eigenstates()

    N = len(evals)
    K = len(a_ops)
    
    #only Lindblad collapse terms
    if K==0:
        Heb = qdiags(evals,0,dims=H.dims)
        L = liouvillian(Heb, c_ops=[c_op.transform(ekets) for c_op in c_ops])
        return L, ekets
    
    
    A = np.array([a_ops[k][0].transform(ekets).full() for k in range(K)])
    Jw = np.zeros((K, N, N), dtype=complex)

    # pre-calculate matrix elements and spectral densities
    # W[m,n] = real(evals[m] - evals[n])
    W = np.real(evals[:,np.newaxis] - evals[np.newaxis,:])

    for k in range(K):
        # do explicit loops here in case spectra_cb[k] can not deal with array arguments
        for n in range(N):
            for m in range(N):
                Jw[k, n, m] = a_ops[k][1](W[n, m])

    dw_min = np.abs(W[W.nonzero()]).min()

    # pre-calculate mapping between global index I and system indices a,b
    Iabs = np.empty((N*N,3),dtype=int)
    for I, Iab in enumerate(Iabs):
        # important: use [:] to change array values, instead of creating new variable Iab
        Iab[0]  = I
        Iab[1:] = vec2mat_index(N, I)

    # unitary part + dissipation from c_ops (if given):
    Heb = qdiags(evals,0,dims=H.dims)
    L = liouvillian(Heb, c_ops=[c_op.transform(ekets) for c_op in c_ops])
    
    # dissipative part:
    rows = []
    cols = []
    data = []
    for I, a, b in Iabs:
        # only check use_secular once per I
        if use_secular:
            # only loop over those indices J which actually contribute
            Jcds = Iabs[np.where(np.abs(W[a, b] - W[Iabs[:,1], Iabs[:,2]]) < dw_min * sec_cutoff)]
        else:
            Jcds = Iabs
        for J, c, d in Jcds:
            elem = 0+0j
            # summed over k, i.e., each operator coupling the system to the environment
            elem += 0.5 * np.sum(A[:, a, c] * A[:, d, b] * (Jw[:, c, a] + Jw[:, d, b]))
            if b==d:
                #                  sum_{k,n} A[k, a, n] * A[k, n, c] * Jw[k, c, n])
                elem -= 0.5 * np.sum(A[:, a, :] * A[:, :, c] * Jw[:, c, :])
            if a==c:
                #                  sum_{k,n} A[k, d, n] * A[k, n, b] * Jw[k, d, n])
                elem -= 0.5 * np.sum(A[:, d, :] * A[:, :, b] * Jw[:, d, :])
            if elem != 0:
                rows.append(I)
                cols.append(J)
                data.append(elem)

    R = arr_coo2fast(np.array(data, dtype=complex),
                    np.array(rows, dtype=np.int32),
                    np.array(cols, dtype=np.int32), N**2, N**2)
    
    L.data = L.data + R
    
    return L, ekets
Esempio n. 33
0
def essolve(H, rho0, tlist, c_op_list, e_ops):
    """
    Evolution of a state vector or density matrix (`rho0`) for a given
    Hamiltonian (`H`) and set of collapse operators (`c_op_list`), by
    expressing the ODE as an exponential series. The output is either
    the state vector at arbitrary points in time (`tlist`), or the
    expectation values of the supplied operators (`e_ops`).

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

    rho0 : :class:`qutip.qobj`
        Initial state density matrix.

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

    c_op_list : list of :class:`qutip.qobj`
        ``list`` of :class:`qutip.qobj` collapse operators.

    e_ops : list of :class:`qutip.qobj`
        ``list`` of :class:`qutip.qobj` operators for which to evaluate
        expectation values.


    Returns
    -------
     expt_array : array
        Expectation values of wavefunctions/density matrices for the
        times specified in ``tlist``.


    .. note:: This solver does not support time-dependent Hamiltonians.

    """
    n_expt_op = len(e_ops)
    n_tsteps = len(tlist)

    # Calculate the Liouvillian
    if (c_op_list is None or len(c_op_list) == 0) and isket(rho0):
        L = H
    else:
        L = liouvillian(H, c_op_list)

    es = ode2es(L, rho0)

    # evaluate the expectation values
    if n_expt_op == 0:
        results = [Qobj()] * n_tsteps
    else:
        results = np.zeros([n_expt_op, n_tsteps], dtype=complex)

    for n, e in enumerate(e_ops):
        results[n, :] = expect(e, esval(es, tlist))

    data = Result()
    data.solver = "essolve"
    data.times = tlist
    data.expect = [np.real(results[n, :]) if e.isherm else results[n, :] for n, e in enumerate(e_ops)]

    return data
Esempio n. 34
0
def essolve(H, rho0, tlist, c_op_list, e_ops):
    """
    Evolution of a state vector or density matrix (`rho0`) for a given
    Hamiltonian (`H`) and set of collapse operators (`c_op_list`), by
    expressing the ODE as an exponential series. The output is either
    the state vector at arbitrary points in time (`tlist`), or the
    expectation values of the supplied operators (`e_ops`).

    .. deprecated:: 4.6.0
        :obj:`~essolev` will be removed in QuTiP 5.  Please use :obj:`~sesolve`
        or :obj:`~mesolve` for general-purpose integration of the
        Schroedinger/Lindblad master equation.  This will likely be faster than
        :obj:`~essolve` for you.

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

    rho0 : :class:`qutip.qobj`
        Initial state density matrix.

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

    c_op_list : list of :class:`qutip.qobj`
        ``list`` of :class:`qutip.qobj` collapse operators.

    e_ops : list of :class:`qutip.qobj`
        ``list`` of :class:`qutip.qobj` operators for which to evaluate
        expectation values.


    Returns
    -------
     expt_array : array
        Expectation values of wavefunctions/density matrices for the
        times specified in ``tlist``.


    .. note:: This solver does not support time-dependent Hamiltonians.

    """
    n_expt_op = len(e_ops)
    n_tsteps = len(tlist)

    # Calculate the Liouvillian
    if (c_op_list is None or len(c_op_list) == 0) and isket(rho0):
        L = H
    else:
        L = liouvillian(H, c_op_list)

    es = ode2es(L, rho0)

    # evaluate the expectation values
    if n_expt_op == 0:
        results = [Qobj()] * n_tsteps
    else:
        results = np.zeros([n_expt_op, n_tsteps], dtype=complex)

    for n, e in enumerate(e_ops):
        results[n, :] = expect(e, esval(es, tlist))

    data = Result()
    data.solver = "essolve"
    data.times = tlist
    data.expect = [
        np.real(results[n, :]) if e.isherm else results[n, :]
        for n, e in enumerate(e_ops)
    ]

    return data
Esempio n. 35
0
def steadystate(A, c_op_list=[], **kwargs):
    """Calculates the steady state for quantum evolution subject to the
    supplied Hamiltonian or Liouvillian operator and (if given a Hamiltonian) a
    list of collapse operators.

    If the user passes a Hamiltonian then it, along with the list of collapse
    operators, will be converted into a Liouvillian operator in Lindblad form.

    Parameters
    ----------
    A : qobj
        A Hamiltonian or Liouvillian operator.

    c_op_list : list
        A list of collapse operators.

    method : str {'direct', 'eigen', 'iterative-bicg',
                  'iterative-gmres', 'svd', 'power'}
        Method for solving the underlying linear equation. Direct LU solver
        'direct' (default), sparse eigenvalue problem 'eigen',
        iterative GMRES method 'iterative-gmres', iterative LGMRES method
        'iterative-lgmres', SVD 'svd' (dense), or inverse-power method 'power'.

    sparse : bool, optional, default=True
        Solve for the steady state using sparse algorithms. If set to False,
        the underlying Liouvillian operator will be converted into a dense
        matrix. Use only for 'smaller' systems.

    use_rcm : bool, optional, default=True
        Use reverse Cuthill-Mckee reordering to minimize fill-in in the
        LU factorization of the Liouvillian.

    use_wbm : bool, optional, default=False
        Use Weighted Bipartite Matching reordering to make the Liouvillian
        diagonally dominant.  This is useful for iterative preconditioners
        only, and is set to ``True`` by default when finding a preconditioner.

    weight : float, optional
        Sets the size of the elements used for adding the unity trace condition
        to the linear solvers.  This is set to the average abs value of the
        Liouvillian elements if not specified by the user.

    use_umfpack : bool {False, True}
        Use umfpack solver instead of SuperLU.  For SciPy 0.14+, this option
        requires installing scikits.umfpack.

    maxiter : int, optional, default=10000
        Maximum number of iterations to perform if using an iterative method.

    tol : float, optional, default=1e-9
        Tolerance used for terminating solver solution when using iterative
        solvers.

    permc_spec : str, optional, default='COLAMD'
        Column ordering used internally by superLU for the 'direct' LU
        decomposition method. Options include 'COLAMD' and 'NATURAL'.
        If using RCM then this is set to 'NATURAL' automatically unless
        explicitly specified.

    use_precond : bool optional, default = True
        ITERATIVE ONLY. Use an incomplete sparse LU decomposition as a
        preconditioner for the 'iterative' GMRES and BICG solvers.
        Speeds up convergence time by orders of magnitude in many cases.

    M : {sparse matrix, dense matrix, LinearOperator}, optional
        Preconditioner for A. The preconditioner should approximate the inverse
        of A. Effective preconditioning dramatically improves the rate of
        convergence, for iterative methods only .  If no preconditioner is
        given and ``use_precond=True``, then one is generated automatically.

    fill_factor : float, optional, default=10
        ITERATIVE ONLY. Specifies the fill ratio upper bound (>=1) of the iLU
        preconditioner.  Lower values save memory at the cost of longer
        execution times and a possible singular factorization.

    drop_tol : float, optional, default=1e-3
        ITERATIVE ONLY. Sets the threshold for the magnitude of preconditioner
        elements that should be dropped.  Can be reduced for a courser
        factorization at the cost of an increased number of iterations, and a
        possible singular factorization.

    diag_pivot_thresh : float, optional, default=None
        ITERATIVE ONLY. Sets the threshold between [0,1] for which diagonal
        elements are considered acceptable pivot points when using a
        preconditioner.  A value of zero forces the pivot to be the diagonal
        element.

    ILU_MILU : str, optional, default='smilu_2'
        Selects the incomplete LU decomposition method algoithm used in
        creating the preconditoner. Should only be used by advanced users.


    Returns
    -------
    dm : qobj
        Steady state density matrix.

    Notes
    -----
    The SVD method works only for dense operators (i.e. small systems).

    """
    ss_args = _default_steadystate_args()
    for key in kwargs.keys():
        if key in ss_args.keys():
            ss_args[key] = kwargs[key]
        else:
            raise Exception(
                "Invalid keyword argument '"+key+"' passed to steadystate.")

    # Set column perm to NATURAL if using RCM and not specified by user
    if ss_args['use_rcm'] and ('permc_spec' not in kwargs.keys()):
        ss_args['permc_spec'] = 'NATURAL'

    # Set use_wbm=True if using iterative solver with preconditioner and
    # not explicitly set to False by user
    if (ss_args['method'] in ['iterative-lgmres', 'iterative-gmres']) \
            and ('use_wbm' not in kwargs.keys()):
        ss_args['use_wbm'] = True

    n_op = len(c_op_list)

    if isoper(A):
        if n_op == 0:
            raise TypeError('Cannot calculate the steady state for a ' +
                            'non-dissipative system ' +
                            '(no collapse operators given)')
        else:
            A = liouvillian(A, c_op_list)
    if not issuper(A):
        raise TypeError('Solving for steady states requires ' +
                        'Liouvillian (super) operators')

    # Set weight parameter to avg abs val in L if not set explicitly
    if 'weight' not in kwargs.keys():
        ss_args['weight'] = np.mean(np.abs(A.data.data.max()))

    if ss_args['method'] == 'direct':
        if ss_args['sparse']:
            return _steadystate_direct_sparse(A, ss_args)
        else:
            return _steadystate_direct_dense(A)

    elif ss_args['method'] == 'eigen':
        return _steadystate_eigen(A, ss_args)

    elif ss_args['method'] in ['iterative-gmres', 'iterative-lgmres']:
        return _steadystate_iterative(A, ss_args)

    elif ss_args['method'] == 'svd':
        return _steadystate_svd_dense(A, ss_args)

    elif ss_args['method'] == 'power':
        return _steadystate_power(A, ss_args)

    else:
        raise ValueError('Invalid method argument for steadystate.')
Esempio n. 36
0
def bloch_redfield_tensor(H, a_ops, spectra_cb, c_ops=None, use_secular=True):
    """
    Calculate the Bloch-Redfield tensor for a system given a set of operators
    and corresponding spectral functions that describes the system's coupling
    to its environment.

    .. note::

        This tensor generation requires a time-independent Hamiltonian.

    Parameters
    ----------

    H : :class:`qutip.qobj`
        System Hamiltonian.

    a_ops : list of :class:`qutip.qobj`
        List of system operators that couple to the environment.

    spectra_cb : list of callback functions
        List of callback functions that evaluate the noise power spectrum
        at a given frequency.

    c_ops : list of :class:`qutip.qobj`
        List of system collapse operators.

    use_secular : bool
        Flag (True of False) that indicates if the secular approximation should
        be used.

    Returns
    -------

    R, kets: :class:`qutip.Qobj`, list of :class:`qutip.Qobj`

        R is the Bloch-Redfield tensor and kets is a list eigenstates of the
        Hamiltonian.

    """

    # Sanity checks for input parameters
    if not isinstance(H, Qobj):
        raise TypeError("H must be an instance of Qobj")

    for a in a_ops:
        if not isinstance(a, Qobj) or not a.isherm:
            raise TypeError("Operators in a_ops must be Hermitian Qobj.")

    # default spectrum
    if not spectra_cb:
        spectra_cb = [lambda w: 1.0 for _ in a_ops]

    # use the eigenbasis
    evals, ekets = H.eigenstates()

    N = len(evals)
    K = len(a_ops)
    A = np.zeros((K, N, N), dtype=complex)  # TODO: use sparse here
    W = np.zeros((N, N))

    # pre-calculate matrix elements
    for n in range(N):
        for m in range(N):
            W[m, n] = np.real(evals[m] - evals[n])

    for k in range(K):
        # A[k,n,m] = a_ops[k].matrix_element(ekets[n], ekets[m])
        A[k, :, :] = a_ops[k].transform(ekets).full()

    dw_min = abs(W[W.nonzero()]).min()

    # unitary part
    Heb = H.transform(ekets)
    if c_ops is not None:
        R = liouvillian(Heb, c_ops=[c_op.transform(ekets) for c_op in c_ops])
    else:
        R = -1.0j * (spre(Heb) - spost(Heb))
    R.data = R.data.tolil()
    for I in range(N * N):
        a, b = vec2mat_index(N, I)
        for J in range(N * N):
            c, d = vec2mat_index(N, J)

            # unitary part: use spre and spost above, same as this:
            # R.data[I,J] = -1j * W[a,b] * (a == c) * (b == d)

            if use_secular is False or abs(W[a, b] - W[c, d]) < dw_min / 10.0:

                # dissipative part:
                for k in range(K):
                    # for each operator coupling the system to the environment

                    R.data[I, J] += ((A[k, a, c] * A[k, d, b] / 2) *
                                     (spectra_cb[k](W[c, a]) +
                                      spectra_cb[k](W[d, b])))
                    s1 = s2 = 0
                    for n in range(N):
                        s1 += A[k, a, n] * A[k, n, c] * spectra_cb[k](W[c, n])
                        s2 += A[k, d, n] * A[k, n, b] * spectra_cb[k](W[d, n])

                    R.data[I, J] += - (b == d) * s1 / 2 - (a == c) * s2 / 2

    R.data = R.data.tocsr()
    return R, ekets
Esempio n. 37
0
    def configure(self, H_sys, coup_op, coup_strength, temperature,
                     N_cut, N_exp, cut_freq, planck=None, boltzmann=None,
                     renorm=None, bnd_cut_approx=None,
                     options=None, progress_bar=None, stats=None):
        """
        Calls configure from :class:`HEOMSolver` and sets any attributes
        that are specific to this subclass
        """
        start_config = timeit.default_timer()

        HEOMSolver.configure(self, H_sys, coup_op, coup_strength,
                    temperature, N_cut, N_exp,
                    planck=planck, boltzmann=boltzmann,
                    options=options, progress_bar=progress_bar, stats=stats)
        self.cut_freq = cut_freq
        if renorm is not None: self.renorm = renorm
        if bnd_cut_approx is not None: self.bnd_cut_approx = bnd_cut_approx

        # Load local values for optional parameters
        # Constants and Hamiltonian.
        hbar = self.planck
        options = self.options
        progress_bar = self.progress_bar
        stats = self.stats


        if stats:
            ss_conf = stats.sections.get('config')
            if ss_conf is None:
                ss_conf = stats.add_section('config')

        c, nu = self._calc_matsubara_params()

        if renorm:
            norm_plus, norm_minus = self._calc_renorm_factors()
            if stats:
                stats.add_message('options', 'renormalisation', ss_conf)
        # Dimensions et by system
        sup_dim = H_sys.dims[0][0]**2
        unit_sys = qeye(H_sys.dims[0])

        # Use shorthands (mainly as in referenced PRL)
        lam0 = self.coup_strength
        gam = self.cut_freq
        N_c = self.N_cut
        N_m = self.N_exp
        Q = coup_op # Q as shorthand for coupling operator
        beta = 1.0/(self.boltzmann*self.temperature)

        # Ntot is the total number of ancillary elements in the hierarchy
        # Ntot = factorial(N_c + N_m) / (factorial(N_c)*factorial(N_m))
        # Turns out to be the same as nstates from state_number_enumerate
        N_he, he2idx, idx2he = enr_state_dictionaries([N_c + 1]*N_m , N_c)

        unit_helems = sp.identity(N_he, format='csr')
        if self.bnd_cut_approx:
            # the Tanimura boundary cut off operator
            if stats:
                stats.add_message('options', 'boundary cutoff approx', ss_conf)
            op = -2*spre(Q)*spost(Q.dag()) + spre(Q.dag()*Q) + spost(Q.dag()*Q)

            approx_factr = ((2*lam0 / (beta*gam*hbar)) - 1j*lam0) / hbar
            for k in range(N_m):
                approx_factr -= (c[k] / nu[k])
            L_bnd = -approx_factr*op.data
            L_helems = sp.kron(unit_helems, L_bnd)
        else:
            L_helems = sp.csr_matrix((N_he*sup_dim, N_he*sup_dim),
                                     dtype=complex)

        # Build the hierarchy element interaction matrix
        if stats: start_helem_constr = timeit.default_timer()

        unit_sup = spre(unit_sys).data
        spreQ = spre(Q).data
        spostQ = spost(Q).data
        commQ = (spre(Q) - spost(Q)).data
        N_he_interact = 0

        for he_idx in range(N_he):
            he_state = list(idx2he[he_idx])
            n_excite = sum(he_state)

            # The diagonal elements for the hierarchy operator
            # coeff for diagonal elements
            sum_n_m_freq = 0.0
            for k in range(N_m):
                sum_n_m_freq += he_state[k]*nu[k]

            op = -sum_n_m_freq*unit_sup
            L_he = _pad_csr(op, N_he, N_he, he_idx, he_idx)
            L_helems += L_he

            # Add the neighour interations
            he_state_neigh = copy(he_state)
            for k in range(N_m):

                n_k = he_state[k]
                if n_k >= 1:
                    # find the hierarchy element index of the neighbour before
                    # this element, for this Matsubara term
                    he_state_neigh[k] = n_k - 1
                    he_idx_neigh = he2idx[tuple(he_state_neigh)]

                    op = c[k]*spreQ - np.conj(c[k])*spostQ
                    if renorm:
                        op = -1j*norm_minus[n_k, k]*op
                    else:
                        op = -1j*n_k*op

                    L_he = _pad_csr(op, N_he, N_he, he_idx, he_idx_neigh)
                    L_helems += L_he
                    N_he_interact += 1

                    he_state_neigh[k] = n_k

                if n_excite <= N_c - 1:
                    # find the hierarchy element index of the neighbour after
                    # this element, for this Matsubara term
                    he_state_neigh[k] = n_k + 1
                    he_idx_neigh = he2idx[tuple(he_state_neigh)]

                    op = commQ
                    if renorm:
                        op = -1j*norm_plus[n_k, k]*op
                    else:
                        op = -1j*op

                    L_he = _pad_csr(op, N_he, N_he, he_idx, he_idx_neigh)
                    L_helems += L_he
                    N_he_interact += 1

                    he_state_neigh[k] = n_k

        if stats:
            stats.add_timing('hierarchy contruct',
                             timeit.default_timer() - start_helem_constr,
                            ss_conf)
            stats.add_count('Num hierarchy elements', N_he, ss_conf)
            stats.add_count('Num he interactions', N_he_interact, ss_conf)

        # Setup Liouvillian
        if stats: start_louvillian = timeit.default_timer()
        H_he = sp.kron(unit_helems, liouvillian(H_sys).data)

        L_helems += H_he

        if stats:
            stats.add_timing('Liouvillian contruct',
                             timeit.default_timer() - start_louvillian,
                            ss_conf)

        if stats: start_integ_conf = timeit.default_timer()

        r = scipy.integrate.ode(cy_ode_rhs)

        r.set_f_params(L_helems.data, L_helems.indices, L_helems.indptr)
        r.set_integrator('zvode', method=options.method, order=options.order,
                         atol=options.atol, rtol=options.rtol,
                         nsteps=options.nsteps, first_step=options.first_step,
                         min_step=options.min_step, max_step=options.max_step)

        if stats:
            time_now = timeit.default_timer()
            stats.add_timing('Liouvillian contruct',
                             time_now - start_integ_conf,
                            ss_conf)
            if ss_conf.total_time is None:
                ss_conf.total_time = time_now - start_config
            else:
                ss_conf.total_time += time_now - start_config

        self._ode = r
        self._N_he = N_he
        self._sup_dim = sup_dim
        self._configured = True
Esempio n. 38
0
def _mesolve_const(H, rho0, tlist, c_op_list, e_ops, args, opt, progress_bar):
    """
    Evolve the density matrix using an ODE solver, for constant hamiltonian
    and collapse operators.
    """

    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)

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

    L = liouvillian(H, c_op_list)

    #
    # setup integrator
    #
    initial_vector = mat2vec(rho0.full()).ravel('F')
    if issuper(rho0):
        r = scipy.integrate.ode(_ode_super_func)
        r.set_f_params(L.data)
    else:
        if opt.use_openmp and L.data.nnz >= qset.openmp_thresh:
            r = scipy.integrate.ode(cy_ode_rhs_openmp)
            r.set_f_params(L.data.data, L.data.indices, L.data.indptr,
                           opt.openmp_threads)
        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,
                     max_step=opt.max_step)
    r.set_initial_value(initial_vector, tlist[0])

    #
    # call generic ODE code
    #
    return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
Esempio n. 39
0
def _mesolve_func_td(L_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):
        rho0 = ket2dm(rho0)

    #
    # construct liouvillian
    #
    new_args = None

    if len(c_op_list) > 0:
        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 type(args) is dict:
        new_args = {}
        for key in args:
            if isinstance(args[key], Qobj):
                if isoper(args[key]):
                    new_args[key] = (-1j * (spre(args[key]) - spost(args[key]))).data
                else:
                    new_args[key] = args[key].data
            else:
                new_args[key] = args[key]

    elif type(args) is list or type(args) is tuple:
        new_args = []
        for arg in args:
            if isinstance(arg, Qobj):
                if isoper(arg):
                    new_args.append((-1j * (spre(arg) - spost(arg))).data)
                else:
                    new_args.append(arg.data)
            else:
                new_args.append(arg)

        if type(args) is tuple:
            new_args = tuple(new_args)
    else:
        if isinstance(args, Qobj):
            if isoper(args):
                new_args = (-1j * (spre(args) - spost(args))).data
            else:
                new_args = args.data
        else:
            new_args = args

    #
    # setup integrator
    #
    initial_vector = mat2vec(rho0.full()).ravel()
    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,
        max_step=opt.max_step,
    )
    r.set_initial_value(initial_vector, tlist[0])
    r.set_f_params(L_data, L_func, new_args)

    #
    # call generic ODE code
    #
    return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
Esempio n. 40
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)
Esempio n. 41
0
def _mesolve_list_func_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
    #
    if isket(rho0):
        rho0 = rho0 * rho0.dag()

    #
    # construct liouvillian in list-function format
    #
    L_list = []
    if opt.rhs_with_state:
        constant_func = lambda x, y, z: 1.0
    else:
        constant_func = lambda x, y: 1.0

    # add all hamitonian terms to the lagrangian list
    for h_spec in H_list:

        if isinstance(h_spec, Qobj):
            h = h_spec
            h_coeff = constant_func

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

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

        if isoper(h):
            L_list.append([(-1j * (spre(h) - spost(h))).data, h_coeff, False])

        elif issuper(h):
            L_list.append([h.data, h_coeff, False])

        else:
            raise TypeError(
                "Incorrect specification of time-dependent " + "Hamiltonian (expected operator or superoperator)"
            )

    # add all collapse operators to the liouvillian list
    for c_spec in c_list:

        if isinstance(c_spec, Qobj):
            c = c_spec
            c_coeff = constant_func
            c_square = False

        elif isinstance(c_spec, list) and isinstance(c_spec[0], Qobj):
            c = c_spec[0]
            c_coeff = c_spec[1]
            c_square = True

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

        if isoper(c):
            L_list.append([liouvillian(None, [c], data_only=True), c_coeff, c_square])

        elif issuper(c):
            L_list.append([c.data, c_coeff, c_square])

        else:
            raise TypeError(
                "Incorrect specification of time-dependent "
                + "collapse operators (expected operator or "
                + "superoperator)"
            )

    #
    # setup integrator
    #
    initial_vector = mat2vec(rho0.full()).ravel()
    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,
        max_step=opt.max_step,
    )
    r.set_initial_value(initial_vector, tlist[0])
    r.set_f_params(L_list, args)

    #
    # call generic ODE code
    #
    return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
Esempio n. 42
0
def bloch_redfield_tensor(H, a_ops, spectra_cb, c_ops=[], use_secular=True):
    """
    Calculate the Bloch-Redfield tensor for a system given a set of operators
    and corresponding spectral functions that describes the system's coupling
    to its environment.

    .. note::

        This tensor generation requires a time-independent Hamiltonian.

    Parameters
    ----------

    H : :class:`qutip.qobj`
        System Hamiltonian.

    a_ops : list of :class:`qutip.qobj`
        List of system operators that couple to the environment.

    spectra_cb : list of callback functions
        List of callback functions that evaluate the noise power spectrum
        at a given frequency.

    c_ops : list of :class:`qutip.qobj`
        List of system collapse operators.

    use_secular : bool
        Flag (True of False) that indicates if the secular approximation should
        be used.

    Returns
    -------

    R, kets: :class:`qutip.Qobj`, list of :class:`qutip.Qobj`

        R is the Bloch-Redfield tensor and kets is a list eigenstates of the
        Hamiltonian.

    """

    # Sanity checks for input parameters
    if not isinstance(H, Qobj):
        raise TypeError("H must be an instance of Qobj")

    for a in a_ops:
        if not isinstance(a, Qobj) or not a.isherm:
            raise TypeError("Operators in a_ops must be Hermitian Qobj.")

    # default spectrum
    if not spectra_cb:
        spectra_cb = [lambda w: 1.0 for _ in a_ops]

    if c_ops is None:
        c_ops = []

    # use the eigenbasis
    evals, ekets = H.eigenstates()

    N = len(evals)
    K = len(a_ops)
    A = np.zeros((K, N, N), dtype=complex)  # TODO: use sparse here
    Jw = np.zeros((K, N, N), dtype=complex)

    # pre-calculate matrix elements and spectral densities
    # W[m,n] = real(evals[m] - evals[n])
    W = np.real(evals[:,np.newaxis] - evals[np.newaxis,:])

    for k in range(K):
        # A[k,n,m] = a_ops[k].matrix_element(ekets[n], ekets[m])
        A[k, :, :] = a_ops[k].transform(ekets).full()
        # do explicit loops here in case spectra_cb[k] can not deal with array arguments
        for n in range(N):
            for m in range(N):
                Jw[k, n, m] = spectra_cb[k](W[n, m])

    dw_min = abs(W[W.nonzero()]).min()

    # pre-calculate mapping between global index I and system indices a,b
    Iabs = np.empty((N*N,3),dtype=int)
    for I, Iab in enumerate(Iabs):
        # important: use [:] to change array values, instead of creating new variable Iab
        Iab[0]  = I
        Iab[1:] = vec2mat_index(N, I)

    # unitary part + dissipation from c_ops (if given):
    Heb = H.transform(ekets)
    R = liouvillian(Heb, c_ops=[c_op.transform(ekets) for c_op in c_ops])
    R.data = R.data.tolil()

    # dissipative part:
    for I, a, b in Iabs:
        # only check use_secular once per I
        if use_secular:
            # only loop over those indices J which actually contribute
            Jcds = Iabs[np.where(abs(W[a, b] - W[Iabs[:,1], Iabs[:,2]]) < dw_min / 10.0)]
        else:
            Jcds = Iabs
        for J, c, d in Jcds:
            # summed over k, i.e., each operator coupling the system to the environment
            R.data[I, J] += 0.5 * np.sum(A[:, a, c] * A[:, d, b] * (Jw[:, c, a] + Jw[:, d, b]))
            if b==d:
                #                  sum_{k,n} A[k, a, n] * A[k, n, c] * Jw[k, c, n])
                R.data[I, J] -= 0.5 * np.sum(A[:, a, :] * A[:, :, c] * Jw[:, c, :])
            if a==c:
                #                  sum_{k,n} A[k, d, n] * A[k, n, b] * Jw[k, d, n])
                R.data[I, J] -= 0.5 * np.sum(A[:, d, :] * A[:, :, b] * Jw[:, d, :])

    R.data = R.data.tocsr()
    return R, ekets
Esempio n. 43
0
def _mesolve_func_td(L_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):
        rho0 = ket2dm(rho0)

    #
    # construct liouvillian
    #
    new_args = None

    if len(c_op_list) > 0:
        L_data = liouvillian(None, c_op_list).data
    else:
        n, m = rho0.shape
        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 = {}
        for key in args:
            if isinstance(args[key], Qobj):
                if isoper(args[key]):
                    new_args[key] = (-1j *
                                     (spre(args[key]) - spost(args[key])))
                else:
                    new_args[key] = args[key]
            else:
                new_args[key] = args[key]

    elif type(args) is list or type(args) is tuple:
        new_args = []
        for arg in args:
            if isinstance(arg, Qobj):
                if isoper(arg):
                    new_args.append((-1j * (spre(arg) - spost(arg))).data)
                else:
                    new_args.append(arg.data)
            else:
                new_args.append(arg)

        if type(args) is tuple:
            new_args = tuple(new_args)
    else:
        if isinstance(args, Qobj):
            if isoper(args):
                new_args = (-1j * (spre(args) - spost(args)))
            else:
                new_args = args
        else:
            new_args = args

    #
    # setup integrator
    #
    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:
        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,
                     max_step=opt.max_step)
    r.set_initial_value(initial_vector, tlist[0])
    r.set_f_params(L_data, L_func, new_args)

    #
    # call generic ODE code
    #
    return _generic_ode_solve(r, rho0, tlist, e_ops, opt, progress_bar)
Esempio n. 44
0
def _mesolve_list_td(H_func, rho0, tlist, c_op_list, expt_ops, args, opt):
    """!
    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,
        # fallback on the unitary schrodinger equation solver
        if len(c_op_list) == 0:
            return _sesolve_list_td(H_func, rho0, tlist, expt_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:
        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)]
    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[" + str(k) + "],Linds[" + str(k) + "],Lptrs[" +
                   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 cyq_td_ode_rhs',
                       '<string>', 'exec')
        exec(code, globals())
        odeconfig.tdfunc = cyq_td_ode_rhs

    #
    # setup integrator
    #
    initial_vector = mat2vec(rho0.full())
    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, expt_ops, opt, vec2mat)
Esempio n. 45
0
    def gradient_CFIM(self, M, obj_fun):
        '''
        Input:
        1) M:
            TYPE: list of Qobj (matrix)
            DESCRIPTION: measurement. It takes the form [M1,M2,...], where M1, M2 ...
                        are matrices and satisfies M1+M2+... = identity matrix.
        3) obj_fun
            TYPE: string
            DESCRIPTION: obj_fun = {'f0','f1','exact'}.
            Different with the single-parameter case, the GRAPE here uses two alternative objective functions:
            'f0': $f_{0}=\sum_{\alpha} 1/F_{\alpha\alpha}$.
            'f1': the lower bound $f_{1}=d^2/TrF$.
            Notice both above functions only use the gradient of diagonal entries of CFIM.
            'exact': exact gradient for TrF^{-1}, however, it is ONLY valid for two-parameter systems.
        Output:
        1) updated values of self.control_coefficients.
        Notice: To run this function, the function 'propagation_multiple()' has to be run first.
        '''
        if len(self.control_Hamiltonian) > 2 and obj_fun == 'exact':
            raise TypeError('the "exact" mode is only valid for two-parameter estimations!')

        num = len(self.times)
        dim = self.freeHamiltonian.dims[0][0]
        dt = self.times[1] - self.times[0]
        Hc_coeff = self.control_coefficients
        D = self.propagator

        rhoTvec = self.rho[num-1]
        rhoTmat = Qobj(vec2mat(rhoTvec.full()))
        drhoTvec = self.rho_derivative[num-1]
        drhoTmat = [Qobj(vec2mat(drhoTvec[i].full())) for i in range(0, len(self.Hamiltonian_derivative))]

        #Generation of L1 and L2 for diagonal entries of CFIM (i.e., alpha = beta):
        L1 = [Qobj(np.zeros((dim, dim))) for i in range(0, len(self.Hamiltonian_derivative))]
        L2 = [Qobj(np.zeros((dim, dim))) for i in range(0, len(self.Hamiltonian_derivative))]
        for para_i in range(0, len(self.Hamiltonian_derivative)):
            for mi in range(0, len(M)):
                ptemp = (rhoTmat * M[mi]).tr()
                dptemp = (drhoTmat[para_i] * M[mi]).tr()
                if ptemp != 0:
                    L1[para_i] += (dptemp / ptemp) * M[mi]
                    L2[para_i] += ((dptemp / ptemp) ** 2) * M[mi]

        # Generation L2 for off-diagonal entries of CFIM in two-parameter estimation:
        if len(self.Hamiltonian_derivative) == 2:
            L2_offdiag = Qobj(np.zeros((dim, dim)))
            for mi in range(0, len(M)):
                ptp_2para = (rhoTmat * M[mi]).tr()
                dptp0_2para = (drhoTmat[0] * M[mi]).tr()
                dptp1_2para = (drhoTmat[1] * M[mi]).tr()
                L2_offdiag += (dptp0_2para * dptp1_2para / ptp_2para / ptp_2para) * M[mi]

        # Generation of CFIM at the target time
        CFIM_temp = CR.CFIM(rhoTmat, drhoTmat, M)
        norm_f0 = 0.
        for ci in range(0, len(self.Hamiltonian_derivative)):
            norm_f0 += 1 / CFIM_temp[ci, ci]
        norm_f0 = norm_f0**2
        M2_2para = [[] for i in range(0, 2)]
        M3_2para = [[] for i in range(0, 2)]

        #update
        for ki in range(0, len(self.control_Hamiltonian)):
            Hk = 1.j * Qobj(liouvillian(self.control_Hamiltonian[ki]).full())
            Hc_ki = Hc_coeff[ki]
            for ti in range(0, num):
                Mj1 = 1.j * D[ti+1][num-1] * Hk * self.rho[ti]
                Mj1mat = Qobj(vec2mat(Mj1.fulll()))
                delta = 0.

                for para_i in range(0, len(self.control_Hamiltonian)):
                    dH0_i = 1.j * Qobj(liouvillian(self.Hamiltonian_derivative[para_i]).full)
                    Mj2 = Qobj(np.zeros((dim*dim, 1)))
                    for ri in range(0,ti+1):
                        Mj2 += D[ti+1][num-1] * Hk * D[ri+1][ti] * dH0_i * self.rho[ri]

                    Mj3 = Qobj(np.zeros((dim*dim,1)))
                    for ri in range(ti+1, num):
                        Mj3 += D[ri+1][num-1] * dH0_i * D[ti+1][ri] * Hk * self.rho[ti]
                    Mj2mat = Qobj(vec2mat(Mj2.full()))
                    Mj3mat = Qobj(vec2mat(Mj3.full()))

                    term1 = dt * (L2[para_i] * Mj1mat).tr()
                    term2 = -2 * (dt ** 2) * (L1[para_i] * (Mj2mat + Mj3mat)).tr()
                    if obj_fun == 'f0':
                        delta += np.real(term1+term2)/((CFIM_temp[para_i, para_i])**2)/norm_f0
                    elif obj_fun == 'f1':
                        delta += np.real(term1+term2)/(float(len(self.control_Hamiltonian))**2)
                    elif obj_fun == 'exact':
                        delta += np.real(term1+term2)*(CFIM_temp[1-para_i, 1-para_i]**2 + CFIM_temp[0, 1]**2) \
                                /(CFIM_temp.tr()**2)
                        M2_2para[para_i] = Mj2mat
                        M3_2para[para_i] = Mj3mat

                if obj_fun == 'exact':
                    grad_offdiag = dt * (L2_offdiag * Mj1mat).tr() \
                                - (dt**2) * (L1[1] * (M2_2para[0] + M3_2para[0])).tr() \
                                - (dt**2) * (L1[0] * (M2_2para[1] + M3_2para[1])).tr()
                    delta += - np.real(2 * grad_offdiag * CFIM_temp[0, 1]/CFIM_temp.tr())

                Hc_ki[ti] += Hc_ki[ti] + self.epsilon * delta
            Hc_coeff[ki] = Hc_ki
        self.control_coefficients = Hc_coeff
Esempio n. 46
0
def bloch_redfield_tensor(H, a_ops, spectra_cb, c_ops=[], use_secular=True):
    """
    Calculate the Bloch-Redfield tensor for a system given a set of operators
    and corresponding spectral functions that describes the system's coupling
    to its environment.

    .. note::

        This tensor generation requires a time-independent Hamiltonian.

    Parameters
    ----------

    H : :class:`qutip.qobj`
        System Hamiltonian.

    a_ops : list of :class:`qutip.qobj`
        List of system operators that couple to the environment.

    spectra_cb : list of callback functions
        List of callback functions that evaluate the noise power spectrum
        at a given frequency.

    c_ops : list of :class:`qutip.qobj`
        List of system collapse operators.

    use_secular : bool
        Flag (True of False) that indicates if the secular approximation should
        be used.

    Returns
    -------

    R, kets: :class:`qutip.Qobj`, list of :class:`qutip.Qobj`

        R is the Bloch-Redfield tensor and kets is a list eigenstates of the
        Hamiltonian.

    """

    # Sanity checks for input parameters
    if not isinstance(H, Qobj):
        raise TypeError("H must be an instance of Qobj")

    for a in a_ops:
        if not isinstance(a, Qobj) or not a.isherm:
            raise TypeError("Operators in a_ops must be Hermitian Qobj.")

    # default spectrum
    if not spectra_cb:
        spectra_cb = [lambda w: 1.0 for _ in a_ops]

    if c_ops is None:
        c_ops = []

    # use the eigenbasis
    evals, ekets = H.eigenstates()

    N = len(evals)
    K = len(a_ops)

    #only Lindblad collapse terms
    if K == 0:
        Heb = H.transform(ekets)
        L = liouvillian(Heb, c_ops=[c_op.transform(ekets) for c_op in c_ops])
        return L, ekets

    A = np.array([a_ops[k].transform(ekets).full() for k in range(K)])
    Jw = np.zeros((K, N, N), dtype=complex)

    # pre-calculate matrix elements and spectral densities
    # W[m,n] = real(evals[m] - evals[n])
    W = np.real(evals[:, np.newaxis] - evals[np.newaxis, :])

    for k in range(K):
        # do explicit loops here in case spectra_cb[k] can not deal with array arguments
        for n in range(N):
            for m in range(N):
                Jw[k, n, m] = spectra_cb[k](W[n, m])

    dw_min = np.abs(W[W.nonzero()]).min()

    # pre-calculate mapping between global index I and system indices a,b
    Iabs = np.empty((N * N, 3), dtype=int)
    for I, Iab in enumerate(Iabs):
        # important: use [:] to change array values, instead of creating new variable Iab
        Iab[0] = I
        Iab[1:] = vec2mat_index(N, I)

    # unitary part + dissipation from c_ops (if given):
    Heb = H.transform(ekets)
    L = liouvillian(Heb, c_ops=[c_op.transform(ekets) for c_op in c_ops])

    # dissipative part:
    rows = []
    cols = []
    data = []
    for I, a, b in Iabs:
        # only check use_secular once per I
        if use_secular:
            # only loop over those indices J which actually contribute
            Jcds = Iabs[np.where(
                np.abs(W[a, b] - W[Iabs[:, 1], Iabs[:, 2]]) < dw_min / 10.0)]
        else:
            Jcds = Iabs
        for J, c, d in Jcds:
            elem = 0 + 0j
            # summed over k, i.e., each operator coupling the system to the environment
            elem += 0.5 * (A[:, a, c] * A[:, d, b] *
                           (Jw[:, c, a] + Jw[:, d, b]))[0]
            if b == d:
                #                  sum_{k,n} A[k, a, n] * A[k, n, c] * Jw[k, c, n])
                elem -= 0.5 * np.sum(A[:, a, :] * A[:, :, c] * Jw[:, c, :])
            if a == c:
                #                  sum_{k,n} A[k, d, n] * A[k, n, b] * Jw[k, d, n])
                elem -= 0.5 * np.sum(A[:, d, :] * A[:, :, b] * Jw[:, d, :])
            if elem != 0:
                rows.append(I)
                cols.append(J)
                data.append(elem)

    R = sp.coo_matrix((np.array(data), (np.array(rows), np.array(cols))),
                      shape=(N**2, N**2),
                      dtype=complex).tocsr()

    L.data = L.data + R

    return L, ekets
Esempio n. 47
0
def steadystate(A, c_op_list=[], **kwargs):

    """Calculates the steady state for quantum evolution subject to the
    supplied Hamiltonian or Liouvillian operator and (if given a Hamiltonian) a
    list of collapse operators.

    If the user passes a Hamiltonian then it, along with the list of collapse
    operators, will be converted into a Liouvillian operator in Lindblad form.


    Parameters
    ----------
    A : qobj
        A Hamiltonian or Liouvillian operator.

    c_op_list : list
        A list of collapse operators.

    method : str {'direct', 'iterative', 'iterative-bicg', 'svd', 'power'}
        Method for solving the underlying linear equation. Direct solver
        'direct' (default), iterative GMRES method 'iterative',
        iterative method BICGSTAB 'iterative-bicg', SVD 'svd' (dense), 
        or inverse-power method 'power'.

    sparse : bool, {True, False}
        Solve for the steady state using sparse algorithms. If set to False,
        the underlying Liouvillian operator will be converted into a dense
        matrix. Use only for 'smaller' systems.

    use_rcm : bool, {True, False}
        Use reverse Cuthill-Mckee reordering to minimize fill-in in the
        LU factorization of the Liouvillian.
    
    use_umfpack : bool {False, True}
        Use umfpack solver instead of SuperLU.  For SciPy 0.14+, this option
        requires installing scikits.umfpack.

    maxiter : int, optional
        Maximum number of iterations to perform if using an iterative method
        such as 'iterative' (default=1000), or 'power' (default=10).

    tol : float, optional, default=1e-5
        Tolerance used for terminating solver solution when using iterative
        solvers.

    use_precond : bool optional, default = True
        ITERATIVE ONLY. Use an incomplete sparse LU decomposition as a
        preconditioner for the 'iterative' GMRES and BICG solvers.
        Speeds up convergence time by orders of magnitude in many cases.

    M : {sparse matrix, dense matrix, LinearOperator}, optional
        Preconditioner for A. The preconditioner should approximate the inverse
        of A. Effective preconditioning dramatically improves the rate of
        convergence, for iterative methods.  Does not affect other solvers.

    fill_factor : float, default=12
        ITERATIVE ONLY. Specifies the fill ratio upper bound (>=1) of the iLU
        preconditioner.  Lower values save memory at the cost of longer
        execution times and a possible singular factorization.

    drop_tol : float, default=1e-3
        ITERATIVE ONLY. Sets the threshold for the magnitude of preconditioner
        elements that should be dropped.  Can be reduced for a courser
        factorization at the cost of an increased number of iterations, and a
        possible singular factorization.

    diag_pivot_thresh : float, default=None
        ITERATIVE ONLY. Sets the threshold between [0,1] for which diagonal
        elements are considered acceptable pivot points when using a
        preconditioner.  A value of zero forces the pivot to be the diagonal
        element.

    Returns
    -------
    dm : qobj
        Steady state density matrix.


    Notes
    -----
    The SVD method works only for dense operators (i.e. small systems).

    """
    ss_args = _default_steadystate_args()
    for key in kwargs.keys():
        if key in ss_args.keys():
            ss_args[key]=kwargs[key]
        else:
            raise Exception('Invalid keyword argument passed to steadystate.')
        
    n_op = len(c_op_list)

    if isoper(A):
        if n_op == 0:
            raise TypeError('Cannot calculate the steady state for a ' +
                            'non-dissipative system ' +
                            '(no collapse operators given)')
        else:
            A = liouvillian(A, c_op_list)
    if not issuper(A):
        raise TypeError('Solving for steady states requires ' +
                        'Liouvillian (super) operators')

    if ss_args['method'] == 'direct':
        if ss_args['sparse']:
            return _steadystate_direct_sparse(A, ss_args)
        else:
            return _steadystate_direct_dense(A)

    elif ss_args['method'] == 'iterative':
        return _steadystate_iterative(A, ss_args)

    elif ss_args['method'] == 'iterative-bicg':
        return _steadystate_iterative_bicg(A, ss_args)

    elif ss_args['method'] == 'svd':
        return _steadystate_svd_dense(A, ss_args)

    elif ss_args['method'] == 'power':
        return _steadystate_power(A, ss_args)

    else:
        raise ValueError('Invalid method argument for steadystate.')