示例#1
0
    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
示例#2
0
    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