diff --git a/.coveragerc b/.coveragerc index d88c4c4f9..d3441069b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -10,7 +10,7 @@ show_missing = True precision = 2 omit = *__init__* - tests/* + *tests* virtualenvs/* [html] diff --git a/toqito/helper/tests/test_npa_constraints.py b/toqito/helper/tests/test_npa_hierarchy.py similarity index 94% rename from toqito/helper/tests/test_npa_constraints.py rename to toqito/helper/tests/test_npa_hierarchy.py index b55f74479..dc7d45959 100644 --- a/toqito/helper/tests/test_npa_constraints.py +++ b/toqito/helper/tests/test_npa_hierarchy.py @@ -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)]) @@ -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 diff --git a/toqito/matrix_ops/tensor.py b/toqito/matrix_ops/tensor.py index 120a4017d..d24ddf447 100644 --- a/toqito/matrix_ops/tensor.py +++ b/toqito/matrix_ops/tensor.py @@ -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: @@ -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] diff --git a/toqito/matrix_ops/tests/test_tensor.py b/toqito/matrix_ops/tests/test_tensor.py index 39c1d1561..e82c246b0 100644 --- a/toqito/matrix_ops/tests/test_tensor.py +++ b/toqito/matrix_ops/tests/test_tensor.py @@ -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()