def test_csr_kron(): "spmath: zcsr_kron" num_test = 5 for _ in range(num_test): ra = np.random.randint(2, 100) rb = np.random.randint(2, 100) A = rand_herm(ra, 0.5).data B = rand_herm(rb, 0.5).data As = A.tocsr(1) Bs = B.tocsr(1) C = sp.kron(As, Bs, format='csr') D = zcsr_kron(A, B) assert_almost_equal(C.data, D.data) assert_equal(C.indices, D.indices) assert_equal(C.indptr, D.indptr) for _ in range(num_test): ra = np.random.randint(2, 100) rb = np.random.randint(2, 100) A = rand_ket(ra, 0.5).data B = rand_herm(rb, 0.5).data As = A.tocsr(1) Bs = B.tocsr(1) C = sp.kron(As, Bs, format='csr') D = zcsr_kron(A, B) assert_almost_equal(C.data, D.data) assert_equal(C.indices, D.indices) assert_equal(C.indptr, D.indptr) for _ in range(num_test): ra = np.random.randint(2, 100) rb = np.random.randint(2, 100) A = rand_dm(ra, 0.5).data B = rand_herm(rb, 0.5).data As = A.tocsr(1) Bs = B.tocsr(1) C = sp.kron(As, Bs, format='csr') D = zcsr_kron(A, B) assert_almost_equal(C.data, D.data) assert_equal(C.indices, D.indices) assert_equal(C.indptr, D.indptr) for _ in range(num_test): ra = np.random.randint(2, 100) rb = np.random.randint(2, 100) A = rand_ket(ra, 0.5).data B = rand_ket(rb, 0.5).data As = A.tocsr(1) Bs = B.tocsr(1) C = sp.kron(As, Bs, format='csr') D = zcsr_kron(A, B) assert_almost_equal(C.data, D.data) assert_equal(C.indices, D.indices) assert_equal(C.indptr, D.indptr)
def test_csr_kron(): "spmath: zcsr_kron" for kk in range(10): ra = np.random.randint(2,100) rb = np.random.randint(2,100) A = rand_herm(ra,0.5).data B = rand_herm(rb,0.5).data As = A.tocsr(1) Bs = B.tocsr(1) C = sp.kron(As,Bs, format='csr') D = zcsr_kron(A, B) assert_almost_equal(C.data, D.data) assert_equal(C.indices, D.indices) assert_equal(C.indptr, D.indptr) for kk in range(10): ra = np.random.randint(2,100) rb = np.random.randint(2,100) A = rand_ket(ra,0.5).data B = rand_herm(rb,0.5).data As = A.tocsr(1) Bs = B.tocsr(1) C = sp.kron(As,Bs, format='csr') D = zcsr_kron(A, B) assert_almost_equal(C.data, D.data) assert_equal(C.indices, D.indices) assert_equal(C.indptr, D.indptr) for kk in range(10): ra = np.random.randint(2,100) rb = np.random.randint(2,100) A = rand_dm(ra,0.5).data B = rand_herm(rb,0.5).data As = A.tocsr(1) Bs = B.tocsr(1) C = sp.kron(As,Bs, format='csr') D = zcsr_kron(A, B) assert_almost_equal(C.data, D.data) assert_equal(C.indices, D.indices) assert_equal(C.indptr, D.indptr) for kk in range(10): ra = np.random.randint(2,100) rb = np.random.randint(2,100) A = rand_ket(ra,0.5).data B = rand_ket(rb,0.5).data As = A.tocsr(1) Bs = B.tocsr(1) C = sp.kron(As,Bs, format='csr') D = zcsr_kron(A, B) assert_almost_equal(C.data, D.data) assert_equal(C.indices, D.indices) assert_equal(C.indptr, D.indptr)
def sprepost(A, B): """Superoperator formed from pre-multiplication by operator A and post- multiplication of operator B. Parameters ---------- A : Qobj or QobjEvo Quantum operator for pre-multiplication. B : Qobj or QobjEvo Quantum operator for post-multiplication. Returns -------- super : Qobj or QobjEvo Superoperator formed from input quantum objects. """ if isinstance(A, QobjEvo) or isinstance(B, QobjEvo): return spre(A) * spost(B) else: dims = [[ _drop_projected_dims(A.dims[0]), _drop_projected_dims(B.dims[1]) ], [_drop_projected_dims(A.dims[1]), _drop_projected_dims(B.dims[0])]] data = zcsr_kron(B.data.T, A.data) return Qobj(data, dims=dims, superrep='super')
def spre(A): """Superoperator formed from pre-multiplication by operator A. Parameters ---------- A : Qobj or QobjEvo Quantum operator for pre-multiplication. Returns -------- super :Qobj or QobjEvo Superoperator formed from input quantum object. """ if isinstance(A, QobjEvo): return A.apply(spre) if not isinstance(A, Qobj): raise TypeError('Input is not a quantum object') if not A.isoper: raise TypeError('Input is not a quantum operator') S = Qobj(isherm=A.isherm, superrep='super') S.dims = [[A.dims[0], A.dims[1]], [A.dims[0], A.dims[1]]] S.data = zcsr_kron(fast_identity(np.prod(A.shape[1])), A.data) return S
def sprepost(A, B): """Superoperator formed from pre-multiplication by operator A and post- multiplication of operator B. Parameters ---------- A : Qobj Quantum operator for pre-multiplication. B : Qobj Quantum operator for post-multiplication. Returns -------- super : Qobj Superoperator formed from input quantum objects. """ dims = [[_drop_projected_dims(A.dims[0]), _drop_projected_dims(B.dims[1])], [_drop_projected_dims(A.dims[1]), _drop_projected_dims(B.dims[0])]] data = zcsr_kron(B.data.T, A.data) return Qobj(data, dims=dims, superrep='super')
def spre(A): """Superoperator formed from pre-multiplication by operator A. Parameters ---------- A : qobj Quantum operator for pre-multiplication. Returns -------- super :qobj Superoperator formed from input quantum object. """ if not isinstance(A, Qobj): raise TypeError('Input is not a quantum object') if not A.isoper: raise TypeError('Input is not a quantum operator') S = Qobj(isherm=A.isherm, superrep='super') S.dims = [[A.dims[0], A.dims[1]], [A.dims[0], A.dims[1]]] S.data = zcsr_kron(fast_identity(np.prod(A.shape[1])), A.data) return S
def spost(A): """Superoperator formed from post-multiplication by operator A Parameters ---------- A : qobj Quantum operator for post multiplication. Returns ------- super : qobj Superoperator formed from input qauntum object. """ if not isinstance(A, Qobj): raise TypeError('Input is not a quantum object') if not A.isoper: raise TypeError('Input is not a quantum operator') S = Qobj(isherm=A.isherm, superrep='super') S.dims = [[A.dims[0], A.dims[1]], [A.dims[0], A.dims[1]]] S.data = zcsr_kron(A.data.T, fast_identity(np.prod(A.shape[0]))) return S
def spost(A): """Superoperator formed from post-multiplication by operator A Parameters ---------- A : qobj Quantum operator for post multiplication. Returns ------- super : qobj Superoperator formed from input qauntum object. """ if not isinstance(A, Qobj): raise TypeError('Input is not a quantum object') if not A.isoper: raise TypeError('Input is not a quantum operator') S = Qobj(isherm=A.isherm, superrep='super') S.dims = [[A.dims[0], A.dims[1]], [A.dims[0], A.dims[1]]] S.data = zcsr_kron(A.data.T.tocsr(), sp.identity(np.prod(A.shape[0]),dtype=complex, format='csr')) return S
def _pseudo_inverse_sparse(L, rhoss, w=None, **pseudo_args): """ Internal function for computing the pseudo inverse of an Liouvillian using sparse matrix methods. See pseudo_inverse for details. """ N = np.prod(L.dims[0][0]) rhoss_vec = operator_to_vector(rhoss) tr_op = tensor([identity(n) for n in L.dims[0][0]]) tr_op_vec = operator_to_vector(tr_op) P = zcsr_kron(rhoss_vec.data, tr_op_vec.data.T) I = sp.eye(N*N, N*N, format='csr') Q = I - P if w is None: L = 1.0j*(1e-15)*spre(tr_op) + L else: if w != 0.0: L = 1.0j*w*spre(tr_op) + L else: L = 1.0j*(1e-15)*spre(tr_op) + L if pseudo_args['use_rcm']: perm = reverse_cuthill_mckee(L.data) A = sp_permute(L.data, perm, perm) Q = sp_permute(Q, perm, perm) else: if ss_args['solver'] == 'scipy': A = L.data.tocsc() A.sort_indices() if pseudo_args['method'] == 'splu': if settings.has_mkl: A = L.data.tocsr() A.sort_indices() LIQ = mkl_spsolve(A, Q.toarray()) else: pspec = pseudo_args['permc_spec'] diag_p_thresh = pseudo_args['diag_pivot_thresh'] pseudo_args = pseudo_args['ILU_MILU'] lu = sp.linalg.splu(A, permc_spec=pspec, diag_pivot_thresh=diag_p_thresh, options=dict(ILU_MILU=pseudo_args)) LIQ = lu.solve(Q.toarray()) elif pseudo_args['method'] == 'spilu': lu = sp.linalg.spilu(A, permc_spec=pseudo_args['permc_spec'], fill_factor=pseudo_args['fill_factor'], drop_tol=pseudo_args['drop_tol']) LIQ = lu.solve(Q.toarray()) else: raise ValueError("unsupported method '%s'" % method) R = sp.csr_matrix(Q * LIQ) if pseudo_args['use_rcm']: rev_perm = np.argsort(perm) R = sp_permute(R, rev_perm, rev_perm, 'csr') return Qobj(R, dims=L.dims)
def liouvillian(H, c_ops=[], data_only=False, chi=None): """Assembles the Liouvillian superoperator from a Hamiltonian and a ``list`` of collapse operators. Like liouvillian, but with an experimental implementation which avoids creating extra Qobj instances, which can be advantageous for large systems. Parameters ---------- H : qobj System Hamiltonian. c_ops : array_like A ``list`` or ``array`` of collapse operators. Returns ------- L : qobj Liouvillian superoperator. """ if chi and len(chi) != len(c_ops): raise ValueError('chi must be a list with same length as c_ops') if H is not None: if H.isoper: op_dims = H.dims op_shape = H.shape elif H.issuper: op_dims = H.dims[0] op_shape = [np.prod(op_dims[0]), np.prod(op_dims[0])] else: raise TypeError("Invalid type for Hamiltonian.") else: # no hamiltonian given, pick system size from a collapse operator if isinstance(c_ops, list) and len(c_ops) > 0: c = c_ops[0] if c.isoper: op_dims = c.dims op_shape = c.shape elif c.issuper: op_dims = c.dims[0] op_shape = [np.prod(op_dims[0]), np.prod(op_dims[0])] else: raise TypeError("Invalid type for collapse operator.") else: raise TypeError("Either H or c_ops must be given.") sop_dims = [[op_dims[0], op_dims[0]], [op_dims[1], op_dims[1]]] sop_shape = [np.prod(op_dims), np.prod(op_dims)] spI = fast_identity(op_shape[0]) if H: if H.isoper: Ht = H.data.T data = -1j * zcsr_kron(spI, H.data) data += 1j * zcsr_kron(Ht, spI) else: data = H.data else: data = fast_csr_matrix(shape=(sop_shape[0], sop_shape[1])) for idx, c_op in enumerate(c_ops): if c_op.issuper: data = data + c_op.data else: cd = c_op.data.H c = c_op.data if chi: data = data + np.exp(1j * chi[idx]) * \ zcsr_kron(c.conj(), c) else: data = data + zcsr_kron(c.conj(), c) cdc = cd * c cdct = cdc.T data = data - 0.5 * zcsr_kron(spI, cdc) data = data - 0.5 * zcsr_kron(cdct, spI) if data_only: return data else: L = Qobj() L.dims = sop_dims L.data = data L.isherm = False L.superrep = 'super' return L
def tensor(*args): """Calculates the tensor product of input operators. Parameters ---------- args : array_like ``list`` or ``array`` of quantum objects for tensor product. Returns ------- obj : qobj A composite quantum object. Examples -------- >>> tensor([sigmax(), sigmax()]) # doctest: +SKIP Quantum object: dims = [[2, 2], [2, 2]], \ shape = [4, 4], type = oper, isHerm = True Qobj data = [[ 0.+0.j 0.+0.j 0.+0.j 1.+0.j] [ 0.+0.j 0.+0.j 1.+0.j 0.+0.j] [ 0.+0.j 1.+0.j 0.+0.j 0.+0.j] [ 1.+0.j 0.+0.j 0.+0.j 0.+0.j]] """ if not args: raise TypeError("Requires at least one input argument") if len(args) == 1 and isinstance(args[0], (list, np.ndarray)): # this is the case when tensor is called on the form: # tensor([q1, q2, q3, ...]) qlist = args[0] elif len(args) == 1 and isinstance(args[0], Qobj): # tensor is called with a single Qobj as an argument, do nothing return args[0] else: # this is the case when tensor is called on the form: # tensor(q1, q2, q3, ...) qlist = args if not all([isinstance(q, Qobj) for q in qlist]): # raise error if one of the inputs is not a quantum object raise TypeError("One of inputs is not a quantum object") out = Qobj() if qlist[0].issuper: out.superrep = qlist[0].superrep if not all([q.superrep == out.superrep for q in qlist]): raise TypeError("In tensor products of superroperators, all must" + "have the same representation") out.isherm = True for n, q in enumerate(qlist): if n == 0: out.data = q.data out.dims = q.dims else: out.data = zcsr_kron(out.data, q.data) out.dims = [out.dims[0] + q.dims[0], out.dims[1] + q.dims[1]] out.isherm = out.isherm and q.isherm if not out.isherm: out._isherm = None return out.tidyup() if qutip.settings.auto_tidyup else out
def liouvillian(H, c_ops=[], data_only=False, chi=None): """Assembles the Liouvillian superoperator from a Hamiltonian and a ``list`` of collapse operators. Like liouvillian, but with an experimental implementation which avoids creating extra Qobj instances, which can be advantageous for large systems. Parameters ---------- H : Qobj or QobjEvo System Hamiltonian. c_ops : array_like of Qobj or QobjEvo A ``list`` or ``array`` of collapse operators. Returns ------- L : Qobj or QobjEvo Liouvillian superoperator. """ if isinstance(c_ops, (Qobj, QobjEvo)): c_ops = [c_ops] if chi and len(chi) != len(c_ops): raise ValueError('chi must be a list with same length as c_ops') h = None if H is not None: if isinstance(H, QobjEvo): h = H.cte else: h = H if h.isoper: op_dims = h.dims op_shape = h.shape elif h.issuper: op_dims = h.dims[0] op_shape = [np.prod(op_dims[0]), np.prod(op_dims[0])] else: raise TypeError("Invalid type for Hamiltonian.") else: # no hamiltonian given, pick system size from a collapse operator if isinstance(c_ops, list) and len(c_ops) > 0: if isinstance(c_ops[0], QobjEvo): c = c_ops[0].cte else: c = c_ops[0] if c.isoper: op_dims = c.dims op_shape = c.shape elif c.issuper: op_dims = c.dims[0] op_shape = [np.prod(op_dims[0]), np.prod(op_dims[0])] else: raise TypeError("Invalid type for collapse operator.") else: raise TypeError("Either H or c_ops must be given.") sop_dims = [[op_dims[0], op_dims[0]], [op_dims[1], op_dims[1]]] sop_shape = [np.prod(op_dims), np.prod(op_dims)] spI = fast_identity(op_shape[0]) td = False L = None if isinstance(H, QobjEvo): td = True def H2L(H): if H.isoper: return -1.0j * (spre(H) - spost(H)) else: return H L = H.apply(H2L) data = L.cte.data elif isinstance(H, Qobj): if H.isoper: Ht = H.data.T data = -1j * zcsr_kron(spI, H.data) data += 1j * zcsr_kron(Ht, spI) else: data = H.data else: data = fast_csr_matrix(shape=(sop_shape[0], sop_shape[1])) td_c_ops = [] for idx, c_op in enumerate(c_ops): if isinstance(c_op, QobjEvo): td = True if c_op.const: c_ = c_op.cte elif chi: td_c_ops.append(lindblad_dissipator(c_op, chi=chi[idx])) continue else: td_c_ops.append(lindblad_dissipator(c_op)) continue else: c_ = c_op if c_.issuper: data = data + c_.data else: cd = c_.data.H c = c_.data if chi: data = data + np.exp(1j * chi[idx]) * \ zcsr_kron(c.conj(), c) else: data = data + zcsr_kron(c.conj(), c) cdc = cd * c cdct = cdc.T data = data - 0.5 * zcsr_kron(spI, cdc) data = data - 0.5 * zcsr_kron(cdct, spI) if not td: if data_only: return data else: L = Qobj() L.dims = sop_dims L.data = data L.superrep = 'super' return L else: if not L: l = Qobj() l.dims = sop_dims l.data = data l.superrep = 'super' L = QobjEvo(l) else: L.cte.data = data for c_op in td_c_ops: L += c_op return L
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 N_temp = 1 for i in H_sys.dims[0]: N_temp *= i sup_dim = N_temp**2 unit_sys = qeye(N_temp) # 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 = fast_identity(N_he) 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 = zcsr_kron(unit_helems, L_bnd) else: L_helems = fast_csr_matrix(shape=(N_he*sup_dim, N_he*sup_dim)) # 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 = cy_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 = cy_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 = cy_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 = zcsr_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
def _pseudo_inverse_sparse(L, rhoss, w=None, **pseudo_args): """ Internal function for computing the pseudo inverse of an Liouvillian using sparse matrix methods. See pseudo_inverse for details. """ N = np.prod(L.dims[0][0]) rhoss_vec = operator_to_vector(rhoss) tr_op = tensor([identity(n) for n in L.dims[0][0]]) tr_op_vec = operator_to_vector(tr_op) P = zcsr_kron(rhoss_vec.data, tr_op_vec.data.T) I = sp.eye(N * N, N * N, format='csr') Q = I - P if w is None: L = 1.0j * (1e-15) * spre(tr_op) + L else: if w != 0.0: L = 1.0j * w * spre(tr_op) + L else: L = 1.0j * (1e-15) * spre(tr_op) + L if pseudo_args['use_rcm']: perm = sp.csgraph.reverse_cuthill_mckee(L.data) A = sp_permute(L.data, perm, perm) Q = sp_permute(Q, perm, perm) else: if ss_args['solver'] == 'scipy': A = L.data.tocsc() A.sort_indices() if pseudo_args['method'] == 'splu': if settings.has_mkl: A = L.data.tocsr() A.sort_indices() LIQ = mkl_spsolve(A, Q.toarray()) else: pspec = pseudo_args['permc_spec'] diag_p_thresh = pseudo_args['diag_pivot_thresh'] pseudo_args = pseudo_args['ILU_MILU'] lu = sp.linalg.splu(A, permc_spec=pspec, diag_pivot_thresh=diag_p_thresh, options=dict(ILU_MILU=pseudo_args)) LIQ = lu.solve(Q.toarray()) elif pseudo_args['method'] == 'spilu': lu = sp.linalg.spilu(A, permc_spec=pseudo_args['permc_spec'], fill_factor=pseudo_args['fill_factor'], drop_tol=pseudo_args['drop_tol']) LIQ = lu.solve(Q.toarray()) else: raise ValueError("unsupported method '%s'" % method) R = sp.csr_matrix(Q * LIQ) if pseudo_args['use_rcm']: rev_perm = np.argsort(perm) R = sp_permute(R, rev_perm, rev_perm, 'csr') return Qobj(R, dims=L.dims)
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 = fast_identity(N_he) 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 = zcsr_kron(unit_helems, L_bnd) else: L_helems = fast_csr_matrix(shape=(N_he * sup_dim, N_he * sup_dim)) # 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 = cy_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 = cy_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 = cy_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 = zcsr_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
def tensor(*args): """Calculates the tensor product of input operators. Parameters ---------- args : array_like ``list`` or ``array`` of quantum objects for tensor product. Returns ------- obj : qobj A composite quantum object. Examples -------- >>> tensor([sigmax(), sigmax()]) Quantum object: dims = [[2, 2], [2, 2]], \ shape = [4, 4], type = oper, isHerm = True Qobj data = [[ 0.+0.j 0.+0.j 0.+0.j 1.+0.j] [ 0.+0.j 0.+0.j 1.+0.j 0.+0.j] [ 0.+0.j 1.+0.j 0.+0.j 0.+0.j] [ 1.+0.j 0.+0.j 0.+0.j 0.+0.j]] """ if not args: raise TypeError("Requires at least one input argument") if len(args) == 1 and isinstance(args[0], (list, np.ndarray)): # this is the case when tensor is called on the form: # tensor([q1, q2, q3, ...]) qlist = args[0] elif len(args) == 1 and isinstance(args[0], Qobj): # tensor is called with a single Qobj as an argument, do nothing return args[0] else: # this is the case when tensor is called on the form: # tensor(q1, q2, q3, ...) qlist = args if not all([isinstance(q, Qobj) for q in qlist]): # raise error if one of the inputs is not a quantum object raise TypeError("One of inputs is not a quantum object") out = Qobj() if qlist[0].issuper: out.superrep = qlist[0].superrep if not all([q.superrep == out.superrep for q in qlist]): raise TypeError("In tensor products of superroperators, all must" + "have the same representation") out.isherm = True for n, q in enumerate(qlist): if n == 0: out.data = q.data out.dims = q.dims else: out.data = zcsr_kron(out.data, q.data) out.dims = [out.dims[0] + q.dims[0], out.dims[1] + q.dims[1]] out.isherm = out.isherm and q.isherm if not out.isherm: out._isherm = None return out.tidyup() if qutip.settings.auto_tidyup else out