From 4d7eb114243605e309c213f8f7f7c871a53d5df8 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Mon, 22 Dec 2025 09:42:27 +0100 Subject: [PATCH 01/17] Added tuna17 surface layout and tuna9 code layout --- .../connectivity/connectivity_surface_code.py | 26 ++++ .../language/intrf_declarative_circuit.py | 23 ++-- .../surface_code/surface_code_connectivity.py | 119 ++++++++++++++++-- .../structure/intrf_circuit_operation.py | 6 + 4 files changed, 157 insertions(+), 17 deletions(-) diff --git a/src/qce_circuit/connectivity/connectivity_surface_code.py b/src/qce_circuit/connectivity/connectivity_surface_code.py index 9389d7b..21fc35f 100644 --- a/src/qce_circuit/connectivity/connectivity_surface_code.py +++ b/src/qce_circuit/connectivity/connectivity_surface_code.py @@ -288,6 +288,32 @@ def contains(self, element: Union[IFeedlineID, IQubitID, IEdgeID]) -> bool: # endregion +class SurfaceTuna17Layer(Surface17Layer): + """ + Singleton class, implementing ISurfaceCodeLayer interface to describe a surface-17 layout. + Extends Surface-17 layout by setting frequency groups to common mid-frequency range. Suppresses parking constraints. + """ + _frequency_group_lookup: Dict[IQubitID, FrequencyGroupIdentifier] = { + QubitIDObj('D1'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID), + QubitIDObj('D2'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID), + QubitIDObj('D3'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID), + QubitIDObj('D4'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID), + QubitIDObj('D5'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID), + QubitIDObj('D6'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID), + QubitIDObj('D7'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID), + QubitIDObj('D8'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID), + QubitIDObj('D9'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID), + QubitIDObj('Z1'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID), + QubitIDObj('Z2'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID), + QubitIDObj('Z3'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID), + QubitIDObj('Z4'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID), + QubitIDObj('X1'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID), + QubitIDObj('X2'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID), + QubitIDObj('X3'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID), + QubitIDObj('X4'): FrequencyGroupIdentifier(_id=FrequencyGroup.MID), + } + + def get_neighbors(element: Union[IQubitID, IEdgeID], connectivity: IConnectivityLayer, order: int = 1) -> List[IQubitID]: """ Functionality for both qubit-ID and edge-ID. diff --git a/src/qce_circuit/language/intrf_declarative_circuit.py b/src/qce_circuit/language/intrf_declarative_circuit.py index 6bec946..e91e5b4 100644 --- a/src/qce_circuit/language/intrf_declarative_circuit.py +++ b/src/qce_circuit/language/intrf_declarative_circuit.py @@ -5,7 +5,7 @@ from abc import ABC, abstractmethod, ABCMeta from dataclasses import dataclass, field from multipledispatch import dispatch -from typing import List, Union, Dict, Optional +from typing import List, Union, Dict, Optional, TypeVar, Generic from enum import Enum, unique import numpy as np from numpy.typing import NDArray @@ -35,6 +35,9 @@ ) +T = TypeVar("T") + + @unique class InitialStateEnum(Enum): """Enum class, containing different initial state options. Mostly for cosmetic purposes.""" @@ -47,13 +50,13 @@ class InitialStateEnum(Enum): @dataclass(frozen=True) -class InitialStateContainer: +class InitialStateContainer(Generic[T]): """ Data class, holding reference to qubits and their initial state. """ - initial_states: Dict[int, InitialStateEnum] + initial_states: Dict[T, InitialStateEnum] """Index pointers to data qubits only.""" - ancilla_initial_states: Dict[int, InitialStateEnum] = field(default_factory=dict) + ancilla_initial_states: Dict[T, InitialStateEnum] = field(default_factory=dict) """Index pointers to ancilla qubits only.""" # region Class Properties @@ -63,7 +66,7 @@ def distance(self) -> int: @property def as_array(self) -> np.ndarray: - sorted_indices: List[int] = list(sorted(self.initial_states.keys())) + sorted_indices: List[T] = list(sorted(self.initial_states.keys())) # Maps initial state to binary to_bit_conversion: Dict[InitialStateEnum, int] = { InitialStateEnum.ZERO: 0, @@ -77,10 +80,10 @@ def as_array(self) -> np.ndarray: # endregion # region Class Methods - def get_initial_state(self, qubit_index: int) -> InitialStateEnum: + def get_initial_state(self, qubit_index: T) -> InitialStateEnum: return self.initial_states[qubit_index] - def get_data_qubit_operation(self, qubit_index: int, initial_state_index: int, **kwargs) -> ICircuitOperation: + def get_data_qubit_operation(self, qubit_index: T, initial_state_index: T, **kwargs) -> ICircuitOperation: """ :param qubit_index: Index corresponding to qubit-ID in circuit. Will be passed to operation constructor. :param initial_state_index: Index corresponding to qubit-ID in initial state container. @@ -99,7 +102,7 @@ def get_data_qubit_operation(self, qubit_index: int, initial_state_index: int, * **kwargs, ) - def get_ancilla_qubit_operation(self, qubit_index: int, initial_state_index: int, **kwargs) -> ICircuitOperation: + def get_ancilla_qubit_operation(self, qubit_index: T, initial_state_index: T, **kwargs) -> ICircuitOperation: """ :param qubit_index: Index corresponding to qubit-ID in circuit. Will be passed to operation constructor. :param initial_state_index: Index corresponding to qubit-ID in initial state container. @@ -118,7 +121,7 @@ def get_ancilla_qubit_operation(self, qubit_index: int, initial_state_index: int **kwargs, ) - def get_operation(self, qubit_index: int, initial_state: InitialStateEnum, **kwargs) -> ICircuitOperation: + def get_operation(self, qubit_index: T, initial_state: InitialStateEnum, **kwargs) -> ICircuitOperation: """ :param qubit_index: Index corresponding to qubit-ID in circuit. Will be passed to operation constructor. :param initial_state: Initial state enum. @@ -149,7 +152,7 @@ def from_ordered_list(cls, initial_states: List[InitialStateEnum], ancilla_initi if ancilla_initial_states is None: ancilla_initial_states = [] - return InitialStateContainer( + 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)}, ) 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 1606784..7d596ff 100644 --- a/src/qce_circuit/library/surface_code/surface_code_connectivity.py +++ b/src/qce_circuit/library/surface_code/surface_code_connectivity.py @@ -9,6 +9,7 @@ from qce_circuit.connectivity.connectivity_surface_code import ( ParityGroup, StabilizerType, + SurfaceTuna17Layer, ) from qce_circuit.connectivity.generic_gate_sequence import ( IGenericSurfaceCodeLayer, @@ -345,6 +346,110 @@ def __init__(self): # endregion +class Surface17Round4Code(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('Z4'), QubitIDObj('D8'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z1'), QubitIDObj('D4'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X2'), QubitIDObj('D5'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X1'), QubitIDObj('D1'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z2'), QubitIDObj('D6'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('X3'), QubitIDObj('D8'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z4'), QubitIDObj('D5'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z1'), QubitIDObj('D1'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X2'), QubitIDObj('D6'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X1'), QubitIDObj('D2'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z2'), QubitIDObj('D3'))), + ], + ), + GateSequenceLayer( + _park_operations=[ + ], + _gate_operations=[ + Operation.type_gate(EdgeIDObj(QubitIDObj('X3'), QubitIDObj('D4'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z4'), QubitIDObj('D9'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z1'), QubitIDObj('D5'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X2'), QubitIDObj('D2'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z3'), QubitIDObj('D7'))), + 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('Z4'), QubitIDObj('D6'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z1'), QubitIDObj('D2'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X2'), QubitIDObj('D3'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('Z3'), QubitIDObj('D4'))), + Operation.type_gate(EdgeIDObj(QubitIDObj('X4'), QubitIDObj('D9'))), + ], + ), + ], + 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')] + ), + ], + surface_code_layer=SurfaceTuna17Layer(), + ) + # endregion + + if __name__ == '__main__': from qce_circuit.visualization.visualize_layout.display_connectivity import ( plot_gate_sequences, @@ -352,13 +457,13 @@ def __init__(self): ) 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=Surface13ARound4Code(), - ) - plot_stabilizer_specific_gate_sequences( - description=Surface13BRound4Code(), - ) - plot_stabilizer_specific_gate_sequences( - description=Surface17Round8Code(), + description=Surface17Round4Code(), ) plt.show() diff --git a/src/qce_circuit/structure/intrf_circuit_operation.py b/src/qce_circuit/structure/intrf_circuit_operation.py index c448be8..e881062 100644 --- a/src/qce_circuit/structure/intrf_circuit_operation.py +++ b/src/qce_circuit/structure/intrf_circuit_operation.py @@ -12,6 +12,7 @@ RelationTypeNotImplementedException, ) from qce_circuit.utilities.custom_warnings import OperationNotFoundWarning +from qce_circuit.utilities.array_manipulation import unique_in_order @unique @@ -289,6 +290,11 @@ class ICircuitOperation(ICircuitNode, IRelationComponent['ICircuitOperation'], I def channel_identifiers(self) -> List[ChannelIdentifier]: """:return: Array-like of channel identifiers to which this operation applies to.""" raise InterfaceMethodException + + @property + def unique_channel_indices(self) -> List[int]: + """:return: Unique list of channel indices.""" + return unique_in_order([channel.id for channel in self.channel_identifiers]) # endregion # region Interface Methods From 8b150b775e238a84a1ff28af172819d1b70b47e3 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Mon, 5 Jan 2026 16:53:04 +0100 Subject: [PATCH 02/17] Updated W.I.P. kernel indexing for calibration --- .../acquisition_indexing/kernel_repetition_code.py | 6 +++++- .../visualization/visualize_layout/display_connectivity.py | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/qce_circuit/structure/acquisition_indexing/kernel_repetition_code.py b/src/qce_circuit/structure/acquisition_indexing/kernel_repetition_code.py index 4d563b6..b84820e 100644 --- a/src/qce_circuit/structure/acquisition_indexing/kernel_repetition_code.py +++ b/src/qce_circuit/structure/acquisition_indexing/kernel_repetition_code.py @@ -169,7 +169,11 @@ def indexing_kernels(self) -> List[IIndexingKernel]: """:return: Array-like of ordered indexing kernels that describe self.""" repetition_kernels: List[IIndexingKernel] = self._repetition_kernels calibration_kernel: List[IIndexingKernel] = [self._calibration_kernel] - result: List[IIndexingKernel] = repetition_kernels + calibration_kernel + + result: List[IIndexingKernel] = repetition_kernels + if not self.include_qutrit_calibration_points: + return result + result += calibration_kernel return result # endregion diff --git a/src/qce_circuit/visualization/visualize_layout/display_connectivity.py b/src/qce_circuit/visualization/visualize_layout/display_connectivity.py index 8c2ebec..33c9316 100644 --- a/src/qce_circuit/visualization/visualize_layout/display_connectivity.py +++ b/src/qce_circuit/visualization/visualize_layout/display_connectivity.py @@ -444,7 +444,7 @@ def plot_gate_sequences(description: IGenericSurfaceCodeLayer, **kwargs) -> IFig return fig, axes[0] -def plot_stabilizer_specific_gate_sequences(description: IGenericSurfaceCodeLayer, include_element_labels: bool = True, **kwargs) -> IFigureAxesPair: +def plot_stabilizer_specific_gate_sequences(description: IGenericSurfaceCodeLayer, include_element_labels: bool = True, connectivity: ISurfaceCodeLayer = Surface17Layer(), **kwargs) -> IFigureAxesPair: """ Constructs a similar gate sequence plot as 'plot_gate_sequences'. However, the gate-sequence info is taken from description parameter @@ -463,7 +463,7 @@ def plot_stabilizer_specific_gate_sequences(description: IGenericSurfaceCodeLaye for i, ax in enumerate(axes): descriptor: AllGreyVisualConnectivityDescription = AllGreyVisualConnectivityDescription( - connectivity=Surface17Layer(), + connectivity=connectivity, gate_sequence=description.get_gate_sequence_at_index(i), layout_spacing=1.0, include_element_labels=include_element_labels, @@ -479,4 +479,4 @@ def plot_stabilizer_specific_gate_sequences(description: IGenericSurfaceCodeLaye ) kwargs[SubplotKeywordEnum.HOST_AXES.value] = (fig, ax) plot_layout_description(descriptor, **kwargs) - return fig, axes[0] \ No newline at end of file + return fig, axes[0] From 35a6d1253e9e8b29ce03d300b215f5bcbc9be1e2 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Mon, 5 Jan 2026 23:03:10 +0100 Subject: [PATCH 03/17] Added initial state to array in an ordered manner based on provided qubit_order --- .../language/intrf_declarative_circuit.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/qce_circuit/language/intrf_declarative_circuit.py b/src/qce_circuit/language/intrf_declarative_circuit.py index e91e5b4..b21e61a 100644 --- a/src/qce_circuit/language/intrf_declarative_circuit.py +++ b/src/qce_circuit/language/intrf_declarative_circuit.py @@ -66,7 +66,17 @@ def distance(self) -> int: @property def as_array(self) -> np.ndarray: - sorted_indices: List[T] = list(sorted(self.initial_states.keys())) + return self.as_ordered_array(qubit_order=None) + # endregion + + # region Class Methods + def get_initial_state(self, qubit_index: T) -> InitialStateEnum: + return self.initial_states[qubit_index] + + def as_ordered_array(self, qubit_order: Optional[List[T]] = None) -> np.ndarray: + sorted_indices: List[T] = qubit_order + if sorted_indices is None: + sorted_indices: List[T] = list(sorted(self.initial_states.keys())) # Maps initial state to binary to_bit_conversion: Dict[InitialStateEnum, int] = { InitialStateEnum.ZERO: 0, @@ -77,11 +87,6 @@ def as_array(self) -> np.ndarray: InitialStateEnum.PLUS_I: 1, } return np.asarray([to_bit_conversion[self.initial_states[index]] for index in sorted_indices]) - # endregion - - # region Class Methods - def get_initial_state(self, qubit_index: T) -> InitialStateEnum: - return self.initial_states[qubit_index] def get_data_qubit_operation(self, qubit_index: T, initial_state_index: T, **kwargs) -> ICircuitOperation: """ From f855ec4f239c9868fe53bb5c4063a31a7d11b10b Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Wed, 14 Jan 2026 13:25:25 +0100 Subject: [PATCH 04/17] Added (single) line component for edge connections on quantum surface --- .../visualize_layout/display_connectivity.py | 33 +++++++++---------- .../visualize_layout/plaquette_components.py | 4 ++- .../visualize_layout/polygon_component.py | 27 +++++++++++++++ .../visualize_layout/style_manager.py | 12 ++++++- 4 files changed, 56 insertions(+), 20 deletions(-) diff --git a/src/qce_circuit/visualization/visualize_layout/display_connectivity.py b/src/qce_circuit/visualization/visualize_layout/display_connectivity.py index 33c9316..a790eda 100644 --- a/src/qce_circuit/visualization/visualize_layout/display_connectivity.py +++ b/src/qce_circuit/visualization/visualize_layout/display_connectivity.py @@ -37,6 +37,7 @@ ) from qce_circuit.visualization.visualize_layout.polygon_component import ( PolylineComponent, + LineComponent, GateOperationComponent, ) from qce_circuit.visualization.visualize_circuit.display_circuit import CircuitAxesFormat @@ -183,22 +184,15 @@ def get_element_components(self) -> List[IDrawComponent]: )) return result - def get_line_components(self) -> List[IDrawComponent]: - return [ - PolylineComponent( - vertices=[ - self.identifier_to_pivot(QubitIDObj('D7')), - self.identifier_to_pivot(QubitIDObj('Z3')), - self.identifier_to_pivot(QubitIDObj('D4')), - self.identifier_to_pivot(QubitIDObj('Z1')), - self.identifier_to_pivot(QubitIDObj('D5')), - self.identifier_to_pivot(QubitIDObj('Z4')), - self.identifier_to_pivot(QubitIDObj('D6')), - self.identifier_to_pivot(QubitIDObj('Z2')), - self.identifier_to_pivot(QubitIDObj('D3')), - ], - ) - ] + def get_element_edges_components(self) -> List[IDrawComponent]: + result: List[IDrawComponent] = [] + for edge_id in self.connectivity.edge_ids: + result.append(LineComponent( + pivot0=self.identifier_to_pivot(edge_id.qubit_ids[0]) + self.pivot, + pivot1=self.identifier_to_pivot(edge_id.qubit_ids[1]) + self.pivot, + alignment=TransformAlignment.MID_CENTER, + )) + return result def get_operation_components(self) -> List[IDrawComponent]: park_components: List[IDrawComponent] = [ @@ -300,7 +294,7 @@ class AllGreyVisualConnectivityDescription(VisualConnectivityDescription): """ Data class, overwriting VisualConnectivityDescription by forcing single plaquette color. """ - plaquette_color_overwrite: str = field(default="#b0b0b0") + plaquette_color_overwrite: str = field(default_factory=lambda: StyleManager.read_config().color_background_base) # region Class Methods def get_plaquette_components(self) -> List[IDrawComponent]: @@ -370,7 +364,7 @@ class StabilizerGroupVisualConnectivityDescription(VisualConnectivityDescription """ Data class, overwriting VisualConnectivityDescription by implementing stabilizer group element visualization. """ - element_color_overwrite: str = field(default="#c4c4c4") + element_color_overwrite: str = field(default_factory=lambda: StyleManager.read_config().color_element) # region Class Methods def get_element_components(self) -> List[IDrawComponent]: @@ -417,6 +411,9 @@ def plot_layout_description(description: VisualConnectivityDescription, **kwargs for draw_component in description.get_element_components(): draw_component.draw(axes=ax) + for draw_component in description.get_element_edges_components(): + draw_component.draw(axes=ax) + for draw_component in description.get_operation_components(): draw_component.draw(axes=ax) diff --git a/src/qce_circuit/visualization/visualize_layout/plaquette_components.py b/src/qce_circuit/visualization/visualize_layout/plaquette_components.py index cacf589..639a63a 100644 --- a/src/qce_circuit/visualization/visualize_layout/plaquette_components.py +++ b/src/qce_circuit/visualization/visualize_layout/plaquette_components.py @@ -62,9 +62,11 @@ def draw(self, axes: plt.Axes) -> plt.Axes: height=self.rectilinear_transform.height, rotation_point=self.rectilinear_transform.pivot.to_tuple(), angle=self.rotation, - edgecolor='none', + edgecolor=self.style_settings.background_color, + linewidth=0.1, # Fixed (small) width, to prevent aliasing facecolor=self.style_settings.background_color, # Depends on background type zorder=self.style_settings.zorder, + antialiased=False, ) axes.add_patch(rectangle) return axes diff --git a/src/qce_circuit/visualization/visualize_layout/polygon_component.py b/src/qce_circuit/visualization/visualize_layout/polygon_component.py index 8c6fafb..92394e2 100644 --- a/src/qce_circuit/visualization/visualize_layout/polygon_component.py +++ b/src/qce_circuit/visualization/visualize_layout/polygon_component.py @@ -46,6 +46,33 @@ def draw(self, axes: plt.Axes) -> plt.Axes: # endregion +@dataclass(frozen=True) +class LineComponent(IDrawComponent): + """ + Data class, containing dimension data for drawing circle. + """ + pivot0: Vec2D + pivot1: Vec2D + alignment: TransformAlignment = field(default=TransformAlignment.MID_LEFT) + style_settings: LineSettings = field(default_factory=lambda: StyleManager.read_config().edge_style) + + # region Class Properties + @property + def vertices(self) -> List[Vec2D]: + return [self.pivot0, self.pivot1] + # endregion + + # region Interface Methods + def draw(self, axes: plt.Axes) -> plt.Axes: + """Method used for drawing component on Axes.""" + return PolylineComponent( + vertices=self.vertices, + alignment=self.alignment, + style_settings=self.style_settings, + ).draw(axes=axes) + # endregion + + @dataclass(frozen=True) class GateOperationComponent(IDrawComponent): """ diff --git a/src/qce_circuit/visualization/visualize_layout/style_manager.py b/src/qce_circuit/visualization/visualize_layout/style_manager.py index 81bf29d..49cd37d 100644 --- a/src/qce_circuit/visualization/visualize_layout/style_manager.py +++ b/src/qce_circuit/visualization/visualize_layout/style_manager.py @@ -84,11 +84,12 @@ class StyleSettings: Data class, describing a variety of parameter settings for stylization. """ # Color schemes + color_background_base: str = field(default='#e5e5e5') # #b0b0b0 color_background_x: str = field(default='#7ba3e3') color_background_z: str = field(default='#2bab4b') # '#3870c9' color_text: str = field(default='black') color_outline: str = field(default='black') - color_element: str = field(default='#b3c7e8') + color_element: str = field(default='#808080') color_element_outline: str = field(default='#1c50a3') color_park_operation: str = field(default='black') color_gate_operation: str = field(default='black') @@ -160,6 +161,15 @@ def line_style(self) -> LineSettings: zorder=self.zorder_line, ) + @property + def edge_style(self) -> LineSettings: + return LineSettings( + line_color=self.color_element, + line_width=self.width_line_small, + line_style='-', + zorder=self.zorder_line, + ) + @property def park_operation_style(self) -> ParkOperationStyleSettings: return ParkOperationStyleSettings( From 35f985092938ac6b083e06fb28a0248dc71a8d81 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:41:40 +0100 Subject: [PATCH 05/17] Added data qubit in gate sequence highlight color --- .../visualization/visualize_layout/display_connectivity.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/qce_circuit/visualization/visualize_layout/display_connectivity.py b/src/qce_circuit/visualization/visualize_layout/display_connectivity.py index a790eda..85ed2ba 100644 --- a/src/qce_circuit/visualization/visualize_layout/display_connectivity.py +++ b/src/qce_circuit/visualization/visualize_layout/display_connectivity.py @@ -365,11 +365,13 @@ class StabilizerGroupVisualConnectivityDescription(VisualConnectivityDescription Data class, overwriting VisualConnectivityDescription by implementing stabilizer group element visualization. """ element_color_overwrite: str = field(default_factory=lambda: StyleManager.read_config().color_element) + element_highlight_color_overwrite: str = field(default_factory=lambda: StyleManager.read_config().color_element_outline) # region Class Methods def get_element_components(self) -> List[IDrawComponent]: result: List[IDrawComponent] = [] style_setting: StyleSettings = StyleManager.read_config() + gate_sequence_data_qubit_ids: List[IQubitID] = [qubit_id for qubit_id in self.gate_sequence.qubit_ids if qubit_id in self.connectivity.data_qubit_ids] for qubit_id in self.connectivity.qubit_ids: background_color = self.element_color_overwrite @@ -377,6 +379,8 @@ def get_element_components(self) -> List[IDrawComponent]: background_color = style_setting.color_background_z if qubit_id in [parity_group.ancilla_id for parity_group in self.connectivity.parity_group_x]: background_color = style_setting.color_background_x + if qubit_id in gate_sequence_data_qubit_ids: + background_color = self.element_highlight_color_overwrite result.append(DotComponent( pivot=self.identifier_to_pivot(qubit_id) + self.pivot, From 7ee0edde06d4cffc388fe874d07c67124028fab6 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:42:16 +0100 Subject: [PATCH 06/17] Added unique rectilinear margins for width and height individually --- .../draw_components/operation_components.py | 9 +++++---- .../visualize_circuit/style_manager.py | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 10 deletions(-) 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 98019fb..288001d 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 @@ -71,12 +71,13 @@ class RectangleBlock(IRectTransformComponent, IDrawComponent): @property def rectilinear_transform(self) -> IRectTransform: """:return: 'Hard' rectilinear transform boundary. Should be treated as 'personal zone'.""" - margin: float = self.style_settings.rectilinear_margin - margin_pivot_shift: Vec2D = Vec2D(x=margin/2, y=0.0) + margin_width: float = self.style_settings.rectilinear_margin_width + margin_height: float = self.style_settings.rectilinear_margin_height + margin_pivot_shift: Vec2D = Vec2D(x=margin_width/2, y=0.0) return RectTransform( _pivot_strategy=FixedPivot(self.pivot + margin_pivot_shift), - _width_strategy=FixedLength(self.width - margin), - _height_strategy=FixedLength(self.height - margin), + _width_strategy=FixedLength(self.width - margin_width), + _height_strategy=FixedLength(self.height - margin_height), _parent_alignment=self.alignment, ) # endregion diff --git a/src/qce_circuit/visualization/visualize_circuit/style_manager.py b/src/qce_circuit/visualization/visualize_circuit/style_manager.py index a7b4a66..bc4b855 100644 --- a/src/qce_circuit/visualization/visualize_circuit/style_manager.py +++ b/src/qce_circuit/visualization/visualize_circuit/style_manager.py @@ -42,8 +42,10 @@ class OperationStyleSettings: dot_radius: float font_size: float subtext_font_size: float - rectilinear_margin: float - """Margin variable used to shrink the drawn rectangle to allow for 'white-space'.""" + rectilinear_margin_width: float + """Margin variable used to shrink the drawn rectangle to allow for 'white-space' (width dimension).""" + rectilinear_margin_height: float + """Margin variable used to shrink the drawn rectangle to allow for 'white-space' (height dimension).""" @dataclass(frozen=True) @@ -112,7 +114,8 @@ class StyleSettings: line_style_border: str = field(default='-') # Spacing - rectilinear_margin: float = field(default=0.1) + rectilinear_margin_width: float = field(default=0.1) + rectilinear_margin_height: float = field(default=0.1) # Header enable_state_description: bool = field(default=True) @@ -144,7 +147,8 @@ def operation_style(self) -> OperationStyleSettings: dot_radius=self.radius_dot, font_size=self.font_size, subtext_font_size=self.font_size_small, - rectilinear_margin=self.rectilinear_margin, + rectilinear_margin_width=self.rectilinear_margin_width, + rectilinear_margin_height=self.rectilinear_margin_height, ) @property @@ -159,7 +163,8 @@ def vacant_operation_style(self) -> OperationStyleSettings: dot_radius=self.radius_dot, font_size=self.font_size, subtext_font_size=self.font_size_small, - rectilinear_margin=self.rectilinear_margin, + rectilinear_margin_width=self.rectilinear_margin_width, + rectilinear_margin_height=self.rectilinear_margin_height, ) @property @@ -174,7 +179,8 @@ def empty_operation_style(self) -> OperationStyleSettings: dot_radius=self.radius_dot, font_size=self.font_size, subtext_font_size=self.font_size_small, - rectilinear_margin=self.rectilinear_margin, + rectilinear_margin_width=self.rectilinear_margin_width, + rectilinear_margin_height=self.rectilinear_margin_height, ) @property From 0fdec912dc17f0a1deac0abdce08b7aed996b239 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Wed, 14 Jan 2026 17:51:04 +0100 Subject: [PATCH 07/17] Updated circuit plotting system with many more style settings available. Fixed measure icon arrow shrink --- .../visualize_circuit/display_circuit.py | 38 ++++++++++++++++--- .../draw_components/channel_components.py | 8 ++-- .../factory_draw_components.py | 25 +++++++++++- .../draw_components/icon_components.py | 4 +- .../draw_components/operation_components.py | 2 +- .../draw_components/transform_constructor.py | 5 ++- .../visualize_circuit/style_manager.py | 32 +++++++++++++++- 7 files changed, 96 insertions(+), 18 deletions(-) diff --git a/src/qce_circuit/visualization/visualize_circuit/display_circuit.py b/src/qce_circuit/visualization/visualize_circuit/display_circuit.py index d5898b9..95ed5c7 100644 --- a/src/qce_circuit/visualization/visualize_circuit/display_circuit.py +++ b/src/qce_circuit/visualization/visualize_circuit/display_circuit.py @@ -48,6 +48,7 @@ ) from qce_circuit.visualization.visualize_circuit.draw_components.transform_constructor import TransformConstructor from qce_circuit.visualization.visualize_circuit.intrf_draw_component import IDrawComponent +from qce_circuit.visualization.visualize_circuit.style_manager import StyleManager from qce_circuit.utilities.geometric_definitions import ( IRectTransformComponent, IRectTransform, @@ -158,27 +159,40 @@ class VisualCircuitDescription: composite_operations: List[ICircuitCompositeOperation] """Array of composite operations.""" channel_label_map: Dict[int, str] = field(default_factory=dict) + """Channel identifier index to label mapping""" + channel_color_map: Dict[int, str] = field(default_factory=dict) + """Channel identifier index to color mapping""" minimalist: bool = field(default=False) """Minimalist drawing style, passed to factories""" + channel_height_scaling: float = field(default_factory=lambda: StyleManager.read_config().channel_height_scaling) + """Scalar for (duration) height of visualized circuit. Adds white space inbetween channel components.""" + channel_width_scaling: float = field(default_factory=lambda: StyleManager.read_config().channel_width_scaling) + """Scalar for (duration) width of visualized circuit. Adds white space inbetween time-grouped components.""" # region Class Properties @property def channel_spacing(self) -> float: - return self.channel_height * 1.2 + return self.channel_height * self.channel_height_scaling @property def figure_size(self) -> Tuple[float, float]: """:return: Figure size (x, y) depending on visualized circuit components.""" - return (self.channel_width, self.channel_spacing * len(self.channel_indices)) + return (self.channel_width * self.channel_width_scaling, self.channel_spacing * len(self.channel_indices)) # endregion # region Class Methods def get_channel_bar(self, index: int) -> ChannelBar: """:return: Channel bar drawing components.""" + style_settings = StyleManager.read_config().channel_style + identifier = self.channel_indices[index] + if identifier in self.channel_color_map: + style_settings = style_settings.update_color(line_color=self.channel_color_map[identifier]) + return ChannelBar( pivot=Vec2D(x=0, y=-1 * index * self.channel_spacing), - width=self.channel_width, + width=self.channel_width * self.channel_width_scaling, height=self.channel_height, + style_settings=style_settings, ) def get_channel_header(self, index: int) -> ChannelHeader: @@ -189,12 +203,17 @@ def get_channel_header(self, index: int) -> ChannelHeader: if index in self.channel_label_map: channel_name = f'{self.channel_label_map[index]}' + style_settings = StyleManager.read_config().channel_style + if channel_index in self.channel_color_map: + style_settings = style_settings.update_color(line_color=self.channel_color_map[channel_index]) + channel_state = self.channel_states[index].value return ChannelHeader( pivot=Vec2D(x=0, y=-1 * index * self.channel_spacing), height=FixedLength(self.channel_height), channel_name=channel_name, state_description=rf'$|{channel_state}\rangle$', + style_settings=style_settings, ) def get_transform_constructor(self) -> TransformConstructor: @@ -202,6 +221,7 @@ def get_transform_constructor(self) -> TransformConstructor: channel_height=self.channel_height, channel_spacing=self.channel_spacing, channel_indices=self.channel_indices, + channel_width_scaling=self.channel_width_scaling, ) def get_operation_draw_components(self) -> List[IDrawComponent]: @@ -227,7 +247,7 @@ def get_operation_draw_components(self) -> List[IDrawComponent]: Identity: IdentityFactory(), Hadamard: HadamardFactory(), Barrier: BarrierFactory(), - VirtualPark: VirtualParkFactory(), + VirtualPark: VirtualParkFactory(channel_identifier_color_map=self.channel_color_map), VirtualVacant: VirtualVacantFactory(), VirtualTwoQubitVacant: VirtualTwoQubitVacantFactory(), VirtualEmpty: VirtualEmptyFactory(), @@ -318,9 +338,12 @@ def reorder_map(original_order: Dict[T, Any], specific_order: List[T]) -> Dict[T return result -def construct_visual_description(circuit: IDeclarativeCircuit, custom_channel_order: Optional[List[int]] = None, custom_channel_map: Optional[Dict[int, str]] = None, minimalist: bool = False) -> VisualCircuitDescription: +def construct_visual_description(circuit: IDeclarativeCircuit, custom_channel_order: Optional[List[int]] = None, custom_channel_map: Optional[Dict[int, str]] = None, channel_color_map: Dict[int, str] = None, minimalist: bool = False) -> VisualCircuitDescription: """:return: Draw description based on declarative circuit interface instance.""" channel_indices: List[int] = unique_in_order([identifier.id for identifier in circuit.occupied_qubit_channels]) + # Construct custom channel color map + if channel_color_map is None: + channel_color_map = {} # Apply custom channel order if custom_channel_order is None: custom_channel_order = [] @@ -351,6 +374,7 @@ def construct_visual_description(circuit: IDeclarativeCircuit, custom_channel_or channel_height=1.0, channel_indices=channel_indices, channel_label_map=custom_channel_map, + channel_color_map=channel_color_map, channel_states=channel_states, operations=operations, composite_operations=circuit.composite_operations, @@ -570,7 +594,7 @@ def plot_debug_schedule(**kwargs) -> IFigureAxesPair: return fig, ax -def plot_circuit(circuit: IDeclarativeCircuit, channel_order: List[int] = None, channel_map: Optional[Dict[int, str]] = None, compact_visualization: bool = True, minimalist_visualization: bool = False, **kwargs) -> IFigureAxesPair: +def plot_circuit(circuit: IDeclarativeCircuit, channel_order: List[int] = None, channel_map: Optional[Dict[int, str]] = None, channel_color_map: Dict[int, str] = None, compact_visualization: bool = True, minimalist_visualization: bool = False, **kwargs) -> IFigureAxesPair: if compact_visualization: with temporary_override_get_registry_at(VISUALIZATION_DURATION_REGISTRY): with clear_lru_cache(RelationLink.get_start_time): @@ -579,6 +603,7 @@ def plot_circuit(circuit: IDeclarativeCircuit, channel_order: List[int] = None, circuit=circuit, custom_channel_order=channel_order, custom_channel_map=channel_map, + channel_color_map=channel_color_map, minimalist=minimalist_visualization, ), **kwargs @@ -590,6 +615,7 @@ def plot_circuit(circuit: IDeclarativeCircuit, channel_order: List[int] = None, circuit=circuit, custom_channel_order=channel_order, custom_channel_map=channel_map, + channel_color_map=channel_color_map, minimalist=minimalist_visualization, ), **kwargs diff --git a/src/qce_circuit/visualization/visualize_circuit/draw_components/channel_components.py b/src/qce_circuit/visualization/visualize_circuit/draw_components/channel_components.py index ff60df3..8eec3f3 100644 --- a/src/qce_circuit/visualization/visualize_circuit/draw_components/channel_components.py +++ b/src/qce_circuit/visualization/visualize_circuit/draw_components/channel_components.py @@ -52,10 +52,10 @@ def count_latex_characters(latex_str: str) -> int: return total_count -def estimate_latex_length(latex_str: str, font_size: float) -> float: +def estimate_latex_length(latex_str: str, font_size: float, scaling_factor: float = 1.0) -> float: # Define a scaling factor and calculate the average character width - scaling_factor: float = 0.025 - avg_char_width: float = scaling_factor * font_size + _scaling_factor: float = 0.025 * scaling_factor + avg_char_width: float = _scaling_factor * font_size # Get the total character count char_count: int = count_latex_characters(latex_str=latex_str) @@ -100,7 +100,7 @@ def divider_width(self) -> float: @property def channel_name_width(self) -> float: - return estimate_latex_length(latex_str=self.channel_name, font_size=self.style_settings.font_size) + return self.style_settings.name_description_width @property def state_description_width(self) -> float: 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 b291601..1f0fe63 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 @@ -2,7 +2,7 @@ # Module containing functionality for constructing draw components from operation class types. # ------------------------------------------- from dataclasses import dataclass, field -from typing import List +from typing import List, Optional, Dict from qce_circuit.structure.intrf_circuit_operation import ( ICircuitOperation, ChannelIdentifier, @@ -71,7 +71,10 @@ HorizontalVariableIndicator, RoundedRectangleHighlight, ) -from qce_circuit.visualization.visualize_circuit.style_manager import StyleManager +from qce_circuit.visualization.visualize_circuit.style_manager import ( + StyleManager, + ChannelStyleSettings, +) class DefaultFactory(IOperationDrawComponentFactory[ICircuitOperation, IDrawComponent]): @@ -158,6 +161,7 @@ def construct(self, operation: Rx90, transform_constructor: ITransformConstructo height=transform.height, alignment=transform.parent_alignment, header_text=f"+{RotationAxis.X.value}/2", + style_settings=StyleManager.read_config().operation_minimalist_style, ) return BlockRotation( @@ -187,6 +191,7 @@ def construct(self, operation: Rxm90, transform_constructor: ITransformConstruct height=transform.height, alignment=transform.parent_alignment, header_text=f"-{RotationAxis.X.value}/2", + style_settings=StyleManager.read_config().operation_minimalist_style, ) return BlockRotation( @@ -264,6 +269,7 @@ def construct(self, operation: Ry90, transform_constructor: ITransformConstructo height=transform.height, alignment=transform.parent_alignment, header_text=f"+{RotationAxis.Y.value}/2", + style_settings=StyleManager.read_config().operation_minimalist_style, ) return BlockRotation( @@ -293,6 +299,7 @@ def construct(self, operation: Rym90, transform_constructor: ITransformConstruct height=transform.height, alignment=transform.parent_alignment, header_text=f"-{RotationAxis.Y.value}/2", + style_settings=StyleManager.read_config().operation_minimalist_style, ) return BlockRotation( @@ -431,6 +438,14 @@ def construct(self, operation: Hadamard, transform_constructor: ITransformConstr class VirtualParkFactory(IOperationDrawComponentFactory[VirtualPark, IDrawComponent]): + # region Class Constructor + def __init__(self, channel_identifier_color_map: Optional[Dict[int, str]] = None): + if channel_identifier_color_map is None: + channel_identifier_color_map = dict() + self.channel_identifier_color_map: Dict[int, str] = channel_identifier_color_map + """(Optional) colormap for updating line-color style. Artistic purpose only.""" + # endregion + # region Interface Methods def construct(self, operation: VirtualPark, transform_constructor: ITransformConstructor) -> IDrawComponent: """:return: Draw component based on operation type.""" @@ -438,12 +453,17 @@ def construct(self, operation: VirtualPark, transform_constructor: ITransformCon identifier=operation.channel_identifiers[0], time_component=operation, ) + style_settings: ChannelStyleSettings = StyleManager.read_config().channel_style + if operation.qubit_index in self.channel_identifier_color_map: + style_settings = style_settings.update_color(line_color=self.channel_identifier_color_map[operation.qubit_index]) + if operation.net_zero: return SquareNetZeroParkBlock( pivot=transform.pivot, height=transform.height, width=transform.width, alignment=transform.parent_alignment, + style_settings=style_settings, ) return SquareParkBlock( @@ -451,6 +471,7 @@ def construct(self, operation: VirtualPark, transform_constructor: ITransformCon height=transform.height, width=transform.width, alignment=transform.parent_alignment, + style_settings=style_settings, ) # endregion diff --git a/src/qce_circuit/visualization/visualize_circuit/draw_components/icon_components.py b/src/qce_circuit/visualization/visualize_circuit/draw_components/icon_components.py index 4ff9783..a2bff19 100644 --- a/src/qce_circuit/visualization/visualize_circuit/draw_components/icon_components.py +++ b/src/qce_circuit/visualization/visualize_circuit/draw_components/icon_components.py @@ -88,8 +88,10 @@ def draw(self, axes: plt.Axes) -> plt.Axes: head_width=self.arrow_head_width, tail_width=self.arrow_thickness, ), + shrinkA=0, + shrinkB=0, color=self.style_settings.icon_color, - linewidth=self.circle_thickness, + linewidth=self.arrow_thickness, ) arrow_base = patches.Circle( xy=self.circle_center.to_tuple(), 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 288001d..8db48db 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 @@ -73,7 +73,7 @@ def rectilinear_transform(self) -> IRectTransform: """:return: 'Hard' rectilinear transform boundary. Should be treated as 'personal zone'.""" margin_width: float = self.style_settings.rectilinear_margin_width margin_height: float = self.style_settings.rectilinear_margin_height - margin_pivot_shift: Vec2D = Vec2D(x=margin_width/2, y=0.0) + margin_pivot_shift: Vec2D = Vec2D(x=0.0, y=0.0) return RectTransform( _pivot_strategy=FixedPivot(self.pivot + margin_pivot_shift), _width_strategy=FixedLength(self.width - margin_width), diff --git a/src/qce_circuit/visualization/visualize_circuit/draw_components/transform_constructor.py b/src/qce_circuit/visualization/visualize_circuit/draw_components/transform_constructor.py index c61339a..b66cf5d 100644 --- a/src/qce_circuit/visualization/visualize_circuit/draw_components/transform_constructor.py +++ b/src/qce_circuit/visualization/visualize_circuit/draw_components/transform_constructor.py @@ -18,18 +18,19 @@ class TransformConstructor(ITransformConstructor): channel_spacing: float channel_indices: List[int] """Array of unique, ordered channel indices.""" + channel_width_scaling: float = 1.0 # region Interface Methods def identifier_to_pivot(self, identifier: ChannelIdentifier, time_component: IDurationComponent) -> Vec2D: """:return: Pivot based on channel identifier and duration component.""" return Vec2D( - x=time_component.start_time, + x=time_component.start_time * self.channel_width_scaling, y=-1 * self.channel_indices.index(identifier.id) * self.channel_spacing ) def identifier_to_width(self, time_component: IDurationComponent) -> float: """:return: Rectilinear transform height based on duration component.""" - return time_component.duration + return time_component.duration * self.channel_width_scaling def identifier_to_height(self, identifier: ChannelIdentifier) -> float: """:return: Rectilinear transform height based on channel identifier.""" diff --git a/src/qce_circuit/visualization/visualize_circuit/style_manager.py b/src/qce_circuit/visualization/visualize_circuit/style_manager.py index bc4b855..bae06ce 100644 --- a/src/qce_circuit/visualization/visualize_circuit/style_manager.py +++ b/src/qce_circuit/visualization/visualize_circuit/style_manager.py @@ -2,7 +2,7 @@ # Module for specific (Quantum circuit visualization) style manager # ------------------------------------------- import os -from dataclasses import dataclass, field +from dataclasses import dataclass, field, replace import threading from contextlib import contextmanager from qce_circuit.utilities.singleton_base import Singleton @@ -23,10 +23,16 @@ class ChannelStyleSettings: line_width: float font_size: float divider_width: float + name_description_width: float state_description_width: float enable_state_description: bool enable_label_description: bool + # region Class Methods + def update_color(self, line_color: str) -> 'ChannelStyleSettings': + return replace(self, line_color=line_color) + # endregion + @dataclass(frozen=True) class OperationStyleSettings: @@ -92,6 +98,7 @@ class StyleSettings: color_icon: str = field(default='black') color_outline: str = field(default='black') color_outline_dim: str = field(default='darkgrey') + color_channel_bar: str = field(default='black') color_highlight_background: str = field(default='lightblue') color_highlight_outline: str = field(default='blue') @@ -102,6 +109,7 @@ class StyleSettings: width_border: float = field(default=2.0) width_divider: float = field(default=0.4) width_state_description: float = field(default=0.7) + width_name_description: float = field(default=0.5) # Radius radius_dot: float = field(default=0.1) @@ -115,7 +123,10 @@ class StyleSettings: # Spacing rectilinear_margin_width: float = field(default=0.1) + rectilinear_margin_minimalist_broad_width: float = field(default=0.1) rectilinear_margin_height: float = field(default=0.1) + channel_height_scaling: float = field(default=1.2) + channel_width_scaling: float = field(default=1.0) # Header enable_state_description: bool = field(default=True) @@ -125,9 +136,10 @@ class StyleSettings: @property def channel_style(self) -> ChannelStyleSettings: return ChannelStyleSettings( - line_color=self.color_outline, + line_color=self.color_channel_bar, text_color=self.color_text, line_width=self.width_line, + name_description_width=self.width_name_description, font_size=self.font_size, divider_width=self.width_divider, state_description_width=self.width_state_description, @@ -151,6 +163,22 @@ def operation_style(self) -> OperationStyleSettings: rectilinear_margin_height=self.rectilinear_margin_height, ) + @property + def operation_minimalist_style(self) -> OperationStyleSettings: + return OperationStyleSettings( + border_color=self.color_outline, + background_color=self.color_background, + text_color=self.color_text, + border_width=self.width_border, + line_width=self.width_line, + border_line_style=self.line_style_border, + dot_radius=self.radius_dot, + font_size=self.font_size, + subtext_font_size=self.font_size_small, + rectilinear_margin_width=self.rectilinear_margin_minimalist_broad_width + self.rectilinear_margin_width, + rectilinear_margin_height=self.rectilinear_margin_height, + ) + @property def vacant_operation_style(self) -> OperationStyleSettings: return OperationStyleSettings( From 6fae6d1fb115ec5e53f058ec2a9bb38fd772ab83 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Wed, 14 Jan 2026 23:45:11 +0100 Subject: [PATCH 08/17] Refactored circuit operations copy with dataclass replace method. Updated duration strategy to be adjustable on init. Updated (square) block to expose width as optional free parameter. --- .../structure/circuit_operations.py | 230 ++++++++---------- .../structure/registry_duration.py | 2 + .../factory_draw_components.py | 12 +- .../draw_components/operation_components.py | 6 +- .../visualize_circuit/style_manager.py | 19 +- .../visualize_layout/display_connectivity.py | 7 +- .../visualize_layout/element_components.py | 1 + 7 files changed, 129 insertions(+), 148 deletions(-) diff --git a/src/qce_circuit/structure/circuit_operations.py b/src/qce_circuit/structure/circuit_operations.py index f7ded10..4c7ca7d 100644 --- a/src/qce_circuit/structure/circuit_operations.py +++ b/src/qce_circuit/structure/circuit_operations.py @@ -2,7 +2,7 @@ # Module describing the declarative operations. # ------------------------------------------- from abc import ABC, abstractmethod -from dataclasses import dataclass, field +from dataclasses import dataclass, field, replace from typing import List, Optional, Dict from qce_circuit.utilities.custom_exceptions import InterfaceMethodException from qce_circuit.structure.intrf_circuit_operation import ( @@ -114,7 +114,7 @@ class Reset(SingleQubitOperation, ICircuitOperation): """ Reset operation covers all qubit channels. """ - duration_strategy: IDurationStrategy = field(init=False, default=GlobalDurationStrategy(GlobalRegistryKey.RESET)) + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.RESET)) # region Interface Properties @property @@ -132,9 +132,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return Reset( - qubit_index=self.qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -165,11 +165,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return Wait( - qubit_index=self.qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), - qubit_channel=self.qubit_channel, - duration_strategy=self.duration_strategy, + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -179,7 +177,7 @@ class Identity(SingleQubitOperation, ICircuitOperation): """ Identity operation. """ - duration_strategy: IDurationStrategy = field(init=False, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) # region Interface Properties @property @@ -197,9 +195,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return Identity( - qubit_index=self.qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -209,7 +207,7 @@ class Hadamard(SingleQubitOperation, ICircuitOperation): """ Hadamard operation. """ - duration_strategy: IDurationStrategy = field(init=False, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) # region Interface Properties @property @@ -227,9 +225,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return Hadamard( - qubit_index=self.qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -239,7 +237,7 @@ class Rx180(SingleQubitOperation, ICircuitOperation): """ Rotation-X (180 degrees) operation. """ - duration_strategy: IDurationStrategy = field(init=False, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) # region Interface Properties @property @@ -257,9 +255,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return Rx180( - qubit_index=self.qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -269,7 +267,7 @@ class Rx90(SingleQubitOperation, ICircuitOperation): """ Rotation-X (90 degrees) operation. """ - duration_strategy: IDurationStrategy = field(init=False, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) # region Interface Properties @property @@ -287,9 +285,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return Rx90( - qubit_index=self.qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -299,7 +297,7 @@ class Rxm90(SingleQubitOperation, ICircuitOperation): """ Rotation-X (-90 degrees) operation. """ - duration_strategy: IDurationStrategy = field(init=False, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) # region Interface Properties @property @@ -317,9 +315,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return Rxm90( - qubit_index=self.qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -329,7 +327,7 @@ class RxTheta(SingleQubitOperation, ICircuitOperation): """ Rotation-X (theta degrees) operation. """ - duration_strategy: IDurationStrategy = field(init=False, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) # region Interface Properties @property @@ -347,9 +345,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return RxTheta( - qubit_index=self.qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -359,7 +357,7 @@ class Ry180(SingleQubitOperation, ICircuitOperation): """ Rotation-Y (180 degrees) operation. """ - duration_strategy: IDurationStrategy = field(init=False, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) # region Interface Properties @property @@ -377,9 +375,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return Ry180( - qubit_index=self.qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -389,7 +387,7 @@ class Ry90(SingleQubitOperation, ICircuitOperation): """ Rotation-Y (90 degrees) operation. """ - duration_strategy: IDurationStrategy = field(init=False, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) # region Interface Properties @property @@ -407,9 +405,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return Ry90( - qubit_index=self.qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -419,7 +417,7 @@ class Rym90(SingleQubitOperation, ICircuitOperation): """ Rotation-Y (-90 degrees) operation. """ - duration_strategy: IDurationStrategy = field(init=False, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) # region Interface Properties @property @@ -437,9 +435,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return Rym90( - qubit_index=self.qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -449,7 +447,7 @@ class RyTheta(SingleQubitOperation, ICircuitOperation): """ Rotation-Y (theta degrees) operation. """ - duration_strategy: IDurationStrategy = field(init=False, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) # region Interface Properties @property @@ -467,9 +465,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return RyTheta( - qubit_index=self.qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -479,7 +477,7 @@ class Rx180ef(SingleQubitOperation, ICircuitOperation): """ Rotation-X (180 degrees) operation between excited (e) and second-excited (f) state. """ - duration_strategy: IDurationStrategy = field(init=False, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) # region Interface Properties @property @@ -497,9 +495,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return Rx180ef( - qubit_index=self.qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -509,7 +507,7 @@ class VirtualPhase(SingleQubitOperation, ICircuitOperation): """ Virtual (Z) phase rotation operation. """ - duration_strategy: IDurationStrategy = field(init=False, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) # region Interface Properties @property @@ -527,9 +525,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return VirtualPhase( - qubit_index=self.qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -540,7 +538,7 @@ class VirtualPark(SingleQubitOperation, ICircuitOperation): Virtual park operation. Usually only interesting when working with frequency-tunable qubits. """ - duration_strategy: IDurationStrategy = field(init=False, default=GlobalDurationStrategy(GlobalRegistryKey.FLUX)) + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.FLUX)) net_zero: bool = field(init=True, default=False) """Boolean describing the net-zero behaviour of the virtual parking. - Mainly useful for visualization.""" @@ -560,9 +558,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return VirtualPark( - qubit_index=self.qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -572,7 +570,7 @@ class Rphi90(SingleQubitOperation, ICircuitOperation): """ Rotation- [Xcos(phi) + Ysin(phi)] (90 degrees) operation. """ - duration_strategy: IDurationStrategy = field(init=False, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) # region Interface Properties @property @@ -590,9 +588,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return Rphi90( - qubit_index=self.qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -661,11 +659,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return TwoQubitOperation( - _control_qubit_index=self.control_qubit_index, - _target_qubit_index=self.target_qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), - duration_strategy=self.duration_strategy, + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) def apply_modifiers_to_self(self) -> ICircuitOperation: @@ -698,7 +694,7 @@ class CPhase(TwoQubitOperation, ICircuitOperation): """ Control-Phase operation. """ - duration_strategy: IDurationStrategy = field(init=False, default=GlobalDurationStrategy(GlobalRegistryKey.FLUX)) + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.FLUX)) # region Interface Properties @property @@ -719,10 +715,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return CPhase( - _control_qubit_index=self.control_qubit_index, - _target_qubit_index=self.target_qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -732,7 +727,7 @@ class TwoQubitVirtualPhase(TwoQubitOperation, ICircuitOperation): """ Virtual (Z) phase rotation operation. """ - duration_strategy: IDurationStrategy = field(init=False, default=FixedDurationStrategy(duration=0.0)) + duration_strategy: IDurationStrategy = field(init=True, default=FixedDurationStrategy(duration=0.0)) # region Interface Properties @property @@ -751,10 +746,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return TwoQubitVirtualPhase( - _control_qubit_index=self.control_qubit_index, - _target_qubit_index=self.target_qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -768,7 +762,7 @@ class DispersiveMeasure(IAcquisitionOperation): acquisition_strategy: IAcquisitionStrategy = field(init=True, repr=False) acquisition_tag: str = field(init=True, default='', repr=True) relation: IRelationLink[ICircuitOperation] = field(default_factory=RelationLink.no_relation, repr=False) - duration_strategy: IDurationStrategy = field(init=False, default=GlobalDurationStrategy(GlobalRegistryKey.READOUT), repr=False) + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.READOUT), repr=False) _acquisition_identifier: AcquisitionIdentifier = field(init=False, repr=False) # region Interface Properties @@ -833,11 +827,10 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return DispersiveMeasure( - qubit_index=self.qubit_index, + return replace( + self, acquisition_strategy=self.acquisition_strategy.copy(strategy_transfer_lookup=relation_transfer_lookup), - acquisition_tag=self.acquisition_tag, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) def apply_modifiers_to_self(self) -> IAcquisitionOperation: @@ -880,7 +873,7 @@ class Barrier(ICircuitOperation): """ qubit_indices: List[int] = field(init=True) relation: IRelationLink[ICircuitOperation] = field(init=False, default_factory=RelationLink.no_relation) - duration_strategy: IDurationStrategy = field(init=False, default=GlobalDurationStrategy(GlobalRegistryKey.BARRIER)) + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.BARRIER)) # region Interface Properties @property @@ -924,9 +917,7 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return Barrier( - qubit_indices=self.qubit_indices, - ) + return replace(self) def apply_modifiers_to_self(self) -> ICircuitOperation: """ @@ -1010,10 +1001,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return VirtualQECOperation( - qubit_indices=self.qubit_indices, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), - duration_strategy=self.duration_strategy, + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) def apply_modifiers_to_self(self) -> ICircuitOperation: @@ -1071,11 +1061,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return VirtualVacant( - qubit_index=self.qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), - qubit_channel=self.qubit_channel, - duration_strategy=self.duration_strategy, + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -1105,10 +1093,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return VirtualTwoQubitVacant( - _control_qubit_index=self.control_qubit_index, - _target_qubit_index=self.target_qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -1137,11 +1124,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return VirtualEmpty( - qubit_index=self.qubit_index, - relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), - qubit_channel=self.qubit_channel, - duration_strategy=self.duration_strategy, + return replace( + self, + relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup) ) # endregion @@ -1193,7 +1178,8 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return VirtualOptional( + return replace( + self, operation=self.operation.copy( relation_transfer_lookup=relation_transfer_lookup, ) @@ -1273,12 +1259,11 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return VirtualInjectedError( + return replace( + self, operation=self.operation.copy( relation_transfer_lookup=relation_transfer_lookup, ), - line_style_border_overwrite=self.line_style_border_overwrite, - color_background_overwrite=self.color_background_overwrite, ) def apply_modifiers_to_self(self) -> ICircuitOperation: @@ -1313,7 +1298,7 @@ class VirtualWait(SingleQubitOperation, ICircuitOperation): Allow to wait on specific (qubit) channel. Intended to display wait time. """ - duration_strategy: IDurationStrategy = field(init=False, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) + duration_strategy: IDurationStrategy = field(init=True, default=GlobalDurationStrategy(GlobalRegistryKey.MICROWAVE)) qubit_channel: QubitChannel = field(init=True, default=QubitChannel.ALL) header_text: str = field(init=True, default="W") body_text: str = field(init=True, default="") @@ -1334,12 +1319,9 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return VirtualWait( - qubit_index=self.qubit_index, + return replace( + self, relation=self.relation.copy(relation_transfer_lookup=relation_transfer_lookup), - qubit_channel=self.qubit_channel, - header_text=self.header_text, - body_text=self.body_text, ) # endregion @@ -1438,11 +1420,11 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return VirtualColorOverwrite( + return replace( + self, operation=self.operation.copy( relation_transfer_lookup=relation_transfer_lookup, - ), - _color_overwrite=self._color_overwrite, + ) ) def apply_modifiers_to_self(self) -> ICircuitOperation: @@ -1542,11 +1524,11 @@ def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircu :param relation_transfer_lookup: Lookup table used to transfer relation link. :return: Copy of self with updated relation link. """ - return VirtualTwoQubitColorOverwrite( + return replace( + self, operation=self.operation.copy( relation_transfer_lookup=relation_transfer_lookup, - ), - _color_overwrite=self._color_overwrite, + ) ) def apply_modifiers_to_self(self) -> ICircuitOperation: diff --git a/src/qce_circuit/structure/registry_duration.py b/src/qce_circuit/structure/registry_duration.py index e53b73f..63e5efe 100644 --- a/src/qce_circuit/structure/registry_duration.py +++ b/src/qce_circuit/structure/registry_duration.py @@ -29,6 +29,7 @@ class GlobalRegistryKey(Enum): READOUT = 'default_allocated_readout_duration' MICROWAVE = 'default_allocated_microwave_duration' + MICROWAVE_BROAD = 'default_allocated_microwave_broad_duration' FLUX = 'default_allocated_flux_duration' RESET = 'default_allocated_reset_duration' QEC_BLOCK = 'default_allocated_qec_duration' @@ -43,6 +44,7 @@ class GlobalDurationRegistry(IRegistryGetter[GlobalRegistryKey, float]): _global_registry: Dict[str, float] = field(default_factory=lambda: { GlobalRegistryKey.READOUT.value: 2.0, GlobalRegistryKey.MICROWAVE.value: 1.0, + GlobalRegistryKey.MICROWAVE_BROAD.value: 2.0, GlobalRegistryKey.FLUX.value: 1.0, GlobalRegistryKey.RESET.value: 2.0, GlobalRegistryKey.QEC_BLOCK.value: 2.0, 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 1f0fe63..b915e4a 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 @@ -158,14 +158,15 @@ def construct(self, operation: Rx90, transform_constructor: ITransformConstructo if self.minimalist: return BlockHeaderBody( pivot=transform.pivot, + width=transform.width, height=transform.height, alignment=transform.parent_alignment, header_text=f"+{RotationAxis.X.value}/2", - style_settings=StyleManager.read_config().operation_minimalist_style, ) return BlockRotation( pivot=transform.pivot, + width=transform.width, height=transform.height, alignment=transform.parent_alignment, rotation_axes=RotationAxis.X, @@ -188,14 +189,15 @@ def construct(self, operation: Rxm90, 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}/2", - style_settings=StyleManager.read_config().operation_minimalist_style, ) return BlockRotation( pivot=transform.pivot, + width=transform.width, height=transform.height, alignment=transform.parent_alignment, rotation_axes=RotationAxis.X, @@ -266,14 +268,15 @@ def construct(self, operation: Ry90, transform_constructor: ITransformConstructo if self.minimalist: return BlockHeaderBody( pivot=transform.pivot, + width=transform.width, height=transform.height, alignment=transform.parent_alignment, header_text=f"+{RotationAxis.Y.value}/2", - style_settings=StyleManager.read_config().operation_minimalist_style, ) return BlockRotation( pivot=transform.pivot, + width=transform.width, height=transform.height, alignment=transform.parent_alignment, rotation_axes=RotationAxis.Y, @@ -296,14 +299,15 @@ def construct(self, operation: Rym90, 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.Y.value}/2", - style_settings=StyleManager.read_config().operation_minimalist_style, ) return BlockRotation( pivot=transform.pivot, + width=transform.width, height=transform.height, alignment=transform.parent_alignment, rotation_axes=RotationAxis.Y, 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 8db48db..6cad294 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 @@ -178,6 +178,7 @@ class SquareBlock(IRectTransformComponent, IDrawComponent): """ pivot: Vec2D height: float + width: float = field(default=None) 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) @@ -195,9 +196,12 @@ def draw(self, axes: plt.Axes) -> plt.Axes: return self._base_block.draw(axes=axes) def __post_init__(self): + if self.width is None: + object.__setattr__(self, 'width', self.height) + object.__setattr__(self, '_base_block', RectangleBlock( pivot=self.pivot, - width=self.height, + width=self.width, height=self.height, alignment=self.alignment, style_settings=self.style_settings, diff --git a/src/qce_circuit/visualization/visualize_circuit/style_manager.py b/src/qce_circuit/visualization/visualize_circuit/style_manager.py index bae06ce..1f7fadd 100644 --- a/src/qce_circuit/visualization/visualize_circuit/style_manager.py +++ b/src/qce_circuit/visualization/visualize_circuit/style_manager.py @@ -106,6 +106,7 @@ class StyleSettings: width_line: float = field(default=2.0) width_line_small: float = field(default=1.0) width_line_icon: float = field(default=6.0) + width_line_bar: float = field(default=2.0) width_border: float = field(default=2.0) width_divider: float = field(default=0.4) width_state_description: float = field(default=0.7) @@ -138,7 +139,7 @@ def channel_style(self) -> ChannelStyleSettings: return ChannelStyleSettings( line_color=self.color_channel_bar, text_color=self.color_text, - line_width=self.width_line, + line_width=self.width_line_bar, name_description_width=self.width_name_description, font_size=self.font_size, divider_width=self.width_divider, @@ -163,22 +164,6 @@ def operation_style(self) -> OperationStyleSettings: rectilinear_margin_height=self.rectilinear_margin_height, ) - @property - def operation_minimalist_style(self) -> OperationStyleSettings: - return OperationStyleSettings( - border_color=self.color_outline, - background_color=self.color_background, - text_color=self.color_text, - border_width=self.width_border, - line_width=self.width_line, - border_line_style=self.line_style_border, - dot_radius=self.radius_dot, - font_size=self.font_size, - subtext_font_size=self.font_size_small, - rectilinear_margin_width=self.rectilinear_margin_minimalist_broad_width + self.rectilinear_margin_width, - rectilinear_margin_height=self.rectilinear_margin_height, - ) - @property def vacant_operation_style(self) -> OperationStyleSettings: return OperationStyleSettings( diff --git a/src/qce_circuit/visualization/visualize_layout/display_connectivity.py b/src/qce_circuit/visualization/visualize_layout/display_connectivity.py index 85ed2ba..3ee5c75 100644 --- a/src/qce_circuit/visualization/visualize_layout/display_connectivity.py +++ b/src/qce_circuit/visualization/visualize_layout/display_connectivity.py @@ -366,6 +366,7 @@ class StabilizerGroupVisualConnectivityDescription(VisualConnectivityDescription """ element_color_overwrite: str = field(default_factory=lambda: StyleManager.read_config().color_element) element_highlight_color_overwrite: str = field(default_factory=lambda: StyleManager.read_config().color_element_outline) + include_gate_sequence_labels: bool = field(default=False) # region Class Methods def get_element_components(self) -> List[IDrawComponent]: @@ -392,7 +393,7 @@ def get_element_components(self) -> List[IDrawComponent]: zorder=style_setting.zorder_element, ), )) - if self.include_element_labels: + if self.include_element_labels or (self.include_gate_sequence_labels and qubit_id in self.gate_sequence.qubit_ids): result.append(TextComponent( pivot=self.identifier_to_pivot(qubit_id) + self.pivot, text=qubit_id.id, @@ -445,7 +446,7 @@ def plot_gate_sequences(description: IGenericSurfaceCodeLayer, **kwargs) -> IFig return fig, axes[0] -def plot_stabilizer_specific_gate_sequences(description: IGenericSurfaceCodeLayer, include_element_labels: bool = True, connectivity: ISurfaceCodeLayer = Surface17Layer(), **kwargs) -> IFigureAxesPair: +def plot_stabilizer_specific_gate_sequences(description: IGenericSurfaceCodeLayer, include_element_labels: bool = True, include_gate_sequence_element_labels: bool = True, connectivity: ISurfaceCodeLayer = Surface17Layer(), **kwargs) -> IFigureAxesPair: """ Constructs a similar gate sequence plot as 'plot_gate_sequences'. However, the gate-sequence info is taken from description parameter @@ -454,6 +455,7 @@ def plot_stabilizer_specific_gate_sequences(description: IGenericSurfaceCodeLaye :param description: Generic surface code layer definition including parity-groups and gate sequence. :param kwargs: Keyword arguments passed to figure constructor. :param include_element_labels: Boolean to enable or disable element label text. + :param include_gate_sequence_element_labels: Boolean to enable or disable (gate sequence only) element label text. :return: Figure and Axes pair. """ sequence_count: int = description.gate_sequence_count @@ -477,6 +479,7 @@ def plot_stabilizer_specific_gate_sequences(description: IGenericSurfaceCodeLaye gate_sequence=description.get_gate_sequence_at_index(i), layout_spacing=1.0, include_element_labels=include_element_labels, + include_gate_sequence_labels=include_gate_sequence_element_labels, ) kwargs[SubplotKeywordEnum.HOST_AXES.value] = (fig, ax) plot_layout_description(descriptor, **kwargs) diff --git a/src/qce_circuit/visualization/visualize_layout/element_components.py b/src/qce_circuit/visualization/visualize_layout/element_components.py index ca80cca..42add11 100644 --- a/src/qce_circuit/visualization/visualize_layout/element_components.py +++ b/src/qce_circuit/visualization/visualize_layout/element_components.py @@ -189,6 +189,7 @@ def draw(self, axes: plt.Axes) -> plt.Axes: s=self.text, color=self.color, fontsize=self.style_settings.font_size, + weight='bold', ha='center', va='center', zorder=self.style_settings.zorder, From c3031eeb2fbf563be6682c9efedc5e5a2c299ab9 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Mon, 19 Jan 2026 23:20:59 +0100 Subject: [PATCH 09/17] Added argument that turns off antialiasing for text elements. Removed initial state key sorting when calling as_array() on initial state object --- src/qce_circuit/language/intrf_declarative_circuit.py | 2 +- .../visualization/visualize_layout/element_components.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qce_circuit/language/intrf_declarative_circuit.py b/src/qce_circuit/language/intrf_declarative_circuit.py index b21e61a..98dd05a 100644 --- a/src/qce_circuit/language/intrf_declarative_circuit.py +++ b/src/qce_circuit/language/intrf_declarative_circuit.py @@ -76,7 +76,7 @@ def get_initial_state(self, qubit_index: T) -> InitialStateEnum: def as_ordered_array(self, qubit_order: Optional[List[T]] = None) -> np.ndarray: sorted_indices: List[T] = qubit_order if sorted_indices is None: - sorted_indices: List[T] = list(sorted(self.initial_states.keys())) + sorted_indices: List[T] = list(self.initial_states.keys()) # Maps initial state to binary to_bit_conversion: Dict[InitialStateEnum, int] = { InitialStateEnum.ZERO: 0, diff --git a/src/qce_circuit/visualization/visualize_layout/element_components.py b/src/qce_circuit/visualization/visualize_layout/element_components.py index 42add11..8c8d256 100644 --- a/src/qce_circuit/visualization/visualize_layout/element_components.py +++ b/src/qce_circuit/visualization/visualize_layout/element_components.py @@ -190,6 +190,7 @@ def draw(self, axes: plt.Axes) -> plt.Axes: color=self.color, fontsize=self.style_settings.font_size, weight='bold', + antialiased=False, ha='center', va='center', zorder=self.style_settings.zorder, From 4a44bfa402c4987f74d5ac7bf914f603cecc5286 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Tue, 20 Jan 2026 21:00:53 +0100 Subject: [PATCH 10/17] Added parity groups property which combines X and Z type parities. Later represents all parities, including Y type and potential mixing such as XZZX types --- .../connectivity/intrf_connectivity_surface_code.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/qce_circuit/connectivity/intrf_connectivity_surface_code.py b/src/qce_circuit/connectivity/intrf_connectivity_surface_code.py index a456005..0107ecb 100644 --- a/src/qce_circuit/connectivity/intrf_connectivity_surface_code.py +++ b/src/qce_circuit/connectivity/intrf_connectivity_surface_code.py @@ -166,6 +166,11 @@ def parity_group_z(self) -> List[IParityGroup]: """:return: (All) parity groups part of Z-stabilizers.""" raise InterfaceMethodException + @property + def parity_groups(self) -> List[IParityGroup]: + """:return: (All) X- and Z-type parity groups.""" + return self.parity_group_x + self.parity_group_z + @property @abstractmethod def data_qubit_ids(self) -> List[IQubitID]: From 0056bee128793403973d83b2ed36d48991203a3f Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Sun, 25 Jan 2026 11:50:15 +0100 Subject: [PATCH 11/17] Updated noise settings handlers --- .../addon_stim/noise_factories/factory_pauli_noise.py | 8 ++++++-- src/qce_circuit/addon_stim/noise_settings_manager.py | 6 ++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/qce_circuit/addon_stim/noise_factories/factory_pauli_noise.py b/src/qce_circuit/addon_stim/noise_factories/factory_pauli_noise.py index 3e51cb8..070aff4 100644 --- a/src/qce_circuit/addon_stim/noise_factories/factory_pauli_noise.py +++ b/src/qce_circuit/addon_stim/noise_factories/factory_pauli_noise.py @@ -84,9 +84,13 @@ def get_pauli_error(t: float, t1: float, t2: float) -> Tuple[float, float, float if t == 0: return 0, 0, 0 - px = 0.25 * (1 - np.exp(-t / t1)) + exp_t1 = np.exp(-t / t1) + exp_t2 = np.exp(-t / t2) + + px = 0.25 * (1 - exp_t1) py = px - pz = 0.5 * (1 - np.exp(-t / t2)) - 0.25 * (1 - np.exp(-t / t1)) + pz = 0.5 * (1 - exp_t2) - px + # Clamp values px = min(max(px, 0.0), 1.0) py = min(max(py, 0.0), 1.0) diff --git a/src/qce_circuit/addon_stim/noise_settings_manager.py b/src/qce_circuit/addon_stim/noise_settings_manager.py index 55fe417..90f576c 100644 --- a/src/qce_circuit/addon_stim/noise_settings_manager.py +++ b/src/qce_circuit/addon_stim/noise_settings_manager.py @@ -104,6 +104,12 @@ def get_noise_settings(self, qubit_id: IQubitID) -> QubitNoiseModelParameters: return self.individual_noise[qubit_id] return self.get_default_noise_settings() + def get_operation_duration(self, identifier: str) -> float: + duration_mapper: Dict[str, float] = self.operation_durations.duration_mapper + if identifier in duration_mapper: + return duration_mapper[identifier] + return self.operation_durations.default_duration + def to_dict(self): # Manually serialize, especially for the individual_noise dictionary serialized_data = { From 965b41ec77b6c7e05ef3e3bf428fc1c9276aca62 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Sun, 25 Jan 2026 14:16:30 +0100 Subject: [PATCH 12/17] Added two-qubit noise parameters to noise settings --- .../addon_stim/noise_settings_manager.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/qce_circuit/addon_stim/noise_settings_manager.py b/src/qce_circuit/addon_stim/noise_settings_manager.py index 90f576c..2c57baf 100644 --- a/src/qce_circuit/addon_stim/noise_settings_manager.py +++ b/src/qce_circuit/addon_stim/noise_settings_manager.py @@ -6,7 +6,7 @@ from dataclasses import dataclass, field, asdict, fields, is_dataclass from typing import Dict, List, Any, get_type_hints from qce_circuit.utilities.singleton_base import Singleton -from qce_circuit.connectivity.intrf_channel_identifier import IQubitID, QubitIDObj +from qce_circuit.connectivity.intrf_channel_identifier import IQubitID, QubitIDObj, IEdgeID from qce_circuit.utilities.readwrite_yaml import ( get_yaml_file_path, write_yaml, @@ -77,6 +77,17 @@ def __post_init__(self): # endregion +@dataclass(frozen=True) +class EdgeNoiseModelParameters: + """Data class, containing noise model parameters for two-qubit edge.""" + two_qubit_gate_error: float = field(default=0.0) + + # region Class Methods + def __post_init__(self): + typecast_dataclass_fields(self) + # endregion + + @dataclass(frozen=True) class NoiseSettings: """ @@ -86,8 +97,10 @@ class NoiseSettings: default_t2: float = field(default=20e-6) default_assignment_error: float = field(default=0.01) default_single_qubit_gate_error: float = field(default=0.0) + default_two_qubit_gate_error: float = field(default=0.0) individual_noise: Dict[IQubitID, QubitNoiseModelParameters] = field(default_factory=dict) + pair_noise: Dict[IEdgeID, EdgeNoiseModelParameters] = field(default_factory=dict) operation_durations: OperationDurationParameters = field(default_factory=OperationDurationParameters) # region Class Methods @@ -118,6 +131,7 @@ def to_dict(self): "default_assignment_error": self.default_assignment_error, "default_single_qubit_gate_error": self.default_single_qubit_gate_error, "individual_noise": {key.id: asdict(value) for key, value in self.individual_noise.items()}, + "pair_noise": {key.id: asdict(value) for key, value in self.pair_noise.items()}, "operation_durations": asdict(self.operation_durations), } return serialized_data @@ -127,14 +141,17 @@ def from_dict(cls, data: Dict): # Extract and remove the individual_noise from the input dictionary to handle it separately data_copy = data.copy() individual_noise_data = data_copy.pop("individual_noise", {}) + pair_noise_data = data_copy.pop("pair_noise", {}) operation_durations_data = data_copy.pop("operation_durations", asdict(OperationDurationParameters())) # Reconstruct the individual_noise dictionary individual_noise = {QubitIDObj(key): QubitNoiseModelParameters(**value) for key, value in individual_noise_data.items()} + pair_noise = {QubitIDObj(key): EdgeNoiseModelParameters(**value) for key, value in pair_noise_data.items()} operation_durations = OperationDurationParameters(**operation_durations_data) # Construct and return the NoiseSettings instance return cls( **data_copy, individual_noise=individual_noise, + pair_noise=pair_noise, operation_durations=operation_durations, ) # endregion From 836e577a857a25e3cc65dc3c3a40b0d1f7193ada Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Fri, 30 Jan 2026 11:03:44 +0100 Subject: [PATCH 13/17] Fixed edge related error key --- src/qce_circuit/addon_stim/noise_settings_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qce_circuit/addon_stim/noise_settings_manager.py b/src/qce_circuit/addon_stim/noise_settings_manager.py index 2c57baf..1db790d 100644 --- a/src/qce_circuit/addon_stim/noise_settings_manager.py +++ b/src/qce_circuit/addon_stim/noise_settings_manager.py @@ -6,7 +6,7 @@ from dataclasses import dataclass, field, asdict, fields, is_dataclass from typing import Dict, List, Any, get_type_hints from qce_circuit.utilities.singleton_base import Singleton -from qce_circuit.connectivity.intrf_channel_identifier import IQubitID, QubitIDObj, IEdgeID +from qce_circuit.connectivity.intrf_channel_identifier import IQubitID, QubitIDObj, IEdgeID, EdgeIDObj from qce_circuit.utilities.readwrite_yaml import ( get_yaml_file_path, write_yaml, @@ -145,7 +145,7 @@ def from_dict(cls, data: Dict): operation_durations_data = data_copy.pop("operation_durations", asdict(OperationDurationParameters())) # Reconstruct the individual_noise dictionary individual_noise = {QubitIDObj(key): QubitNoiseModelParameters(**value) for key, value in individual_noise_data.items()} - pair_noise = {QubitIDObj(key): EdgeNoiseModelParameters(**value) for key, value in pair_noise_data.items()} + pair_noise = {EdgeIDObj.from_qubit_ids(*key.split("-")): EdgeNoiseModelParameters(**value) for key, value in pair_noise_data.items()} operation_durations = OperationDurationParameters(**operation_durations_data) # Construct and return the NoiseSettings instance return cls( From ef5cb1bdf40414d09a29c15aacb3b37cf1ae400d Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Fri, 13 Feb 2026 15:36:04 +0100 Subject: [PATCH 14/17] Updated drawing factories with new visualization details --- .../addon_stim/noise_settings_manager.py | 10 +++++++ .../factory_draw_components.py | 30 ++++++++++++++----- .../draw_components/operation_components.py | 2 +- .../visualize_circuit/style_manager.py | 3 ++ .../visualize_layout/style_manager.py | 6 ++-- 5 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/qce_circuit/addon_stim/noise_settings_manager.py b/src/qce_circuit/addon_stim/noise_settings_manager.py index 1db790d..f4a70da 100644 --- a/src/qce_circuit/addon_stim/noise_settings_manager.py +++ b/src/qce_circuit/addon_stim/noise_settings_manager.py @@ -112,11 +112,21 @@ def get_default_noise_settings(self) -> QubitNoiseModelParameters: single_qubit_gate_error=self.default_single_qubit_gate_error, ) + def get_default_pair_noise_settings(self) -> EdgeNoiseModelParameters: + return EdgeNoiseModelParameters( + two_qubit_gate_error=0.0, + ) + def get_noise_settings(self, qubit_id: IQubitID) -> QubitNoiseModelParameters: if qubit_id in self.individual_noise: return self.individual_noise[qubit_id] return self.get_default_noise_settings() + def get_pair_noise_settings(self, edge_id: IEdgeID) -> EdgeNoiseModelParameters: + if edge_id in self.pair_noise: + return self.pair_noise[edge_id] + return self.get_default_pair_noise_settings() + def get_operation_duration(self, identifier: str) -> float: duration_mapper: Dict[str, float] = self.operation_durations.duration_mapper if identifier in duration_mapper: 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 b915e4a..340916f 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 @@ -650,12 +650,15 @@ def construct(self, operation: ICircuitCompositeOperation, transform_constructor for channel_identifier in operation.channel_identifiers ] transform: IRectTransform = transform_constructor.combine_transforms(transforms=transforms) + text_string: str = "" + if operation.nr_of_repetitions is not "": + text_string = f"x{operation.nr_of_repetitions}" return RoundedRectangleHighlight( pivot=transform.pivot, width=transform.width, height=transform.height, alignment=transform.parent_alignment, - text_string=f'x{operation.nr_of_repetitions}' + text_string=text_string, ) # endregion @@ -738,12 +741,25 @@ def __init__(self, callback_draw_manager: IOperationDrawComponentFactoryManager) # region Interface Methods def construct(self, operation: IColorOverwrite, transform_constructor: ITransformConstructor) -> IDrawComponent: """:return: Draw component based on operation type.""" - with StyleManager.temporary_override(**dict( - color_text=operation.color_overwrite, - color_icon=operation.color_overwrite, - color_outline=operation.color_overwrite, - color_outline_dim=operation.color_overwrite, - )): + overwrite_line: bool = any([ + isinstance(operation.wrapped_operation, CPhase), + ]) + overwrite_virtual_park: bool = isinstance(operation.wrapped_operation, VirtualPark) + overwrite_dict = dict( + color_background=operation.color_overwrite, + ) + if overwrite_line: + overwrite_dict = dict( + color_text=operation.color_overwrite, + color_icon=operation.color_overwrite, + color_outline=operation.color_overwrite, + color_outline_dim=operation.color_overwrite, + ) + if overwrite_virtual_park: + overwrite_dict = dict( + color_channel_bar_cover=operation.color_overwrite, + ) + with StyleManager.temporary_override(**overwrite_dict): draw_component: IDrawComponent = self._factory_manager.construct( operation=operation.wrapped_operation, transform_constructor=transform_constructor, 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 6cad294..603289a 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 @@ -401,7 +401,7 @@ def draw(self, axes: plt.Axes) -> plt.Axes: cover_arc_ycoords, linestyle='-', linewidth=self.style_settings.line_width * 2, - color='white', # + color=self.style_settings.cover_color, zorder=-16, ) diff --git a/src/qce_circuit/visualization/visualize_circuit/style_manager.py b/src/qce_circuit/visualization/visualize_circuit/style_manager.py index 1f7fadd..68f0b1b 100644 --- a/src/qce_circuit/visualization/visualize_circuit/style_manager.py +++ b/src/qce_circuit/visualization/visualize_circuit/style_manager.py @@ -20,6 +20,7 @@ class ChannelStyleSettings: """ line_color: str text_color: str + cover_color: str line_width: float font_size: float divider_width: float @@ -99,6 +100,7 @@ class StyleSettings: color_outline: str = field(default='black') color_outline_dim: str = field(default='darkgrey') color_channel_bar: str = field(default='black') + color_channel_bar_cover: str = field(default='white') color_highlight_background: str = field(default='lightblue') color_highlight_outline: str = field(default='blue') @@ -139,6 +141,7 @@ def channel_style(self) -> ChannelStyleSettings: return ChannelStyleSettings( line_color=self.color_channel_bar, text_color=self.color_text, + cover_color=self.color_channel_bar_cover, line_width=self.width_line_bar, name_description_width=self.width_name_description, font_size=self.font_size, diff --git a/src/qce_circuit/visualization/visualize_layout/style_manager.py b/src/qce_circuit/visualization/visualize_layout/style_manager.py index 49cd37d..42ad66c 100644 --- a/src/qce_circuit/visualization/visualize_layout/style_manager.py +++ b/src/qce_circuit/visualization/visualize_layout/style_manager.py @@ -90,7 +90,7 @@ class StyleSettings: color_text: str = field(default='black') color_outline: str = field(default='black') color_element: str = field(default='#808080') - color_element_outline: str = field(default='#1c50a3') + color_element_outline: str = field(default='#d62e28') color_park_operation: str = field(default='black') color_gate_operation: str = field(default='black') @@ -98,12 +98,12 @@ class StyleSettings: width_line_small: float = field(default=2.0) width_line_medium: float = field(default=4.0) width_line_large: float = field(default=8.0) - width_line_thick: float = field(default=12.0) + width_line_thick: float = field(default=8.0) # Radius radius_dot: float = field(default=0.2) radius_hexagon: float = field(default=0.3) - radius_dot_indicator: float = field(default=0.3) + radius_dot_indicator: float = field(default=0.28) # Font sizes font_size: float = field(default=16.0) From 423972b90512b95eba07d4b286830f7871e80f98 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Fri, 13 Feb 2026 15:39:35 +0100 Subject: [PATCH 15/17] Fixed syntax warning with string literal --- .../draw_components/factory_draw_components.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 340916f..65aef74 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 @@ -651,7 +651,7 @@ def construct(self, operation: ICircuitCompositeOperation, transform_constructor ] transform: IRectTransform = transform_constructor.combine_transforms(transforms=transforms) text_string: str = "" - if operation.nr_of_repetitions is not "": + if operation.nr_of_repetitions != "": text_string = f"x{operation.nr_of_repetitions}" return RoundedRectangleHighlight( pivot=transform.pivot, From 4b13d309398ea9202e5f46e1f33f9dec80e82631 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Fri, 13 Feb 2026 15:47:04 +0100 Subject: [PATCH 16/17] Fixed bug triggered by unit test --- .../structure/acquisition_indexing/kernel_repetition_code.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qce_circuit/structure/acquisition_indexing/kernel_repetition_code.py b/src/qce_circuit/structure/acquisition_indexing/kernel_repetition_code.py index b84820e..a20613a 100644 --- a/src/qce_circuit/structure/acquisition_indexing/kernel_repetition_code.py +++ b/src/qce_circuit/structure/acquisition_indexing/kernel_repetition_code.py @@ -173,7 +173,7 @@ def indexing_kernels(self) -> List[IIndexingKernel]: result: List[IIndexingKernel] = repetition_kernels if not self.include_qutrit_calibration_points: return result - result += calibration_kernel + result = repetition_kernels + calibration_kernel return result # endregion From 5adad2486e08723ac13c9b4b26e5360c99b9d569 Mon Sep 17 00:00:00 2001 From: SeanvdMeer <18538762+minisean@users.noreply.github.com> Date: Fri, 13 Feb 2026 15:59:14 +0100 Subject: [PATCH 17/17] Updated removed antialiased argument as it does not do much and it restricts to >3.8 python usage --- .../visualization/visualize_layout/element_components.py | 1 - .../visualization/visualize_layout/plaquette_components.py | 1 - 2 files changed, 2 deletions(-) diff --git a/src/qce_circuit/visualization/visualize_layout/element_components.py b/src/qce_circuit/visualization/visualize_layout/element_components.py index 8c8d256..42add11 100644 --- a/src/qce_circuit/visualization/visualize_layout/element_components.py +++ b/src/qce_circuit/visualization/visualize_layout/element_components.py @@ -190,7 +190,6 @@ def draw(self, axes: plt.Axes) -> plt.Axes: color=self.color, fontsize=self.style_settings.font_size, weight='bold', - antialiased=False, ha='center', va='center', zorder=self.style_settings.zorder, diff --git a/src/qce_circuit/visualization/visualize_layout/plaquette_components.py b/src/qce_circuit/visualization/visualize_layout/plaquette_components.py index 639a63a..ef4173e 100644 --- a/src/qce_circuit/visualization/visualize_layout/plaquette_components.py +++ b/src/qce_circuit/visualization/visualize_layout/plaquette_components.py @@ -66,7 +66,6 @@ def draw(self, axes: plt.Axes) -> plt.Axes: linewidth=0.1, # Fixed (small) width, to prevent aliasing facecolor=self.style_settings.background_color, # Depends on background type zorder=self.style_settings.zorder, - antialiased=False, ) axes.add_patch(rectangle) return axes