Block-Sparse Tensor

A SubTensor is a numpy.ndarray with a tuple of SZ for quantum labels associated. It can be initialized using a numpy.ndarray.shape and a tuple of SZ.

>>> from pyblock3.algebra.core import SubTensor
>>> from pyblock3.algebra.symmetry import SZ
>>> x = SubTensor.zeros((4,3), q_labels=(SZ(0, 0, 0), SZ(1, 1, 2)))
>>> x
(Q=) (< N=0 SZ=0 PG=0 >, < N=1 SZ=1/2 PG=2 >) (R=) array([[0., 0., 0.],
      [0., 0., 0.],
      [0., 0., 0.],
      [0., 0., 0.]])
>>> x[1,:] = 1
>>> x
(Q=) (< N=0 SZ=0 PG=0 >, < N=1 SZ=1/2 PG=2 >) (R=) array([[0., 0., 0.],
      [1., 1., 1.],
      [0., 0., 0.],
      [0., 0., 0.]])
>>> x.q_labels
(< N=0 SZ=0 PG=0 >, < N=1 SZ=1/2 PG=2 >)

A SparseTensor represents a block-sparse tensor, which contains a list of SubTensor. A quantum-number conserving SparseTensor can be initialized using a BondInfo, pattern and dq. pattern is a string of ‘+’ or ‘-’, indicating how to combine SZ to get dq. dq is the conserved quantum number. For 1D SparseTensor, the initialization method does not require quantum-number conservation.

>>> from pyblock3.algebra.core import SparseTensor
>>> from pyblock3.algebra.symmetry import SZ, BondInfo
>>> x = BondInfo({SZ(0, 0, 0): 1, SZ(1, 1, 2): 2, SZ(-1, -1, 2): 2})
>>> SparseTensor.random((x, x), pattern='++', dq=SZ(0, 0, 0))
0 (Q=) (< N=-1 SZ=-1/2 PG=2 >, < N=1 SZ=1/2 PG=2 >) (R=) array([[0.89718406, 0.85419892],
      [0.65863698, 0.98023596]])
1 (Q=) (< N=0 SZ=0 PG=0 >, < N=0 SZ=0 PG=0 >) (R=) array([[0.69742141]])
2 (Q=) (< N=1 SZ=1/2 PG=2 >, < N=-1 SZ=-1/2 PG=2 >) (R=) array([[0.50722408, 0.34099007],
      [0.40760832, 0.8430552 ]])

Note that the resulting tensor has three non-zero blocks, for each block, the quantum numbers adds to dq, which is SZ(0, 0, 0). So this is a quantum-number-conserving block-sparse tensor.

SparseTensor supports most common numpy.ndarray operations:

>>> import numpy as np
>>> x = SparseTensor.random((x, x), pattern='++', dq=SZ(0, 0, 0))
>>> y = 2 * x
>>> np.linalg.norm(y)
3.386356824229238
>>> np.linalg.norm(x)
1.693178412114619
>>> np.tensordot(x, y, axes=1)
0 (Q=) (< N=-1 SZ=-1/2 PG=2 >, < N=-1 SZ=-1/2 PG=2 >) (R=) array([[0.37106833, 0.92381267],
      [0.23117763, 1.27553566]])
1 (Q=) (< N=0 SZ=0 PG=0 >, < N=0 SZ=0 PG=0 >) (R=) array([[1.25199691]])
2 (Q=) (< N=1 SZ=1/2 PG=2 >, < N=1 SZ=1/2 PG=2 >) (R=) array([[0.66650452, 0.63606196],
      [0.61864202, 0.98009947]])

A FermionTensor contains two SparseTensor, including blocks with odd dq and even dq, respectively.

FlatSparseTensor is another representation of block-sparse tensor, where quantum-number are combined together to a single integer, and floating-point contents of all blocks are merged to one single “flattened” numpy.ndarray.

FlatSparseTensor has the same interface as SparseTensor, but FlatSparseTensor provides much faster C++ implementation for functions like tensordot and tranpose. For debugging purpose, FlatSparseTensor also has pure python implementation, which can be activated by setting ENABLE_FAST_IMPLS = False in flat.py.