Skip to content

Commit

Permalink
Pytest: toqito/matrix_ops/tensor.py (#319)
Browse files Browse the repository at this point in the history
* skip coverage from tests

* rename tests file in npa_constraints to npa_hierarchy

* use parametrize

* array inside list

* remove shape

* changes to the main func

* nit

* remove abs
  • Loading branch information
purva-thakre authored Dec 5, 2023
1 parent ca9f6f1 commit 70ecd26
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 181 deletions.
2 changes: 1 addition & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ show_missing = True
precision = 2
omit =
*__init__*
tests/*
*tests*
virtualenvs/*

[html]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def test_cglmp_inequality(k):
objective = cvxpy.Maximize(i_b)
problem = cvxpy.Problem(objective, npa)
val = problem.solve()
np.testing.assert_equal(np.allclose(val, 2.914, atol=1e-3), True)
assert pytest.approx(val, 1e-3) == 2.914


@pytest.mark.parametrize("k, expected_size", [("1+a", 9), ("1+ab", 25)])
Expand All @@ -78,4 +78,4 @@ def test_cglmp_dimension(k, expected_size):
for variable in problem.variables():
if variable.name() == "R":
r_size = variable.shape[0]
np.testing.assert_equal(np.allclose(r_size, expected_size), True)
assert r_size == expected_size
15 changes: 1 addition & 14 deletions toqito/matrix_ops/tensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def tensor(*args) -> np.ndarray:
result = None

# Input is provided as a list of numpy matrices.
if len(args) == 1 and isinstance(args[0], list):
if (len(args) == 1 and isinstance(args[0], list)) or (len(args) == 1 and isinstance(args[0], np.ndarray)):
if len(args[0]) == 1:
return args[0][0]
if len(args[0]) == 2:
Expand All @@ -130,19 +130,6 @@ def tensor(*args) -> np.ndarray:
result = np.kron(result, args[0][i])
return result

if len(args) == 1 and isinstance(args[0], np.ndarray):
# If the numpy array is just a single matrix, so the dimensions are
# provided as an (x, y)-tuple.
if len(args[0].shape) == 2:
return args[0]
if len(args[0]) == 2:
return np.kron(args[0][0], args[0][1])
if len(args[0]) >= 3:
result = args[0][0]
for i in range(1, len(args[0])):
result = np.kron(result, args[0][i])
return result

# Tensor product one matrix `n` times with itself.
if len(args) == 2 and isinstance(args[1], int):
num_tensor = args[1]
Expand Down
218 changes: 54 additions & 164 deletions toqito/matrix_ops/tests/test_tensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,174 +5,64 @@
from toqito.matrix_ops import tensor
from toqito.states import basis

e_0, e_1 = basis(2, 0), basis(2, 1)
matrix1 = np.array([[1, 2]])
matrix2 = np.array([[3], [4]])
matrix3 = np.array([[5, 6]])
matrix4 = np.array([[7, 8]])

@pytest.mark.parametrize("test_input, len_input, expected", [
# standard tensor product on vectors
((e_0, e_0), 2, np.kron(e_0, e_0)),
# tensor product of 1 item to should return the item
([np.array([[1, 2], [3, 4]])], 1, np.array([[1, 2], [3, 4]])),
# tensor product of multiple args as input
((np.identity(2), np.identity(2), np.identity(2), np.identity(2)), 4, np.identity(16)),
# tensor product of array of 2 arrays
(np.array([np.array([[1, 2], [3, 4]]), np.array([[5, 6], [7, 8]])]), 1, np.array(
[[5, 6, 10, 12], [7, 8, 14, 16], [15, 18, 20, 24], [21, 24, 28, 32]])),
# tensor product of vector with n = 0
((e_0, 0), 2, None),
# tensor product of vector with n = 1
((e_0, 1), 2, e_0),
# tensor product of vector with n = 2
((e_0, 2), 2, np.kron(e_0, e_0)),
# tensor product of vector with n = 3
((e_0, 3), 2, np.kron(np.kron(e_0, e_0), e_0)),
# tensor product of empty list
([], 1, None),
# tensor product of list with one item
([e_0], 1, e_0),
# tensor product of list with two items
([e_0, e_1], 1, np.kron(e_0, e_1)),
# tensor product of list with three items
([e_0, e_1, e_0], 1, np.kron(np.kron(e_0, e_1), e_0)),
# tensor product of array of 3 arrays of identity matrices
(np.array([np.identity(2), np.identity(2), np.identity(2)]), 1 , np.identity(8)),
# ((np.array([np.identity(2), np.identity(2), np.identity(2)])), 1, np.identity(8)),
# tensor product of array of 4 arrays of identity matrices
(np.array([np.identity(2), np.identity(2), np.identity(2), np.identity(2)]), 1, np.identity(16)),
# tensor product with a numpy array containing three or more matrices
(np.array([matrix1, matrix2, matrix3, matrix4], dtype=object), 1, np.kron(
np.kron(matrix1, np.kron(matrix2, matrix3)), matrix4)),
# tensor product of 1 matrix inside a list
([np.array([np.identity(4)])], 1, np.identity(4))])
def test_tensor_multiple_input(test_input, len_input, expected):
"""Test function works as expected."""
if len_input == 1:
calculated = tensor(test_input)
assert calculated is expected or (calculated == expected).all()
elif len_input == 2:
calculated = tensor(test_input[0], test_input[1])
assert calculated is expected or (calculated == expected).all()
elif len_input == 4:
calculated = tensor(test_input[0], test_input[1], test_input[2], test_input[3])
assert (calculated == expected).all()

def test_tensor():
"""Test standard tensor on vectors."""
e_0 = basis(2, 0)
expected_res = np.kron(e_0, e_0)

res = tensor(e_0, e_0)

bool_mat = np.isclose(res, expected_res)
np.testing.assert_equal(np.all(bool_mat), True)


def test_tensor_single_arg():
"""Performing tensor product on one item should return item back."""
input_arr = np.array([[1, 2], [3, 4]])
res = tensor(input_arr)

bool_mat = np.isclose(res, input_arr)
np.testing.assert_equal(np.all(bool_mat), True)


def test_tensor_array_of_numpy_arrays_two():
"""Performing tensor product on two numpy array of numpy arrays."""
input_arr = np.array([np.array([[1, 2], [3, 4]]), np.array([[5, 6], [7, 8]])])
res = tensor(input_arr)

expected_res = np.array([[5, 6, 10, 12], [7, 8, 14, 16], [15, 18, 20, 24], [21, 24, 28, 32]])

bool_mat = np.isclose(res, expected_res)
np.testing.assert_equal(np.all(bool_mat), True)


def test_tensor_array_of_numpy_arrays_three():
"""Performing tensor product on three numpy array of numpy arrays."""
input_arr = np.array([np.identity(2), np.identity(2), np.identity(2)])
res = tensor(input_arr)

expected_res = np.identity(8)

bool_mat = np.isclose(res, expected_res)
np.testing.assert_equal(np.all(bool_mat), True)


def test_tensor_array_of_numpy_arrays_four():
"""Performing tensor product on four numpy array of numpy arrays."""
input_arr = np.array([np.identity(2), np.identity(2), np.identity(2), np.identity(2)])
res = tensor(input_arr)

expected_res = np.identity(16)

bool_mat = np.isclose(res, expected_res)
np.testing.assert_equal(np.all(bool_mat), True)


def test_tensor_multiple_args():
"""Performing tensor product on multiple matrices."""
input_arr_1 = np.identity(2)
input_arr_2 = np.identity(2)
input_arr_3 = np.identity(2)
input_arr_4 = np.identity(2)
res = tensor(input_arr_1, input_arr_2, input_arr_3, input_arr_4)

bool_mat = np.isclose(res, np.identity(16))
np.testing.assert_equal(np.all(bool_mat), True)


def test_tensor_n_0():
"""Test tensor n=0 times."""
e_0 = basis(2, 0)
expected_res = None

res = tensor(e_0, 0)
np.testing.assert_equal(res, expected_res)


def test_tensor_n_1():
"""Test tensor n=1 times."""
e_0 = basis(2, 0)
expected_res = e_0

res = tensor(e_0, 1)
bool_mat = np.isclose(res, expected_res)
np.testing.assert_equal(np.all(bool_mat), True)


def test_tensor_n_2():
"""Test tensor n=2 times."""
e_0 = basis(2, 0)
expected_res = np.kron(e_0, e_0)

res = tensor(e_0, 2)
bool_mat = np.isclose(res, expected_res)
np.testing.assert_equal(np.all(bool_mat), True)


def test_tensor_n_3():
"""Test tensor n=3 times."""
e_0 = basis(2, 0)
expected_res = np.kron(np.kron(e_0, e_0), e_0)

res = tensor(e_0, 3)
bool_mat = np.isclose(res, expected_res)
np.testing.assert_equal(np.all(bool_mat), True)


def test_tensor_list_0():
"""Test tensor empty list."""
expected_res = None

res = tensor([])
np.testing.assert_equal(res, expected_res)


def test_tensor_list_1():
"""Test tensor list with one item."""
e_0 = basis(2, 0)
expected_res = e_0

res = tensor([e_0])

bool_mat = np.isclose(res, expected_res)
np.testing.assert_equal(np.all(bool_mat), True)


def test_tensor_list_2():
"""Test tensor list with two items."""
e_0, e_1 = basis(2, 0), basis(2, 1)
expected_res = np.kron(e_0, e_1)

res = tensor([e_0, e_1])

bool_mat = np.isclose(res, expected_res)
np.testing.assert_equal(np.all(bool_mat), True)


def test_tensor_list_3():
"""Test tensor list with three items."""
e_0, e_1 = basis(2, 0), basis(2, 1)
expected_res = np.kron(np.kron(e_0, e_1), e_0)

res = tensor([e_0, e_1, e_0])

bool_mat = np.isclose(res, expected_res)
np.testing.assert_equal(np.all(bool_mat), True)


def test_tensor_with_three_or_more_matrices():
"""Test tensor product with a numpy array containing three or more matrices."""
# Three matrices to be Kronecker multiplied
matrix1 = np.array([[1, 2]])
matrix2 = np.array([[3], [4]])
matrix3 = np.array([[5, 6]])
matrix4 = np.array([[7, 8]])

# The numpy array containing the matrices
matrices = np.array([matrix1, matrix2, matrix3, matrix4], dtype=object)

# Expected output: Kronecker product of matrix1, matrix2, and matrix3
expected_output = np.kron(np.kron(matrix1, np.kron(matrix2, matrix3)), matrix4)

# Call the tensor function
result = tensor(matrices)

# Assert that the result is as expected
np.testing.assert_array_equal(result, expected_output)


def test_tensor_empty_args():
r"""Test tensor with no arguments."""
with pytest.raises(ValueError):
with pytest.raises(ValueError, match = "The `tensor` function must take either a matrix or vector."):
tensor()

0 comments on commit 70ecd26

Please sign in to comment.