Source code for braket.circuits.text_diagram_builders.ascii_circuit_diagram

# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
#     http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.

from __future__ import annotations

from functools import reduce
from typing import Literal, Union

import braket.circuits.circuit as cir
from braket.circuits.compiler_directive import CompilerDirective
from braket.circuits.gate import Gate
from braket.circuits.instruction import Instruction
from braket.circuits.result_type import ResultType
from braket.circuits.text_diagram_builders.text_circuit_diagram import TextCircuitDiagram
from braket.registers.qubit_set import QubitSet


[docs] class AsciiCircuitDiagram(TextCircuitDiagram): """Builds ASCII string circuit diagrams."""
[docs] @staticmethod def build_diagram(circuit: cir.Circuit) -> str: """Build a text circuit diagram. Args: circuit (Circuit): Circuit for which to build a diagram. Returns: str: string circuit diagram. """ return AsciiCircuitDiagram._build(circuit)
@classmethod def _vertical_delimiter(cls) -> str: """Character that connects qubits of multi-qubit gates.""" return "|" @classmethod def _qubit_line_character(cls) -> str: """Character used for the qubit line.""" return "-" @classmethod def _box_pad(cls) -> int: """number of blank space characters around the gate name.""" return 0 @classmethod def _qubit_line_spacing_above(cls) -> int: """number of empty lines above the qubit line.""" return 1 @classmethod def _qubit_line_spacing_below(cls) -> int: """number of empty lines below the qubit line.""" return 0 @classmethod def _duplicate_time_at_bottom(cls, lines: str) -> None: # duplicate times after an empty line lines.append(lines[0]) @classmethod def _create_diagram_column( cls, circuit_qubits: QubitSet, items: list[Union[Instruction, ResultType]], global_phase: float | None = None, ) -> str: """Return a column in the ASCII string diagram of the circuit for a given list of items. Args: circuit_qubits (QubitSet): qubits in circuit items (list[Union[Instruction, ResultType]]): list of instructions or result types global_phase (float | None): the integrated global phase up to this column Returns: str: an ASCII string diagram for the specified moment in time for a column. """ symbols = {qubit: cls._qubit_line_character() for qubit in circuit_qubits} connections = {qubit: "none" for qubit in circuit_qubits} for item in items: if isinstance(item, ResultType) and not item.target: target_qubits = circuit_qubits control_qubits = QubitSet() target_and_control = target_qubits.union(control_qubits) qubits = circuit_qubits ascii_symbols = [item.ascii_symbols[0]] * len(circuit_qubits) elif isinstance(item, Instruction) and isinstance(item.operator, CompilerDirective): target_qubits = circuit_qubits control_qubits = QubitSet() target_and_control = target_qubits.union(control_qubits) qubits = circuit_qubits ascii_symbol = item.ascii_symbols[0] marker = "*" * len(ascii_symbol) num_after = len(circuit_qubits) - 1 after = ["|"] * (num_after - 1) + ([marker] if num_after else []) ascii_symbols = [ascii_symbol, *after] elif ( isinstance(item, Instruction) and isinstance(item.operator, Gate) and item.operator.name == "GPhase" ): target_qubits = circuit_qubits control_qubits = QubitSet() target_and_control = QubitSet() qubits = circuit_qubits ascii_symbols = cls._qubit_line_character() * len(circuit_qubits) else: if isinstance(item.target, list): target_qubits = reduce(QubitSet.union, map(QubitSet, item.target), QubitSet()) else: target_qubits = item.target control_qubits = getattr(item, "control", QubitSet()) control_state = getattr(item, "control_state", "1" * len(control_qubits)) map_control_qubit_states = dict(zip(control_qubits, control_state)) target_and_control = target_qubits.union(control_qubits) qubits = QubitSet(range(min(target_and_control), max(target_and_control) + 1)) ascii_symbols = item.ascii_symbols for qubit in qubits: # Determine if the qubit is part of the item or in the middle of a # multi qubit item. if qubit in target_qubits: item_qubit_index = [ index for index, q in enumerate(target_qubits) if q == qubit ][0] power_string = ( f"^{power}" if ( (power := getattr(item, "power", 1)) != 1 # this has the limitation of not printing the power # when a user has a gate genuinely named C, but # is necessary to enable proper printing of custom # gates with built-in control qubits and ascii_symbols[item_qubit_index] != "C" ) else "" ) symbols[qubit] = ( f"({ascii_symbols[item_qubit_index]}{power_string})" if power_string else ascii_symbols[item_qubit_index] ) elif qubit in control_qubits: symbols[qubit] = "C" if map_control_qubit_states[qubit] else "N" else: symbols[qubit] = "|" # Set the margin to be a connector if not on the first qubit if target_and_control and qubit != min(target_and_control): connections[qubit] = "above" output = cls._create_output(symbols, connections, circuit_qubits, global_phase) return output # Ignore flake8 issue caused by Literal["above", "below", "both", "none"] # flake8: noqa: BCS005 @classmethod def _draw_symbol( cls, symbol: str, symbols_width: int, connection: Literal["above", "below", "both", "none"] ) -> str: """Create a string representing the symbol. Args: symbol (str): the gate name symbols_width (int): size of the expected output. The output will be filled with cls._qubit_line_character() if needed. connection (Literal["above", "below", "both", "none"]): character indicating if the gate also involve a qubit with a lower index. Returns: str: a string representing the symbol. """ connection_char = cls._vertical_delimiter() if connection in ["above"] else " " output = "{0:{width}}\n".format( connection_char, width=symbols_width + 1 ) + "{0:{fill}{align}{width}}\n".format( symbol, fill=cls._qubit_line_character(), align="<", width=symbols_width + 1, ) return output