is_block_positive(
mat: ndarray,
k: int = 1,
dim: int | list[int] | None = None,
effort: int = 2,
rtol: float = 1e-05,
) -> bool
Check if matrix is block positive 1.
Parameters:
-
mat
(ndarray)
–
A bipartite Hermitian operator.
-
k
(int, default:
1
)
–
A positive integer indicating that the function should determine whether or not the input operator is k-block
positive, i.e., whether or not it remains nonnegative under left and right multiplication by vectors with
Schmidt rank <= k (default 1).
-
dim
(int | list[int] | None, default:
None
)
–
The dimension of the two sub-systems. By default it's assumed to be equal.
-
effort
(int, default:
2
)
–
An integer value indicating the amount of computation you want to devote to determine block positivity
before giving up.
-
rtol
(float, default:
1e-05
)
–
The relative tolerance parameter (default 1e-05).
Returns:
-
bool
–
Return True if matrix is k-block positive definite, False if not, or raise a runtime error if we are unable
-
bool
–
to determine whether or not the operator is block positive.
Raises:
-
RuntimeError
–
Unable to determine k-block positivity. Please consider increasing the relative tolerance or the
effort level.
Examples:
The swap operator is always block positive, since it is the Choi
matrix of the transpose map.
from toqito.perms.swap_operator import swap_operator
from toqito.matrix_props.is_block_positive import is_block_positive
mat = swap_operator(3)
print(is_block_positive(mat=mat))
However, it's not 2 - block positive.
from toqito.perms.swap_operator import swap_operator
from toqito.matrix_props.is_block_positive import is_block_positive
mat = swap_operator(3)
print(is_block_positive(mat=mat, k=2))
References
1 Johnston, Nathaniel. Norms and Cones in the Theory of Quantum Entanglement. (2012).
Source code in toqito/matrix_props/is_block_positive.py
| def is_block_positive(
mat: np.ndarray,
k: int = 1,
dim: int | list[int] | None = None,
effort: int = 2,
rtol: float = 1e-5,
) -> bool:
r"""Check if matrix is block positive [@johnston2012norms].
Args:
mat: A bipartite Hermitian operator.
k: A positive integer indicating that the function should determine whether or not the input operator is k-block
positive, i.e., whether or not it remains nonnegative under left and right multiplication by vectors with
Schmidt rank <= k (default 1).
dim: The dimension of the two sub-systems. By default it's assumed to be equal.
effort: An integer value indicating the amount of computation you want to devote to determine block positivity
before giving up.
rtol: The relative tolerance parameter (default 1e-05).
Returns:
Return `True` if matrix is k-block positive definite, `False` if not, or raise a runtime error if we are unable
to determine whether or not the operator is block positive.
Raises:
RuntimeError: Unable to determine k-block positivity. Please consider increasing the relative tolerance or the
effort level.
Examples:
The swap operator is always block positive, since it is the Choi
matrix of the transpose map.
```python exec="1" source="above" result="text"
from toqito.perms.swap_operator import swap_operator
from toqito.matrix_props.is_block_positive import is_block_positive
mat = swap_operator(3)
print(is_block_positive(mat=mat))
```
However, it's not 2 - block positive.
```python exec="1" source="above" result="text"
from toqito.perms.swap_operator import swap_operator
from toqito.matrix_props.is_block_positive import is_block_positive
mat = swap_operator(3)
print(is_block_positive(mat=mat, k=2))
```
"""
if not is_hermitian(mat):
return False
dim_xy = mat.shape[0]
# Set default dimension if none was provided.
if dim is None:
dim_val = int(np.round(np.sqrt(dim_xy)))
elif isinstance(dim, int):
dim_val = dim
else:
dim_val = None
# Allow the user to enter in a single integer for dimension.
if dim_val is not None:
dim_arr = np.array([dim_val, dim_xy / dim_val])
dim_arr[1] = int(np.round(dim_arr[1]))
else:
dim_arr = np.array(dim)
dim_arr = np.array(dim_arr, dtype=int)
# When a local dimension is small, block positivity is trivial.
if min(dim_arr) <= k:
return is_positive_semidefinite(mat)
op_norm = np.linalg.norm(mat, ord=2)
# We compute the S(k)-norm of this operator since
# X k-block positive iff:
# c >= S(k)-norm of(c*I - X)
# See Corollary 4.2.9. of `[@johnston2012norms].
c_mat = op_norm * np.eye(dim_xy) - mat
lower_bound, upper_bound = sk_operator_norm(c_mat, k, dim_arr, op_norm, effort)
# block positive
# Note that QETLAB is more conservative here and multiplies
# by (1 - rtol). After some experiments though, I found out
# that probably due to numerical inaccuracies of CVXPY the check
# upper_bound <= op_norm * (1 - rtol)
# would fail even for k - block positive matrices. So, we choose to
# relax this inequality by increasing RHS. Additionally, the check
# upper_bound <= op_norm * (1 - rtol)
# has the "undesired" property that increasing tolerance makes the
# inequality more difficult to satisfy but usually the reverse holds,
# i.e increased tolerance parameter relaxes the problem.
if upper_bound <= op_norm * (1 + rtol):
return True
# not block positive
if lower_bound >= op_norm * (1 - rtol):
return False
raise RuntimeError(
"Unable to determine k-block positivity. Please consider increasing the relative tolerance or the effort level."
)
|