diff --git a/src/qce_circuit/connectivity/generic_gate_sequence.py b/src/qce_circuit/connectivity/generic_gate_sequence.py index 7faeda8..1346e75 100644 --- a/src/qce_circuit/connectivity/generic_gate_sequence.py +++ b/src/qce_circuit/connectivity/generic_gate_sequence.py @@ -70,26 +70,27 @@ def ancilla_qubit_ids(self) -> List[IQubitID]: @property def feedline_ids(self) -> List[IFeedlineID]: """:return: All feedline-ID's in device layer.""" - return Surface17Layer().feedline_ids + return self._surface_code_layer.feedline_ids # endregion # region IConnectivityLayer Interface Properties @property def qubit_ids(self) -> List[IQubitID]: """:return: (All) qubit-ID's in device layer.""" - return Surface17Layer().qubit_ids + return self._surface_code_layer.qubit_ids @property def edge_ids(self) -> List[IEdgeID]: """:return: (All) edge-ID's in device layer.""" - return Surface17Layer().edge_ids + return self._surface_code_layer.edge_ids # endregion # region Class Constructor - def __init__(self, gate_sequences: List[GateSequenceLayer], parity_group_z: List[IParityGroup], parity_group_x: List[IParityGroup]): + def __init__(self, gate_sequences: List[GateSequenceLayer], parity_group_z: List[IParityGroup], parity_group_x: List[IParityGroup], 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 + self._surface_code_layer: ISurfaceCodeLayer = surface_code_layer # endregion # region IGateSequenceLayer Interface Methods @@ -124,21 +125,21 @@ def get_parity_group(self, element: Union[IQubitID, IEdgeID]) -> List[IParityGro def get_frequency_group_identifier(self, element: IQubitID) -> FrequencyGroupIdentifier: """:return: Frequency group identifier based on qubit-ID.""" - return Surface17Layer().get_frequency_group_identifier(element=element) + return self._surface_code_layer.get_frequency_group_identifier(element=element) # endregion # region IDeviceLayer Interface Methods def get_connected_qubits(self, feedline: IFeedlineID) -> List[IQubitID]: """:return: Qubit-ID's connected to feedline-ID.""" - return Surface17Layer().get_connected_qubits(feedline=feedline) + return self._surface_code_layer.get_connected_qubits(feedline=feedline) def get_connected_feedline(self, qubit: IQubitID) -> IFeedlineID: """:return: Feedline-ID's connected to qubit-ID.""" - return Surface17Layer().get_connected_feedline(qubit=qubit) + return self._surface_code_layer.get_connected_feedline(qubit=qubit) def contains(self, element: Union[IFeedlineID, IQubitID, IEdgeID]) -> bool: """:return: Boolean, whether element is part of device layer or not.""" - return Surface17Layer().contains(element=element) + return self._surface_code_layer.contains(element=element) # endregion # region IConnectivityLayer Interface Methods @@ -147,9 +148,9 @@ def get_neighbors(self, qubit: IQubitID, order: int = 1) -> List[IQubitID]: Requires :param order: to be higher or equal to 1. :return: qubit neighbors separated by order. (order=1, nearest neighbors). """ - return Surface17Layer().get_neighbors(qubit=qubit, order=order) + return self._surface_code_layer.get_neighbors(qubit=qubit, order=order) def get_edges(self, qubit: IQubitID) -> List[IEdgeID]: """:return: All qubit-to-qubit edges from qubit-ID.""" - return Surface17Layer().get_edges(qubit=qubit) + return self._surface_code_layer.get_edges(qubit=qubit) # endregion diff --git a/src/qce_circuit/library/surface_code/__init__.py b/src/qce_circuit/library/surface_code/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/qce_circuit/library/surface_code/surface_code_connectivity.py b/src/qce_circuit/library/surface_code/surface_code_connectivity.py new file mode 100644 index 0000000..f83bda5 --- /dev/null +++ b/src/qce_circuit/library/surface_code/surface_code_connectivity.py @@ -0,0 +1,364 @@ +# ------------------------------------------- +# Module containing surface-code connectivity. +# ------------------------------------------- +from qce_circuit.utilities.singleton_base import SingletonABCMeta +from qce_circuit.connectivity.intrf_channel_identifier import ( + QubitIDObj, + EdgeIDObj, +) +from qce_circuit.connectivity.connectivity_surface_code import ( + ParityGroup, + StabilizerType, +) +from qce_circuit.connectivity.generic_gate_sequence import ( + IGenericSurfaceCodeLayer, + GenericSurfaceCode, +) +from qce_circuit.connectivity.intrf_connectivity_gate_sequence import ( + GateSequenceLayer, + Operation, +) + + +class Surface13ARound4Code(GenericSurfaceCode, IGenericSurfaceCodeLayer, metaclass=SingletonABCMeta): + + # region Class Constructor + def __init__(self): + super().__init__( + gate_sequences=[ + GateSequenceLayer( + _park_operations=[ + Operation.type_park(QubitIDObj('D8')), + Operation.type_park(QubitIDObj('X3')), + Operation.type_park(QubitIDObj('X2')), + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('Z3'), QubitIDObj('D7'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('D5'), QubitIDObj('Z1'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z4'), QubitIDObj('D9'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + Operation.type_park(QubitIDObj('X3')), + Operation.type_park(QubitIDObj('X2')), + Operation.type_park(QubitIDObj('D1')), + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('D4'), QubitIDObj('Z3'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('D6'), QubitIDObj('Z4'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z1'), QubitIDObj('D2'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + Operation.type_park(QubitIDObj('Z3')), + Operation.type_park(QubitIDObj('X3')), + Operation.type_park(QubitIDObj('D9')), + Operation.type_park(QubitIDObj('X2')), + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('D4'), QubitIDObj('Z1'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z4'), QubitIDObj('D8'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('D6'), QubitIDObj('Z2'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + Operation.type_park(QubitIDObj('X3')), + Operation.type_park(QubitIDObj('X2')), + Operation.type_park(QubitIDObj('D2')), + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('D5'), QubitIDObj('Z4'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z1'), QubitIDObj('D1'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z2'), QubitIDObj('D3'))), + ], + ), + ], + 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=[], + ) + # endregion + + +class Surface13BRound4Code(GenericSurfaceCode, IGenericSurfaceCodeLayer, metaclass=SingletonABCMeta): + + # region Class Constructor + def __init__(self): + super().__init__( + gate_sequences=[ + GateSequenceLayer( + _park_operations=[ + Operation.type_park(QubitIDObj('D8')), + Operation.type_park(QubitIDObj('Z4')), + Operation.type_park(QubitIDObj('D1')), + Operation.type_park(QubitIDObj('Z2')), + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('X3'), QubitIDObj('D8'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('D6'), QubitIDObj('X2'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X1'), QubitIDObj('D2'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + Operation.type_park(QubitIDObj('D8')), + Operation.type_park(QubitIDObj('Z4')), + Operation.type_park(QubitIDObj('Z1')), + Operation.type_park(QubitIDObj('D2')), + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('X3'), QubitIDObj('D7'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('D5'), QubitIDObj('X2'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X1'), QubitIDObj('D1'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + Operation.type_park(QubitIDObj('D8')), + Operation.type_park(QubitIDObj('Z4')), + Operation.type_park(QubitIDObj('Z1')), + Operation.type_park(QubitIDObj('D2')), + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('X4'), QubitIDObj('D9'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('D5'), QubitIDObj('X3'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X2'), QubitIDObj('D3'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + Operation.type_park(QubitIDObj('Z3')), + Operation.type_park(QubitIDObj('D9')), + Operation.type_park(QubitIDObj('Z1')), + Operation.type_park(QubitIDObj('D3')), + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('D4'), QubitIDObj('X3'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X4'), QubitIDObj('D8'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X2'), QubitIDObj('D2'))), + ], + ), + ], + parity_group_z=[ + 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'), QubitIDObj('D5'), QubitIDObj('D6')] + ), + ParityGroup( + _parity_type=StabilizerType.STABILIZER_Z, + _ancilla_qubit=QubitIDObj('X3'), + _data_qubits=[QubitIDObj('D4'), QubitIDObj('D5'), QubitIDObj('D7'), QubitIDObj('D8')] + ), + ParityGroup( + _parity_type=StabilizerType.STABILIZER_Z, + _ancilla_qubit=QubitIDObj('X4'), + _data_qubits=[QubitIDObj('D8'), QubitIDObj('D9')] + ), + ], + parity_group_x=[], + ) + # endregion + + +class Surface17Round8Code(GenericSurfaceCode, IGenericSurfaceCodeLayer, metaclass=SingletonABCMeta): + + # region Class Constructor + def __init__(self): + super().__init__( + gate_sequences=[ + GateSequenceLayer( + _park_operations=[ + Operation.type_park(QubitIDObj('D8')), + Operation.type_park(QubitIDObj('Z4')), + Operation.type_park(QubitIDObj('D1')), + Operation.type_park(QubitIDObj('Z2')), + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('X3'), QubitIDObj('D8'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('D6'), QubitIDObj('X2'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X1'), QubitIDObj('D2'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + Operation.type_park(QubitIDObj('D8')), + Operation.type_park(QubitIDObj('Z4')), + Operation.type_park(QubitIDObj('Z1')), + Operation.type_park(QubitIDObj('D2')), + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('X3'), QubitIDObj('D7'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('D5'), QubitIDObj('X2'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X1'), QubitIDObj('D1'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + Operation.type_park(QubitIDObj('D8')), + Operation.type_park(QubitIDObj('Z4')), + Operation.type_park(QubitIDObj('Z1')), + Operation.type_park(QubitIDObj('D2')), + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('X4'), QubitIDObj('D9'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('D5'), QubitIDObj('X3'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X2'), QubitIDObj('D3'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + Operation.type_park(QubitIDObj('Z3')), + Operation.type_park(QubitIDObj('D9')), + Operation.type_park(QubitIDObj('Z1')), + Operation.type_park(QubitIDObj('D3')), + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('D4'), QubitIDObj('X3'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X4'), QubitIDObj('D8'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X2'), QubitIDObj('D2'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + Operation.type_park(QubitIDObj('D8')), + Operation.type_park(QubitIDObj('X3')), + Operation.type_park(QubitIDObj('X2')), + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('Z3'), QubitIDObj('D7'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('D5'), QubitIDObj('Z1'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z4'), QubitIDObj('D9'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + Operation.type_park(QubitIDObj('X3')), + Operation.type_park(QubitIDObj('X2')), + Operation.type_park(QubitIDObj('D1')), + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('D4'), QubitIDObj('Z3'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('D6'), QubitIDObj('Z4'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z1'), QubitIDObj('D2'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + Operation.type_park(QubitIDObj('Z3')), + Operation.type_park(QubitIDObj('X3')), + Operation.type_park(QubitIDObj('D9')), + Operation.type_park(QubitIDObj('X2')), + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('D4'), QubitIDObj('Z1'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z4'), QubitIDObj('D8'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('D6'), QubitIDObj('Z2'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + Operation.type_park(QubitIDObj('X3')), + Operation.type_park(QubitIDObj('X2')), + Operation.type_park(QubitIDObj('D2')), + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('D5'), QubitIDObj('Z4'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z1'), QubitIDObj('D1'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z2'), QubitIDObj('D3'))), + ], + ), + ], + 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_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'), QubitIDObj('D5'), QubitIDObj('D6')] + ), + ParityGroup( + _parity_type=StabilizerType.STABILIZER_Z, + _ancilla_qubit=QubitIDObj('X3'), + _data_qubits=[QubitIDObj('D4'), QubitIDObj('D5'), QubitIDObj('D7'), QubitIDObj('D8')] + ), + ParityGroup( + _parity_type=StabilizerType.STABILIZER_Z, + _ancilla_qubit=QubitIDObj('X4'), + _data_qubits=[QubitIDObj('D8'), QubitIDObj('D9')] + ), + ], + ) + # endregion + + +if __name__ == '__main__': + from qce_circuit.visualization.visualize_layout.display_connectivity import ( + plot_gate_sequences, + plot_stabilizer_specific_gate_sequences, + ) + import matplotlib.pyplot as plt + + plot_stabilizer_specific_gate_sequences( + description=Surface13ARound4Code(), + ) + plot_stabilizer_specific_gate_sequences( + description=Surface13BRound4Code(), + ) + plot_stabilizer_specific_gate_sequences( + description=Surface17Round8Code(), + ) + plt.show() diff --git a/src/qce_circuit/utilities/geometric_definitions/vector_elements.py b/src/qce_circuit/utilities/geometric_definitions/vector_elements.py index 51b905e..40af241 100644 --- a/src/qce_circuit/utilities/geometric_definitions/vector_elements.py +++ b/src/qce_circuit/utilities/geometric_definitions/vector_elements.py @@ -50,6 +50,11 @@ def __add__(self, other): return Vec2D(x=self.x + other.x, y=self.y + other.y) raise NotImplementedError(f"Addition with anything other than {Vec2D} is not implemented.") + def __sub__(self, other): + if isinstance(other, Vec2D): + return Vec2D(x=self.x - other.x, y=self.y - other.y) + raise NotImplementedError(f"Subtraction with anything other than {Vec2D} is not implemented.") + def __mul__(self, other): return Vec2D( x=self.x * other, diff --git a/src/qce_circuit/visualization/visualize_circuit/display_circuit.py b/src/qce_circuit/visualization/visualize_circuit/display_circuit.py index d672a62..e34a899 100644 --- a/src/qce_circuit/visualization/visualize_circuit/display_circuit.py +++ b/src/qce_circuit/visualization/visualize_circuit/display_circuit.py @@ -199,7 +199,7 @@ def get_transform_constructor(self) -> TransformConstructor: ) def get_operation_draw_components(self) -> List[IDrawComponent]: - minimalist: bool = True + minimalist: bool = False individual_component_factory = DrawComponentFactoryManager( default_factory=DefaultFactory(), factory_lookup={ @@ -319,7 +319,7 @@ def construct_visual_description(circuit: IDeclarativeCircuit, custom_channel_or # Construct custom channel map if custom_channel_map is None: custom_channel_map = { - channel_index: channel_index + channel_index: str(channel_index) for channel_index in channel_indices } channel_indices = reorder_indices(original_order=channel_indices, specific_order=custom_channel_order) diff --git a/src/qce_circuit/visualization/visualize_layout/display_connectivity.py b/src/qce_circuit/visualization/visualize_layout/display_connectivity.py index 3aa6dfc..29e4032 100644 --- a/src/qce_circuit/visualization/visualize_layout/display_connectivity.py +++ b/src/qce_circuit/visualization/visualize_layout/display_connectivity.py @@ -3,10 +3,10 @@ # ------------------------------------------- from dataclasses import dataclass, field from collections.abc import Iterable -from typing import Dict, List +from typing import Dict, List, Union import numpy as np from qce_circuit.connectivity.intrf_channel_identifier import IQubitID, QubitIDObj -from qce_circuit.connectivity.intrf_connectivity_surface_code import ISurfaceCodeLayer +from qce_circuit.connectivity.intrf_connectivity_surface_code import ISurfaceCodeLayer, IParityGroup from qce_circuit.connectivity.connectivity_surface_code import Surface17Layer from qce_circuit.connectivity.intrf_connectivity_gate_sequence import ( GateSequenceLayer, @@ -15,6 +15,7 @@ from qce_circuit.utilities.geometric_definitions import ( TransformAlignment, Vec2D, + Line2D, ) from qce_circuit.visualization.visualize_circuit.intrf_draw_component import IDrawComponent from qce_circuit.visualization.visualize_layout.style_manager import ( @@ -26,6 +27,7 @@ from qce_circuit.visualization.visualize_layout.plaquette_components import ( RectanglePlaquette, TrianglePlaquette, + DiagonalPlaquette, ) from qce_circuit.visualization.visualize_layout.element_components import ( DotComponent, @@ -71,51 +73,97 @@ def get_plaquette_components(self) -> List[IDrawComponent]: diagonal_spacing: float = self.layout_spacing * np.sqrt(2) for parity_group in self.connectivity.parity_group_x: + ancilla_coordinates: Vec2D = self.identifier_to_pivot(identifier=parity_group.ancilla_id) + relative_coordinates: List[Vec2D] = [ + self.identifier_to_pivot(identifier=qubit_id) - ancilla_coordinates + for qubit_id in parity_group.data_ids + ] + mean_relative_coordinates: Vec2D = Vec2D( + x=np.mean([v.x for v in relative_coordinates]), + y=np.mean([v.y for v in relative_coordinates]), + ) + mean_center: bool = all(np.isclose(mean_relative_coordinates.to_vector(), Vec2D(0.0, 0.0).to_vector())) + if len(parity_group.data_ids) == 4: result.append( RectanglePlaquette( pivot=self.identifier_to_pivot(parity_group.ancilla_id) + self.pivot, width=diagonal_spacing, height=diagonal_spacing, - rotation=self.identifier_to_rotation(parity_group.ancilla_id), + rotation=self.identifier_to_rotation(parity_group), alignment=TransformAlignment.MID_CENTER, style_settings=StyleManager.read_config().plaquette_style_x, ) ) if len(parity_group.data_ids) == 2: - result.append( - TrianglePlaquette( - pivot=self.identifier_to_pivot(parity_group.ancilla_id) + self.pivot, - width=diagonal_spacing, - height=diagonal_spacing, - rotation=self.identifier_to_rotation(parity_group.ancilla_id), - alignment=TransformAlignment.MID_CENTER, - style_settings=StyleManager.read_config().plaquette_style_x, + if mean_center: + result.append( + DiagonalPlaquette( + pivot=self.identifier_to_pivot(parity_group.ancilla_id) + self.pivot, + width=diagonal_spacing, + height=diagonal_spacing, + rotation=self.identifier_to_rotation(parity_group), + alignment=TransformAlignment.MID_CENTER, + style_settings=StyleManager.read_config().plaquette_style_x, + ) + ) + else: + result.append( + TrianglePlaquette( + pivot=self.identifier_to_pivot(parity_group.ancilla_id) + self.pivot, + width=diagonal_spacing, + height=diagonal_spacing, + rotation=self.identifier_to_rotation(parity_group), + alignment=TransformAlignment.MID_CENTER, + style_settings=StyleManager.read_config().plaquette_style_x, + ) ) - ) for parity_group in self.connectivity.parity_group_z: + ancilla_coordinates: Vec2D = self.identifier_to_pivot(identifier=parity_group.ancilla_id) + relative_coordinates: List[Vec2D] = [ + self.identifier_to_pivot(identifier=qubit_id) - ancilla_coordinates + for qubit_id in parity_group.data_ids + ] + mean_relative_coordinates: Vec2D = Vec2D( + x=np.mean([v.x for v in relative_coordinates]), + y=np.mean([v.y for v in relative_coordinates]), + ) + mean_center: bool = all(np.isclose(mean_relative_coordinates.to_vector(), Vec2D(0.0, 0.0).to_vector())) + if len(parity_group.data_ids) == 4: result.append( RectanglePlaquette( pivot=self.identifier_to_pivot(parity_group.ancilla_id) + self.pivot, width=diagonal_spacing, height=diagonal_spacing, - rotation=self.identifier_to_rotation(parity_group.ancilla_id), + rotation=self.identifier_to_rotation(parity_group), alignment=TransformAlignment.MID_CENTER, style_settings=StyleManager.read_config().plaquette_style_z, ) ) if len(parity_group.data_ids) == 2: - result.append( - TrianglePlaquette( - pivot=self.identifier_to_pivot(parity_group.ancilla_id) + self.pivot, - width=diagonal_spacing, - height=diagonal_spacing, - rotation=self.identifier_to_rotation(parity_group.ancilla_id), - alignment=TransformAlignment.MID_CENTER, - style_settings=StyleManager.read_config().plaquette_style_z, + if mean_center: + result.append( + DiagonalPlaquette( + pivot=self.identifier_to_pivot(parity_group.ancilla_id) + self.pivot, + width=diagonal_spacing, + height=diagonal_spacing, + rotation=self.identifier_to_rotation(parity_group), + alignment=TransformAlignment.MID_CENTER, + style_settings=StyleManager.read_config().plaquette_style_z, + ) + ) + else: + result.append( + TrianglePlaquette( + pivot=self.identifier_to_pivot(parity_group.ancilla_id) + self.pivot, + width=diagonal_spacing, + height=diagonal_spacing, + rotation=self.identifier_to_rotation(parity_group), + alignment=TransformAlignment.MID_CENTER, + style_settings=StyleManager.read_config().plaquette_style_z, + ) ) - ) return result def get_element_components(self) -> List[IDrawComponent]: @@ -193,33 +241,52 @@ def identifier_to_pivot(self, identifier: IQubitID) -> Vec2D: return map_qubits[identifier].rotate(np.deg2rad(self.rotation)) + self.pivot return self.pivot # Default - def identifier_to_rotation(self, identifier: IQubitID) -> float: + def identifier_to_rotation(self, identifier: Union[IQubitID, IParityGroup]) -> float: """:return: Rotation based on (parity group) ancilla identifier.""" rotation_offset: float = -45 hexagon_rotation: float = 30 - - # Surface-17 layout - map_qubits: Dict[IQubitID, float] = { - QubitIDObj('Z3'): self.rotation + rotation_offset + 0, - QubitIDObj('X4'): self.rotation + rotation_offset + 270, - QubitIDObj('Z4'): self.rotation + rotation_offset, - QubitIDObj('X3'): self.rotation + rotation_offset, - QubitIDObj('X2'): self.rotation + rotation_offset, - QubitIDObj('Z1'): self.rotation + rotation_offset, - QubitIDObj('X1'): self.rotation + rotation_offset + 90, - QubitIDObj('Z2'): self.rotation + rotation_offset + 180, - QubitIDObj('D1'): self.rotation + rotation_offset + hexagon_rotation, - QubitIDObj('D2'): self.rotation + rotation_offset + hexagon_rotation, - QubitIDObj('D3'): self.rotation + rotation_offset + hexagon_rotation, - QubitIDObj('D4'): self.rotation + rotation_offset + hexagon_rotation, - QubitIDObj('D5'): self.rotation + rotation_offset + hexagon_rotation, - QubitIDObj('D6'): self.rotation + rotation_offset + hexagon_rotation, - QubitIDObj('D7'): self.rotation + rotation_offset + hexagon_rotation, - QubitIDObj('D8'): self.rotation + rotation_offset + hexagon_rotation, - QubitIDObj('D9'): self.rotation + rotation_offset + hexagon_rotation, - } - if identifier in map_qubits: - return map_qubits[identifier] + # Guard clause, if identifier is data qubit or not present in ancilla group + is_single_qubit_id: bool = isinstance(identifier, IQubitID) + if is_single_qubit_id: + if identifier in self.connectivity.data_qubit_ids: + return self.rotation + rotation_offset + hexagon_rotation + return self.rotation + rotation_offset + identifier: IParityGroup + + # Guard clause, if weight-4 plaquette, return default + if len(identifier.data_ids) == 4: + return self.rotation + rotation_offset + + ancilla_coordinates: Vec2D = self.identifier_to_pivot(identifier=identifier.ancilla_id) + relative_coordinates: List[Vec2D] = [ + self.identifier_to_pivot(identifier=qubit_id) - ancilla_coordinates + for qubit_id in identifier.data_ids + ] + mean_relative_coordinates: Vec2D = Vec2D( + x=np.mean([v.x for v in relative_coordinates]), + y=np.mean([v.y for v in relative_coordinates]), + ) + mean_center: bool = all(np.isclose(mean_relative_coordinates.to_vector(), Vec2D(0.0, 0.0).to_vector())) + + if mean_center: # Weight-2 diagonal + line = Line2D(start=relative_coordinates[0], end=relative_coordinates[1]) + slope = (line.end.y - line.start.y) / (line.end.x - line.start.x) + if slope == +1.0: + pass + elif slope == -1.0: + rotation_offset += 90 + else: # Weight-2 triangle + if mean_relative_coordinates.x == 0.0 and mean_relative_coordinates.y > 0.0: + rotation_offset += 90 + elif mean_relative_coordinates.x == 0.0 and mean_relative_coordinates.y < 0.0: + rotation_offset += 270 + elif mean_relative_coordinates.x > 0.0 and mean_relative_coordinates.y == 0.0: + rotation_offset += 0 + elif mean_relative_coordinates.x < 0.0 and mean_relative_coordinates.y == 0.0: + rotation_offset += 180 + + if identifier.ancilla_id in self.connectivity.ancilla_qubit_ids: + return self.rotation + rotation_offset return self.rotation # default # endregion @@ -251,7 +318,7 @@ def get_plaquette_components(self) -> List[IDrawComponent]: pivot=self.identifier_to_pivot(parity_group.ancilla_id) + self.pivot, width=diagonal_spacing, height=diagonal_spacing, - rotation=self.identifier_to_rotation(parity_group.ancilla_id), + rotation=self.identifier_to_rotation(parity_group), alignment=TransformAlignment.MID_CENTER, style_settings=style_settings, ) @@ -262,7 +329,7 @@ def get_plaquette_components(self) -> List[IDrawComponent]: pivot=self.identifier_to_pivot(parity_group.ancilla_id) + self.pivot, width=diagonal_spacing, height=diagonal_spacing, - rotation=self.identifier_to_rotation(parity_group.ancilla_id), + rotation=self.identifier_to_rotation(parity_group), alignment=TransformAlignment.MID_CENTER, style_settings=style_settings, ) @@ -274,7 +341,7 @@ def get_plaquette_components(self) -> List[IDrawComponent]: pivot=self.identifier_to_pivot(parity_group.ancilla_id) + self.pivot, width=diagonal_spacing, height=diagonal_spacing, - rotation=self.identifier_to_rotation(parity_group.ancilla_id), + rotation=self.identifier_to_rotation(parity_group), alignment=TransformAlignment.MID_CENTER, style_settings=style_settings, ) @@ -285,7 +352,7 @@ def get_plaquette_components(self) -> List[IDrawComponent]: pivot=self.identifier_to_pivot(parity_group.ancilla_id) + self.pivot, width=diagonal_spacing, height=diagonal_spacing, - rotation=self.identifier_to_rotation(parity_group.ancilla_id), + rotation=self.identifier_to_rotation(parity_group), alignment=TransformAlignment.MID_CENTER, style_settings=style_settings, ) @@ -329,38 +396,6 @@ def get_element_components(self) -> List[IDrawComponent]: alignment=TransformAlignment.MID_CENTER, )) return result - - def identifier_to_rotation(self, identifier: IQubitID) -> float: - """:return: Rotation based on (parity group) ancilla identifier.""" - rotation_offset: float = -45 - hexagon_rotation: float = 30 - - # Surface-17 layout - map_qubits: Dict[IQubitID, float] = { - QubitIDObj('Z3'): self.rotation + rotation_offset + 0, - QubitIDObj('X4'): self.rotation + rotation_offset + 270, - QubitIDObj('Z4'): self.rotation + rotation_offset - 90, - QubitIDObj('X3'): self.rotation + rotation_offset + 180, - QubitIDObj('X3'): self.rotation + rotation_offset + 90, - QubitIDObj('X2'): self.rotation + rotation_offset - 90, - QubitIDObj('Z1'): self.rotation + rotation_offset + 90, - QubitIDObj('X1'): self.rotation + rotation_offset + 90, - QubitIDObj('Z2'): self.rotation + rotation_offset + 180, - QubitIDObj('D1'): self.rotation + rotation_offset + hexagon_rotation, - QubitIDObj('D2'): self.rotation + rotation_offset + hexagon_rotation, - QubitIDObj('D3'): self.rotation + rotation_offset + hexagon_rotation, - QubitIDObj('D4'): self.rotation + rotation_offset + hexagon_rotation, - QubitIDObj('D5'): self.rotation + rotation_offset + hexagon_rotation, - QubitIDObj('D6'): self.rotation + rotation_offset + hexagon_rotation, - QubitIDObj('D7'): self.rotation + rotation_offset + hexagon_rotation, - QubitIDObj('D8'): self.rotation + rotation_offset + hexagon_rotation, - QubitIDObj('D9'): self.rotation + rotation_offset + hexagon_rotation, - } - if identifier in map_qubits: - print(identifier, map_qubits[identifier]) - return map_qubits[identifier] - return self.rotation # default - # endregion diff --git a/src/qce_circuit/visualization/visualize_layout/plaquette_components.py b/src/qce_circuit/visualization/visualize_layout/plaquette_components.py index f461a18..cacf589 100644 --- a/src/qce_circuit/visualization/visualize_layout/plaquette_components.py +++ b/src/qce_circuit/visualization/visualize_layout/plaquette_components.py @@ -122,3 +122,60 @@ def draw(self, axes: plt.Axes) -> plt.Axes: return axes # endregion + +@dataclass(frozen=True) +class DiagonalPlaquette(IRectTransformComponent, IDrawComponent): + """ + Data class, containing dimension data for drawing plaquette triangle. + """ + pivot: Vec2D + width: float + height: float + rotation: float = field(default=0) + background_type: BackgroundType = field(default=BackgroundType.X) + alignment: TransformAlignment = field(default=TransformAlignment.MID_LEFT) + style_settings: PlaquetteStyleSettings = field(default_factory=lambda: StyleManager.read_config().plaquette_style_x) + + # region Interface Properties + @property + def rectilinear_transform(self) -> IRectTransform: + """:return: 'Hard' rectilinear transform boundary. Should be treated as 'personal zone'.""" + return RectTransform( + _pivot_strategy=FixedPivot(self.pivot), + _width_strategy=FixedLength(self.width), + _height_strategy=FixedLength(self.height), + _parent_alignment=self.alignment, + ) + # endregion + + # region Class Properties + @property + def polygon_vertices(self) -> List[Vec2D]: + transform: IRectTransform = self.rectilinear_transform + vertices: List[Vec2D] = [ + Vec2D(-self.width/2, +self.height/2), + Vec2D(+self.width/3.5, +self.height/3.5), + Vec2D(+self.width/2, -self.height/2), + Vec2D(-self.width/3.5, -self.height/3.5), + ] + vertices = [transform.pivot + vertex for vertex in vertices] + # Apply rotation + rotation_pivot: Vec2D = transform.pivot + vertices = [vertex.rotate(np.deg2rad(self.rotation), rotation_pivot.to_tuple()) for vertex in vertices] + return vertices + # endregion + + # region Class Methods + def draw(self, axes: plt.Axes) -> plt.Axes: + """Method used for drawing component on Axes.""" + rectangle = patches.Polygon( + [vertex.to_tuple() for vertex in self.polygon_vertices], + closed=True, + edgecolor='none', + facecolor=self.style_settings.background_color, # Depends on background type + zorder=self.style_settings.zorder, + ) + axes.add_patch(rectangle) + return axes + # endregion +