From 5445d90716a26d44d1ac474c6828226802e644c2 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Sat, 28 Feb 2026 12:58:43 +0100 Subject: [PATCH 1/7] Updated include Y stabilizer typing --- src/qce_circuit/connectivity/intrf_connectivity_surface_code.py | 1 + 1 file changed, 1 insertion(+) 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): From 05d6895fcd3898150a3a37885c75491377ab0817 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Sun, 1 Mar 2026 20:44:26 +0100 Subject: [PATCH 2/7] Added logical observable data structure and interface for error detection and correction generalization --- .../connectivity/generic_gate_sequence.py | 130 +++++++++++++++++- .../visualize_layout/element_components.py | 2 +- 2 files changed, 127 insertions(+), 5 deletions(-) diff --git a/src/qce_circuit/connectivity/generic_gate_sequence.py b/src/qce_circuit/connectivity/generic_gate_sequence.py index 1c58043..5de8c6e 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,20 @@ 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 + } + ) + return self._logical_observables[basis] # endregion # region IGateSequenceLayer Interface Methods 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, From 6dad9966e73784b379d75ec68a7243ee5ff1606e Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Sun, 1 Mar 2026 23:27:26 +0100 Subject: [PATCH 3/7] Added y-Theta and Phi-Theta circuit operations and draw factories --- .../structure/circuit_operations.py | 31 +++++++++++++++++++ .../visualize_circuit/display_circuit.py | 3 ++ .../factory_draw_components.py | 20 ++++++++++++ 3 files changed, 54 insertions(+) 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..634030e 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, @@ -335,6 +336,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) From 47d3579ea3d123198bd81d4390ced799c0b7aa50 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Tue, 10 Mar 2026 17:01:12 +0100 Subject: [PATCH 4/7] Added operator duality block for visualization --- .../connectivity/generic_gate_sequence.py | 3 +- .../factory_draw_components.py | 25 ++++- .../draw_components/operation_components.py | 92 +++++++++++++++++++ 3 files changed, 115 insertions(+), 5 deletions(-) diff --git a/src/qce_circuit/connectivity/generic_gate_sequence.py b/src/qce_circuit/connectivity/generic_gate_sequence.py index 5de8c6e..c3e8b1c 100644 --- a/src/qce_circuit/connectivity/generic_gate_sequence.py +++ b/src/qce_circuit/connectivity/generic_gate_sequence.py @@ -244,7 +244,8 @@ def get_logical_observable(self, basis: StabilizerType) -> ILogicalObservable: 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 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 634030e..2459670 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 @@ -61,6 +61,7 @@ RotationAngle, SquareParkBlock, SquareNetZeroParkBlock, + DualityBlock, ) from qce_circuit.visualization.visualize_circuit.draw_components.multi_pivot_components import ( BlockTwoQubitGate, @@ -130,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}", @@ -137,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, @@ -740,10 +743,24 @@ def construct(self, operation: VirtualInjectedError, transform_constructor: ITra 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, - ) + # Special exception coherent/stochastic error + special_exception: bool = isinstance(operation.operation, Rx180) + if not special_exception: + draw_component: IDrawComponent = self._factory_manager.construct( + operation=operation.operation, + transform_constructor=transform_constructor, + ) + else: + 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, + ) 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..c3dde3a 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,95 @@ 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 + 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="#37CBDB", # 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=f"${RotationAxis.X.value}_{{p}}$", + 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=rf'$\mathtt{{R_{{{RotationAxis.X.value}}}{{{RotationAngle.THETA.value}}}}}$', + 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 From e7fc5c48792006b2e3938ddfcd0024a53ffbde96 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Sat, 14 Mar 2026 20:27:50 +0100 Subject: [PATCH 5/7] Added repetition-9 code for tunable coupler layout --- .../repetition_code_connectivity.py | 113 +++++++++ .../surface_code/surface_code_connectivity.py | 223 ++++++++++++++++++ 2 files changed, 336 insertions(+) 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 From 14df2e0fca3ead0944aed6cd3bdaceb89084f94f Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Mon, 23 Mar 2026 19:11:17 +0100 Subject: [PATCH 6/7] Added more temporary implementation for duality block --- .../factory_draw_components.py | 41 +++++++++++++------ .../draw_components/operation_components.py | 9 ++-- 2 files changed, 35 insertions(+), 15 deletions(-) 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 2459670..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 @@ -739,18 +739,11 @@ 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, - )): - # Special exception coherent/stochastic error - special_exception: bool = isinstance(operation.operation, Rx180) - if not special_exception: - draw_component: IDrawComponent = self._factory_manager.construct( - operation=operation.operation, - transform_constructor=transform_constructor, - ) - else: + 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, @@ -759,8 +752,32 @@ def construct(self, operation: VirtualInjectedError, transform_constructor: ITra 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 c3dde3a..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 @@ -527,6 +527,9 @@ class DualityBlock(IRectTransformComponent, IDrawComponent): 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) @@ -561,7 +564,7 @@ def draw(self, axes: plt.Axes) -> plt.Axes: linewidth=self.style_settings.border_width, linestyle=self.style_settings.border_line_style, edgecolor=self.style_settings.border_color, - facecolor="#37CBDB", # self.style_settings.background_color, + facecolor=self.face_color_left, # self.style_settings.background_color, zorder=-1, ) @@ -577,7 +580,7 @@ def draw(self, axes: plt.Axes) -> plt.Axes: axes.text( x=text_center.x, y=text_center.y, - s=f"${RotationAxis.X.value}_{{p}}$", + s=self.text_left, fontsize=self.style_settings.font_size, color=self.style_settings.text_color, ha='center', @@ -591,7 +594,7 @@ def draw(self, axes: plt.Axes) -> plt.Axes: axes.text( x=text_center.x, y=text_center.y, - s=rf'$\mathtt{{R_{{{RotationAxis.X.value}}}{{{RotationAngle.THETA.value}}}}}$', + s=self.text_right, fontsize=self.style_settings.font_size, color=self.style_settings.text_color, ha='center', From 5a666864eef2cce810e58c0436440f022c8d682c Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Wed, 25 Mar 2026 18:49:47 +0100 Subject: [PATCH 7/7] Added code dimension enum for initial state container --- .../language/intrf_declarative_circuit.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) 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