def __init__(self, data: np.ndarray, charges: List[BaseCharge], flows: Union[np.ndarray, List[bool]], order: Optional[List[Union[List, np.ndarray]]] = None, check_consistency: Optional[bool] = False) -> None: """ Args: data: An np.ndarray containing the actual data. charges: A list of `BaseCharge` objects. flows: The flows of the tensor indices, `False` for inflowing, `True` for outflowing. order: An optional order argument, determining the shape and order of the tensor. check_consistency: If `True`, check if `len(data)` is consistent with number of non-zero elements given by the charges. This usually causes significant overhead, so use only for debugging. """ super().__init__(data=data, charges=charges, flows=flows, order=order, check_consistency=check_consistency) if check_consistency and (len(self._charges) > 0): num_non_zero_elements = compute_num_nonzero( self._charges, self._flows) if num_non_zero_elements != len(data.flat): raise ValueError("number of tensor elements {} defined " "by `charges` is different from" " len(data)={}".format( num_non_zero_elements, len(data.flat)))
def test_compute_num_nonzero(num_charges): np.random.seed(12) D = 40 qs = [np.random.randint(-3, 3, (D, num_charges)) for _ in range(3)] charges = [BaseCharge(q, charge_types=[U1Charge] * num_charges) for q in qs] flows = [False, True, False] np_flows = [1, -1, 1] fused = fuse_many_ndarray_charges([qs[n] * np_flows[n] for n in range(3)], [U1Charge] * num_charges) nz1 = compute_num_nonzero(charges, flows) #pylint: disable=no-member nz2 = len( np.nonzero( np.logical_and.reduce( fused == np.zeros((1, num_charges), dtype=np.int16), axis=1))[0]) assert nz1 == nz2
def zeros(cls, indices: Sequence[Index], dtype: Optional[Type[np.number]] = None) -> "BlockSparseTensor": """ Initialize a symmetric tensor with zeros. Args: indices: List of `Index` objects, one for each leg. dtype: An optional numpy dtype. The dtype of the tensor Returns: BlockSparseTensor """ charges, flows = get_flat_meta_data(indices) num_non_zero_elements = compute_num_nonzero(charges, flows) tmp = np.append(0, np.cumsum([len(i.flat_charges) for i in indices])) order = [list(np.arange(tmp[n], tmp[n + 1])) for n in range(len(tmp) - 1)] return cls( data=np.zeros((num_non_zero_elements,), dtype=dtype), charges=charges, flows=flows, order=order, check_consistency=False)
def outerproduct(tensor1: BlockSparseTensor, tensor2: BlockSparseTensor) -> BlockSparseTensor: """ Compute the outer product of two `BlockSparseTensor`. The first `tensor1.ndim` indices of the resulting tensor are the indices of `tensor1`, the last `tensor2.ndim` indices are those of `tensor2`. Args: tensor1: A tensor. tensor2: A tensor. Returns: BlockSparseTensor: The result of taking the outer product. """ final_charges = tensor1._charges + tensor2._charges final_flows = list(tensor1._flows) + list(tensor2._flows) order2 = [ list(np.asarray(s) + len(tensor1._charges)) for s in tensor2._order ] data = np.zeros(compute_num_nonzero(final_charges, final_flows), dtype=tensor1.dtype) if ((len(tensor1.data) > 0) and (len(tensor2.data) > 0)) and (len(data) > 0): # find the location of the zero block in the output final_block_maps, final_block_charges, _ = _find_diagonal_sparse_blocks( final_charges, final_flows, len(tensor1._charges)) index = np.nonzero( final_block_charges == final_block_charges.identity_charges( dim=1))[0][0] data[final_block_maps[index].ravel()] = np.outer( tensor1.data, tensor2.data).ravel() return BlockSparseTensor(data, charges=final_charges, flows=final_flows, order=tensor1._order + order2, check_consistency=False)