def floquet_master_equation_tensor(Alist, f_energies): """ Construct a tensor that represents the master equation in the floquet basis (with constant Hamiltonian and collapse operators). Simplest RWA approximation [Grifoni et al, Phys.Rep. 304 229 (1998)] Parameters ---------- Alist : list A list of Floquet-Markov master equation rate matrices. f_energies : array The Floquet energies. Returns ------- output : array The Floquet-Markov master equation tensor `R`. """ if isinstance(Alist, list): # Alist can be a list of rate matrices corresponding # to different operators that couple to the environment N, M = np.shape(Alist[0]) else: # or a simple rate matrix, in which case we put it in a list Alist = [Alist] N, M = np.shape(Alist[0]) R = Qobj(scipy.sparse.csr_matrix((N * N, N * N)), [[N, N], [N, N]], [N * N, N * N]) 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) R.data[I, J] = -1.0j * (f_energies[a] - f_energies[b]) * \ (a == c) * (b == d) for A in Alist: s1 = s2 = 0 for n in range(N): s1 += A[a, n] * (n == c) * (n == d) - A[n, a] * \ (a == c) * (a == d) s2 += (A[n, a] + A[n, b]) * (a == c) * (b == d) dR = (a == b) * s1 - 0.5 * (1 - (a == b)) * s2 if dR != 0.0: R.data[I, J] += dR R.data = R.data.tocsr() return R
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
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
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
def bloch_redfield_tensor(H, a_ops, spectra_cb, 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. 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. 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) 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