예제 #1
0
def test_ChargeArray_reshape_raises():
    Ds = [8, 9, 10, 11]
    indices = [
        Index(U1Charge.random(dimension=Ds[n], minval=-5, maxval=5), False)
        for n in range(4)
    ]
    arr = ChargeArray.random(indices)

    with pytest.raises(ValueError, match=r"The shape \(2, 4, 9, 2, 5, 11\)"):
        arr.reshape([2, 4, 9, 2, 5, 11])

    with pytest.raises(ValueError, match="A tensor with"):
        arr.reshape([64, 65])

    arr2 = arr.reshape([72, 110])
    with pytest.raises(
            ValueError,
            match=r"The shape \(9, 8, 10, 11\) is incompatible with the"
            r" elementary shape \(8, 9, 10, 11\) of the tensor."):

        arr2.reshape([9, 8, 10, 11])

    Ds = [8, 9, 0, 11]
    indices = [
        Index(U1Charge.random(dimension=Ds[n], minval=-5, maxval=5), False)
        for n in range(4)
    ]
    arr3 = ChargeArray.random(indices)
    with pytest.raises(ValueError):
        arr3.reshape([72, 0])
def test_ChargeArray_reshape_raises():
    Ds = [8, 9, 10, 11]
    indices = [Index(U1Charge.random(-5, 5, Ds[n]), False) for n in range(4)]
    arr = ChargeArray.random(indices)
    with pytest.raises(ValueError):
        arr.reshape([64, 65])

    arr2 = arr.reshape([72, 110])
    with pytest.raises(ValueError):
        arr2.reshape([9, 8, 10, 11])

    Ds = [8, 9, 0, 11]
    indices = [Index(U1Charge.random(-5, 5, Ds[n]), False) for n in range(4)]
    arr3 = ChargeArray.random(indices)
    with pytest.raises(ValueError):
        arr3.reshape([72, 0])
예제 #3
0
def test_ChargeArray_transpose_reshape_contiguous(num_charges, chargetype):
    Ds = np.array([8, 9, 10, 11])
    flows = [True, False, True, False]
    indices = [
        Index(get_charge(chargetype, num_charges, Ds[n]), flows[n])
        for n in range(4)
    ]
    arr = ChargeArray.random(indices)
    nparr = np.reshape(arr.data, Ds)

    arr2 = arr.transpose([2, 0, 1, 3])
    nparr2 = nparr.transpose([2, 0, 1, 3])
    arr3 = arr2.reshape([80, 99])
    nparr3 = nparr2.reshape([80, 99])
    arr4 = arr3.transpose([1, 0])
    nparr4 = nparr3.transpose([1, 0])

    arr5 = arr4.reshape([9, 11, 10, 8])
    nparr5 = nparr4.reshape([9, 11, 10, 8])
    np.testing.assert_allclose(arr3.contiguous().data,
                               np.ascontiguousarray(nparr3).flat)
    np.testing.assert_allclose(arr4.contiguous().data,
                               np.ascontiguousarray(nparr4).flat)
    np.testing.assert_allclose(arr5.contiguous().data,
                               np.ascontiguousarray(nparr5).flat)
def test_ChargeArray_transpose_reshape_transpose_data(num_charges):
    Ds = np.array([8, 9, 10, 11])
    flows = [True, False, True, False]
    indices = [
        Index(
            BaseCharge(np.random.randint(-5, 6, (num_charges, Ds[n])),
                       charge_types=[U1Charge] * num_charges), flows[n])
        for n in range(4)
    ]
    arr = ChargeArray.random(indices)
    nparr = np.reshape(arr.data, Ds)

    arr2 = arr.transpose([2, 0, 1, 3])
    nparr2 = nparr.transpose([2, 0, 1, 3])
    arr3 = arr2.reshape([80, 99])
    nparr3 = nparr2.reshape([80, 99])
    arr4 = arr3.transpose([1, 0])
    nparr4 = nparr3.transpose([1, 0])

    arr5 = arr4.reshape([9, 11, 10, 8])
    nparr5 = nparr4.reshape([9, 11, 10, 8])
    np.testing.assert_allclose(arr3.transpose_data().data,
                               np.ascontiguousarray(nparr3).flat)
    np.testing.assert_allclose(arr4.transpose_data().data,
                               np.ascontiguousarray(nparr4).flat)
    np.testing.assert_allclose(arr5.transpose_data().data,
                               np.ascontiguousarray(nparr5).flat)
예제 #5
0
def test_ChargeArray_todense(dtype, num_charges, chargetype):
  Ds = [8, 9, 10, 11]
  indices = [
      Index(get_charge(chargetype, num_charges, Ds[n]), False) for n in range(4)
  ]
  arr = ChargeArray.random(indices, dtype=dtype)
  np.testing.assert_allclose(arr.todense(), np.reshape(arr.data, Ds))
예제 #6
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
예제 #7
0
def conj(tensor: ChargeArray) -> ChargeArray:
  """
  Return the complex conjugate of `tensor` in a new 
  `ChargeArray`.
  Args:
    tensor: A `ChargeArray` object.
  Returns:
    ChargeArray
  """
  return tensor.conj()
예제 #8
0
def test_herm_raises(chargetype, dtype):
    np.random.seed(10)
    D = 10
    rank = 3
    charges = [get_charge(chargetype, 1, D) for _ in range(rank)]
    flows = np.random.choice([True, False], size=rank, replace=True)
    inds = [Index(c, f) for c, f in zip(charges, flows)]
    T = ChargeArray.random(inds, dtype=dtype)
    with pytest.raises(ValueError, match="hermitian"):
        T.H
예제 #9
0
def test_herm(chargetype, dtype):
    np.random.seed(10)
    D = 10
    rank = 2
    charges = [get_charge(chargetype, 1, D) for _ in range(rank)]
    flows = np.random.choice([True, False], size=rank, replace=True)
    inds = [Index(c, f) for c, f in zip(charges, flows)]
    T = ChargeArray.random(inds, dtype=dtype)
    TH = T.H
    np.testing.assert_allclose(TH.todense(), T.todense().T.conj())
예제 #10
0
def test_ChargeArray_conj(dtype):
    Ds = np.array([8, 9, 10, 11])
    flows = [True, False, True, False]
    indices = [
        Index(U1Charge.random(dimension=Ds[n], minval=-5, maxval=5), flows[n])
        for n in range(4)
    ]
    arr = ChargeArray.random(indices, dtype=dtype)
    conj = arr.conj()
    np.testing.assert_allclose(conj.data, np.conj(arr.data))
예제 #11
0
def test_ChargeArray_init_raises(chargetype):
    np.random.seed(10)
    D = 10
    rank = 4
    charges = [get_charge(chargetype, 1, D) for _ in range(rank)]
    data = np.random.uniform(0, 1, size=D**rank)
    flows = np.random.choice([True, False], size=rank, replace=True)
    order = [[n + 10] for n in range(rank)]
    with pytest.raises(ValueError):
        ChargeArray(data, charges, flows, order=order)
def test_ChargeArray_todense(dtype, num_charges):
    Ds = [8, 9, 10, 11]
    indices = [
        Index(
            BaseCharge(np.random.randint(-5, 6, (num_charges, Ds[n])),
                       charge_types=[U1Charge] * num_charges), False)
        for n in range(4)
    ]
    arr = ChargeArray.random(indices, dtype=dtype)
    np.testing.assert_allclose(arr.todense(), np.reshape(arr.data, Ds))
def test_ChargeArray_transpose_raises():
    Ds = np.array([8, 9, 10, 11])
    flows = [True, False, True, False]
    indices = [
        Index(U1Charge.random(-5, 5, Ds[n]), flows[n]) for n in range(4)
    ]
    arr = ChargeArray.random(indices)
    order = [2, 1, 0]
    with pytest.raises(ValueError):
        arr.transpose(order)
예제 #14
0
def test_ChargeArray_generic(dtype, chargetype):
  Ds = [8, 9, 10, 11]
  indices = [Index(get_charge(chargetype, 1, Ds[n]), False) for n in range(4)]
  arr = ChargeArray.random(indices, dtype=dtype)
  assert arr.ndim == 4
  assert arr.dtype == dtype
  np.testing.assert_allclose(arr.shape, Ds)
  np.testing.assert_allclose(arr.flat_flows, [False, False, False, False])
  for n in range(4):
    assert charge_equal(indices[n]._charges[0], arr.flat_charges[n])
    assert arr.sparse_shape[n] == indices[n]
예제 #15
0
def test_ChargeArray_transpose(chargetype):
    Ds = np.array([8, 9, 10, 11])
    flows = [True, False, True, False]
    indices = [
        Index(get_charge(chargetype, 1, Ds[n]), flows[n]) for n in range(4)
    ]
    arr = ChargeArray.random(indices)
    order = [2, 1, 0, 3]
    arr2 = arr.transpose(order)
    np.testing.assert_allclose(Ds[order], arr2.shape)
    np.testing.assert_allclose(arr2._order, [[2], [1], [0], [3]])
    np.testing.assert_allclose(arr2.flows, [[True], [False], [True], [False]])
예제 #16
0
def transpose(tensor: ChargeArray,
              order: Sequence[int] = np.asarray([1, 0]),
              shuffle: Optional[bool] = False) -> ChargeArray:
  """
  Transpose the tensor into the new order `order`. If `shuffle=False`
  no data-reshuffling is done.
  Args:
    order: The new order of indices.
    shuffle: If `True`, reshuffle data.
  Returns:
    ChargeArray: The transposed tensor.
  """
  return tensor.transpose(order, shuffle)
예제 #17
0
def test_ChargeArray_init(chargetype):
    np.random.seed(10)
    D = 10
    rank = 4
    charges = [get_charge(chargetype, 1, D) for _ in range(rank)]
    data = np.random.uniform(0, 1, size=D**rank)
    flows = np.random.choice([True, False], size=rank, replace=True)
    order = [[n] for n in range(rank)]
    arr = ChargeArray(data, charges, flows, order=order)
    np.testing.assert_allclose(data, arr.data)
    for c1, c2 in zip(charges, arr.charges):
        assert charge_equal(c1, c2[0])
    for c1, c2 in zip(charges, arr._charges):
        assert charge_equal(c1, c2)
예제 #18
0
def test_diag_raises():
    np.random.seed(10)
    Ds = [8, 9, 10]
    rank = len(Ds)
    indices = [
        Index(
            BaseCharge(np.random.randint(-2, 3, (1, Ds[n])),
                       charge_types=[U1Charge]), False) for n in range(rank)
    ]
    arr = BlockSparseTensor.random(indices)
    chargearr = ChargeArray.random([indices[0], indices[1]])
    with pytest.raises(ValueError):
        diag(arr)
    with pytest.raises(ValueError):
        diag(chargearr)
예제 #19
0
def test_ChargeArray_reshape_with_index(dtype, chargetype):
    Ds = [8, 9, 10, 11]
    R = len(Ds)
    indices = [
        Index(get_charge(chargetype, 1, Ds[n]), False) for n in range(R)
    ]
    arr = ChargeArray.random(indices, dtype=dtype)
    arr2 = arr.reshape([indices[0] * indices[1], indices[2] * indices[3]])
    cnt = 0
    for n in range(arr2.ndim):
        for m in range(len(arr2.charges[n])):
            assert charge_equal(arr2.charges[n][m], indices[cnt].charges)
            cnt += 1
    np.testing.assert_allclose(arr2.shape, [72, 110])
    assert arr2.ndim == 2
예제 #20
0
def test_repr():
    np.random.seed(10)
    dtype = np.float64
    D = 10
    rank = 3
    charges = [U1Charge.random(D, -2, 2) for _ in range(rank)]
    flows = np.random.choice([True, False], size=rank, replace=True)
    inds = [Index(c, f) for c, f in zip(charges, flows)]
    T = ChargeArray.random(inds, dtype=dtype)
    actual = T.__repr__()
    expected = "ChargeArray\n   shape: (10, 10, 10)\n  " +\
      " charge types: ['U1Charge']\n   dtype: " +\
      repr(T.dtype.name) + "\n   flat flows: " + \
      repr(list(flows)) + "\n   order: " + repr(T._order)
    assert actual == expected
예제 #21
0
def test_ChargeArray_contiguous(num_charges, chargetype):
    Ds = np.array([8, 9, 10, 11])
    order = [2, 0, 1, 3]
    flows = [True, False, True, False]
    indices = [
        Index(get_charge(chargetype, num_charges, Ds[n]), flows[n])
        for n in range(4)
    ]
    arr = ChargeArray.random(indices)
    data = np.ascontiguousarray(np.transpose(np.reshape(arr.data, Ds), order))
    arr2 = arr.transpose(order).contiguous()
    data3 = np.reshape(arr2.data, Ds[order])
    np.testing.assert_allclose(data, data3)
    np.testing.assert_allclose(arr2.shape, Ds[order])
    np.testing.assert_allclose(arr2._order, [[0], [1], [2], [3]])
    np.testing.assert_allclose(arr2.flows, [[True], [True], [False], [False]])
예제 #22
0
def test_create_diag(dtype, num_charges):
    np.random.seed(10)
    D = 200
    index = Index(
        BaseCharge(np.random.randint(-2, 3, (num_charges, D)),
                   charge_types=[U1Charge] * num_charges), False)

    arr = ChargeArray.random([index], dtype=dtype)
    diagarr = diag(arr)
    dense = np.ravel(diagarr.todense())
    np.testing.assert_allclose(np.sort(dense[dense != 0.0]),
                               np.sort(diagarr.data[diagarr.data != 0.0]))

    sparse_blocks, charges, block_shapes = _find_diagonal_sparse_blocks(
        diagarr.flat_charges, diagarr.flat_flows, 1)
    #in range(index._charges[0].unique_charges.shape[1]):
    for n, block in enumerate(sparse_blocks):
        shape = block_shapes[:, n]
        block_diag = np.diag(np.reshape(diagarr.data[block], shape))
        np.testing.assert_allclose(
            arr.data[np.squeeze(index._charges[0] == charges[n])], block_diag)
예제 #23
0
def test_ChargeArray_arithmetic_raises():
    np.random.seed(10)
    dtype = np.float64
    D = 10
    rank = 3
    charges = [U1Charge.random(D, -2, 2) for _ in range(rank)]
    flows = np.random.choice([True, False], size=rank, replace=True)
    inds = [Index(c, f) for c, f in zip(charges, flows)]
    T = ChargeArray.random(inds, dtype=dtype)
    with pytest.raises(NotImplementedError):
        T - T
    with pytest.raises(NotImplementedError):
        T + T
    with pytest.raises(NotImplementedError):
        -T
    with pytest.raises(NotImplementedError):
        T * 5
    with pytest.raises(NotImplementedError):
        5 * T
    with pytest.raises(NotImplementedError):
        T / 5
예제 #24
0
def reshape(
    tensor: ChargeArray, shape: Union[List[Index], Tuple[Index, ...], List[int],
                                      Tuple[int, ...]]
) -> ChargeArray:
  """
  Reshape `tensor` into `shape.
  `ChargeArray.reshape` works the same as the dense 
  version, with the notable exception that the tensor can only be 
  reshaped into a form compatible with its elementary shape. 
  The elementary shape is the shape determined by ChargeArray._charges.
  For example, while the following reshaping is possible for regular 
  dense numpy tensor,
  ```
  A = np.random.rand(6,6,6)
  np.reshape(A, (2,3,6,6))
  ```
  the same code for ChargeArray
  ```
  q1 = U1Charge(np.random.randint(0,10,6))
  q2 = U1Charge(np.random.randint(0,10,6))
  q3 = U1Charge(np.random.randint(0,10,6))
  i1 = Index(charges=q1,flow=False)
  i2 = Index(charges=q2,flow=True)
  i3 = Index(charges=q3,flow=False)
  A = ChargeArray.randn(indices=[i1,i2,i3])
  print(A.shape) #prints (6,6,6)
  A.reshape((2,3,6,6)) #raises ValueError
  ```
  raises a `ValueError` since (2,3,6,6)
  is incompatible with the elementary shape (6,6,6) of the tensor.
  
  Args:
    tensor: A symmetric tensor.
    shape: The new shape. Can either be a list of `Index` 
      or a list of `int`.
  Returns:
    ChargeArray: A new tensor reshaped into `shape`
  """

  return tensor.reshape(shape)
예제 #25
0
def test_ChargeArray_transpose_reshape(chargetype):
    Ds = np.array([8, 9, 10, 11])
    flows = [True, False, True, False]
    indices = [
        Index(get_charge(chargetype, 1, Ds[n]), flows[n]) for n in range(4)
    ]
    arr = ChargeArray.random(indices)
    arr2 = arr.transpose([2, 0, 1, 3])
    arr3 = arr2.reshape([80, 99])
    np.testing.assert_allclose(arr3.shape, [80, 99])
    np.testing.assert_allclose(arr3._order, [[2, 0], [1, 3]])
    np.testing.assert_allclose(arr3.flows, [[True, True], [False, False]])

    arr4 = arr3.transpose([1, 0])
    np.testing.assert_allclose(arr4.shape, [99, 80])
    np.testing.assert_allclose(arr4._order, [[1, 3], [2, 0]])
    np.testing.assert_allclose(arr4.flows, [[False, False], [True, True]])

    arr5 = arr4.reshape([9, 11, 10, 8])
    np.testing.assert_allclose(arr5.shape, [9, 11, 10, 8])
    np.testing.assert_allclose(arr5._order, [[1], [3], [2], [0]])
    np.testing.assert_allclose(arr5.flows, [[False], [False], [True], [True]])
예제 #26
0
def test_repr():
    np.random.seed(10)
    dtype = np.float64
    D = 10
    rank = 3
    charges = [U1Charge.random(D, -2, 2) for _ in range(rank)]
    flows = np.random.choice([True, False], size=rank, replace=True)
    inds = [Index(c, f) for c, f in zip(charges, flows)]
    T = ChargeArray.random(inds, dtype=dtype)
    actual = T.__repr__()
    expected = "ChargeArray\n   shape: (10, 10, 10)\n  " +\
      " charge types: ['U1Charge']\n   dtype: " +\
      repr(T.dtype.name) + "\n   flat flows: " + \
      repr(list(flows)) + "\n   order: " + repr(T._order)
    assert actual == expected

    res = tensordot(T, T.conj(), ([0, 1, 2], [0, 1, 2]))
    actual = res.__repr__()
    expected = "BlockSparseTensor\n   shape: ()\n  " +\
      " charge types: no charge types (scalar)\n   dtype: " +\
      repr(res.dtype.name) + "\n   flat flows: " + \
      repr(list(res.flat_flows)) + "\n   order: " + repr(res._order)
    assert actual == expected
예제 #27
0
def test_ChargeArray_reshape(dtype, Ds, chargetype):
    flat_Ds = sum(Ds, [])
    R = len(flat_Ds)
    indices = [
        Index(get_charge(chargetype, 1, flat_Ds[n]), False) for n in range(R)
    ]
    arr = ChargeArray.random(indices, dtype=dtype)

    ds = [np.prod(D) for D in Ds]
    arr2 = arr.reshape(ds)
    cnt = 0
    for n in range(arr2.ndim):
        for m in range(len(arr2.charges[n])):
            assert charge_equal(arr2.charges[n][m], indices[cnt].charges)
            cnt += 1
    order = []
    flows = []
    start = 0
    for D in Ds:
        order.append(list(range(start, start + len(D))))
        start += len(D)
        flows.append([False] * len(D))

    np.testing.assert_allclose(arr2.shape, ds)
    for n in range(len(arr2._order)):
        np.testing.assert_allclose(arr2._order[n], order[n])
        np.testing.assert_allclose(arr2.flows[n], flows[n])
    assert arr2.ndim == len(Ds)
    arr3 = arr.reshape(flat_Ds)
    for n in range(len(Ds)):
        assert charge_equal(arr3.charges[n][0], indices[n].charges)

    np.testing.assert_allclose(arr3.shape, flat_Ds)
    np.testing.assert_allclose(arr3._order, [[n] for n in range(len(flat_Ds))])
    np.testing.assert_allclose(arr3.flows,
                               [[False] for n in range(len(flat_Ds))])
    assert arr3.ndim == len(flat_Ds)
예제 #28
0
def diag(tensor: ChargeArray) -> Any:
  """
  Return a diagonal `BlockSparseTensor` from a `ChargeArray`, or 
  return the diagonal of a `BlockSparseTensor` as a `ChargeArray`.
  For input of type `BlockSparseTensor`:
    The full diagonal is obtained from finding the diagonal blocks of the 
    `BlockSparseTensor`, taking the diagonal elements of those and packing
    the result into a ChargeArray. Note that the computed diagonal elements 
    are usually different from the  diagonal elements obtained from 
    converting the `BlockSparseTensor` to dense storage and taking the diagonal.
    Note that the flow of the resulting 1d `ChargeArray` object is `False`.
  Args:
    tensor: A `ChargeArray`.
  Returns:
    ChargeArray: A 1d `CharggeArray` containing the diagonal of `tensor`, 
      or a diagonal matrix of type `BlockSparseTensor` containing `tensor` 
      on its diagonal.

  """
  if tensor.ndim > 2:
    raise ValueError("`diag` currently only implemented for matrices, "
                     "found `ndim={}".format(tensor.ndim))
  if not isinstance(tensor, BlockSparseTensor):
    if tensor.ndim > 1:
      raise ValueError(
          "`diag` currently only implemented for `ChargeArray` with ndim=1, "
          "found `ndim={}`".format(tensor.ndim))
    flat_charges = tensor._charges + tensor._charges
    flat_flows = list(tensor._flows) + list(np.logical_not(tensor._flows))
    flat_order = list(tensor.flat_order) + list(
        np.asarray(tensor.flat_order) + len(tensor._charges))
    tr_partition = len(tensor._order[0])
    blocks, charges, shapes = _find_transposed_diagonal_sparse_blocks(
        flat_charges, flat_flows, tr_partition, flat_order)
    data = np.zeros(
        np.int64(np.sum(np.prod(shapes, axis=0))), dtype=tensor.dtype)
    lookup, unique, labels = compute_sparse_lookup(tensor._charges,
                                                   tensor._flows, charges)
    for n, block in enumerate(blocks):
      label = labels[np.nonzero(unique == charges[n])[0][0]]
      data[block] = np.ravel(
          np.diag(tensor.data[np.nonzero(lookup == label)[0]]))

    order = [
        tensor._order[0],
        list(np.asarray(tensor._order[0]) + len(tensor._charges))
    ]
    new_charges = [tensor._charges[0].copy(), tensor._charges[0].copy()]
    return BlockSparseTensor(
        data,
        charges=new_charges,
        flows=list(tensor._flows) + list(np.logical_not(tensor._flows)),
        order=order,
        check_consistency=False)

  flat_charges = tensor._charges
  flat_flows = tensor._flows
  flat_order = tensor.flat_order
  tr_partition = len(tensor._order[0])
  sparse_blocks, charges, block_shapes = _find_transposed_diagonal_sparse_blocks(#pylint: disable=line-too-long
      flat_charges, flat_flows, tr_partition, flat_order)

  shapes = np.min(block_shapes, axis=0)
  if len(sparse_blocks) > 0:
    data = np.concatenate([
        np.diag(np.reshape(tensor.data[sparse_blocks[n]], block_shapes[:, n]))
        for n in range(len(sparse_blocks))
    ])
    charge_labels = np.concatenate([
        np.full(shapes[n], fill_value=n, dtype=np.int16)
        for n in range(len(sparse_blocks))
    ])

  else:
    data = np.empty(0, dtype=tensor.dtype)
    charge_labels = np.empty(0, dtype=np.int16)
  newcharges = [charges[charge_labels]]
  flows = [False]
  return ChargeArray(data, newcharges, flows)
예제 #29
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
예제 #30
0
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]