示例#1
0
def eig(matrix: BlockSparseTensor) -> Tuple[ChargeArray, BlockSparseTensor]:
    """
  Compute the eigen decomposition of an `M` by `M` matrix `matrix`.
  Args:
    matrix: A matrix (i.e. a rank-2 tensor) of type  `BlockSparseTensor`

  Returns:
    (ChargeArray,BlockSparseTensor): The eigenvalues and eigenvectors

  """
    if matrix.ndim != 2:
        raise NotImplementedError(
            "eig currently supports only rank-2 tensors.")

    flat_charges = matrix._charges
    flat_flows = matrix._flows
    flat_order = matrix.flat_order
    tr_partition = len(matrix._order[0])
    blocks, charges, shapes = _find_transposed_diagonal_sparse_blocks(
        flat_charges, flat_flows, tr_partition, flat_order)

    eigvals = []
    v_blocks = []
    for n, block in enumerate(blocks):
        e, v = np.linalg.eig(np.reshape(matrix.data[block], shapes[:, n]))
        eigvals.append(e)
        v_blocks.append(v)
    tmp_labels = [
        np.full(len(eigvals[n]), fill_value=n, dtype=np.int16)
        for n in range(len(eigvals))
    ]
    if len(tmp_labels) > 0:
        eigvalscharge_labels = np.concatenate(tmp_labels)
    else:
        eigvalscharge_labels = np.empty(0, dtype=np.int16)

    eigvalscharge = charges[eigvalscharge_labels]

    if len(eigvals) > 0:
        all_eigvals = np.concatenate(eigvals)
    else:
        all_eigvals = np.empty(0, dtype=get_real_dtype(matrix.dtype))

    E = ChargeArray(all_eigvals, [eigvalscharge], [False])
    charges_v = [eigvalscharge
                 ] + [matrix._charges[o] for o in matrix._order[0]]
    order_v = [[0]] + [list(np.arange(1, len(matrix._order[0]) + 1))]
    flows_v = [True] + [matrix._flows[o] for o in matrix._order[0]]
    if len(v_blocks) > 0:
        all_v_blocks = np.concatenate([np.ravel(v.T) for v in v_blocks])
    else:
        all_v_blocks = np.empty(0, dtype=matrix.dtype)

    V = BlockSparseTensor(all_v_blocks,
                          charges=charges_v,
                          flows=flows_v,
                          order=order_v,
                          check_consistency=False).transpose()

    return E, V  #pytype: disable=bad-return-type
示例#2
0
def svd(matrix: BlockSparseTensor,
        full_matrices: Optional[bool] = True,
        compute_uv: Optional[bool] = True,
        hermitian: Optional[bool] = False) -> Any:
  """
  Compute the singular value decomposition of `matrix`.
  The matrix if factorized into `u * s * vh`, with 
  `u` and `vh` the left and right singular vectors of `matrix`,
  and `s` its singular values.
  Args:
    matrix: A matrix (i.e. an order-2 tensor) of type  `BlockSparseTensor`
    full_matrices: If `True`, expand `u` and `v` to square matrices
      If `False` return the "economic" svd, i.e. `u.shape[1]=s.shape[0]`
      and `v.shape[0]=s.shape[1]`
    compute_uv: If `True`, return `u` and `v`.
    hermitian: If `True`, assume hermiticity of `matrix`.
  Returns:
    If `compute_uv` is `True`: Three BlockSparseTensors `U,S,V`.
    If `compute_uv` is `False`: A BlockSparseTensors `S` containing the 
      singular values.
  """

  if matrix.ndim != 2:
    raise NotImplementedError("svd currently supports only tensors of order 2.")

  flat_charges = matrix._charges
  flat_flows = matrix._flows
  flat_order = matrix.flat_order
  tr_partition = len(matrix._order[0])
  blocks, charges, shapes = _find_transposed_diagonal_sparse_blocks(
      flat_charges, flat_flows, tr_partition, flat_order)

  u_blocks = []
  singvals = []
  v_blocks = []
  for n, block in enumerate(blocks):
    out = np.linalg.svd(
        np.reshape(matrix.data[block], shapes[:, n]), full_matrices, compute_uv,
        hermitian)
    if compute_uv:
      u_blocks.append(out[0])
      singvals.append(out[1])
      v_blocks.append(out[2])

    else:
      singvals.append(out)

  tmp_labels = [
      np.full(len(singvals[n]), fill_value=n, dtype=np.int16)
      for n in range(len(singvals))
  ]
  if len(tmp_labels) > 0:
    left_singval_charge_labels = np.concatenate(tmp_labels)
  else:

    left_singval_charge_labels = np.empty(0, dtype=np.int16)
  left_singval_charge = charges[left_singval_charge_labels]
  if len(singvals) > 0:
    all_singvals = np.concatenate(singvals)
  else:
    all_singvals = np.empty(0, dtype=get_real_dtype(matrix.dtype))
  S = ChargeArray(all_singvals, [left_singval_charge], [False])

  if compute_uv:
    #define the new charges on the two central bonds
    tmp_left_labels = [
        np.full(u_blocks[n].shape[1], fill_value=n, dtype=np.int16)
        for n in range(len(u_blocks))
    ]
    if len(tmp_left_labels) > 0:
      left_charge_labels = np.concatenate(tmp_left_labels)
    else:
      left_charge_labels = np.empty(0, dtype=np.int16)

    tmp_right_labels = [
        np.full(v_blocks[n].shape[0], fill_value=n, dtype=np.int16)
        for n in range(len(v_blocks))
    ]
    if len(tmp_right_labels) > 0:
      right_charge_labels = np.concatenate(tmp_right_labels)
    else:
      right_charge_labels = np.empty(0, dtype=np.int16)
    new_left_charge = charges[left_charge_labels]
    new_right_charge = charges[right_charge_labels]

    charges_u = [new_left_charge
                ] + [matrix._charges[o] for o in matrix._order[0]]
    order_u = [[0]] + [list(np.arange(1, len(matrix._order[0]) + 1))]
    flows_u = [True] + [matrix._flows[o] for o in matrix._order[0]]
    charges_v = [new_right_charge
                ] + [matrix._charges[o] for o in matrix._order[1]]
    flows_v = [False] + [matrix._flows[o] for o in matrix._order[1]]
    order_v = [[0]] + [list(np.arange(1, len(matrix._order[1]) + 1))]
    # We fill in data into the transposed U
    # note that transposing is essentially free
    if len(u_blocks) > 0:
      all_u_blocks = np.concatenate([np.ravel(u.T) for u in u_blocks])
      all_v_blocks = np.concatenate([np.ravel(v) for v in v_blocks])
    else:
      all_u_blocks = np.empty(0, dtype=matrix.dtype)
      all_v_blocks = np.empty(0, dtype=matrix.dtype)

    return BlockSparseTensor(
        all_u_blocks,
        charges=charges_u,
        flows=flows_u,
        order=order_u,
        check_consistency=False).transpose((1, 0)), S, BlockSparseTensor(
            all_v_blocks,
            charges=charges_v,
            flows=flows_v,
            order=order_v,
            check_consistency=False)

  return S
def svd(bt,
        tensor: BlockSparseTensor,
        pivot_axis: int,
        max_singular_values: Optional[int] = None,
        max_truncation_error: Optional[float] = None,
        relative: Optional[bool] = False
        ) -> Tuple[Tensor, Tensor, Tensor, Tensor]:
    """
  Computes the singular value decomposition (SVD) of a tensor.
  See tensornetwork.backends.tensorflow.decompositions for details.
  """

    left_dims = tensor.shape[:pivot_axis]
    right_dims = tensor.shape[pivot_axis:]

    matrix = bt.reshape(tensor, [np.prod(left_dims), np.prod(right_dims)])

    flat_charges = matrix._charges
    flat_flows = matrix._flows
    flat_order = matrix.flat_order
    tr_partition = len(matrix._order[0])
    blocks, charges, shapes = _find_transposed_diagonal_sparse_blocks(
        flat_charges, flat_flows, tr_partition, flat_order)

    u_blocks = []
    singvals = []
    v_blocks = []
    for n, b in enumerate(blocks):
        out = np.linalg.svd(np.reshape(matrix.data[b], shapes[:, n]),
                            full_matrices=False,
                            compute_uv=True)
        u_blocks.append(out[0])
        singvals.append(out[1])
        v_blocks.append(out[2])

    orig_num_singvals = np.int64(np.sum([len(s) for s in singvals]))
    discarded_singvals = np.zeros(0, dtype=get_real_dtype(tensor.dtype))
    if (max_singular_values
            is not None) and (max_singular_values >= orig_num_singvals):
        max_singular_values = None

    if (max_truncation_error is not None) or (max_singular_values is not None):
        max_D = np.max([len(s) for s in singvals]) if len(singvals) > 0 else 0

        #extend singvals of all blocks into a matrix by padding each block with 0
        if len(singvals) > 0:
            extended_singvals = np.stack([
                np.append(s, np.zeros(max_D - len(s), dtype=s.dtype))
                for s in singvals
            ],
                                         axis=1)
        else:
            extended_singvals = np.empty((0, 0),
                                         dtype=get_real_dtype(tensor.dtype))

        extended_flat_singvals = np.ravel(extended_singvals)
        #sort singular values
        inds = np.argsort(extended_flat_singvals, kind='stable')
        discarded_inds = np.zeros(0, dtype=SIZE_T)
        if inds.shape[0] > 0:
            maxind = inds[-1]
        else:
            maxind = 0
        if max_truncation_error is not None:
            if relative and (len(singvals) > 0):
                max_truncation_error = max_truncation_error * np.max(
                    [s[0] for s in singvals])

            kept_inds_mask = np.sqrt(
                np.cumsum(np.square(
                    extended_flat_singvals[inds]))) > max_truncation_error
            trunc_inds_mask = np.logical_not(kept_inds_mask)
            discarded_inds = inds[trunc_inds_mask]
            inds = inds[kept_inds_mask]
        if max_singular_values is not None:
            #if the original number of non-zero singular values
            #is smaller than `max_singular_values` we need to reset
            #`max_singular_values` (we were filling in 0.0 into singular
            #value blocks to facilitate trunction steps, thus we could end up
            #with more singular values than originally there).
            if max_singular_values > orig_num_singvals:
                max_singular_values = orig_num_singvals
            if max_singular_values < len(inds):
                discarded_inds = np.append(discarded_inds,
                                           inds[:(-1) * max_singular_values])
                inds = inds[(-1) * max_singular_values::]

        if len(inds) == 0:
            #special case of truncation to 0 dimension;
            warnings.warn("svd_decomposition truncated to 0 dimensions. "
                          "Adjusting to `max_singular_values = 1`")
            inds = np.asarray([maxind])

        if extended_singvals.shape[1] > 0:
            #pylint: disable=no-member
            keep = np.divmod(inds, extended_singvals.shape[1])
        else:
            keep = (np.zeros(1, dtype=SIZE_T), np.zeros(1, dtype=SIZE_T))
        newsingvals = [
            extended_singvals[keep[0][keep[1] == n],
                              keep[1][keep[1] == n]][::-1]
            for n in range(extended_singvals.shape[1])
        ]

        discarded_singvals = extended_flat_singvals[discarded_inds]
        singvals = newsingvals
    if len(singvals) > 0:
        left_singval_charge_labels = np.concatenate([
            np.full(singvals[n].shape[0], fill_value=n, dtype=np.int16)
            for n in range(len(singvals))
        ])
        all_singvals = np.concatenate(singvals)
        #define the new charges on the two central bonds
        left_charge_labels = np.concatenate([
            np.full(len(singvals[n]), fill_value=n, dtype=np.int16)
            for n in range(len(u_blocks))
        ])
        right_charge_labels = np.concatenate([
            np.full(len(singvals[n]), fill_value=n, dtype=np.int16)
            for n in range(len(v_blocks))
        ])
        all_ublocks = np.concatenate([
            np.ravel(np.transpose(u_blocks[n][:, 0:len(singvals[n])]))
            for n in range(len(u_blocks))
        ])
        all_vblocks = np.concatenate([
            np.ravel(v_blocks[n][0:len(singvals[n]), :])
            for n in range(len(v_blocks))
        ])
    else:
        left_singval_charge_labels = np.empty(0, dtype=np.int16)
        all_singvals = np.empty(0, dtype=get_real_dtype(tensor.dtype))
        left_charge_labels = np.empty(0, dtype=np.int16)
        right_charge_labels = np.empty(0, dtype=np.int16)
        all_ublocks = np.empty(0, dtype=get_real_dtype(tensor.dtype))
        all_vblocks = np.empty(0, dtype=get_real_dtype(tensor.dtype))
    left_singval_charge = charges[left_singval_charge_labels]
    S = ChargeArray(all_singvals, [left_singval_charge], [False])

    new_left_charge = charges[left_charge_labels]
    new_right_charge = charges[right_charge_labels]

    #get the indices of the new tensors U,S and V
    charges_u = [new_left_charge
                 ] + [matrix._charges[o] for o in matrix._order[0]]
    order_u = [[0]] + [list(np.arange(1, len(matrix._order[0]) + 1))]
    flows_u = [True] + [matrix._flows[o] for o in matrix._order[0]]
    charges_v = [new_right_charge
                 ] + [matrix._charges[o] for o in matrix._order[1]]
    flows_v = [False] + [matrix._flows[o] for o in matrix._order[1]]
    order_v = [[0]] + [list(np.arange(1, len(matrix._order[1]) + 1))]

    #We fill in data into the transposed U
    U = BlockSparseTensor(all_ublocks,
                          charges=charges_u,
                          flows=flows_u,
                          order=order_u,
                          check_consistency=False).transpose((1, 0))

    V = BlockSparseTensor(all_vblocks,
                          charges=charges_v,
                          flows=flows_v,
                          order=order_v,
                          check_consistency=False)
    left_shape = left_dims + (S.shape[0], )
    right_shape = (S.shape[0], ) + right_dims
    return U.reshape(left_shape), S, V.reshape(
        right_shape), discarded_singvals[discarded_singvals > 0.0]
示例#4
0
def test_get_real_dtype():
    assert get_real_dtype(np.complex128) == np.float64
    assert get_real_dtype(np.complex64) == np.float32
    assert get_real_dtype(np.float64) == np.float64
    assert get_real_dtype(np.float32) == np.float32