diff --git a/src/qce_circuit/structure/circuit_operations.py b/src/qce_circuit/structure/circuit_operations.py index 5195a3b..4240fa8 100644 --- a/src/qce_circuit/structure/circuit_operations.py +++ b/src/qce_circuit/structure/circuit_operations.py @@ -1198,6 +1198,123 @@ def apply_flatten_to_self(self) -> ICircuitOperation: # endregion +@dataclass(frozen=False, unsafe_hash=True) +class VirtualWait(SingleQubitOperation, ICircuitOperation): + """ + Virtual wait position (behaves as Wait). + Allow to wait on specific (qubit) channel. + Intended to display wait time. + """ + duration_strategy: IDurationStrategy = field(init=False, 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="") + + # region Interface Properties + @property + def channel_identifiers(self) -> List[ChannelIdentifier]: + """:return: Array-like of channel identifiers to which this operation applies to.""" + return [ + ChannelIdentifier(_id=self.qubit_index, _channel=self.qubit_channel), + ] + # endregion + + # region Interface Methods + def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircuitOperation]] = None) -> 'VirtualWait': + """ + Creates a copy from self. Excluding any relation details. + :param relation_transfer_lookup: Lookup table used to transfer relation link. + :return: Copy of self with updated relation link. + """ + return VirtualWait( + qubit_index=self.qubit_index, + 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 + + +@dataclass(frozen=False, unsafe_hash=True) +class VirtualColorOverwrite(ICircuitOperation): + """ + Data class, containing single-qubit operation. + Acts as a visualization wrapper for coloring visualization. + """ + operation: SingleQubitOperation + color_overwrite: str = field(init=True, default="k") + + # region Interface Properties + @property + def channel_identifiers(self) -> List[ChannelIdentifier]: + """:return: Array-like of channel identifiers to which this operation applies to.""" + return self.operation.channel_identifiers + + @property + def nr_of_repetitions(self) -> int: + """:return: Number of repetitions for this object.""" + return self.operation.nr_of_repetitions + + @property + def relation_link(self) -> IRelationLink[ICircuitOperation]: + """:return: Description of relation to other circuit node.""" + return self.operation.relation + + @relation_link.setter + def relation_link(self, link: IRelationLink[ICircuitOperation]): + """:sets: Description of relation to other circuit node.""" + self.operation.relation = link + + @property + def start_time(self) -> float: + """:return: Start time [a.u.].""" + return self.operation.start_time + + @property + def duration(self) -> float: + """:return: Duration [ns].""" + return self.operation.duration + # endregion + + # region Interface Methods + def copy(self, relation_transfer_lookup: Optional[Dict[ICircuitOperation, ICircuitOperation]] = None) -> 'VirtualColorOverwrite': + """ + Creates a copy from self. Excluding any relation details. + :param relation_transfer_lookup: Lookup table used to transfer relation link. + :return: Copy of self with updated relation link. + """ + return VirtualColorOverwrite( + operation=self.operation.copy( + relation_transfer_lookup=relation_transfer_lookup, + ) + ) + + def apply_modifiers_to_self(self) -> ICircuitOperation: + """ + WARNING: Applies modifiers inplace. + Applies modifiers such as repetition and state-control. + :return: Modified self. + """ + return self + + def decomposed_operations(self) -> List[ICircuitOperation]: + """ + Functions similar to a 'flatten' operation. + Mostly intended for composite-operations such that they can apply repetition and state-dependent registries. + :return: Array-like of decomposed operations. + """ + return [self] + + def apply_flatten_to_self(self) -> ICircuitOperation: + """ + WARNING: Applies a flatten modification inplace. + :return: Modified self. + """ + return self + # endregion + + if __name__ == '__main__': from qce_circuit.structure.registry_duration import ( DurationRegistry, diff --git a/src/qce_circuit/visualization/visualize_circuit/display_circuit.py b/src/qce_circuit/visualization/visualize_circuit/display_circuit.py index 8d58cf8..387aae0 100644 --- a/src/qce_circuit/visualization/visualize_circuit/display_circuit.py +++ b/src/qce_circuit/visualization/visualize_circuit/display_circuit.py @@ -31,8 +31,10 @@ VirtualVacant, VirtualTwoQubitVacant, VirtualEmpty, + VirtualWait, VirtualOptional, VirtualInjectedError, + VirtualColorOverwrite, ) from qce_circuit.visualization.visualize_circuit.draw_components.annotation_components import ( HorizontalVariableIndicator, @@ -110,8 +112,10 @@ VirtualVacantFactory, VirtualTwoQubitVacantFactory, VirtualEmptyFactory, + VirtualWaitFactory, VirtualOptionalFactory, VirtualInjectedErrorFactory, + VirtualColorOverwriteFactory, ) from qce_circuit.visualization.visualize_circuit.draw_components.factory_multi_draw_components import \ MultiTwoQubitBlockFactory @@ -220,12 +224,14 @@ def get_operation_draw_components(self) -> List[IDrawComponent]: VirtualVacant: VirtualVacantFactory(), VirtualTwoQubitVacant: VirtualTwoQubitVacantFactory(), VirtualEmpty: VirtualEmptyFactory(), + VirtualWait: VirtualWaitFactory(), } ) callback_draw_manager = deepcopy(individual_component_factory) individual_component_factory.factory_lookup.update({ VirtualOptional: VirtualOptionalFactory(callback_draw_manager=callback_draw_manager), VirtualInjectedError: VirtualInjectedErrorFactory(callback_draw_manager=callback_draw_manager), + VirtualColorOverwrite: VirtualColorOverwriteFactory(callback_draw_manager=callback_draw_manager), }) factory_manager: BulkDrawComponentFactoryManager = BulkDrawComponentFactoryManager( 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 e1bad30..3f1e0e0 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 @@ -33,6 +33,8 @@ VirtualEmpty, VirtualOptional, VirtualInjectedError, + VirtualWait, + VirtualColorOverwrite, ) from qce_circuit.visualization.visualize_circuit.intrf_draw_component import IDrawComponent from qce_circuit.visualization.visualize_circuit.intrf_factory_draw_components import ( @@ -50,6 +52,7 @@ RectangleTextBlock, RectangleVacantBlock, BlockRotation, + BlockHeaderBody, BlockMeasure, RotationAxis, RotationAngle, @@ -439,6 +442,25 @@ def construct(self, operation: VirtualEmpty, transform_constructor: ITransformCo # endregion +class VirtualWaitFactory(IOperationDrawComponentFactory[VirtualWait, IDrawComponent]): + + # region Interface Methods + def construct(self, operation: VirtualWait, transform_constructor: ITransformConstructor) -> IDrawComponent: + """:return: Draw component based on operation type.""" + transform: IRectTransform = transform_constructor.construct_transform( + identifier=operation.channel_identifiers[0], + time_component=operation, + ) + return BlockHeaderBody( + pivot=transform.pivot, + height=transform.height, + alignment=transform.parent_alignment, + header_text=operation.header_text, + body_text=operation.body_text, + ) + # endregion + + class MeasureFactory(IOperationDrawComponentFactory[DispersiveMeasure, IDrawComponent]): # region Interface Methods @@ -581,4 +603,30 @@ def construct(self, operation: VirtualInjectedError, transform_constructor: ITra transform_constructor=transform_constructor, ) return draw_component - # endregion \ No newline at end of file + # endregion + + +class VirtualColorOverwriteFactory(IOperationDrawComponentFactory[VirtualColorOverwrite, IDrawComponent]): + """ + Behaviour class, implementing construction of draw component with additional requirements. + """ + + # region Class Constructor + def __init__(self, callback_draw_manager: IOperationDrawComponentFactoryManager): + self._factory_manager: IOperationDrawComponentFactoryManager = callback_draw_manager + # endregion + + # region Interface Methods + def construct(self, operation: VirtualColorOverwrite, 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, + )): + draw_component: IDrawComponent = self._factory_manager.construct( + operation=operation.operation, + transform_constructor=transform_constructor, + ) + return draw_component + # endregion diff --git a/src/qce_circuit/visualization/visualize_circuit/draw_components/operation_components.py b/src/qce_circuit/visualization/visualize_circuit/draw_components/operation_components.py index 25cae02..9a997c0 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 @@ -70,10 +70,12 @@ 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) return RectTransform( - _pivot_strategy=FixedPivot(self.pivot), - _width_strategy=FixedLength(self.width), - _height_strategy=FixedLength(self.height), + _pivot_strategy=FixedPivot(self.pivot + margin_pivot_shift), + _width_strategy=FixedLength(self.width - margin), + _height_strategy=FixedLength(self.height - margin), _parent_alignment=self.alignment, ) # endregion @@ -118,6 +120,7 @@ def draw(self, axes: plt.Axes) -> plt.Axes: y=self.text_center.y, s=self.text_string, fontsize=self.style_settings.font_size, + color=self.style_settings.text_color, ha='center', va='center', ) @@ -280,6 +283,40 @@ def draw(self, axes: plt.Axes) -> plt.Axes: y=self.text_center.y, s=rf'$\mathtt{{R_{{{self.rotation_axes.value}}}^{{{self.rotation_angle.value}}}}}$', fontsize=self.style_settings.font_size, + color=self.style_settings.text_color, + ha='center', + va='center', + ) + return axes + # endregion + + +@dataclass(frozen=True) +class BlockHeaderBody(SquareTextBlock, IRectTransformComponent, IDrawComponent): + """ + Data class, containing dimension data for drawing schedule block. + """ + header_text: str = field(default="") + body_text: str = field(default="") + + # region Class Methods + def draw(self, axes: plt.Axes) -> plt.Axes: + axes = self._base_block.draw(axes=axes) + axes.text( + x=self.text_center.x, + y=self.text_center.y, + s=self.header_text, + fontsize=self.style_settings.font_size, + color=self.style_settings.text_color, + ha='center', + va='center', + ) + axes.text( + x=self.text_center.x, + y=(self.text_center.y + self.rectilinear_transform.bot_pivot.y) / 2, + s=self.body_text, + fontsize=self.style_settings.subtext_font_size, + color=self.style_settings.text_color, ha='center', va='center', ) diff --git a/src/qce_circuit/visualization/visualize_circuit/style_manager.py b/src/qce_circuit/visualization/visualize_circuit/style_manager.py index 21e5d57..20cb22a 100644 --- a/src/qce_circuit/visualization/visualize_circuit/style_manager.py +++ b/src/qce_circuit/visualization/visualize_circuit/style_manager.py @@ -37,6 +37,9 @@ class OperationStyleSettings: border_line_style: str 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'.""" @dataclass(frozen=True) @@ -102,6 +105,9 @@ class StyleSettings: # Line styles line_style_border: str = field(default='-') + # Spacing + rectilinear_margin: float = field(default=0.0) + # region Class Properties @property def channel_style(self) -> ChannelStyleSettings: @@ -123,6 +129,8 @@ def operation_style(self) -> OperationStyleSettings: 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=self.rectilinear_margin, ) @property @@ -136,6 +144,8 @@ def vacant_operation_style(self) -> OperationStyleSettings: 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=self.rectilinear_margin, ) @property @@ -149,6 +159,8 @@ def empty_operation_style(self) -> OperationStyleSettings: 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=self.rectilinear_margin, ) @property