Source code for toqito.state_props.is_sic_povm

"""Determine whether a collection of vectors forms a SIC POVM."""

from typing import Sequence

import numpy as np

from toqito.matrix_ops import vectors_to_gram_matrix
from toqito.state_ops import normalize


[docs] def is_sic_povm(states: Sequence[np.ndarray], *, tol: float = 1e-6) -> bool: r"""Check if the provided vectors yield a symmetric informationally complete POVM. A set of \(d^2\) unit vectors \(\{\ket{\psi_j}\}\) in \(\mathbb{C}^d\) forms a symmetric informationally complete POVM (SIC POVM) when \[ \left| \langle \psi_j, \psi_k \rangle \right|^2 = \frac{1}{d + 1} \quad \text{for all } j \neq k, \] and the projectors satisfy \(\sum_j \ket{\psi_j}\!\bra{\psi_j} = d \mathbb{I}\). Examples: Qubit tetrahedron SIC. ```python exec="1" source="above" import numpy as np from toqito.state_props import is_sic_povm omega = np.exp(2j * np.pi / 3) sic_vectors = [ np.array([0, 1], dtype=np.complex128), np.array([np.sqrt(2/3), 1/np.sqrt(3)], dtype=np.complex128), np.array([np.sqrt(2/3), omega / np.sqrt(3)], dtype=np.complex128), np.array([np.sqrt(2/3), (omega**2) / np.sqrt(3)], dtype=np.complex128), ] print(is_sic_povm(sic_vectors)) ``` Non-SIC vector family. ```python exec="1" source="above" import numpy as np from toqito.state_props import is_sic_povm from toqito.states import basis e0, e1 = basis(2, 0), basis(2, 1) non_sic = [e0, e1, (e0 + e1) / np.sqrt(2), (e0 - e1) / np.sqrt(2)] print(is_sic_povm(non_sic)) ``` Raises: ValueError: If the vectors cannot represent valid quantum states. Args: states: Collection of vectors to test. tol: Numerical tolerance used for equality comparisons. Returns: `True` when the vectors form a SIC POVM and `False` otherwise. """ if not states: raise ValueError("At least one vector must be provided.") normalized_states = [normalize(state, tol=tol) for state in states] dimension = normalized_states[0].size if any(state.size != dimension for state in normalized_states): raise ValueError("All SIC vectors must have the same dimension.") if dimension == 0: raise ValueError("States must have non-zero dimension.") num_states = len(normalized_states) if dimension**2 != num_states: return False gram = vectors_to_gram_matrix(normalized_states) if not np.allclose(np.diag(gram), 1.0, atol=tol): return False target_overlap = 1.0 / (dimension + 1.0) off_diag_mask = ~np.eye(num_states, dtype=bool) off_diag_values = np.abs(gram) ** 2 if not np.allclose(off_diag_values[off_diag_mask], target_overlap, atol=tol): return False frame_operator = np.zeros((dimension, dimension), dtype=np.complex128) for state in normalized_states: frame_operator += np.outer(state, state.conj()) if not np.allclose(frame_operator, dimension * np.eye(dimension, dtype=np.complex128), atol=tol): return False return True