Source code for povm_toolbox.quantum_info.base.base_dual
# (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.
"""BaseDual."""
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Any
import numpy as np
from qiskit.quantum_info import Operator, SparsePauliOp
from .base_frame import BaseFrame, LabelT
[docs]
class BaseDual(BaseFrame[LabelT], ABC):
"""Abstract base class that contains all methods that any specific Dual should implement."""
@property
def num_outcomes(self) -> int:
"""The number of outcomes of the Dual."""
return self.num_operators
[docs]
def get_omegas(
self,
observable: SparsePauliOp | Operator,
outcome_idx: LabelT | set[LabelT] | None = None,
) -> float | dict[LabelT, float] | np.ndarray:
r"""Return the decomposition weights of the provided observable.
Computes the :math:`\omega_k` in
.. math::
\mathcal{O} = \sum_{k=1}^n \omega_k M_k
where :math:`\mathcal{O}` is the ``observable`` and :math:`M_k` are the effects of the POVM
of which ``self`` is the dual. The closed form for computing :math:`\omega_k` is
.. math::
\omega_k = \text{Tr}\left[\mathcal{O} D_k\right]
where :math:`D_k` make of this dual frame (i.e. ``self``).
.. note::
In the frame theory formalism, the mapping
:math:`A: \mathcal{O} \mapsto \{\text{Tr}\left[\mathcal{O} D_k\right]\}_k`
is referred to as the *analysis operator*, which is implemented by
the :meth:`.analysis` method.
Args:
observable: the observable for which to compute the decomposition weights.
outcome_idx: label or set of labels indicating which decomposition weights
are queried. If ``None``, all weights are queried.
Returns:
Decomposition weight(s) associated to the effect(s) specified by ``outcome_idx``. If a
specific outcome was queried, a ``float`` is returned. If a specific set of outcomes was
queried, a dictionary mapping outcome labels to weights is returned. If all outcomes
were queried, an array with all weights is returned.
"""
return self.analysis(observable, outcome_idx)
[docs]
@abstractmethod
def is_dual_to(self, frame: BaseFrame) -> bool:
"""Check if ``self`` is a dual to another frame.
Args:
frame: the other frame to check duality against.
Returns:
Whether ``self`` is dual to ``frame``.
"""
[docs]
@classmethod
@abstractmethod
def build_dual_from_frame(cls, frame: BaseFrame, alphas: tuple[Any] | None = None) -> BaseDual:
"""Construct a dual frame to another (primal) frame.
Args:
frame: The primal frame from which we will build the dual frame.
alphas: parameters of the frame super-operator used to build the dual frame.
If ``None``, the parameters are set as the traces of each operator in the primal
frame.
Returns:
A dual frame to the supplied ``frame``.
"""