diff --git a/src/qce_circuit/connectivity/generic_gate_sequence.py b/src/qce_circuit/connectivity/generic_gate_sequence.py index 1c58043..c3e8b1c 100644 --- a/src/qce_circuit/connectivity/generic_gate_sequence.py +++ b/src/qce_circuit/connectivity/generic_gate_sequence.py @@ -1,9 +1,13 @@ # ------------------------------------------- # Module containing interface and implementation of generic (Surface17) gate sequences. # ------------------------------------------- -from abc import ABCMeta, abstractmethod -from typing import List, Union -from qce_circuit.utilities.custom_exceptions import ElementNotIncludedException, InterfaceMethodException +import warnings +from abc import ABC, ABCMeta, abstractmethod +from typing import List, Union, Optional, Dict +from qce_circuit.utilities.custom_exceptions import ( + ElementNotIncludedException, + InterfaceMethodException, +) from qce_circuit.utilities.array_manipulation import unique_in_order from qce_circuit.connectivity.intrf_channel_identifier import ( IQubitID, @@ -14,6 +18,7 @@ ISurfaceCodeLayer, IParityGroup, FrequencyGroupIdentifier, + StabilizerType, ) from qce_circuit.connectivity.intrf_connectivity_gate_sequence import ( IGateSequenceLayer, @@ -22,6 +27,46 @@ from qce_circuit.connectivity.connectivity_surface_code import Surface17Layer +class ILogicalObservable(ABC): + """ + Interface class, describing logical observable. + """ + + # region Interface Properties + @property + @abstractmethod + def observable_basis(self) -> StabilizerType: + """:return: (StabilizerType) basis of logical observable.""" + raise InterfaceMethodException + + @property + @abstractmethod + def involved_data_qubit_ids(self) -> List[IQubitID]: + """:return: Array-like of data-qubit ID's involved in observable.""" + raise InterfaceMethodException + + @property + @abstractmethod + def concordant_stabilizer_qubit_ids(self) -> List[IQubitID]: + """ + :return: Array-like of ancilla-qubit ID's that describe the stabilizers which are concordant with the observable. + """ + raise InterfaceMethodException + # endregion + + # region Interface Methods + @abstractmethod + def contains(self, element: IQubitID) -> bool: + """:return: True if element is contained in logical observable, else False.""" + raise InterfaceMethodException + + @abstractmethod + def get_projection_basis(self, qubit_id: IQubitID) -> StabilizerType: + """:return: Projection basis per qubit in observable.""" + raise InterfaceMethodException + # endregion + + class IGenericSurfaceCodeLayer(ISurfaceCodeLayer, IGateSequenceLayer, metaclass=ABCMeta): """ Interface class, combining ISurfaceCodeLayer and IGateSequenceLayer. @@ -35,6 +80,63 @@ def get_gate_sequence_indices(self, parity_group: IParityGroup) -> List[int]: Returns an empty list if parity-group is not present. """ raise InterfaceMethodException + + @abstractmethod + def get_logical_observable(self, basis: StabilizerType) -> ILogicalObservable: + """:return: Logical observable corresponding to basis.""" + raise InterfaceMethodException + # endregion + + +class LogicalObservable(ILogicalObservable): + """ + Data class, implementing ILogicalObservable interface. + """ + + # region Interface Properties + @property + def observable_basis(self) -> StabilizerType: + """:return: (StabilizerType) basis of logical observable.""" + return self._observable_basis + + @property + def involved_data_qubit_ids(self) -> List[IQubitID]: + """:return: Array-like of data-qubit ID's involved in observable.""" + return unique_in_order(self._data_qubit_projections.keys()) + + @property + def concordant_stabilizer_qubit_ids(self) -> List[IQubitID]: + """ + :return: Array-like of ancilla-qubit ID's that describe the stabilizers which are concordant with the observable. + """ + return self._supporting_stabilizers + # endregion + + # region Class Constructor + def __init__( + self, + observable_basis: StabilizerType, + data_qubit_projections: Dict[IQubitID, StabilizerType], + supporting_stabilizers: List[IQubitID], + default_projections: StabilizerType = StabilizerType.STABILIZER_Z, + ): + self._observable_basis: StabilizerType = observable_basis + self._data_qubit_projections: Dict[IQubitID, StabilizerType] = data_qubit_projections + self._supporting_stabilizers: List[IQubitID] = supporting_stabilizers + self._default_projections: StabilizerType = default_projections + # endregion + + # region Interface Methods + def contains(self, element: IQubitID) -> bool: + """:return: True if element is contained in logical observable, else False.""" + return element in self._data_qubit_projections + + def get_projection_basis(self, qubit_id: IQubitID) -> StabilizerType: + """:return: Projection basis per qubit in observable.""" + if qubit_id not in self._data_qubit_projections: + # raise ObservableElementNotIncludedException(f"{qubit_id} is not in logical observable. Use one of self.involved_data_qubit_ids.") + return self._default_projections + return self._data_qubit_projections[qubit_id] # endregion @@ -100,10 +202,16 @@ def edge_ids(self) -> List[IEdgeID]: # endregion # region Class Constructor - def __init__(self, gate_sequences: List[GateSequenceLayer], parity_group_z: List[IParityGroup], parity_group_x: List[IParityGroup], surface_code_layer: ISurfaceCodeLayer = Surface17Layer()): + def __init__(self, gate_sequences: List[GateSequenceLayer], parity_group_z: List[IParityGroup], parity_group_x: List[IParityGroup], logical_observables: Optional[List[ILogicalObservable]] = None, surface_code_layer: ISurfaceCodeLayer = Surface17Layer()): self._gate_sequences: List[GateSequenceLayer] = gate_sequences self._parity_group_z: List[IParityGroup] = parity_group_z self._parity_group_x: List[IParityGroup] = parity_group_x + if logical_observables is None: + logical_observables = [] + self._logical_observables: Dict[StabilizerType, ILogicalObservable] = { + logical_observable.observable_basis: logical_observable + for logical_observable in logical_observables + } self._surface_code_layer: ISurfaceCodeLayer = surface_code_layer # endregion @@ -125,6 +233,21 @@ def get_gate_sequence_indices(self, parity_group: IParityGroup) -> List[int]: continue return result + + def get_logical_observable(self, basis: StabilizerType) -> ILogicalObservable: + """:return: Logical observable corresponding to basis.""" + default_fallback: bool = basis not in self._logical_observables + if default_fallback: + warnings.warn(f"Logical observable for basis {basis.name} is not defined in GenericSurfaceCode. Defaulting to {StabilizerType.STABILIZER_Z.name} for all data qubit ID's.") + return LogicalObservable( + observable_basis=StabilizerType.STABILIZER_Z, + data_qubit_projections={ + data_qubit_id: StabilizerType.STABILIZER_Z + for data_qubit_id in self.data_qubit_ids + }, + supporting_stabilizers=StabilizerType.STABILIZER_Z, + ) + return self._logical_observables[basis] # endregion # region IGateSequenceLayer Interface Methods diff --git a/src/qce_circuit/connectivity/intrf_connectivity_surface_code.py b/src/qce_circuit/connectivity/intrf_connectivity_surface_code.py index 0107ecb..b116d2f 100644 --- a/src/qce_circuit/connectivity/intrf_connectivity_surface_code.py +++ b/src/qce_circuit/connectivity/intrf_connectivity_surface_code.py @@ -17,6 +17,7 @@ class StabilizerType(Enum): STABILIZER_X = 0 STABILIZER_Z = 1 + STABILIZER_Y = 2 class IParityGroup(ABC): diff --git a/src/qce_circuit/language/intrf_declarative_circuit.py b/src/qce_circuit/language/intrf_declarative_circuit.py index 98dd05a..8476c36 100644 --- a/src/qce_circuit/language/intrf_declarative_circuit.py +++ b/src/qce_circuit/language/intrf_declarative_circuit.py @@ -7,6 +7,7 @@ from multipledispatch import dispatch from typing import List, Union, Dict, Optional, TypeVar, Generic from enum import Enum, unique +import math import numpy as np from numpy.typing import NDArray from qce_circuit.utilities.custom_exceptions import InterfaceMethodException @@ -49,6 +50,13 @@ class InitialStateEnum(Enum): MINUS_I = '-i' +@unique +class CodeDimension(Enum): + """Enum class, describing code dimensions.""" + REPETITION_CODE = "1D" + SURFACE_CODE = "2D" + + @dataclass(frozen=True) class InitialStateContainer(Generic[T]): """ @@ -58,10 +66,13 @@ class InitialStateContainer(Generic[T]): """Index pointers to data qubits only.""" ancilla_initial_states: Dict[T, InitialStateEnum] = field(default_factory=dict) """Index pointers to ancilla qubits only.""" + code_dimension: CodeDimension = field(default=CodeDimension.REPETITION_CODE) # region Class Properties @property def distance(self) -> int: + if self.code_dimension == CodeDimension.SURFACE_CODE: + return int(math.sqrt(len(self.initial_states))) return len(self.initial_states) @property @@ -149,7 +160,7 @@ def get_operation(self, qubit_index: T, initial_state: InitialStateEnum, **kwarg raise NotImplementedError(f"Initial state {initial_state} is not supported.") @classmethod - def from_ordered_list(cls, initial_states: List[InitialStateEnum], ancilla_initial_states: Optional[List[InitialStateEnum]] = None) -> 'InitialStateContainer': + def from_ordered_list(cls, initial_states: List[InitialStateEnum], ancilla_initial_states: Optional[List[InitialStateEnum]] = None, code_dimension: CodeDimension = CodeDimension.REPETITION_CODE) -> 'InitialStateContainer': """ :return: Class method constructor based on ordered array of initial state. Where each element index corresponds to qubit index. @@ -160,6 +171,7 @@ def from_ordered_list(cls, initial_states: List[InitialStateEnum], ancilla_initi return InitialStateContainer[int]( initial_states={i: state for i, state in enumerate(initial_states)}, ancilla_initial_states={i: state for i, state in enumerate(ancilla_initial_states)}, + code_dimension=code_dimension, ) @classmethod diff --git a/src/qce_circuit/library/repetition_code/repetition_code_connectivity.py b/src/qce_circuit/library/repetition_code/repetition_code_connectivity.py index 3d70b69..f17a5d7 100644 --- a/src/qce_circuit/library/repetition_code/repetition_code_connectivity.py +++ b/src/qce_circuit/library/repetition_code/repetition_code_connectivity.py @@ -9,10 +9,12 @@ from qce_circuit.connectivity.connectivity_surface_code import ( ParityGroup, StabilizerType, + SurfaceTuna17Layer, ) from qce_circuit.connectivity.generic_gate_sequence import ( IGenericSurfaceCodeLayer, GenericSurfaceCode, + LogicalObservable, ) from qce_circuit.connectivity.intrf_connectivity_gate_sequence import ( GateSequenceLayer, @@ -262,6 +264,117 @@ def __init__(self): # endregion +class Repetition9Round2Code(GenericSurfaceCode, IGenericSurfaceCodeLayer, metaclass=SingletonABCMeta): + + # region Class Constructor + def __init__(self): + super().__init__( + gate_sequences=[ + GateSequenceLayer( + _park_operations=[ + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('X4'), QubitIDObj('D8'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X3'), QubitIDObj('D7'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z3'), QubitIDObj('D4'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z1'), QubitIDObj('D5'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z4'), QubitIDObj('D6'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z2'), QubitIDObj('D3'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X2'), QubitIDObj('D2'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X1'), QubitIDObj('D1'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('X4'), QubitIDObj('D9'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X3'), QubitIDObj('D8'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z3'), QubitIDObj('D7'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z1'), QubitIDObj('D4'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z4'), QubitIDObj('D5'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z2'), QubitIDObj('D6'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X2'), QubitIDObj('D3'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X1'), QubitIDObj('D2'))), + ], + ), + ], + parity_group_z=[ + ParityGroup( + _parity_type=StabilizerType.STABILIZER_Z, + _ancilla_qubit=QubitIDObj('Z1'), + _data_qubits=[ QubitIDObj('D4'), QubitIDObj('D5')] + ), + ParityGroup( + _parity_type=StabilizerType.STABILIZER_Z, + _ancilla_qubit=QubitIDObj('Z2'), + _data_qubits=[QubitIDObj('D3'), QubitIDObj('D6')] + ), + ParityGroup( + _parity_type=StabilizerType.STABILIZER_Z, + _ancilla_qubit=QubitIDObj('Z3'), + _data_qubits=[QubitIDObj('D4'), QubitIDObj('D7')] + ), + ParityGroup( + _parity_type=StabilizerType.STABILIZER_Z, + _ancilla_qubit=QubitIDObj('Z4'), + _data_qubits=[QubitIDObj('D5'), QubitIDObj('D6')] + ), + ParityGroup( + _parity_type=StabilizerType.STABILIZER_Z, + _ancilla_qubit=QubitIDObj('X1'), + _data_qubits=[ QubitIDObj('D1'), QubitIDObj('D2')] + ), + ParityGroup( + _parity_type=StabilizerType.STABILIZER_Z, + _ancilla_qubit=QubitIDObj('X2'), + _data_qubits=[QubitIDObj('D2'), QubitIDObj('D3')] + ), + ParityGroup( + _parity_type=StabilizerType.STABILIZER_Z, + _ancilla_qubit=QubitIDObj('X3'), + _data_qubits=[QubitIDObj('D7'), QubitIDObj('D8')] + ), + ParityGroup( + _parity_type=StabilizerType.STABILIZER_Z, + _ancilla_qubit=QubitIDObj('X4'), + _data_qubits=[QubitIDObj('D8'), QubitIDObj('D9')] + ), + ], + parity_group_x=[ + ], + logical_observables=[ + LogicalObservable( + observable_basis=StabilizerType.STABILIZER_Z, + data_qubit_projections={ + QubitIDObj("D1"): StabilizerType.STABILIZER_Z, + QubitIDObj("D2"): StabilizerType.STABILIZER_Z, + QubitIDObj("D3"): StabilizerType.STABILIZER_Z, + QubitIDObj("D4"): StabilizerType.STABILIZER_Z, + QubitIDObj("D5"): StabilizerType.STABILIZER_Z, + QubitIDObj("D6"): StabilizerType.STABILIZER_Z, + QubitIDObj("D7"): StabilizerType.STABILIZER_Z, + QubitIDObj("D8"): StabilizerType.STABILIZER_Z, + QubitIDObj("D9"): StabilizerType.STABILIZER_Z, + }, + supporting_stabilizers=[ + QubitIDObj("Z1"), + QubitIDObj("Z2"), + QubitIDObj("Z3"), + QubitIDObj("Z4"), + QubitIDObj("X1"), + QubitIDObj("X2"), + QubitIDObj("X3"), + QubitIDObj("X4"), + ], + default_projections=StabilizerType.STABILIZER_Z + ) + ], + surface_code_layer=SurfaceTuna17Layer(), + ) + # endregion + + class Repetition5Round4Code(GenericSurfaceCode, IGenericSurfaceCodeLayer, metaclass=SingletonABCMeta): """ Data class, containing repetition code for distance-5 as ran for the error injection measurements (XX-11-2024) diff --git a/src/qce_circuit/library/surface_code/surface_code_connectivity.py b/src/qce_circuit/library/surface_code/surface_code_connectivity.py index 7d596ff..28d51fa 100644 --- a/src/qce_circuit/library/surface_code/surface_code_connectivity.py +++ b/src/qce_circuit/library/surface_code/surface_code_connectivity.py @@ -14,6 +14,7 @@ from qce_circuit.connectivity.generic_gate_sequence import ( IGenericSurfaceCodeLayer, GenericSurfaceCode, + LogicalObservable, ) from qce_circuit.connectivity.intrf_connectivity_gate_sequence import ( GateSequenceLayer, @@ -445,6 +446,228 @@ def __init__(self): _data_qubits=[QubitIDObj('D8'), QubitIDObj('D9')] ), ], + logical_observables=[ + LogicalObservable( + observable_basis=StabilizerType.STABILIZER_X, + data_qubit_projections={ + QubitIDObj("D1"): StabilizerType.STABILIZER_X, + QubitIDObj("D4"): StabilizerType.STABILIZER_X, + QubitIDObj("D7"): StabilizerType.STABILIZER_X, + }, + supporting_stabilizers=[ + QubitIDObj("X1"), + QubitIDObj("X2"), + QubitIDObj("X3"), + QubitIDObj("X4"), + ], + default_projections=StabilizerType.STABILIZER_X + ), + LogicalObservable( + observable_basis=StabilizerType.STABILIZER_Y, + data_qubit_projections={ + QubitIDObj("D1"): StabilizerType.STABILIZER_X, + QubitIDObj("D4"): StabilizerType.STABILIZER_X, + QubitIDObj("D7"): StabilizerType.STABILIZER_Y, + QubitIDObj("D8"): StabilizerType.STABILIZER_Z, + QubitIDObj("D9"): StabilizerType.STABILIZER_Z, + }, + supporting_stabilizers=[ + QubitIDObj("Z4"), + QubitIDObj("Z2"), + ], + default_projections=StabilizerType.STABILIZER_Z + ), + LogicalObservable( + observable_basis=StabilizerType.STABILIZER_Z, + data_qubit_projections={ + QubitIDObj("D7"): StabilizerType.STABILIZER_Z, + QubitIDObj("D8"): StabilizerType.STABILIZER_Z, + QubitIDObj("D9"): StabilizerType.STABILIZER_Z, + }, + supporting_stabilizers=[ + QubitIDObj("Z1"), + QubitIDObj("Z2"), + QubitIDObj("Z3"), + QubitIDObj("Z4"), + ], + default_projections=StabilizerType.STABILIZER_Z + ) + ], + surface_code_layer=SurfaceTuna17Layer(), + ) + # endregion + + +class Surface17Round8BCode(GenericSurfaceCode, IGenericSurfaceCodeLayer, metaclass=SingletonABCMeta): + + # region Class Constructor + def __init__(self): + super().__init__( + gate_sequences=[ + GateSequenceLayer( + _park_operations=[ + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('X3'), QubitIDObj('D7'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X2'), QubitIDObj('D5'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X1'), QubitIDObj('D1'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('X3'), QubitIDObj('D8'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X2'), QubitIDObj('D6'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X1'), QubitIDObj('D2'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('X3'), QubitIDObj('D4'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X2'), QubitIDObj('D2'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X4'), QubitIDObj('D8'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('X3'), QubitIDObj('D5'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X2'), QubitIDObj('D3'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X4'), QubitIDObj('D9'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('Z4'), QubitIDObj('D8'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z1'), QubitIDObj('D4'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z2'), QubitIDObj('D6'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('Z4'), QubitIDObj('D5'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z1'), QubitIDObj('D1'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z2'), QubitIDObj('D3'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('Z4'), QubitIDObj('D9'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z1'), QubitIDObj('D5'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z3'), QubitIDObj('D7'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('Z4'), QubitIDObj('D6'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z1'), QubitIDObj('D2'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z3'), QubitIDObj('D4'))), + ], + ), + ], + parity_group_z=[ + ParityGroup( + _parity_type=StabilizerType.STABILIZER_Z, + _ancilla_qubit=QubitIDObj('Z1'), + _data_qubits=[QubitIDObj('D1'), QubitIDObj('D2'), QubitIDObj('D4'), QubitIDObj('D5')] + ), + ParityGroup( + _parity_type=StabilizerType.STABILIZER_Z, + _ancilla_qubit=QubitIDObj('Z2'), + _data_qubits=[QubitIDObj('D3'), QubitIDObj('D6')] + ), + ParityGroup( + _parity_type=StabilizerType.STABILIZER_Z, + _ancilla_qubit=QubitIDObj('Z3'), + _data_qubits=[QubitIDObj('D4'), QubitIDObj('D7')] + ), + ParityGroup( + _parity_type=StabilizerType.STABILIZER_Z, + _ancilla_qubit=QubitIDObj('Z4'), + _data_qubits=[QubitIDObj('D5'), QubitIDObj('D6'), QubitIDObj('D8'), QubitIDObj('D9')] + ), + ], + parity_group_x=[ + ParityGroup( + _parity_type=StabilizerType.STABILIZER_X, + _ancilla_qubit=QubitIDObj('X1'), + _data_qubits=[QubitIDObj('D1'), QubitIDObj('D2')] + ), + ParityGroup( + _parity_type=StabilizerType.STABILIZER_X, + _ancilla_qubit=QubitIDObj('X2'), + _data_qubits=[QubitIDObj('D2'), QubitIDObj('D3'), QubitIDObj('D5'), QubitIDObj('D6')] + ), + ParityGroup( + _parity_type=StabilizerType.STABILIZER_X, + _ancilla_qubit=QubitIDObj('X3'), + _data_qubits=[QubitIDObj('D4'), QubitIDObj('D5'), QubitIDObj('D7'), QubitIDObj('D8')] + ), + ParityGroup( + _parity_type=StabilizerType.STABILIZER_X, + _ancilla_qubit=QubitIDObj('X4'), + _data_qubits=[QubitIDObj('D8'), QubitIDObj('D9')] + ), + ], + logical_observables=[ + LogicalObservable( + observable_basis=StabilizerType.STABILIZER_X, + data_qubit_projections={ + QubitIDObj("D1"): StabilizerType.STABILIZER_X, + QubitIDObj("D4"): StabilizerType.STABILIZER_X, + QubitIDObj("D7"): StabilizerType.STABILIZER_X, + }, + supporting_stabilizers=[ + QubitIDObj("X1"), + QubitIDObj("X2"), + QubitIDObj("X3"), + QubitIDObj("X4"), + ], + default_projections=StabilizerType.STABILIZER_X + ), + LogicalObservable( + observable_basis=StabilizerType.STABILIZER_Y, + data_qubit_projections={ + QubitIDObj("D1"): StabilizerType.STABILIZER_X, + QubitIDObj("D4"): StabilizerType.STABILIZER_X, + QubitIDObj("D7"): StabilizerType.STABILIZER_Y, + QubitIDObj("D8"): StabilizerType.STABILIZER_Z, + QubitIDObj("D9"): StabilizerType.STABILIZER_Z, + }, + supporting_stabilizers=[ + QubitIDObj("Z4"), + QubitIDObj("Z2"), + ], + default_projections=StabilizerType.STABILIZER_Z + ), + LogicalObservable( + observable_basis=StabilizerType.STABILIZER_Z, + data_qubit_projections={ + QubitIDObj("D7"): StabilizerType.STABILIZER_Z, + QubitIDObj("D8"): StabilizerType.STABILIZER_Z, + QubitIDObj("D9"): StabilizerType.STABILIZER_Z, + }, + supporting_stabilizers=[ + QubitIDObj("Z1"), + QubitIDObj("Z2"), + QubitIDObj("Z3"), + QubitIDObj("Z4"), + ], + default_projections=StabilizerType.STABILIZER_Z + ) + ], surface_code_layer=SurfaceTuna17Layer(), ) # endregion diff --git a/src/qce_circuit/structure/circuit_operations.py b/src/qce_circuit/structure/circuit_operations.py index 4c7ca7d..2384bc4 100644 --- a/src/qce_circuit/structure/circuit_operations.py +++ b/src/qce_circuit/structure/circuit_operations.py @@ -595,6 +595,37 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu # endregion + +@dataclass(frozen=False, unsafe_hash=True) +class RPhiTheta(SingleQubitOperation, ICircuitOperation): + """ + Rotation- [Xcos(phi) + Ysin(phi)] (Theta degrees) operation. + """ + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) + + # region Interface Properties + @property + def channel_identifiers(self) -> List[ChannelIdentifier]: + """:return: Array-like of channel identifiers to which this operation applies to.""" + return [ + ChannelIdentifier(_id=self.qubit_index, _channel=QubitChannel.MICROWAVE), + ] + # endregion + + # region Interface Methods + def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircuitOperation]] = None) -> 'RPhiTheta': + """ + Creates a copy from self. Excluding any relation details. + :param relation_transfer_lookup: Lookup table used to transfer relation link. + :return: Copy of self with updated relation link. + """ + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) + ) + # endregion + + @dataclass(frozen=False, unsafe_hash=True) class TwoQubitOperation(ITwoQubitOperation): """ diff --git a/src/qce_circuit/visualization/visualize_circuit/display_circuit.py b/src/qce_circuit/visualization/visualize_circuit/display_circuit.py index 95ed5c7..6ca7dfd 100644 --- a/src/qce_circuit/visualization/visualize_circuit/display_circuit.py +++ b/src/qce_circuit/visualization/visualize_circuit/display_circuit.py @@ -18,6 +18,7 @@ Ry90, Rym90, RyTheta, + RPhiTheta, Rx180ef, VirtualPhase, Rphi90, @@ -103,6 +104,7 @@ Ry90Factory, Rym90Factory, RyThetaFactory, + RPhiThetaFactory, Rx180efFactory, ZPhaseFactory, Rphi90Factory, @@ -241,6 +243,7 @@ def get_operation_draw_components(self) -> List[IDrawComponent]: Ry90: Ry90Factory(minimalist=minimalist), Rym90: Rym90Factory(minimalist=minimalist), RyTheta: RyThetaFactory(), + RPhiTheta: RPhiThetaFactory(), Rx180ef: Rx180efFactory(minimalist=minimalist), Rphi90: Rphi90Factory(), VirtualPhase: ZPhaseFactory(), diff --git a/src/qce_circuit/visualization/visualize_circuit/draw_components/factory_draw_components.py b/src/qce_circuit/visualization/visualize_circuit/draw_components/factory_draw_components.py index 65aef74..d807975 100644 --- a/src/qce_circuit/visualization/visualize_circuit/draw_components/factory_draw_components.py +++ b/src/qce_circuit/visualization/visualize_circuit/draw_components/factory_draw_components.py @@ -22,6 +22,7 @@ Ry90, Rym90, RyTheta, + RPhiTheta, VirtualPhase, Reset, Wait, @@ -60,6 +61,7 @@ RotationAngle, SquareParkBlock, SquareNetZeroParkBlock, + DualityBlock, ) from qce_circuit.visualization.visualize_circuit.draw_components.multi_pivot_components import ( BlockTwoQubitGate, @@ -129,6 +131,7 @@ def construct(self, operation: Rx180, transform_constructor: ITransformConstruct if self.minimalist: return BlockHeaderBody( pivot=transform.pivot, + width=transform.width, height=transform.height, alignment=transform.parent_alignment, header_text=f"{RotationAxis.X.value}", @@ -136,6 +139,7 @@ def construct(self, operation: Rx180, transform_constructor: ITransformConstruct return BlockRotation( pivot=transform.pivot, + width=transform.width, height=transform.height, alignment=transform.parent_alignment, rotation_axes=RotationAxis.X, @@ -335,6 +339,25 @@ def construct(self, operation: RyTheta, transform_constructor: ITransformConstru # endregion +class RPhiThetaFactory(IOperationDrawComponentFactory[RPhiTheta, IDrawComponent]): + + # region Interface Methods + def construct(self, operation: RPhiTheta, transform_constructor: ITransformConstructor) -> IDrawComponent: + """:return: Draw component based on operation type.""" + transform: IRectTransform = transform_constructor.construct_transform( + identifier=operation.channel_identifiers[0], + time_component=operation, + ) + return BlockRotation( + pivot=transform.pivot, + height=transform.height, + alignment=transform.parent_alignment, + rotation_axes=RotationAxis.PHI, + rotation_angle=RotationAngle.THETA, + ) + # endregion + + @dataclass(frozen=True) class Rx180efFactory(IOperationDrawComponentFactory[Rx180, IDrawComponent]): minimalist: bool = field(default=False) @@ -716,14 +739,45 @@ def __init__(self, callback_draw_manager: IOperationDrawComponentFactoryManager) # region Interface Methods def construct(self, operation: VirtualInjectedError, transform_constructor: ITransformConstructor) -> IDrawComponent: """:return: Draw component based on operation type.""" - with StyleManager.temporary_override(**dict( - line_style_border=operation.line_style_border_overwrite, - color_background=operation.color_background_overwrite, - )): - draw_component: IDrawComponent = self._factory_manager.construct( - operation=operation.operation, - transform_constructor=transform_constructor, - ) + if isinstance(operation.operation, Identity): + with StyleManager.temporary_override(**dict( + line_style_border=operation.line_style_border_overwrite, + color_background="white", + )): + transform: IRectTransform = transform_constructor.construct_transform( + identifier=operation.operation.channel_identifiers[0], + time_component=operation.operation, + ) + draw_component: IDrawComponent = DualityBlock( + pivot=transform.pivot, + width=transform.width, + height=transform.height, + text_left=f"${RotationAxis.X.value}$", + text_right=f"$I$", + alignment=transform.parent_alignment, + ) + else: + with StyleManager.temporary_override(**dict( + line_style_border=operation.line_style_border_overwrite, + color_background=operation.color_background_overwrite, + )): + # Special exception coherent/stochastic error + if isinstance(operation.operation, Rx180): + transform: IRectTransform = transform_constructor.construct_transform( + identifier=operation.operation.channel_identifiers[0], + time_component=operation.operation, + ) + draw_component: IDrawComponent = DualityBlock( + pivot=transform.pivot, + width=transform.width, + height=transform.height, + alignment=transform.parent_alignment, + ) + else: + draw_component: IDrawComponent = self._factory_manager.construct( + operation=operation.operation, + transform_constructor=transform_constructor, + ) return draw_component # endregion diff --git a/src/qce_circuit/visualization/visualize_circuit/draw_components/operation_components.py b/src/qce_circuit/visualization/visualize_circuit/draw_components/operation_components.py index 603289a..2c192f0 100644 --- a/src/qce_circuit/visualization/visualize_circuit/draw_components/operation_components.py +++ b/src/qce_circuit/visualization/visualize_circuit/draw_components/operation_components.py @@ -517,3 +517,98 @@ def draw(self, axes: plt.Axes) -> plt.Axes: return axes # endregion + + +@dataclass(frozen=True) +class DualityBlock(IRectTransformComponent, IDrawComponent): + """ + Data class, containing dimension data for drawing schedule block. + """ + pivot: Vec2D + width: float + height: float + face_color_left: str = field(default="#37CBDB") + text_left: str = field(default=f"${RotationAxis.X.value}_{{p}}$") + text_right: str = field(default=rf'$\mathtt{{R_{{{RotationAxis.X.value}}}{{{RotationAngle.THETA.value}}}}}$') + alignment: TransformAlignment = field(default=TransformAlignment.MID_LEFT) + style_settings: OperationStyleSettings = field(default_factory=lambda: StyleManager.read_config().operation_style) + _base_block: RectangleBlock = field(init=False) + + # region Interface Properties + @property + def rectilinear_transform(self) -> IRectTransform: + """:return: 'Hard' rectilinear transform boundary. Should be treated as 'personal zone'.""" + return self._base_block.rectilinear_transform + # endregion + + # region Class Methods + def draw(self, axes: plt.Axes) -> plt.Axes: + """Method used for drawing component on Axes.""" + axes = self._base_block.draw(axes=axes) + + origin_x, origin_y = self.rectilinear_transform.origin_pivot.to_tuple() + w = self.rectilinear_transform.width + h = self.rectilinear_transform.height + x_offset = 0.05 + vertices = [ + (origin_x, origin_y), # (0.0, 0.0) + (origin_x + (0.5 - x_offset) * w, origin_y), # (0.5-x, 0.0) + (origin_x + (0.5 + x_offset) * w, origin_y + h), # (0.5+x, 1.0) + (origin_x, origin_y + h) # (0.0, 1.0) + ] + + # Create the Polygon patch + polygon = patches.Polygon( + xy=vertices, + closed=True, + linewidth=self.style_settings.border_width, + linestyle=self.style_settings.border_line_style, + edgecolor=self.style_settings.border_color, + facecolor=self.face_color_left, # self.style_settings.background_color, + zorder=-1, + ) + + # PlotErrorType.COHERENT_SIMULATION: "#DB379A", # #DB8D37 (#DB379A) + # PlotErrorType.STOCHASTIC_SIMULATION: "#37CBDB", # #7537DB (#37CBDB) + axes.add_patch(polygon) + + # Draw X + text_center = Vec2D.from_vector(0.5 * ( + self.rectilinear_transform.center_pivot.to_vector() + + self.rectilinear_transform.left_pivot.to_vector()) + ) + axes.text( + x=text_center.x, + y=text_center.y, + s=self.text_left, + fontsize=self.style_settings.font_size, + color=self.style_settings.text_color, + ha='center', + va='center', + ) + # Draw RX + text_center = Vec2D.from_vector(0.5 * ( + self.rectilinear_transform.center_pivot.to_vector() + + self.rectilinear_transform.right_pivot.to_vector()) + ) + axes.text( + x=text_center.x, + y=text_center.y, + s=self.text_right, + fontsize=self.style_settings.font_size, + color=self.style_settings.text_color, + ha='center', + va='center', + ) + + return axes + + def __post_init__(self): + object.__setattr__(self, '_base_block', RectangleBlock( + pivot=self.pivot, + width=self.width, + height=self.height, + alignment=self.alignment, + style_settings=self.style_settings, + )) + # endregion diff --git a/src/qce_circuit/visualization/visualize_layout/element_components.py b/src/qce_circuit/visualization/visualize_layout/element_components.py index 42add11..ad35e35 100644 --- a/src/qce_circuit/visualization/visualize_layout/element_components.py +++ b/src/qce_circuit/visualization/visualize_layout/element_components.py @@ -189,7 +189,7 @@ def draw(self, axes: plt.Axes) -> plt.Axes: s=self.text, color=self.color, fontsize=self.style_settings.font_size, - weight='bold', + fontweight='bold', ha='center', va='center', zorder=self.style_settings.zorder,