Skip to content
This repository was archived by the owner on Nov 7, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
63cddc4
started implementing block-sparse tensors
mganahl Oct 22, 2019
2910b27
removed files
mganahl Oct 22, 2019
6dafdd7
Merge remote-tracking branch 'upstream/master' into block_sparse
mganahl Oct 24, 2019
46f1e10
working on AbelianIndex
mganahl Oct 25, 2019
9ba1d21
Merge remote-tracking branch 'upstream/master' into block_sparse
mganahl Nov 28, 2019
91f32a6
working in block sparisty
mganahl Nov 29, 2019
58feabc
added reshape
mganahl Nov 30, 2019
307f2dc
added Index, an index type for symmetric tensors
mganahl Nov 30, 2019
1ebbc7f
added small tutorial
mganahl Nov 30, 2019
1eb3d6f
added docstring
mganahl Nov 30, 2019
d25d8aa
fixed bug in retrieve_diagonal_blocks
mganahl Nov 30, 2019
ae8cda6
TODO added
mganahl Nov 30, 2019
bbac9c4
improved initialization a bit
mganahl Nov 30, 2019
db828c7
more efficient initialization
mganahl Dec 1, 2019
99204f7
just formatting
mganahl Dec 1, 2019
73a9628
added random
mganahl Dec 1, 2019
efa64a4
added fuse_degeneracies
mganahl Dec 1, 2019
7619162
fixed bug in reshape
mganahl Dec 1, 2019
2be30a9
dosctring, typing
mganahl Dec 1, 2019
742824f
removed TODO
mganahl Dec 1, 2019
2e6c395
removed confusing code line
mganahl Dec 1, 2019
ab13d4a
bug removed
mganahl Dec 1, 2019
d375b1d
comment
mganahl Dec 1, 2019
2727cd0
added __mul__ to Index
mganahl Dec 2, 2019
283e364
added sparse_shape
mganahl Dec 2, 2019
7328ad4
more in tutorial
mganahl Dec 2, 2019
e5b6147
comment
mganahl Dec 2, 2019
eb91c79
added new test function
mganahl Dec 2, 2019
a544dbc
testing function hacking
mganahl Dec 2, 2019
0457cca
docstring
mganahl Dec 2, 2019
95958a7
small speed up
mganahl Dec 3, 2019
ac3d980
Remove gui directory (migrated to another repo) (#399)
coryell Dec 3, 2019
5d2d2ba
a slightly more elegant code
mganahl Dec 7, 2019
04eadf3
use one more np function
mganahl Dec 7, 2019
2ea5674
removed some crazy slow code
mganahl Dec 7, 2019
5d8c86a
faster code
mganahl Dec 7, 2019
22a642e
Merge remote-tracking branch 'upstream/experimental_blocksparse' into…
mganahl Dec 7, 2019
4eae410
Update README.md (#404)
Dec 9, 2019
04c8573
add return_data
mganahl Dec 9, 2019
7c2d5a0
Merge remote-tracking branch 'upstream/experimental_blocksparse' into…
mganahl Dec 9, 2019
29bb154
Merge remote-tracking branch 'upstream/master' into block_sparse
mganahl Dec 9, 2019
33d1a40
doc
mganahl Dec 9, 2019
fb1978a
bug fix
mganahl Dec 9, 2019
5228f56
Merge remote-tracking branch 'upstream/experimental_blocksparse' into…
mganahl Dec 9, 2019
0d4a625
a little faster
mganahl Dec 11, 2019
82a4148
substantial speedup
mganahl Dec 11, 2019
7bd7be7
renaming
mganahl Dec 11, 2019
7a8c7df
Merge remote-tracking branch 'upstream/experimental_blocksparse' into…
mganahl Dec 11, 2019
d9c094b
removed todo
mganahl Dec 11, 2019
06c3f3c
some comments
mganahl Dec 11, 2019
426fd1a
comments
mganahl Dec 11, 2019
7f3e148
fixed some bug in reshape
mganahl Dec 11, 2019
19c3fe8
comments
mganahl Dec 11, 2019
5c8fd3e
default value changed
mganahl Dec 11, 2019
94c8c2c
fixed bug, old version is now faster again
mganahl Dec 12, 2019
7eec7f0
cleaned up reshape
mganahl Dec 12, 2019
c188ab9
started adding tests
mganahl Dec 12, 2019
d7ab7ab
Merge remote-tracking branch 'upstream/experimental_blocksparse' into…
mganahl Dec 12, 2019
d228f61
Merge remote-tracking branch 'upstream/experimental_blocksparse' into…
mganahl Dec 14, 2019
46aeec1
replace kron with broadcasting
mganahl Dec 14, 2019
c73a511
Merge remote-tracking branch 'upstream/experimental_blocksparse' into…
mganahl Dec 15, 2019
6844f2c
column-major -> row-major
mganahl Dec 15, 2019
4f4ba93
documentation
mganahl Dec 15, 2019
8a5dbfd
Merge remote-tracking branch 'upstream/experimental_blocksparse' into…
mganahl Dec 15, 2019
d583e2b
added function to compute unique charges and charge degeneracies
mganahl Dec 17, 2019
b9f45cb
improved block finding, fixed bug in reshape
mganahl Dec 18, 2019
69309eb
fuse_charge_pair added
mganahl Dec 18, 2019
5026ed3
use is_leave
mganahl Dec 18, 2019
8ada65d
new tests
mganahl Dec 18, 2019
11ab7c0
removed TODO, BlockSparseTensor.shape returns ref instead of copy
mganahl Dec 18, 2019
04c4028
added tests
mganahl Dec 18, 2019
e521d35
added tests
mganahl Dec 18, 2019
3fec7ba
column-major -> row-major
mganahl Dec 19, 2019
a6f91a6
fix broken tests
mganahl Dec 19, 2019
7f4f3ce
test added
mganahl Dec 19, 2019
86adb1b
mostly docstring
mganahl Dec 19, 2019
5657456
docstring
mganahl Dec 19, 2019
1a07b78
Merge remote-tracking branch 'upstream/experimental_blocksparse' into…
mganahl Dec 19, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 60 additions & 60 deletions tensornetwork/block_tensor/block_tensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,24 +160,35 @@ def compute_nonzero_block_shapes(charges: List[np.ndarray],
return charge_shape_dict


def retrieve_non_zero_diagonal_blocks_old_version(
def retrieve_non_zero_diagonal_blocks(
data: np.ndarray,
charges: List[np.ndarray],
flows: List[Union[bool, int]],
row_charges: List[Union[List, np.ndarray]],
column_charges: List[Union[List, np.ndarray]],
row_flows: List[Union[bool, int]],
column_flows: List[Union[bool, int]],
return_data: Optional[bool] = True) -> Dict:
"""
Deprecated: this version is about 2 times slower (worst case) than the current used
implementation
Given the meta data and underlying data of a symmetric matrix, compute
all diagonal blocks and return them in a dict.
`row_charges` and `column_charges` are lists of np.ndarray. The tensor
is viewed as a matrix with rows given by fusing `row_charges` and
columns given by fusing `column_charges`. Note that `column_charges`
are never explicitly fused (`row_charges` are).
Args:
data: An np.ndarray of the data. The number of elements in `data`
has to match the number of non-zero elements defined by `charges`
and `flows`
charges: List of np.ndarray, one for each leg.
Each np.ndarray `charges[leg]` is of shape `(D[leg],)`.
row_charges: List of np.ndarray, one for each leg of the row-indices.
Each np.ndarray `row_charges[leg]` is of shape `(D[leg],)`.
The bond dimension `D[leg]` can vary on each leg.
flows: A list of integers, one for each leg,
column_charges: List of np.ndarray, one for each leg of the column-indices.
Each np.ndarray `row_charges[leg]` is of shape `(D[leg],)`.
The bond dimension `D[leg]` can vary on each leg.
row_flows: A list of integers, one for each entry in `row_charges`.
with values `1` or `-1`, denoting the flow direction
of the charges on each leg. `1` is inflowing, `-1` is outflowing
charge.
column_flows: A list of integers, one for each entry in `column_charges`.
with values `1` or `-1`, denoting the flow direction
of the charges on each leg. `1` is inflowing, `-1` is outflowing
charge.
Expand All @@ -193,20 +204,25 @@ def retrieve_non_zero_diagonal_blocks_old_version(
dict: Dictionary mapping quantum numbers (integers) to either an np.ndarray
or a python list of locations and shapes, depending on the value of `return_data`.
"""
if len(charges) != 2:
raise ValueError("input has to be a two-dimensional symmetric matrix")
flows = row_flows.copy()
flows.extend(column_flows)
check_flows(flows)
if len(flows) != len(charges):
raise ValueError("`len(flows)` is different from `len(charges)`")
if len(flows) != (len(row_charges) + len(column_charges)):
raise ValueError(
"`len(flows)` is different from `len(row_charges) + len(column_charges)`"
)

#we multiply the flows into the charges
row_charges = flows[0] * charges[0] # a list of charges on each row
column_charges = flows[1] * charges[1] # a list of charges on each column
#since we are using row-major we have to fuse the row charges anyway.
fused_row_charges = fuse_charges(row_charges, row_flows)
#get the unique row-charges
unique_row_charges, row_dims = np.unique(
fused_row_charges, return_counts=True)

#get the unique charges
unique_row_charges, row_dims = np.unique(row_charges, return_counts=True)
unique_column_charges, column_dims = np.unique(
column_charges, return_counts=True)
#get the unique column-charges
#we only care about their degeneracies, not their order; that's much faster
#to compute since we don't have to fuse all charges explicitly
unique_column_charges, column_dims = compute_fused_charge_degeneracies(
column_charges, column_flows)
#get the charges common to rows and columns (only those matter)
common_charges = np.intersect1d(
unique_row_charges, -unique_column_charges, assume_unique=True)
Expand All @@ -217,8 +233,8 @@ def retrieve_non_zero_diagonal_blocks_old_version(
column_degeneracies = dict(zip(unique_column_charges, column_dims))

# we only care about charges common to row and columns
mask = np.isin(row_charges, common_charges)
relevant_row_charges = row_charges[mask]
mask = np.isin(fused_row_charges, common_charges)
relevant_row_charges = fused_row_charges[mask]

#some numpy magic to get the index locations of the blocks
#we generate a vector of `len(relevant_row_charges) which,
Expand Down Expand Up @@ -261,35 +277,24 @@ def retrieve_non_zero_diagonal_blocks_old_version(
return blocks


def retrieve_non_zero_diagonal_blocks(
def retrieve_non_zero_diagonal_blocks_old_version(
data: np.ndarray,
row_charges: List[Union[List, np.ndarray]],
column_charges: List[Union[List, np.ndarray]],
row_flows: List[Union[bool, int]],
column_flows: List[Union[bool, int]],
charges: List[np.ndarray],
flows: List[Union[bool, int]],
return_data: Optional[bool] = True) -> Dict:
"""
Deprecated: this version is about 2 times slower (worst case) than the current used
implementation
Given the meta data and underlying data of a symmetric matrix, compute
all diagonal blocks and return them in a dict.
`row_charges` and `column_charges` are lists of np.ndarray. The tensor
is viewed as a matrix with rows given by fusing `row_charges` and
columns given by fusing `column_charges`. Note that `column_charges`
are never explicitly fused (`row_charges` are).
Args:
data: An np.ndarray of the data. The number of elements in `data`
has to match the number of non-zero elements defined by `charges`
and `flows`
row_charges: List of np.ndarray, one for each leg of the row-indices.
Each np.ndarray `row_charges[leg]` is of shape `(D[leg],)`.
The bond dimension `D[leg]` can vary on each leg.
column_charges: List of np.ndarray, one for each leg of the column-indices.
Each np.ndarray `row_charges[leg]` is of shape `(D[leg],)`.
charges: List of np.ndarray, one for each leg.
Each np.ndarray `charges[leg]` is of shape `(D[leg],)`.
The bond dimension `D[leg]` can vary on each leg.
row_flows: A list of integers, one for each entry in `row_charges`.
with values `1` or `-1`, denoting the flow direction
of the charges on each leg. `1` is inflowing, `-1` is outflowing
charge.
column_flows: A list of integers, one for each entry in `column_charges`.
flows: A list of integers, one for each leg,
with values `1` or `-1`, denoting the flow direction
of the charges on each leg. `1` is inflowing, `-1` is outflowing
charge.
Expand All @@ -305,25 +310,20 @@ def retrieve_non_zero_diagonal_blocks(
dict: Dictionary mapping quantum numbers (integers) to either an np.ndarray
or a python list of locations and shapes, depending on the value of `return_data`.
"""
flows = row_flows.copy()
flows.extend(column_flows)
if len(charges) != 2:
raise ValueError("input has to be a two-dimensional symmetric matrix")
check_flows(flows)
if len(flows) != (len(row_charges) + len(column_charges)):
raise ValueError(
"`len(flows)` is different from `len(row_charges) + len(column_charges)`"
)
if len(flows) != len(charges):
raise ValueError("`len(flows)` is different from `len(charges)`")

#since we are using row-major we have to fuse the row charges anyway.
fused_row_charges = fuse_charges(row_charges, row_flows)
#get the unique row-charges
unique_row_charges, row_dims = np.unique(
fused_row_charges, return_counts=True)
#we multiply the flows into the charges
row_charges = flows[0] * charges[0] # a list of charges on each row
column_charges = flows[1] * charges[1] # a list of charges on each column

#get the unique column-charges
#we only care about their degeneracies, not their order; that's much faster
#to compute since we don't have to fuse all charges explicitly
unique_column_charges, column_dims = compute_fused_charge_degeneracies(
column_charges, column_flows)
#get the unique charges
unique_row_charges, row_dims = np.unique(row_charges, return_counts=True)
unique_column_charges, column_dims = np.unique(
column_charges, return_counts=True)
#get the charges common to rows and columns (only those matter)
common_charges = np.intersect1d(
unique_row_charges, -unique_column_charges, assume_unique=True)
Expand All @@ -334,8 +334,8 @@ def retrieve_non_zero_diagonal_blocks(
column_degeneracies = dict(zip(unique_column_charges, column_dims))

# we only care about charges common to row and columns
mask = np.isin(fused_row_charges, common_charges)
relevant_row_charges = fused_row_charges[mask]
mask = np.isin(row_charges, common_charges)
relevant_row_charges = row_charges[mask]

#some numpy magic to get the index locations of the blocks
#we generate a vector of `len(relevant_row_charges) which,
Expand Down Expand Up @@ -585,8 +585,8 @@ def compute_mapping_table(charges: List[np.ndarray],
# is moving quickest when iterating through the linear data
# transposing is done taking, for each value of the indices i_0 to i_N-2
# the junk i_N-1 that gives non-zero
tables = np.meshgrid([np.arange(c.shape[0]) for c in charges], indexing='ij')
tables = tables[::-1] #reverse the order

#for example
raise NotImplementedError()


Expand Down
32 changes: 32 additions & 0 deletions tensornetwork/block_tensor/block_tensor_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import numpy as np
import pytest
# pylint: disable=line-too-long
from tensornetwork.block_tensor.block_tensor import BlockSparseTensor, compute_num_nonzero
from index import Index

np_dtypes = [np.float32, np.float16, np.float64, np.complex64, np.complex128]


@pytest.mark.parametrize("dtype", np_dtypes)
def test_block_sparse_init(dtype):
D = 10 #bond dimension
B = 10 #number of blocks
rank = 4
flows = np.asarray([1 for _ in range(rank)])
flows[-2::] = -1
charges = [
np.random.randint(-B // 2, B // 2 + 1, D).astype(np.int16)
for _ in range(rank)
]
indices = [
Index(charges=charges[n], flow=flows[n], name='index{}'.format(n))
for n in range(rank)
]
num_elements = compute_num_nonzero([i.charges for i in indices],
[i.flow for i in indices])
A = BlockSparseTensor.random(indices=indices, dtype=dtype)
assert A.dtype == dtype
for r in range(rank):
assert A.indices[r].name == 'index{}'.format(r)
assert A.dense_shape == tuple([D] * rank)
assert len(A.data) == num_elements
25 changes: 13 additions & 12 deletions tensornetwork/block_tensor/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,10 @@ def fuse_charge_pair(q1: Union[List, np.ndarray], flow1: int,
for U(1) charges). `q1` and `q2` typically belong to two consecutive
legs of `BlockSparseTensor`.
Given `q1 = [0,1,2]` and `q2 = [10,100]`, this returns
`[10, 11, 12, 100, 101, 102]`.
`[10, 100, 11, 101, 12, 102]`.
When using row-major ordering of indices in `BlockSparseTensor`,
the position of q1 should be "to the left" of the position of q2.

Args:
q1: Iterable of integers
flow1: Flow direction of charge `q1`.
Expand All @@ -142,15 +143,17 @@ def fuse_charge_pair(q1: Union[List, np.ndarray], flow1: int,
np.ndarray: The result of fusing `q1` with `q2`.
"""
return np.reshape(
flow2 * np.asarray(q2)[:, None] + flow1 * np.asarray(q1)[None, :],
flow1 * np.asarray(q1)[:, None] + flow2 * np.asarray(q2)[None, :],
len(q1) * len(q2))


def fuse_charges(charges: List[Union[List, np.ndarray]],
flows: List[int]) -> np.ndarray:
"""
Fuse all `charges` by simple addition (valid
for U(1) charges).
for U(1) charges). Charges are fused from "right to left",
in accordance with row-major order (see `fuse_charges_pair`).

Args:
chargs: A list of charges to be fused.
flows: A list of flows, one for each element in `charges`.
Expand All @@ -173,19 +176,17 @@ def fuse_degeneracies(degen1: Union[List, np.ndarray],
Fuse degeneracies `degen1` and `degen2` of two leg-charges
by simple kronecker product. `degen1` and `degen2` typically belong to two
consecutive legs of `BlockSparseTensor`.
Given `q1 = [0,1,2]` and `q2 = [10,100]`, this returns
`[10, 11, 12, 100, 101, 102]`.
Given `degen1 = [1, 2, 3]` and `degen2 = [10, 100]`, this returns
`[10, 100, 20, 200, 30, 300]`.
When using row-major ordering of indices in `BlockSparseTensor`,
the position of q1 should be "to the left" of the position of q2.
the position of `degen1` should be "to the left" of the position of `degen2`.
Args:
q1: Iterable of integers
flow1: Flow direction of charge `q1`.
q2: Iterable of integers
flow2: Flow direction of charge `q2`.
degen1: Iterable of integers
degen2: Iterable of integers
Returns:
np.ndarray: The result of fusing `q1` with `q2`.
np.ndarray: The result of fusing `dege1` with `degen2`.
"""
return np.reshape(degen2[:, None] * degen1[None, :],
return np.reshape(degen1[:, None] * degen2[None, :],
len(degen1) * len(degen2))


Expand Down
Loading