diff --git a/toqito/state_props/is_separable.py b/toqito/state_props/is_separable.py index 61e86756c..044931aed 100644 --- a/toqito/state_props/is_separable.py +++ b/toqito/state_props/is_separable.py @@ -67,15 +67,12 @@ def is_separable(state: np.ndarray, dim: None | int | list[int] = None, level: i state_len = state.shape[1] state_rank = np.linalg.matrix_rank(state) state = state / np.trace(state) - eps = np.finfo(float).eps if dim is None: dim = int(np.round(np.sqrt(state_len))) if isinstance(dim, int): dim = np.array([dim, state_len / dim]) # pylint: disable=redefined-variable-type - if np.abs(dim[1] - np.round(dim[1])) >= 2 * state_len * eps: - raise ValueError("The parameter `dim` must evenly divide the length of the state.") dim[1] = np.round(dim[1]) # nD, xD, pD diff --git a/toqito/state_props/tests/test_is_separable.py b/toqito/state_props/tests/test_is_separable.py index 97b24e142..1d34bf208 100644 --- a/toqito/state_props/tests/test_is_separable.py +++ b/toqito/state_props/tests/test_is_separable.py @@ -1,49 +1,75 @@ """Test is_separable.""" import numpy as np +import pytest from toqito.channels import partial_trace from toqito.matrix_props import is_density from toqito.rand import random_density_matrix from toqito.state_props.is_separable import is_separable -from toqito.states import basis, bell, isotropic, tile - - -def test_non_positive_semidefinite_matrix(): - """Ensure separability of non-positive semidefinite matrix is invalid.""" - with np.testing.assert_raises(ValueError): - state = np.array([[-1, -1], [-1, -1]]) - is_separable(state) - - -def test_psd_matrix_local_dim_one(): - """Every positive semidefinite matrix is separable when one of the local dimensions is 1.""" - np.testing.assert_equal(is_separable(np.identity(2)), True) - - -def test_invalid_dim_parameter(): - """The dimension of the state must evenly divide the length of the state.""" - with np.testing.assert_raises(ValueError): - dim = 3 - rho = isotropic(dim, 1 / (dim + 1)) - is_separable(rho, dim + 1) - - -def test_entangled_ppt_criterion(): - """Determined to be entangled via the PPT criterion.""" - rho = bell(0) * bell(0).conj().T - np.testing.assert_equal(is_separable(rho), False) - - -def test_ppt_small_dimensions(): - """Determined to be separable via sufficiency of the PPT criterion in small dimensions.""" - e_0, e_1, e_2 = basis(3, 0), basis(3, 1), basis(3, 2) - psi = 1 / np.sqrt(3) * e_0 + 1 / np.sqrt(3) * e_1 + 1 / np.sqrt(3) * e_2 - - e_0, e_1 = basis(2, 0), basis(2, 1) - phi = np.kron((1 / np.sqrt(2) * e_0 + 1 / np.sqrt(2) * e_1), psi) - sigma = phi * phi.conj().T - np.testing.assert_equal(is_separable(sigma), True) +from toqito.states import basis, bell, tile + + +@pytest.mark.parametrize( + "test_input", + [ + # Ensure separability of non-positive semidefinite matrix is invalid + (np.array([[-1, -1], [-1, -1]])), + ], +) +def test_werner_state_invalid(test_input): + """Test function works as expected for an invalid input.""" + with pytest.raises(ValueError): + is_separable(test_input) + + +p_var, a_var, b_var = 0.4, 0.8, 0.64 +rho_chen = np.array( + [ + [p_var * a_var**2, 0, 0, p_var * a_var * b_var], + [0, (1 - p_var) * a_var**2, (1 - p_var) * a_var * b_var, 0], + [0, (1 - p_var) * a_var * b_var, (1 - p_var) * a_var**2, 0], + [p_var * a_var * b_var, 0, 0, p_var * a_var**2], + ] +) + + +@pytest.mark.parametrize( + "test_input", + [ + # Determined to be entangled via the PPT criterion. + (bell(0) * bell(0).conj().T), + # Determined to be entangled by using Theorem 1 and Remark 1 of :cite:`Chen_2003_Matrix`. + (rho_chen), + ], +) +def test_not_is_separable(test_input): + """Check an expected non seperable test input is identified correctly.""" + assert not is_separable(test_input) + + +e_0, e_1, e_2 = basis(3, 0), basis(3, 1), basis(3, 2) +psi = 1 / np.sqrt(3) * e_0 + 1 / np.sqrt(3) * e_1 + 1 / np.sqrt(3) * e_2 + +e_0, e_1 = basis(2, 0), basis(2, 1) +phi = np.kron((1 / np.sqrt(2) * e_0 + 1 / np.sqrt(2) * e_1), psi) + + +@pytest.mark.parametrize( + "test_input", + [ + # Every positive semidefinite matrix is separable when one of the local dimensions is 1. + (np.identity(2)), + # Determined to be separable via sufficiency of the PPT criterion in small dimensions. + (phi * phi.conj().T), + ], +) +def test_is_separable(test_input): + """Check an expected seperable test input is identified correctly.""" + # without an input for the dimension + assert is_separable(test_input) + # wth int dim + assert is_separable(test_input, dim = 2) def test_ppt_low_rank(): @@ -56,12 +82,9 @@ def test_ppt_low_rank(): rho_cut = rho_cut / np.trace(rho_cut) pt_state_alice = partial_trace(rho_cut, [1], [3, 2]) - np.testing.assert_equal(is_density(rho_cut), True) - np.testing.assert_equal(is_density(np.array(pt_state_alice)), True) - np.testing.assert_equal( - np.linalg.matrix_rank(rho_cut) + np.linalg.matrix_rank(pt_state_alice) <= 2 * m * n - m - n + 2, - True, - ) + assert is_density(rho_cut) + assert is_density(np.array(pt_state_alice)) + assert np.linalg.matrix_rank(rho_cut) + np.linalg.matrix_rank(pt_state_alice) - 2 * m * n - m - n + 2 <= 1e-1 # TODO # np.testing.assert_equal(is_separable(rho), True) @@ -74,19 +97,5 @@ def test_entangled_realignment_criterion(): for i in range(5): rho = rho - tile(i) * tile(i).conj().T rho = rho / 4 - np.testing.assert_equal(is_density(rho), True) - np.testing.assert_equal(is_separable(rho), False) - - -def test_entangled_cross_norm_realignment_criterion(): - """Determined to be entangled by using Theorem 1 and Remark 1 of :cite:`Chen_2003_Matrix`.""" - p_var, a_var, b_var = 0.4, 0.8, 0.64 - rho = np.array( - [ - [p_var * a_var**2, 0, 0, p_var * a_var * b_var], - [0, (1 - p_var) * a_var**2, (1 - p_var) * a_var * b_var, 0], - [0, (1 - p_var) * a_var * b_var, (1 - p_var) * a_var**2, 0], - [p_var * a_var * b_var, 0, 0, p_var * a_var**2], - ] - ) - np.testing.assert_equal(is_separable(rho), False) + assert is_density(rho) + assert not is_separable(rho)