diff --git a/.github/workflows/build-test-actions.yml b/.github/workflows/build-test-actions.yml index ee5f2c22..fbb89081 100644 --- a/.github/workflows/build-test-actions.yml +++ b/.github/workflows/build-test-actions.yml @@ -39,7 +39,7 @@ jobs: python -m pip install --upgrade pip pip install poetry poetry install --no-interaction - - name: Code Analyzer Checks + - name: Additional linter/formatter checks run: | poetry run pylint --fail-under=10.0 toqito/ diff --git a/docs/conf.py b/docs/conf.py index 176310e7..50b1b124 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -43,19 +43,19 @@ "sphinx.ext.githubpages", "sphinx.ext.napoleon", "sphinxcontrib.bibtex", - "sphinx_wagtail_theme" + "sphinx_wagtail_theme", ] bibtex_bibfiles = ["books.bib", "articles.bib"] bibtex_default_style = "unsrt" suppress_warnings = ["bibtex.duplicate_label", "bibtex.duplicate_citation"] # we need to skip these warnigns because all the references appear twice, in a function docstring -# and on the references page. +# and on the references page. master_doc = "index" # autosummary_generate = True # autodoc_typehints = "none" -autoapi_dirs = ['../toqito'] -autoapi_type = 'python' +autoapi_dirs = ["../toqito"] +autoapi_type = "python" autoapi_ignore = [ "*/channel_metrics/tests/*", "*/rand/tests/*", @@ -74,8 +74,9 @@ "*/states/tests/*", "*/channel_props/tests/*", "*/measurement_ops/tests/*", - "*/measurement_props/tests/*"] -autodoc_typehints = 'description' + "*/measurement_props/tests/*", +] +autodoc_typehints = "description" autoapi_add_toctree_entry = False autoapi_keep_files = False @@ -94,7 +95,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'sphinx_wagtail_theme' +html_theme = "sphinx_wagtail_theme" html_logo = "figures/logo.svg" html_favicon = "figures/favicon.ico" @@ -106,9 +107,7 @@ # This github_url will point to the appropriate page in the default branch. # Ex: Getting Started -> https://github.com/vprusso/toqito/blob/master/docs/getting_started.rst -html_theme_options = dict( - github_url = "https://github.com/vprusso/toqito/blob/master/docs/" -) +html_theme_options = dict(github_url="https://github.com/vprusso/toqito/blob/master/docs/") # Show in footer when the docs were last updated. html_last_updated_fmt = "%b %d, %Y" diff --git a/pyproject.toml b/pyproject.toml index fb04c6c7..18b54d81 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,7 +87,6 @@ ignore = ["D407", "D203", "D213", "D416", "PLR0912", "PLR0911", "PLR0915", "PLR2 # PLR0915 -- Checks for functions or methods with too many statements. The default number is 50. # PLR2004 -- Checks if an equality or an inequality is compared to a variable or a num (prefers variable pre) # PLR0913 Too many arguments in function definition - [tool.ruff.lint.per-file-ignores] "__init__.py" = ["I001"] # I001 - skip checking Import block is un-sorted or un-formatted diff --git a/setup.py b/setup.py index 5a060ded..4536bc8f 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ "pytest", "pytest-cov", "qiskit", - "scs" + "scs", ] setuptools.setup( diff --git a/toqito/channel_metrics/__init__.py b/toqito/channel_metrics/__init__.py index 79bb56d9..d8cf2f4d 100644 --- a/toqito/channel_metrics/__init__.py +++ b/toqito/channel_metrics/__init__.py @@ -1,4 +1,5 @@ """Distance metrics for quantum channels.""" + from toqito.channel_metrics.channel_fidelity import channel_fidelity from toqito.channel_metrics.diamond_norm import diamond_norm from toqito.channel_metrics.fidelity_of_separability import fidelity_of_separability diff --git a/toqito/channel_metrics/channel_fidelity.py b/toqito/channel_metrics/channel_fidelity.py index ccb32d5c..622690e1 100644 --- a/toqito/channel_metrics/channel_fidelity.py +++ b/toqito/channel_metrics/channel_fidelity.py @@ -1,4 +1,5 @@ """Compute the channel fidelity between two quantum channels.""" + import cvxpy import numpy as np diff --git a/toqito/channel_metrics/completely_bounded_spectral_norm.py b/toqito/channel_metrics/completely_bounded_spectral_norm.py index e61f9d32..0785ed74 100644 --- a/toqito/channel_metrics/completely_bounded_spectral_norm.py +++ b/toqito/channel_metrics/completely_bounded_spectral_norm.py @@ -1,4 +1,5 @@ """Compute the completely bounded spectral norm of a quantum channel.""" + import numpy as np from toqito.channel_metrics import completely_bounded_trace_norm diff --git a/toqito/channel_metrics/completely_bounded_trace_norm.py b/toqito/channel_metrics/completely_bounded_trace_norm.py index 7b3f6316..02d60094 100644 --- a/toqito/channel_metrics/completely_bounded_trace_norm.py +++ b/toqito/channel_metrics/completely_bounded_trace_norm.py @@ -1,4 +1,5 @@ """Compute the completely bounded trace norm of a quantum channel.""" + import cvxpy as cp import numpy as np @@ -41,9 +42,7 @@ def completely_bounded_trace_norm(phi: np.ndarray) -> float: dim_lx, dim_ly = phi.shape if dim_lx != dim_ly: - raise ValueError( - "The input and output spaces of the superoperator phi must both be square." - ) + raise ValueError("The input and output spaces of the superoperator phi must both be square.") if is_quantum_channel(phi): return 1 @@ -65,8 +64,7 @@ def completely_bounded_trace_norm(phi: np.ndarray) -> float: a_var = cp.bmat([[y0, -phi], [-phi.conj().T, y1]]) constraints += [a_var >> 0] objective = cp.Minimize( - cp.norm(cp.partial_trace(y0, dims=(dim, dim), axis=1)) - + cp.norm(cp.partial_trace(y1, dims=(dim, dim), axis=1)) + cp.norm(cp.partial_trace(y0, dims=(dim, dim), axis=1)) + cp.norm(cp.partial_trace(y1, dims=(dim, dim), axis=1)) ) problem = cp.Problem(objective, constraints) diff --git a/toqito/channel_metrics/diamond_norm.py b/toqito/channel_metrics/diamond_norm.py index 4a105cde..5c734e3d 100644 --- a/toqito/channel_metrics/diamond_norm.py +++ b/toqito/channel_metrics/diamond_norm.py @@ -1,4 +1,5 @@ """Compute the diamond norm between two quantum channels.""" + import cvxpy import numpy as np diff --git a/toqito/channel_metrics/fidelity_of_separability.py b/toqito/channel_metrics/fidelity_of_separability.py index 481e58e5..0a8be9e2 100644 --- a/toqito/channel_metrics/fidelity_of_separability.py +++ b/toqito/channel_metrics/fidelity_of_separability.py @@ -146,7 +146,7 @@ def fidelity_of_separability( # List of extenstion systems and dimension of the Choi matrix. sys_ext = list(range(2, 2 + k - 1)) - dim_choi = dim_r * (dim_a ** k) + dim_choi = dim_r * (dim_a**k) # Projection onto symmetric subspace on AA'. pi_sym = symmetric_projection(dim_a, 2) @@ -163,9 +163,7 @@ def fidelity_of_separability( pi_sym * picos.partial_trace( (picos.partial_transpose(psi, [0], psi_dims) @ picos.I(dim_a)) - * permute_systems( - choi_partial @ picos.I(dim_b * dim_a), [1, 4, 3, 2], dim_list - ), + * permute_systems(choi_partial @ picos.I(dim_b * dim_a), [1, 4, 3, 2], dim_list), [0, 2], dim_list, ) @@ -173,9 +171,7 @@ def fidelity_of_separability( ), ) - problem.add_constraint( - picos.partial_trace(choi, list(range(1, k + 1)), choi_dims) == picos.I(dim_r) - ) + problem.add_constraint(picos.partial_trace(choi, list(range(1, k + 1)), choi_dims) == picos.I(dim_r)) problem.add_constraint(choi >> 0) # k-extendablility of Choi state diff --git a/toqito/channel_metrics/tests/test_channel_fidelity.py b/toqito/channel_metrics/tests/test_channel_fidelity.py index 0148b3d1..a1b1339b 100644 --- a/toqito/channel_metrics/tests/test_channel_fidelity.py +++ b/toqito/channel_metrics/tests/test_channel_fidelity.py @@ -1,4 +1,5 @@ """Tests for channel_fidelity.""" + import numpy as np import pytest @@ -8,24 +9,36 @@ dephasing_channel = dephasing(4) depolarizing_channel = depolarizing(4) -@pytest.mark.parametrize("input1, input2, expected_value", [ - # fidelity of identical channels - (dephasing_channel, dephasing_channel, 1), - # fidelity of different channels - (dephasing_channel, depolarizing_channel, 1/2)]) + +@pytest.mark.parametrize( + "input1, input2, expected_value", + [ + # fidelity of identical channels + (dephasing_channel, dephasing_channel, 1), + # fidelity of different channels + (dephasing_channel, depolarizing_channel, 1 / 2), + ], +) def test_channel_fidelity(input1, input2, expected_value): """Test functions works as expected for valid inputs.""" calculated_value = channel_fidelity(input1, input2) assert pytest.approx(expected_value, 1e-3) == calculated_value -@pytest.mark.parametrize("input1, input2, expected_msg", [ - # Inconsistent dimensions between Choi matrices - (depolarizing_channel, depolarizing(2), "The Choi matrices provided should be of equal dimension."), - # Non-square inputs for channel fidelity - (np.array([[1, 2, 3], [4, 5, 6]]), np.array([[1, 2, 3], [4, 5, 6]]), "The Choi matrix provided must be square.")]) +@pytest.mark.parametrize( + "input1, input2, expected_msg", + [ + # Inconsistent dimensions between Choi matrices + (depolarizing_channel, depolarizing(2), "The Choi matrices provided should be of equal dimension."), + # Non-square inputs for channel fidelity + ( + np.array([[1, 2, 3], [4, 5, 6]]), + np.array([[1, 2, 3], [4, 5, 6]]), + "The Choi matrix provided must be square.", + ), + ], +) def test_channel_fidelity_raises_error(input1, input2, expected_msg): """Test functions works as expected for valid inputs.""" - with pytest.raises(ValueError, match = expected_msg): + with pytest.raises(ValueError, match=expected_msg): channel_fidelity(input1, input2) - diff --git a/toqito/channel_metrics/tests/test_completely_bounded_spectral_norm.py b/toqito/channel_metrics/tests/test_completely_bounded_spectral_norm.py index 4da5d409..2c0ec6b7 100644 --- a/toqito/channel_metrics/tests/test_completely_bounded_spectral_norm.py +++ b/toqito/channel_metrics/tests/test_completely_bounded_spectral_norm.py @@ -1,6 +1,5 @@ """Tests for completely_bounded_spectral_norm.""" - from toqito.channel_metrics import ( completely_bounded_spectral_norm, completely_bounded_trace_norm, diff --git a/toqito/channel_metrics/tests/test_completely_bounded_trace_norm.py b/toqito/channel_metrics/tests/test_completely_bounded_trace_norm.py index 5cbd533e..46b1565e 100644 --- a/toqito/channel_metrics/tests/test_completely_bounded_trace_norm.py +++ b/toqito/channel_metrics/tests/test_completely_bounded_trace_norm.py @@ -1,4 +1,5 @@ """Tests for completely_bounded_trace_norm.""" + import numpy as np import pytest @@ -14,13 +15,17 @@ diameter = np.max(dist) -@pytest.mark.parametrize("test_input, expected", [ - # The diamond norm of a quantum channel is 1 - (dephasing(2), 1), - # the diamond norm of a CP map - (np.eye(4), 4.0), - # unitaries channel, phi, diameter in terms of the eigenvalues of U - (phi, diameter)]) +@pytest.mark.parametrize( + "test_input, expected", + [ + # The diamond norm of a quantum channel is 1 + (dephasing(2), 1), + # the diamond norm of a CP map + (np.eye(4), 4.0), + # unitaries channel, phi, diameter in terms of the eigenvalues of U + (phi, diameter), + ], +) def test_cb_trace_norm(test_input, expected): """Test function works as expected for valid inputs.""" calculated_value = completely_bounded_trace_norm(test_input) diff --git a/toqito/channel_metrics/tests/test_diamond_norm.py b/toqito/channel_metrics/tests/test_diamond_norm.py index b01fba4c..004536ca 100644 --- a/toqito/channel_metrics/tests/test_diamond_norm.py +++ b/toqito/channel_metrics/tests/test_diamond_norm.py @@ -1,4 +1,5 @@ """Tests for diamond_norm.""" + import numpy as np import pytest @@ -6,24 +7,35 @@ from toqito.channels import dephasing, depolarizing -@pytest.mark.parametrize("test_input1, test_input_2, expected", [ - # The diamond norm of identical channels should yield 0 - (dephasing(2), dephasing(2), 0), - # the diamond norm of different channels - (dephasing(2), depolarizing(2), 1)]) +@pytest.mark.parametrize( + "test_input1, test_input_2, expected", + [ + # The diamond norm of identical channels should yield 0 + (dephasing(2), dephasing(2), 0), + # the diamond norm of different channels + (dephasing(2), depolarizing(2), 1), + ], +) def test_diamond_norm_valid_inputs(test_input1, test_input_2, expected): """Test function works as expected for valid inputs.""" calculated_value = diamond_norm(test_input1, test_input_2) assert pytest.approx(expected, 1e-3) == calculated_value -@pytest.mark.parametrize("test_input1, test_input_2, expected_msg", [ - # Inconsistent dimensions between Choi matrices - (depolarizing(4), dephasing(2), "The Choi matrices provided should be of equal dimension."), - # Non-square inputs for diamond norm - (np.array([[1, 2, 3], [4, 5, 6]]), np.array([[1, 2, 3], [4, 5, 6]]), "The Choi matrix provided must be square.")]) +@pytest.mark.parametrize( + "test_input1, test_input_2, expected_msg", + [ + # Inconsistent dimensions between Choi matrices + (depolarizing(4), dephasing(2), "The Choi matrices provided should be of equal dimension."), + # Non-square inputs for diamond norm + ( + np.array([[1, 2, 3], [4, 5, 6]]), + np.array([[1, 2, 3], [4, 5, 6]]), + "The Choi matrix provided must be square.", + ), + ], +) def test_diamond_norm_invalid_inputs(test_input1, test_input_2, expected_msg): """Test function raises error as expected for invalid inputs.""" - with pytest.raises(ValueError, match = expected_msg): + with pytest.raises(ValueError, match=expected_msg): diamond_norm(test_input1, test_input_2) - diff --git a/toqito/channel_metrics/tests/test_fidelity_of_separability.py b/toqito/channel_metrics/tests/test_fidelity_of_separability.py index 366f454b..cb97a67f 100644 --- a/toqito/channel_metrics/tests/test_fidelity_of_separability.py +++ b/toqito/channel_metrics/tests/test_fidelity_of_separability.py @@ -27,13 +27,18 @@ mixed_rho = max_mixed(8, is_sparse=False) -@pytest.mark.parametrize("test_input, input_dim, expected_msg, expected_error", [ - (bad_rho, [2, 2, 2], "Provided input state is not a density matrix.", ValueError), - (purification_state, [2, 2], "For Channel SDP: require tripartite state dims.", AssertionError), - (mixed_rho, [2, 2, 2], "This function only works for pure states.", ValueError)]) + +@pytest.mark.parametrize( + "test_input, input_dim, expected_msg, expected_error", + [ + (bad_rho, [2, 2, 2], "Provided input state is not a density matrix.", ValueError), + (purification_state, [2, 2], "For Channel SDP: require tripartite state dims.", AssertionError), + (mixed_rho, [2, 2, 2], "This function only works for pure states.", ValueError), + ], +) def test_errors_channel_SDP(test_input, input_dim, expected_msg, expected_error): """Tests for raised errors in channel SDP function.""" - with pytest.raises(expected_error, match = expected_msg): + with pytest.raises(expected_error, match=expected_msg): fidelity_of_separability(test_input, input_dim) diff --git a/toqito/channel_ops/__init__.py b/toqito/channel_ops/__init__.py index f35ef92b..3583867e 100644 --- a/toqito/channel_ops/__init__.py +++ b/toqito/channel_ops/__init__.py @@ -1,4 +1,5 @@ """A number of operations on quantum channels.""" + from toqito.channel_ops.apply_channel import apply_channel from toqito.channel_ops.partial_channel import partial_channel from toqito.channel_ops.choi_to_kraus import choi_to_kraus diff --git a/toqito/channel_ops/apply_channel.py b/toqito/channel_ops/apply_channel.py index a4ed0cbd..dd32a185 100644 --- a/toqito/channel_ops/apply_channel.py +++ b/toqito/channel_ops/apply_channel.py @@ -1,6 +1,5 @@ """Apply channel to an operator.""" - import itertools import numpy as np @@ -147,7 +146,4 @@ def apply_channel(mat: np.ndarray, phi_op: np.ndarray | list[list[np.ndarray]]) order="F", ) return a_mat @ b_mat - raise ValueError( - "Invalid: The variable `phi_op` must either be a list of " - "Kraus operators or as a Choi matrix." - ) + raise ValueError("Invalid: The variable `phi_op` must either be a list of Kraus operators or as a Choi matrix.") diff --git a/toqito/channel_ops/choi_to_kraus.py b/toqito/channel_ops/choi_to_kraus.py index 8886cb97..49bcd7b7 100644 --- a/toqito/channel_ops/choi_to_kraus.py +++ b/toqito/channel_ops/choi_to_kraus.py @@ -1,6 +1,5 @@ """Compute a list of Kraus operators from the Choi matrix.""" - import numpy as np from toqito.helper import channel_dim @@ -108,8 +107,7 @@ def choi_to_kraus( return kraus_0 kraus_1 = [ - np.sign(eigval) * k_mat - for eigval, k_mat in zip(filter(lambda eigval: abs(eigval) > tol, eigvals), kraus_0) + np.sign(eigval) * k_mat for eigval, k_mat in zip(filter(lambda eigval: abs(eigval) > tol, eigvals), kraus_0) ] else: u_mat, singular_values, vh_mat = np.linalg.svd(choi_mat, full_matrices=False) diff --git a/toqito/channel_ops/dual_channel.py b/toqito/channel_ops/dual_channel.py index c150e92a..46592faa 100644 --- a/toqito/channel_ops/dual_channel.py +++ b/toqito/channel_ops/dual_channel.py @@ -1,6 +1,5 @@ """Compute the dual of a map.""" - import numpy as np from toqito.helper import channel_dim @@ -95,7 +94,4 @@ def dual_channel( if len(phi_op.shape) == 2: d_in, d_out, _ = channel_dim(phi_op, dim=dims, compute_env_dim=False) return swap(phi_op.conj(), dim=[[d_in[0], d_out[0]], [d_in[1], d_out[1]]]) - raise ValueError( - "Invalid: The variable `phi_op` must either be a list of " - "Kraus operators or as a Choi matrix." - ) + raise ValueError("Invalid: The variable `phi_op` must either be a list of Kraus operators or as a Choi matrix.") diff --git a/toqito/channel_ops/kraus_to_choi.py b/toqito/channel_ops/kraus_to_choi.py index 858d56a6..3fd97302 100644 --- a/toqito/channel_ops/kraus_to_choi.py +++ b/toqito/channel_ops/kraus_to_choi.py @@ -1,4 +1,5 @@ """Compute the Choi matrix of a list of Kraus operators.""" + import numpy as np from toqito.channel_ops import partial_channel diff --git a/toqito/channel_ops/partial_channel.py b/toqito/channel_ops/partial_channel.py index d22f1d85..af92808b 100644 --- a/toqito/channel_ops/partial_channel.py +++ b/toqito/channel_ops/partial_channel.py @@ -1,6 +1,5 @@ """Apply channel a subsystem of an operator.""" - import itertools import numpy as np @@ -192,6 +191,5 @@ def partial_channel( return phi_x raise ValueError( - "The `phi_map` variable is assumed to be provided as " - "either a Choi matrix or a list of Kraus operators." + "The `phi_map` variable is assumed to be provided as either a Choi matrix or a list of Kraus operators." ) diff --git a/toqito/channel_ops/tests/test_apply_channel.py b/toqito/channel_ops/tests/test_apply_channel.py index e57d27d8..b5eee6bb 100644 --- a/toqito/channel_ops/tests/test_apply_channel.py +++ b/toqito/channel_ops/tests/test_apply_channel.py @@ -1,4 +1,5 @@ """Tests for apply_channel.""" + import numpy as np import pytest @@ -11,21 +12,26 @@ kraus_3 = np.array([[-1, 0], [0, 0], [0, -1]]) kraus_4 = np.array([[0, 0], [1, 1], [0, 0]]) -@pytest.mark.parametrize("input_mat, expected_result, apply_channel_arg2", [ - # The swap operator is the Choi matrix of the transpose map. - # The following test is a (non-ideal, but illustrative) way of computing the transpose of a matrix. - (np.array([[1, 4, 7], [2, 5, 8], [3, 6, 9]]), - np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), swap_operator(3)), - # The swap operator is the Choi matrix of the transpose map. - # The following test is a (non-ideal, but illustrative) way of computing the transpose of a non-square matrix. - (np.array([[0, 1], [2, 3], [4, 5]]), - np.array([[0, 2, 4], [1, 3, 5]]), swap_operator([2, 3])), - # Apply Kraus map. - # The following test computes PHI(X) where X = [[1, 2], [3, 4]] and where PHI is the superoperator defined by: - # Phi(X) = [[1,5],[1,0],[0,2]] X [[0,1][2,3][4,5]].conj().T -[[1,0],[0,0],[0,1]] X [[0,0][1,1],[0,0]].conj().T - (np.array([[1, 2], [3, 4]]), - np.array([[22, 95, 174], [2, 8, 14], [8, 29, 64]]), - [[kraus_1, kraus_2], [kraus_3, kraus_4]])]) + +@pytest.mark.parametrize( + "input_mat, expected_result, apply_channel_arg2", + [ + # The swap operator is the Choi matrix of the transpose map. + # The following test is a (non-ideal, but illustrative) way of computing the transpose of a matrix. + (np.array([[1, 4, 7], [2, 5, 8], [3, 6, 9]]), np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), swap_operator(3)), + # The swap operator is the Choi matrix of the transpose map. + # The following test is a (non-ideal, but illustrative) way of computing the transpose of a non-square matrix. + (np.array([[0, 1], [2, 3], [4, 5]]), np.array([[0, 2, 4], [1, 3, 5]]), swap_operator([2, 3])), + # Apply Kraus map. + # The following test computes PHI(X) where X = [[1, 2], [3, 4]] and where PHI is the superoperator defined by: + # Phi(X) = [[1,5],[1,0],[0,2]] X [[0,1][2,3][4,5]].conj().T -[[1,0],[0,0],[0,1]] X [[0,0][1,1],[0,0]].conj().T + ( + np.array([[1, 2], [3, 4]]), + np.array([[22, 95, 174], [2, 8, 14], [8, 29, 64]]), + [[kraus_1, kraus_2], [kraus_3, kraus_4]], + ), + ], +) def test_apply_channel(input_mat, expected_result, apply_channel_arg2): """Test function works as expected for valid inputs.""" calculated_result = apply_channel(input_mat, apply_channel_arg2) diff --git a/toqito/channel_ops/tests/test_choi_to_kraus.py b/toqito/channel_ops/tests/test_choi_to_kraus.py index e36cc5ef..6a1af540 100644 --- a/toqito/channel_ops/tests/test_choi_to_kraus.py +++ b/toqito/channel_ops/tests/test_choi_to_kraus.py @@ -1,4 +1,5 @@ """Tests for choi_to_kraus.""" + import numpy as np import pytest @@ -7,34 +8,40 @@ choi_mat_swap = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) kraus_ops_swap = [ - [ - np.array([[0.0, 0.70710678], [-0.70710678, 0.0]]), - np.array([[0.0, -0.70710678], [0.70710678, 0.0]]), - ], - [ - np.array([[0.0, 0.70710678], [0.70710678, 0.0]]), - np.array([[0.0, 0.70710678], [0.70710678, 0.0]]), - ], - [np.array([[1.0, 0.0], [0.0, 0.0]]), np.array([[1.0, 0.0], [0.0, 0.0]])], - [np.array([[0.0, 0.0], [0.0, 1.0]]), np.array([[0.0, 0.0], [0.0, 1.0]])],] + [ + np.array([[0.0, 0.70710678], [-0.70710678, 0.0]]), + np.array([[0.0, -0.70710678], [0.70710678, 0.0]]), + ], + [ + np.array([[0.0, 0.70710678], [0.70710678, 0.0]]), + np.array([[0.0, 0.70710678], [0.70710678, 0.0]]), + ], + [np.array([[1.0, 0.0], [0.0, 0.0]]), np.array([[1.0, 0.0], [0.0, 0.0]])], + [np.array([[0.0, 0.0], [0.0, 1.0]]), np.array([[0.0, 0.0], [0.0, 1.0]])], +] choi_mat_iso = np.array( - [ - [1.0, 0.0, 0.0, 1.0, 0.0, 0.0], - [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - [1.0, 0.0, 0.0, 1.0, 0.0, 0.0], - [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - ] - ) + [ + [1.0, 0.0, 0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [1.0, 0.0, 0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + ] +) kraus_op_iso = [np.array([[-1.0, 0.0, 0.0], [0.0, -1.0, 0.0]])] -@pytest.mark.parametrize("test_input, expected, input_dim", [ - # choi of swap - (choi_mat_swap, kraus_ops_swap, None), - (choi_mat_iso, kraus_op_iso, [3, 2])]) -def test_choi_to_kraus(test_input,expected, input_dim): + +@pytest.mark.parametrize( + "test_input, expected, input_dim", + [ + # choi of swap + (choi_mat_swap, kraus_ops_swap, None), + (choi_mat_iso, kraus_op_iso, [3, 2]), + ], +) +def test_choi_to_kraus(test_input, expected, input_dim): """Test function works as expected for valid inputs.""" if input_dim is None: calculated = choi_to_kraus(test_input) @@ -42,82 +49,85 @@ def test_choi_to_kraus(test_input,expected, input_dim): for i, cal_value in enumerate(calculated): assert np.isclose(expected[i], cal_value).all() - calculated = choi_to_kraus(test_input, dim = input_dim) + calculated = choi_to_kraus(test_input, dim=input_dim) for i, cal_value in enumerate(calculated): assert np.isclose(expected[i], cal_value).all() choi_mat_non_square = swap_operator([2, 3]) kraus_ops_non_square = [ - [ - np.array([[1.0, 0.0, 0.0], [0.0, 0.0, 0.0]]), - np.array([[1.0, 0.0], [0.0, 0.0], [0.0, 0.0]]), - ], - [ - np.array([[0.0, -1.0, 0.0], [0.0, 0.0, 0.0]]), - np.array([[-0.0, -0.0], [-1.0, -0.0], [-0.0, -0.0]]), - ], - [ - np.array([[0.0, 0.0, -1.0], [0.0, 0.0, 0.0]]), - np.array([[-0.0, -0.0], [-0.0, -0.0], [-1.0, -0.0]]), - ], - [ - np.array([[0.0, 0.0, 0.0], [-1.0, 0.0, 0.0]]), - np.array([[-0.0, -1.0], [-0.0, -0.0], [-0.0, -0.0]]), - ], - [ - np.array([[0.0, 0.0, 0.0], [0.0, -1.0, 0.0]]), - np.array([[-0.0, -0.0], [-0.0, -1.0], [-0.0, -0.0]]), - ], - [ - np.array([[0.0, 0.0, 0.0], [0.0, 0.0, 1.0]]), - np.array([[0.0, 0.0], [0.0, 0.0], [0.0, 1.0]]), - ], - ] + [ + np.array([[1.0, 0.0, 0.0], [0.0, 0.0, 0.0]]), + np.array([[1.0, 0.0], [0.0, 0.0], [0.0, 0.0]]), + ], + [ + np.array([[0.0, -1.0, 0.0], [0.0, 0.0, 0.0]]), + np.array([[-0.0, -0.0], [-1.0, -0.0], [-0.0, -0.0]]), + ], + [ + np.array([[0.0, 0.0, -1.0], [0.0, 0.0, 0.0]]), + np.array([[-0.0, -0.0], [-0.0, -0.0], [-1.0, -0.0]]), + ], + [ + np.array([[0.0, 0.0, 0.0], [-1.0, 0.0, 0.0]]), + np.array([[-0.0, -1.0], [-0.0, -0.0], [-0.0, -0.0]]), + ], + [ + np.array([[0.0, 0.0, 0.0], [0.0, -1.0, 0.0]]), + np.array([[-0.0, -0.0], [-0.0, -1.0], [-0.0, -0.0]]), + ], + [ + np.array([[0.0, 0.0, 0.0], [0.0, 0.0, 1.0]]), + np.array([[0.0, 0.0], [0.0, 0.0], [0.0, 1.0]]), + ], +] choi_mat_reduced_rank = np.array( - [ - [1, 0, 1, 0], - [0, 1, 0, 1], - [1, 0, 1, 0], - [0, 1, 0, -1], - ] - ) + [ + [1, 0, 1, 0], + [0, 1, 0, 1], + [1, 0, 1, 0], + [0, 1, 0, -1], + ] +) c0 = np.sqrt(1 / (2 + 2 * np.sqrt(2))) c1 = np.sqrt(1 / (2 + 2 * np.sqrt(2)) + 1) kraus_ops_reduced_rank = [ - [ - np.array([[0, 0], [c0, -c1]]), - np.array([[0, 0], [-c0, c1]]), - ], - [ - np.array([[0, 0], [c1, c0]]), - np.array([[0, 0], [c1, c0]]), - ], - [np.array([[-1, -1], [0, 0]]), np.array([[-1, -1], [0, 0]])], - ] + [ + np.array([[0, 0], [c0, -c1]]), + np.array([[0, 0], [-c0, c1]]), + ], + [ + np.array([[0, 0], [c1, c0]]), + np.array([[0, 0], [c1, c0]]), + ], + [np.array([[-1, -1], [0, 0]]), np.array([[-1, -1], [0, 0]])], +] + -@pytest.mark.parametrize("test_input, expected, input_dim", [ - # Choi matrix of the swap operator for non square input/output. - (choi_mat_non_square, kraus_ops_non_square, [[3, 2], [2, 3]]), - # Choi matrix of a hermicity preserving map with reduced rank - (choi_mat_reduced_rank, kraus_ops_reduced_rank, None)]) +@pytest.mark.parametrize( + "test_input, expected, input_dim", + [ + # Choi matrix of the swap operator for non square input/output. + (choi_mat_non_square, kraus_ops_non_square, [[3, 2], [2, 3]]), + # Choi matrix of a hermicity preserving map with reduced rank + (choi_mat_reduced_rank, kraus_ops_reduced_rank, None), + ], +) def test_choi_to_kraus_non_square_reduced_rank(test_input, expected, input_dim): """Choi to kraus output for non-square input/output and reduced rank scenarios.""" calculated = choi_to_kraus(test_input, dim=input_dim) assert all( - np.allclose(k_op[0], res_k_op[0]) and np.allclose(k_op[1], res_k_op[1]) - for k_op, res_k_op in zip(expected, calculated)) + np.allclose(k_op[0], res_k_op[0]) and np.allclose(k_op[1], res_k_op[1]) + for k_op, res_k_op in zip(expected, calculated) + ) def test_choi_to_kraus_general_map(): """Choi matrix of a map that erases everything and keeps the M(1, 2) entry of the input matrix.""" - choi_mat = np.array( - [[0.0, 0.0, 0.0, 1.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]] - ) + choi_mat = np.array([[0.0, 0.0, 0.0, 1.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]]) kraus_ops = [np.array([[1, 0], [0, 0]]), np.array([[0, 0], [0, 1]])] res_kraus_ops = choi_to_kraus(choi_mat) assert np.isclose(kraus_ops, res_kraus_ops).all() - diff --git a/toqito/channel_ops/tests/test_dual_channel.py b/toqito/channel_ops/tests/test_dual_channel.py index ac2fcd23..3588f39d 100644 --- a/toqito/channel_ops/tests/test_dual_channel.py +++ b/toqito/channel_ops/tests/test_dual_channel.py @@ -1,4 +1,5 @@ """Tests for dual_channel.""" + import numpy as np import pytest @@ -12,72 +13,82 @@ expected_res_cp = [np.array([[1, 0, -1j, 0]]).T, np.array([[0, 1, 0, -1j]]).T] expected_res_2d = [ - [np.array([[1, 0, -1j, 0]]).T, np.array([[1, 0, -1j, 0]]).T], - [np.array([[0, 1, 0, -1j]]).T, np.array([[0, 1, 0, -1j]]).T], - ] + [np.array([[1, 0, -1j, 0]]).T, np.array([[1, 0, -1j, 0]]).T], + [np.array([[0, 1, 0, -1j]]).T, np.array([[0, 1, 0, -1j]]).T], +] input_diff_dims = np.array( - [ - [1, -1j, 0, 0, 0, 1], - [1j, -1, 0, 0, 0, -1j], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [1, 1j, 0, 0, 0, 1], - ] - ) + [ + [1, -1j, 0, 0, 0, 1], + [1j, -1, 0, 0, 0, -1j], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [1, 1j, 0, 0, 0, 1], + ] +) expected_res_diff_dims = np.array( - [ - [1, 0, 0, 1j, 0, 1], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [-1j, 0, 0, -1, 0, 1j], - [0, 0, 0, 0, 0, 0], - [1, 0, 0, -1j, 0, 1], - ] - ) + [ + [1, 0, 0, 1j, 0, 1], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [-1j, 0, 0, -1, 0, 1j], + [0, 0, 0, 0, 0, 0], + [1, 0, 0, -1j, 0, 1], + ] +) expected_swap = np.array( - [ - [1, 0, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0], - [0, 0, 0, 0, 1, 0], - [0, 1, 0, 0, 0, 0], - [0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 1], - ] - ) -@pytest.mark.parametrize("test_input, expected, input_dim", [ - # Test dual_channel on a channel represented as Kraus operators (1d list, CP map) - ([kraus_1, kraus_2], expected_res_cp, None), - # Test dual_channel on a channel represented as Kraus operators (2d list). - ([[kraus_1, kraus_1], [kraus_2, kraus_2]], expected_res_2d, None), - # Test dual_channel on a 9x9 Choi matrix, inferring dims=[3,3] - (choi(1, 1, 0), choi(1, 0, 1), None), - # Test dual_channel on a Choi matrix with different input and output dimensions. - (input_diff_dims, expected_res_diff_dims, [3, 2]), - # Dual of a channel that transposes 3x2 matrices - (swap_operator([2, 3]), expected_swap, [[3, 2], [2, 3]])]) + [ + [1, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 1, 0], + [0, 1, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 1], + ] +) + + +@pytest.mark.parametrize( + "test_input, expected, input_dim", + [ + # Test dual_channel on a channel represented as Kraus operators (1d list, CP map) + ([kraus_1, kraus_2], expected_res_cp, None), + # Test dual_channel on a channel represented as Kraus operators (2d list). + ([[kraus_1, kraus_1], [kraus_2, kraus_2]], expected_res_2d, None), + # Test dual_channel on a 9x9 Choi matrix, inferring dims=[3,3] + (choi(1, 1, 0), choi(1, 0, 1), None), + # Test dual_channel on a Choi matrix with different input and output dimensions. + (input_diff_dims, expected_res_diff_dims, [3, 2]), + # Dual of a channel that transposes 3x2 matrices + (swap_operator([2, 3]), expected_swap, [[3, 2], [2, 3]]), + ], +) def test_dual_channel(test_input, expected, input_dim): """Test function works as expected for valid inputs.""" if input_dim is None: calculated = dual_channel(test_input) assert np.isclose(calculated, expected).all() - calculated = dual_channel(test_input, dims = input_dim) + calculated = dual_channel(test_input, dims=input_dim) assert np.isclose(calculated, expected).all() -@pytest.mark.parametrize("test_input", [ - # If the channel is represented as an array, it must be two-dimensional (a matrix). - (np.array([1, 2, 3, 4])), - # Test output of function when the dimensions must be specified. If the size of the Choi matrix is not a perfect - # square, the dimensions of the input and output spaces must be specified. - (np.arange(36).reshape(6, 6)), - # error for an invalid input - ([0])]) + +@pytest.mark.parametrize( + "test_input", + [ + # If the channel is represented as an array, it must be two-dimensional (a matrix). + (np.array([1, 2, 3, 4])), + # Test output of function when the dimensions must be specified. If the size of the Choi matrix is not a perfect + # square, the dimensions of the input and output spaces must be specified. + (np.arange(36).reshape(6, 6)), + # error for an invalid input + ([0]), + ], +) def test_dual_channel_error(test_input): """Test function raises error as expected for invalid inputs.""" with pytest.raises(ValueError): dual_channel(test_input) - diff --git a/toqito/channel_ops/tests/test_kraus_to_choi.py b/toqito/channel_ops/tests/test_kraus_to_choi.py index 279e5933..118d5a7f 100644 --- a/toqito/channel_ops/tests/test_kraus_to_choi.py +++ b/toqito/channel_ops/tests/test_kraus_to_choi.py @@ -1,4 +1,5 @@ """Tests for kraus_to_choi.""" + import numpy as np import pytest @@ -15,10 +16,11 @@ kraus_8_transpose = np.array([[0, 0], [0, 1]]).conj().T kraus_ops_transpose = [ - [kraus_1_transpose, kraus_2_transpose], - [kraus_3_transpose, kraus_4_transpose], - [kraus_5_transpose, kraus_6_transpose], - [kraus_7_transpose, kraus_8_transpose],] + [kraus_1_transpose, kraus_2_transpose], + [kraus_3_transpose, kraus_4_transpose], + [kraus_5_transpose, kraus_6_transpose], + [kraus_7_transpose, kraus_8_transpose], +] expected_choi_res_transpose = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) @@ -33,11 +35,11 @@ kraus_8_swap_operator_non_unique = np.array([[0, 1 / np.sqrt(2)], [-1 / np.sqrt(2), 0]]).conj().T kraus_ops_swap_operator_non_unique = [ - [kraus_1_swap_operator_non_unique, kraus_2_swap_operator_non_unique], - [kraus_3_swap_operator_non_unique, kraus_4_swap_operator_non_unique], - [kraus_5_swap_operator_non_unique, kraus_6_swap_operator_non_unique], - [kraus_7_swap_operator_non_unique, kraus_8_swap_operator_non_unique], - ] + [kraus_1_swap_operator_non_unique, kraus_2_swap_operator_non_unique], + [kraus_3_swap_operator_non_unique, kraus_4_swap_operator_non_unique], + [kraus_5_swap_operator_non_unique, kraus_6_swap_operator_non_unique], + [kraus_7_swap_operator_non_unique, kraus_8_swap_operator_non_unique], +] expected_choi_res_swap_operator_non_unique = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]]) @@ -47,7 +49,9 @@ kraus_4_dephasing_channel = np.array([[0, 0], [0, 1]]) kraus_ops_dephasing_channel = [ - [kraus_1_dephasing_channel, kraus_2_dephasing_channel], [kraus_3_dephasing_channel, kraus_4_dephasing_channel]] + [kraus_1_dephasing_channel, kraus_2_dephasing_channel], + [kraus_3_dephasing_channel, kraus_4_dephasing_channel], +] expected_choi_res_dephasing_channel = dephasing(2) @@ -62,62 +66,65 @@ kraus_8_depolarizing_channel = np.array([[0, 0], [0, 1 / np.sqrt(2)]]) kraus_ops_depolarizing_channel = [ - [kraus_1_depolarizing_channel, kraus_2_depolarizing_channel], - [kraus_3_depolarizing_channel, kraus_4_depolarizing_channel], - [kraus_5_depolarizing_channel, kraus_6_depolarizing_channel], - [kraus_7_depolarizing_channel, kraus_8_depolarizing_channel], - ] + [kraus_1_depolarizing_channel, kraus_2_depolarizing_channel], + [kraus_3_depolarizing_channel, kraus_4_depolarizing_channel], + [kraus_5_depolarizing_channel, kraus_6_depolarizing_channel], + [kraus_7_depolarizing_channel, kraus_8_depolarizing_channel], +] expected_choi_res_depolarizing_channel = depolarizing(2) v_mat = np.array([[1, 0, 0], [0, 1, 0]]) expected_v_mat = np.array( - [ - [1, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - [1, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0], - ] - ) + [ + [1, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [1, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + ] +) kraus_1 = np.array([[1, 0, 0], [0, 1, 0]]) kraus_2 = np.array( - [ - [0, 0, 1, 0], - [0, 1, 0, 0], - [1, 0, 0, 1], - ] - ) + [ + [0, 0, 1, 0], + [0, 1, 0, 0], + [1, 0, 0, 1], + ] +) expected_non_square = np.array( - [ - [0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - ] - ) - -@pytest.mark.parametrize("test_input, expected", [ - # Choi matrix of the transpose map is the swap operator. - (kraus_ops_transpose, expected_choi_res_transpose), - # As Kraus operators are non-unique, these also should yield the swap operator - (kraus_ops_swap_operator_non_unique, expected_choi_res_swap_operator_non_unique), - # Kraus operators for dephasing channel should yield the proper Choi matrix. - (kraus_ops_dephasing_channel, expected_choi_res_dephasing_channel), - # Kraus operators for depolarizing channel should yield the proper Choi matrix - (kraus_ops_depolarizing_channel, expected_choi_res_depolarizing_channel), - # Kraus operators for an isometry - ([v_mat], expected_v_mat), - # Kraus operators for non square inputs and outputs - ([[kraus_1, kraus_2]], expected_non_square) -]) + [ + [0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + ] +) + + +@pytest.mark.parametrize( + "test_input, expected", + [ + # Choi matrix of the transpose map is the swap operator. + (kraus_ops_transpose, expected_choi_res_transpose), + # As Kraus operators are non-unique, these also should yield the swap operator + (kraus_ops_swap_operator_non_unique, expected_choi_res_swap_operator_non_unique), + # Kraus operators for dephasing channel should yield the proper Choi matrix. + (kraus_ops_dephasing_channel, expected_choi_res_dephasing_channel), + # Kraus operators for depolarizing channel should yield the proper Choi matrix + (kraus_ops_depolarizing_channel, expected_choi_res_depolarizing_channel), + # Kraus operators for an isometry + ([v_mat], expected_v_mat), + # Kraus operators for non square inputs and outputs + ([[kraus_1, kraus_2]], expected_non_square), + ], +) def test_kraus_to_choi(test_input, expected): """Test function works as expected for valid inputs.""" calculated = kraus_to_choi(test_input) assert np.isclose(calculated, expected).all() - diff --git a/toqito/channel_ops/tests/test_partial_channel.py b/toqito/channel_ops/tests/test_partial_channel.py index 8711184c..4b5d66df 100644 --- a/toqito/channel_ops/tests/test_partial_channel.py +++ b/toqito/channel_ops/tests/test_partial_channel.py @@ -1,4 +1,5 @@ """Tests for partial_channel.""" + import numpy as np import pytest @@ -7,100 +8,105 @@ from toqito.matrices import pauli rho_first_system = np.array( + [ [ - [ - 0.3500, - -0.1220 - 0.0219 * 1j, - -0.1671 - 0.0030 * 1j, - -0.1170 - 0.0694 * 1j, - ], - [ - -0.0233 + 0.0219 * 1j, - 0.1228, - -0.2775 + 0.0492 * 1j, - -0.2613 + 0.0529 * 1j, - ], - [ - -0.2671 + 0.0030 * 1j, - -0.2775 - 0.0492 * 1j, - 0.1361, - 0.0202 + 0.0062 * 1j, - ], - [ - -0.2170 + 0.0694 * 1j, - -0.2613 - 0.0529 * 1j, - 0.2602 - 0.0062 * 1j, - 0.2530, - ], - ] - ) + 0.3500, + -0.1220 - 0.0219 * 1j, + -0.1671 - 0.0030 * 1j, + -0.1170 - 0.0694 * 1j, + ], + [ + -0.0233 + 0.0219 * 1j, + 0.1228, + -0.2775 + 0.0492 * 1j, + -0.2613 + 0.0529 * 1j, + ], + [ + -0.2671 + 0.0030 * 1j, + -0.2775 - 0.0492 * 1j, + 0.1361, + 0.0202 + 0.0062 * 1j, + ], + [ + -0.2170 + 0.0694 * 1j, + -0.2613 - 0.0529 * 1j, + 0.2602 - 0.0062 * 1j, + 0.2530, + ], + ] +) expected_res_first_system = np.array( - [ - [0.2364 + 0.0j, 0.0 + 0.0j, -0.2142 + 0.02495j, 0.0 + 0.0j], - [0.0 + 0.0j, 0.2364 + 0.0j, 0.0 + 0.0j, -0.2142 + 0.02495j], - [-0.2642 - 0.02495j, 0.0 + 0.0j, 0.19455 + 0.0j, 0.0 + 0.0j], - [0.0 + 0.0j, -0.2642 - 0.02495j, 0.0 + 0.0j, 0.19455 + 0.0j], - ] - ) + [ + [0.2364 + 0.0j, 0.0 + 0.0j, -0.2142 + 0.02495j, 0.0 + 0.0j], + [0.0 + 0.0j, 0.2364 + 0.0j, 0.0 + 0.0j, -0.2142 + 0.02495j], + [-0.2642 - 0.02495j, 0.0 + 0.0j, 0.19455 + 0.0j, 0.0 + 0.0j], + [0.0 + 0.0j, -0.2642 - 0.02495j, 0.0 + 0.0j, 0.19455 + 0.0j], + ] +) rho_second_system = np.array( + [ [ - [ - 0.3101, - -0.0220 - 0.0219 * 1j, - -0.0671 - 0.0030 * 1j, - -0.0170 - 0.0694 * 1j, - ], - [ - -0.0220 + 0.0219 * 1j, - 0.1008, - -0.0775 + 0.0492 * 1j, - -0.0613 + 0.0529 * 1j, - ], - [ - -0.0671 + 0.0030 * 1j, - -0.0775 - 0.0492 * 1j, - 0.1361, - 0.0602 + 0.0062 * 1j, - ], - [ - -0.0170 + 0.0694 * 1j, - -0.0613 - 0.0529 * 1j, - 0.0602 - 0.0062 * 1j, - 0.4530, - ], - ] - ) + 0.3101, + -0.0220 - 0.0219 * 1j, + -0.0671 - 0.0030 * 1j, + -0.0170 - 0.0694 * 1j, + ], + [ + -0.0220 + 0.0219 * 1j, + 0.1008, + -0.0775 + 0.0492 * 1j, + -0.0613 + 0.0529 * 1j, + ], + [ + -0.0671 + 0.0030 * 1j, + -0.0775 - 0.0492 * 1j, + 0.1361, + 0.0602 + 0.0062 * 1j, + ], + [ + -0.0170 + 0.0694 * 1j, + -0.0613 - 0.0529 * 1j, + 0.0602 - 0.0062 * 1j, + 0.4530, + ], + ] +) expected_res_second_system = np.array( - [ - [0.2231 + 0.0j, 0.0191 - 0.00785j, 0.0 + 0.0j, 0.0 + 0.0j], - [0.0191 + 0.00785j, 0.2769 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], - [0.0 + 0.0j, 0.0 + 0.0j, 0.2231 + 0.0j, 0.0191 - 0.00785j], - [0.0 + 0.0j, 0.0 + 0.0j, 0.0191 + 0.00785j, 0.2769 + 0.0j], - ] - ) + [ + [0.2231 + 0.0j, 0.0191 - 0.00785j, 0.0 + 0.0j, 0.0 + 0.0j], + [0.0191 + 0.00785j, 0.2769 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], + [0.0 + 0.0j, 0.0 + 0.0j, 0.2231 + 0.0j, 0.0191 - 0.00785j], + [0.0 + 0.0j, 0.0 + 0.0j, 0.0191 + 0.00785j, 0.2769 + 0.0j], + ] +) rho_dim_list = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]) expected_res_dim_list = np.array( - [ - [3.5, 0.0, 5.5, 0.0], - [0.0, 3.5, 0.0, 5.5], - [11.5, 0.0, 13.5, 0.0], - [0.0, 11.5, 0.0, 13.5], - ] - ) - -@pytest.mark.parametrize("test_input, expected, sys_arg, dim_arg", [ - # Perform the partial map using the depolarizing channel as the Choi matrix on first system - (rho_first_system, expected_res_first_system, None, None), - # Perform the partial map using the depolarizing channel as the Choi matrix on second system - (rho_second_system, expected_res_second_system, 1, None), - # Test uses the depolarizing channel as the Choi matrix on first system when the dimension is specified as list. - (rho_dim_list, expected_res_dim_list, 2, [2, 2])]) + [ + [3.5, 0.0, 5.5, 0.0], + [0.0, 3.5, 0.0, 5.5], + [11.5, 0.0, 13.5, 0.0], + [0.0, 11.5, 0.0, 13.5], + ] +) + + +@pytest.mark.parametrize( + "test_input, expected, sys_arg, dim_arg", + [ + # Perform the partial map using the depolarizing channel as the Choi matrix on first system + (rho_first_system, expected_res_first_system, None, None), + # Perform the partial map using the depolarizing channel as the Choi matrix on second system + (rho_second_system, expected_res_second_system, 1, None), + # Test uses the depolarizing channel as the Choi matrix on first system when the dimension is specified as list. + (rho_dim_list, expected_res_dim_list, 2, [2, 2]), + ], +) def test_partial_channel(test_input, expected, sys_arg, dim_arg): """Test function works as expected for valid inputs.""" if sys_arg is None and dim_arg is None: @@ -113,7 +119,6 @@ def test_partial_channel(test_input, expected, sys_arg, dim_arg): assert np.isclose(calculated, expected).all() - @pytest.mark.parametrize("nested", [1, 2, 3]) def test_partial_channel_cpt_kraus(nested): """Perform the partial map using the Kraus representation of the depolarizing channel.""" @@ -138,16 +143,19 @@ def test_partial_channel_cpt_kraus(nested): assert np.isclose(expected_res, res).all() - -@pytest.mark.parametrize("test_input, map_arg, sys_arg, dim_arg", [ - # Matrix must be square - (np.array([[1, 1, 1, 1], [5, 6, 7, 8], [3, 3, 3, 3]]), depolarizing(3), None, None), - # Matrix must be square with sys arg - (np.array([[1, 2, 3, 4], [2, 2, 2, 2], [12, 11, 10, 9]]), depolarizing(3), 2, None), - # Invalid dimension for partial map - (np.array([[1, 2, 3, 4], [5, 6, 7, 8], [12, 11, 10, 9]]), depolarizing(3), 1, [2, 2]), - # Invalid map argument for partial map - (np.array([[1, 2, 3, 4], [5, 6, 7, 8], [12, 11, 10, 9]]), 5, 2, [2,2])]) +@pytest.mark.parametrize( + "test_input, map_arg, sys_arg, dim_arg", + [ + # Matrix must be square + (np.array([[1, 1, 1, 1], [5, 6, 7, 8], [3, 3, 3, 3]]), depolarizing(3), None, None), + # Matrix must be square with sys arg + (np.array([[1, 2, 3, 4], [2, 2, 2, 2], [12, 11, 10, 9]]), depolarizing(3), 2, None), + # Invalid dimension for partial map + (np.array([[1, 2, 3, 4], [5, 6, 7, 8], [12, 11, 10, 9]]), depolarizing(3), 1, [2, 2]), + # Invalid map argument for partial map + (np.array([[1, 2, 3, 4], [5, 6, 7, 8], [12, 11, 10, 9]]), 5, 2, [2, 2]), + ], +) def test_partial_channel_error(test_input, map_arg, sys_arg, dim_arg): """Test function raises error as expected for invalid inputs.""" if sys_arg is not None: @@ -155,4 +163,4 @@ def test_partial_channel_error(test_input, map_arg, sys_arg, dim_arg): partial_channel(test_input, map_arg, sys_arg, dim_arg) with pytest.raises(ValueError): - partial_channel(test_input, map_arg) + partial_channel(test_input, map_arg) diff --git a/toqito/channel_props/__init__.py b/toqito/channel_props/__init__.py index 911c6f6b..60381e1e 100644 --- a/toqito/channel_props/__init__.py +++ b/toqito/channel_props/__init__.py @@ -1,4 +1,5 @@ """A number of properties of quantum channels.""" + from toqito.channel_props.is_herm_preserving import is_herm_preserving from toqito.channel_props.is_completely_positive import is_completely_positive from toqito.channel_props.is_positive import is_positive diff --git a/toqito/channel_props/choi_rank.py b/toqito/channel_props/choi_rank.py index 5aca5baf..82fa27bc 100644 --- a/toqito/channel_props/choi_rank.py +++ b/toqito/channel_props/choi_rank.py @@ -1,6 +1,5 @@ """Calculate the Choi rank of a channel.""" - import numpy as np from toqito.channel_ops import kraus_to_choi diff --git a/toqito/channel_props/is_completely_positive.py b/toqito/channel_props/is_completely_positive.py index 16f4657a..be16e49f 100644 --- a/toqito/channel_props/is_completely_positive.py +++ b/toqito/channel_props/is_completely_positive.py @@ -1,6 +1,5 @@ """Is channel completely positive.""" - import numpy as np from toqito.channel_ops import kraus_to_choi diff --git a/toqito/channel_props/is_herm_preserving.py b/toqito/channel_props/is_herm_preserving.py index f03a2b6b..f8c59ed0 100644 --- a/toqito/channel_props/is_herm_preserving.py +++ b/toqito/channel_props/is_herm_preserving.py @@ -1,6 +1,5 @@ """Is channel Hermiticity-preserving.""" - import numpy as np from toqito.channel_ops import kraus_to_choi diff --git a/toqito/channel_props/is_positive.py b/toqito/channel_props/is_positive.py index 17c9f10d..85342777 100644 --- a/toqito/channel_props/is_positive.py +++ b/toqito/channel_props/is_positive.py @@ -1,6 +1,5 @@ """Is channel positive.""" - import numpy as np from toqito.channel_ops import kraus_to_choi diff --git a/toqito/channel_props/is_quantum_channel.py b/toqito/channel_props/is_quantum_channel.py index dcb166cf..350a9c5a 100644 --- a/toqito/channel_props/is_quantum_channel.py +++ b/toqito/channel_props/is_quantum_channel.py @@ -1,6 +1,5 @@ """Is quantum channel.""" - import numpy as np from toqito.channel_ops import kraus_to_choi diff --git a/toqito/channel_props/is_trace_preserving.py b/toqito/channel_props/is_trace_preserving.py index 3f65524e..1dd5064f 100644 --- a/toqito/channel_props/is_trace_preserving.py +++ b/toqito/channel_props/is_trace_preserving.py @@ -1,6 +1,5 @@ """Is channel trace-preserving.""" - import numpy as np from toqito.channels import partial_trace diff --git a/toqito/channel_props/is_unital.py b/toqito/channel_props/is_unital.py index 4f85cec4..6b701c2e 100644 --- a/toqito/channel_props/is_unital.py +++ b/toqito/channel_props/is_unital.py @@ -1,4 +1,5 @@ """Determine whether channel is unital.""" + import numpy as np from toqito.channel_ops import apply_channel diff --git a/toqito/channel_props/is_unitary.py b/toqito/channel_props/is_unitary.py index a7ff53aa..304bc69f 100644 --- a/toqito/channel_props/is_unitary.py +++ b/toqito/channel_props/is_unitary.py @@ -1,6 +1,5 @@ """Is channel unitary.""" - import numpy as np from toqito.channel_ops import choi_to_kraus diff --git a/toqito/channel_props/tests/test_choi_rank.py b/toqito/channel_props/tests/test_choi_rank.py index 01cecc38..90a27034 100644 --- a/toqito/channel_props/tests/test_choi_rank.py +++ b/toqito/channel_props/tests/test_choi_rank.py @@ -1,4 +1,5 @@ """Tests for choi_rank.""" + import numpy as np import pytest diff --git a/toqito/channel_props/tests/test_is_completely_positive.py b/toqito/channel_props/tests/test_is_completely_positive.py index c679d310..bf76fa46 100644 --- a/toqito/channel_props/tests/test_is_completely_positive.py +++ b/toqito/channel_props/tests/test_is_completely_positive.py @@ -1,4 +1,5 @@ """Tests for is_completely_positive.""" + import numpy as np from toqito.channel_props import is_completely_positive diff --git a/toqito/channel_props/tests/test_is_herm_preserving.py b/toqito/channel_props/tests/test_is_herm_preserving.py index e7b31bc0..d2db0e41 100644 --- a/toqito/channel_props/tests/test_is_herm_preserving.py +++ b/toqito/channel_props/tests/test_is_herm_preserving.py @@ -1,4 +1,5 @@ """Tests for is_herm_preserving.""" + import numpy as np from toqito.channel_props import is_herm_preserving diff --git a/toqito/channel_props/tests/test_is_positive.py b/toqito/channel_props/tests/test_is_positive.py index edf1d11a..846f51ce 100644 --- a/toqito/channel_props/tests/test_is_positive.py +++ b/toqito/channel_props/tests/test_is_positive.py @@ -1,4 +1,5 @@ """Tests for is_positive.""" + import numpy as np from toqito.channel_props import is_positive diff --git a/toqito/channel_props/tests/test_is_quantum_channel.py b/toqito/channel_props/tests/test_is_quantum_channel.py index c292b5e1..89f95d6e 100644 --- a/toqito/channel_props/tests/test_is_quantum_channel.py +++ b/toqito/channel_props/tests/test_is_quantum_channel.py @@ -1,4 +1,5 @@ """Tests for is_quantum_channel.""" + import numpy as np from toqito.channel_props import is_quantum_channel diff --git a/toqito/channel_props/tests/test_is_trace_preserving.py b/toqito/channel_props/tests/test_is_trace_preserving.py index 709e73f6..369ed0c5 100644 --- a/toqito/channel_props/tests/test_is_trace_preserving.py +++ b/toqito/channel_props/tests/test_is_trace_preserving.py @@ -1,4 +1,5 @@ """Tests for is_trace_preserving.""" + import numpy as np import pytest @@ -9,9 +10,10 @@ kraus_ops = [[np.identity(2), np.identity(2)], [unitary_mat, -unitary_mat]] -@pytest.mark.parametrize("input_unitary, expected_result, dims", [ - (kraus_ops, False, None), (depolarizing(2), True, None), (depolarizing(4), True, 4), (depolarizing(4), False, 2)]) +@pytest.mark.parametrize( + "input_unitary, expected_result, dims", + [(kraus_ops, False, None), (depolarizing(2), True, None), (depolarizing(4), True, 4), (depolarizing(4), False, 2)], +) def test_is_trace_prserving(input_unitary, expected_result, dims): """Test function works as expected.""" - assert is_trace_preserving(input_unitary, dim = dims) == expected_result - + assert is_trace_preserving(input_unitary, dim=dims) == expected_result diff --git a/toqito/channel_props/tests/test_is_unital.py b/toqito/channel_props/tests/test_is_unital.py index 57c34063..39fb64f9 100644 --- a/toqito/channel_props/tests/test_is_unital.py +++ b/toqito/channel_props/tests/test_is_unital.py @@ -1,4 +1,5 @@ """Tests for is_unital.""" + import numpy as np from toqito.channel_ops import kraus_to_choi diff --git a/toqito/channel_props/tests/test_is_unitary.py b/toqito/channel_props/tests/test_is_unitary.py index ce98e229..0ee9bb6c 100644 --- a/toqito/channel_props/tests/test_is_unitary.py +++ b/toqito/channel_props/tests/test_is_unitary.py @@ -1,4 +1,5 @@ """Tests for is_unitary.""" + import numpy as np from toqito.channel_props import is_unitary diff --git a/toqito/channels/choi.py b/toqito/channels/choi.py index f50ea8ee..8e2d153e 100644 --- a/toqito/channels/choi.py +++ b/toqito/channels/choi.py @@ -1,4 +1,5 @@ """The Choi channel.""" + import numpy as np from toqito.states import max_entangled @@ -102,7 +103,4 @@ def choi(a_var: int = 1, b_var: int = 1, c_var: int = 0) -> np.ndarray: """ psi = max_entangled(3, False, False) - return ( - np.diag([a_var + 1, c_var, b_var, b_var, a_var + 1, c_var, c_var, b_var, a_var + 1]) - - psi * psi.conj().T - ) + return np.diag([a_var + 1, c_var, b_var, b_var, a_var + 1, c_var, c_var, b_var, a_var + 1]) - psi * psi.conj().T diff --git a/toqito/channels/dephasing.py b/toqito/channels/dephasing.py index fdeeda70..f46bb9bc 100644 --- a/toqito/channels/dephasing.py +++ b/toqito/channels/dephasing.py @@ -1,4 +1,5 @@ """The dephasing channel.""" + import numpy as np from toqito.states import max_entangled diff --git a/toqito/channels/depolarizing.py b/toqito/channels/depolarizing.py index 641a8510..af687017 100644 --- a/toqito/channels/depolarizing.py +++ b/toqito/channels/depolarizing.py @@ -1,4 +1,5 @@ """The depolarizing channel.""" + import numpy as np from toqito.states import max_entangled @@ -91,4 +92,4 @@ def depolarizing(dim: int, param_p: float = 0) -> np.ndarray: # Gives a sparse non-normalized state. psi = max_entangled(dim=dim, is_sparse=False, is_normalized=False) - return (1 - param_p) * np.identity(dim ** 2) / dim + param_p * (psi * psi.conj().T) + return (1 - param_p) * np.identity(dim**2) / dim + param_p * (psi * psi.conj().T) diff --git a/toqito/channels/partial_trace.py b/toqito/channels/partial_trace.py index 4fae3c97..4a86eb69 100644 --- a/toqito/channels/partial_trace.py +++ b/toqito/channels/partial_trace.py @@ -1,6 +1,5 @@ """The partial trace.""" - import numpy as np from cvxpy.expressions.expression import Expression from cvxpy.expressions.variable import Variable @@ -166,9 +165,7 @@ def partial_trace( if (num_sys := len(dim)) == 1: dim = np.array([dim[0], len(input_mat) / dim[0]]) if np.abs(dim[1] - np.round(dim[1])) >= 2 * len(input_mat) * np.finfo(float).eps: - raise ValueError( - "Invalid: If `dim` is a scalar, `dim` must evenly divide `len(input_mat)`." - ) + raise ValueError("Invalid: If `dim` is a scalar, `dim` must evenly divide `len(input_mat)`.") dim[1] = np.round(dim[1]) num_sys = 2 @@ -183,9 +180,7 @@ def partial_trace( elif isinstance(sys, int): prod_dim_sys = np.prod(dim[sys]) # pylint: disable=redefined-variable-type else: - raise ValueError( - "Invalid: The variable `sys` must either be of type int or of a list of ints." - ) + raise ValueError("Invalid: The variable `sys` must either be of type int or of a list of ints.") sub_prod = prod_dim / prod_dim_sys sub_sys_vec = prod_dim * np.ones(int(sub_prod)) / sub_prod @@ -214,9 +209,7 @@ def partial_trace( order="F", ) - pt_mat = permuted_reshaped_mat[ - :, :, list(range(0, int(sub_sys_vec[0] ** 2), int(sub_sys_vec[0] + 1))) - ] + pt_mat = permuted_reshaped_mat[:, :, list(range(0, int(sub_sys_vec[0] ** 2), int(sub_sys_vec[0] + 1)))] pt_mat = np.sum(pt_mat, axis=2) return pt_mat diff --git a/toqito/channels/partial_transpose.py b/toqito/channels/partial_transpose.py index 352ed47e..3629be53 100644 --- a/toqito/channels/partial_transpose.py +++ b/toqito/channels/partial_transpose.py @@ -1,6 +1,5 @@ """The partial transpose.""" - import numpy as np from cvxpy.expressions.expression import Expression from cvxpy.expressions.variable import Variable diff --git a/toqito/channels/realignment.py b/toqito/channels/realignment.py index 830d6461..942e1974 100644 --- a/toqito/channels/realignment.py +++ b/toqito/channels/realignment.py @@ -1,6 +1,5 @@ """The realignment channel.""" - import numpy as np from toqito.channels import partial_transpose diff --git a/toqito/channels/reduction.py b/toqito/channels/reduction.py index c8ff89fd..5440b199 100644 --- a/toqito/channels/reduction.py +++ b/toqito/channels/reduction.py @@ -1,4 +1,5 @@ """The reduction channel.""" + import numpy as np from scipy.sparse import identity @@ -47,5 +48,5 @@ def reduction(dim: int, k: int = 1) -> np.ndarray: """ psi = max_entangled(dim, False, False) - identity_matrix = identity(dim ** 2) + identity_matrix = identity(dim**2) return k * identity_matrix.toarray() - psi @ psi.conj().T diff --git a/toqito/channels/tests/test_choi.py b/toqito/channels/tests/test_choi.py index c0d47e6e..1afdae4d 100644 --- a/toqito/channels/tests/test_choi.py +++ b/toqito/channels/tests/test_choi.py @@ -1,4 +1,5 @@ """Test choi.""" + import numpy as np from toqito.channels import choi diff --git a/toqito/channels/tests/test_dephasing.py b/toqito/channels/tests/test_dephasing.py index 76d40c0b..bd297a92 100644 --- a/toqito/channels/tests/test_dephasing.py +++ b/toqito/channels/tests/test_dephasing.py @@ -1,4 +1,5 @@ """Test dephasing.""" + import numpy as np from toqito.channel_ops import apply_channel diff --git a/toqito/channels/tests/test_depolarizing.py b/toqito/channels/tests/test_depolarizing.py index 2248b07d..5adab5dd 100644 --- a/toqito/channels/tests/test_depolarizing.py +++ b/toqito/channels/tests/test_depolarizing.py @@ -1,4 +1,5 @@ """Test depolarizing.""" + import numpy as np from toqito.channel_ops import apply_channel @@ -7,9 +8,7 @@ def test_depolarizing_complete_depolarizing(): """Maps every density matrix to the maximally-mixed state.""" - test_input_mat = np.array( - [[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0], [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]] - ) + test_input_mat = np.array([[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0], [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]]) expected_res = 1 / 4 * np.identity(4) @@ -25,9 +24,7 @@ def test_depolarizing_partially_depolarizing(): param_p = 0.5 res = apply_channel(test_input_mat, depolarizing(4, param_p)) - expected_res = (1 - param_p) * np.trace(test_input_mat) * np.identity( - 4 - ) / 4 + param_p * test_input_mat + expected_res = (1 - param_p) * np.trace(test_input_mat) * np.identity(4) / 4 + param_p * test_input_mat bool_mat = np.isclose(expected_res, res) np.testing.assert_equal(np.all(bool_mat), True) diff --git a/toqito/channels/tests/test_partial_trace.py b/toqito/channels/tests/test_partial_trace.py index efec312a..1eebe464 100644 --- a/toqito/channels/tests/test_partial_trace.py +++ b/toqito/channels/tests/test_partial_trace.py @@ -1,4 +1,5 @@ """Test partial_trace.""" + import re import cvxpy @@ -9,11 +10,23 @@ from toqito.channels import partial_trace -@pytest.mark.parametrize("input_mat, , sys_arg, dim_arg, msg", [ - (np.array([[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0], [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]]), "invalid_input", None, - "Invalid: The variable `sys` must either be of type int or of a list of ints."), - (np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]), [1], [2], - re.escape("Invalid: If `dim` is a scalar, `dim` must evenly divide `len(input_mat)`."))]) +@pytest.mark.parametrize( + "input_mat, , sys_arg, dim_arg, msg", + [ + ( + np.array([[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0], [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]]), + "invalid_input", + None, + "Invalid: The variable `sys` must either be of type int or of a list of ints.", + ), + ( + np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]), + [1], + [2], + re.escape("Invalid: If `dim` is a scalar, `dim` must evenly divide `len(input_mat)`."), + ), + ], +) def test_invalid_input(input_mat, sys_arg, dim_arg, msg): """Test error is raised as expected for an invalid input.""" with pytest.raises(ValueError, match=msg): @@ -35,345 +48,582 @@ def test_partial_trace_cvxpy(): test_input_mat6 = np.arange(1, 4097).reshape(64, 64) test_input_mat7 = np.arange(1, 4097).reshape(64, 64) -@pytest.mark.parametrize("input_mat, expected_result, sys_arg, dim_arg", [ - # use default sys and dim values - (test_input_mat, np.array([[7, 11], [23, 27]]), None, None), - # specify sys value as a list but use default dim value - (test_input_mat, np.array([[12, 14], [20, 22]]), [0], None), - # specify sys value as an int but use default dim value - (test_input_mat, np.array([[12, 14], [20, 22]]), 0, None), - # specify non-zero sys value and default dim value - (test_input_mat, np.array([[7, 11], [23, 27]]), [1], None), - # specify dim value as int and default sys value - (test_input_mat, np.array([[34]]), None, 1), - # specify non-zero sys value and dim value - (test_input_mat, 34, [1], [1, 4]), - # 4 x 4 pt_1 : trace out first subsystem - (test_input_mat, np.array([[12, 14], [20, 22]]), [0], [2, 2]), - # 4 x 4 pt_2 : trace out second subsystem - (test_input_mat, np.array([[7, 11], [23, 27]]), [1], [2, 2]), - # 8 x 8 pt_1 : trace out first subsystem - (test_input_mat2, np.array( - [[38, 40, 42, 44], [54, 56, 58, 60], [70, 72, 74, 76], [86, 88, 90, 92]]), [0], [2, 2, 2]), - # 8 x 8 pt_2 : trace out second subsystem - (test_input_mat2, np.array( - [[20, 22, 28, 30], [36, 38, 44, 46], [84, 86, 92, 94], [100, 102, 108, 110]]), [1], [2, 2, 2]), - # 8 x 8 pt_3 : trace out third subsystem - (test_input_mat2, np.array( - [[11, 15, 19, 23], [43, 47, 51, 55], [75, 79, 83, 87], [107, 111, 115, 119]]), [2], [2, 2, 2]), - # 8 x 8 pt_3 : trace out first and second subsystem - (test_input_mat2, np.array([[112, 116], [144, 148]]), [0, 1], [2, 2, 2]), - # 8 x 8 pt_3 : trace out first and third subsystem - (test_input_mat2, np.array([[94, 102], [158, 166]]), [0, 2], [2, 2, 2]), - # 8 x 8 pt_3 : trace out second and third subsystem - (test_input_mat2, np.array([[58, 74], [186, 202]]), [1, 2], [2, 2, 2]), - # 6-by-6 matrix for subsystems 2 x 3 : trace out first subsystem - (test_input_mat3, np.array([[23, 25, 27], [35, 37, 39], [47, 49, 51]]), [0], [2, 3]), - # 6-by-6 matrix for subsystems 2 x 3 : trace out second subsystem - (test_input_mat3, np.array([[24, 33], [78, 87]]), [1], [2, 3]), - # 6-by-6 matrix for subsystems 2 x 3 : trace out first and second subsystem - (test_input_mat3, np.array([[111]]), [0, 1], [2, 3]), - # 6-by-6 matrix for subsystems 3 x 2 : trace out first and second subsystem - (test_input_mat3, np.array([[111]]),[0, 1], [3, 2]), - # 6-by-6 matrix for subsystems 3 x 2 : trace out first subsystem - (test_input_mat3, np.array([[45, 48], [63, 66]]),[0], [3, 2]), - # 6-by-6 matrix for subsystems 3 x 2 : trace out second subsystem - (test_input_mat3, np.array([[9, 13, 17], [33, 37, 41], [57, 61, 65]]),[1], [3, 2]), - # 9-by-9 matrix for subsystems 3 x 3 : trace out first subsystem - (test_input_mat4,np.array([[93, 96, 99], [120, 123, 126], [147, 150, 153]]) ,[0], [3, 3]), - # 9-by-9 matrix for subsystems 3 x 3 : trace out second subsystem - (test_input_mat4, np.array([[33, 42, 51], [114, 123, 132], [195, 204, 213]]),[1], [3, 3]), - # 9-by-9 matrix for subsystems 3 x 3 : trace out first and second subsystem - (test_input_mat4, np.array([[369]]),[0, 1], [3, 3]), - # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out first subsystem - (test_input_mat5, np.array( - [ - [138, 140, 142, 144, 146, 148, 150, 152], - [170, 172, 174, 176, 178, 180, 182, 184], - [202, 204, 206, 208, 210, 212, 214, 216], - [234, 236, 238, 240, 242, 244, 246, 248], - [266, 268, 270, 272, 274, 276, 278, 280], - [298, 300, 302, 304, 306, 308, 310, 312], - [330, 332, 334, 336, 338, 340, 342, 344], - [362, 364, 366, 368, 370, 372, 374, 376], - ] - ),[0], [2, 2, 2, 2]), - # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out second subsystem - (test_input_mat5, np.array( - [ - [70, 72, 74, 76, 86, 88, 90, 92], - [102, 104, 106, 108, 118, 120, 122, 124], - [134, 136, 138, 140, 150, 152, 154, 156], - [166, 168, 170, 172, 182, 184, 186, 188], - [326, 328, 330, 332, 342, 344, 346, 348], - [358, 360, 362, 364, 374, 376, 378, 380], - [390, 392, 394, 396, 406, 408, 410, 412], - [422, 424, 426, 428, 438, 440, 442, 444], - ] - ),[1], [2, 2, 2, 2]), - # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out third subsystem - (test_input_mat5, np.array( - [ - [36, 38, 44, 46, 52, 54, 60, 62], - [68, 70, 76, 78, 84, 86, 92, 94], - [164, 166, 172, 174, 180, 182, 188, 190], - [196, 198, 204, 206, 212, 214, 220, 222], - [292, 294, 300, 302, 308, 310, 316, 318], - [324, 326, 332, 334, 340, 342, 348, 350], - [420, 422, 428, 430, 436, 438, 444, 446], - [452, 454, 460, 462, 468, 470, 476, 478], - ] - ),[2], [2, 2, 2, 2]), - # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out fourth subsystem - (test_input_mat5, np.array( - [ - [19, 23, 27, 31, 35, 39, 43, 47], - [83, 87, 91, 95, 99, 103, 107, 111], - [147, 151, 155, 159, 163, 167, 171, 175], - [211, 215, 219, 223, 227, 231, 235, 239], - [275, 279, 283, 287, 291, 295, 299, 303], - [339, 343, 347, 351, 355, 359, 363, 367], - [403, 407, 411, 415, 419, 423, 427, 431], - [467, 471, 475, 479, 483, 487, 491, 495], - ] - ),[3], [2, 2, 2, 2]), - # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out first and second subsystem - (test_input_mat5, np.array( - [ - [412, 416, 420, 424], - [476, 480, 484, 488], - [540, 544, 548, 552], - [604, 608, 612, 616], - ] - ),[0, 1], [2, 2, 2, 2]), - # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out first and third subsystem - (test_input_mat5, np.array( - [ - [344, 348, 360, 364], - [408, 412, 424, 428], - [600, 604, 616, 620], - [664, 668, 680, 684], - ] - ),[0, 2], [2, 2, 2, 2]), - # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out first and fourth subsystem - (test_input_mat5, np.array( - [[310, 318, 326, 334], [438, 446, 454, 462], [566, 574, 582, 590], [694, 702, 710, 718]] - ),[0, 3], [2, 2, 2, 2]), - # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out second and third subsystem - (test_input_mat5, np.array( - [[208, 212, 240, 244], [272, 276, 304, 308], [720, 724, 752, 756], [784, 788, 816, 820]] - ),[1, 2], [2, 2, 2, 2]), - # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out second and fourth subsystem - (test_input_mat5, np.array( - [[174, 182, 206, 214], [302, 310, 334, 342], [686, 694, 718, 726], [814, 822, 846, 854]] - ),[1, 3], [2, 2, 2, 2]), - # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out third and fourth subsystem - (test_input_mat5, np.array( - [[106, 122, 138, 154], [362, 378, 394, 410], [618, 634, 650, 666], [874, 890, 906, 922]] - ),[2, 3], [2, 2, 2, 2]), - # 16-by-16 matrix for subsystems 2 x 4 : trace out first subsystem - (test_input_mat5, np.array( - [ - [138, 140, 142, 144, 146, 148, 150, 152], - [170, 172, 174, 176, 178, 180, 182, 184], - [202, 204, 206, 208, 210, 212, 214, 216], - [234, 236, 238, 240, 242, 244, 246, 248], - [266, 268, 270, 272, 274, 276, 278, 280], - [298, 300, 302, 304, 306, 308, 310, 312], - [330, 332, 334, 336, 338, 340, 342, 344], - [362, 364, 366, 368, 370, 372, 374, 376], - ] - ),[0], [2, 2, 4]), - # 16-by-16 matrix for subsystems 2 x 4 : trace out second subsystem - (test_input_mat5, np.array( - [ - [70, 72, 74, 76, 86, 88, 90, 92], - [102, 104, 106, 108, 118, 120, 122, 124], - [134, 136, 138, 140, 150, 152, 154, 156], - [166, 168, 170, 172, 182, 184, 186, 188], - [326, 328, 330, 332, 342, 344, 346, 348], - [358, 360, 362, 364, 374, 376, 378, 380], - [390, 392, 394, 396, 406, 408, 410, 412], - [422, 424, 426, 428, 438, 440, 442, 444], - ] - ),[1], [2, 2, 4]), - # 16-by-16 matrix for subsystems 2 x 4 : trace out third subsystem - (test_input_mat5, np.array( - [[106, 122, 138, 154], [362, 378, 394, 410], [618, 634, 650, 666], [874, 890, 906, 922]] - ),[2], [2, 2, 4]), - # 16-by-16 matrix for subsystems 2 x 4 : trace out first and second subsystem - (test_input_mat5, np.array( - [ - [412, 416, 420, 424], - [476, 480, 484, 488], - [540, 544, 548, 552], - [604, 608, 612, 616], - ] - ),[0, 1], [2, 2, 4]), - # 16-by-16 matrix for subsystems 2 x 4 : trace out first and third subsystem - (test_input_mat5, np.array([[756, 788], [1268, 1300]]),[0, 2], [2, 2, 4]), - # 16-by-16 matrix for subsystems 2 x 4 : trace out second and third subsystem - (test_input_mat5, np.array([[484, 548], [1508, 1572]]),[1, 2], [2, 2, 4]), - # 16-by-16 matrix for subsystems 4 x 2 : trace out first subsystem - (test_input_mat5, np.array( - [ - [412, 416, 420, 424], - [476, 480, 484, 488], - [540, 544, 548, 552], - [604, 608, 612, 616], - ] - ),[0], [4, 2, 2]), - # 16-by-16 matrix for subsystems 4 x 2 : trace out second subsystem - (test_input_mat5, np.array( - [ - [36, 38, 44, 46, 52, 54, 60, 62], - [68, 70, 76, 78, 84, 86, 92, 94], - [164, 166, 172, 174, 180, 182, 188, 190], - [196, 198, 204, 206, 212, 214, 220, 222], - [292, 294, 300, 302, 308, 310, 316, 318], - [324, 326, 332, 334, 340, 342, 348, 350], - [420, 422, 428, 430, 436, 438, 444, 446], - [452, 454, 460, 462, 468, 470, 476, 478], - ] - ),[1], [4, 2, 2]), - # 16-by-16 matrix for subsystems 4 x 2 : trace out third subsystem - (test_input_mat5, np.array( - [ - [19, 23, 27, 31, 35, 39, 43, 47], - [83, 87, 91, 95, 99, 103, 107, 111], - [147, 151, 155, 159, 163, 167, 171, 175], - [211, 215, 219, 223, 227, 231, 235, 239], - [275, 279, 283, 287, 291, 295, 299, 303], - [339, 343, 347, 351, 355, 359, 363, 367], - [403, 407, 411, 415, 419, 423, 427, 431], - [467, 471, 475, 479, 483, 487, 491, 495], - ] - ),[2], [4, 2, 2]), - # 16-by-16 matrix for subsystems 4 x 2 : trace out first and second subsystem - (test_input_mat5, np.array([[960, 968], [1088, 1096]]),[0, 1], [4, 2, 2]), - # 16-by-16 matrix for subsystems 4 x 2 : trace out first and third subsystem - (test_input_mat5, np.array([[892, 908], [1148, 1164]]),[0, 2], [4, 2, 2]), - # 16-by-16 matrix for subsystems 4 x 2 : trace out second and third subsystem - (test_input_mat5, np.array( - [[106, 122, 138, 154], [362, 378, 394, 410], [618, 634, 650, 666], [874, 890, 906, 922]] - ),[1, 2], [4, 2, 2]), - # 16-by-16 matrix for subsystems 4 x 4 : trace out first subsystem - (test_input_mat5, np.array( - [[412, 416, 420, 424], [476, 480, 484, 488], [540, 544, 548, 552], [604, 608, 612, 616]] - ),[0], [4, 4]), - # 16-by-16 matrix for subsystems 4 x 4 : trace out second subsystem - (test_input_mat5, np.array( - [[106, 122, 138, 154], [362, 378, 394, 410], [618, 634, 650, 666], [874, 890, 906, 922]] - ),[1], [4, 4]), - # To Do : 64-by-64 matrix for subsystems 4 x 4 x 2 x 2 - # Trace out first subsystem: - # Trace out second subsystem: - # Trace out third subsystem: - # Trace out fourth subsystem: - # Trace out first and second subsystem: - # Trace out first and third subsystem: - # Trace out first and fourth subsystem: - # Trace out second and third subsystem: - # Trace out second and fourth subsystem: - # Trace out third and fourth subsystem: - # 64-by-64 matrix for subsystems 4 x 4 x 2 x 2 : trace out third and fourth subsystem - (test_input_mat6, np.array( - [ - [394, 410, 426, 442, 458, 474, 490, 506, 522, 538, 554, 570, 586, 602, 618, 634], - [1418, 1434, 1450, 1466, 1482, 1498, 1514, 1530, 1546, 1562, 1578, 1594, 1610, 1626, 1642,1658], - [2442, 2458, 2474, 2490, 2506, 2522, 2538, 2554, 2570, 2586, 2602, 2618, 2634, 2650, 2666, 2682], - [3466, 3482, 3498, 3514, 3530, 3546, 3562, 3578, 3594, 3610, 3626, 3642, 3658, 3674, 3690,3706], - [4490, 4506, 4522, 4538, 4554, 4570, 4586, 4602, 4618, 4634, 4650, 4666, 4682, 4698, 4714, 4730], - [5514, 5530, 5546, 5562, 5578, 5594, 5610, 5626, 5642, 5658, 5674, 5690, 5706, 5722, 5738, 5754], - [6538, 6554, 6570, 6586, 6602, 6618, 6634, 6650, 6666, 6682, 6698, 6714, 6730, 6746, 6762, 6778], - [7562, 7578, 7594, 7610, 7626, 7642, 7658, 7674, 7690, 7706, 7722, 7738, 7754, 7770, 7786, 7802], - [8586, 8602, 8618, 8634, 8650, 8666, 8682, 8698, 8714, 8730, 8746, 8762, 8778, 8794, 8810, 8826], - [9610, 9626, 9642, 9658, 9674, 9690, 9706, 9722, 9738, 9754, 9770, 9786, 9802, 9818, 9834, 9850], - [ - 10634, 10650, 10666, 10682, 10698, 10714, 10730, 10746, 10762, 10778, 10794, 10810, 10826, - 10842, 10858, 10874], - [ - 11658, 11674, 11690, 11706, 11722, 11738, 11754, 11770, 11786, 11802, 11818, 11834, 11850, 11866, - 11882, 11898], - [ - 12682, 12698, 12714, 12730, 12746, 12762, 12778, 12794, 12810, 12826, 12842, 12858, 12874, 12890, - 12906, 12922], - [ - 13706, 13722, 13738, 13754, 13770, 13786, 13802, 13818, 13834, 13850, 13866, 13882, 13898, 13914, - 13930, 13946], - [ - 14730, 14746, 14762, 14778, 14794, 14810, 14826, 14842, 14858, 14874, 14890, 14906, 14922,14938, - 14954, 14970], - [ - 15754, 15770, 15786, 15802, 15818, 15834, 15850, 15866, 15882, 15898, 15914, 15930, 15946, 15962, 15978, - 15994], - ] - ),[2, 3], [4, 4, 2, 2]), - # 64-by-64 matrix for subsystems 4 x 4 x 2 x 2 : trace out first, second and third subsystem - (test_input_mat6, np.array([[64512, 64544], [66560, 66592]]),[0, 1, 2], [4, 4, 2, 2]), - # 64-by-64 matrix for subsystems 4 x 4 x 2 x 2 : trace out first, fourth and third subsystem - (test_input_mat6, np.array( - [ - [26536, 26600, 26664, 26728], - [30632, 30696, 30760, 30824], - [34728, 34792, 34856, 34920], - [38824, 38888, 38952, 39016], - ] - ),[0, 2, 3], [4, 4, 2, 2]), - # 64-by-64 matrix for subsystems 4 x 4 x 2 x 2 : trace out first, second, fourth and third subsystem - (test_input_mat6, np.array([[131104]]),[0, 1, 2, 3], [4, 4, 2, 2]), - # 64-by-64 matrix for subsystems 4 x 4 x 4 : trace out first subsystem - (test_input_mat7, np.array([[ - 6244, 6248, 6252, 6256, 6260, 6264, 6268, 6272, 6276, 6280, 6284, 6288, 6292, 6296, 6300, 6304], - [6500, 6504, 6508, 6512, 6516, 6520, 6524, 6528, 6532, 6536, 6540, 6544, 6548, 6552, 6556, 6560], - [6756, 6760, 6764, 6768, 6772, 6776, 6780, 6784, 6788, 6792, 6796, 6800, 6804, 6808, 6812, 6816], - [7012, 7016, 7020, 7024, 7028, 7032, 7036, 7040, 7044, 7048, 7052, 7056, 7060, 7064, 7068, 7072], - [7268, 7272, 7276, 7280, 7284, 7288, 7292, 7296, 7300, 7304, 7308, 7312, 7316, 7320, 7324, 7328], - [7524, 7528, 7532, 7536, 7540, 7544, 7548, 7552, 7556, 7560, 7564, 7568, 7572, 7576, 7580, 7584], - [7780, 7784, 7788, 7792, 7796, 7800, 7804, 7808, 7812, 7816, 7820, 7824, 7828, 7832, 7836, 7840], - [8036, 8040, 8044, 8048, 8052, 8056, 8060, 8064, 8068, 8072, 8076, 8080, 8084, 8088, 8092, 8096], - [8292, 8296, 8300, 8304, 8308, 8312, 8316, 8320, 8324, 8328, 8332, 8336, 8340, 8344, 8348, 8352], - [8548, 8552, 8556, 8560, 8564, 8568, 8572, 8576, 8580, 8584, 8588, 8592, 8596, 8600, 8604, 8608], - [8804, 8808, 8812, 8816, 8820, 8824, 8828, 8832, 8836, 8840, 8844, 8848, 8852, 8856, 8860, 8864], - [9060, 9064, 9068, 9072, 9076, 9080, 9084, 9088, 9092, 9096, 9100, 9104, 9108, 9112, 9116, 9120], - [9316, 9320, 9324, 9328, 9332, 9336, 9340, 9344, 9348, 9352, 9356, 9360, 9364, 9368, 9372, 9376], - [9572, 9576, 9580, 9584, 9588, 9592, 9596, 9600, 9604, 9608, 9612, 9616, 9620, 9624, 9628, 9632], - [9828, 9832, 9836, 9840, 9844, 9848, 9852, 9856, 9860, 9864, 9868, 9872, 9876, 9880, 9884, 9888], - [ - 10084, 10088, 10092, 10096, 10100, 10104, 10108, 10112, 10116, 10120, 10124, 10128, 10132, 10136, - 10140, 10144]]),[0], [4, 4, 4]), - # 64-by-64 matrix for subsystems 4 x 4 x 4 : trace out first and second subsystem - (test_input_mat7, np.array( - [ - [31216, 31232, 31248, 31264], - [32240, 32256, 32272, 32288], - [33264, 33280, 33296, 33312], - [34288, 34304, 34320, 34336], - ] - ),[0, 1], [4, 4, 4]), - # 64-by-64 matrix for subsystems 4 x 4 x 4 : trace out first and third subsystem - (test_input_mat7, np.array( - [ - [26536, 26600, 26664, 26728], - [30632, 30696, 30760, 30824], - [34728, 34792, 34856, 34920], - [38824, 38888, 38952, 39016], - ] - ),[0, 2], [4, 4, 4]), - # 64-by-64 matrix for subsystems 4 x 4 x 4 : trace out second and third subsystem - (test_input_mat7, np.array( - [ - [7816, 8072, 8328, 8584], - [24200, 24456, 24712, 24968], - [40584, 40840, 41096, 41352], - [56968, 57224, 57480, 57736], - ] - ),[1, 2], [4, 4, 4]), - # 64-by-64 matrix for subsystems 4 x 4 x 4 : trace out first, second and third subsystem - (test_input_mat7, np.array([[131104]]),[0, 1, 2], [4, 4, 4]),]) + +@pytest.mark.parametrize( + "input_mat, expected_result, sys_arg, dim_arg", + [ + # use default sys and dim values + (test_input_mat, np.array([[7, 11], [23, 27]]), None, None), + # specify sys value as a list but use default dim value + (test_input_mat, np.array([[12, 14], [20, 22]]), [0], None), + # specify sys value as an int but use default dim value + (test_input_mat, np.array([[12, 14], [20, 22]]), 0, None), + # specify non-zero sys value and default dim value + (test_input_mat, np.array([[7, 11], [23, 27]]), [1], None), + # specify dim value as int and default sys value + (test_input_mat, np.array([[34]]), None, 1), + # specify non-zero sys value and dim value + (test_input_mat, 34, [1], [1, 4]), + # 4 x 4 pt_1 : trace out first subsystem + (test_input_mat, np.array([[12, 14], [20, 22]]), [0], [2, 2]), + # 4 x 4 pt_2 : trace out second subsystem + (test_input_mat, np.array([[7, 11], [23, 27]]), [1], [2, 2]), + # 8 x 8 pt_1 : trace out first subsystem + ( + test_input_mat2, + np.array([[38, 40, 42, 44], [54, 56, 58, 60], [70, 72, 74, 76], [86, 88, 90, 92]]), + [0], + [2, 2, 2], + ), + # 8 x 8 pt_2 : trace out second subsystem + ( + test_input_mat2, + np.array([[20, 22, 28, 30], [36, 38, 44, 46], [84, 86, 92, 94], [100, 102, 108, 110]]), + [1], + [2, 2, 2], + ), + # 8 x 8 pt_3 : trace out third subsystem + ( + test_input_mat2, + np.array([[11, 15, 19, 23], [43, 47, 51, 55], [75, 79, 83, 87], [107, 111, 115, 119]]), + [2], + [2, 2, 2], + ), + # 8 x 8 pt_3 : trace out first and second subsystem + (test_input_mat2, np.array([[112, 116], [144, 148]]), [0, 1], [2, 2, 2]), + # 8 x 8 pt_3 : trace out first and third subsystem + (test_input_mat2, np.array([[94, 102], [158, 166]]), [0, 2], [2, 2, 2]), + # 8 x 8 pt_3 : trace out second and third subsystem + (test_input_mat2, np.array([[58, 74], [186, 202]]), [1, 2], [2, 2, 2]), + # 6-by-6 matrix for subsystems 2 x 3 : trace out first subsystem + (test_input_mat3, np.array([[23, 25, 27], [35, 37, 39], [47, 49, 51]]), [0], [2, 3]), + # 6-by-6 matrix for subsystems 2 x 3 : trace out second subsystem + (test_input_mat3, np.array([[24, 33], [78, 87]]), [1], [2, 3]), + # 6-by-6 matrix for subsystems 2 x 3 : trace out first and second subsystem + (test_input_mat3, np.array([[111]]), [0, 1], [2, 3]), + # 6-by-6 matrix for subsystems 3 x 2 : trace out first and second subsystem + (test_input_mat3, np.array([[111]]), [0, 1], [3, 2]), + # 6-by-6 matrix for subsystems 3 x 2 : trace out first subsystem + (test_input_mat3, np.array([[45, 48], [63, 66]]), [0], [3, 2]), + # 6-by-6 matrix for subsystems 3 x 2 : trace out second subsystem + (test_input_mat3, np.array([[9, 13, 17], [33, 37, 41], [57, 61, 65]]), [1], [3, 2]), + # 9-by-9 matrix for subsystems 3 x 3 : trace out first subsystem + (test_input_mat4, np.array([[93, 96, 99], [120, 123, 126], [147, 150, 153]]), [0], [3, 3]), + # 9-by-9 matrix for subsystems 3 x 3 : trace out second subsystem + (test_input_mat4, np.array([[33, 42, 51], [114, 123, 132], [195, 204, 213]]), [1], [3, 3]), + # 9-by-9 matrix for subsystems 3 x 3 : trace out first and second subsystem + (test_input_mat4, np.array([[369]]), [0, 1], [3, 3]), + # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out first subsystem + ( + test_input_mat5, + np.array( + [ + [138, 140, 142, 144, 146, 148, 150, 152], + [170, 172, 174, 176, 178, 180, 182, 184], + [202, 204, 206, 208, 210, 212, 214, 216], + [234, 236, 238, 240, 242, 244, 246, 248], + [266, 268, 270, 272, 274, 276, 278, 280], + [298, 300, 302, 304, 306, 308, 310, 312], + [330, 332, 334, 336, 338, 340, 342, 344], + [362, 364, 366, 368, 370, 372, 374, 376], + ] + ), + [0], + [2, 2, 2, 2], + ), + # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out second subsystem + ( + test_input_mat5, + np.array( + [ + [70, 72, 74, 76, 86, 88, 90, 92], + [102, 104, 106, 108, 118, 120, 122, 124], + [134, 136, 138, 140, 150, 152, 154, 156], + [166, 168, 170, 172, 182, 184, 186, 188], + [326, 328, 330, 332, 342, 344, 346, 348], + [358, 360, 362, 364, 374, 376, 378, 380], + [390, 392, 394, 396, 406, 408, 410, 412], + [422, 424, 426, 428, 438, 440, 442, 444], + ] + ), + [1], + [2, 2, 2, 2], + ), + # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out third subsystem + ( + test_input_mat5, + np.array( + [ + [36, 38, 44, 46, 52, 54, 60, 62], + [68, 70, 76, 78, 84, 86, 92, 94], + [164, 166, 172, 174, 180, 182, 188, 190], + [196, 198, 204, 206, 212, 214, 220, 222], + [292, 294, 300, 302, 308, 310, 316, 318], + [324, 326, 332, 334, 340, 342, 348, 350], + [420, 422, 428, 430, 436, 438, 444, 446], + [452, 454, 460, 462, 468, 470, 476, 478], + ] + ), + [2], + [2, 2, 2, 2], + ), + # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out fourth subsystem + ( + test_input_mat5, + np.array( + [ + [19, 23, 27, 31, 35, 39, 43, 47], + [83, 87, 91, 95, 99, 103, 107, 111], + [147, 151, 155, 159, 163, 167, 171, 175], + [211, 215, 219, 223, 227, 231, 235, 239], + [275, 279, 283, 287, 291, 295, 299, 303], + [339, 343, 347, 351, 355, 359, 363, 367], + [403, 407, 411, 415, 419, 423, 427, 431], + [467, 471, 475, 479, 483, 487, 491, 495], + ] + ), + [3], + [2, 2, 2, 2], + ), + # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out first and second subsystem + ( + test_input_mat5, + np.array( + [ + [412, 416, 420, 424], + [476, 480, 484, 488], + [540, 544, 548, 552], + [604, 608, 612, 616], + ] + ), + [0, 1], + [2, 2, 2, 2], + ), + # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out first and third subsystem + ( + test_input_mat5, + np.array( + [ + [344, 348, 360, 364], + [408, 412, 424, 428], + [600, 604, 616, 620], + [664, 668, 680, 684], + ] + ), + [0, 2], + [2, 2, 2, 2], + ), + # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out first and fourth subsystem + ( + test_input_mat5, + np.array([[310, 318, 326, 334], [438, 446, 454, 462], [566, 574, 582, 590], [694, 702, 710, 718]]), + [0, 3], + [2, 2, 2, 2], + ), + # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out second and third subsystem + ( + test_input_mat5, + np.array([[208, 212, 240, 244], [272, 276, 304, 308], [720, 724, 752, 756], [784, 788, 816, 820]]), + [1, 2], + [2, 2, 2, 2], + ), + # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out second and fourth subsystem + ( + test_input_mat5, + np.array([[174, 182, 206, 214], [302, 310, 334, 342], [686, 694, 718, 726], [814, 822, 846, 854]]), + [1, 3], + [2, 2, 2, 2], + ), + # 16-by-16 matrix for subsystems (2 x 2) x (2 x 2) : trace out third and fourth subsystem + ( + test_input_mat5, + np.array([[106, 122, 138, 154], [362, 378, 394, 410], [618, 634, 650, 666], [874, 890, 906, 922]]), + [2, 3], + [2, 2, 2, 2], + ), + # 16-by-16 matrix for subsystems 2 x 4 : trace out first subsystem + ( + test_input_mat5, + np.array( + [ + [138, 140, 142, 144, 146, 148, 150, 152], + [170, 172, 174, 176, 178, 180, 182, 184], + [202, 204, 206, 208, 210, 212, 214, 216], + [234, 236, 238, 240, 242, 244, 246, 248], + [266, 268, 270, 272, 274, 276, 278, 280], + [298, 300, 302, 304, 306, 308, 310, 312], + [330, 332, 334, 336, 338, 340, 342, 344], + [362, 364, 366, 368, 370, 372, 374, 376], + ] + ), + [0], + [2, 2, 4], + ), + # 16-by-16 matrix for subsystems 2 x 4 : trace out second subsystem + ( + test_input_mat5, + np.array( + [ + [70, 72, 74, 76, 86, 88, 90, 92], + [102, 104, 106, 108, 118, 120, 122, 124], + [134, 136, 138, 140, 150, 152, 154, 156], + [166, 168, 170, 172, 182, 184, 186, 188], + [326, 328, 330, 332, 342, 344, 346, 348], + [358, 360, 362, 364, 374, 376, 378, 380], + [390, 392, 394, 396, 406, 408, 410, 412], + [422, 424, 426, 428, 438, 440, 442, 444], + ] + ), + [1], + [2, 2, 4], + ), + # 16-by-16 matrix for subsystems 2 x 4 : trace out third subsystem + ( + test_input_mat5, + np.array([[106, 122, 138, 154], [362, 378, 394, 410], [618, 634, 650, 666], [874, 890, 906, 922]]), + [2], + [2, 2, 4], + ), + # 16-by-16 matrix for subsystems 2 x 4 : trace out first and second subsystem + ( + test_input_mat5, + np.array( + [ + [412, 416, 420, 424], + [476, 480, 484, 488], + [540, 544, 548, 552], + [604, 608, 612, 616], + ] + ), + [0, 1], + [2, 2, 4], + ), + # 16-by-16 matrix for subsystems 2 x 4 : trace out first and third subsystem + (test_input_mat5, np.array([[756, 788], [1268, 1300]]), [0, 2], [2, 2, 4]), + # 16-by-16 matrix for subsystems 2 x 4 : trace out second and third subsystem + (test_input_mat5, np.array([[484, 548], [1508, 1572]]), [1, 2], [2, 2, 4]), + # 16-by-16 matrix for subsystems 4 x 2 : trace out first subsystem + ( + test_input_mat5, + np.array( + [ + [412, 416, 420, 424], + [476, 480, 484, 488], + [540, 544, 548, 552], + [604, 608, 612, 616], + ] + ), + [0], + [4, 2, 2], + ), + # 16-by-16 matrix for subsystems 4 x 2 : trace out second subsystem + ( + test_input_mat5, + np.array( + [ + [36, 38, 44, 46, 52, 54, 60, 62], + [68, 70, 76, 78, 84, 86, 92, 94], + [164, 166, 172, 174, 180, 182, 188, 190], + [196, 198, 204, 206, 212, 214, 220, 222], + [292, 294, 300, 302, 308, 310, 316, 318], + [324, 326, 332, 334, 340, 342, 348, 350], + [420, 422, 428, 430, 436, 438, 444, 446], + [452, 454, 460, 462, 468, 470, 476, 478], + ] + ), + [1], + [4, 2, 2], + ), + # 16-by-16 matrix for subsystems 4 x 2 : trace out third subsystem + ( + test_input_mat5, + np.array( + [ + [19, 23, 27, 31, 35, 39, 43, 47], + [83, 87, 91, 95, 99, 103, 107, 111], + [147, 151, 155, 159, 163, 167, 171, 175], + [211, 215, 219, 223, 227, 231, 235, 239], + [275, 279, 283, 287, 291, 295, 299, 303], + [339, 343, 347, 351, 355, 359, 363, 367], + [403, 407, 411, 415, 419, 423, 427, 431], + [467, 471, 475, 479, 483, 487, 491, 495], + ] + ), + [2], + [4, 2, 2], + ), + # 16-by-16 matrix for subsystems 4 x 2 : trace out first and second subsystem + (test_input_mat5, np.array([[960, 968], [1088, 1096]]), [0, 1], [4, 2, 2]), + # 16-by-16 matrix for subsystems 4 x 2 : trace out first and third subsystem + (test_input_mat5, np.array([[892, 908], [1148, 1164]]), [0, 2], [4, 2, 2]), + # 16-by-16 matrix for subsystems 4 x 2 : trace out second and third subsystem + ( + test_input_mat5, + np.array([[106, 122, 138, 154], [362, 378, 394, 410], [618, 634, 650, 666], [874, 890, 906, 922]]), + [1, 2], + [4, 2, 2], + ), + # 16-by-16 matrix for subsystems 4 x 4 : trace out first subsystem + ( + test_input_mat5, + np.array([[412, 416, 420, 424], [476, 480, 484, 488], [540, 544, 548, 552], [604, 608, 612, 616]]), + [0], + [4, 4], + ), + # 16-by-16 matrix for subsystems 4 x 4 : trace out second subsystem + ( + test_input_mat5, + np.array([[106, 122, 138, 154], [362, 378, 394, 410], [618, 634, 650, 666], [874, 890, 906, 922]]), + [1], + [4, 4], + ), + # To Do : 64-by-64 matrix for subsystems 4 x 4 x 2 x 2 + # Trace out first subsystem: + # Trace out second subsystem: + # Trace out third subsystem: + # Trace out fourth subsystem: + # Trace out first and second subsystem: + # Trace out first and third subsystem: + # Trace out first and fourth subsystem: + # Trace out second and third subsystem: + # Trace out second and fourth subsystem: + # Trace out third and fourth subsystem: + # 64-by-64 matrix for subsystems 4 x 4 x 2 x 2 : trace out third and fourth subsystem + ( + test_input_mat6, + np.array( + [ + [394, 410, 426, 442, 458, 474, 490, 506, 522, 538, 554, 570, 586, 602, 618, 634], + [1418, 1434, 1450, 1466, 1482, 1498, 1514, 1530, 1546, 1562, 1578, 1594, 1610, 1626, 1642, 1658], + [2442, 2458, 2474, 2490, 2506, 2522, 2538, 2554, 2570, 2586, 2602, 2618, 2634, 2650, 2666, 2682], + [3466, 3482, 3498, 3514, 3530, 3546, 3562, 3578, 3594, 3610, 3626, 3642, 3658, 3674, 3690, 3706], + [4490, 4506, 4522, 4538, 4554, 4570, 4586, 4602, 4618, 4634, 4650, 4666, 4682, 4698, 4714, 4730], + [5514, 5530, 5546, 5562, 5578, 5594, 5610, 5626, 5642, 5658, 5674, 5690, 5706, 5722, 5738, 5754], + [6538, 6554, 6570, 6586, 6602, 6618, 6634, 6650, 6666, 6682, 6698, 6714, 6730, 6746, 6762, 6778], + [7562, 7578, 7594, 7610, 7626, 7642, 7658, 7674, 7690, 7706, 7722, 7738, 7754, 7770, 7786, 7802], + [8586, 8602, 8618, 8634, 8650, 8666, 8682, 8698, 8714, 8730, 8746, 8762, 8778, 8794, 8810, 8826], + [9610, 9626, 9642, 9658, 9674, 9690, 9706, 9722, 9738, 9754, 9770, 9786, 9802, 9818, 9834, 9850], + [ + 10634, + 10650, + 10666, + 10682, + 10698, + 10714, + 10730, + 10746, + 10762, + 10778, + 10794, + 10810, + 10826, + 10842, + 10858, + 10874, + ], + [ + 11658, + 11674, + 11690, + 11706, + 11722, + 11738, + 11754, + 11770, + 11786, + 11802, + 11818, + 11834, + 11850, + 11866, + 11882, + 11898, + ], + [ + 12682, + 12698, + 12714, + 12730, + 12746, + 12762, + 12778, + 12794, + 12810, + 12826, + 12842, + 12858, + 12874, + 12890, + 12906, + 12922, + ], + [ + 13706, + 13722, + 13738, + 13754, + 13770, + 13786, + 13802, + 13818, + 13834, + 13850, + 13866, + 13882, + 13898, + 13914, + 13930, + 13946, + ], + [ + 14730, + 14746, + 14762, + 14778, + 14794, + 14810, + 14826, + 14842, + 14858, + 14874, + 14890, + 14906, + 14922, + 14938, + 14954, + 14970, + ], + [ + 15754, + 15770, + 15786, + 15802, + 15818, + 15834, + 15850, + 15866, + 15882, + 15898, + 15914, + 15930, + 15946, + 15962, + 15978, + 15994, + ], + ] + ), + [2, 3], + [4, 4, 2, 2], + ), + # 64-by-64 matrix for subsystems 4 x 4 x 2 x 2 : trace out first, second and third subsystem + (test_input_mat6, np.array([[64512, 64544], [66560, 66592]]), [0, 1, 2], [4, 4, 2, 2]), + # 64-by-64 matrix for subsystems 4 x 4 x 2 x 2 : trace out first, fourth and third subsystem + ( + test_input_mat6, + np.array( + [ + [26536, 26600, 26664, 26728], + [30632, 30696, 30760, 30824], + [34728, 34792, 34856, 34920], + [38824, 38888, 38952, 39016], + ] + ), + [0, 2, 3], + [4, 4, 2, 2], + ), + # 64-by-64 matrix for subsystems 4 x 4 x 2 x 2 : trace out first, second, fourth and third subsystem + (test_input_mat6, np.array([[131104]]), [0, 1, 2, 3], [4, 4, 2, 2]), + # 64-by-64 matrix for subsystems 4 x 4 x 4 : trace out first subsystem + ( + test_input_mat7, + np.array( + [ + [6244, 6248, 6252, 6256, 6260, 6264, 6268, 6272, 6276, 6280, 6284, 6288, 6292, 6296, 6300, 6304], + [6500, 6504, 6508, 6512, 6516, 6520, 6524, 6528, 6532, 6536, 6540, 6544, 6548, 6552, 6556, 6560], + [6756, 6760, 6764, 6768, 6772, 6776, 6780, 6784, 6788, 6792, 6796, 6800, 6804, 6808, 6812, 6816], + [7012, 7016, 7020, 7024, 7028, 7032, 7036, 7040, 7044, 7048, 7052, 7056, 7060, 7064, 7068, 7072], + [7268, 7272, 7276, 7280, 7284, 7288, 7292, 7296, 7300, 7304, 7308, 7312, 7316, 7320, 7324, 7328], + [7524, 7528, 7532, 7536, 7540, 7544, 7548, 7552, 7556, 7560, 7564, 7568, 7572, 7576, 7580, 7584], + [7780, 7784, 7788, 7792, 7796, 7800, 7804, 7808, 7812, 7816, 7820, 7824, 7828, 7832, 7836, 7840], + [8036, 8040, 8044, 8048, 8052, 8056, 8060, 8064, 8068, 8072, 8076, 8080, 8084, 8088, 8092, 8096], + [8292, 8296, 8300, 8304, 8308, 8312, 8316, 8320, 8324, 8328, 8332, 8336, 8340, 8344, 8348, 8352], + [8548, 8552, 8556, 8560, 8564, 8568, 8572, 8576, 8580, 8584, 8588, 8592, 8596, 8600, 8604, 8608], + [8804, 8808, 8812, 8816, 8820, 8824, 8828, 8832, 8836, 8840, 8844, 8848, 8852, 8856, 8860, 8864], + [9060, 9064, 9068, 9072, 9076, 9080, 9084, 9088, 9092, 9096, 9100, 9104, 9108, 9112, 9116, 9120], + [9316, 9320, 9324, 9328, 9332, 9336, 9340, 9344, 9348, 9352, 9356, 9360, 9364, 9368, 9372, 9376], + [9572, 9576, 9580, 9584, 9588, 9592, 9596, 9600, 9604, 9608, 9612, 9616, 9620, 9624, 9628, 9632], + [9828, 9832, 9836, 9840, 9844, 9848, 9852, 9856, 9860, 9864, 9868, 9872, 9876, 9880, 9884, 9888], + [ + 10084, + 10088, + 10092, + 10096, + 10100, + 10104, + 10108, + 10112, + 10116, + 10120, + 10124, + 10128, + 10132, + 10136, + 10140, + 10144, + ], + ] + ), + [0], + [4, 4, 4], + ), + # 64-by-64 matrix for subsystems 4 x 4 x 4 : trace out first and second subsystem + ( + test_input_mat7, + np.array( + [ + [31216, 31232, 31248, 31264], + [32240, 32256, 32272, 32288], + [33264, 33280, 33296, 33312], + [34288, 34304, 34320, 34336], + ] + ), + [0, 1], + [4, 4, 4], + ), + # 64-by-64 matrix for subsystems 4 x 4 x 4 : trace out first and third subsystem + ( + test_input_mat7, + np.array( + [ + [26536, 26600, 26664, 26728], + [30632, 30696, 30760, 30824], + [34728, 34792, 34856, 34920], + [38824, 38888, 38952, 39016], + ] + ), + [0, 2], + [4, 4, 4], + ), + # 64-by-64 matrix for subsystems 4 x 4 x 4 : trace out second and third subsystem + ( + test_input_mat7, + np.array( + [ + [7816, 8072, 8328, 8584], + [24200, 24456, 24712, 24968], + [40584, 40840, 41096, 41352], + [56968, 57224, 57480, 57736], + ] + ), + [1, 2], + [4, 4, 4], + ), + # 64-by-64 matrix for subsystems 4 x 4 x 4 : trace out first, second and third subsystem + (test_input_mat7, np.array([[131104]]), [0, 1, 2], [4, 4, 4]), + ], +) def test_is_trace_prserving(input_mat, expected_result, sys_arg, dim_arg): """Test function works as expected.""" res = partial_trace(input_mat, sys_arg, dim_arg) assert (res == expected_result).all() - - diff --git a/toqito/channels/tests/test_partial_transpose.py b/toqito/channels/tests/test_partial_transpose.py index 5353a17a..5cc83ba5 100644 --- a/toqito/channels/tests/test_partial_transpose.py +++ b/toqito/channels/tests/test_partial_transpose.py @@ -1,4 +1,5 @@ """Test partial_transpose.""" + import cvxpy import numpy as np from cvxpy.atoms.affine.vstack import Vstack @@ -688,9 +689,7 @@ def test_partial_transpose_16_by_16_subsystems_2_2_2_2(): def test_partial_transpose_bell_state(): """Test partial transpose on a Bell state.""" rho = bell(2) * bell(2).conj().T - expected_res = np.array( - [[0, 0, 0, 1 / 2], [0, 1 / 2, 0, 0], [0, 0, 1 / 2, 0], [1 / 2, 0, 0, 0]] - ) + expected_res = np.array([[0, 0, 0, 1 / 2], [0, 1 / 2, 0, 0], [0, 0, 1 / 2, 0], [1 / 2, 0, 0, 0]]) res = partial_transpose(rho) np.testing.assert_equal(np.allclose(res, expected_res), True) diff --git a/toqito/channels/tests/test_realignment.py b/toqito/channels/tests/test_realignment.py index 985cedae..41a9ae06 100644 --- a/toqito/channels/tests/test_realignment.py +++ b/toqito/channels/tests/test_realignment.py @@ -1,4 +1,5 @@ """Test realignment.""" + import numpy as np from toqito.channels import realignment diff --git a/toqito/channels/tests/test_reduction.py b/toqito/channels/tests/test_reduction.py index be0da732..663c0054 100644 --- a/toqito/channels/tests/test_reduction.py +++ b/toqito/channels/tests/test_reduction.py @@ -1,4 +1,5 @@ """Test reduction.""" + import numpy as np from toqito.channels import reduction diff --git a/toqito/helper/__init__.py b/toqito/helper/__init__.py index 7e146ccb..e8a218d0 100644 --- a/toqito/helper/__init__.py +++ b/toqito/helper/__init__.py @@ -1,4 +1,5 @@ """Helper functions for dealing with cvxpy objects.""" + from toqito.helper.expr_as_np_array import expr_as_np_array from toqito.helper.np_array_as_expr import np_array_as_expr from toqito.helper.update_odometer import update_odometer diff --git a/toqito/helper/channel_dim.py b/toqito/helper/channel_dim.py index 476cd3c7..a6a3ed0e 100644 --- a/toqito/helper/channel_dim.py +++ b/toqito/helper/channel_dim.py @@ -1,6 +1,5 @@ """Channel dimensions.""" - import itertools import numpy as np @@ -56,9 +55,7 @@ def channel_dim( # 2. [[K1], [K2], .. [Kr]] # 3. [[K1, K2, .. Kr]] and r > 2 is_cpt = False - if isinstance(phi[0], list) and ( - sz_phi_op[1] == 1 or (sz_phi_op[0] == 1 and sz_phi_op[1] > 2) - ): + if isinstance(phi[0], list) and (sz_phi_op[1] == 1 or (sz_phi_op[0] == 1 and sz_phi_op[1] > 2)): # get a flat list of Kraus operators. phi = list(itertools.chain(*phi)) is_cpt = True @@ -83,15 +80,12 @@ def channel_dim( raise ValueError("The input and output spaces of PHI must be square.") if np.any(dim != np.vstack([dim_in, dim_out]).T): - raise ValueError( - "The dimensions of PHI do not match those provided in the DIM argument." - ) + raise ValueError("The dimensions of PHI do not match those provided in the DIM argument.") if (is_cpt and any(k_mat.shape != (dim[0, 1], dim[0, 0]) for k_mat in phi)) or ( not is_cpt and any( - k_mat[0].shape != (dim[0, 1], dim[0, 0]) or k_mat[1].shape != (dim[1, 1], dim[1, 0]) - for k_mat in phi + k_mat[0].shape != (dim[0, 1], dim[0, 0]) or k_mat[1].shape != (dim[1, 1], dim[1, 0]) for k_mat in phi ) ): raise ValueError("The Kraus operators of PHI do not all have the same size.") diff --git a/toqito/helper/cvx_kron.py b/toqito/helper/cvx_kron.py index 338c9d8a..1837ca88 100644 --- a/toqito/helper/cvx_kron.py +++ b/toqito/helper/cvx_kron.py @@ -1,6 +1,5 @@ """Kronecker product for CVXPY objects.""" - import cvxpy import numpy as np from cvxpy.expressions.expression import Expression diff --git a/toqito/helper/expr_as_np_array.py b/toqito/helper/expr_as_np_array.py index d5b5ccd3..69a11006 100644 --- a/toqito/helper/expr_as_np_array.py +++ b/toqito/helper/expr_as_np_array.py @@ -1,4 +1,5 @@ """Cvxpy expression as np.array.""" + import numpy as np from cvxpy.expressions.expression import Expression diff --git a/toqito/helper/np_array_as_expr.py b/toqito/helper/np_array_as_expr.py index 7ba8101e..766b89df 100644 --- a/toqito/helper/np_array_as_expr.py +++ b/toqito/helper/np_array_as_expr.py @@ -1,4 +1,5 @@ """np.array object as cvxpy expression object.""" + import numpy as np from cvxpy import bmat from cvxpy.expressions.expression import Expression diff --git a/toqito/helper/npa_hierarchy.py b/toqito/helper/npa_hierarchy.py index bc7af902..762d883e 100644 --- a/toqito/helper/npa_hierarchy.py +++ b/toqito/helper/npa_hierarchy.py @@ -1,6 +1,5 @@ """NPA constraints.""" - from collections import namedtuple from itertools import product @@ -225,9 +224,7 @@ def npa_constraints( # pylint: disable=too-many-locals for a_ans in range(a_out): for b_ans in range(b_out): sum_all_meas_and_trace += sum( - assemblage[x_alice_in, y_bob_in][ - i + a_ans * referee_dim, i + b_ans * referee_dim - ] + assemblage[x_alice_in, y_bob_in][i + a_ans * referee_dim, i + b_ans * referee_dim] for i in range(referee_dim) ) diff --git a/toqito/helper/tests/test_channel_dim.py b/toqito/helper/tests/test_channel_dim.py index f8db2ea9..3300754d 100644 --- a/toqito/helper/tests/test_channel_dim.py +++ b/toqito/helper/tests/test_channel_dim.py @@ -1,4 +1,5 @@ """Test channel dimensions.""" + import numpy as np import pytest diff --git a/toqito/helper/tests/test_cvx_kron.py b/toqito/helper/tests/test_cvx_kron.py index c510e284..0eebb9d5 100644 --- a/toqito/helper/tests/test_cvx_kron.py +++ b/toqito/helper/tests/test_cvx_kron.py @@ -1,4 +1,5 @@ """Test cvx_kron.""" + import cvxpy import numpy as np diff --git a/toqito/helper/tests/test_expr_as_np_array.py b/toqito/helper/tests/test_expr_as_np_array.py index 2b641548..f2187de1 100644 --- a/toqito/helper/tests/test_expr_as_np_array.py +++ b/toqito/helper/tests/test_expr_as_np_array.py @@ -1,4 +1,5 @@ """Test expr_as_np_array.""" + import cvxpy import numpy as np diff --git a/toqito/helper/tests/test_np_array_as_expr.py b/toqito/helper/tests/test_np_array_as_expr.py index d77a7dee..527c7738 100644 --- a/toqito/helper/tests/test_np_array_as_expr.py +++ b/toqito/helper/tests/test_np_array_as_expr.py @@ -1,4 +1,5 @@ """Test np_array_as_expr.""" + import cvxpy import numpy as np diff --git a/toqito/helper/tests/test_npa_hierarchy.py b/toqito/helper/tests/test_npa_hierarchy.py index dc7d4595..838dcd17 100644 --- a/toqito/helper/tests/test_npa_hierarchy.py +++ b/toqito/helper/tests/test_npa_hierarchy.py @@ -1,4 +1,5 @@ """Test npa_constraints.""" + from collections import defaultdict import cvxpy diff --git a/toqito/helper/tests/test_update_odometer.py b/toqito/helper/tests/test_update_odometer.py index 2a675c05..5297c439 100644 --- a/toqito/helper/tests/test_update_odometer.py +++ b/toqito/helper/tests/test_update_odometer.py @@ -1,4 +1,5 @@ """Test update_odometer.""" + import numpy as np from toqito.helper import update_odometer diff --git a/toqito/helper/update_odometer.py b/toqito/helper/update_odometer.py index 0fd78b51..ac7ad1f8 100644 --- a/toqito/helper/update_odometer.py +++ b/toqito/helper/update_odometer.py @@ -1,12 +1,9 @@ """Update odometer.""" - import numpy as np -def update_odometer( - old_ind: list[int] | np.ndarray, upper_lim: list[int] | np.ndarray -) -> list[int]: +def update_odometer(old_ind: list[int] | np.ndarray, upper_lim: list[int] | np.ndarray) -> list[int]: r"""Increase a vector as odometer. Increases the last entry of the vector `old_ind` by 1, unless that would diff --git a/toqito/matrices/__init__.py b/toqito/matrices/__init__.py index aff4fa40..2de8ab9d 100644 --- a/toqito/matrices/__init__.py +++ b/toqito/matrices/__init__.py @@ -1,4 +1,5 @@ """Matrices.""" + from toqito.matrices.gen_pauli_x import gen_pauli_x from toqito.matrices.gen_pauli_z import gen_pauli_z from toqito.matrices.cnot import cnot diff --git a/toqito/matrices/cnot.py b/toqito/matrices/cnot.py index bd26d9ab..2754e2c1 100644 --- a/toqito/matrices/cnot.py +++ b/toqito/matrices/cnot.py @@ -1,4 +1,5 @@ """CNOT matrix.""" + import numpy as np diff --git a/toqito/matrices/fourier.py b/toqito/matrices/fourier.py index b03bc6ad..fbd36aca 100644 --- a/toqito/matrices/fourier.py +++ b/toqito/matrices/fourier.py @@ -1,4 +1,5 @@ """Fourier matrix.""" + import numpy as np diff --git a/toqito/matrices/gell_mann.py b/toqito/matrices/gell_mann.py index f752a767..6907b470 100644 --- a/toqito/matrices/gell_mann.py +++ b/toqito/matrices/gell_mann.py @@ -1,6 +1,5 @@ """Gell-Mann matrices.""" - import numpy as np import scipy diff --git a/toqito/matrices/gen_gell_mann.py b/toqito/matrices/gen_gell_mann.py index 5361da07..eb7a8cea 100644 --- a/toqito/matrices/gen_gell_mann.py +++ b/toqito/matrices/gen_gell_mann.py @@ -1,13 +1,10 @@ """Generalized Gell-Mann matrices.""" - import numpy as np from scipy import sparse -def gen_gell_mann( - ind_1: int, ind_2: int, dim: int, is_sparse: bool = False -) -> np.ndarray | sparse.lil_matrix: +def gen_gell_mann(ind_1: int, ind_2: int, dim: int, is_sparse: bool = False) -> np.ndarray | sparse.lil_matrix: r"""Produce a generalized Gell-Mann operator :cite:`WikiGellMann`. Construct a :code:`dim`-by-:code:`dim` Hermitian operator. These matrices diff --git a/toqito/matrices/gen_pauli.py b/toqito/matrices/gen_pauli.py index eba278ed..ef1a0c42 100644 --- a/toqito/matrices/gen_pauli.py +++ b/toqito/matrices/gen_pauli.py @@ -1,4 +1,5 @@ """Generalized Pauli matrices.""" + import numpy as np from toqito.matrices import gen_pauli_x, gen_pauli_z diff --git a/toqito/matrices/gen_pauli_x.py b/toqito/matrices/gen_pauli_x.py index f5acb14d..9bb81ceb 100644 --- a/toqito/matrices/gen_pauli_x.py +++ b/toqito/matrices/gen_pauli_x.py @@ -1,4 +1,5 @@ """Generalized Pauli-X matrix.""" + import numpy as np diff --git a/toqito/matrices/gen_pauli_z.py b/toqito/matrices/gen_pauli_z.py index 7e1e4fb7..5f0db9b2 100644 --- a/toqito/matrices/gen_pauli_z.py +++ b/toqito/matrices/gen_pauli_z.py @@ -1,4 +1,5 @@ """Generalized Pauli-Z matrix.""" + from cmath import exp, pi import numpy as np diff --git a/toqito/matrices/hadamard.py b/toqito/matrices/hadamard.py index a0140f3f..088fbbf5 100644 --- a/toqito/matrices/hadamard.py +++ b/toqito/matrices/hadamard.py @@ -1,4 +1,5 @@ """Hadamard matrix.""" + import numpy as np @@ -43,10 +44,7 @@ def hadamard(n_param: int = 1) -> np.ndarray: """ return 2 ** (-n_param / 2) * np.array( - [ - [(-1) ** _hamming_distance(i & j) for i in range(2 ** n_param)] - for j in range(2 ** n_param) - ] + [[(-1) ** _hamming_distance(i & j) for i in range(2**n_param)] for j in range(2**n_param)] ) diff --git a/toqito/matrices/pauli.py b/toqito/matrices/pauli.py index b97fe891..c16a245f 100644 --- a/toqito/matrices/pauli.py +++ b/toqito/matrices/pauli.py @@ -1,15 +1,12 @@ """Pauli matrices.""" - import numpy as np from scipy import sparse from toqito.matrix_ops import tensor -def pauli( - ind: int | str | list[int] | list[str], is_sparse: bool = False -) -> np.ndarray | sparse.csr_matrix: +def pauli(ind: int | str | list[int] | list[str], is_sparse: bool = False) -> np.ndarray | sparse.csr_matrix: r"""Produce a Pauli operator :cite:`WikiPauli`. Provides the 2-by-2 Pauli matrix indicated by the value of :code:`ind`. The diff --git a/toqito/matrices/standard_basis.py b/toqito/matrices/standard_basis.py index d7eec329..b4351f6d 100644 --- a/toqito/matrices/standard_basis.py +++ b/toqito/matrices/standard_basis.py @@ -1,4 +1,5 @@ """Construct standard basis.""" + import numpy as np diff --git a/toqito/matrices/tests/test_cnot.py b/toqito/matrices/tests/test_cnot.py index bac0f7bf..14d5a7b5 100644 --- a/toqito/matrices/tests/test_cnot.py +++ b/toqito/matrices/tests/test_cnot.py @@ -1,4 +1,5 @@ """Test cnot.""" + import numpy as np from toqito.matrices import cnot diff --git a/toqito/matrices/tests/test_cyclic_permutation.py b/toqito/matrices/tests/test_cyclic_permutation.py index 853524f4..5294879e 100644 --- a/toqito/matrices/tests/test_cyclic_permutation.py +++ b/toqito/matrices/tests/test_cyclic_permutation.py @@ -1,4 +1,5 @@ """Test cyclic_permutation.""" + import numpy as np from toqito.matrices import cyclic_permutation_matrix @@ -10,6 +11,7 @@ def test_cyclic_permutation_fixed(): res = cyclic_permutation_matrix(n) assert np.allclose(np.linalg.matrix_power(res, n), np.eye(n)) + def test_cyclic_permutation_successive(): """Test a successive cyclic permuation matrix.""" n = 4 @@ -18,6 +20,7 @@ def test_cyclic_permutation_successive(): res = cyclic_permutation_matrix(n, k) assert np.allclose(np.linalg.matrix_power(res, n), np.eye(n)) + def test_cyclic_permutation_checks(): """Run checks to confrim a proper cyclic permutation.""" for n in (2, 4, 6, 8, 10): diff --git a/toqito/matrices/tests/test_fourier.py b/toqito/matrices/tests/test_fourier.py index 31514f23..b2d0d745 100644 --- a/toqito/matrices/tests/test_fourier.py +++ b/toqito/matrices/tests/test_fourier.py @@ -1,4 +1,5 @@ """Test fourier.""" + import numpy as np from toqito.matrices import fourier diff --git a/toqito/matrices/tests/test_gell_mann.py b/toqito/matrices/tests/test_gell_mann.py index 78badeb2..502cfbb8 100644 --- a/toqito/matrices/tests/test_gell_mann.py +++ b/toqito/matrices/tests/test_gell_mann.py @@ -1,4 +1,5 @@ """Test gell_mann.""" + import numpy as np from scipy.sparse import csr_matrix diff --git a/toqito/matrices/tests/test_gen_gell_mann.py b/toqito/matrices/tests/test_gen_gell_mann.py index 3b0ed861..7442d799 100644 --- a/toqito/matrices/tests/test_gen_gell_mann.py +++ b/toqito/matrices/tests/test_gen_gell_mann.py @@ -1,4 +1,5 @@ """Test gen_gell_mann.""" + import numpy as np from toqito.matrices import gen_gell_mann diff --git a/toqito/matrices/tests/test_gen_pauli.py b/toqito/matrices/tests/test_gen_pauli.py index 2c96c623..9fa819ec 100644 --- a/toqito/matrices/tests/test_gen_pauli.py +++ b/toqito/matrices/tests/test_gen_pauli.py @@ -1,4 +1,5 @@ """Test gen_pauli.""" + import numpy as np from toqito.matrices import gen_pauli diff --git a/toqito/matrices/tests/test_hadamard.py b/toqito/matrices/tests/test_hadamard.py index b642834b..5c936ff0 100644 --- a/toqito/matrices/tests/test_hadamard.py +++ b/toqito/matrices/tests/test_hadamard.py @@ -1,4 +1,5 @@ """Test hadamard.""" + import numpy as np from toqito.matrices import hadamard diff --git a/toqito/matrices/tests/test_pauli.py b/toqito/matrices/tests/test_pauli.py index d1949d57..6292616f 100644 --- a/toqito/matrices/tests/test_pauli.py +++ b/toqito/matrices/tests/test_pauli.py @@ -1,4 +1,5 @@ """Test pauli.""" + import numpy as np from scipy.sparse import issparse diff --git a/toqito/matrices/tests/test_standard_basis.py b/toqito/matrices/tests/test_standard_basis.py index 9616e9d4..78e498bd 100644 --- a/toqito/matrices/tests/test_standard_basis.py +++ b/toqito/matrices/tests/test_standard_basis.py @@ -1,4 +1,5 @@ """Test standard_basis.""" + import numpy as np from toqito.matrices import standard_basis diff --git a/toqito/matrix_ops/__init__.py b/toqito/matrix_ops/__init__.py index 394b444a..c7177112 100644 --- a/toqito/matrix_ops/__init__.py +++ b/toqito/matrix_ops/__init__.py @@ -1,4 +1,5 @@ """Operations on matrices and vectors.""" + from toqito.matrix_ops.tensor import tensor from toqito.matrix_ops.vec import vec from toqito.matrix_ops.unvec import unvec diff --git a/toqito/matrix_ops/calculate_vector_matrix_dimension.py b/toqito/matrix_ops/calculate_vector_matrix_dimension.py index 2f8e3421..5b762923 100644 --- a/toqito/matrix_ops/calculate_vector_matrix_dimension.py +++ b/toqito/matrix_ops/calculate_vector_matrix_dimension.py @@ -1,4 +1,5 @@ """Calculate the (common) dimension of a set of vectors or matrices.""" + import numpy as np diff --git a/toqito/matrix_ops/inner_product.py b/toqito/matrix_ops/inner_product.py index 259a0a9b..0a176bb8 100644 --- a/toqito/matrix_ops/inner_product.py +++ b/toqito/matrix_ops/inner_product.py @@ -1,4 +1,5 @@ """Inner product operation.""" + import numpy as np diff --git a/toqito/matrix_ops/outer_product.py b/toqito/matrix_ops/outer_product.py index 1dd0d77b..6ec1c3ba 100644 --- a/toqito/matrix_ops/outer_product.py +++ b/toqito/matrix_ops/outer_product.py @@ -1,4 +1,5 @@ """Outer product operation.""" + import numpy as np diff --git a/toqito/matrix_ops/tensor.py b/toqito/matrix_ops/tensor.py index bf30cb8a..710db03e 100644 --- a/toqito/matrix_ops/tensor.py +++ b/toqito/matrix_ops/tensor.py @@ -1,4 +1,5 @@ """Tensor product operation.""" + import numpy as np diff --git a/toqito/matrix_ops/tests/test_calculate_vector_matrix_dimension.py b/toqito/matrix_ops/tests/test_calculate_vector_matrix_dimension.py index d4fd6e25..4e8673a2 100644 --- a/toqito/matrix_ops/tests/test_calculate_vector_matrix_dimension.py +++ b/toqito/matrix_ops/tests/test_calculate_vector_matrix_dimension.py @@ -1,4 +1,5 @@ """Test calculate_vector_matrix_dimension.""" + import numpy as np import pytest @@ -10,33 +11,39 @@ def test_1d_vector_dimension(): vector = np.array([1, 2, 3, 4]) assert calculate_vector_matrix_dimension(vector) == 4 + def test_2d_column_vector_dimension(): """Verify dimension calculation for 2D column vectors.""" vector_2d_col = np.array([[1], [2], [3], [4]]) assert calculate_vector_matrix_dimension(vector_2d_col) == 4 + def test_2d_row_vector_dimension(): """Verify dimension calculation for 2D row vectors.""" vector_2d_row = np.array([[1, 2, 3, 4]]) assert calculate_vector_matrix_dimension(vector_2d_row) == 4 + def test_square_matrix_dimension(): """Verify dimension calculation for square matrices.""" matrix = np.array([[1, 0], [0, 1]]) assert calculate_vector_matrix_dimension(matrix) == 2 + def test_non_square_matrix_error(): """Verify error handling for non-square matrices.""" non_square_matrix = np.array([[1, 2, 3], [4, 5, 6]]) with pytest.raises(ValueError): calculate_vector_matrix_dimension(non_square_matrix) + def test_invalid_input_error(): """Verify error handling for invalid inputs.""" invalid_input = "not an array" with pytest.raises(ValueError): calculate_vector_matrix_dimension(invalid_input) + def test_higher_dimensional_array_error(): """Verify error handling for higher-dimensional arrays.""" higher_dim_array = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) @@ -46,7 +53,7 @@ def test_higher_dimensional_array_error(): def test_numpy_array_with_non_numeric_types(): """Verify dimension calculation for numpy arrays with non-numeric types.""" - non_numeric_array = np.array(['a', 'b', 'c', 'd']) + non_numeric_array = np.array(["a", "b", "c", "d"]) assert calculate_vector_matrix_dimension(non_numeric_array) == 4 diff --git a/toqito/matrix_ops/tests/test_inner_product.py b/toqito/matrix_ops/tests/test_inner_product.py index 5131b6cc..a894bfcc 100644 --- a/toqito/matrix_ops/tests/test_inner_product.py +++ b/toqito/matrix_ops/tests/test_inner_product.py @@ -1,29 +1,36 @@ """Test inner_product.""" + import numpy as np import pytest from toqito.matrix_ops import inner_product -@pytest.mark.parametrize("v1, v2, expected_result", [ - # Test with two vectors, no complications. - (np.array([1, 2, 3]), np.array([4, 5, 6]), 32), - # Test with two vectors, with negative input value. - (np.array([-1, 2, 3]), np.array([4, 5, 6]), 24), - # Test with two vectors, with negative expected output. - (np.array([1, 2, -3]), np.array([4, 5, 6]), -4), -]) +@pytest.mark.parametrize( + "v1, v2, expected_result", + [ + # Test with two vectors, no complications. + (np.array([1, 2, 3]), np.array([4, 5, 6]), 32), + # Test with two vectors, with negative input value. + (np.array([-1, 2, 3]), np.array([4, 5, 6]), 24), + # Test with two vectors, with negative expected output. + (np.array([1, 2, -3]), np.array([4, 5, 6]), -4), + ], +) def test_inner_product(v1, v2, expected_result): """Test function works as expected for valid input.""" assert inner_product(v1, v2) == expected_result -@pytest.mark.parametrize("v1, v2", [ - # Different dimensions of vectors. - (np.array([1, 2, 3]), np.array([4, 5, 6, 7])), - # Vector and 2D array. - (np.array([1, 2, 3]), np.array([[4, 5, 6], [7, 8, 9]])), -]) +@pytest.mark.parametrize( + "v1, v2", + [ + # Different dimensions of vectors. + (np.array([1, 2, 3]), np.array([4, 5, 6, 7])), + # Vector and 2D array. + (np.array([1, 2, 3]), np.array([[4, 5, 6], [7, 8, 9]])), + ], +) def test_inner_product_invalid_input(v1, v2): """Test function works as expected for an invalid input.""" with pytest.raises(ValueError): diff --git a/toqito/matrix_ops/tests/test_outer_product.py b/toqito/matrix_ops/tests/test_outer_product.py index 6fc6c10b..d13f5f53 100644 --- a/toqito/matrix_ops/tests/test_outer_product.py +++ b/toqito/matrix_ops/tests/test_outer_product.py @@ -1,27 +1,34 @@ """Test outer_product.""" + import numpy as np import pytest from toqito.matrix_ops import outer_product -@pytest.mark.parametrize("v1, v2, expected_result", [ - # Test with two vectors, no complications. - (np.array([1, 2, 3]), np.array([4, 5, 6]), np.array([[4, 5, 6], [8, 10, 12], [12, 15, 18]])), - # Test with two vectors, with negative input/output values. - (np.array([-1, 2, 3]), np.array([4, 5, 6]), np.array([[-4, -5, -6], [8, 10, 12], [12, 15, 18]])), -]) +@pytest.mark.parametrize( + "v1, v2, expected_result", + [ + # Test with two vectors, no complications. + (np.array([1, 2, 3]), np.array([4, 5, 6]), np.array([[4, 5, 6], [8, 10, 12], [12, 15, 18]])), + # Test with two vectors, with negative input/output values. + (np.array([-1, 2, 3]), np.array([4, 5, 6]), np.array([[-4, -5, -6], [8, 10, 12], [12, 15, 18]])), + ], +) def test_outer_product(v1, v2, expected_result): """Test function works as expected for a valid input.""" np.testing.assert_array_equal(outer_product(v1, v2), expected_result) -@pytest.mark.parametrize("v1, v2", [ - # Different dimensions of vectors. - (np.array([1, 2, 3]), np.array([4, 5, 6, 7])), - # Vector and 2D array. - (np.array([1, 2, 3]), np.array([[4, 5, 6], [7, 8, 9]])), -]) +@pytest.mark.parametrize( + "v1, v2", + [ + # Different dimensions of vectors. + (np.array([1, 2, 3]), np.array([4, 5, 6, 7])), + # Vector and 2D array. + (np.array([1, 2, 3]), np.array([[4, 5, 6], [7, 8, 9]])), + ], +) def test_outer_product_invalid_input(v1, v2): """Test function works as expected for an invalid input.""" with pytest.raises(ValueError): diff --git a/toqito/matrix_ops/tests/test_tensor.py b/toqito/matrix_ops/tests/test_tensor.py index e82c246b..afbd5890 100644 --- a/toqito/matrix_ops/tests/test_tensor.py +++ b/toqito/matrix_ops/tests/test_tensor.py @@ -1,4 +1,5 @@ """Test tensor.""" + import numpy as np import pytest @@ -11,42 +12,53 @@ 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))]) + +@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: @@ -60,9 +72,7 @@ def test_tensor_multiple_input(test_input, len_input, expected): assert (calculated == expected).all() - - def test_tensor_empty_args(): r"""Test tensor with no arguments.""" - with pytest.raises(ValueError, match = "The `tensor` function must take either a matrix or vector."): + with pytest.raises(ValueError, match="The `tensor` function must take either a matrix or vector."): tensor() diff --git a/toqito/matrix_ops/tests/test_unvec.py b/toqito/matrix_ops/tests/test_unvec.py index 3b0c1743..0a39348a 100644 --- a/toqito/matrix_ops/tests/test_unvec.py +++ b/toqito/matrix_ops/tests/test_unvec.py @@ -1,16 +1,20 @@ """Test unvec.""" + import numpy as np import pytest from toqito.matrix_ops import unvec -@pytest.mark.parametrize("vector, shape, expected_result", [ - # Test standard unvec operation on a vector. - (np.array([1, 3, 2, 4]), None, np.array([[1, 2], [3, 4]])), - # Test standard unvec operation on a vector with custom dimension. - (np.array([1, 3, 2, 4]), [4, 1], np.array([[1], [3], [2], [4]])), -]) +@pytest.mark.parametrize( + "vector, shape, expected_result", + [ + # Test standard unvec operation on a vector. + (np.array([1, 3, 2, 4]), None, np.array([[1, 2], [3, 4]])), + # Test standard unvec operation on a vector with custom dimension. + (np.array([1, 3, 2, 4]), [4, 1], np.array([[1], [3], [2], [4]])), + ], +) def test_unvec(vector, shape, expected_result): """Test function works as expected for a valid input.""" np.testing.assert_array_equal(unvec(vector, shape), expected_result) diff --git a/toqito/matrix_ops/tests/test_vec.py b/toqito/matrix_ops/tests/test_vec.py index 51fef66c..5fe52c39 100644 --- a/toqito/matrix_ops/tests/test_vec.py +++ b/toqito/matrix_ops/tests/test_vec.py @@ -1,14 +1,18 @@ """Test vec.""" + import numpy as np import pytest from toqito.matrix_ops import vec -@pytest.mark.parametrize("vector, expected_result", [ - # Test standard vec operation on a vector. - (np.array(np.array([[1, 2], [3, 4]])), np.array([[1], [3], [2], [4]])), -]) +@pytest.mark.parametrize( + "vector, expected_result", + [ + # Test standard vec operation on a vector. + (np.array(np.array([[1, 2], [3, 4]])), np.array([[1], [3], [2], [4]])), + ], +) def test_vec(vector, expected_result): """Test function works as expected for a valid input.""" np.testing.assert_array_equal(vec(vector), expected_result) diff --git a/toqito/matrix_ops/tests/test_vector_to_density_matrix.py b/toqito/matrix_ops/tests/test_vector_to_density_matrix.py index f6fa2010..e7bdd1b3 100644 --- a/toqito/matrix_ops/tests/test_vector_to_density_matrix.py +++ b/toqito/matrix_ops/tests/test_vector_to_density_matrix.py @@ -1,24 +1,28 @@ """Test vector_to_density_matrix.""" + import numpy as np import pytest from toqito.matrix_ops import vector_to_density_matrix -@pytest.mark.parametrize("input_vector, expected_output, exception", [ - # Test conversion of 1D vector to density matrix - (np.array([1, 2, 3]), np.array([[1, 2, 3], [2, 4, 6], [3, 6, 9]]), None), - # Test conversion of column vector to density matrix - (np.array([[1], [2], [3]]), np.array([[1, 2, 3], [2, 4, 6], [3, 6, 9]]), None), - # Test conversion of row vector to density matrix - (np.array([[1, 2, 3]]), np.array([[1, 2, 3], [2, 4, 6], [3, 6, 9]]), None), - # Test that square matrix is returned unchanged - (np.array([[1, 0], [0, 1]]), np.array([[1, 0], [0, 1]]), None), - # Test that non-square matrix raises ValueError - (np.array([[1, 0, 0], [0, 1, 0]]), None, ValueError), - # Test that higher-dimensional array raises ValueError - (np.array([[[1, 0], [0, 1]]]), None, ValueError), -]) +@pytest.mark.parametrize( + "input_vector, expected_output, exception", + [ + # Test conversion of 1D vector to density matrix + (np.array([1, 2, 3]), np.array([[1, 2, 3], [2, 4, 6], [3, 6, 9]]), None), + # Test conversion of column vector to density matrix + (np.array([[1], [2], [3]]), np.array([[1, 2, 3], [2, 4, 6], [3, 6, 9]]), None), + # Test conversion of row vector to density matrix + (np.array([[1, 2, 3]]), np.array([[1, 2, 3], [2, 4, 6], [3, 6, 9]]), None), + # Test that square matrix is returned unchanged + (np.array([[1, 0], [0, 1]]), np.array([[1, 0], [0, 1]]), None), + # Test that non-square matrix raises ValueError + (np.array([[1, 0, 0], [0, 1, 0]]), None, ValueError), + # Test that higher-dimensional array raises ValueError + (np.array([[[1, 0], [0, 1]]]), None, ValueError), + ], +) def test_vector_to_density_matrix(input_vector, expected_output, exception): """Test vector to density matrix functionality.""" if exception: diff --git a/toqito/matrix_ops/tests/test_vectors_from_gram_matrix.py b/toqito/matrix_ops/tests/test_vectors_from_gram_matrix.py index 42ef38af..e7af80f2 100644 --- a/toqito/matrix_ops/tests/test_vectors_from_gram_matrix.py +++ b/toqito/matrix_ops/tests/test_vectors_from_gram_matrix.py @@ -1,22 +1,21 @@ """Test vectors_from_gram_matrix.""" + import numpy as np import pytest from toqito.matrix_ops import vectors_from_gram_matrix -@pytest.mark.parametrize("gram, expected_result", [ - # Gram matrix is identity matrix. - ( - np.identity(4), - [ - np.array([1, 0, 0, 0]), - np.array([0, 1, 0, 0]), - np.array([0, 0, 1, 0]), - np.array([0, 0, 0, 1]) - ] - ), -]) +@pytest.mark.parametrize( + "gram, expected_result", + [ + # Gram matrix is identity matrix. + ( + np.identity(4), + [np.array([1, 0, 0, 0]), np.array([0, 1, 0, 0]), np.array([0, 0, 1, 0]), np.array([0, 0, 0, 1])], + ), + ], +) def test_vectors_from_gram_matrix(gram, expected_result): """Test able to extract vectors from Gram matrix.""" vectors = vectors_from_gram_matrix(gram) @@ -34,10 +33,13 @@ def test_vectors_from_gram_matrix_not_psd(): assert np.allclose(vectors[2][0], -1 / 2) -@pytest.mark.parametrize("gram", [ - # Non-square matrix. - (np.array([[1, 2], [4, 5], [7, 8]])), -]) +@pytest.mark.parametrize( + "gram", + [ + # Non-square matrix. + (np.array([[1, 2], [4, 5], [7, 8]])), + ], +) def test_vectors_from_gram_matrix_invalid_input(gram): """Test function works as expected for an invalid input.""" with pytest.raises(np.linalg.LinAlgError): diff --git a/toqito/matrix_ops/tests/test_vectors_to_gram_matrix.py b/toqito/matrix_ops/tests/test_vectors_to_gram_matrix.py index f35fd7b0..9a44bff6 100644 --- a/toqito/matrix_ops/tests/test_vectors_to_gram_matrix.py +++ b/toqito/matrix_ops/tests/test_vectors_to_gram_matrix.py @@ -1,4 +1,5 @@ """Test vectors_to_gram_matrix.""" + import numpy as np import pytest @@ -8,19 +9,25 @@ e_0, e_1 = np.array([[1], [0]]), np.array([[0], [1]]) -@pytest.mark.parametrize("vectors, expected_result", [ - # Trine states. - (trine(), np.array([[1, -1 / 2, -1 / 2], [-1 / 2, 1, -1 / 2], [-1 / 2, -1 / 2, 1]])), -]) +@pytest.mark.parametrize( + "vectors, expected_result", + [ + # Trine states. + (trine(), np.array([[1, -1 / 2, -1 / 2], [-1 / 2, 1, -1 / 2], [-1 / 2, -1 / 2, 1]])), + ], +) def test_vectors_to_gram_matrix(vectors, expected_result): """Test able to construct Gram matrix from vectors.""" np.testing.assert_allclose(vectors_to_gram_matrix(vectors), expected_result) -@pytest.mark.parametrize("vectors", [ - # Vectors of different sizes. - ([np.array([1, 2, 3]), np.array([1, 2])]), -]) +@pytest.mark.parametrize( + "vectors", + [ + # Vectors of different sizes. + ([np.array([1, 2, 3]), np.array([1, 2])]), + ], +) def test_vectors_to_gram_matrix_invalid_input(vectors): """Test function works as expected for an invalid input.""" with np.testing.assert_raises(ValueError): diff --git a/toqito/matrix_ops/unvec.py b/toqito/matrix_ops/unvec.py index 1d551074..360110bb 100644 --- a/toqito/matrix_ops/unvec.py +++ b/toqito/matrix_ops/unvec.py @@ -1,4 +1,5 @@ """Unvec operation.""" + import numpy as np diff --git a/toqito/matrix_ops/vec.py b/toqito/matrix_ops/vec.py index e19de3f3..3b8ec49e 100644 --- a/toqito/matrix_ops/vec.py +++ b/toqito/matrix_ops/vec.py @@ -1,4 +1,5 @@ """Vec operation.""" + import numpy as np diff --git a/toqito/matrix_ops/vector_to_density_matrix.py b/toqito/matrix_ops/vector_to_density_matrix.py index f3f58ccf..d54d763a 100644 --- a/toqito/matrix_ops/vector_to_density_matrix.py +++ b/toqito/matrix_ops/vector_to_density_matrix.py @@ -1,4 +1,5 @@ """Convert row or column vector to density matrix.""" + import numpy as np diff --git a/toqito/matrix_ops/vectors_from_gram_matrix.py b/toqito/matrix_ops/vectors_from_gram_matrix.py index fa8811d2..4919d2c0 100644 --- a/toqito/matrix_ops/vectors_from_gram_matrix.py +++ b/toqito/matrix_ops/vectors_from_gram_matrix.py @@ -1,4 +1,5 @@ """Vectors associated to Gram matrix.""" + import numpy as np import scipy diff --git a/toqito/matrix_ops/vectors_to_gram_matrix.py b/toqito/matrix_ops/vectors_to_gram_matrix.py index 7d2f003a..dd1da854 100644 --- a/toqito/matrix_ops/vectors_to_gram_matrix.py +++ b/toqito/matrix_ops/vectors_to_gram_matrix.py @@ -1,4 +1,5 @@ """Gram matrix from list of vectors.""" + import numpy as np diff --git a/toqito/matrix_props/__init__.py b/toqito/matrix_props/__init__.py index b94d8469..0d8d3024 100644 --- a/toqito/matrix_props/__init__.py +++ b/toqito/matrix_props/__init__.py @@ -1,4 +1,5 @@ """Properties of matrices and vectors.""" + from toqito.matrix_props.has_same_dimension import has_same_dimension from toqito.matrix_props.is_square import is_square from toqito.matrix_props.kp_norm import kp_norm diff --git a/toqito/matrix_props/has_same_dimension.py b/toqito/matrix_props/has_same_dimension.py index a711a505..6ba61900 100644 --- a/toqito/matrix_props/has_same_dimension.py +++ b/toqito/matrix_props/has_same_dimension.py @@ -1,4 +1,5 @@ """Check if dimensions of list of vectors or matrices are equal.""" + import numpy as np diff --git a/toqito/matrix_props/is_block_positive.py b/toqito/matrix_props/is_block_positive.py index 4d3757ed..ac83c3a5 100644 --- a/toqito/matrix_props/is_block_positive.py +++ b/toqito/matrix_props/is_block_positive.py @@ -1,6 +1,5 @@ """Is matrix block positive.""" - import numpy as np from toqito.matrix_props.is_hermitian import is_hermitian diff --git a/toqito/matrix_props/is_circulant.py b/toqito/matrix_props/is_circulant.py index bf55d386..4ec14d18 100644 --- a/toqito/matrix_props/is_circulant.py +++ b/toqito/matrix_props/is_circulant.py @@ -1,4 +1,5 @@ """Is matrix circulant.""" + import numpy as np diff --git a/toqito/matrix_props/is_commuting.py b/toqito/matrix_props/is_commuting.py index 2425ca99..34317d39 100644 --- a/toqito/matrix_props/is_commuting.py +++ b/toqito/matrix_props/is_commuting.py @@ -1,4 +1,5 @@ """Is matrix commuting.""" + import numpy as np diff --git a/toqito/matrix_props/is_density.py b/toqito/matrix_props/is_density.py index de359ae6..123c3fb2 100644 --- a/toqito/matrix_props/is_density.py +++ b/toqito/matrix_props/is_density.py @@ -1,4 +1,5 @@ """Is matrix a density matrix.""" + import numpy as np from toqito.matrix_props import is_positive_semidefinite diff --git a/toqito/matrix_props/is_diagonal.py b/toqito/matrix_props/is_diagonal.py index 63d8a660..e98c79db 100644 --- a/toqito/matrix_props/is_diagonal.py +++ b/toqito/matrix_props/is_diagonal.py @@ -1,4 +1,5 @@ """Is matrix a diagonal matrix.""" + import numpy as np from toqito.matrix_props import is_square diff --git a/toqito/matrix_props/is_diagonally_dominant.py b/toqito/matrix_props/is_diagonally_dominant.py index b4ca9518..e18efe44 100644 --- a/toqito/matrix_props/is_diagonally_dominant.py +++ b/toqito/matrix_props/is_diagonally_dominant.py @@ -1,4 +1,5 @@ """Is matrix diagonally dominant.""" + import numpy as np from toqito.matrix_props import is_square diff --git a/toqito/matrix_props/is_hermitian.py b/toqito/matrix_props/is_hermitian.py index 80a9ddf8..0dc36dc7 100644 --- a/toqito/matrix_props/is_hermitian.py +++ b/toqito/matrix_props/is_hermitian.py @@ -1,4 +1,5 @@ """Is matrix a Hermitian matrix.""" + import numpy as np from toqito.matrix_props import is_square diff --git a/toqito/matrix_props/is_idempotent.py b/toqito/matrix_props/is_idempotent.py index c0a6f444..20891f90 100644 --- a/toqito/matrix_props/is_idempotent.py +++ b/toqito/matrix_props/is_idempotent.py @@ -1,4 +1,5 @@ """Is the matrix idempotent.""" + import numpy as np from toqito.matrix_props import is_square diff --git a/toqito/matrix_props/is_identity.py b/toqito/matrix_props/is_identity.py index 034f8d9a..ea3111c4 100644 --- a/toqito/matrix_props/is_identity.py +++ b/toqito/matrix_props/is_identity.py @@ -1,4 +1,5 @@ """Is matrix the identity matrix.""" + import numpy as np from toqito.matrix_props import is_square diff --git a/toqito/matrix_props/is_linearly_independent.py b/toqito/matrix_props/is_linearly_independent.py index 40b2b427..1991c520 100644 --- a/toqito/matrix_props/is_linearly_independent.py +++ b/toqito/matrix_props/is_linearly_independent.py @@ -1,4 +1,5 @@ """Is linearly independent.""" + import numpy as np diff --git a/toqito/matrix_props/is_normal.py b/toqito/matrix_props/is_normal.py index ee00f48f..c197df9b 100644 --- a/toqito/matrix_props/is_normal.py +++ b/toqito/matrix_props/is_normal.py @@ -1,4 +1,5 @@ """Is matrix a normal matrix.""" + import numpy as np from toqito.matrix_props import is_square diff --git a/toqito/matrix_props/is_orthonormal.py b/toqito/matrix_props/is_orthonormal.py index 18259d98..d97604e5 100644 --- a/toqito/matrix_props/is_orthonormal.py +++ b/toqito/matrix_props/is_orthonormal.py @@ -1,4 +1,5 @@ """Tests for is_orthonormal.""" + import numpy as np from toqito.state_props.is_mutually_orthogonal import is_mutually_orthogonal @@ -37,6 +38,4 @@ def is_orthonormal(vectors: list[np.ndarray]) -> bool: :return: True if vectors are orthonormal; False otherwise. """ - return is_mutually_orthogonal(vectors) and np.allclose( - np.dot(vectors, vectors.T), np.eye(vectors.shape[0]) - ) + return is_mutually_orthogonal(vectors) and np.allclose(np.dot(vectors, vectors.T), np.eye(vectors.shape[0])) diff --git a/toqito/matrix_props/is_permutation.py b/toqito/matrix_props/is_permutation.py index 3d856cfc..1d14533d 100644 --- a/toqito/matrix_props/is_permutation.py +++ b/toqito/matrix_props/is_permutation.py @@ -1,4 +1,5 @@ """Is matrix a permutation matrix.""" + import numpy as np diff --git a/toqito/matrix_props/is_positive_definite.py b/toqito/matrix_props/is_positive_definite.py index d9c03ec9..4d494ae3 100644 --- a/toqito/matrix_props/is_positive_definite.py +++ b/toqito/matrix_props/is_positive_definite.py @@ -1,4 +1,5 @@ """Is matrix a positive definite matrix.""" + import numpy as np from toqito.matrix_props import is_hermitian # pylint: disable=unused-import diff --git a/toqito/matrix_props/is_positive_semidefinite.py b/toqito/matrix_props/is_positive_semidefinite.py index d0851aa4..2b8586f8 100644 --- a/toqito/matrix_props/is_positive_semidefinite.py +++ b/toqito/matrix_props/is_positive_semidefinite.py @@ -1,4 +1,5 @@ """Is matrix a positive semidefinite matrix.""" + import numpy as np from toqito.matrix_props import is_hermitian diff --git a/toqito/matrix_props/is_projection.py b/toqito/matrix_props/is_projection.py index 0826c792..8597cc89 100644 --- a/toqito/matrix_props/is_projection.py +++ b/toqito/matrix_props/is_projection.py @@ -1,4 +1,5 @@ """Is matrix a projection matrix.""" + import numpy as np from toqito.matrix_props import is_square diff --git a/toqito/matrix_props/is_square.py b/toqito/matrix_props/is_square.py index e7113055..a2bff399 100644 --- a/toqito/matrix_props/is_square.py +++ b/toqito/matrix_props/is_square.py @@ -1,4 +1,5 @@ """Is matrix a square matrix.""" + import numpy as np diff --git a/toqito/matrix_props/is_symmetric.py b/toqito/matrix_props/is_symmetric.py index 76fb8ded..e8e097c4 100644 --- a/toqito/matrix_props/is_symmetric.py +++ b/toqito/matrix_props/is_symmetric.py @@ -1,4 +1,5 @@ """Is matrix a symmetric matrix.""" + import numpy as np from toqito.matrix_props import is_square diff --git a/toqito/matrix_props/is_totally_positive.py b/toqito/matrix_props/is_totally_positive.py index 508ecaa9..983442c0 100644 --- a/toqito/matrix_props/is_totally_positive.py +++ b/toqito/matrix_props/is_totally_positive.py @@ -1,4 +1,5 @@ """Is matrix totally positive.""" + from itertools import combinations import numpy as np diff --git a/toqito/matrix_props/is_unitary.py b/toqito/matrix_props/is_unitary.py index a90ef5da..ce13ee9b 100644 --- a/toqito/matrix_props/is_unitary.py +++ b/toqito/matrix_props/is_unitary.py @@ -1,4 +1,5 @@ """Is matrix a unitary matrix.""" + import numpy as np from toqito.matrix_props import is_square @@ -83,6 +84,4 @@ def is_unitary(mat: np.ndarray, rtol: float = 1e-05, atol: float = 1e-08) -> boo id_mat = np.eye(len(mat)) # If U^* * U = I U * U^*, the matrix "U" is unitary. - return np.allclose(uc_u_mat, id_mat, rtol=rtol, atol=atol) and np.allclose( - u_uc_mat, id_mat, rtol=rtol, atol=atol - ) + return np.allclose(uc_u_mat, id_mat, rtol=rtol, atol=atol) and np.allclose(u_uc_mat, id_mat, rtol=rtol, atol=atol) diff --git a/toqito/matrix_props/kp_norm.py b/toqito/matrix_props/kp_norm.py index 1536cb62..8dbe2bcf 100644 --- a/toqito/matrix_props/kp_norm.py +++ b/toqito/matrix_props/kp_norm.py @@ -1,4 +1,5 @@ """Kp-norm for matrices.""" + import numpy as np diff --git a/toqito/matrix_props/majorizes.py b/toqito/matrix_props/majorizes.py index 8e854a47..f9a050e6 100644 --- a/toqito/matrix_props/majorizes.py +++ b/toqito/matrix_props/majorizes.py @@ -1,6 +1,5 @@ """Determine if one vector or matrix majorizes another.""" - import numpy as np diff --git a/toqito/matrix_props/sk_norm.py b/toqito/matrix_props/sk_norm.py index 5cf44ede..1598f080 100644 --- a/toqito/matrix_props/sk_norm.py +++ b/toqito/matrix_props/sk_norm.py @@ -1,6 +1,5 @@ """Compute the S(k)-norm of a matrix.""" - import warnings import cvxpy @@ -92,9 +91,7 @@ def sk_operator_norm( # pylint: disable=too-many-locals if isinstance(dim, int): dim = np.array([dim, dim_xy / dim]) # pylint: disable=redefined-variable-type if np.abs(dim[1] - np.round(dim[1])) >= 2 * dim_xy * np.finfo(float).eps: - raise ValueError( - "If `dim` is a scalar, it must evenly divide the length of the matrix." - ) + raise ValueError("If `dim` is a scalar, it must evenly divide the length of the matrix.") dim[1] = int(np.round(dim[1])) dim = np.array(dim, dtype=int) @@ -118,9 +115,7 @@ def sk_operator_norm( # pylint: disable=too-many-locals # If X is rank 1 then the S(k)-norm is easy to compute via Proposition 10 of [1]. if rank == 1: u_mat, _, v_mat = np.linalg.svd(mat, full_matrices=False) - lower_bound = ( - op_norm * sk_vector_norm(u_mat[:, 0], k, dim) * sk_vector_norm(v_mat[0, :], k, dim) - ) + lower_bound = op_norm * sk_vector_norm(u_mat[:, 0], k, dim) * sk_vector_norm(v_mat[0, :], k, dim) upper_bound = lower_bound return lower_bound, upper_bound @@ -164,12 +159,7 @@ def sk_operator_norm( # pylint: disable=too-many-locals if k == 1: lower_bound = max( lower_bound, - ( - np.trace(mat) - + np.sqrt( - (prod_dim * np.trace(mat @ mat) - np.trace(mat) ** 2) / (prod_dim - 1) - ) - ) + (np.trace(mat) + np.sqrt((prod_dim * np.trace(mat @ mat) - np.trace(mat) ** 2) / (prod_dim - 1))) / prod_dim, ) @@ -177,14 +167,11 @@ def sk_operator_norm( # pylint: disable=too-many-locals # Use the upper bound of Proposition 15 of [1]. upper_bound = min( upper_bound, - sum( - abs(eig_val[i]) * sk_vector_norm(eig_vec[:, i], k, dim) ** 2 - for i in range(prod_dim) - ), + sum(abs(eig_val[i]) * sk_vector_norm(eig_vec[:, i], k, dim) ** 2 for i in range(prod_dim)), ) # Use the upper bound of Proposition 4.2.11 of [3]. - upper_bound = min(upper_bound, kp_norm(realignment(mat, dim), k ** 2, 2)) + upper_bound = min(upper_bound, kp_norm(realignment(mat, dim), k**2, 2)) # Use the lower bound of Theorem 4.2.17 of [3]. if is_projection: @@ -192,17 +179,14 @@ def sk_operator_norm( # pylint: disable=too-many-locals lower_bound, min( 1, - k - / np.ceil( - (dim[0] + dim[1] - np.sqrt((dim[0] - dim[1]) ** 2 + 4 * rank - 4)) / 2 - ), + k / np.ceil((dim[0] + dim[1] - np.sqrt((dim[0] - dim[1]) ** 2 + 4 * rank - 4)) / 2), ), ) lower_bound = max( lower_bound, (min(dim) - k) - * (rank + np.sqrt((prod_dim * rank - rank ** 2) / (prod_dim - 1))) + * (rank + np.sqrt((prod_dim * rank - rank**2) / (prod_dim - 1))) / (prod_dim * (min(dim) - 1)) + (k - 1) / (min(dim) - 1), ) @@ -213,10 +197,10 @@ def sk_operator_norm( # pylint: disable=too-many-locals # Use a randomized iterative method to try to improve the lower bound. if is_positive: - for _ in range(5 ** effort): + for _ in range(5**effort): lower_bound = max( lower_bound, - __lower_bound_sk_norm_randomized(mat, k, dim, tol ** 2), + __lower_bound_sk_norm_randomized(mat, k, dim, tol**2), ) # break out of the function if the target value has already been met @@ -258,8 +242,7 @@ def sk_operator_norm( # pylint: disable=too-many-locals xmineig = min(eig_val) lower_bound = max( lower_bound, - np.real(cvx_optval) * (1 - dim[1] * gs / (2 * dim[1] - 1)) - + xmineig * gs / (2 * dim[1] - 2), + np.real(cvx_optval) * (1 - dim[1] * gs / (2 * dim[1] - 1)) + xmineig * gs / (2 * dim[1] - 2), ) # Done the effort = 1 SDP, now get better upper bounds via symmetric @@ -275,9 +258,7 @@ def sk_operator_norm( # pylint: disable=too-many-locals rho = cvxpy.Variable((prod_sym_dim, prod_sym_dim), hermitian=True, name="rho") objective = cvxpy.Maximize( - cvxpy.real( - cvxpy.trace(mat @ partial_trace(rho, list(range(2, j + 1)), sym_dim)) - ) + cvxpy.real(cvxpy.trace(mat @ partial_trace(rho, list(range(2, j + 1)), sym_dim))) ) constraints = [ @@ -300,8 +281,7 @@ def sk_operator_norm( # pylint: disable=too-many-locals gs = min(1 - roots) lower_bound = max( lower_bound, - np.real(cvx_optval) * (1 - dim[1] * gs / (2 * dim[1] - 1)) - + xmineig * gs / (2 * dim[1] - 2), + np.real(cvx_optval) * (1 - dim[1] * gs / (2 * dim[1] - 1)) + xmineig * gs / (2 * dim[1] - 2), ) lower_bound = op_norm * lower_bound @@ -313,12 +293,9 @@ def sk_operator_norm( # pylint: disable=too-many-locals # This function checks whether or not the lower bound or upper bound # already computed meets the desired target value (within numerical error) # and thus we can abort early. -def __target_is_proved( - lower_bound: float, upper_bound: float, op_norm: float, tol: float, target: float -) -> bool: +def __target_is_proved(lower_bound: float, upper_bound: float, op_norm: float, tol: float, target: float) -> bool: return op_norm * (lower_bound + tol) >= op_norm * upper_bound or ( - target is not None - and (op_norm * (lower_bound - tol) >= target or op_norm * (upper_bound + tol) <= target) + target is not None and (op_norm * (lower_bound - tol) >= target or op_norm * (upper_bound + tol) <= target) ) @@ -340,14 +317,10 @@ def __lower_bound_sk_norm_randomized( # pylint: disable=too-many-locals dim_a, dim_b = dim psi = max_entangled(k, is_normalized=False) - left_swap_entagled_kron_id = swap( - np.kron(psi, np.eye(dim_a * dim_b)), [2, 3], [k, k, dim_a, dim_b], row_only=True - ) + left_swap_entagled_kron_id = swap(np.kron(psi, np.eye(dim_a * dim_b)), [2, 3], [k, k, dim_a, dim_b], row_only=True) swap_entagled_kron_id = left_swap_entagled_kron_id @ left_swap_entagled_kron_id.conj().T - swap_entagled_kron_mat = swap( - np.kron(psi @ psi.conj().T, mat), [2, 3], [k, k, dim_a, dim_b], row_only=False - ) + swap_entagled_kron_mat = swap(np.kron(psi @ psi.conj().T, mat), [2, 3], [k, k, dim_a, dim_b], row_only=False) opt_vec = None if start_vec is not None: diff --git a/toqito/matrix_props/tests/test_has_same_dimension.py b/toqito/matrix_props/tests/test_has_same_dimension.py index f6c2efc0..5c2a3220 100644 --- a/toqito/matrix_props/tests/test_has_same_dimension.py +++ b/toqito/matrix_props/tests/test_has_same_dimension.py @@ -1,4 +1,5 @@ """Tests for has_same_dimension.""" + import pytest from toqito.matrix_props import has_same_dimension diff --git a/toqito/matrix_props/tests/test_is_block_positive.py b/toqito/matrix_props/tests/test_is_block_positive.py index 0e935216..3f4702ae 100644 --- a/toqito/matrix_props/tests/test_is_block_positive.py +++ b/toqito/matrix_props/tests/test_is_block_positive.py @@ -1,4 +1,5 @@ """Test is_block_positive.""" + import numpy as np import pytest from picos import partial_transpose @@ -12,22 +13,27 @@ b_0 = bell(0) b_3 = bell(3) v_0 = np.kron(b_0, b_0) -y_mat = (np.kron(np.eye(4), b_0 @ b_0.T) / 2 + np.kron( - b_3 @ b_3.T, partial_transpose(b_3 @ b_3.T, [0]))) / 3 - v_0 @ v_0.T / 4 +y_mat = ( + np.kron(np.eye(4), b_0 @ b_0.T) / 2 + np.kron(b_3 @ b_3.T, partial_transpose(b_3 @ b_3.T, [0])) +) / 3 - v_0 @ v_0.T / 4 mat = swap(y_mat, [2, 3], [2, 2, 2, 2]) -@pytest.mark.parametrize("input_mat, expected_bool_1_block, expected_bool_2_block", [ - # Test Swap is 1-block positive but not 2-block positive - (swap_operator(2), True, False), - (swap_operator(3), True, False), - (swap_operator(4), True, False), - # Test Choi map is 1-block positive but not 2-block positive. - (choi(), True, False), - # Test that the positive linear map introduced in :cite:`Bandyopadhyay_2015_Limitations` is block positive - (mat, True, None), - # non-hermitian input is not is_block_positive - (np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), False, None)]) +@pytest.mark.parametrize( + "input_mat, expected_bool_1_block, expected_bool_2_block", + [ + # Test Swap is 1-block positive but not 2-block positive + (swap_operator(2), True, False), + (swap_operator(3), True, False), + (swap_operator(4), True, False), + # Test Choi map is 1-block positive but not 2-block positive. + (choi(), True, False), + # Test that the positive linear map introduced in :cite:`Bandyopadhyay_2015_Limitations` is block positive + (mat, True, None), + # non-hermitian input is not is_block_positive + (np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), False, None), + ], +) def test_is_block_positive(input_mat, expected_bool_1_block, expected_bool_2_block): """Test function works as expected for valid default inputs.""" if expected_bool_2_block is not None: @@ -47,20 +53,29 @@ def test_is_block_positive(input_mat, expected_bool_1_block, expected_bool_2_blo assert not is_block_positive(input_mat, rtol=0.001) -@pytest.mark.parametrize("input_mat, expected_bool", [ - # Test Swap is 1-block positive but not 2-block positive - (swap_operator(2), True), - (swap_operator(3), True), - (swap_operator(4), True),]) +@pytest.mark.parametrize( + "input_mat, expected_bool", + [ + # Test Swap is 1-block positive but not 2-block positive + (swap_operator(2), True), + (swap_operator(3), True), + (swap_operator(4), True), + ], +) def test_dim_None(input_mat, expected_bool): """Check input dimensions are set correctly when the input is None.""" if expected_bool is True: assert is_block_positive(input_mat, 1, None) -@pytest.mark.parametrize("input_mat, input_dim", [ - # Test Swap is 1-block positive but not 2-block positive - (swap_operator(2), 2), - ((swap_operator(2), [2, 2]))]) + +@pytest.mark.parametrize( + "input_mat, input_dim", + [ + # Test Swap is 1-block positive but not 2-block positive + (swap_operator(2), 2), + ((swap_operator(2), [2, 2])), + ], +) def test_dim_input(input_mat, input_dim): """Check input dimensions are set correctly when the input dim is an int or list.""" assert is_block_positive(input_mat, 1, input_dim) diff --git a/toqito/matrix_props/tests/test_is_circulant.py b/toqito/matrix_props/tests/test_is_circulant.py index 5cd45f15..39cc6e85 100644 --- a/toqito/matrix_props/tests/test_is_circulant.py +++ b/toqito/matrix_props/tests/test_is_circulant.py @@ -1,4 +1,5 @@ """Test is_circulant.""" + import numpy as np from toqito.matrix_props import is_circulant diff --git a/toqito/matrix_props/tests/test_is_commuting.py b/toqito/matrix_props/tests/test_is_commuting.py index 01814bcc..846ea2d9 100644 --- a/toqito/matrix_props/tests/test_is_commuting.py +++ b/toqito/matrix_props/tests/test_is_commuting.py @@ -1,4 +1,5 @@ """Test is_commuting.""" + import numpy as np from toqito.matrix_props import is_commuting diff --git a/toqito/matrix_props/tests/test_is_density.py b/toqito/matrix_props/tests/test_is_density.py index 87209216..2a98ee8c 100644 --- a/toqito/matrix_props/tests/test_is_density.py +++ b/toqito/matrix_props/tests/test_is_density.py @@ -1,4 +1,5 @@ """Test is_density.""" + import numpy as np from toqito.matrix_props import is_density diff --git a/toqito/matrix_props/tests/test_is_diagonal.py b/toqito/matrix_props/tests/test_is_diagonal.py index 53de4e0c..61e3300e 100644 --- a/toqito/matrix_props/tests/test_is_diagonal.py +++ b/toqito/matrix_props/tests/test_is_diagonal.py @@ -1,4 +1,5 @@ """Test is_diagonal.""" + import numpy as np from toqito.matrix_props import is_diagonal diff --git a/toqito/matrix_props/tests/test_is_diagonally_dominant.py b/toqito/matrix_props/tests/test_is_diagonally_dominant.py index 522654e1..6780008d 100644 --- a/toqito/matrix_props/tests/test_is_diagonally_dominant.py +++ b/toqito/matrix_props/tests/test_is_diagonally_dominant.py @@ -1,4 +1,5 @@ """Test is_diagonally_dominant.""" + import numpy as np from toqito.matrix_props import is_diagonally_dominant diff --git a/toqito/matrix_props/tests/test_is_hermitian.py b/toqito/matrix_props/tests/test_is_hermitian.py index bc24795b..9f571cb4 100644 --- a/toqito/matrix_props/tests/test_is_hermitian.py +++ b/toqito/matrix_props/tests/test_is_hermitian.py @@ -1,4 +1,5 @@ """Test is_hermitian.""" + import numpy as np from toqito.matrix_props import is_hermitian diff --git a/toqito/matrix_props/tests/test_is_idempotent.py b/toqito/matrix_props/tests/test_is_idempotent.py index aadbaf55..d4018884 100644 --- a/toqito/matrix_props/tests/test_is_idempotent.py +++ b/toqito/matrix_props/tests/test_is_idempotent.py @@ -1,4 +1,5 @@ """Test is_idempotent.""" + import numpy as np from toqito.matrix_props import is_idempotent diff --git a/toqito/matrix_props/tests/test_is_identity.py b/toqito/matrix_props/tests/test_is_identity.py index 96df682a..cb11c189 100644 --- a/toqito/matrix_props/tests/test_is_identity.py +++ b/toqito/matrix_props/tests/test_is_identity.py @@ -1,4 +1,5 @@ """Test is_identity.""" + import numpy as np from toqito.matrix_props import is_identity diff --git a/toqito/matrix_props/tests/test_is_linearly_independent.py b/toqito/matrix_props/tests/test_is_linearly_independent.py index ac461959..4d194ae0 100644 --- a/toqito/matrix_props/tests/test_is_linearly_independent.py +++ b/toqito/matrix_props/tests/test_is_linearly_independent.py @@ -1,14 +1,18 @@ """Test is_linearly_independent.""" + import numpy as np import pytest from toqito.matrix_props import is_linearly_independent -@pytest.mark.parametrize("vectors, expected_result", [ - ([np.array([[1], [0], [1]]), np.array([[1], [1], [0]]), np.array([[0], [0], [1]])], True), - ([np.array([[0], [1], [2]]), np.array([[1], [2], [3]]), np.array([[3], [5], [7]])], False), -]) +@pytest.mark.parametrize( + "vectors, expected_result", + [ + ([np.array([[1], [0], [1]]), np.array([[1], [1], [0]]), np.array([[0], [0], [1]])], True), + ([np.array([[0], [1], [2]]), np.array([[1], [2], [3]]), np.array([[3], [5], [7]])], False), + ], +) def test_is_linearly_independent(vectors, expected_result): """Test for linear independence/dependence of vectors.""" np.testing.assert_equal(is_linearly_independent(vectors), expected_result) diff --git a/toqito/matrix_props/tests/test_is_normal.py b/toqito/matrix_props/tests/test_is_normal.py index df8e1968..87b6a168 100644 --- a/toqito/matrix_props/tests/test_is_normal.py +++ b/toqito/matrix_props/tests/test_is_normal.py @@ -1,4 +1,5 @@ """Test is_normal.""" + import numpy as np from toqito.matrix_props import is_normal diff --git a/toqito/matrix_props/tests/test_is_orthonormal.py b/toqito/matrix_props/tests/test_is_orthonormal.py index 59640470..8d0edc63 100644 --- a/toqito/matrix_props/tests/test_is_orthonormal.py +++ b/toqito/matrix_props/tests/test_is_orthonormal.py @@ -1,4 +1,5 @@ """Test is_orthonormal.""" + import numpy as np from toqito.matrix_props import is_orthonormal diff --git a/toqito/matrix_props/tests/test_is_permutation.py b/toqito/matrix_props/tests/test_is_permutation.py index c91166f2..fa15d05a 100644 --- a/toqito/matrix_props/tests/test_is_permutation.py +++ b/toqito/matrix_props/tests/test_is_permutation.py @@ -1,4 +1,5 @@ """Test is_permutation.""" + import numpy as np from toqito.matrix_props import is_permutation diff --git a/toqito/matrix_props/tests/test_is_positive_definite.py b/toqito/matrix_props/tests/test_is_positive_definite.py index c13ba693..0bc3d24a 100644 --- a/toqito/matrix_props/tests/test_is_positive_definite.py +++ b/toqito/matrix_props/tests/test_is_positive_definite.py @@ -1,4 +1,5 @@ """Test is_positive_definite.""" + import numpy as np from toqito.matrix_props import is_positive_definite @@ -42,11 +43,7 @@ def test_is_not_positive_definite(): [1], ] ) - gram_eps = ( - 1 - / (1 - 2 * eps) - * (gram + eps * (v_vec @ v_vec.conj().T + w_vec @ w_vec.conj().T - 3 * np.identity(4))) - ) + gram_eps = 1 / (1 - 2 * eps) * (gram + eps * (v_vec @ v_vec.conj().T + w_vec @ w_vec.conj().T - 3 * np.identity(4))) np.testing.assert_equal(is_positive_definite(gram_eps), False) diff --git a/toqito/matrix_props/tests/test_is_positive_semidefinite.py b/toqito/matrix_props/tests/test_is_positive_semidefinite.py index f1ecfcd3..ebc41515 100644 --- a/toqito/matrix_props/tests/test_is_positive_semidefinite.py +++ b/toqito/matrix_props/tests/test_is_positive_semidefinite.py @@ -1,4 +1,5 @@ """Test is_positive_semidefinite.""" + import numpy as np from toqito.matrix_props import is_positive_semidefinite diff --git a/toqito/matrix_props/tests/test_is_projection.py b/toqito/matrix_props/tests/test_is_projection.py index 28df9422..70b015e2 100644 --- a/toqito/matrix_props/tests/test_is_projection.py +++ b/toqito/matrix_props/tests/test_is_projection.py @@ -1,4 +1,5 @@ """Test is_projection.""" + import numpy as np from toqito.matrix_props import is_projection diff --git a/toqito/matrix_props/tests/test_is_square.py b/toqito/matrix_props/tests/test_is_square.py index 89a5e48e..2360b771 100644 --- a/toqito/matrix_props/tests/test_is_square.py +++ b/toqito/matrix_props/tests/test_is_square.py @@ -1,4 +1,5 @@ """Test is_square.""" + import numpy as np from toqito.matrix_props import is_square diff --git a/toqito/matrix_props/tests/test_is_symmetric.py b/toqito/matrix_props/tests/test_is_symmetric.py index 0a8cc8d7..de4748fd 100644 --- a/toqito/matrix_props/tests/test_is_symmetric.py +++ b/toqito/matrix_props/tests/test_is_symmetric.py @@ -1,4 +1,5 @@ """Test is_symmetric.""" + import numpy as np from toqito.matrix_props import is_symmetric diff --git a/toqito/matrix_props/tests/test_is_totally_positive.py b/toqito/matrix_props/tests/test_is_totally_positive.py index db9ba7c4..59635527 100644 --- a/toqito/matrix_props/tests/test_is_totally_positive.py +++ b/toqito/matrix_props/tests/test_is_totally_positive.py @@ -1,41 +1,48 @@ """Test is_totally_positive.""" + import numpy as np import pytest from toqito.matrix_props import is_totally_positive -@pytest.mark.parametrize("mat, tol, sub_sizes, expected_result", [ - # 2x2 matrix that is known to be totally positive. - (np.array([[1, 2], [2, 5]]), 1e-6, None, True), - # 2x2 matrix that is not totally positive. - (np.array([[1, -2], [3, 4]]), 1e-6, None, False), - # 3x3 that is totally positive. - (np.array([[1, 2, 3], [2, 5, 8], [3, 8, 14]]), 1e-6, None, True), - # 3x3 that is not totally positive - (np.array([[1, 2, 3], [-1, -2, -3], [2, 5, 8]]), 1e-6, None, False), - # Matrix with complex entries (which should not be totally positive). - (np.array([[1 + 1j, 2], [3, 4]]), 1e-6, None, False), - # Matrix that is borderline not totally positive due to a very small negative determinant. - (np.array([[1, 2], [3, 4 - 1e-10]]), 1e-6, None, False), - # Test specifying custom sizes for submatrices. - (np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), 1e-6, [1, 2], False), - # Test specifying a custom tolerance level. - (np.array([[1, 2], [3, 4 - 1e-10]]), 1e-9, None, False), - # Test a matrix that is just a single row or column. - (np.array([[1, 2, 3]]), 1e-6, None, True), - # Test the identity matrix, which should be totally positive. - (np.identity(3), 1e-6, None, True), -]) +@pytest.mark.parametrize( + "mat, tol, sub_sizes, expected_result", + [ + # 2x2 matrix that is known to be totally positive. + (np.array([[1, 2], [2, 5]]), 1e-6, None, True), + # 2x2 matrix that is not totally positive. + (np.array([[1, -2], [3, 4]]), 1e-6, None, False), + # 3x3 that is totally positive. + (np.array([[1, 2, 3], [2, 5, 8], [3, 8, 14]]), 1e-6, None, True), + # 3x3 that is not totally positive + (np.array([[1, 2, 3], [-1, -2, -3], [2, 5, 8]]), 1e-6, None, False), + # Matrix with complex entries (which should not be totally positive). + (np.array([[1 + 1j, 2], [3, 4]]), 1e-6, None, False), + # Matrix that is borderline not totally positive due to a very small negative determinant. + (np.array([[1, 2], [3, 4 - 1e-10]]), 1e-6, None, False), + # Test specifying custom sizes for submatrices. + (np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), 1e-6, [1, 2], False), + # Test specifying a custom tolerance level. + (np.array([[1, 2], [3, 4 - 1e-10]]), 1e-9, None, False), + # Test a matrix that is just a single row or column. + (np.array([[1, 2, 3]]), 1e-6, None, True), + # Test the identity matrix, which should be totally positive. + (np.identity(3), 1e-6, None, True), + ], +) def test_is_totally_positive(mat, tol, sub_sizes, expected_result): """Test function works as expected for a valid input.""" np.testing.assert_equal(is_totally_positive(mat, tol, sub_sizes), expected_result) -@pytest.mark.parametrize("mat, tol, sub_sizes", [ - # Empty matrix is an invalid input - (np.array([]), 1e-6, None), -]) +@pytest.mark.parametrize( + "mat, tol, sub_sizes", + [ + # Empty matrix is an invalid input + (np.array([]), 1e-6, None), + ], +) def test_is_totally_positive_invalid(mat, tol, sub_sizes): """Test function works as expected for an invalid input.""" with np.testing.assert_raises(ValueError): diff --git a/toqito/matrix_props/tests/test_is_unitary.py b/toqito/matrix_props/tests/test_is_unitary.py index cb8db444..7810f907 100644 --- a/toqito/matrix_props/tests/test_is_unitary.py +++ b/toqito/matrix_props/tests/test_is_unitary.py @@ -1,4 +1,5 @@ """Test is_unitary.""" + import numpy as np from toqito.matrix_props import is_unitary diff --git a/toqito/matrix_props/tests/test_kp_norm.py b/toqito/matrix_props/tests/test_kp_norm.py index a8552ca8..8fbfe993 100644 --- a/toqito/matrix_props/tests/test_kp_norm.py +++ b/toqito/matrix_props/tests/test_kp_norm.py @@ -29,7 +29,5 @@ def test_kp_norm(vector, k, p, norm_to_compare): def test_no_default_kp_values(): """Test kp_norm does not have any default values for k or p.""" - with pytest.raises( - TypeError, match=re.escape("kp_norm() missing 2 required positional arguments: 'k' and 'p'") - ): + with pytest.raises(TypeError, match=re.escape("kp_norm() missing 2 required positional arguments: 'k' and 'p'")): kp_norm(bell(0)) # pylint: disable=no-value-for-parameter diff --git a/toqito/matrix_props/tests/test_majorizes.py b/toqito/matrix_props/tests/test_majorizes.py index 6b8bb84c..814416b3 100644 --- a/toqito/matrix_props/tests/test_majorizes.py +++ b/toqito/matrix_props/tests/test_majorizes.py @@ -1,4 +1,5 @@ """Test majorizes.""" + import numpy as np from toqito.channels import partial_trace diff --git a/toqito/matrix_props/tests/test_sk_norm.py b/toqito/matrix_props/tests/test_sk_norm.py index b732259c..1dd8170f 100644 --- a/toqito/matrix_props/tests/test_sk_norm.py +++ b/toqito/matrix_props/tests/test_sk_norm.py @@ -1,4 +1,5 @@ """Test S(k) operator norm.""" + import numpy as np import pytest diff --git a/toqito/matrix_props/tests/test_trace_norm.py b/toqito/matrix_props/tests/test_trace_norm.py index e4dd6d63..f7e7f7b8 100644 --- a/toqito/matrix_props/tests/test_trace_norm.py +++ b/toqito/matrix_props/tests/test_trace_norm.py @@ -1,4 +1,5 @@ """Tests for trace_norm.""" + import numpy as np from toqito.matrix_props import trace_norm diff --git a/toqito/matrix_props/trace_norm.py b/toqito/matrix_props/trace_norm.py index c76f4545..9f024fb7 100644 --- a/toqito/matrix_props/trace_norm.py +++ b/toqito/matrix_props/trace_norm.py @@ -1,4 +1,5 @@ """Trace norm metric.""" + import numpy as np diff --git a/toqito/measurement_ops/__init__.py b/toqito/measurement_ops/__init__.py index 15ea4c1d..e1e897e7 100644 --- a/toqito/measurement_ops/__init__.py +++ b/toqito/measurement_ops/__init__.py @@ -1,2 +1,3 @@ """Operations for measurement operators.""" + from toqito.measurement_ops.measure import measure diff --git a/toqito/measurement_ops/measure.py b/toqito/measurement_ops/measure.py index 9dd1d693..9dc56d4c 100644 --- a/toqito/measurement_ops/measure.py +++ b/toqito/measurement_ops/measure.py @@ -1,4 +1,5 @@ """Determine probability of obtaining measurement outcome.""" + import numpy as np diff --git a/toqito/measurement_ops/tests/test_measure.py b/toqito/measurement_ops/tests/test_measure.py index 51a7c49c..45749235 100644 --- a/toqito/measurement_ops/tests/test_measure.py +++ b/toqito/measurement_ops/tests/test_measure.py @@ -1,4 +1,5 @@ """Test measure.""" + import numpy as np from toqito.measurement_ops import measure diff --git a/toqito/measurement_props/__init__.py b/toqito/measurement_props/__init__.py index c4daa1a3..f76870bb 100644 --- a/toqito/measurement_props/__init__.py +++ b/toqito/measurement_props/__init__.py @@ -1,2 +1,3 @@ """Properties of measurement operators.""" + from toqito.measurement_props.is_povm import is_povm diff --git a/toqito/measurement_props/is_povm.py b/toqito/measurement_props/is_povm.py index 35ed655e..71d98aac 100644 --- a/toqito/measurement_props/is_povm.py +++ b/toqito/measurement_props/is_povm.py @@ -1,4 +1,5 @@ """Determine if a list of matrices are POVM elements.""" + import numpy as np from toqito.matrix_props import is_positive_semidefinite diff --git a/toqito/measurement_props/tests/test_is_povm.py b/toqito/measurement_props/tests/test_is_povm.py index 80e0c7c9..34091cc0 100644 --- a/toqito/measurement_props/tests/test_is_povm.py +++ b/toqito/measurement_props/tests/test_is_povm.py @@ -1,4 +1,5 @@ """Test is_povm.""" + import numpy as np from toqito.measurement_props import is_povm diff --git a/toqito/nonlocal_games/__init__.py b/toqito/nonlocal_games/__init__.py index b4803fdc..ef186202 100644 --- a/toqito/nonlocal_games/__init__.py +++ b/toqito/nonlocal_games/__init__.py @@ -1,4 +1,5 @@ """A number of nonlocal game-related functions for toqito.""" + from toqito.nonlocal_games.extended_nonlocal_game import ExtendedNonlocalGame from toqito.nonlocal_games.nonlocal_game import NonlocalGame from toqito.nonlocal_games.quantum_hedging import QuantumHedging diff --git a/toqito/nonlocal_games/extended_nonlocal_game.py b/toqito/nonlocal_games/extended_nonlocal_game.py index 280d2465..d778b62a 100644 --- a/toqito/nonlocal_games/extended_nonlocal_game.py +++ b/toqito/nonlocal_games/extended_nonlocal_game.py @@ -1,6 +1,5 @@ """Two-player extended nonlocal game.""" - from collections import defaultdict import cvxpy @@ -60,18 +59,18 @@ def __init__(self, prob_mat: np.ndarray, pred_mat: np.ndarray, reps: int = 1) -> pred_mat2 = np.zeros( ( - dim_x ** reps, - dim_y ** reps, - num_alice_out ** reps, - num_bob_out ** reps, - num_alice_in ** reps, - num_bob_in ** reps, + dim_x**reps, + dim_y**reps, + num_alice_out**reps, + num_bob_out**reps, + num_alice_in**reps, + num_bob_in**reps, ) ) i_ind = np.zeros(reps, dtype=int) j_ind = np.zeros(reps, dtype=int) - for i in range(num_alice_in ** reps): - for j in range(num_bob_in ** reps): + for i in range(num_alice_in**reps): + for j in range(num_bob_in**reps): to_tensor = np.empty([reps, dim_x, dim_y, num_alice_out, num_bob_out]) for k in range(reps - 1, -1, -1): to_tensor[k] = pred_mat[:, :, :, :, i_ind[k], j_ind[k]] @@ -109,10 +108,7 @@ def unentangled_value(self) -> float: p_win = np.zeros([dim_x, dim_y], dtype=complex) for x_in in range(alice_in): for y_in in range(bob_in): - p_win += ( - self.prob_mat[x_in, y_in] - * self.pred_mat[:, :, a_out, b_out, x_in, y_in] - ) + p_win += self.prob_mat[x_in, y_in] * self.pred_mat[:, :, a_out, b_out, x_in, y_in] rho = cvxpy.Variable((dim_x, dim_y), hermitian=True) @@ -174,9 +170,7 @@ def nonsignaling_value(self) -> float: for b_out in range(bob_out): for x_in in range(alice_in): for y_in in range(bob_in): - k_var[a_out, b_out, x_in, y_in] = cvxpy.Variable( - (dim_x, dim_y), hermitian=True - ) + k_var[a_out, b_out, x_in, y_in] = cvxpy.Variable((dim_x, dim_y), hermitian=True) constraints.append(k_var[a_out, b_out, x_in, y_in] >> 0) # Define \sigma_a^x variable. @@ -200,8 +194,7 @@ def nonsignaling_value(self) -> float: for x_in in range(alice_in): for y_in in range(bob_in): p_win += self.prob_mat[x_in, y_in] * cvxpy.trace( - self.pred_mat[:, :, a_out, b_out, x_in, y_in].conj().T - @ k_var[a_out, b_out, x_in, y_in] + self.pred_mat[:, :, a_out, b_out, x_in, y_in].conj().T @ k_var[a_out, b_out, x_in, y_in] ) objective = cvxpy.Maximize(cvxpy.real(p_win)) @@ -317,9 +310,7 @@ def __optimize_alice(self, bob_povms) -> tuple[dict, float]: rho = defaultdict(cvxpy.Variable) for x_ques in range(num_inputs_alice): for a_ans in range(num_outputs_alice): - rho[x_ques, a_ans] = cvxpy.Variable( - (dim * num_outputs_bob, dim * num_outputs_bob), hermitian=True - ) + rho[x_ques, a_ans] = cvxpy.Variable((dim * num_outputs_bob, dim * num_outputs_bob), hermitian=True) tau = cvxpy.Variable((dim * num_outputs_bob, dim * num_outputs_bob), hermitian=True) win = 0 diff --git a/toqito/nonlocal_games/nonlocal_game.py b/toqito/nonlocal_games/nonlocal_game.py index c6dbbb94..c0b71bdf 100644 --- a/toqito/nonlocal_games/nonlocal_game.py +++ b/toqito/nonlocal_games/nonlocal_game.py @@ -1,6 +1,5 @@ """Two-player nonlocal game.""" - from collections import defaultdict import cvxpy @@ -52,16 +51,16 @@ def __init__(self, prob_mat: np.ndarray, pred_mat: np.ndarray, reps: int = 1) -> pred_mat2 = np.zeros( ( - num_alice_out ** reps, - num_bob_out ** reps, - num_alice_in ** reps, - num_bob_in ** reps, + num_alice_out**reps, + num_bob_out**reps, + num_alice_in**reps, + num_bob_in**reps, ) ) i_ind = np.zeros(reps, dtype=int) j_ind = np.zeros(reps, dtype=int) - for i in range(num_alice_in ** reps): - for j in range(num_bob_in ** reps): + for i in range(num_alice_in**reps): + for j in range(num_bob_in**reps): to_tensor = np.empty([reps, num_alice_out, num_bob_out]) for k in range(reps - 1, -1, -1): to_tensor[k] = pred_mat[:, :, i_ind[k], j_ind[k]] @@ -113,7 +112,7 @@ def from_bcs_game(cls, constraints: list[np.ndarray], reps: int = 1) -> "Nonloca # Compute prediction matrix of outcomes given questions and answer pairs: # a: Alice's truth assignment to all variables in `c_x` # b: Bob's truth assignment for `v_y` in `c_x` - pred_mat = np.zeros((2 ** num_variables, 2, num_constraints, num_variables)) + pred_mat = np.zeros((2**num_variables, 2, num_constraints, num_variables)) for x_ques in range(num_constraints): for a_ans in range(pred_mat.shape[0]): # Convert to binary representation @@ -153,7 +152,7 @@ def classical_value(self) -> float: self.prob_mat[x_alice_in, y_bob_in] * self.pred_mat[:, :, x_alice_in, y_bob_in] ) p_win = float("-inf") - if num_alice_outputs ** num_alice_inputs < num_bob_outputs ** num_bob_inputs: + if num_alice_outputs**num_alice_inputs < num_bob_outputs**num_bob_inputs: self.pred_mat = np.transpose(self.pred_mat, (1, 0, 3, 2)) ( num_alice_outputs, @@ -169,7 +168,7 @@ def classical_value(self) -> float: # else: # parallel_threads = 5 - for i in range(num_alice_outputs ** num_bob_inputs): + for i in range(num_alice_outputs**num_bob_inputs): # Convert :code:`number` to the base :code:`base` with digits :code:`digits`. number = i base = num_bob_outputs @@ -402,9 +401,7 @@ def __optimize_alice(self, dim, bob_povms) -> tuple[dict, float]: win += ( self.prob_mat[x_ques, y_ques] * self.pred_mat[a_ans, b_ans, x_ques, y_ques] - * cvxpy.trace( - bob_povms[y_ques, b_ans].conj().T @ alice_povms[x_ques, a_ans] - ) + * cvxpy.trace(bob_povms[y_ques, b_ans].conj().T @ alice_povms[x_ques, a_ans]) ) if isinstance( bob_povms[y_ques, b_ans], @@ -414,10 +411,7 @@ def __optimize_alice(self, dim, bob_povms) -> tuple[dict, float]: win += ( self.prob_mat[x_ques, y_ques] * self.pred_mat[a_ans, b_ans, x_ques, y_ques] - * cvxpy.trace( - bob_povms[y_ques, b_ans].value.conj().T - @ alice_povms[x_ques, a_ans] - ) + * cvxpy.trace(bob_povms[y_ques, b_ans].value.conj().T @ alice_povms[x_ques, a_ans]) ) if is_real: @@ -466,9 +460,7 @@ def __optimize_bob(self, dim, alice_povms) -> tuple[dict, float]: win += ( self.prob_mat[x_ques, y_ques] * self.pred_mat[a_ans, b_ans, x_ques, y_ques] - * cvxpy.trace( - bob_povms[y_ques, b_ans].H @ alice_povms[x_ques, a_ans].value - ) + * cvxpy.trace(bob_povms[y_ques, b_ans].H @ alice_povms[x_ques, a_ans].value) ) objective = cvxpy.Maximize(win) @@ -502,9 +494,7 @@ def nonsignaling_value(self) -> float: for b_out in range(bob_out): for x_in in range(alice_in): for y_in in range(bob_in): - k_var[a_out, b_out, x_in, y_in] = cvxpy.Variable( - (dim_x, dim_y), hermitian=True - ) + k_var[a_out, b_out, x_in, y_in] = cvxpy.Variable((dim_x, dim_y), hermitian=True) constraints.append(k_var[a_out, b_out, x_in, y_in] >> 0) # Define \sigma_a^x variable. @@ -528,8 +518,7 @@ def nonsignaling_value(self) -> float: for x_in in range(alice_in): for y_in in range(bob_in): p_win += self.prob_mat[x_in, y_in] * cvxpy.trace( - self.pred_mat[a_out, b_out, x_in, y_in].conj().T - * k_var[a_out, b_out, x_in, y_in] + self.pred_mat[a_out, b_out, x_in, y_in].conj().T * k_var[a_out, b_out, x_in, y_in] ) objective = cvxpy.Maximize(cvxpy.real(p_win)) @@ -609,9 +598,7 @@ def commuting_measurement_value_upper_bound(self, k: int | str = 1) -> float: mat = defaultdict(cvxpy.Variable) for x_in in range(alice_in): for y_in in range(bob_in): - mat[x_in, y_in] = cvxpy.Variable( - (alice_out, bob_out), name=f"M(a, b | {x_in}, {y_in})" - ) + mat[x_in, y_in] = cvxpy.Variable((alice_out, bob_out), name=f"M(a, b | {x_in}, {y_in})") p_win = cvxpy.Constant(0) for a_out in range(alice_out): diff --git a/toqito/nonlocal_games/quantum_hedging.py b/toqito/nonlocal_games/quantum_hedging.py index 6c8815e6..3dfec169 100644 --- a/toqito/nonlocal_games/quantum_hedging.py +++ b/toqito/nonlocal_games/quantum_hedging.py @@ -1,4 +1,5 @@ """Semidefinite programs for obtaining values of quantum hedging scenarios.""" + import cvxpy import numpy as np @@ -90,7 +91,7 @@ def __init__(self, q_a: np.ndarray, num_reps: int) -> None: # π(y1 ⊗ y2 ⊗ x1 ⊗ x2) = y1 ⊗ x1 ⊗ y2 ⊗ x2 # for all y1 ∈ Y1, y2 ∈ Y2, x1 ∈ X1, x2 ∈ X2.). l_1 = list(range(1, self._num_reps + 1)) - l_2 = list(range(self._num_reps + 1, self._num_reps ** 2 + 1)) + l_2 = list(range(self._num_reps + 1, self._num_reps**2 + 1)) if self._num_reps == 1: self._pperm = np.array([1]) else: @@ -121,11 +122,9 @@ def max_prob_outcome_a_primal(self) -> float: :return: The optimal maximal probability for obtaining outcome "a". """ - x_var = cvxpy.Variable((4 ** self._num_reps, 4 ** self._num_reps), PSD=True) + x_var = cvxpy.Variable((4**self._num_reps, 4**self._num_reps), PSD=True) objective = cvxpy.Maximize(cvxpy.trace(self._q_a.conj().T @ x_var)) - constraints = [ - partial_trace(x_var, self._sys, self._dim) == np.identity(2 ** self._num_reps) - ] + constraints = [partial_trace(x_var, self._sys, self._dim) == np.identity(2**self._num_reps)] problem = cvxpy.Problem(objective, constraints) return problem.solve() @@ -150,10 +149,10 @@ def max_prob_outcome_a_dual(self) -> float: :return: The optimal maximal probability for obtaining outcome "a". """ - y_var = cvxpy.Variable((2 ** self._num_reps, 2 ** self._num_reps), hermitian=True) + y_var = cvxpy.Variable((2**self._num_reps, 2**self._num_reps), hermitian=True) objective = cvxpy.Minimize(cvxpy.trace(cvxpy.real(y_var))) - kron_var = cvxpy.kron(np.eye(2 ** self._num_reps), y_var) + kron_var = cvxpy.kron(np.eye(2**self._num_reps), y_var) if self._num_reps == 1: u_var = cvxpy.multiply(cvxpy.multiply(self._pperm, kron_var), self._pperm.conj().T) constraints = [cvxpy.real(u_var) >> self._q_a] @@ -187,11 +186,9 @@ def min_prob_outcome_a_primal(self) -> float: :return: The optimal minimal probability for obtaining outcome "a". """ - x_var = cvxpy.Variable((4 ** self._num_reps, 4 ** self._num_reps), PSD=True) + x_var = cvxpy.Variable((4**self._num_reps, 4**self._num_reps), PSD=True) objective = cvxpy.Minimize(cvxpy.trace(self._q_a.conj().T @ x_var)) - constraints = [ - partial_trace(x_var, self._sys, self._dim) == np.identity(2 ** self._num_reps) - ] + constraints = [partial_trace(x_var, self._sys, self._dim) == np.identity(2**self._num_reps)] problem = cvxpy.Problem(objective, constraints) return problem.solve() @@ -216,10 +213,10 @@ def min_prob_outcome_a_dual(self) -> float: :return: The optimal minimal probability for obtaining outcome "a". """ - y_var = cvxpy.Variable((2 ** self._num_reps, 2 ** self._num_reps), hermitian=True) + y_var = cvxpy.Variable((2**self._num_reps, 2**self._num_reps), hermitian=True) objective = cvxpy.Maximize(cvxpy.trace(cvxpy.real(y_var))) - kron_var = cvxpy.kron(np.eye(2 ** self._num_reps), y_var) + kron_var = cvxpy.kron(np.eye(2**self._num_reps), y_var) if self._num_reps == 1: u_var = cvxpy.multiply(cvxpy.multiply(self._pperm, kron_var), self._pperm.conj().T) diff --git a/toqito/nonlocal_games/tests/test_extended_nonlocal_game.py b/toqito/nonlocal_games/tests/test_extended_nonlocal_game.py index f1c1c73f..a8162a2e 100644 --- a/toqito/nonlocal_games/tests/test_extended_nonlocal_game.py +++ b/toqito/nonlocal_games/tests/test_extended_nonlocal_game.py @@ -1,4 +1,5 @@ """Tests for ExtendedNonlocalGame class.""" + import unittest import numpy as np @@ -66,17 +67,17 @@ def moe_mub_4_in_3_out_game(): mub_0 = [e_0, e_1, e_2] mub_1 = [ (e_0 + e_1 + e_2) / np.sqrt(3), - (e_0 + eta ** 2 * e_1 + eta * e_2) / np.sqrt(3), - (e_0 + eta * e_1 + eta ** 2 * e_2) / np.sqrt(3), + (e_0 + eta**2 * e_1 + eta * e_2) / np.sqrt(3), + (e_0 + eta * e_1 + eta**2 * e_2) / np.sqrt(3), ] mub_2 = [ (e_0 + e_1 + eta * e_2) / np.sqrt(3), - (e_0 + eta ** 2 * e_1 + eta ** 2 * e_2) / np.sqrt(3), + (e_0 + eta**2 * e_1 + eta**2 * e_2) / np.sqrt(3), (e_0 + eta * e_1 + e_2) / np.sqrt(3), ] mub_3 = [ - (e_0 + e_1 + eta ** 2 * e_2) / np.sqrt(3), - (e_0 + eta ** 2 * e_1 + e_2) / np.sqrt(3), + (e_0 + e_1 + eta**2 * e_2) / np.sqrt(3), + (e_0 + eta**2 * e_1 + e_2) / np.sqrt(3), (e_0 + eta * e_1 + eta * e_2) / np.sqrt(3), ] diff --git a/toqito/nonlocal_games/tests/test_nonlocal_game.py b/toqito/nonlocal_games/tests/test_nonlocal_game.py index de82cd16..4993f393 100644 --- a/toqito/nonlocal_games/tests/test_nonlocal_game.py +++ b/toqito/nonlocal_games/tests/test_nonlocal_game.py @@ -1,4 +1,5 @@ """Tests for NonlocalGame class.""" + import unittest import numpy as np diff --git a/toqito/nonlocal_games/tests/test_quantum_hedging.py b/toqito/nonlocal_games/tests/test_quantum_hedging.py index a02c9101..4c2d1a11 100644 --- a/toqito/nonlocal_games/tests/test_quantum_hedging.py +++ b/toqito/nonlocal_games/tests/test_quantum_hedging.py @@ -1,4 +1,5 @@ """Tests for hedging_value function.""" + import unittest from numpy import cos, isclose, kron, pi, sin, sqrt @@ -17,13 +18,13 @@ class TestQuantumHedging(unittest.TestCase): alpha = 1 / sqrt(2) theta = pi / 8 - w_var = alpha * cos(theta) * e_00 + sqrt(1 - alpha ** 2) * sin(theta) * e_11 + w_var = alpha * cos(theta) * e_00 + sqrt(1 - alpha**2) * sin(theta) * e_11 - l_1 = -alpha * sin(theta) * e_00 + sqrt(1 - alpha ** 2) * cos(theta) * e_11 + l_1 = -alpha * sin(theta) * e_00 + sqrt(1 - alpha**2) * cos(theta) * e_11 l_2 = alpha * sin(theta) * e_10 - l_3 = sqrt(1 - alpha ** 2) * cos(theta) * e_01 + l_3 = sqrt(1 - alpha**2) * cos(theta) * e_01 q_1 = w_var * w_var.conj().T q_0 = l_1 * l_1.conj().T + l_2 * l_2.conj().T + l_3 * l_3.conj().T diff --git a/toqito/nonlocal_games/tests/test_xor_game.py b/toqito/nonlocal_games/tests/test_xor_game.py index 0afa3c3b..6b48d6b5 100644 --- a/toqito/nonlocal_games/tests/test_xor_game.py +++ b/toqito/nonlocal_games/tests/test_xor_game.py @@ -1,4 +1,5 @@ """Tests for XORGame class.""" + import unittest import numpy as np diff --git a/toqito/nonlocal_games/xor_game.py b/toqito/nonlocal_games/xor_game.py index 5982b899..d9410855 100644 --- a/toqito/nonlocal_games/xor_game.py +++ b/toqito/nonlocal_games/xor_game.py @@ -1,4 +1,5 @@ """Two-player XOR game.""" + import cvxpy import numpy as np @@ -160,27 +161,21 @@ def __init__( q_0, q_1 = self.prob_mat.shape if tol is None: - self.tol = np.finfo(float).eps * q_0 ** 2 * q_1 ** 2 + self.tol = np.finfo(float).eps * q_0**2 * q_1**2 else: self.tol = tol # Perform some basic error checking to ensure the probability and # predicate matrices are well-defined. if (q_0, q_1) != self.pred_mat.shape: - raise ValueError( - "Invalid: The matrices `prob_mat` and `pred_mat` must" - " be matrices of the same size." - ) + raise ValueError("Invalid: The matrices `prob_mat` and `pred_mat` must be matrices of the same size.") if -np.min(np.min(self.prob_mat)) > self.tol: raise ValueError( - "Invalid: The variable `prob_mat` must be a " - "probability matrix: its entries must be " - "non-negative." + "Invalid: The variable `prob_mat` must be a probability matrix: its entries must be non-negative." ) if np.abs(np.sum(np.sum(self.prob_mat)) - 1) > self.tol: raise ValueError( - "Invalid: The variable `prob_mat` must be a " - "probability matrix: its entries must sum to 1." + "Invalid: The variable `prob_mat` must be a probability matrix: its entries must sum to 1." ) def quantum_value(self) -> float: @@ -221,9 +216,7 @@ def quantum_value(self) -> float: for x_alice in range(alice_in): for y_bob in range(bob_in): - d_mat[x_alice, y_bob] = self.prob_mat[x_alice, y_bob] * (-1) ** ( - self.pred_mat[x_alice, y_bob] - ) + d_mat[x_alice, y_bob] = self.prob_mat[x_alice, y_bob] * (-1) ** (self.pred_mat[x_alice, y_bob]) u_vec = cvxpy.Variable(alice_in, complex=False) v_vec = cvxpy.Variable(bob_in, complex=False) diff --git a/toqito/perms/__init__.py b/toqito/perms/__init__.py index 560b8d62..c24e3925 100644 --- a/toqito/perms/__init__.py +++ b/toqito/perms/__init__.py @@ -1,4 +1,5 @@ """Permutation and combinatorial functions.""" + from toqito.perms.unique_perms import unique_perms from toqito.perms.perfect_matchings import perfect_matchings from toqito.perms.perm_sign import perm_sign diff --git a/toqito/perms/antisymmetric_projection.py b/toqito/perms/antisymmetric_projection.py index e68d8087..cc0cd629 100644 --- a/toqito/perms/antisymmetric_projection.py +++ b/toqito/perms/antisymmetric_projection.py @@ -1,4 +1,5 @@ """Antisymmetric projection operator.""" + from itertools import permutations import numpy as np @@ -76,7 +77,7 @@ def antisymmetric_projection(dim: int, p_param: int = 2, partial: bool = False) :return: Projection onto the antisymmetric subspace. """ - dimp = dim ** p_param + dimp = dim**p_param if p_param == 1: return sparse.eye(dim) @@ -89,9 +90,7 @@ def antisymmetric_projection(dim: int, p_param: int = 2, partial: bool = False) anti_proj = sparse.lil_matrix((dimp, dimp)) for j in range(p_fac): - anti_proj += perm_sign(p_list[j, :]) * permutation_operator( - dim * np.ones(p_param), p_list[j, :], False, True - ) + anti_proj += perm_sign(p_list[j, :]) * permutation_operator(dim * np.ones(p_param), p_list[j, :], False, True) anti_proj = anti_proj / p_fac if partial: diff --git a/toqito/perms/perfect_matchings.py b/toqito/perms/perfect_matchings.py index d730b14f..1d08af4b 100644 --- a/toqito/perms/perfect_matchings.py +++ b/toqito/perms/perfect_matchings.py @@ -1,4 +1,5 @@ """Perfect matchings.""" + import numpy as np diff --git a/toqito/perms/perm_sign.py b/toqito/perms/perm_sign.py index 3a214d08..08469748 100644 --- a/toqito/perms/perm_sign.py +++ b/toqito/perms/perm_sign.py @@ -1,4 +1,5 @@ """Calculate permutation sign.""" + import numpy as np from scipy import linalg diff --git a/toqito/perms/permutation_operator.py b/toqito/perms/permutation_operator.py index f599017c..ba08ede6 100644 --- a/toqito/perms/permutation_operator.py +++ b/toqito/perms/permutation_operator.py @@ -1,4 +1,5 @@ """Permutation operator.""" + import numpy as np import scipy as sp diff --git a/toqito/perms/permute_systems.py b/toqito/perms/permute_systems.py index 5bbf9a46..4bedc398 100644 --- a/toqito/perms/permute_systems.py +++ b/toqito/perms/permute_systems.py @@ -1,4 +1,5 @@ """Permute systems.""" + import functools import operator @@ -178,9 +179,7 @@ def permute_systems( if sorted(perm) != list(range(1, num_sys + 1)): raise ValueError("InvalidPerm: `perm` must be a permutation vector.") if input_mat_dims[0] != prod_dim_r or (not row_only and input_mat_dims[1] != prod_dim_c): - raise ValueError( - "InvalidDim: The dimensions specified in DIM do not agree with the size of X." - ) + raise ValueError("InvalidDim: The dimensions specified in DIM do not agree with the size of X.") if is_vec: # If `input_mat` is a 1-by-X row vector, ensure we "flatten it" appropriately: if input_mat.shape[0] == 1: @@ -188,9 +187,7 @@ def permute_systems( vec_orien = 1 permuted_mat_1 = input_mat.reshape(dim[vec_orien, ::-1].astype(int), order="F") if inv_perm: - permuted_mat = vec( - np.transpose(permuted_mat_1, np.argsort(num_sys - np.array(perm[::-1]))) - ).T + permuted_mat = vec(np.transpose(permuted_mat_1, np.argsort(num_sys - np.array(perm[::-1])))).T else: permuted_mat = vec(np.transpose(permuted_mat_1, num_sys - np.array(perm[::-1]))).T @@ -211,7 +208,7 @@ def permute_systems( if isinstance(input_mat, (sparse.csr_matrix, sparse.dia_matrix)): input_mat = input_mat.toarray() permuted_mat = input_mat[row_perm, :] - permuted_mat = np.array(permuted_mat) # pylint: disable=redefined-variable-type + permuted_mat = np.array(permuted_mat) # pylint: disable=redefined-variable-type else: permuted_mat = input_mat[row_perm, :] diff --git a/toqito/perms/swap.py b/toqito/perms/swap.py index daf2badb..c82e0d8f 100644 --- a/toqito/perms/swap.py +++ b/toqito/perms/swap.py @@ -1,4 +1,5 @@ """Swap.""" + import numpy as np from toqito.perms import permute_systems diff --git a/toqito/perms/swap_operator.py b/toqito/perms/swap_operator.py index 1617c001..112bf3dc 100644 --- a/toqito/perms/swap_operator.py +++ b/toqito/perms/swap_operator.py @@ -1,4 +1,5 @@ """Swap operator.""" + import numpy as np import scipy as sp diff --git a/toqito/perms/symmetric_projection.py b/toqito/perms/symmetric_projection.py index 5c2c8f55..51761a88 100644 --- a/toqito/perms/symmetric_projection.py +++ b/toqito/perms/symmetric_projection.py @@ -1,4 +1,5 @@ """Symmetric projection operator.""" + import math from itertools import permutations @@ -8,7 +9,7 @@ from toqito.perms import permutation_operator -def symmetric_projection( dim: int, p_val: int = 2, partial: bool = False ) -> [np.ndarray, scipy.sparse.lil_matrix]: +def symmetric_projection(dim: int, p_val: int = 2, partial: bool = False) -> [np.ndarray, scipy.sparse.lil_matrix]: r"""Produce the projection onto the symmetric subspace :cite:`Chen_2014_Symmetric`. For a complex Euclidean space :math:`\mathcal{X}` and a positive integer :math:`n`, the projection onto the @@ -75,7 +76,7 @@ def symmetric_projection( dim: int, p_val: int = 2, partial: bool = False ) -> [ :return: Projection onto the symmetric subspace. """ - dimp = dim ** p_val + dimp = dim**p_val if p_val == 1: return np.eye(dim) diff --git a/toqito/perms/tests/test_antisymmetric_projection.py b/toqito/perms/tests/test_antisymmetric_projection.py index 192daae1..60be2267 100644 --- a/toqito/perms/tests/test_antisymmetric_projection.py +++ b/toqito/perms/tests/test_antisymmetric_projection.py @@ -16,17 +16,21 @@ anti_proj_3_3_partial[19] = -0.40824829 anti_proj_3_3_partial[21] = 0.40824829 -@pytest.mark.parametrize("dim, p_param, partial, expected_result", [ - # Dimension is 2 and p is equal to 1. - (2, 1, False, np.array([[1, 0], [0, 1]])), - # The `p` value is greater than the dimension `d`. - (2, 3, False, np.zeros((8, 8))), - # The dimension is 2. - (2, 2, False, np.array([[0, 0, 0, 0], [0, 0.5, -0.5, 0], [0, -0.5, 0.5, 0], [0, 0, 0, 0]])), - # The `dim` is 3, the `p` is 3, and `partial` is True. - (3, 3, True, anti_proj_3_3_partial) -]) + +@pytest.mark.parametrize( + "dim, p_param, partial, expected_result", + [ + # Dimension is 2 and p is equal to 1. + (2, 1, False, np.array([[1, 0], [0, 1]])), + # The `p` value is greater than the dimension `d`. + (2, 3, False, np.zeros((8, 8))), + # The dimension is 2. + (2, 2, False, np.array([[0, 0, 0, 0], [0, 0.5, -0.5, 0], [0, -0.5, 0.5, 0], [0, 0, 0, 0]])), + # The `dim` is 3, the `p` is 3, and `partial` is True. + (3, 3, True, anti_proj_3_3_partial), + ], +) def test_antisymmetric_projection(dim, p_param, partial, expected_result): """Test function works as expected for a valid input.""" proj = antisymmetric_projection(dim=dim, p_param=p_param, partial=partial).todense() - assert abs(proj - expected_result).all() <= 1E-3 + assert abs(proj - expected_result).all() <= 1e-3 diff --git a/toqito/perms/tests/test_perfect_matchings.py b/toqito/perms/tests/test_perfect_matchings.py index e17b74ab..432133dd 100644 --- a/toqito/perms/tests/test_perfect_matchings.py +++ b/toqito/perms/tests/test_perfect_matchings.py @@ -1,4 +1,5 @@ """Test perfect_matchings.""" + import numpy as np from toqito.perms import perfect_matchings diff --git a/toqito/perms/tests/test_perm_sign.py b/toqito/perms/tests/test_perm_sign.py index d9167bbc..18351b31 100644 --- a/toqito/perms/tests/test_perm_sign.py +++ b/toqito/perms/tests/test_perm_sign.py @@ -1,4 +1,5 @@ """Test perm_sign.""" + import numpy as np from toqito.perms import perm_sign diff --git a/toqito/perms/tests/test_permutation_operator.py b/toqito/perms/tests/test_permutation_operator.py index 64bec5c3..a7bd831e 100644 --- a/toqito/perms/tests/test_permutation_operator.py +++ b/toqito/perms/tests/test_permutation_operator.py @@ -1,4 +1,5 @@ """Test permutation_operator.""" + import numpy as np from toqito.perms import permutation_operator diff --git a/toqito/perms/tests/test_permute_systems.py b/toqito/perms/tests/test_permute_systems.py index cda27616..4fe098fc 100644 --- a/toqito/perms/tests/test_permute_systems.py +++ b/toqito/perms/tests/test_permute_systems.py @@ -1,4 +1,5 @@ """Test permute_systems.""" + import numpy as np from toqito.perms import permute_systems diff --git a/toqito/perms/tests/test_swap.py b/toqito/perms/tests/test_swap.py index 3cb1a173..d00a1577 100644 --- a/toqito/perms/tests/test_swap.py +++ b/toqito/perms/tests/test_swap.py @@ -1,449 +1,521 @@ """Test swap.""" + import numpy as np import pytest from toqito.perms import swap -@pytest.mark.parametrize("input_matrix, sys, dim, row_only, expected_result", [ - # 4-by-4 matrix. Default argument (systems [1, 2]): - ( - np.arange(1, 17).reshape(4, 4).transpose(), - None, None, False, - np.array([[1, 9, 5, 13], [3, 11, 7, 15], [2, 10, 6, 14], [4, 12, 8, 16]]) - ), - # Hard-coded argument (systems [1, 2]): - ( - np.arange(1, 17).reshape(4, 4).transpose(), - [1, 2], [2, 2], False, - np.array([[1, 9, 5, 13], [3, 11, 7, 15], [2, 10, 6, 14], [4, 12, 8, 16]]) - ), - # Hard-coded argument (systems [2, 1])--this should be identical to the prior two cases. - ( - np.arange(1, 17).reshape(4, 4).transpose(), - [2, 1], [2, 2], False, - np.array([[1, 9, 5, 13], [3, 11, 7, 15], [2, 10, 6, 14], [4, 12, 8, 16]]) - ), - # Swap operation on vector. - ( - np.array([1, 2, 3, 4]), - None, None, False, - np.array([1, 3, 2, 4]) - ), - # Test swap operation when int is provided. - ( - np.array([[1, 5, 9, 13], [2, 6, 10, 14], [3, 7, 11, 15], [4, 8, 12, 16]]), - [1, 2], 2, False, - np.array([[1, 9, 5, 13], [3, 11, 7, 15], [2, 10, 6, 14], [4, 12, 8, 16]]) - ), - # Test swap on an 8-by-8 matrix. Swapping on systems "1" and "2". - ( - np.arange(1, 65).reshape(8, 8).transpose(), - [1, 2], [2, 2, 2], False, - np.array([ - [1, 9, 33, 41, 17, 25, 49, 57], - [2, 10, 34, 42, 18, 26, 50, 58], - [5, 13, 37, 45, 21, 29, 53, 61], - [6, 14, 38, 46, 22, 30, 54, 62], - [3, 11, 35, 43, 19, 27, 51, 59], - [4, 12, 36, 44, 20, 28, 52, 60], - [7, 15, 39, 47, 23, 31, 55, 63], - [8, 16, 40, 48, 24, 32, 56, 64]] - ) - ), - # Swapping on systems "2" and "1" (should be same as swapping on "1" and "2"): - ( - np.arange(1, 65).reshape(8, 8).transpose(), - [2, 1], [2, 2, 2], False, - np.array([ - [1, 9, 33, 41, 17, 25, 49, 57], - [2, 10, 34, 42, 18, 26, 50, 58], - [5, 13, 37, 45, 21, 29, 53, 61], - [6, 14, 38, 46, 22, 30, 54, 62], - [3, 11, 35, 43, 19, 27, 51, 59], - [4, 12, 36, 44, 20, 28, 52, 60], - [7, 15, 39, 47, 23, 31, 55, 63], - [8, 16, 40, 48, 24, 32, 56, 64]] - ) - ), - # Swapping on systems "1" and "3": - ( - np.arange(1, 65).reshape(8, 8).transpose(), - [1, 3], [2, 2, 2], False, - np.array( - [ - [1, 33, 17, 49, 9, 41, 25, 57], - [5, 37, 21, 53, 13, 45, 29, 61], - [3, 35, 19, 51, 11, 43, 27, 59], - [7, 39, 23, 55, 15, 47, 31, 63], - [2, 34, 18, 50, 10, 42, 26, 58], - [6, 38, 22, 54, 14, 46, 30, 62], - [4, 36, 20, 52, 12, 44, 28, 60], - [8, 40, 24, 56, 16, 48, 32, 64]] - ) - ), - # Swapping on systems "3" and "1" (should be same as swapping on "1" and "3"): - ( - np.arange(1, 65).reshape(8, 8).transpose(), - [3, 1], [2, 2, 2], False, - np.array( - [ - [1, 33, 17, 49, 9, 41, 25, 57], - [5, 37, 21, 53, 13, 45, 29, 61], - [3, 35, 19, 51, 11, 43, 27, 59], - [7, 39, 23, 55, 15, 47, 31, 63], - [2, 34, 18, 50, 10, 42, 26, 58], - [6, 38, 22, 54, 14, 46, 30, 62], - [4, 36, 20, 52, 12, 44, 28, 60], - [8, 40, 24, 56, 16, 48, 32, 64]] - ) - ), - # Swapping on systems "2" and "3": - ( - np.arange(1, 65).reshape(8, 8).transpose(), - [2, 3], [2, 2, 2], False, - np.array( - [ - [1, 17, 9, 25, 33, 49, 41, 57], - [3, 19, 11, 27, 35, 51, 43, 59], - [2, 18, 10, 26, 34, 50, 42, 58], - [4, 20, 12, 28, 36, 52, 44, 60], - [5, 21, 13, 29, 37, 53, 45, 61], - [7, 23, 15, 31, 39, 55, 47, 63], - [6, 22, 14, 30, 38, 54, 46, 62], - [8, 24, 16, 32, 40, 56, 48, 64]] - ) - ), - # Swapping on systems "3" and "2" (should be same as swapping on "2" and "3"): - ( - np.arange(1, 65).reshape(8, 8).transpose(), - [3, 2], [2, 2, 2], False, - np.array( - [ - [1, 17, 9, 25, 33, 49, 41, 57], - [3, 19, 11, 27, 35, 51, 43, 59], - [2, 18, 10, 26, 34, 50, 42, 58], - [4, 20, 12, 28, 36, 52, 44, 60], - [5, 21, 13, 29, 37, 53, 45, 61], - [7, 23, 15, 31, 39, 55, 47, 63], - [6, 22, 14, 30, 38, 54, 46, 62], - [8, 24, 16, 32, 40, 56, 48, 64]] - ) - ), - # Test swap on an 16-by-16 matrix. - ( - np.arange(1, 257).reshape(16, 16).transpose(), - [1, 2], [2, 2, 2, 2], False, - np.array( - [ - [1, 17, 33, 49, 129, 145, 161, 177, 65, 81, 97, 113, 193, 209, 225, 241], - [2, 18, 34, 50, 130, 146, 162, 178, 66, 82, 98, 114, 194, 210, 226, 242], - [3, 19, 35, 51, 131, 147, 163, 179, 67, 83, 99, 115, 195, 211, 227, 243], - [4, 20, 36, 52, 132, 148, 164, 180, 68, 84, 100, 116, 196, 212, 228, 244], - [9, 25, 41, 57, 137, 153, 169, 185, 73, 89, 105, 121, 201, 217, 233, 249], - [10, 26, 42, 58, 138, 154, 170, 186, 74, 90, 106, 122, 202, 218, 234, 250], - [11, 27, 43, 59, 139, 155, 171, 187, 75, 91, 107, 123, 203, 219, 235, 251], - [12, 28, 44, 60, 140, 156, 172, 188, 76, 92, 108, 124, 204, 220, 236, 252], - [5, 21, 37, 53, 133, 149, 165, 181, 69, 85, 101, 117, 197, 213, 229, 245], - [6, 22, 38, 54, 134, 150, 166, 182, 70, 86, 102, 118, 198, 214, 230, 246], - [7, 23, 39, 55, 135, 151, 167, 183, 71, 87, 103, 119, 199, 215, 231, 247], - [8, 24, 40, 56, 136, 152, 168, 184, 72, 88, 104, 120, 200, 216, 232, 248], - [13, 29, 45, 61, 141, 157, 173, 189, 77, 93, 109, 125, 205, 221, 237, 253], - [14, 30, 46, 62, 142, 158, 174, 190, 78, 94, 110, 126, 206, 222, 238, 254], - [15, 31, 47, 63, 143, 159, 175, 191, 79, 95, 111, 127, 207, 223, 239, 255], - [16, 32, 48, 64, 144, 160, 176, 192, 80, 96, 112, 128, 208, 224, 240, 256]] - ) - ), - # Swapping on systems "2" and "1" (should be same as swapping on "1" and "2"): - ( - np.arange(1, 257).reshape(16, 16).transpose(), - [2, 1], [2, 2, 2, 2], False, - np.array( - [ - [1, 17, 33, 49, 129, 145, 161, 177, 65, 81, 97, 113, 193, 209, 225, 241], - [2, 18, 34, 50, 130, 146, 162, 178, 66, 82, 98, 114, 194, 210, 226, 242], - [3, 19, 35, 51, 131, 147, 163, 179, 67, 83, 99, 115, 195, 211, 227, 243], - [4, 20, 36, 52, 132, 148, 164, 180, 68, 84, 100, 116, 196, 212, 228, 244], - [9, 25, 41, 57, 137, 153, 169, 185, 73, 89, 105, 121, 201, 217, 233, 249], - [10, 26, 42, 58, 138, 154, 170, 186, 74, 90, 106, 122, 202, 218, 234, 250], - [11, 27, 43, 59, 139, 155, 171, 187, 75, 91, 107, 123, 203, 219, 235, 251], - [12, 28, 44, 60, 140, 156, 172, 188, 76, 92, 108, 124, 204, 220, 236, 252], - [5, 21, 37, 53, 133, 149, 165, 181, 69, 85, 101, 117, 197, 213, 229, 245], - [6, 22, 38, 54, 134, 150, 166, 182, 70, 86, 102, 118, 198, 214, 230, 246], - [7, 23, 39, 55, 135, 151, 167, 183, 71, 87, 103, 119, 199, 215, 231, 247], - [8, 24, 40, 56, 136, 152, 168, 184, 72, 88, 104, 120, 200, 216, 232, 248], - [13, 29, 45, 61, 141, 157, 173, 189, 77, 93, 109, 125, 205, 221, 237, 253], - [14, 30, 46, 62, 142, 158, 174, 190, 78, 94, 110, 126, 206, 222, 238, 254], - [15, 31, 47, 63, 143, 159, 175, 191, 79, 95, 111, 127, 207, 223, 239, 255], - [16, 32, 48, 64, 144, 160, 176, 192, 80, 96, 112, 128, 208, 224, 240, 256]] - ) - ), - # Swapping on systems "1" and "3": - ( - np.arange(1, 257).reshape(16, 16).transpose(), - [1, 3], [2, 2, 2, 2], False, - np.array( - [ - [1, 17, 129, 145, 65, 81, 193, 209, 33, 49, 161, 177, 97, 113, 225, 241], - [2, 18, 130, 146, 66, 82, 194, 210, 34, 50, 162, 178, 98, 114, 226, 242], - [9, 25, 137, 153, 73, 89, 201, 217, 41, 57, 169, 185, 105, 121, 233, 249], - [10, 26, 138, 154, 74, 90, 202, 218, 42, 58, 170, 186, 106, 122, 234, 250], - [5, 21, 133, 149, 69, 85, 197, 213, 37, 53, 165, 181, 101, 117, 229, 245], - [6, 22, 134, 150, 70, 86, 198, 214, 38, 54, 166, 182, 102, 118, 230, 246], - [13, 29, 141, 157, 77, 93, 205, 221, 45, 61, 173, 189, 109, 125, 237, 253], - [14, 30, 142, 158, 78, 94, 206, 222, 46, 62, 174, 190, 110, 126, 238, 254], - [3, 19, 131, 147, 67, 83, 195, 211, 35, 51, 163, 179, 99, 115, 227, 243], - [4, 20, 132, 148, 68, 84, 196, 212, 36, 52, 164, 180, 100, 116, 228, 244], - [11, 27, 139, 155, 75, 91, 203, 219, 43, 59, 171, 187, 107, 123, 235, 251], - [12, 28, 140, 156, 76, 92, 204, 220, 44, 60, 172, 188, 108, 124, 236, 252], - [7, 23, 135, 151, 71, 87, 199, 215, 39, 55, 167, 183, 103, 119, 231, 247], - [8, 24, 136, 152, 72, 88, 200, 216, 40, 56, 168, 184, 104, 120, 232, 248], - [15, 31, 143, 159, 79, 95, 207, 223, 47, 63, 175, 191, 111, 127, 239, 255], - [16, 32, 144, 160, 80, 96, 208, 224, 48, 64, 176, 192, 112, 128, 240, 256]] - ) - ), - # Swapping on systems "3" and "1" (should be same as swapping on "1" and "3"): - ( - np.arange(1, 257).reshape(16, 16).transpose(), - [3, 1], [2, 2, 2, 2], False, - np.array( - [ - [1, 17, 129, 145, 65, 81, 193, 209, 33, 49, 161, 177, 97, 113, 225, 241], - [2, 18, 130, 146, 66, 82, 194, 210, 34, 50, 162, 178, 98, 114, 226, 242], - [9, 25, 137, 153, 73, 89, 201, 217, 41, 57, 169, 185, 105, 121, 233, 249], - [10, 26, 138, 154, 74, 90, 202, 218, 42, 58, 170, 186, 106, 122, 234, 250], - [5, 21, 133, 149, 69, 85, 197, 213, 37, 53, 165, 181, 101, 117, 229, 245], - [6, 22, 134, 150, 70, 86, 198, 214, 38, 54, 166, 182, 102, 118, 230, 246], - [13, 29, 141, 157, 77, 93, 205, 221, 45, 61, 173, 189, 109, 125, 237, 253], - [14, 30, 142, 158, 78, 94, 206, 222, 46, 62, 174, 190, 110, 126, 238, 254], - [3, 19, 131, 147, 67, 83, 195, 211, 35, 51, 163, 179, 99, 115, 227, 243], - [4, 20, 132, 148, 68, 84, 196, 212, 36, 52, 164, 180, 100, 116, 228, 244], - [11, 27, 139, 155, 75, 91, 203, 219, 43, 59, 171, 187, 107, 123, 235, 251], - [12, 28, 140, 156, 76, 92, 204, 220, 44, 60, 172, 188, 108, 124, 236, 252], - [7, 23, 135, 151, 71, 87, 199, 215, 39, 55, 167, 183, 103, 119, 231, 247], - [8, 24, 136, 152, 72, 88, 200, 216, 40, 56, 168, 184, 104, 120, 232, 248], - [15, 31, 143, 159, 79, 95, 207, 223, 47, 63, 175, 191, 111, 127, 239, 255], - [16, 32, 144, 160, 80, 96, 208, 224, 48, 64, 176, 192, 112, 128, 240, 256]] - ) - ), - # Swapping on systems "1" and "4": - ( - np.arange(1, 257).reshape(16, 16).transpose(), - [1, 4], [2, 2, 2, 2], False, - np.array( - [ - [1, 129, 33, 161, 65, 193, 97, 225, 17, 145, 49, 177, 81, 209, 113, 241], - [9, 137, 41, 169, 73, 201, 105, 233, 25, 153, 57, 185, 89, 217, 121, 249], - [3, 131, 35, 163, 67, 195, 99, 227, 19, 147, 51, 179, 83, 211, 115, 243], - [11, 139, 43, 171, 75, 203, 107, 235, 27, 155, 59, 187, 91, 219, 123, 251], - [5, 133, 37, 165, 69, 197, 101, 229, 21, 149, 53, 181, 85, 213, 117, 245], - [13, 141, 45, 173, 77, 205, 109, 237, 29, 157, 61, 189, 93, 221, 125, 253], - [7, 135, 39, 167, 71, 199, 103, 231, 23, 151, 55, 183, 87, 215, 119, 247], - [15, 143, 47, 175, 79, 207, 111, 239, 31, 159, 63, 191, 95, 223, 127, 255], - [2, 130, 34, 162, 66, 194, 98, 226, 18, 146, 50, 178, 82, 210, 114, 242], - [10, 138, 42, 170, 74, 202, 106, 234, 26, 154, 58, 186, 90, 218, 122, 250], - [4, 132, 36, 164, 68, 196, 100, 228, 20, 148, 52, 180, 84, 212, 116, 244], - [12, 140, 44, 172, 76, 204, 108, 236, 28, 156, 60, 188, 92, 220, 124, 252], - [6, 134, 38, 166, 70, 198, 102, 230, 22, 150, 54, 182, 86, 214, 118, 246], - [14, 142, 46, 174, 78, 206, 110, 238, 30, 158, 62, 190, 94, 222, 126, 254], - [8, 136, 40, 168, 72, 200, 104, 232, 24, 152, 56, 184, 88, 216, 120, 248], - [16, 144, 48, 176, 80, 208, 112, 240, 32, 160, 64, 192, 96, 224, 128, 256]] - ) - ), - # Swapping on systems "4" and "1" (should be same as swapping on "1" and "4"): - ( - np.arange(1, 257).reshape(16, 16).transpose(), - [4, 1], [2, 2, 2, 2], False, - np.array( - [ - [1, 129, 33, 161, 65, 193, 97, 225, 17, 145, 49, 177, 81, 209, 113, 241], - [9, 137, 41, 169, 73, 201, 105, 233, 25, 153, 57, 185, 89, 217, 121, 249], - [3, 131, 35, 163, 67, 195, 99, 227, 19, 147, 51, 179, 83, 211, 115, 243], - [11, 139, 43, 171, 75, 203, 107, 235, 27, 155, 59, 187, 91, 219, 123, 251], - [5, 133, 37, 165, 69, 197, 101, 229, 21, 149, 53, 181, 85, 213, 117, 245], - [13, 141, 45, 173, 77, 205, 109, 237, 29, 157, 61, 189, 93, 221, 125, 253], - [7, 135, 39, 167, 71, 199, 103, 231, 23, 151, 55, 183, 87, 215, 119, 247], - [15, 143, 47, 175, 79, 207, 111, 239, 31, 159, 63, 191, 95, 223, 127, 255], - [2, 130, 34, 162, 66, 194, 98, 226, 18, 146, 50, 178, 82, 210, 114, 242], - [10, 138, 42, 170, 74, 202, 106, 234, 26, 154, 58, 186, 90, 218, 122, 250], - [4, 132, 36, 164, 68, 196, 100, 228, 20, 148, 52, 180, 84, 212, 116, 244], - [12, 140, 44, 172, 76, 204, 108, 236, 28, 156, 60, 188, 92, 220, 124, 252], - [6, 134, 38, 166, 70, 198, 102, 230, 22, 150, 54, 182, 86, 214, 118, 246], - [14, 142, 46, 174, 78, 206, 110, 238, 30, 158, 62, 190, 94, 222, 126, 254], - [8, 136, 40, 168, 72, 200, 104, 232, 24, 152, 56, 184, 88, 216, 120, 248], - [16, 144, 48, 176, 80, 208, 112, 240, 32, 160, 64, 192, 96, 224, 128, 256]] - ) - ), - # Swapping on systems "2" and "3": - ( - np.arange(1, 257).reshape(16, 16).transpose(), - [2, 3], [2, 2, 2, 2], False, - np.array( - [ - [1, 17, 65, 81, 33, 49, 97, 113, 129, 145, 193, 209, 161, 177, 225, 241], - [2, 18, 66, 82, 34, 50, 98, 114, 130, 146, 194, 210, 162, 178, 226, 242], - [5, 21, 69, 85, 37, 53, 101, 117, 133, 149, 197, 213, 165, 181, 229, 245], - [6, 22, 70, 86, 38, 54, 102, 118, 134, 150, 198, 214, 166, 182, 230, 246], - [3, 19, 67, 83, 35, 51, 99, 115, 131, 147, 195, 211, 163, 179, 227, 243], - [4, 20, 68, 84, 36, 52, 100, 116, 132, 148, 196, 212, 164, 180, 228, 244], - [7, 23, 71, 87, 39, 55, 103, 119, 135, 151, 199, 215, 167, 183, 231, 247], - [8, 24, 72, 88, 40, 56, 104, 120, 136, 152, 200, 216, 168, 184, 232, 248], - [9, 25, 73, 89, 41, 57, 105, 121, 137, 153, 201, 217, 169, 185, 233, 249], - [10, 26, 74, 90, 42, 58, 106, 122, 138, 154, 202, 218, 170, 186, 234, 250], - [13, 29, 77, 93, 45, 61, 109, 125, 141, 157, 205, 221, 173, 189, 237, 253], - [14, 30, 78, 94, 46, 62, 110, 126, 142, 158, 206, 222, 174, 190, 238, 254], - [11, 27, 75, 91, 43, 59, 107, 123, 139, 155, 203, 219, 171, 187, 235, 251], - [12, 28, 76, 92, 44, 60, 108, 124, 140, 156, 204, 220, 172, 188, 236, 252], - [15, 31, 79, 95, 47, 63, 111, 127, 143, 159, 207, 223, 175, 191, 239, 255], - [16, 32, 80, 96, 48, 64, 112, 128, 144, 160, 208, 224, 176, 192, 240, 256]] - ) - ), - # Swapping on systems "3" and "2" (should be same as swapping on "2" and "3"): - ( - np.arange(1, 257).reshape(16, 16).transpose(), - [3, 2], [2, 2, 2, 2], False, - np.array( - [ - [1, 17, 65, 81, 33, 49, 97, 113, 129, 145, 193, 209, 161, 177, 225, 241], - [2, 18, 66, 82, 34, 50, 98, 114, 130, 146, 194, 210, 162, 178, 226, 242], - [5, 21, 69, 85, 37, 53, 101, 117, 133, 149, 197, 213, 165, 181, 229, 245], - [6, 22, 70, 86, 38, 54, 102, 118, 134, 150, 198, 214, 166, 182, 230, 246], - [3, 19, 67, 83, 35, 51, 99, 115, 131, 147, 195, 211, 163, 179, 227, 243], - [4, 20, 68, 84, 36, 52, 100, 116, 132, 148, 196, 212, 164, 180, 228, 244], - [7, 23, 71, 87, 39, 55, 103, 119, 135, 151, 199, 215, 167, 183, 231, 247], - [8, 24, 72, 88, 40, 56, 104, 120, 136, 152, 200, 216, 168, 184, 232, 248], - [9, 25, 73, 89, 41, 57, 105, 121, 137, 153, 201, 217, 169, 185, 233, 249], - [10, 26, 74, 90, 42, 58, 106, 122, 138, 154, 202, 218, 170, 186, 234, 250], - [13, 29, 77, 93, 45, 61, 109, 125, 141, 157, 205, 221, 173, 189, 237, 253], - [14, 30, 78, 94, 46, 62, 110, 126, 142, 158, 206, 222, 174, 190, 238, 254], - [11, 27, 75, 91, 43, 59, 107, 123, 139, 155, 203, 219, 171, 187, 235, 251], - [12, 28, 76, 92, 44, 60, 108, 124, 140, 156, 204, 220, 172, 188, 236, 252], - [15, 31, 79, 95, 47, 63, 111, 127, 143, 159, 207, 223, 175, 191, 239, 255], - [16, 32, 80, 96, 48, 64, 112, 128, 144, 160, 208, 224, 176, 192, 240, 256]] - ) - ), - # Swapping on systems "2" and "4": - ( - np.arange(1, 257).reshape(16, 16).transpose(), - [2, 4], [2, 2, 2, 2], False, - np.array( - [ - [1, 65, 33, 97, 17, 81, 49, 113, 129, 193, 161, 225, 145, 209, 177, 241], - [5, 69, 37, 101, 21, 85, 53, 117, 133, 197, 165, 229, 149, 213, 181, 245], - [3, 67, 35, 99, 19, 83, 51, 115, 131, 195, 163, 227, 147, 211, 179, 243], - [7, 71, 39, 103, 23, 87, 55, 119, 135, 199, 167, 231, 151, 215, 183, 247], - [2, 66, 34, 98, 18, 82, 50, 114, 130, 194, 162, 226, 146, 210, 178, 242], - [6, 70, 38, 102, 22, 86, 54, 118, 134, 198, 166, 230, 150, 214, 182, 246], - [4, 68, 36, 100, 20, 84, 52, 116, 132, 196, 164, 228, 148, 212, 180, 244], - [8, 72, 40, 104, 24, 88, 56, 120, 136, 200, 168, 232, 152, 216, 184, 248], - [9, 73, 41, 105, 25, 89, 57, 121, 137, 201, 169, 233, 153, 217, 185, 249], - [13, 77, 45, 109, 29, 93, 61, 125, 141, 205, 173, 237, 157, 221, 189, 253], - [11, 75, 43, 107, 27, 91, 59, 123, 139, 203, 171, 235, 155, 219, 187, 251], - [15, 79, 47, 111, 31, 95, 63, 127, 143, 207, 175, 239, 159, 223, 191, 255], - [10, 74, 42, 106, 26, 90, 58, 122, 138, 202, 170, 234, 154, 218, 186, 250], - [14, 78, 46, 110, 30, 94, 62, 126, 142, 206, 174, 238, 158, 222, 190, 254], - [12, 76, 44, 108, 28, 92, 60, 124, 140, 204, 172, 236, 156, 220, 188, 252], - [16, 80, 48, 112, 32, 96, 64, 128, 144, 208, 176, 240, 160, 224, 192, 256]] - ) - ), - # Swapping on systems "4" and "2" (should be same as swapping on "2" and "4"): - ( - np.arange(1, 257).reshape(16, 16).transpose(), - [4, 2], [2, 2, 2, 2], False, - np.array( - [ - [1, 65, 33, 97, 17, 81, 49, 113, 129, 193, 161, 225, 145, 209, 177, 241], - [5, 69, 37, 101, 21, 85, 53, 117, 133, 197, 165, 229, 149, 213, 181, 245], - [3, 67, 35, 99, 19, 83, 51, 115, 131, 195, 163, 227, 147, 211, 179, 243], - [7, 71, 39, 103, 23, 87, 55, 119, 135, 199, 167, 231, 151, 215, 183, 247], - [2, 66, 34, 98, 18, 82, 50, 114, 130, 194, 162, 226, 146, 210, 178, 242], - [6, 70, 38, 102, 22, 86, 54, 118, 134, 198, 166, 230, 150, 214, 182, 246], - [4, 68, 36, 100, 20, 84, 52, 116, 132, 196, 164, 228, 148, 212, 180, 244], - [8, 72, 40, 104, 24, 88, 56, 120, 136, 200, 168, 232, 152, 216, 184, 248], - [9, 73, 41, 105, 25, 89, 57, 121, 137, 201, 169, 233, 153, 217, 185, 249], - [13, 77, 45, 109, 29, 93, 61, 125, 141, 205, 173, 237, 157, 221, 189, 253], - [11, 75, 43, 107, 27, 91, 59, 123, 139, 203, 171, 235, 155, 219, 187, 251], - [15, 79, 47, 111, 31, 95, 63, 127, 143, 207, 175, 239, 159, 223, 191, 255], - [10, 74, 42, 106, 26, 90, 58, 122, 138, 202, 170, 234, 154, 218, 186, 250], - [14, 78, 46, 110, 30, 94, 62, 126, 142, 206, 174, 238, 158, 222, 190, 254], - [12, 76, 44, 108, 28, 92, 60, 124, 140, 204, 172, 236, 156, 220, 188, 252], - [16, 80, 48, 112, 32, 96, 64, 128, 144, 208, 176, 240, 160, 224, 192, 256]] - ) - ), - # Swapping on systems "3" and "4": - ( - np.arange(1, 257).reshape(16, 16).transpose(), - [3, 4], [2, 2, 2, 2], False, - np.array( - [ - [1, 33, 17, 49, 65, 97, 81, 113, 129, 161, 145, 177, 193, 225, 209, 241], - [3, 35, 19, 51, 67, 99, 83, 115, 131, 163, 147, 179, 195, 227, 211, 243], - [2, 34, 18, 50, 66, 98, 82, 114, 130, 162, 146, 178, 194, 226, 210, 242], - [4, 36, 20, 52, 68, 100, 84, 116, 132, 164, 148, 180, 196, 228, 212, 244], - [5, 37, 21, 53, 69, 101, 85, 117, 133, 165, 149, 181, 197, 229, 213, 245], - [7, 39, 23, 55, 71, 103, 87, 119, 135, 167, 151, 183, 199, 231, 215, 247], - [6, 38, 22, 54, 70, 102, 86, 118, 134, 166, 150, 182, 198, 230, 214, 246], - [8, 40, 24, 56, 72, 104, 88, 120, 136, 168, 152, 184, 200, 232, 216, 248], - [9, 41, 25, 57, 73, 105, 89, 121, 137, 169, 153, 185, 201, 233, 217, 249], - [11, 43, 27, 59, 75, 107, 91, 123, 139, 171, 155, 187, 203, 235, 219, 251], - [10, 42, 26, 58, 74, 106, 90, 122, 138, 170, 154, 186, 202, 234, 218, 250], - [12, 44, 28, 60, 76, 108, 92, 124, 140, 172, 156, 188, 204, 236, 220, 252], - [13, 45, 29, 61, 77, 109, 93, 125, 141, 173, 157, 189, 205, 237, 221, 253], - [15, 47, 31, 63, 79, 111, 95, 127, 143, 175, 159, 191, 207, 239, 223, 255], - [14, 46, 30, 62, 78, 110, 94, 126, 142, 174, 158, 190, 206, 238, 222, 254], - [16, 48, 32, 64, 80, 112, 96, 128, 144, 176, 160, 192, 208, 240, 224, 256]] - ) - ), - # Swapping on systems "4" and "3" (should be same as swapping on "3" and "4"): - ( - np.arange(1, 257).reshape(16, 16).transpose(), - [4, 3], [2, 2, 2, 2], False, - np.array( - [ - [1, 33, 17, 49, 65, 97, 81, 113, 129, 161, 145, 177, 193, 225, 209, 241], - [3, 35, 19, 51, 67, 99, 83, 115, 131, 163, 147, 179, 195, 227, 211, 243], - [2, 34, 18, 50, 66, 98, 82, 114, 130, 162, 146, 178, 194, 226, 210, 242], - [4, 36, 20, 52, 68, 100, 84, 116, 132, 164, 148, 180, 196, 228, 212, 244], - [5, 37, 21, 53, 69, 101, 85, 117, 133, 165, 149, 181, 197, 229, 213, 245], - [7, 39, 23, 55, 71, 103, 87, 119, 135, 167, 151, 183, 199, 231, 215, 247], - [6, 38, 22, 54, 70, 102, 86, 118, 134, 166, 150, 182, 198, 230, 214, 246], - [8, 40, 24, 56, 72, 104, 88, 120, 136, 168, 152, 184, 200, 232, 216, 248], - [9, 41, 25, 57, 73, 105, 89, 121, 137, 169, 153, 185, 201, 233, 217, 249], - [11, 43, 27, 59, 75, 107, 91, 123, 139, 171, 155, 187, 203, 235, 219, 251], - [10, 42, 26, 58, 74, 106, 90, 122, 138, 170, 154, 186, 202, 234, 218, 250], - [12, 44, 28, 60, 76, 108, 92, 124, 140, 172, 156, 188, 204, 236, 220, 252], - [13, 45, 29, 61, 77, 109, 93, 125, 141, 173, 157, 189, 205, 237, 221, 253], - [15, 47, 31, 63, 79, 111, 95, 127, 143, 175, 159, 191, 207, 239, 223, 255], - [14, 46, 30, 62, 78, 110, 94, 126, 142, 174, 158, 190, 206, 238, 222, 254], - [16, 48, 32, 64, 80, 112, 96, 128, 144, 176, 160, 192, 208, 240, 224, 256]] - ) - ), -]) +@pytest.mark.parametrize( + "input_matrix, sys, dim, row_only, expected_result", + [ + # 4-by-4 matrix. Default argument (systems [1, 2]): + ( + np.arange(1, 17).reshape(4, 4).transpose(), + None, + None, + False, + np.array([[1, 9, 5, 13], [3, 11, 7, 15], [2, 10, 6, 14], [4, 12, 8, 16]]), + ), + # Hard-coded argument (systems [1, 2]): + ( + np.arange(1, 17).reshape(4, 4).transpose(), + [1, 2], + [2, 2], + False, + np.array([[1, 9, 5, 13], [3, 11, 7, 15], [2, 10, 6, 14], [4, 12, 8, 16]]), + ), + # Hard-coded argument (systems [2, 1])--this should be identical to the prior two cases. + ( + np.arange(1, 17).reshape(4, 4).transpose(), + [2, 1], + [2, 2], + False, + np.array([[1, 9, 5, 13], [3, 11, 7, 15], [2, 10, 6, 14], [4, 12, 8, 16]]), + ), + # Swap operation on vector. + (np.array([1, 2, 3, 4]), None, None, False, np.array([1, 3, 2, 4])), + # Test swap operation when int is provided. + ( + np.array([[1, 5, 9, 13], [2, 6, 10, 14], [3, 7, 11, 15], [4, 8, 12, 16]]), + [1, 2], + 2, + False, + np.array([[1, 9, 5, 13], [3, 11, 7, 15], [2, 10, 6, 14], [4, 12, 8, 16]]), + ), + # Test swap on an 8-by-8 matrix. Swapping on systems "1" and "2". + ( + np.arange(1, 65).reshape(8, 8).transpose(), + [1, 2], + [2, 2, 2], + False, + np.array( + [ + [1, 9, 33, 41, 17, 25, 49, 57], + [2, 10, 34, 42, 18, 26, 50, 58], + [5, 13, 37, 45, 21, 29, 53, 61], + [6, 14, 38, 46, 22, 30, 54, 62], + [3, 11, 35, 43, 19, 27, 51, 59], + [4, 12, 36, 44, 20, 28, 52, 60], + [7, 15, 39, 47, 23, 31, 55, 63], + [8, 16, 40, 48, 24, 32, 56, 64], + ] + ), + ), + # Swapping on systems "2" and "1" (should be same as swapping on "1" and "2"): + ( + np.arange(1, 65).reshape(8, 8).transpose(), + [2, 1], + [2, 2, 2], + False, + np.array( + [ + [1, 9, 33, 41, 17, 25, 49, 57], + [2, 10, 34, 42, 18, 26, 50, 58], + [5, 13, 37, 45, 21, 29, 53, 61], + [6, 14, 38, 46, 22, 30, 54, 62], + [3, 11, 35, 43, 19, 27, 51, 59], + [4, 12, 36, 44, 20, 28, 52, 60], + [7, 15, 39, 47, 23, 31, 55, 63], + [8, 16, 40, 48, 24, 32, 56, 64], + ] + ), + ), + # Swapping on systems "1" and "3": + ( + np.arange(1, 65).reshape(8, 8).transpose(), + [1, 3], + [2, 2, 2], + False, + np.array( + [ + [1, 33, 17, 49, 9, 41, 25, 57], + [5, 37, 21, 53, 13, 45, 29, 61], + [3, 35, 19, 51, 11, 43, 27, 59], + [7, 39, 23, 55, 15, 47, 31, 63], + [2, 34, 18, 50, 10, 42, 26, 58], + [6, 38, 22, 54, 14, 46, 30, 62], + [4, 36, 20, 52, 12, 44, 28, 60], + [8, 40, 24, 56, 16, 48, 32, 64], + ] + ), + ), + # Swapping on systems "3" and "1" (should be same as swapping on "1" and "3"): + ( + np.arange(1, 65).reshape(8, 8).transpose(), + [3, 1], + [2, 2, 2], + False, + np.array( + [ + [1, 33, 17, 49, 9, 41, 25, 57], + [5, 37, 21, 53, 13, 45, 29, 61], + [3, 35, 19, 51, 11, 43, 27, 59], + [7, 39, 23, 55, 15, 47, 31, 63], + [2, 34, 18, 50, 10, 42, 26, 58], + [6, 38, 22, 54, 14, 46, 30, 62], + [4, 36, 20, 52, 12, 44, 28, 60], + [8, 40, 24, 56, 16, 48, 32, 64], + ] + ), + ), + # Swapping on systems "2" and "3": + ( + np.arange(1, 65).reshape(8, 8).transpose(), + [2, 3], + [2, 2, 2], + False, + np.array( + [ + [1, 17, 9, 25, 33, 49, 41, 57], + [3, 19, 11, 27, 35, 51, 43, 59], + [2, 18, 10, 26, 34, 50, 42, 58], + [4, 20, 12, 28, 36, 52, 44, 60], + [5, 21, 13, 29, 37, 53, 45, 61], + [7, 23, 15, 31, 39, 55, 47, 63], + [6, 22, 14, 30, 38, 54, 46, 62], + [8, 24, 16, 32, 40, 56, 48, 64], + ] + ), + ), + # Swapping on systems "3" and "2" (should be same as swapping on "2" and "3"): + ( + np.arange(1, 65).reshape(8, 8).transpose(), + [3, 2], + [2, 2, 2], + False, + np.array( + [ + [1, 17, 9, 25, 33, 49, 41, 57], + [3, 19, 11, 27, 35, 51, 43, 59], + [2, 18, 10, 26, 34, 50, 42, 58], + [4, 20, 12, 28, 36, 52, 44, 60], + [5, 21, 13, 29, 37, 53, 45, 61], + [7, 23, 15, 31, 39, 55, 47, 63], + [6, 22, 14, 30, 38, 54, 46, 62], + [8, 24, 16, 32, 40, 56, 48, 64], + ] + ), + ), + # Test swap on an 16-by-16 matrix. + ( + np.arange(1, 257).reshape(16, 16).transpose(), + [1, 2], + [2, 2, 2, 2], + False, + np.array( + [ + [1, 17, 33, 49, 129, 145, 161, 177, 65, 81, 97, 113, 193, 209, 225, 241], + [2, 18, 34, 50, 130, 146, 162, 178, 66, 82, 98, 114, 194, 210, 226, 242], + [3, 19, 35, 51, 131, 147, 163, 179, 67, 83, 99, 115, 195, 211, 227, 243], + [4, 20, 36, 52, 132, 148, 164, 180, 68, 84, 100, 116, 196, 212, 228, 244], + [9, 25, 41, 57, 137, 153, 169, 185, 73, 89, 105, 121, 201, 217, 233, 249], + [10, 26, 42, 58, 138, 154, 170, 186, 74, 90, 106, 122, 202, 218, 234, 250], + [11, 27, 43, 59, 139, 155, 171, 187, 75, 91, 107, 123, 203, 219, 235, 251], + [12, 28, 44, 60, 140, 156, 172, 188, 76, 92, 108, 124, 204, 220, 236, 252], + [5, 21, 37, 53, 133, 149, 165, 181, 69, 85, 101, 117, 197, 213, 229, 245], + [6, 22, 38, 54, 134, 150, 166, 182, 70, 86, 102, 118, 198, 214, 230, 246], + [7, 23, 39, 55, 135, 151, 167, 183, 71, 87, 103, 119, 199, 215, 231, 247], + [8, 24, 40, 56, 136, 152, 168, 184, 72, 88, 104, 120, 200, 216, 232, 248], + [13, 29, 45, 61, 141, 157, 173, 189, 77, 93, 109, 125, 205, 221, 237, 253], + [14, 30, 46, 62, 142, 158, 174, 190, 78, 94, 110, 126, 206, 222, 238, 254], + [15, 31, 47, 63, 143, 159, 175, 191, 79, 95, 111, 127, 207, 223, 239, 255], + [16, 32, 48, 64, 144, 160, 176, 192, 80, 96, 112, 128, 208, 224, 240, 256], + ] + ), + ), + # Swapping on systems "2" and "1" (should be same as swapping on "1" and "2"): + ( + np.arange(1, 257).reshape(16, 16).transpose(), + [2, 1], + [2, 2, 2, 2], + False, + np.array( + [ + [1, 17, 33, 49, 129, 145, 161, 177, 65, 81, 97, 113, 193, 209, 225, 241], + [2, 18, 34, 50, 130, 146, 162, 178, 66, 82, 98, 114, 194, 210, 226, 242], + [3, 19, 35, 51, 131, 147, 163, 179, 67, 83, 99, 115, 195, 211, 227, 243], + [4, 20, 36, 52, 132, 148, 164, 180, 68, 84, 100, 116, 196, 212, 228, 244], + [9, 25, 41, 57, 137, 153, 169, 185, 73, 89, 105, 121, 201, 217, 233, 249], + [10, 26, 42, 58, 138, 154, 170, 186, 74, 90, 106, 122, 202, 218, 234, 250], + [11, 27, 43, 59, 139, 155, 171, 187, 75, 91, 107, 123, 203, 219, 235, 251], + [12, 28, 44, 60, 140, 156, 172, 188, 76, 92, 108, 124, 204, 220, 236, 252], + [5, 21, 37, 53, 133, 149, 165, 181, 69, 85, 101, 117, 197, 213, 229, 245], + [6, 22, 38, 54, 134, 150, 166, 182, 70, 86, 102, 118, 198, 214, 230, 246], + [7, 23, 39, 55, 135, 151, 167, 183, 71, 87, 103, 119, 199, 215, 231, 247], + [8, 24, 40, 56, 136, 152, 168, 184, 72, 88, 104, 120, 200, 216, 232, 248], + [13, 29, 45, 61, 141, 157, 173, 189, 77, 93, 109, 125, 205, 221, 237, 253], + [14, 30, 46, 62, 142, 158, 174, 190, 78, 94, 110, 126, 206, 222, 238, 254], + [15, 31, 47, 63, 143, 159, 175, 191, 79, 95, 111, 127, 207, 223, 239, 255], + [16, 32, 48, 64, 144, 160, 176, 192, 80, 96, 112, 128, 208, 224, 240, 256], + ] + ), + ), + # Swapping on systems "1" and "3": + ( + np.arange(1, 257).reshape(16, 16).transpose(), + [1, 3], + [2, 2, 2, 2], + False, + np.array( + [ + [1, 17, 129, 145, 65, 81, 193, 209, 33, 49, 161, 177, 97, 113, 225, 241], + [2, 18, 130, 146, 66, 82, 194, 210, 34, 50, 162, 178, 98, 114, 226, 242], + [9, 25, 137, 153, 73, 89, 201, 217, 41, 57, 169, 185, 105, 121, 233, 249], + [10, 26, 138, 154, 74, 90, 202, 218, 42, 58, 170, 186, 106, 122, 234, 250], + [5, 21, 133, 149, 69, 85, 197, 213, 37, 53, 165, 181, 101, 117, 229, 245], + [6, 22, 134, 150, 70, 86, 198, 214, 38, 54, 166, 182, 102, 118, 230, 246], + [13, 29, 141, 157, 77, 93, 205, 221, 45, 61, 173, 189, 109, 125, 237, 253], + [14, 30, 142, 158, 78, 94, 206, 222, 46, 62, 174, 190, 110, 126, 238, 254], + [3, 19, 131, 147, 67, 83, 195, 211, 35, 51, 163, 179, 99, 115, 227, 243], + [4, 20, 132, 148, 68, 84, 196, 212, 36, 52, 164, 180, 100, 116, 228, 244], + [11, 27, 139, 155, 75, 91, 203, 219, 43, 59, 171, 187, 107, 123, 235, 251], + [12, 28, 140, 156, 76, 92, 204, 220, 44, 60, 172, 188, 108, 124, 236, 252], + [7, 23, 135, 151, 71, 87, 199, 215, 39, 55, 167, 183, 103, 119, 231, 247], + [8, 24, 136, 152, 72, 88, 200, 216, 40, 56, 168, 184, 104, 120, 232, 248], + [15, 31, 143, 159, 79, 95, 207, 223, 47, 63, 175, 191, 111, 127, 239, 255], + [16, 32, 144, 160, 80, 96, 208, 224, 48, 64, 176, 192, 112, 128, 240, 256], + ] + ), + ), + # Swapping on systems "3" and "1" (should be same as swapping on "1" and "3"): + ( + np.arange(1, 257).reshape(16, 16).transpose(), + [3, 1], + [2, 2, 2, 2], + False, + np.array( + [ + [1, 17, 129, 145, 65, 81, 193, 209, 33, 49, 161, 177, 97, 113, 225, 241], + [2, 18, 130, 146, 66, 82, 194, 210, 34, 50, 162, 178, 98, 114, 226, 242], + [9, 25, 137, 153, 73, 89, 201, 217, 41, 57, 169, 185, 105, 121, 233, 249], + [10, 26, 138, 154, 74, 90, 202, 218, 42, 58, 170, 186, 106, 122, 234, 250], + [5, 21, 133, 149, 69, 85, 197, 213, 37, 53, 165, 181, 101, 117, 229, 245], + [6, 22, 134, 150, 70, 86, 198, 214, 38, 54, 166, 182, 102, 118, 230, 246], + [13, 29, 141, 157, 77, 93, 205, 221, 45, 61, 173, 189, 109, 125, 237, 253], + [14, 30, 142, 158, 78, 94, 206, 222, 46, 62, 174, 190, 110, 126, 238, 254], + [3, 19, 131, 147, 67, 83, 195, 211, 35, 51, 163, 179, 99, 115, 227, 243], + [4, 20, 132, 148, 68, 84, 196, 212, 36, 52, 164, 180, 100, 116, 228, 244], + [11, 27, 139, 155, 75, 91, 203, 219, 43, 59, 171, 187, 107, 123, 235, 251], + [12, 28, 140, 156, 76, 92, 204, 220, 44, 60, 172, 188, 108, 124, 236, 252], + [7, 23, 135, 151, 71, 87, 199, 215, 39, 55, 167, 183, 103, 119, 231, 247], + [8, 24, 136, 152, 72, 88, 200, 216, 40, 56, 168, 184, 104, 120, 232, 248], + [15, 31, 143, 159, 79, 95, 207, 223, 47, 63, 175, 191, 111, 127, 239, 255], + [16, 32, 144, 160, 80, 96, 208, 224, 48, 64, 176, 192, 112, 128, 240, 256], + ] + ), + ), + # Swapping on systems "1" and "4": + ( + np.arange(1, 257).reshape(16, 16).transpose(), + [1, 4], + [2, 2, 2, 2], + False, + np.array( + [ + [1, 129, 33, 161, 65, 193, 97, 225, 17, 145, 49, 177, 81, 209, 113, 241], + [9, 137, 41, 169, 73, 201, 105, 233, 25, 153, 57, 185, 89, 217, 121, 249], + [3, 131, 35, 163, 67, 195, 99, 227, 19, 147, 51, 179, 83, 211, 115, 243], + [11, 139, 43, 171, 75, 203, 107, 235, 27, 155, 59, 187, 91, 219, 123, 251], + [5, 133, 37, 165, 69, 197, 101, 229, 21, 149, 53, 181, 85, 213, 117, 245], + [13, 141, 45, 173, 77, 205, 109, 237, 29, 157, 61, 189, 93, 221, 125, 253], + [7, 135, 39, 167, 71, 199, 103, 231, 23, 151, 55, 183, 87, 215, 119, 247], + [15, 143, 47, 175, 79, 207, 111, 239, 31, 159, 63, 191, 95, 223, 127, 255], + [2, 130, 34, 162, 66, 194, 98, 226, 18, 146, 50, 178, 82, 210, 114, 242], + [10, 138, 42, 170, 74, 202, 106, 234, 26, 154, 58, 186, 90, 218, 122, 250], + [4, 132, 36, 164, 68, 196, 100, 228, 20, 148, 52, 180, 84, 212, 116, 244], + [12, 140, 44, 172, 76, 204, 108, 236, 28, 156, 60, 188, 92, 220, 124, 252], + [6, 134, 38, 166, 70, 198, 102, 230, 22, 150, 54, 182, 86, 214, 118, 246], + [14, 142, 46, 174, 78, 206, 110, 238, 30, 158, 62, 190, 94, 222, 126, 254], + [8, 136, 40, 168, 72, 200, 104, 232, 24, 152, 56, 184, 88, 216, 120, 248], + [16, 144, 48, 176, 80, 208, 112, 240, 32, 160, 64, 192, 96, 224, 128, 256], + ] + ), + ), + # Swapping on systems "4" and "1" (should be same as swapping on "1" and "4"): + ( + np.arange(1, 257).reshape(16, 16).transpose(), + [4, 1], + [2, 2, 2, 2], + False, + np.array( + [ + [1, 129, 33, 161, 65, 193, 97, 225, 17, 145, 49, 177, 81, 209, 113, 241], + [9, 137, 41, 169, 73, 201, 105, 233, 25, 153, 57, 185, 89, 217, 121, 249], + [3, 131, 35, 163, 67, 195, 99, 227, 19, 147, 51, 179, 83, 211, 115, 243], + [11, 139, 43, 171, 75, 203, 107, 235, 27, 155, 59, 187, 91, 219, 123, 251], + [5, 133, 37, 165, 69, 197, 101, 229, 21, 149, 53, 181, 85, 213, 117, 245], + [13, 141, 45, 173, 77, 205, 109, 237, 29, 157, 61, 189, 93, 221, 125, 253], + [7, 135, 39, 167, 71, 199, 103, 231, 23, 151, 55, 183, 87, 215, 119, 247], + [15, 143, 47, 175, 79, 207, 111, 239, 31, 159, 63, 191, 95, 223, 127, 255], + [2, 130, 34, 162, 66, 194, 98, 226, 18, 146, 50, 178, 82, 210, 114, 242], + [10, 138, 42, 170, 74, 202, 106, 234, 26, 154, 58, 186, 90, 218, 122, 250], + [4, 132, 36, 164, 68, 196, 100, 228, 20, 148, 52, 180, 84, 212, 116, 244], + [12, 140, 44, 172, 76, 204, 108, 236, 28, 156, 60, 188, 92, 220, 124, 252], + [6, 134, 38, 166, 70, 198, 102, 230, 22, 150, 54, 182, 86, 214, 118, 246], + [14, 142, 46, 174, 78, 206, 110, 238, 30, 158, 62, 190, 94, 222, 126, 254], + [8, 136, 40, 168, 72, 200, 104, 232, 24, 152, 56, 184, 88, 216, 120, 248], + [16, 144, 48, 176, 80, 208, 112, 240, 32, 160, 64, 192, 96, 224, 128, 256], + ] + ), + ), + # Swapping on systems "2" and "3": + ( + np.arange(1, 257).reshape(16, 16).transpose(), + [2, 3], + [2, 2, 2, 2], + False, + np.array( + [ + [1, 17, 65, 81, 33, 49, 97, 113, 129, 145, 193, 209, 161, 177, 225, 241], + [2, 18, 66, 82, 34, 50, 98, 114, 130, 146, 194, 210, 162, 178, 226, 242], + [5, 21, 69, 85, 37, 53, 101, 117, 133, 149, 197, 213, 165, 181, 229, 245], + [6, 22, 70, 86, 38, 54, 102, 118, 134, 150, 198, 214, 166, 182, 230, 246], + [3, 19, 67, 83, 35, 51, 99, 115, 131, 147, 195, 211, 163, 179, 227, 243], + [4, 20, 68, 84, 36, 52, 100, 116, 132, 148, 196, 212, 164, 180, 228, 244], + [7, 23, 71, 87, 39, 55, 103, 119, 135, 151, 199, 215, 167, 183, 231, 247], + [8, 24, 72, 88, 40, 56, 104, 120, 136, 152, 200, 216, 168, 184, 232, 248], + [9, 25, 73, 89, 41, 57, 105, 121, 137, 153, 201, 217, 169, 185, 233, 249], + [10, 26, 74, 90, 42, 58, 106, 122, 138, 154, 202, 218, 170, 186, 234, 250], + [13, 29, 77, 93, 45, 61, 109, 125, 141, 157, 205, 221, 173, 189, 237, 253], + [14, 30, 78, 94, 46, 62, 110, 126, 142, 158, 206, 222, 174, 190, 238, 254], + [11, 27, 75, 91, 43, 59, 107, 123, 139, 155, 203, 219, 171, 187, 235, 251], + [12, 28, 76, 92, 44, 60, 108, 124, 140, 156, 204, 220, 172, 188, 236, 252], + [15, 31, 79, 95, 47, 63, 111, 127, 143, 159, 207, 223, 175, 191, 239, 255], + [16, 32, 80, 96, 48, 64, 112, 128, 144, 160, 208, 224, 176, 192, 240, 256], + ] + ), + ), + # Swapping on systems "3" and "2" (should be same as swapping on "2" and "3"): + ( + np.arange(1, 257).reshape(16, 16).transpose(), + [3, 2], + [2, 2, 2, 2], + False, + np.array( + [ + [1, 17, 65, 81, 33, 49, 97, 113, 129, 145, 193, 209, 161, 177, 225, 241], + [2, 18, 66, 82, 34, 50, 98, 114, 130, 146, 194, 210, 162, 178, 226, 242], + [5, 21, 69, 85, 37, 53, 101, 117, 133, 149, 197, 213, 165, 181, 229, 245], + [6, 22, 70, 86, 38, 54, 102, 118, 134, 150, 198, 214, 166, 182, 230, 246], + [3, 19, 67, 83, 35, 51, 99, 115, 131, 147, 195, 211, 163, 179, 227, 243], + [4, 20, 68, 84, 36, 52, 100, 116, 132, 148, 196, 212, 164, 180, 228, 244], + [7, 23, 71, 87, 39, 55, 103, 119, 135, 151, 199, 215, 167, 183, 231, 247], + [8, 24, 72, 88, 40, 56, 104, 120, 136, 152, 200, 216, 168, 184, 232, 248], + [9, 25, 73, 89, 41, 57, 105, 121, 137, 153, 201, 217, 169, 185, 233, 249], + [10, 26, 74, 90, 42, 58, 106, 122, 138, 154, 202, 218, 170, 186, 234, 250], + [13, 29, 77, 93, 45, 61, 109, 125, 141, 157, 205, 221, 173, 189, 237, 253], + [14, 30, 78, 94, 46, 62, 110, 126, 142, 158, 206, 222, 174, 190, 238, 254], + [11, 27, 75, 91, 43, 59, 107, 123, 139, 155, 203, 219, 171, 187, 235, 251], + [12, 28, 76, 92, 44, 60, 108, 124, 140, 156, 204, 220, 172, 188, 236, 252], + [15, 31, 79, 95, 47, 63, 111, 127, 143, 159, 207, 223, 175, 191, 239, 255], + [16, 32, 80, 96, 48, 64, 112, 128, 144, 160, 208, 224, 176, 192, 240, 256], + ] + ), + ), + # Swapping on systems "2" and "4": + ( + np.arange(1, 257).reshape(16, 16).transpose(), + [2, 4], + [2, 2, 2, 2], + False, + np.array( + [ + [1, 65, 33, 97, 17, 81, 49, 113, 129, 193, 161, 225, 145, 209, 177, 241], + [5, 69, 37, 101, 21, 85, 53, 117, 133, 197, 165, 229, 149, 213, 181, 245], + [3, 67, 35, 99, 19, 83, 51, 115, 131, 195, 163, 227, 147, 211, 179, 243], + [7, 71, 39, 103, 23, 87, 55, 119, 135, 199, 167, 231, 151, 215, 183, 247], + [2, 66, 34, 98, 18, 82, 50, 114, 130, 194, 162, 226, 146, 210, 178, 242], + [6, 70, 38, 102, 22, 86, 54, 118, 134, 198, 166, 230, 150, 214, 182, 246], + [4, 68, 36, 100, 20, 84, 52, 116, 132, 196, 164, 228, 148, 212, 180, 244], + [8, 72, 40, 104, 24, 88, 56, 120, 136, 200, 168, 232, 152, 216, 184, 248], + [9, 73, 41, 105, 25, 89, 57, 121, 137, 201, 169, 233, 153, 217, 185, 249], + [13, 77, 45, 109, 29, 93, 61, 125, 141, 205, 173, 237, 157, 221, 189, 253], + [11, 75, 43, 107, 27, 91, 59, 123, 139, 203, 171, 235, 155, 219, 187, 251], + [15, 79, 47, 111, 31, 95, 63, 127, 143, 207, 175, 239, 159, 223, 191, 255], + [10, 74, 42, 106, 26, 90, 58, 122, 138, 202, 170, 234, 154, 218, 186, 250], + [14, 78, 46, 110, 30, 94, 62, 126, 142, 206, 174, 238, 158, 222, 190, 254], + [12, 76, 44, 108, 28, 92, 60, 124, 140, 204, 172, 236, 156, 220, 188, 252], + [16, 80, 48, 112, 32, 96, 64, 128, 144, 208, 176, 240, 160, 224, 192, 256], + ] + ), + ), + # Swapping on systems "4" and "2" (should be same as swapping on "2" and "4"): + ( + np.arange(1, 257).reshape(16, 16).transpose(), + [4, 2], + [2, 2, 2, 2], + False, + np.array( + [ + [1, 65, 33, 97, 17, 81, 49, 113, 129, 193, 161, 225, 145, 209, 177, 241], + [5, 69, 37, 101, 21, 85, 53, 117, 133, 197, 165, 229, 149, 213, 181, 245], + [3, 67, 35, 99, 19, 83, 51, 115, 131, 195, 163, 227, 147, 211, 179, 243], + [7, 71, 39, 103, 23, 87, 55, 119, 135, 199, 167, 231, 151, 215, 183, 247], + [2, 66, 34, 98, 18, 82, 50, 114, 130, 194, 162, 226, 146, 210, 178, 242], + [6, 70, 38, 102, 22, 86, 54, 118, 134, 198, 166, 230, 150, 214, 182, 246], + [4, 68, 36, 100, 20, 84, 52, 116, 132, 196, 164, 228, 148, 212, 180, 244], + [8, 72, 40, 104, 24, 88, 56, 120, 136, 200, 168, 232, 152, 216, 184, 248], + [9, 73, 41, 105, 25, 89, 57, 121, 137, 201, 169, 233, 153, 217, 185, 249], + [13, 77, 45, 109, 29, 93, 61, 125, 141, 205, 173, 237, 157, 221, 189, 253], + [11, 75, 43, 107, 27, 91, 59, 123, 139, 203, 171, 235, 155, 219, 187, 251], + [15, 79, 47, 111, 31, 95, 63, 127, 143, 207, 175, 239, 159, 223, 191, 255], + [10, 74, 42, 106, 26, 90, 58, 122, 138, 202, 170, 234, 154, 218, 186, 250], + [14, 78, 46, 110, 30, 94, 62, 126, 142, 206, 174, 238, 158, 222, 190, 254], + [12, 76, 44, 108, 28, 92, 60, 124, 140, 204, 172, 236, 156, 220, 188, 252], + [16, 80, 48, 112, 32, 96, 64, 128, 144, 208, 176, 240, 160, 224, 192, 256], + ] + ), + ), + # Swapping on systems "3" and "4": + ( + np.arange(1, 257).reshape(16, 16).transpose(), + [3, 4], + [2, 2, 2, 2], + False, + np.array( + [ + [1, 33, 17, 49, 65, 97, 81, 113, 129, 161, 145, 177, 193, 225, 209, 241], + [3, 35, 19, 51, 67, 99, 83, 115, 131, 163, 147, 179, 195, 227, 211, 243], + [2, 34, 18, 50, 66, 98, 82, 114, 130, 162, 146, 178, 194, 226, 210, 242], + [4, 36, 20, 52, 68, 100, 84, 116, 132, 164, 148, 180, 196, 228, 212, 244], + [5, 37, 21, 53, 69, 101, 85, 117, 133, 165, 149, 181, 197, 229, 213, 245], + [7, 39, 23, 55, 71, 103, 87, 119, 135, 167, 151, 183, 199, 231, 215, 247], + [6, 38, 22, 54, 70, 102, 86, 118, 134, 166, 150, 182, 198, 230, 214, 246], + [8, 40, 24, 56, 72, 104, 88, 120, 136, 168, 152, 184, 200, 232, 216, 248], + [9, 41, 25, 57, 73, 105, 89, 121, 137, 169, 153, 185, 201, 233, 217, 249], + [11, 43, 27, 59, 75, 107, 91, 123, 139, 171, 155, 187, 203, 235, 219, 251], + [10, 42, 26, 58, 74, 106, 90, 122, 138, 170, 154, 186, 202, 234, 218, 250], + [12, 44, 28, 60, 76, 108, 92, 124, 140, 172, 156, 188, 204, 236, 220, 252], + [13, 45, 29, 61, 77, 109, 93, 125, 141, 173, 157, 189, 205, 237, 221, 253], + [15, 47, 31, 63, 79, 111, 95, 127, 143, 175, 159, 191, 207, 239, 223, 255], + [14, 46, 30, 62, 78, 110, 94, 126, 142, 174, 158, 190, 206, 238, 222, 254], + [16, 48, 32, 64, 80, 112, 96, 128, 144, 176, 160, 192, 208, 240, 224, 256], + ] + ), + ), + # Swapping on systems "4" and "3" (should be same as swapping on "3" and "4"): + ( + np.arange(1, 257).reshape(16, 16).transpose(), + [4, 3], + [2, 2, 2, 2], + False, + np.array( + [ + [1, 33, 17, 49, 65, 97, 81, 113, 129, 161, 145, 177, 193, 225, 209, 241], + [3, 35, 19, 51, 67, 99, 83, 115, 131, 163, 147, 179, 195, 227, 211, 243], + [2, 34, 18, 50, 66, 98, 82, 114, 130, 162, 146, 178, 194, 226, 210, 242], + [4, 36, 20, 52, 68, 100, 84, 116, 132, 164, 148, 180, 196, 228, 212, 244], + [5, 37, 21, 53, 69, 101, 85, 117, 133, 165, 149, 181, 197, 229, 213, 245], + [7, 39, 23, 55, 71, 103, 87, 119, 135, 167, 151, 183, 199, 231, 215, 247], + [6, 38, 22, 54, 70, 102, 86, 118, 134, 166, 150, 182, 198, 230, 214, 246], + [8, 40, 24, 56, 72, 104, 88, 120, 136, 168, 152, 184, 200, 232, 216, 248], + [9, 41, 25, 57, 73, 105, 89, 121, 137, 169, 153, 185, 201, 233, 217, 249], + [11, 43, 27, 59, 75, 107, 91, 123, 139, 171, 155, 187, 203, 235, 219, 251], + [10, 42, 26, 58, 74, 106, 90, 122, 138, 170, 154, 186, 202, 234, 218, 250], + [12, 44, 28, 60, 76, 108, 92, 124, 140, 172, 156, 188, 204, 236, 220, 252], + [13, 45, 29, 61, 77, 109, 93, 125, 141, 173, 157, 189, 205, 237, 221, 253], + [15, 47, 31, 63, 79, 111, 95, 127, 143, 175, 159, 191, 207, 239, 223, 255], + [14, 46, 30, 62, 78, 110, 94, 126, 142, 174, 158, 190, 206, 238, 222, 254], + [16, 48, 32, 64, 80, 112, 96, 128, 144, 176, 160, 192, 208, 240, 224, 256], + ] + ), + ), + ], +) def test_swap(input_matrix, sys, dim, row_only, expected_result): """Test swap operation.""" result = swap(input_matrix, sys, dim, row_only) assert np.allclose(result, expected_result) -@pytest.mark.parametrize("input_matrix, sys, dim, row_only", [ - # Invalid dim parameters. - ( - np.array([[1, 5, 9, 13], [2, 6, 10, 14], [3, 7, 11, 15], [4, 8, 12, 16]]), - [1, 2], 5, False, - ), - # Invalid sys parameters. - ( - np.array([[1, 5, 9, 13], [2, 6, 10, 14], [3, 7, 11, 15], [4, 8, 12, 16]]), - [0], None, False, - ), - # Invalid sys parameters (length). - ( - np.array([[1, 5, 9, 13], [2, 6, 10, 14], [3, 7, 11, 15], [4, 8, 12, 16]]), - [1], 2, False, - ), -]) +@pytest.mark.parametrize( + "input_matrix, sys, dim, row_only", + [ + # Invalid dim parameters. + ( + np.array([[1, 5, 9, 13], [2, 6, 10, 14], [3, 7, 11, 15], [4, 8, 12, 16]]), + [1, 2], + 5, + False, + ), + # Invalid sys parameters. + ( + np.array([[1, 5, 9, 13], [2, 6, 10, 14], [3, 7, 11, 15], [4, 8, 12, 16]]), + [0], + None, + False, + ), + # Invalid sys parameters (length). + ( + np.array([[1, 5, 9, 13], [2, 6, 10, 14], [3, 7, 11, 15], [4, 8, 12, 16]]), + [1], + 2, + False, + ), + ], +) def test_invalid_swap(input_matrix, sys, dim, row_only): """Test function works as expected for an invalid input.""" with pytest.raises(ValueError): swap(input_matrix, sys, dim, row_only) - diff --git a/toqito/perms/tests/test_swap_operator.py b/toqito/perms/tests/test_swap_operator.py index bf8bccd5..98c3794c 100644 --- a/toqito/perms/tests/test_swap_operator.py +++ b/toqito/perms/tests/test_swap_operator.py @@ -1,4 +1,5 @@ """Test swap_operator.""" + import numpy as np from toqito.perms import swap_operator diff --git a/toqito/perms/tests/test_symmetric_projection.py b/toqito/perms/tests/test_symmetric_projection.py index 00ca9c62..989c8ff6 100644 --- a/toqito/perms/tests/test_symmetric_projection.py +++ b/toqito/perms/tests/test_symmetric_projection.py @@ -1,4 +1,5 @@ """Test symmetric_projection.""" + import numpy as np from toqito.perms import symmetric_projection diff --git a/toqito/perms/tests/test_unique_perms.py b/toqito/perms/tests/test_unique_perms.py index 43445f7c..d392e452 100644 --- a/toqito/perms/tests/test_unique_perms.py +++ b/toqito/perms/tests/test_unique_perms.py @@ -1,4 +1,5 @@ """Test unique_perms.""" + import numpy as np from toqito.perms import unique_perms diff --git a/toqito/perms/unique_perms.py b/toqito/perms/unique_perms.py index fabb0aba..5ef05ad9 100644 --- a/toqito/perms/unique_perms.py +++ b/toqito/perms/unique_perms.py @@ -1,4 +1,5 @@ """Calculate unique permutations.""" + from dataclasses import dataclass diff --git a/toqito/rand/__init__.py b/toqito/rand/__init__.py index 682a2a49..95ad56d3 100644 --- a/toqito/rand/__init__.py +++ b/toqito/rand/__init__.py @@ -1,4 +1,5 @@ """Generate random quantum states and measurements.""" + from toqito.rand.random_unitary import random_unitary from toqito.rand.random_density_matrix import random_density_matrix from toqito.rand.random_ginibre import random_ginibre diff --git a/toqito/rand/random_circulant_gram.py b/toqito/rand/random_circulant_gram.py index b1082636..ac0391a8 100644 --- a/toqito/rand/random_circulant_gram.py +++ b/toqito/rand/random_circulant_gram.py @@ -1,4 +1,5 @@ """Generate random circulant Gram matrix.""" + import numpy as np diff --git a/toqito/rand/random_density_matrix.py b/toqito/rand/random_density_matrix.py index d2787092..2378181f 100644 --- a/toqito/rand/random_density_matrix.py +++ b/toqito/rand/random_density_matrix.py @@ -1,4 +1,5 @@ """Generate random density matrix.""" + import numpy as np from toqito.rand import random_unitary diff --git a/toqito/rand/random_ginibre.py b/toqito/rand/random_ginibre.py index e3d7efbe..0c7f49d7 100644 --- a/toqito/rand/random_ginibre.py +++ b/toqito/rand/random_ginibre.py @@ -1,4 +1,5 @@ """Generate Ginibre random matrix.""" + import numpy as np diff --git a/toqito/rand/random_povm.py b/toqito/rand/random_povm.py index 365f32c2..c6d1fb22 100644 --- a/toqito/rand/random_povm.py +++ b/toqito/rand/random_povm.py @@ -1,4 +1,5 @@ """Generate random POVM.""" + import numpy as np @@ -60,16 +61,12 @@ def random_povm(dim: int, num_inputs: int, num_outputs: int) -> np.ndarray: povms = [] gram_vectors = np.random.normal(size=(num_inputs, num_outputs, dim, dim)) for input_block in gram_vectors: - normalizer = sum( - np.array(output_block).T.conj() @ output_block for output_block in input_block - ) + normalizer = sum(np.array(output_block).T.conj() @ output_block for output_block in input_block) u_mat, d_mat, _ = np.linalg.svd(normalizer) output_povms = [] for output_block in input_block: - partial = ( - np.array(output_block, dtype=complex).dot(u_mat).dot(np.diag(d_mat ** (-1 / 2.0))) - ) + partial = np.array(output_block, dtype=complex).dot(u_mat).dot(np.diag(d_mat ** (-1 / 2.0))) internal = partial.dot(np.diag(np.ones(dim)) ** (1 / 2.0)) output_povms.append(internal.T.conj() @ internal) povms.append(output_povms) diff --git a/toqito/rand/random_state_vector.py b/toqito/rand/random_state_vector.py index 7ce047fd..2b1d52a9 100644 --- a/toqito/rand/random_state_vector.py +++ b/toqito/rand/random_state_vector.py @@ -1,4 +1,5 @@ """Generate random state vector.""" + import numpy as np from toqito.perms import swap diff --git a/toqito/rand/random_unitary.py b/toqito/rand/random_unitary.py index 5a81e78b..6631888c 100644 --- a/toqito/rand/random_unitary.py +++ b/toqito/rand/random_unitary.py @@ -1,4 +1,5 @@ """Generate random unitary.""" + import numpy as np diff --git a/toqito/rand/tests/test_random_circulant_gram.py b/toqito/rand/tests/test_random_circulant_gram.py index 98c05da2..0b47b7fe 100644 --- a/toqito/rand/tests/test_random_circulant_gram.py +++ b/toqito/rand/tests/test_random_circulant_gram.py @@ -1,4 +1,5 @@ """Test random_circulant_gram.""" + import numpy as np import pytest from numpy.testing import assert_array_almost_equal, assert_equal @@ -19,8 +20,6 @@ 10, ], ) - - def test_random_circulant_gram(dim): """Test for random_circulant_gram function.""" # Generate a random circulant Gram matrix. diff --git a/toqito/rand/tests/test_random_density_matrix.py b/toqito/rand/tests/test_random_density_matrix.py index 12b8a19c..b92c51d2 100644 --- a/toqito/rand/tests/test_random_density_matrix.py +++ b/toqito/rand/tests/test_random_density_matrix.py @@ -1,4 +1,5 @@ """Test random_density_matrix.""" + import numpy as np import pytest @@ -6,31 +7,29 @@ from toqito.rand import random_density_matrix -@pytest.mark.parametrize("dim, is_real, k_param, distance_metric", [ - # Generate random non-real density matrix. - (2, False, None, "haar"), - # Generate random real density matrix. - (2, True, None, "haar"), - # Random non-real density matrix according to Bures metric. - (2, False, None, "bures"), - # Generate random non-real density matrix all params. - (2, True, 2, "haar"), -]) +@pytest.mark.parametrize( + "dim, is_real, k_param, distance_metric", + [ + # Generate random non-real density matrix. + (2, False, None, "haar"), + # Generate random real density matrix. + (2, True, None, "haar"), + # Random non-real density matrix according to Bures metric. + (2, False, None, "bures"), + # Generate random non-real density matrix all params. + (2, True, 2, "haar"), + ], +) def test_random_density(dim, is_real, k_param, distance_metric): """Test function works as expected for a valid input.""" if k_param == dim: - mat = random_density_matrix( - dim=dim, is_real=is_real, k_param=k_param, distance_metric=distance_metric - ) + mat = random_density_matrix(dim=dim, is_real=is_real, k_param=k_param, distance_metric=distance_metric) np.testing.assert_equal(is_density(mat), True) -@pytest.mark.parametrize("dim, is_real, distance_metric", [ - (2, False, "haar"), - (2, True, "haar"), - (3, False, "bures"), - (3, True, "bures") -]) +@pytest.mark.parametrize( + "dim, is_real, distance_metric", [(2, False, "haar"), (2, True, "haar"), (3, False, "bures"), (3, True, "bures")] +) def test_random_density_matrix(dim, is_real, distance_metric): """Test function output is real or complex.""" dm = random_density_matrix(dim, is_real, distance_metric=distance_metric) diff --git a/toqito/rand/tests/test_random_ginibre.py b/toqito/rand/tests/test_random_ginibre.py index b1947d67..394b64dc 100644 --- a/toqito/rand/tests/test_random_ginibre.py +++ b/toqito/rand/tests/test_random_ginibre.py @@ -1,4 +1,5 @@ """Test random_ginibre.""" + import numpy as np import pytest @@ -13,14 +14,17 @@ def test_random_ginibre_dims(dim_n, dim_m): np.testing.assert_equal(gin_mat.shape, (dim_n, dim_m)) -@pytest.mark.parametrize("dim_n, dim_m", [ - # Negative dim_n. - (-1, 3), - # Negative dim_m. - (3, -2), - # Negative dim_n and dim_m. - (-4, -2), -]) +@pytest.mark.parametrize( + "dim_n, dim_m", + [ + # Negative dim_n. + (-1, 3), + # Negative dim_m. + (3, -2), + # Negative dim_n and dim_m. + (-4, -2), + ], +) def test_random_ginibre_negative_dims(dim_n, dim_m): """Negative dimensions are not allowed.""" with pytest.raises(ValueError, match="negative dimensions are not allowed"): diff --git a/toqito/rand/tests/test_random_povm.py b/toqito/rand/tests/test_random_povm.py index cb26ae56..34250f86 100644 --- a/toqito/rand/tests/test_random_povm.py +++ b/toqito/rand/tests/test_random_povm.py @@ -1,4 +1,5 @@ """Test random_povm.""" + import numpy as np import pytest diff --git a/toqito/rand/tests/test_random_state_vector.py b/toqito/rand/tests/test_random_state_vector.py index f53fb841..f000dc6a 100644 --- a/toqito/rand/tests/test_random_state_vector.py +++ b/toqito/rand/tests/test_random_state_vector.py @@ -1,22 +1,26 @@ """Test random_state_vector.""" + import pytest from toqito.rand import random_state_vector from toqito.state_props import is_pure -@pytest.mark.parametrize("dim, is_real, k_param", [ - # Check that complex state vector from random state vector is pure. - (2, False, 0), - # Check that complex state vector with k_param > 0. - (2, False, 1), - # Check that complex state vector with k_param > 0 and dim list. - ([2, 2], False, 1), - # Check that real state vector with k_param > 0. - (2, True, 1), - # Check that real state vector from random state vector is pure. - (2, True, 0), -]) +@pytest.mark.parametrize( + "dim, is_real, k_param", + [ + # Check that complex state vector from random state vector is pure. + (2, False, 0), + # Check that complex state vector with k_param > 0. + (2, False, 1), + # Check that complex state vector with k_param > 0 and dim list. + ([2, 2], False, 1), + # Check that real state vector with k_param > 0. + (2, True, 1), + # Check that real state vector from random state vector is pure. + (2, True, 0), + ], +) def test_random_state_vector(dim, is_real, k_param): """Test function works as expected for a valid input.""" # We expect the density matrix of any random state vector to be pure. diff --git a/toqito/rand/tests/test_random_states.py b/toqito/rand/tests/test_random_states.py index afa1790a..f7a37ac1 100644 --- a/toqito/rand/tests/test_random_states.py +++ b/toqito/rand/tests/test_random_states.py @@ -1,4 +1,5 @@ """Test random_states.""" + import numpy as np import pytest diff --git a/toqito/rand/tests/test_random_unitary.py b/toqito/rand/tests/test_random_unitary.py index 91641c26..664e6505 100644 --- a/toqito/rand/tests/test_random_unitary.py +++ b/toqito/rand/tests/test_random_unitary.py @@ -1,4 +1,5 @@ """Test random_unitary.""" + import pytest from toqito.matrix_props import is_unitary @@ -9,9 +10,7 @@ @pytest.mark.parametrize("is_real", [True, False]) def test_random_unitary_int_dim(dim, is_real): """Test function works as expected for a valid int input.""" - mat = random_unitary( - dim=dim, is_real=is_real - ) + mat = random_unitary(dim=dim, is_real=is_real) assert is_unitary(mat) @@ -21,9 +20,7 @@ def test_random_unitary_int_dim(dim, is_real): def test_random_unitary_list_dims(dim_n, dim_m, is_real): """Test function works as expected for a valid input of list.""" if dim_n == dim_m: - mat = random_unitary( - dim=[dim_n, dim_m], is_real=is_real - ) + mat = random_unitary(dim=[dim_n, dim_m], is_real=is_real) assert is_unitary(mat) diff --git a/toqito/state_metrics/__init__.py b/toqito/state_metrics/__init__.py index 7951bbe6..17830e6d 100644 --- a/toqito/state_metrics/__init__.py +++ b/toqito/state_metrics/__init__.py @@ -1,4 +1,5 @@ """Distance metrics for quantum states.""" + from toqito.state_metrics.hilbert_schmidt import hilbert_schmidt from toqito.state_metrics.hilbert_schmidt_inner_product import hilbert_schmidt_inner_product from toqito.state_metrics.helstrom_holevo import helstrom_holevo diff --git a/toqito/state_metrics/bures_angle.py b/toqito/state_metrics/bures_angle.py index 1f318671..30c00353 100644 --- a/toqito/state_metrics/bures_angle.py +++ b/toqito/state_metrics/bures_angle.py @@ -1,4 +1,5 @@ """Bures angle metric.""" + import numpy as np from toqito.state_metrics import fidelity diff --git a/toqito/state_metrics/bures_distance.py b/toqito/state_metrics/bures_distance.py index 7148a08b..dac516b5 100644 --- a/toqito/state_metrics/bures_distance.py +++ b/toqito/state_metrics/bures_distance.py @@ -1,4 +1,5 @@ """Bures distance metric.""" + import numpy as np from toqito.state_metrics import fidelity diff --git a/toqito/state_metrics/fidelity.py b/toqito/state_metrics/fidelity.py index 80e3ce75..39901cbe 100644 --- a/toqito/state_metrics/fidelity.py +++ b/toqito/state_metrics/fidelity.py @@ -1,4 +1,5 @@ """Fidelity metric.""" + import cvxpy import numpy as np import scipy diff --git a/toqito/state_metrics/fidelity_of_separability.py b/toqito/state_metrics/fidelity_of_separability.py index 5ba6ca88..9dba7624 100644 --- a/toqito/state_metrics/fidelity_of_separability.py +++ b/toqito/state_metrics/fidelity_of_separability.py @@ -2,6 +2,7 @@ The constraints for this function are positive partial transpose (PPT) & k-extendible states. """ + import numpy as np import picos @@ -145,7 +146,7 @@ def fidelity_of_separability( # Extend the number of dimensions based on the level `k`. new dims for AB with k-extendibility in subsystem B dim_direct_sum_ab_k = [dim_a] + [dim_b] * (k) # new dims for a linear op acting on the space of sigma_ab_k - dim_op_sigma_ab_k = dim_a * dim_b ** k + dim_op_sigma_ab_k = dim_a * dim_b**k # A list of the symmetrically extended subsystems based on the level `k`. sub_sys_ext = list(range(2, 2 + k - 1)) @@ -153,7 +154,6 @@ def fidelity_of_separability( # unitary permutation operator in B1,B2,...,Bk permutation_op = symmetric_projection(dim_b, k) - # defining the problem objective: Re[Tr[X_AB]] problem = picos.Problem(verbosity=verbosity_option) linear_op_ab = picos.ComplexVariable("x_ab", input_state_rho.shape) @@ -185,4 +185,4 @@ def fidelity_of_separability( problem.add_constraint(picos.partial_transpose(sigma_ab_k, sys, dim_direct_sum_ab_k) >> 0) solution = problem.solve(solver=solver_option) - return solution.value ** 2 + return solution.value**2 diff --git a/toqito/state_metrics/helstrom_holevo.py b/toqito/state_metrics/helstrom_holevo.py index 8924dfe4..99fd22ab 100644 --- a/toqito/state_metrics/helstrom_holevo.py +++ b/toqito/state_metrics/helstrom_holevo.py @@ -1,4 +1,5 @@ """Helstrom-Holevo metric.""" + import numpy as np from toqito.matrix_props import is_density, trace_norm diff --git a/toqito/state_metrics/hilbert_schmidt.py b/toqito/state_metrics/hilbert_schmidt.py index bb53775e..87f2bfdc 100644 --- a/toqito/state_metrics/hilbert_schmidt.py +++ b/toqito/state_metrics/hilbert_schmidt.py @@ -1,4 +1,5 @@ """Hilbert-Schmidt metric.""" + import numpy as np from toqito.matrix_props import is_density diff --git a/toqito/state_metrics/hilbert_schmidt_inner_product.py b/toqito/state_metrics/hilbert_schmidt_inner_product.py index 760c3f0e..a68bb7d7 100644 --- a/toqito/state_metrics/hilbert_schmidt_inner_product.py +++ b/toqito/state_metrics/hilbert_schmidt_inner_product.py @@ -1,4 +1,5 @@ """Hilbert-Schmidt Inner Product.""" + import numpy as np diff --git a/toqito/state_metrics/matsumoto_fidelity.py b/toqito/state_metrics/matsumoto_fidelity.py index dfba1135..d7767849 100644 --- a/toqito/state_metrics/matsumoto_fidelity.py +++ b/toqito/state_metrics/matsumoto_fidelity.py @@ -1,4 +1,5 @@ """Matsumoto fidelity metric.""" + import cvxpy import numpy as np import scipy @@ -87,9 +88,7 @@ def matsumoto_fidelity(rho: np.ndarray, sigma: np.ndarray) -> float: # If `rho` or `sigma` is a cvxpy variable then compute Matsumoto fidelity via # semidefinite programming, so that this function can be used in the # objective function or constraints of other cvxpy optimization problems. - if isinstance(rho, cvxpy.atoms.affine.vstack.Vstack) or isinstance( - sigma, cvxpy.atoms.affine.vstack.Vstack - ): + if isinstance(rho, cvxpy.atoms.affine.vstack.Vstack) or isinstance(sigma, cvxpy.atoms.affine.vstack.Vstack): w_var = cvxpy.Variable(rho.shape, hermitian=True) objective = cvxpy.Maximize(cvxpy.real(cvxpy.trace(w_var))) constraints = [cvxpy.bmat([[rho, w_var], [w_var, sigma]]) >> 0] diff --git a/toqito/state_metrics/sub_fidelity.py b/toqito/state_metrics/sub_fidelity.py index be3dc933..b6ebfe79 100644 --- a/toqito/state_metrics/sub_fidelity.py +++ b/toqito/state_metrics/sub_fidelity.py @@ -1,4 +1,5 @@ """Sub-fidelity metric.""" + import numpy as np from toqito.matrix_props import is_density @@ -69,6 +70,5 @@ def sub_fidelity(rho: np.ndarray, sigma: np.ndarray) -> float: raise ValueError("Sub-fidelity is only defined for density operators.") return np.real( - np.trace(rho * sigma) - + np.sqrt(2 * (np.trace(rho * sigma) ** 2 - np.trace(rho * sigma * rho * sigma))) + np.trace(rho * sigma) + np.sqrt(2 * (np.trace(rho * sigma) ** 2 - np.trace(rho * sigma * rho * sigma))) ) diff --git a/toqito/state_metrics/tests/test_bures_angle.py b/toqito/state_metrics/tests/test_bures_angle.py index 60e75f4b..ff998dd6 100644 --- a/toqito/state_metrics/tests/test_bures_angle.py +++ b/toqito/state_metrics/tests/test_bures_angle.py @@ -1,4 +1,5 @@ """Tests for bures_angle.""" + import numpy as np from toqito.state_metrics import bures_angle diff --git a/toqito/state_metrics/tests/test_bures_distance.py b/toqito/state_metrics/tests/test_bures_distance.py index be14a702..3808e0a9 100644 --- a/toqito/state_metrics/tests/test_bures_distance.py +++ b/toqito/state_metrics/tests/test_bures_distance.py @@ -1,4 +1,5 @@ """Tests for bures_distance.""" + import numpy as np from toqito.state_metrics import bures_distance diff --git a/toqito/state_metrics/tests/test_fidelity.py b/toqito/state_metrics/tests/test_fidelity.py index 3f81b280..2fd4aa82 100644 --- a/toqito/state_metrics/tests/test_fidelity.py +++ b/toqito/state_metrics/tests/test_fidelity.py @@ -1,4 +1,5 @@ """Tests for fidelity.""" + import cvxpy import numpy as np diff --git a/toqito/state_metrics/tests/test_helstrom_holevo.py b/toqito/state_metrics/tests/test_helstrom_holevo.py index d73ed4b0..d6054eac 100644 --- a/toqito/state_metrics/tests/test_helstrom_holevo.py +++ b/toqito/state_metrics/tests/test_helstrom_holevo.py @@ -1,4 +1,5 @@ """Tests for helstrom_holevo.""" + import numpy as np from toqito.state_metrics import helstrom_holevo diff --git a/toqito/state_metrics/tests/test_hilbert_schmidt.py b/toqito/state_metrics/tests/test_hilbert_schmidt.py index 74326090..8aca8959 100644 --- a/toqito/state_metrics/tests/test_hilbert_schmidt.py +++ b/toqito/state_metrics/tests/test_hilbert_schmidt.py @@ -1,4 +1,5 @@ """Tests for hilbert_schmidt.""" + import numpy as np from toqito.state_metrics import hilbert_schmidt diff --git a/toqito/state_metrics/tests/test_hilbert_schmidt_inner_product.py b/toqito/state_metrics/tests/test_hilbert_schmidt_inner_product.py index 53bd3c52..39a0dcd0 100644 --- a/toqito/state_metrics/tests/test_hilbert_schmidt_inner_product.py +++ b/toqito/state_metrics/tests/test_hilbert_schmidt_inner_product.py @@ -38,7 +38,5 @@ def test_hilbert_schmidt_inner_product_linearity(): lhs = beta_1 * hilbert_schmidt_inner_product( random_hermitian_operator, b_mat_1 ) + beta_2 * hilbert_schmidt_inner_product(random_hermitian_operator, b_mat_2) - rhs = hilbert_schmidt_inner_product( - random_hermitian_operator, beta_1 * b_mat_1 + beta_2 * b_mat_2 - ) + rhs = hilbert_schmidt_inner_product(random_hermitian_operator, beta_1 * b_mat_1 + beta_2 * b_mat_2) np.testing.assert_equal(np.isclose(lhs, rhs), True) diff --git a/toqito/state_metrics/tests/test_matsumoto_fidelity.py b/toqito/state_metrics/tests/test_matsumoto_fidelity.py index 30b00894..f89a4b68 100644 --- a/toqito/state_metrics/tests/test_matsumoto_fidelity.py +++ b/toqito/state_metrics/tests/test_matsumoto_fidelity.py @@ -1,4 +1,5 @@ """Tests for matsumoto_fidelity.""" + import cvxpy import numpy as np import pytest @@ -17,18 +18,23 @@ rho5 = cvxpy.bmat([[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0], [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]]) -@pytest.mark.parametrize("input1, input2, expected", [ - # when inputs are the same - (rho, rho, 1), - # inputs are non-identical - (rho1, sigma1, 0.996), - (rho2, sigma2, 0.774), - # when inputs are the same for cvxpy variable - (rho5, rho5, 1),]) + +@pytest.mark.parametrize( + "input1, input2, expected", + [ + # when inputs are the same + (rho, rho, 1), + # inputs are non-identical + (rho1, sigma1, 0.996), + (rho2, sigma2, 0.774), + # when inputs are the same for cvxpy variable + (rho5, rho5, 1), + ], +) def test_matsumoto_fidelity(input1, input2, expected): """Test functions works as expected for valid inputs.""" calculated_result = matsumoto_fidelity(input1, input2) - assert abs(calculated_result-expected) <= 1e-03 + assert abs(calculated_result - expected) <= 1e-03 rho3 = np.array([[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]]) @@ -37,11 +43,16 @@ def test_matsumoto_fidelity(input1, input2, expected): rho4 = np.array([[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0], [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]]) sigma4 = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) -@pytest.mark.parametrize("input1, input2, expected_msg", [ - # non square dims - (rho3, sigma3, "Matsumoto fidelity is only defined for density operators."), - # invalid dims - (rho4, sigma4, "InvalidDim: `rho` and `sigma` must be matrices of the same size."),]) + +@pytest.mark.parametrize( + "input1, input2, expected_msg", + [ + # non square dims + (rho3, sigma3, "Matsumoto fidelity is only defined for density operators."), + # invalid dims + (rho4, sigma4, "InvalidDim: `rho` and `sigma` must be matrices of the same size."), + ], +) def test_matsumoto_fidelity_invalid_input(input1, input2, expected_msg): """Test function raises an error for invalid inputs.""" with pytest.raises(ValueError, match=expected_msg): diff --git a/toqito/state_metrics/tests/test_sub_fidelity.py b/toqito/state_metrics/tests/test_sub_fidelity.py index 4d60caae..35dc6943 100644 --- a/toqito/state_metrics/tests/test_sub_fidelity.py +++ b/toqito/state_metrics/tests/test_sub_fidelity.py @@ -1,4 +1,5 @@ """Tests for sub_fidelity.""" + import numpy as np from toqito.state_metrics import fidelity, sub_fidelity diff --git a/toqito/state_metrics/tests/test_trace_distance.py b/toqito/state_metrics/tests/test_trace_distance.py index 62448f2c..d2ddf116 100644 --- a/toqito/state_metrics/tests/test_trace_distance.py +++ b/toqito/state_metrics/tests/test_trace_distance.py @@ -1,4 +1,5 @@ """Tests for trace_distance.""" + import numpy as np from toqito.state_metrics import trace_distance diff --git a/toqito/state_metrics/trace_distance.py b/toqito/state_metrics/trace_distance.py index a24f7a36..317357a4 100644 --- a/toqito/state_metrics/trace_distance.py +++ b/toqito/state_metrics/trace_distance.py @@ -1,4 +1,5 @@ """Trace distance metric.""" + import numpy as np from toqito.matrix_props import is_density, trace_norm diff --git a/toqito/state_ops/__init__.py b/toqito/state_ops/__init__.py index 51ebd612..0d3401df 100644 --- a/toqito/state_ops/__init__.py +++ b/toqito/state_ops/__init__.py @@ -1,3 +1,4 @@ """Operations on quantum states.""" + from toqito.state_ops.pure_to_mixed import pure_to_mixed from toqito.state_ops.schmidt_decomposition import schmidt_decomposition diff --git a/toqito/state_ops/pure_to_mixed.py b/toqito/state_ops/pure_to_mixed.py index fec4eb06..4bfe7c3f 100644 --- a/toqito/state_ops/pure_to_mixed.py +++ b/toqito/state_ops/pure_to_mixed.py @@ -1,4 +1,5 @@ """Pure to mixed operation.""" + import numpy as np diff --git a/toqito/state_ops/schmidt_decomposition.py b/toqito/state_ops/schmidt_decomposition.py index ed2fcb14..eaef6ebb 100644 --- a/toqito/state_ops/schmidt_decomposition.py +++ b/toqito/state_ops/schmidt_decomposition.py @@ -1,6 +1,5 @@ """Schmidt decomposition operation.""" - import numpy as np diff --git a/toqito/state_ops/tests/test_pure_to_mixed.py b/toqito/state_ops/tests/test_pure_to_mixed.py index e5849443..187b7df2 100644 --- a/toqito/state_ops/tests/test_pure_to_mixed.py +++ b/toqito/state_ops/tests/test_pure_to_mixed.py @@ -1,4 +1,5 @@ """Test pure_to_mixed.""" + import numpy as np from toqito.state_ops import pure_to_mixed @@ -7,9 +8,7 @@ def test_pure_to_mixed_state_vector(): """Convert pure state to mixed state vector.""" - expected_res = np.array( - [[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0], [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]] - ) + expected_res = np.array([[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0], [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]]) phi = bell(0) res = pure_to_mixed(phi) @@ -20,9 +19,7 @@ def test_pure_to_mixed_state_vector(): def test_pure_to_mixed_density_matrix(): """Convert pure state to mixed state density matrix.""" - expected_res = np.array( - [[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0], [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]] - ) + expected_res = np.array([[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0], [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]]) phi = bell(0) * bell(0).conj().T res = pure_to_mixed(phi) diff --git a/toqito/state_ops/tests/test_schmidt_decomposition.py b/toqito/state_ops/tests/test_schmidt_decomposition.py index 9af4a1cd..007b2d84 100644 --- a/toqito/state_ops/tests/test_schmidt_decomposition.py +++ b/toqito/state_ops/tests/test_schmidt_decomposition.py @@ -1,4 +1,5 @@ """Test schmidt_decomposition.""" + import numpy as np import pytest @@ -15,33 +16,58 @@ pure_vec = -1 / np.sqrt(2) * np.array([[1], [0], [1], [0]]) -@pytest.mark.parametrize("test_input, expected_u_mat, expected_vt_mat, expected_singular_vals, reconstruct", [ - # Schmidt decomposition of the 3-D maximally entangled state - (max_entangled(3), np.identity(3), np.identity(3), 1 / np.sqrt(3) * np.array([[1], [1], [1]]), False), - # Schmidt decomposition of two-qubit state. The Schmidt decomposition of | phi > = 1/2(|00> + |01> + |10> + |11>) is - # the state |+>|+> where |+> = 1/sqrt(2) * (|0> + |1>). - (phi1, 1 / np.sqrt(2) * np.array([[-1], [-1]]), 1 / np.sqrt(2) * np.array([[-1], [-1]]), np.array([[1]]), False), - # Schmidt decomposition of two-qubit state. The Schmidt decomposition of | phi > = 1/2(|00> + |01> + |10> - |11>) is - # the state 1/sqrt(2) * (|0>|+> + |1>|->). - (phi2, np.array([[-1, -1], [-1, 1]]), 1 / np.sqrt(2) * np.array([[-1, -1], [-1, 1]]), - 1 / np.sqrt(2) * np.array([[1], [1]]), True), - # Schmidt decomposition of two-qubit state. The Schmidt decomposition of 1/2* (|00> + |11>) has Schmidt coefficients - # equal to 1/2[1, 1] - (phi3, np.array([[1, 0], [0, 1]]), np.array([[1, 0], [0, 1]]), 1 / 2 * np.array([[1], [1]]), True), - # Schmidt decomposition of two-qubit state. The Schmidt decomposition of 1/2 * (|00> - |01> + |10> + |11>) has - # Schmidt coefficients equal to [1, 1] - (phi4, np.array([[-1, 0], [0, 1]]), 1 / np.sqrt(2) * np.array([[-1, 1], [1, 1]]), - 1 / np.sqrt(2) * np.array([[1], [1]]), False), - # Schmidt decomposition of a pure state with a dimension list - (pure_vec, 1 / np.sqrt(2) * np.array([[-1], [-1]]), np.array([[1], [0]]), np.array([[1]]), False), - # Test on standard basis vectors - (np.kron(e_1, e_1), np.array([[0], [1]]), np.array([[0], [1]]), np.array([[1]]), False), - # separable density matrix - (np.identity(4), np.array([[[-0.70710678],[ 0.]],[[ 0.], [-0.70710678]]]), np.array( - [[[-0.70710678],[ 0.]],[[ 0.], [-0.70710678]]]),np.array([[2.]]), False) - ]) +@pytest.mark.parametrize( + "test_input, expected_u_mat, expected_vt_mat, expected_singular_vals, reconstruct", + [ + # Schmidt decomposition of the 3-D maximally entangled state + (max_entangled(3), np.identity(3), np.identity(3), 1 / np.sqrt(3) * np.array([[1], [1], [1]]), False), + # Schmidt decomposition of two-qubit state. The Schmidt decomposition of | phi > = 1/2(|00> + |01> + |10> + + # |11>) is the state |+>|+> where |+> = 1/sqrt(2) * (|0> + |1>). + ( + phi1, + 1 / np.sqrt(2) * np.array([[-1], [-1]]), + 1 / np.sqrt(2) * np.array([[-1], [-1]]), + np.array([[1]]), + False, + ), + # Schmidt decomposition of two-qubit state. The Schmidt decomposition of | phi > = 1/2(|00> + |01> + |10> - + # |11>) is the state 1/sqrt(2) * (|0>|+> + |1>|->). + ( + phi2, + np.array([[-1, -1], [-1, 1]]), + 1 / np.sqrt(2) * np.array([[-1, -1], [-1, 1]]), + 1 / np.sqrt(2) * np.array([[1], [1]]), + True, + ), + # Schmidt decomposition of two-qubit state. The Schmidt decomposition of 1/2* (|00> + |11>) has Schmidt + # coefficients equal to 1/2[1, 1] + (phi3, np.array([[1, 0], [0, 1]]), np.array([[1, 0], [0, 1]]), 1 / 2 * np.array([[1], [1]]), True), + # Schmidt decomposition of two-qubit state. The Schmidt decomposition of 1/2 * (|00> - |01> + |10> + |11>) has + # Schmidt coefficients equal to [1, 1] + ( + phi4, + np.array([[-1, 0], [0, 1]]), + 1 / np.sqrt(2) * np.array([[-1, 1], [1, 1]]), + 1 / np.sqrt(2) * np.array([[1], [1]]), + False, + ), + # Schmidt decomposition of a pure state with a dimension list + (pure_vec, 1 / np.sqrt(2) * np.array([[-1], [-1]]), np.array([[1], [0]]), np.array([[1]]), False), + # Test on standard basis vectors + (np.kron(e_1, e_1), np.array([[0], [1]]), np.array([[0], [1]]), np.array([[1]]), False), + # separable density matrix + ( + np.identity(4), + np.array([[[-0.70710678], [0.0]], [[0.0], [-0.70710678]]]), + np.array([[[-0.70710678], [0.0]], [[0.0], [-0.70710678]]]), + np.array([[2.0]]), + False, + ), + ], +) def test_schmidt_decomposition_no_input_dim( - test_input, expected_u_mat, expected_vt_mat, expected_singular_vals, reconstruct): + test_input, expected_u_mat, expected_vt_mat, expected_singular_vals, reconstruct +): """Test function works as expected for valid inputs.""" calculated_singular_vals, calculated_u_mat, calculated_vt_mat = schmidt_decomposition(test_input) assert (calculated_singular_vals - expected_singular_vals).all() <= 0.1 @@ -50,79 +76,83 @@ def test_schmidt_decomposition_no_input_dim( if reconstruct is True: s_decomp = ( - calculated_singular_vals[0] * np.atleast_2d(np.kron(calculated_u_mat[:, 0], calculated_vt_mat[:, 0])).T - + calculated_singular_vals[1] * np.atleast_2d(np.kron(calculated_u_mat[:, 1], calculated_vt_mat[:, 1])).T) + calculated_singular_vals[0] * np.atleast_2d(np.kron(calculated_u_mat[:, 0], calculated_vt_mat[:, 0])).T + + calculated_singular_vals[1] * np.atleast_2d(np.kron(calculated_u_mat[:, 1], calculated_vt_mat[:, 1])).T + ) assert np.linalg.norm(test_input - s_decomp) <= 0.001 @pytest.mark.parametrize( - "test_input, input_dim, input_param, expected_u_mat, expected_vt_mat, expected_singular_vals", [ - # Schmidt decomposition of the 3-D maximally entangled state - (max_entangled(3), [3, 3], None, np.identity(3), np.identity(3), 1 / np.sqrt(3) * np.array([[1], [1], [1]])), - # Schmidt decomposition of a pure state with a dimension list when input_dim is list and k_param is 1 - (pure_vec, [2, 2], 1, 1 / np.sqrt(2) * np.array([[-1], [-1]]), np.array([[1], [0]]), np.array([[1]])), - # Schmidt decomposition of a pure state with a dimension list when input_dim is list and k_param is 2 - (pure_vec, [2, 2], 2, 1 / np.sqrt(2) * np.array([[-1, -1], [-1, 1]]), np.identity(2),np.array([[1], [0]])), - # Input dim is None - Schmidt decomposition of the 3-D maximally entangled state - (max_entangled(3), None, None, np.identity(3), np.identity(3), 1 / np.sqrt(3) * np.array([[1], [1], [1]])), - # separable density matrix - (np.identity(4), 2, None, np.array([[[-0.70710678],[ 0.]],[[ 0.], [-0.70710678]]]), np.array( - [[[-0.70710678],[ 0.]],[[ 0.], [-0.70710678]]]), np.array([[2.]])), - ]) + "test_input, input_dim, input_param, expected_u_mat, expected_vt_mat, expected_singular_vals", + [ + # Schmidt decomposition of the 3-D maximally entangled state + (max_entangled(3), [3, 3], None, np.identity(3), np.identity(3), 1 / np.sqrt(3) * np.array([[1], [1], [1]])), + # Schmidt decomposition of a pure state with a dimension list when input_dim is list and k_param is 1 + (pure_vec, [2, 2], 1, 1 / np.sqrt(2) * np.array([[-1], [-1]]), np.array([[1], [0]]), np.array([[1]])), + # Schmidt decomposition of a pure state with a dimension list when input_dim is list and k_param is 2 + (pure_vec, [2, 2], 2, 1 / np.sqrt(2) * np.array([[-1, -1], [-1, 1]]), np.identity(2), np.array([[1], [0]])), + # Input dim is None - Schmidt decomposition of the 3-D maximally entangled state + (max_entangled(3), None, None, np.identity(3), np.identity(3), 1 / np.sqrt(3) * np.array([[1], [1], [1]])), + # separable density matrix + ( + np.identity(4), + 2, + None, + np.array([[[-0.70710678], [0.0]], [[0.0], [-0.70710678]]]), + np.array([[[-0.70710678], [0.0]], [[0.0], [-0.70710678]]]), + np.array([[2.0]]), + ), + ], +) def test_schmidt_decomposition_input_dim( - test_input, input_dim, input_param, expected_u_mat,expected_vt_mat, expected_singular_vals): + test_input, input_dim, input_param, expected_u_mat, expected_vt_mat, expected_singular_vals +): """Test function works as expected for valid inputs.""" if input_param is None: - calculated_singular_vals, calculated_u_mat, calculated_vt_mat = schmidt_decomposition( - test_input, dim = input_dim) + calculated_singular_vals, calculated_u_mat, calculated_vt_mat = schmidt_decomposition(test_input, dim=input_dim) else: calculated_singular_vals, calculated_u_mat, calculated_vt_mat = schmidt_decomposition( - test_input, dim = input_dim, k_param = input_param ) + test_input, dim=input_dim, k_param=input_param + ) assert (calculated_singular_vals - expected_singular_vals).all() <= 0.1 assert (calculated_u_mat - expected_u_mat).all() <= 0.1 assert (calculated_vt_mat - expected_vt_mat).all() <= 0.1 - def test_schmidt_decomp_random_state(): """Test for random state.""" rho = random_state_vector(8) singular_vals, u_mat, vt_mat = schmidt_decomposition(rho, [2, 4]) reconstructed = np.sum( - [ - singular_vals[i, 0] * tensor(u_mat[:, [i]], vt_mat[:, [i]]) - for i in range(len(singular_vals)) - ], + [singular_vals[i, 0] * tensor(u_mat[:, [i]], vt_mat[:, [i]]) for i in range(len(singular_vals))], axis=0, ) assert np.isclose(rho, reconstructed).all() - def test_schmidt_decomp_random_operator(): """Test for random operator.""" rho = random_density_matrix(8) singular_vals, u_mat, vt_mat = schmidt_decomposition(rho, [2, 4]) reconstructed = np.sum( - [ - singular_vals[i, 0] * tensor(u_mat[:, :, i], vt_mat[:, :, i]) - for i in range(len(singular_vals)) - ], + [singular_vals[i, 0] * tensor(u_mat[:, :, i], vt_mat[:, :, i]) for i in range(len(singular_vals))], axis=0, ) assert np.isclose(rho, reconstructed).all() def test_allclose_phi5(): - """Checks output of phi5 is close to expected.""" - phi5 = ( - (1 + np.sqrt(6)) / (2 * np.sqrt(6)) * np.kron(e_0, e_0) + (1 - np.sqrt(6)) / (2 * np.sqrt(6)) * np.kron(e_0, e_1) - + (np.sqrt(2) - np.sqrt(3)) / (2 * np.sqrt(6)) * np.kron(e_1, e_0) + (np.sqrt(2) + np.sqrt(3)) / ( - 2 * np.sqrt(6)) * np.kron(e_1, e_1)) - calculated_singular_vals, calculated_u_mat, calculated_vt_mat = schmidt_decomposition(phi5) - expected_singular_vals = np.array([[0.8660254], [0.5]]) - expected_u_mat = np.array([[-0.81649658, 0.57735027], [ 0.57735027, 0.81649658]]) - expected_v_mat = 1 / np.sqrt(2) * np.array([[-1, 1], [1, 1]]) - np.testing.assert_allclose(calculated_singular_vals, expected_singular_vals, 1e-5) - np.testing.assert_allclose(calculated_vt_mat, expected_v_mat, 1e-5) - np.testing.assert_allclose(calculated_u_mat, expected_u_mat, 1e-5) + """Checks output of phi5 is close to expected.""" + phi5 = ( + (1 + np.sqrt(6)) / (2 * np.sqrt(6)) * np.kron(e_0, e_0) + + (1 - np.sqrt(6)) / (2 * np.sqrt(6)) * np.kron(e_0, e_1) + + (np.sqrt(2) - np.sqrt(3)) / (2 * np.sqrt(6)) * np.kron(e_1, e_0) + + (np.sqrt(2) + np.sqrt(3)) / (2 * np.sqrt(6)) * np.kron(e_1, e_1) + ) + calculated_singular_vals, calculated_u_mat, calculated_vt_mat = schmidt_decomposition(phi5) + expected_singular_vals = np.array([[0.8660254], [0.5]]) + expected_u_mat = np.array([[-0.81649658, 0.57735027], [0.57735027, 0.81649658]]) + expected_v_mat = 1 / np.sqrt(2) * np.array([[-1, 1], [1, 1]]) + np.testing.assert_allclose(calculated_singular_vals, expected_singular_vals, 1e-5) + np.testing.assert_allclose(calculated_vt_mat, expected_v_mat, 1e-5) + np.testing.assert_allclose(calculated_u_mat, expected_u_mat, 1e-5) diff --git a/toqito/state_opt/__init__.py b/toqito/state_opt/__init__.py index 65217ce7..cd3158f6 100644 --- a/toqito/state_opt/__init__.py +++ b/toqito/state_opt/__init__.py @@ -1,4 +1,5 @@ """Optimizations over quantum states.""" + from toqito.state_opt.optimal_clone import optimal_clone from toqito.state_opt.ppt_distinguishability import ppt_distinguishability from toqito.state_opt.state_distinguishability import state_distinguishability diff --git a/toqito/state_opt/optimal_clone.py b/toqito/state_opt/optimal_clone.py index 86ed907d..4ef5f48c 100644 --- a/toqito/state_opt/optimal_clone.py +++ b/toqito/state_opt/optimal_clone.py @@ -1,6 +1,5 @@ """Calculates success probability of approximately cloning a quantum state.""" - import cvxpy import numpy as np @@ -115,11 +114,7 @@ def optimal_clone( # Q = ∑_{k=1}^N p_k |ψ_k ⊗ ψ_k ⊗ ψ_k> <ψ_k ⊗ ψ_k ⊗ ψ_k| q_a = np.zeros((dim, dim)) for k, state in enumerate(states): - q_a += ( - probs[k] - * tensor(state, state, state.conj()) - * tensor(state, state, state.conj()).conj().T - ) + q_a += probs[k] * tensor(state, state, state.conj()) * tensor(state, state, state.conj()).conj().T # The system is over: # Y_1 ⊗ Z_1 ⊗ X_1, ... , Y_n ⊗ Z_n ⊗ X_n. @@ -166,15 +161,13 @@ def primal_problem(q_a: np.ndarray, pperm: np.ndarray, num_reps: int) -> float: dim = 2 * np.ones((1, num_spaces * num_reps)).astype(int).flatten() dim = dim.tolist() - x_var = cvxpy.Variable((8 ** num_reps, 8 ** num_reps), hermitian=True) + x_var = cvxpy.Variable((8**num_reps, 8**num_reps), hermitian=True) if num_reps == 1: objective = cvxpy.Maximize(cvxpy.trace(cvxpy.real(q_a.conj().T @ x_var))) else: - objective = cvxpy.Maximize( - cvxpy.trace(cvxpy.real(pperm @ q_a.conj().T @ pperm.conj().T @ x_var)) - ) + objective = cvxpy.Maximize(cvxpy.trace(cvxpy.real(pperm @ q_a.conj().T @ pperm.conj().T @ x_var))) constraints = [ - partial_trace(x_var, sys, dim) == np.identity(2 ** num_reps), + partial_trace(x_var, sys, dim) == np.identity(2**num_reps), x_var >> 0, ] problem = cvxpy.Problem(objective, constraints) @@ -187,10 +180,10 @@ def dual_problem(q_a: np.ndarray, pperm: np.ndarray, num_reps: int) -> float: :return: The optimal value of performing a counterfeit attack. """ - y_var = cvxpy.Variable((2 ** num_reps, 2 ** num_reps), hermitian=True) + y_var = cvxpy.Variable((2**num_reps, 2**num_reps), hermitian=True) objective = cvxpy.Minimize(cvxpy.trace(cvxpy.real(y_var))) - kron_var = cvxpy.kron(cvxpy.kron(np.eye(2 ** num_reps), np.eye(2 ** num_reps)), y_var) + kron_var = cvxpy.kron(cvxpy.kron(np.eye(2**num_reps), np.eye(2**num_reps)), y_var) if num_reps == 1: constraints = [cvxpy.real(kron_var) >> q_a] diff --git a/toqito/state_opt/ppt_distinguishability.py b/toqito/state_opt/ppt_distinguishability.py index a65f96d7..2824d704 100644 --- a/toqito/state_opt/ppt_distinguishability.py +++ b/toqito/state_opt/ppt_distinguishability.py @@ -1,4 +1,5 @@ """PPT state distinguishability.""" + import numpy as np import picos @@ -124,20 +125,10 @@ def ppt_distinguishability( if primal_dual == "primal": return _min_error_primal( - vectors=vectors, - subsystems=subsystems, - dimensions=dimensions, - probs=probs, - solver=solver, - strategy=strategy + vectors=vectors, subsystems=subsystems, dimensions=dimensions, probs=probs, solver=solver, strategy=strategy ) return _min_error_dual( - vectors=vectors, - subsystems=subsystems, - dimensions=dimensions, - probs=probs, - solver=solver, - strategy=strategy + vectors=vectors, subsystems=subsystems, dimensions=dimensions, probs=probs, solver=solver, strategy=strategy ) @@ -147,7 +138,7 @@ def _min_error_primal( dimensions: list[int], probs: list[float], solver: str = "cvxopt", - strategy: str = "min_error" + strategy: str = "min_error", ): """Primal problem for the SDP with PPT constraints.""" n = len(vectors) @@ -161,13 +152,17 @@ def _min_error_primal( problem.add_constraint(picos.sum(measurements) == picos.I(d)) # Add PPT constraint. - problem.add_list_of_constraints([ - picos.partial_transpose( - meas, - subsystems=subsystems, - dimensions=dimensions, - ) >> 0 for meas in measurements - ]) + problem.add_list_of_constraints( + [ + picos.partial_transpose( + meas, + subsystems=subsystems, + dimensions=dimensions, + ) + >> 0 + for meas in measurements + ] + ) dms = [vector_to_density_matrix(vector) for vector in vectors] if strategy == "unambig": @@ -188,7 +183,7 @@ def _min_error_dual( dimensions: list[int], probs: list[float], solver: str = "cvxopt", - strategy: str = "min_error" + strategy: str = "min_error", ): """Semidefinite program with PPT constraints (dual problem).""" d = vectors[0].shape[0] @@ -200,13 +195,17 @@ def _min_error_dual( q_vars = [picos.HermitianVariable(f"Q[{i}]", (d, d)) for i in range(len(vectors))] y_var = picos.HermitianVariable("Y", (d, d)) - problem.add_list_of_constraints([ - y_var - probs[i] * vector_to_density_matrix(vectors[i]) >> picos.partial_transpose( - q_var, - subsystems=subsystems, - dimensions=dimensions, - ) for i, q_var in enumerate(q_vars) - ]) + problem.add_list_of_constraints( + [ + y_var - probs[i] * vector_to_density_matrix(vectors[i]) + >> picos.partial_transpose( + q_var, + subsystems=subsystems, + dimensions=dimensions, + ) + for i, q_var in enumerate(q_vars) + ] + ) problem.add_list_of_constraints([q_var >> 0 for q_var in q_vars]) problem.set_objective("min", picos.trace(y_var)) diff --git a/toqito/state_opt/state_distinguishability.py b/toqito/state_opt/state_distinguishability.py index 0f204e84..fe78d8ed 100644 --- a/toqito/state_opt/state_distinguishability.py +++ b/toqito/state_opt/state_distinguishability.py @@ -1,4 +1,5 @@ """State distinguishability.""" + import numpy as np import picos @@ -108,10 +109,10 @@ def state_distinguishability( def _min_error_primal( - vectors: list[np.ndarray], - dim: int, - probs: list[float] = None, - solver: str = "cvxopt", + vectors: list[np.ndarray], + dim: int, + probs: list[float] = None, + solver: str = "cvxopt", ) -> tuple[float, list[picos.HermitianVariable]]: """Find the primal problem for minimum-error quantum state distinguishability SDP.""" n = len(vectors) @@ -125,19 +126,16 @@ def _min_error_primal( dms = [vector_to_density_matrix(vector) for vector in vectors] - problem.set_objective( - "max", - np.real(picos.sum([(probs[i] * dms[i] | measurements[i]) for i in range(n)])) - ) + problem.set_objective("max", np.real(picos.sum([(probs[i] * dms[i] | measurements[i]) for i in range(n)]))) solution = problem.solve(solver=solver) return solution.value, measurements def _min_error_dual( - vectors: list[np.ndarray], - dim: int, - probs: list[float] = None, - solver: str = "cvxopt", + vectors: list[np.ndarray], + dim: int, + probs: list[float] = None, + solver: str = "cvxopt", ) -> tuple[float, list[picos.HermitianVariable]]: """Find the dual problem for minimum-error quantum state distinguishability SDP.""" n = len(vectors) @@ -146,10 +144,7 @@ def _min_error_dual( # Set up variables and constraints for SDP: y_var = picos.HermitianVariable("Y", (dim, dim)) problem.add_list_of_constraints( - [ - y_var >> probs[i] * vector_to_density_matrix(vector) - for i, vector in enumerate(vectors) - ] + [y_var >> probs[i] * vector_to_density_matrix(vector) for i, vector in enumerate(vectors)] ) # Objective function: diff --git a/toqito/state_opt/state_exclusion.py b/toqito/state_opt/state_exclusion.py index 6174793e..8a27d278 100644 --- a/toqito/state_opt/state_exclusion.py +++ b/toqito/state_opt/state_exclusion.py @@ -1,4 +1,5 @@ """State exclusion.""" + import numpy as np import picos @@ -120,10 +121,10 @@ def state_exclusion( def _min_error_primal( - vectors: list[np.ndarray], - dim: int, - probs: list[float] = None, - solver: str = "cvxopt", + vectors: list[np.ndarray], + dim: int, + probs: list[float] = None, + solver: str = "cvxopt", ) -> tuple[float, list[picos.HermitianVariable]]: """Find the primal problem for minimum-error quantum state exclusion SDP.""" n = len(vectors) @@ -135,19 +136,13 @@ def _min_error_primal( problem.add_constraint(picos.sum(measurements) == picos.I(dim)) dms = [vector_to_density_matrix(vector) for vector in vectors] - problem.set_objective( - "min", - np.real(picos.sum([(probs[i] * dms[i] | measurements[i]) for i in range(n)])) - ) + problem.set_objective("min", np.real(picos.sum([(probs[i] * dms[i] | measurements[i]) for i in range(n)]))) solution = problem.solve(solver=solver) return solution.value, measurements def _min_error_dual( - vectors: list[np.ndarray], - dim: int, - probs: list[float] = None, - solver: str = "cvxopt" + vectors: list[np.ndarray], dim: int, probs: list[float] = None, solver: str = "cvxopt" ) -> tuple[float, list[picos.HermitianVariable]]: """Find the dual problem for minimum-error quantum state exclusion SDP.""" n = len(vectors) @@ -156,10 +151,7 @@ def _min_error_dual( # Set up variables and constraints for SDP: y_var = picos.HermitianVariable("Y", (dim, dim)) problem.add_list_of_constraints( - [ - y_var << probs[i] * vector_to_density_matrix(vector) - for i, vector in enumerate(vectors) - ] + [y_var << probs[i] * vector_to_density_matrix(vector) for i, vector in enumerate(vectors)] ) # Objective function: diff --git a/toqito/state_opt/state_helper.py b/toqito/state_opt/state_helper.py index 929a18e6..f8f9c037 100644 --- a/toqito/state_opt/state_helper.py +++ b/toqito/state_opt/state_helper.py @@ -1,4 +1,5 @@ """Helper functions for checking validity of states and probability vectors.""" + import numpy as np diff --git a/toqito/state_opt/symmetric_extension_hierarchy.py b/toqito/state_opt/symmetric_extension_hierarchy.py index 1d1231b4..13245b9b 100644 --- a/toqito/state_opt/symmetric_extension_hierarchy.py +++ b/toqito/state_opt/symmetric_extension_hierarchy.py @@ -1,6 +1,5 @@ """PPT symmetric extension hierarchy.""" - import cvxpy import numpy as np @@ -190,10 +189,7 @@ def symmetric_extension_hierarchy( meas.append(cvxpy.Variable((dim_xy, dim_xy), PSD=True)) x_var.append(cvxpy.Variable((dim_xyy, dim_xyy), PSD=True)) constraints.append(partial_trace(x_var[k], sys_list, dim_list) == meas[k]) - constraints.append( - np.kron(np.identity(dim_x), sym) @ x_var[k] @ np.kron(np.identity(dim_x), sym) - == x_var[k] - ) + constraints.append(np.kron(np.identity(dim_x), sym) @ x_var[k] @ np.kron(np.identity(dim_x), sym) == x_var[k]) constraints.append(partial_transpose(x_var[k], [0], dim_list) >> 0) for sys in range(level - 1): constraints.append(partial_transpose(x_var[k], [sys + 2], dim_list) >> 0) diff --git a/toqito/state_opt/tests/test_optimal_clone.py b/toqito/state_opt/tests/test_optimal_clone.py index a9fe789e..3b2893f3 100644 --- a/toqito/state_opt/tests/test_optimal_clone.py +++ b/toqito/state_opt/tests/test_optimal_clone.py @@ -1,4 +1,5 @@ """Tests for optimal_clone.""" + import numpy as np import pytest @@ -13,29 +14,35 @@ probs = [1 / 4, 1 / 4, 1 / 4, 1 / 4] -@pytest.mark.parametrize("input_states, input_probs, num_reps, input_strategy, expected", [ - # Probability of counterfeit attack on Wiesner's quantum money - (states, probs, 1, False, 3/4), - # Probability of counterfeit attack on Wiesner's quantum money with 2 parallel repitions - (states, probs, 2, False, 3/4), - # Counterfeit attack on Wiesner's quantum money (primal problem) - (states, probs, 1, True, 3/4), - # Counterfeit attack on Wiesner's quantum money (primal problem) with 2 parallel repitions - (states, probs, 2, True, 3/4),]) +@pytest.mark.parametrize( + "input_states, input_probs, num_reps, input_strategy, expected", + [ + # Probability of counterfeit attack on Wiesner's quantum money + (states, probs, 1, False, 3 / 4), + # Probability of counterfeit attack on Wiesner's quantum money with 2 parallel repitions + (states, probs, 2, False, 3 / 4), + # Counterfeit attack on Wiesner's quantum money (primal problem) + (states, probs, 1, True, 3 / 4), + # Counterfeit attack on Wiesner's quantum money (primal problem) with 2 parallel repitions + (states, probs, 2, True, 3 / 4), + ], +) def test_optimal_clone(input_states, input_probs, num_reps, input_strategy, expected): """Test functions work as expected.""" - expected_result = expected ** num_reps + expected_result = expected**num_reps calculated_result = optimal_clone(input_states, input_probs, num_reps, input_strategy) assert pytest.approx(expected_result, 0.1) == calculated_result -@pytest.mark.parametrize("input_states, input_probs, expected", [ - # Probability of counterfeit attack on Wiesner's quantum money - (states, probs, 3/4),]) +@pytest.mark.parametrize( + "input_states, input_probs, expected", + [ + # Probability of counterfeit attack on Wiesner's quantum money + (states, probs, 3 / 4), + ], +) def test_optimal_clone_default_reps_strategy(input_states, input_probs, expected): """Test functions work as expected.""" expected_result = expected calculated_result = optimal_clone(input_states, input_probs) assert pytest.approx(expected_result, 0.1) == calculated_result - - diff --git a/toqito/state_opt/tests/test_ppt_distinguishability.py b/toqito/state_opt/tests/test_ppt_distinguishability.py index cc0bf062..39f8fff8 100644 --- a/toqito/state_opt/tests/test_ppt_distinguishability.py +++ b/toqito/state_opt/tests/test_ppt_distinguishability.py @@ -1,4 +1,5 @@ """Test ppt_distinguishability.""" + import numpy as np import pytest @@ -36,7 +37,7 @@ def test_ppt_distinguishability_yyd_density_matrices(): subsystems=[0, 2], dimensions=[2, 2, 2, 2], strategy="min_error", - primal_dual="primal" + primal_dual="primal", ) dual_res, _ = ppt_distinguishability( vectors=states, @@ -44,7 +45,7 @@ def test_ppt_distinguishability_yyd_density_matrices(): subsystems=[0, 2], dimensions=[2, 2, 2, 2], strategy="min_error", - primal_dual="dual" + primal_dual="dual", ) assert np.isclose(primal_res, 7 / 8, atol=0.001) assert np.isclose(dual_res, 7 / 8, atol=0.001) @@ -55,7 +56,7 @@ def test_ppt_distinguishability_yyd_density_matrices(): subsystems=[0, 2], dimensions=[2, 2, 2, 2], strategy="unambig", - primal_dual="primal" + primal_dual="primal", ) assert np.isclose(primal_res, 3 / 4, atol=0.001) @@ -86,7 +87,7 @@ def test_ppt_distinguishability_yyd_vectors(): subsystems=[0, 2], dimensions=[2, 2, 2, 2], strategy="min_error", - primal_dual="primal" + primal_dual="primal", ) dual_res, _ = ppt_distinguishability( vectors=states, @@ -94,7 +95,7 @@ def test_ppt_distinguishability_yyd_vectors(): subsystems=[0, 2], dimensions=[2, 2, 2, 2], strategy="min_error", - primal_dual="dual" + primal_dual="dual", ) assert np.isclose(primal_res, 7 / 8, atol=0.001) @@ -106,7 +107,7 @@ def test_ppt_distinguishability_yyd_vectors(): subsystems=[0, 2], dimensions=[2, 2, 2, 2], strategy="unambig", - primal_dual="primal" + primal_dual="primal", ) assert np.isclose(primal_res, 3 / 4, atol=0.001) @@ -195,7 +196,7 @@ def test_ppt_distinguishability_four_bell_states(): ] probs = [1 / 4, 1 / 4, 1 / 4, 1 / 4] - exp_res = 1 / 2 * (1 + np.sqrt(1 - eps ** 2)) + exp_res = 1 / 2 * (1 + np.sqrt(1 - eps**2)) # Min-error tests: primal_res, _ = ppt_distinguishability( @@ -204,7 +205,7 @@ def test_ppt_distinguishability_four_bell_states(): subsystems=[0, 2], dimensions=[2, 2, 2, 2], strategy="min_error", - primal_dual="primal" + primal_dual="primal", ) dual_res, _ = ppt_distinguishability( vectors=states, @@ -212,21 +213,24 @@ def test_ppt_distinguishability_four_bell_states(): subsystems=[0, 2], dimensions=[2, 2, 2, 2], strategy="min_error", - primal_dual="dual" + primal_dual="dual", ) assert np.isclose(primal_res, exp_res, atol=0.001) assert np.isclose(dual_res, exp_res, atol=0.001) -@pytest.mark.parametrize("vectors, probs, solver, subsystems, dimensions, strategy, primal_dual", [ - # Bell states (default uniform probs with dual). - ([bell(0), bell(1), bell(2), np.array([[1], [0]])], None, "cvxopt", [0], [2, 2], "min_error", "dual"), -]) +@pytest.mark.parametrize( + "vectors, probs, solver, subsystems, dimensions, strategy, primal_dual", + [ + # Bell states (default uniform probs with dual). + ([bell(0), bell(1), bell(2), np.array([[1], [0]])], None, "cvxopt", [0], [2, 2], "min_error", "dual"), + ], +) def test_ppt_state_distinguishability_invalid_vectors( vectors, probs, solver, subsystems, dimensions, strategy, primal_dual ): """Test function works as expected for an invalid input.""" - with pytest.raises(ValueError, match = "Vectors for state distinguishability must all have the same dimension."): + with pytest.raises(ValueError, match="Vectors for state distinguishability must all have the same dimension."): ppt_distinguishability( vectors=vectors, probs=probs, @@ -234,19 +238,22 @@ def test_ppt_state_distinguishability_invalid_vectors( dimensions=dimensions, strategy=strategy, solver=solver, - primal_dual=primal_dual + primal_dual=primal_dual, ) -@pytest.mark.parametrize("vectors, probs, solver, subsystems, dimensions, strategy, primal_dual", [ - # Bell states (default uniform probs with dual). - ([bell(0), bell(1), bell(2), bell(3)], None, "cvxopt", [0], [2, 2], "unambig", "dual"), -]) +@pytest.mark.parametrize( + "vectors, probs, solver, subsystems, dimensions, strategy, primal_dual", + [ + # Bell states (default uniform probs with dual). + ([bell(0), bell(1), bell(2), bell(3)], None, "cvxopt", [0], [2, 2], "unambig", "dual"), + ], +) def test_ppr_state_distinguishability_invalid_strategy( vectors, probs, solver, subsystems, dimensions, strategy, primal_dual ): """Test function works as expected for an invalid input.""" - with pytest.raises(ValueError, match = "Minimum-error PPT distinguishability only supported at this time."): + with pytest.raises(ValueError, match="Minimum-error PPT distinguishability only supported at this time."): ppt_distinguishability( vectors=vectors, probs=probs, @@ -254,5 +261,5 @@ def test_ppr_state_distinguishability_invalid_strategy( dimensions=dimensions, strategy=strategy, solver=solver, - primal_dual=primal_dual + primal_dual=primal_dual, ) diff --git a/toqito/state_opt/tests/test_state_distinguishability.py b/toqito/state_opt/tests/test_state_distinguishability.py index 63d0aa06..2444e32f 100644 --- a/toqito/state_opt/tests/test_state_distinguishability.py +++ b/toqito/state_opt/tests/test_state_distinguishability.py @@ -1,4 +1,5 @@ """Test state_distinguishability.""" + import pytest from toqito.matrices import standard_basis @@ -9,35 +10,35 @@ e_0, e_1 = standard_basis(2) -@pytest.mark.parametrize("vectors, probs, solver, primal_dual, expected_result", [ - # Bell states (default uniform probs with primal). - ([bell(0), bell(1), bell(2), bell(3)], None, "cvxopt", "primal", 1), - # Bell states (default uniform probs with dual). - ([bell(0), bell(1), bell(2), bell(3)], None, "cvxopt", "dual", 1), - # Bell states uniform probs with primal. - ([bell(0), bell(1), bell(2), bell(3)], [1/4, 1/4, 1/4, 1/4], "cvxopt", "primal", 1), - # Bell states uniform probs with dual. - ([bell(0), bell(1), bell(2), bell(3)], [1/4, 1/4, 1/4, 1/4], "cvxopt", "dual", 1), - # Density matrix Bell states (default uniform probs with dual). - ( - [vector_to_density_matrix(bell(0)), vector_to_density_matrix(bell(1))], - None, - "cvxopt", - "dual", - 1 - ), -]) +@pytest.mark.parametrize( + "vectors, probs, solver, primal_dual, expected_result", + [ + # Bell states (default uniform probs with primal). + ([bell(0), bell(1), bell(2), bell(3)], None, "cvxopt", "primal", 1), + # Bell states (default uniform probs with dual). + ([bell(0), bell(1), bell(2), bell(3)], None, "cvxopt", "dual", 1), + # Bell states uniform probs with primal. + ([bell(0), bell(1), bell(2), bell(3)], [1 / 4, 1 / 4, 1 / 4, 1 / 4], "cvxopt", "primal", 1), + # Bell states uniform probs with dual. + ([bell(0), bell(1), bell(2), bell(3)], [1 / 4, 1 / 4, 1 / 4, 1 / 4], "cvxopt", "dual", 1), + # Density matrix Bell states (default uniform probs with dual). + ([vector_to_density_matrix(bell(0)), vector_to_density_matrix(bell(1))], None, "cvxopt", "dual", 1), + ], +) def test_state_distinguishability(vectors, probs, solver, primal_dual, expected_result): """Test function works as expected for a valid input.""" val, _ = state_distinguishability(vectors=vectors, probs=probs, solver=solver, primal_dual=primal_dual) - assert abs(val - expected_result) <=1e-8 + assert abs(val - expected_result) <= 1e-8 -@pytest.mark.parametrize("vectors, probs, solver, primal_dual", [ - # Bell states (default uniform probs with dual). - ([bell(0), bell(1), bell(2), e_0], None, "cvxopt", "dual"), -]) +@pytest.mark.parametrize( + "vectors, probs, solver, primal_dual", + [ + # Bell states (default uniform probs with dual). + ([bell(0), bell(1), bell(2), e_0], None, "cvxopt", "dual"), + ], +) def test_state_distinguishability_invalid_vectors(vectors, probs, solver, primal_dual): """Test function works as expected for an invalid input.""" - with pytest.raises(ValueError, match = "Vectors for state distinguishability must all have the same dimension."): + with pytest.raises(ValueError, match="Vectors for state distinguishability must all have the same dimension."): state_distinguishability(vectors=vectors, probs=probs, solver=solver, primal_dual=primal_dual) diff --git a/toqito/state_opt/tests/test_state_exclusion.py b/toqito/state_opt/tests/test_state_exclusion.py index 44aeb43d..253fa1c7 100644 --- a/toqito/state_opt/tests/test_state_exclusion.py +++ b/toqito/state_opt/tests/test_state_exclusion.py @@ -1,4 +1,5 @@ """Test state_exclusion.""" + import pytest from toqito.matrices import standard_basis @@ -9,35 +10,35 @@ e_0, e_1 = standard_basis(2) -@pytest.mark.parametrize("vectors, probs, solver, primal_dual, expected_result", [ - # Bell states (default uniform probs with primal). - ([bell(0), bell(1), bell(2), bell(3)], None, "cvxopt", "primal", 0), - # Bell states (default uniform probs with dual). - ([bell(0), bell(1), bell(2), bell(3)], None, "cvxopt", "dual", 0), - # Bell states uniform probs with primal. - ([bell(0), bell(1), bell(2), bell(3)], [1/4, 1/4, 1/4, 1/4], "cvxopt", "primal", 0), - # Bell states uniform probs with dual. - ([bell(0), bell(1), bell(2), bell(3)], [1/4, 1/4, 1/4, 1/4], "cvxopt", "dual", 0), - # Density matrix Bell states (default uniform probs with dual). - ( - [pure_to_mixed(bell(0)), pure_to_mixed(bell(1))], - None, - "cvxopt", - "dual", - 0 - ), -]) +@pytest.mark.parametrize( + "vectors, probs, solver, primal_dual, expected_result", + [ + # Bell states (default uniform probs with primal). + ([bell(0), bell(1), bell(2), bell(3)], None, "cvxopt", "primal", 0), + # Bell states (default uniform probs with dual). + ([bell(0), bell(1), bell(2), bell(3)], None, "cvxopt", "dual", 0), + # Bell states uniform probs with primal. + ([bell(0), bell(1), bell(2), bell(3)], [1 / 4, 1 / 4, 1 / 4, 1 / 4], "cvxopt", "primal", 0), + # Bell states uniform probs with dual. + ([bell(0), bell(1), bell(2), bell(3)], [1 / 4, 1 / 4, 1 / 4, 1 / 4], "cvxopt", "dual", 0), + # Density matrix Bell states (default uniform probs with dual). + ([pure_to_mixed(bell(0)), pure_to_mixed(bell(1))], None, "cvxopt", "dual", 0), + ], +) def test_state_exclusion(vectors, probs, solver, primal_dual, expected_result): """Test function works as expected for a valid input.""" val, _ = state_exclusion(vectors=vectors, probs=probs, solver=solver, primal_dual=primal_dual) - assert abs(val - expected_result) <=1e-8 + assert abs(val - expected_result) <= 1e-8 -@pytest.mark.parametrize("vectors, probs, solver, primal_dual", [ - # Bell states (default uniform probs with dual). - ([bell(0), bell(1), bell(2), e_0], None, "cvxopt", "dual"), -]) +@pytest.mark.parametrize( + "vectors, probs, solver, primal_dual", + [ + # Bell states (default uniform probs with dual). + ([bell(0), bell(1), bell(2), e_0], None, "cvxopt", "dual"), + ], +) def test_state_exclusion_invalid_vectors(vectors, probs, solver, primal_dual): """Test function works as expected for an invalid input.""" - with pytest.raises(ValueError, match = "Vectors for state distinguishability must all have the same dimension."): + with pytest.raises(ValueError, match="Vectors for state distinguishability must all have the same dimension."): state_exclusion(vectors=vectors, probs=probs, solver=solver, primal_dual=primal_dual) diff --git a/toqito/state_opt/tests/test_symmetric_extension_hierarchy.py b/toqito/state_opt/tests/test_symmetric_extension_hierarchy.py index fdc0a34c..39656d3d 100644 --- a/toqito/state_opt/tests/test_symmetric_extension_hierarchy.py +++ b/toqito/state_opt/tests/test_symmetric_extension_hierarchy.py @@ -1,4 +1,5 @@ """Test symmetric_extension_hierarchy.""" + import numpy as np import pytest @@ -53,7 +54,7 @@ def test_symmetric_extension_hierarchy_four_bell_with_resource_state_lvl_1(): # Level 1 of the hierarchy should be identical to the known PPT value # for this case. res = symmetric_extension_hierarchy(states=states, probs=None, level=1) - exp_res = 1 / 2 * (1 + np.sqrt(1 - eps ** 2)) + exp_res = 1 / 2 * (1 + np.sqrt(1 - eps**2)) np.testing.assert_equal(np.isclose(res, exp_res), True) @@ -84,7 +85,7 @@ def test_symmetric_extension_hierarchy_four_bell_with_resource_state(): ] res = symmetric_extension_hierarchy(states=states, probs=None, level=2) - exp_res = 1 / 2 * (1 + np.sqrt(1 - eps ** 2)) + exp_res = 1 / 2 * (1 + np.sqrt(1 - eps**2)) np.testing.assert_equal(np.isclose(res, exp_res), True) diff --git a/toqito/state_props/__init__.py b/toqito/state_props/__init__.py index 263c949c..54d7fcd7 100644 --- a/toqito/state_props/__init__.py +++ b/toqito/state_props/__init__.py @@ -1,4 +1,5 @@ """Properties of quantum states.""" + from toqito.state_props.is_ensemble import is_ensemble from toqito.state_props.is_pure import is_pure from toqito.state_props.is_mixed import is_mixed diff --git a/toqito/state_props/concurrence.py b/toqito/state_props/concurrence.py index 36e3ae1c..54fa2a37 100644 --- a/toqito/state_props/concurrence.py +++ b/toqito/state_props/concurrence.py @@ -1,4 +1,5 @@ """Concurrence property.""" + import numpy as np from toqito.matrices import pauli diff --git a/toqito/state_props/entanglement_of_formation.py b/toqito/state_props/entanglement_of_formation.py index 077169e9..02b2e729 100644 --- a/toqito/state_props/entanglement_of_formation.py +++ b/toqito/state_props/entanglement_of_formation.py @@ -67,7 +67,6 @@ def entanglement_of_formation(rho: np.ndarray, dim: list[int] | int = None) -> f dim_x, dim_y = rho.shape round_dim = int(np.round(np.sqrt(max(dim_x, dim_y)))) - if dim is None: dim = round_dim @@ -79,9 +78,7 @@ def entanglement_of_formation(rho: np.ndarray, dim: list[int] | int = None) -> f dim[1] = np.round(dim[1]) if np.prod(dim) != max(dim_x, dim_y): - raise ValueError( - "Invalid dimension: Please provide local dimensions that match the size of `rho`." - ) + raise ValueError("Invalid dimension: Please provide local dimensions that match the size of `rho`.") # If :code:`rho` is a rank-1 density matrix, turn it into a vector instead # so we can compute the entanglement-of-formation easily. tmp_rho = scipy.linalg.orth(rho) @@ -102,8 +99,8 @@ def entanglement_of_formation(rho: np.ndarray, dim: list[int] | int = None) -> f if dim_x == 4: rho_c = concurrence(rho) - rho_c1 = (1 + np.sqrt(1 - rho_c ** 2)) / 2 - rho_c2 = (1 - np.sqrt(1 - rho_c ** 2)) / 2 + rho_c1 = (1 + np.sqrt(1 - rho_c**2)) / 2 + rho_c2 = (1 - np.sqrt(1 - rho_c**2)) / 2 rho_c1_log2 = 0 if rho_c1 == 0 else np.log2(rho_c1) rho_c2_log2 = 0 if rho_c2 == 0 else np.log2(rho_c2) diff --git a/toqito/state_props/has_symmetric_extension.py b/toqito/state_props/has_symmetric_extension.py index 76789a3b..96a48b82 100644 --- a/toqito/state_props/has_symmetric_extension.py +++ b/toqito/state_props/has_symmetric_extension.py @@ -1,6 +1,5 @@ """Determine whether there exists a symmetric extension for a given quantum state.""" - import numpy as np from picos import partial_trace @@ -106,9 +105,7 @@ def has_symmetric_extension( if isinstance(dim, int): dim = np.array([dim, len_mat / dim]) # pylint: disable=redefined-variable-type if np.abs(dim[1] - np.round(dim[1])) >= 2 * len_mat * np.finfo(float).eps: - raise ValueError( - "If `dim` is a scalar, it must evenly divide the length of the matrix." - ) + raise ValueError("If `dim` is a scalar, it must evenly divide the length of the matrix.") dim[1] = int(np.round(dim[1])) dim = np.int_(dim) diff --git a/toqito/state_props/in_separable_ball.py b/toqito/state_props/in_separable_ball.py index faf04a1d..6d86bb12 100644 --- a/toqito/state_props/in_separable_ball.py +++ b/toqito/state_props/in_separable_ball.py @@ -1,4 +1,5 @@ """Checks whether operator is in the ball of separability centered at the maximally-mixed state.""" + import numpy as np diff --git a/toqito/state_props/is_antidistinguishable.py b/toqito/state_props/is_antidistinguishable.py index 6d19e321..a844fc18 100644 --- a/toqito/state_props/is_antidistinguishable.py +++ b/toqito/state_props/is_antidistinguishable.py @@ -1,4 +1,5 @@ """Check if set of states are antidistinguishable.""" + import numpy as np from toqito.state_opt import state_exclusion diff --git a/toqito/state_props/is_distinguishable.py b/toqito/state_props/is_distinguishable.py index 9e7dec4f..ed21f692 100644 --- a/toqito/state_props/is_distinguishable.py +++ b/toqito/state_props/is_distinguishable.py @@ -1,4 +1,5 @@ """Check if set of states are distinguishable.""" + import numpy as np from toqito.state_opt import state_distinguishability diff --git a/toqito/state_props/is_ensemble.py b/toqito/state_props/is_ensemble.py index 33584bfb..f7291f29 100644 --- a/toqito/state_props/is_ensemble.py +++ b/toqito/state_props/is_ensemble.py @@ -1,4 +1,5 @@ """Check if set of states form an ensemble.""" + import numpy as np from toqito.matrix_props import is_positive_semidefinite diff --git a/toqito/state_props/is_mixed.py b/toqito/state_props/is_mixed.py index 00f173cd..74ae3d07 100644 --- a/toqito/state_props/is_mixed.py +++ b/toqito/state_props/is_mixed.py @@ -1,4 +1,5 @@ """Check if state is mixed.""" + import numpy as np from toqito.state_props import is_pure diff --git a/toqito/state_props/is_mutually_orthogonal.py b/toqito/state_props/is_mutually_orthogonal.py index c4249891..51e346d7 100644 --- a/toqito/state_props/is_mutually_orthogonal.py +++ b/toqito/state_props/is_mutually_orthogonal.py @@ -1,6 +1,5 @@ """Check if states are mutually orthogonal.""" - from typing import Any import numpy as np diff --git a/toqito/state_props/is_mutually_unbiased_basis.py b/toqito/state_props/is_mutually_unbiased_basis.py index 4aeb89e6..e9151cc4 100644 --- a/toqito/state_props/is_mutually_unbiased_basis.py +++ b/toqito/state_props/is_mutually_unbiased_basis.py @@ -1,4 +1,5 @@ """Check if states form mutually unbiased basis.""" + from typing import Any import numpy as np diff --git a/toqito/state_props/is_npt.py b/toqito/state_props/is_npt.py index 7214d2ed..e35c3e8d 100644 --- a/toqito/state_props/is_npt.py +++ b/toqito/state_props/is_npt.py @@ -1,6 +1,5 @@ """Check if state has NPT (negative partial transpose) criterion.""" - import numpy as np from toqito.state_props import is_ppt diff --git a/toqito/state_props/is_ppt.py b/toqito/state_props/is_ppt.py index 0b7de41b..052750ce 100644 --- a/toqito/state_props/is_ppt.py +++ b/toqito/state_props/is_ppt.py @@ -1,6 +1,5 @@ """Check if violates the PPT criterion.""" - import numpy as np from toqito.channels import partial_transpose diff --git a/toqito/state_props/is_product.py b/toqito/state_props/is_product.py index 3779fab2..15ffb514 100644 --- a/toqito/state_props/is_product.py +++ b/toqito/state_props/is_product.py @@ -1,6 +1,5 @@ """Check if state is product.""" - import numpy as np from toqito.perms import permute_systems, swap diff --git a/toqito/state_props/is_pure.py b/toqito/state_props/is_pure.py index 9431d5ef..36c8eab0 100644 --- a/toqito/state_props/is_pure.py +++ b/toqito/state_props/is_pure.py @@ -1,6 +1,5 @@ """Check if state is pure.""" - import numpy as np diff --git a/toqito/state_props/is_separable.py b/toqito/state_props/is_separable.py index 46b0435d..61e86756 100644 --- a/toqito/state_props/is_separable.py +++ b/toqito/state_props/is_separable.py @@ -1,6 +1,5 @@ """Check if state is separable.""" - import numpy as np from picos import partial_trace @@ -10,9 +9,7 @@ from toqito.state_props.has_symmetric_extension import has_symmetric_extension -def is_separable( - state: np.ndarray, dim: None | int | list[int] = None, level: int = 2, tol: float = 1e-8 -) -> bool: +def is_separable(state: np.ndarray, dim: None | int | list[int] = None, level: int = 2, tol: float = 1e-8) -> bool: r"""Determine if a given state (given as a density matrix) is a separable state :cite:`WikiSepSt`. Examples @@ -135,7 +132,7 @@ def is_separable( # Another test that is strictly stronger than the realignment criterion. if trace_norm(realignment(state - np.kron(pt_state_alice, pt_state_bob), dim)) > np.sqrt( - 1 - np.trace(pt_state_alice ** 2 @ pt_state_bob ** 2) + 1 - np.trace(pt_state_alice**2 @ pt_state_bob**2) ): # Determined to be entangled by using Theorem 1 of reference. # C.-J. Zhang, Y.-S. Zhang, S. Zhang, and G.-C. Guo. @@ -151,13 +148,9 @@ def is_separable( # Check these tests. if min_dim == 2: # Check if X is separable from spectrum. - if (lam[0] - lam[2 * max_dim - 1]) ** 2 <= 4 * lam[2 * max_dim - 2] * lam[ - 2 * max_dim - ] + tol ** 2: + if (lam[0] - lam[2 * max_dim - 1]) ** 2 <= 4 * lam[2 * max_dim - 2] * lam[2 * max_dim] + tol**2: print("Determined to be separable by inspecting its eigenvalues.") - print( - "N. Johnston. Separability from spectrum for qubit-qudit states. Phys. Rev. A, 88:062330, 2013." - ) + print("N. Johnston. Separability from spectrum for qubit-qudit states. Phys. Rev. A, 88:062330, 2013.") return True # For the rest of the block-matrix tests, we need the 2-dimensional subsystem to be the @@ -177,7 +170,7 @@ def is_separable( # Check if X is a rank-1 perturbation of the identity, which is # necessarily separable if it's PPT, which we have already checked. - if lam[1] - lam[prod_dim - 1] < tol ** 2: + if lam[1] - lam[prod_dim - 1] < tol**2: # Determined to be separable by being a small rank-1 perturbation of the maximally-mixed state. # G. Vidal and R. Tarrach. Robustness of entanglement. # Phys. Rev. A, 59:141-155, 1999. diff --git a/toqito/state_props/is_unextendible_product_basis.py b/toqito/state_props/is_unextendible_product_basis.py index 5716515f..c8ed8cd1 100644 --- a/toqito/state_props/is_unextendible_product_basis.py +++ b/toqito/state_props/is_unextendible_product_basis.py @@ -1,4 +1,5 @@ """Check if a set of states form an unextendible product basis.""" + from itertools import permutations import numpy as np @@ -103,7 +104,7 @@ def is_unextendible_product_basis(vecs: list[np.ndarray], dims: list[int]) -> tu witness_found = True for i in range(num_parties): # For the i-th party, acquire the matrix. - mat = np.stack([vecs_split[col , i, :] for col in part_ordered[i]]) + mat = np.stack([vecs_split[col, i, :] for col in part_ordered[i]]) # Find the basis of the null space. null_basis = null_space(mat) # If null space is empty then break. diff --git a/toqito/state_props/l1_norm_coherence.py b/toqito/state_props/l1_norm_coherence.py index 9dd38166..bd127fe2 100644 --- a/toqito/state_props/l1_norm_coherence.py +++ b/toqito/state_props/l1_norm_coherence.py @@ -1,4 +1,5 @@ """Compute the l1-norm of coherence of a quantum state.""" + import numpy as np from toqito.state_ops import pure_to_mixed diff --git a/toqito/state_props/log_negativity.py b/toqito/state_props/log_negativity.py index ae3753c5..59178a53 100644 --- a/toqito/state_props/log_negativity.py +++ b/toqito/state_props/log_negativity.py @@ -1,6 +1,5 @@ """Logarithmic negativity property.""" - import numpy as np from picos import partial_transpose @@ -71,8 +70,7 @@ def log_negativity(rho: np.ndarray, dim: list[int] | int = None) -> float: if np.prod(dim) != rho_dims[0]: raise ValueError( - "InvalidDim: Please provide local dimensions in the " - "argument `dim` that match the size of `rho`." + "InvalidDim: Please provide local dimensions in the argument `dim` that match the size of `rho`." ) dim = [int(x.item()) for x in dim] diff --git a/toqito/state_props/negativity.py b/toqito/state_props/negativity.py index e5997cdb..03c51ead 100644 --- a/toqito/state_props/negativity.py +++ b/toqito/state_props/negativity.py @@ -1,6 +1,5 @@ """Negativity property.""" - import numpy as np from picos import partial_transpose @@ -72,8 +71,7 @@ def negativity(rho: np.ndarray, dim: list[int] | int = None) -> float: if np.prod(dim) != rho_dims[0]: raise ValueError( - "InvalidDim: Please provide local dimensions in the " - "argument `dim` that match the size of `rho`." + "InvalidDim: Please provide local dimensions in the argument `dim` that match the size of `rho`." ) dim = [int(x.item()) for x in dim] diff --git a/toqito/state_props/purity.py b/toqito/state_props/purity.py index 4df45d71..915fe7d4 100644 --- a/toqito/state_props/purity.py +++ b/toqito/state_props/purity.py @@ -1,4 +1,5 @@ """State purity.""" + import numpy as np from toqito.matrix_props import is_density diff --git a/toqito/state_props/schmidt_rank.py b/toqito/state_props/schmidt_rank.py index f2de095f..34885e7a 100644 --- a/toqito/state_props/schmidt_rank.py +++ b/toqito/state_props/schmidt_rank.py @@ -1,6 +1,5 @@ """Schmidt rank of state.""" - import numpy as np from toqito.perms import swap diff --git a/toqito/state_props/sk_vec_norm.py b/toqito/state_props/sk_vec_norm.py index 5265d407..47f6a829 100644 --- a/toqito/state_props/sk_vec_norm.py +++ b/toqito/state_props/sk_vec_norm.py @@ -1,6 +1,5 @@ """Compute the S(k)-norm of a vector.""" - import numpy as np from toqito.state_ops import schmidt_decomposition diff --git a/toqito/state_props/tests/test_concurrence.py b/toqito/state_props/tests/test_concurrence.py index b6bc7a68..81532c92 100644 --- a/toqito/state_props/tests/test_concurrence.py +++ b/toqito/state_props/tests/test_concurrence.py @@ -1,4 +1,5 @@ """Test concurrence.""" + import numpy as np import pytest diff --git a/toqito/state_props/tests/test_entanglement_of_formation.py b/toqito/state_props/tests/test_entanglement_of_formation.py index 73c5a6cd..4cb625ee 100644 --- a/toqito/state_props/tests/test_entanglement_of_formation.py +++ b/toqito/state_props/tests/test_entanglement_of_formation.py @@ -1,4 +1,5 @@ """Test entanglement_of_formation.""" + import numpy as np import pytest @@ -36,20 +37,28 @@ def test_entanglement_of_formation(rho, dim, expected_result): # Invalid local dimension for entanglement_of_formation. (np.identity(4), 3, "Invalid dimension: Please provide local dimensions that match the size of `rho`."), # Not presently known how to calculate for mixed states. - (3 / 4 * e_0 @ e_0.conj().T + 1 / 4 * e_1 @ e_1.conj().T, None, + ( + 3 / 4 * e_0 @ e_0.conj().T + 1 / 4 * e_1 @ e_1.conj().T, + None, "Invalid input: It is presently only known how to compute " "the entanglement-of-formation for two-qubit states and pure " - "states."), + "states.", + ), # Invalid non-square matrix for entanglement_of_formation. ( - np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]), None, - "Invalid dimension: `rho` must be either a vector or square matrix."), + np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]), + None, + "Invalid dimension: `rho` must be either a vector or square matrix.", + ), # The entanglement-of-formation on a maximally mixed with list dim - (max_mixed(4, False) @ max_mixed(4, False).conj().T, [1, 1], - "Invalid dimension: Please provide local dimensions that match the size of `rho`.") + ( + max_mixed(4, False) @ max_mixed(4, False).conj().T, + [1, 1], + "Invalid dimension: Please provide local dimensions that match the size of `rho`.", + ), ], ) def test_entanglement_of_formation_invalid(rho, dim, error_msg): """Ensures that an integer above 4 is error-checked.""" - with pytest.raises(ValueError, match = error_msg): + with pytest.raises(ValueError, match=error_msg): entanglement_of_formation(rho, dim) diff --git a/toqito/state_props/tests/test_has_symmetric_extension.py b/toqito/state_props/tests/test_has_symmetric_extension.py index 7265bbcb..96c83d67 100644 --- a/toqito/state_props/tests/test_has_symmetric_extension.py +++ b/toqito/state_props/tests/test_has_symmetric_extension.py @@ -1,4 +1,5 @@ """Test has_symmetric_extension.""" + import numpy as np import pytest @@ -33,9 +34,7 @@ ) def test_has_symmetric_extension(rho, level, dim, ppt, expected_result): """Test function works as expected for a valid input.""" - np.testing.assert_equal( - has_symmetric_extension(rho=rho, level=level, dim=dim, ppt=ppt), expected_result - ) + np.testing.assert_equal(has_symmetric_extension(rho=rho, level=level, dim=dim, ppt=ppt), expected_result) @pytest.mark.parametrize( diff --git a/toqito/state_props/tests/test_in_separable_ball.py b/toqito/state_props/tests/test_in_separable_ball.py index 5f0fcedb..30851948 100644 --- a/toqito/state_props/tests/test_in_separable_ball.py +++ b/toqito/state_props/tests/test_in_separable_ball.py @@ -1,4 +1,5 @@ """Test in_separable_ball.""" + import numpy as np import pytest @@ -19,9 +20,7 @@ (np.zeros((4, 4)), False), # Test eigenvalues of matrix not in separable ball returns False. ( - np.linalg.eigvalsh( - random_u_mat @ np.diag(np.array([1.01, 1, 0.99, 0])) / 3 @ random_u_mat.conj().T - ), + np.linalg.eigvalsh(random_u_mat @ np.diag(np.array([1.01, 1, 0.99, 0])) / 3 @ random_u_mat.conj().T), False, ), ], diff --git a/toqito/state_props/tests/test_is_antidistinguishable.py b/toqito/state_props/tests/test_is_antidistinguishable.py index 5049bb98..bf9215b0 100644 --- a/toqito/state_props/tests/test_is_antidistinguishable.py +++ b/toqito/state_props/tests/test_is_antidistinguishable.py @@ -1,16 +1,20 @@ """Test is_antidistinguishable.""" + import pytest from toqito.state_props import is_antidistinguishable from toqito.states import bell, trine -@pytest.mark.parametrize("states", [ - # The Bell states are known to be antidistinguishable. - ([bell(0), bell(1), bell(2), bell(3)]), - # The trine states are known to be antidistinguishable. - ([trine()[0], trine()[1], trine()[2]]), -]) +@pytest.mark.parametrize( + "states", + [ + # The Bell states are known to be antidistinguishable. + ([bell(0), bell(1), bell(2), bell(3)]), + # The trine states are known to be antidistinguishable. + ([trine()[0], trine()[1], trine()[2]]), + ], +) def test_is_antidistinguishable(states): """Test function works as expected for a valid input.""" assert is_antidistinguishable(states) diff --git a/toqito/state_props/tests/test_is_distinguishable.py b/toqito/state_props/tests/test_is_distinguishable.py index a3eb3728..40dae5f7 100644 --- a/toqito/state_props/tests/test_is_distinguishable.py +++ b/toqito/state_props/tests/test_is_distinguishable.py @@ -1,16 +1,20 @@ """Test is_distinguishable.""" + import pytest from toqito.state_props import is_distinguishable from toqito.states import bell, trine -@pytest.mark.parametrize("states, probs, is_dist", [ - # The Bell states are known to be distinguishable. - ([bell(0), bell(1), bell(2), bell(3)], [1 / 4, 1 / 4, 1 / 4, 1 / 4], True), - # The trine states are known to not be distinguishable. - ([trine()[0], trine()[1], trine()[2]], [1 / 4, 1 / 4, 1 / 4, 1 / 4], False), -]) +@pytest.mark.parametrize( + "states, probs, is_dist", + [ + # The Bell states are known to be distinguishable. + ([bell(0), bell(1), bell(2), bell(3)], [1 / 4, 1 / 4, 1 / 4, 1 / 4], True), + # The trine states are known to not be distinguishable. + ([trine()[0], trine()[1], trine()[2]], [1 / 4, 1 / 4, 1 / 4, 1 / 4], False), + ], +) def test_is_distinguishable(states, probs, is_dist): """Test function works as expected for a valid input.""" assert is_distinguishable(states, probs) == is_dist diff --git a/toqito/state_props/tests/test_is_ensemble.py b/toqito/state_props/tests/test_is_ensemble.py index 3534afda..cec96d1c 100644 --- a/toqito/state_props/tests/test_is_ensemble.py +++ b/toqito/state_props/tests/test_is_ensemble.py @@ -1,4 +1,5 @@ """Test is_ensemble.""" + import numpy as np import pytest diff --git a/toqito/state_props/tests/test_is_mixed.py b/toqito/state_props/tests/test_is_mixed.py index 48287467..eb468152 100644 --- a/toqito/state_props/tests/test_is_mixed.py +++ b/toqito/state_props/tests/test_is_mixed.py @@ -1,4 +1,5 @@ """Test is_mixed.""" + import numpy as np import pytest diff --git a/toqito/state_props/tests/test_is_mutually_orthogonal.py b/toqito/state_props/tests/test_is_mutually_orthogonal.py index a569feee..d02cfa29 100644 --- a/toqito/state_props/tests/test_is_mutually_orthogonal.py +++ b/toqito/state_props/tests/test_is_mutually_orthogonal.py @@ -1,4 +1,5 @@ """Test is_mutually_orthogonal.""" + import numpy as np import pytest diff --git a/toqito/state_props/tests/test_is_mutually_unbiased_basis.py b/toqito/state_props/tests/test_is_mutually_unbiased_basis.py index 6d0eed15..09c04ed8 100644 --- a/toqito/state_props/tests/test_is_mutually_unbiased_basis.py +++ b/toqito/state_props/tests/test_is_mutually_unbiased_basis.py @@ -1,4 +1,5 @@ """Test is_mutually_unbiased_basis.""" + import numpy as np import pytest @@ -40,38 +41,43 @@ e_0, e_1 = basis(2, 0), basis(2, 1) -@pytest.mark.parametrize("states, expected_result", [ - # Return True for MUB of dimension 2. - (MUB_2, True), - # Return True for MUB of dimension 4. - (MUB_4, True), - # Return False for non-MUB of dimension 2. - ( - [ - e_0, e_1, - 1 / np.sqrt(2) * (e_0 + e_1), e_1, - 1 / np.sqrt(2) * (e_0 + 1j * e_1), e_0, - ], - False - ), - # Return False for any vectors such that the number of vectors % dim != 0: - ( - [ - np.array([1, 0]), - np.array([1, 0]), - np.array([1, 0]), - ], - False - ), - # Return False for any vectors such that the number of vectors % dim != 0: - ( - [ - np.array([1, 0]), - ], - False - ), -]) +@pytest.mark.parametrize( + "states, expected_result", + [ + # Return True for MUB of dimension 2. + (MUB_2, True), + # Return True for MUB of dimension 4. + (MUB_4, True), + # Return False for non-MUB of dimension 2. + ( + [ + e_0, + e_1, + 1 / np.sqrt(2) * (e_0 + e_1), + e_1, + 1 / np.sqrt(2) * (e_0 + 1j * e_1), + e_0, + ], + False, + ), + # Return False for any vectors such that the number of vectors % dim != 0: + ( + [ + np.array([1, 0]), + np.array([1, 0]), + np.array([1, 0]), + ], + False, + ), + # Return False for any vectors such that the number of vectors % dim != 0: + ( + [ + np.array([1, 0]), + ], + False, + ), + ], +) def test_is_mutually_unbiased(states, expected_result): """Test function works as expected for a valid input.""" np.testing.assert_equal(is_mutually_unbiased_basis(states), expected_result) - diff --git a/toqito/state_props/tests/test_is_npt.py b/toqito/state_props/tests/test_is_npt.py index 5e4f9c78..5a6148ac 100644 --- a/toqito/state_props/tests/test_is_npt.py +++ b/toqito/state_props/tests/test_is_npt.py @@ -1,4 +1,5 @@ """Test is_npt.""" + import numpy as np import pytest diff --git a/toqito/state_props/tests/test_is_ppt.py b/toqito/state_props/tests/test_is_ppt.py index b905c594..2864dec5 100644 --- a/toqito/state_props/tests/test_is_ppt.py +++ b/toqito/state_props/tests/test_is_ppt.py @@ -1,4 +1,5 @@ """Test is_ppt.""" + import numpy as np import pytest diff --git a/toqito/state_props/tests/test_is_product.py b/toqito/state_props/tests/test_is_product.py index cb176997..2aae409f 100644 --- a/toqito/state_props/tests/test_is_product.py +++ b/toqito/state_props/tests/test_is_product.py @@ -1,4 +1,5 @@ """Test is_product_vector.""" + import numpy as np import pytest diff --git a/toqito/state_props/tests/test_is_pure.py b/toqito/state_props/tests/test_is_pure.py index deac063f..8de5063b 100644 --- a/toqito/state_props/tests/test_is_pure.py +++ b/toqito/state_props/tests/test_is_pure.py @@ -1,4 +1,5 @@ """Test is_pure.""" + import numpy as np import pytest diff --git a/toqito/state_props/tests/test_is_separable.py b/toqito/state_props/tests/test_is_separable.py index 2873d1b8..97b24e14 100644 --- a/toqito/state_props/tests/test_is_separable.py +++ b/toqito/state_props/tests/test_is_separable.py @@ -1,4 +1,5 @@ """Test is_separable.""" + import numpy as np from toqito.channels import partial_trace @@ -58,8 +59,7 @@ def test_ppt_low_rank(): 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, + np.linalg.matrix_rank(rho_cut) + np.linalg.matrix_rank(pt_state_alice) <= 2 * m * n - m - n + 2, True, ) # TODO @@ -83,10 +83,10 @@ def test_entangled_cross_norm_realignment_criterion(): 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], + [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) diff --git a/toqito/state_props/tests/test_is_unextendible_product_basis.py b/toqito/state_props/tests/test_is_unextendible_product_basis.py index 68987633..171efc94 100644 --- a/toqito/state_props/tests/test_is_unextendible_product_basis.py +++ b/toqito/state_props/tests/test_is_unextendible_product_basis.py @@ -1,4 +1,5 @@ """Test is_unextendible_product_basis.""" + import numpy as np import pytest @@ -36,14 +37,9 @@ def test_unextendible_product_basis_invalid(states, dims): ([tile(0), tile(1), tile(2), tile(3)], [3, 3], False), # Check if Shifts is correctly identified as UPB. ( - [ - tensor([e_0, e_1, e_p]), - tensor([e_1, e_p, e_0]), - tensor([e_p, e_0, e_1]), - tensor([e_m, e_m, e_m]) - ], + [tensor([e_0, e_1, e_p]), tensor([e_1, e_p, e_0]), tensor([e_p, e_0, e_1]), tensor([e_m, e_m, e_m])], [2, 2, 2], - True + True, ), ], ) diff --git a/toqito/state_props/tests/test_l1_norm_coherence.py b/toqito/state_props/tests/test_l1_norm_coherence.py index 38cf2093..bde2ee8e 100644 --- a/toqito/state_props/tests/test_l1_norm_coherence.py +++ b/toqito/state_props/tests/test_l1_norm_coherence.py @@ -1,4 +1,5 @@ """Test l1_norm_coherence.""" + import numpy as np from toqito.state_props import l1_norm_coherence diff --git a/toqito/state_props/tests/test_log_negativity.py b/toqito/state_props/tests/test_log_negativity.py index 0d1303b3..ea633bf3 100644 --- a/toqito/state_props/tests/test_log_negativity.py +++ b/toqito/state_props/tests/test_log_negativity.py @@ -1,4 +1,5 @@ """Test log_negativity.""" + import numpy as np import pytest diff --git a/toqito/state_props/tests/test_negativity.py b/toqito/state_props/tests/test_negativity.py index 70fd55e9..2d2f3d4f 100644 --- a/toqito/state_props/tests/test_negativity.py +++ b/toqito/state_props/tests/test_negativity.py @@ -1,4 +1,5 @@ """Test negativity.""" + import numpy as np import pytest diff --git a/toqito/state_props/tests/test_purity.py b/toqito/state_props/tests/test_purity.py index 174c1eb4..35b38566 100644 --- a/toqito/state_props/tests/test_purity.py +++ b/toqito/state_props/tests/test_purity.py @@ -1,4 +1,5 @@ """Tests for purity.""" + import numpy as np import pytest diff --git a/toqito/state_props/tests/test_schmidt_rank.py b/toqito/state_props/tests/test_schmidt_rank.py index 56854381..fb1f1957 100644 --- a/toqito/state_props/tests/test_schmidt_rank.py +++ b/toqito/state_props/tests/test_schmidt_rank.py @@ -1,4 +1,5 @@ """Test schmidt_rank.""" + import numpy as np import pytest diff --git a/toqito/state_props/tests/test_sk_vec_norm.py b/toqito/state_props/tests/test_sk_vec_norm.py index 39107080..23a76c11 100644 --- a/toqito/state_props/tests/test_sk_vec_norm.py +++ b/toqito/state_props/tests/test_sk_vec_norm.py @@ -1,4 +1,5 @@ """Test sk_vector_norm.""" + import numpy as np import pytest @@ -21,6 +22,7 @@ def test_sk_norm_maximally_entagled_state_with_dim(n, k, dim): res = sk_vector_norm(v_vec, k=k, dim=dim) assert np.isclose(res, 1.0) + @pytest.mark.parametrize("n, k, expected_result", [(4, 2, 0.7), (5, 2, 0.63)]) def test_sk_norm_maximally_entagled_state_with_none_dim(n, k, expected_result): """The S(k)-norm of the maximally entagled state dim = None.""" diff --git a/toqito/state_props/tests/test_von_neumann_entropy.py b/toqito/state_props/tests/test_von_neumann_entropy.py index 5328fd6e..631db88d 100644 --- a/toqito/state_props/tests/test_von_neumann_entropy.py +++ b/toqito/state_props/tests/test_von_neumann_entropy.py @@ -1,4 +1,5 @@ """Tests for von_neumann_entropy.""" + import numpy as np import pytest diff --git a/toqito/state_props/von_neumann_entropy.py b/toqito/state_props/von_neumann_entropy.py index 0a31cc66..0d00433a 100644 --- a/toqito/state_props/von_neumann_entropy.py +++ b/toqito/state_props/von_neumann_entropy.py @@ -1,4 +1,5 @@ """Von neumann entropy metric.""" + import numpy as np from toqito.matrix_props import is_density diff --git a/toqito/states/__init__.py b/toqito/states/__init__.py index 55480ed8..48861a4c 100644 --- a/toqito/states/__init__.py +++ b/toqito/states/__init__.py @@ -1,4 +1,5 @@ """Quantum states.""" + from toqito.states.basis import basis from toqito.states.bb84 import bb84 from toqito.states.bell import bell diff --git a/toqito/states/basis.py b/toqito/states/basis.py index 8f357725..37c6d794 100644 --- a/toqito/states/basis.py +++ b/toqito/states/basis.py @@ -1,4 +1,5 @@ """Basis state.""" + import numpy as np @@ -44,9 +45,7 @@ def basis(dim: int, pos: int) -> np.ndarray: """ if pos >= dim: - raise ValueError( - "Invalid: The `pos` variable needs to be less than `dim` for ket function." - ) + raise ValueError("Invalid: The `pos` variable needs to be less than `dim` for ket function.") ret = np.array(list(int(x) for x in list(f"{0:0{dim}}"))) ret[pos] = 1 diff --git a/toqito/states/bb84.py b/toqito/states/bb84.py index 3a3e327e..178e8ae1 100644 --- a/toqito/states/bb84.py +++ b/toqito/states/bb84.py @@ -1,4 +1,5 @@ """BB84 basis states.""" + import numpy as np from toqito.matrices import standard_basis diff --git a/toqito/states/bell.py b/toqito/states/bell.py index 470f6e7e..22cc8ad9 100644 --- a/toqito/states/bell.py +++ b/toqito/states/bell.py @@ -1,4 +1,5 @@ """Bell state.""" + import numpy as np from toqito.states import basis diff --git a/toqito/states/brauer.py b/toqito/states/brauer.py index 02c3e456..8ccc1da0 100644 --- a/toqito/states/brauer.py +++ b/toqito/states/brauer.py @@ -1,4 +1,5 @@ """Brauer states.""" + import numpy as np from toqito.matrix_ops import tensor @@ -71,7 +72,5 @@ def brauer(dim: int, p_val: int) -> np.ndarray: # Turn these perfect matchings into the corresponding states. for i in range(num_matchings): - state[:, i] = permute_systems( - phi, matchings[i, :], dim * np.ones((1, 2 * p_val), dtype=int)[0] - ) + state[:, i] = permute_systems(phi, matchings[i, :], dim * np.ones((1, 2 * p_val), dtype=int)[0]) return state diff --git a/toqito/states/breuer.py b/toqito/states/breuer.py index a9b2b0b2..3098df05 100644 --- a/toqito/states/breuer.py +++ b/toqito/states/breuer.py @@ -1,4 +1,5 @@ """Breuer state.""" + import numpy as np from toqito.perms import symmetric_projection diff --git a/toqito/states/chessboard.py b/toqito/states/chessboard.py index 87b4465b..67ccb559 100644 --- a/toqito/states/chessboard.py +++ b/toqito/states/chessboard.py @@ -1,4 +1,5 @@ """Chessboard state.""" + import numpy as np diff --git a/toqito/states/domino.py b/toqito/states/domino.py index f2ae14ef..9af1cb2c 100644 --- a/toqito/states/domino.py +++ b/toqito/states/domino.py @@ -1,4 +1,5 @@ """Domino state.""" + import numpy as np from toqito.states import basis diff --git a/toqito/states/gen_bell.py b/toqito/states/gen_bell.py index c8ec4c84..0d9a0919 100644 --- a/toqito/states/gen_bell.py +++ b/toqito/states/gen_bell.py @@ -1,4 +1,5 @@ """Generalized Bell state.""" + import numpy as np from toqito.matrices import gen_pauli diff --git a/toqito/states/ghz.py b/toqito/states/ghz.py index a4a0c785..64b3a95a 100644 --- a/toqito/states/ghz.py +++ b/toqito/states/ghz.py @@ -1,4 +1,5 @@ """GHZ state.""" + import numpy as np from scipy import sparse @@ -82,17 +83,15 @@ def ghz(dim: int, num_qubits: int, coeff: list[int] = None) -> sparse: if num_qubits < 1: raise ValueError("InvalidNumQubits: `num_qubits` must be at least 1.") if len(coeff) != dim: - raise ValueError( - "InvalidCoeff: The variable `coeff` must be a vector of length equal to `dim`." - ) + raise ValueError("InvalidCoeff: The variable `coeff` must be a vector of length equal to `dim`.") # Construct the state (and do it in a way that is less memory-intensive # than naively tensoring things together. dim_sum = 1 for i in range(1, num_qubits): - dim_sum += dim ** i + dim_sum += dim**i - ret_ghz_state = sparse.lil_matrix((dim ** num_qubits, 1)) + ret_ghz_state = sparse.lil_matrix((dim**num_qubits, 1)) for i in range(1, dim + 1): ret_ghz_state[(i - 1) * dim_sum] = coeff[i - 1] return ret_ghz_state diff --git a/toqito/states/gisin.py b/toqito/states/gisin.py index 78de283f..60fb52f7 100644 --- a/toqito/states/gisin.py +++ b/toqito/states/gisin.py @@ -1,4 +1,5 @@ """Gisin state.""" + import numpy as np diff --git a/toqito/states/horodecki.py b/toqito/states/horodecki.py index ce9fd6ba..1045bb5e 100644 --- a/toqito/states/horodecki.py +++ b/toqito/states/horodecki.py @@ -1,4 +1,5 @@ """Horodecki state.""" + import numpy as np @@ -118,7 +119,7 @@ def horodecki(a_param: float, dim: list[int] = None) -> np.ndarray: if np.array_equal(dim, np.array([3, 3])): n_a_param = 1 / (8 * a_param + 1) b_param = (1 + a_param) / 2 - c_param = np.sqrt(1 - a_param ** 2) / 2 + c_param = np.sqrt(1 - a_param**2) / 2 horo_state = n_a_param * np.array( [ @@ -138,7 +139,7 @@ def horodecki(a_param: float, dim: list[int] = None) -> np.ndarray: if np.array_equal(dim, np.array([2, 4])): n_a_param = 1 / (7 * a_param + 1) b_param = (1 + a_param) / 2 - c_param = np.sqrt(1 - a_param ** 2) / 2 + c_param = np.sqrt(1 - a_param**2) / 2 horo_state = n_a_param * np.array( [ diff --git a/toqito/states/isotropic.py b/toqito/states/isotropic.py index 7f8c3c06..bdd9f0ed 100644 --- a/toqito/states/isotropic.py +++ b/toqito/states/isotropic.py @@ -1,4 +1,5 @@ """Isotropic state.""" + import numpy as np from toqito.states import max_entangled diff --git a/toqito/states/max_entangled.py b/toqito/states/max_entangled.py index 22eb37c4..48279430 100644 --- a/toqito/states/max_entangled.py +++ b/toqito/states/max_entangled.py @@ -1,4 +1,5 @@ """Maximally entangled state.""" + import numpy as np import scipy as sp @@ -55,7 +56,7 @@ def max_entangled(dim: int, is_sparse: bool = False, is_normalized: bool = True) """ mat = sp.sparse.identity(dim) if is_sparse else np.identity(dim) - psi = np.reshape(mat, (dim ** 2, 1)) + psi = np.reshape(mat, (dim**2, 1)) if is_normalized: psi = psi / np.sqrt(dim) return psi diff --git a/toqito/states/max_mixed.py b/toqito/states/max_mixed.py index a3d62c75..a08ed4e6 100644 --- a/toqito/states/max_mixed.py +++ b/toqito/states/max_mixed.py @@ -1,4 +1,5 @@ """Maximally mixed state.""" + import numpy as np from scipy import sparse diff --git a/toqito/states/mutually_unbiased_basis.py b/toqito/states/mutually_unbiased_basis.py index 267bb95b..f7896ff3 100644 --- a/toqito/states/mutually_unbiased_basis.py +++ b/toqito/states/mutually_unbiased_basis.py @@ -1,4 +1,5 @@ """Construct a set of mutually unbiased bases.""" + import numpy as np from sympy import isprime, primerange @@ -38,7 +39,7 @@ def mutually_unbiased_basis(dim: int) -> list[np.ndarray]: pauli_z = gen_pauli(0, 1, dim) for j in range(dim, 0, -1): - _, eigen_vec = np.linalg.eig(pauli_x @ pauli_z**(j)) + _, eigen_vec = np.linalg.eig(pauli_x @ pauli_z ** (j)) mats.append(eigen_vec) elif _is_prime_power(dim) and not isprime(dim): raise ValueError(f"Dimension {dim} is a prime power but not prime (more complicated no support at the moment).") diff --git a/toqito/states/pusey_barrett_rudolph.py b/toqito/states/pusey_barrett_rudolph.py index 82ee60cd..24483af1 100644 --- a/toqito/states/pusey_barrett_rudolph.py +++ b/toqito/states/pusey_barrett_rudolph.py @@ -1,4 +1,5 @@ """Construct a set of mutually unbiased bases.""" + import itertools import numpy as np @@ -50,8 +51,8 @@ def pusey_barrett_rudolph(n: int, theta: float) -> list[np.ndarray]: """ e_0, e_1 = standard_basis(2) - psi_0 = np.cos(theta/2) * e_0 + np.sin(theta/2) * e_1 - psi_1 = np.cos(theta/2) * e_0 - np.sin(theta/2) * e_1 + psi_0 = np.cos(theta / 2) * e_0 + np.sin(theta / 2) * e_1 + psi_1 = np.cos(theta / 2) * e_0 - np.sin(theta / 2) * e_1 psi = [psi_0, psi_1] binary_strings = list(itertools.product([0, 1], repeat=n)) diff --git a/toqito/states/singlet.py b/toqito/states/singlet.py index 87fc9c24..4cc57b89 100644 --- a/toqito/states/singlet.py +++ b/toqito/states/singlet.py @@ -1,4 +1,5 @@ """Generalized singlet state.""" + import numpy as np from toqito.perms import swap_operator @@ -70,4 +71,4 @@ def singlet(dim: int) -> np.ndarray: :return: The singlet state of dimension `dim`. """ - return (np.identity(dim ** 2) - swap_operator([dim, dim])) / ((dim ** 2) - dim) + return (np.identity(dim**2) - swap_operator([dim, dim])) / ((dim**2) - dim) diff --git a/toqito/states/tests/test_basis.py b/toqito/states/tests/test_basis.py index eace40a1..ed2b7b08 100644 --- a/toqito/states/tests/test_basis.py +++ b/toqito/states/tests/test_basis.py @@ -1,4 +1,5 @@ """Test basis.""" + import numpy as np import pytest diff --git a/toqito/states/tests/test_bb84.py b/toqito/states/tests/test_bb84.py index 70b47263..6f41a7a5 100644 --- a/toqito/states/tests/test_bb84.py +++ b/toqito/states/tests/test_bb84.py @@ -1,4 +1,5 @@ """Test BB84.""" + import numpy as np from toqito.states import bb84 diff --git a/toqito/states/tests/test_bell.py b/toqito/states/tests/test_bell.py index 5e09623b..ac714cfc 100644 --- a/toqito/states/tests/test_bell.py +++ b/toqito/states/tests/test_bell.py @@ -1,4 +1,5 @@ """Test bell.""" + import numpy as np import pytest diff --git a/toqito/states/tests/test_brauer.py b/toqito/states/tests/test_brauer.py index a4c2ff88..b20f669b 100644 --- a/toqito/states/tests/test_brauer.py +++ b/toqito/states/tests/test_brauer.py @@ -1,4 +1,5 @@ """Test brauer.""" + import numpy as np import pytest diff --git a/toqito/states/tests/test_breuer.py b/toqito/states/tests/test_breuer.py index bc3a6339..d65b946a 100644 --- a/toqito/states/tests/test_breuer.py +++ b/toqito/states/tests/test_breuer.py @@ -1,4 +1,5 @@ """Test breuer.""" + import numpy as np import pytest diff --git a/toqito/states/tests/test_chessboard.py b/toqito/states/tests/test_chessboard.py index 20663b10..e295165c 100644 --- a/toqito/states/tests/test_chessboard.py +++ b/toqito/states/tests/test_chessboard.py @@ -1,4 +1,5 @@ """Test chessboard.""" + import numpy as np from toqito.states import chessboard diff --git a/toqito/states/tests/test_domino.py b/toqito/states/tests/test_domino.py index 7006e469..e4a43063 100644 --- a/toqito/states/tests/test_domino.py +++ b/toqito/states/tests/test_domino.py @@ -1,4 +1,5 @@ """Test domino.""" + import numpy as np import pytest diff --git a/toqito/states/tests/test_gen_bell.py b/toqito/states/tests/test_gen_bell.py index 12773b4b..cbddf86e 100644 --- a/toqito/states/tests/test_gen_bell.py +++ b/toqito/states/tests/test_gen_bell.py @@ -1,4 +1,5 @@ """Test gen_bell.""" + import numpy as np import pytest diff --git a/toqito/states/tests/test_ghz.py b/toqito/states/tests/test_ghz.py index 34665e84..248f5cbf 100644 --- a/toqito/states/tests/test_ghz.py +++ b/toqito/states/tests/test_ghz.py @@ -1,4 +1,5 @@ """Test ghz.""" + import numpy as np import pytest diff --git a/toqito/states/tests/test_gisin.py b/toqito/states/tests/test_gisin.py index 60a4cb8f..6cdaabd6 100644 --- a/toqito/states/tests/test_gisin.py +++ b/toqito/states/tests/test_gisin.py @@ -1,4 +1,5 @@ """Test gisin.""" + import numpy as np from toqito.states import gisin diff --git a/toqito/states/tests/test_horodecki.py b/toqito/states/tests/test_horodecki.py index 0d124e7a..6b5bbef1 100644 --- a/toqito/states/tests/test_horodecki.py +++ b/toqito/states/tests/test_horodecki.py @@ -1,4 +1,5 @@ """Test horodecki.""" + import numpy as np import pytest diff --git a/toqito/states/tests/test_isotropic.py b/toqito/states/tests/test_isotropic.py index 03514c7f..8ac68857 100644 --- a/toqito/states/tests/test_isotropic.py +++ b/toqito/states/tests/test_isotropic.py @@ -1,4 +1,5 @@ """Test isotropic.""" + import numpy as np from toqito.states import isotropic diff --git a/toqito/states/tests/test_max_entangled.py b/toqito/states/tests/test_max_entangled.py index 3c7105c8..f464d1a8 100644 --- a/toqito/states/tests/test_max_entangled.py +++ b/toqito/states/tests/test_max_entangled.py @@ -1,4 +1,5 @@ """Test max_entangled.""" + import numpy as np import pytest diff --git a/toqito/states/tests/test_max_mixed.py b/toqito/states/tests/test_max_mixed.py index 030fd753..18380589 100644 --- a/toqito/states/tests/test_max_mixed.py +++ b/toqito/states/tests/test_max_mixed.py @@ -1,4 +1,5 @@ """Test max_mixed.""" + import numpy as np from toqito.states import max_mixed diff --git a/toqito/states/tests/test_mutually_unbiased_basis.py b/toqito/states/tests/test_mutually_unbiased_basis.py index 252cb96a..c98d4cf8 100644 --- a/toqito/states/tests/test_mutually_unbiased_basis.py +++ b/toqito/states/tests/test_mutually_unbiased_basis.py @@ -1,4 +1,5 @@ """Test mutually_unbiased_basis.""" + import re import numpy as np @@ -20,7 +21,7 @@ def test_mutually_unbiased_basis_prime_power_not_prime(dim): """Dimension is a prime power but not prime (this is not presently supported).""" with pytest.raises( ValueError, - match=re.escape(f"Dimension {dim} is a prime power but not prime (more complicated no support at the moment).") + match=re.escape(f"Dimension {dim} is a prime power but not prime (more complicated no support at the moment)."), ): mutually_unbiased_basis(dim) @@ -28,26 +29,27 @@ def test_mutually_unbiased_basis_prime_power_not_prime(dim): @pytest.mark.parametrize("dim", [6, 10, 12]) def test_mutually_unbiased_basis_unknown_for_dim(dim): """Dimension requested does not (at present) have a known way to generate.""" - with pytest.raises( - ValueError, match=re.escape(f"No general construction of MUBs is known for dimension: {dim}.") - ): + with pytest.raises(ValueError, match=re.escape(f"No general construction of MUBs is known for dimension: {dim}.")): mutually_unbiased_basis(dim) -@pytest.mark.parametrize("n, expected_result", [ - # Hard-coded non-prime power case. - (1, False), - # Non-prime powers. - (6, False), - (10, False), - (12, False), - (15, False), - # Prime powers. - (2, True), - (3, True), - (4, True), - (5, True), -]) +@pytest.mark.parametrize( + "n, expected_result", + [ + # Hard-coded non-prime power case. + (1, False), + # Non-prime powers. + (6, False), + (10, False), + (12, False), + (15, False), + # Prime powers. + (2, True), + (3, True), + (4, True), + (5, True), + ], +) def test_is_prime_power(n, expected_result): """Test function works as expected for an invalid input.""" np.testing.assert_array_equal(_is_prime_power(n), expected_result) diff --git a/toqito/states/tests/test_pusey_barrett_rudolph.py b/toqito/states/tests/test_pusey_barrett_rudolph.py index 8b06df32..7a5c7c22 100644 --- a/toqito/states/tests/test_pusey_barrett_rudolph.py +++ b/toqito/states/tests/test_pusey_barrett_rudolph.py @@ -1,32 +1,38 @@ """Test pusey_barrett_rudolph.""" + import numpy as np import pytest from toqito.states import pusey_barrett_rudolph -@pytest.mark.parametrize("n, theta, expected_value", [ - # When `theta = 0`, this should simply be the [1, 0 ..., 0] vectors. - (1, 0, [np.array([1, 0]).reshape(-1, 1), np.array([1, 0]).reshape(-1, 1)]), - # When `theta = 0`, this should simply be the [1, 0 ..., 0] vectors. - ( - 2, 0, - [ - np.array([1, 0, 0, 0]).reshape(-1, 1), - np.array([1, 0, 0, 0]).reshape(-1, 1), - np.array([1, 0, 0, 0]).reshape(-1, 1), - np.array([1, 0, 0, 0]).reshape(-1, 1), - ] - ), - # n=1 and theta=0.5 - ( - 1, 0.5, - [ - np.array([np.cos(1/4), np.sin(1/4)]).reshape(-1, 1), - np.array([np.cos(1/4), -np.sin(1/4)]).reshape(-1, 1) - ] - ), -]) +@pytest.mark.parametrize( + "n, theta, expected_value", + [ + # When `theta = 0`, this should simply be the [1, 0 ..., 0] vectors. + (1, 0, [np.array([1, 0]).reshape(-1, 1), np.array([1, 0]).reshape(-1, 1)]), + # When `theta = 0`, this should simply be the [1, 0 ..., 0] vectors. + ( + 2, + 0, + [ + np.array([1, 0, 0, 0]).reshape(-1, 1), + np.array([1, 0, 0, 0]).reshape(-1, 1), + np.array([1, 0, 0, 0]).reshape(-1, 1), + np.array([1, 0, 0, 0]).reshape(-1, 1), + ], + ), + # n=1 and theta=0.5 + ( + 1, + 0.5, + [ + np.array([np.cos(1 / 4), np.sin(1 / 4)]).reshape(-1, 1), + np.array([np.cos(1 / 4), -np.sin(1 / 4)]).reshape(-1, 1), + ], + ), + ], +) def test_pusey_barrett_rudolph(n, theta, expected_value): """Test functions works as expected for valid inputs.""" states = pusey_barrett_rudolph(n, theta) diff --git a/toqito/states/tests/test_singlet.py b/toqito/states/tests/test_singlet.py index 90f908e0..dc34bda6 100644 --- a/toqito/states/tests/test_singlet.py +++ b/toqito/states/tests/test_singlet.py @@ -1,4 +1,5 @@ """Test singlet.""" + import numpy as np from toqito.states import bell, singlet diff --git a/toqito/states/tests/test_tile.py b/toqito/states/tests/test_tile.py index 8a5be861..55a513cd 100644 --- a/toqito/states/tests/test_tile.py +++ b/toqito/states/tests/test_tile.py @@ -1,4 +1,5 @@ """Test tile.""" + import numpy as np import pytest diff --git a/toqito/states/tests/test_trine.py b/toqito/states/tests/test_trine.py index 779bab0f..fecf1813 100644 --- a/toqito/states/tests/test_trine.py +++ b/toqito/states/tests/test_trine.py @@ -1,4 +1,5 @@ """Test trine.""" + import numpy as np from toqito.states import trine @@ -17,11 +18,11 @@ def test_trine(): # Trine[1] np.testing.assert_array_equal( states[1], - -1/2 * (np.array([[1], [0]]) + np.sqrt(3) * np.array([[0], [1]])), + -1 / 2 * (np.array([[1], [0]]) + np.sqrt(3) * np.array([[0], [1]])), ) # Trine[2] np.testing.assert_array_equal( states[2], - -1/2 * (np.array([[1], [0]]) - np.sqrt(3) * np.array([[0], [1]])), + -1 / 2 * (np.array([[1], [0]]) - np.sqrt(3) * np.array([[0], [1]])), ) diff --git a/toqito/states/tests/test_w_state.py b/toqito/states/tests/test_w_state.py index 8693438e..ad7a5aae 100644 --- a/toqito/states/tests/test_w_state.py +++ b/toqito/states/tests/test_w_state.py @@ -1,4 +1,5 @@ """Test w_state.""" + import numpy as np import pytest @@ -9,9 +10,7 @@ def test_w_state_3(): """The 3-qubit W-state.""" e_0, e_1 = basis(2, 0), basis(2, 1) - expected_res = ( - 1 / np.sqrt(3) * (tensor(e_1, e_0, e_0) + tensor(e_0, e_1, e_0) + tensor(e_0, e_0, e_1)) - ) + expected_res = 1 / np.sqrt(3) * (tensor(e_1, e_0, e_0) + tensor(e_0, e_1, e_0) + tensor(e_0, e_0, e_1)) res = w_state(3) np.testing.assert_allclose(res, expected_res, atol=0.2) diff --git a/toqito/states/tests/test_werner.py b/toqito/states/tests/test_werner.py index 067b0457..d0c34e31 100644 --- a/toqito/states/tests/test_werner.py +++ b/toqito/states/tests/test_werner.py @@ -1,4 +1,5 @@ """Test werner.""" + import numpy as np import pytest @@ -28,16 +29,19 @@ def test_werner_multipartite_valid(): np.testing.assert_equal(is_density(state), True) -@pytest.mark.parametrize("dim, alpha", [ - # Invalid alpha length (not matching p!-1 for any integer p > 1) - (2, [0.5, 0.6, 0.7]), - # Test with an integer (which is not a valid type for alpha) - (2, 5), - # Test with a string (which is not a valid type for alpha) - (2, "invalid"), - # Test with a dictionary (which is not a valid type for alpha) - (2, {"key": "value"}), -]) +@pytest.mark.parametrize( + "dim, alpha", + [ + # Invalid alpha length (not matching p!-1 for any integer p > 1) + (2, [0.5, 0.6, 0.7]), + # Test with an integer (which is not a valid type for alpha) + (2, 5), + # Test with a string (which is not a valid type for alpha) + (2, "invalid"), + # Test with a dictionary (which is not a valid type for alpha) + (2, {"key": "value"}), + ], +) def test_werner_state_invalid(dim, alpha): """Test function works as expected for an invalid input.""" with pytest.raises(ValueError): diff --git a/toqito/states/tile.py b/toqito/states/tile.py index 541c11e8..2da2bd70 100644 --- a/toqito/states/tile.py +++ b/toqito/states/tile.py @@ -1,4 +1,5 @@ """Tile state.""" + import numpy as np from toqito.states import basis diff --git a/toqito/states/trine.py b/toqito/states/trine.py index 742576dd..ec0fc44b 100644 --- a/toqito/states/trine.py +++ b/toqito/states/trine.py @@ -1,4 +1,5 @@ """Trine states.""" + import numpy as np from toqito.states import basis @@ -39,6 +40,6 @@ def trine() -> list[np.ndarray]: e_0, e_1 = basis(2, 0), basis(2, 1) return [ e_0, - -1/2 * (e_0 + np.sqrt(3) * e_1), - -1/2 * (e_0 - np.sqrt(3) * e_1), + -1 / 2 * (e_0 + np.sqrt(3) * e_1), + -1 / 2 * (e_0 - np.sqrt(3) * e_1), ] diff --git a/toqito/states/w_state.py b/toqito/states/w_state.py index 12909edf..6cc87c0a 100644 --- a/toqito/states/w_state.py +++ b/toqito/states/w_state.py @@ -1,4 +1,5 @@ """W-state.""" + import numpy as np from scipy import sparse @@ -82,14 +83,11 @@ def w_state(num_qubits: int, coeff: list[int] = None) -> np.ndarray: if num_qubits < 2: raise ValueError("InvalidNumQubits: `num_qubits` must be at least 2.") if len(coeff) != num_qubits: - raise ValueError( - "InvalidCoeff: The variable `coeff` must be a vector " - "of length equal to `num_qubits`." - ) + raise ValueError("InvalidCoeff: The variable `coeff` must be a vector of length equal to `num_qubits`.") - ret_w_state = sparse.csr_matrix((2 ** num_qubits, 1)).toarray() + ret_w_state = sparse.csr_matrix((2**num_qubits, 1)).toarray() for i in range(num_qubits): - ret_w_state[2 ** i] = coeff[num_qubits - i - 1] + ret_w_state[2**i] = coeff[num_qubits - i - 1] return np.around(ret_w_state, 4) diff --git a/toqito/states/werner.py b/toqito/states/werner.py index b7e35000..6dab7bd1 100644 --- a/toqito/states/werner.py +++ b/toqito/states/werner.py @@ -1,4 +1,5 @@ """Werner state.""" + import itertools import numpy as np @@ -100,9 +101,7 @@ def werner(dim: int, alpha: float | list[float]) -> np.ndarray: if n_var == i + 1: break if n_var < i: - raise ValueError( - "InvalidAlpha: The `alpha` vector must contain p!-1 entries for some integer p > 1." - ) + raise ValueError("InvalidAlpha: The `alpha` vector must contain p!-1 entries for some integer p > 1.") # Done error checking and computing the number of parties # -- now compute the Werner state. @@ -111,9 +110,7 @@ def werner(dim: int, alpha: float | list[float]) -> np.ndarray: rho = np.identity(dim**n_var) for i in range(2, n_fac): - rho -= alpha[i - 1] * permutation_operator( - dim, sorted_perms[i - 1, :], False, True - ) + rho -= alpha[i - 1] * permutation_operator(dim, sorted_perms[i - 1, :], False, True) rho = rho / np.trace(rho) return rho