From 03d5d1cb702edaf18bcdfd493c1677b7f874fdba Mon Sep 17 00:00:00 2001 From: ErikQQY <2283984853@qq.com> Date: Mon, 29 May 2023 17:30:08 +0800 Subject: [PATCH 1/4] Refactor partial_trace and partial_transpose Signed-off-by: ErikQQY <2283984853@qq.com> --- tests/test_matrix_props/test_is_block_positive.py | 5 +++-- tests/test_state_props/test_is_separable.py | 6 +++--- toqito/channel_metrics/channel_fidelity.py | 4 ++-- toqito/channel_props/is_trace_preserving.py | 9 ++++++--- toqito/matrix_props/majorizes.py | 4 ++-- toqito/state_props/entanglement_of_formation.py | 4 ++-- toqito/state_props/has_symmetric_extension.py | 4 ++-- toqito/state_props/is_ppt.py | 5 +++-- toqito/state_props/is_separable.py | 10 +++++++--- toqito/state_props/log_negativity.py | 6 ++++-- toqito/state_props/negativity.py | 6 ++++-- 11 files changed, 38 insertions(+), 25 deletions(-) diff --git a/tests/test_matrix_props/test_is_block_positive.py b/tests/test_matrix_props/test_is_block_positive.py index b3891ea9b..982b518ae 100644 --- a/tests/test_matrix_props/test_is_block_positive.py +++ b/tests/test_matrix_props/test_is_block_positive.py @@ -3,9 +3,10 @@ import pytest from toqito.states import bell -from toqito.channels import choi, partial_transpose +from toqito.channels import choi from toqito.perms import swap, swap_operator from toqito.matrix_props import is_block_positive +from picos import partial_transpose @pytest.mark.parametrize("dim", [2, 3, 4]) @@ -36,7 +37,7 @@ def test_is_block_positive(): 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, 1)) + + 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]) np.testing.assert_equal(is_block_positive(mat), True) diff --git a/tests/test_state_props/test_is_separable.py b/tests/test_state_props/test_is_separable.py index b745a9ed1..98c5d3b21 100644 --- a/tests/test_state_props/test_is_separable.py +++ b/tests/test_state_props/test_is_separable.py @@ -2,7 +2,7 @@ import numpy as np from toqito.state_props.is_separable import is_separable -from toqito.channels import partial_trace +from picos import partial_trace from toqito.matrix_props import is_density from toqito.states import basis, bell, isotropic, tile from toqito.random import random_density_matrix @@ -53,10 +53,10 @@ def test_ppt_low_rank(): u, s, v_h = np.linalg.svd(rho) rho_cut = u[:, :m-1] @ np.diag(s[:m-1]) @ v_h[:m-1] rho_cut = rho_cut / np.trace(rho_cut) - pt_state_alice = partial_trace(rho_cut, 2, dim=[3, 2]) + pt_state_alice = partial_trace(rho_cut, [1], dim=[3, 2]) np.testing.assert_equal(is_density(rho_cut), True) - np.testing.assert_equal(is_density(pt_state_alice), True) + np.testing.assert_equal(is_density(np.array(pt_state_alice)), True) np.testing.assert_equal( np.linalg.matrix_rank(rho_cut) + np.linalg.matrix_rank(pt_state_alice) <= 2 * m * n - m - n + 2, True diff --git a/toqito/channel_metrics/channel_fidelity.py b/toqito/channel_metrics/channel_fidelity.py index d3aee161f..b45519aa0 100644 --- a/toqito/channel_metrics/channel_fidelity.py +++ b/toqito/channel_metrics/channel_fidelity.py @@ -2,7 +2,7 @@ import cvxpy import numpy as np -from toqito.channels import partial_trace +from picos import partial_trace def channel_fidelity(choi_1: np.ndarray, choi_2: np.ndarray) -> float: @@ -90,7 +90,7 @@ def channel_fidelity(choi_1: np.ndarray, choi_2: np.ndarray) -> float: constraints.append(cvxpy.bmat([[choi_1, q_var.H], [q_var, choi_2]]) >> 0) - constraints.append(lam * np.identity(dim) <= cvxpy.real(partial_trace(q_var, [2], [dim, dim]))) + constraints.append(lam * np.identity(dim) <= cvxpy.real(partial_trace(q_var, [1], [dim, dim]))) problem = cvxpy.Problem(objective, constraints) diff --git a/toqito/channel_props/is_trace_preserving.py b/toqito/channel_props/is_trace_preserving.py index b8481c9c8..a1e6932e4 100644 --- a/toqito/channel_props/is_trace_preserving.py +++ b/toqito/channel_props/is_trace_preserving.py @@ -4,7 +4,7 @@ import numpy as np from toqito.matrix_props import is_identity -from toqito.channels import partial_trace +from picos import partial_trace def is_trace_preserving( @@ -100,5 +100,8 @@ def is_trace_preserving( mat = k_l.conj().T @ k_r else: - mat = partial_trace(input_mat=phi, sys=sys, dim=dim) - return is_identity(mat, rtol=rtol, atol=atol) + if dim == None: + mat = partial_trace(phi, [sys-1]) + else: + mat = partial_trace(phi, [sys-1], dim) + return is_identity(np.array(mat), rtol=rtol, atol=atol) diff --git a/toqito/matrix_props/majorizes.py b/toqito/matrix_props/majorizes.py index c6859a083..cb1c307d1 100644 --- a/toqito/matrix_props/majorizes.py +++ b/toqito/matrix_props/majorizes.py @@ -34,11 +34,11 @@ def majorizes(a_var: np.ndarray | list[int], b_var: np.ndarray | list[int]) -> b >>> from toqito.matrix_props import majorizes >>> from toqito.states import max_entangled - >>> from toqito.channels import partial_trace + >>> from picos import partial_trace >>> >>> v_vec = max_entangled(3) >>> rho = v_vec * v_vec.conj().T - >>> majorizes(partial_trace(rho), rho) + >>> majorizes(partial_trace(rho, [1]), rho) False References diff --git a/toqito/state_props/entanglement_of_formation.py b/toqito/state_props/entanglement_of_formation.py index b165a1127..ec7c49150 100644 --- a/toqito/state_props/entanglement_of_formation.py +++ b/toqito/state_props/entanglement_of_formation.py @@ -77,7 +77,7 @@ def entanglement_of_formation(rho: np.ndarray, dim: list[int] | int = None) -> f raise ValueError( "Invalid dimension: Please provide local dimensions that match the size of `rho`." ) - + dim = np.int_(dim) # 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) @@ -88,7 +88,7 @@ def entanglement_of_formation(rho: np.ndarray, dim: list[int] | int = None) -> f # Start computing entanglement-of-formation. if min(dim_x, dim_y) == 1: rho = rho[:] - return von_neumann_entropy(partial_trace(rho * rho.conj().T, 2, dim)) + return von_neumann_entropy(partial_trace(rho * rho.conj().T, [1], dim)) # Case: :code:`rho` is a density matrix. if dim_x == dim_y: diff --git a/toqito/state_props/has_symmetric_extension.py b/toqito/state_props/has_symmetric_extension.py index 2d68eb2a8..7c6901e6f 100644 --- a/toqito/state_props/has_symmetric_extension.py +++ b/toqito/state_props/has_symmetric_extension.py @@ -2,7 +2,7 @@ from __future__ import annotations import numpy as np -from toqito.channels import partial_trace +from picos import partial_trace from toqito.matrix_props import is_positive_semidefinite from toqito.state_opt import symmetric_extension_hierarchy from toqito.state_props import is_ppt @@ -122,7 +122,7 @@ def has_symmetric_extension(rho: np.ndarray, level: int = 2, dim: np.ndarray | i # (2-copy, non-PPT) symmetric extension that is much faster to use than semidefinite # programming [CJKLZB14]_. if level == 2 and not ppt and dim_x == 2 and dim_y == 2: - return np.trace(np.linalg.matrix_power(partial_trace(rho, 1), 2)) >= np.trace( + return np.trace(np.linalg.matrix_power(partial_trace(rho, [0]), 2)) >= np.trace( np.linalg.matrix_power(rho, 2) ) - 4 * np.sqrt(np.linalg.det(rho)) diff --git a/toqito/state_props/is_ppt.py b/toqito/state_props/is_ppt.py index 7b39bfbfd..bf977b84d 100644 --- a/toqito/state_props/is_ppt.py +++ b/toqito/state_props/is_ppt.py @@ -82,9 +82,10 @@ def is_ppt( eps = np.finfo(float).eps sqrt_rho_dims = np.round(np.sqrt(list(mat.shape))) + sqrt_rho_dims = np.int_(sqrt_rho_dims) if dim is None: - dim = np.array([[sqrt_rho_dims[0], sqrt_rho_dims[0]], [sqrt_rho_dims[1], sqrt_rho_dims[1]]]) + dim = [[sqrt_rho_dims[0], sqrt_rho_dims[0]], [sqrt_rho_dims[1], sqrt_rho_dims[1]]] if tol is None: tol = np.sqrt(eps) - return is_positive_semidefinite(partial_transpose(mat, sys, dim), tol) + return is_positive_semidefinite(partial_transpose(mat, [sys-1], dim), tol) diff --git a/toqito/state_props/is_separable.py b/toqito/state_props/is_separable.py index ffd53d18e..3ba126201 100644 --- a/toqito/state_props/is_separable.py +++ b/toqito/state_props/is_separable.py @@ -3,13 +3,15 @@ import numpy as np -from toqito.channels import partial_trace, realignment +from toqito.channels import realignment from toqito.matrix_props import is_positive_semidefinite from toqito.state_props import is_ppt, in_separable_ball from toqito.state_props.has_symmetric_extension import has_symmetric_extension from toqito.state_metrics import trace_norm from toqito.perms import swap +from picos import partial_trace + def is_separable( state: np.ndarray, dim: None | int | list[int] = None, level: int = 2, tol: float = 1e-8) -> bool: @@ -86,9 +88,11 @@ def is_separable( if min_dim == 1: # Every positive semidefinite matrix is separable when one of the local dimensions is 1. return True + + dim = [int(x) for x in dim] - pt_state_alice = partial_trace(state, 2, dim) - pt_state_bob = partial_trace(state, 1, dim) + pt_state_alice = partial_trace(state, [1], dim) + pt_state_bob = partial_trace(state, [0], dim) # Check the PPT criterion. if not is_ppt(state, 2, dim, tol): diff --git a/toqito/state_props/log_negativity.py b/toqito/state_props/log_negativity.py index fff2e8754..2a656a804 100644 --- a/toqito/state_props/log_negativity.py +++ b/toqito/state_props/log_negativity.py @@ -4,7 +4,7 @@ import numpy as np from toqito.state_ops import pure_to_mixed -from toqito.channels import partial_transpose +from picos import partial_transpose def log_negativity(rho: np.ndarray, dim: list[int] | int = None) -> float: @@ -73,6 +73,8 @@ def log_negativity(rho: np.ndarray, dim: list[int] | int = None) -> float: "InvalidDim: Please provide local dimensions in the " "argument `dim` that match the size of `rho`." ) + + dim = [int(x) for x in dim] # Compute the log-negativity. - return np.log2(np.linalg.norm(partial_transpose(rho, 2, dim), ord="nuc")) + return np.log2(np.linalg.norm(partial_transpose(rho, [1], dim), ord="nuc")) diff --git a/toqito/state_props/negativity.py b/toqito/state_props/negativity.py index b82055de4..d90538564 100644 --- a/toqito/state_props/negativity.py +++ b/toqito/state_props/negativity.py @@ -4,7 +4,7 @@ import numpy as np from toqito.state_ops import pure_to_mixed -from toqito.channels import partial_transpose +from picos import partial_transpose def negativity(rho: np.ndarray, dim: list[int] | int = None) -> float: @@ -74,6 +74,8 @@ def negativity(rho: np.ndarray, dim: list[int] | int = None) -> float: "InvalidDim: Please provide local dimensions in the " "argument `dim` that match the size of `rho`." ) + + dim = [int(x) for x in dim] # Compute the negativity. - return (np.linalg.norm(partial_transpose(rho, 2, dim), ord="nuc") - 1) / 2 + return (np.linalg.norm(partial_transpose(rho, [1], dim), ord="nuc") - 1) / 2 From 75193716414c7b5ad875367ee88b75378cb99987 Mon Sep 17 00:00:00 2001 From: ErikQQY <2283984853@qq.com> Date: Thu, 1 Jun 2023 13:07:08 +0800 Subject: [PATCH 2/4] partail_trace done Signed-off-by: ErikQQY <2283984853@qq.com> --- tests/test_channels/test_partial_trace.py | 112 +++++++++--------- tests/test_matrix_props/test_majorizes.py | 4 +- toqito/channels/partial_trace.py | 48 +++++--- toqito/matrix_props/sk_norm.py | 4 +- toqito/nonlocal_games/quantum_hedging.py | 4 +- toqito/state_opt/optimal_clone.py | 1 + .../symmetric_extension_hierarchy.py | 2 +- .../state_props/entanglement_of_formation.py | 2 +- 8 files changed, 97 insertions(+), 80 deletions(-) diff --git a/tests/test_channels/test_partial_trace.py b/tests/test_channels/test_partial_trace.py index a021d6177..61244b5f3 100644 --- a/tests/test_channels/test_partial_trace.py +++ b/tests/test_channels/test_partial_trace.py @@ -34,7 +34,7 @@ def test_partial_trace_sys(): expected_res = np.array([[12, 14], [20, 22]]) - res = partial_trace(test_input_mat, 1) + res = partial_trace(test_input_mat, [0]) bool_mat = np.isclose(expected_res, res) np.testing.assert_equal(np.all(bool_mat), True) @@ -51,7 +51,7 @@ def test_partial_trace_sys_int_dim_int(): expected_res = np.array([[7, 11], [23, 27]]) - res = partial_trace(test_input_mat, 2, 2) + res = partial_trace(test_input_mat, [1]) bool_mat = np.isclose(expected_res, res) np.testing.assert_equal(np.all(bool_mat), True) @@ -68,7 +68,7 @@ def test_partial_trace_sys_int_dim_int_2(): expected_res = 34 - res = partial_trace(test_input_mat, 2, 1) + res = partial_trace(test_input_mat, [1], [1, 4]) bool_mat = np.isclose(expected_res, res) np.testing.assert_equal(np.all(bool_mat), True) @@ -78,8 +78,8 @@ def test_partial_trace_4_by_4(): """Test for 4-by-4 matrix.""" test_input_mat = np.arange(1, 17).reshape(4, 4) - pt_1 = partial_trace(test_input_mat, [1], [2, 2]) - pt_2 = partial_trace(test_input_mat, [2], [2, 2]) + pt_1 = partial_trace(test_input_mat, [0], [2, 2]) + pt_2 = partial_trace(test_input_mat, [1], [2, 2]) expected_pt_1 = np.array([[12, 14], [20, 22]]) bool_mat = np.isclose(expected_pt_1, pt_1) @@ -95,7 +95,7 @@ def test_partial_trace_8_by_8(): test_input_mat = np.arange(1, 65).reshape(8, 8) # Trace out first subsystem: - pt_1 = partial_trace(test_input_mat, [1], [2, 2, 2]) + pt_1 = partial_trace(test_input_mat, [0], [2, 2, 2]) expected_pt_1 = np.array( [[38, 40, 42, 44], [54, 56, 58, 60], [70, 72, 74, 76], [86, 88, 90, 92]] ) @@ -103,7 +103,7 @@ def test_partial_trace_8_by_8(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out second subsystem: - pt_2 = partial_trace(test_input_mat, [2], [2, 2, 2]) + pt_2 = partial_trace(test_input_mat, [1], [2, 2, 2]) expected_pt_2 = np.array( [[20, 22, 28, 30], [36, 38, 44, 46], [84, 86, 92, 94], [100, 102, 108, 110]] ) @@ -111,7 +111,7 @@ def test_partial_trace_8_by_8(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out third subsystem: - pt_3 = partial_trace(test_input_mat, [3], [2, 2, 2]) + pt_3 = partial_trace(test_input_mat, [2], [2, 2, 2]) expected_pt_3 = np.array( [[11, 15, 19, 23], [43, 47, 51, 55], [75, 79, 83, 87], [107, 111, 115, 119]] ) @@ -119,19 +119,19 @@ def test_partial_trace_8_by_8(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out first and second subsystem: - pt_1_2 = partial_trace(test_input_mat, [1, 2], [2, 2, 2]) + pt_1_2 = partial_trace(test_input_mat, [0, 1], [2, 2, 2]) expected_pt_1_2 = np.array([[112, 116], [144, 148]]) bool_mat = np.isclose(expected_pt_1_2, pt_1_2) np.testing.assert_equal(np.all(bool_mat), True) # Trace out first and third subsystem: - pt_1_3 = partial_trace(test_input_mat, [1, 3], [2, 2, 2]) + pt_1_3 = partial_trace(test_input_mat, [0, 2], [2, 2, 2]) expected_pt_1_3 = np.array([[94, 102], [158, 166]]) bool_mat = np.isclose(expected_pt_1_3, pt_1_3) np.testing.assert_equal(np.all(bool_mat), True) # Trace out second and third subsystem: - pt_2_3 = partial_trace(test_input_mat, [2, 3], [2, 2, 2]) + pt_2_3 = partial_trace(test_input_mat, [1, 2], [2, 2, 2]) expected_pt_2_3 = np.array([[58, 74], [186, 202]]) bool_mat = np.isclose(expected_pt_2_3, pt_2_3) np.testing.assert_equal(np.all(bool_mat), True) @@ -142,19 +142,19 @@ def test_partial_trace_6_by_6_subsystems_2_3(): test_input_mat = np.arange(1, 37).reshape(6, 6) # Trace out first subsystem: - pt_1 = partial_trace(test_input_mat, [1], [2, 3]) + pt_1 = partial_trace(test_input_mat, [0], [2, 3]) expected_pt_1 = np.array([[23, 25, 27], [35, 37, 39], [47, 49, 51]]) bool_mat = np.isclose(expected_pt_1, pt_1) np.testing.assert_equal(np.all(bool_mat), True) # Trace out second subsystem: - pt_2 = partial_trace(test_input_mat, [2], [2, 3]) + pt_2 = partial_trace(test_input_mat, [1], [2, 3]) expected_pt_2 = np.array([[24, 33], [78, 87]]) bool_mat = np.isclose(expected_pt_2, pt_2) np.testing.assert_equal(np.all(bool_mat), True) # Trace out first and second subsystem: - pt_1_2 = partial_trace(test_input_mat, [1, 2], [2, 3]) + pt_1_2 = partial_trace(test_input_mat, [0, 1], [2, 3]) expected_pt_1_2 = np.array([[111]]) bool_mat = np.isclose(expected_pt_1_2, pt_1_2) np.testing.assert_equal(np.all(bool_mat), True) @@ -165,19 +165,19 @@ def test_partial_trace_6_by_6_subsystems_3_2(): test_input_mat = np.arange(1, 37).reshape(6, 6) # Trace out first subsystem: - pt_1 = partial_trace(test_input_mat, [1], [3, 2]) + pt_1 = partial_trace(test_input_mat, [0], [3, 2]) expected_pt_1 = np.array([[45, 48], [63, 66]]) bool_mat = np.isclose(expected_pt_1, pt_1) np.testing.assert_equal(np.all(bool_mat), True) # Trace out second subsystem: - pt_2 = partial_trace(test_input_mat, [2], [3, 2]) + pt_2 = partial_trace(test_input_mat, [1], [3, 2]) expected_pt_2 = np.array([[9, 13, 17], [33, 37, 41], [57, 61, 65]]) bool_mat = np.isclose(expected_pt_2, pt_2) np.testing.assert_equal(np.all(bool_mat), True) # Trace out first and second subsystem: - pt_1_2 = partial_trace(test_input_mat, [1, 2], [3, 2]) + pt_1_2 = partial_trace(test_input_mat, [0, 1], [3, 2]) expected_pt_1_2 = np.array([[111]]) bool_mat = np.isclose(expected_pt_1_2, pt_1_2) np.testing.assert_equal(np.all(bool_mat), True) @@ -188,19 +188,19 @@ def test_partial_trace_9_by_9_subsystems_3_3(): test_input_mat = np.arange(1, 82).reshape(9, 9) # Partial trace on first subsystem: - pt_1 = partial_trace(test_input_mat, [1], [3, 3]) + pt_1 = partial_trace(test_input_mat, [0], [3, 3]) expected_pt_1 = np.array([[93, 96, 99], [120, 123, 126], [147, 150, 153]]) bool_mat = np.isclose(expected_pt_1, pt_1) np.testing.assert_equal(np.all(bool_mat), True) # Partial trace on second subsystem: - pt_2 = partial_trace(test_input_mat, [2], [3, 3]) + pt_2 = partial_trace(test_input_mat, [1], [3, 3]) expected_pt_2 = np.array([[33, 42, 51], [114, 123, 132], [195, 204, 213]]) bool_mat = np.isclose(expected_pt_2, pt_2) np.testing.assert_equal(np.all(bool_mat), True) # Partial trace on first and second subsystems: - pt_1_2 = partial_trace(test_input_mat, [1, 2], [3, 3]) + pt_1_2 = partial_trace(test_input_mat, [0, 1], [3, 3]) expected_pt_1_2 = np.array([[369]]) bool_mat = np.isclose(expected_pt_1_2, pt_1_2) np.testing.assert_equal(np.all(bool_mat), True) @@ -211,7 +211,7 @@ def test_partial_trace_16_by_16_subsystems_2_2_2_2(): test_input_mat = np.arange(1, 257).reshape(16, 16) # Trace out first subsystem: - pt_1 = partial_trace(test_input_mat, [1], [2, 2, 2, 2]) + pt_1 = partial_trace(test_input_mat, [0], [2, 2, 2, 2]) expected_pt_1 = np.array( [ [138, 140, 142, 144, 146, 148, 150, 152], @@ -228,7 +228,7 @@ def test_partial_trace_16_by_16_subsystems_2_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out second subsystem: - pt_2 = partial_trace(test_input_mat, [2], [2, 2, 2, 2]) + pt_2 = partial_trace(test_input_mat, [1], [2, 2, 2, 2]) expected_pt_2 = np.array( [ [70, 72, 74, 76, 86, 88, 90, 92], @@ -245,7 +245,7 @@ def test_partial_trace_16_by_16_subsystems_2_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out third subsystem: - pt_3 = partial_trace(test_input_mat, [3], [2, 2, 2, 2]) + pt_3 = partial_trace(test_input_mat, [2], [2, 2, 2, 2]) expected_pt_3 = np.array( [ [36, 38, 44, 46, 52, 54, 60, 62], @@ -262,7 +262,7 @@ def test_partial_trace_16_by_16_subsystems_2_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out fourth subsystem: - pt_4 = partial_trace(test_input_mat, [4], [2, 2, 2, 2]) + pt_4 = partial_trace(test_input_mat, [3], [2, 2, 2, 2]) expected_pt_4 = np.array( [ [19, 23, 27, 31, 35, 39, 43, 47], @@ -279,7 +279,7 @@ def test_partial_trace_16_by_16_subsystems_2_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out first and second subsystem: - pt_1_2 = partial_trace(test_input_mat, [1, 2], [2, 2, 2, 2]) + pt_1_2 = partial_trace(test_input_mat, [0, 1], [2, 2, 2, 2]) expected_pt_1_2 = np.array( [ [412, 416, 420, 424], @@ -293,7 +293,7 @@ def test_partial_trace_16_by_16_subsystems_2_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out first and third subsystem: - pt_1_3 = partial_trace(test_input_mat, [1, 3], [2, 2, 2, 2]) + pt_1_3 = partial_trace(test_input_mat, [0, 2], [2, 2, 2, 2]) expected_pt_1_3 = np.array( [ [344, 348, 360, 364], @@ -306,7 +306,7 @@ def test_partial_trace_16_by_16_subsystems_2_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out first and fourth subsystem: - pt_1_4 = partial_trace(test_input_mat, [1, 4], [2, 2, 2, 2]) + pt_1_4 = partial_trace(test_input_mat, [0, 3], [2, 2, 2, 2]) expected_pt_1_4 = np.array( [[310, 318, 326, 334], [438, 446, 454, 462], [566, 574, 582, 590], [694, 702, 710, 718]] @@ -315,7 +315,7 @@ def test_partial_trace_16_by_16_subsystems_2_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out second and third subsystem: - pt_2_3 = partial_trace(test_input_mat, [2, 3], [2, 2, 2, 2]) + pt_2_3 = partial_trace(test_input_mat, [1, 2], [2, 2, 2, 2]) expected_pt_2_3 = np.array( [[208, 212, 240, 244], [272, 276, 304, 308], [720, 724, 752, 756], [784, 788, 816, 820]] ) @@ -323,7 +323,7 @@ def test_partial_trace_16_by_16_subsystems_2_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out second and fourth subsystem: - pt_2_4 = partial_trace(test_input_mat, [2, 4], [2, 2, 2, 2]) + pt_2_4 = partial_trace(test_input_mat, [1, 3], [2, 2, 2, 2]) expected_pt_2_4 = np.array( [[174, 182, 206, 214], [302, 310, 334, 342], [686, 694, 718, 726], [814, 822, 846, 854]] ) @@ -331,7 +331,7 @@ def test_partial_trace_16_by_16_subsystems_2_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out third and fourth subsystem: - pt_3_4 = partial_trace(test_input_mat, [3, 4], [2, 2, 2, 2]) + pt_3_4 = partial_trace(test_input_mat, [2, 3], [2, 2, 2, 2]) expected_pt_3_4 = np.array( [[106, 122, 138, 154], [362, 378, 394, 410], [618, 634, 650, 666], [874, 890, 906, 922]] ) @@ -344,7 +344,7 @@ def test_partial_trace_16_by_16_subsystems_2_2_4(): test_input_mat = np.arange(1, 257).reshape(16, 16) # Trace out first subsystem: - pt_1 = partial_trace(test_input_mat, [1], [2, 2, 4]) + pt_1 = partial_trace(test_input_mat, [0], [2, 2, 4]) expected_pt_1 = np.array( [ [138, 140, 142, 144, 146, 148, 150, 152], @@ -361,7 +361,7 @@ def test_partial_trace_16_by_16_subsystems_2_2_4(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out second subsystem: - pt_2 = partial_trace(test_input_mat, [2], [2, 2, 4]) + pt_2 = partial_trace(test_input_mat, [1], [2, 2, 4]) expected_pt_2 = np.array( [ [70, 72, 74, 76, 86, 88, 90, 92], @@ -378,7 +378,7 @@ def test_partial_trace_16_by_16_subsystems_2_2_4(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out third subsystem: - pt_3 = partial_trace(test_input_mat, [3], [2, 2, 4]) + pt_3 = partial_trace(test_input_mat, [2], [2, 2, 4]) expected_pt_3 = np.array( [[106, 122, 138, 154], [362, 378, 394, 410], [618, 634, 650, 666], [874, 890, 906, 922]] ) @@ -386,7 +386,7 @@ def test_partial_trace_16_by_16_subsystems_2_2_4(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out first and second subsystem: - pt_1_2 = partial_trace(test_input_mat, [1, 2], [2, 2, 4]) + pt_1_2 = partial_trace(test_input_mat, [0, 1], [2, 2, 4]) expected_pt_1_2 = np.array( [ [412, 416, 420, 424], @@ -399,13 +399,13 @@ def test_partial_trace_16_by_16_subsystems_2_2_4(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out first and third subsystem: - pt_1_3 = partial_trace(test_input_mat, [1, 3], [2, 2, 4]) + pt_1_3 = partial_trace(test_input_mat, [0, 2], [2, 2, 4]) expected_pt_1_3 = np.array([[756, 788], [1268, 1300]]) bool_mat = np.isclose(expected_pt_1_3, pt_1_3) np.testing.assert_equal(np.all(bool_mat), True) # Trace out second and third subsystem: - pt_2_3 = partial_trace(test_input_mat, [2, 3], [2, 2, 4]) + pt_2_3 = partial_trace(test_input_mat, [1, 2], [2, 2, 4]) expected_pt_2_3 = np.array([[484, 548], [1508, 1572]]) bool_mat = np.isclose(expected_pt_2_3, pt_2_3) np.testing.assert_equal(np.all(bool_mat), True) @@ -416,7 +416,7 @@ def test_partial_trace_16_by_16_subsystems_4_2_2(): test_input_mat = np.arange(1, 257).reshape(16, 16) # Trace out the first subsystem: - pt_1 = partial_trace(test_input_mat, [1], [4, 2, 2]) + pt_1 = partial_trace(test_input_mat, [0], [4, 2, 2]) expected_pt_1 = np.array( [ [412, 416, 420, 424], @@ -429,7 +429,7 @@ def test_partial_trace_16_by_16_subsystems_4_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out the second subsystem: - pt_2 = partial_trace(test_input_mat, [2], [4, 2, 2]) + pt_2 = partial_trace(test_input_mat, [1], [4, 2, 2]) expected_pt_2 = np.array( [ [36, 38, 44, 46, 52, 54, 60, 62], @@ -446,7 +446,7 @@ def test_partial_trace_16_by_16_subsystems_4_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out the third subsystem: - pt_3 = partial_trace(test_input_mat, [3], [4, 2, 2]) + pt_3 = partial_trace(test_input_mat, [2], [4, 2, 2]) expected_pt_3 = np.array( [ [19, 23, 27, 31, 35, 39, 43, 47], @@ -463,19 +463,19 @@ def test_partial_trace_16_by_16_subsystems_4_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out the first and second subsystems: - pt_1_2 = partial_trace(test_input_mat, [1, 2], [4, 2, 2]) + pt_1_2 = partial_trace(test_input_mat, [0, 1], [4, 2, 2]) expected_pt_1_2 = np.array([[960, 968], [1088, 1096]]) bool_mat = np.isclose(expected_pt_1_2, pt_1_2) np.testing.assert_equal(np.all(bool_mat), True) # Trace out the first and third subsystems: - pt_1_3 = partial_trace(test_input_mat, [1, 3], [4, 2, 2]) + pt_1_3 = partial_trace(test_input_mat, [0, 2], [4, 2, 2]) expected_pt_1_3 = np.array([[892, 908], [1148, 1164]]) bool_mat = np.isclose(expected_pt_1_3, pt_1_3) np.testing.assert_equal(np.all(bool_mat), True) # Trace out the second and third subsystems: - pt_2_3 = partial_trace(test_input_mat, [2, 3], [4, 2, 2]) + pt_2_3 = partial_trace(test_input_mat, [1, 2], [4, 2, 2]) expected_pt_2_3 = np.array( [[106, 122, 138, 154], [362, 378, 394, 410], [618, 634, 650, 666], [874, 890, 906, 922]] ) @@ -488,7 +488,7 @@ def test_partial_trace_16_by_16_subsystems_4_4(): test_input_mat = np.arange(1, 257).reshape(16, 16) # Trace out first subsystem: - pt_1 = partial_trace(test_input_mat, [1], [4, 4]) + pt_1 = partial_trace(test_input_mat, [0], [4, 4]) expected_pt_1 = np.array( [[412, 416, 420, 424], [476, 480, 484, 488], [540, 544, 548, 552], [604, 608, 612, 616]] ) @@ -496,7 +496,7 @@ def test_partial_trace_16_by_16_subsystems_4_4(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out second subsystem: - pt_2 = partial_trace(test_input_mat, [2], [4, 4]) + pt_2 = partial_trace(test_input_mat, [1], [4, 4]) expected_pt_2 = np.array( [[106, 122, 138, 154], [362, 378, 394, 410], [618, 634, 650, 666], [874, 890, 906, 922]] ) @@ -521,7 +521,7 @@ def test_partial_trace_64_by_64_subsystems_4_4_2_2(): # Trace out second and fourth subsystem: # Trace out third and fourth subsystem: - pt_3_4 = partial_trace(test_input_mat, [3, 4], [4, 4, 2, 2]) + pt_3_4 = partial_trace(test_input_mat, [2, 3], [4, 4, 2, 2]) expected_pt_3_4 = np.array( [ [394, 410, 426, 442, 458, 474, 490, 506, 522, 538, 554, 570, 586, 602, 618, 634], @@ -801,13 +801,13 @@ def test_partial_trace_64_by_64_subsystems_4_4_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out first, second, and third subsystem: - pt_1_2_3 = partial_trace(test_input_mat, [1, 2, 3], [4, 4, 2, 2]) + pt_1_2_3 = partial_trace(test_input_mat, [0, 1, 2], [4, 4, 2, 2]) expected_pt_1_2_3 = np.array([[64512, 64544], [66560, 66592]]) bool_mat = np.isclose(expected_pt_1_2_3, pt_1_2_3) np.testing.assert_equal(np.all(bool_mat), True) # Trace out first, third, and fourth subsystem: - pt_1_3_4 = partial_trace(test_input_mat, [1, 3, 4], [4, 4, 2, 2]) + pt_1_3_4 = partial_trace(test_input_mat, [0, 2, 3], [4, 4, 2, 2]) expected_pt_1_3_4 = np.array( [ [26536, 26600, 26664, 26728], @@ -820,7 +820,7 @@ def test_partial_trace_64_by_64_subsystems_4_4_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out first, second, third, and fourth subsystem: - pt_1_2_3_4 = partial_trace(test_input_mat, [1, 2, 3, 4], [4, 4, 2, 2]) + pt_1_2_3_4 = partial_trace(test_input_mat, [0, 1, 2, 3], [4, 4, 2, 2]) expected_pt_1_2_3_4 = np.array([[131104]]) bool_mat = np.isclose(expected_pt_1_2_3_4, pt_1_2_3_4) np.testing.assert_equal(np.all(bool_mat), True) @@ -831,7 +831,7 @@ def test_partial_trace_64_by_64_subsystems_4_4_4(): test_input_mat = np.arange(1, 4097).reshape(64, 64) # Trace out first subsystem: - pt_1 = partial_trace(test_input_mat, [1], [4, 4, 4]) + pt_1 = partial_trace(test_input_mat, [0], [4, 4, 4]) expected_pt_1 = np.array( [ [ @@ -1132,7 +1132,7 @@ def test_partial_trace_64_by_64_subsystems_4_4_4(): # Trace out third subsystem: # Trace out first and second subsystem: - pt_1_2 = partial_trace(test_input_mat, [1, 2], [4, 4, 4]) + pt_1_2 = partial_trace(test_input_mat, [0, 1], [4, 4, 4]) expected_pt_1_2 = np.array( [ [31216, 31232, 31248, 31264], @@ -1145,7 +1145,7 @@ def test_partial_trace_64_by_64_subsystems_4_4_4(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out first and third subsystem: - pt_1_3 = partial_trace(test_input_mat, [1, 3], [4, 4, 4]) + pt_1_3 = partial_trace(test_input_mat, [0, 2], [4, 4, 4]) expected_pt_1_3 = np.array( [ [26536, 26600, 26664, 26728], @@ -1158,7 +1158,7 @@ def test_partial_trace_64_by_64_subsystems_4_4_4(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out second and third subsystem: - pt_2_3 = partial_trace(test_input_mat, [2, 3], [4, 4, 4]) + pt_2_3 = partial_trace(test_input_mat, [1, 2], [4, 4, 4]) expected_pt_2_3 = np.array( [ [7816, 8072, 8328, 8584], @@ -1171,7 +1171,7 @@ def test_partial_trace_64_by_64_subsystems_4_4_4(): np.testing.assert_equal(np.all(bool_mat), True) # Trace out first, second, and third subsystems: - pt_1_2_3 = partial_trace(test_input_mat, [1, 2, 3], [4, 4, 4]) + pt_1_2_3 = partial_trace(test_input_mat, [0, 1, 2], [4, 4, 4]) expected_pt_1_2_3 = np.array([[131104]]) bool_mat = np.isclose(expected_pt_1_2_3, pt_1_2_3) @@ -1180,16 +1180,16 @@ def test_partial_trace_64_by_64_subsystems_4_4_4(): def test_partial_trace_invalid_sys_arg(): """The `sys` argument must be either a list or int.""" - with np.testing.assert_raises(ValueError): + with np.testing.assert_raises(IndexError): rho = np.array([[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0], [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]]) partial_trace(rho, "invalid_input") def test_partial_trace_non_square_matrix_dim_2(): """Matrix must be square for partial trace.""" - with np.testing.assert_raises(ValueError): + with np.testing.assert_raises(TypeError): rho = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]) - partial_trace(rho, 2, [2]) + partial_trace(rho, [1], [2]) def test_partial_trace_cvxpy(): diff --git a/tests/test_matrix_props/test_majorizes.py b/tests/test_matrix_props/test_majorizes.py index 58a02c744..92343158b 100644 --- a/tests/test_matrix_props/test_majorizes.py +++ b/tests/test_matrix_props/test_majorizes.py @@ -15,14 +15,14 @@ def test_majorizes_max_entangled(): """Test that max entangled partial trace returns False.""" v_vec = max_entangled(3) rho = v_vec * v_vec.conj().T - np.testing.assert_equal(majorizes(partial_trace(rho), rho), False) + np.testing.assert_equal(majorizes(partial_trace(rho, [1], [3, 3]), rho), False) def test_majorizes_max_entangled_flip(): """Test that max entangled partial trace returns True (flipped args).""" v_vec = max_entangled(3) rho = v_vec * v_vec.conj().T - np.testing.assert_equal(majorizes(rho, partial_trace(rho)), True) + np.testing.assert_equal(majorizes(rho, partial_trace(rho, [1], [3, 3])), True) if __name__ == "__main__": diff --git a/toqito/channels/partial_trace.py b/toqito/channels/partial_trace.py index 3bdb38e48..b15f7840f 100644 --- a/toqito/channels/partial_trace.py +++ b/toqito/channels/partial_trace.py @@ -2,6 +2,7 @@ from __future__ import annotations import numpy as np +import picos as pc from cvxpy.expressions.expression import Expression from cvxpy.expressions.variable import Variable @@ -11,8 +12,8 @@ def partial_trace( input_mat: np.ndarray | Variable, - sys: int | list[int] = 2, - dim: int | list[int] = None, + sys: list[int] = [1], + dim: list[int] = [2, 2], ) -> np.ndarray | Expression: r""" Compute the partial trace of a matrix [WikPtrace]_. @@ -78,7 +79,7 @@ def partial_trace( >>> test_input_mat = np.array( >>> [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]] >>> ) - >>> partial_trace(test_input_mat, 1) + >>> partial_trace(test_input_mat, [0]) [[12, 14], [20, 22]] @@ -111,11 +112,11 @@ def partial_trace( >>> from toqito.channels import partial_trace >>> import numpy as np - >>> partial_trace(test_input_mat, [1, 3], [2, 2, 2, 2]) - [[344, 348, 360, 364], - [408, 412, 424, 428], - [600, 604, 616, 620], - [664, 668, 680, 684]]) + >>> partial_trace(test_input_mat, [0, 2], [2, 2, 2, 2]) + [ 3.44e+02 3.48e+02 3.60e+02 3.64e+02] + [ 4.08e+02 4.12e+02 4.24e+02 4.28e+02] + [ 6.00e+02 6.04e+02 6.16e+02 6.20e+02] + [ 6.64e+02 6.68e+02 6.80e+02 6.84e+02] References ========== @@ -124,7 +125,7 @@ def partial_trace( :raises ValueError: If matrix dimension is not equal to the number of subsystems. :param input_mat: A square matrix. - :param sys: Scalar or vector specifying the size of the subsystems. + :param sys: Vector specifying the size of the subsystems. :param dim: Dimension of the subsystems. If :code:`None`, all dimensions are assumed to be equal. :return: The partial trace of matrix :code:`input_mat`. @@ -132,10 +133,26 @@ def partial_trace( # If the input matrix is a CVX variable for an SDP, we convert it to a numpy array, # perform the partial trace, and convert it back to a CVX variable. if isinstance(input_mat, Variable): - rho_np = expr_as_np_array(input_mat) - traced_rho = partial_trace(rho_np, sys, dim) - traced_rho = np_array_as_expr(traced_rho) - return traced_rho + if isinstance(sys, (list, np.ndarray)): + if len(sys) == 1: + pt_mat = partial_trace_for_cvxpy_variable(input_mat, sys[0]+1, dim) + return pt_mat + else: + pt_mat = partial_trace_for_cvxpy_variable(input_mat, np.array(sys)+1, dim) + return pt_mat + else: + pt_mat = partial_trace_for_cvxpy_variable(input_mat, sys+1, dim) + + pt_mat = pc.partial_trace(input_mat, sys, dim) + return pt_mat + +def partial_trace_for_cvxpy_variable( + input_mat: np.ndarray | Variable, + sys: int | list[int] = 2, + dim: int | list[int] = None, +) -> np.ndarray | Expression: + + input_mat = expr_as_np_array(input_mat) if dim is None: dim = np.array([np.round(np.sqrt(len(input_mat)))]) @@ -157,7 +174,7 @@ def partial_trace( num_sys = 2 prod_dim = np.prod(dim) - if isinstance(sys, list): + if isinstance(sys, (list, np.ndarray)): if len(sys) == 1: prod_dim_sys = np.prod(dim[sys[0] - 1]) else: @@ -200,5 +217,6 @@ def partial_trace( :, :, list(range(0, int(sub_sys_vec[0] ** 2), int(sub_sys_vec[0] + 1))) ] pt_mat = np.sum(pt_mat, axis=2) + traced_rho = np_array_as_expr(pt_mat) - return pt_mat + return traced_rho diff --git a/toqito/matrix_props/sk_norm.py b/toqito/matrix_props/sk_norm.py index d0b174fcf..93780754e 100644 --- a/toqito/matrix_props/sk_norm.py +++ b/toqito/matrix_props/sk_norm.py @@ -238,7 +238,7 @@ def sk_operator_norm( if k == 1: constraints.append(partial_transpose(rho, 2, dim) >> 0) else: - constraints.append(k * cvxpy.kron(partial_trace(rho, 2, dim), np.eye(dim[1])) >> rho) + constraints.append(k * cvxpy.kron(partial_trace(rho, [1], dim), np.eye(dim[1])) >> rho) problem = cvxpy.Problem(objective, constraints) cvx_optval = problem.solve() @@ -276,7 +276,7 @@ def sk_operator_norm( 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(3, j + 2)), sym_dim)) + cvxpy.trace(mat @ partial_trace(rho, list(range(2, j + 1)), sym_dim)) ) ) diff --git a/toqito/nonlocal_games/quantum_hedging.py b/toqito/nonlocal_games/quantum_hedging.py index ef930606e..dc13fddf4 100644 --- a/toqito/nonlocal_games/quantum_hedging.py +++ b/toqito/nonlocal_games/quantum_hedging.py @@ -80,9 +80,7 @@ def __init__(self, q_a: np.ndarray, num_reps: int) -> None: self._q_a = q_a self._num_reps = num_reps - self._sys = list(range(1, 2 * self._num_reps, 2)) - if len(self._sys) == 1: - self._sys = self._sys[0] + self._sys = list(range(0, 2 * self._num_reps-1, 2)) self._dim = 2 * np.ones((1, 2 * self._num_reps)).astype(int).flatten() self._dim = self._dim.tolist() diff --git a/toqito/state_opt/optimal_clone.py b/toqito/state_opt/optimal_clone.py index 3c6ed0f6b..370890162 100644 --- a/toqito/state_opt/optimal_clone.py +++ b/toqito/state_opt/optimal_clone.py @@ -160,6 +160,7 @@ def primal_problem(q_a: np.ndarray, pperm: np.ndarray, num_reps: int) -> float: sys = list(range(1, num_spaces * num_reps)) sys = [elem for elem in sys if elem % num_spaces != 0] + sys = [elem-1 for elem in sys] # The dimension of each subsystem is assumed to be of dimension 2. dim = 2 * np.ones((1, num_spaces * num_reps)).astype(int).flatten() diff --git a/toqito/state_opt/symmetric_extension_hierarchy.py b/toqito/state_opt/symmetric_extension_hierarchy.py index f95d98d63..e618652d4 100644 --- a/toqito/state_opt/symmetric_extension_hierarchy.py +++ b/toqito/state_opt/symmetric_extension_hierarchy.py @@ -177,7 +177,7 @@ def symmetric_extension_hierarchy( dim_list = [dim_x] + [dim_y] * level # The `sys_list` variable contains the numbering pertaining to the symmetrically extended # spaces. - sys_list = list(range(3, 3 + level - 1)) + sys_list = list(range(2, 2 + level - 1)) sym = symmetric_projection(dim_y, level) dim_xyy = np.prod(dim_list) diff --git a/toqito/state_props/entanglement_of_formation.py b/toqito/state_props/entanglement_of_formation.py index ec7c49150..620013fd2 100644 --- a/toqito/state_props/entanglement_of_formation.py +++ b/toqito/state_props/entanglement_of_formation.py @@ -77,7 +77,6 @@ def entanglement_of_formation(rho: np.ndarray, dim: list[int] | int = None) -> f raise ValueError( "Invalid dimension: Please provide local dimensions that match the size of `rho`." ) - dim = np.int_(dim) # 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) @@ -88,6 +87,7 @@ def entanglement_of_formation(rho: np.ndarray, dim: list[int] | int = None) -> f # Start computing entanglement-of-formation. if min(dim_x, dim_y) == 1: rho = rho[:] + dim = [x for x in dim] return von_neumann_entropy(partial_trace(rho * rho.conj().T, [1], dim)) # Case: :code:`rho` is a density matrix. From 7fda5b70f25c46e7dadbca694c791a0f9bc16ece Mon Sep 17 00:00:00 2001 From: ErikQQY <2283984853@qq.com> Date: Sat, 3 Jun 2023 11:06:57 +0800 Subject: [PATCH 3/4] Done refactoring Signed-off-by: ErikQQY <2283984853@qq.com> --- tests/test_channels/test_partial_trace.py | 4 +- tests/test_channels/test_partial_transpose.py | 86 ++++++++----------- toqito/channels/partial_trace.py | 60 +++++-------- toqito/channels/partial_transpose.py | 15 ++-- toqito/channels/realignment.py | 3 +- toqito/matrix_props/sk_norm.py | 4 +- .../symmetric_extension_hierarchy.py | 5 +- toqito/state_props/has_symmetric_extension.py | 2 + 8 files changed, 76 insertions(+), 103 deletions(-) diff --git a/tests/test_channels/test_partial_trace.py b/tests/test_channels/test_partial_trace.py index 61244b5f3..51a24e71b 100644 --- a/tests/test_channels/test_partial_trace.py +++ b/tests/test_channels/test_partial_trace.py @@ -1180,14 +1180,14 @@ def test_partial_trace_64_by_64_subsystems_4_4_4(): def test_partial_trace_invalid_sys_arg(): """The `sys` argument must be either a list or int.""" - with np.testing.assert_raises(IndexError): + with np.testing.assert_raises(ValueError): rho = np.array([[1 / 2, 0, 0, 1 / 2], [0, 0, 0, 0], [0, 0, 0, 0], [1 / 2, 0, 0, 1 / 2]]) partial_trace(rho, "invalid_input") def test_partial_trace_non_square_matrix_dim_2(): """Matrix must be square for partial trace.""" - with np.testing.assert_raises(TypeError): + with np.testing.assert_raises(ValueError): rho = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]) partial_trace(rho, [1], [2]) diff --git a/tests/test_channels/test_partial_transpose.py b/tests/test_channels/test_partial_transpose.py index 166e2b741..a1b1381f9 100644 --- a/tests/test_channels/test_partial_transpose.py +++ b/tests/test_channels/test_partial_transpose.py @@ -13,19 +13,19 @@ def test_partial_transpose_bipartite(): rho = np.arange(16).reshape(4, 4) # Partial transpose of first subsystem: - res = partial_transpose(rho, [1], [2, 2]) + res = partial_transpose(rho, [0], [2, 2]) expected_res = np.array([[0, 1, 8, 9], [4, 5, 12, 13], [2, 3, 10, 11], [6, 7, 14, 15]]) bool_mat = np.isclose(expected_res, res) np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose of second subsystem: - res = partial_transpose(rho, [2], [2, 2]) + res = partial_transpose(rho, [1], [2, 2]) expected_res = np.array([[0, 4, 2, 6], [1, 5, 3, 7], [8, 12, 10, 14], [9, 13, 11, 15]]) bool_mat = np.isclose(expected_res, res) np.testing.assert_equal(np.all(bool_mat), True) # Full transpose: - res = partial_transpose(rho, [1, 2], [2, 2]) + res = partial_transpose(rho, [0, 1], [2, 2]) expected_res = np.array([[0, 4, 8, 12], [1, 5, 9, 13], [2, 6, 10, 14], [3, 7, 11, 15]]) bool_mat = np.isclose(expected_res, res) np.testing.assert_equal(np.all(bool_mat), True) @@ -59,7 +59,7 @@ def test_partial_transpose_sys(): expected_res = np.array([[1, 2, 9, 10], [5, 6, 13, 14], [3, 4, 11, 12], [7, 8, 15, 16]]) - res = partial_transpose(test_input_mat, 1) + res = partial_transpose(test_input_mat, [0]) bool_mat = np.isclose(res, expected_res) np.testing.assert_equal(np.all(bool_mat), True) @@ -71,7 +71,7 @@ def test_partial_transpose_sys_vec(): expected_res = np.array([[1, 5, 9, 13], [2, 6, 10, 14], [3, 7, 11, 15], [4, 8, 12, 16]]) - res = partial_transpose(test_input_mat, [1, 2]) + res = partial_transpose(test_input_mat, [0, 1]) bool_mat = np.isclose(res, expected_res) np.testing.assert_equal(np.all(bool_mat), True) @@ -83,7 +83,7 @@ def test_partial_transpose_sys_vec_dim_vec(): expected_res = np.array([[1, 5, 9, 13], [2, 6, 10, 14], [3, 7, 11, 15], [4, 8, 12, 16]]) - res = partial_transpose(test_input_mat, [1, 2], [2, 2]) + res = partial_transpose(test_input_mat, [0, 1], [2, 2]) bool_mat = np.isclose(res, expected_res) np.testing.assert_equal(np.all(bool_mat), True) @@ -97,7 +97,7 @@ def test_partial_transpose_norm_diff(): in the standard transpose of the matrix. """ test_input_mat = np.arange(1, 17).reshape(4, 4) - res = np.linalg.norm(partial_transpose(test_input_mat, [1, 2]) - test_input_mat.conj().T) + res = np.linalg.norm(partial_transpose(test_input_mat, [0, 1]) - test_input_mat.conj().T) expected_res = 0 np.testing.assert_equal(np.isclose(res, expected_res), True) @@ -108,7 +108,7 @@ def test_partial_transpose_8_by_8_subsystems_2_2_2(): test_input_mat = np.arange(1, 65).reshape(8, 8) # Partial transpose on first subsystem: - pt_1 = partial_transpose(test_input_mat, [1], [2, 2, 2]) + pt_1 = partial_transpose(test_input_mat, [0], [2, 2, 2]) expected_pt_1 = np.array( [ [1, 2, 3, 4, 33, 34, 35, 36], @@ -125,7 +125,7 @@ def test_partial_transpose_8_by_8_subsystems_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose on second subsystem: - pt_2 = partial_transpose(test_input_mat, [2], [2, 2, 2]) + pt_2 = partial_transpose(test_input_mat, [1], [2, 2, 2]) expected_pt_2 = np.array( [ [1, 2, 17, 18, 5, 6, 21, 22], @@ -142,7 +142,7 @@ def test_partial_transpose_8_by_8_subsystems_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose on third subsystem: - pt_3 = partial_transpose(test_input_mat, [3], [2, 2, 2]) + pt_3 = partial_transpose(test_input_mat, [2], [2, 2, 2]) expected_pt_3 = np.array( [ [1, 9, 3, 11, 5, 13, 7, 15], @@ -159,7 +159,7 @@ def test_partial_transpose_8_by_8_subsystems_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose on first and second subsystem: - pt_1_2 = partial_transpose(test_input_mat, [1, 2], [2, 2, 2]) + pt_1_2 = partial_transpose(test_input_mat, [0, 1], [2, 2, 2]) expected_pt_1_2 = np.array( [ [1, 2, 17, 18, 33, 34, 49, 50], @@ -176,7 +176,7 @@ def test_partial_transpose_8_by_8_subsystems_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose on first and third subsystem: - pt_1_3 = partial_transpose(test_input_mat, [1, 3], [2, 2, 2]) + pt_1_3 = partial_transpose(test_input_mat, [0, 2], [2, 2, 2]) expected_pt_1_3 = np.array( [ [1, 9, 3, 11, 33, 41, 35, 43], @@ -193,7 +193,7 @@ def test_partial_transpose_8_by_8_subsystems_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose on second and third subsystem: - pt_2_3 = partial_transpose(test_input_mat, [2, 3], [2, 2, 2]) + pt_2_3 = partial_transpose(test_input_mat, [1, 2], [2, 2, 2]) expected_pt_2_3 = np.array( [ [1, 9, 17, 25, 5, 13, 21, 29], @@ -215,7 +215,7 @@ def test_partial_transpose_8_by_8_subsystems_2_4(): test_input_mat = np.arange(1, 65).reshape(8, 8) # Partial transpose on the first subsystem: - pt_1 = partial_transpose(test_input_mat, [1], [2, 4]) + pt_1 = partial_transpose(test_input_mat, [0], [2, 4]) expected_pt_1 = np.array( [ [1, 2, 3, 4, 33, 34, 35, 36], @@ -232,7 +232,7 @@ def test_partial_transpose_8_by_8_subsystems_2_4(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose on the second subsystem: - pt_2 = partial_transpose(test_input_mat, [2], [2, 4]) + pt_2 = partial_transpose(test_input_mat, [1], [2, 4]) expected_pt_2 = np.array( [ [1, 9, 17, 25, 5, 13, 21, 29], @@ -249,7 +249,7 @@ def test_partial_transpose_8_by_8_subsystems_2_4(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose on the first and second subsystem: - pt_1_2 = partial_transpose(test_input_mat, [1, 2], [2, 4]) + pt_1_2 = partial_transpose(test_input_mat, [0, 1], [2, 4]) expected_pt_1_2 = np.array( [ [1, 9, 17, 25, 33, 41, 49, 57], @@ -271,7 +271,7 @@ def test_partial_transpose_8_by_8_subsystems_4_2(): test_input_mat = np.arange(1, 65).reshape(8, 8) # Partial transpose on the first subsystem: - pt_1 = partial_transpose(test_input_mat, [1], [4, 2]) + pt_1 = partial_transpose(test_input_mat, [0], [4, 2]) expected_pt_1 = np.array( [ [1, 2, 17, 18, 33, 34, 49, 50], @@ -288,7 +288,7 @@ def test_partial_transpose_8_by_8_subsystems_4_2(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose on the second subsystem: - pt_2 = partial_transpose(test_input_mat, [2], [4, 2]) + pt_2 = partial_transpose(test_input_mat, [1], [4, 2]) expected_pt_2 = np.array( [ [1, 9, 3, 11, 5, 13, 7, 15], @@ -305,7 +305,7 @@ def test_partial_transpose_8_by_8_subsystems_4_2(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose on the first and second subsystem: - pt_1_2 = partial_transpose(test_input_mat, [1, 2], [4, 2]) + pt_1_2 = partial_transpose(test_input_mat, [0, 1], [4, 2]) expected_pt_1_2 = np.array( [ [1, 9, 17, 25, 33, 41, 49, 57], @@ -327,7 +327,7 @@ def test_partial_transpose_6_by_6_subsystems_2_3(): test_input_mat = np.arange(1, 37).reshape(6, 6) # Partial transpose on first subsystem: - pt_1 = partial_transpose(test_input_mat, [1], [2, 3]) + pt_1 = partial_transpose(test_input_mat, [0], [2, 3]) expected_pt_1 = np.array( [ [1, 2, 3, 19, 20, 21], @@ -342,7 +342,7 @@ def test_partial_transpose_6_by_6_subsystems_2_3(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose on second subsystem: - pt_2 = partial_transpose(test_input_mat, [2], [2, 3]) + pt_2 = partial_transpose(test_input_mat, [1], [2, 3]) expected_pt_2 = np.array( [ [1, 7, 13, 4, 10, 16], @@ -357,7 +357,7 @@ def test_partial_transpose_6_by_6_subsystems_2_3(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose on first and second subsystems: - pt_1_2 = partial_transpose(test_input_mat, [1, 2], [2, 3]) + pt_1_2 = partial_transpose(test_input_mat, [0, 1], [2, 3]) expected_pt_1_2 = np.array( [ [1, 7, 13, 19, 25, 31], @@ -377,7 +377,7 @@ def test_partial_transpose_6_by_6_subsystems_3_2(): test_input_mat = np.arange(1, 37).reshape(6, 6) # Partial transpose on first subsystem: - pt_1 = partial_transpose(test_input_mat, [1], [3, 2]) + pt_1 = partial_transpose(test_input_mat, [0], [3, 2]) expected_pt_1 = np.array( [ [1, 2, 13, 14, 25, 26], @@ -392,7 +392,7 @@ def test_partial_transpose_6_by_6_subsystems_3_2(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose on second subsystem: - pt_2 = partial_transpose(test_input_mat, [2], [3, 2]) + pt_2 = partial_transpose(test_input_mat, [1], [3, 2]) expected_pt_2 = np.array( [ [1, 7, 3, 9, 5, 11], @@ -407,7 +407,7 @@ def test_partial_transpose_6_by_6_subsystems_3_2(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose on first and second subsystems: - pt_1_2 = partial_transpose(test_input_mat, [1, 2], [3, 2]) + pt_1_2 = partial_transpose(test_input_mat, [0, 1], [3, 2]) expected_pt_1_2 = np.array( [ [1, 7, 13, 19, 25, 31], @@ -427,7 +427,7 @@ def test_partial_transpose_16_by_16_subsystems_2_2_2_2(): rho = np.arange(256).reshape(16, 16) # Partial transpose of first subsystem: - res = partial_transpose(rho, [1], [2, 2, 2, 2]) + res = partial_transpose(rho, [0], [2, 2, 2, 2]) expected_res = np.array( [ [0, 1, 2, 3, 4, 5, 6, 7, 128, 129, 130, 131, 132, 133, 134, 135], @@ -452,7 +452,7 @@ def test_partial_transpose_16_by_16_subsystems_2_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose of second subsystem: - res = partial_transpose(rho, [2], [2, 2, 2, 2]) + res = partial_transpose(rho, [1], [2, 2, 2, 2]) expected_res = np.array( [ [0, 1, 2, 3, 64, 65, 66, 67, 8, 9, 10, 11, 72, 73, 74, 75], @@ -477,7 +477,7 @@ def test_partial_transpose_16_by_16_subsystems_2_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose of third subsystem - res = partial_transpose(rho, [3], [2, 2, 2, 2]) + res = partial_transpose(rho, [2], [2, 2, 2, 2]) expected_res = np.array( [ [0, 1, 32, 33, 4, 5, 36, 37, 8, 9, 40, 41, 12, 13, 44, 45], @@ -502,7 +502,7 @@ def test_partial_transpose_16_by_16_subsystems_2_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose of fourth subsystem - res = partial_transpose(rho, [4], [2, 2, 2, 2]) + res = partial_transpose(rho, [3], [2, 2, 2, 2]) expected_res = np.array( [ [0, 16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 28, 14, 30], @@ -527,7 +527,7 @@ def test_partial_transpose_16_by_16_subsystems_2_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose of first and second subsystem: - res = partial_transpose(rho, [1, 2], [2, 2, 2, 2]) + res = partial_transpose(rho, [0, 1], [2, 2, 2, 2]) expected_res = np.array( [ [0, 1, 2, 3, 64, 65, 66, 67, 128, 129, 130, 131, 192, 193, 194, 195], @@ -552,7 +552,7 @@ def test_partial_transpose_16_by_16_subsystems_2_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose of first and third subsystem: - res = partial_transpose(rho, [1, 3], [2, 2, 2, 2]) + res = partial_transpose(rho, [0, 2], [2, 2, 2, 2]) expected_res = np.array( [ [0, 1, 32, 33, 4, 5, 36, 37, 128, 129, 160, 161, 132, 133, 164, 165], @@ -577,7 +577,7 @@ def test_partial_transpose_16_by_16_subsystems_2_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose of first and fourth subsystem - res = partial_transpose(rho, [1, 4], [2, 2, 2, 2]) + res = partial_transpose(rho, [0, 3], [2, 2, 2, 2]) expected_res = np.array( [ [0, 16, 2, 18, 4, 20, 6, 22, 128, 144, 130, 146, 132, 148, 134, 150], @@ -602,7 +602,7 @@ def test_partial_transpose_16_by_16_subsystems_2_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose of second and third subsystem: - res = partial_transpose(rho, [2, 3], [2, 2, 2, 2]) + res = partial_transpose(rho, [1, 2], [2, 2, 2, 2]) expected_res = np.array( [ [0, 1, 32, 33, 64, 65, 96, 97, 8, 9, 40, 41, 72, 73, 104, 105], @@ -627,7 +627,7 @@ def test_partial_transpose_16_by_16_subsystems_2_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose of second and fourth subsystem: - res = partial_transpose(rho, [2, 4], [2, 2, 2, 2]) + res = partial_transpose(rho, [1, 3], [2, 2, 2, 2]) expected_res = np.array( [ [0, 16, 2, 18, 64, 80, 66, 82, 8, 24, 10, 26, 72, 88, 74, 90], @@ -652,7 +652,7 @@ def test_partial_transpose_16_by_16_subsystems_2_2_2_2(): np.testing.assert_equal(np.all(bool_mat), True) # Partial transpose of third and fourth subsystem - res = partial_transpose(rho, [3, 4], [2, 2, 2, 2]) + res = partial_transpose(rho, [2, 3], [2, 2, 2, 2]) expected_res = np.array( [ [0, 16, 32, 48, 4, 20, 36, 52, 8, 24, 40, 56, 12, 28, 44, 60], @@ -708,26 +708,12 @@ def test_partial_transpose_cvxpy(): np.testing.assert_equal(isinstance(x_pt, Vstack), True) -def test_partial_transpose_non_square(): - """Test partial transpose on non square matrices .""" - rho = np.kron(np.eye(2, 3), np.ones((2, 3))) - rho = np.kron(rho, np.eye(2, 3)) - - dim = np.array([[2, 2, 2], [3, 3, 3]]) - - res = partial_transpose(rho, sys=2, dim=dim) - - expected = np.kron(np.eye(2, 3), np.ones((3, 2))) - expected = np.kron(expected, np.eye(2, 3)) - np.testing.assert_equal(np.allclose(res, expected), True) - - def test_partial_transpose_three_subsystems(): """Test partial transpose on 3 - subsystems .""" mat = np.arange(64).reshape((8, 8)) input_mat = np.kron(np.eye(2, 2), mat) - res = partial_transpose(input_mat, sys=[2, 3, 4], dim=[2, 2, 2, 2]) + res = partial_transpose(input_mat, [1, 2, 3], [2, 2, 2, 2]) expected = np.kron(np.eye(2, 2), mat.T) np.testing.assert_equal(np.allclose(res, expected), True) diff --git a/toqito/channels/partial_trace.py b/toqito/channels/partial_trace.py index b15f7840f..572caf02a 100644 --- a/toqito/channels/partial_trace.py +++ b/toqito/channels/partial_trace.py @@ -2,7 +2,6 @@ from __future__ import annotations import numpy as np -import picos as pc from cvxpy.expressions.expression import Expression from cvxpy.expressions.variable import Variable @@ -12,8 +11,8 @@ def partial_trace( input_mat: np.ndarray | Variable, - sys: list[int] = [1], - dim: list[int] = [2, 2], + sys: int | list[int] = [1], + dim: int | list[int] = None, ) -> np.ndarray | Expression: r""" Compute the partial trace of a matrix [WikPtrace]_. @@ -64,7 +63,7 @@ def partial_trace( [[ 7, 11], [23, 27]] - By specifying the :code:`sys = 1` argument, we can perform the partial trace over the first + By specifying the :code:`sys = [0]` argument, we can perform the partial trace over the first subsystem (instead of the default second subsystem as done above). Performing the partial trace over the first subsystem yields the following matrix @@ -113,10 +112,10 @@ def partial_trace( >>> from toqito.channels import partial_trace >>> import numpy as np >>> partial_trace(test_input_mat, [0, 2], [2, 2, 2, 2]) - [ 3.44e+02 3.48e+02 3.60e+02 3.64e+02] - [ 4.08e+02 4.12e+02 4.24e+02 4.28e+02] - [ 6.00e+02 6.04e+02 6.16e+02 6.20e+02] - [ 6.64e+02 6.68e+02 6.80e+02 6.84e+02] + [[344, 348, 360, 364], + [408, 412, 424, 428], + [600, 604, 616, 620], + [664, 668, 680, 684]]) References ========== @@ -125,7 +124,7 @@ def partial_trace( :raises ValueError: If matrix dimension is not equal to the number of subsystems. :param input_mat: A square matrix. - :param sys: Vector specifying the size of the subsystems. + :param sys: Scalar or vector specifying the size of the subsystems. :param dim: Dimension of the subsystems. If :code:`None`, all dimensions are assumed to be equal. :return: The partial trace of matrix :code:`input_mat`. @@ -133,26 +132,10 @@ def partial_trace( # If the input matrix is a CVX variable for an SDP, we convert it to a numpy array, # perform the partial trace, and convert it back to a CVX variable. if isinstance(input_mat, Variable): - if isinstance(sys, (list, np.ndarray)): - if len(sys) == 1: - pt_mat = partial_trace_for_cvxpy_variable(input_mat, sys[0]+1, dim) - return pt_mat - else: - pt_mat = partial_trace_for_cvxpy_variable(input_mat, np.array(sys)+1, dim) - return pt_mat - else: - pt_mat = partial_trace_for_cvxpy_variable(input_mat, sys+1, dim) - - pt_mat = pc.partial_trace(input_mat, sys, dim) - return pt_mat - -def partial_trace_for_cvxpy_variable( - input_mat: np.ndarray | Variable, - sys: int | list[int] = 2, - dim: int | list[int] = None, -) -> np.ndarray | Expression: - - input_mat = expr_as_np_array(input_mat) + rho_np = expr_as_np_array(input_mat) + traced_rho = partial_trace(rho_np, sys, dim) + traced_rho = np_array_as_expr(traced_rho) + return traced_rho if dim is None: dim = np.array([np.round(np.sqrt(len(input_mat)))]) @@ -174,15 +157,15 @@ def partial_trace_for_cvxpy_variable( num_sys = 2 prod_dim = np.prod(dim) - if isinstance(sys, (list, np.ndarray)): + if isinstance(sys, list): if len(sys) == 1: - prod_dim_sys = np.prod(dim[sys[0] - 1]) + prod_dim_sys = np.prod(dim[sys[0]]) else: prod_dim_sys = 1 for idx in sys: - prod_dim_sys *= dim[idx - 1] + prod_dim_sys *= dim[idx] elif isinstance(sys, int): - prod_dim_sys = np.prod(dim[sys - 1]) + prod_dim_sys = np.prod(dim[sys]) else: raise ValueError( "Invalid: The variable `sys` must either be of type int or of a list of ints." @@ -191,12 +174,14 @@ def partial_trace_for_cvxpy_variable( sub_prod = prod_dim / prod_dim_sys sub_sys_vec = prod_dim * np.ones(int(sub_prod)) / sub_prod + if isinstance(sys, list): + sys = np.array(sys) if isinstance(sys, int): - sys = [sys] - set_diff = list(set(list(range(1, num_sys + 1))) - set(sys)) + sys = np.array([sys]) + set_diff = list(set(list(range(1, num_sys + 1))) - set(sys+1)) perm = set_diff - perm.extend(sys) + perm.extend(sys+1) a_mat = permute_systems(input_mat, perm, dim) @@ -217,6 +202,5 @@ def partial_trace_for_cvxpy_variable( :, :, list(range(0, int(sub_sys_vec[0] ** 2), int(sub_sys_vec[0] + 1))) ] pt_mat = np.sum(pt_mat, axis=2) - traced_rho = np_array_as_expr(pt_mat) - return traced_rho + return pt_mat \ No newline at end of file diff --git a/toqito/channels/partial_transpose.py b/toqito/channels/partial_transpose.py index 778f203fb..d6434b30b 100644 --- a/toqito/channels/partial_transpose.py +++ b/toqito/channels/partial_transpose.py @@ -8,10 +8,9 @@ from toqito.perms import permute_systems from toqito.helper import expr_as_np_array, np_array_as_expr - def partial_transpose( rho: np.ndarray | Variable, - sys: list[int] | np.ndarray | int = 2, + sys: list[int] | np.ndarray | int = [1], dim: list[int] | np.ndarray = None, ) -> np.ndarray | Expression: r"""Compute the partial transpose of a matrix [WikPtrans]_. @@ -157,14 +156,14 @@ def partial_transpose( prod_dim_r = int(np.prod(dim[0, :])) prod_dim_c = int(np.prod(dim[1, :])) - sub_prod_r = np.prod(dim[0, sys - 1]) - sub_prod_c = np.prod(dim[1, sys - 1]) + sub_prod_r = np.prod(dim[0, sys]) + sub_prod_c = np.prod(dim[1, sys]) sub_sys_vec_r = prod_dim_r * np.ones(int(sub_prod_r)) / sub_prod_r sub_sys_vec_c = prod_dim_c * np.ones(int(sub_prod_c)) / sub_prod_c - set_diff = list(set(list(range(1, num_sys + 1))) - set(sys)) - perm = sys.tolist()[:] + set_diff = list(set(list(range(1, num_sys + 1))) - set(sys+1)) + perm = (sys+1).tolist()[:] perm.extend(set_diff) # Permute the subsystems so that we just have to do the partial transpose @@ -192,8 +191,8 @@ def partial_transpose( ) # Return the subsystems back to their original positions. - dim[:, sys - 1] = np.flipud(dim[:, sys - 1]) + dim[:, sys] = np.flipud(dim[:, sys]) dim = dim[:, (np.array(perm) - 1).tolist()] - return permute_systems(z_tmp, perm, dim, False, True) + return permute_systems(z_tmp, perm, dim, False, True) \ No newline at end of file diff --git a/toqito/channels/realignment.py b/toqito/channels/realignment.py index 39d847cb4..f499cd5d7 100644 --- a/toqito/channels/realignment.py +++ b/toqito/channels/realignment.py @@ -78,9 +78,10 @@ def realignment(input_mat: np.ndarray, dim: int | list[int] = None) -> np.ndarra dim = np.array([dim, dim]) dim_x = np.array([[dim[0][1], dim[0][0]], [dim[1][0], dim[1][1]]]) + dim_x = np.int_(dim_x) dim_y = np.array([[dim[1][0], dim[0][0]], [dim[0][1], dim[1][1]]]) x_tmp = swap(input_mat, [1, 2], dim, True) - y_tmp = partial_transpose(x_tmp, sys=1, dim=dim_x) + y_tmp = partial_transpose(x_tmp, [0], dim_x) return swap(y_tmp, [1, 2], dim_y, True) diff --git a/toqito/matrix_props/sk_norm.py b/toqito/matrix_props/sk_norm.py index 93780754e..a32fd7c49 100644 --- a/toqito/matrix_props/sk_norm.py +++ b/toqito/matrix_props/sk_norm.py @@ -236,7 +236,7 @@ def sk_operator_norm( constraints = [rho >> 0, cvxpy.real(cvxpy.trace(rho)) <= 1] if k == 1: - constraints.append(partial_transpose(rho, 2, dim) >> 0) + constraints.append(partial_transpose(rho, [1], dim) >> 0) else: constraints.append(k * cvxpy.kron(partial_trace(rho, [1], dim), np.eye(dim[1])) >> rho) @@ -284,7 +284,7 @@ def sk_operator_norm( rho >> 0, cvxpy.real(cvxpy.trace(rho)) <= 1, sym_proj @ rho @ sym_proj == rho, - partial_transpose(rho, list(range(1, int(np.ceil(j / 2)) + 2)), sym_dim) >> 0, + partial_transpose(rho, list(range(0, int(np.ceil(j / 2)) + 1)), sym_dim) >> 0, ] problem = cvxpy.Problem(objective, constraints) diff --git a/toqito/state_opt/symmetric_extension_hierarchy.py b/toqito/state_opt/symmetric_extension_hierarchy.py index e618652d4..65049c7de 100644 --- a/toqito/state_opt/symmetric_extension_hierarchy.py +++ b/toqito/state_opt/symmetric_extension_hierarchy.py @@ -175,6 +175,7 @@ def symmetric_extension_hierarchy( dim_x, dim_y = int(dim[0]), int(dim[1]) dim_list = [dim_x] + [dim_y] * level + dim_list = np.int_(dim_list) # The `sys_list` variable contains the numbering pertaining to the symmetrically extended # spaces. sys_list = list(range(2, 2 + level - 1)) @@ -189,9 +190,9 @@ def symmetric_extension_hierarchy( 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], 1, dim_list) >> 0) + 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 + 3, dim_list) >> 0) + constraints.append(partial_transpose(x_var[k], [sys + 2], dim_list) >> 0) obj_func.append(probs[k] * cvxpy.trace(states[k].conj().T @ meas[k])) diff --git a/toqito/state_props/has_symmetric_extension.py b/toqito/state_props/has_symmetric_extension.py index 7c6901e6f..f4bb00961 100644 --- a/toqito/state_props/has_symmetric_extension.py +++ b/toqito/state_props/has_symmetric_extension.py @@ -107,6 +107,8 @@ def has_symmetric_extension(rho: np.ndarray, level: int = 2, dim: np.ndarray | i ) dim[1] = int(np.round(dim[1])) + dim = np.int_(dim) + dim_x, dim_y = int(dim[0]), int(dim[1]) # In certain situations, we don't need semidefinite programming. if level == 1 or len_mat <= 6 and ppt: From ba67c16cb223761722ed6ff1e2e933ea570af32e Mon Sep 17 00:00:00 2001 From: ErikQQY <2283984853@qq.com> Date: Sat, 3 Jun 2023 23:10:22 +0800 Subject: [PATCH 4/4] Fix non-square test and wrong import Signed-off-by: ErikQQY <2283984853@qq.com> --- tests/test_channels/test_partial_trace.py | 11 ++++++++++ tests/test_channels/test_partial_transpose.py | 22 +++++++++++++++++++ tests/test_state_props/test_is_separable.py | 4 ++-- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/tests/test_channels/test_partial_trace.py b/tests/test_channels/test_partial_trace.py index 51a24e71b..d425ecc9a 100644 --- a/tests/test_channels/test_partial_trace.py +++ b/tests/test_channels/test_partial_trace.py @@ -40,6 +40,17 @@ def test_partial_trace_sys(): np.testing.assert_equal(np.all(bool_mat), True) +def test_partial_trace_sys_int(): + """Supply `sys` argument as int.""" + test_input_mat = np.arange(1, 17).reshape(4, 4) + + expected_res = np.array([[12, 14], [20, 22]]) + + res = partial_trace(test_input_mat, 0) + + bool_mat = np.isclose(expected_res, res) + np.testing.assert_equal(np.all(bool_mat), True) + def test_partial_trace_sys_int_dim_int(): """ Default second subsystem. diff --git a/tests/test_channels/test_partial_transpose.py b/tests/test_channels/test_partial_transpose.py index a1b1381f9..6dcc9344e 100644 --- a/tests/test_channels/test_partial_transpose.py +++ b/tests/test_channels/test_partial_transpose.py @@ -30,6 +30,16 @@ def test_partial_transpose_bipartite(): bool_mat = np.isclose(expected_res, res) np.testing.assert_equal(np.all(bool_mat), True) +def test_partial_transpose_sys_int(): + """Partial transpose `sys` argument is provided as `int`.""" + test_input_mat = np.arange(1, 17).reshape(4, 4) + + expected_res = np.array([[1, 2, 9, 10], [5, 6, 13, 14], [3, 4, 11, 12], [7, 8, 15, 16]]) + + res = partial_transpose(test_input_mat, sys=0) + + bool_mat = np.isclose(res, expected_res) + np.testing.assert_equal(np.all(bool_mat), True) def test_partial_transpose(): """ @@ -693,6 +703,18 @@ def test_partial_transpose_non_square_matrix(): test_input_mat = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [13, 14, 15, 16]]) partial_transpose(test_input_mat) +def test_partial_transpose_non_square(): + """Test partial transpose on non square matrices .""" + rho = np.kron(np.eye(2, 3), np.ones((2, 3))) + rho = np.kron(rho, np.eye(2, 3)) + + dim = np.array([[2, 2, 2], [3, 3, 3]]) + + res = partial_transpose(rho, sys=1, dim=dim) + + expected = np.kron(np.eye(2, 3), np.ones((3, 2))) + expected = np.kron(expected, np.eye(2, 3)) + np.testing.assert_equal(np.allclose(res, expected), True) def test_partial_transpose_non_square_matrix_2(): """Matrix must be square.""" diff --git a/tests/test_state_props/test_is_separable.py b/tests/test_state_props/test_is_separable.py index 98c5d3b21..fbcb37998 100644 --- a/tests/test_state_props/test_is_separable.py +++ b/tests/test_state_props/test_is_separable.py @@ -2,7 +2,7 @@ import numpy as np from toqito.state_props.is_separable import is_separable -from picos import partial_trace +from toqito.channels import partial_trace from toqito.matrix_props import is_density from toqito.states import basis, bell, isotropic, tile from toqito.random import random_density_matrix @@ -53,7 +53,7 @@ def test_ppt_low_rank(): u, s, v_h = np.linalg.svd(rho) rho_cut = u[:, :m-1] @ np.diag(s[:m-1]) @ v_h[:m-1] rho_cut = rho_cut / np.trace(rho_cut) - pt_state_alice = partial_trace(rho_cut, [1], dim=[3, 2]) + pt_state_alice = partial_trace(rho_cut, [1], [3, 2]) np.testing.assert_equal(is_density(rho_cut), True) np.testing.assert_equal(is_density(np.array(pt_state_alice)), True)