Source code for qhronology.utilities.diagrams

# Project: Qhronology (https://github.com/lgbishop/qhronology)
# Author: lgbishop <lgbishop@protonmail.com>
# Copyright: Lachlan G. Bishop 2025
# License: AGPLv3 (non-commercial use), proprietary (commercial use)
# For more details, see the README in the project repository:
# https://github.com/lgbishop/qhronology,
# or visit the website:
# https://qhronology.org.

"""
Classes for the creation of diagrams of quantum states, gates, and circuits.
Not intended to be used directly by the user.
"""

# https://peps.python.org/pep-0649/
# https://peps.python.org/pep-0749/
from __future__ import annotations

import copy
from enum import StrEnum
import math
import statistics
import textwrap

import numpy as np

from qhronology.utilities.helpers import flatten_list


[docs] class Families(StrEnum): WIRE = "WIRE" PUSH = "PUSH" GATE = "GATE" LSTICK = "LSTICK" RSTICK = "RSTICK" TARG = "TARG" METER = "METER" TRACE = "TRACE" TERM = "TERM" WORMHOLE = "WORMHOLE" COMPOSITION = "COMPOSITION"
[docs] class Sections(StrEnum): INPUTS = "inputs" GATES = "gates" OUTPUTS = "outputs"
[docs] class Styles(StrEnum): ASCII = "ascii" UNICODE = "unicode" SHADED = "shaded"
[docs] class Connections(StrEnum): NONE = "none" CLASSICAL = "classical" QUANTUM = "quantum"
[docs] class Directions(StrEnum): UPPER = "upper" LOWER = "lower" LEFT = "left" RIGHT = "right"
# The central template for constructing every diagram cell. # Each of these entries is a unique keyword string which is to be assigned a corresponding visualization character (see STYLES below). CELL_TEMPLATE = np.array( [ [ "exterior_corner_left_upper", "block_connector_left_upper", "exterior_horizontal_left_upper", "wire_upper_unset", "exterior_horizontal_right_upper", "block_connector_right_upper", "exterior_corner_right_upper", ], [ "exterior_vertical_left_upper", "edge_corner_left_upper", "edge_horizontal_left_upper", "edge_connector_upper_unset", "edge_horizontal_right_upper", "edge_corner_right_upper", "exterior_vertical_right_lower", ], [ "exterior_vertical_left_upper", "edge_vertical_left_upper", "pad_corner", "pad_upper", "pad_corner", "edge_vertical_right_upper", "exterior_vertical_right_lower", ], [ "wire_left_unset", "edge_connector_left_unset", "pad_left", "label", "pad_right", "edge_connector_right_unset", "wire_right_unset", ], [ "exterior_vertical_left_lower", "edge_vertical_left_lower", "pad_corner", "pad_lower", "pad_corner", "edge_vertical_right_lower", "exterior_vertical_right_lower", ], [ "exterior_vertical_left_lower", "edge_corner_left_lower", "edge_horizontal_left_lower", "edge_connector_lower_unset", "edge_horizontal_right_lower", "edge_corner_right_lower", "exterior_vertical_right_lower", ], [ "exterior_corner_left_lower", "block_connector_left_lower", "exterior_horizontal_left_lower", "wire_lower_unset", "exterior_horizontal_right_lower", "block_connector_right_lower", "exterior_corner_right_lower", ], ], dtype="object", ) # The dictionary for assigning the actual text characters to each component of the template. STYLES = { "GATE_SINGLE": { "block_connector_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "block_connector_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "block_connector_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "block_connector_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_left_quantum": { "ascii": "|", "unicode": "┨", "unicode_alt": "░", }, "edge_connector_lower_quantum": { "ascii": "-", "unicode": "┯", "unicode_alt": "░", }, "edge_connector_right_quantum": { "ascii": "|", "unicode": "┠", "unicode_alt": "░", }, "edge_connector_upper_quantum": { "ascii": "-", "unicode": "┷", "unicode_alt": "░", }, "edge_connector_left_classical": { "ascii": "|", "unicode": "╡", "unicode_alt": "░", }, "edge_connector_lower_classical": { "ascii": "-", "unicode": "╥", "unicode_alt": "░", }, "edge_connector_right_classical": { "ascii": "|", "unicode": "╞", "unicode_alt": "░", }, "edge_connector_upper_classical": { "ascii": "-", "unicode": "╨", "unicode_alt": "░", }, "edge_connector_left_none": {"ascii": "|", "unicode": "┃", "unicode_alt": "░"}, "edge_connector_lower_none": {"ascii": "-", "unicode": "━", "unicode_alt": "░"}, "edge_connector_right_none": {"ascii": "|", "unicode": "┃", "unicode_alt": "░"}, "edge_connector_upper_none": {"ascii": "-", "unicode": "━", "unicode_alt": "░"}, "edge_corner_left_lower": {"ascii": "+", "unicode": "┗", "unicode_alt": "░"}, "edge_corner_left_upper": {"ascii": "+", "unicode": "┏", "unicode_alt": "░"}, "edge_corner_right_lower": {"ascii": "+", "unicode": "┛", "unicode_alt": "░"}, "edge_corner_right_upper": {"ascii": "+", "unicode": "┓", "unicode_alt": "░"}, "edge_horizontal_left_lower": { "ascii": "-", "unicode": "━", "unicode_alt": "░", }, "edge_horizontal_left_upper": { "ascii": "-", "unicode": "━", "unicode_alt": "░", }, "edge_horizontal_right_lower": { "ascii": "-", "unicode": "━", "unicode_alt": "░", }, "edge_horizontal_right_upper": { "ascii": "-", "unicode": "━", "unicode_alt": "░", }, "edge_vertical_left_lower": {"ascii": "|", "unicode": "┃", "unicode_alt": "░"}, "edge_vertical_left_upper": {"ascii": "|", "unicode": "┃", "unicode_alt": "░"}, "edge_vertical_right_lower": {"ascii": "|", "unicode": "┃", "unicode_alt": "░"}, "edge_vertical_right_upper": {"ascii": "|", "unicode": "┃", "unicode_alt": "░"}, "empty": {"ascii": " ", "unicode": " ", "unicode_alt": " "}, "exterior_horizontal_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_horizontal_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_horizontal_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_horizontal_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_vertical_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_vertical_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_vertical_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_vertical_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_corner_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_corner_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_corner_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_corner_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "label": {"ascii": "label", "unicode": "label", "unicode_alt": "label"}, "replacement": {"ascii": "empty", "unicode": "empty", "unicode_alt": "░"}, "pad_corner": {"ascii": "empty", "unicode": "empty", "unicode_alt": "░"}, "pad_left": {"ascii": "empty", "unicode": "empty", "unicode_alt": "░"}, "pad_lower": {"ascii": "empty", "unicode": "empty", "unicode_alt": "░"}, "pad_right": {"ascii": "empty", "unicode": "empty", "unicode_alt": "░"}, "pad_upper": {"ascii": "empty", "unicode": "empty", "unicode_alt": "░"}, "wire_left_quantum": {"ascii": "-", "unicode": "─", "unicode_alt": "─"}, "wire_lower_quantum": {"ascii": "|", "unicode": "│", "unicode_alt": "│"}, "wire_right_quantum": {"ascii": "-", "unicode": "─", "unicode_alt": "─"}, "wire_upper_quantum": {"ascii": "|", "unicode": "│", "unicode_alt": "│"}, "wire_left_classical": {"ascii": "=", "unicode": "═", "unicode_alt": "═"}, "wire_lower_classical": {"ascii": "#", "unicode": "║", "unicode_alt": "║"}, "wire_right_classical": {"ascii": "=", "unicode": "═", "unicode_alt": "═"}, "wire_upper_classical": {"ascii": "#", "unicode": "║", "unicode_alt": "║"}, "wire_left_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_lower_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_right_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_upper_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_blend_left": {"ascii": "+", "unicode": "┣", "unicode_alt": "░"}, "edge_blend_right": {"ascii": "+", "unicode": "┫", "unicode_alt": "░"}, }, "PUSH": { "block_connector_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "block_connector_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "block_connector_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "block_connector_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_left_quantum": { "ascii": "-", "unicode": "─", "unicode_alt": "─", }, "edge_connector_lower_quantum": { "ascii": "|", "unicode": "│", "unicode_alt": "│", }, "edge_connector_right_quantum": { "ascii": "-", "unicode": "─", "unicode_alt": "─", }, "edge_connector_upper_quantum": { "ascii": "|", "unicode": "│", "unicode_alt": "│", }, "edge_connector_left_classical": { "ascii": "=", "unicode": "═", "unicode_alt": "═", }, "edge_connector_lower_classical": { "ascii": "#", "unicode": "║", "unicode_alt": "║", }, "edge_connector_right_classical": { "ascii": "=", "unicode": "═", "unicode_alt": "═", }, "edge_connector_upper_classical": { "ascii": "#", "unicode": "║", "unicode_alt": "║", }, "edge_connector_left_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_lower_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_right_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_upper_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_corner_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_corner_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_corner_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_corner_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_horizontal_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_horizontal_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_horizontal_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_horizontal_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_vertical_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_vertical_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_vertical_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_vertical_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "empty": {"ascii": " ", "unicode": " ", "unicode_alt": " "}, "exterior_horizontal_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_horizontal_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_horizontal_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_horizontal_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_vertical_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_vertical_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_vertical_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_vertical_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_corner_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_corner_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_corner_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_corner_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "label": {"ascii": "label", "unicode": "label", "unicode_alt": "label"}, "replacement": {"ascii": "empty", "unicode": "empty", "unicode_alt": "empty"}, "pad_corner": {"ascii": "empty", "unicode": "empty", "unicode_alt": "empty"}, "pad_left": { "ascii": "edge_connector_left_unset", "unicode": "edge_connector_left_unset", "unicode_alt": "edge_connector_left_unset", }, "pad_lower": { "ascii": "edge_connector_lower_unset", "unicode": "edge_connector_lower_unset", "unicode_alt": "edge_connector_lower_unset", }, "pad_right": { "ascii": "edge_connector_right_unset", "unicode": "edge_connector_right_unset", "unicode_alt": "edge_connector_right_unset", }, "pad_upper": { "ascii": "edge_connector_upper_unset", "unicode": "edge_connector_upper_unset", "unicode_alt": "edge_connector_upper_unset", }, "wire_left_quantum": {"ascii": "-", "unicode": "─", "unicode_alt": "─"}, "wire_lower_quantum": {"ascii": "|", "unicode": "│", "unicode_alt": "│"}, "wire_right_quantum": {"ascii": "-", "unicode": "─", "unicode_alt": "─"}, "wire_upper_quantum": {"ascii": "|", "unicode": "│", "unicode_alt": "│"}, "wire_left_classical": {"ascii": "=", "unicode": "═", "unicode_alt": "═"}, "wire_lower_classical": {"ascii": "#", "unicode": "║", "unicode_alt": "║"}, "wire_right_classical": {"ascii": "=", "unicode": "═", "unicode_alt": "═"}, "wire_upper_classical": {"ascii": "#", "unicode": "║", "unicode_alt": "║"}, "wire_left_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_lower_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_right_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_upper_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, }, "LSTICK_SINGLE": { "block_connector_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "block_connector_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "block_connector_right_lower": { "ascii": "emptyempty", "unicode": "emptyempty", "unicode_alt": "emptyempty", }, "block_connector_right_upper": { "ascii": "emptyempty", "unicode": "emptyempty", "unicode_alt": "emptyempty", }, "edge_connector_left_quantum": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_lower_quantum": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_right_quantum": { "ascii": "--", "unicode": "──", "unicode_alt": "──", }, "edge_connector_upper_quantum": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_left_classical": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_lower_classical": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_right_classical": { "ascii": "==", "unicode": "══", "unicode_alt": "══", }, "edge_connector_upper_classical": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_left_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_lower_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_right_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_upper_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_corner_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_corner_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_corner_right_lower": { "ascii": "emptyempty", "unicode": "emptyempty", "unicode_alt": "emptyempty", }, "edge_corner_right_upper": { "ascii": "emptyempty", "unicode": "emptyempty", "unicode_alt": "emptyempty", }, "edge_horizontal_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_horizontal_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_horizontal_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_horizontal_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_vertical_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_vertical_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_vertical_right_lower": { "ascii": "emptyempty", "unicode": "emptyempty", "unicode_alt": "emptyempty", }, "edge_vertical_right_upper": { "ascii": "emptyempty", "unicode": "emptyempty", "unicode_alt": "emptyempty", }, "empty": {"ascii": " ", "unicode": " ", "unicode_alt": " "}, "exterior_horizontal_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_horizontal_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_horizontal_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_horizontal_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_vertical_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_vertical_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_vertical_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_vertical_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_corner_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_corner_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_corner_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_corner_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "label": {"ascii": "label", "unicode": "label", "unicode_alt": "label"}, "replacement": {"ascii": "empty", "unicode": "empty", "unicode_alt": "empty"}, "pad_corner": {"ascii": "empty", "unicode": "empty", "unicode_alt": "empty"}, "pad_left": {"ascii": "empty", "unicode": "empty", "unicode_alt": "empty"}, "pad_lower": {"ascii": "empty", "unicode": "empty", "unicode_alt": "empty"}, "pad_right": { "ascii": "wire_right_unset", "unicode": "wire_right_unset", "unicode_alt": "wire_right_unset", }, "pad_upper": {"ascii": "empty", "unicode": "empty", "unicode_alt": "empty"}, "wire_left_quantum": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_lower_quantum": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_right_quantum": {"ascii": "-", "unicode": "─", "unicode_alt": "─"}, "wire_upper_quantum": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_left_classical": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_lower_classical": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_right_classical": {"ascii": "=", "unicode": "═", "unicode_alt": "═"}, "wire_upper_classical": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_left_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_lower_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_right_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_upper_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "label_connector": {"ascii": "empty", "unicode": "╶", "unicode_alt": "╶"}, "bracket_connector_right_quantum": { "ascii": "empty", "unicode": "╶", "unicode_alt": "╶", }, "bracket_connector_right_classical": { "ascii": "wire_right_unset", "unicode": "wire_right_unset", "unicode_alt": "wire_right_unset", }, "bracket_connector_right_none": { "ascii": "wire_right_unset", "unicode": "wire_right_unset", "unicode_alt": "wire_right_unset", }, }, "RSTICK_SINGLE": { "block_connector_left_lower": { "ascii": "emptyempty", "unicode": "emptyempty", "unicode_alt": "emptyempty", }, "block_connector_left_upper": { "ascii": "emptyempty", "unicode": "emptyempty", "unicode_alt": "emptyempty", }, "block_connector_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "block_connector_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_left_quantum": { "ascii": "--", "unicode": "──", "unicode_alt": "──", }, "edge_connector_lower_quantum": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_right_quantum": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_upper_quantum": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_left_classical": { "ascii": "==", "unicode": "══", "unicode_alt": "══", }, "edge_connector_lower_classical": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_right_classical": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_upper_classical": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_left_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_lower_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_right_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_connector_upper_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_corner_left_lower": { "ascii": "emptyempty", "unicode": "emptyempty", "unicode_alt": "emptyempty", }, "edge_corner_left_upper": { "ascii": "emptyempty", "unicode": "emptyempty", "unicode_alt": "emptyempty", }, "edge_corner_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_corner_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_horizontal_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_horizontal_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_horizontal_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_horizontal_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_vertical_left_lower": { "ascii": "emptyempty", "unicode": "emptyempty", "unicode_alt": "emptyempty", }, "edge_vertical_left_upper": { "ascii": "emptyempty", "unicode": "emptyempty", "unicode_alt": "emptyempty", }, "edge_vertical_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "edge_vertical_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "empty": {"ascii": " ", "unicode": " ", "unicode_alt": " "}, "exterior_horizontal_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_horizontal_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_horizontal_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_horizontal_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_vertical_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_vertical_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_vertical_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_vertical_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_corner_left_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_corner_left_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_corner_right_lower": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "exterior_corner_right_upper": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "label": {"ascii": "label", "unicode": "label", "unicode_alt": "label"}, "replacement": {"ascii": "empty", "unicode": "empty", "unicode_alt": "empty"}, "pad_corner": {"ascii": "empty", "unicode": "empty", "unicode_alt": "empty"}, "pad_left": { "ascii": "wire_left_unset", "unicode": "wire_left_unset", "unicode_alt": "wire_left_unset", }, "pad_lower": {"ascii": "empty", "unicode": "empty", "unicode_alt": "empty"}, "pad_right": {"ascii": "empty", "unicode": "empty", "unicode_alt": "empty"}, "pad_upper": {"ascii": "empty", "unicode": "empty", "unicode_alt": "empty"}, "wire_left_quantum": {"ascii": "-", "unicode": "─", "unicode_alt": "─"}, "wire_lower_quantum": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_right_quantum": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_upper_quantum": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_left_classical": {"ascii": "=", "unicode": "═", "unicode_alt": "═"}, "wire_lower_classical": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_right_classical": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_upper_classical": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_left_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_lower_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_right_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "wire_upper_none": { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", }, "label_connector": {"ascii": "empty", "unicode": "╴", "unicode_alt": "╴"}, "bracket_connector_left_quantum": { "ascii": "empty", "unicode": "╴", "unicode_alt": "╴", }, "bracket_connector_left_classical": { "ascii": "wire_left_unset", "unicode": "wire_left_unset", "unicode_alt": "wire_left_unset", }, "bracket_connector_left_none": { "ascii": "wire_left_unset", "unicode": "wire_left_unset", "unicode_alt": "wire_left_unset", }, }, } # Derivative styles style_GATE_UPPER = dict(STYLES["GATE_SINGLE"]) style_GATE_UPPER["block_connector_left_lower"] = { "ascii": "|", "unicode": "┃", "unicode_alt": "░", } style_GATE_UPPER["block_connector_right_lower"] = { "ascii": "|", "unicode": "┃", "unicode_alt": "░", } style_GATE_UPPER["edge_connector_lower_quantum"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_UPPER["edge_connector_lower_classical"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_UPPER["edge_connector_lower_none"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_UPPER["edge_corner_left_lower"] = { "ascii": "|", "unicode": "┃", "unicode_alt": "░", } style_GATE_UPPER["edge_corner_right_lower"] = { "ascii": "|", "unicode": "┃", "unicode_alt": "░", } style_GATE_UPPER["edge_horizontal_left_lower"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_UPPER["edge_horizontal_right_lower"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_UPPER["exterior_horizontal_left_lower"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_UPPER["exterior_horizontal_right_lower"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_UPPER["wire_lower_quantum"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_UPPER["wire_lower_classical"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_UPPER["wire_lower_none"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } STYLES.update({"GATE_UPPER": style_GATE_UPPER}) style_GATE_MIDDLE = dict(STYLES["GATE_SINGLE"]) style_GATE_MIDDLE["block_connector_left_lower"] = { "ascii": "|", "unicode": "┃", "unicode_alt": "░", } style_GATE_MIDDLE["block_connector_right_lower"] = { "ascii": "|", "unicode": "┃", "unicode_alt": "░", } style_GATE_MIDDLE["edge_connector_lower_quantum"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_MIDDLE["edge_connector_lower_classical"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_MIDDLE["edge_connector_lower_none"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_MIDDLE["edge_corner_left_lower"] = { "ascii": "|", "unicode": "┃", "unicode_alt": "░", } style_GATE_MIDDLE["edge_corner_right_lower"] = { "ascii": "|", "unicode": "┃", "unicode_alt": "░", } style_GATE_MIDDLE["edge_horizontal_left_lower"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_MIDDLE["edge_horizontal_right_lower"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_MIDDLE["wire_lower_quantum"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_MIDDLE["wire_lower_classical"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_MIDDLE["wire_lower_none"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_MIDDLE["block_connector_left_upper"] = { "ascii": "|", "unicode": "┃", "unicode_alt": "░", } style_GATE_MIDDLE["block_connector_right_upper"] = { "ascii": "|", "unicode": "┃", "unicode_alt": "░", } style_GATE_MIDDLE["edge_connector_upper_quantum"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_MIDDLE["edge_connector_upper_classical"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_MIDDLE["edge_connector_upper_none"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_MIDDLE["edge_corner_left_upper"] = { "ascii": "|", "unicode": "┃", "unicode_alt": "░", } style_GATE_MIDDLE["edge_corner_right_upper"] = { "ascii": "|", "unicode": "┃", "unicode_alt": "░", } style_GATE_MIDDLE["edge_horizontal_left_upper"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_MIDDLE["edge_horizontal_right_upper"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_MIDDLE["wire_upper_quantum"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_MIDDLE["wire_upper_classical"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_MIDDLE["wire_upper_none"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_MIDDLE["edge_connector_left_quantum"] = { "ascii": "|", "unicode": "┨", "unicode_alt": "░", } style_GATE_MIDDLE["edge_connector_right_quantum"] = { "ascii": "|", "unicode": "┠", "unicode_alt": "░", } style_GATE_MIDDLE["edge_connector_left_classical"] = { "ascii": "|", "unicode": "╡", "unicode_alt": "░", } style_GATE_MIDDLE["edge_connector_right_classical"] = { "ascii": "|", "unicode": "╞", "unicode_alt": "░", } style_GATE_MIDDLE["edge_connector_left_none"] = { "ascii": "|", "unicode": "┃", "unicode_alt": "░", } style_GATE_MIDDLE["edge_connector_right_none"] = { "ascii": "|", "unicode": "┃", "unicode_alt": "░", } style_GATE_MIDDLE["exterior_horizontal_left_lower"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_MIDDLE["exterior_horizontal_right_lower"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_MIDDLE["exterior_horizontal_left_upper"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_MIDDLE["exterior_horizontal_right_upper"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } STYLES.update({"GATE_MIDDLE": style_GATE_MIDDLE}) style_GATE_LOWER = dict(STYLES["GATE_SINGLE"]) style_GATE_LOWER["block_connector_left_upper"] = { "ascii": "|", "unicode": "┃", "unicode_alt": "░", } style_GATE_LOWER["block_connector_right_upper"] = { "ascii": "|", "unicode": "┃", "unicode_alt": "░", } style_GATE_LOWER["edge_connector_upper_quantum"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_LOWER["edge_connector_upper_classical"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_LOWER["edge_connector_upper_none"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_LOWER["edge_corner_left_upper"] = { "ascii": "|", "unicode": "┃", "unicode_alt": "░", } style_GATE_LOWER["edge_corner_right_upper"] = { "ascii": "|", "unicode": "┃", "unicode_alt": "░", } style_GATE_LOWER["edge_horizontal_left_upper"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_LOWER["edge_horizontal_right_upper"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_LOWER["exterior_horizontal_left_upper"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_LOWER["exterior_horizontal_right_upper"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_LOWER["wire_upper_quantum"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_LOWER["wire_upper_classical"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } style_GATE_LOWER["wire_upper_none"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "░", } STYLES.update({"GATE_LOWER": style_GATE_LOWER}) style_GATE_SINGLE = dict(STYLES["GATE_SINGLE"]) STYLES.update({"METER_SINGLE": style_GATE_SINGLE}) style_GATE_UPPER = dict(STYLES["GATE_UPPER"]) STYLES.update({"METER_UPPER": style_GATE_UPPER}) style_GATE_MIDDLE = dict(STYLES["GATE_MIDDLE"]) STYLES.update({"METER_MIDDLE": style_GATE_MIDDLE}) style_GATE_LOWER = dict(STYLES["GATE_LOWER"]) STYLES.update({"METER_LOWER": style_GATE_LOWER}) style_LSTICK_UPPER = dict(STYLES["LSTICK_SINGLE"]) style_LSTICK_UPPER["edge_connector_right_quantum"] = { "ascii": "(bracket_connector_right_unset", "unicode": "⎧bracket_connector_right_unset", "unicode_alt": "⎧bracket_connector_right_unset", } style_LSTICK_UPPER["edge_connector_right_classical"] = { "ascii": "(bracket_connector_right_unset", "unicode": "⎧bracket_connector_right_unset", "unicode_alt": "⎧bracket_connector_right_unset", } style_LSTICK_UPPER["edge_corner_right_lower"] = { "ascii": "| ", "unicode": "⎪ ", "unicode_alt": "⎪ ", } style_LSTICK_UPPER["block_connector_right_lower"] = { "ascii": "| ", "unicode": "⎪ ", "unicode_alt": "⎪ ", } style_LSTICK_UPPER["edge_vertical_right_lower"] = { "ascii": "| ", "unicode": "⎪ ", "unicode_alt": "⎪ ", } style_LSTICK_UPPER["edge_vertical_right_upper"] = { "ascii": "|", "unicode": " ", "unicode_alt": " ", } STYLES.update({"LSTICK_UPPER": style_LSTICK_UPPER}) style_LSTICK_MIDDLE = dict(STYLES["LSTICK_SINGLE"]) style_LSTICK_MIDDLE["edge_connector_right_quantum"] = { "ascii": "(bracket_connector_right_unset", "unicode": "⎨bracket_connector_right_unset", "unicode_alt": "⎨bracket_connector_right_unset", } style_LSTICK_MIDDLE["edge_connector_right_classical"] = { "ascii": "(bracket_connector_right_unset", "unicode": "⎨bracket_connector_right_unset", "unicode_alt": "⎨bracket_connector_right_unset", } style_LSTICK_MIDDLE["edge_connector_right_none"] = { "ascii": "|bracket_connector_right_unset", "unicode": "⎪bracket_connector_right_unset", "unicode_alt": "⎪bracket_connector_right_unset", } style_LSTICK_MIDDLE["edge_corner_right_lower"] = { "ascii": "| ", "unicode": "⎪ ", "unicode_alt": "⎪ ", } style_LSTICK_MIDDLE["block_connector_right_lower"] = { "ascii": "| ", "unicode": "⎪ ", "unicode_alt": "⎪ ", } style_LSTICK_MIDDLE["edge_vertical_right_lower"] = { "ascii": "| ", "unicode": "⎪ ", "unicode_alt": "⎪ ", } style_LSTICK_MIDDLE["edge_corner_right_upper"] = { "ascii": "| ", "unicode": "⎪ ", "unicode_alt": "⎪ ", } style_LSTICK_MIDDLE["block_connector_right_upper"] = { "ascii": "| ", "unicode": "⎪ ", "unicode_alt": "⎪ ", } style_LSTICK_MIDDLE["edge_vertical_right_upper"] = { "ascii": "| ", "unicode": "⎪ ", "unicode_alt": "⎪ ", } STYLES.update({"LSTICK_MIDDLE": style_LSTICK_MIDDLE}) style_LSTICK_LOWER = dict(STYLES["LSTICK_SINGLE"]) style_LSTICK_LOWER["edge_connector_right_quantum"] = { "ascii": "(bracket_connector_right_unset", "unicode": "⎩bracket_connector_right_unset", "unicode_alt": "⎩bracket_connector_right_unset", } style_LSTICK_LOWER["edge_connector_right_classical"] = { "ascii": "(bracket_connector_right_unset", "unicode": "⎩bracket_connector_right_unset", "unicode_alt": "⎩bracket_connector_right_unset", } style_LSTICK_LOWER["edge_corner_right_upper"] = { "ascii": "| ", "unicode": "⎪ ", "unicode_alt": "⎪ ", } style_LSTICK_LOWER["block_connector_right_upper"] = { "ascii": "| ", "unicode": "⎪ ", "unicode_alt": "⎪ ", } style_LSTICK_LOWER["edge_vertical_right_lower"] = { "ascii": " ", "unicode": " ", "unicode_alt": " ", } style_LSTICK_LOWER["edge_vertical_right_upper"] = { "ascii": "| ", "unicode": "⎪ ", "unicode_alt": "⎪ ", } STYLES.update({"LSTICK_LOWER": style_LSTICK_LOWER}) style_RSTICK_UPPER = dict(STYLES["RSTICK_SINGLE"]) style_RSTICK_UPPER["edge_connector_left_quantum"] = { "ascii": "bracket_connector_left_unset)", "unicode": "bracket_connector_left_unset⎫", "unicode_alt": "bracket_connector_left_unset⎫", } style_RSTICK_UPPER["edge_connector_left_classical"] = { "ascii": "bracket_connector_left_unset)", "unicode": "bracket_connector_left_unset⎫", "unicode_alt": "bracket_connector_left_unset⎫", } style_RSTICK_UPPER["edge_corner_left_lower"] = { "ascii": " |", "unicode": " ⎪", "unicode_alt": " ⎪", } style_RSTICK_UPPER["block_connector_left_lower"] = { "ascii": " |", "unicode": " ⎪", "unicode_alt": " ⎪", } style_RSTICK_UPPER["edge_vertical_left_lower"] = { "ascii": " |", "unicode": " ⎪", "unicode_alt": " ⎪", } style_LSTICK_UPPER["edge_vertical_left_upper"] = { "ascii": " ", "unicode": " ", "unicode_alt": " ", } STYLES.update({"RSTICK_UPPER": style_RSTICK_UPPER}) style_RSTICK_MIDDLE = dict(STYLES["RSTICK_SINGLE"]) style_RSTICK_MIDDLE["edge_connector_left_quantum"] = { "ascii": "bracket_connector_left_unset)", "unicode": "bracket_connector_left_unset⎬", "unicode_alt": "bracket_connector_left_unset⎬", } style_RSTICK_MIDDLE["edge_connector_left_classical"] = { "ascii": "bracket_connector_left_unset)", "unicode": "bracket_connector_left_unset⎬", "unicode_alt": "bracket_connector_left_unset⎬", } style_RSTICK_MIDDLE["edge_connector_left_none"] = { "ascii": "bracket_connector_left_unset|", "unicode": "bracket_connector_left_unset⎪", "unicode_alt": "bracket_connector_left_unset⎪", } style_RSTICK_MIDDLE["edge_corner_left_lower"] = { "ascii": " |", "unicode": " ⎪", "unicode_alt": " ⎪", } style_RSTICK_MIDDLE["block_connector_left_lower"] = { "ascii": " |", "unicode": " ⎪", "unicode_alt": " ⎪", } style_RSTICK_MIDDLE["edge_vertical_left_lower"] = { "ascii": " |", "unicode": " ⎪", "unicode_alt": " ⎪", } style_RSTICK_MIDDLE["edge_corner_left_upper"] = { "ascii": " |", "unicode": " ⎪", "unicode_alt": " ⎪", } style_RSTICK_MIDDLE["block_connector_left_upper"] = { "ascii": " |", "unicode": " ⎪", "unicode_alt": " ⎪", } style_RSTICK_MIDDLE["edge_vertical_left_upper"] = { "ascii": " |", "unicode": " ⎪", "unicode_alt": " ⎪", } STYLES.update({"RSTICK_MIDDLE": style_RSTICK_MIDDLE}) style_RSTICK_LOWER = dict(STYLES["RSTICK_SINGLE"]) style_RSTICK_LOWER["edge_connector_left_quantum"] = { "ascii": "bracket_connector_left_unset)", "unicode": "bracket_connector_left_unset⎭", "unicode_alt": "bracket_connector_left_unset⎭", } style_RSTICK_LOWER["edge_connector_left_classical"] = { "ascii": "bracket_connector_left_unset)", "unicode": "bracket_connector_left_unset⎭", "unicode_alt": "bracket_connector_left_unset⎭", } style_RSTICK_LOWER["edge_corner_left_upper"] = { "ascii": " |", "unicode": " ⎪", "unicode_alt": " ⎪", } style_RSTICK_LOWER["block_connector_left_upper"] = { "ascii": " |", "unicode": " ⎪", "unicode_alt": " ⎪", } style_RSTICK_LOWER["edge_vertical_left_lower"] = { "ascii": " ", "unicode": " ", "unicode_alt": " ", } style_RSTICK_LOWER["edge_vertical_left_upper"] = { "ascii": " |", "unicode": " ⎪", "unicode_alt": " ⎪", } STYLES.update({"RSTICK_LOWER": style_RSTICK_LOWER}) style_TARG = dict(STYLES["PUSH"]) style_TARG["label"] = {"ascii": "@", "unicode": "⨁", "unicode_alt": "⨁"} # ⊕, ⨁ STYLES.update({"TARG": style_TARG}) style_CTRL = dict(STYLES["PUSH"]) style_CTRL["label"] = {"ascii": "*", "unicode": "●", "unicode_alt": "●"} STYLES.update({"CTRL": style_CTRL}) style_OCTRL = dict(STYLES["PUSH"]) style_OCTRL["label"] = {"ascii": "o", "unicode": "○", "unicode_alt": "○"} STYLES.update({"OCTRL": style_OCTRL}) style_SWAP = dict(STYLES["PUSH"]) style_SWAP["label"] = {"ascii": "X", "unicode": "╳", "unicode_alt": "╳"} STYLES.update({"SWAP": style_SWAP}) style_TRACE = dict(STYLES["RSTICK_SINGLE"]) style_TRACE["label_connector_left"] = {"ascii": "!", "unicode": "⏚", "unicode_alt": "⏚"} STYLES.update({"TRACE": style_TRACE}) style_TERM = dict(STYLES["RSTICK_SINGLE"]) style_TERM["label_connector_left"] = { "ascii": "wire_left_unset", "unicode": "wire_left_unset", "unicode_alt": "wire_left_unset", } STYLES.update({"TERM": style_TERM}) style_WORMHOLE_PAST = dict(STYLES["LSTICK_SINGLE"]) style_WORMHOLE_PAST["label_connector_right"] = { "ascii": "<", "unicode": "◀", "unicode_alt": "◀", } STYLES.update({"WORMHOLE_PAST": style_WORMHOLE_PAST}) style_WORMHOLE_FUTURE = dict(STYLES["RSTICK_SINGLE"]) style_WORMHOLE_FUTURE["label_connector_left"] = { "ascii": ">", "unicode": "▶", "unicode_alt": "▶", } STYLES.update({"WORMHOLE_FUTURE": style_WORMHOLE_FUTURE}) style_WIRE_QN = dict(STYLES["PUSH"]) style_WIRE_QN["label"] = {"ascii": "-", "unicode": "─", "unicode_alt": "─"} STYLES.update({"WIRE_QN": style_WIRE_QN}) style_WIRE_CN = dict(STYLES["PUSH"]) style_WIRE_CN["label"] = {"ascii": "=", "unicode": "═", "unicode_alt": "═"} STYLES.update({"WIRE_CN": style_WIRE_CN}) style_WIRE_NQ = dict(STYLES["PUSH"]) style_WIRE_NQ["label"] = {"ascii": "|", "unicode": "│", "unicode_alt": "│"} STYLES.update({"WIRE_NQ": style_WIRE_NQ}) style_WIRE_NC = dict(STYLES["PUSH"]) style_WIRE_NC["label"] = {"ascii": "#", "unicode": "║", "unicode_alt": "║"} STYLES.update({"WIRE_NC": style_WIRE_NC}) style_WIRE_QQ = dict(STYLES["PUSH"]) style_WIRE_QQ["label"] = {"ascii": "-", "unicode": "┼", "unicode_alt": "┼"} STYLES.update({"WIRE_QQ": style_WIRE_QQ}) style_WIRE_QC = dict(STYLES["PUSH"]) style_WIRE_QC["label"] = {"ascii": "-", "unicode": "╫", "unicode_alt": "╫"} STYLES.update({"WIRE_QC": style_WIRE_QC}) style_WIRE_CQ = dict(STYLES["PUSH"]) style_WIRE_CQ["label"] = {"ascii": "=", "unicode": "╪", "unicode_alt": "╪"} STYLES.update({"WIRE_CQ": style_WIRE_CQ}) style_WIRE_CC = dict(STYLES["PUSH"]) style_WIRE_CC["label"] = {"ascii": "=", "unicode": "╬", "unicode_alt": "╬"} STYLES.update({"WIRE_CC": style_WIRE_CC}) # Useful sets for control statements family_wide_gates = { "METER_SINGLE", "METER_UPPER", "METER_MIDDLE", "METER_LOWER", "GATE_SINGLE", "GATE_UPPER", "GATE_MIDDLE", "GATE_LOWER", } family_wide_sticks = { "LSTICK_UPPER", "LSTICK_MIDDLE", "LSTICK_LOWER", "RSTICK_UPPER", "RSTICK_MIDDLE", "RSTICK_LOWER", } def change_wire_family_horizontal(string, replacement): string_list = list(string) string_list[-2] = replacement return "".join(string_list) def change_wire_family_vertical(string, replacement): string_list = list(string) string_list[-1] = replacement return "".join(string_list) class VisualizationProperties: def __init__( self, pad: tuple[int, int] | None = None, sep: tuple[int, int] | None = None, dimensions: tuple[int, int] | None = None, style: str | None = None, ): pad = (0, 0) if pad is None else pad sep = {"upper": 1, "lower": 1, "left": 1, "right": 1} if sep is None else sep if isinstance(sep, tuple) is True: sep = {"upper": sep[1], "lower": sep[1], "left": sep[0], "right": sep[0]} dimensions = (0, 0) if dimensions is None else dimensions style = Styles.UNICODE.value if style is None else style self.pad = pad self.sep = sep self.dimensions = dimensions self.style = style @property def pad(self): return self._pad @pad.setter def pad(self, pad): self._pad = pad @property def sep(self): return self._sep @sep.setter def sep(self, sep): self._sep = sep @property def dimensions(self): return self._dimensions @dimensions.setter def dimensions(self, dimensions): if dimensions[0] is None: dimensions = list(dimensions) dimensions[0] = 0 dimensions = tuple(dimensions) if dimensions[1] is None: dimensions = list(dimensions) dimensions[1] = 0 dimensions = tuple(dimensions) self._dimensions = dimensions @property def style(self): return self._style @style.setter def style(self, style): self._style = style
[docs] class DiagramCell(VisualizationProperties): """A class for visualizing individual "cells" (the smallest indivisible units) of quantum circuit diagrams and storing their metadata.""" def __init__( self, *args, family: str | None = None, connections: dict[str, str] | None = None, label: str | None = None, quantikz_arguments: dict[str, str] | None = None, **kwargs, ): super().__init__(*args, **kwargs) family = "GATE_SINGLE" if family is None else family connections = ( { "upper": "none", "lower": "none", "left": "none", "right": "none", } if connections is None else connections ) label = " " if label is None else label quantikz_arguments = ( {"compulsory": "TODO", "optional": "TODO"} if quantikz_arguments is None else quantikz_arguments ) self.family = family self.connections = connections self.label = label if not any( family in self.family for family in { "GATE", "METER", "LSTICK", "RSTICK", "WORMHOLE", "TRACE", "TERM", } ): self.label = " " self.quantikz_arguments = quantikz_arguments self._styles = copy.deepcopy(STYLES) @property def family(self): return self._family @family.setter def family(self, family): self._family = family @property def connections(self): return self._connections @connections.setter def connections(self, connections): self._connections = connections @property def quantikz_arguments(self): return self._quantikz_arguments @quantikz_arguments.setter def quantikz_arguments(self, quantikz_arguments): self._quantikz_arguments = quantikz_arguments @property def width(self): return self.dimensions[0] @width.setter def width(self, width): self.dimensions[0] = width @property def width_label(self): return math.ceil((len(self.label) - 1) / 2) def width_min(self, pad=None): pad = self.pad if pad is None else pad width_edge = 1 width_min = max(0, pad[0] + self.width_label + width_edge) return width_min @property def height(self): return self.dimensions[1] @height.setter def height(self, height): self.dimensions[1] = height def height_min(self, pad=None): pad = self.pad if pad is None else pad height_edge = 1 height_min = max(self.height, pad[1] + height_edge) return height_min def cell( self, pad: tuple[int, int] | None = None, sep: tuple[int, int] | None = None, dimensions: tuple[int, int] | None = None, style: str | None = None, ): pad = self.pad if pad is None else pad sep = self.sep if sep is None else sep if isinstance(sep, tuple) is True: sep = {"upper": sep[1], "lower": sep[1], "left": sep[0], "right": sep[0]} if dimensions is None: width = self.width height = self.height else: width = dimensions[0] height = dimensions[1] style = self.style if style is None else style width = max(width, self.width_min(pad)) height = max(height, self.height_min(pad)) cell = copy.deepcopy(CELL_TEMPLATE.copy()) styles = copy.deepcopy(self._styles) unsets = [ "wire_left_unset", "wire_lower_unset", "wire_right_unset", "wire_upper_unset", "edge_connector_left_unset", "edge_connector_lower_unset", "edge_connector_right_unset", "edge_connector_upper_unset", "bracket_connector_right_none", "bracket_connector_left_none", ] # Label padding for the *STICK family so that they are left-aligned or right-aligned. # This removes the small padding sticks for STICK cells which do not have a label, e.g., the bottom cell (*STICK_LOWER). # This also connects any cells with non-empty labels to the circuit with a wire. label = self.label if "STICK" in self.family: # Check for empty labels. (For non-labelled *STICK_* variants.) if label.isspace() is True: if "LSTICK" in self.family: styles[self.family]["pad_right"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", } if "RSTICK" in self.family: styles[self.family]["pad_left"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", } styles[self.family]["label_connector"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", } styles[self.family]["label_connector"] = { "ascii": "empty", "unicode": "empty", "unicode_alt": "empty", } if any(family in self.family for family in {"LSTICK"}): label = label + styles[self.family]["label_connector"][style] if any(family in self.family for family in {"RSTICK"}): label = styles[self.family]["label_connector"][style] + label if any(family in self.family for family in {"WORMHOLE_PAST"}): label = label + styles[self.family]["label_connector_right"][style] if any( family in self.family for family in {"WORMHOLE_FUTURE", "TRACE", "TERM"} ): label = styles[self.family]["label_connector_left"][style] + label # Replace label if cell should not have a label. if ( any(family in self.family for family in ["GATE", "METER"]) and self.label.isspace() is True ): styles[self.family]["label"] = { "ascii": "replacement", "unicode": "replacement", "unicode_alt": "replacement", } codes = list(set(flatten_list([*styles[self.family].keys()] + [unsets]))) # Replacement of self-referencing components. for n in range(0, cell.shape[0]): for m in range(0, cell.shape[1]): while True: cell_before = cell[n][m] for code, substitution in styles[self.family].items(): cell[n][m] = cell[n][m].replace(code, substitution[style]) label = label.replace(code, substitution[style]) for position, kind in self.connections.items(): cell[n][m] = cell[n][m].replace( f"wire_{position}_unset", f"wire_{position}_{kind}" ) cell[n][m] = cell[n][m].replace( f"edge_connector_{position}_unset", f"edge_connector_{position}_{kind}", ) cell[n][m] = cell[n][m].replace( f"bracket_connector_{position}_unset", f"bracket_connector_{position}_{kind}", ) label = label.replace( f"wire_{position}_unset", f"wire_{position}_{kind}" ) label = label.replace( f"edge_connector_{position}_unset", f"edge_connector_{position}_{kind}", ) label = label.replace( f"bracket_connector_{position}_unset", f"bracket_connector_{position}_{kind}", ) cell_after = cell[n][m] if cell_after == cell_before: break for n in range(0, cell.shape[0]): for m in range(0, cell.shape[1]): for code in codes: if code in styles[self.family].keys(): cell[n][m] = cell[n][m].replace( code, styles[self.family][code][style] ) label = label.replace(code, styles[self.family][code][style]) # Horizontal padding width_label = max(self.width_label, math.ceil((len(label) - 1) / 2)) width_desired = width width_total = int((cell.shape[1] - 1) / 2) - 2 + width_label + pad[0] if width_desired < width_total: width_extra = width_total - width_desired else: width_extra = width_desired - width_total columns = [m for m in range(0, cell.shape[1])] median = statistics.median(columns) columns[columns.index(median - 1)] = [columns[columns.index(median - 1)]] * ( pad[0] + self.width_label + width_extra ) columns = flatten_list(columns) columns[columns.index(median + 1)] = [columns[columns.index(median + 1)]] * ( pad[0] + self.width_label + width_extra ) columns = flatten_list(columns) cell = cell[0 : cell.shape[0], columns] # Vertical padding height_desired = height height_increase_from_label = 0 height_total = ( int((cell.shape[0] - 1) / 2) - 2 + height_increase_from_label + pad[1] ) if height is not None: height_extra = height_desired - height_total else: height_extra = 0 if height_extra < 0: raise ValueError( """The current cell cannot be as short as the specified height.""" ) rows = [n for n in range(0, cell.shape[0])] median = statistics.median(rows) rows[rows.index(median - 1)] = [rows[rows.index(median - 1)]] * ( pad[1] + height_extra ) rows = flatten_list(rows) rows[rows.index(median + 1)] = [rows[rows.index(median + 1)]] * ( pad[1] + height_extra ) rows = flatten_list(rows) cell = cell[rows, 0 : cell.shape[1]] # Adjust width trim_left = math.ceil((len(label) - 1) / 2) trim_right = math.floor((len(label) - 1) / 2) if any( family in self.family for family in {"RSTICK", "TRACE", "TERM", "WORMHOLE_FUTURE"} ): trim_left = math.floor((len(label) - 1) / 2) trim_right = math.ceil((len(label) - 1) / 2) middle_row = int((cell.shape[0] - 1) / 2) middle_column = int((cell.shape[1] - 1) / 2) for k in range(1, trim_left + 1): cell[middle_row][middle_column - k] = "" for k in range(1, trim_right + 1): cell[middle_row][middle_column + k] = "" # Insert label for n in range(0, cell.shape[0]): for m in range(0, cell.shape[1]): cell[n][m] = cell[n][m].replace("label", label) # Separations for position, length in sep.items(): if position == "upper": rows = [n for n in range(0, cell.shape[0])] rows[rows.index(min(rows))] = [rows[rows.index(min(rows))]] * length rows = flatten_list(rows) cell = cell[rows, 0 : cell.shape[1]] elif position == "lower": rows = [n for n in range(0, cell.shape[0])] rows[rows.index(max(rows))] = [rows[rows.index(max(rows))]] * length rows = flatten_list(rows) cell = cell[rows, 0 : cell.shape[1]] elif position == "left": columns = [m for m in range(0, cell.shape[1])] columns[columns.index(min(columns))] = [ columns[columns.index(min(columns))] ] * length columns = flatten_list(columns) cell = cell[0 : cell.shape[0], columns] elif position == "right": columns = [m for m in range(0, cell.shape[1])] columns[columns.index(max(columns))] = [ columns[columns.index(max(columns))] ] * length columns = flatten_list(columns) cell = cell[0 : cell.shape[0], columns] return cell def visualize(self, **kwargs): cell = self.cell(**kwargs) visualization = "\n".join(["".join(row) for row in cell]) visualization = textwrap.dedent(visualization) print(visualization)
[docs] class DiagramColumn(VisualizationProperties): """A class for assembling a collection of :python:`DiagramCell` instances into a column.""" def __init__( self, *args, cells: list[DiagramCell], section: str | None = None, **kwargs ): super().__init__(*args, **kwargs) section = Sections.GATES.value if section is None else section self.cells = cells self.section = section @property def section(self): return self._section @section.setter def section(self, section): self._section = section @property def cells(self): return self._cells @cells.setter def cells(self, cells): self._cells = cells @property def family(self): family = Families.COMPOSITION.value for archetype in family: family_truths = list( set( [ (True if archetype in cell.family else False) for cell in self.cells ] ) ) if len(family_truths) == 1 and family_truths[0] is True: family = archetype return family @property def width(self): width = 0 if self.dimensions[0] is not None: width = self.dimensions[0] return width @width.setter def width(self, width): self.dimensions[0] = width def width_min(self, pad=None): pad = self.pad if pad is None else pad widths = [] for cell in self.cells: widths.append(cell.width_min(pad)) width_min = max(flatten_list([widths, self.width])) return width_min @property def height(self): height = 0 if self.dimensions[1] is not None: height = self.dimensions[1] return height @height.setter def height(self, height): self.dimensions[1] = height def height_min(self, pad=None): pad = self.pad if pad is None else pad heights = [] for cell in self.cells: heights.append(cell.height_min(pad)) height_min = max(flatten_list([heights, self.height])) return height_min def column( self, pad: tuple[int, int] | None = None, sep: tuple[int, int] | None = None, dimensions: tuple[int, int] | None = None, style: str | None = None, sep_collapse: bool = True, sep_trim: dict[str, bool] | None = None, ): pad = self.pad if pad is None else pad sep = self.sep if sep is None else sep if isinstance(sep, tuple) is True: sep = {"upper": sep[1], "lower": sep[1], "left": sep[0], "right": sep[0]} dimensions = self.dimensions if dimensions is None else dimensions style = self.style if style is None else style sep_trim_default = { "upper": False, "lower": False, "left": False, "right": False, } sep_trim = copy.deepcopy(sep_trim_default) if sep_trim is None else sep_trim for position, value in sep_trim_default.items(): if position not in sep_trim: sep_trim[position] = value column = [] # Determine width of column from its individual cells. width = 0 label_width_max = 0 for cell in self.cells: pad_cell = copy.deepcopy(pad) pad_cell = cell.pad if pad_cell is None else pad_cell width_total = cell.width_label width = max( width, dimensions[0], self.width_min(pad_cell), cell.width_min(), width_total, ) height = dimensions[1] label_width_max = max(label_width_max, len(cell.label)) for k, cell in enumerate(self.cells): # Pad label with spaces on relevant side. if self.section == Sections.INPUTS.value: cell.label = f"{cell.label:{' '}>{label_width_max}}" if self.section == Sections.OUTPUTS.value: cell.label = f"{cell.label:{' '}<{label_width_max}}" pad_cell = copy.deepcopy(pad) if pad_cell is None or pad_cell is False: pad_cell = cell.pad sep_cell = copy.deepcopy(sep) if sep is None or sep is False: sep_cell = cell.sep if sep_collapse is True and k != 0: sep_cell["upper"] = 0 if k == 0 and sep_trim["upper"] is True: sep_cell["upper"] = 0 if k == len(self.cells) - 1 and sep_trim["lower"] is True: sep_cell["lower"] = 0 if sep_trim["left"] is True: sep_cell["left"] = 0 if sep_trim["right"] is True: sep_cell["right"] = 0 style_cell = style if style is None or style is False: style_cell = cell.style cell_string_array = cell.cell( pad=pad_cell, sep=sep_cell, dimensions=(width, height), style=style_cell ) column.append(cell_string_array) column = np.vstack(tuple(column)) return column def visualize(self, return_string: bool | None = None, **kwargs): column = self.column(**kwargs) visualization = "\n".join(["".join(row) for row in column]) visualization = textwrap.dedent(visualization) if return_string is True: return visualization else: print(visualization) def diagram( self, sep: tuple[int, int] | None = None, return_string: bool | None = None, **kwargs, ): if isinstance(sep, tuple) is True: sep = {"upper": sep[1], "lower": sep[1], "left": sep[0], "right": sep[0]} return_string = False if return_string is None else return_string return self.visualize(sep=sep, return_string=return_string, **kwargs)
[docs] class DiagramCircuit(VisualizationProperties): """A class for assembling :python:`DiagramColumn` instances together into a grid.""" def __init__(self, *args, columns: list, **kwargs): super().__init__(*args, **kwargs) self.columns = columns @property def columns(self): return self._columns @columns.setter def columns(self, columns): self._columns = columns @property def num_columns(self): return len(self.columns) @property def num_rows(self): num_rows = [] for column in self.columns: num_rows.append(len(column.cells)) num_rows = list(set(num_rows)) if len(num_rows) != 1: raise ValueError("""The provided columns have an unequal number of rows.""") return num_rows[0] def height_min(self, row_num): heights = [] for column in self.columns: heights.append(column.height_min()) return max(heights) def width_min(self, column_num): return self.columns[column_num].width_min() @property def cells(self): cells = [[] for k in range(0, self.num_columns)] for index, column in enumerate(self.columns): for cell in column.cells: cells[index].append(cell) return cells def grid( self, pad: tuple[int, int] | None = None, sep: tuple[int, int] | None = None, dimensions: tuple[int, int] | None = None, style: str | None = None, uniform_spacing: bool | None = None, force_separation: bool | None = None, ): pad = self.pad if pad is None else pad sep = self.sep if sep is None else sep if isinstance(sep, tuple) is True: sep = {"upper": sep[1], "lower": sep[1], "left": sep[0], "right": sep[0]} dimensions = self.dimensions if dimensions is None else dimensions style = self.style if style is None else style uniform_spacing = False if uniform_spacing is None else uniform_spacing force_separation = False if force_separation is None else force_separation # Set wire types. for index_column, column in enumerate(self.cells): for index_row, cell in enumerate(column): if ( index_column > 0 and self.cells[index_column - 1][index_row].connections["right"] == "classical" ): self.cells[index_column][index_row].connections[ "left" ] = "classical" if "WIRE" in self.cells[index_column][index_row].family: old_wire_family = self.cells[index_column][index_row].family new_wire_family = change_wire_family_horizontal( old_wire_family, "C" ) self.cells[index_column][index_row].family = new_wire_family self.cells[index_column][index_row].connections[ "right" ] = "classical" grid = [[] for k in range(0, self.num_columns)] # Determine width of columns from their individual cells. width = 0 height = 0 label_widths_max = [] for index_column, column in enumerate(self.cells): label_width_max = 0 for index_row, cell in enumerate(column): pad_cell = copy.deepcopy(pad) pad_cell = cell.pad if pad_cell is None else pad_cell height = max( height, dimensions[1], self.height_min(index_row), self.columns[index_column].height_min(pad_cell), cell.height_min(), ) label_width_max = max(label_width_max, len(cell.label)) label_widths_max.append(label_width_max) # Pad label with spaces on relevant side. for index, column in enumerate(self.columns): if column.section == Sections.INPUTS.value: for cell in column.cells: cell.label = f"{cell.label:{' '}>{label_widths_max[index]}}" if column.section == Sections.OUTPUTS.value: for cell in column.cells: cell.label = f"{cell.label:{' '}<{label_widths_max[index]}}" for index_column, column in enumerate(self.cells): for index_row, cell in enumerate(column): pad_cell = copy.deepcopy(pad) if pad_cell is None or pad_cell is False: pad_cell = cell.pad width = max( 0, dimensions[0], self.width_min(index_column), self.columns[index_column].width_min(pad_cell), cell.width_min(), ) sep_cell = copy.deepcopy(sep) if sep_cell is None or sep_cell is False: sep_cell = cell.sep if sep_cell["lower"] == sep_cell["upper"]: sep_cell["upper"] = math.ceil((sep_cell["upper"] - 1) / 2) sep_cell["lower"] = math.floor((sep_cell["lower"] - 1) / 2) column_family_previous = [] column_family_next = [] if len(self.columns) > 1: column_family_previous = [ cell.family for cell in self.columns[index_column - 1].cells ] if index_column == 0: column_family_previous = [ "PUSH" for cell in self.columns[index_column - 1].cells ] column_family_current = [ cell.family for cell in self.columns[index_column].cells ] pad_cell_original = pad_cell[0] if self.columns[index_column].section == Sections.INPUTS.value: sep_cell["right"] += pad_cell[0] + 1 pad_cell = list(pad_cell) pad_cell[0] = 1 pad_cell = tuple(pad_cell) if self.columns[index_column].section == Sections.OUTPUTS.value: sep_cell["left"] += pad_cell[0] + 1 pad_cell = list(pad_cell) pad_cell[0] = 1 pad_cell = tuple(pad_cell) style_cell = copy.deepcopy(style) if style is None or style is False: style_cell = cell.style cell_string_array = cell.cell( pad=pad_cell, sep=sep_cell, dimensions=(width, height), style=style_cell, ) cell_family_previous = ( self.columns[index_column].cells[index_row - 1].family ) if ( len(self.columns[index_column].cells) != 1 and sep["lower"] == sep["upper"] ): if "STICK" not in cell.family or "WORMHOLE" not in cell.family: if "LOWER" not in cell.family or "SINGLE" not in cell.family: if index_row + 1 != self.num_rows: if index_row == 0: if ( "LOWER" not in cell.family and "SINGLE" not in cell.family ): cell_string_array = np.delete( cell_string_array, (-1), axis=0 ) else: if ( "LOWER" not in cell.family and "SINGLE" not in cell.family ): cell_string_array = np.delete( cell_string_array, (-1), axis=0 ) if ( "LOWER" in cell_family_previous or "SINGLE" in cell_family_previous ): cell_string_array = np.delete( cell_string_array, (0), axis=0 ) else: if ( "LOWER" in cell_family_previous or "SINGLE" in cell_family_previous ): cell_string_array = np.delete( cell_string_array, (0), axis=0 ) else: if index_row + 1 != self.num_rows: cell_string_array = np.delete( cell_string_array, (-1), axis=0 ) # Split array entries up in cases of multi-character strings. cell_string_array = np.array( [ [char for string in row for char in string] for row in cell_string_array ] ) # Trimming. trim_left = 0 trim_right = 0 if index_column != max(range(0, self.num_columns)): column_family_next = [ cell.family for cell in self.columns[index_column + 1].cells ] # Account for the padding difference between singlemode and multimode LSTICKs and RSTICK. if self.columns[index_column].section == Sections.INPUTS.value: if not any( family in family_wide_sticks for family in column_family_current ): trim_right += 3 if self.columns[index_column].section == Sections.OUTPUTS.value: if not any( family in family_wide_sticks for family in column_family_current ): trim_left += 3 # Trim extra spacing to the left of the inputs and to the right of the outputs. if self.columns[index_column].section == Sections.INPUTS.value: trim_left += sep["left"] + max(1, pad_cell_original - 1) if self.columns[index_column].section == Sections.OUTPUTS.value: trim_right += sep["right"] + max(2, pad_cell_original - 1) - 1 # Make spacing equal to horizontal separation (not double). if self.columns[index_column].section == Sections.GATES.value: if index_column != min(range(0, self.num_columns)): trim_left += math.ceil(sep["left"] / 2) if index_column != max(range(0, self.num_columns)): trim_right += math.floor(sep["right"] / 2) # Trim inputs and outputs when no gates. if self.columns[index_column].section == Sections.INPUTS.value: if ( index_column != max(range(0, self.num_columns)) and self.columns[index_column + 1].section == Sections.OUTPUTS.value ): trim_right += math.ceil((sep["right"] + 1) / 2) + math.ceil( (pad_cell_original) / 2 ) if self.columns[index_column].section == Sections.OUTPUTS.value: if ( index_column != min(range(0, self.num_columns)) and self.columns[index_column - 1].section == Sections.INPUTS.value ): trim_left += ( math.floor((sep["left"] + 1) / 2) + 1 + math.floor((pad_cell_original) / 2) ) # Trim the spacing between the end gates if either has a neighbouring *STICK. if self.columns[index_column].section == Sections.GATES.value: if ( index_column != min(range(0, self.num_columns)) and self.columns[index_column - 1].section == Sections.INPUTS.value ): trim_left += math.ceil((sep["left"] + 1) / 2) - 1 if ( index_column != max(range(0, self.num_columns)) and self.columns[index_column + 1].section == Sections.OUTPUTS.value ): trim_right += math.floor((sep["right"] + 1) / 2) # :python:`force_separation` argument. if force_separation is True: uniform_spacing = True if self.columns[index_column].section == Sections.GATES.value: if ( any( family in family_wide_gates for family in column_family_current ) is False ): # +1 accounts for edge of block gate. trim_left += pad_cell[0] + 1 trim_right += pad_cell[0] + 1 if self.columns[index_column].section == Sections.INPUTS.value: trim_right += pad_cell_original + 1 if ( index_column != max(range(0, self.num_columns)) and self.columns[index_column + 1].section == Sections.OUTPUTS.value ): trim_right -= pad_cell_original + 1 if self.columns[index_column].section == Sections.OUTPUTS.value: trim_left += pad_cell_original + 1 if ( index_column != min(range(0, self.num_columns)) and self.columns[index_column - 1].section == Sections.INPUTS.value ): trim_left -= pad_cell_original + 1 else: if not any( family in family_wide_gates for family in column_family_current ): trim_left += max(pad_cell_original - 2, 0) else: if self.columns[ index_column ].section == Sections.GATES.value and not any( family in family_wide_gates for family in column_family_current ): if ( index_column != max(range(0, self.num_columns)) and self.columns[index_column + 1].section == Sections.OUTPUTS.value ): trim_right += 1 if uniform_spacing is False: if ( self.columns[index_column].section == Sections.GATES.value and len(self.columns) > 1 ): if not any( family in family_wide_gates for family in column_family_current ) and not any( family in family_wide_gates for family in column_family_next ): trim_right += math.floor(pad_cell[0] / 2) if not any( family in family_wide_gates for family in column_family_previous ) and not any( family in family_wide_gates for family in column_family_current ): # +1 accounts for edge of block gate. trim_left += math.ceil(pad_cell[0] / 2) + 1 for k in range(0, trim_left): cell_string_array = np.delete(cell_string_array, (0), axis=1) for k in range(0, trim_right): cell_string_array = np.delete(cell_string_array, (-1), axis=1) grid[index_column].append(cell_string_array) columns = [] for k in range(0, self.num_columns): columns.append(np.vstack(tuple(grid[k]))) grid = np.hstack(tuple(columns)) styles = copy.deepcopy(STYLES) # Blending and other hacky fixes. # (Ideally should be fixed properly elsewhere in future). if style != "ascii": # Blending not needed for ASCII. blend_targets_left = { styles["GATE_SINGLE"]["edge_connector_left_classical"][style], styles["GATE_SINGLE"]["edge_connector_left_quantum"][style], styles["GATE_SINGLE"]["edge_connector_left_none"][style], styles["GATE_SINGLE"]["edge_vertical_left_upper"][style], } blend_targets_right = { styles["GATE_SINGLE"]["edge_connector_right_classical"][style], styles["GATE_SINGLE"]["edge_connector_right_quantum"][style], styles["GATE_SINGLE"]["edge_connector_right_none"][style], styles["GATE_SINGLE"]["edge_vertical_right_upper"][style], } for n in range(0, grid.shape[0]): for m in range(0, grid.shape[1]): # Vertically separated block gates together if they are overlapping. if ( grid[n, m] == styles["GATE_SINGLE"]["edge_corner_left_lower"][style] and n + 1 != grid.shape[0] ): if grid[n + 1, m] in blend_targets_left: grid[n, m] = styles["GATE_SINGLE"]["edge_blend_left"][style] if ( grid[n, m] == styles["GATE_SINGLE"]["edge_corner_right_lower"][style] and n + 1 != grid.shape[0] ): if grid[n + 1, m] in blend_targets_right: grid[n, m] = styles["GATE_SINGLE"]["edge_blend_right"][ style ] # LSTICK and RSTICK smoothing when no label. if ( grid[n, m] == styles["LSTICK_MIDDLE"]["edge_connector_right_quantum"][ style ][0] or grid[n, m] == styles["LSTICK_MIDDLE"]["edge_connector_right_classical"][ style ][0] ): if m - 1 >= 0 and grid[n, m - 1] == " ": grid[n, m] = styles["LSTICK_MIDDLE"][ "edge_connector_right_none" ][style][0] if ( grid[n, m] == styles["RSTICK_MIDDLE"]["edge_connector_left_quantum"][ style ][-1] or grid[n, m] == styles["RSTICK_MIDDLE"]["edge_connector_left_classical"][ style ][-1] ): if m + 1 <= grid.shape[1] and grid[n, m + 1] == " ": grid[n, m] = styles["RSTICK_MIDDLE"][ "edge_connector_left_none" ][style][-1] return grid def visualize(self, return_string: bool | None = None, **kwargs): return_string = False if return_string is None else return_string grid = self.grid(**kwargs) # Trim empty rows at top and bottom of the entire grid. while grid.shape[0] > 0 and np.all(len(set(grid[0])) == 1 and grid[0] == " "): grid = grid[1:] while grid.shape[0] > 0 and np.all(len(set(grid[-1])) == 1 and grid[-1] == " "): grid = grid[:-1] visualization = "\n".join(["".join(row) for row in grid]) visualization = textwrap.dedent(visualization) if return_string is True: return visualization else: print(visualization) def diagram(self, sep: tuple[int, int], **kwargs): if isinstance(sep, tuple) is True: sep = {"upper": sep[1], "lower": sep[1], "left": sep[0], "right": sep[0]} return self.visualize(sep=sep, **kwargs)
def partition_systems(systems, boundaries): systems = sorted(list(set(systems))) boundaries = sorted(list(set(boundaries))) partitions = [] boundaries[-1] = max(max(systems), max(boundaries)) boundaries.sort() for n in boundaries: remaining = list(systems) current = [] for m in remaining: if m <= n: current.append(m) systems.remove(m) current.sort() partitions.append(current) return partitions def assign_family(family, systems, targets, controls, anticontrols, boundaries): systems_occupied = list(set(targets + controls + anticontrols)) partitions = partition_systems(systems, boundaries) family_list = [None for k in systems] family = flatten_list([family]) for k, partition in enumerate(partitions): partition_occupied = list(set(partition) & set(systems_occupied)) partition_targets = list(set(partition) & set(targets)) partitions_controls = list(set(partition) & set(controls)) partitions_anticontrols = list(set(partition) & set(anticontrols)) for n in partition: if n in partition_targets: if family[k] in {"GATE", "METER"}: if ( n - 1 not in partition_targets and n + 1 not in partition_targets ): family_list[n] = family[k] + "_SINGLE" elif n - 1 not in partition_targets and n + 1 in partition_targets: family_list[n] = family[k] + "_UPPER" elif n - 1 in partition_targets and n + 1 not in partition_targets: family_list[n] = family[k] + "_LOWER" else: family_list[n] = family[k] + "_MIDDLE" elif family[k] in {"LSTICK", "RSTICK"}: if len(partition_targets) == 1: family_list[n] = family[k] + "_SINGLE" elif n == min(partition_targets): family_list[n] = family[k] + "_UPPER" elif n == max(partition_targets): family_list[n] = family[k] + "_LOWER" else: family_list[n] = family[k] + "_MIDDLE" else: family_list[n] = family[k] elif n in partitions_controls: family_list[n] = "CTRL" elif n in partitions_anticontrols: family_list[n] = "OCTRL" else: family_list[n] = "WIRE_QN" if ( n in range(min(partition_occupied), max(partition_occupied) + 1) and n not in partition_occupied ): family_list[n] = "WIRE_QQ" return family_list def assign_connections(systems, targets, controls, anticontrols, boundaries): systems_occupied = list(set(targets + controls + anticontrols)) partitions = partition_systems(systems, boundaries) connections_list = [ {"upper": "none", "lower": "none", "left": "quantum", "right": "quantum"} for k in systems ] for k, partition in enumerate(partitions): partition_occupied = list(set(partition) & set(systems_occupied)) for n in partition: if n in range(min(partition_occupied), max(partition_occupied) + 1): if n != min(partition_occupied): connections_list[n]["upper"] = "quantum" if n != max(partition_occupied): connections_list[n]["lower"] = "quantum" return connections_list
[docs] class VisualizationMixin: """A mixin for endowing classes derived from the base class :py:class:`~qhronology.utilities.objects.QuantumObject` the ability to be visualized as quantum circuit diagram elements.""" def _diagram_column( self, pad: tuple[int, int] | None = None, sep: tuple[int, int] | None = None, style: str | None = None, ) -> DiagramColumn: label_list = [None for k in self.systems] sep_list = [sep for k in self.systems] style_list = [style for k in self.systems] family = flatten_list([self.family]) labels = flatten_list([self.labels]) family = flatten_list( [ x for _, x in sorted( zip(self.boundaries, family), key=lambda pair: pair[0] ) ] ) labels = flatten_list( [ x for _, x in sorted( zip(self.boundaries, labels), key=lambda pair: pair[0] ) ] ) family_list = assign_family( family=family, systems=self.systems, targets=self.targets, controls=self.controls, anticontrols=self.anticontrols, boundaries=self.boundaries, ) connections_list = assign_connections( systems=self.systems, targets=self.targets, controls=self.controls, anticontrols=self.anticontrols, boundaries=self.boundaries, ) for k, connections in enumerate(connections_list): if "METER" in family and k in self.targets: connections_list[k]["right"] = "classical" partitions = partition_systems(self.systems, self.boundaries) for k, partition in enumerate(partitions): partition_targets = list(set(partition) & set(self.targets)) partition_target_middle_index = math.floor((len(partition_targets) - 1) / 2) label_list[partition_targets[partition_target_middle_index]] = labels[k] cells_list = [] for k in self.systems: cells_list.append( DiagramCell( family=family_list[k], connections=connections_list[k], label=label_list[k], quantikz_arguments={"compulsory": "TODO", "optional": "TODO"}, pad=pad, sep=sep_list[k], dimensions=None, style=style_list[k], ) ) return DiagramColumn(cells=cells_list)
[docs] def diagram( self, pad: tuple[int, int] | None = None, sep: tuple[int, int] | None = None, style: str | None = None, return_string: bool | None = None, ) -> None | str: """Print or return a circuit diagram of the quantum object as a multiline string. Arguments --------- pad : tuple[int, int] A two-tuple of non-negative integers specifying intra-gate padding (i.e., the horizontal and vertical interior paddings between the content at the centre of each gate (e.g., label) and its outer edge (e.g., block border)). Defaults to :python:`(1, 0)`. sep : tuple[int, int] A two-tuple of non-negative integers specifying inter-gate separation (i.e., the horizontal and vertical exterior separation distances between the edges of neighbouring gates). Defaults to :python:`(1, 1)`. style : str A string specifying the style for the circuit visualization to take. Can be any of :python:`"ascii"`, :python:`"unicode"`, or :python:`"unicode_alt"`. Defaults to :python:`"unicode"`. return_string : bool Whether to return the assembled diagram as a multiline string. Defaults to :python:`False`. Returns ------- None Returned if :python:`return_string` is :python:`False`. str The rendered circuit diagram. Returned if :python:`return_string` is :python:`True`. Note ---- The quality of the visualization depends greatly on the output's configuration. For best results, the terminal should have a monospace font with good Unicode coverage. """ pad = (1, 0) if pad is None else pad sep = (1, 1) if sep is None else sep style = Styles.UNICODE.value if style is None else style uniform_spacing = False force_separation = True return_string = False if return_string is None else return_string section = Sections.GATES.value if "STICK" in self.family: section = Sections.INPUTS.value pad_sections = {Sections.INPUTS.value: (2, 0), Sections.GATES.value: (0, 0)} cells = [*self._diagram_column(pad=pad, sep=sep, style=style).cells] column = DiagramColumn( cells=flatten_list(cells), pad=pad_sections[section], section=section ) grid = DiagramCircuit(columns=flatten_list([column])) grid.diagram( pad=pad, sep=sep, style=style, uniform_spacing=uniform_spacing, force_separation=force_separation, return_string=return_string, )