def test_expected_shapes(self):
     val = tf.zeros((2, 3, 4, 5))
     u, s, vh, _ = decompositions.svd_decomposition(val, 2)
     self.assertEqual(u.shape, (2, 3, 6))
     self.assertEqual(s.shape, (6, ))
     self.assertAllClose(s, np.zeros(6))
     self.assertEqual(vh.shape, (6, 4, 5))
Esempio n. 2
0
    def split_node(
        self,
        node: Node,
        left_edges: List[Edge],
        right_edges: List[Edge],
        max_singular_values: Optional[int] = None,
        max_truncation_err: Optional[float] = None
    ) -> Tuple[Node, Node, tf.Tensor]:
        """Split a Node using Singular Value Decomposition.

    Let M be the matrix created by flattening left_edges and right_edges into
    2 axes. Let U S V* = M be the Singular Value Decomposition of M.
    This will split the network into 2 nodes. The left node's tensor will be
    U * sqrt(S) and the right node's tensor will be sqrt(S) * (V*) where V* is
    the adjoint of V.

    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.
      max_singular_values: The maximum number of singular values to keep.
      max_truncation_err: The maximum allowed truncation error.

    Returns:
      left_node: A new node created that connects to all of the `left_edges`.
      right_node: A new node created that connects to all of the `right_edges`.
      truncated_singular_values: A vector of the dropped smallest singular
        values.
    """
        node.reorder_edges(left_edges + right_edges)
        u, s, vh, trun_vals = decompositions.svd_decomposition(
            node.tensor, len(left_edges), max_singular_values,
            max_truncation_err)
        sqrt_s = tf.sqrt(s)
        u_s = u * sqrt_s
        # We have to do this since we are doing element-wise multiplication against
        # the first axis of vh. If we don't, it's possible one of the other axes of
        # vh will be the same size as sqrt_s and would multiply across that axis
        # instead, which is bad.
        sqrt_s_broadcast_shape = tf.concat(
            [tf.shape(sqrt_s), [1] * (len(vh.shape) - 1)], axis=-1)
        vh_s = vh * tf.reshape(sqrt_s, sqrt_s_broadcast_shape)
        left_node = self.add_node(u_s)
        for i, edge in enumerate(left_edges):
            left_node.add_edge(edge, i)
            edge.update_axis(i, node, i, left_node)
        right_node = self.add_node(vh_s)
        for i, edge in enumerate(right_edges):
            # i + 1 to account for the new edge.
            right_node.add_edge(edge, i + 1)
            edge.update_axis(i + len(left_edges), node, i + 1, right_node)
        self.connect(left_node[-1], right_node[0])
        self.nodes_set.remove(node)
        return left_node, right_node, trun_vals
 def test_max_truncation_error(self):
     random_matrix = np.random.rand(10, 10)
     unitary1, _, unitary2 = np.linalg.svd(random_matrix)
     singular_values = np.array(range(10))
     val = unitary1.dot(np.diag(singular_values).dot(unitary2.T))
     u, s, vh, trun = decompositions.svd_decomposition(
         val, 1, max_truncation_error=math.sqrt(5.1))
     self.assertEqual(u.shape, (10, 7))
     self.assertEqual(s.shape, (7, ))
     self.assertAllClose(s, np.arange(9, 2, -1))
     self.assertEqual(vh.shape, (7, 10))
     self.assertAllClose(trun, np.arange(2, -1, -1))
Esempio n. 4
0
  def split_node(self,
                 node: Node,
                 left_edges: List[Edge],
                 right_edges: List[Edge],
                 max_singular_values: Optional[int] = None,
                 max_truncation_err: Optional[float] = None
                ) -> Tuple[Node, Node, tf.Tensor]:
    """Split a Node using Singular Value Decomposition.

    Let M be the matrix created by flattening left_edges and right_edges into
    2 axes. Let U S V* = M be the Singular Value Decomposition of M.
    This will split the network into 2 nodes. The left node's tensor will be
    U * sqrt(S) and the right node's tensor will be sqrt(S) * (V*) where V* is
    the adjoint of V.

    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.
      max_singular_values: The maximum number of singular values to keep.
      max_truncation_err: The maximum allowed truncation error.

    Returns:
      left_node: A new node created that connects to all of the `left_edges`.
      right_node: A new node created that connects to all of the `right_edges`.
      truncated_singular_values: A vector of the dropped smallest singular
        values.
    """
    node.reorder_edges(left_edges + right_edges)
    u, s, vh, trun_vals = decompositions.svd_decomposition(
        node.tensor, len(left_edges), max_singular_values, max_truncation_err)
    sqrt_s = tf.sqrt(s)
    u_s = u * sqrt_s
    # We have to do this since we are doing element-wise multiplication against
    # the first axis of vh. If we don't, it's possible one of the other axes of
    # vh will be the same size as sqrt_s and would multiply across that axis
    # instead, which is bad.
    sqrt_s_broadcast_shape = tf.concat(
        [tf.shape(sqrt_s), [1] * (len(vh.shape) - 1)], axis=-1)
    vh_s = vh * tf.reshape(sqrt_s, sqrt_s_broadcast_shape)
    left_node = self.add_node(u_s)
    for i, edge in enumerate(left_edges):
      left_node.add_edge(edge, i)
      edge.update_axis(i, node, i, left_node)
    right_node = self.add_node(vh_s)
    for i, edge in enumerate(right_edges):
      # i + 1 to account for the new edge.
      right_node.add_edge(edge, i + 1)
      edge.update_axis(i + len(left_edges), node, i + 1, right_node)
    self.connect(left_node[-1], right_node[0])
    self.nodes_set.remove(node)
    return left_node, right_node, trun_vals
Esempio n. 5
0
    def split_node_full_svd(
        self,
        node: network_components.Node,
        left_edges: List[network_components.Edge],
        right_edges: List[network_components.Edge],
        max_singular_values: Optional[int] = None,
        max_truncation_err: Optional[float] = None
    ) -> Tuple[network_components.Node, network_components.Node,
               network_components.Node, tf.Tensor]:
        """Split a node by doing a full singular value decomposition.

    Let M be the matrix created by flattening left_edges and right_edges into
    2 axes. Let U S V* = M be the Singular Value Decomposition of M.
    The left most node will be U tensor of the SVD, the middle node is
    the diagonal matrix of the singular values, ordered largest to smallest,
    and the right most node will be the V* tensor of the SVD.

    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.
      max_singular_values: The maximum number of singular values to keep.
      max_truncation_err: The maximum allowed truncation error.

    Returns:
      left_node: The new left node created. Its underlying tensor is the same
        as the U tensor from the SVD.
      singular_values_node: The new node representing the diagonal matrix of
        singular values.
      right_node: The new right node created. Its underlying tensor is the same
        as the V* tensor from the SVD.
      truncated_singular_values: The vector of truncated singular values.
    """
        node.reorder_edges(left_edges + right_edges)
        u, s, vh, trun_vals = decompositions.svd_decomposition(
            node.tensor, len(left_edges), max_singular_values,
            max_truncation_err)
        left_node = self.add_node(u)
        singular_values_node = self.add_node(tf.linalg.diag(s))
        right_node = self.add_node(vh)
        for i, edge in enumerate(left_edges):
            left_node.add_edge(edge, i)
            edge.update_axis(i, node, i, left_node)
        for i, edge in enumerate(right_edges):
            # i + 1 to account for the new edge.
            right_node.add_edge(edge, i + 1)
            edge.update_axis(i + len(left_edges), node, i + 1, right_node)
        self.connect(left_node[-1], singular_values_node[0])
        self.connect(singular_values_node[1], right_node[0])
        self.nodes_set.remove(node)
        return left_node, singular_values_node, right_node, trun_vals
Esempio n. 6
0
  def split_node_full_svd(self,
                          node: Node,
                          left_edges: List[Edge],
                          right_edges: List[Edge],
                          max_singular_values: Optional[int] = None,
                          max_truncation_err: Optional[float] = None
                         ) -> Tuple[Node, Node, Node, tf.Tensor]:
    """Split a node by doing a full singular value decomposition.

    Let M be the matrix created by flattening left_edges and right_edges into
    2 axes. Let U S V* = M be the Singular Value Decomposition of M.
    The left most node will be U tensor of the SVD, the middle node is
    the diagonal matrix of the singular values, ordered largest to smallest,
    and the right most node will be the V* tensor of the SVD.

    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.
      max_singular_values: The maximum number of singular values to keep.
      max_truncation_err: The maximum allowed truncation error.

    Returns:
      left_node: The new left node created. Its underlying tensor is the same
        as the U tensor from the SVD.
      singular_values_node: The new node representing the diagonal matrix of
        singular values.
      right_node: The new right node created. Its underlying tensor is the same
        as the V* tensor from the SVD.
      truncated_singular_values: The vector of truncated singular values.
    """
    node.reorder_edges(left_edges + right_edges)
    u, s, vh, trun_vals = decompositions.svd_decomposition(
        node.tensor, len(left_edges), max_singular_values, max_truncation_err)
    left_node = self.add_node(u)
    singular_values_node = self.add_node(tf.linalg.diag(s))
    right_node = self.add_node(vh)
    for i, edge in enumerate(left_edges):
      left_node.add_edge(edge, i)
      edge.update_axis(i, node, i, left_node)
    for i, edge in enumerate(right_edges):
      # i + 1 to account for the new edge.
      right_node.add_edge(edge, i + 1)
      edge.update_axis(i + len(left_edges), node, i + 1, right_node)
    self.connect(left_node[-1], singular_values_node[0])
    self.connect(singular_values_node[1], right_node[0])
    self.nodes_set.remove(node)
    return left_node, singular_values_node, right_node, trun_vals