Skip to content

Commit

Permalink
Upated Indexing from 1 -> 0 (#602)
Browse files Browse the repository at this point in the history
* Upated Indexing from 1 -> 0

* cleaning up linting issues

* Update: Updated test cases

* ruff cleanup

* fixing more tests

* fixing trailling whitespace

* removing unrequired print statements

* fix to partial taranspose

* removed unrequired print

* ruff fix

* Applying recommended changes

* Updating perfect matching to start from 0

* Update test_perfect_matchings.py

* Update permutation_operator.py

* Update permutation_operator.py

* Update toqito/channels/partial_trace.py

Co-authored-by: Purva Thakre <[email protected]>

* Update toqito/channels/partial_transpose.py

Co-authored-by: Purva Thakre <[email protected]>

* Apply suggestions from code review

---------

Co-authored-by: Purva Thakre <[email protected]>
  • Loading branch information
anushkrishnav and purva-thakre authored Jun 3, 2024
1 parent 49ecfd4 commit 8646d6a
Show file tree
Hide file tree
Showing 17 changed files with 82 additions and 79 deletions.
4 changes: 2 additions & 2 deletions toqito/channel_metrics/fidelity_of_separability.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def fidelity_of_separability(
raise ValueError("This function only works for pure states.")

# We first permure psi_{BAR} to psi_{RAB} to simplify the code.
psi = permute_systems(psi, [3, 2, 1], psi_dims)
psi = permute_systems(psi, [2, 1, 0], psi_dims)
dim_b, dim_a, dim_r = psi_dims
psi_dims = [dim_r, dim_a, dim_b]

Expand Down Expand Up @@ -163,7 +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), [0, 3, 2, 1], dim_list),
[0, 2],
dim_list,
)
Expand Down
2 changes: 1 addition & 1 deletion toqito/channel_ops/partial_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def partial_channel(

phi_map = permute_systems(
np.kron(np.kron(psi_r1 * psi_c1.conj().T, phi_map), psi_r2 * psi_c2.conj().T),
[1, 3, 5, 2, 4, 6],
[0, 2, 4, 1, 3, 5],
dim,
)

Expand Down
4 changes: 2 additions & 2 deletions toqito/channels/partial_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,10 @@ def partial_trace(
sys = np.array(sys)
if isinstance(sys, int):
sys = np.array([sys])
set_diff = list(set(list(range(1, num_sys + 1))) - set(sys + 1))

set_diff = list(set(list(range(num_sys))) - set(sys))
perm = set_diff
perm.extend(sys + 1)
perm.extend(sys)

a_mat = permute_systems(input_mat, perm, dim)

Expand Down
6 changes: 3 additions & 3 deletions toqito/channels/partial_transpose.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,8 @@ def partial_transpose(
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 + 1))
perm = (sys + 1).tolist()[:]
set_diff = list(set(list(range(num_sys ))) - set(sys ))
perm = (sys ).tolist()[:]
perm.extend(set_diff)

# Permute the subsystems so that we just have to do the partial transpose
Expand Down Expand Up @@ -198,6 +198,6 @@ def partial_transpose(
# Return the subsystems back to their original positions.
dim[:, sys] = np.flipud(dim[:, sys])

dim = dim[:, (np.array(perm) - 1).tolist()]
dim = dim[:, (np.array(perm) ).tolist()]

return permute_systems(z_tmp, perm, dim, False, True)
4 changes: 2 additions & 2 deletions toqito/nonlocal_games/quantum_hedging.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ def __init__(self, q_a: np.ndarray, num_reps: int) -> None:
# action:
# π(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_1 = list(range(self._num_reps ))
l_2 = list(range(self._num_reps , self._num_reps**2 ))
if self._num_reps == 1:
self._pperm = np.array([1])
else:
Expand Down
2 changes: 1 addition & 1 deletion toqito/perms/antisymmetric_projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def antisymmetric_projection(dim: int, p_param: int = 2, partial: bool = False)
if dim < p_param:
return np.zeros((dimp, dimp * (1 - partial)))

p_list = np.array(list(permutations(np.arange(1, p_param + 1))))
p_list = np.array(list(permutations(np.arange(p_param ))))
p_fac = p_list.shape[0]

anti_proj = np.zeros((dimp, dimp))
Expand Down
14 changes: 7 additions & 7 deletions toqito/perms/perfect_matchings.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,27 @@ def perfect_matchings(num: list[int] | int | np.ndarray) -> np.ndarray:
Examples
==========
This is an example of how to generate all perfect matchings of the numbers 1, 2, 3, 4.
This is an example of how to generate all perfect matchings of the numbers 0, 1, 2, 3.
>>> from toqito.perms import perfect_matchings
>>> perfect_matchings(4)
array([[1, 2, 3, 4],
[1, 3, 2, 4],
[1, 4, 3, 2]])
array([[0, 1, 2, 3],
[0, 2, 1, 3],
[0, 3, 2, 1]])
References
==========
.. bibliography::
:filter: docname in docnames
:param num: Either an even integer, indicating that you would like all perfect matchings of the
integers 1,2, ... N, or a `list` or `np.array` containing an even number of distinct
integers 0, 1, ... N-1, or a `list` or `np.array` containing an even number of distinct
entries, indicating that you would like all perfect matchings of those entries.
:return: An array containing all valid perfect matchings of size :code:`num`.
"""
if isinstance(num, int):
num = np.arange(1, num + 1)
num = np.arange(num)
if isinstance(num, list):
num = np.array(num)

Expand Down Expand Up @@ -73,3 +72,4 @@ def perfect_matchings(num: list[int] | int | np.ndarray) -> np.ndarray:
matchings = np.vstack((matchings, s_vec))

return matchings

8 changes: 4 additions & 4 deletions toqito/perms/permutation_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def permutation_operator(
qubits
.. math::
P_{2, [2, 1]} =
P_{2, [1, 0]} =
\begin{pmatrix}
1 & 0 & 0 & 0 \\
0 & 0 & 1 & 0 \\
Expand All @@ -38,13 +38,12 @@ def permutation_operator(
Using :code:`toqito`, this can be achieved in the following manner.
>>> from toqito.perms import permutation_operator
>>> permutation_operator(2, [2, 1])
>>> permutation_operator(2, [1, 0])
array([[1., 0., 0., 0.],
[0., 0., 1., 0.],
[0., 1., 0., 0.],
[0., 0., 0., 1.]])
:param dim: The dimensions of the subsystems to be permuted.
:param perm: A permutation vector.
:param inv_perm: Boolean dictating if :code:`perm` is inverse or not.
Expand All @@ -54,10 +53,11 @@ def permutation_operator(
"""
# Allow the user to enter a single number for `dim`.
if isinstance(dim, int):
dim = dim * np.ones(max(perm))
dim = [dim] * np.ones(max(perm) + 1)
if isinstance(dim, list):
dim = np.array(dim)

mat = sp.sparse.identity(int(np.prod(dim))) if is_sparse else np.identity(int(np.prod(dim)))
# Swap the rows of the identity matrix appropriately.

return permute_systems(mat, perm, dim, True, inv_perm)
19 changes: 12 additions & 7 deletions toqito/perms/permute_systems.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def permute_systems(
For spaces :math:`\mathcal{A}` and :math:`\mathcal{B}` where :math:`\text{dim}(\mathcal{A}) =
\text{dim}(\mathcal{B}) = 2` we may consider an operator :math:`X \in \mathcal{A} \otimes \mathcal{B}`. Applying the
`permute_systems` function with vector :math:`[2,1]` on :math:`X`, we may reorient the spaces such that :math:`X \in
`permute_systems` function with vector :math:`[1,0]` on :math:`X`, we may reorient the spaces such that :math:`X \in
\mathcal{B} \otimes \mathcal{A}`.
For example, if we define :math:`X \in \mathcal{A} \otimes \mathcal{B}` as
Expand All @@ -53,7 +53,7 @@ def permute_systems(
yield the following matrix
.. math::
X_{[2,1]} = \begin{pmatrix}
X_{[1,0]} = \begin{pmatrix}
1 & 3 & 2 & 4 \\
9 & 11 & 10 & 12 \\
5 & 7 & 6 & 8 \\
Expand All @@ -63,15 +63,15 @@ def permute_systems(
>>> from toqito.perms import permute_systems
>>> import numpy as np
>>> test_input_mat = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])
>>> permute_systems(test_input_mat, [2, 1])
>>> permute_systems(test_input_mat, [1, 0])
array([[ 1, 3, 2, 4],
[ 9, 11, 10, 12],
[ 5, 7, 6, 8],
[13, 15, 14, 16]])
For spaces :math:`\mathcal{A}, \mathcal{B}`, and :math:`\mathcal{C}` where :math:`\text{dim}(\mathcal{A}) =
\text{dim}(\mathcal{B}) = \text{dim}(\mathcal{C}) = 2` we may consider an operator :math:`X \in \mathcal{A} \otimes
\mathcal{B} \otimes \mathcal{C}`. Applying the :code:`permute_systems` function with vector :math:`[2,3,1]` on
\mathcal{B} \otimes \mathcal{C}`. Applying the :code:`permute_systems` function with vector :math:`[1,2,0]` on
:math:`X`, we may reorient the spaces such that :math:`X \in \mathcal{B} \otimes \mathcal{C} \otimes \mathcal{A}`.
For example, if we define :math:`X \in \mathcal{A} \otimes \mathcal{B} \otimes \mathcal{C}` as
Expand All @@ -93,7 +93,7 @@ def permute_systems(
\otimes \mathcal{C}` yield the following matrix
.. math::
X_{[2, 3, 1]} =
X_{[1, 2, 0]} =
\begin{pmatrix}
1 & 5 & 2 & 6 & 3 & 7 & 4, 8 \\
33 & 37 & 34 & 38 & 35 & 39 & 36 & 40 \\
Expand All @@ -119,7 +119,7 @@ def permute_systems(
... [57, 58, 59, 60, 61, 62, 63, 64],
... ]
... )
>>> permute_systems(test_input_mat, [2, 3, 1])
>>> permute_systems(test_input_mat, [1, 2, 0])
array([[ 1, 5, 2, 6, 3, 7, 4, 8],
[33, 37, 34, 38, 35, 39, 36, 40],
[ 9, 13, 10, 14, 11, 15, 12, 16],
Expand All @@ -145,6 +145,7 @@ def permute_systems(
else:
input_mat_dims = input_mat.shape


is_vec = np.min(input_mat_dims) == 1
num_sys = len(perm)

Expand Down Expand Up @@ -176,7 +177,7 @@ def permute_systems(
prod_dim_r = int(np.prod(dim[0, :]))
prod_dim_c = int(np.prod(dim[1, :]))

if sorted(perm) != list(range(1, num_sys + 1)):
if sorted(perm) != list(range(num_sys )):
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.")
Expand All @@ -185,6 +186,10 @@ def permute_systems(
if input_mat.shape[0] == 1:
input_mat = input_mat[0]
vec_orien = 1
# Rather than using subtraction to generate new indices,
# it's better to use methods designed for handling permutations directly.
# This avoids the risk of negative indices and is more straightforward.
num_sys -= 1 # 0-indexing (Since we're using 0-indexing, we need to subtract 1 from the number of subsystems.)
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
Expand Down
2 changes: 1 addition & 1 deletion toqito/perms/swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def swap(
raise ValueError("InvalidSys: `sys` must be a vector with exactly two elements.")

# Swap the indicated subsystems.
perm = np.array(range(1, num_sys + 1))
perm = np.array(range(num_sys ))
sys = np.array(sys) - 1

perm[sys] = perm[sys[::-1]]
Expand Down
2 changes: 1 addition & 1 deletion toqito/perms/symmetric_projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def symmetric_projection(dim: int, p_val: int = 2, partial: bool = False) -> [np
if p_val == 1:
return np.eye(dim)

p_list = np.array(list(permutations(np.arange(1, p_val + 1))))
p_list = np.array(list(permutations(np.arange(p_val ))))
p_fac = math.factorial(p_val)
sym_proj = np.zeros((dimp, dimp))

Expand Down
36 changes: 18 additions & 18 deletions toqito/perms/tests/test_perfect_matchings.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
def test_perfect_matchings_num_4():
"""All perfect matchings of size 4."""
res = perfect_matchings(4)
expected_res = np.array([[1, 2, 3, 4], [1, 3, 2, 4], [1, 4, 3, 2]])
expected_res = np.array([[0, 1, 2, 3], [0, 2, 1, 3], [0, 3, 2, 1]])
bool_mat = np.isclose(res, expected_res)
np.testing.assert_equal(np.all(bool_mat), True)


def test_perfect_matchings_num_4_list():
"""All perfect matchings of size 4 with input as list."""
res = perfect_matchings([1, 2, 3, 4])
expected_res = np.array([[1, 2, 3, 4], [1, 3, 2, 4], [1, 4, 3, 2]])
res = perfect_matchings([0, 1, 2, 3])
expected_res = np.array([[0, 1, 2, 3], [0, 2, 1, 3], [0, 3, 2, 1]])
bool_mat = np.isclose(res, expected_res)
np.testing.assert_equal(np.all(bool_mat), True)

Expand All @@ -35,21 +35,21 @@ def test_perfect_matchings_num_6():

expected_res = np.array(
[
[1, 2, 3, 4, 5, 6],
[1, 2, 3, 5, 4, 6],
[1, 2, 3, 6, 5, 4],
[1, 3, 2, 4, 5, 6],
[1, 3, 2, 5, 4, 6],
[1, 3, 2, 6, 5, 4],
[1, 4, 3, 2, 5, 6],
[1, 4, 3, 5, 2, 6],
[1, 4, 3, 6, 5, 2],
[1, 5, 3, 4, 2, 6],
[1, 5, 3, 2, 4, 6],
[1, 5, 3, 6, 2, 4],
[1, 6, 3, 4, 5, 2],
[1, 6, 3, 5, 4, 2],
[1, 6, 3, 2, 5, 4],
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 4, 3, 5],
[0, 1, 2, 5, 4, 3],
[0, 2, 1, 3, 4, 5],
[0, 2, 1, 4, 3, 5],
[0, 2, 1, 5, 4, 3],
[0, 3, 2, 1, 4, 5],
[0, 3, 2, 4, 1, 5],
[0, 3, 2, 5, 4, 1],
[0, 4, 2, 3, 1, 5],
[0, 4, 2, 1, 3, 5],
[0, 4, 2, 5, 1, 3],
[0, 5, 2, 3, 4, 1],
[0, 5, 2, 4, 3, 1],
[0, 5, 2, 1, 4, 3]
]
)
bool_mat = np.isclose(res, expected_res)
Expand Down
36 changes: 18 additions & 18 deletions toqito/perms/tests/test_permutation_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,28 @@


def test_permutation_operator_standard_swap():
"""Generates the standard swap operator on two qubits."""
"""Generates the standard swap operator on two qubits with zero-based indexing."""
expected_res = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]])

res = permutation_operator(2, [2, 1])
res = permutation_operator(2, [1, 0])

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


def test_permutation_operator_standard_swap_list_dim():
"""Generates the standard swap operator on two qubits."""
"""Generates the standard swap operator on two qubits with zero-based indexing."""
expected_res = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]])

res = permutation_operator([2, 2], [2, 1])
res = permutation_operator([2, 2], [1, 0])

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


def test_permutation_operator_sparse_option():
"""Sparse swap operator on two qutrits."""
res = permutation_operator(3, [2, 1], False, True)
"""Sparse swap operator on two qutrits with zero-based indexing."""
res = permutation_operator(3, [1, 0], False, True)

expected_res = np.array(
[
Expand All @@ -46,17 +46,17 @@ def test_permutation_operator_sparse_option():
np.testing.assert_equal(np.all(bool_mat), True)


def test_permutation_operator_dim_3_perm_1_2():
"""Test permutation operator when dim is 3 and perm is [1, 2]."""
res = permutation_operator(3, [1, 2])
def test_permutation_operator_dim_3_perm_0_1():
"""Test permutation operator when dim is 3 and perm is [0, 1] using zero-based indexing."""
res = permutation_operator(3, [0, 1])
expected_res = np.identity(9)
bool_mat = np.isclose(res, expected_res)
np.testing.assert_equal(np.all(bool_mat), True)


def test_permutation_operator_dim_2_perm_1_3_2():
"""Test permutation operator when dim is 2 and perm is [1, 3, 2]."""
res = permutation_operator(2, [1, 3, 2])
def test_permutation_operator_dim_2_perm_0_2_1():
"""Test permutation operator when dim is 2 and perm is [0, 2, 1] using zero-based indexing."""
res = permutation_operator(2, [0, 2, 1])
expected_res = np.array(
[
[
Expand All @@ -75,17 +75,17 @@ def test_permutation_operator_dim_2_perm_1_3_2():
np.testing.assert_equal(np.all(bool_mat), True)


def test_permutation_operator_dim_2_2_perm_1_2():
"""Test permutation operator when dim is [2, 2] and perm is [1, 2]."""
res = permutation_operator([2, 2], [1, 2])
def test_permutation_operator_dim_2_2_perm_0_1():
"""Test permutation operator when dim is [2, 2] and perm is [0, 1] using zero-based indexing."""
res = permutation_operator([2, 2], [0, 1])
expected_res = np.identity(4)
bool_mat = np.isclose(res, expected_res)
np.testing.assert_equal(np.all(bool_mat), True)


def test_permutation_operator_dim_2_2_perm_2_1():
"""Test permutation operator when dim is [2, 2] and perm is [2, 1]."""
res = permutation_operator([2, 2], [2, 1])
def test_permutation_operator_dim_2_2_perm_1_0():
"""Test permutation operator when dim is [2, 2] and perm is [1, 0] using zero-based indexing."""
res = permutation_operator([2, 2], [1, 0])
expected_res = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]])
bool_mat = np.isclose(res, expected_res)
np.testing.assert_equal(np.all(bool_mat), True)
Loading

0 comments on commit 8646d6a

Please sign in to comment.