Source code for graphix.measurements

"""Data structure for single-qubit measurements in MBQC."""

from __future__ import annotations

import math
from dataclasses import dataclass
from typing import (
    Literal,
    NamedTuple,
    SupportsInt,
    TypeAlias,
)

from graphix import utils
from graphix.fundamentals import AbstractPlanarMeasurement, Axis, Plane, Sign

# Ruff suggests to move this import to a type-checking block, but dataclass requires it here
from graphix.parameter import ExpressionOrFloat  # noqa: TC001

Outcome: TypeAlias = Literal[0, 1]


def outcome(b: bool) -> Outcome:
    """Return 1 if True, 0 if False."""
    return 1 if b else 0


def toggle_outcome(outcome: Outcome) -> Outcome:
    """Toggle outcome."""
    return 1 if outcome == 0 else 0


@dataclass
class Domains:
    """Represent `X^sZ^t` where s and t are XOR of results from given sets of indices."""

    s_domain: set[int]
    t_domain: set[int]


[docs] @dataclass class Measurement(AbstractPlanarMeasurement): r"""An MBQC measurement. Attributes ---------- angle : Expressionor Float The angle of the measurement in units of :math:`\pi`. Should be between [0, 2). plane : graphix.fundamentals.Plane The measurement plane. """ angle: ExpressionOrFloat plane: Plane
[docs] def isclose(self, other: Measurement, rel_tol: float = 1e-09, abs_tol: float = 0.0) -> bool: """Compare if two measurements have the same plane and their angles are close. Example ------- >>> from graphix.measurements import Measurement >>> from graphix.fundamentals import Plane >>> Measurement(0.0, Plane.XY).isclose(Measurement(0.0, Plane.XY)) True >>> Measurement(0.0, Plane.XY).isclose(Measurement(0.0, Plane.YZ)) False >>> Measurement(0.1, Plane.XY).isclose(Measurement(0.0, Plane.XY)) False """ return ( math.isclose(self.angle, other.angle, rel_tol=rel_tol, abs_tol=abs_tol) if isinstance(self.angle, float) and isinstance(other.angle, float) else self.angle == other.angle ) and self.plane == other.plane
[docs] def to_plane_or_axis(self) -> Plane | Axis: """Return the measurements's plane or axis. Returns ------- Plane | Axis Notes ----- Measurements with Pauli angles (i.e., ``self.angle == n/2`` with ``n`` an integer) are interpreted as `Axis` instances. """ if pm := PauliMeasurement.try_from(self.plane, self.angle): return pm.axis return self.plane
[docs] def to_plane(self) -> Plane: """Return the measurement's plane. Returns ------- Plane """ return self.plane
class PauliMeasurement(NamedTuple): """Pauli measurement.""" axis: Axis sign: Sign @staticmethod def try_from(plane: Plane, angle: ExpressionOrFloat) -> PauliMeasurement | None: """Return the Pauli measurement description if a given measure is Pauli.""" angle_double = 2 * angle if not isinstance(angle_double, SupportsInt) or not utils.is_integer(angle_double): return None angle_double_mod_4 = int(angle_double) % 4 axis = plane.cos if angle_double_mod_4 % 2 == 0 else plane.sin sign = Sign.minus_if(angle_double_mod_4 >= 2) return PauliMeasurement(axis, sign)