Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "Tensor Network and Structure Matrix Validation" #13

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
35 changes: 1 addition & 34 deletions src/tnsu/structure_matrix_constructor.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import numpy as np

"""
A module for Structure Matrices construction.
A File for Structure Matrices constructions.
"""


Expand Down Expand Up @@ -143,36 +143,3 @@ def rectangular_peps_obc(height: int, width: int):
new_row = new_row[order]
structure_matrix[i, np.nonzero(structure_matrix[i, :])[0]] = new_row
return structure_matrix


def is_valid(structure_matrix: np.ndarray) -> bool:
try:
if len(structure_matrix.shape) != 2:
print(
f"structure_matrix must be a matrix, " f"instead got a {len(structure_matrix.shape)} dimension tensor."
)
return False
n, m = structure_matrix.shape
for i in range(n):
row = structure_matrix[i, :]
row = row[row > 0]
sorted_row = np.sort(row)
len_row = len(row)
expected_row_values = np.arange(1, len_row + 1)
if not np.all(sorted_row == expected_row_values):
expected_str = "-".join([str(num) for num in expected_row_values])
actual_str = "-".join([str(num) for num in sorted_row])
print(
f"Error in structure_matrix given. For row [{i}] "
f"expected values are {expected_str}, instead got {actual_str}."
)
return False
for j in range(m):
column = structure_matrix[:, j]
if np.sum(column > 0) != 2:
print(f"Weight vector [{j}] is not connected to two tensors.")
return False
return True
except Exception as e:
print(f"Failed with unexpected behavior. {e}")
return False
166 changes: 90 additions & 76 deletions src/tnsu/tensor_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

from typing import TypedDict, Optional
import pathlib
from tnsu.structure_matrix_constructor import is_valid

DEFAULT_NETWORKS_FOLDER = str(pathlib.Path(__file__).parent / "networks")

Expand All @@ -16,7 +15,7 @@ class _EdgesDict(TypedDict):


class TensorNetwork:
"""A Tensor-Network object as used in the field of Quantum Information and Quantum Computation"""
"""A Tensor-Network object. Used in the field of Quantum Information and Quantum Computation"""

def __init__(
self,
Expand Down Expand Up @@ -70,50 +69,115 @@ def __init__(
f"Spin dimension should be an integer larger than 0. " f"Instead got {spin_dim}."
)

# Verify the structure matrix is legit
assert is_valid(structure_matrix), "Got an invalid structure matrix."
# verify the structure matrix is legit
assert len(structure_matrix.shape) == 2, (
f"The given structure_matrix have {len(structure_matrix.shape)} " f"dimensions, instead of 2."
)
n, m = structure_matrix.shape
for i in range(n):
row = structure_matrix[i, :]
row = row[row > 0]
assert len(set(row)) == len(row), (
f"Error in structure_matrix given. There are two different weights "
f"connected to the same dimension in tensor [{i}]."
)
for j in range(m):
column = structure_matrix[:, j]
assert np.sum(column > 0) == 2, f"Weight vector [{j}] is not connected to two tensors."

# Handle the tensors
if tensors is not None:
assert n == len(tensors), (
f"Num of rows in structure_matrix is "
f"{n}, while num of tensors is "
f"{len(tensors)}. They should be equal, a row for each tensor."
)
# generate a list of uniform weights in case didn't get one as an input
if weights is None:
weights = [0] * m
for j in range(m):
for i in range(n):
if structure_matrix[i, j] > 0:
break
weight_dim = tensors[i].shape[structure_matrix[i, j]]
weights[j] = np.ones(weight_dim, dtype=np.float) / weight_dim

# generate a random gaussian tensors list in case didn't get one as input
n, m = structure_matrix.shape
if tensors is None:
tensors = []
new_tensor = None
else:
tensors = [0] * n
for i in range(n):
tensor_shape = [spin_dim] + [virtual_dim] * int(np.sum(structure_matrix[i, :] > 0))

# initialize with random real values
tensor_shape = [spin_dim] + [0] * np.sum(structure_matrix[i, :] > 0)
for j in range(m):
if structure_matrix[i, j] > 0:
assert structure_matrix[i, j] <= len(tensor_shape) - 1, (
f"structure_matrix[{i}, {j}] = "
f"{structure_matrix[i, j]} while "
f"it should have been "
f"<= {len(tensor_shape) - 1}."
)
if weights is not None:
tensor_shape[structure_matrix[i, j]] = len(weights[j])
else:
tensor_shape[structure_matrix[i, j]] = virtual_dim
if real_init and not imag_init:
new_tensor = np.random.normal(
tensors[i] = np.random.normal(
loc=random_init_real_loc * np.ones(tensor_shape),
scale=random_init_real_scale,
)
# initialize with random imaginary values
elif imag_init and not real_init:
new_tensor = 1j * np.random.normal(
tensors[i] = 1j * np.random.normal(
loc=random_init_imag_loc * np.ones(tensor_shape),
scale=random_init_imag_scale,
)
# initialize with random complex values
elif real_init and imag_init:
new_tensor = np.random.normal(
tensors[i] = np.random.normal(
loc=random_init_real_loc * np.ones(tensor_shape),
scale=random_init_real_scale,
) + 1j * np.random.normal(
loc=random_init_imag_loc * np.ones(tensor_shape),
scale=random_init_imag_scale,
)
tensors.append(new_tensor)
else:
raise TypeError

# generate a list of uniform weights in case didn't get one as an input
if weights is None:
weights = []
for j in range(m):
for i in range(n):
if structure_matrix[i, j] > 0:
weight_dim = tensors[i].shape[structure_matrix[i, j]]
weights.append(np.ones(weight_dim, dtype=float) / weight_dim)
break # generate one weight vector per edge
# generate a weights list in case didn't get one
if weights is None:
weights = [0] * m
for j in range(m):
for i in range(n):
if structure_matrix[i, j] > 0:
break
weight_dim = tensors[i].shape[structure_matrix[i, j]]
weights[j] = np.ones(weight_dim) / weight_dim

assert m == len(weights), (
f"Num of columns in structure_matrix is "
f"{m}, while num of weights is "
f"{len(weights)}. They should be equal !"
)

# check the connectivity of each tensor in the generated tensor network
for i in range(n):
# all tensor virtual legs connected
assert len(tensors[i].shape) - 1 == np.sum(structure_matrix[i, :] > 0), (
f"tensor [{i}] is connected to {len(tensors[i].shape) - 1} "
f"weight vectors but have "
f"{np.sum(structure_matrix[i, :] > 0)} virtual dimensions."
)

# verify each neighboring tensors has identical interaction dimension to their shared weights
for i in range(n):
for j in range(m):
tensor_dim = structure_matrix[i, j]
if tensor_dim > 0:
assert tensors[i].shape[tensor_dim] == len(weights[j]), (
f"Dimension {tensor_dim} size of "
f"Tensor [{i}] is"
f" {tensors[i].shape[tensor_dim]}, "
f"while size of weight "
f"vector [{j}] is {len(weights[j])}. "
f"They should be equal !"
)
self.virtual_dim = virtual_dim
self.spin_dim = spin_dim
self.tensors = tensors
Expand All @@ -124,56 +188,6 @@ def __init__(
self.su_logger = None
self.state_dict = None

# Check validation of Tensor Network
assert self.is_valid(), "Invalid Tensor Network."

def is_valid(self):
n, m = self.structure_matrix.shape

# Verify tensors match structure matrix
if m != len(self.weights):
print(
f"Num of columns in structure_matrix is "
f"{m}, while num of weights is "
f"{len(self.weights)}. They should be equal !"
)
return False
if n != len(self.tensors):
print(
f"Num of rows in structure_matrix is "
f"{n}, while num of tensors is "
f"{len(self.tensors)}. They should be equal, a row for each tensor."
)
return False

# Check the connectivity of each tensor in the generated tensor network
for i in range(n):
# all tensor virtual legs connected
if len(self.tensors[i].shape) - 1 != np.sum(self.structure_matrix[i, :] > 0):
print(
f"tensor [{i}] is connected to {len(self.tensors[i].shape) - 1} "
f"weight vectors but have "
f"{np.sum(self.structure_matrix[i, :] > 0)} virtual dimensions."
)
return False

# Verify each neighboring tensors has identical interaction dimension to their shared weights
for i in range(n):
for j in range(m):
tensor_dim = self.structure_matrix[i, j]
if tensor_dim > 0:
if self.tensors[i].shape[tensor_dim] != len(self.weights[j]):
print(
f"Dimension {tensor_dim} size of "
f"Tensor [{i}] is"
f" {self.tensors[i].shape[tensor_dim]}, "
f"while size of weight "
f"vector [{j}] is {len(self.weights[j])}. "
f"They should be equal !"
)
return False
return True

def create_state_dict(self):
"""
Creates a state dictionary with all the Tensor Network object parameters
Expand Down
12 changes: 0 additions & 12 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,3 @@ def test_spin_operators():
assert np.sum(np.abs(sx_1 - sx_1_expected)) < 1e-12
assert np.sum(np.abs(sy_1 - sy_1_expected)) < 1e-12
assert np.sum(np.abs(sz_1 - sz_1_expected)) < 1e-12


def test_structure_matrix_validation():
fail_too_many_dimensions = np.array([[[1.0]]])
fail_repeated_indices = np.array([[1, 2, 3, 3, 0, 0], [0, 4, 0, 2, 1, 3], [3, 0, 1, 0, 4, 2]])
fail_higher_index = np.array([[1, 2, 3, 6, 0, 0], [0, 6, 0, 2, 1, 3], [3, 0, 1, 0, 6, 2]])
fail_edge_for_three = np.array([[1, 2, 3, 0, 4, 0], [0, 4, 0, 2, 1, 3], [3, 0, 1, 0, 4, 2]])
assert not smc.is_valid(fail_too_many_dimensions)
assert not smc.is_valid(fail_repeated_indices)
assert not smc.is_valid(fail_higher_index)
assert not smc.is_valid(fail_edge_for_three)
assert not smc.is_valid(np.inf)
Loading