Source code for graphix.command

"""Data validator command classes."""

from __future__ import annotations

import dataclasses
import enum
import logging
from enum import Enum
from typing import ClassVar, Literal

from graphix import utils
from graphix.clifford import Clifford, Domains
from graphix.measurements import Measurement
from graphix.repr_mixins import DataclassReprMixin
from graphix.states import BasicStates, State

Node = int

logger = logging.getLogger(__name__)


[docs] class CommandKind(Enum): """Tag for command kind.""" N = enum.auto() M = enum.auto() E = enum.auto() C = enum.auto() X = enum.auto() Z = enum.auto() S = enum.auto() T = enum.auto() ApplyNoise = enum.auto() # see noise_models/noise_model.py
class _KindChecker: """Enforce tag field declaration.""" def __init_subclass__(cls) -> None: super().__init_subclass__() utils.check_kind(cls, {"CommandKind": CommandKind, "Clifford": Clifford}) class BaseCommand(DataclassReprMixin): """Base class for pattern command.""" @dataclasses.dataclass(repr=False) class BaseN(BaseCommand): r"""Base preparation command. Represent a preparation of a node. In `graphix`, a preparation is an instance of class `N`, with an initial state (defaults to :class:`~graphix.states.BasicStates.PLUS`). The base class `BaseN` allows users to define new class of preparation commands with different abstractions. For example, in the context of blind computations, the server only knows which node is prepared, and the initial state are given by the :class:`graphix.simulator.PrepareMethod` provided by the client. Parameters ---------- node : int Index of the qubit to prepare. """ node: int kind: ClassVar[Literal[CommandKind.N]] = dataclasses.field(default=CommandKind.N, init=False)
[docs] @dataclasses.dataclass(repr=False) class N(BaseN, _KindChecker): r"""Preparation command. Parameters ---------- node : int Index of the qubit to prepare. state : ~graphix.states.State, optional Initial state, defaults to :class:`~graphix.states.BasicStates.PLUS`. """ state: State = dataclasses.field(default_factory=lambda: BasicStates.PLUS) kind: ClassVar[Literal[CommandKind.N]] = dataclasses.field(default=CommandKind.N, init=False)
@dataclasses.dataclass(repr=False) class BaseM(BaseCommand): """Base measurement command. Represent a measurement of a node. In `graphix`, a measurement is an instance of class `M`, with given plane, angles, and domains. The base class `BaseM` allows users to define new class of measurements with different abstractions. For example, in the context of blind computations, the server only knows which node is measured, and the parameters are given by the :class:`graphix.simulator.MeasureMethod` provided by the client. """ node: Node kind: ClassVar[Literal[CommandKind.M]] = dataclasses.field(default=CommandKind.M, init=False)
[docs] @dataclasses.dataclass(repr=False) class M(BaseM, _KindChecker): r"""Measurement command. Parameters ---------- node : int Node index of the measured qubit. measurement : Measurement Measurement description. s_domain : set[int], optional Domain for the X byproduct operator. t_domain : set[int], optional Domain for the Z byproduct operator. """ measurement: Measurement = Measurement.X s_domain: set[Node] = dataclasses.field(default_factory=set) t_domain: set[Node] = dataclasses.field(default_factory=set) kind: ClassVar[Literal[CommandKind.M]] = dataclasses.field(default=CommandKind.M, init=False) def clifford(self, clifford_gate: Clifford) -> M: r"""Return a new measurement command with a Clifford applied. Parameters ---------- clifford_gate : ~graphix.clifford.Clifford Clifford gate to apply before the measurement. Returns ------- :class:`M` Equivalent command representing the pattern ``MC``. """ domains = clifford_gate.commute_domains(Domains(self.s_domain, self.t_domain)) return M( self.node, self.measurement.clifford(clifford_gate), domains.s_domain, domains.t_domain, )
[docs] @dataclasses.dataclass(repr=False) class E(_KindChecker, BaseCommand): r"""Entanglement command between two qubits. Parameters ---------- nodes : tuple[int, int] Pair of nodes to entangle. """ nodes: tuple[Node, Node] kind: ClassVar[Literal[CommandKind.E]] = dataclasses.field(default=CommandKind.E, init=False)
[docs] @dataclasses.dataclass(repr=False) class C(_KindChecker, BaseCommand): r"""Local Clifford gate command. Parameters ---------- node : int Node index on which to apply the gate. clifford : ~graphix.clifford.Clifford Clifford operator to apply. """ node: Node clifford: Clifford kind: ClassVar[Literal[CommandKind.C]] = dataclasses.field(default=CommandKind.C, init=False)
[docs] @dataclasses.dataclass(repr=False) class X(_KindChecker, BaseCommand): r"""X correction command. Parameters ---------- node : int Node to correct. domain : set[int], optional Domain for the byproduct operator. """ node: Node domain: set[Node] = dataclasses.field(default_factory=set) kind: ClassVar[Literal[CommandKind.X]] = dataclasses.field(default=CommandKind.X, init=False)
[docs] @dataclasses.dataclass(repr=False) class Z(_KindChecker, BaseCommand): r"""Z correction command. Parameters ---------- node : int Node to correct. domain : set[int], optional Domain for the byproduct operator. """ node: Node domain: set[Node] = dataclasses.field(default_factory=set) kind: ClassVar[Literal[CommandKind.Z]] = dataclasses.field(default=CommandKind.Z, init=False)
@dataclasses.dataclass(repr=False) class S(_KindChecker, BaseCommand): r"""S command. Parameters ---------- node : int Node for the byproduct operator. domain : set[int], optional Domain on which to apply the operator. """ node: Node domain: set[Node] = dataclasses.field(default_factory=set) kind: ClassVar[Literal[CommandKind.S]] = dataclasses.field(default=CommandKind.S, init=False) @dataclasses.dataclass(repr=False) class T(_KindChecker, BaseCommand): r"""T command. Parameters ---------- None The T command acts globally without parameters. """ kind: ClassVar[Literal[CommandKind.T]] = dataclasses.field(default=CommandKind.T, init=False) Command = N | M | E | C | X | Z | S | T Correction = X | Z