Skip to content

Commit

Permalink
Merge pull request #271 from vprusso/state_exclusion_improve
Browse files Browse the repository at this point in the history
Update and improvements to state exclusion code and applications.
  • Loading branch information
vprusso authored Nov 25, 2023
2 parents 77e9363 + 7df247a commit 7e419bf
Show file tree
Hide file tree
Showing 16 changed files with 246 additions and 87 deletions.
13 changes: 4 additions & 9 deletions toqito/matrix_ops/tests/test_vectors_to_gram_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,20 @@
import numpy as np
import pytest

from toqito.states import trine
from toqito.matrix_ops import vectors_to_gram_matrix


e_0, e_1 = np.array([[1], [0]]), np.array([[0], [1]])
trine = [
e_0,
1 / 2 * (-e_0 + np.sqrt(3) * e_1),
-1 / 2 * (e_0 + np.sqrt(3) * e_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]])),
(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."""
vectors = vectors_to_gram_matrix(trine)
np.testing.assert_allclose(vectors, expected_result)
np.testing.assert_allclose(vectors_to_gram_matrix(vectors), expected_result)


@pytest.mark.parametrize("vectors", [
Expand All @@ -29,4 +24,4 @@ def test_vectors_to_gram_matrix(vectors, expected_result):
])
def test_vectors_to_gram_matrix_invalid_input(vectors):
with np.testing.assert_raises(ValueError):
vectors_to_gram_matrix(vectors)
vectors_to_gram_matrix(vectors)
8 changes: 4 additions & 4 deletions toqito/matrix_props/is_totally_positive.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"""Is matrix totally positive."""
import numpy as np
from itertools import combinations
import numpy as np


def is_totally_positive(mat, tol=1e-6, sub_sizes=None):
"""
def is_totally_positive(mat: np.ndarray, tol: float = 1e-6, sub_sizes: list | None = None):
r"""
Determines whether a matrix is totally positive.
A totally positive matrix is a square matrix where all the minors are positive. Equivalently, the determinant of
Expand Down Expand Up @@ -45,7 +45,7 @@ def is_totally_positive(mat, tol=1e-6, sub_sizes=None):
https://en.wikipedia.org/wiki/Totally_positive_matrix
:param mat: Matrix to check.
:param atol: The absolute tolerance parameter (default 1e-06).
:param tol: The absolute tolerance parameter (default 1e-06).
:param sub_sizes: List of sizes of submatrices to consider. Default is all sizes up to :code:`min(mat.shape)`.
:return: Return :code:`True` if matrix is totally positive, and :code:`False` otherwise.
"""
Expand Down
4 changes: 2 additions & 2 deletions toqito/perms/permute_systems.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,8 @@ def permute_systems(
# This condition is only necessary if the `input_mat` variable is sparse.
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)
permuted_mat = input_mat[row_perm, :]
permuted_mat = np.array(permuted_mat) # pylint: disable=redefined-variable-type
else:
permuted_mat = input_mat[row_perm, :]

Expand Down
1 change: 0 additions & 1 deletion toqito/rand/tests/test_random_state_vector.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""Test random_state_vector."""
import numpy as np
import pytest

from toqito.rand import random_state_vector
Expand Down
1 change: 0 additions & 1 deletion toqito/rand/tests/test_random_unitary.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""Test random_unitary."""
import numpy as np
import pytest

from toqito.matrix_props import is_unitary
Expand Down
93 changes: 51 additions & 42 deletions toqito/state_opt/state_exclusion.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
import numpy as np
import picos

from toqito.state_ops import pure_to_mixed


def state_exclusion(
vectors: list[np.ndarray],
Expand All @@ -12,10 +10,9 @@ def state_exclusion(
primal_dual: str = "dual",
) -> tuple[float, list[picos.HermitianVariable]]:
r"""
Compute probability of single state exclusion.
Compute probability of single state conclusive state exclusion.
The *quantum state exclusion* problem involves a collection of :math:`n`
quantum states
The *quantum state exclusion* problem involves a collection of :math:`n` quantum states
.. math::
\rho = \{ \rho_0, \ldots, \rho_n \},
Expand All @@ -25,29 +22,37 @@ def state_exclusion(
.. math::
p = \{ p_0, \ldots, p_n \}.
Alice chooses :math:`i` with probability :math:`p_i` and creates the state
:math:`\rho_i`.
Alice chooses :math:`i` with probability :math:`p_i` and creates the state :math:`\rho_i`.
Bob wants to guess which state he was *not* given from the collection of states. State exclusion implies that
ability to discard (with certainty) at least one out of the "n" possible quantum states by applying a measurement.
Bob wants to guess which state he was *not* given from the collection of
states. State exclusion implies that ability to discard (with certainty) at
least one out of the "n" possible quantum states by applying a measurement.
This function implements the following semidefinite program that provides the optimal probability with which Bob can
conduct quantum state exclusion.
This function implements the following semidefinite program that provides
the optimal probability with which Bob can conduct quantum state exclusion.
.. math::
\begin{equation}
\begin{aligned}
\text{minimize:} \quad & \sum_{i=1}^n p_i \langle M_i, \rho_i \rangle \\
\text{subject to:} \quad & \sum_{i=1}^n M_i = \mathbb{I}_{\mathcal{X}}, \\
& M_0, \ldots, M_n \in \text{Pos}(\mathcal{X}).
\end{aligned}
\end{equation}
.. math::
\begin{equation}
\begin{aligned}
\text{minimize:} \quad & \sum_{i=0}^n p_i \langle M_i,
\rho_i \rangle \\
\text{subject to:} \quad & M_0 + \ldots + M_n =
\mathbb{I}, \\
& M_0, \ldots, M_n >= 0.
\text{maximize:} \quad & \text{Tr}(Y)
\text{subject to:} \quad & Y \leq M_1, \\
& Y \leq M_2, \\
& \vdots \\
& Y \leq M_n, \\
& Y \text{Herm}(\mathcal{X}).
\end{aligned}
\end{equation}
The conclusive state exclusion SDP is written explicitly in [BJOP14]_. The problem of conclusive
state exclusion was also thought about under a different guise in [PBR12]_.
The conclusive state exclusion SDP is written explicitly in [BJOP14]_. The problem of conclusive state exclusion was
also thought about under a different guise in [PBR12]_.
Examples
==========
Expand All @@ -62,9 +67,8 @@ def state_exclusion(
\end{aligned}
\end{equation}
It is not possible to conclusively exclude either of the two states. We can see that the result
of the function in :code:`toqito` yields a value of :math:`0` as the probability for this to
occur.
It is not possible to conclusively exclude either of the two states. We can see that the result of the function in
:code:`toqito` yields a value of :math:`0` as the probability for this to occur.
>>> from toqito.state_opt import state_exclusion
>>> from toqito.states import bell
Expand All @@ -84,31 +88,37 @@ def state_exclusion(
arXiv:1111.3328
.. [BJOP14] "Conclusive exclusion of quantum states"
Bandyopadhyay, Somshubhro, Jain, Rahul, Oppenheim, Jonathan,
Perry, Christopher
Bandyopadhyay, Somshubhro, Jain, Rahul, Oppenheim, Jonathan, Perry, Christopher
Physical Review A 89.2 (2014): 022336.
arXiv:1306.4683
:param vectors: A list of states provided as vectors.
:param probs: Respective list of probabilities each state is selected. If no
probabilities are provided, a uniform probability distribution is assumed.
:param solver: Optimization option for `picos` solver. Default option is
`solver_option="cvxopt"`.
:param primal_dual: Option for the optimization problem
:param solver: Optimization option for `picos` solver. Default option is `solver_option="cvxopt"`.
:param primal_dual: Option for the optimization problem.
:return: The optimal probability with which Bob can guess the state he was
not given from `states` along with the optimal set of measurements.
"""
if primal_dual == "primal":
return _min_error_primal(vectors, probs, solver)
if not all(vector.shape == vectors[0].shape for vector in vectors):
raise ValueError("Vectors for state exclusion must all have the same dimension.")

# Assumes a uniform probabilities distribution among the states if one is not explicitly provided.
n = vectors[0].shape[0]
probs = [1 / n] * n if probs is None else probs

return _min_error_dual(vectors, probs, solver)
if primal_dual == "primal":
return _min_error_primal(vectors=vectors, probs=probs, solver=solver)
return _min_error_dual(vectors=vectors, probs=probs, solver=solver)


def _min_error_primal(vectors: list[np.ndarray], probs: list[float] = None, solver: str = "cvxopt"):
def _min_error_primal(
vectors: list[np.ndarray],
probs: list[float] = None,
solver: str = "cvxopt",
) -> tuple[float, list[picos.HermitianVariable]]:
"""Find the primal problem for minimum-error quantum state exclusion SDP."""
n, dim = len(vectors), vectors[0].shape[0]
if probs is None:
probs = [1 / len(vectors)] * len(vectors)

problem = picos.Problem()
measurements = [picos.HermitianVariable(f"M[{i}]", (dim, dim)) for i in range(n)]
Expand All @@ -120,7 +130,7 @@ def _min_error_primal(vectors: list[np.ndarray], probs: list[float] = None, solv
"min",
picos.sum(
[
(probs[i] * pure_to_mixed(vectors[i].reshape(-1, 1)) | measurements[i])
(probs[i] * vectors[i] @ vectors[i].conj().T | measurements[i])
for i in range(n)
]
),
Expand All @@ -130,20 +140,19 @@ def _min_error_primal(vectors: list[np.ndarray], probs: list[float] = None, solv


def _min_error_dual(
vectors: list[np.ndarray], probs: list[float] = None, solver: str = "cvxopt"
) -> float:
vectors: list[np.ndarray],
probs: list[float] = None,
solver: str = "cvxopt"
) -> tuple[float, list[picos.HermitianVariable]]:
"""Find the dual problem for minimum-error quantum state exclusion SDP."""
dim = vectors[0].shape[0]
if probs is None:
probs = [1 / len(vectors)] * len(vectors)

n, dim = len(vectors), vectors[0].shape[0]
problem = picos.Problem()

# Set up variables and constraints for SDP:
y_var = picos.HermitianVariable("Y", (dim, dim))
problem.add_list_of_constraints(
[
y_var << probs[i] * pure_to_mixed(vector.reshape(-1, 1))
y_var << probs[i] * vector @ vector.conj().T
for i, vector in enumerate(vectors)
]
)
Expand All @@ -152,6 +161,6 @@ def _min_error_dual(
problem.set_objective("max", picos.trace(y_var))
solution = problem.solve(solver=solver)

measurements = [problem.get_constraint(k).dual for k in range(len(vectors))]
measurements = [problem.get_constraint(k).dual for k in range(n)]

return solution.value, measurements
47 changes: 24 additions & 23 deletions toqito/state_opt/tests/test_state_exclusion.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
"""Test state_exclusion."""
import numpy as np
import pytest

from toqito.states import bell
from toqito.matrices import standard_basis
from toqito.state_opt import state_exclusion


def test_conclusive_state_exclusion():
"""Conclusive state exclusion for single vector state."""
e_0, e_1 = standard_basis(2)
states = [
1 / np.sqrt(2) * (np.kron(e_0, e_0) + np.kron(e_1, e_1)),
1 / np.sqrt(2) * (np.kron(e_0, e_0) - np.kron(e_1, e_1)),
1 / np.sqrt(2) * (np.kron(e_0, e_1) + np.kron(e_1, e_0)),
1 / np.sqrt(2) * (np.kron(e_0, e_1) - np.kron(e_1, e_0)),
]
# No probabilities provided
primal_value, _ = state_exclusion(vectors=states, probs=None, primal_dual="primal")
np.testing.assert_equal(np.isclose(primal_value, 0), True)
e_0, e_1 = standard_basis(2)

dual_value, _ = state_exclusion(vectors=states, probs=None, primal_dual="dual")
np.testing.assert_equal(np.isclose(dual_value, 0), True)

# Probabilities provided
primal_value, _ = state_exclusion(
vectors=states, probs=[1 / 4, 1 / 4, 1 / 4, 1 / 4], primal_dual="primal"
)
np.testing.assert_equal(np.isclose(primal_value, 0), True)
@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),
])
def test_conclusive_state_exclusion(vectors, probs, solver, primal_dual, expected_result):
val, _ = state_exclusion(vectors=vectors, probs=probs, solver=solver, primal_dual=primal_dual)
assert abs(val- expected_result) <=1e-8

dual_value, _ = state_exclusion(
vectors=states, probs=[1 / 4, 1 / 4, 1 / 4, 1 / 4], primal_dual="dual"
)
np.testing.assert_equal(np.isclose(dual_value, 0), True)

@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):
with pytest.raises(ValueError):
state_exclusion(vectors=vectors, probs=probs, solver=solver, primal_dual=primal_dual)
1 change: 1 addition & 0 deletions toqito/state_props/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@
from toqito.state_props.is_separable import is_separable
from toqito.state_props.has_symmetric_extension import has_symmetric_extension
from toqito.state_props.sk_vec_norm import sk_vector_norm
from toqito.state_props.is_antidistinguishable import is_antidistinguishable
67 changes: 67 additions & 0 deletions toqito/state_props/is_antidistinguishable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""Check if set of states are antidistinguishable."""
import numpy as np
from toqito.state_opt import state_exclusion


def is_antidistinguishable(states: list[np.ndarray]) -> bool:
r"""
Check whether a collection of vectors are antidistinguishable or not [TK18]_.
The ability to determine whether a set of quantum states are antidistinguishable can be obtained via the state
exclusion SDP [BJOP14]_ such that we ignore the associated probabilities with which the states are chosen from the set of
vectors.
Examples
========
The set of Bell states are an example of antidistinguishable states. Recall that the Bell states are defined as:
.. math::
u_1 = \frac{1}{\sqrt{2}} \left(|00\rangle + |11\rangle\right), &\quad
u_2 = \frac{1}{\sqrt{2}} \left(|00\rangle - |11\rangle\right), \\
u_3 = \frac{1}{\sqrt{2}} \left(|01\rangle + |10\rangle\right), &\quad
u_4 = \frac{1}{\sqrt{2}} \left(|01\rangle - |10\rangle\right).
It can be checked in :code`toqito` that the Bell states are antidistinguishable:
>>> from toqito.states import bell
>>> from toqito.state_props import is_antidistinguishable
>>>
>>> bell_states = [bell(0), bell(1), bell(2), bell(3)]
>>> is_antidistinguishable(bell_states)
True
Consider the following measurement operators
.. math::
M_i = \frac{1}{3}\left(\mathbb{I}_{\mathcal{X} - u_i u_i^*\right)
for all :math:`1 \leq i \leq 4`. It can be verified that these constitute a valid set of POVMs, that is
:math:`\sum_{i=1}^4 M_i = \mathbb{I}_{\mathcal{X}}` and :math:`M_i \in \text{Pos}(\mathcal{X})` for all :math:`1
\leq i \leq 4`. It may also be verified that
.. math::
\sum_{i=1}^4 \langle M_i, u_i u_i^* \rangle = 0,
and hence, the Bell states are antidistinguishable.
References
==========
.. [TK18] Heinosaari, Teiko, and Oskari Kerppo.
"Antidistinguishability of pure quantum states."
Journal of Physics A: Mathematical and Theoretical 51.36 (2018): 365303.
https://arxiv.org/abs/1804.10457
.. [BJOP14] Bandyopadhyay, Somshubhro, Jain, Rahul, Oppenheim, Jonathan, Perry, Christopher
"Conclusive exclusion of quantum states"
Physical Review A 89.2 (2014): 022336.
arXiv:1306.4683
:param states: A set of vectors consisting of quantum states to determine the antidistinguishability of.
:return: :code:`True` if the vectors are antidistinguishable; :code:`False` otherwise.
"""
probs = [1] * len(states)

# The dual problem is less computationally intensive to compute in comparison to primal.
opt_val, _ = state_exclusion(vectors=states, probs=probs, primal_dual="dual")
return np.isclose(opt_val, 0)
Loading

0 comments on commit 7e419bf

Please sign in to comment.