Source code for povm_toolbox.quantum_info.base.base_frame

# (C) Copyright IBM 2024.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""BaseFrame."""

from __future__ import annotations

import math
from abc import ABC, abstractmethod
from typing import Generic, TypeVar

import numpy as np
from qiskit.quantum_info import Operator, SparsePauliOp

LabelT = TypeVar("LabelT")
"""Each operator in the frame is identified by a label.

This is the type of these labels. For instance, labels could be strings, integers, or it could be
tuples of integers among other possibilities.
"""


[docs] class BaseFrame(ABC, Generic[LabelT]): """Abstract base class that contains all methods that any specific frame should implement. A frame is a generalization of the notion of the basis of a vector space to sets that may not necessarily be linearly independent. Consider a Hilbert space of finite dimension :math:`d`, then the set of Hermitian operators is an operator-valued vector space on the Hilbert space. Therefore, a set of Hermitian operators that spans the entire Hilbert space is said to be a frame. If a set of operators does not span the entire Hilbert space, it can still be considered as a frame on the subspace it spans. """ @property @abstractmethod def informationally_complete(self) -> bool: """If the frame spans the entire Hilbert space.""" @property @abstractmethod def dimension(self) -> int: """The dimension of the Hilbert space on which the frame operators act.""" @property def num_subsystems(self) -> int: r"""The number of subsystems which the frame operators act on. For qubits, this is always :math:`\log_2(`:attr:`.dimension`:math:`)`. """ return int(math.log2(self.dimension)) @property @abstractmethod def num_operators(self) -> int: """The number of frame operators of the frame.""" @abstractmethod def _check_validity(self) -> None: """Check if frame axioms are fulfilled.""" @abstractmethod def __len__(self) -> int: """Return the number of frame operators of the POVM."""
[docs] @abstractmethod def analysis( self, hermitian_op: SparsePauliOp | Operator, frame_op_idx: LabelT | set[LabelT] | None = None, ) -> float | dict[LabelT, float] | np.ndarray: r"""Return the frame coefficients of ``hermitian_op``. This method implements the *analysis operator* :math:`A` of the frame :math:`\{F_k\}_k`: .. math:: A: \mathcal{O} \mapsto \{ \mathrm{Tr}\left[F_k \mathcal{O} \right] \}_k, where :math:`c_k = \mathrm{Tr}\left[F_k \mathcal{O} \right]` are called the *frame coefficients* of the Hermitian operator :math:`\mathcal{O}`. Args: hermitian_op: a hermitian operator whose frame coefficients to compute. frame_op_idx: label or set of labels indicating which coefficients are queried. If ``None``, all coefficients are queried. Returns: Frame coefficients, specified by ``frame_op_idx``, of the Hermitian operator ``hermitian_op``. If a specific coefficient was queried, a ``float`` is returned. If a specific set of coefficients was queried, a dictionary mapping labels to coefficients is returned. If all coefficients were queried, an array with all coefficients is returned. Raises: TypeError: when the provided single or sequence of labels ``frame_op_idx`` does not have a valid type. ValueError: when the dimension of the provided ``hermitian_op`` does not match the dimension of the frame operators. """