def sp_reshape(A, shape, format='csr'): """ Reshapes a sparse matrix. Parameters ---------- A : sparse_matrix Input matrix in any format shape : list/tuple Desired shape of new matrix format : string {'csr','coo','csc','lil'} Optional string indicating desired output format Returns ------- B : csr_matrix Reshaped sparse matrix References ---------- http://stackoverflow.com/questions/16511879/reshape-sparse-matrix-efficiently-python-scipy-0-12 """ if not hasattr(shape, '__len__') or len(shape) != 2: raise ValueError('Shape must be a list of two integers') C = A.tocoo() nrows, ncols = C.shape size = nrows * ncols new_size = shape[0] * shape[1] if new_size != size: raise ValueError('Total size of new array must be unchanged.') flat_indices = ncols * C.row + C.col new_row, new_col = divmod(flat_indices, shape[1]) if format == 'csr': return arr_coo2fast(C.data, new_row, new_col, shape[0], shape[1]) else: B = sp.coo_matrix((C.data, (new_row, new_col)), shape=shape) if format == 'coo': return B elif format == 'csc': return B.tocsc() elif format == 'lil': return B.tolil() else: raise ValueError('Return format not valid.')
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 _permute(Q, order): Qcoo = Q.data.tocoo() if Q.isket: cy_index_permute(Qcoo.row, np.array(Q.dims[0], dtype=np.int32), np.array(order, dtype=np.int32)) new_dims = [[Q.dims[0][i] for i in order], Q.dims[1]] elif Q.isbra: cy_index_permute(Qcoo.col, np.array(Q.dims[1], dtype=np.int32), np.array(order, dtype=np.int32)) new_dims = [Q.dims[0], [Q.dims[1][i] for i in order]] elif Q.isoper: cy_index_permute(Qcoo.row, np.array(Q.dims[0], dtype=np.int32), np.array(order, dtype=np.int32)) cy_index_permute(Qcoo.col, np.array(Q.dims[1], dtype=np.int32), np.array(order, dtype=np.int32)) new_dims = [[Q.dims[0][i] for i in order], [Q.dims[1][i] for i in order]] elif Q.isoperket: # For superoperators, we expect order to be something like # [[0, 2], [1, 3]], which tells us to permute according to # [0, 2, 1 ,3], and then group indices according to the length # of each sublist. # As another example, # permuting [[[1, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3]]] by # [[0, 3], [1, 4], [2, 5]] should give # [[[1, 1], [2, 2], [3, 3]], [[1, 1], [2, 2], [3, 3]]]. # # Get the breakout of the left index into dims. # Since this is a super, the left index itself breaks into left # and right indices, each of which breaks down further. # The best way to deal with that here is to flatten dims. flat_order = np.array(sum(order, []), dtype=np.int32) q_dims = np.array(sum(Q.dims[0], []), dtype=np.int32) cy_index_permute(Qcoo.row, q_dims, flat_order) # Finally, we need to restructure the now-decomposed left index # into left and right subindices, so that the overall dims we return # are of the form specified by order. new_dims = [q_dims[i] for i in flat_order] new_dims = list(_chunk_dims(new_dims, order)) new_dims = [new_dims, [1]] elif Q.isoperbra: flat_order = np.array(sum(order, []), dtype=np.int32) q_dims = np.array(sum(Q.dims[1], []), dtype=np.int32) cy_index_permute(Qcoo.col, q_dims, flat_order) new_dims = [q_dims[i] for i in flat_order] new_dims = list(_chunk_dims(new_dims, order)) new_dims = [[1], new_dims] elif Q.issuper: flat_order = np.array(sum(order, []), dtype=np.int32) q_dims = np.array(sum(Q.dims[0], []), dtype=np.int32) cy_index_permute(Qcoo.row, q_dims, flat_order) cy_index_permute(Qcoo.col, q_dims, flat_order) new_dims = [q_dims[i] for i in flat_order] new_dims = list(_chunk_dims(new_dims, order)) new_dims = [new_dims, new_dims] else: raise TypeError('Invalid quantum object for permutation.') return arr_coo2fast(Qcoo.data, Qcoo.row, Qcoo.col, Qcoo.shape[0], Qcoo.shape[1]), new_dims
def _permute(Q, order): if Q.isket: dims, perm = _perm_inds(Q.dims[0], order) nzs = Q.data.nonzero()[0] wh = np.where(perm == nzs)[0] data = np.ones(len(wh), dtype=complex) cols = perm[wh].T[0] perm_matrix = arr_coo2fast(data, wh.astype(np.int32), cols.astype(np.int32), Q.shape[0], Q.shape[0]) return perm_matrix * Q.data, Q.dims elif Q.isbra: dims, perm = _perm_inds(Q.dims[1], order) nzs = Q.data.nonzero()[1] wh = np.where(perm == nzs)[0] data = np.ones(len(wh), dtype=complex) rows = perm[wh].T[0] perm_matrix = arr_coo2fast(data, rows.astype(np.int32), wh.astype(np.int32), Q.shape[1], Q.shape[1]) return Q.data * perm_matrix, Q.dims elif Q.isoper: dims, perm = _perm_inds(Q.dims[0], order) data = np.ones(Q.shape[0], dtype=complex) rows = np.arange(Q.shape[0], dtype=np.int32) perm_matrix = arr_coo2fast(data, rows, (perm.T[0]).astype(np.int32), Q.shape[1], Q.shape[1]) dims_part = list(dims[order]) dims = [dims_part, dims_part] return (perm_matrix * Q.data) * perm_matrix.T, dims elif Q.issuper or Q.isoperket: # For superoperators, we expect order to be something like # [[0, 2], [1, 3]], which tells us to permute according to # [0, 2, 1 ,3], and then group indices according to the length # of each sublist. # As another example, # permuting [[[1, 2, 3], [1, 2, 3]], [[1, 2, 3], [1, 2, 3]]] by # [[0, 3], [1, 4], [2, 5]] should give # [[[1, 1], [2, 2], [3, 3]], [[1, 1], [2, 2], [3, 3]]]. # # Get the breakout of the left index into dims. # Since this is a super, the left index itself breaks into left # and right indices, each of which breaks down further. # The best way to deal with that here is to flatten dims. flat_order = sum(order, []) q_dims_left = sum(Q.dims[0], []) dims, perm = _perm_inds(q_dims_left, flat_order) dims = dims.flatten() data = np.ones(Q.shape[0], dtype=complex) rows = np.arange(Q.shape[0], dtype=np.int32) perm_matrix = arr_coo2fast(data, rows, (perm.T[0]).astype(np.int32), Q.shape[0], Q.shape[0]) dims_part = list(dims[flat_order]) # Finally, we need to restructure the now-decomposed left index # into left and right subindices, so that the overall dims we return # are of the form specified by order. dims_part = list(_chunk_dims(dims_part, order)) perm_left = (perm_matrix * Q.data) if Q.type == 'operator-ket': return perm_left, [dims_part, [1]] elif Q.type == 'super': return perm_left * perm_matrix.T, [dims_part, dims_part] else: raise TypeError('Invalid quantum object for permutation.')