def _sp_op(self, i, mat, h_list, mod_term, err=1.e-6): if not h_list: return np.zeros((mat.shape)) logging.debug(__('> OP on mat {}...', i)) n, m = mat.shape partial_transform = self._partial_transform a = self.get_sub_vec(-1) a_h = np.conj(a) density = self._partial_product(i, a, a_h) inv_density = linalg.inv(density + np.identity(m) * err) sp = self.get_sub_vec(i) sp_h = np.conj(np.transpose(sp)) projection = np.identity(n) - np.dot(sp, sp_h) tmp = partial_transform(i, a, mat) for mat_j in h_list: tmp = partial_transform(i, tmp, mat_j) for j, mat_j in mod_term: if j != i: tmp = partial_transform(j, tmp, mat_j) tmp = self._partial_product(i, tmp, a_h) ans = np.dot(projection, np.dot(tmp, inv_density)) return ans
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 fbr2dvr_mat(self, mat): r"""Transform a matrix from FBR to DVR. Parameters ---------- mat : (n, n) ndarray Returns ------- (n, n) ndarray """ return np.dot(np.transpose(self._u_mat), np.dot(mat, self._u_mat))
def dvr2fbr_mat(self, mat): r"""Transform a matrix from DVR to FBR. Parameters ---------- mat : (n, n) ndarray Returns ------- (n, n) ndarray """ return np.dot(self._u_mat, np.dot(mat, np.transpose(self._u_mat)))
def partial_trace(array1, i, array2, j): r"""Partial trace of 2 tensors, return a matrix. +----+ | | -i- 1 -- 2 -j- Parameters ---------- array1 : ndarray i : {int, None} if i is None then j must be None. array2 : ndarray j : {int, None} if j is None then i must be None. Returns ------- ans : ndarray Of shape `(n, m)` """ if i is not None and j is not None: shape_1, shape_2 = map(list, (array1.shape, array2.shape)) n, m = shape_1[i], shape_2[j] array1 = np.moveaxis(array1, i, 0) array1 = np.reshape(array1, (n, -1)) array2 = np.moveaxis(array2, j, -1) array2 = np.reshape(array2, (-1, m)) elif i is None and j is None: array1 = np.reshape(array1, -1) array2 = np.reshape(array2, -1) else: raise TypeError('Invalid parameters i={} and j={}!'.format(i, j)) ans = np.dot(array1, array2) return ans
def _extend_space(self): head = len(self._search_space) self._search_space += self._trial_vecs self._column_space += list(map(self._matvec, self._trial_vecs)) tail = len(self._search_space) v, a_v = self._search_space, self._column_space for i in range(head): for j in range(head, tail): self._submatrix[i, j] = np.dot(np.conj(v[i]), a_v[j]) self._submatrix[j, i] = np.conj(self._submatrix[i, j]) for i in range(head, tail): for j in range(head, i): self._submatrix[i, j] = np.dot(np.conj(v[i]), a_v[j]) self._submatrix[j, i] = np.conj(self._submatrix[i, j]) self._submatrix[i, i] = np.dot(np.conj(v[i]), a_v[i]) return self._submatrix[:tail, :tail]
def _partial_product(i, a, b): r""" Parameters ---------- i : {int, None} a : (..., n, ...) ndarray b : (..., m, ...) ndarray Returns ------- mat : (n, m) ndarray Or return a float if i is None. """ if i is None: a = np.reshape(a, -1) b = np.reshape(b, -1) else: n = a.shape[i] m = b.shape[i] a = np.moveaxis(a, i, 0) a = np.reshape(a, (n, -1)) b = np.moveaxis(b, i, -1) b = np.reshape(b, (-1, m)) mat = np.dot(a, b) return mat
def _calc_trial_vecs(self): if self._precondition is None: dim = len(self._search_space[0]) self._precondition = self.davidson_precondition( dim, self._matvec ) # ritz_vecs = [None] * dim # else: # ritz_vecs = list(self._get_ritz_vecs()) self._trial_vecs = [] precondition = self._precondition zipped = zip( self._residuals, self._residual_norms, self._get_ritz_vecs(), self._convergence ) for residual, norm_, ritz_vec, conv in zipped: # remove linear dependency in self._residuals if norm_ ** 2 > self.lin_dep_lim and not conv: vec = precondition( residual, self._ritz_vals[0], ritz_vec ) vec *= 1. / norm(vec) for base in self._search_space: vec -= np.dot(np.conj(base), vec) * base norm_ = norm(vec) # remove linear dependency between trial_vecs and # self._search_space if norm_ ** 2 > self.lin_dep_lim: vec *= 1. / norm_ self._trial_vecs.append(vec) return self._trial_vecs
def __partial_product(array1, i, array2, j): r"""Times a matrix to a tensor. | -- 1 -i--j- 2 -- | Parameters ---------- array1 : ndarray i : int array2 : 2-d ndarray j : int Returns ------- ans : ndarray """ shape_1, shape_2 = map(list, (array1.shape, array2.shape)) n, m = shape_1.pop(i), shape_2.pop(j) new_shape = shape_1[:i] + shape_2 + shape_1[i:] array1 = np.moveaxis(array1, i, -1) array1 = np.reshape(array1, (-1, n)) array2 = np.moveaxis(array2, j, 0) array2 = np.reshape(array2, (m, -1)) ans = np.dot(array1, array2) ans = np.reshape(ans, shape_1 + [-1]) ans = np.moveaxis(ans, -1, i) ans = np.reshape(ans, new_shape) return ans
def normalize(self, forced=False): """Normalize the array of self. Only work when self.normalized. Set `forced` to `True` to normalize any way. """ array = self.array if array is None or (not self.normalized and not forced): return axis = self.axis if axis is None: norm = np.array(self.local_norm()) self.set_array(array / norm) ans = norm else: norm = linalg.norm shape = self.shape dim = shape.pop(axis) array = np.reshape(np.moveaxis(array, axis, 0), (dim, -1)) vecs = [] norm_list = [] for vec_i in array: for vec_j in vecs: vec_i -= vec_j * np.dot(np.conj(vec_j), vec_i) norm_ = norm(vec_i) vecs.append(vec_i / norm_) norm_list.append(norm_) array = np.array(vecs) array = np.moveaxis(np.reshape(array, [-1] + shape), 0, axis) self.set_array(array) ans = norm_list return ans
def energy_expection(self, vec=None): if vec is None: vec = self.vec a_tensor = self.get_sub_vec(-1, vec) mod_terms = self.update_mod_terms(vec=vec, write=False) ans = 0. for r, term in enumerate(mod_terms): h_a = self._coeff_op(a_tensor, term) h_a = np.reshape(h_a, -1) a_h = np.conj(np.reshape(a_tensor, -1)) ans += np.dot(a_h, h_a) return ans
def dvr2fbr_vec(self, vec): r"""Transform a vector from DVR to FBR. Parameters ---------- mat : (n,) ndarray Returns ------- (n,) ndarray """ return np.dot(self._u_mat, vec)
def fbr2dvr_vec(self, vec): r"""Transform a vector from FBR to DVR. Parameters ---------- mat : (n,) ndarray Returns ------- (n,) ndarray """ vec = np.reshape(vec, -1) return np.dot(np.transpose(self._u_mat), vec)
def expection(op, vec): r"""Calculate the energy expection. Parameters ---------- op : (n, n) ndarray or LinearOpearator vec : (n,) ndarray Returns ------- expection : float """ vec_h = np.conjugate(vec) e = np.dot(vec_h, op.dot(vec)) return e
def _orthonormalize(self, use_svd=True): if use_svd and self._trial_vecs: trial_mat = np.transpose(np.array(self._trial_vecs)) trial_mat = orth(trial_mat) self._trial_vecs = list(np.transpose(trial_mat)) elif self._trial_vecs: vecs = [] for vec_i in self._trial_vecs: for vec_j in vecs: vec_i -= vec_j * np.dot(vec_j.conj(), vec_i) norm_ = norm(vec_i) if norm_ > 1.e-7: vecs.append(vec_i / norm_) self._trial_vecs = vecs return self._trial_vecs
def projector(self, comp=False): """[Deprecated] Return the projector corresponding to self. Returns ------- ans : ndarray """ axis = self.axis if axis is not None: array = self.array shape = self.shape dim = shape.pop(self.axis) comp_dim = np.prod(shape) array = np.moveaxis(array, axis, -1) array = np.reshape(array, (-1, dim)) array_h = np.conj(np.transpose(array)) ans = np.dot(array, array_h) if comp: identity = np.identity(comp_dim) ans = identity - ans ans = np.reshape(ans, shape * 2) return ans else: raise RuntimeError('Need to specific the normalization axis!')
def _eval(op, vec): vec_h = np.conj(np.transpose(vec)) mod_op = np.dot(vec_h, np.dot(op, vec)) return mod_op
def matvec(vec, mat=h): vec = np.reshape(vec, h.shape) ans = np.dot(mat, vec) return np.reshape(ans, -1)
def split(self, axis, indice=None, root=None, child=None, rank=None, err=None, normalized=False): """Split the root Tensor to a certain axis/certain axes. Parameters ---------- axis : {int, [int]} rank : int Max rank in SVD err : float Max error in SVD indice : (int, int) Linkage between root and child root : Tensor Tensor to be a new root node. `None` to create a new Tensor. child : Tensor Tensor to be a new child node. `None` to create a new Tensor. Returns ------- root : Tensor New root node in the same environment of self. child : Tensor New child node in the same environment of self. Notes ----- When split a Tensor, this method should let root.unite(i) (i.e. unite with child) be a inversion in terms of the tensor network. """ if self.axis is not None: raise RuntimeError('Can only split the root Tensor!') try: axes1 = list(sorted(axis)) except TypeError: axes1 = [axis] default_indice = (0, axis) else: default_indice = (0, 0) axes2 = [i for i in range(self.order) if i not in axes1] index1, index2 = indice if indice is not None else default_indice # save all data needed in `self` a = self.array children = list(self.children(axis=None)) name = self.name shape = self.shape shape1, shape2 = [shape[i] for i in axes1], [shape[i] for i in axes2] # name settings only for clarity.. if '+' in name: name1, name2 = name.split('+') else: name1, name2 = name + '\'', name # Calculate arrays for new tensors for n, i in enumerate(axes1): a = np.moveaxis(a, i, n) a = np.reshape(a, (np.prod([1] + shape1), np.prod([1] + shape2))) u, s, vh = compressed_svd(a, rank=rank, err=err) root_array = np.reshape(np.dot(u, s), shape1 + [-1]) root_array = np.moveaxis(root_array, -1, index1) child_array = np.reshape(vh, [-1] + shape2) child_array = np.moveaxis(child_array, 0, index2) # Create/write new tensors. cls = type(self) if root is None: root = cls(name=name1, array=root_array, axis=None, normalized=normalized) else: root.axis = None root.set_array(root_array) if child is None: child = cls(name=name2, array=child_array, axis=index2, normalized=normalized) else: child.axis = index2 child.set_array(child_array) # Fix linkage info axes1.insert(index1, None) axes2.insert(index2, None) unlink = self.unlink link = self.link link_info = [(root, index1, child, index2)] for i, t, j in children: is_1 = i in axes1 axes = axes1 if is_1 else axes2 tensor = root if is_1 else child unlink(self, i, t, j) link_info.append((tensor, axes.index(i), t, j)) for linkage in link_info: link(*linkage) return root, child