def test_hilbert_dist(): """ Metrics: Hilbert distance. """ diag1 = np.array([0.5, 0.5, 0, 0]) diag2 = np.array([0, 0, 0.5, 0.5]) r1 = qdiags(diag1, 0) r2 = qdiags(diag2, 0) assert_(abs(hilbert_dist(r1, r2) - 1) <= 1e-6)
def test_hilbert_dist(): """ Metrics: Hilbert distance. """ diag1 = np.array([0.5, 0.5, 0, 0]) diag2 = np.array([0, 0, 0.5, 0.5]) r1 = qdiags(diag1, 0) r2 = qdiags(diag2, 0) assert_(abs(hilbert_dist(r1, r2)-1) <= 1e-6)
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