Source code for qhronology.utilities.diagrams

# Project: Qhronology (https://github.com/lgbishop/qhronology)
# Author: lgbishop <lachlanbishop@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.com.

"""
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", ) # "STYLES": assign 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 = "unicode" 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"}) is True: label = label + styles[self.family]["label_connector"][style] if any(family in self.family for family in {"RSTICK"}) is True: 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"} ) is True ): 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 ``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 ``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 ( any( family in family_wide_sticks for family in column_family_current ) is False ): trim_right += 3 if self.columns[index_column].section == Sections.OUTPUTS.value: if ( any( family in family_wide_sticks for family in column_family_current ) is False ): 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) # ``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 ( any( family in family_wide_gates for family in column_family_current ) is False ): trim_left += max(pad_cell_original - 2, 0) else: if ( self.columns[index_column].section == Sections.GATES.value and any( family in family_wide_gates for family in column_family_current ) is False ): 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 ( any( family in family_wide_gates for family in column_family_current ) is False and any( family in family_wide_gates for family in column_family_next ) is False ): trim_right += math.floor(pad_cell[0] / 2) if ( any( family in family_wide_gates for family in column_family_previous ) is False and any( family in family_wide_gates for family in column_family_current ) is False ): # +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 = list(set(systems)) boundaries = 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 describing the horizontal and vertical interior paddings between the content at the centre of the object (e.g., its label) and its outer edge (e.g., block border). Both integers must be non-negative. Defaults to ``(0, 0)``. sep : tuple[int, int] A two-tuple describing the horizontal and vertical exterior separation distances at the object's edges. Both integers must be non-negative. Defaults to ``(1, 1)``. style : str A string specifying the style for the circuit visualization to take. Can be any of ``"ascii"``, ``"unicode"``, or ``"unicode_alt"``. Defaults to ``"unicode"``. return_string : bool Whether to return the assembled diagram as a multiline string. Defaults to ``False``. Returns ------- None Returned only if ``return_string`` is ``False``. str The rendered circuit diagram. Returned only if ``return_string`` is ``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 = "unicode" if style is None else style return_string = False if return_string is None else return_string uniform_spacing = False force_separation = True 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, )