Coverage for src/braket/circuits/ascii_circuit_diagram.py : 100%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Copyright 2019-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"). You
4# may not use this file except in compliance with the License. A copy of
5# the License is located at
6#
7# http://aws.amazon.com/apache2.0/
8#
9# or in the "license" file accompanying this file. This file is
10# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11# ANY KIND, either express or implied. See the License for the specific
12# language governing permissions and limitations under the License.
14from typing import List, Tuple, Union
16from braket.circuits.circuit_diagram import CircuitDiagram
17from braket.circuits.gate import Gate
18from braket.circuits.instruction import Instruction
19from braket.circuits.qubit_set import QubitSet
20from braket.circuits.result_type import ResultType
23class AsciiCircuitDiagram(CircuitDiagram):
24 """Builds ASCII string circuit diagrams."""
26 @staticmethod
27 def build_diagram(circuit) -> str:
28 """
29 Build an ASCII string circuit diagram.
31 Args:
32 circuit (Circuit): Circuit for which to build a diagram.
34 Returns:
35 str: ASCII string circuit diagram.
36 """
38 if not circuit.instructions:
39 return ""
41 circuit_qubits = circuit.qubits
42 circuit_qubits.sort()
44 # Y Axis Column
45 y_axis_width = len(str(int(max(circuit_qubits))))
46 y_axis_str = "{0:{width}} : |\n".format("T", width=y_axis_width + 1)
47 for qubit in circuit_qubits:
48 y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5)
49 y_axis_str += "q{0:{width}} : -\n".format(str(int(qubit)), width=y_axis_width)
51 time_slices = circuit.moments.time_slices()
52 column_strs = []
54 # Moment columns
55 for time, instructions in time_slices.items():
56 moment_str = AsciiCircuitDiagram._ascii_diagram_column_set(
57 str(time), circuit_qubits, instructions
58 )
59 column_strs.append(moment_str)
61 # Result type columns
62 additional_result_types, target_result_types = AsciiCircuitDiagram._categorize_result_types(
63 circuit.result_types
64 )
65 if target_result_types:
66 column_strs.append(
67 AsciiCircuitDiagram._ascii_diagram_column_set(
68 "Result Types", circuit_qubits, target_result_types
69 )
70 )
72 # Unite strings
73 lines = y_axis_str.split("\n")
74 for col_str in column_strs:
75 for i, line_in_col in enumerate(col_str.split("\n")):
76 lines[i] += line_in_col
78 # Time on top and bottom
79 lines.append(lines[0])
81 # Additional result types line on bottom
82 if additional_result_types:
83 lines.append(f"\nAdditional result types: {', '.join(additional_result_types)}")
85 return "\n".join(lines)
87 @staticmethod
88 def _ascii_group_items(
89 circuit_qubits: QubitSet, items: List[Union[Instruction, ResultType]],
90 ) -> List[Tuple[QubitSet, List[Instruction]]]:
91 """
92 Group instructions in a moment for ASCII diagram
94 Args:
95 circuit_qubits (QubitSet): set of qubits in circuit
96 items (List[Union[Instruction, ResultType]]): list of instructions or result types
98 Returns:
99 List[(QubitSet, List[Union[Instruction, ResultType]])]: list of grouped instructions
100 or result types
101 """
102 groupings = []
103 for item in items:
104 # Can only print Gate operators for instructions at the moment
105 if isinstance(item, Instruction) and not isinstance(item.operator, Gate):
106 continue
108 if isinstance(item, ResultType) and not item.target:
109 qubit_range = circuit_qubits
110 else:
111 qubit_range = QubitSet(range(min(item.target), max(item.target) + 1))
113 found_grouping = False
114 for group in groupings:
115 qubits_added = group[0]
116 instr_group = group[1]
117 # Take into account overlapping multi-qubit gates
118 if not qubits_added.intersection(set(qubit_range)):
119 instr_group.append(item)
120 qubits_added.update(qubit_range)
121 found_grouping = True
122 break
124 if not found_grouping:
125 groupings.append((qubit_range, [item]))
127 return groupings
129 @staticmethod
130 def _categorize_result_types(result_types: List[ResultType]) -> Tuple[List[ResultType]]:
131 """
132 Categorize result types into result types with target and those without.
134 Args:
135 result_types (List[ResultType]): list of result types
137 Returns:
138 Tuple: first element is a list of result types without `target` attribute;
139 second element is a list of result types with `target` attribute
140 """
141 additional_result_types = []
142 target_result_types = []
143 for result_type in result_types:
144 if hasattr(result_type, "target"):
145 target_result_types.append(result_type)
146 else:
147 additional_result_types.extend(result_type.ascii_symbols)
148 return (additional_result_types, target_result_types)
150 @staticmethod
151 def _ascii_diagram_column_set(
152 col_title: str, circuit_qubits: QubitSet, items: List[Union[Instruction, ResultType]]
153 ) -> str:
154 """
155 Return a set of columns in the ASCII string diagram of the circuit for a list of items.
157 Args:
158 col_title (str): title of column set
159 circuit_qubits (QubitSet): qubits in circuit
160 items (List[Union[Instruction, ResultType]]): list of instructions or result types
162 Returns:
163 str: An ASCII string diagram for the column set.
164 """
166 # Group items to separate out overlapping multi-qubit items
167 groupings = AsciiCircuitDiagram._ascii_group_items(circuit_qubits, items)
169 column_strs = [
170 AsciiCircuitDiagram._ascii_diagram_column(circuit_qubits, grouping[1])
171 for grouping in groupings
172 ]
174 # Unite column strings
175 lines = column_strs[0].split("\n")
176 for column_str in column_strs[1:]:
177 for i, moment_line in enumerate(column_str.split("\n")):
178 lines[i] += moment_line
180 # Adjust for column title width
181 col_title_width = len(col_title)
182 symbols_width = len(lines[0]) - 1
183 if symbols_width < col_title_width:
184 diff = col_title_width - symbols_width
185 for i in range(len(lines) - 1):
186 if lines[i].endswith("-"):
187 lines[i] += "-" * diff
188 else:
189 lines[i] += " "
191 first_line = "{:^{width}}|\n".format(col_title, width=len(lines[0]) - 1)
193 return first_line + "\n".join(lines)
195 @staticmethod
196 def _ascii_diagram_column(
197 circuit_qubits: QubitSet, items: List[Union[Instruction, ResultType]]
198 ) -> str:
199 """
200 Return a column in the ASCII string diagram of the circuit for a given list of items.
202 Args:
203 circuit_qubits (QubitSet): qubits in circuit
204 items (List[Union[Instruction, ResultType]]): list of instructions or result types
206 Returns:
207 str: An ASCII string diagram for the specified moment in time for a column.
208 """
209 symbols = {qubit: "-" for qubit in circuit_qubits}
210 margins = {qubit: " " for qubit in circuit_qubits}
212 for item in items:
213 if isinstance(item, ResultType) and not item.target:
214 target_qubits = circuit_qubits
215 qubits = circuit_qubits
216 ascii_symbols = [item.ascii_symbols[0]] * len(circuit_qubits)
217 else:
218 target_qubits = item.target
219 qubits = circuit_qubits.intersection(
220 set(range(min(item.target), max(item.target) + 1))
221 )
222 ascii_symbols = (
223 item.operator.ascii_symbols
224 if isinstance(item, Instruction)
225 else item.ascii_symbols
226 )
228 for qubit in qubits:
229 # Determine if the qubit is part of the item or in the middle of a
230 # multi qubit item.
231 if qubit in target_qubits:
232 item_qubit_index = [
233 index for index, q in enumerate(target_qubits) if q == qubit
234 ][0]
235 symbols[qubit] = ascii_symbols[item_qubit_index]
236 else:
237 symbols[qubit] = "|"
239 # Set the margin to be a connector if not on the first qubit
240 if qubit != min(target_qubits):
241 margins[qubit] = "|"
243 symbols_width = max([len(symbol) for symbol in symbols.values()])
245 output = ""
246 for qubit in circuit_qubits:
247 output += "{0:{width}}\n".format(margins[qubit], width=symbols_width + 1)
248 output += "{0:{fill}{align}{width}}\n".format(
249 symbols[qubit], fill="-", align="<", width=symbols_width + 1
250 )
251 return output