def propagator(self, tau=0.1, method='Trotter'): r"""Construct the propagator Parameters ---------- tau : float Time interval at each step. Returns ------- p1 : (n, n) ndarray :math:`e^{-iV\tau/2}` p2 : (n, n) ndarray :math:`e^{-iV\tau}` p3 : (n, n) ndarray :math:`e^{-iT\tau}` """ hbar = self.hbar if 'Trotter' in method: diag, v = scipy.linalg.eigh(self.t_mat()) p3 = np.exp(-1.0j * hbar * tau * diag) p3 = np.dot(v, np.dot(np.diag(p3), np.transpose(v))) p2 = np.exp(-1.0j * hbar * tau * np.diag(self.v_mat())) p2 = np.diag(p2) p1 = np.exp(-1.0j * hbar * tau * np.diag(0.5 * self.v_mat())) p1 = np.diag(p1) return p1, p2, p3
def gen_h_terms(self, extra=None, kinetic_only=False): r"""Use a simple seperated Hamiltonian operator:: h_0 ... h_p-1 \ | / \|/ + r Notice that r is a index rather than a tensor Parameters ---------- extra : [[(int, float -> float)]] kinetic_only : bool Returns ------- h_terms : [[(int, (n_i, n_i) ndarray)]] A list of Hamiltonian matrix. (with ``length == rank``) """ dvr_list = self.dvr_list if kinetic_only: h_terms = [[(i, dvr.t_mat())] for i, dvr in enumerate(self.dvr_list)] else: h_terms = [[(i, dvr.h_mat())] for i, dvr in enumerate(self.dvr_list)] if extra is not None: for term in extra: t_i = [(i, np.diag(func(np.asarray(self.grid_points_list[i])))) for i, func in term] h_terms.append(t_i) self.h_terms = h_terms return h_terms
def fine_grain_mps(C, dims, direction, _trunc=False): """Fine-graining of one-site MPS into three site by SVD.:: |st |s |t -i- C -k- = -i- A -m- B -k- Parameters ---------- C : (st, i, k) ndarray A MPS matrix. dims : (int, int) [s, t]. direction : {'>', '<'} '>': move to right; '<' move to left _trunc : int, optional Set m in compress_svd to _trunc. Not compressed if not _trunc. Returns ------- A : (s, i, m) ndarray A MPS matrix. B : (t, m, k) ndarray A MPS matrix. Notes ----- If direction == '>', A is (left-)canonical; if direction == '<', B is (right-)canonical. """ sh = dims + [C.shape[1], C.shape[2]] # [s, t, i, k] mat = np.reshape(C, sh) mat = np.transpose(mat, (0, 2, 1, 3)) # mat[s, i, t, k] mat = np.reshape(mat, (sh[0] * sh[2], sh[1] * sh[3])) # mat[si, tk] U, S, V = np.linalg.svd(mat, full_matrices=0) if _trunc: U, S, V, compress_error = compress_svd(U, S, V, _trunc) if direction == '>': A = U B = np.matmul(np.diag(S), V) # [m, tk] elif direction == '<': A = np.matmul(U, np.diag(S)) B = V A = np.reshape(A, (sh[0], sh[2], -1)) # [s, i, m] B = np.reshape(B, (-1, sh[1], sh[3])) # [m, t, k] B = np.transpose(B, (1, 0, 2)) # [t, m, k] return A, B
def number_operator(self): if self.dense: ans = np.diag(np.arange(self.dim)) else: def matvec(x): return self.raising(self.lowering(x)) ans = self.operator(matvec=matvec) return ans
def hamiltonian(self): coeff = self.hbar * self.omega if self.dense: ans = np.diag(coeff * (0.5 + np.arange(self.dim))) else: def matvec(x): return coeff * (self.raising(self.lowering(x)) + 0.5 * x) ans = self.operator(matvec=matvec) return ans
def v_mat(self): r"""Return the potential energy matrix with the given potential in DVR. Returns ------- (n, n) np.ndarray A 2-d diagonal matrix. """ v = self.v_func(self.grid_points) v_matrix = np.diag(v) return v_matrix
def t_mat(self): """Return the kinetic energy matrix in DVR. Returns ------- (n, n) np.ndarray A 2-d matrix. """ factor = -self.hbar**2 / (2 * self.m_e) j = np.arange(1, self.n + 1) t_matrix = np.diag(-(j * np.pi / self.length)**2) t_matrix = factor * self.fbr2dvr_mat(t_matrix) return t_matrix
def _move_right(mat, mat_next, _trunc): shape = np.shape(mat) # SVD on mat[si, j] mat = np.reshape(mat, (shape[0] * shape[1], shape[2])) U, S, V = np.linalg.svd(mat, full_matrices=0) # truncated to compress. U, S, V, compress_error = compress_svd(U, S, V, _trunc) mat = np.reshape(U, (shape[0], shape[1], -1)) # U[si, m] SV = np.matmul(np.diag(S), V) mat_next = np.einsum('mj,tjk->tmk', SV, mat_next) return mat, mat_next
def _diff_n(self): if self.corr.exp_coeff.ndim == 1: gamma = np.diag(self.corr.exp_coeff) ans = [] for i, j in product(range(self.k_max), repeat=2): g = gamma[i, j] if not np.allclose(g, 0.0): term = [(i, 0.5j * self.hbar * g * self.numberer(i))] if i != j: at = self.creator(i) a = self.annihilator(j) term.extend([(i, at), (j, a)]) ans.append(term) return ans
def _move_left(mat, mat_prev, _trunc): shape = np.shape(mat) mat = np.reshape( np.transpose(mat, (1, 0, 2)), # mat[i, s, j] (shape[1], shape[0] * shape[2])) # SVD on mat[i, sj] U, S, V = np.linalg.svd(mat, full_matrices=0) # truncated to compress. U, S, V, compress_error = compress_svd(U, S, V, _trunc) mat = np.reshape(V, (-1, shape[0], shape[2])) # V[m, sj] mat = np.transpose(mat, (1, 0, 2)) # mat[s, m, j] US = np.matmul(U, np.diag(S)) mat_prev = np.einsum('rhi,im->rhm', mat_prev, US) return mat, mat_prev
def _diff_n(self): if self.corr.exp_coeff.ndim == 1: gamma = np.diag(self.corr.exp_coeff) ans = [] for i, j in product(range(self.k_max), repeat=2): g = gamma[i, j] if not np.allclose(g, 0.0): term = [(i, -g * self._numberer(i))] if i != j: n_i = self._sqrt_numberer(i) n_j = self._sqrt_numberer(j) raiser = self._raiser(i) lower = self._lower(j) term.extend([(i, raiser @ n_i), (j, n_j @ lower)]) ans.append(term) return ans
def compressed_svd(a, rank=None, err=None, **kwargs): u, s, vh = svd(a, full_matrices=False, **kwargs) if err is not None: total_error = 0.0 for n, s_i in reversed(list(enumerate(s))): total_error += s_i if total_error > err: rank = n + 1 break if rank is None: rank = 1 if rank is not None and rank <= 0 : raise RuntimeError('The matrix must have positive rank!') if rank is not None and rank <= len(s): s = s[:rank] u = u[:, :rank] vh = vh[:rank,:] s = np.diag(s) return u, s, vh
def creator(self, k): """Acting on 0-th index""" dim = self.n_dims[k] raiser = np.eye(dim, k=1) sqrt_n = np.diag(np.sqrt(np.arange(dim))) return raiser @ sqrt_n
def _sqrt_numberer(self, k, start=0): return np.diag( np.sqrt(np.arange(start, start + self.n_dims[k], dtype=DTYPE)))
def _lower(self, k): """Acting on 0-th index""" dim = self.n_dims[k] sqrt_n = np.diag(np.sqrt(np.arange(dim, dtype=DTYPE))) return sqrt_n @ np.eye(dim, k=-1, dtype=DTYPE)
def autocomplete(self, n_bond_dict, max_entangled=False): """Autocomplete the tensors linked to `self.root` with suitable initial value. Parameters ---------- n_bond_dict : {Leaf: int} A dictionary to specify the dimensions of each primary basis. max_entangled : bool Whether to use the max entangled state as initial value (for finite temperature and imaginary-time propagation). Default is `False`. """ for t in self.root.visitor(leaf=False): if t.array is None: axis = t.axis if max_entangled and not any(t.children(leaf=False)): if len(list(t.children(leaf=True))) != 2 or axis is None: raise RuntimeError('Not correct tensor graph for FT.') for i, leaf, j in t.children(): if not leaf.name.endswith("'"): n_leaf = n_bond_dict[(t, i, leaf, j)] break p, p_i = t[axis] n_parent = n_bond_dict[(p, p_i, t, axis)] vec_i = np.diag(np.ones((n_leaf, )) / np.sqrt(n_leaf)) vec_i = np.reshape(vec_i, -1) init_vecs = [vec_i] print(np.shape(init_vecs), np.shape(self._local_matvec(leaf))) da = DavidsonAlgorithm(self._local_matvec(leaf), init_vecs=init_vecs, n_vals=n_parent) array = da.kernel(search_mode=True) if len(array) >= n_parent: array = array[:n_parent] else: for j in range(n_parent - len(array)): v = np.zeros((n_leaf**2, )) v[j] = 1.0 array.append(v) assert len(array) == n_parent assert np.allclose(array[0], vec_i) array = np.reshape(array, (n_parent, n_leaf, n_leaf)) else: n_children = [] for i, child, j in t.children(): n_children.append(n_bond_dict[(t, i, child, j)]) if axis is not None: p, p_i = t[axis] n_parent = n_bond_dict[(p, p_i, t, axis)] shape = [n_parent] + n_children else: n_parent = 1 shape = n_children array = np.zeros((n_parent, np.prod(n_children))) for n, v_i in zip(self.triangular(n_children), array): v_i[n] = 1. array = np.reshape(array, shape) if axis is not None: array = np.moveaxis(array, 0, axis) t.set_array(array) t.normalize(forced=True) assert (t.axis is None or np.linalg.matrix_rank(t.local_norm()) == t.shape[t.axis]) if __debug__: for t in self.root.visitor(): t.check_completness(strict=True) return
def _numberer(self, k): return np.diag(np.arange(self.n_dims[k], dtype=DTYPE))
def annihilator(self, k): """Acting on 0-th index""" dim = self.n_dims[k] lower = np.eye(dim, k=-1) sqrt_n = np.diag(np.sqrt(np.arange(dim))) return sqrt_n @ lower
def numberer(self, k): """Acting on 0-th index""" return np.diag(np.arange(self.n_dims[k]))
def annihilation_operator(self): if self.dense: ans = np.diag(np.sqrt(np.arange(1, self.dim)), 1) else: ans = self.operator(matvec=self.lowering, rmatvec=self.raising) return ans
def _numberer(self, k, start=0): return np.diag(np.arange(start, start + self.n_dims[k]))