# 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,
)