Skip to content

is_block_positive

Checks if the matrix is block positive.

is_block_positive

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))
True

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))
False

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."
    )