def position(self, site: int, normalize: Optional[bool] = True) -> np.number: """ Shift FiniteMPS.center_position to `site`. Args: site: The site to which FiniteMPS.center_position should be shifted normalize: If `True`, normalize matrices when shifting. Returns: Tensor: The norm of the tensor at FiniteMPS.center_position """ #`site` has to be between 0 and len(mps) - 1 if site >= len(self.nodes) or site < 0: raise ValueError('site = {} not between values' ' 0 < site < N = {}'.format(site, len(self))) #nothing to do if site == self.center_position: return self.backend.norm(self.nodes[self.center_position].tensor) #shift center_position to the right using QR decomposition if site > self.center_position: n = self.center_position for n in range(self.center_position, site): Q, R = split_node_qr( self.nodes[n], left_edges=[self.nodes[n][0], self.nodes[n][1]], right_edges=[self.nodes[n][2]], left_name=self.nodes[n].name) self.nodes[n] = Q #Q is a left-isometric tensor of rank 3 self.nodes[n + 1] = contract(R[1], name=self.nodes[n + 1].name) Z = norm(self.nodes[n + 1]) # for an mps with > O(10) sites one needs to normalize to avoid # over or underflow errors; this takes care of the normalization if normalize: self.nodes[n + 1].tensor /= Z self.center_position = site #shift center_position to the left using RQ decomposition elif site < self.center_position: for n in reversed(range(site + 1, self.center_position + 1)): R, Q = split_node_rq( self.nodes[n], left_edges=[self.nodes[n][0]], right_edges=[self.nodes[n][1], self.nodes[n][2]], right_name=self.nodes[n].name) # for an mps with > O(10) sites one needs to normalize to avoid # over or underflow errors; this takes care of the normalization self.nodes[n] = Q #Q is a right-isometric tensor of rank 3 self.nodes[n - 1] = contract(R[0], name=self.nodes[n - 1].name) Z = norm(self.nodes[n - 1]) if normalize: self.nodes[n - 1].tensor /= Z self.center_position = site #return the norm of the last R tensor (useful for checks) return Z
def split_node_qr( self, node: network_components.BaseNode, left_edges: List[network_components.Edge], right_edges: List[network_components.Edge], left_name: Optional[Text] = None, right_name: Optional[Text] = None, edge_name: Optional[Text] = None, ) -> Tuple[network_components.BaseNode, network_components.BaseNode]: """Split a `Node` using QR decomposition Let M be the matrix created by flattening left_edges and right_edges into 2 axes. Let :math:`QR = M` be the QR Decomposition of :math:`M`. This will split the network into 2 nodes. The left node's tensor will be :math:`Q` (an orthonormal matrix) and the right node's tensor will be :math:`R` (an upper triangular matrix) Args: node: The node you want to split. left_edges: The edges you want connected to the new left node. right_edges: The edges you want connected to the new right node. left_name: The name of the new left node. If `None`, a name will be generated automatically. right_name: The name of the new right node. If `None`, a name will be generated automatically. edge_name: The name of the new `Edge` connecting the new left and right node. If `None`, a name will be generated automatically. Returns: A tuple containing: left_node: A new node created that connects to all of the `left_edges`. Its underlying tensor is :math:`Q` right_node: A new node created that connects to all of the `right_edges`. Its underlying tensor is :math:`R` """ q, r = network_operations.split_node_qr(node, left_edges, right_edges, left_name, right_name, edge_name) left_node = self.add_node(q) right_node = self.add_node(r) self.nodes_set.remove(node) node.disable() return left_node, right_node