From 22041b70aa2cfdcf311c98f6ed8392136ccbf32c Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 1 Dec 2023 10:36:28 +0100 Subject: [PATCH 01/62] replace control symbols --- src/braket/circuits/angled_gate.py | 6 +- src/braket/circuits/ascii_circuit_diagram.py | 4 +- src/braket/circuits/gate.py | 2 +- src/braket/circuits/gates.py | 20 ++-- src/braket/circuits/quantum_operator.py | 2 +- .../circuits/test_ascii_circuit_diagram.py | 96 +++++++++---------- 6 files changed, 65 insertions(+), 65 deletions(-) diff --git a/src/braket/circuits/angled_gate.py b/src/braket/circuits/angled_gate.py index e453177a7..bf2adc4bc 100644 --- a/src/braket/circuits/angled_gate.py +++ b/src/braket/circuits/angled_gate.py @@ -46,7 +46,7 @@ def __init__( printing a diagram of a circuit. The length must be the same as `qubit_count`, and index ordering is expected to correlate with the target ordering on the instruction. For instance, if a CNOT instruction has the control qubit on the first index and - target qubit on the second index, the ASCII symbols should have `["C", "X"]` to + target qubit on the second index, the ASCII symbols should have `["⏺", "X"]` to correlate a symbol with that index. Raises: @@ -147,7 +147,7 @@ def __init__( printing a diagram of a circuit. The length must be the same as `qubit_count`, and index ordering is expected to correlate with the target ordering on the instruction. For instance, if a CNOT instruction has the control qubit on the first index and - target qubit on the second index, the ASCII symbols should have `["C", "X"]` to + target qubit on the second index, the ASCII symbols should have `["⏺", "X"]` to correlate a symbol with that index. Raises: @@ -265,7 +265,7 @@ def __init__( printing a diagram of a circuit. The length must be the same as `qubit_count`, and index ordering is expected to correlate with the target ordering on the instruction. For instance, if a CNOT instruction has the control qubit on the first index and - target qubit on the second index, the ASCII symbols should have `["C", "X"]` to + target qubit on the second index, the ASCII symbols should have `["⏺", "X"]` to correlate a symbol with that index. Raises: diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 2c7024574..2edf7840c 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -358,7 +358,7 @@ def _ascii_diagram_column( # 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" + and ascii_symbols[item_qubit_index] != "⏺" ) else "" ) @@ -368,7 +368,7 @@ def _ascii_diagram_column( else ascii_symbols[item_qubit_index] ) elif qubit in control_qubits: - symbols[qubit] = "C" if map_control_qubit_states[qubit] else "N" + symbols[qubit] = "⏺" if map_control_qubit_states[qubit] else "○" else: symbols[qubit] = "|" diff --git a/src/braket/circuits/gate.py b/src/braket/circuits/gate.py index 2183a2329..5f5f4d609 100644 --- a/src/braket/circuits/gate.py +++ b/src/braket/circuits/gate.py @@ -42,7 +42,7 @@ def __init__(self, qubit_count: Optional[int], ascii_symbols: Sequence[str]): printing a diagram of circuits. Length must be the same as `qubit_count`, and index ordering is expected to correlate with target ordering on the instruction. For instance, if CNOT instruction has the control qubit on the first index and - target qubit on the second index. Then ASCII symbols would have ["C", "X"] to + target qubit on the second index. Then ASCII symbols would have ["⏺", "X"] to correlate a symbol with that index. Raises: diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index e955c4bb0..2356a75da 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -1539,7 +1539,7 @@ class CNot(Gate): """ def __init__(self): - super().__init__(qubit_count=None, ascii_symbols=["C", "X"]) + super().__init__(qubit_count=None, ascii_symbols=["⏺", "X"]) @property def _qasm_name(self) -> str: @@ -2023,7 +2023,7 @@ def __init__(self, angle: Union[FreeParameterExpression, float]): super().__init__( angle=angle, qubit_count=None, - ascii_symbols=["C", angled_ascii_characters("PHASE", angle)], + ascii_symbols=["⏺", angled_ascii_characters("PHASE", angle)], ) @property @@ -2108,7 +2108,7 @@ def __init__(self, angle: Union[FreeParameterExpression, float]): super().__init__( angle=angle, qubit_count=None, - ascii_symbols=["C", angled_ascii_characters("PHASE00", angle)], + ascii_symbols=["⏺", angled_ascii_characters("PHASE00", angle)], ) @property @@ -2193,7 +2193,7 @@ def __init__(self, angle: Union[FreeParameterExpression, float]): super().__init__( angle=angle, qubit_count=None, - ascii_symbols=["C", angled_ascii_characters("PHASE01", angle)], + ascii_symbols=["⏺", angled_ascii_characters("PHASE01", angle)], ) @property @@ -2278,7 +2278,7 @@ def __init__(self, angle: Union[FreeParameterExpression, float]): super().__init__( angle=angle, qubit_count=None, - ascii_symbols=["C", angled_ascii_characters("PHASE10", angle)], + ascii_symbols=["⏺", angled_ascii_characters("PHASE10", angle)], ) @property @@ -2357,7 +2357,7 @@ class CV(Gate): """ def __init__(self): - super().__init__(qubit_count=None, ascii_symbols=["C", "V"]) + super().__init__(qubit_count=None, ascii_symbols=["⏺", "V"]) @property def _qasm_name(self) -> str: @@ -2434,7 +2434,7 @@ class CY(Gate): """ def __init__(self): - super().__init__(qubit_count=None, ascii_symbols=["C", "Y"]) + super().__init__(qubit_count=None, ascii_symbols=["⏺", "Y"]) @property def _qasm_name(self) -> str: @@ -2511,7 +2511,7 @@ class CZ(Gate): """ def __init__(self): - super().__init__(qubit_count=None, ascii_symbols=["C", "Z"]) + super().__init__(qubit_count=None, ascii_symbols=["⏺", "Z"]) @property def _qasm_name(self) -> str: @@ -3008,7 +3008,7 @@ class CCNot(Gate): """ def __init__(self): - super().__init__(qubit_count=None, ascii_symbols=["C", "C", "X"]) + super().__init__(qubit_count=None, ascii_symbols=["⏺", "⏺", "X"]) @property def _qasm_name(self) -> str: @@ -3116,7 +3116,7 @@ class CSwap(Gate): """ def __init__(self): - super().__init__(qubit_count=None, ascii_symbols=["C", "SWAP", "SWAP"]) + super().__init__(qubit_count=None, ascii_symbols=["⏺", "SWAP", "SWAP"]) @property def _qasm_name(self) -> str: diff --git a/src/braket/circuits/quantum_operator.py b/src/braket/circuits/quantum_operator.py index df67bf199..878bd093d 100644 --- a/src/braket/circuits/quantum_operator.py +++ b/src/braket/circuits/quantum_operator.py @@ -39,7 +39,7 @@ def __init__(self, qubit_count: Optional[int], ascii_symbols: Sequence[str]): Length must be the same as `qubit_count`, and index ordering is expected to correlate with target ordering on the instruction. For instance, if CNOT instruction has the control qubit on the first index and - target qubit on the second index. Then ASCII symbols would have ["C", "X"] to + target qubit on the second index. Then ASCII symbols would have ["⏺", "X"] to correlate a symbol with that index. Raises: diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py index 916bfb050..c9ee0fba3 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -186,33 +186,33 @@ def test_time_width(): expected = ( "T : |0|1|2|3|4|5|6|7|8|9|10|11|12|13|", " ", - "q0 : -C-------------------------------", + "q0 : -⏺-------------------------------", " | ", - "q1 : -X-C-----------------------------", + "q1 : -X-⏺-----------------------------", " | ", - "q2 : ---X-C---------------------------", + "q2 : ---X-⏺---------------------------", " | ", - "q3 : -----X-C-------------------------", + "q3 : -----X-⏺-------------------------", " | ", - "q4 : -------X-C-----------------------", + "q4 : -------X-⏺-----------------------", " | ", - "q5 : ---------X-C---------------------", + "q5 : ---------X-⏺---------------------", " | ", - "q6 : -----------X-C-------------------", + "q6 : -----------X-⏺-------------------", " | ", - "q7 : -------------X-C-----------------", + "q7 : -------------X-⏺-----------------", " | ", - "q8 : ---------------X-C---------------", + "q8 : ---------------X-⏺---------------", " | ", - "q9 : -----------------X-C-------------", + "q9 : -----------------X-⏺-------------", " | ", - "q10 : -------------------X-C-----------", + "q10 : -------------------X-⏺-----------", " | ", - "q11 : ---------------------X--C--------", + "q11 : ---------------------X--⏺--------", " | ", - "q12 : ------------------------X--C-----", + "q12 : ------------------------X--⏺-----", " | ", - "q13 : ---------------------------X--C--", + "q13 : ---------------------------X--⏺--", " | ", "q14 : ------------------------------X--", "", @@ -228,7 +228,7 @@ def test_connector_across_two_qubits(): " ", "q2 : -H---", " ", - "q3 : -C-H-", + "q3 : -⏺-H-", " | ", "q4 : -X-H-", " ", @@ -244,9 +244,9 @@ def test_neg_control_qubits(): expected = ( "T : |0|", " ", - "q0 : -N-", + "q0 : -○-", " | ", - "q1 : -C-", + "q1 : -⏺-", " | ", "q2 : -X-", "", @@ -278,9 +278,9 @@ def test_connector_across_three_qubits(): " ", "q2 : -H---", " ", - "q3 : -C-H-", + "q3 : -⏺-H-", " | ", - "q4 : -C-H-", + "q4 : -⏺-H-", " | ", "q5 : -X-H-", "", @@ -294,9 +294,9 @@ def test_overlapping_qubits(): expected = ( "T : | 0 |1|", " ", - "q0 : -C---H-", + "q0 : -⏺---H-", " | ", - "q1 : -|-C---", + "q1 : -|-⏺---", " | | ", "q2 : -X-|---", " | ", @@ -314,7 +314,7 @@ def test_overlapping_qubits_angled_gates(): " ", "q0 : -ZZ(0.15)---H-", " | ", - "q1 : -|--------C---", + "q1 : -|--------⏺---", " | | ", "q2 : -ZZ(0.15)-|---", " | ", @@ -332,7 +332,7 @@ def test_connector_across_gt_two_qubits(): " ", "q2 : -H-----", " ", - "q3 : ---C---", + "q3 : ---⏺---", " | ", "q4 : -H-|-H-", " | ", @@ -348,7 +348,7 @@ def test_connector_across_non_used_qubits(): expected = ( "T : | 0 |1|", " ", - "q3 : ---C---", + "q3 : ---⏺---", " | ", "q4 : -H-|-H-", " | ", @@ -402,7 +402,7 @@ def test_verbatim_2q_no_preceding(): expected = ( "T : | 0 |1|2| 3 |", " ", - "q0 : -StartVerbatim-H-C-EndVerbatim-", + "q0 : -StartVerbatim-H-⏺-EndVerbatim-", " | | | ", "q1 : -*************---X-***********-", "", @@ -416,7 +416,7 @@ def test_verbatim_2q_preceding(): expected = ( "T : |0| 1 |2|3| 4 |", " ", - "q0 : -H-StartVerbatim-H-C-EndVerbatim-", + "q0 : -H-StartVerbatim-H-⏺-EndVerbatim-", " | | | ", "q1 : ---*************---X-***********-", "", @@ -430,7 +430,7 @@ def test_verbatim_2q_following(): expected = ( "T : | 0 |1|2| 3 |4|", " ", - "q0 : -StartVerbatim-H-C-EndVerbatim-H-", + "q0 : -StartVerbatim-H-⏺-EndVerbatim-H-", " | | | ", "q1 : -*************---X-***********---", "", @@ -444,9 +444,9 @@ def test_verbatim_3q_no_preceding(): expected = ( "T : | 0 |1|2|3| 4 |", " ", - "q0 : -StartVerbatim-H-C---EndVerbatim-", + "q0 : -StartVerbatim-H-⏺---EndVerbatim-", " | | | ", - "q1 : -|---------------X-C-|-----------", + "q1 : -|---------------X-⏺-|-----------", " | | | ", "q2 : -*************-----X-***********-", "", @@ -460,9 +460,9 @@ def test_verbatim_3q_preceding(): expected = ( "T : |0| 1 |2|3|4| 5 |", " ", - "q0 : -H-StartVerbatim-H-C---EndVerbatim-", + "q0 : -H-StartVerbatim-H-⏺---EndVerbatim-", " | | | ", - "q1 : ---|---------------X-C-|-----------", + "q1 : ---|---------------X-⏺-|-----------", " | | | ", "q2 : ---*************-----X-***********-", "", @@ -476,9 +476,9 @@ def test_verbatim_3q_following(): expected = ( "T : | 0 |1|2|3| 4 |5|", " ", - "q0 : -StartVerbatim-H-C---EndVerbatim-H-", + "q0 : -StartVerbatim-H-⏺---EndVerbatim-H-", " | | | ", - "q1 : -|---------------X-C-|-------------", + "q1 : -|---------------X-⏺-|-------------", " | | | ", "q2 : -*************-----X-***********---", "", @@ -496,7 +496,7 @@ def test_verbatim_different_qubits(): " | | ", "q1 : -H-|---------------|-------------", " | | ", - "q3 : ---|---------------|-----------C-", + "q3 : ---|---------------|-----------⏺-", " | | | ", "q4 : ---*************---***********-X-", "", @@ -510,11 +510,11 @@ def test_verbatim_qubset_qubits(): expected = ( "T : |0|1|2| 3 |4| 5 |6|", " ", - "q0 : ---C---StartVerbatim---EndVerbatim---", + "q0 : ---⏺---StartVerbatim---EndVerbatim---", " | | | ", - "q1 : -H-X-C-|-------------H-|-------------", + "q1 : -H-X-⏺-|-------------H-|-------------", " | | | ", - "q2 : -----X-|---------------|-----------C-", + "q2 : -----X-|---------------|-----------⏺-", " | | | ", "q3 : -------*************---***********-X-", "", @@ -538,7 +538,7 @@ def to_ir(self, target): " ", "q0 : -H---", " ", - "q1 : -H-C-", + "q1 : -H-⏺-", " | ", "q2 : ---X-", "", @@ -614,9 +614,9 @@ def test_multiple_result_types(): expected = ( "T : | 0 |1| Result Types |", " ", - "q0 : -C---H-Variance(Y)----Sample(Y)-", + "q0 : -⏺---H-Variance(Y)----Sample(Y)-", " | | ", - "q1 : -|-C------------------Sample(Y)-", + "q1 : -|-⏺------------------Sample(Y)-", " | | | ", "q2 : -X-|---Expectation(Y)-Sample(Y)-", " | | ", @@ -642,9 +642,9 @@ def test_multiple_result_types_with_state_vector_amplitude(): expected = ( "T : | 0 |1| Result Types |", " ", - "q0 : -C---H-Variance(Y)------------", + "q0 : -⏺---H-Variance(Y)------------", " | ", - "q1 : -|-C---Expectation(Hermitian)-", + "q1 : -|-⏺---Expectation(Hermitian)-", " | | ", "q2 : -X-|--------------------------", " | ", @@ -677,9 +677,9 @@ def test_multiple_result_types_with_custom_hermitian_ascii_symbol(): expected = ( "T : | 0 |1| Result Types |", " ", - "q0 : -C---H-Variance(Y)---------", + "q0 : -⏺---H-Variance(Y)---------", " | ", - "q1 : -|-C---Expectation(MyHerm)-", + "q1 : -|-⏺---Expectation(MyHerm)-", " | | | ", "q2 : -X-|---Expectation(MyHerm)-", " | ", @@ -829,7 +829,7 @@ def test_hamiltonian(): expected = ( "T : |0|1| 2 | Result Types |", " ", - "q0 : -H-C-Rx(theta)-AdjointGradient(Hamiltonian)-", + "q0 : -H-⏺-Rx(theta)-AdjointGradient(Hamiltonian)-", " | | ", "q1 : ---X-----------AdjointGradient(Hamiltonian)-", " | ", @@ -849,7 +849,7 @@ def __init__(self): class CFoo(Gate): def __init__(self): - super().__init__(qubit_count=2, ascii_symbols=["C", "FOO"]) + super().__init__(qubit_count=2, ascii_symbols=["⏺", "FOO"]) class FooFoo(Gate): def __init__(self): @@ -863,9 +863,9 @@ def __init__(self): expected = ( "T : | 0 | 1 | 2 | 3 | 4 |", " ", - "q0 : -H---------(FOO^-1)-C-------C-------C-------", + "q0 : -H---------(FOO^-1)-⏺-------⏺-------⏺-------", " | | | ", - "q1 : -(H^0)--------------(FOO^2)-C-------(FOO^4)-", + "q1 : -(H^0)--------------(FOO^2)-⏺-------(FOO^4)-", " | | ", "q2 : -(H^-3.14)------------------(FOO^3)-(FOO^4)-", "", From 57ad5f912652d159bef8a895df62998cd4dd9952 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 1 Dec 2023 00:33:36 +0100 Subject: [PATCH 02/62] use box drawing characters --- src/braket/circuits/ascii_circuit_diagram.py | 26 +- .../circuits/test_ascii_circuit_diagram.py | 586 +++++++++--------- 2 files changed, 314 insertions(+), 298 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 2edf7840c..2f71026c3 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -111,16 +111,16 @@ def _prepare_diagram_vars( ) -> tuple[str, float | None]: # Y Axis Column y_axis_width = len(str(int(max(circuit_qubits)))) - y_axis_str = "{0:{width}} : |\n".format("T", width=y_axis_width + 1) + y_axis_str = "{0:{width}} : │\n".format("T", width=y_axis_width + 1) global_phase = None if any(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): - y_axis_str += "{0:{width}} : |\n".format("GP", width=y_axis_width) + y_axis_str += "{0:{width}} : │\n".format("GP", width=y_axis_width) global_phase = 0 for qubit in circuit_qubits: y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) - y_axis_str += "q{0:{width}} : -\n".format(str(int(qubit)), width=y_axis_width) + y_axis_str += "q{0:{width}} : ─\n".format(str(int(qubit)), width=y_axis_width) return y_axis_str, global_phase @@ -272,12 +272,12 @@ def _ascii_diagram_column_set( if symbols_width < col_title_width: diff = col_title_width - symbols_width for i in range(len(lines) - 1): - if lines[i].endswith("-"): - lines[i] += "-" * diff + if lines[i].endswith("─"): + lines[i] += "─" * diff else: lines[i] += " " - first_line = "{:^{width}}|\n".format(col_title, width=len(lines[0]) - 1) + first_line = "{:^{width}}│\n".format(col_title, width=len(lines[0]) - 1) return first_line + "\n".join(lines) @@ -298,7 +298,7 @@ def _ascii_diagram_column( Returns: str: an ASCII string diagram for the specified moment in time for a column. """ - symbols = {qubit: "-" for qubit in circuit_qubits} + symbols = {qubit: "─" for qubit in circuit_qubits} margins = {qubit: " " for qubit in circuit_qubits} for item in items: @@ -316,7 +316,7 @@ def _ascii_diagram_column( 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 []) + after = ["┼"] * (num_after - 1) + ([marker] if num_after else []) ascii_symbols = [ascii_symbol] + after elif ( isinstance(item, Instruction) @@ -327,7 +327,7 @@ def _ascii_diagram_column( control_qubits = QubitSet() target_and_control = QubitSet() qubits = circuit_qubits - ascii_symbols = "-" * len(circuit_qubits) + ascii_symbols = "─" * len(circuit_qubits) else: if isinstance(item.target, list): target_qubits = reduce(QubitSet.union, map(QubitSet, item.target), QubitSet()) @@ -370,11 +370,11 @@ def _ascii_diagram_column( elif qubit in control_qubits: symbols[qubit] = "⏺" if map_control_qubit_states[qubit] else "○" else: - symbols[qubit] = "|" + 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): - margins[qubit] = "|" + margins[qubit] = "│" output = AsciiCircuitDiagram._create_output(symbols, margins, circuit_qubits, global_phase) return output @@ -394,7 +394,7 @@ def _create_output( f"{global_phase:.2f}" if isinstance(global_phase, float) else str(global_phase) ) symbols_width = max([symbols_width, len(global_phase_str)]) - output += "{0:{fill}{align}{width}}|\n".format( + output += "{0:{fill}{align}{width}}│\n".format( global_phase_str, fill=" ", align="^", @@ -404,7 +404,7 @@ def _create_output( for qubit in qubits: output += "{0:{width}}\n".format(margins[qubit], width=symbols_width + 1) output += "{0:{fill}{align}{width}}\n".format( - symbols[qubit], fill="-", align="<", width=symbols_width + 1 + symbols[qubit], fill="─", align="<", width=symbols_width + 1 ) return output diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py index c9ee0fba3..667e292c9 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -36,14 +36,14 @@ def test_only_gphase_circuit(): def test_one_gate_one_qubit(): circ = Circuit().h(0) - expected = ("T : |0|", " ", "q0 : -H-", "", "T : |0|") + expected = ("T : │0│", " ", "q0 : ─H─", "", "T : │0│") _assert_correct_diagram(circ, expected) def test_one_gate_one_qubit_rotation(): circ = Circuit().rx(angle=3.14, target=0) # Column formats to length of the gate plus the ascii representation for the angle. - expected = ("T : | 0 |", " ", "q0 : -Rx(3.14)-", "", "T : | 0 |") + expected = ("T : │ 0 │", " ", "q0 : ─Rx(3.14)─", "", "T : │ 0 │") _assert_correct_diagram(circ, expected) @@ -52,11 +52,11 @@ def test_one_gate_one_qubit_rotation_with_parameter(): circ = Circuit().rx(angle=theta, target=0) # Column formats to length of the gate plus the ascii representation for the angle. expected = ( - "T : | 0 |", + "T : │ 0 │", " ", - "q0 : -Rx(theta)-", + "q0 : ─Rx(theta)─", "", - "T : | 0 |", + "T : │ 0 │", "", "Unassigned parameters: [theta].", ) @@ -67,12 +67,12 @@ def test_one_gate_one_qubit_rotation_with_parameter(): def test_one_gate_with_global_phase(target): circ = Circuit().x(target=target).gphase(0.15) expected = ( - "T : |0| 1 |", - "GP : |0|0.15|", + "T : │0│ 1 │", + "GP : │0│0.15│", " ", - f"q{target} : -X------", + f"q{target} : ─X──────", "", - "T : |0| 1 |", + "T : │0│ 1 │", "", "Global phase: 0.15", ) @@ -82,12 +82,12 @@ def test_one_gate_with_global_phase(target): def test_one_gate_with_zero_global_phase(): circ = Circuit().gphase(-0.15).x(target=0).gphase(0.15) expected = ( - "T : | 0 | 1 |", - "GP : |-0.15|0.00|", + "T : │ 0 │ 1 │", + "GP : │-0.15│0.00│", " ", - "q0 : -X----------", + "q0 : ─X──────────", "", - "T : | 0 | 1 |", + "T : │ 0 │ 1 │", ) _assert_correct_diagram(circ, expected) @@ -97,11 +97,11 @@ def test_one_gate_one_qubit_rotation_with_unicode(): circ = Circuit().rx(angle=theta, target=0) # Column formats to length of the gate plus the ascii representation for the angle. expected = ( - "T : | 0 |", + "T : │ 0 │", " ", - "q0 : -Rx(θ)-", + "q0 : ─Rx(θ)─", "", - "T : | 0 |", + "T : │ 0 │", "", "Unassigned parameters: [θ].", ) @@ -112,12 +112,12 @@ def test_one_gate_with_parametric_expression_global_phase_(): theta = FreeParameter("\u03B8") circ = Circuit().x(target=0).gphase(2 * theta).x(0).gphase(1) expected = ( - "T : |0| 1 | 2 |", - "GP : |0|2*θ|2*θ + 1.0|", + "T : │0│ 1 │ 2 │", + "GP : │0│2*θ│2*θ + 1.0│", " ", - "q0 : -X-X-------------", + "q0 : ─X─X─────────────", "", - "T : |0| 1 | 2 |", + "T : │0│ 1 │ 2 │", "", "Global phase: 2*θ + 1.0", "", @@ -132,11 +132,11 @@ def test_one_gate_one_qubit_rotation_with_parameter_assigned(): new_circ = circ.make_bound_circuit({"theta": np.pi}) # Column formats to length of the gate plus the ascii representation for the angle. expected = ( - "T : | 0 |", + "T : │ 0 │", " ", - "q0 : -Rx(3.14)-", + "q0 : ─Rx(3.14)─", "", - "T : | 0 |", + "T : │ 0 │", ) _assert_correct_diagram(new_circ, expected) @@ -144,13 +144,13 @@ def test_one_gate_one_qubit_rotation_with_parameter_assigned(): def test_qubit_width(): circ = Circuit().h(0).h(100) expected = ( - "T : |0|", + "T : │0│", " ", - "q0 : -H-", + "q0 : ─H─", " ", - "q100 : -H-", + "q100 : ─H─", "", - "T : |0|", + "T : │0│", ) _assert_correct_diagram(circ, expected) @@ -165,13 +165,13 @@ def to_ir(self, target): circ = Circuit().h(0).h(1).add_instruction(Instruction(Foo(), 0)) expected = ( - "T : |0| 1 |", + "T : │0│ 1 │", " ", - "q0 : -H-FOO-", + "q0 : ─H─FOO─", " ", - "q1 : -H-----", + "q1 : ─H─────", "", - "T : |0| 1 |", + "T : │0│ 1 │", ) _assert_correct_diagram(circ, expected) @@ -184,39 +184,39 @@ def test_time_width(): break circ.cnot(qubit, qubit + 1) expected = ( - "T : |0|1|2|3|4|5|6|7|8|9|10|11|12|13|", + "T : │0│1│2│3│4│5│6│7│8│9│10│11│12│13│", " ", - "q0 : -⏺-------------------------------", - " | ", - "q1 : -X-⏺-----------------------------", - " | ", - "q2 : ---X-⏺---------------------------", - " | ", - "q3 : -----X-⏺-------------------------", - " | ", - "q4 : -------X-⏺-----------------------", - " | ", - "q5 : ---------X-⏺---------------------", - " | ", - "q6 : -----------X-⏺-------------------", - " | ", - "q7 : -------------X-⏺-----------------", - " | ", - "q8 : ---------------X-⏺---------------", - " | ", - "q9 : -----------------X-⏺-------------", - " | ", - "q10 : -------------------X-⏺-----------", - " | ", - "q11 : ---------------------X--⏺--------", - " | ", - "q12 : ------------------------X--⏺-----", - " | ", - "q13 : ---------------------------X--⏺--", - " | ", - "q14 : ------------------------------X--", - "", - "T : |0|1|2|3|4|5|6|7|8|9|10|11|12|13|", + "q0 : ─⏺───────────────────────────────", + " │ ", + "q1 : ─X─⏺─────────────────────────────", + " │ ", + "q2 : ───X─⏺───────────────────────────", + " │ ", + "q3 : ─────X─⏺─────────────────────────", + " │ ", + "q4 : ───────X─⏺───────────────────────", + " │ ", + "q5 : ─────────X─⏺─────────────────────", + " │ ", + "q6 : ───────────X─⏺───────────────────", + " │ ", + "q7 : ─────────────X─⏺─────────────────", + " │ ", + "q8 : ───────────────X─⏺───────────────", + " │ ", + "q9 : ─────────────────X─⏺─────────────", + " │ ", + "q10 : ───────────────────X─⏺───────────", + " │ ", + "q11 : ─────────────────────X──⏺────────", + " │ ", + "q12 : ────────────────────────X──⏺─────", + " │ ", + "q13 : ───────────────────────────X──⏺──", + " │ ", + "q14 : ──────────────────────────────X──", + "", + "T : │0│1│2│3│4│5│6│7│8│9│10│11│12│13│", ) _assert_correct_diagram(circ, expected) @@ -224,17 +224,17 @@ def test_time_width(): def test_connector_across_two_qubits(): circ = Circuit().cnot(3, 4).h(range(2, 6)) expected = ( - "T : |0|1|", + "T : │0│1│", " ", - "q2 : -H---", + "q2 : ─H───", " ", - "q3 : -⏺-H-", - " | ", - "q4 : -X-H-", + "q3 : ─⏺─H─", + " │ ", + "q4 : ─X─H─", " ", - "q5 : -H---", + "q5 : ─H───", "", - "T : |0|1|", + "T : │0│1│", ) _assert_correct_diagram(circ, expected) @@ -242,15 +242,15 @@ def test_connector_across_two_qubits(): def test_neg_control_qubits(): circ = Circuit().x(2, control=[0, 1], control_state=[0, 1]) expected = ( - "T : |0|", + "T : │0│", " ", - "q0 : -○-", - " | ", - "q1 : -⏺-", - " | ", - "q2 : -X-", + "q0 : ─○─", + " │ ", + "q1 : ─⏺─", + " │ ", + "q2 : ─X─", "", - "T : |0|", + "T : │0│", ) _assert_correct_diagram(circ, expected) @@ -258,15 +258,31 @@ def test_neg_control_qubits(): def test_only_neg_control_qubits(): circ = Circuit().x(2, control=[0, 1], control_state=0) expected = ( - "T : |0|", + "T : │0│", " ", - "q0 : -N-", - " | ", - "q1 : -N-", - " | ", - "q2 : -X-", + "q0 : ─○─", + " │ ", + "q1 : ─⏺─", + " │ ", + "q2 : ─X─", "", - "T : |0|", + "T : │0│", + ) + _assert_correct_diagram(circ, expected) + + +def test_only_neg_control_qubits(): + circ = Circuit().x(2, control=[0, 1], control_state=0) + expected = ( + "T : │0│", + " ", + "q0 : ─○─", + " │ ", + "q1 : ─○─", + " │ ", + "q2 : ─X─", + "", + "T : │0│", ) _assert_correct_diagram(circ, expected) @@ -274,17 +290,17 @@ def test_only_neg_control_qubits(): def test_connector_across_three_qubits(): circ = Circuit().x(control=(3, 4), target=5).h(range(2, 6)) expected = ( - "T : |0|1|", + "T : │0│1│", " ", - "q2 : -H---", + "q2 : ─H───", " ", - "q3 : -⏺-H-", - " | ", - "q4 : -⏺-H-", - " | ", - "q5 : -X-H-", + "q3 : ─⏺─H─", + " │ ", + "q4 : ─⏺─H─", + " │ ", + "q5 : ─X─H─", "", - "T : |0|1|", + "T : │0│1│", ) _assert_correct_diagram(circ, expected) @@ -292,17 +308,17 @@ def test_connector_across_three_qubits(): def test_overlapping_qubits(): circ = Circuit().cnot(0, 2).x(control=1, target=3).h(0) expected = ( - "T : | 0 |1|", + "T : │ 0 │1│", " ", - "q0 : -⏺---H-", - " | ", - "q1 : -|-⏺---", - " | | ", - "q2 : -X-|---", - " | ", - "q3 : ---X---", + "q0 : ─⏺───H─", + " │ ", + "q1 : ─┼─⏺───", + " │ │ ", + "q2 : ─X─┼───", + " │ ", + "q3 : ───X───", "", - "T : | 0 |1|", + "T : │ 0 │1│", ) _assert_correct_diagram(circ, expected) @@ -310,17 +326,17 @@ def test_overlapping_qubits(): def test_overlapping_qubits_angled_gates(): circ = Circuit().zz(0, 2, 0.15).x(control=1, target=3).h(0) expected = ( - "T : | 0 |1|", + "T : │ 0 │1│", " ", - "q0 : -ZZ(0.15)---H-", - " | ", - "q1 : -|--------⏺---", - " | | ", - "q2 : -ZZ(0.15)-|---", - " | ", - "q3 : ----------X---", + "q0 : ─ZZ(0.15)───H─", + " │ ", + "q1 : ─┼────────⏺───", + " │ │ ", + "q2 : ─ZZ(0.15)─┼───", + " │ ", + "q3 : ──────────X───", "", - "T : | 0 |1|", + "T : │ 0 │1│", ) _assert_correct_diagram(circ, expected) @@ -328,17 +344,17 @@ def test_overlapping_qubits_angled_gates(): def test_connector_across_gt_two_qubits(): circ = Circuit().h(4).x(control=3, target=5).h(4).h(2) expected = ( - "T : | 0 |1|", + "T : │ 0 │1│", " ", - "q2 : -H-----", + "q2 : ─H─────", " ", - "q3 : ---⏺---", - " | ", - "q4 : -H-|-H-", - " | ", - "q5 : ---X---", + "q3 : ───⏺───", + " │ ", + "q4 : ─H─┼─H─", + " │ ", + "q5 : ───X───", "", - "T : | 0 |1|", + "T : │ 0 │1│", ) _assert_correct_diagram(circ, expected) @@ -346,17 +362,17 @@ def test_connector_across_gt_two_qubits(): def test_connector_across_non_used_qubits(): circ = Circuit().h(4).cnot(3, 100).h(4).h(101) expected = ( - "T : | 0 |1|", + "T : │ 0 │1│", " ", - "q3 : ---⏺---", - " | ", - "q4 : -H-|-H-", - " | ", - "q100 : ---X---", + "q3 : ───⏺───", + " │ ", + "q4 : ─H─┼─H─", + " │ ", + "q100 : ───X───", " ", - "q101 : -H-----", + "q101 : ─H─────", "", - "T : | 0 |1|", + "T : │ 0 │1│", ) _assert_correct_diagram(circ, expected) @@ -364,11 +380,11 @@ def test_connector_across_non_used_qubits(): def test_verbatim_1q_no_preceding(): circ = Circuit().add_verbatim_box(Circuit().h(0)) expected = ( - "T : | 0 |1| 2 |", + "T : │ 0 │1│ 2 │", " ", - "q0 : -StartVerbatim-H-EndVerbatim-", + "q0 : ─StartVerbatim─H─EndVerbatim─", "", - "T : | 0 |1| 2 |", + "T : │ 0 │1│ 2 │", ) _assert_correct_diagram(circ, expected) @@ -376,11 +392,11 @@ def test_verbatim_1q_no_preceding(): def test_verbatim_1q_preceding(): circ = Circuit().h(0).add_verbatim_box(Circuit().h(0)) expected = ( - "T : |0| 1 |2| 3 |", + "T : │0│ 1 │2│ 3 │", " ", - "q0 : -H-StartVerbatim-H-EndVerbatim-", + "q0 : ─H─StartVerbatim─H─EndVerbatim─", "", - "T : |0| 1 |2| 3 |", + "T : │0│ 1 │2│ 3 │", ) _assert_correct_diagram(circ, expected) @@ -388,11 +404,11 @@ def test_verbatim_1q_preceding(): def test_verbatim_1q_following(): circ = Circuit().add_verbatim_box(Circuit().h(0)).h(0) expected = ( - "T : | 0 |1| 2 |3|", + "T : │ 0 │1│ 2 │3│", " ", - "q0 : -StartVerbatim-H-EndVerbatim-H-", + "q0 : ─StartVerbatim─H─EndVerbatim─H─", "", - "T : | 0 |1| 2 |3|", + "T : │ 0 │1│ 2 │3│", ) _assert_correct_diagram(circ, expected) @@ -400,13 +416,13 @@ def test_verbatim_1q_following(): def test_verbatim_2q_no_preceding(): circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1)) expected = ( - "T : | 0 |1|2| 3 |", + "T : │ 0 │1│2│ 3 │", " ", - "q0 : -StartVerbatim-H-⏺-EndVerbatim-", - " | | | ", - "q1 : -*************---X-***********-", + "q0 : ─StartVerbatim─H─⏺─EndVerbatim─", + " │ │ │ ", + "q1 : ─*************───X─***********─", "", - "T : | 0 |1|2| 3 |", + "T : │ 0 │1│2│ 3 │", ) _assert_correct_diagram(circ, expected) @@ -414,13 +430,13 @@ def test_verbatim_2q_no_preceding(): def test_verbatim_2q_preceding(): circ = Circuit().h(0).add_verbatim_box(Circuit().h(0).cnot(0, 1)) expected = ( - "T : |0| 1 |2|3| 4 |", + "T : │0│ 1 │2│3│ 4 │", " ", - "q0 : -H-StartVerbatim-H-⏺-EndVerbatim-", - " | | | ", - "q1 : ---*************---X-***********-", + "q0 : ─H─StartVerbatim─H─⏺─EndVerbatim─", + " │ │ │ ", + "q1 : ───*************───X─***********─", "", - "T : |0| 1 |2|3| 4 |", + "T : │0│ 1 │2│3│ 4 │", ) _assert_correct_diagram(circ, expected) @@ -428,13 +444,13 @@ def test_verbatim_2q_preceding(): def test_verbatim_2q_following(): circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1)).h(0) expected = ( - "T : | 0 |1|2| 3 |4|", + "T : │ 0 │1│2│ 3 │4│", " ", - "q0 : -StartVerbatim-H-⏺-EndVerbatim-H-", - " | | | ", - "q1 : -*************---X-***********---", + "q0 : ─StartVerbatim─H─⏺─EndVerbatim─H─", + " │ │ │ ", + "q1 : ─*************───X─***********───", "", - "T : | 0 |1|2| 3 |4|", + "T : │ 0 │1│2│ 3 │4│", ) _assert_correct_diagram(circ, expected) @@ -442,15 +458,15 @@ def test_verbatim_2q_following(): def test_verbatim_3q_no_preceding(): circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1).cnot(1, 2)) expected = ( - "T : | 0 |1|2|3| 4 |", + "T : │ 0 │1│2│3│ 4 │", " ", - "q0 : -StartVerbatim-H-⏺---EndVerbatim-", - " | | | ", - "q1 : -|---------------X-⏺-|-----------", - " | | | ", - "q2 : -*************-----X-***********-", + "q0 : ─StartVerbatim─H─⏺───EndVerbatim─", + " │ │ │ ", + "q1 : ─┼───────────────X─⏺─┼───────────", + " │ │ │ ", + "q2 : ─*************─────X─***********─", "", - "T : | 0 |1|2|3| 4 |", + "T : │ 0 │1│2│3│ 4 │", ) _assert_correct_diagram(circ, expected) @@ -458,15 +474,15 @@ def test_verbatim_3q_no_preceding(): def test_verbatim_3q_preceding(): circ = Circuit().h(0).add_verbatim_box(Circuit().h(0).cnot(0, 1).cnot(1, 2)) expected = ( - "T : |0| 1 |2|3|4| 5 |", + "T : │0│ 1 │2│3│4│ 5 │", " ", - "q0 : -H-StartVerbatim-H-⏺---EndVerbatim-", - " | | | ", - "q1 : ---|---------------X-⏺-|-----------", - " | | | ", - "q2 : ---*************-----X-***********-", + "q0 : ─H─StartVerbatim─H─⏺───EndVerbatim─", + " │ │ │ ", + "q1 : ───┼───────────────X─⏺─┼───────────", + " │ │ │ ", + "q2 : ───*************─────X─***********─", "", - "T : |0| 1 |2|3|4| 5 |", + "T : │0│ 1 │2│3│4│ 5 │", ) _assert_correct_diagram(circ, expected) @@ -474,15 +490,15 @@ def test_verbatim_3q_preceding(): def test_verbatim_3q_following(): circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1).cnot(1, 2)).h(0) expected = ( - "T : | 0 |1|2|3| 4 |5|", + "T : │ 0 │1│2│3│ 4 │5│", " ", - "q0 : -StartVerbatim-H-⏺---EndVerbatim-H-", - " | | | ", - "q1 : -|---------------X-⏺-|-------------", - " | | | ", - "q2 : -*************-----X-***********---", + "q0 : ─StartVerbatim─H─⏺───EndVerbatim─H─", + " │ │ │ ", + "q1 : ─┼───────────────X─⏺─┼─────────────", + " │ │ │ ", + "q2 : ─*************─────X─***********───", "", - "T : | 0 |1|2|3| 4 |5|", + "T : │ 0 │1│2│3│ 4 │5│", ) _assert_correct_diagram(circ, expected) @@ -490,17 +506,17 @@ def test_verbatim_3q_following(): def test_verbatim_different_qubits(): circ = Circuit().h(1).add_verbatim_box(Circuit().h(0)).cnot(3, 4) expected = ( - "T : |0| 1 |2| 3 |4|", + "T : │0│ 1 │2│ 3 │4│", " ", - "q0 : ---StartVerbatim-H-EndVerbatim---", - " | | ", - "q1 : -H-|---------------|-------------", - " | | ", - "q3 : ---|---------------|-----------⏺-", - " | | | ", - "q4 : ---*************---***********-X-", + "q0 : ───StartVerbatim─H─EndVerbatim───", + " │ │ ", + "q1 : ─H─┼───────────────┼─────────────", + " │ │ ", + "q3 : ───┼───────────────┼───────────⏺─", + " │ │ │ ", + "q4 : ───*************───***********─X─", "", - "T : |0| 1 |2| 3 |4|", + "T : │0│ 1 │2│ 3 │4│", ) _assert_correct_diagram(circ, expected) @@ -508,17 +524,17 @@ def test_verbatim_different_qubits(): def test_verbatim_qubset_qubits(): circ = Circuit().h(1).cnot(0, 1).cnot(1, 2).add_verbatim_box(Circuit().h(1)).cnot(2, 3) expected = ( - "T : |0|1|2| 3 |4| 5 |6|", + "T : │0│1│2│ 3 │4│ 5 │6│", " ", - "q0 : ---⏺---StartVerbatim---EndVerbatim---", - " | | | ", - "q1 : -H-X-⏺-|-------------H-|-------------", - " | | | ", - "q2 : -----X-|---------------|-----------⏺-", - " | | | ", - "q3 : -------*************---***********-X-", + "q0 : ───⏺───StartVerbatim───EndVerbatim───", + " │ │ │ ", + "q1 : ─H─X─⏺─┼─────────────H─┼─────────────", + " │ │ │ ", + "q2 : ─────X─┼───────────────┼───────────⏺─", + " │ │ │ ", + "q3 : ───────*************───***********─X─", "", - "T : |0|1|2| 3 |4| 5 |6|", + "T : │0│1│2│ 3 │4│ 5 │6│", ) _assert_correct_diagram(circ, expected) @@ -534,15 +550,15 @@ def to_ir(self, target): circ = Circuit().h(0).h(1).cnot(1, 2).add_instruction(Instruction(Foo(), 0)) expected = ( - "T : |0|1|", + "T : │0│1│", " ", - "q0 : -H---", + "q0 : ─H───", " ", - "q1 : -H-⏺-", - " | ", - "q2 : ---X-", + "q1 : ─H─⏺─", + " │ ", + "q2 : ───X─", "", - "T : |0|1|", + "T : │0│1│", ) _assert_correct_diagram(circ, expected) @@ -550,13 +566,13 @@ def to_ir(self, target): def test_result_types_target_none(): circ = Circuit().h(0).h(100).probability() expected = ( - "T : |0|Result Types|", + "T : │0│Result Types│", " ", - "q0 : -H-Probability--", - " | ", - "q100 : -H-Probability--", + "q0 : ─H─Probability──", + " │ ", + "q100 : ─H─Probability──", "", - "T : |0|Result Types|", + "T : │0│Result Types│", ) _assert_correct_diagram(circ, expected) @@ -570,15 +586,15 @@ def test_result_types_target_some(): .expectation(observable=Observable.Y() @ Observable.Z(), target=[0, 100]) ) expected = ( - "T : |0| Result Types |", + "T : │0│ Result Types │", " ", - "q0 : -H-Expectation(Y@Z)-", - " | ", - "q1 : -H-|----------------", - " | ", - "q100 : -H-Expectation(Y@Z)-", + "q0 : ─H─Expectation(Y@Z)─", + " │ ", + "q1 : ─H─┼────────────────", + " │ ", + "q100 : ─H─Expectation(Y@Z)─", "", - "T : |0| Result Types |", + "T : │0│ Result Types │", ) _assert_correct_diagram(circ, expected) @@ -586,15 +602,15 @@ def test_result_types_target_some(): def test_additional_result_types(): circ = Circuit().h(0).h(1).h(100).state_vector().amplitude(["110", "001"]) expected = ( - "T : |0|", + "T : │0│", " ", - "q0 : -H-", + "q0 : ─H─", " ", - "q1 : -H-", + "q1 : ─H─", " ", - "q100 : -H-", + "q100 : ─H─", "", - "T : |0|", + "T : │0│", "", "Additional result types: StateVector, Amplitude(110,001)", ) @@ -612,17 +628,17 @@ def test_multiple_result_types(): .sample(observable=Observable.Y()) ) expected = ( - "T : | 0 |1| Result Types |", + "T : │ 0 │1│ Result Types │", " ", - "q0 : -⏺---H-Variance(Y)----Sample(Y)-", - " | | ", - "q1 : -|-⏺------------------Sample(Y)-", - " | | | ", - "q2 : -X-|---Expectation(Y)-Sample(Y)-", - " | | ", - "q3 : ---X------------------Sample(Y)-", + "q0 : ─⏺───H─Variance(Y)────Sample(Y)─", + " │ │ ", + "q1 : ─┼─⏺──────────────────Sample(Y)─", + " │ │ │ ", + "q2 : ─X─┼───Expectation(Y)─Sample(Y)─", + " │ │ ", + "q3 : ───X──────────────────Sample(Y)─", "", - "T : | 0 |1| Result Types |", + "T : │ 0 │1│ Result Types │", ) _assert_correct_diagram(circ, expected) @@ -640,17 +656,17 @@ def test_multiple_result_types_with_state_vector_amplitude(): .state_vector() ) expected = ( - "T : | 0 |1| Result Types |", + "T : │ 0 │1│ Result Types │", " ", - "q0 : -⏺---H-Variance(Y)------------", - " | ", - "q1 : -|-⏺---Expectation(Hermitian)-", - " | | ", - "q2 : -X-|--------------------------", - " | ", - "q3 : ---X---Expectation(Y)---------", + "q0 : ─⏺───H─Variance(Y)────────────", + " │ ", + "q1 : ─┼─⏺───Expectation(Hermitian)─", + " │ │ ", + "q2 : ─X─┼──────────────────────────", + " │ ", + "q3 : ───X───Expectation(Y)─────────", "", - "T : | 0 |1| Result Types |", + "T : │ 0 │1│ Result Types │", "", "Additional result types: Amplitude(0001), StateVector", ) @@ -675,17 +691,17 @@ def test_multiple_result_types_with_custom_hermitian_ascii_symbol(): ) ) expected = ( - "T : | 0 |1| Result Types |", + "T : │ 0 │1│ Result Types │", " ", - "q0 : -⏺---H-Variance(Y)---------", - " | ", - "q1 : -|-⏺---Expectation(MyHerm)-", - " | | | ", - "q2 : -X-|---Expectation(MyHerm)-", - " | ", - "q3 : ---X---Expectation(Y)------", + "q0 : ─⏺───H─Variance(Y)─────────", + " │ ", + "q1 : ─┼─⏺───Expectation(MyHerm)─", + " │ │ │ ", + "q2 : ─X─┼───Expectation(MyHerm)─", + " │ ", + "q3 : ───X───Expectation(Y)──────", "", - "T : | 0 |1| Result Types |", + "T : │ 0 │1│ Result Types │", ) _assert_correct_diagram(circ, expected) @@ -693,13 +709,13 @@ def test_multiple_result_types_with_custom_hermitian_ascii_symbol(): def test_noise_1qubit(): circ = Circuit().h(0).x(1).bit_flip(1, 0.1) expected = ( - "T : | 0 |", + "T : │ 0 │", " ", - "q0 : -H---------", + "q0 : ─H─────────", " ", - "q1 : -X-BF(0.1)-", + "q1 : ─X─BF(0.1)─", "", - "T : | 0 |", + "T : │ 0 │", ) _assert_correct_diagram(circ, expected) @@ -707,15 +723,15 @@ def test_noise_1qubit(): def test_noise_2qubit(): circ = Circuit().h(1).kraus((0, 2), [np.eye(4)]) expected = ( - "T : | 0 |", + "T : │ 0 │", " ", - "q0 : ---KR-", - " | ", - "q1 : -H-|--", - " | ", - "q2 : ---KR-", + "q0 : ───KR─", + " │ ", + "q1 : ─H─┼──", + " │ ", + "q2 : ───KR─", "", - "T : | 0 |", + "T : │ 0 │", ) _assert_correct_diagram(circ, expected) @@ -723,13 +739,13 @@ def test_noise_2qubit(): def test_noise_multi_probabilities(): circ = Circuit().h(0).x(1).pauli_channel(1, 0.1, 0.2, 0.3) expected = ( - "T : | 0 |", + "T : │ 0 │", " ", - "q0 : -H-----------------", + "q0 : ─H─────────────────", " ", - "q1 : -X-PC(0.1,0.2,0.3)-", + "q1 : ─X─PC(0.1,0.2,0.3)─", "", - "T : | 0 |", + "T : │ 0 │", ) _assert_correct_diagram(circ, expected) @@ -740,13 +756,13 @@ def test_noise_multi_probabilities_with_parameter(): c = FreeParameter("c") circ = Circuit().h(0).x(1).pauli_channel(1, a, b, c) expected = ( - "T : | 0 |", + "T : │ 0 │", " ", - "q0 : -H-----------", + "q0 : ─H───────────", " ", - "q1 : -X-PC(a,b,c)-", + "q1 : ─X─PC(a,b,c)─", "", - "T : | 0 |", + "T : │ 0 │", "", "Unassigned parameters: [a, b, c].", ) @@ -760,11 +776,11 @@ def test_pulse_gate_1_qubit_circuit(): .pulse_gate(0, PulseSequence().set_phase(Frame("x", Port("px", 1e-9), 1e9, 0), 0)) ) expected = ( - "T : |0|1 |", + "T : │0│1 │", " ", - "q0 : -H-PG-", + "q0 : ─H─PG─", "", - "T : |0|1 |", + "T : │0│1 │", ) _assert_correct_diagram(circ, expected) @@ -776,13 +792,13 @@ def test_pulse_gate_multi_qubit_circuit(): .pulse_gate([0, 1], PulseSequence().set_phase(Frame("x", Port("px", 1e-9), 1e9, 0), 0)) ) expected = ( - "T : |0|1 |", + "T : │0│1 │", " ", - "q0 : -H-PG-", - " | ", - "q1 : ---PG-", + "q0 : ─H─PG─", + " │ ", + "q1 : ───PG─", "", - "T : |0|1 |", + "T : │0│1 │", ) _assert_correct_diagram(circ, expected) @@ -804,13 +820,13 @@ def test_circuit_with_nested_target_list(): ) expected = ( - "T : |0| Result Types |", + "T : │0│ Result Types │", " ", - "q0 : -H-Expectation(Hamiltonian)-", - " | ", - "q1 : -H-Expectation(Hamiltonian)-", + "q0 : ─H─Expectation(Hamiltonian)─", + " │ ", + "q1 : ─H─Expectation(Hamiltonian)─", "", - "T : |0| Result Types |", + "T : │0│ Result Types │", ) _assert_correct_diagram(circ, expected) @@ -827,15 +843,15 @@ def test_hamiltonian(): ) ) expected = ( - "T : |0|1| 2 | Result Types |", + "T : │0│1│ 2 │ Result Types │", " ", - "q0 : -H-⏺-Rx(theta)-AdjointGradient(Hamiltonian)-", - " | | ", - "q1 : ---X-----------AdjointGradient(Hamiltonian)-", - " | ", - "q2 : ---------------AdjointGradient(Hamiltonian)-", + "q0 : ─H─⏺─Rx(theta)─AdjointGradient(Hamiltonian)─", + " │ │ ", + "q1 : ───X───────────AdjointGradient(Hamiltonian)─", + " │ ", + "q2 : ───────────────AdjointGradient(Hamiltonian)─", "", - "T : |0|1| 2 | Result Types |", + "T : │0│1│ 2 │ Result Types │", "", "Unassigned parameters: [theta].", ) @@ -861,14 +877,14 @@ def __init__(self): circ.add_instruction(Instruction(CFoo(), (1, 2), control=0, power=3)) circ.add_instruction(Instruction(FooFoo(), (1, 2), control=0, power=4)) expected = ( - "T : | 0 | 1 | 2 | 3 | 4 |", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", " ", - "q0 : -H---------(FOO^-1)-⏺-------⏺-------⏺-------", - " | | | ", - "q1 : -(H^0)--------------(FOO^2)-⏺-------(FOO^4)-", - " | | ", - "q2 : -(H^-3.14)------------------(FOO^3)-(FOO^4)-", + "q0 : ─H─────────(FOO^-1)─⏺───────⏺───────⏺───────", + " │ │ │ ", + "q1 : ─(H^0)──────────────(FOO^2)─⏺───────(FOO^4)─", + " │ │ ", + "q2 : ─(H^-3.14)──────────────────(FOO^3)─(FOO^4)─", "", - "T : | 0 | 1 | 2 | 3 | 4 |", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", ) _assert_correct_diagram(circ, expected) From 90be4f85cc4b1d8fffd5a84d3a45c2d2b4bce4b5 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 22 Dec 2023 17:03:13 -0500 Subject: [PATCH 03/62] fix tests --- .../circuits/test_ascii_circuit_diagram.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py index 667e292c9..6ec64da7d 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -255,22 +255,6 @@ def test_neg_control_qubits(): _assert_correct_diagram(circ, expected) -def test_only_neg_control_qubits(): - circ = Circuit().x(2, control=[0, 1], control_state=0) - expected = ( - "T : │0│", - " ", - "q0 : ─○─", - " │ ", - "q1 : ─⏺─", - " │ ", - "q2 : ─X─", - "", - "T : │0│", - ) - _assert_correct_diagram(circ, expected) - - def test_only_neg_control_qubits(): circ = Circuit().x(2, control=[0, 1], control_state=0) expected = ( From 36f6a795d9ef52a0b2ffa4dfcd34fc29c9b8695d Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Sun, 24 Dec 2023 18:00:42 -0500 Subject: [PATCH 04/62] switch to large b/w circle --- src/braket/circuits/angled_gate.py | 6 +- src/braket/circuits/ascii_circuit_diagram.py | 4 +- src/braket/circuits/gate.py | 2 +- src/braket/circuits/gates.py | 20 ++-- src/braket/circuits/quantum_operator.py | 2 +- .../circuits/test_ascii_circuit_diagram.py | 100 +++++++++--------- 6 files changed, 67 insertions(+), 67 deletions(-) diff --git a/src/braket/circuits/angled_gate.py b/src/braket/circuits/angled_gate.py index bf2adc4bc..78357b909 100644 --- a/src/braket/circuits/angled_gate.py +++ b/src/braket/circuits/angled_gate.py @@ -46,7 +46,7 @@ def __init__( printing a diagram of a circuit. The length must be the same as `qubit_count`, and index ordering is expected to correlate with the target ordering on the instruction. For instance, if a CNOT instruction has the control qubit on the first index and - target qubit on the second index, the ASCII symbols should have `["⏺", "X"]` to + target qubit on the second index, the ASCII symbols should have `["●", "X"]` to correlate a symbol with that index. Raises: @@ -147,7 +147,7 @@ def __init__( printing a diagram of a circuit. The length must be the same as `qubit_count`, and index ordering is expected to correlate with the target ordering on the instruction. For instance, if a CNOT instruction has the control qubit on the first index and - target qubit on the second index, the ASCII symbols should have `["⏺", "X"]` to + target qubit on the second index, the ASCII symbols should have `["●", "X"]` to correlate a symbol with that index. Raises: @@ -265,7 +265,7 @@ def __init__( printing a diagram of a circuit. The length must be the same as `qubit_count`, and index ordering is expected to correlate with the target ordering on the instruction. For instance, if a CNOT instruction has the control qubit on the first index and - target qubit on the second index, the ASCII symbols should have `["⏺", "X"]` to + target qubit on the second index, the ASCII symbols should have `["●", "X"]` to correlate a symbol with that index. Raises: diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 2f71026c3..dd3fa3741 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -358,7 +358,7 @@ def _ascii_diagram_column( # 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] != "⏺" + and ascii_symbols[item_qubit_index] != "●" ) else "" ) @@ -368,7 +368,7 @@ def _ascii_diagram_column( else ascii_symbols[item_qubit_index] ) elif qubit in control_qubits: - symbols[qubit] = "⏺" if map_control_qubit_states[qubit] else "○" + symbols[qubit] = "●" if map_control_qubit_states[qubit] else "◯" else: symbols[qubit] = "┼" diff --git a/src/braket/circuits/gate.py b/src/braket/circuits/gate.py index 5f5f4d609..43edf9010 100644 --- a/src/braket/circuits/gate.py +++ b/src/braket/circuits/gate.py @@ -42,7 +42,7 @@ def __init__(self, qubit_count: Optional[int], ascii_symbols: Sequence[str]): printing a diagram of circuits. Length must be the same as `qubit_count`, and index ordering is expected to correlate with target ordering on the instruction. For instance, if CNOT instruction has the control qubit on the first index and - target qubit on the second index. Then ASCII symbols would have ["⏺", "X"] to + target qubit on the second index. Then ASCII symbols would have ["●", "X"] to correlate a symbol with that index. Raises: diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index 2356a75da..8dd5277dc 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -1539,7 +1539,7 @@ class CNot(Gate): """ def __init__(self): - super().__init__(qubit_count=None, ascii_symbols=["⏺", "X"]) + super().__init__(qubit_count=None, ascii_symbols=["●", "X"]) @property def _qasm_name(self) -> str: @@ -2023,7 +2023,7 @@ def __init__(self, angle: Union[FreeParameterExpression, float]): super().__init__( angle=angle, qubit_count=None, - ascii_symbols=["⏺", angled_ascii_characters("PHASE", angle)], + ascii_symbols=["●", angled_ascii_characters("PHASE", angle)], ) @property @@ -2108,7 +2108,7 @@ def __init__(self, angle: Union[FreeParameterExpression, float]): super().__init__( angle=angle, qubit_count=None, - ascii_symbols=["⏺", angled_ascii_characters("PHASE00", angle)], + ascii_symbols=["●", angled_ascii_characters("PHASE00", angle)], ) @property @@ -2193,7 +2193,7 @@ def __init__(self, angle: Union[FreeParameterExpression, float]): super().__init__( angle=angle, qubit_count=None, - ascii_symbols=["⏺", angled_ascii_characters("PHASE01", angle)], + ascii_symbols=["●", angled_ascii_characters("PHASE01", angle)], ) @property @@ -2278,7 +2278,7 @@ def __init__(self, angle: Union[FreeParameterExpression, float]): super().__init__( angle=angle, qubit_count=None, - ascii_symbols=["⏺", angled_ascii_characters("PHASE10", angle)], + ascii_symbols=["●", angled_ascii_characters("PHASE10", angle)], ) @property @@ -2357,7 +2357,7 @@ class CV(Gate): """ def __init__(self): - super().__init__(qubit_count=None, ascii_symbols=["⏺", "V"]) + super().__init__(qubit_count=None, ascii_symbols=["●", "V"]) @property def _qasm_name(self) -> str: @@ -2434,7 +2434,7 @@ class CY(Gate): """ def __init__(self): - super().__init__(qubit_count=None, ascii_symbols=["⏺", "Y"]) + super().__init__(qubit_count=None, ascii_symbols=["●", "Y"]) @property def _qasm_name(self) -> str: @@ -2511,7 +2511,7 @@ class CZ(Gate): """ def __init__(self): - super().__init__(qubit_count=None, ascii_symbols=["⏺", "Z"]) + super().__init__(qubit_count=None, ascii_symbols=["●", "Z"]) @property def _qasm_name(self) -> str: @@ -3008,7 +3008,7 @@ class CCNot(Gate): """ def __init__(self): - super().__init__(qubit_count=None, ascii_symbols=["⏺", "⏺", "X"]) + super().__init__(qubit_count=None, ascii_symbols=["●", "●", "X"]) @property def _qasm_name(self) -> str: @@ -3116,7 +3116,7 @@ class CSwap(Gate): """ def __init__(self): - super().__init__(qubit_count=None, ascii_symbols=["⏺", "SWAP", "SWAP"]) + super().__init__(qubit_count=None, ascii_symbols=["●", "SWAP", "SWAP"]) @property def _qasm_name(self) -> str: diff --git a/src/braket/circuits/quantum_operator.py b/src/braket/circuits/quantum_operator.py index 878bd093d..d9d2e389e 100644 --- a/src/braket/circuits/quantum_operator.py +++ b/src/braket/circuits/quantum_operator.py @@ -39,7 +39,7 @@ def __init__(self, qubit_count: Optional[int], ascii_symbols: Sequence[str]): Length must be the same as `qubit_count`, and index ordering is expected to correlate with target ordering on the instruction. For instance, if CNOT instruction has the control qubit on the first index and - target qubit on the second index. Then ASCII symbols would have ["⏺", "X"] to + target qubit on the second index. Then ASCII symbols would have ["●", "X"] to correlate a symbol with that index. Raises: diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py index 6ec64da7d..4ad75cc49 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -186,33 +186,33 @@ def test_time_width(): expected = ( "T : │0│1│2│3│4│5│6│7│8│9│10│11│12│13│", " ", - "q0 : ─⏺───────────────────────────────", + "q0 : ─●───────────────────────────────", " │ ", - "q1 : ─X─⏺─────────────────────────────", + "q1 : ─X─●─────────────────────────────", " │ ", - "q2 : ───X─⏺───────────────────────────", + "q2 : ───X─●───────────────────────────", " │ ", - "q3 : ─────X─⏺─────────────────────────", + "q3 : ─────X─●─────────────────────────", " │ ", - "q4 : ───────X─⏺───────────────────────", + "q4 : ───────X─●───────────────────────", " │ ", - "q5 : ─────────X─⏺─────────────────────", + "q5 : ─────────X─●─────────────────────", " │ ", - "q6 : ───────────X─⏺───────────────────", + "q6 : ───────────X─●───────────────────", " │ ", - "q7 : ─────────────X─⏺─────────────────", + "q7 : ─────────────X─●─────────────────", " │ ", - "q8 : ───────────────X─⏺───────────────", + "q8 : ───────────────X─●───────────────", " │ ", - "q9 : ─────────────────X─⏺─────────────", + "q9 : ─────────────────X─●─────────────", " │ ", - "q10 : ───────────────────X─⏺───────────", + "q10 : ───────────────────X─●───────────", " │ ", - "q11 : ─────────────────────X──⏺────────", + "q11 : ─────────────────────X──●────────", " │ ", - "q12 : ────────────────────────X──⏺─────", + "q12 : ────────────────────────X──●─────", " │ ", - "q13 : ───────────────────────────X──⏺──", + "q13 : ───────────────────────────X──●──", " │ ", "q14 : ──────────────────────────────X──", "", @@ -228,7 +228,7 @@ def test_connector_across_two_qubits(): " ", "q2 : ─H───", " ", - "q3 : ─⏺─H─", + "q3 : ─●─H─", " │ ", "q4 : ─X─H─", " ", @@ -244,9 +244,9 @@ def test_neg_control_qubits(): expected = ( "T : │0│", " ", - "q0 : ─○─", + "q0 : ─◯─", " │ ", - "q1 : ─⏺─", + "q1 : ─●─", " │ ", "q2 : ─X─", "", @@ -260,9 +260,9 @@ def test_only_neg_control_qubits(): expected = ( "T : │0│", " ", - "q0 : ─○─", + "q0 : ─◯─", " │ ", - "q1 : ─○─", + "q1 : ─◯─", " │ ", "q2 : ─X─", "", @@ -278,9 +278,9 @@ def test_connector_across_three_qubits(): " ", "q2 : ─H───", " ", - "q3 : ─⏺─H─", + "q3 : ─●─H─", " │ ", - "q4 : ─⏺─H─", + "q4 : ─●─H─", " │ ", "q5 : ─X─H─", "", @@ -294,9 +294,9 @@ def test_overlapping_qubits(): expected = ( "T : │ 0 │1│", " ", - "q0 : ─⏺───H─", + "q0 : ─●───H─", " │ ", - "q1 : ─┼─⏺───", + "q1 : ─┼─●───", " │ │ ", "q2 : ─X─┼───", " │ ", @@ -314,7 +314,7 @@ def test_overlapping_qubits_angled_gates(): " ", "q0 : ─ZZ(0.15)───H─", " │ ", - "q1 : ─┼────────⏺───", + "q1 : ─┼────────●───", " │ │ ", "q2 : ─ZZ(0.15)─┼───", " │ ", @@ -332,7 +332,7 @@ def test_connector_across_gt_two_qubits(): " ", "q2 : ─H─────", " ", - "q3 : ───⏺───", + "q3 : ───●───", " │ ", "q4 : ─H─┼─H─", " │ ", @@ -348,7 +348,7 @@ def test_connector_across_non_used_qubits(): expected = ( "T : │ 0 │1│", " ", - "q3 : ───⏺───", + "q3 : ───●───", " │ ", "q4 : ─H─┼─H─", " │ ", @@ -402,7 +402,7 @@ def test_verbatim_2q_no_preceding(): expected = ( "T : │ 0 │1│2│ 3 │", " ", - "q0 : ─StartVerbatim─H─⏺─EndVerbatim─", + "q0 : ─StartVerbatim─H─●─EndVerbatim─", " │ │ │ ", "q1 : ─*************───X─***********─", "", @@ -416,7 +416,7 @@ def test_verbatim_2q_preceding(): expected = ( "T : │0│ 1 │2│3│ 4 │", " ", - "q0 : ─H─StartVerbatim─H─⏺─EndVerbatim─", + "q0 : ─H─StartVerbatim─H─●─EndVerbatim─", " │ │ │ ", "q1 : ───*************───X─***********─", "", @@ -430,7 +430,7 @@ def test_verbatim_2q_following(): expected = ( "T : │ 0 │1│2│ 3 │4│", " ", - "q0 : ─StartVerbatim─H─⏺─EndVerbatim─H─", + "q0 : ─StartVerbatim─H─●─EndVerbatim─H─", " │ │ │ ", "q1 : ─*************───X─***********───", "", @@ -444,9 +444,9 @@ def test_verbatim_3q_no_preceding(): expected = ( "T : │ 0 │1│2│3│ 4 │", " ", - "q0 : ─StartVerbatim─H─⏺───EndVerbatim─", + "q0 : ─StartVerbatim─H─●───EndVerbatim─", " │ │ │ ", - "q1 : ─┼───────────────X─⏺─┼───────────", + "q1 : ─┼───────────────X─●─┼───────────", " │ │ │ ", "q2 : ─*************─────X─***********─", "", @@ -460,9 +460,9 @@ def test_verbatim_3q_preceding(): expected = ( "T : │0│ 1 │2│3│4│ 5 │", " ", - "q0 : ─H─StartVerbatim─H─⏺───EndVerbatim─", + "q0 : ─H─StartVerbatim─H─●───EndVerbatim─", " │ │ │ ", - "q1 : ───┼───────────────X─⏺─┼───────────", + "q1 : ───┼───────────────X─●─┼───────────", " │ │ │ ", "q2 : ───*************─────X─***********─", "", @@ -476,9 +476,9 @@ def test_verbatim_3q_following(): expected = ( "T : │ 0 │1│2│3│ 4 │5│", " ", - "q0 : ─StartVerbatim─H─⏺───EndVerbatim─H─", + "q0 : ─StartVerbatim─H─●───EndVerbatim─H─", " │ │ │ ", - "q1 : ─┼───────────────X─⏺─┼─────────────", + "q1 : ─┼───────────────X─●─┼─────────────", " │ │ │ ", "q2 : ─*************─────X─***********───", "", @@ -496,7 +496,7 @@ def test_verbatim_different_qubits(): " │ │ ", "q1 : ─H─┼───────────────┼─────────────", " │ │ ", - "q3 : ───┼───────────────┼───────────⏺─", + "q3 : ───┼───────────────┼───────────●─", " │ │ │ ", "q4 : ───*************───***********─X─", "", @@ -510,11 +510,11 @@ def test_verbatim_qubset_qubits(): expected = ( "T : │0│1│2│ 3 │4│ 5 │6│", " ", - "q0 : ───⏺───StartVerbatim───EndVerbatim───", + "q0 : ───●───StartVerbatim───EndVerbatim───", " │ │ │ ", - "q1 : ─H─X─⏺─┼─────────────H─┼─────────────", + "q1 : ─H─X─●─┼─────────────H─┼─────────────", " │ │ │ ", - "q2 : ─────X─┼───────────────┼───────────⏺─", + "q2 : ─────X─┼───────────────┼───────────●─", " │ │ │ ", "q3 : ───────*************───***********─X─", "", @@ -538,7 +538,7 @@ def to_ir(self, target): " ", "q0 : ─H───", " ", - "q1 : ─H─⏺─", + "q1 : ─H─●─", " │ ", "q2 : ───X─", "", @@ -614,9 +614,9 @@ def test_multiple_result_types(): expected = ( "T : │ 0 │1│ Result Types │", " ", - "q0 : ─⏺───H─Variance(Y)────Sample(Y)─", + "q0 : ─●───H─Variance(Y)────Sample(Y)─", " │ │ ", - "q1 : ─┼─⏺──────────────────Sample(Y)─", + "q1 : ─┼─●──────────────────Sample(Y)─", " │ │ │ ", "q2 : ─X─┼───Expectation(Y)─Sample(Y)─", " │ │ ", @@ -642,9 +642,9 @@ def test_multiple_result_types_with_state_vector_amplitude(): expected = ( "T : │ 0 │1│ Result Types │", " ", - "q0 : ─⏺───H─Variance(Y)────────────", + "q0 : ─●───H─Variance(Y)────────────", " │ ", - "q1 : ─┼─⏺───Expectation(Hermitian)─", + "q1 : ─┼─●───Expectation(Hermitian)─", " │ │ ", "q2 : ─X─┼──────────────────────────", " │ ", @@ -677,9 +677,9 @@ def test_multiple_result_types_with_custom_hermitian_ascii_symbol(): expected = ( "T : │ 0 │1│ Result Types │", " ", - "q0 : ─⏺───H─Variance(Y)─────────", + "q0 : ─●───H─Variance(Y)─────────", " │ ", - "q1 : ─┼─⏺───Expectation(MyHerm)─", + "q1 : ─┼─●───Expectation(MyHerm)─", " │ │ │ ", "q2 : ─X─┼───Expectation(MyHerm)─", " │ ", @@ -829,7 +829,7 @@ def test_hamiltonian(): expected = ( "T : │0│1│ 2 │ Result Types │", " ", - "q0 : ─H─⏺─Rx(theta)─AdjointGradient(Hamiltonian)─", + "q0 : ─H─●─Rx(theta)─AdjointGradient(Hamiltonian)─", " │ │ ", "q1 : ───X───────────AdjointGradient(Hamiltonian)─", " │ ", @@ -849,7 +849,7 @@ def __init__(self): class CFoo(Gate): def __init__(self): - super().__init__(qubit_count=2, ascii_symbols=["⏺", "FOO"]) + super().__init__(qubit_count=2, ascii_symbols=["●", "FOO"]) class FooFoo(Gate): def __init__(self): @@ -863,9 +863,9 @@ def __init__(self): expected = ( "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", " ", - "q0 : ─H─────────(FOO^-1)─⏺───────⏺───────⏺───────", + "q0 : ─H─────────(FOO^-1)─●───────●───────●───────", " │ │ │ ", - "q1 : ─(H^0)──────────────(FOO^2)─⏺───────(FOO^4)─", + "q1 : ─(H^0)──────────────(FOO^2)─●───────(FOO^4)─", " │ │ ", "q2 : ─(H^-3.14)──────────────────(FOO^3)─(FOO^4)─", "", From aae35ff6d52ca5b10c2cb80f5d8d319324e51821 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 26 Dec 2023 16:11:23 -0500 Subject: [PATCH 05/62] first attempt to box symbols --- src/braket/circuits/ascii_circuit_diagram.py | 49 +++++++++++++++++--- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index dd3fa3741..8208e025e 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -14,7 +14,7 @@ from __future__ import annotations from functools import reduce -from typing import Union +from typing import Literal, Union import braket.circuits.circuit as cir from braket.circuits.circuit_diagram import CircuitDiagram @@ -121,6 +121,7 @@ def _prepare_diagram_vars( for qubit in circuit_qubits: y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) y_axis_str += "q{0:{width}} : ─\n".format(str(int(qubit)), width=y_axis_width) + y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) return y_axis_str, global_phase @@ -367,6 +368,9 @@ def _ascii_diagram_column( if power_string else ascii_symbols[item_qubit_index] ) + if symbols[qubit] in ["●", "◯", "─"]: + continue + symbols[qubit] = f"┤ {symbols[qubit]} ├" elif qubit in control_qubits: symbols[qubit] = "●" if map_control_qubit_states[qubit] else "◯" else: @@ -386,7 +390,7 @@ def _create_output( qubits: QubitSet, global_phase: float | None, ) -> str: - symbols_width = max([len(symbol) for symbol in symbols.values()]) + symbols_width = max([len(symbol) for symbol in symbols.values()]) + 2 output = "" if global_phase is not None: @@ -401,11 +405,42 @@ def _create_output( width=symbols_width, ) - for qubit in qubits: - output += "{0:{width}}\n".format(margins[qubit], width=symbols_width + 1) - output += "{0:{fill}{align}{width}}\n".format( - symbols[qubit], fill="─", align="<", width=symbols_width + 1 - ) + output += AsciiCircuitDiagram._draw_symbol(symbols[qubits[0]], symbols_width, "first") + for qubit in qubits[1:-1]: + output += AsciiCircuitDiagram._draw_symbol(symbols[qubit], symbols_width) + output += AsciiCircuitDiagram._draw_symbol(symbols[qubits[-1]], symbols_width, "last") + return output + + @staticmethod + def _draw_symbol( + symbol: str, symbols_width: int, position: Literal["first, middle, last"] = "middle" + ) -> str: + if symbol in ["●", "◯"]: + top = "│" + bottom = "│" + if position == "first": + top = "" + elif position == "last": + bottom = "" + elif symbol == "┼": + top = "│" + bottom = "│" + elif symbol == "─": + top = "" + bottom = "" + else: + top = "┌" + "─" * (len(symbol) - 2) + "┐" + bottom = "└" + "─" * (len(symbol) - 2) + "┘" + + output = "{0:{fill}{align}{width}}\n".format( + top, fill=" ", align="^", width=symbols_width + 1 + ) + output += "{0:{fill}{align}{width}}\n".format( + symbol, fill="─", align="^", width=symbols_width + 1 + ) + output += "{0:{fill}{align}{width}}\n".format( + bottom, fill=" ", align="^", width=symbols_width + 1 + ) return output @staticmethod From 13d69280698e9117702e56fa43b1d4f4d562a4ab Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 26 Dec 2023 17:34:03 -0500 Subject: [PATCH 06/62] fix single qubit circuit --- src/braket/circuits/ascii_circuit_diagram.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 8208e025e..c50a1af0f 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -86,8 +86,8 @@ def build_diagram(circuit: cir.Circuit) -> str: for i, line_in_col in enumerate(col_str.split("\n")): lines[i] += line_in_col - # Time on top and bottom - lines.append(lines[0]) + # Replace the last (empty) line with time + lines[-1] = lines[0] if global_phase: lines.append(f"\nGlobal phase: {global_phase}") @@ -364,7 +364,7 @@ def _ascii_diagram_column( else "" ) symbols[qubit] = ( - f"({ascii_symbols[item_qubit_index]}{power_string})" + f"{ascii_symbols[item_qubit_index]}{power_string}" if power_string else ascii_symbols[item_qubit_index] ) @@ -408,7 +408,8 @@ def _create_output( output += AsciiCircuitDiagram._draw_symbol(symbols[qubits[0]], symbols_width, "first") for qubit in qubits[1:-1]: output += AsciiCircuitDiagram._draw_symbol(symbols[qubit], symbols_width) - output += AsciiCircuitDiagram._draw_symbol(symbols[qubits[-1]], symbols_width, "last") + if len(qubits) > 1: + output += AsciiCircuitDiagram._draw_symbol(symbols[qubits[-1]], symbols_width, "last") return output @staticmethod From 8fc586b10480ab6003eefaa3ccc36fe071deced0 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 26 Dec 2023 19:30:39 -0500 Subject: [PATCH 07/62] rewrite first tests --- .../circuits/test_ascii_circuit_diagram.py | 198 +++++++++--------- 1 file changed, 104 insertions(+), 94 deletions(-) diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py index 4ad75cc49..64b680795 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -36,14 +36,26 @@ def test_only_gphase_circuit(): def test_one_gate_one_qubit(): circ = Circuit().h(0) - expected = ("T : │0│", " ", "q0 : ─H─", "", "T : │0│") + expected = ( + "T : │ 0 │", + " ┌───┐ ", + "q0 : ──┤ H ├──", + " └───┘ ", + "T : │ 0 │", + ) _assert_correct_diagram(circ, expected) def test_one_gate_one_qubit_rotation(): circ = Circuit().rx(angle=3.14, target=0) # Column formats to length of the gate plus the ascii representation for the angle. - expected = ("T : │ 0 │", " ", "q0 : ─Rx(3.14)─", "", "T : │ 0 │") + expected = ( + "T : │ 0 │", + " ┌──────────┐ ", + "q0 : ──┤ Rx(3.14) ├──", + " └──────────┘ ", + "T : │ 0 │", + ) _assert_correct_diagram(circ, expected) @@ -52,11 +64,11 @@ def test_one_gate_one_qubit_rotation_with_parameter(): circ = Circuit().rx(angle=theta, target=0) # Column formats to length of the gate plus the ascii representation for the angle. expected = ( - "T : │ 0 │", - " ", - "q0 : ─Rx(theta)─", - "", - "T : │ 0 │", + "T : │ 0 │", + " ┌───────────┐ ", + "q0 : ──┤ Rx(theta) ├──", + " └───────────┘ ", + "T : │ 0 │", "", "Unassigned parameters: [theta].", ) @@ -67,12 +79,12 @@ def test_one_gate_one_qubit_rotation_with_parameter(): def test_one_gate_with_global_phase(target): circ = Circuit().x(target=target).gphase(0.15) expected = ( - "T : │0│ 1 │", - "GP : │0│0.15│", - " ", - f"q{target} : ─X──────", - "", - "T : │0│ 1 │", + "T : │ 0 │ 1 │", + "GP : │ 0 │0.15│", + " ┌───┐ ", + f"q{target} : ──┤ X ├───────", + " └───┘ ", + "T : │ 0 │ 1 │", "", "Global phase: 0.15", ) @@ -82,12 +94,12 @@ def test_one_gate_with_global_phase(target): def test_one_gate_with_zero_global_phase(): circ = Circuit().gphase(-0.15).x(target=0).gphase(0.15) expected = ( - "T : │ 0 │ 1 │", - "GP : │-0.15│0.00│", - " ", - "q0 : ─X──────────", - "", - "T : │ 0 │ 1 │", + "T : │ 0 │ 1 │", + "GP : │ -0.15 │0.00│", + " ┌───┐ ", + "q0 : ──┤ X ├───────", + " └───┘ ", + "T : │ 0 │ 1 │", ) _assert_correct_diagram(circ, expected) @@ -97,11 +109,11 @@ def test_one_gate_one_qubit_rotation_with_unicode(): circ = Circuit().rx(angle=theta, target=0) # Column formats to length of the gate plus the ascii representation for the angle. expected = ( - "T : │ 0 │", - " ", - "q0 : ─Rx(θ)─", - "", - "T : │ 0 │", + "T : │ 0 │", + " ┌───────┐ ", + "q0 : ──┤ Rx(θ) ├──", + " └───────┘ ", + "T : │ 0 │", "", "Unassigned parameters: [θ].", ) @@ -112,12 +124,12 @@ def test_one_gate_with_parametric_expression_global_phase_(): theta = FreeParameter("\u03B8") circ = Circuit().x(target=0).gphase(2 * theta).x(0).gphase(1) expected = ( - "T : │0│ 1 │ 2 │", - "GP : │0│2*θ│2*θ + 1.0│", - " ", - "q0 : ─X─X─────────────", - "", - "T : │0│ 1 │ 2 │", + "T : │ 0 │ 1 │ 2 │", + "GP : │ 0 │ 2*θ │2*θ + 1.0│", + " ┌───┐ ┌───┐ ", + "q0 : ──┤ X ├───┤ X ├────────────", + " └───┘ └───┘ ", + "T : │ 0 │ 1 │ 2 │", "", "Global phase: 2*θ + 1.0", "", @@ -132,11 +144,11 @@ def test_one_gate_one_qubit_rotation_with_parameter_assigned(): new_circ = circ.make_bound_circuit({"theta": np.pi}) # Column formats to length of the gate plus the ascii representation for the angle. expected = ( - "T : │ 0 │", - " ", - "q0 : ─Rx(3.14)─", - "", - "T : │ 0 │", + "T : │ 0 │", + " ┌──────────┐ ", + "q0 : ──┤ Rx(3.14) ├──", + " └──────────┘ ", + "T : │ 0 │", ) _assert_correct_diagram(new_circ, expected) @@ -144,13 +156,14 @@ def test_one_gate_one_qubit_rotation_with_parameter_assigned(): def test_qubit_width(): circ = Circuit().h(0).h(100) expected = ( - "T : │0│", - " ", - "q0 : ─H─", - " ", - "q100 : ─H─", - "", - "T : │0│", + "T : │ 0 │", + " ┌───┐ ", + "q0 : ──┤ H ├──", + " └───┘ ", + " ┌───┐ ", + "q100 : ──┤ H ├──", + " └───┘ ", + "T : │ 0 │", ) _assert_correct_diagram(circ, expected) @@ -165,58 +178,52 @@ def to_ir(self, target): circ = Circuit().h(0).h(1).add_instruction(Instruction(Foo(), 0)) expected = ( - "T : │0│ 1 │", - " ", - "q0 : ─H─FOO─", - " ", - "q1 : ─H─────", - "", - "T : │0│ 1 │", + "T : │ 0 │ 1 │", + " ┌───┐ ┌─────┐ ", + "q0 : ──┤ H ├───┤ FOO ├──", + " └───┘ └─────┘ ", + " ┌───┐ ", + "q1 : ──┤ H ├────────────", + " └───┘ ", + "T : │ 0 │ 1 │", ) _assert_correct_diagram(circ, expected) def test_time_width(): circ = Circuit() - num_qubits = 15 + num_qubits = 8 for qubit in range(num_qubits): if qubit == num_qubits - 1: break circ.cnot(qubit, qubit + 1) expected = ( - "T : │0│1│2│3│4│5│6│7│8│9│10│11│12│13│", - " ", - "q0 : ─●───────────────────────────────", - " │ ", - "q1 : ─X─●─────────────────────────────", - " │ ", - "q2 : ───X─●───────────────────────────", - " │ ", - "q3 : ─────X─●─────────────────────────", - " │ ", - "q4 : ───────X─●───────────────────────", - " │ ", - "q5 : ─────────X─●─────────────────────", - " │ ", - "q6 : ───────────X─●───────────────────", - " │ ", - "q7 : ─────────────X─●─────────────────", - " │ ", - "q8 : ───────────────X─●───────────────", - " │ ", - "q9 : ─────────────────X─●─────────────", - " │ ", - "q10 : ───────────────────X─●───────────", - " │ ", - "q11 : ─────────────────────X──●────────", - " │ ", - "q12 : ────────────────────────X──●─────", - " │ ", - "q13 : ───────────────────────────X──●──", - " │ ", - "q14 : ──────────────────────────────X──", - "", - "T : │0│1│2│3│4│5│6│7│8│9│10│11│12│13│", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │", + " ", + "q0 : ────●────────────────────────────────────────────────────", + " │ ", + " ┌───┐ ", + "q1 : ──┤ X ├─────●────────────────────────────────────────────", + " └───┘ │ ", + " ┌───┐ ", + "q2 : ──────────┤ X ├─────●────────────────────────────────────", + " └───┘ │ ", + " ┌───┐ ", + "q3 : ──────────────────┤ X ├─────●────────────────────────────", + " └───┘ │ ", + " ┌───┐ ", + "q4 : ──────────────────────────┤ X ├─────●────────────────────", + " └───┘ │ ", + " ┌───┐ ", + "q5 : ──────────────────────────────────┤ X ├─────●────────────", + " └───┘ │ ", + " ┌───┐ ", + "q6 : ──────────────────────────────────────────┤ X ├─────●────", + " └───┘ │ ", + " ┌───┐ ", + "q7 : ──────────────────────────────────────────────────┤ X ├──", + " └───┘ ", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │", ) _assert_correct_diagram(circ, expected) @@ -224,17 +231,20 @@ def test_time_width(): def test_connector_across_two_qubits(): circ = Circuit().cnot(3, 4).h(range(2, 6)) expected = ( - "T : │0│1│", - " ", - "q2 : ─H───", - " ", - "q3 : ─●─H─", - " │ ", - "q4 : ─X─H─", - " ", - "q5 : ─H───", - "", - "T : │0│1│", + "T : │ 0 │ 1 │", + " ┌───┐ ", + "q2 : ──┤ H ├──────────", + " └───┘ ", + " ┌───┐ ", + "q3 : ────●─────┤ H ├──", + " │ └───┘ ", + " ┌───┐ ┌───┐ ", + "q4 : ──┤ X ├───┤ H ├──", + " └───┘ └───┘ ", + " ┌───┐ ", + "q5 : ──┤ H ├──────────", + " └───┘ ", + "T : │ 0 │ 1 │", ) _assert_correct_diagram(circ, expected) From 1ef0bf254450a0e7baecd547c6ad4748a25ab9f1 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 26 Dec 2023 22:12:28 -0500 Subject: [PATCH 08/62] first try to draw verbatim box --- src/braket/circuits/ascii_circuit_diagram.py | 69 +++++++++++++------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index c50a1af0f..5067f6b3b 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -301,6 +301,7 @@ def _ascii_diagram_column( """ symbols = {qubit: "─" for qubit in circuit_qubits} margins = {qubit: " " 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: @@ -315,10 +316,10 @@ def _ascii_diagram_column( 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 + ascii_symbols = [ascii_symbol] * len(circuit_qubits) + connections = {qubit: "both" for qubit in circuit_qubits[1:-1]} + connections[circuit_qubits[-1]] = "above" + connections[circuit_qubits[0]] = "below" elif ( isinstance(item, Instruction) and isinstance(item.operator, Gate) @@ -368,11 +369,23 @@ def _ascii_diagram_column( if power_string else ascii_symbols[item_qubit_index] ) - if symbols[qubit] in ["●", "◯", "─"]: + if symbols[qubit] in ["●", "◯"]: + if min(target_qubits) < qubit < max(target_qubits): + connections[qubit] = "below" + elif qubit < max(target_qubits): + connections[qubit] = "below" + elif min(target_qubits) < qubit: + connections[qubit] = "above" continue - symbols[qubit] = f"┤ {symbols[qubit]} ├" + elif qubit in control_qubits: symbols[qubit] = "●" if map_control_qubit_states[qubit] else "◯" + if min(target_qubits) < qubit < max(target_qubits): + connections[qubit] = "below" + elif qubit < max(target_qubits): + connections[qubit] = "below" + elif min(target_qubits) < qubit: + connections[qubit] = "above" else: symbols[qubit] = "┼" @@ -380,17 +393,17 @@ def _ascii_diagram_column( if target_and_control and qubit != min(target_and_control): margins[qubit] = "│" - output = AsciiCircuitDiagram._create_output(symbols, margins, circuit_qubits, global_phase) + output = AsciiCircuitDiagram._create_output(symbols, connections, circuit_qubits, global_phase) return output @staticmethod def _create_output( symbols: dict[Qubit, str], - margins: dict[Qubit, str], + connections: dict[Qubit, str], qubits: QubitSet, global_phase: float | None, ) -> str: - symbols_width = max([len(symbol) for symbol in symbols.values()]) + 2 + symbols_width = max([len(symbol) for symbol in symbols.values()]) + 4 output = "" if global_phase is not None: @@ -405,31 +418,41 @@ def _create_output( width=symbols_width, ) - output += AsciiCircuitDiagram._draw_symbol(symbols[qubits[0]], symbols_width, "first") - for qubit in qubits[1:-1]: - output += AsciiCircuitDiagram._draw_symbol(symbols[qubit], symbols_width) - if len(qubits) > 1: - output += AsciiCircuitDiagram._draw_symbol(symbols[qubits[-1]], symbols_width, "last") + for qubit in qubits: + output += AsciiCircuitDiagram._draw_symbol(symbols[qubit], symbols_width, connections[qubit]) return output @staticmethod def _draw_symbol( - symbol: str, symbols_width: int, position: Literal["first, middle, last"] = "middle" + symbol: str, symbols_width: int, connection: Literal["above, below, both, none"] = "none" ) -> str: + top = "" + bottom = "" if symbol in ["●", "◯"]: - top = "│" - bottom = "│" - if position == "first": - top = "" - elif position == "last": - bottom = "" + if connection == "above" or connection == "both": + top = "│" + if connection == "below" or connection == "both": + bottom = "│" + elif symbol in ["StartVerbatim", "EndVerbatim"]: + if connection == "below": + top = "┌─" + "─" * len(symbol) + "─┐" + bottom = "│ " + " " * len(symbol) + " │" + symbol = f"┤ {symbol} ├" + elif connection == "both": + top = bottom = "│ " + " " * len(symbol) + " │" + symbol = "┤ " + " " * len(symbol) + " ├" + elif connection == "above": + top = "│ " + " " * len(symbol) + " │" + bottom = "└─" + "─" * len(symbol) + "─┘" + symbol = "┤ " + " " * len(symbol) + " ├" elif symbol == "┼": top = "│" bottom = "│" elif symbol == "─": - top = "" - bottom = "" + # We do not box when no gate is applied. + pass else: + symbol = f"┤ {symbol} ├" top = "┌" + "─" * (len(symbol) - 2) + "┐" bottom = "└" + "─" * (len(symbol) - 2) + "┘" From 5e63a378fcba83809173ebd6aba25c4865a90494 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 26 Dec 2023 22:37:11 -0500 Subject: [PATCH 09/62] revamp verbatim box --- src/braket/circuits/ascii_circuit_diagram.py | 24 +++++++++----------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 5067f6b3b..1c98f3c3b 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -317,9 +317,10 @@ def _ascii_diagram_column( qubits = circuit_qubits ascii_symbol = item.ascii_symbols[0] ascii_symbols = [ascii_symbol] * len(circuit_qubits) - connections = {qubit: "both" for qubit in circuit_qubits[1:-1]} - connections[circuit_qubits[-1]] = "above" - connections[circuit_qubits[0]] = "below" + if len(circuit_qubits) > 1: + connections = {qubit: "both" for qubit in circuit_qubits[1:-1]} + connections[circuit_qubits[-1]] = "above" + connections[circuit_qubits[0]] = "below" elif ( isinstance(item, Instruction) and isinstance(item.operator, Gate) @@ -435,16 +436,13 @@ def _draw_symbol( bottom = "│" elif symbol in ["StartVerbatim", "EndVerbatim"]: if connection == "below": - top = "┌─" + "─" * len(symbol) + "─┐" - bottom = "│ " + " " * len(symbol) + " │" - symbol = f"┤ {symbol} ├" + bottom = "║" elif connection == "both": - top = bottom = "│ " + " " * len(symbol) + " │" - symbol = "┤ " + " " * len(symbol) + " ├" + top = bottom = symbol = "║" elif connection == "above": - top = "│ " + " " * len(symbol) + " │" - bottom = "└─" + "─" * len(symbol) + "─┘" - symbol = "┤ " + " " * len(symbol) + " ├" + top = "║" + symbol = "╨" + bottom = "" elif symbol == "┼": top = "│" bottom = "│" @@ -452,9 +450,9 @@ def _draw_symbol( # We do not box when no gate is applied. pass else: + top = f"┌─{'─' * len(symbol)}─┐" + bottom = f"└─{'─' * len(symbol)}─┘" symbol = f"┤ {symbol} ├" - top = "┌" + "─" * (len(symbol) - 2) + "┐" - bottom = "└" + "─" * (len(symbol) - 2) + "┘" output = "{0:{fill}{align}{width}}\n".format( top, fill=" ", align="^", width=symbols_width + 1 From 50a4957a36b21d0c7a2738055f866b5ad46bea8b Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 26 Dec 2023 23:10:21 -0500 Subject: [PATCH 10/62] update tests and skip outdated --- src/braket/circuits/ascii_circuit_diagram.py | 1 + .../circuits/test_ascii_circuit_diagram.py | 292 +++++++++--------- 2 files changed, 147 insertions(+), 146 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 1c98f3c3b..30ac0d4aa 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -404,6 +404,7 @@ def _create_output( qubits: QubitSet, global_phase: float | None, ) -> str: + # We add 4 because of the edges of the box, i.e. "┤ " and " ├" symbols_width = max([len(symbol) for symbol in symbols.values()]) + 4 output = "" diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py index 64b680795..8770ab43a 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -37,11 +37,11 @@ def test_only_gphase_circuit(): def test_one_gate_one_qubit(): circ = Circuit().h(0) expected = ( - "T : │ 0 │", - " ┌───┐ ", - "q0 : ──┤ H ├──", - " └───┘ ", - "T : │ 0 │", + "T : │ 0 │", + " ┌───┐ ", + "q0 : ─┤ H ├─", + " └───┘ ", + "T : │ 0 │", ) _assert_correct_diagram(circ, expected) @@ -50,11 +50,11 @@ def test_one_gate_one_qubit_rotation(): circ = Circuit().rx(angle=3.14, target=0) # Column formats to length of the gate plus the ascii representation for the angle. expected = ( - "T : │ 0 │", - " ┌──────────┐ ", - "q0 : ──┤ Rx(3.14) ├──", - " └──────────┘ ", - "T : │ 0 │", + "T : │ 0 │", + " ┌──────────┐ ", + "q0 : ─┤ Rx(3.14) ├─", + " └──────────┘ ", + "T : │ 0 │", ) _assert_correct_diagram(circ, expected) @@ -64,11 +64,11 @@ def test_one_gate_one_qubit_rotation_with_parameter(): circ = Circuit().rx(angle=theta, target=0) # Column formats to length of the gate plus the ascii representation for the angle. expected = ( - "T : │ 0 │", - " ┌───────────┐ ", - "q0 : ──┤ Rx(theta) ├──", - " └───────────┘ ", - "T : │ 0 │", + "T : │ 0 │", + " ┌───────────┐ ", + "q0 : ─┤ Rx(theta) ├─", + " └───────────┘ ", + "T : │ 0 │", "", "Unassigned parameters: [theta].", ) @@ -79,12 +79,12 @@ def test_one_gate_one_qubit_rotation_with_parameter(): def test_one_gate_with_global_phase(target): circ = Circuit().x(target=target).gphase(0.15) expected = ( - "T : │ 0 │ 1 │", - "GP : │ 0 │0.15│", - " ┌───┐ ", - f"q{target} : ──┤ X ├───────", - " └───┘ ", - "T : │ 0 │ 1 │", + "T : │ 0 │ 1 │", + "GP : │ 0 │0.15 │", + " ┌───┐ ", + f"q{target} : ─┤ X ├───────", + " └───┘ ", + "T : │ 0 │ 1 │", "", "Global phase: 0.15", ) @@ -94,12 +94,12 @@ def test_one_gate_with_global_phase(target): def test_one_gate_with_zero_global_phase(): circ = Circuit().gphase(-0.15).x(target=0).gphase(0.15) expected = ( - "T : │ 0 │ 1 │", - "GP : │ -0.15 │0.00│", - " ┌───┐ ", - "q0 : ──┤ X ├───────", - " └───┘ ", - "T : │ 0 │ 1 │", + "T : │ 0 │ 1 │", + "GP : │-0.15│0.00 │", + " ┌───┐ ", + "q0 : ─┤ X ├───────", + " └───┘ ", + "T : │ 0 │ 1 │", ) _assert_correct_diagram(circ, expected) @@ -109,11 +109,11 @@ def test_one_gate_one_qubit_rotation_with_unicode(): circ = Circuit().rx(angle=theta, target=0) # Column formats to length of the gate plus the ascii representation for the angle. expected = ( - "T : │ 0 │", - " ┌───────┐ ", - "q0 : ──┤ Rx(θ) ├──", - " └───────┘ ", - "T : │ 0 │", + "T : │ 0 │", + " ┌───────┐ ", + "q0 : ─┤ Rx(θ) ├─", + " └───────┘ ", + "T : │ 0 │", "", "Unassigned parameters: [θ].", ) @@ -124,12 +124,12 @@ def test_one_gate_with_parametric_expression_global_phase_(): theta = FreeParameter("\u03B8") circ = Circuit().x(target=0).gphase(2 * theta).x(0).gphase(1) expected = ( - "T : │ 0 │ 1 │ 2 │", - "GP : │ 0 │ 2*θ │2*θ + 1.0│", - " ┌───┐ ┌───┐ ", - "q0 : ──┤ X ├───┤ X ├────────────", - " └───┘ └───┘ ", - "T : │ 0 │ 1 │ 2 │", + "T : │ 0 │ 1 │ 2 │", + "GP : │ 0 │ 2*θ │2*θ + 1.0│", + " ┌───┐ ┌───┐ ", + "q0 : ─┤ X ├─┤ X ├───────────", + " └───┘ └───┘ ", + "T : │ 0 │ 1 │ 2 │", "", "Global phase: 2*θ + 1.0", "", @@ -144,11 +144,11 @@ def test_one_gate_one_qubit_rotation_with_parameter_assigned(): new_circ = circ.make_bound_circuit({"theta": np.pi}) # Column formats to length of the gate plus the ascii representation for the angle. expected = ( - "T : │ 0 │", - " ┌──────────┐ ", - "q0 : ──┤ Rx(3.14) ├──", - " └──────────┘ ", - "T : │ 0 │", + "T : │ 0 │", + " ┌──────────┐ ", + "q0 : ─┤ Rx(3.14) ├─", + " └──────────┘ ", + "T : │ 0 │", ) _assert_correct_diagram(new_circ, expected) @@ -156,14 +156,14 @@ def test_one_gate_one_qubit_rotation_with_parameter_assigned(): def test_qubit_width(): circ = Circuit().h(0).h(100) expected = ( - "T : │ 0 │", - " ┌───┐ ", - "q0 : ──┤ H ├──", - " └───┘ ", - " ┌───┐ ", - "q100 : ──┤ H ├──", - " └───┘ ", - "T : │ 0 │", + "T : │ 0 │", + " ┌───┐ ", + "q0 : ─┤ H ├─", + " └───┘ ", + " ┌───┐ ", + "q100 : ─┤ H ├─", + " └───┘ ", + "T : │ 0 │", ) _assert_correct_diagram(circ, expected) @@ -178,14 +178,14 @@ def to_ir(self, target): circ = Circuit().h(0).h(1).add_instruction(Instruction(Foo(), 0)) expected = ( - "T : │ 0 │ 1 │", - " ┌───┐ ┌─────┐ ", - "q0 : ──┤ H ├───┤ FOO ├──", - " └───┘ └─────┘ ", - " ┌───┐ ", - "q1 : ──┤ H ├────────────", - " └───┘ ", - "T : │ 0 │ 1 │", + "T : │ 0 │ 1 │", + " ┌───┐ ┌─────┐ ", + "q0 : ─┤ H ├─┤ FOO ├─", + " └───┘ └─────┘ ", + " ┌───┐ ", + "q1 : ─┤ H ├─────────", + " └───┘ ", + "T : │ 0 │ 1 │", ) _assert_correct_diagram(circ, expected) @@ -198,32 +198,32 @@ def test_time_width(): break circ.cnot(qubit, qubit + 1) expected = ( - "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │", - " ", - "q0 : ────●────────────────────────────────────────────────────", - " │ ", - " ┌───┐ ", - "q1 : ──┤ X ├─────●────────────────────────────────────────────", - " └───┘ │ ", - " ┌───┐ ", - "q2 : ──────────┤ X ├─────●────────────────────────────────────", - " └───┘ │ ", - " ┌───┐ ", - "q3 : ──────────────────┤ X ├─────●────────────────────────────", - " └───┘ │ ", - " ┌───┐ ", - "q4 : ──────────────────────────┤ X ├─────●────────────────────", - " └───┘ │ ", - " ┌───┐ ", - "q5 : ──────────────────────────────────┤ X ├─────●────────────", - " └───┘ │ ", - " ┌───┐ ", - "q6 : ──────────────────────────────────────────┤ X ├─────●────", - " └───┘ │ ", - " ┌───┐ ", - "q7 : ──────────────────────────────────────────────────┤ X ├──", - " └───┘ ", - "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │", + " ", + "q0 : ───●───────────────────────────────────────", + " │ ", + " ┌───┐ ", + "q1 : ─┤ X ├───●─────────────────────────────────", + " └───┘ │ ", + " ┌───┐ ", + "q2 : ───────┤ X ├───●───────────────────────────", + " └───┘ │ ", + " ┌───┐ ", + "q3 : ─────────────┤ X ├───●─────────────────────", + " └───┘ │ ", + " ┌───┐ ", + "q4 : ───────────────────┤ X ├───●───────────────", + " └───┘ │ ", + " ┌───┐ ", + "q5 : ─────────────────────────┤ X ├───●─────────", + " └───┘ │ ", + " ┌───┐ ", + "q6 : ───────────────────────────────┤ X ├───●───", + " └───┘ │ ", + " ┌───┐ ", + "q7 : ─────────────────────────────────────┤ X ├─", + " └───┘ ", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │", ) _assert_correct_diagram(circ, expected) @@ -231,24 +231,24 @@ def test_time_width(): def test_connector_across_two_qubits(): circ = Circuit().cnot(3, 4).h(range(2, 6)) expected = ( - "T : │ 0 │ 1 │", - " ┌───┐ ", - "q2 : ──┤ H ├──────────", - " └───┘ ", - " ┌───┐ ", - "q3 : ────●─────┤ H ├──", - " │ └───┘ ", - " ┌───┐ ┌───┐ ", - "q4 : ──┤ X ├───┤ H ├──", - " └───┘ └───┘ ", - " ┌───┐ ", - "q5 : ──┤ H ├──────────", - " └───┘ ", - "T : │ 0 │ 1 │", + "T : │ 0 │ 1 │", + " ┌───┐ ", + "q2 : ─┤ H ├───────", + " └───┘ ", + " ┌───┐ ", + "q3 : ───●───┤ H ├─", + " │ └───┘ ", + " ┌───┐ ┌───┐ ", + "q4 : ─┤ X ├─┤ H ├─", + " └───┘ └───┘ ", + " ┌───┐ ", + "q5 : ─┤ H ├───────", + " └───┘ ", + "T : │ 0 │ 1 │", ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_neg_control_qubits(): circ = Circuit().x(2, control=[0, 1], control_state=[0, 1]) expected = ( @@ -264,7 +264,7 @@ def test_neg_control_qubits(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_only_neg_control_qubits(): circ = Circuit().x(2, control=[0, 1], control_state=0) expected = ( @@ -280,7 +280,7 @@ def test_only_neg_control_qubits(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_connector_across_three_qubits(): circ = Circuit().x(control=(3, 4), target=5).h(range(2, 6)) expected = ( @@ -298,7 +298,7 @@ def test_connector_across_three_qubits(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_overlapping_qubits(): circ = Circuit().cnot(0, 2).x(control=1, target=3).h(0) expected = ( @@ -316,7 +316,7 @@ def test_overlapping_qubits(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_overlapping_qubits_angled_gates(): circ = Circuit().zz(0, 2, 0.15).x(control=1, target=3).h(0) expected = ( @@ -334,7 +334,7 @@ def test_overlapping_qubits_angled_gates(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_connector_across_gt_two_qubits(): circ = Circuit().h(4).x(control=3, target=5).h(4).h(2) expected = ( @@ -352,7 +352,7 @@ def test_connector_across_gt_two_qubits(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_connector_across_non_used_qubits(): circ = Circuit().h(4).cnot(3, 100).h(4).h(101) expected = ( @@ -374,11 +374,11 @@ def test_connector_across_non_used_qubits(): def test_verbatim_1q_no_preceding(): circ = Circuit().add_verbatim_box(Circuit().h(0)) expected = ( - "T : │ 0 │1│ 2 │", - " ", - "q0 : ─StartVerbatim─H─EndVerbatim─", - "", - "T : │ 0 │1│ 2 │", + "T : │ 0 │ 1 │ 2 │", + " ┌───┐ ", + "q0 : ───StartVerbatim───┤ H ├───EndVerbatim───", + " └───┘ ", + "T : │ 0 │ 1 │ 2 │", ) _assert_correct_diagram(circ, expected) @@ -386,11 +386,11 @@ def test_verbatim_1q_no_preceding(): def test_verbatim_1q_preceding(): circ = Circuit().h(0).add_verbatim_box(Circuit().h(0)) expected = ( - "T : │0│ 1 │2│ 3 │", - " ", - "q0 : ─H─StartVerbatim─H─EndVerbatim─", - "", - "T : │0│ 1 │2│ 3 │", + "T : │ 0 │ 1 │ 2 │ 3 │", + " ┌───┐ ┌───┐ ", + "q0 : ─┤ H ├───StartVerbatim───┤ H ├───EndVerbatim───", + " └───┘ └───┘ ", + "T : │ 0 │ 1 │ 2 │ 3 │", ) _assert_correct_diagram(circ, expected) @@ -398,15 +398,15 @@ def test_verbatim_1q_preceding(): def test_verbatim_1q_following(): circ = Circuit().add_verbatim_box(Circuit().h(0)).h(0) expected = ( - "T : │ 0 │1│ 2 │3│", - " ", - "q0 : ─StartVerbatim─H─EndVerbatim─H─", - "", - "T : │ 0 │1│ 2 │3│", + "T : │ 0 │ 1 │ 2 │ 3 │", + " ┌───┐ ┌───┐ ", + "q0 : ───StartVerbatim───┤ H ├───EndVerbatim───┤ H ├─", + " └───┘ └───┘ ", + "T : │ 0 │ 1 │ 2 │ 3 │", ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_verbatim_2q_no_preceding(): circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1)) expected = ( @@ -420,7 +420,7 @@ def test_verbatim_2q_no_preceding(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_verbatim_2q_preceding(): circ = Circuit().h(0).add_verbatim_box(Circuit().h(0).cnot(0, 1)) expected = ( @@ -434,7 +434,7 @@ def test_verbatim_2q_preceding(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_verbatim_2q_following(): circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1)).h(0) expected = ( @@ -448,7 +448,7 @@ def test_verbatim_2q_following(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_verbatim_3q_no_preceding(): circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1).cnot(1, 2)) expected = ( @@ -464,7 +464,7 @@ def test_verbatim_3q_no_preceding(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_verbatim_3q_preceding(): circ = Circuit().h(0).add_verbatim_box(Circuit().h(0).cnot(0, 1).cnot(1, 2)) expected = ( @@ -480,7 +480,7 @@ def test_verbatim_3q_preceding(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_verbatim_3q_following(): circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1).cnot(1, 2)).h(0) expected = ( @@ -496,7 +496,7 @@ def test_verbatim_3q_following(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_verbatim_different_qubits(): circ = Circuit().h(1).add_verbatim_box(Circuit().h(0)).cnot(3, 4) expected = ( @@ -514,7 +514,7 @@ def test_verbatim_different_qubits(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_verbatim_qubset_qubits(): circ = Circuit().h(1).cnot(0, 1).cnot(1, 2).add_verbatim_box(Circuit().h(1)).cnot(2, 3) expected = ( @@ -532,7 +532,7 @@ def test_verbatim_qubset_qubits(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_ignore_non_gates(): class Foo(Operator): @property @@ -556,7 +556,7 @@ def to_ir(self, target): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_result_types_target_none(): circ = Circuit().h(0).h(100).probability() expected = ( @@ -570,7 +570,7 @@ def test_result_types_target_none(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_result_types_target_some(): circ = ( Circuit() @@ -592,7 +592,7 @@ def test_result_types_target_some(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_additional_result_types(): circ = Circuit().h(0).h(1).h(100).state_vector().amplitude(["110", "001"]) expected = ( @@ -610,7 +610,7 @@ def test_additional_result_types(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_multiple_result_types(): circ = ( Circuit() @@ -636,7 +636,7 @@ def test_multiple_result_types(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_multiple_result_types_with_state_vector_amplitude(): circ = ( Circuit() @@ -666,7 +666,7 @@ def test_multiple_result_types_with_state_vector_amplitude(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_multiple_result_types_with_custom_hermitian_ascii_symbol(): herm_matrix = (Observable.Y() @ Observable.Z()).to_matrix() circ = ( @@ -699,7 +699,7 @@ def test_multiple_result_types_with_custom_hermitian_ascii_symbol(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_noise_1qubit(): circ = Circuit().h(0).x(1).bit_flip(1, 0.1) expected = ( @@ -713,7 +713,7 @@ def test_noise_1qubit(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_noise_2qubit(): circ = Circuit().h(1).kraus((0, 2), [np.eye(4)]) expected = ( @@ -729,7 +729,7 @@ def test_noise_2qubit(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_noise_multi_probabilities(): circ = Circuit().h(0).x(1).pauli_channel(1, 0.1, 0.2, 0.3) expected = ( @@ -743,7 +743,7 @@ def test_noise_multi_probabilities(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_noise_multi_probabilities_with_parameter(): a = FreeParameter("a") b = FreeParameter("b") @@ -762,7 +762,7 @@ def test_noise_multi_probabilities_with_parameter(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_pulse_gate_1_qubit_circuit(): circ = ( Circuit() @@ -778,7 +778,7 @@ def test_pulse_gate_1_qubit_circuit(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_pulse_gate_multi_qubit_circuit(): circ = ( Circuit() @@ -796,11 +796,11 @@ def test_pulse_gate_multi_qubit_circuit(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def _assert_correct_diagram(circ, expected): assert AsciiCircuitDiagram.build_diagram(circ) == "\n".join(expected) - +@pytest.mark.skip def test_circuit_with_nested_target_list(): circ = ( Circuit() @@ -824,7 +824,7 @@ def test_circuit_with_nested_target_list(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_hamiltonian(): circ = ( Circuit() @@ -851,7 +851,7 @@ def test_hamiltonian(): ) _assert_correct_diagram(circ, expected) - +@pytest.mark.skip def test_power(): class Foo(Gate): def __init__(self): From f976a0046e9d7a1dae6d96ec6d948c16514adea6 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 26 Dec 2023 23:17:49 -0500 Subject: [PATCH 11/62] modify last test --- .../circuits/test_ascii_circuit_diagram.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py index 8770ab43a..8b738cab6 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -869,16 +869,18 @@ def __init__(self): circ.add_instruction(Instruction(Foo(), 0, power=-1)) circ.add_instruction(Instruction(CFoo(), (0, 1), power=2)) circ.add_instruction(Instruction(CFoo(), (1, 2), control=0, power=3)) - circ.add_instruction(Instruction(FooFoo(), (1, 2), control=0, power=4)) + circ.add_instruction(Instruction(FooFoo(), (1, 3), control=[0, 2], power=4)) expected = ( - "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", + "T : | 0 | 1 | 2 | 3 | 4 |", " ", - "q0 : ─H─────────(FOO^-1)─●───────●───────●───────", - " │ │ │ ", - "q1 : ─(H^0)──────────────(FOO^2)─●───────(FOO^4)─", - " │ │ ", - "q2 : ─(H^-3.14)──────────────────(FOO^3)─(FOO^4)─", - "", - "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", + "q0 : -H---------(FOO^-1)-C-------C-------C-------", + " | | | ", + "q1 : -(H^0)--------------(FOO^2)-C-------(FOO^4)-", + " | | ", + "q2 : -(H^-3.14)------------------(FOO^3)-C-------", + " | ", + "q3 : ------------------------------------(FOO^4)-", + "", + "T : | 0 | 1 | 2 | 3 | 4 |", ) _assert_correct_diagram(circ, expected) From ab97d5b93079552191fd8b69c148cf8b472467ee Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 26 Dec 2023 23:27:12 -0500 Subject: [PATCH 12/62] fix connections --- src/braket/circuits/ascii_circuit_diagram.py | 16 +++++------ .../circuits/test_ascii_circuit_diagram.py | 27 ++++++++++--------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 30ac0d4aa..0c825848a 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -371,21 +371,21 @@ def _ascii_diagram_column( else ascii_symbols[item_qubit_index] ) if symbols[qubit] in ["●", "◯"]: - if min(target_qubits) < qubit < max(target_qubits): + if min(target_and_control) < qubit < max(target_and_control): + connections[qubit] = "both" + elif qubit < max(target_and_control): connections[qubit] = "below" - elif qubit < max(target_qubits): - connections[qubit] = "below" - elif min(target_qubits) < qubit: + elif min(target_and_control) < qubit: connections[qubit] = "above" continue elif qubit in control_qubits: symbols[qubit] = "●" if map_control_qubit_states[qubit] else "◯" - if min(target_qubits) < qubit < max(target_qubits): - connections[qubit] = "below" - elif qubit < max(target_qubits): + if min(target_and_control) < qubit < max(target_and_control): + connections[qubit] = "both" + elif qubit < max(target_and_control): connections[qubit] = "below" - elif min(target_qubits) < qubit: + elif min(target_and_control) < qubit: connections[qubit] = "above" else: symbols[qubit] = "┼" diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py index 8b738cab6..df3aabeb4 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -851,7 +851,7 @@ def test_hamiltonian(): ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip + def test_power(): class Foo(Gate): def __init__(self): @@ -871,16 +871,19 @@ def __init__(self): circ.add_instruction(Instruction(CFoo(), (1, 2), control=0, power=3)) circ.add_instruction(Instruction(FooFoo(), (1, 3), control=[0, 2], power=4)) expected = ( - "T : | 0 | 1 | 2 | 3 | 4 |", - " ", - "q0 : -H---------(FOO^-1)-C-------C-------C-------", - " | | | ", - "q1 : -(H^0)--------------(FOO^2)-C-------(FOO^4)-", - " | | ", - "q2 : -(H^-3.14)------------------(FOO^3)-C-------", - " | ", - "q3 : ------------------------------------(FOO^4)-", - "", - "T : | 0 | 1 | 2 | 3 | 4 |", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", + " ┌───┐ ┌────────┐ ", + "q0 : ────┤ H ├────┤ FOO^-1 ├─────●─────────●─────────●─────", + " └───┘ └────────┘ │ │ │ ", + " ┌─────┐ ┌───────┐ │ ┌───────┐ ", + "q1 : ───┤ H^0 ├──────────────┤ FOO^2 ├─────●─────┤ FOO^4 ├─", + " └─────┘ └───────┘ │ └───────┘ ", + " ┌─────────┐ ┌───────┐ │ ", + "q2 : ─┤ H^-3.14 ├──────────────────────┤ FOO^3 ├─────●─────", + " └─────────┘ └───────┘ │ ", + " ┌───────┐ ", + "q3 : ────────────────────────────────────────────┤ FOO^4 ├─", + " └───────┘ ", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", ) _assert_correct_diagram(circ, expected) From 5cf1e76714b0360c163828ab32a3f13e3a96994d Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 26 Dec 2023 23:27:41 -0500 Subject: [PATCH 13/62] fix linter --- src/braket/circuits/ascii_circuit_diagram.py | 8 +++-- .../circuits/test_ascii_circuit_diagram.py | 31 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 0c825848a..a2f9f8f58 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -394,7 +394,9 @@ def _ascii_diagram_column( if target_and_control and qubit != min(target_and_control): margins[qubit] = "│" - output = AsciiCircuitDiagram._create_output(symbols, connections, circuit_qubits, global_phase) + output = AsciiCircuitDiagram._create_output( + symbols, connections, circuit_qubits, global_phase + ) return output @staticmethod @@ -421,7 +423,9 @@ def _create_output( ) for qubit in qubits: - output += AsciiCircuitDiagram._draw_symbol(symbols[qubit], symbols_width, connections[qubit]) + output += AsciiCircuitDiagram._draw_symbol( + symbols[qubit], symbols_width, connections[qubit] + ) return output @staticmethod diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py index df3aabeb4..f1b82f979 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -248,6 +248,7 @@ def test_connector_across_two_qubits(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_neg_control_qubits(): circ = Circuit().x(2, control=[0, 1], control_state=[0, 1]) @@ -264,6 +265,7 @@ def test_neg_control_qubits(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_only_neg_control_qubits(): circ = Circuit().x(2, control=[0, 1], control_state=0) @@ -280,6 +282,7 @@ def test_only_neg_control_qubits(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_connector_across_three_qubits(): circ = Circuit().x(control=(3, 4), target=5).h(range(2, 6)) @@ -298,6 +301,7 @@ def test_connector_across_three_qubits(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_overlapping_qubits(): circ = Circuit().cnot(0, 2).x(control=1, target=3).h(0) @@ -316,6 +320,7 @@ def test_overlapping_qubits(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_overlapping_qubits_angled_gates(): circ = Circuit().zz(0, 2, 0.15).x(control=1, target=3).h(0) @@ -334,6 +339,7 @@ def test_overlapping_qubits_angled_gates(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_connector_across_gt_two_qubits(): circ = Circuit().h(4).x(control=3, target=5).h(4).h(2) @@ -352,6 +358,7 @@ def test_connector_across_gt_two_qubits(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_connector_across_non_used_qubits(): circ = Circuit().h(4).cnot(3, 100).h(4).h(101) @@ -406,6 +413,7 @@ def test_verbatim_1q_following(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_verbatim_2q_no_preceding(): circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1)) @@ -420,6 +428,7 @@ def test_verbatim_2q_no_preceding(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_verbatim_2q_preceding(): circ = Circuit().h(0).add_verbatim_box(Circuit().h(0).cnot(0, 1)) @@ -434,6 +443,7 @@ def test_verbatim_2q_preceding(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_verbatim_2q_following(): circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1)).h(0) @@ -448,6 +458,7 @@ def test_verbatim_2q_following(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_verbatim_3q_no_preceding(): circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1).cnot(1, 2)) @@ -464,6 +475,7 @@ def test_verbatim_3q_no_preceding(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_verbatim_3q_preceding(): circ = Circuit().h(0).add_verbatim_box(Circuit().h(0).cnot(0, 1).cnot(1, 2)) @@ -480,6 +492,7 @@ def test_verbatim_3q_preceding(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_verbatim_3q_following(): circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1).cnot(1, 2)).h(0) @@ -496,6 +509,7 @@ def test_verbatim_3q_following(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_verbatim_different_qubits(): circ = Circuit().h(1).add_verbatim_box(Circuit().h(0)).cnot(3, 4) @@ -514,6 +528,7 @@ def test_verbatim_different_qubits(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_verbatim_qubset_qubits(): circ = Circuit().h(1).cnot(0, 1).cnot(1, 2).add_verbatim_box(Circuit().h(1)).cnot(2, 3) @@ -532,6 +547,7 @@ def test_verbatim_qubset_qubits(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_ignore_non_gates(): class Foo(Operator): @@ -556,6 +572,7 @@ def to_ir(self, target): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_result_types_target_none(): circ = Circuit().h(0).h(100).probability() @@ -570,6 +587,7 @@ def test_result_types_target_none(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_result_types_target_some(): circ = ( @@ -592,6 +610,7 @@ def test_result_types_target_some(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_additional_result_types(): circ = Circuit().h(0).h(1).h(100).state_vector().amplitude(["110", "001"]) @@ -610,6 +629,7 @@ def test_additional_result_types(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_multiple_result_types(): circ = ( @@ -636,6 +656,7 @@ def test_multiple_result_types(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_multiple_result_types_with_state_vector_amplitude(): circ = ( @@ -666,6 +687,7 @@ def test_multiple_result_types_with_state_vector_amplitude(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_multiple_result_types_with_custom_hermitian_ascii_symbol(): herm_matrix = (Observable.Y() @ Observable.Z()).to_matrix() @@ -699,6 +721,7 @@ def test_multiple_result_types_with_custom_hermitian_ascii_symbol(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_noise_1qubit(): circ = Circuit().h(0).x(1).bit_flip(1, 0.1) @@ -713,6 +736,7 @@ def test_noise_1qubit(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_noise_2qubit(): circ = Circuit().h(1).kraus((0, 2), [np.eye(4)]) @@ -729,6 +753,7 @@ def test_noise_2qubit(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_noise_multi_probabilities(): circ = Circuit().h(0).x(1).pauli_channel(1, 0.1, 0.2, 0.3) @@ -743,6 +768,7 @@ def test_noise_multi_probabilities(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_noise_multi_probabilities_with_parameter(): a = FreeParameter("a") @@ -762,6 +788,7 @@ def test_noise_multi_probabilities_with_parameter(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_pulse_gate_1_qubit_circuit(): circ = ( @@ -778,6 +805,7 @@ def test_pulse_gate_1_qubit_circuit(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_pulse_gate_multi_qubit_circuit(): circ = ( @@ -796,10 +824,12 @@ def test_pulse_gate_multi_qubit_circuit(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def _assert_correct_diagram(circ, expected): assert AsciiCircuitDiagram.build_diagram(circ) == "\n".join(expected) + @pytest.mark.skip def test_circuit_with_nested_target_list(): circ = ( @@ -824,6 +854,7 @@ def test_circuit_with_nested_target_list(): ) _assert_correct_diagram(circ, expected) + @pytest.mark.skip def test_hamiltonian(): circ = ( From 1cafb019fc1ed3f249a41ff7fb1111799230a134 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 26 Dec 2023 23:44:01 -0500 Subject: [PATCH 14/62] update more tests --- .../circuits/test_ascii_circuit_diagram.py | 172 ++++++++++-------- 1 file changed, 92 insertions(+), 80 deletions(-) diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py index f1b82f979..192643a2c 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -249,131 +249,143 @@ def test_connector_across_two_qubits(): _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_neg_control_qubits(): circ = Circuit().x(2, control=[0, 1], control_state=[0, 1]) expected = ( - "T : │0│", - " ", - "q0 : ─◯─", - " │ ", - "q1 : ─●─", - " │ ", - "q2 : ─X─", - "", - "T : │0│", + "T : │ 0 │", + " ", + "q0 : ───◯───", + " │ ", + " │ ", + "q1 : ───●───", + " │ ", + " ┌───┐ ", + "q2 : ─┤ X ├─", + " └───┘ ", + "T : │ 0 │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_only_neg_control_qubits(): circ = Circuit().x(2, control=[0, 1], control_state=0) expected = ( - "T : │0│", - " ", - "q0 : ─◯─", - " │ ", - "q1 : ─◯─", - " │ ", - "q2 : ─X─", - "", - "T : │0│", + "T : │ 0 │", + " ", + "q0 : ───◯───", + " │ ", + " │ ", + "q1 : ───◯───", + " │ ", + " ┌───┐ ", + "q2 : ─┤ X ├─", + " └───┘ ", + "T : │ 0 │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_connector_across_three_qubits(): circ = Circuit().x(control=(3, 4), target=5).h(range(2, 6)) expected = ( - "T : │0│1│", - " ", - "q2 : ─H───", - " ", - "q3 : ─●─H─", - " │ ", - "q4 : ─●─H─", - " │ ", - "q5 : ─X─H─", - "", - "T : │0│1│", + "T : │ 0 │ 1 │", + " ┌───┐ ", + "q2 : ─┤ H ├───────", + " └───┘ ", + " ┌───┐ ", + "q3 : ───●───┤ H ├─", + " │ └───┘ ", + " │ ┌───┐ ", + "q4 : ───●───┤ H ├─", + " │ └───┘ ", + " ┌───┐ ┌───┐ ", + "q5 : ─┤ X ├─┤ H ├─", + " └───┘ └───┘ ", + "T : │ 0 │ 1 │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_overlapping_qubits(): circ = Circuit().cnot(0, 2).x(control=1, target=3).h(0) expected = ( - "T : │ 0 │1│", - " ", - "q0 : ─●───H─", - " │ ", - "q1 : ─┼─●───", - " │ │ ", - "q2 : ─X─┼───", - " │ ", - "q3 : ───X───", - "", - "T : │ 0 │1│", + "T : │ 0 │ 1 │", + " ┌───┐ ", + "q0 : ───●─────────┤ H ├─", + " │ └───┘ ", + " │ ", + "q1 : ───┼─────●─────────", + " │ │ ", + " ┌───┐ │ ", + "q2 : ─┤ X ├───┼─────────", + " └───┘ │ ", + " ┌───┐ ", + "q3 : ───────┤ X ├───────", + " └───┘ ", + "T : │ 0 │ 1 │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_overlapping_qubits_angled_gates(): circ = Circuit().zz(0, 2, 0.15).x(control=1, target=3).h(0) expected = ( - "T : │ 0 │1│", - " ", - "q0 : ─ZZ(0.15)───H─", - " │ ", - "q1 : ─┼────────●───", - " │ │ ", - "q2 : ─ZZ(0.15)─┼───", - " │ ", - "q3 : ──────────X───", - "", - "T : │ 0 │1│", + "T : │ 0 │ 1 │", + " ┌──────────┐ ┌───┐ ", + "q0 : ─┤ ZZ(0.15) ├───────┤ H ├─", + " └──────────┘ └───┘ ", + " │ ", + "q1 : ───────┼────────●─────────", + " │ │ ", + " ┌──────────┐ │ ", + "q2 : ─┤ ZZ(0.15) ├───┼─────────", + " └──────────┘ │ ", + " ┌───┐ ", + "q3 : ──────────────┤ X ├───────", + " └───┘ ", + "T : │ 0 │ 1 │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_connector_across_gt_two_qubits(): circ = Circuit().h(4).x(control=3, target=5).h(4).h(2) expected = ( - "T : │ 0 │1│", - " ", - "q2 : ─H─────", - " ", - "q3 : ───●───", - " │ ", - "q4 : ─H─┼─H─", - " │ ", - "q5 : ───X───", - "", - "T : │ 0 │1│", + "T : │ 0 │ 1 │", + " ┌───┐ ", + "q2 : ─┤ H ├─────────────", + " └───┘ ", + " ", + "q3 : ─────────●─────────", + " │ ", + " ┌───┐ │ ┌───┐ ", + "q4 : ─┤ H ├───┼───┤ H ├─", + " └───┘ │ └───┘ ", + " ┌───┐ ", + "q5 : ───────┤ X ├───────", + " └───┘ ", + "T : │ 0 │ 1 │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_connector_across_non_used_qubits(): circ = Circuit().h(4).cnot(3, 100).h(4).h(101) expected = ( - "T : │ 0 │1│", - " ", - "q3 : ───●───", - " │ ", - "q4 : ─H─┼─H─", - " │ ", - "q100 : ───X───", - " ", - "q101 : ─H─────", - "", - "T : │ 0 │1│", + "T : │ 0 │ 1 │", + " ", + "q3 : ─────────●─────────", + " │ ", + " ┌───┐ │ ┌───┐ ", + "q4 : ─┤ H ├───┼───┤ H ├─", + " └───┘ │ └───┘ ", + " ┌───┐ ", + "q100 : ───────┤ X ├───────", + " └───┘ ", + " ┌───┐ ", + "q101 : ─┤ H ├─────────────", + " └───┘ ", + "T : │ 0 │ 1 │", ) _assert_correct_diagram(circ, expected) From de9fa6d16d03cc11c6789ae7e0d6f18ebf661afe Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 00:00:21 -0500 Subject: [PATCH 15/62] update verbatix box tests --- .../circuits/test_ascii_circuit_diagram.py | 184 +++++++++--------- 1 file changed, 96 insertions(+), 88 deletions(-) diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py index 192643a2c..81af97204 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -426,141 +426,147 @@ def test_verbatim_1q_following(): _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_verbatim_2q_no_preceding(): circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1)) expected = ( - "T : │ 0 │1│2│ 3 │", - " ", - "q0 : ─StartVerbatim─H─●─EndVerbatim─", - " │ │ │ ", - "q1 : ─*************───X─***********─", - "", - "T : │ 0 │1│2│ 3 │", + "T : │ 0 │ 1 │ 2 │ 3 │", + " ┌───┐ ", + "q0 : ───StartVerbatim───┤ H ├───●─────EndVerbatim───", + " ║ └───┘ │ ║ ", + " ║ ┌───┐ ║ ", + "q1 : ─────────╨───────────────┤ X ├────────╨────────", + " └───┘ ", + "T : │ 0 │ 1 │ 2 │ 3 │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_verbatim_2q_preceding(): circ = Circuit().h(0).add_verbatim_box(Circuit().h(0).cnot(0, 1)) expected = ( - "T : │0│ 1 │2│3│ 4 │", - " ", - "q0 : ─H─StartVerbatim─H─●─EndVerbatim─", - " │ │ │ ", - "q1 : ───*************───X─***********─", - "", - "T : │0│ 1 │2│3│ 4 │", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", + " ┌───┐ ┌───┐ ", + "q0 : ─┤ H ├───StartVerbatim───┤ H ├───●─────EndVerbatim───", + " └───┘ ║ └───┘ │ ║ ", + " ║ ┌───┐ ║ ", + "q1 : ───────────────╨───────────────┤ X ├────────╨────────", + " └───┘ ", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_verbatim_2q_following(): circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1)).h(0) expected = ( - "T : │ 0 │1│2│ 3 │4│", - " ", - "q0 : ─StartVerbatim─H─●─EndVerbatim─H─", - " │ │ │ ", - "q1 : ─*************───X─***********───", - "", - "T : │ 0 │1│2│ 3 │4│", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", + " ┌───┐ ┌───┐ ", + "q0 : ───StartVerbatim───┤ H ├───●─────EndVerbatim───┤ H ├─", + " ║ └───┘ │ ║ └───┘ ", + " ║ ┌───┐ ║ ", + "q1 : ─────────╨───────────────┤ X ├────────╨──────────────", + " └───┘ ", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_verbatim_3q_no_preceding(): circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1).cnot(1, 2)) expected = ( - "T : │ 0 │1│2│3│ 4 │", - " ", - "q0 : ─StartVerbatim─H─●───EndVerbatim─", - " │ │ │ ", - "q1 : ─┼───────────────X─●─┼───────────", - " │ │ │ ", - "q2 : ─*************─────X─***********─", - "", - "T : │ 0 │1│2│3│ 4 │", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", + " ┌───┐ ", + "q0 : ───StartVerbatim───┤ H ├───●───────────EndVerbatim───", + " ║ └───┘ │ ║ ", + " ║ ┌───┐ ║ ", + "q1 : ─────────║───────────────┤ X ├───●──────────║────────", + " ║ └───┘ │ ║ ", + " ║ ┌───┐ ║ ", + "q2 : ─────────╨─────────────────────┤ X ├────────╨────────", + " └───┘ ", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_verbatim_3q_preceding(): circ = Circuit().h(0).add_verbatim_box(Circuit().h(0).cnot(0, 1).cnot(1, 2)) expected = ( - "T : │0│ 1 │2│3│4│ 5 │", - " ", - "q0 : ─H─StartVerbatim─H─●───EndVerbatim─", - " │ │ │ ", - "q1 : ───┼───────────────X─●─┼───────────", - " │ │ │ ", - "q2 : ───*************─────X─***********─", - "", - "T : │0│ 1 │2│3│4│ 5 │", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │", + " ┌───┐ ┌───┐ ", + "q0 : ─┤ H ├───StartVerbatim───┤ H ├───●───────────EndVerbatim───", + " └───┘ ║ └───┘ │ ║ ", + " ║ ┌───┐ ║ ", + "q1 : ───────────────║───────────────┤ X ├───●──────────║────────", + " ║ └───┘ │ ║ ", + " ║ ┌───┐ ║ ", + "q2 : ───────────────╨─────────────────────┤ X ├────────╨────────", + " └───┘ ", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_verbatim_3q_following(): circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1).cnot(1, 2)).h(0) expected = ( - "T : │ 0 │1│2│3│ 4 │5│", - " ", - "q0 : ─StartVerbatim─H─●───EndVerbatim─H─", - " │ │ │ ", - "q1 : ─┼───────────────X─●─┼─────────────", - " │ │ │ ", - "q2 : ─*************─────X─***********───", - "", - "T : │ 0 │1│2│3│ 4 │5│", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │", + " ┌───┐ ┌───┐ ", + "q0 : ───StartVerbatim───┤ H ├───●───────────EndVerbatim───┤ H ├─", + " ║ └───┘ │ ║ └───┘ ", + " ║ ┌───┐ ║ ", + "q1 : ─────────║───────────────┤ X ├───●──────────║──────────────", + " ║ └───┘ │ ║ ", + " ║ ┌───┐ ║ ", + "q2 : ─────────╨─────────────────────┤ X ├────────╨──────────────", + " └───┘ ", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_verbatim_different_qubits(): circ = Circuit().h(1).add_verbatim_box(Circuit().h(0)).cnot(3, 4) expected = ( - "T : │0│ 1 │2│ 3 │4│", - " ", - "q0 : ───StartVerbatim─H─EndVerbatim───", - " │ │ ", - "q1 : ─H─┼───────────────┼─────────────", - " │ │ ", - "q3 : ───┼───────────────┼───────────●─", - " │ │ │ ", - "q4 : ───*************───***********─X─", - "", - "T : │0│ 1 │2│ 3 │4│", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", + " ┌───┐ ", + "q0 : ─────────StartVerbatim───┤ H ├───EndVerbatim─────────", + " ║ └───┘ ║ ", + " ┌───┐ ║ ║ ", + "q1 : ─┤ H ├─────────║──────────────────────║──────────────", + " └───┘ ║ ║ ", + " ║ ║ ", + "q3 : ───────────────║──────────────────────║──────────●───", + " ║ ║ │ ", + " ║ ║ ┌───┐ ", + "q4 : ───────────────╨──────────────────────╨────────┤ X ├─", + " └───┘ ", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_verbatim_qubset_qubits(): circ = Circuit().h(1).cnot(0, 1).cnot(1, 2).add_verbatim_box(Circuit().h(1)).cnot(2, 3) expected = ( - "T : │0│1│2│ 3 │4│ 5 │6│", - " ", - "q0 : ───●───StartVerbatim───EndVerbatim───", - " │ │ │ ", - "q1 : ─H─X─●─┼─────────────H─┼─────────────", - " │ │ │ ", - "q2 : ─────X─┼───────────────┼───────────●─", - " │ │ │ ", - "q3 : ───────*************───***********─X─", - "", - "T : │0│1│2│ 3 │4│ 5 │6│", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │", + " ", + "q0 : ─────────●───────────StartVerbatim───────────EndVerbatim─────────", + " │ ║ ║ ", + " ┌───┐ ┌───┐ ║ ┌───┐ ║ ", + "q1 : ─┤ H ├─┤ X ├───●───────────║─────────┤ H ├────────║──────────────", + " └───┘ └───┘ │ ║ └───┘ ║ ", + " ┌───┐ ║ ║ ", + "q2 : ─────────────┤ X ├─────────║──────────────────────║──────────●───", + " └───┘ ║ ║ │ ", + " ║ ║ ┌───┐ ", + "q3 : ───────────────────────────╨──────────────────────╨────────┤ X ├─", + " └───┘ ", + "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_ignore_non_gates(): class Foo(Operator): @property @@ -572,15 +578,17 @@ def to_ir(self, target): circ = Circuit().h(0).h(1).cnot(1, 2).add_instruction(Instruction(Foo(), 0)) expected = ( - "T : │0│1│", - " ", - "q0 : ─H───", - " ", - "q1 : ─H─●─", - " │ ", - "q2 : ───X─", - "", - "T : │0│1│", + "T : │ 0 │ 1 │", + " ┌───┐ ", + "q0 : ─┤ H ├───────", + " └───┘ ", + " ┌───┐ ", + "q1 : ─┤ H ├───●───", + " └───┘ │ ", + " ┌───┐ ", + "q2 : ───────┤ X ├─", + " └───┘ ", + "T : │ 0 │ 1 │", ) _assert_correct_diagram(circ, expected) From a1fefe647f43820bc9ac562f30e0d6d560faf22f Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 00:25:53 -0500 Subject: [PATCH 16/62] update last tests, left 6 xfail --- .../circuits/test_ascii_circuit_diagram.py | 276 +++++++++--------- 1 file changed, 145 insertions(+), 131 deletions(-) diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py index 81af97204..98430dc04 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -593,22 +593,22 @@ def to_ir(self, target): _assert_correct_diagram(circ, expected) -@pytest.mark.skip +@pytest.mark.xfail def test_result_types_target_none(): circ = Circuit().h(0).h(100).probability() expected = ( - "T : │0│Result Types│", - " ", - "q0 : ─H─Probability──", - " │ ", - "q100 : ─H─Probability──", - "", - "T : │0│Result Types│", + "T : │ 0 │ Result Types │", + " ┌───┐ ┌─────────────┐ ", + "q0 : ─┤ H ├─┤ Probability ├─", + " └───┘ └──────┬──────┘ ", + " ┌───┐ ┌──────┴──────┐ ", + "q100 : ─┤ H ├─┤ Probability ├─", + " └───┘ └─────────────┘ ", + "T : │ 0 │ Result Types │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_result_types_target_some(): circ = ( Circuit() @@ -618,39 +618,42 @@ def test_result_types_target_some(): .expectation(observable=Observable.Y() @ Observable.Z(), target=[0, 100]) ) expected = ( - "T : │0│ Result Types │", - " ", - "q0 : ─H─Expectation(Y@Z)─", - " │ ", - "q1 : ─H─┼────────────────", - " │ ", - "q100 : ─H─Expectation(Y@Z)─", - "", - "T : │0│ Result Types │", + "T : │ 0 │ Result Types │", + " ┌───┐ ┌──────────────────┐ ", + "q0 : ─┤ H ├─┤ Expectation(Y@Z) ├─", + " └───┘ └──────────────────┘ ", + " ┌───┐ │ ", + "q1 : ─┤ H ├───────────┼──────────", + " └───┘ │ ", + " ┌───┐ ┌──────────────────┐ ", + "q100 : ─┤ H ├─┤ Expectation(Y@Z) ├─", + " └───┘ └──────────────────┘ ", + "T : │ 0 │ Result Types │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_additional_result_types(): circ = Circuit().h(0).h(1).h(100).state_vector().amplitude(["110", "001"]) expected = ( - "T : │0│", - " ", - "q0 : ─H─", - " ", - "q1 : ─H─", - " ", - "q100 : ─H─", - "", - "T : │0│", + "T : │ 0 │", + " ┌───┐ ", + "q0 : ─┤ H ├─", + " └───┘ ", + " ┌───┐ ", + "q1 : ─┤ H ├─", + " └───┘ ", + " ┌───┐ ", + "q100 : ─┤ H ├─", + " └───┘ ", + "T : │ 0 │", "", "Additional result types: StateVector, Amplitude(110,001)", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip +@pytest.mark.xfail def test_multiple_result_types(): circ = ( Circuit() @@ -662,22 +665,24 @@ def test_multiple_result_types(): .sample(observable=Observable.Y()) ) expected = ( - "T : │ 0 │1│ Result Types │", - " ", - "q0 : ─●───H─Variance(Y)────Sample(Y)─", - " │ │ ", - "q1 : ─┼─●──────────────────Sample(Y)─", - " │ │ │ ", - "q2 : ─X─┼───Expectation(Y)─Sample(Y)─", - " │ │ ", - "q3 : ───X──────────────────Sample(Y)─", - "", - "T : │ 0 │1│ Result Types │", + "T : │ 0 │ 1 │ Result Types │", + " ┌───┐ ┌─────────────┐ ┌───────────┐ ", + "q0 : ───●─────────┤ H ├───┤ Variance(Y) ├──┤ Sample(Y) ├─", + " │ └───┘ └─────────────┘ └─────┬─────┘ ", + " │ ┌─────┴─────┐ ", + "q1 : ───┼─────●────────────────────────────┤ Sample(Y) ├─", + " │ │ └─────┬─────┘ ", + " ┌───┐ │ ┌────────────────┐ ┌─────┴─────┐ ", + "q2 : ─┤ X ├───┼─────────┤ Expectation(Y) ├─┤ Sample(Y) ├─", + " └───┘ │ └────────────────┘ └─────┬─────┘ ", + " ┌───┐ ┌─────┴─────┐ ", + "q3 : ───────┤ X ├──────────────────────────┤ Sample(Y) ├─", + " └───┘ └───────────┘ ", + "T : │ 0 │ 1 │ Result Types │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_multiple_result_types_with_state_vector_amplitude(): circ = ( Circuit() @@ -691,24 +696,27 @@ def test_multiple_result_types_with_state_vector_amplitude(): .state_vector() ) expected = ( - "T : │ 0 │1│ Result Types │", - " ", - "q0 : ─●───H─Variance(Y)────────────", - " │ ", - "q1 : ─┼─●───Expectation(Hermitian)─", - " │ │ ", - "q2 : ─X─┼──────────────────────────", - " │ ", - "q3 : ───X───Expectation(Y)─────────", - "", - "T : │ 0 │1│ Result Types │", + "T : │ 0 │ 1 │ Result Types │", + " ┌───┐ ┌─────────────┐ ", + "q0 : ───●─────────┤ H ├───────┤ Variance(Y) ├──────", + " │ └───┘ └─────────────┘ ", + " │ ┌────────────────────────┐ ", + "q1 : ───┼─────●─────────┤ Expectation(Hermitian) ├─", + " │ │ └────────────────────────┘ ", + " ┌───┐ │ ", + "q2 : ─┤ X ├───┼────────────────────────────────────", + " └───┘ │ ", + " ┌───┐ ┌────────────────┐ ", + "q3 : ───────┤ X ├───────────┤ Expectation(Y) ├─────", + " └───┘ └────────────────┘ ", + "T : │ 0 │ 1 │ Result Types │", "", "Additional result types: Amplitude(0001), StateVector", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip +@pytest.mark.xfail def test_multiple_result_types_with_custom_hermitian_ascii_symbol(): herm_matrix = (Observable.Y() @ Observable.Z()).to_matrix() circ = ( @@ -727,89 +735,92 @@ def test_multiple_result_types_with_custom_hermitian_ascii_symbol(): ) ) expected = ( - "T : │ 0 │1│ Result Types │", - " ", - "q0 : ─●───H─Variance(Y)─────────", - " │ ", - "q1 : ─┼─●───Expectation(MyHerm)─", - " │ │ │ ", - "q2 : ─X─┼───Expectation(MyHerm)─", - " │ ", - "q3 : ───X───Expectation(Y)──────", - "", - "T : │ 0 │1│ Result Types │", + "T : │ 0 │ 1 │ Result Types │", + " ┌───┐ ┌─────────────┐ ", + "q0 : ───●─────────┤ H ├─────┤ Variance(Y) ├─────", + " │ └───┘ └─────────────┘ ", + " │ ┌─────────────────────┐ ", + "q1 : ───┼─────●─────────┤ Expectation(MyHerm) ├─", + " │ │ └──────────┬──────────┘ ", + " ┌───┐ │ ┌──────────┴──────────┐ ", + "q2 : ─┤ X ├───┼─────────┤ Expectation(MyHerm) ├─", + " └───┘ │ └─────────────────────┘ ", + " ┌───┐ ┌────────────────┐ ", + "q3 : ───────┤ X ├──────────┤ Expectation(Y) ├───", + " └───┘ └────────────────┘ ", + "T : │ 0 │ 1 │ Result Types │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_noise_1qubit(): circ = Circuit().h(0).x(1).bit_flip(1, 0.1) expected = ( - "T : │ 0 │", - " ", - "q0 : ─H─────────", - " ", - "q1 : ─X─BF(0.1)─", - "", - "T : │ 0 │", + "T : │ 0 │", + " ┌───┐ ", + "q0 : ─┤ H ├─────────────", + " └───┘ ", + " ┌───┐ ┌─────────┐ ", + "q1 : ─┤ X ├─┤ BF(0.1) ├─", + " └───┘ └─────────┘ ", + "T : │ 0 │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_noise_2qubit(): circ = Circuit().h(1).kraus((0, 2), [np.eye(4)]) expected = ( - "T : │ 0 │", - " ", - "q0 : ───KR─", - " │ ", - "q1 : ─H─┼──", - " │ ", - "q2 : ───KR─", - "", - "T : │ 0 │", + "T : │ 0 │", + " ┌────┐ ", + "q0 : ───────┤ KR ├─", + " └────┘ ", + " ┌───┐ │ ", + "q1 : ─┤ H ├────┼───", + " └───┘ │ ", + " ┌────┐ ", + "q2 : ───────┤ KR ├─", + " └────┘ ", + "T : │ 0 │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_noise_multi_probabilities(): circ = Circuit().h(0).x(1).pauli_channel(1, 0.1, 0.2, 0.3) expected = ( - "T : │ 0 │", - " ", - "q0 : ─H─────────────────", - " ", - "q1 : ─X─PC(0.1,0.2,0.3)─", - "", - "T : │ 0 │", + "T : │ 0 │", + " ┌───┐ ", + "q0 : ─┤ H ├─────────────────────", + " └───┘ ", + " ┌───┐ ┌─────────────────┐ ", + "q1 : ─┤ X ├─┤ PC(0.1,0.2,0.3) ├─", + " └───┘ └─────────────────┘ ", + "T : │ 0 │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_noise_multi_probabilities_with_parameter(): a = FreeParameter("a") b = FreeParameter("b") c = FreeParameter("c") circ = Circuit().h(0).x(1).pauli_channel(1, a, b, c) expected = ( - "T : │ 0 │", - " ", - "q0 : ─H───────────", - " ", - "q1 : ─X─PC(a,b,c)─", - "", - "T : │ 0 │", + "T : │ 0 │", + " ┌───┐ ", + "q0 : ─┤ H ├───────────────", + " └───┘ ", + " ┌───┐ ┌───────────┐ ", + "q1 : ─┤ X ├─┤ PC(a,b,c) ├─", + " └───┘ └───────────┘ ", + "T : │ 0 │", "", "Unassigned parameters: [a, b, c].", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def test_pulse_gate_1_qubit_circuit(): circ = ( Circuit() @@ -817,16 +828,16 @@ def test_pulse_gate_1_qubit_circuit(): .pulse_gate(0, PulseSequence().set_phase(Frame("x", Port("px", 1e-9), 1e9, 0), 0)) ) expected = ( - "T : │0│1 │", - " ", - "q0 : ─H─PG─", - "", - "T : │0│1 │", + "T : │ 0 │ 1 │", + " ┌───┐ ┌────┐ ", + "q0 : ─┤ H ├─┤ PG ├─", + " └───┘ └────┘ ", + "T : │ 0 │ 1 │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip +@pytest.mark.xfail def test_pulse_gate_multi_qubit_circuit(): circ = ( Circuit() @@ -834,23 +845,23 @@ def test_pulse_gate_multi_qubit_circuit(): .pulse_gate([0, 1], PulseSequence().set_phase(Frame("x", Port("px", 1e-9), 1e9, 0), 0)) ) expected = ( - "T : │0│1 │", - " ", - "q0 : ─H─PG─", - " │ ", - "q1 : ───PG─", - "", - "T : │0│1 │", + "T : │ 0 │ 1 │", + " ┌───┐ ┌────┐ ", + "q0 : ─┤ H ├─┤ PG ├─", + " └───┘ └─┬──┘ ", + " ┌─┴──┐ ", + "q1 : ───────┤ PG ├─", + " └────┘ ", + "T : │ 0 │ 1 │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip def _assert_correct_diagram(circ, expected): assert AsciiCircuitDiagram.build_diagram(circ) == "\n".join(expected) -@pytest.mark.skip +@pytest.mark.xfail def test_circuit_with_nested_target_list(): circ = ( Circuit() @@ -864,18 +875,19 @@ def test_circuit_with_nested_target_list(): ) expected = ( - "T : │0│ Result Types │", - " ", - "q0 : ─H─Expectation(Hamiltonian)─", - " │ ", - "q1 : ─H─Expectation(Hamiltonian)─", - "", - "T : │0│ Result Types │", + "T : │ 0 │ Result Types │", + " ┌───┐ ┌──────────────────────────┐ ", + "q0 : ─┤ H ├─┤ Expectation(Hamiltonian) ├─", + " └───┘ └─────────────┬────────────┘ ", + " ┌───┐ ┌─────────────┴────────────┐ ", + "q1 : ─┤ H ├─┤ Expectation(Hamiltonian) ├─", + " └───┘ └──────────────────────────┘ ", + "T : │ 0 │ Result Types │", ) _assert_correct_diagram(circ, expected) -@pytest.mark.skip +@pytest.mark.xfail def test_hamiltonian(): circ = ( Circuit() @@ -888,15 +900,17 @@ def test_hamiltonian(): ) ) expected = ( - "T : │0│1│ 2 │ Result Types │", - " ", - "q0 : ─H─●─Rx(theta)─AdjointGradient(Hamiltonian)─", - " │ │ ", - "q1 : ───X───────────AdjointGradient(Hamiltonian)─", - " │ ", - "q2 : ───────────────AdjointGradient(Hamiltonian)─", - "", - "T : │0│1│ 2 │ Result Types │", + "T : │ 0 │ 1 │ 2 │ Result Types │", + " ┌───┐ ┌───────────┐ ┌──────────────────────────────┐ ", + "q0 : ─┤ H ├───●───┤ Rx(theta) ├─┤ AdjointGradient(Hamiltonian) ├─", + " └───┘ │ └───────────┘ └───────────────┬──────────────┘ ", + " ┌───┐ ┌───────────────┴──────────────┐ ", + "q1 : ───────┤ X ├───────────────┤ AdjointGradient(Hamiltonian) ├─", + " └───┘ └───────────────┬──────────────┘ ", + " ┌───────────────┴──────────────┐ ", + "q2 : ───────────────────────────┤ AdjointGradient(Hamiltonian) ├─", + " └──────────────────────────────┘ ", + "T : │ 0 │ 1 │ 2 │ Result Types │", "", "Unassigned parameters: [theta].", ) From dd0f222e85185e7c17eb121d689eba921dd1bad4 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 00:37:59 -0500 Subject: [PATCH 17/62] remove margin --- src/braket/circuits/ascii_circuit_diagram.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index a2f9f8f58..e36162b6e 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -300,7 +300,6 @@ def _ascii_diagram_column( str: an ASCII string diagram for the specified moment in time for a column. """ symbols = {qubit: "─" for qubit in circuit_qubits} - margins = {qubit: " " for qubit in circuit_qubits} connections = {qubit: "none" for qubit in circuit_qubits} for item in items: @@ -390,10 +389,6 @@ def _ascii_diagram_column( 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): - margins[qubit] = "│" - output = AsciiCircuitDiagram._create_output( symbols, connections, circuit_qubits, global_phase ) From 5a40951644fd7a2c47858a9cc9fd0a149c371c49 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 10:06:13 -0500 Subject: [PATCH 18/62] add connecting edges --- src/braket/circuits/ascii_circuit_diagram.py | 56 +++++--- .../circuits/test_ascii_circuit_diagram.py | 128 +++++++++--------- 2 files changed, 99 insertions(+), 85 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index e36162b6e..fdfc7a7d4 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -308,6 +308,10 @@ def _ascii_diagram_column( control_qubits = QubitSet() target_and_control = target_qubits.union(control_qubits) qubits = circuit_qubits + if len(qubits) > 1: + connections |= {qubit: "both" for qubit in qubits[1:-1]} + connections[qubits[-1]] = "above" + connections[qubits[0]] = "below" ascii_symbols = [item.ascii_symbols[0]] * len(circuit_qubits) elif isinstance(item, Instruction) and isinstance(item.operator, CompilerDirective): target_qubits = circuit_qubits @@ -342,6 +346,10 @@ def _ascii_diagram_column( target_and_control = target_qubits.union(control_qubits) qubits = QubitSet(range(min(target_and_control), max(target_and_control) + 1)) + if len(qubits) > 1: + connections |= {qubit: "both" for qubit in qubits[1:-1]} + connections[qubits[-1]] = "above" + connections[qubits[0]] = "below" ascii_symbols = item.ascii_symbols @@ -427,42 +435,54 @@ def _create_output( def _draw_symbol( symbol: str, symbols_width: int, connection: Literal["above, below, both, none"] = "none" ) -> str: + def fill_symbol(symbol: str, filler: str, width: int | None = None) -> str: + return "{0:{fill}{align}{width}}".format( + symbol, + fill=filler, + align="^", + width=width if width is not None else len(symbol) + 1, + ) + top = "" bottom = "" if symbol in ["●", "◯"]: - if connection == "above" or connection == "both": - top = "│" - if connection == "below" or connection == "both": - bottom = "│" + if connection in ["above", "both"]: + top = fill_symbol("│", " ") + if connection in ["below", "both"]: + bottom = fill_symbol("│", " ") + symbol = fill_symbol(f"{symbol}", "─") elif symbol in ["StartVerbatim", "EndVerbatim"]: if connection == "below": bottom = "║" elif connection == "both": - top = bottom = symbol = "║" + top = bottom = "║" + symbol = "║" elif connection == "above": top = "║" symbol = "╨" bottom = "" + top = fill_symbol(top, " ") + bottom = fill_symbol(bottom, " ") + symbol = fill_symbol(symbol, "─") elif symbol == "┼": - top = "│" - bottom = "│" + top = fill_symbol("│", " ") + bottom = fill_symbol("│", " ") + symbol = fill_symbol(f"{symbol}", "─") elif symbol == "─": # We do not box when no gate is applied. pass else: - top = f"┌─{'─' * len(symbol)}─┐" - bottom = f"└─{'─' * len(symbol)}─┘" + top_edge_symbol = "┴" if connection == "above" or connection == "both" else "─" + top = f"┌─{fill_symbol(top_edge_symbol, '─', len(symbol))}─┐" + + bottom_edge_symbol = "┬" if connection == "below" or connection == "both" else "─" + bottom = f"└─{fill_symbol(bottom_edge_symbol, '─', len(symbol))}─┘" + symbol = f"┤ {symbol} ├" - output = "{0:{fill}{align}{width}}\n".format( - top, fill=" ", align="^", width=symbols_width + 1 - ) - output += "{0:{fill}{align}{width}}\n".format( - symbol, fill="─", align="^", width=symbols_width + 1 - ) - output += "{0:{fill}{align}{width}}\n".format( - bottom, fill=" ", align="^", width=symbols_width + 1 - ) + output = fill_symbol(top, " ", symbols_width + 1) + "\n" + output += fill_symbol(symbol, "─", symbols_width + 1) + "\n" + output += fill_symbol(bottom, " ", symbols_width + 1) + "\n" return output @staticmethod diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py index 98430dc04..8d3d97161 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -202,25 +202,25 @@ def test_time_width(): " ", "q0 : ───●───────────────────────────────────────", " │ ", - " ┌───┐ ", + " ┌─┴─┐ ", "q1 : ─┤ X ├───●─────────────────────────────────", " └───┘ │ ", - " ┌───┐ ", + " ┌─┴─┐ ", "q2 : ───────┤ X ├───●───────────────────────────", " └───┘ │ ", - " ┌───┐ ", + " ┌─┴─┐ ", "q3 : ─────────────┤ X ├───●─────────────────────", " └───┘ │ ", - " ┌───┐ ", + " ┌─┴─┐ ", "q4 : ───────────────────┤ X ├───●───────────────", " └───┘ │ ", - " ┌───┐ ", + " ┌─┴─┐ ", "q5 : ─────────────────────────┤ X ├───●─────────", " └───┘ │ ", - " ┌───┐ ", + " ┌─┴─┐ ", "q6 : ───────────────────────────────┤ X ├───●───", " └───┘ │ ", - " ┌───┐ ", + " ┌─┴─┐ ", "q7 : ─────────────────────────────────────┤ X ├─", " └───┘ ", "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │", @@ -238,7 +238,7 @@ def test_connector_across_two_qubits(): " ┌───┐ ", "q3 : ───●───┤ H ├─", " │ └───┘ ", - " ┌───┐ ┌───┐ ", + " ┌─┴─┐ ┌───┐ ", "q4 : ─┤ X ├─┤ H ├─", " └───┘ └───┘ ", " ┌───┐ ", @@ -259,7 +259,7 @@ def test_neg_control_qubits(): " │ ", "q1 : ───●───", " │ ", - " ┌───┐ ", + " ┌─┴─┐ ", "q2 : ─┤ X ├─", " └───┘ ", "T : │ 0 │", @@ -277,7 +277,7 @@ def test_only_neg_control_qubits(): " │ ", "q1 : ───◯───", " │ ", - " ┌───┐ ", + " ┌─┴─┐ ", "q2 : ─┤ X ├─", " └───┘ ", "T : │ 0 │", @@ -298,7 +298,7 @@ def test_connector_across_three_qubits(): " │ ┌───┐ ", "q4 : ───●───┤ H ├─", " │ └───┘ ", - " ┌───┐ ┌───┐ ", + " ┌─┴─┐ ┌───┐ ", "q5 : ─┤ X ├─┤ H ├─", " └───┘ └───┘ ", "T : │ 0 │ 1 │", @@ -316,10 +316,10 @@ def test_overlapping_qubits(): " │ ", "q1 : ───┼─────●─────────", " │ │ ", - " ┌───┐ │ ", + " ┌─┴─┐ │ ", "q2 : ─┤ X ├───┼─────────", " └───┘ │ ", - " ┌───┐ ", + " ┌─┴─┐ ", "q3 : ───────┤ X ├───────", " └───┘ ", "T : │ 0 │ 1 │", @@ -333,14 +333,14 @@ def test_overlapping_qubits_angled_gates(): "T : │ 0 │ 1 │", " ┌──────────┐ ┌───┐ ", "q0 : ─┤ ZZ(0.15) ├───────┤ H ├─", - " └──────────┘ └───┘ ", - " │ ", - "q1 : ───────┼────────●─────────", - " │ │ ", - " ┌──────────┐ │ ", + " └────┬─────┘ └───┘ ", + " │ ", + "q1 : ──────┼─────────●─────────", + " │ │ ", + " ┌────┴─────┐ │ ", "q2 : ─┤ ZZ(0.15) ├───┼─────────", " └──────────┘ │ ", - " ┌───┐ ", + " ┌─┴─┐ ", "q3 : ──────────────┤ X ├───────", " └───┘ ", "T : │ 0 │ 1 │", @@ -361,7 +361,7 @@ def test_connector_across_gt_two_qubits(): " ┌───┐ │ ┌───┐ ", "q4 : ─┤ H ├───┼───┤ H ├─", " └───┘ │ └───┘ ", - " ┌───┐ ", + " ┌─┴─┐ ", "q5 : ───────┤ X ├───────", " └───┘ ", "T : │ 0 │ 1 │", @@ -379,7 +379,7 @@ def test_connector_across_non_used_qubits(): " ┌───┐ │ ┌───┐ ", "q4 : ─┤ H ├───┼───┤ H ├─", " └───┘ │ └───┘ ", - " ┌───┐ ", + " ┌─┴─┐ ", "q100 : ───────┤ X ├───────", " └───┘ ", " ┌───┐ ", @@ -433,7 +433,7 @@ def test_verbatim_2q_no_preceding(): " ┌───┐ ", "q0 : ───StartVerbatim───┤ H ├───●─────EndVerbatim───", " ║ └───┘ │ ║ ", - " ║ ┌───┐ ║ ", + " ║ ┌─┴─┐ ║ ", "q1 : ─────────╨───────────────┤ X ├────────╨────────", " └───┘ ", "T : │ 0 │ 1 │ 2 │ 3 │", @@ -448,7 +448,7 @@ def test_verbatim_2q_preceding(): " ┌───┐ ┌───┐ ", "q0 : ─┤ H ├───StartVerbatim───┤ H ├───●─────EndVerbatim───", " └───┘ ║ └───┘ │ ║ ", - " ║ ┌───┐ ║ ", + " ║ ┌─┴─┐ ║ ", "q1 : ───────────────╨───────────────┤ X ├────────╨────────", " └───┘ ", "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", @@ -463,7 +463,7 @@ def test_verbatim_2q_following(): " ┌───┐ ┌───┐ ", "q0 : ───StartVerbatim───┤ H ├───●─────EndVerbatim───┤ H ├─", " ║ └───┘ │ ║ └───┘ ", - " ║ ┌───┐ ║ ", + " ║ ┌─┴─┐ ║ ", "q1 : ─────────╨───────────────┤ X ├────────╨──────────────", " └───┘ ", "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", @@ -478,10 +478,10 @@ def test_verbatim_3q_no_preceding(): " ┌───┐ ", "q0 : ───StartVerbatim───┤ H ├───●───────────EndVerbatim───", " ║ └───┘ │ ║ ", - " ║ ┌───┐ ║ ", + " ║ ┌─┴─┐ ║ ", "q1 : ─────────║───────────────┤ X ├───●──────────║────────", " ║ └───┘ │ ║ ", - " ║ ┌───┐ ║ ", + " ║ ┌─┴─┐ ║ ", "q2 : ─────────╨─────────────────────┤ X ├────────╨────────", " └───┘ ", "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", @@ -496,10 +496,10 @@ def test_verbatim_3q_preceding(): " ┌───┐ ┌───┐ ", "q0 : ─┤ H ├───StartVerbatim───┤ H ├───●───────────EndVerbatim───", " └───┘ ║ └───┘ │ ║ ", - " ║ ┌───┐ ║ ", + " ║ ┌─┴─┐ ║ ", "q1 : ───────────────║───────────────┤ X ├───●──────────║────────", " ║ └───┘ │ ║ ", - " ║ ┌───┐ ║ ", + " ║ ┌─┴─┐ ║ ", "q2 : ───────────────╨─────────────────────┤ X ├────────╨────────", " └───┘ ", "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │", @@ -514,10 +514,10 @@ def test_verbatim_3q_following(): " ┌───┐ ┌───┐ ", "q0 : ───StartVerbatim───┤ H ├───●───────────EndVerbatim───┤ H ├─", " ║ └───┘ │ ║ └───┘ ", - " ║ ┌───┐ ║ ", + " ║ ┌─┴─┐ ║ ", "q1 : ─────────║───────────────┤ X ├───●──────────║──────────────", " ║ └───┘ │ ║ ", - " ║ ┌───┐ ║ ", + " ║ ┌─┴─┐ ║ ", "q2 : ─────────╨─────────────────────┤ X ├────────╨──────────────", " └───┘ ", "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │", @@ -538,7 +538,7 @@ def test_verbatim_different_qubits(): " ║ ║ ", "q3 : ───────────────║──────────────────────║──────────●───", " ║ ║ │ ", - " ║ ║ ┌───┐ ", + " ║ ║ ┌─┴─┐ ", "q4 : ───────────────╨──────────────────────╨────────┤ X ├─", " └───┘ ", "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", @@ -553,13 +553,13 @@ def test_verbatim_qubset_qubits(): " ", "q0 : ─────────●───────────StartVerbatim───────────EndVerbatim─────────", " │ ║ ║ ", - " ┌───┐ ┌───┐ ║ ┌───┐ ║ ", + " ┌───┐ ┌─┴─┐ ║ ┌───┐ ║ ", "q1 : ─┤ H ├─┤ X ├───●───────────║─────────┤ H ├────────║──────────────", " └───┘ └───┘ │ ║ └───┘ ║ ", - " ┌───┐ ║ ║ ", + " ┌─┴─┐ ║ ║ ", "q2 : ─────────────┤ X ├─────────║──────────────────────║──────────●───", " └───┘ ║ ║ │ ", - " ║ ║ ┌───┐ ", + " ║ ║ ┌─┴─┐ ", "q3 : ───────────────────────────╨──────────────────────╨────────┤ X ├─", " └───┘ ", "T : │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │", @@ -585,7 +585,7 @@ def to_ir(self, target): " ┌───┐ ", "q1 : ─┤ H ├───●───", " └───┘ │ ", - " ┌───┐ ", + " ┌─┴─┐ ", "q2 : ───────┤ X ├─", " └───┘ ", "T : │ 0 │ 1 │", @@ -593,7 +593,6 @@ def to_ir(self, target): _assert_correct_diagram(circ, expected) -@pytest.mark.xfail def test_result_types_target_none(): circ = Circuit().h(0).h(100).probability() expected = ( @@ -621,11 +620,11 @@ def test_result_types_target_some(): "T : │ 0 │ Result Types │", " ┌───┐ ┌──────────────────┐ ", "q0 : ─┤ H ├─┤ Expectation(Y@Z) ├─", - " └───┘ └──────────────────┘ ", - " ┌───┐ │ ", - "q1 : ─┤ H ├───────────┼──────────", - " └───┘ │ ", - " ┌───┐ ┌──────────────────┐ ", + " └───┘ └────────┬─────────┘ ", + " ┌───┐ │ ", + "q1 : ─┤ H ├──────────┼───────────", + " └───┘ │ ", + " ┌───┐ ┌────────┴─────────┐ ", "q100 : ─┤ H ├─┤ Expectation(Y@Z) ├─", " └───┘ └──────────────────┘ ", "T : │ 0 │ Result Types │", @@ -653,7 +652,6 @@ def test_additional_result_types(): _assert_correct_diagram(circ, expected) -@pytest.mark.xfail def test_multiple_result_types(): circ = ( Circuit() @@ -672,10 +670,10 @@ def test_multiple_result_types(): " │ ┌─────┴─────┐ ", "q1 : ───┼─────●────────────────────────────┤ Sample(Y) ├─", " │ │ └─────┬─────┘ ", - " ┌───┐ │ ┌────────────────┐ ┌─────┴─────┐ ", + " ┌─┴─┐ │ ┌────────────────┐ ┌─────┴─────┐ ", "q2 : ─┤ X ├───┼─────────┤ Expectation(Y) ├─┤ Sample(Y) ├─", " └───┘ │ └────────────────┘ └─────┬─────┘ ", - " ┌───┐ ┌─────┴─────┐ ", + " ┌─┴─┐ ┌─────┴─────┐ ", "q3 : ───────┤ X ├──────────────────────────┤ Sample(Y) ├─", " └───┘ └───────────┘ ", "T : │ 0 │ 1 │ Result Types │", @@ -703,10 +701,10 @@ def test_multiple_result_types_with_state_vector_amplitude(): " │ ┌────────────────────────┐ ", "q1 : ───┼─────●─────────┤ Expectation(Hermitian) ├─", " │ │ └────────────────────────┘ ", - " ┌───┐ │ ", + " ┌─┴─┐ │ ", "q2 : ─┤ X ├───┼────────────────────────────────────", " └───┘ │ ", - " ┌───┐ ┌────────────────┐ ", + " ┌─┴─┐ ┌────────────────┐ ", "q3 : ───────┤ X ├───────────┤ Expectation(Y) ├─────", " └───┘ └────────────────┘ ", "T : │ 0 │ 1 │ Result Types │", @@ -716,7 +714,6 @@ def test_multiple_result_types_with_state_vector_amplitude(): _assert_correct_diagram(circ, expected) -@pytest.mark.xfail def test_multiple_result_types_with_custom_hermitian_ascii_symbol(): herm_matrix = (Observable.Y() @ Observable.Z()).to_matrix() circ = ( @@ -742,10 +739,10 @@ def test_multiple_result_types_with_custom_hermitian_ascii_symbol(): " │ ┌─────────────────────┐ ", "q1 : ───┼─────●─────────┤ Expectation(MyHerm) ├─", " │ │ └──────────┬──────────┘ ", - " ┌───┐ │ ┌──────────┴──────────┐ ", + " ┌─┴─┐ │ ┌──────────┴──────────┐ ", "q2 : ─┤ X ├───┼─────────┤ Expectation(MyHerm) ├─", " └───┘ │ └─────────────────────┘ ", - " ┌───┐ ┌────────────────┐ ", + " ┌─┴─┐ ┌────────────────┐ ", "q3 : ───────┤ X ├──────────┤ Expectation(Y) ├───", " └───┘ └────────────────┘ ", "T : │ 0 │ 1 │ Result Types │", @@ -774,11 +771,11 @@ def test_noise_2qubit(): "T : │ 0 │", " ┌────┐ ", "q0 : ───────┤ KR ├─", - " └────┘ ", - " ┌───┐ │ ", - "q1 : ─┤ H ├────┼───", - " └───┘ │ ", - " ┌────┐ ", + " └─┬──┘ ", + " ┌───┐ │ ", + "q1 : ─┤ H ├───┼────", + " └───┘ │ ", + " ┌─┴──┐ ", "q2 : ───────┤ KR ├─", " └────┘ ", "T : │ 0 │", @@ -837,7 +834,6 @@ def test_pulse_gate_1_qubit_circuit(): _assert_correct_diagram(circ, expected) -@pytest.mark.xfail def test_pulse_gate_multi_qubit_circuit(): circ = ( Circuit() @@ -861,7 +857,6 @@ def _assert_correct_diagram(circ, expected): assert AsciiCircuitDiagram.build_diagram(circ) == "\n".join(expected) -@pytest.mark.xfail def test_circuit_with_nested_target_list(): circ = ( Circuit() @@ -878,8 +873,8 @@ def test_circuit_with_nested_target_list(): "T : │ 0 │ Result Types │", " ┌───┐ ┌──────────────────────────┐ ", "q0 : ─┤ H ├─┤ Expectation(Hamiltonian) ├─", - " └───┘ └─────────────┬────────────┘ ", - " ┌───┐ ┌─────────────┴────────────┐ ", + " └───┘ └────────────┬─────────────┘ ", + " ┌───┐ ┌────────────┴─────────────┐ ", "q1 : ─┤ H ├─┤ Expectation(Hamiltonian) ├─", " └───┘ └──────────────────────────┘ ", "T : │ 0 │ Result Types │", @@ -887,7 +882,6 @@ def test_circuit_with_nested_target_list(): _assert_correct_diagram(circ, expected) -@pytest.mark.xfail def test_hamiltonian(): circ = ( Circuit() @@ -903,11 +897,11 @@ def test_hamiltonian(): "T : │ 0 │ 1 │ 2 │ Result Types │", " ┌───┐ ┌───────────┐ ┌──────────────────────────────┐ ", "q0 : ─┤ H ├───●───┤ Rx(theta) ├─┤ AdjointGradient(Hamiltonian) ├─", - " └───┘ │ └───────────┘ └───────────────┬──────────────┘ ", - " ┌───┐ ┌───────────────┴──────────────┐ ", + " └───┘ │ └───────────┘ └──────────────┬───────────────┘ ", + " ┌─┴─┐ ┌──────────────┴───────────────┐ ", "q1 : ───────┤ X ├───────────────┤ AdjointGradient(Hamiltonian) ├─", - " └───┘ └───────────────┬──────────────┘ ", - " ┌───────────────┴──────────────┐ ", + " └───┘ └──────────────┬───────────────┘ ", + " ┌──────────────┴───────────────┐ ", "q2 : ───────────────────────────┤ AdjointGradient(Hamiltonian) ├─", " └──────────────────────────────┘ ", "T : │ 0 │ 1 │ 2 │ Result Types │", @@ -940,13 +934,13 @@ def __init__(self): " ┌───┐ ┌────────┐ ", "q0 : ────┤ H ├────┤ FOO^-1 ├─────●─────────●─────────●─────", " └───┘ └────────┘ │ │ │ ", - " ┌─────┐ ┌───────┐ │ ┌───────┐ ", + " ┌─────┐ ┌───┴───┐ │ ┌───┴───┐ ", "q1 : ───┤ H^0 ├──────────────┤ FOO^2 ├─────●─────┤ FOO^4 ├─", - " └─────┘ └───────┘ │ └───────┘ ", - " ┌─────────┐ ┌───────┐ │ ", + " └─────┘ └───────┘ │ └───┬───┘ ", + " ┌─────────┐ ┌───┴───┐ │ ", "q2 : ─┤ H^-3.14 ├──────────────────────┤ FOO^3 ├─────●─────", " └─────────┘ └───────┘ │ ", - " ┌───────┐ ", + " ┌───┴───┐ ", "q3 : ────────────────────────────────────────────┤ FOO^4 ├─", " └───────┘ ", "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", From d563c78981eceedc68c85f328fff8b80fa13e8e1 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 10:50:24 -0500 Subject: [PATCH 19/62] code coverage --- src/braket/circuits/ascii_circuit_diagram.py | 12 ------ .../circuits/test_ascii_circuit_diagram.py | 38 ++++++++++++------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index fdfc7a7d4..07928ec93 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -267,17 +267,6 @@ def _ascii_diagram_column_set( for i, moment_line in enumerate(column_str.split("\n")): lines[i] += moment_line - # Adjust for column title width - col_title_width = len(col_title) - symbols_width = len(lines[0]) - 1 - if symbols_width < col_title_width: - diff = col_title_width - symbols_width - for i in range(len(lines) - 1): - if lines[i].endswith("─"): - lines[i] += "─" * diff - else: - lines[i] += " " - first_line = "{:^{width}}│\n".format(col_title, width=len(lines[0]) - 1) return first_line + "\n".join(lines) @@ -384,7 +373,6 @@ def _ascii_diagram_column( connections[qubit] = "below" elif min(target_and_control) < qubit: connections[qubit] = "above" - continue elif qubit in control_qubits: symbols[qubit] = "●" if map_control_qubit_states[qubit] else "◯" diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py index 8d3d97161..15693d3c0 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -229,18 +229,18 @@ def test_time_width(): def test_connector_across_two_qubits(): - circ = Circuit().cnot(3, 4).h(range(2, 6)) + circ = Circuit().cnot(4, 3).h(range(2, 6)) expected = ( "T : │ 0 │ 1 │", " ┌───┐ ", "q2 : ─┤ H ├───────", " └───┘ ", - " ┌───┐ ", - "q3 : ───●───┤ H ├─", - " │ └───┘ ", - " ┌─┴─┐ ┌───┐ ", - "q4 : ─┤ X ├─┤ H ├─", - " └───┘ └───┘ ", + " ┌───┐ ┌───┐ ", + "q3 : ─┤ X ├─┤ H ├─", + " └─┬─┘ └───┘ ", + " │ ┌───┐ ", + "q4 : ───●───┤ H ├─", + " └───┘ ", " ┌───┐ ", "q5 : ─┤ H ├───────", " └───┘ ", @@ -250,18 +250,18 @@ def test_connector_across_two_qubits(): def test_neg_control_qubits(): - circ = Circuit().x(2, control=[0, 1], control_state=[0, 1]) + circ = Circuit().x(1, control=[0, 2], control_state=[0, 1]) expected = ( "T : │ 0 │", " ", "q0 : ───◯───", " │ ", - " │ ", - "q1 : ───●───", - " │ ", " ┌─┴─┐ ", - "q2 : ─┤ X ├─", - " └───┘ ", + "q1 : ─┤ X ├─", + " └─┬─┘ ", + " │ ", + "q2 : ───●───", + " ", "T : │ 0 │", ) _assert_correct_diagram(circ, expected) @@ -593,6 +593,18 @@ def to_ir(self, target): _assert_correct_diagram(circ, expected) +def test_single_qubit_result_types_target_none(): + circ = Circuit().h(0).probability() + expected = ( + "T : │ 0 │ Result Types │", + " ┌───┐ ┌─────────────┐ ", + "q0 : ─┤ H ├─┤ Probability ├─", + " └───┘ └─────────────┘ ", + "T : │ 0 │ Result Types │", + ) + _assert_correct_diagram(circ, expected) + + def test_result_types_target_none(): circ = Circuit().h(0).h(100).probability() expected = ( From 28aeb3287733e4d8457eb7a6768d7582c894e357 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 11:03:48 -0500 Subject: [PATCH 20/62] finish code coverage --- src/braket/circuits/ascii_circuit_diagram.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 07928ec93..da953a485 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -369,19 +369,19 @@ def _ascii_diagram_column( if symbols[qubit] in ["●", "◯"]: if min(target_and_control) < qubit < max(target_and_control): connections[qubit] = "both" - elif qubit < max(target_and_control): - connections[qubit] = "below" - elif min(target_and_control) < qubit: + elif qubit == max(target_and_control): connections[qubit] = "above" + else: + connections[qubit] = "below" elif qubit in control_qubits: symbols[qubit] = "●" if map_control_qubit_states[qubit] else "◯" if min(target_and_control) < qubit < max(target_and_control): connections[qubit] = "both" - elif qubit < max(target_and_control): - connections[qubit] = "below" - elif min(target_and_control) < qubit: + elif qubit == max(target_and_control): connections[qubit] = "above" + else: + connections[qubit] = "below" else: symbols[qubit] = "┼" From a1b83c4572d00361bdbb98755b55461c7cf526e4 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 12:00:45 -0500 Subject: [PATCH 21/62] decomplexify --- src/braket/circuits/ascii_circuit_diagram.py | 175 +++++++++++-------- 1 file changed, 105 insertions(+), 70 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index da953a485..dc9122d9f 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -271,10 +271,72 @@ def _ascii_diagram_column_set( return first_line + "\n".join(lines) + @staticmethod + def _build_parameters( + circuit_qubits: QubitSet, item: ResultType | Instruction, connections: dict[Qubit, str] + ) -> tuple: + map_control_qubit_states = {} + + if isinstance(item, ResultType) and not item.target: + target_qubits = circuit_qubits + control_qubits = QubitSet() + qubits = circuit_qubits + if len(qubits) > 1: + connections |= {qubit: "both" for qubit in qubits[1:-1]} + connections[qubits[-1]] = "above" + connections[qubits[0]] = "below" + 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() + qubits = circuit_qubits + ascii_symbol = item.ascii_symbols[0] + ascii_symbols = [ascii_symbol] * len(circuit_qubits) + if len(circuit_qubits) > 1: + connections = {qubit: "both" for qubit in circuit_qubits[1:-1]} + connections[circuit_qubits[-1]] = "above" + connections[circuit_qubits[0]] = "below" + elif ( + isinstance(item, Instruction) + and isinstance(item.operator, Gate) + and item.operator.name == "GPhase" + ): + target_qubits = circuit_qubits + control_qubits = QubitSet() + qubits = circuit_qubits + ascii_symbols = "─" * 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()) + map_control_qubit_states = AsciiCircuitDiagram._build_map_control_qubits( + item, control_qubits + ) + + target_and_control = target_qubits.union(control_qubits) + qubits = QubitSet(range(min(target_and_control), max(target_and_control) + 1)) + if len(qubits) > 1: + connections |= {qubit: "both" for qubit in qubits[1:-1]} + connections[qubits[-1]] = "above" + connections[qubits[0]] = "below" + + ascii_symbols = item.ascii_symbols + + return ( + target_qubits, + control_qubits, + qubits, + connections, + ascii_symbols, + map_control_qubit_states, + ) + @staticmethod def _ascii_diagram_column( circuit_qubits: QubitSet, - items: list[Union[Instruction, ResultType]], + items: list[Instruction | ResultType], global_phase: float | None = None, ) -> str: """ @@ -282,7 +344,7 @@ def _ascii_diagram_column( Args: circuit_qubits (QubitSet): qubits in circuit - items (list[Union[Instruction, ResultType]]): list of instructions or result types + items (list[Instruction | ResultType]): list of instructions or result types global_phase (float | None): the integrated global phase up to this column Returns: @@ -292,55 +354,15 @@ def _ascii_diagram_column( 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 - if len(qubits) > 1: - connections |= {qubit: "both" for qubit in qubits[1:-1]} - connections[qubits[-1]] = "above" - connections[qubits[0]] = "below" - 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] - ascii_symbols = [ascii_symbol] * len(circuit_qubits) - if len(circuit_qubits) > 1: - connections = {qubit: "both" for qubit in circuit_qubits[1:-1]} - connections[circuit_qubits[-1]] = "above" - connections[circuit_qubits[0]] = "below" - 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 = "─" * 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()) - map_control_qubit_states = AsciiCircuitDiagram._build_map_control_qubits( - item, control_qubits - ) - - target_and_control = target_qubits.union(control_qubits) - qubits = QubitSet(range(min(target_and_control), max(target_and_control) + 1)) - if len(qubits) > 1: - connections |= {qubit: "both" for qubit in qubits[1:-1]} - connections[qubits[-1]] = "above" - connections[qubits[0]] = "below" - - ascii_symbols = item.ascii_symbols + ( + target_qubits, + control_qubits, + qubits, + connections, + ascii_symbols, + map_control_qubit_states, + ) = AsciiCircuitDiagram._build_parameters(circuit_qubits, item, connections) + target_and_control = target_qubits.union(control_qubits) for qubit in qubits: # Determine if the qubit is part of the item or in the middle of a @@ -419,17 +441,20 @@ def _create_output( ) return output + @staticmethod + def _fill_symbol(symbol: str, filler: str, width: int | None = None) -> str: + return "{0:{fill}{align}{width}}".format( + symbol, + fill=filler, + align="^", + width=width if width is not None else len(symbol) + 1, + ) + @staticmethod def _draw_symbol( symbol: str, symbols_width: int, connection: Literal["above, below, both, none"] = "none" ) -> str: - def fill_symbol(symbol: str, filler: str, width: int | None = None) -> str: - return "{0:{fill}{align}{width}}".format( - symbol, - fill=filler, - align="^", - width=width if width is not None else len(symbol) + 1, - ) + fill_symbol = AsciiCircuitDiagram._fill_symbol top = "" bottom = "" @@ -440,18 +465,7 @@ def fill_symbol(symbol: str, filler: str, width: int | None = None) -> str: bottom = fill_symbol("│", " ") symbol = fill_symbol(f"{symbol}", "─") elif symbol in ["StartVerbatim", "EndVerbatim"]: - if connection == "below": - bottom = "║" - elif connection == "both": - top = bottom = "║" - symbol = "║" - elif connection == "above": - top = "║" - symbol = "╨" - bottom = "" - top = fill_symbol(top, " ") - bottom = fill_symbol(bottom, " ") - symbol = fill_symbol(symbol, "─") + top, symbol, bottom = AsciiCircuitDiagram._build_verbatim_box(symbol, connection) elif symbol == "┼": top = fill_symbol("│", " ") bottom = fill_symbol("│", " ") @@ -473,6 +487,27 @@ def fill_symbol(symbol: str, filler: str, width: int | None = None) -> str: output += fill_symbol(bottom, " ", symbols_width + 1) + "\n" return output + @staticmethod + def _build_verbatim_box( + symbol: Literal["StartVerbatim", "EndVerbatim"], + connection: Literal["above, below, both, none"] = "none", + ) -> str: + top = "" + bottom = "" + if connection == "below": + bottom = "║" + elif connection == "both": + top = bottom = "║" + symbol = "║" + elif connection == "above": + top = "║" + symbol = "╨" + top = AsciiCircuitDiagram._fill_symbol(top, " ") + bottom = AsciiCircuitDiagram._fill_symbol(bottom, " ") + symbol = AsciiCircuitDiagram._fill_symbol(symbol, "─") + + return top, symbol, bottom + @staticmethod def _build_map_control_qubits(item: Instruction, control_qubits: QubitSet) -> dict(Qubit, int): control_state = getattr(item, "control_state", None) From 34ceccf8a04056fb73b53e9d6a27ec043825aab3 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 12:25:27 -0500 Subject: [PATCH 22/62] add xfail test --- .../circuits/test_ascii_circuit_diagram.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py index 15693d3c0..b7e9c7f49 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -958,3 +958,29 @@ def __init__(self): "T : │ 0 │ 1 │ 2 │ 3 │ 4 │", ) _assert_correct_diagram(circ, expected) + + +@pytest.mark.xfail +def test_unbalanced_ascii_symbols(): + class FooFoo(Gate): + def __init__(self): + super().__init__(qubit_count=2, ascii_symbols=["FOOO", "FOO"]) + + circ = Circuit().add_instruction(Instruction(FooFoo(), (1, 3), control=[0, 2], power=4)) + expected = ( + "T : │ 0 │", + " ", + "q0 : ─────●──────", + " │ ", + " ┌───┴────┐ ", + "q1 : ─┤ FOOO^4 ├─", + " └───┬────┘ ", + " │ ", + "q2 : ─────●──────", + " │ ", + " ┌───┴───┐ ", + "q3 : ─┤ FOO^4 ├──", + " └───────┘ ", + "T : │ 0 │", + ) + _assert_correct_diagram(circ, expected) From 9fcd08df201e840cc8350813bd63b53cb980ffbc Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 12:32:11 -0500 Subject: [PATCH 23/62] move AsciiDiagram to BoxDrawingDiagram --- src/braket/circuits/__init__.py | 2 +- ...gram.py => box_drawing_circuit_diagram.py} | 41 ++++++++++--------- src/braket/circuits/circuit.py | 8 ++-- ...py => test_box_drawing_circuit_diagram.py} | 8 ++-- .../braket/circuits/test_circuit.py | 4 +- 5 files changed, 33 insertions(+), 30 deletions(-) rename src/braket/circuits/{ascii_circuit_diagram.py => box_drawing_circuit_diagram.py} (93%) rename test/unit_tests/braket/circuits/{test_ascii_circuit_diagram.py => test_box_drawing_circuit_diagram.py} (99%) diff --git a/src/braket/circuits/__init__.py b/src/braket/circuits/__init__.py index d2788746c..370e675f0 100644 --- a/src/braket/circuits/__init__.py +++ b/src/braket/circuits/__init__.py @@ -20,7 +20,7 @@ result_types, ) from braket.circuits.angled_gate import AngledGate, DoubleAngledGate # noqa: F401 -from braket.circuits.ascii_circuit_diagram import AsciiCircuitDiagram # noqa: F401 +from braket.circuits.box_drawing_circuit_diagram import BoxDrawingCircuitDiagram # noqa: F401 from braket.circuits.circuit import Circuit # noqa: F401 from braket.circuits.circuit_diagram import CircuitDiagram # noqa: F401 from braket.circuits.compiler_directive import CompilerDirective # noqa: F401 diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/box_drawing_circuit_diagram.py similarity index 93% rename from src/braket/circuits/ascii_circuit_diagram.py rename to src/braket/circuits/box_drawing_circuit_diagram.py index dc9122d9f..7f04a0d2d 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/box_drawing_circuit_diagram.py @@ -28,7 +28,7 @@ from braket.registers.qubit_set import QubitSet -class AsciiCircuitDiagram(CircuitDiagram): +class BoxDrawingCircuitDiagram(CircuitDiagram): """Builds ASCII string circuit diagrams.""" @staticmethod @@ -52,7 +52,7 @@ def build_diagram(circuit: cir.Circuit) -> str: circuit_qubits = circuit.qubits circuit_qubits.sort() - y_axis_str, global_phase = AsciiCircuitDiagram._prepare_diagram_vars( + y_axis_str, global_phase = BoxDrawingCircuitDiagram._prepare_diagram_vars( circuit, circuit_qubits ) @@ -61,21 +61,22 @@ def build_diagram(circuit: cir.Circuit) -> str: # Moment columns for time, instructions in time_slices.items(): - global_phase = AsciiCircuitDiagram._compute_moment_global_phase( + global_phase = BoxDrawingCircuitDiagram._compute_moment_global_phase( global_phase, instructions ) - moment_str = AsciiCircuitDiagram._ascii_diagram_column_set( + moment_str = BoxDrawingCircuitDiagram._ascii_diagram_column_set( str(time), circuit_qubits, instructions, global_phase ) column_strs.append(moment_str) # Result type columns - additional_result_types, target_result_types = AsciiCircuitDiagram._categorize_result_types( - circuit.result_types - ) + ( + additional_result_types, + target_result_types, + ) = BoxDrawingCircuitDiagram._categorize_result_types(circuit.result_types) if target_result_types: column_strs.append( - AsciiCircuitDiagram._ascii_diagram_column_set( + BoxDrawingCircuitDiagram._ascii_diagram_column_set( "Result Types", circuit_qubits, target_result_types, global_phase ) ) @@ -254,10 +255,12 @@ def _ascii_diagram_column_set( """ # Group items to separate out overlapping multi-qubit items - groupings = AsciiCircuitDiagram._ascii_group_items(circuit_qubits, items) + groupings = BoxDrawingCircuitDiagram._ascii_group_items(circuit_qubits, items) column_strs = [ - AsciiCircuitDiagram._ascii_diagram_column(circuit_qubits, grouping[1], global_phase) + BoxDrawingCircuitDiagram._ascii_diagram_column( + circuit_qubits, grouping[1], global_phase + ) for grouping in groupings ] @@ -311,7 +314,7 @@ def _build_parameters( else: target_qubits = item.target control_qubits = getattr(item, "control", QubitSet()) - map_control_qubit_states = AsciiCircuitDiagram._build_map_control_qubits( + map_control_qubit_states = BoxDrawingCircuitDiagram._build_map_control_qubits( item, control_qubits ) @@ -361,7 +364,7 @@ def _ascii_diagram_column( connections, ascii_symbols, map_control_qubit_states, - ) = AsciiCircuitDiagram._build_parameters(circuit_qubits, item, connections) + ) = BoxDrawingCircuitDiagram._build_parameters(circuit_qubits, item, connections) target_and_control = target_qubits.union(control_qubits) for qubit in qubits: @@ -407,7 +410,7 @@ def _ascii_diagram_column( else: symbols[qubit] = "┼" - output = AsciiCircuitDiagram._create_output( + output = BoxDrawingCircuitDiagram._create_output( symbols, connections, circuit_qubits, global_phase ) return output @@ -436,7 +439,7 @@ def _create_output( ) for qubit in qubits: - output += AsciiCircuitDiagram._draw_symbol( + output += BoxDrawingCircuitDiagram._draw_symbol( symbols[qubit], symbols_width, connections[qubit] ) return output @@ -454,7 +457,7 @@ def _fill_symbol(symbol: str, filler: str, width: int | None = None) -> str: def _draw_symbol( symbol: str, symbols_width: int, connection: Literal["above, below, both, none"] = "none" ) -> str: - fill_symbol = AsciiCircuitDiagram._fill_symbol + fill_symbol = BoxDrawingCircuitDiagram._fill_symbol top = "" bottom = "" @@ -465,7 +468,7 @@ def _draw_symbol( bottom = fill_symbol("│", " ") symbol = fill_symbol(f"{symbol}", "─") elif symbol in ["StartVerbatim", "EndVerbatim"]: - top, symbol, bottom = AsciiCircuitDiagram._build_verbatim_box(symbol, connection) + top, symbol, bottom = BoxDrawingCircuitDiagram._build_verbatim_box(symbol, connection) elif symbol == "┼": top = fill_symbol("│", " ") bottom = fill_symbol("│", " ") @@ -502,9 +505,9 @@ def _build_verbatim_box( elif connection == "above": top = "║" symbol = "╨" - top = AsciiCircuitDiagram._fill_symbol(top, " ") - bottom = AsciiCircuitDiagram._fill_symbol(bottom, " ") - symbol = AsciiCircuitDiagram._fill_symbol(symbol, "─") + top = BoxDrawingCircuitDiagram._fill_symbol(top, " ") + bottom = BoxDrawingCircuitDiagram._fill_symbol(bottom, " ") + symbol = BoxDrawingCircuitDiagram._fill_symbol(symbol, "─") return top, symbol, bottom diff --git a/src/braket/circuits/circuit.py b/src/braket/circuits/circuit.py index aeae8585d..21997c71a 100644 --- a/src/braket/circuits/circuit.py +++ b/src/braket/circuits/circuit.py @@ -21,7 +21,7 @@ import oqpy from braket.circuits import compiler_directives -from braket.circuits.ascii_circuit_diagram import AsciiCircuitDiagram +from braket.circuits.box_drawing_circuit_diagram import BoxDrawingCircuitDiagram from braket.circuits.free_parameter import FreeParameter from braket.circuits.free_parameter_expression import FreeParameterExpression from braket.circuits.gate import Gate @@ -1092,13 +1092,13 @@ def adjoint(self) -> Circuit: circ.add_result_type(result_type) return circ - def diagram(self, circuit_diagram_class: type = AsciiCircuitDiagram) -> str: + def diagram(self, circuit_diagram_class: type = BoxDrawingCircuitDiagram) -> str: """ Get a diagram for the current circuit. Args: circuit_diagram_class (type): A `CircuitDiagram` class that builds the - diagram for this circuit. Default = `AsciiCircuitDiagram`. + diagram for this circuit. Default = `BoxDrawingCircuitDiagram`. Returns: str: An ASCII string circuit diagram. @@ -1493,7 +1493,7 @@ def __repr__(self) -> str: ) def __str__(self): - return self.diagram(AsciiCircuitDiagram) + return self.diagram(BoxDrawingCircuitDiagram) def __eq__(self, other): if isinstance(other, Circuit): diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py similarity index 99% rename from test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py rename to test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py index b7e9c7f49..83addfd6c 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py @@ -15,7 +15,7 @@ import pytest from braket.circuits import ( - AsciiCircuitDiagram, + BoxDrawingCircuitDiagram, Circuit, FreeParameter, Gate, @@ -27,11 +27,11 @@ def test_empty_circuit(): - assert AsciiCircuitDiagram.build_diagram(Circuit()) == "" + assert BoxDrawingCircuitDiagram.build_diagram(Circuit()) == "" def test_only_gphase_circuit(): - assert AsciiCircuitDiagram.build_diagram(Circuit().gphase(0.1)) == "Global phase: 0.1" + assert BoxDrawingCircuitDiagram.build_diagram(Circuit().gphase(0.1)) == "Global phase: 0.1" def test_one_gate_one_qubit(): @@ -866,7 +866,7 @@ def test_pulse_gate_multi_qubit_circuit(): def _assert_correct_diagram(circ, expected): - assert AsciiCircuitDiagram.build_diagram(circ) == "\n".join(expected) + assert BoxDrawingCircuitDiagram.build_diagram(circ) == "\n".join(expected) def test_circuit_with_nested_target_list(): diff --git a/test/unit_tests/braket/circuits/test_circuit.py b/test/unit_tests/braket/circuits/test_circuit.py index 928cff757..298d7c7d3 100644 --- a/test/unit_tests/braket/circuits/test_circuit.py +++ b/test/unit_tests/braket/circuits/test_circuit.py @@ -18,7 +18,7 @@ import braket.ir.jaqcd as jaqcd from braket.circuits import ( - AsciiCircuitDiagram, + BoxDrawingCircuitDiagram, Circuit, FreeParameter, Gate, @@ -179,7 +179,7 @@ def test_repr_result_types(cnot_prob): def test_str(h): - expected = AsciiCircuitDiagram.build_diagram(h) + expected = BoxDrawingCircuitDiagram.build_diagram(h) assert str(h) == expected From d627658f495d8e75035f594edf4941906c718436 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 12:44:14 -0500 Subject: [PATCH 24/62] keep ascii diagrams as legacy --- src/braket/circuits/__init__.py | 1 + src/braket/circuits/ascii_circuit_diagram.py | 421 +++++++++ src/braket/circuits/circuit.py | 8 +- .../circuits/test_ascii_circuit_diagram.py | 874 ++++++++++++++++++ 4 files changed, 1300 insertions(+), 4 deletions(-) create mode 100644 src/braket/circuits/ascii_circuit_diagram.py create mode 100644 test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py diff --git a/src/braket/circuits/__init__.py b/src/braket/circuits/__init__.py index 370e675f0..d63dbad1e 100644 --- a/src/braket/circuits/__init__.py +++ b/src/braket/circuits/__init__.py @@ -20,6 +20,7 @@ result_types, ) from braket.circuits.angled_gate import AngledGate, DoubleAngledGate # noqa: F401 +from braket.circuits.ascii_circuit_diagram import AsciiCircuitDiagram # noqa: F401 from braket.circuits.box_drawing_circuit_diagram import BoxDrawingCircuitDiagram # noqa: F401 from braket.circuits.circuit import Circuit # noqa: F401 from braket.circuits.circuit_diagram import CircuitDiagram # noqa: F401 diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py new file mode 100644 index 000000000..ee8099041 --- /dev/null +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -0,0 +1,421 @@ +# 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 Union + +import braket.circuits.circuit as cir +from braket.circuits.circuit_diagram import CircuitDiagram +from braket.circuits.compiler_directive import CompilerDirective +from braket.circuits.gate import Gate +from braket.circuits.instruction import Instruction +from braket.circuits.moments import MomentType +from braket.circuits.noise import Noise +from braket.circuits.result_type import ResultType +from braket.registers.qubit import Qubit +from braket.registers.qubit_set import QubitSet + + +class AsciiCircuitDiagram(CircuitDiagram): + """Builds ASCII string circuit diagrams.""" + + @staticmethod + def build_diagram(circuit: cir.Circuit) -> str: + """ + Build an ASCII string circuit diagram. + + Args: + circuit (Circuit): Circuit for which to build a diagram. + + Returns: + str: ASCII string circuit diagram. + """ + + if not circuit.instructions: + return "" + + if all(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): + return f"Global phase: {circuit.global_phase}" + + circuit_qubits = circuit.qubits + circuit_qubits.sort() + + y_axis_str, global_phase = AsciiCircuitDiagram._prepare_diagram_vars( + circuit, circuit_qubits + ) + + time_slices = circuit.moments.time_slices() + column_strs = [] + + # Moment columns + for time, instructions in time_slices.items(): + global_phase = AsciiCircuitDiagram._compute_moment_global_phase( + global_phase, instructions + ) + moment_str = AsciiCircuitDiagram._ascii_diagram_column_set( + str(time), circuit_qubits, instructions, global_phase + ) + column_strs.append(moment_str) + + # Result type columns + additional_result_types, target_result_types = AsciiCircuitDiagram._categorize_result_types( + circuit.result_types + ) + if target_result_types: + column_strs.append( + AsciiCircuitDiagram._ascii_diagram_column_set( + "Result Types", circuit_qubits, target_result_types, global_phase + ) + ) + + # Unite strings + lines = y_axis_str.split("\n") + for col_str in column_strs: + for i, line_in_col in enumerate(col_str.split("\n")): + lines[i] += line_in_col + + # Time on top and bottom + lines.append(lines[0]) + + if global_phase: + lines.append(f"\nGlobal phase: {global_phase}") + + # Additional result types line on bottom + if additional_result_types: + lines.append(f"\nAdditional result types: {', '.join(additional_result_types)}") + + # A list of parameters in the circuit to the currently assigned values. + if circuit.parameters: + lines.append( + "\nUnassigned parameters: " + f"{sorted(circuit.parameters, key=lambda param: param.name)}." + ) + + return "\n".join(lines) + + @staticmethod + def _prepare_diagram_vars( + circuit: cir.Circuit, circuit_qubits: QubitSet + ) -> tuple[str, float | None]: + # Y Axis Column + y_axis_width = len(str(int(max(circuit_qubits)))) + y_axis_str = "{0:{width}} : |\n".format("T", width=y_axis_width + 1) + + global_phase = None + if any(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): + y_axis_str += "{0:{width}} : |\n".format("GP", width=y_axis_width) + global_phase = 0 + + for qubit in circuit_qubits: + y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) + y_axis_str += "q{0:{width}} : -\n".format(str(int(qubit)), width=y_axis_width) + + return y_axis_str, global_phase + + @staticmethod + def _compute_moment_global_phase( + global_phase: float | None, items: list[Instruction] + ) -> float | None: + """ + Compute the integrated phase at a certain moment. + + Args: + global_phase (float | None): The integrated phase up to the computed moment + items (list[Instruction]): list of instructions + + Returns: + float | None: The updated integrated phase. + """ + moment_phase = 0 + for item in items: + if ( + isinstance(item, Instruction) + and isinstance(item.operator, Gate) + and item.operator.name == "GPhase" + ): + moment_phase += item.operator.angle + return global_phase + moment_phase if global_phase is not None else None + + @staticmethod + def _ascii_group_items( + circuit_qubits: QubitSet, + items: list[Union[Instruction, ResultType]], + ) -> list[tuple[QubitSet, list[Instruction]]]: + """ + Group instructions in a moment for ASCII diagram + + Args: + circuit_qubits (QubitSet): set of qubits in circuit + items (list[Union[Instruction, ResultType]]): list of instructions or result types + + Returns: + list[tuple[QubitSet, list[Instruction]]]: list of grouped instructions or result types. + """ + groupings = [] + for item in items: + # Can only print Gate and Noise operators for instructions at the moment + if isinstance(item, Instruction) and not isinstance( + item.operator, (Gate, Noise, CompilerDirective) + ): + continue + + # As a zero-qubit gate, GPhase can be grouped with anything. We set qubit_range + # to an empty list and we just add it to the first group below. + if ( + isinstance(item, Instruction) + and isinstance(item.operator, Gate) + and item.operator.name == "GPhase" + ): + qubit_range = QubitSet() + elif (isinstance(item, ResultType) and not item.target) or ( + isinstance(item, Instruction) and isinstance(item.operator, CompilerDirective) + ): + qubit_range = circuit_qubits + else: + if isinstance(item.target, list): + target = reduce(QubitSet.union, map(QubitSet, item.target), QubitSet()) + else: + target = item.target + control = getattr(item, "control", QubitSet()) + target_and_control = target.union(control) + qubit_range = QubitSet(range(min(target_and_control), max(target_and_control) + 1)) + + found_grouping = False + for group in groupings: + qubits_added = group[0] + instr_group = group[1] + # Take into account overlapping multi-qubit gates + if not qubits_added.intersection(set(qubit_range)): + instr_group.append(item) + qubits_added.update(qubit_range) + found_grouping = True + break + + if not found_grouping: + groupings.append((qubit_range, [item])) + + return groupings + + @staticmethod + def _categorize_result_types( + result_types: list[ResultType], + ) -> tuple[list[str], list[ResultType]]: + """ + Categorize result types into result types with target and those without. + + Args: + result_types (list[ResultType]): list of result types + + Returns: + tuple[list[str], list[ResultType]]: first element is a list of result types + without `target` attribute; second element is a list of result types with + `target` attribute + """ + additional_result_types = [] + target_result_types = [] + for result_type in result_types: + if hasattr(result_type, "target"): + target_result_types.append(result_type) + else: + additional_result_types.extend(result_type.ascii_symbols) + return additional_result_types, target_result_types + + @staticmethod + def _ascii_diagram_column_set( + col_title: str, + circuit_qubits: QubitSet, + items: list[Union[Instruction, ResultType]], + global_phase: float | None, + ) -> str: + """ + Return a set of columns in the ASCII string diagram of the circuit for a list of items. + + Args: + col_title (str): title of column set + 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 set + + Returns: + str: An ASCII string diagram for the column set. + """ + + # Group items to separate out overlapping multi-qubit items + groupings = AsciiCircuitDiagram._ascii_group_items(circuit_qubits, items) + + column_strs = [ + AsciiCircuitDiagram._ascii_diagram_column(circuit_qubits, grouping[1], global_phase) + for grouping in groupings + ] + + # Unite column strings + lines = column_strs[0].split("\n") + for column_str in column_strs[1:]: + for i, moment_line in enumerate(column_str.split("\n")): + lines[i] += moment_line + + # Adjust for column title width + col_title_width = len(col_title) + symbols_width = len(lines[0]) - 1 + if symbols_width < col_title_width: + diff = col_title_width - symbols_width + for i in range(len(lines) - 1): + if lines[i].endswith("-"): + lines[i] += "-" * diff + else: + lines[i] += " " + + first_line = "{:^{width}}|\n".format(col_title, width=len(lines[0]) - 1) + + return first_line + "\n".join(lines) + + @staticmethod + def _ascii_diagram_column( + 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: "-" for qubit in circuit_qubits} + margins = {qubit: " " 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 = "-" * 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()) + map_control_qubit_states = AsciiCircuitDiagram._build_map_control_qubits( + item, control_qubits + ) + + 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] != "●" + ) + 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] = "●" if map_control_qubit_states[qubit] else "◯" + 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): + margins[qubit] = "|" + + output = AsciiCircuitDiagram._create_output(symbols, margins, circuit_qubits, global_phase) + return output + + @staticmethod + def _create_output( + symbols: dict[Qubit, str], + margins: dict[Qubit, str], + qubits: QubitSet, + global_phase: float | None, + ) -> str: + symbols_width = max([len(symbol) for symbol in symbols.values()]) + output = "" + + if global_phase is not None: + global_phase_str = ( + f"{global_phase:.2f}" if isinstance(global_phase, float) else str(global_phase) + ) + symbols_width = max([symbols_width, len(global_phase_str)]) + output += "{0:{fill}{align}{width}}|\n".format( + global_phase_str, + fill=" ", + align="^", + width=symbols_width, + ) + + for qubit in qubits: + output += "{0:{width}}\n".format(margins[qubit], width=symbols_width + 1) + output += "{0:{fill}{align}{width}}\n".format( + symbols[qubit], fill="-", align="<", width=symbols_width + 1 + ) + return output + + @staticmethod + def _build_map_control_qubits(item: Instruction, control_qubits: QubitSet) -> dict(Qubit, int): + control_state = getattr(item, "control_state", None) + if control_state is not None: + map_control_qubit_states = { + qubit: state for qubit, state in zip(control_qubits, control_state) + } + else: + map_control_qubit_states = {qubit: 1 for qubit in control_qubits} + + return map_control_qubit_states diff --git a/src/braket/circuits/circuit.py b/src/braket/circuits/circuit.py index 21997c71a..8075294e5 100644 --- a/src/braket/circuits/circuit.py +++ b/src/braket/circuits/circuit.py @@ -712,7 +712,7 @@ def apply_gate_noise( >>> print(circ) T : |0|1|2| - q0 : -X-Z-C- + q0 : -X-Z-●- | q1 : -Y-X-X- @@ -723,7 +723,7 @@ def apply_gate_noise( >>> print(circ.apply_gate_noise(noise, target_gates = Gate.X)) T : | 0 | 1 |2| - q0 : -X-DEPO(0.1)-Z-----------C- + q0 : -X-DEPO(0.1)-Z-----------●- | q1 : -Y-----------X-DEPO(0.1)-X- @@ -733,7 +733,7 @@ def apply_gate_noise( >>> print(circ.apply_gate_noise(noise, target_qubits = 1)) T : | 0 | 1 | 2 | - q0 : -X-----------Z-----------C----------- + q0 : -X-----------Z-----------●----------- | q1 : -Y-DEPO(0.1)-X-DEPO(0.1)-X-DEPO(0.1)- @@ -746,7 +746,7 @@ def apply_gate_noise( ... ) T : | 0 | 1 |2| - q0 : -X-DEPO(0.1)-Z-----------C- + q0 : -X-DEPO(0.1)-Z-----------●- | q1 : -Y-DEPO(0.1)-X-DEPO(0.1)-X- diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py new file mode 100644 index 000000000..8a86427bf --- /dev/null +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -0,0 +1,874 @@ +# 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. + +import numpy as np +import pytest + +from braket.circuits import ( + AsciiCircuitDiagram, + Circuit, + FreeParameter, + Gate, + Instruction, + Observable, + Operator, +) +from braket.pulse import Frame, Port, PulseSequence + + +def test_empty_circuit(): + assert AsciiCircuitDiagram.build_diagram(Circuit()) == "" + + +def test_only_gphase_circuit(): + assert AsciiCircuitDiagram.build_diagram(Circuit().gphase(0.1)) == "Global phase: 0.1" + + +def test_one_gate_one_qubit(): + circ = Circuit().h(0) + expected = ("T : |0|", " ", "q0 : -H-", "", "T : |0|") + _assert_correct_diagram(circ, expected) + + +def test_one_gate_one_qubit_rotation(): + circ = Circuit().rx(angle=3.14, target=0) + # Column formats to length of the gate plus the ascii representation for the angle. + expected = ("T : | 0 |", " ", "q0 : -Rx(3.14)-", "", "T : | 0 |") + _assert_correct_diagram(circ, expected) + + +def test_one_gate_one_qubit_rotation_with_parameter(): + theta = FreeParameter("theta") + circ = Circuit().rx(angle=theta, target=0) + # Column formats to length of the gate plus the ascii representation for the angle. + expected = ( + "T : | 0 |", + " ", + "q0 : -Rx(theta)-", + "", + "T : | 0 |", + "", + "Unassigned parameters: [theta].", + ) + _assert_correct_diagram(circ, expected) + + +@pytest.mark.parametrize("target", [0, 1]) +def test_one_gate_with_global_phase(target): + circ = Circuit().x(target=target).gphase(0.15) + expected = ( + "T : |0| 1 |", + "GP : |0|0.15|", + " ", + f"q{target} : -X------", + "", + "T : |0| 1 |", + "", + "Global phase: 0.15", + ) + _assert_correct_diagram(circ, expected) + + +def test_one_gate_with_zero_global_phase(): + circ = Circuit().gphase(-0.15).x(target=0).gphase(0.15) + expected = ( + "T : | 0 | 1 |", + "GP : |-0.15|0.00|", + " ", + "q0 : -X----------", + "", + "T : | 0 | 1 |", + ) + _assert_correct_diagram(circ, expected) + + +def test_one_gate_one_qubit_rotation_with_unicode(): + theta = FreeParameter("\u03B8") + circ = Circuit().rx(angle=theta, target=0) + # Column formats to length of the gate plus the ascii representation for the angle. + expected = ( + "T : | 0 |", + " ", + "q0 : -Rx(θ)-", + "", + "T : | 0 |", + "", + "Unassigned parameters: [θ].", + ) + _assert_correct_diagram(circ, expected) + + +def test_one_gate_with_parametric_expression_global_phase_(): + theta = FreeParameter("\u03B8") + circ = Circuit().x(target=0).gphase(2 * theta).x(0).gphase(1) + expected = ( + "T : |0| 1 | 2 |", + "GP : |0|2*θ|2*θ + 1.0|", + " ", + "q0 : -X-X-------------", + "", + "T : |0| 1 | 2 |", + "", + "Global phase: 2*θ + 1.0", + "", + "Unassigned parameters: [θ].", + ) + _assert_correct_diagram(circ, expected) + + +def test_one_gate_one_qubit_rotation_with_parameter_assigned(): + theta = FreeParameter("theta") + circ = Circuit().rx(angle=theta, target=0) + new_circ = circ.make_bound_circuit({"theta": np.pi}) + # Column formats to length of the gate plus the ascii representation for the angle. + expected = ( + "T : | 0 |", + " ", + "q0 : -Rx(3.14)-", + "", + "T : | 0 |", + ) + _assert_correct_diagram(new_circ, expected) + + +def test_qubit_width(): + circ = Circuit().h(0).h(100) + expected = ( + "T : |0|", + " ", + "q0 : -H-", + " ", + "q100 : -H-", + "", + "T : |0|", + ) + _assert_correct_diagram(circ, expected) + + +def test_gate_width(): + class Foo(Gate): + def __init__(self): + super().__init__(qubit_count=1, ascii_symbols=["FOO"]) + + def to_ir(self, target): + return "foo" + + circ = Circuit().h(0).h(1).add_instruction(Instruction(Foo(), 0)) + expected = ( + "T : |0| 1 |", + " ", + "q0 : -H-FOO-", + " ", + "q1 : -H-----", + "", + "T : |0| 1 |", + ) + _assert_correct_diagram(circ, expected) + + +def test_time_width(): + circ = Circuit() + num_qubits = 15 + for qubit in range(num_qubits): + if qubit == num_qubits - 1: + break + circ.cnot(qubit, qubit + 1) + expected = ( + "T : |0|1|2|3|4|5|6|7|8|9|10|11|12|13|", + " ", + "q0 : -●-------------------------------", + " | ", + "q1 : -X-●-----------------------------", + " | ", + "q2 : ---X-●---------------------------", + " | ", + "q3 : -----X-●-------------------------", + " | ", + "q4 : -------X-●-----------------------", + " | ", + "q5 : ---------X-●---------------------", + " | ", + "q6 : -----------X-●-------------------", + " | ", + "q7 : -------------X-●-----------------", + " | ", + "q8 : ---------------X-●---------------", + " | ", + "q9 : -----------------X-●-------------", + " | ", + "q10 : -------------------X-●-----------", + " | ", + "q11 : ---------------------X--●--------", + " | ", + "q12 : ------------------------X--●-----", + " | ", + "q13 : ---------------------------X--●--", + " | ", + "q14 : ------------------------------X--", + "", + "T : |0|1|2|3|4|5|6|7|8|9|10|11|12|13|", + ) + _assert_correct_diagram(circ, expected) + + +def test_connector_across_two_qubits(): + circ = Circuit().cnot(3, 4).h(range(2, 6)) + expected = ( + "T : |0|1|", + " ", + "q2 : -H---", + " ", + "q3 : -●-H-", + " | ", + "q4 : -X-H-", + " ", + "q5 : -H---", + "", + "T : |0|1|", + ) + _assert_correct_diagram(circ, expected) + + +def test_neg_control_qubits(): + circ = Circuit().x(2, control=[0, 1], control_state=[0, 1]) + expected = ( + "T : |0|", + " ", + "q0 : -◯-", + " | ", + "q1 : -●-", + " | ", + "q2 : -X-", + "", + "T : |0|", + ) + _assert_correct_diagram(circ, expected) + + +def test_only_neg_control_qubits(): + circ = Circuit().x(2, control=[0, 1], control_state=0) + expected = ( + "T : |0|", + " ", + "q0 : -◯-", + " | ", + "q1 : -◯-", + " | ", + "q2 : -X-", + "", + "T : |0|", + ) + _assert_correct_diagram(circ, expected) + + +def test_connector_across_three_qubits(): + circ = Circuit().x(control=(3, 4), target=5).h(range(2, 6)) + expected = ( + "T : |0|1|", + " ", + "q2 : -H---", + " ", + "q3 : -●-H-", + " | ", + "q4 : -●-H-", + " | ", + "q5 : -X-H-", + "", + "T : |0|1|", + ) + _assert_correct_diagram(circ, expected) + + +def test_overlapping_qubits(): + circ = Circuit().cnot(0, 2).x(control=1, target=3).h(0) + expected = ( + "T : | 0 |1|", + " ", + "q0 : -●---H-", + " | ", + "q1 : -|-●---", + " | | ", + "q2 : -X-|---", + " | ", + "q3 : ---X---", + "", + "T : | 0 |1|", + ) + _assert_correct_diagram(circ, expected) + + +def test_overlapping_qubits_angled_gates(): + circ = Circuit().zz(0, 2, 0.15).x(control=1, target=3).h(0) + expected = ( + "T : | 0 |1|", + " ", + "q0 : -ZZ(0.15)---H-", + " | ", + "q1 : -|--------●---", + " | | ", + "q2 : -ZZ(0.15)-|---", + " | ", + "q3 : ----------X---", + "", + "T : | 0 |1|", + ) + _assert_correct_diagram(circ, expected) + + +def test_connector_across_gt_two_qubits(): + circ = Circuit().h(4).x(control=3, target=5).h(4).h(2) + expected = ( + "T : | 0 |1|", + " ", + "q2 : -H-----", + " ", + "q3 : ---●---", + " | ", + "q4 : -H-|-H-", + " | ", + "q5 : ---X---", + "", + "T : | 0 |1|", + ) + _assert_correct_diagram(circ, expected) + + +def test_connector_across_non_used_qubits(): + circ = Circuit().h(4).cnot(3, 100).h(4).h(101) + expected = ( + "T : | 0 |1|", + " ", + "q3 : ---●---", + " | ", + "q4 : -H-|-H-", + " | ", + "q100 : ---X---", + " ", + "q101 : -H-----", + "", + "T : | 0 |1|", + ) + _assert_correct_diagram(circ, expected) + + +def test_verbatim_1q_no_preceding(): + circ = Circuit().add_verbatim_box(Circuit().h(0)) + expected = ( + "T : | 0 |1| 2 |", + " ", + "q0 : -StartVerbatim-H-EndVerbatim-", + "", + "T : | 0 |1| 2 |", + ) + _assert_correct_diagram(circ, expected) + + +def test_verbatim_1q_preceding(): + circ = Circuit().h(0).add_verbatim_box(Circuit().h(0)) + expected = ( + "T : |0| 1 |2| 3 |", + " ", + "q0 : -H-StartVerbatim-H-EndVerbatim-", + "", + "T : |0| 1 |2| 3 |", + ) + _assert_correct_diagram(circ, expected) + + +def test_verbatim_1q_following(): + circ = Circuit().add_verbatim_box(Circuit().h(0)).h(0) + expected = ( + "T : | 0 |1| 2 |3|", + " ", + "q0 : -StartVerbatim-H-EndVerbatim-H-", + "", + "T : | 0 |1| 2 |3|", + ) + _assert_correct_diagram(circ, expected) + + +def test_verbatim_2q_no_preceding(): + circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1)) + expected = ( + "T : | 0 |1|2| 3 |", + " ", + "q0 : -StartVerbatim-H-●-EndVerbatim-", + " | | | ", + "q1 : -*************---X-***********-", + "", + "T : | 0 |1|2| 3 |", + ) + _assert_correct_diagram(circ, expected) + + +def test_verbatim_2q_preceding(): + circ = Circuit().h(0).add_verbatim_box(Circuit().h(0).cnot(0, 1)) + expected = ( + "T : |0| 1 |2|3| 4 |", + " ", + "q0 : -H-StartVerbatim-H-●-EndVerbatim-", + " | | | ", + "q1 : ---*************---X-***********-", + "", + "T : |0| 1 |2|3| 4 |", + ) + _assert_correct_diagram(circ, expected) + + +def test_verbatim_2q_following(): + circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1)).h(0) + expected = ( + "T : | 0 |1|2| 3 |4|", + " ", + "q0 : -StartVerbatim-H-●-EndVerbatim-H-", + " | | | ", + "q1 : -*************---X-***********---", + "", + "T : | 0 |1|2| 3 |4|", + ) + _assert_correct_diagram(circ, expected) + + +def test_verbatim_3q_no_preceding(): + circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1).cnot(1, 2)) + expected = ( + "T : | 0 |1|2|3| 4 |", + " ", + "q0 : -StartVerbatim-H-●---EndVerbatim-", + " | | | ", + "q1 : -|---------------X-●-|-----------", + " | | | ", + "q2 : -*************-----X-***********-", + "", + "T : | 0 |1|2|3| 4 |", + ) + _assert_correct_diagram(circ, expected) + + +def test_verbatim_3q_preceding(): + circ = Circuit().h(0).add_verbatim_box(Circuit().h(0).cnot(0, 1).cnot(1, 2)) + expected = ( + "T : |0| 1 |2|3|4| 5 |", + " ", + "q0 : -H-StartVerbatim-H-●---EndVerbatim-", + " | | | ", + "q1 : ---|---------------X-●-|-----------", + " | | | ", + "q2 : ---*************-----X-***********-", + "", + "T : |0| 1 |2|3|4| 5 |", + ) + _assert_correct_diagram(circ, expected) + + +def test_verbatim_3q_following(): + circ = Circuit().add_verbatim_box(Circuit().h(0).cnot(0, 1).cnot(1, 2)).h(0) + expected = ( + "T : | 0 |1|2|3| 4 |5|", + " ", + "q0 : -StartVerbatim-H-●---EndVerbatim-H-", + " | | | ", + "q1 : -|---------------X-●-|-------------", + " | | | ", + "q2 : -*************-----X-***********---", + "", + "T : | 0 |1|2|3| 4 |5|", + ) + _assert_correct_diagram(circ, expected) + + +def test_verbatim_different_qubits(): + circ = Circuit().h(1).add_verbatim_box(Circuit().h(0)).cnot(3, 4) + expected = ( + "T : |0| 1 |2| 3 |4|", + " ", + "q0 : ---StartVerbatim-H-EndVerbatim---", + " | | ", + "q1 : -H-|---------------|-------------", + " | | ", + "q3 : ---|---------------|-----------●-", + " | | | ", + "q4 : ---*************---***********-X-", + "", + "T : |0| 1 |2| 3 |4|", + ) + _assert_correct_diagram(circ, expected) + + +def test_verbatim_qubset_qubits(): + circ = Circuit().h(1).cnot(0, 1).cnot(1, 2).add_verbatim_box(Circuit().h(1)).cnot(2, 3) + expected = ( + "T : |0|1|2| 3 |4| 5 |6|", + " ", + "q0 : ---●---StartVerbatim---EndVerbatim---", + " | | | ", + "q1 : -H-X-●-|-------------H-|-------------", + " | | | ", + "q2 : -----X-|---------------|-----------●-", + " | | | ", + "q3 : -------*************---***********-X-", + "", + "T : |0|1|2| 3 |4| 5 |6|", + ) + _assert_correct_diagram(circ, expected) + + +def test_ignore_non_gates(): + class Foo(Operator): + @property + def name(self) -> str: + return "foo" + + def to_ir(self, target): + return "foo" + + circ = Circuit().h(0).h(1).cnot(1, 2).add_instruction(Instruction(Foo(), 0)) + expected = ( + "T : |0|1|", + " ", + "q0 : -H---", + " ", + "q1 : -H-●-", + " | ", + "q2 : ---X-", + "", + "T : |0|1|", + ) + _assert_correct_diagram(circ, expected) + + +def test_result_types_target_none(): + circ = Circuit().h(0).h(100).probability() + expected = ( + "T : |0|Result Types|", + " ", + "q0 : -H-Probability--", + " | ", + "q100 : -H-Probability--", + "", + "T : |0|Result Types|", + ) + _assert_correct_diagram(circ, expected) + + +def test_result_types_target_some(): + circ = ( + Circuit() + .h(0) + .h(1) + .h(100) + .expectation(observable=Observable.Y() @ Observable.Z(), target=[0, 100]) + ) + expected = ( + "T : |0| Result Types |", + " ", + "q0 : -H-Expectation(Y@Z)-", + " | ", + "q1 : -H-|----------------", + " | ", + "q100 : -H-Expectation(Y@Z)-", + "", + "T : |0| Result Types |", + ) + _assert_correct_diagram(circ, expected) + + +def test_additional_result_types(): + circ = Circuit().h(0).h(1).h(100).state_vector().amplitude(["110", "001"]) + expected = ( + "T : |0|", + " ", + "q0 : -H-", + " ", + "q1 : -H-", + " ", + "q100 : -H-", + "", + "T : |0|", + "", + "Additional result types: StateVector, Amplitude(110,001)", + ) + _assert_correct_diagram(circ, expected) + + +def test_multiple_result_types(): + circ = ( + Circuit() + .cnot(0, 2) + .cnot(1, 3) + .h(0) + .variance(observable=Observable.Y(), target=0) + .expectation(observable=Observable.Y(), target=2) + .sample(observable=Observable.Y()) + ) + expected = ( + "T : | 0 |1| Result Types |", + " ", + "q0 : -●---H-Variance(Y)----Sample(Y)-", + " | | ", + "q1 : -|-●------------------Sample(Y)-", + " | | | ", + "q2 : -X-|---Expectation(Y)-Sample(Y)-", + " | | ", + "q3 : ---X------------------Sample(Y)-", + "", + "T : | 0 |1| Result Types |", + ) + _assert_correct_diagram(circ, expected) + + +def test_multiple_result_types_with_state_vector_amplitude(): + circ = ( + Circuit() + .cnot(0, 2) + .cnot(1, 3) + .h(0) + .variance(observable=Observable.Y(), target=0) + .expectation(observable=Observable.Y(), target=3) + .expectation(observable=Observable.Hermitian(np.array([[1.0, 0.0], [0.0, 1.0]])), target=1) + .amplitude(["0001"]) + .state_vector() + ) + expected = ( + "T : | 0 |1| Result Types |", + " ", + "q0 : -●---H-Variance(Y)------------", + " | ", + "q1 : -|-●---Expectation(Hermitian)-", + " | | ", + "q2 : -X-|--------------------------", + " | ", + "q3 : ---X---Expectation(Y)---------", + "", + "T : | 0 |1| Result Types |", + "", + "Additional result types: Amplitude(0001), StateVector", + ) + _assert_correct_diagram(circ, expected) + + +def test_multiple_result_types_with_custom_hermitian_ascii_symbol(): + herm_matrix = (Observable.Y() @ Observable.Z()).to_matrix() + circ = ( + Circuit() + .cnot(0, 2) + .cnot(1, 3) + .h(0) + .variance(observable=Observable.Y(), target=0) + .expectation(observable=Observable.Y(), target=3) + .expectation( + observable=Observable.Hermitian( + matrix=herm_matrix, + display_name="MyHerm", + ), + target=[1, 2], + ) + ) + expected = ( + "T : | 0 |1| Result Types |", + " ", + "q0 : -●---H-Variance(Y)---------", + " | ", + "q1 : -|-●---Expectation(MyHerm)-", + " | | | ", + "q2 : -X-|---Expectation(MyHerm)-", + " | ", + "q3 : ---X---Expectation(Y)------", + "", + "T : | 0 |1| Result Types |", + ) + _assert_correct_diagram(circ, expected) + + +def test_noise_1qubit(): + circ = Circuit().h(0).x(1).bit_flip(1, 0.1) + expected = ( + "T : | 0 |", + " ", + "q0 : -H---------", + " ", + "q1 : -X-BF(0.1)-", + "", + "T : | 0 |", + ) + _assert_correct_diagram(circ, expected) + + +def test_noise_2qubit(): + circ = Circuit().h(1).kraus((0, 2), [np.eye(4)]) + expected = ( + "T : | 0 |", + " ", + "q0 : ---KR-", + " | ", + "q1 : -H-|--", + " | ", + "q2 : ---KR-", + "", + "T : | 0 |", + ) + _assert_correct_diagram(circ, expected) + + +def test_noise_multi_probabilities(): + circ = Circuit().h(0).x(1).pauli_channel(1, 0.1, 0.2, 0.3) + expected = ( + "T : | 0 |", + " ", + "q0 : -H-----------------", + " ", + "q1 : -X-PC(0.1,0.2,0.3)-", + "", + "T : | 0 |", + ) + _assert_correct_diagram(circ, expected) + + +def test_noise_multi_probabilities_with_parameter(): + a = FreeParameter("a") + b = FreeParameter("b") + c = FreeParameter("c") + circ = Circuit().h(0).x(1).pauli_channel(1, a, b, c) + expected = ( + "T : | 0 |", + " ", + "q0 : -H-----------", + " ", + "q1 : -X-PC(a,b,c)-", + "", + "T : | 0 |", + "", + "Unassigned parameters: [a, b, c].", + ) + _assert_correct_diagram(circ, expected) + + +def test_pulse_gate_1_qubit_circuit(): + circ = ( + Circuit() + .h(0) + .pulse_gate(0, PulseSequence().set_phase(Frame("x", Port("px", 1e-9), 1e9, 0), 0)) + ) + expected = ( + "T : |0|1 |", + " ", + "q0 : -H-PG-", + "", + "T : |0|1 |", + ) + _assert_correct_diagram(circ, expected) + + +def test_pulse_gate_multi_qubit_circuit(): + circ = ( + Circuit() + .h(0) + .pulse_gate([0, 1], PulseSequence().set_phase(Frame("x", Port("px", 1e-9), 1e9, 0), 0)) + ) + expected = ( + "T : |0|1 |", + " ", + "q0 : -H-PG-", + " | ", + "q1 : ---PG-", + "", + "T : |0|1 |", + ) + _assert_correct_diagram(circ, expected) + + +def _assert_correct_diagram(circ, expected): + assert AsciiCircuitDiagram.build_diagram(circ) == "\n".join(expected) + + +def test_circuit_with_nested_target_list(): + circ = ( + Circuit() + .h(0) + .h(1) + .expectation( + observable=(2 * Observable.Y()) @ (-3 * Observable.I()) + - 0.75 * Observable.Y() @ Observable.Z(), + target=[[0, 1], [0, 1]], + ) + ) + + expected = ( + "T : |0| Result Types |", + " ", + "q0 : -H-Expectation(Hamiltonian)-", + " | ", + "q1 : -H-Expectation(Hamiltonian)-", + "", + "T : |0| Result Types |", + ) + _assert_correct_diagram(circ, expected) + + +def test_hamiltonian(): + circ = ( + Circuit() + .h(0) + .cnot(0, 1) + .rx(0, FreeParameter("theta")) + .adjoint_gradient( + 4 * (2e-5 * Observable.Z() + 2 * (3 * Observable.X() @ (2 * Observable.Y()))), + [[0], [1, 2]], + ) + ) + expected = ( + "T : |0|1| 2 | Result Types |", + " ", + "q0 : -H-●-Rx(theta)-AdjointGradient(Hamiltonian)-", + " | | ", + "q1 : ---X-----------AdjointGradient(Hamiltonian)-", + " | ", + "q2 : ---------------AdjointGradient(Hamiltonian)-", + "", + "T : |0|1| 2 | Result Types |", + "", + "Unassigned parameters: [theta].", + ) + _assert_correct_diagram(circ, expected) + + +def test_power(): + class Foo(Gate): + def __init__(self): + super().__init__(qubit_count=1, ascii_symbols=["FOO"]) + + class CFoo(Gate): + def __init__(self): + super().__init__(qubit_count=2, ascii_symbols=["●", "FOO"]) + + class FooFoo(Gate): + def __init__(self): + super().__init__(qubit_count=2, ascii_symbols=["FOO", "FOO"]) + + circ = Circuit().h(0, power=1).h(1, power=0).h(2, power=-3.14) + circ.add_instruction(Instruction(Foo(), 0, power=-1)) + circ.add_instruction(Instruction(CFoo(), (0, 1), power=2)) + circ.add_instruction(Instruction(CFoo(), (1, 2), control=0, power=3)) + circ.add_instruction(Instruction(FooFoo(), (1, 2), control=0, power=4)) + expected = ( + "T : | 0 | 1 | 2 | 3 | 4 |", + " ", + "q0 : -H---------(FOO^-1)-●-------●-------●-------", + " | | | ", + "q1 : -(H^0)--------------(FOO^2)-●-------(FOO^4)-", + " | | ", + "q2 : -(H^-3.14)------------------(FOO^3)-(FOO^4)-", + "", + "T : | 0 | 1 | 2 | 3 | 4 |", + ) + _assert_correct_diagram(circ, expected) From a1c44fe804f5251e564a7727f7eb3886034c495e Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 12:56:18 -0500 Subject: [PATCH 25/62] add default_diagram_builder field --- src/braket/circuits/circuit.py | 3 ++- .../braket/circuits/test_ascii_circuit_diagram.py | 8 ++++---- .../braket/circuits/test_box_drawing_circuit_diagram.py | 8 ++++---- test/unit_tests/braket/circuits/test_circuit.py | 7 +++++++ 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/braket/circuits/circuit.py b/src/braket/circuits/circuit.py index 8075294e5..c3f36b3d3 100644 --- a/src/braket/circuits/circuit.py +++ b/src/braket/circuits/circuit.py @@ -82,6 +82,7 @@ class Circuit: """ _ALL_QUBITS = "ALL" # Flag to indicate all qubits in _qubit_observable_mapping + default_diagram_builder = BoxDrawingCircuitDiagram @classmethod def register_subroutine(cls, func: SubroutineCallable) -> None: @@ -1493,7 +1494,7 @@ def __repr__(self) -> str: ) def __str__(self): - return self.diagram(BoxDrawingCircuitDiagram) + return self.diagram(self.default_diagram_builder) def __eq__(self, other): if isinstance(other, Circuit): diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py index 8a86427bf..2c27da0d3 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -26,6 +26,10 @@ from braket.pulse import Frame, Port, PulseSequence +def _assert_correct_diagram(circ, expected): + assert AsciiCircuitDiagram.build_diagram(circ) == "\n".join(expected) + + def test_empty_circuit(): assert AsciiCircuitDiagram.build_diagram(Circuit()) == "" @@ -787,10 +791,6 @@ def test_pulse_gate_multi_qubit_circuit(): _assert_correct_diagram(circ, expected) -def _assert_correct_diagram(circ, expected): - assert AsciiCircuitDiagram.build_diagram(circ) == "\n".join(expected) - - def test_circuit_with_nested_target_list(): circ = ( Circuit() diff --git a/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py b/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py index 83addfd6c..31cec62f1 100644 --- a/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py @@ -26,6 +26,10 @@ from braket.pulse import Frame, Port, PulseSequence +def _assert_correct_diagram(circ, expected): + assert BoxDrawingCircuitDiagram.build_diagram(circ) == "\n".join(expected) + + def test_empty_circuit(): assert BoxDrawingCircuitDiagram.build_diagram(Circuit()) == "" @@ -865,10 +869,6 @@ def test_pulse_gate_multi_qubit_circuit(): _assert_correct_diagram(circ, expected) -def _assert_correct_diagram(circ, expected): - assert BoxDrawingCircuitDiagram.build_diagram(circ) == "\n".join(expected) - - def test_circuit_with_nested_target_list(): circ = ( Circuit() diff --git a/test/unit_tests/braket/circuits/test_circuit.py b/test/unit_tests/braket/circuits/test_circuit.py index 298d7c7d3..fb7dbc7d1 100644 --- a/test/unit_tests/braket/circuits/test_circuit.py +++ b/test/unit_tests/braket/circuits/test_circuit.py @@ -18,6 +18,7 @@ import braket.ir.jaqcd as jaqcd from braket.circuits import ( + AsciiCircuitDiagram, BoxDrawingCircuitDiagram, Circuit, FreeParameter, @@ -183,6 +184,12 @@ def test_str(h): assert str(h) == expected +def test_change_diagram_builder(h): + Circuit.default_diagram_builder = AsciiCircuitDiagram + expected = AsciiCircuitDiagram.build_diagram(h) + assert str(h) == expected + + def test_equality(): circ_1 = Circuit().h(0).probability([0, 1]) circ_2 = Circuit().h(0).probability([0, 1]) From 3db7791ec0cb6914a67debf1a0feb0e49cd22f39 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 13:09:04 -0500 Subject: [PATCH 26/62] replace back circles by C/N --- src/braket/circuits/angled_gate.py | 6 +- src/braket/circuits/ascii_circuit_diagram.py | 4 +- .../circuits/box_drawing_circuit_diagram.py | 10 +- src/braket/circuits/circuit.py | 8 +- src/braket/circuits/gate.py | 2 +- src/braket/circuits/gates.py | 20 ++-- src/braket/circuits/quantum_operator.py | 2 +- .../circuits/test_ascii_circuit_diagram.py | 100 +++++++++--------- .../test_box_drawing_circuit_diagram.py | 2 +- 9 files changed, 77 insertions(+), 77 deletions(-) diff --git a/src/braket/circuits/angled_gate.py b/src/braket/circuits/angled_gate.py index 78357b909..e453177a7 100644 --- a/src/braket/circuits/angled_gate.py +++ b/src/braket/circuits/angled_gate.py @@ -46,7 +46,7 @@ def __init__( printing a diagram of a circuit. The length must be the same as `qubit_count`, and index ordering is expected to correlate with the target ordering on the instruction. For instance, if a CNOT instruction has the control qubit on the first index and - target qubit on the second index, the ASCII symbols should have `["●", "X"]` to + target qubit on the second index, the ASCII symbols should have `["C", "X"]` to correlate a symbol with that index. Raises: @@ -147,7 +147,7 @@ def __init__( printing a diagram of a circuit. The length must be the same as `qubit_count`, and index ordering is expected to correlate with the target ordering on the instruction. For instance, if a CNOT instruction has the control qubit on the first index and - target qubit on the second index, the ASCII symbols should have `["●", "X"]` to + target qubit on the second index, the ASCII symbols should have `["C", "X"]` to correlate a symbol with that index. Raises: @@ -265,7 +265,7 @@ def __init__( printing a diagram of a circuit. The length must be the same as `qubit_count`, and index ordering is expected to correlate with the target ordering on the instruction. For instance, if a CNOT instruction has the control qubit on the first index and - target qubit on the second index, the ASCII symbols should have `["●", "X"]` to + target qubit on the second index, the ASCII symbols should have `["C", "X"]` to correlate a symbol with that index. Raises: diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index ee8099041..2c7024574 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -358,7 +358,7 @@ def _ascii_diagram_column( # 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] != "●" + and ascii_symbols[item_qubit_index] != "C" ) else "" ) @@ -368,7 +368,7 @@ def _ascii_diagram_column( else ascii_symbols[item_qubit_index] ) elif qubit in control_qubits: - symbols[qubit] = "●" if map_control_qubit_states[qubit] else "◯" + symbols[qubit] = "C" if map_control_qubit_states[qubit] else "N" else: symbols[qubit] = "|" diff --git a/src/braket/circuits/box_drawing_circuit_diagram.py b/src/braket/circuits/box_drawing_circuit_diagram.py index 7f04a0d2d..c93ab0295 100644 --- a/src/braket/circuits/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/box_drawing_circuit_diagram.py @@ -382,7 +382,7 @@ def _ascii_diagram_column( # 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] != "●" + and ascii_symbols[item_qubit_index] != "C" ) else "" ) @@ -391,7 +391,7 @@ def _ascii_diagram_column( if power_string else ascii_symbols[item_qubit_index] ) - if symbols[qubit] in ["●", "◯"]: + if symbols[qubit] in ["C", "N"]: if min(target_and_control) < qubit < max(target_and_control): connections[qubit] = "both" elif qubit == max(target_and_control): @@ -400,7 +400,7 @@ def _ascii_diagram_column( connections[qubit] = "below" elif qubit in control_qubits: - symbols[qubit] = "●" if map_control_qubit_states[qubit] else "◯" + symbols[qubit] = "C" if map_control_qubit_states[qubit] else "N" if min(target_and_control) < qubit < max(target_and_control): connections[qubit] = "both" elif qubit == max(target_and_control): @@ -461,12 +461,12 @@ def _draw_symbol( top = "" bottom = "" - if symbol in ["●", "◯"]: + if symbol in ["C", "N"]: if connection in ["above", "both"]: top = fill_symbol("│", " ") if connection in ["below", "both"]: bottom = fill_symbol("│", " ") - symbol = fill_symbol(f"{symbol}", "─") + symbol = fill_symbol("●" if symbol == "C" else "◯", "─") elif symbol in ["StartVerbatim", "EndVerbatim"]: top, symbol, bottom = BoxDrawingCircuitDiagram._build_verbatim_box(symbol, connection) elif symbol == "┼": diff --git a/src/braket/circuits/circuit.py b/src/braket/circuits/circuit.py index c3f36b3d3..ddc78d640 100644 --- a/src/braket/circuits/circuit.py +++ b/src/braket/circuits/circuit.py @@ -713,7 +713,7 @@ def apply_gate_noise( >>> print(circ) T : |0|1|2| - q0 : -X-Z-●- + q0 : -X-Z-C- | q1 : -Y-X-X- @@ -724,7 +724,7 @@ def apply_gate_noise( >>> print(circ.apply_gate_noise(noise, target_gates = Gate.X)) T : | 0 | 1 |2| - q0 : -X-DEPO(0.1)-Z-----------●- + q0 : -X-DEPO(0.1)-Z-----------C- | q1 : -Y-----------X-DEPO(0.1)-X- @@ -734,7 +734,7 @@ def apply_gate_noise( >>> print(circ.apply_gate_noise(noise, target_qubits = 1)) T : | 0 | 1 | 2 | - q0 : -X-----------Z-----------●----------- + q0 : -X-----------Z-----------C----------- | q1 : -Y-DEPO(0.1)-X-DEPO(0.1)-X-DEPO(0.1)- @@ -747,7 +747,7 @@ def apply_gate_noise( ... ) T : | 0 | 1 |2| - q0 : -X-DEPO(0.1)-Z-----------●- + q0 : -X-DEPO(0.1)-Z-----------C- | q1 : -Y-DEPO(0.1)-X-DEPO(0.1)-X- diff --git a/src/braket/circuits/gate.py b/src/braket/circuits/gate.py index 43edf9010..2183a2329 100644 --- a/src/braket/circuits/gate.py +++ b/src/braket/circuits/gate.py @@ -42,7 +42,7 @@ def __init__(self, qubit_count: Optional[int], ascii_symbols: Sequence[str]): printing a diagram of circuits. Length must be the same as `qubit_count`, and index ordering is expected to correlate with target ordering on the instruction. For instance, if CNOT instruction has the control qubit on the first index and - target qubit on the second index. Then ASCII symbols would have ["●", "X"] to + target qubit on the second index. Then ASCII symbols would have ["C", "X"] to correlate a symbol with that index. Raises: diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index 8dd5277dc..e955c4bb0 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -1539,7 +1539,7 @@ class CNot(Gate): """ def __init__(self): - super().__init__(qubit_count=None, ascii_symbols=["●", "X"]) + super().__init__(qubit_count=None, ascii_symbols=["C", "X"]) @property def _qasm_name(self) -> str: @@ -2023,7 +2023,7 @@ def __init__(self, angle: Union[FreeParameterExpression, float]): super().__init__( angle=angle, qubit_count=None, - ascii_symbols=["●", angled_ascii_characters("PHASE", angle)], + ascii_symbols=["C", angled_ascii_characters("PHASE", angle)], ) @property @@ -2108,7 +2108,7 @@ def __init__(self, angle: Union[FreeParameterExpression, float]): super().__init__( angle=angle, qubit_count=None, - ascii_symbols=["●", angled_ascii_characters("PHASE00", angle)], + ascii_symbols=["C", angled_ascii_characters("PHASE00", angle)], ) @property @@ -2193,7 +2193,7 @@ def __init__(self, angle: Union[FreeParameterExpression, float]): super().__init__( angle=angle, qubit_count=None, - ascii_symbols=["●", angled_ascii_characters("PHASE01", angle)], + ascii_symbols=["C", angled_ascii_characters("PHASE01", angle)], ) @property @@ -2278,7 +2278,7 @@ def __init__(self, angle: Union[FreeParameterExpression, float]): super().__init__( angle=angle, qubit_count=None, - ascii_symbols=["●", angled_ascii_characters("PHASE10", angle)], + ascii_symbols=["C", angled_ascii_characters("PHASE10", angle)], ) @property @@ -2357,7 +2357,7 @@ class CV(Gate): """ def __init__(self): - super().__init__(qubit_count=None, ascii_symbols=["●", "V"]) + super().__init__(qubit_count=None, ascii_symbols=["C", "V"]) @property def _qasm_name(self) -> str: @@ -2434,7 +2434,7 @@ class CY(Gate): """ def __init__(self): - super().__init__(qubit_count=None, ascii_symbols=["●", "Y"]) + super().__init__(qubit_count=None, ascii_symbols=["C", "Y"]) @property def _qasm_name(self) -> str: @@ -2511,7 +2511,7 @@ class CZ(Gate): """ def __init__(self): - super().__init__(qubit_count=None, ascii_symbols=["●", "Z"]) + super().__init__(qubit_count=None, ascii_symbols=["C", "Z"]) @property def _qasm_name(self) -> str: @@ -3008,7 +3008,7 @@ class CCNot(Gate): """ def __init__(self): - super().__init__(qubit_count=None, ascii_symbols=["●", "●", "X"]) + super().__init__(qubit_count=None, ascii_symbols=["C", "C", "X"]) @property def _qasm_name(self) -> str: @@ -3116,7 +3116,7 @@ class CSwap(Gate): """ def __init__(self): - super().__init__(qubit_count=None, ascii_symbols=["●", "SWAP", "SWAP"]) + super().__init__(qubit_count=None, ascii_symbols=["C", "SWAP", "SWAP"]) @property def _qasm_name(self) -> str: diff --git a/src/braket/circuits/quantum_operator.py b/src/braket/circuits/quantum_operator.py index d9d2e389e..df67bf199 100644 --- a/src/braket/circuits/quantum_operator.py +++ b/src/braket/circuits/quantum_operator.py @@ -39,7 +39,7 @@ def __init__(self, qubit_count: Optional[int], ascii_symbols: Sequence[str]): Length must be the same as `qubit_count`, and index ordering is expected to correlate with target ordering on the instruction. For instance, if CNOT instruction has the control qubit on the first index and - target qubit on the second index. Then ASCII symbols would have ["●", "X"] to + target qubit on the second index. Then ASCII symbols would have ["C", "X"] to correlate a symbol with that index. Raises: diff --git a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py index 2c27da0d3..88e32d92f 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -190,33 +190,33 @@ def test_time_width(): expected = ( "T : |0|1|2|3|4|5|6|7|8|9|10|11|12|13|", " ", - "q0 : -●-------------------------------", + "q0 : -C-------------------------------", " | ", - "q1 : -X-●-----------------------------", + "q1 : -X-C-----------------------------", " | ", - "q2 : ---X-●---------------------------", + "q2 : ---X-C---------------------------", " | ", - "q3 : -----X-●-------------------------", + "q3 : -----X-C-------------------------", " | ", - "q4 : -------X-●-----------------------", + "q4 : -------X-C-----------------------", " | ", - "q5 : ---------X-●---------------------", + "q5 : ---------X-C---------------------", " | ", - "q6 : -----------X-●-------------------", + "q6 : -----------X-C-------------------", " | ", - "q7 : -------------X-●-----------------", + "q7 : -------------X-C-----------------", " | ", - "q8 : ---------------X-●---------------", + "q8 : ---------------X-C---------------", " | ", - "q9 : -----------------X-●-------------", + "q9 : -----------------X-C-------------", " | ", - "q10 : -------------------X-●-----------", + "q10 : -------------------X-C-----------", " | ", - "q11 : ---------------------X--●--------", + "q11 : ---------------------X--C--------", " | ", - "q12 : ------------------------X--●-----", + "q12 : ------------------------X--C-----", " | ", - "q13 : ---------------------------X--●--", + "q13 : ---------------------------X--C--", " | ", "q14 : ------------------------------X--", "", @@ -232,7 +232,7 @@ def test_connector_across_two_qubits(): " ", "q2 : -H---", " ", - "q3 : -●-H-", + "q3 : -C-H-", " | ", "q4 : -X-H-", " ", @@ -248,9 +248,9 @@ def test_neg_control_qubits(): expected = ( "T : |0|", " ", - "q0 : -◯-", + "q0 : -N-", " | ", - "q1 : -●-", + "q1 : -C-", " | ", "q2 : -X-", "", @@ -264,9 +264,9 @@ def test_only_neg_control_qubits(): expected = ( "T : |0|", " ", - "q0 : -◯-", + "q0 : -N-", " | ", - "q1 : -◯-", + "q1 : -N-", " | ", "q2 : -X-", "", @@ -282,9 +282,9 @@ def test_connector_across_three_qubits(): " ", "q2 : -H---", " ", - "q3 : -●-H-", + "q3 : -C-H-", " | ", - "q4 : -●-H-", + "q4 : -C-H-", " | ", "q5 : -X-H-", "", @@ -298,9 +298,9 @@ def test_overlapping_qubits(): expected = ( "T : | 0 |1|", " ", - "q0 : -●---H-", + "q0 : -C---H-", " | ", - "q1 : -|-●---", + "q1 : -|-C---", " | | ", "q2 : -X-|---", " | ", @@ -318,7 +318,7 @@ def test_overlapping_qubits_angled_gates(): " ", "q0 : -ZZ(0.15)---H-", " | ", - "q1 : -|--------●---", + "q1 : -|--------C---", " | | ", "q2 : -ZZ(0.15)-|---", " | ", @@ -336,7 +336,7 @@ def test_connector_across_gt_two_qubits(): " ", "q2 : -H-----", " ", - "q3 : ---●---", + "q3 : ---C---", " | ", "q4 : -H-|-H-", " | ", @@ -352,7 +352,7 @@ def test_connector_across_non_used_qubits(): expected = ( "T : | 0 |1|", " ", - "q3 : ---●---", + "q3 : ---C---", " | ", "q4 : -H-|-H-", " | ", @@ -406,7 +406,7 @@ def test_verbatim_2q_no_preceding(): expected = ( "T : | 0 |1|2| 3 |", " ", - "q0 : -StartVerbatim-H-●-EndVerbatim-", + "q0 : -StartVerbatim-H-C-EndVerbatim-", " | | | ", "q1 : -*************---X-***********-", "", @@ -420,7 +420,7 @@ def test_verbatim_2q_preceding(): expected = ( "T : |0| 1 |2|3| 4 |", " ", - "q0 : -H-StartVerbatim-H-●-EndVerbatim-", + "q0 : -H-StartVerbatim-H-C-EndVerbatim-", " | | | ", "q1 : ---*************---X-***********-", "", @@ -434,7 +434,7 @@ def test_verbatim_2q_following(): expected = ( "T : | 0 |1|2| 3 |4|", " ", - "q0 : -StartVerbatim-H-●-EndVerbatim-H-", + "q0 : -StartVerbatim-H-C-EndVerbatim-H-", " | | | ", "q1 : -*************---X-***********---", "", @@ -448,9 +448,9 @@ def test_verbatim_3q_no_preceding(): expected = ( "T : | 0 |1|2|3| 4 |", " ", - "q0 : -StartVerbatim-H-●---EndVerbatim-", + "q0 : -StartVerbatim-H-C---EndVerbatim-", " | | | ", - "q1 : -|---------------X-●-|-----------", + "q1 : -|---------------X-C-|-----------", " | | | ", "q2 : -*************-----X-***********-", "", @@ -464,9 +464,9 @@ def test_verbatim_3q_preceding(): expected = ( "T : |0| 1 |2|3|4| 5 |", " ", - "q0 : -H-StartVerbatim-H-●---EndVerbatim-", + "q0 : -H-StartVerbatim-H-C---EndVerbatim-", " | | | ", - "q1 : ---|---------------X-●-|-----------", + "q1 : ---|---------------X-C-|-----------", " | | | ", "q2 : ---*************-----X-***********-", "", @@ -480,9 +480,9 @@ def test_verbatim_3q_following(): expected = ( "T : | 0 |1|2|3| 4 |5|", " ", - "q0 : -StartVerbatim-H-●---EndVerbatim-H-", + "q0 : -StartVerbatim-H-C---EndVerbatim-H-", " | | | ", - "q1 : -|---------------X-●-|-------------", + "q1 : -|---------------X-C-|-------------", " | | | ", "q2 : -*************-----X-***********---", "", @@ -500,7 +500,7 @@ def test_verbatim_different_qubits(): " | | ", "q1 : -H-|---------------|-------------", " | | ", - "q3 : ---|---------------|-----------●-", + "q3 : ---|---------------|-----------C-", " | | | ", "q4 : ---*************---***********-X-", "", @@ -514,11 +514,11 @@ def test_verbatim_qubset_qubits(): expected = ( "T : |0|1|2| 3 |4| 5 |6|", " ", - "q0 : ---●---StartVerbatim---EndVerbatim---", + "q0 : ---C---StartVerbatim---EndVerbatim---", " | | | ", - "q1 : -H-X-●-|-------------H-|-------------", + "q1 : -H-X-C-|-------------H-|-------------", " | | | ", - "q2 : -----X-|---------------|-----------●-", + "q2 : -----X-|---------------|-----------C-", " | | | ", "q3 : -------*************---***********-X-", "", @@ -542,7 +542,7 @@ def to_ir(self, target): " ", "q0 : -H---", " ", - "q1 : -H-●-", + "q1 : -H-C-", " | ", "q2 : ---X-", "", @@ -618,9 +618,9 @@ def test_multiple_result_types(): expected = ( "T : | 0 |1| Result Types |", " ", - "q0 : -●---H-Variance(Y)----Sample(Y)-", + "q0 : -C---H-Variance(Y)----Sample(Y)-", " | | ", - "q1 : -|-●------------------Sample(Y)-", + "q1 : -|-C------------------Sample(Y)-", " | | | ", "q2 : -X-|---Expectation(Y)-Sample(Y)-", " | | ", @@ -646,9 +646,9 @@ def test_multiple_result_types_with_state_vector_amplitude(): expected = ( "T : | 0 |1| Result Types |", " ", - "q0 : -●---H-Variance(Y)------------", + "q0 : -C---H-Variance(Y)------------", " | ", - "q1 : -|-●---Expectation(Hermitian)-", + "q1 : -|-C---Expectation(Hermitian)-", " | | ", "q2 : -X-|--------------------------", " | ", @@ -681,9 +681,9 @@ def test_multiple_result_types_with_custom_hermitian_ascii_symbol(): expected = ( "T : | 0 |1| Result Types |", " ", - "q0 : -●---H-Variance(Y)---------", + "q0 : -C---H-Variance(Y)---------", " | ", - "q1 : -|-●---Expectation(MyHerm)-", + "q1 : -|-C---Expectation(MyHerm)-", " | | | ", "q2 : -X-|---Expectation(MyHerm)-", " | ", @@ -829,7 +829,7 @@ def test_hamiltonian(): expected = ( "T : |0|1| 2 | Result Types |", " ", - "q0 : -H-●-Rx(theta)-AdjointGradient(Hamiltonian)-", + "q0 : -H-C-Rx(theta)-AdjointGradient(Hamiltonian)-", " | | ", "q1 : ---X-----------AdjointGradient(Hamiltonian)-", " | ", @@ -849,7 +849,7 @@ def __init__(self): class CFoo(Gate): def __init__(self): - super().__init__(qubit_count=2, ascii_symbols=["●", "FOO"]) + super().__init__(qubit_count=2, ascii_symbols=["C", "FOO"]) class FooFoo(Gate): def __init__(self): @@ -863,9 +863,9 @@ def __init__(self): expected = ( "T : | 0 | 1 | 2 | 3 | 4 |", " ", - "q0 : -H---------(FOO^-1)-●-------●-------●-------", + "q0 : -H---------(FOO^-1)-C-------C-------C-------", " | | | ", - "q1 : -(H^0)--------------(FOO^2)-●-------(FOO^4)-", + "q1 : -(H^0)--------------(FOO^2)-C-------(FOO^4)-", " | | ", "q2 : -(H^-3.14)------------------(FOO^3)-(FOO^4)-", "", diff --git a/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py b/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py index 31cec62f1..2bf758e7d 100644 --- a/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py @@ -930,7 +930,7 @@ def __init__(self): class CFoo(Gate): def __init__(self): - super().__init__(qubit_count=2, ascii_symbols=["●", "FOO"]) + super().__init__(qubit_count=2, ascii_symbols=["C", "FOO"]) class FooFoo(Gate): def __init__(self): From ec8e6f3f411b5e4bc30831181aa4e801a9a8cd83 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 18:29:59 -0500 Subject: [PATCH 27/62] remove duplicate code --- src/braket/circuits/ascii_circuit_diagram.py | 40 +++- .../circuits/box_drawing_circuit_diagram.py | 199 ++---------------- 2 files changed, 47 insertions(+), 192 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 2c7024574..b913983ea 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -31,6 +31,8 @@ class AsciiCircuitDiagram(CircuitDiagram): """Builds ASCII string circuit diagrams.""" + vdelim = "|" + @staticmethod def build_diagram(circuit: cir.Circuit) -> str: """ @@ -105,25 +107,40 @@ def build_diagram(circuit: cir.Circuit) -> str: return "\n".join(lines) - @staticmethod + @classmethod def _prepare_diagram_vars( - circuit: cir.Circuit, circuit_qubits: QubitSet + cls, circuit: cir.Circuit, circuit_qubits: QubitSet ) -> tuple[str, float | None]: # Y Axis Column y_axis_width = len(str(int(max(circuit_qubits)))) - y_axis_str = "{0:{width}} : |\n".format("T", width=y_axis_width + 1) + y_axis_str = "{0:{width}} : {vdelim}\n".format( + "T", width=y_axis_width + 1, vdelim=cls.vdelim + ) global_phase = None if any(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): - y_axis_str += "{0:{width}} : |\n".format("GP", width=y_axis_width) + y_axis_str += "{0:{width}} : {vdelim}\n".format( + "GP", width=y_axis_width, vdelim=cls.vdelim + ) global_phase = 0 for qubit in circuit_qubits: - y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) - y_axis_str += "q{0:{width}} : -\n".format(str(int(qubit)), width=y_axis_width) - + y_axis_str += cls._create_qubit_layout(qubit, y_axis_width) return y_axis_str, global_phase + @staticmethod + def _create_qubit_layout(qubit: Qubit, y_axis_width: int) -> None: + """ + Create the layout of the qubit. + + Args: + qubit (Qubit): Qubit to create the layout for. + y_axis_width (int): Width of the y axis. + """ + y_axis_str = "{0:{width}}\n".format(" ", width=y_axis_width + 5) + y_axis_str += "q{0:{width}} : -\n".format(str(int(qubit)), width=y_axis_width) + return y_axis_str + @staticmethod def _compute_moment_global_phase( global_phase: float | None, items: list[Instruction] @@ -232,8 +249,9 @@ def _categorize_result_types( additional_result_types.extend(result_type.ascii_symbols) return additional_result_types, target_result_types - @staticmethod + @classmethod def _ascii_diagram_column_set( + cls, col_title: str, circuit_qubits: QubitSet, items: list[Union[Instruction, ResultType]], @@ -256,7 +274,7 @@ def _ascii_diagram_column_set( groupings = AsciiCircuitDiagram._ascii_group_items(circuit_qubits, items) column_strs = [ - AsciiCircuitDiagram._ascii_diagram_column(circuit_qubits, grouping[1], global_phase) + cls._ascii_diagram_column(circuit_qubits, grouping[1], global_phase) for grouping in groupings ] @@ -277,7 +295,9 @@ def _ascii_diagram_column_set( else: lines[i] += " " - first_line = "{:^{width}}|\n".format(col_title, width=len(lines[0]) - 1) + first_line = "{:^{width}}{vdelim}\n".format( + col_title, width=len(lines[0]) - 1, vdelim=cls.vdelim + ) return first_line + "\n".join(lines) diff --git a/src/braket/circuits/box_drawing_circuit_diagram.py b/src/braket/circuits/box_drawing_circuit_diagram.py index c93ab0295..c28bd929e 100644 --- a/src/braket/circuits/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/box_drawing_circuit_diagram.py @@ -14,27 +14,28 @@ from __future__ import annotations from functools import reduce -from typing import Literal, Union +from typing import Literal import braket.circuits.circuit as cir -from braket.circuits.circuit_diagram import CircuitDiagram +from braket.circuits.ascii_circuit_diagram import AsciiCircuitDiagram from braket.circuits.compiler_directive import CompilerDirective from braket.circuits.gate import Gate from braket.circuits.instruction import Instruction from braket.circuits.moments import MomentType -from braket.circuits.noise import Noise from braket.circuits.result_type import ResultType from braket.registers.qubit import Qubit from braket.registers.qubit_set import QubitSet -class BoxDrawingCircuitDiagram(CircuitDiagram): - """Builds ASCII string circuit diagrams.""" +class BoxDrawingCircuitDiagram(AsciiCircuitDiagram): + """Builds ASCII string circuit diagrams using box-drawing characters.""" + + vdelim = "│" @staticmethod def build_diagram(circuit: cir.Circuit) -> str: """ - Build an ASCII string circuit diagram. + Build an ASCII string circuit diagram using box-drawing characters. Args: circuit (Circuit): Circuit for which to build a diagram. @@ -61,7 +62,7 @@ def build_diagram(circuit: cir.Circuit) -> str: # Moment columns for time, instructions in time_slices.items(): - global_phase = BoxDrawingCircuitDiagram._compute_moment_global_phase( + global_phase = AsciiCircuitDiagram._compute_moment_global_phase( global_phase, instructions ) moment_str = BoxDrawingCircuitDiagram._ascii_diagram_column_set( @@ -73,7 +74,7 @@ def build_diagram(circuit: cir.Circuit) -> str: ( additional_result_types, target_result_types, - ) = BoxDrawingCircuitDiagram._categorize_result_types(circuit.result_types) + ) = AsciiCircuitDiagram._categorize_result_types(circuit.result_types) if target_result_types: column_strs.append( BoxDrawingCircuitDiagram._ascii_diagram_column_set( @@ -107,172 +108,18 @@ def build_diagram(circuit: cir.Circuit) -> str: return "\n".join(lines) @staticmethod - def _prepare_diagram_vars( - circuit: cir.Circuit, circuit_qubits: QubitSet - ) -> tuple[str, float | None]: - # Y Axis Column - y_axis_width = len(str(int(max(circuit_qubits)))) - y_axis_str = "{0:{width}} : │\n".format("T", width=y_axis_width + 1) - - global_phase = None - if any(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): - y_axis_str += "{0:{width}} : │\n".format("GP", width=y_axis_width) - global_phase = 0 - - for qubit in circuit_qubits: - y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) - y_axis_str += "q{0:{width}} : ─\n".format(str(int(qubit)), width=y_axis_width) - y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) - - return y_axis_str, global_phase - - @staticmethod - def _compute_moment_global_phase( - global_phase: float | None, items: list[Instruction] - ) -> float | None: - """ - Compute the integrated phase at a certain moment. - - Args: - global_phase (float | None): The integrated phase up to the computed moment - items (list[Instruction]): list of instructions - - Returns: - float | None: The updated integrated phase. - """ - moment_phase = 0 - for item in items: - if ( - isinstance(item, Instruction) - and isinstance(item.operator, Gate) - and item.operator.name == "GPhase" - ): - moment_phase += item.operator.angle - return global_phase + moment_phase if global_phase is not None else None - - @staticmethod - def _ascii_group_items( - circuit_qubits: QubitSet, - items: list[Union[Instruction, ResultType]], - ) -> list[tuple[QubitSet, list[Instruction]]]: - """ - Group instructions in a moment for ASCII diagram - - Args: - circuit_qubits (QubitSet): set of qubits in circuit - items (list[Union[Instruction, ResultType]]): list of instructions or result types - - Returns: - list[tuple[QubitSet, list[Instruction]]]: list of grouped instructions or result types. - """ - groupings = [] - for item in items: - # Can only print Gate and Noise operators for instructions at the moment - if isinstance(item, Instruction) and not isinstance( - item.operator, (Gate, Noise, CompilerDirective) - ): - continue - - # As a zero-qubit gate, GPhase can be grouped with anything. We set qubit_range - # to an empty list and we just add it to the first group below. - if ( - isinstance(item, Instruction) - and isinstance(item.operator, Gate) - and item.operator.name == "GPhase" - ): - qubit_range = QubitSet() - elif (isinstance(item, ResultType) and not item.target) or ( - isinstance(item, Instruction) and isinstance(item.operator, CompilerDirective) - ): - qubit_range = circuit_qubits - else: - if isinstance(item.target, list): - target = reduce(QubitSet.union, map(QubitSet, item.target), QubitSet()) - else: - target = item.target - control = getattr(item, "control", QubitSet()) - target_and_control = target.union(control) - qubit_range = QubitSet(range(min(target_and_control), max(target_and_control) + 1)) - - found_grouping = False - for group in groupings: - qubits_added = group[0] - instr_group = group[1] - # Take into account overlapping multi-qubit gates - if not qubits_added.intersection(set(qubit_range)): - instr_group.append(item) - qubits_added.update(qubit_range) - found_grouping = True - break - - if not found_grouping: - groupings.append((qubit_range, [item])) - - return groupings - - @staticmethod - def _categorize_result_types( - result_types: list[ResultType], - ) -> tuple[list[str], list[ResultType]]: - """ - Categorize result types into result types with target and those without. - - Args: - result_types (list[ResultType]): list of result types - - Returns: - tuple[list[str], list[ResultType]]: first element is a list of result types - without `target` attribute; second element is a list of result types with - `target` attribute - """ - additional_result_types = [] - target_result_types = [] - for result_type in result_types: - if hasattr(result_type, "target"): - target_result_types.append(result_type) - else: - additional_result_types.extend(result_type.ascii_symbols) - return additional_result_types, target_result_types - - @staticmethod - def _ascii_diagram_column_set( - col_title: str, - circuit_qubits: QubitSet, - items: list[Union[Instruction, ResultType]], - global_phase: float | None, - ) -> str: + def _create_qubit_layout(qubit: Qubit, y_axis_width: int) -> None: """ - Return a set of columns in the ASCII string diagram of the circuit for a list of items. + Create the layout of the qubit. Args: - col_title (str): title of column set - 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 set - - Returns: - str: An ASCII string diagram for the column set. + qubit (Qubit): Qubit to create the layout for. + y_axis_width (int): Width of the y axis. """ - - # Group items to separate out overlapping multi-qubit items - groupings = BoxDrawingCircuitDiagram._ascii_group_items(circuit_qubits, items) - - column_strs = [ - BoxDrawingCircuitDiagram._ascii_diagram_column( - circuit_qubits, grouping[1], global_phase - ) - for grouping in groupings - ] - - # Unite column strings - lines = column_strs[0].split("\n") - for column_str in column_strs[1:]: - for i, moment_line in enumerate(column_str.split("\n")): - lines[i] += moment_line - - first_line = "{:^{width}}│\n".format(col_title, width=len(lines[0]) - 1) - - return first_line + "\n".join(lines) + y_axis_str = "{0:{width}}\n".format(" ", width=y_axis_width + 5) + y_axis_str += "q{0:{width}} : ─\n".format(str(int(qubit)), width=y_axis_width) + y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) + return y_axis_str @staticmethod def _build_parameters( @@ -510,15 +357,3 @@ def _build_verbatim_box( symbol = BoxDrawingCircuitDiagram._fill_symbol(symbol, "─") return top, symbol, bottom - - @staticmethod - def _build_map_control_qubits(item: Instruction, control_qubits: QubitSet) -> dict(Qubit, int): - control_state = getattr(item, "control_state", None) - if control_state is not None: - map_control_qubit_states = { - qubit: state for qubit, state in zip(control_qubits, control_state) - } - else: - map_control_qubit_states = {qubit: 1 for qubit in control_qubits} - - return map_control_qubit_states From fffb6e29046270209be36acf5544e84a7e6b8c2e Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 19:18:45 -0500 Subject: [PATCH 28/62] more simplification --- src/braket/circuits/ascii_circuit_diagram.py | 36 +++++---- .../circuits/box_drawing_circuit_diagram.py | 78 +------------------ 2 files changed, 21 insertions(+), 93 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index b913983ea..7efbe40bb 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -32,9 +32,10 @@ class AsciiCircuitDiagram(CircuitDiagram): """Builds ASCII string circuit diagrams.""" vdelim = "|" + _add_empty_line = True - @staticmethod - def build_diagram(circuit: cir.Circuit) -> str: + @classmethod + def build_diagram(cls, circuit: cir.Circuit) -> str: """ Build an ASCII string circuit diagram. @@ -54,9 +55,7 @@ def build_diagram(circuit: cir.Circuit) -> str: circuit_qubits = circuit.qubits circuit_qubits.sort() - y_axis_str, global_phase = AsciiCircuitDiagram._prepare_diagram_vars( - circuit, circuit_qubits - ) + y_axis_str, global_phase = cls._prepare_diagram_vars(circuit, circuit_qubits) time_slices = circuit.moments.time_slices() column_strs = [] @@ -66,7 +65,7 @@ def build_diagram(circuit: cir.Circuit) -> str: global_phase = AsciiCircuitDiagram._compute_moment_global_phase( global_phase, instructions ) - moment_str = AsciiCircuitDiagram._ascii_diagram_column_set( + moment_str = cls._ascii_diagram_column_set( str(time), circuit_qubits, instructions, global_phase ) column_strs.append(moment_str) @@ -77,19 +76,19 @@ def build_diagram(circuit: cir.Circuit) -> str: ) if target_result_types: column_strs.append( - AsciiCircuitDiagram._ascii_diagram_column_set( + cls._ascii_diagram_column_set( "Result Types", circuit_qubits, target_result_types, global_phase ) ) # Unite strings - lines = y_axis_str.split("\n") - for col_str in column_strs: - for i, line_in_col in enumerate(col_str.split("\n")): - lines[i] += line_in_col + lines = AsciiCircuitDiagram._unite_strings(y_axis_str, column_strs) # Time on top and bottom - lines.append(lines[0]) + if cls._add_empty_line: + lines.append(lines[0]) + else: + lines[-1] = lines[0] if global_phase: lines.append(f"\nGlobal phase: {global_phase}") @@ -107,6 +106,14 @@ def build_diagram(circuit: cir.Circuit) -> str: return "\n".join(lines) + @staticmethod + def _unite_strings(first_column: str, column_strs: list[str]) -> str: + lines = first_column.split("\n") + for col_str in column_strs: + for i, line_in_col in enumerate(col_str.split("\n")): + lines[i] += line_in_col + return lines + @classmethod def _prepare_diagram_vars( cls, circuit: cir.Circuit, circuit_qubits: QubitSet @@ -279,10 +286,7 @@ def _ascii_diagram_column_set( ] # Unite column strings - lines = column_strs[0].split("\n") - for column_str in column_strs[1:]: - for i, moment_line in enumerate(column_str.split("\n")): - lines[i] += moment_line + lines = AsciiCircuitDiagram._unite_strings(column_strs[0], column_strs[1:]) # Adjust for column title width col_title_width = len(col_title) diff --git a/src/braket/circuits/box_drawing_circuit_diagram.py b/src/braket/circuits/box_drawing_circuit_diagram.py index c28bd929e..26de2e721 100644 --- a/src/braket/circuits/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/box_drawing_circuit_diagram.py @@ -16,12 +16,10 @@ from functools import reduce from typing import Literal -import braket.circuits.circuit as cir from braket.circuits.ascii_circuit_diagram import AsciiCircuitDiagram from braket.circuits.compiler_directive import CompilerDirective from braket.circuits.gate import Gate from braket.circuits.instruction import Instruction -from braket.circuits.moments import MomentType from braket.circuits.result_type import ResultType from braket.registers.qubit import Qubit from braket.registers.qubit_set import QubitSet @@ -31,81 +29,7 @@ class BoxDrawingCircuitDiagram(AsciiCircuitDiagram): """Builds ASCII string circuit diagrams using box-drawing characters.""" vdelim = "│" - - @staticmethod - def build_diagram(circuit: cir.Circuit) -> str: - """ - Build an ASCII string circuit diagram using box-drawing characters. - - Args: - circuit (Circuit): Circuit for which to build a diagram. - - Returns: - str: ASCII string circuit diagram. - """ - - if not circuit.instructions: - return "" - - if all(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): - return f"Global phase: {circuit.global_phase}" - - circuit_qubits = circuit.qubits - circuit_qubits.sort() - - y_axis_str, global_phase = BoxDrawingCircuitDiagram._prepare_diagram_vars( - circuit, circuit_qubits - ) - - time_slices = circuit.moments.time_slices() - column_strs = [] - - # Moment columns - for time, instructions in time_slices.items(): - global_phase = AsciiCircuitDiagram._compute_moment_global_phase( - global_phase, instructions - ) - moment_str = BoxDrawingCircuitDiagram._ascii_diagram_column_set( - str(time), circuit_qubits, instructions, global_phase - ) - column_strs.append(moment_str) - - # Result type columns - ( - additional_result_types, - target_result_types, - ) = AsciiCircuitDiagram._categorize_result_types(circuit.result_types) - if target_result_types: - column_strs.append( - BoxDrawingCircuitDiagram._ascii_diagram_column_set( - "Result Types", circuit_qubits, target_result_types, global_phase - ) - ) - - # Unite strings - lines = y_axis_str.split("\n") - for col_str in column_strs: - for i, line_in_col in enumerate(col_str.split("\n")): - lines[i] += line_in_col - - # Replace the last (empty) line with time - lines[-1] = lines[0] - - if global_phase: - lines.append(f"\nGlobal phase: {global_phase}") - - # Additional result types line on bottom - if additional_result_types: - lines.append(f"\nAdditional result types: {', '.join(additional_result_types)}") - - # A list of parameters in the circuit to the currently assigned values. - if circuit.parameters: - lines.append( - "\nUnassigned parameters: " - f"{sorted(circuit.parameters, key=lambda param: param.name)}." - ) - - return "\n".join(lines) + _add_empty_line = False @staticmethod def _create_qubit_layout(qubit: Qubit, y_axis_width: int) -> None: From ebfa98918ecda864cb9cc0c7936d12ad2888ec4e Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 19:22:53 -0500 Subject: [PATCH 29/62] add comment --- src/braket/circuits/ascii_circuit_diagram.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 7efbe40bb..02485e13d 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -85,6 +85,7 @@ def build_diagram(cls, circuit: cir.Circuit) -> str: lines = AsciiCircuitDiagram._unite_strings(y_axis_str, column_strs) # Time on top and bottom + # We only add an empty line for AsciiCircuitDiagram if cls._add_empty_line: lines.append(lines[0]) else: From fbf4d83ddb8f4b05fb5721d231566d57142ac9e4 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 19:45:49 -0500 Subject: [PATCH 30/62] add back build_diagram for readibilty --- src/braket/circuits/box_drawing_circuit_diagram.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/braket/circuits/box_drawing_circuit_diagram.py b/src/braket/circuits/box_drawing_circuit_diagram.py index 26de2e721..36b39d781 100644 --- a/src/braket/circuits/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/box_drawing_circuit_diagram.py @@ -16,6 +16,7 @@ from functools import reduce from typing import Literal +import braket.circuits.circuit as cir from braket.circuits.ascii_circuit_diagram import AsciiCircuitDiagram from braket.circuits.compiler_directive import CompilerDirective from braket.circuits.gate import Gate @@ -31,6 +32,19 @@ class BoxDrawingCircuitDiagram(AsciiCircuitDiagram): vdelim = "│" _add_empty_line = False + @staticmethod + def build_diagram(circuit: cir.Circuit) -> str: + """ + Build an ASCII string circuit diagram using box-drawing characters. + + Args: + circuit (Circuit): Circuit for which to build a diagram. + + Returns: + str: ASCII string circuit diagram. + """ + return super(BoxDrawingCircuitDiagram, BoxDrawingCircuitDiagram).build_diagram(circuit) + @staticmethod def _create_qubit_layout(qubit: Qubit, y_axis_width: int) -> None: """ From c876b510118209f9f4cb19e503b1a3f187ac4f78 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 19:56:42 -0500 Subject: [PATCH 31/62] use a _build_box method --- .../circuits/box_drawing_circuit_diagram.py | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/braket/circuits/box_drawing_circuit_diagram.py b/src/braket/circuits/box_drawing_circuit_diagram.py index 36b39d781..60baf1935 100644 --- a/src/braket/circuits/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/box_drawing_circuit_diagram.py @@ -262,23 +262,32 @@ def _draw_symbol( # We do not box when no gate is applied. pass else: - top_edge_symbol = "┴" if connection == "above" or connection == "both" else "─" - top = f"┌─{fill_symbol(top_edge_symbol, '─', len(symbol))}─┐" - - bottom_edge_symbol = "┬" if connection == "below" or connection == "both" else "─" - bottom = f"└─{fill_symbol(bottom_edge_symbol, '─', len(symbol))}─┘" - - symbol = f"┤ {symbol} ├" + top, symbol, bottom = BoxDrawingCircuitDiagram._build_box(symbol, connection) output = fill_symbol(top, " ", symbols_width + 1) + "\n" output += fill_symbol(symbol, "─", symbols_width + 1) + "\n" output += fill_symbol(bottom, " ", symbols_width + 1) + "\n" return output + @staticmethod + def _build_box( + symbol: str, connection: Literal["above, below, both, none"] + ) -> tuple[str, str, str]: + fill_symbol = BoxDrawingCircuitDiagram._fill_symbol + + top_edge_symbol = "┴" if connection == "above" or connection == "both" else "─" + top = f"┌─{fill_symbol(top_edge_symbol, '─', len(symbol))}─┐" + + bottom_edge_symbol = "┬" if connection == "below" or connection == "both" else "─" + bottom = f"└─{fill_symbol(bottom_edge_symbol, '─', len(symbol))}─┘" + + symbol = f"┤ {symbol} ├" + return top, symbol, bottom + @staticmethod def _build_verbatim_box( symbol: Literal["StartVerbatim", "EndVerbatim"], - connection: Literal["above, below, both, none"] = "none", + connection: Literal["above, below, both, none"], ) -> str: top = "" bottom = "" From a1d056faa887ec85cb0bd7634a92dad29d217d8d Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 20:06:38 -0500 Subject: [PATCH 32/62] simplify _build_parameters --- .../circuits/box_drawing_circuit_diagram.py | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/src/braket/circuits/box_drawing_circuit_diagram.py b/src/braket/circuits/box_drawing_circuit_diagram.py index 60baf1935..f81d9c6b5 100644 --- a/src/braket/circuits/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/box_drawing_circuit_diagram.py @@ -65,25 +65,14 @@ def _build_parameters( ) -> tuple: map_control_qubit_states = {} - if isinstance(item, ResultType) and not item.target: - target_qubits = circuit_qubits - control_qubits = QubitSet() - qubits = circuit_qubits - if len(qubits) > 1: - connections |= {qubit: "both" for qubit in qubits[1:-1]} - connections[qubits[-1]] = "above" - connections[qubits[0]] = "below" - ascii_symbols = [item.ascii_symbols[0]] * len(circuit_qubits) - elif isinstance(item, Instruction) and isinstance(item.operator, CompilerDirective): + if (isinstance(item, ResultType) and not item.target) or ( + isinstance(item, Instruction) and isinstance(item.operator, CompilerDirective) + ): target_qubits = circuit_qubits control_qubits = QubitSet() qubits = circuit_qubits - ascii_symbol = item.ascii_symbols[0] - ascii_symbols = [ascii_symbol] * len(circuit_qubits) - if len(circuit_qubits) > 1: - connections = {qubit: "both" for qubit in circuit_qubits[1:-1]} - connections[circuit_qubits[-1]] = "above" - connections[circuit_qubits[0]] = "below" + ascii_symbols = [item.ascii_symbols[0]] * len(qubits) + BoxDrawingCircuitDiagram._update_connections(qubits, connections) elif ( isinstance(item, Instruction) and isinstance(item.operator, Gate) @@ -105,12 +94,8 @@ def _build_parameters( target_and_control = target_qubits.union(control_qubits) qubits = QubitSet(range(min(target_and_control), max(target_and_control) + 1)) - if len(qubits) > 1: - connections |= {qubit: "both" for qubit in qubits[1:-1]} - connections[qubits[-1]] = "above" - connections[qubits[0]] = "below" - ascii_symbols = item.ascii_symbols + BoxDrawingCircuitDiagram._update_connections(qubits, connections) return ( target_qubits, @@ -121,6 +106,13 @@ def _build_parameters( map_control_qubit_states, ) + @staticmethod + def _update_connections(qubits: QubitSet, connections: dict[Qubit, str]) -> None: + if len(qubits) > 1: + connections |= {qubit: "both" for qubit in qubits[1:-1]} + connections[qubits[-1]] = "above" + connections[qubits[0]] = "below" + @staticmethod def _ascii_diagram_column( circuit_qubits: QubitSet, From a6fe529db2385613db83077e4985be5b2415db97 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 20:14:15 -0500 Subject: [PATCH 33/62] remove unnecessary code --- src/braket/circuits/box_drawing_circuit_diagram.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/braket/circuits/box_drawing_circuit_diagram.py b/src/braket/circuits/box_drawing_circuit_diagram.py index f81d9c6b5..e64a9eca9 100644 --- a/src/braket/circuits/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/box_drawing_circuit_diagram.py @@ -142,7 +142,6 @@ def _ascii_diagram_column( ascii_symbols, map_control_qubit_states, ) = BoxDrawingCircuitDiagram._build_parameters(circuit_qubits, item, connections) - target_and_control = target_qubits.union(control_qubits) for qubit in qubits: # Determine if the qubit is part of the item or in the middle of a @@ -168,22 +167,9 @@ def _ascii_diagram_column( if power_string else ascii_symbols[item_qubit_index] ) - if symbols[qubit] in ["C", "N"]: - if min(target_and_control) < qubit < max(target_and_control): - connections[qubit] = "both" - elif qubit == max(target_and_control): - connections[qubit] = "above" - else: - connections[qubit] = "below" elif qubit in control_qubits: symbols[qubit] = "C" if map_control_qubit_states[qubit] else "N" - if min(target_and_control) < qubit < max(target_and_control): - connections[qubit] = "both" - elif qubit == max(target_and_control): - connections[qubit] = "above" - else: - connections[qubit] = "below" else: symbols[qubit] = "┼" From 24073a04d21084460e9d3259cc317b2919f498ce Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 27 Dec 2023 20:35:18 -0500 Subject: [PATCH 34/62] mutualize create_output --- src/braket/circuits/ascii_circuit_diagram.py | 26 +++++++------ .../circuits/box_drawing_circuit_diagram.py | 37 +++---------------- 2 files changed, 21 insertions(+), 42 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 02485e13d..c18fb7e03 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -33,6 +33,7 @@ class AsciiCircuitDiagram(CircuitDiagram): vdelim = "|" _add_empty_line = True + _buffer_size = 0 @classmethod def build_diagram(cls, circuit: cir.Circuit) -> str: @@ -404,14 +405,15 @@ def _ascii_diagram_column( output = AsciiCircuitDiagram._create_output(symbols, margins, circuit_qubits, global_phase) return output - @staticmethod + @classmethod def _create_output( + cls, symbols: dict[Qubit, str], margins: dict[Qubit, str], qubits: QubitSet, global_phase: float | None, ) -> str: - symbols_width = max([len(symbol) for symbol in symbols.values()]) + symbols_width = max([len(symbol) for symbol in symbols.values()]) + cls._buffer_size output = "" if global_phase is not None: @@ -419,18 +421,20 @@ def _create_output( f"{global_phase:.2f}" if isinstance(global_phase, float) else str(global_phase) ) symbols_width = max([symbols_width, len(global_phase_str)]) - output += "{0:{fill}{align}{width}}|\n".format( - global_phase_str, - fill=" ", - align="^", - width=symbols_width, + output += "{0:{fill}{align}{width}}{vdelim}\n".format( + global_phase_str, fill=" ", align="^", width=symbols_width, vdelim=cls.vdelim ) for qubit in qubits: - output += "{0:{width}}\n".format(margins[qubit], width=symbols_width + 1) - output += "{0:{fill}{align}{width}}\n".format( - symbols[qubit], fill="-", align="<", width=symbols_width + 1 - ) + output += cls._draw_symbol(symbols[qubit], symbols_width, margins[qubit]) + return output + + @classmethod + def _draw_symbol(cls, symbol: str, symbols_width: int, connection: str) -> str: + output = "{0:{width}}\n".format(connection, width=symbols_width + 1) + output += "{0:{fill}{align}{width}}\n".format( + symbol, fill="-", align="<", width=symbols_width + 1 + ) return output @staticmethod diff --git a/src/braket/circuits/box_drawing_circuit_diagram.py b/src/braket/circuits/box_drawing_circuit_diagram.py index e64a9eca9..c4e6467a1 100644 --- a/src/braket/circuits/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/box_drawing_circuit_diagram.py @@ -31,6 +31,7 @@ class BoxDrawingCircuitDiagram(AsciiCircuitDiagram): vdelim = "│" _add_empty_line = False + _buffer_size = 4 @staticmethod def build_diagram(circuit: cir.Circuit) -> str: @@ -178,35 +179,6 @@ def _ascii_diagram_column( ) return output - @staticmethod - def _create_output( - symbols: dict[Qubit, str], - connections: dict[Qubit, str], - qubits: QubitSet, - global_phase: float | None, - ) -> str: - # We add 4 because of the edges of the box, i.e. "┤ " and " ├" - symbols_width = max([len(symbol) for symbol in symbols.values()]) + 4 - output = "" - - if global_phase is not None: - global_phase_str = ( - f"{global_phase:.2f}" if isinstance(global_phase, float) else str(global_phase) - ) - symbols_width = max([symbols_width, len(global_phase_str)]) - output += "{0:{fill}{align}{width}}│\n".format( - global_phase_str, - fill=" ", - align="^", - width=symbols_width, - ) - - for qubit in qubits: - output += BoxDrawingCircuitDiagram._draw_symbol( - symbols[qubit], symbols_width, connections[qubit] - ) - return output - @staticmethod def _fill_symbol(symbol: str, filler: str, width: int | None = None) -> str: return "{0:{fill}{align}{width}}".format( @@ -216,9 +188,12 @@ def _fill_symbol(symbol: str, filler: str, width: int | None = None) -> str: width=width if width is not None else len(symbol) + 1, ) - @staticmethod + @classmethod def _draw_symbol( - symbol: str, symbols_width: int, connection: Literal["above, below, both, none"] = "none" + cls, + symbol: str, + symbols_width: int, + connection: Literal["above, below, both, none"], ) -> str: fill_symbol = BoxDrawingCircuitDiagram._fill_symbol From fcca35696a80ab0aa44ab378d1a7ab6656435c2b Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Thu, 28 Dec 2023 14:21:09 -0500 Subject: [PATCH 35/62] add another xfail test --- .../test_box_drawing_circuit_diagram.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py b/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py index 2bf758e7d..48e695743 100644 --- a/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py @@ -172,6 +172,25 @@ def test_qubit_width(): _assert_correct_diagram(circ, expected) +@pytest.mark.xfail +def test_different_size_boxes(): + circ = Circuit().cnot(0,1).rx(2, 0.3) + expected = ( + "T : │ 0 │", + " ", + "q0 : ──────●───────", + " │ ", + " ┌─┴─┐ ", + "q1 : ────┤ X ├─────", + " └───┘ ", + " ┌──────────┐ ", + "q2 : ─┤ Rx(0.30) ├─", + " └──────────┘ ", + "T : │ 0 │", + ) + _assert_correct_diagram(circ, expected) + + def test_gate_width(): class Foo(Gate): def __init__(self): From 44f3e206496cc0c0e5a62b7777705816cb0d8686 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Thu, 28 Dec 2023 14:23:06 -0500 Subject: [PATCH 36/62] fix linters --- .../braket/circuits/test_box_drawing_circuit_diagram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py b/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py index 48e695743..3c893ec39 100644 --- a/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py @@ -174,7 +174,7 @@ def test_qubit_width(): @pytest.mark.xfail def test_different_size_boxes(): - circ = Circuit().cnot(0,1).rx(2, 0.3) + circ = Circuit().cnot(0, 1).rx(2, 0.3) expected = ( "T : │ 0 │", " ", From 3515dba42e382dfd69422c20a19a084b6e9e4232 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Thu, 28 Dec 2023 15:30:39 -0500 Subject: [PATCH 37/62] fix misalignment --- src/braket/circuits/ascii_circuit_diagram.py | 24 +++++++------ .../circuits/box_drawing_circuit_diagram.py | 34 ++++++++++++------- .../test_box_drawing_circuit_diagram.py | 20 +++++------ 3 files changed, 44 insertions(+), 34 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index c18fb7e03..1c7e9a069 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -31,7 +31,8 @@ class AsciiCircuitDiagram(CircuitDiagram): """Builds ASCII string circuit diagrams.""" - vdelim = "|" + _vdelim = "|" + _qubit_line_char = "-" _add_empty_line = True _buffer_size = 0 @@ -123,13 +124,13 @@ def _prepare_diagram_vars( # Y Axis Column y_axis_width = len(str(int(max(circuit_qubits)))) y_axis_str = "{0:{width}} : {vdelim}\n".format( - "T", width=y_axis_width + 1, vdelim=cls.vdelim + "T", width=y_axis_width + 1, vdelim=cls._vdelim ) global_phase = None if any(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): y_axis_str += "{0:{width}} : {vdelim}\n".format( - "GP", width=y_axis_width, vdelim=cls.vdelim + "GP", width=y_axis_width, vdelim=cls._vdelim ) global_phase = 0 @@ -296,19 +297,20 @@ def _ascii_diagram_column_set( if symbols_width < col_title_width: diff = col_title_width - symbols_width for i in range(len(lines) - 1): - if lines[i].endswith("-"): - lines[i] += "-" * diff + if lines[i].endswith(cls._qubit_line_char): + lines[i] += cls._qubit_line_char * diff else: lines[i] += " " first_line = "{:^{width}}{vdelim}\n".format( - col_title, width=len(lines[0]) - 1, vdelim=cls.vdelim + col_title, width=len(lines[0]) - 1, vdelim=cls._vdelim ) return first_line + "\n".join(lines) - @staticmethod + @classmethod def _ascii_diagram_column( + cls, circuit_qubits: QubitSet, items: list[Union[Instruction, ResultType]], global_phase: float | None = None, @@ -324,7 +326,7 @@ def _ascii_diagram_column( Returns: str: an ASCII string diagram for the specified moment in time for a column. """ - symbols = {qubit: "-" for qubit in circuit_qubits} + symbols = {qubit: cls._qubit_line_char for qubit in circuit_qubits} margins = {qubit: " " for qubit in circuit_qubits} for item in items: @@ -353,7 +355,7 @@ def _ascii_diagram_column( control_qubits = QubitSet() target_and_control = QubitSet() qubits = circuit_qubits - ascii_symbols = "-" * len(circuit_qubits) + ascii_symbols = cls._qubit_line_char * len(circuit_qubits) else: if isinstance(item.target, list): target_qubits = reduce(QubitSet.union, map(QubitSet, item.target), QubitSet()) @@ -422,7 +424,7 @@ def _create_output( ) symbols_width = max([symbols_width, len(global_phase_str)]) output += "{0:{fill}{align}{width}}{vdelim}\n".format( - global_phase_str, fill=" ", align="^", width=symbols_width, vdelim=cls.vdelim + global_phase_str, fill=" ", align="^", width=symbols_width, vdelim=cls._vdelim ) for qubit in qubits: @@ -433,7 +435,7 @@ def _create_output( def _draw_symbol(cls, symbol: str, symbols_width: int, connection: str) -> str: output = "{0:{width}}\n".format(connection, width=symbols_width + 1) output += "{0:{fill}{align}{width}}\n".format( - symbol, fill="-", align="<", width=symbols_width + 1 + symbol, fill=cls._qubit_line_char, align="<", width=symbols_width + 1 ) return output diff --git a/src/braket/circuits/box_drawing_circuit_diagram.py b/src/braket/circuits/box_drawing_circuit_diagram.py index c4e6467a1..1b9faf41d 100644 --- a/src/braket/circuits/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/box_drawing_circuit_diagram.py @@ -29,7 +29,8 @@ class BoxDrawingCircuitDiagram(AsciiCircuitDiagram): """Builds ASCII string circuit diagrams using box-drawing characters.""" - vdelim = "│" + _vdelim = "│" + _qubit_line_char = "─" _add_empty_line = False _buffer_size = 4 @@ -56,7 +57,11 @@ def _create_qubit_layout(qubit: Qubit, y_axis_width: int) -> None: y_axis_width (int): Width of the y axis. """ y_axis_str = "{0:{width}}\n".format(" ", width=y_axis_width + 5) - y_axis_str += "q{0:{width}} : ─\n".format(str(int(qubit)), width=y_axis_width) + y_axis_str += "q{0:{width}} : {qubit_line_char}\n".format( + str(int(qubit)), + width=y_axis_width, + qubit_line_char=BoxDrawingCircuitDiagram._qubit_line_char, + ) y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) return y_axis_str @@ -82,7 +87,7 @@ def _build_parameters( target_qubits = circuit_qubits control_qubits = QubitSet() qubits = circuit_qubits - ascii_symbols = "─" * len(circuit_qubits) + ascii_symbols = BoxDrawingCircuitDiagram._qubit_line_char * len(circuit_qubits) else: if isinstance(item.target, list): target_qubits = reduce(QubitSet.union, map(QubitSet, item.target), QubitSet()) @@ -114,8 +119,9 @@ def _update_connections(qubits: QubitSet, connections: dict[Qubit, str]) -> None connections[qubits[-1]] = "above" connections[qubits[0]] = "below" - @staticmethod + @classmethod def _ascii_diagram_column( + cls, circuit_qubits: QubitSet, items: list[Instruction | ResultType], global_phase: float | None = None, @@ -131,7 +137,7 @@ def _ascii_diagram_column( Returns: str: an ASCII string diagram for the specified moment in time for a column. """ - symbols = {qubit: "─" for qubit in circuit_qubits} + symbols = {qubit: cls._qubit_line_char for qubit in circuit_qubits} connections = {qubit: "none" for qubit in circuit_qubits} for item in items: @@ -142,7 +148,7 @@ def _ascii_diagram_column( connections, ascii_symbols, map_control_qubit_states, - ) = BoxDrawingCircuitDiagram._build_parameters(circuit_qubits, item, connections) + ) = cls._build_parameters(circuit_qubits, item, connections) for qubit in qubits: # Determine if the qubit is part of the item or in the middle of a @@ -174,7 +180,7 @@ def _ascii_diagram_column( else: symbols[qubit] = "┼" - output = BoxDrawingCircuitDiagram._create_output( + output = cls._create_output( symbols, connections, circuit_qubits, global_phase ) return output @@ -185,7 +191,7 @@ def _fill_symbol(symbol: str, filler: str, width: int | None = None) -> str: symbol, fill=filler, align="^", - width=width if width is not None else len(symbol) + 1, + width=width if width is not None else len(symbol), ) @classmethod @@ -217,9 +223,11 @@ def _draw_symbol( else: top, symbol, bottom = BoxDrawingCircuitDiagram._build_box(symbol, connection) - output = fill_symbol(top, " ", symbols_width + 1) + "\n" - output += fill_symbol(symbol, "─", symbols_width + 1) + "\n" - output += fill_symbol(bottom, " ", symbols_width + 1) + "\n" + output = f"{fill_symbol(top, ' ', symbols_width)} \n" + output += ( + f"{fill_symbol(symbol, cls._qubit_line_char, symbols_width)}{cls._qubit_line_char}\n" + ) + output += f"{fill_symbol(bottom, ' ', symbols_width)} \n" return output @staticmethod @@ -253,7 +261,9 @@ def _build_verbatim_box( top = "║" symbol = "╨" top = BoxDrawingCircuitDiagram._fill_symbol(top, " ") + symbol = BoxDrawingCircuitDiagram._fill_symbol( + symbol, BoxDrawingCircuitDiagram._qubit_line_char + ) bottom = BoxDrawingCircuitDiagram._fill_symbol(bottom, " ") - symbol = BoxDrawingCircuitDiagram._fill_symbol(symbol, "─") return top, symbol, bottom diff --git a/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py b/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py index 3c893ec39..779930363 100644 --- a/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py @@ -172,7 +172,6 @@ def test_qubit_width(): _assert_correct_diagram(circ, expected) -@pytest.mark.xfail def test_different_size_boxes(): circ = Circuit().cnot(0, 1).rx(2, 0.3) expected = ( @@ -699,9 +698,9 @@ def test_multiple_result_types(): ) expected = ( "T : │ 0 │ 1 │ Result Types │", - " ┌───┐ ┌─────────────┐ ┌───────────┐ ", - "q0 : ───●─────────┤ H ├───┤ Variance(Y) ├──┤ Sample(Y) ├─", - " │ └───┘ └─────────────┘ └─────┬─────┘ ", + " ┌───┐ ┌─────────────┐ ┌───────────┐ ", + "q0 : ───●─────────┤ H ├──┤ Variance(Y) ├───┤ Sample(Y) ├─", + " │ └───┘ └─────────────┘ └─────┬─────┘ ", " │ ┌─────┴─────┐ ", "q1 : ───┼─────●────────────────────────────┤ Sample(Y) ├─", " │ │ └─────┬─────┘ ", @@ -730,9 +729,9 @@ def test_multiple_result_types_with_state_vector_amplitude(): ) expected = ( "T : │ 0 │ 1 │ Result Types │", - " ┌───┐ ┌─────────────┐ ", - "q0 : ───●─────────┤ H ├───────┤ Variance(Y) ├──────", - " │ └───┘ └─────────────┘ ", + " ┌───┐ ┌─────────────┐ ", + "q0 : ───●─────────┤ H ├──────┤ Variance(Y) ├───────", + " │ └───┘ └─────────────┘ ", " │ ┌────────────────────────┐ ", "q1 : ───┼─────●─────────┤ Expectation(Hermitian) ├─", " │ │ └────────────────────────┘ ", @@ -777,9 +776,9 @@ def test_multiple_result_types_with_custom_hermitian_ascii_symbol(): " ┌─┴─┐ │ ┌──────────┴──────────┐ ", "q2 : ─┤ X ├───┼─────────┤ Expectation(MyHerm) ├─", " └───┘ │ └─────────────────────┘ ", - " ┌─┴─┐ ┌────────────────┐ ", - "q3 : ───────┤ X ├──────────┤ Expectation(Y) ├───", - " └───┘ └────────────────┘ ", + " ┌─┴─┐ ┌────────────────┐ ", + "q3 : ───────┤ X ├─────────┤ Expectation(Y) ├────", + " └───┘ └────────────────┘ ", "T : │ 0 │ 1 │ Result Types │", ) _assert_correct_diagram(circ, expected) @@ -979,7 +978,6 @@ def __init__(self): _assert_correct_diagram(circ, expected) -@pytest.mark.xfail def test_unbalanced_ascii_symbols(): class FooFoo(Gate): def __init__(self): From 8628ab8328b4a2272ea1206392b4728612ca791c Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Thu, 28 Dec 2023 15:31:01 -0500 Subject: [PATCH 38/62] fix linters --- src/braket/circuits/box_drawing_circuit_diagram.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/braket/circuits/box_drawing_circuit_diagram.py b/src/braket/circuits/box_drawing_circuit_diagram.py index 1b9faf41d..3aa233d4e 100644 --- a/src/braket/circuits/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/box_drawing_circuit_diagram.py @@ -180,9 +180,7 @@ def _ascii_diagram_column( else: symbols[qubit] = "┼" - output = cls._create_output( - symbols, connections, circuit_qubits, global_phase - ) + output = cls._create_output(symbols, connections, circuit_qubits, global_phase) return output @staticmethod From bf71647c889dd5cb64fa9933fc9aa2b96f744e9d Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Thu, 28 Dec 2023 15:48:28 -0500 Subject: [PATCH 39/62] cleanup --- .../circuits/box_drawing_circuit_diagram.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/braket/circuits/box_drawing_circuit_diagram.py b/src/braket/circuits/box_drawing_circuit_diagram.py index 3aa233d4e..4ff247b22 100644 --- a/src/braket/circuits/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/box_drawing_circuit_diagram.py @@ -199,7 +199,7 @@ def _draw_symbol( symbols_width: int, connection: Literal["above, below, both, none"], ) -> str: - fill_symbol = BoxDrawingCircuitDiagram._fill_symbol + fill_symbol = cls._fill_symbol top = "" bottom = "" @@ -208,18 +208,17 @@ def _draw_symbol( top = fill_symbol("│", " ") if connection in ["below", "both"]: bottom = fill_symbol("│", " ") - symbol = fill_symbol("●" if symbol == "C" else "◯", "─") + symbol = fill_symbol("●" if symbol == "C" else "◯", cls._qubit_line_char) elif symbol in ["StartVerbatim", "EndVerbatim"]: - top, symbol, bottom = BoxDrawingCircuitDiagram._build_verbatim_box(symbol, connection) + top, symbol, bottom = cls._build_verbatim_box(symbol, connection) elif symbol == "┼": - top = fill_symbol("│", " ") - bottom = fill_symbol("│", " ") - symbol = fill_symbol(f"{symbol}", "─") - elif symbol == "─": + top = bottom = fill_symbol("│", " ") + symbol = fill_symbol(f"{symbol}", cls._qubit_line_char) + elif symbol == cls._qubit_line_char: # We do not box when no gate is applied. pass else: - top, symbol, bottom = BoxDrawingCircuitDiagram._build_box(symbol, connection) + top, symbol, bottom = cls._build_box(symbol, connection) output = f"{fill_symbol(top, ' ', symbols_width)} \n" output += ( From 75bbda3578a4694a32a057c3accafa20372f32dd Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Thu, 28 Dec 2023 15:52:36 -0500 Subject: [PATCH 40/62] make _fill_symbol private --- .../circuits/box_drawing_circuit_diagram.py | 50 ++++++++----------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/src/braket/circuits/box_drawing_circuit_diagram.py b/src/braket/circuits/box_drawing_circuit_diagram.py index 4ff247b22..9205a97f3 100644 --- a/src/braket/circuits/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/box_drawing_circuit_diagram.py @@ -183,15 +183,6 @@ def _ascii_diagram_column( output = cls._create_output(symbols, connections, circuit_qubits, global_phase) return output - @staticmethod - def _fill_symbol(symbol: str, filler: str, width: int | None = None) -> str: - return "{0:{fill}{align}{width}}".format( - symbol, - fill=filler, - align="^", - width=width if width is not None else len(symbol), - ) - @classmethod def _draw_symbol( cls, @@ -199,45 +190,41 @@ def _draw_symbol( symbols_width: int, connection: Literal["above, below, both, none"], ) -> str: - fill_symbol = cls._fill_symbol - top = "" bottom = "" if symbol in ["C", "N"]: if connection in ["above", "both"]: - top = fill_symbol("│", " ") + top = _fill_symbol("│", " ") if connection in ["below", "both"]: - bottom = fill_symbol("│", " ") - symbol = fill_symbol("●" if symbol == "C" else "◯", cls._qubit_line_char) + bottom = _fill_symbol("│", " ") + symbol = _fill_symbol("●" if symbol == "C" else "◯", cls._qubit_line_char) elif symbol in ["StartVerbatim", "EndVerbatim"]: top, symbol, bottom = cls._build_verbatim_box(symbol, connection) elif symbol == "┼": - top = bottom = fill_symbol("│", " ") - symbol = fill_symbol(f"{symbol}", cls._qubit_line_char) + top = bottom = _fill_symbol("│", " ") + symbol = _fill_symbol(f"{symbol}", cls._qubit_line_char) elif symbol == cls._qubit_line_char: # We do not box when no gate is applied. pass else: top, symbol, bottom = cls._build_box(symbol, connection) - output = f"{fill_symbol(top, ' ', symbols_width)} \n" + output = f"{_fill_symbol(top, ' ', symbols_width)} \n" output += ( - f"{fill_symbol(symbol, cls._qubit_line_char, symbols_width)}{cls._qubit_line_char}\n" + f"{_fill_symbol(symbol, cls._qubit_line_char, symbols_width)}{cls._qubit_line_char}\n" ) - output += f"{fill_symbol(bottom, ' ', symbols_width)} \n" + output += f"{_fill_symbol(bottom, ' ', symbols_width)} \n" return output @staticmethod def _build_box( symbol: str, connection: Literal["above, below, both, none"] ) -> tuple[str, str, str]: - fill_symbol = BoxDrawingCircuitDiagram._fill_symbol - top_edge_symbol = "┴" if connection == "above" or connection == "both" else "─" - top = f"┌─{fill_symbol(top_edge_symbol, '─', len(symbol))}─┐" + top = f"┌─{_fill_symbol(top_edge_symbol, '─', len(symbol))}─┐" bottom_edge_symbol = "┬" if connection == "below" or connection == "both" else "─" - bottom = f"└─{fill_symbol(bottom_edge_symbol, '─', len(symbol))}─┘" + bottom = f"└─{_fill_symbol(bottom_edge_symbol, '─', len(symbol))}─┘" symbol = f"┤ {symbol} ├" return top, symbol, bottom @@ -257,10 +244,17 @@ def _build_verbatim_box( elif connection == "above": top = "║" symbol = "╨" - top = BoxDrawingCircuitDiagram._fill_symbol(top, " ") - symbol = BoxDrawingCircuitDiagram._fill_symbol( - symbol, BoxDrawingCircuitDiagram._qubit_line_char - ) - bottom = BoxDrawingCircuitDiagram._fill_symbol(bottom, " ") + top = _fill_symbol(top, " ") + symbol = _fill_symbol(symbol, BoxDrawingCircuitDiagram._qubit_line_char) + bottom = _fill_symbol(bottom, " ") return top, symbol, bottom + + +def _fill_symbol(symbol: str, filler: str, width: int | None = None) -> str: + return "{0:{fill}{align}{width}}".format( + symbol, + fill=filler, + align="^", + width=width if width is not None else len(symbol), + ) From 997a7f76df4ede0031e6f0bdacee61203aa8be39 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Thu, 28 Dec 2023 15:58:39 -0500 Subject: [PATCH 41/62] clean an branching condition --- src/braket/circuits/box_drawing_circuit_diagram.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/braket/circuits/box_drawing_circuit_diagram.py b/src/braket/circuits/box_drawing_circuit_diagram.py index 9205a97f3..40f76aa94 100644 --- a/src/braket/circuits/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/box_drawing_circuit_diagram.py @@ -220,10 +220,10 @@ def _draw_symbol( def _build_box( symbol: str, connection: Literal["above, below, both, none"] ) -> tuple[str, str, str]: - top_edge_symbol = "┴" if connection == "above" or connection == "both" else "─" + top_edge_symbol = "┴" if connection in ["above", "both"] else "─" top = f"┌─{_fill_symbol(top_edge_symbol, '─', len(symbol))}─┐" - bottom_edge_symbol = "┬" if connection == "below" or connection == "both" else "─" + bottom_edge_symbol = "┬" if connection in ["below", "both"] else "─" bottom = f"└─{_fill_symbol(bottom_edge_symbol, '─', len(symbol))}─┘" symbol = f"┤ {symbol} ├" From 7a9771ea1e13d2da0abbd5cb49e60c2c2ac6fa5c Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 19 Jan 2024 12:55:40 -0500 Subject: [PATCH 42/62] draw swap gates with x --- .../circuits/box_drawing_circuit_diagram.py | 8 ++++ .../test_box_drawing_circuit_diagram.py | 42 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/src/braket/circuits/box_drawing_circuit_diagram.py b/src/braket/circuits/box_drawing_circuit_diagram.py index 40f76aa94..0e12e9bf0 100644 --- a/src/braket/circuits/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/box_drawing_circuit_diagram.py @@ -203,6 +203,14 @@ def _draw_symbol( elif symbol == "┼": top = bottom = _fill_symbol("│", " ") symbol = _fill_symbol(f"{symbol}", cls._qubit_line_char) + elif symbol == "SWAP": + if connection in ["above", "both"]: + top = _fill_symbol("│", " ") + if connection in ["below", "both"]: + bottom = _fill_symbol("│", " ") + # replace SWAP by x + # the size of the moment remains as if there was a box with 4 characters inside + symbol = _fill_symbol("x", cls._qubit_line_char) elif symbol == cls._qubit_line_char: # We do not box when no gate is applied. pass diff --git a/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py b/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py index 779930363..07e3aac48 100644 --- a/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py @@ -190,6 +190,24 @@ def test_different_size_boxes(): _assert_correct_diagram(circ, expected) +def test_swap(): + circ = Circuit().swap(0, 2).x(1) + expected = ( + "T : │ 0 │", + " ", + "q0 : ────x───────────", + " │ ", + " │ ┌───┐ ", + "q1 : ────┼─────┤ X ├─", + " │ └───┘ ", + " │ ", + "q2 : ────x───────────", + " ", + "T : │ 0 │", + ) + _assert_correct_diagram(circ, expected) + + def test_gate_width(): class Foo(Gate): def __init__(self): @@ -941,6 +959,30 @@ def test_hamiltonian(): _assert_correct_diagram(circ, expected) +# def test_multi_qubit_Foo(): +# class Foo(Gate): +# def __init__(self): +# super().__init__(qubit_count=2, ascii_symbols=["FOO"]) + +# circ = Circuit() +# circ.add_instruction(Instruction(Foo(), (0, 1))) +# circ.add_instruction(Instruction(Foo(), (0, 2))) +# expected = ( +# "T : │ 0 │ 1 │", +# " ┌─────┐ ┌─────┐ ", +# "q0 : ─┤ ├─┤ ├─", +# " │ FOO │ │ │ ", +# " │ │ │ │ ", +# "q1 : ─┤ ├─│ FOO │─", +# " └─────┘ │ │ ", +# " │ │ ", +# "q2 : ─────────┤ ├─", +# " └─────┘ ", +# "T : │ 0 │ 1 │", +# ) +# _assert_correct_diagram(circ, expected) + + def test_power(): class Foo(Gate): def __init__(self): From 4f363a3377fc0ca9b23008d693a95df3aebbb15c Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 19 Jan 2024 13:01:50 -0500 Subject: [PATCH 43/62] remove commented tests --- .../test_box_drawing_circuit_diagram.py | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py b/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py index 07e3aac48..49220268e 100644 --- a/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py @@ -959,30 +959,6 @@ def test_hamiltonian(): _assert_correct_diagram(circ, expected) -# def test_multi_qubit_Foo(): -# class Foo(Gate): -# def __init__(self): -# super().__init__(qubit_count=2, ascii_symbols=["FOO"]) - -# circ = Circuit() -# circ.add_instruction(Instruction(Foo(), (0, 1))) -# circ.add_instruction(Instruction(Foo(), (0, 2))) -# expected = ( -# "T : │ 0 │ 1 │", -# " ┌─────┐ ┌─────┐ ", -# "q0 : ─┤ ├─┤ ├─", -# " │ FOO │ │ │ ", -# " │ │ │ │ ", -# "q1 : ─┤ ├─│ FOO │─", -# " └─────┘ │ │ ", -# " │ │ ", -# "q2 : ─────────┤ ├─", -# " └─────┘ ", -# "T : │ 0 │ 1 │", -# ) -# _assert_correct_diagram(circ, expected) - - def test_power(): class Foo(Gate): def __init__(self): From 5f0e633895f26bf34ca83bd4e01d306759b8384e Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 19 Jan 2024 14:17:14 -0500 Subject: [PATCH 44/62] clean implementation --- .../circuits/box_drawing_circuit_diagram.py | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/braket/circuits/box_drawing_circuit_diagram.py b/src/braket/circuits/box_drawing_circuit_diagram.py index 0e12e9bf0..6e5923f6f 100644 --- a/src/braket/circuits/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/box_drawing_circuit_diagram.py @@ -192,25 +192,20 @@ def _draw_symbol( ) -> str: top = "" bottom = "" - if symbol in ["C", "N"]: + if symbol in ["C", "N", "SWAP"]: if connection in ["above", "both"]: - top = _fill_symbol("│", " ") + top = _fill_symbol(cls._vdelim, " ") if connection in ["below", "both"]: - bottom = _fill_symbol("│", " ") - symbol = _fill_symbol("●" if symbol == "C" else "◯", cls._qubit_line_char) + bottom = _fill_symbol(cls._vdelim, " ") + new_symbol = {"C": "●", "N": "◯", "SWAP": "x"} + # replace SWAP by x + # the size of the moment remains as if there was a box with 4 characters inside + symbol = _fill_symbol(new_symbol[symbol], cls._qubit_line_char) elif symbol in ["StartVerbatim", "EndVerbatim"]: top, symbol, bottom = cls._build_verbatim_box(symbol, connection) elif symbol == "┼": - top = bottom = _fill_symbol("│", " ") + top = bottom = _fill_symbol(cls._vdelim, " ") symbol = _fill_symbol(f"{symbol}", cls._qubit_line_char) - elif symbol == "SWAP": - if connection in ["above", "both"]: - top = _fill_symbol("│", " ") - if connection in ["below", "both"]: - bottom = _fill_symbol("│", " ") - # replace SWAP by x - # the size of the moment remains as if there was a box with 4 characters inside - symbol = _fill_symbol("x", cls._qubit_line_char) elif symbol == cls._qubit_line_char: # We do not box when no gate is applied. pass From 5fe7ed12681e8a2d8173d3a2ccfec219333880a0 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Thu, 25 Jan 2024 17:57:46 -0500 Subject: [PATCH 45/62] keep AsciiCircuitDiagram as default for now --- src/braket/circuits/circuit.py | 8 ++++---- test/unit_tests/braket/circuits/test_circuit.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/braket/circuits/circuit.py b/src/braket/circuits/circuit.py index ddc78d640..b5f799fc7 100644 --- a/src/braket/circuits/circuit.py +++ b/src/braket/circuits/circuit.py @@ -21,7 +21,7 @@ import oqpy from braket.circuits import compiler_directives -from braket.circuits.box_drawing_circuit_diagram import BoxDrawingCircuitDiagram +from braket.circuits.ascii_circuit_diagram import AsciiCircuitDiagram from braket.circuits.free_parameter import FreeParameter from braket.circuits.free_parameter_expression import FreeParameterExpression from braket.circuits.gate import Gate @@ -82,7 +82,7 @@ class Circuit: """ _ALL_QUBITS = "ALL" # Flag to indicate all qubits in _qubit_observable_mapping - default_diagram_builder = BoxDrawingCircuitDiagram + default_diagram_builder = AsciiCircuitDiagram @classmethod def register_subroutine(cls, func: SubroutineCallable) -> None: @@ -1093,13 +1093,13 @@ def adjoint(self) -> Circuit: circ.add_result_type(result_type) return circ - def diagram(self, circuit_diagram_class: type = BoxDrawingCircuitDiagram) -> str: + def diagram(self, circuit_diagram_class: type = AsciiCircuitDiagram) -> str: """ Get a diagram for the current circuit. Args: circuit_diagram_class (type): A `CircuitDiagram` class that builds the - diagram for this circuit. Default = `BoxDrawingCircuitDiagram`. + diagram for this circuit. Default = `AsciiCircuitDiagram`. Returns: str: An ASCII string circuit diagram. diff --git a/test/unit_tests/braket/circuits/test_circuit.py b/test/unit_tests/braket/circuits/test_circuit.py index fb7dbc7d1..7341ac9ec 100644 --- a/test/unit_tests/braket/circuits/test_circuit.py +++ b/test/unit_tests/braket/circuits/test_circuit.py @@ -180,13 +180,13 @@ def test_repr_result_types(cnot_prob): def test_str(h): - expected = BoxDrawingCircuitDiagram.build_diagram(h) + expected = AsciiCircuitDiagram.build_diagram(h) assert str(h) == expected def test_change_diagram_builder(h): - Circuit.default_diagram_builder = AsciiCircuitDiagram - expected = AsciiCircuitDiagram.build_diagram(h) + Circuit.default_diagram_builder = BoxDrawingCircuitDiagram + expected = BoxDrawingCircuitDiagram.build_diagram(h) assert str(h) == expected From 4b105935a8c333472a4efed4c722be3ee3cb4c11 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 23 Feb 2024 10:55:18 -0500 Subject: [PATCH 46/62] reorganize class structure --- src/braket/circuits/ascii_circuit_diagram.py | 321 +--------------- .../circuits/box_drawing_circuit_diagram.py | 148 ++++---- src/braket/circuits/circuit_diagram.py | 2 +- src/braket/circuits/text_circuit_diagram.py | 356 ++++++++++++++++++ 4 files changed, 445 insertions(+), 382 deletions(-) create mode 100644 src/braket/circuits/text_circuit_diagram.py diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 316ba6088..68bc7d8ba 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -17,290 +17,40 @@ from typing import Union import braket.circuits.circuit as cir -from braket.circuits.circuit_diagram import CircuitDiagram from braket.circuits.compiler_directive import CompilerDirective from braket.circuits.gate import Gate from braket.circuits.instruction import Instruction -from braket.circuits.moments import MomentType -from braket.circuits.noise import Noise from braket.circuits.result_type import ResultType -from braket.registers.qubit import Qubit +from braket.circuits.text_circuit_diagram import TextCircuitDiagram from braket.registers.qubit_set import QubitSet -class AsciiCircuitDiagram(CircuitDiagram): +class AsciiCircuitDiagram(TextCircuitDiagram): """Builds ASCII string circuit diagrams.""" - _vdelim = "|" - _qubit_line_char = "-" - _add_empty_line = True - _buffer_size = 0 - - @classmethod - def build_diagram(cls, circuit: cir.Circuit) -> str: - """ - Build an ASCII string circuit diagram. - - Args: - circuit (cir.Circuit): Circuit for which to build a diagram. - - Returns: - str: ASCII string circuit diagram. - """ - if not circuit.instructions: - return "" - - if all(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): - return f"Global phase: {circuit.global_phase}" - - circuit_qubits = circuit.qubits - circuit_qubits.sort() - - y_axis_str, global_phase = cls._prepare_diagram_vars(circuit, circuit_qubits) - - time_slices = circuit.moments.time_slices() - column_strs = [] - - # Moment columns - for time, instructions in time_slices.items(): - global_phase = AsciiCircuitDiagram._compute_moment_global_phase( - global_phase, instructions - ) - moment_str = cls._ascii_diagram_column_set( - str(time), circuit_qubits, instructions, global_phase - ) - column_strs.append(moment_str) - - # Result type columns - additional_result_types, target_result_types = AsciiCircuitDiagram._categorize_result_types( - circuit.result_types - ) - if target_result_types: - column_strs.append( - cls._ascii_diagram_column_set( - "Result Types", circuit_qubits, target_result_types, global_phase - ) - ) - - # Unite strings - lines = AsciiCircuitDiagram._unite_strings(y_axis_str, column_strs) - - # Time on top and bottom - # We only add an empty line for AsciiCircuitDiagram - if cls._add_empty_line: - lines.append(lines[0]) - else: - lines[-1] = lines[0] - - if global_phase: - lines.append(f"\nGlobal phase: {global_phase}") - - # Additional result types line on bottom - if additional_result_types: - lines.append(f"\nAdditional result types: {', '.join(additional_result_types)}") - - # A list of parameters in the circuit to the currently assigned values. - if circuit.parameters: - lines.append( - "\nUnassigned parameters: " - f"{sorted(circuit.parameters, key=lambda param: param.name)}." - ) - - return "\n".join(lines) + _vdelim = "|" # Character that connects qubits of multi-qubit gates + _qubit_line_char = "-" # Character used for the qubit line + _box_padding = 0 # number of blank space characters around the gate name + _qubit_line_spacing = {"before": 1, "after": 0} # number of empty lines around the qubit line @staticmethod - def _unite_strings(first_column: str, column_strs: list[str]) -> str: - lines = first_column.split("\n") - for col_str in column_strs: - for i, line_in_col in enumerate(col_str.split("\n")): - lines[i] += line_in_col - return lines - - @classmethod - def _prepare_diagram_vars( - cls, circuit: cir.Circuit, circuit_qubits: QubitSet - ) -> tuple[str, float | None]: - # Y Axis Column - y_axis_width = len(str(int(max(circuit_qubits)))) - y_axis_str = "{0:{width}} : {vdelim}\n".format( - "T", width=y_axis_width + 1, vdelim=cls._vdelim - ) - - global_phase = None - if any(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): - y_axis_str += "{0:{width}} : {vdelim}\n".format( - "GP", width=y_axis_width, vdelim=cls._vdelim - ) - global_phase = 0 - - for qubit in circuit_qubits: - y_axis_str += cls._create_qubit_layout(qubit, y_axis_width) - return y_axis_str, global_phase - - @staticmethod - def _create_qubit_layout(qubit: Qubit, y_axis_width: int) -> None: - """ - Create the layout of the qubit. - - Args: - qubit (Qubit): Qubit to create the layout for. - y_axis_width (int): Width of the y axis. + def build_diagram(circuit: cir.Circuit) -> str: """ - y_axis_str = "{0:{width}}\n".format(" ", width=y_axis_width + 5) - y_axis_str += "q{0:{width}} : -\n".format(str(int(qubit)), width=y_axis_width) - return y_axis_str - - @staticmethod - def _compute_moment_global_phase( - global_phase: float | None, items: list[Instruction] - ) -> float | None: - """Compute the integrated phase at a certain moment. + Build a text circuit diagram. Args: - global_phase (float | None): The integrated phase up to the computed moment - items (list[Instruction]): list of instructions + circuit (Circuit): Circuit for which to build a diagram. Returns: - float | None: The updated integrated phase. + str: string circuit diagram. """ - moment_phase = 0 - for item in items: - if ( - isinstance(item, Instruction) - and isinstance(item.operator, Gate) - and item.operator.name == "GPhase" - ): - moment_phase += item.operator.angle - return global_phase + moment_phase if global_phase is not None else None - - @staticmethod - def _ascii_group_items( - circuit_qubits: QubitSet, - items: list[Union[Instruction, ResultType]], - ) -> list[tuple[QubitSet, list[Instruction]]]: - """Group instructions in a moment for ASCII diagram - - Args: - circuit_qubits (QubitSet): set of qubits in circuit - items (list[Union[Instruction, ResultType]]): list of instructions or result types - Returns: - list[tuple[QubitSet, list[Instruction]]]: list of grouped instructions or result types. - """ - groupings = [] - for item in items: - # Can only print Gate and Noise operators for instructions at the moment - if isinstance(item, Instruction) and not isinstance( - item.operator, (Gate, Noise, CompilerDirective) - ): - continue - - # As a zero-qubit gate, GPhase can be grouped with anything. We set qubit_range - # to an empty list and we just add it to the first group below. - if ( - isinstance(item, Instruction) - and isinstance(item.operator, Gate) - and item.operator.name == "GPhase" - ): - qubit_range = QubitSet() - elif (isinstance(item, ResultType) and not item.target) or ( - isinstance(item, Instruction) and isinstance(item.operator, CompilerDirective) - ): - qubit_range = circuit_qubits - else: - if isinstance(item.target, list): - target = reduce(QubitSet.union, map(QubitSet, item.target), QubitSet()) - else: - target = item.target - control = getattr(item, "control", QubitSet()) - target_and_control = target.union(control) - qubit_range = QubitSet(range(min(target_and_control), max(target_and_control) + 1)) - - found_grouping = False - for group in groupings: - qubits_added = group[0] - instr_group = group[1] - # Take into account overlapping multi-qubit gates - if not qubits_added.intersection(set(qubit_range)): - instr_group.append(item) - qubits_added.update(qubit_range) - found_grouping = True - break - - if not found_grouping: - groupings.append((qubit_range, [item])) - - return groupings - - @staticmethod - def _categorize_result_types( - result_types: list[ResultType], - ) -> tuple[list[str], list[ResultType]]: - """Categorize result types into result types with target and those without. - - Args: - result_types (list[ResultType]): list of result types - - Returns: - tuple[list[str], list[ResultType]]: first element is a list of result types - without `target` attribute; second element is a list of result types with - `target` attribute - """ - additional_result_types = [] - target_result_types = [] - for result_type in result_types: - if hasattr(result_type, "target"): - target_result_types.append(result_type) - else: - additional_result_types.extend(result_type.ascii_symbols) - return additional_result_types, target_result_types + return AsciiCircuitDiagram._build_diagram_internal(circuit) @classmethod - def _ascii_diagram_column_set( - cls, - col_title: str, - circuit_qubits: QubitSet, - items: list[Union[Instruction, ResultType]], - global_phase: float | None, - ) -> str: - """Return a set of columns in the ASCII string diagram of the circuit for a list of items. - - Args: - col_title (str): title of column set - 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 set - - Returns: - str: An ASCII string diagram for the column set. - """ - # Group items to separate out overlapping multi-qubit items - groupings = AsciiCircuitDiagram._ascii_group_items(circuit_qubits, items) - - column_strs = [ - cls._ascii_diagram_column(circuit_qubits, grouping[1], global_phase) - for grouping in groupings - ] - - # Unite column strings - lines = AsciiCircuitDiagram._unite_strings(column_strs[0], column_strs[1:]) - - # Adjust for column title width - col_title_width = len(col_title) - symbols_width = len(lines[0]) - 1 - if symbols_width < col_title_width: - diff = col_title_width - symbols_width - for i in range(len(lines) - 1): - if lines[i].endswith(cls._qubit_line_char): - lines[i] += cls._qubit_line_char * diff - else: - lines[i] += " " - - first_line = "{:^{width}}{vdelim}\n".format( - col_title, width=len(lines[0]) - 1, vdelim=cls._vdelim - ) - - return first_line + "\n".join(lines) + def _duplicate_time_at_bottom(cls, lines: str) -> None: + # duplicate times after an empty line + lines.append(lines[0]) @classmethod def _ascii_diagram_column( @@ -355,9 +105,10 @@ def _ascii_diagram_column( else: target_qubits = item.target control_qubits = getattr(item, "control", QubitSet()) - map_control_qubit_states = AsciiCircuitDiagram._build_map_control_qubits( - item, control_qubits - ) + control_state = getattr(item, "control_state", "1" * len(control_qubits)) + map_control_qubit_states = { + qubit: state for qubit, state in 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)) @@ -397,31 +148,7 @@ def _ascii_diagram_column( if target_and_control and qubit != min(target_and_control): margins[qubit] = "|" - output = AsciiCircuitDiagram._create_output(symbols, margins, circuit_qubits, global_phase) - return output - - @classmethod - def _create_output( - cls, - symbols: dict[Qubit, str], - margins: dict[Qubit, str], - qubits: QubitSet, - global_phase: float | None, - ) -> str: - symbols_width = max([len(symbol) for symbol in symbols.values()]) + cls._buffer_size - output = "" - - if global_phase is not None: - global_phase_str = ( - f"{global_phase:.2f}" if isinstance(global_phase, float) else str(global_phase) - ) - symbols_width = max([symbols_width, len(global_phase_str)]) - output += "{0:{fill}{align}{width}}{vdelim}\n".format( - global_phase_str, fill=" ", align="^", width=symbols_width, vdelim=cls._vdelim - ) - - for qubit in qubits: - output += cls._draw_symbol(symbols[qubit], symbols_width, margins[qubit]) + output = cls._create_output(symbols, margins, circuit_qubits, global_phase) return output @classmethod @@ -431,13 +158,3 @@ def _draw_symbol(cls, symbol: str, symbols_width: int, connection: str) -> str: symbol, fill=cls._qubit_line_char, align="<", width=symbols_width + 1 ) return output - - @staticmethod - def _build_map_control_qubits(item: Instruction, control_qubits: QubitSet) -> dict(Qubit, int): - control_state = getattr(item, "control_state", None) - if control_state is not None: - map_control_qubit_states = dict(zip(control_qubits, control_state)) - else: - map_control_qubit_states = {qubit: 1 for qubit in control_qubits} - - return map_control_qubit_states diff --git a/src/braket/circuits/box_drawing_circuit_diagram.py b/src/braket/circuits/box_drawing_circuit_diagram.py index 6e5923f6f..044cce9d8 100644 --- a/src/braket/circuits/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/box_drawing_circuit_diagram.py @@ -17,107 +17,42 @@ from typing import Literal import braket.circuits.circuit as cir -from braket.circuits.ascii_circuit_diagram import AsciiCircuitDiagram 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_circuit_diagram import TextCircuitDiagram from braket.registers.qubit import Qubit from braket.registers.qubit_set import QubitSet -class BoxDrawingCircuitDiagram(AsciiCircuitDiagram): +class BoxDrawingCircuitDiagram(TextCircuitDiagram): """Builds ASCII string circuit diagrams using box-drawing characters.""" - _vdelim = "│" - _qubit_line_char = "─" - _add_empty_line = False - _buffer_size = 4 + _vdelim = "│" # Character that connects qubits of multi-qubit gates + _qubit_line_char = "─" # Character used for the qubit line + _box_padding = 4 # number of blank space characters around the gate name + _qubit_line_spacing = {"before": 1, "after": 1} # number of empty lines around the qubit line @staticmethod def build_diagram(circuit: cir.Circuit) -> str: """ - Build an ASCII string circuit diagram using box-drawing characters. + Build a text circuit diagram. Args: circuit (Circuit): Circuit for which to build a diagram. Returns: - str: ASCII string circuit diagram. + str: string circuit diagram. """ - return super(BoxDrawingCircuitDiagram, BoxDrawingCircuitDiagram).build_diagram(circuit) - @staticmethod - def _create_qubit_layout(qubit: Qubit, y_axis_width: int) -> None: - """ - Create the layout of the qubit. - - Args: - qubit (Qubit): Qubit to create the layout for. - y_axis_width (int): Width of the y axis. - """ - y_axis_str = "{0:{width}}\n".format(" ", width=y_axis_width + 5) - y_axis_str += "q{0:{width}} : {qubit_line_char}\n".format( - str(int(qubit)), - width=y_axis_width, - qubit_line_char=BoxDrawingCircuitDiagram._qubit_line_char, - ) - y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) - return y_axis_str - - @staticmethod - def _build_parameters( - circuit_qubits: QubitSet, item: ResultType | Instruction, connections: dict[Qubit, str] - ) -> tuple: - map_control_qubit_states = {} - - if (isinstance(item, ResultType) and not item.target) or ( - isinstance(item, Instruction) and isinstance(item.operator, CompilerDirective) - ): - target_qubits = circuit_qubits - control_qubits = QubitSet() - qubits = circuit_qubits - ascii_symbols = [item.ascii_symbols[0]] * len(qubits) - BoxDrawingCircuitDiagram._update_connections(qubits, connections) - elif ( - isinstance(item, Instruction) - and isinstance(item.operator, Gate) - and item.operator.name == "GPhase" - ): - target_qubits = circuit_qubits - control_qubits = QubitSet() - qubits = circuit_qubits - ascii_symbols = BoxDrawingCircuitDiagram._qubit_line_char * 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()) - map_control_qubit_states = BoxDrawingCircuitDiagram._build_map_control_qubits( - item, control_qubits - ) - - 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 - BoxDrawingCircuitDiagram._update_connections(qubits, connections) + return BoxDrawingCircuitDiagram._build_diagram_internal(circuit) - return ( - target_qubits, - control_qubits, - qubits, - connections, - ascii_symbols, - map_control_qubit_states, - ) - - @staticmethod - def _update_connections(qubits: QubitSet, connections: dict[Qubit, str]) -> None: - if len(qubits) > 1: - connections |= {qubit: "both" for qubit in qubits[1:-1]} - connections[qubits[-1]] = "above" - connections[qubits[0]] = "below" + @classmethod + def _duplicate_time_at_bottom(cls, lines: str) -> None: + # Do not add a line after the circuit + # It is safe to do because the last line is empty by construction (see ) + lines[-1] = lines[0] @classmethod def _ascii_diagram_column( @@ -183,6 +118,61 @@ def _ascii_diagram_column( output = cls._create_output(symbols, connections, circuit_qubits, global_phase) return output + @staticmethod + def _build_parameters( + circuit_qubits: QubitSet, item: ResultType | Instruction, connections: dict[Qubit, str] + ) -> tuple: + map_control_qubit_states = {} + + if (isinstance(item, ResultType) and not item.target) or ( + isinstance(item, Instruction) and isinstance(item.operator, CompilerDirective) + ): + target_qubits = circuit_qubits + control_qubits = QubitSet() + qubits = circuit_qubits + ascii_symbols = [item.ascii_symbols[0]] * len(qubits) + BoxDrawingCircuitDiagram._update_connections(qubits, connections) + elif ( + isinstance(item, Instruction) + and isinstance(item.operator, Gate) + and item.operator.name == "GPhase" + ): + target_qubits = circuit_qubits + control_qubits = QubitSet() + qubits = circuit_qubits + ascii_symbols = BoxDrawingCircuitDiagram._qubit_line_char * 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 = { + qubit: state for qubit, state in 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 + BoxDrawingCircuitDiagram._update_connections(qubits, connections) + + return ( + target_qubits, + control_qubits, + qubits, + connections, + ascii_symbols, + map_control_qubit_states, + ) + + @staticmethod + def _update_connections(qubits: QubitSet, connections: dict[Qubit, str]) -> None: + if len(qubits) > 1: + connections |= {qubit: "both" for qubit in qubits[1:-1]} + connections[qubits[-1]] = "above" + connections[qubits[0]] = "below" + @classmethod def _draw_symbol( cls, diff --git a/src/braket/circuits/circuit_diagram.py b/src/braket/circuits/circuit_diagram.py index cc39aa7ee..6d3a901b2 100644 --- a/src/braket/circuits/circuit_diagram.py +++ b/src/braket/circuits/circuit_diagram.py @@ -23,7 +23,7 @@ class CircuitDiagram(ABC): @staticmethod @abstractmethod - def build_diagram(circuit: cir.Circuit) -> str: + def build_diagram(cls, circuit: cir.Circuit) -> str: """Build a diagram for the specified `circuit`. Args: diff --git a/src/braket/circuits/text_circuit_diagram.py b/src/braket/circuits/text_circuit_diagram.py new file mode 100644 index 000000000..3429bf0da --- /dev/null +++ b/src/braket/circuits/text_circuit_diagram.py @@ -0,0 +1,356 @@ +# 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 abc import abstractmethod +from functools import reduce +from typing import Union + +import braket.circuits.circuit as cir +from braket.circuits.circuit_diagram import CircuitDiagram +from braket.circuits.compiler_directive import CompilerDirective +from braket.circuits.gate import Gate +from braket.circuits.instruction import Instruction +from braket.circuits.moments import MomentType +from braket.circuits.noise import Noise +from braket.circuits.result_type import ResultType +from braket.registers.qubit import Qubit +from braket.registers.qubit_set import QubitSet + + +class TextCircuitDiagram(CircuitDiagram): + """Builds ASCII string circuit diagrams.""" + + _vdelim = ... # Character that connects qubits of multi-qubit gates, e.g. "|" + _qubit_line_char = ... # Character used for the qubit line, e.g. "-" + _box_padding = ... # number of blank space characters around the gate name, e.g 0 + _qubit_line_spacing = ... # number of empty lines around the qubit line + + @classmethod + def _build_diagram_internal(cls, circuit: cir.Circuit) -> str: + """ + Build a text circuit diagram. + + The procedure follows as: + 1. Prepare the first column composed of the qubit identifiers + 2. Construct the circuit as a list of columns by looping through the + time slices. A column is a string with rows separated via '\n' + a. compute the instantaneous global phase + b. create the column corresponding to the current moment + 3. Add result types at the end of the circuit + 4. Join the columns to get a list of qubit lines + 5. Add a list of optional parameters: + a. the total global phase + b. results types that do not have any target such as statevector + c. the list of unassigned parameters + + Args: + circuit (Circuit): Circuit for which to build a diagram. + + Returns: + str: ASCII string circuit diagram. + """ + + if not circuit.instructions: + return "" + + if all(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): + return f"Global phase: {circuit.global_phase}" + + circuit_qubits = circuit.qubits + circuit_qubits.sort() + + y_axis_str, global_phase = cls._prepare_qubit_identifier_column(circuit, circuit_qubits) + + time_slices = circuit.moments.time_slices() + column_strs = [] + + # Moment columns + for time, instructions in time_slices.items(): + global_phase = cls._compute_moment_global_phase(global_phase, instructions) + moment_str = cls._ascii_diagram_column_set( + str(time), circuit_qubits, instructions, global_phase + ) + column_strs.append(moment_str) + + # Result type columns + additional_result_types, target_result_types = cls._categorize_result_types( + circuit.result_types + ) + if target_result_types: + column_strs.append( + cls._ascii_diagram_column_set( + "Result Types", circuit_qubits, target_result_types, global_phase + ) + ) + + # Unite strings + lines = cls._unite_strings(y_axis_str, column_strs) + cls._duplicate_time_at_bottom(lines) + + if global_phase: + lines.append(f"\nGlobal phase: {global_phase}") + + # Additional result types line on bottom + if additional_result_types: + lines.append(f"\nAdditional result types: {', '.join(additional_result_types)}") + + # A list of parameters in the circuit to the currently assigned values. + if circuit.parameters: + lines.append( + "\nUnassigned parameters: " + f"{sorted(circuit.parameters, key=lambda param: param.name)}." + ) + + return "\n".join(lines) + + @classmethod + def _prepare_qubit_identifier_column( + cls, circuit: cir.Circuit, circuit_qubits: QubitSet + ) -> tuple[str, float | None]: + # Y Axis Column + y_axis_width = len(str(int(max(circuit_qubits)))) + y_axis_str = "{0:{width}} : {vdelim}\n".format( + "T", width=y_axis_width + 1, vdelim=cls._vdelim + ) + + global_phase = None + if any(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): + y_axis_str += "{0:{width}} : {vdelim}\n".format( + "GP", width=y_axis_width, vdelim=cls._vdelim + ) + global_phase = 0 + + for qubit in circuit_qubits: + for _ in range(cls._qubit_line_spacing["before"]): + y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) + + y_axis_str += "q{0:{width}} : {qubit_line_char}\n".format( + str(int(qubit)), + width=y_axis_width, + qubit_line_char=cls._qubit_line_char, + ) + + for _ in range(cls._qubit_line_spacing["after"]): + y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) + return y_axis_str, global_phase + + @staticmethod + def _unite_strings(first_column: str, column_strs: list[str]) -> str: + lines = first_column.split("\n") + for col_str in column_strs: + for i, line_in_col in enumerate(col_str.split("\n")): + lines[i] += line_in_col + return lines + + @staticmethod + def _compute_moment_global_phase( + global_phase: float | None, items: list[Instruction] + ) -> float | None: + """ + Compute the integrated phase at a certain moment. + + Args: + global_phase (float | None): The integrated phase up to the computed moment + items (list[Instruction]): list of instructions + + Returns: + float | None: The updated integrated phase. + """ + moment_phase = 0 + for item in items: + if ( + isinstance(item, Instruction) + and isinstance(item.operator, Gate) + and item.operator.name == "GPhase" + ): + moment_phase += item.operator.angle + return global_phase + moment_phase if global_phase is not None else None + + @staticmethod + def _group_items( + circuit_qubits: QubitSet, + items: list[Union[Instruction, ResultType]], + ) -> list[tuple[QubitSet, list[Instruction]]]: + """ + Group instructions in a moment + + Args: + circuit_qubits (QubitSet): set of qubits in circuit + items (list[Union[Instruction, ResultType]]): list of instructions or result types + + Returns: + list[tuple[QubitSet, list[Instruction]]]: list of grouped instructions or result types. + """ + groupings = [] + for item in items: + # Can only print Gate and Noise operators for instructions at the moment + if isinstance(item, Instruction) and not isinstance( + item.operator, (Gate, Noise, CompilerDirective) + ): + continue + + # As a zero-qubit gate, GPhase can be grouped with anything. We set qubit_range + # to an empty list and we just add it to the first group below. + if ( + isinstance(item, Instruction) + and isinstance(item.operator, Gate) + and item.operator.name == "GPhase" + ): + qubit_range = QubitSet() + elif (isinstance(item, ResultType) and not item.target) or ( + isinstance(item, Instruction) and isinstance(item.operator, CompilerDirective) + ): + qubit_range = circuit_qubits + else: + if isinstance(item.target, list): + target = reduce(QubitSet.union, map(QubitSet, item.target), QubitSet()) + else: + target = item.target + control = getattr(item, "control", QubitSet()) + target_and_control = target.union(control) + qubit_range = QubitSet(range(min(target_and_control), max(target_and_control) + 1)) + + found_grouping = False + for group in groupings: + qubits_added = group[0] + instr_group = group[1] + # Take into account overlapping multi-qubit gates + if not qubits_added.intersection(set(qubit_range)): + instr_group.append(item) + qubits_added.update(qubit_range) + found_grouping = True + break + + if not found_grouping: + groupings.append((qubit_range, [item])) + + return groupings + + @staticmethod + def _categorize_result_types( + result_types: list[ResultType], + ) -> tuple[list[str], list[ResultType]]: + """ + Categorize result types into result types with target and those without. + + Args: + result_types (list[ResultType]): list of result types + + Returns: + tuple[list[str], list[ResultType]]: first element is a list of result types + without `target` attribute; second element is a list of result types with + `target` attribute + """ + additional_result_types = [] + target_result_types = [] + for result_type in result_types: + if hasattr(result_type, "target"): + target_result_types.append(result_type) + else: + additional_result_types.extend(result_type.ascii_symbols) + return additional_result_types, target_result_types + + @classmethod + def _ascii_diagram_column_set( + cls, + col_title: str, + circuit_qubits: QubitSet, + items: list[Union[Instruction, ResultType]], + global_phase: float | None, + ) -> str: + """ + Return a set of columns in the ASCII string diagram of the circuit for a list of items. + + Args: + col_title (str): title of column set + 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 set + + Returns: + str: An ASCII string diagram for the column set. + """ + + # Group items to separate out overlapping multi-qubit items + groupings = cls._group_items(circuit_qubits, items) + + column_strs = [ + cls._ascii_diagram_column(circuit_qubits, grouping[1], global_phase) + for grouping in groupings + ] + + # Unite column strings + lines = cls._unite_strings(column_strs[0], column_strs[1:]) + + # Adjust for column title width + col_title_width = len(col_title) + symbols_width = len(lines[0]) - 1 + if symbols_width < col_title_width: + diff = col_title_width - symbols_width + for i in range(len(lines) - 1): + if lines[i].endswith(cls._qubit_line_char): + lines[i] += cls._qubit_line_char * diff + else: + lines[i] += " " + + first_line = "{:^{width}}{vdelim}\n".format( + col_title, width=len(lines[0]) - 1, vdelim=cls._vdelim + ) + + return first_line + "\n".join(lines) + + @classmethod + def _create_output( + cls, + symbols: dict[Qubit, str], + margins: dict[Qubit, str], + qubits: QubitSet, + global_phase: float | None, + ) -> str: + symbols_width = max([len(symbol) for symbol in symbols.values()]) + cls._box_padding + output = "" + + if global_phase is not None: + global_phase_str = ( + f"{global_phase:.2f}" if isinstance(global_phase, float) else str(global_phase) + ) + symbols_width = max([symbols_width, len(global_phase_str)]) + output += "{0:{fill}{align}{width}}{vdelim}\n".format( + global_phase_str, fill=" ", align="^", width=symbols_width, vdelim=cls._vdelim + ) + + for qubit in qubits: + output += cls._draw_symbol(symbols[qubit], symbols_width, margins[qubit]) + return output + + @classmethod + @abstractmethod + def _ascii_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. + """ From 5da631bdb2ce3baddd16cc5bdca3ff47711f800f Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 23 Feb 2024 10:57:00 -0500 Subject: [PATCH 47/62] fix docstring --- src/braket/circuits/box_drawing_circuit_diagram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/braket/circuits/box_drawing_circuit_diagram.py b/src/braket/circuits/box_drawing_circuit_diagram.py index 044cce9d8..8b6d1bdef 100644 --- a/src/braket/circuits/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/box_drawing_circuit_diagram.py @@ -51,7 +51,7 @@ def build_diagram(circuit: cir.Circuit) -> str: @classmethod def _duplicate_time_at_bottom(cls, lines: str) -> None: # Do not add a line after the circuit - # It is safe to do because the last line is empty by construction (see ) + # It is safe to do because the last line is empty: _qubit_line_spacing["after"] = 1 lines[-1] = lines[0] @classmethod From 70b9791b99ae253369719c78a51efa7470cbdc2c Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 23 Feb 2024 11:18:36 -0500 Subject: [PATCH 48/62] make class-specific method explicit --- src/braket/circuits/__init__.py | 4 +-- src/braket/circuits/circuit.py | 2 +- .../ascii_circuit_diagram.py | 2 +- .../box_drawing_circuit_diagram.py | 2 +- .../text_circuit_diagram.py | 27 +++++++++++++++++++ 5 files changed, 32 insertions(+), 5 deletions(-) rename src/braket/circuits/{ => text_diagram_builders}/ascii_circuit_diagram.py (98%) rename src/braket/circuits/{ => text_diagram_builders}/box_drawing_circuit_diagram.py (99%) rename src/braket/circuits/{ => text_diagram_builders}/text_circuit_diagram.py (93%) diff --git a/src/braket/circuits/__init__.py b/src/braket/circuits/__init__.py index d63dbad1e..00b77aa5e 100644 --- a/src/braket/circuits/__init__.py +++ b/src/braket/circuits/__init__.py @@ -20,8 +20,8 @@ result_types, ) from braket.circuits.angled_gate import AngledGate, DoubleAngledGate # noqa: F401 -from braket.circuits.ascii_circuit_diagram import AsciiCircuitDiagram # noqa: F401 -from braket.circuits.box_drawing_circuit_diagram import BoxDrawingCircuitDiagram # noqa: F401 +from braket.circuits.text_diagram_builders.ascii_circuit_diagram import AsciiCircuitDiagram # noqa: F401 +from braket.circuits.text_diagram_builders.box_drawing_circuit_diagram import BoxDrawingCircuitDiagram # noqa: F401 from braket.circuits.circuit import Circuit # noqa: F401 from braket.circuits.circuit_diagram import CircuitDiagram # noqa: F401 from braket.circuits.compiler_directive import CompilerDirective # noqa: F401 diff --git a/src/braket/circuits/circuit.py b/src/braket/circuits/circuit.py index c8480df16..59908a8d0 100644 --- a/src/braket/circuits/circuit.py +++ b/src/braket/circuits/circuit.py @@ -21,7 +21,7 @@ import oqpy from braket.circuits import compiler_directives -from braket.circuits.ascii_circuit_diagram import AsciiCircuitDiagram +from braket.circuits.text_diagram_builders.ascii_circuit_diagram import AsciiCircuitDiagram from braket.circuits.free_parameter import FreeParameter from braket.circuits.free_parameter_expression import FreeParameterExpression from braket.circuits.gate import Gate diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py similarity index 98% rename from src/braket/circuits/ascii_circuit_diagram.py rename to src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py index 68bc7d8ba..9c9dfb66c 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py @@ -21,7 +21,7 @@ from braket.circuits.gate import Gate from braket.circuits.instruction import Instruction from braket.circuits.result_type import ResultType -from braket.circuits.text_circuit_diagram import TextCircuitDiagram +from braket.circuits.text_diagram_builders.text_circuit_diagram import TextCircuitDiagram from braket.registers.qubit_set import QubitSet diff --git a/src/braket/circuits/box_drawing_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py similarity index 99% rename from src/braket/circuits/box_drawing_circuit_diagram.py rename to src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py index 8b6d1bdef..d7c981d11 100644 --- a/src/braket/circuits/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py @@ -21,7 +21,7 @@ from braket.circuits.gate import Gate from braket.circuits.instruction import Instruction from braket.circuits.result_type import ResultType -from braket.circuits.text_circuit_diagram import TextCircuitDiagram +from braket.circuits.text_diagram_builders.text_circuit_diagram import TextCircuitDiagram from braket.registers.qubit import Qubit from braket.registers.qubit_set import QubitSet diff --git a/src/braket/circuits/text_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py similarity index 93% rename from src/braket/circuits/text_circuit_diagram.py rename to src/braket/circuits/text_diagram_builders/text_circuit_diagram.py index 3429bf0da..621d130d9 100644 --- a/src/braket/circuits/text_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py @@ -37,6 +37,33 @@ class TextCircuitDiagram(CircuitDiagram): _box_padding = ... # number of blank space characters around the gate name, e.g 0 _qubit_line_spacing = ... # number of empty lines around the qubit line + @classmethod + def _duplicate_time_at_bottom(cls, lines: str) -> None: + raise NotImplementedError + + @classmethod + def _ascii_diagram_column( + cls, + circuit_qubits: QubitSet, + items: list[Union[Instruction, ResultType]], + global_phase: float | None = None, + ) -> str: + """Return a column in the 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. + """ + raise NotImplementedError + + @classmethod + def _draw_symbol(cls, symbol: str, symbols_width: int, connection: str) -> str: + raise NotImplementedError + @classmethod def _build_diagram_internal(cls, circuit: cir.Circuit) -> str: """ From 9f946540ad2229b7f09782358bc2614fbf08b7e7 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 23 Feb 2024 11:19:00 -0500 Subject: [PATCH 49/62] fix linters --- src/braket/circuits/__init__.py | 8 ++++++-- src/braket/circuits/circuit.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/braket/circuits/__init__.py b/src/braket/circuits/__init__.py index 00b77aa5e..bff514618 100644 --- a/src/braket/circuits/__init__.py +++ b/src/braket/circuits/__init__.py @@ -20,8 +20,6 @@ result_types, ) from braket.circuits.angled_gate import AngledGate, DoubleAngledGate # noqa: F401 -from braket.circuits.text_diagram_builders.ascii_circuit_diagram import AsciiCircuitDiagram # noqa: F401 -from braket.circuits.text_diagram_builders.box_drawing_circuit_diagram import BoxDrawingCircuitDiagram # noqa: F401 from braket.circuits.circuit import Circuit # noqa: F401 from braket.circuits.circuit_diagram import CircuitDiagram # noqa: F401 from braket.circuits.compiler_directive import CompilerDirective # noqa: F401 @@ -39,3 +37,9 @@ from braket.circuits.qubit import Qubit, QubitInput # noqa: F401 from braket.circuits.qubit_set import QubitSet, QubitSetInput # noqa: F401 from braket.circuits.result_type import ObservableResultType, ResultType # noqa: F401 +from braket.circuits.text_diagram_builders.ascii_circuit_diagram import ( # noqa: F401 + AsciiCircuitDiagram, +) +from braket.circuits.text_diagram_builders.box_drawing_circuit_diagram import ( # noqa: F401 + BoxDrawingCircuitDiagram, +) diff --git a/src/braket/circuits/circuit.py b/src/braket/circuits/circuit.py index 59908a8d0..c085ebb16 100644 --- a/src/braket/circuits/circuit.py +++ b/src/braket/circuits/circuit.py @@ -21,7 +21,6 @@ import oqpy from braket.circuits import compiler_directives -from braket.circuits.text_diagram_builders.ascii_circuit_diagram import AsciiCircuitDiagram from braket.circuits.free_parameter import FreeParameter from braket.circuits.free_parameter_expression import FreeParameterExpression from braket.circuits.gate import Gate @@ -50,6 +49,7 @@ QubitReferenceType, SerializationProperties, ) +from braket.circuits.text_diagram_builders.ascii_circuit_diagram import AsciiCircuitDiagram from braket.circuits.unitary_calculation import calculate_unitary_big_endian from braket.default_simulator.openqasm.interpreter import Interpreter from braket.ir.jaqcd import Program as JaqcdProgram From 60bbc56f76304027e2f36c52491afefb9a39fdd7 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 23 Feb 2024 11:30:50 -0500 Subject: [PATCH 50/62] fix typos --- .../text_circuit_diagram.py | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py index 621d130d9..941e05bc7 100644 --- a/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py @@ -13,7 +13,6 @@ from __future__ import annotations -from abc import abstractmethod from functools import reduce from typing import Union @@ -361,23 +360,3 @@ def _create_output( for qubit in qubits: output += cls._draw_symbol(symbols[qubit], symbols_width, margins[qubit]) return output - - @classmethod - @abstractmethod - def _ascii_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. - """ From 3ded3115ad7d305c562d6365f38fb725fc3e4aff Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 23 Feb 2024 11:32:59 -0500 Subject: [PATCH 51/62] remove forgotten argument --- src/braket/circuits/circuit_diagram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/braket/circuits/circuit_diagram.py b/src/braket/circuits/circuit_diagram.py index 6d3a901b2..cc39aa7ee 100644 --- a/src/braket/circuits/circuit_diagram.py +++ b/src/braket/circuits/circuit_diagram.py @@ -23,7 +23,7 @@ class CircuitDiagram(ABC): @staticmethod @abstractmethod - def build_diagram(cls, circuit: cir.Circuit) -> str: + def build_diagram(circuit: cir.Circuit) -> str: """Build a diagram for the specified `circuit`. Args: From fad61cb51a00f2eda192a4062bb58adc1381a0ff Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 23 Feb 2024 13:26:48 -0500 Subject: [PATCH 52/62] use a utilities class --- .../ascii_circuit_diagram.py | 24 +++- .../box_drawing_circuit_diagram.py | 34 ++++- .../text_circuit_diagram.py | 130 +++++++++--------- 3 files changed, 110 insertions(+), 78 deletions(-) diff --git a/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py index 9c9dfb66c..219eada48 100644 --- a/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py @@ -17,15 +17,16 @@ from typing import Union import braket.circuits.circuit as cir +from braket.circuits.circuit_diagram import CircuitDiagram 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.circuits.text_diagram_builders.text_circuit_diagram import TextCircuitDiagramUtilities from braket.registers.qubit_set import QubitSet -class AsciiCircuitDiagram(TextCircuitDiagram): +class AsciiCircuitDiagram(CircuitDiagram): """Builds ASCII string circuit diagrams.""" _vdelim = "|" # Character that connects qubits of multi-qubit gates @@ -45,7 +46,7 @@ def build_diagram(circuit: cir.Circuit) -> str: str: string circuit diagram. """ - return AsciiCircuitDiagram._build_diagram_internal(circuit) + return TextCircuitDiagramUtilities._build_diagram_internal(AsciiCircuitDiagram, circuit) @classmethod def _duplicate_time_at_bottom(cls, lines: str) -> None: @@ -148,11 +149,26 @@ def _ascii_diagram_column( if target_and_control and qubit != min(target_and_control): margins[qubit] = "|" - output = cls._create_output(symbols, margins, circuit_qubits, global_phase) + output = TextCircuitDiagramUtilities._create_output( + cls, symbols, margins, circuit_qubits, global_phase + ) return output @classmethod def _draw_symbol(cls, symbol: str, symbols_width: int, connection: str) -> str: + """ + Create a string representing the symbol. + + Args: + symbol (str): the gate name + symbols_width (int): size of the expected output. The ouput will be filled with + cls._qubit_line_char if needed. + connection (str): character indicating if the gate also involve a qubit with a lower + index. + + Returns: + str: a string representing the symbol. + """ output = "{0:{width}}\n".format(connection, width=symbols_width + 1) output += "{0:{fill}{align}{width}}\n".format( symbol, fill=cls._qubit_line_char, align="<", width=symbols_width + 1 diff --git a/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py index d7c981d11..109dea70b 100644 --- a/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py @@ -17,16 +17,17 @@ from typing import Literal import braket.circuits.circuit as cir +from braket.circuits.circuit_diagram import CircuitDiagram 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.circuits.text_diagram_builders.text_circuit_diagram import TextCircuitDiagramUtilities from braket.registers.qubit import Qubit from braket.registers.qubit_set import QubitSet -class BoxDrawingCircuitDiagram(TextCircuitDiagram): +class BoxDrawingCircuitDiagram(CircuitDiagram): """Builds ASCII string circuit diagrams using box-drawing characters.""" _vdelim = "│" # Character that connects qubits of multi-qubit gates @@ -46,7 +47,9 @@ def build_diagram(circuit: cir.Circuit) -> str: str: string circuit diagram. """ - return BoxDrawingCircuitDiagram._build_diagram_internal(circuit) + return TextCircuitDiagramUtilities._build_diagram_internal( + BoxDrawingCircuitDiagram, circuit + ) @classmethod def _duplicate_time_at_bottom(cls, lines: str) -> None: @@ -115,7 +118,9 @@ def _ascii_diagram_column( else: symbols[qubit] = "┼" - output = cls._create_output(symbols, connections, circuit_qubits, global_phase) + output = TextCircuitDiagramUtilities._create_output( + BoxDrawingCircuitDiagram, symbols, connections, circuit_qubits, global_phase + ) return output @staticmethod @@ -173,13 +178,28 @@ def _update_connections(qubits: QubitSet, connections: dict[Qubit, str]) -> None connections[qubits[-1]] = "above" connections[qubits[0]] = "below" + # 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"], + connection: Literal["above", "below", "both", "none"], ) -> str: + """ + Create a string representing the symbol inside a box. + + Args: + symbol (str): the gate name + symbols_width (int): size of the expected output. The ouput will be filled with + cls._qubit_line_char if needed. + connection (Literal["above", "below", "both", "none"]): specifies if a connection + will be drawn above and/or below the box. + + Returns: + str: a string representing the symbol. + """ top = "" bottom = "" if symbol in ["C", "N", "SWAP"]: @@ -211,7 +231,7 @@ def _draw_symbol( @staticmethod def _build_box( - symbol: str, connection: Literal["above, below, both, none"] + symbol: str, connection: Literal["above", "below", "both", "none"] ) -> tuple[str, str, str]: top_edge_symbol = "┴" if connection in ["above", "both"] else "─" top = f"┌─{_fill_symbol(top_edge_symbol, '─', len(symbol))}─┐" @@ -225,7 +245,7 @@ def _build_box( @staticmethod def _build_verbatim_box( symbol: Literal["StartVerbatim", "EndVerbatim"], - connection: Literal["above, below, both, none"], + connection: Literal["above", "below", "both", "none"], ) -> str: top = "" bottom = "" diff --git a/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py index 941e05bc7..cc9c6b03f 100644 --- a/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py @@ -17,7 +17,6 @@ from typing import Union import braket.circuits.circuit as cir -from braket.circuits.circuit_diagram import CircuitDiagram from braket.circuits.compiler_directive import CompilerDirective from braket.circuits.gate import Gate from braket.circuits.instruction import Instruction @@ -28,43 +27,11 @@ from braket.registers.qubit_set import QubitSet -class TextCircuitDiagram(CircuitDiagram): - """Builds ASCII string circuit diagrams.""" +class TextCircuitDiagramUtilities: + """Provides helper methods to build string circuit diagrams.""" - _vdelim = ... # Character that connects qubits of multi-qubit gates, e.g. "|" - _qubit_line_char = ... # Character used for the qubit line, e.g. "-" - _box_padding = ... # number of blank space characters around the gate name, e.g 0 - _qubit_line_spacing = ... # number of empty lines around the qubit line - - @classmethod - def _duplicate_time_at_bottom(cls, lines: str) -> None: - raise NotImplementedError - - @classmethod - def _ascii_diagram_column( - cls, - circuit_qubits: QubitSet, - items: list[Union[Instruction, ResultType]], - global_phase: float | None = None, - ) -> str: - """Return a column in the 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. - """ - raise NotImplementedError - - @classmethod - def _draw_symbol(cls, symbol: str, symbols_width: int, connection: str) -> str: - raise NotImplementedError - - @classmethod - def _build_diagram_internal(cls, circuit: cir.Circuit) -> str: + @staticmethod + def _build_diagram_internal(target_class: type, circuit: cir.Circuit) -> str: """ Build a text circuit diagram. @@ -82,6 +49,7 @@ def _build_diagram_internal(cls, circuit: cir.Circuit) -> str: c. the list of unassigned parameters Args: + target_class (type): type of the diagram builder circuit (Circuit): Circuit for which to build a diagram. Returns: @@ -97,33 +65,37 @@ def _build_diagram_internal(cls, circuit: cir.Circuit) -> str: circuit_qubits = circuit.qubits circuit_qubits.sort() - y_axis_str, global_phase = cls._prepare_qubit_identifier_column(circuit, circuit_qubits) + y_axis_str, global_phase = TextCircuitDiagramUtilities._prepare_qubit_identifier_column( + target_class, circuit, circuit_qubits + ) time_slices = circuit.moments.time_slices() column_strs = [] # Moment columns for time, instructions in time_slices.items(): - global_phase = cls._compute_moment_global_phase(global_phase, instructions) - moment_str = cls._ascii_diagram_column_set( - str(time), circuit_qubits, instructions, global_phase + global_phase = TextCircuitDiagramUtilities._compute_moment_global_phase( + global_phase, instructions + ) + moment_str = TextCircuitDiagramUtilities._ascii_diagram_column_set( + target_class, str(time), circuit_qubits, instructions, global_phase ) column_strs.append(moment_str) # Result type columns - additional_result_types, target_result_types = cls._categorize_result_types( - circuit.result_types + additional_result_types, target_result_types = ( + TextCircuitDiagramUtilities._categorize_result_types(circuit.result_types) ) if target_result_types: column_strs.append( - cls._ascii_diagram_column_set( - "Result Types", circuit_qubits, target_result_types, global_phase + TextCircuitDiagramUtilities._ascii_diagram_column_set( + target_class, "Result Types", circuit_qubits, target_result_types, global_phase ) ) # Unite strings - lines = cls._unite_strings(y_axis_str, column_strs) - cls._duplicate_time_at_bottom(lines) + lines = TextCircuitDiagramUtilities._unite_strings(y_axis_str, column_strs) + target_class._duplicate_time_at_bottom(lines) if global_phase: lines.append(f"\nGlobal phase: {global_phase}") @@ -141,34 +113,34 @@ def _build_diagram_internal(cls, circuit: cir.Circuit) -> str: return "\n".join(lines) - @classmethod + @staticmethod def _prepare_qubit_identifier_column( - cls, circuit: cir.Circuit, circuit_qubits: QubitSet + target_class: type, circuit: cir.Circuit, circuit_qubits: QubitSet ) -> tuple[str, float | None]: # Y Axis Column y_axis_width = len(str(int(max(circuit_qubits)))) y_axis_str = "{0:{width}} : {vdelim}\n".format( - "T", width=y_axis_width + 1, vdelim=cls._vdelim + "T", width=y_axis_width + 1, vdelim=target_class._vdelim ) global_phase = None if any(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): y_axis_str += "{0:{width}} : {vdelim}\n".format( - "GP", width=y_axis_width, vdelim=cls._vdelim + "GP", width=y_axis_width, vdelim=target_class._vdelim ) global_phase = 0 for qubit in circuit_qubits: - for _ in range(cls._qubit_line_spacing["before"]): + for _ in range(target_class._qubit_line_spacing["before"]): y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) y_axis_str += "q{0:{width}} : {qubit_line_char}\n".format( str(int(qubit)), width=y_axis_width, - qubit_line_char=cls._qubit_line_char, + qubit_line_char=target_class._qubit_line_char, ) - for _ in range(cls._qubit_line_spacing["after"]): + for _ in range(target_class._qubit_line_spacing["after"]): y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) return y_axis_str, global_phase @@ -288,9 +260,9 @@ def _categorize_result_types( additional_result_types.extend(result_type.ascii_symbols) return additional_result_types, target_result_types - @classmethod + @staticmethod def _ascii_diagram_column_set( - cls, + target_class: type, col_title: str, circuit_qubits: QubitSet, items: list[Union[Instruction, ResultType]], @@ -300,6 +272,7 @@ def _ascii_diagram_column_set( Return a set of columns in the ASCII string diagram of the circuit for a list of items. Args: + target_class (type): type of the diagram builder col_title (str): title of column set circuit_qubits (QubitSet): qubits in circuit items (list[Union[Instruction, ResultType]]): list of instructions or result types @@ -310,15 +283,15 @@ def _ascii_diagram_column_set( """ # Group items to separate out overlapping multi-qubit items - groupings = cls._group_items(circuit_qubits, items) + groupings = TextCircuitDiagramUtilities._group_items(circuit_qubits, items) column_strs = [ - cls._ascii_diagram_column(circuit_qubits, grouping[1], global_phase) + target_class._ascii_diagram_column(circuit_qubits, grouping[1], global_phase) for grouping in groupings ] # Unite column strings - lines = cls._unite_strings(column_strs[0], column_strs[1:]) + lines = TextCircuitDiagramUtilities._unite_strings(column_strs[0], column_strs[1:]) # Adjust for column title width col_title_width = len(col_title) @@ -326,26 +299,45 @@ def _ascii_diagram_column_set( if symbols_width < col_title_width: diff = col_title_width - symbols_width for i in range(len(lines) - 1): - if lines[i].endswith(cls._qubit_line_char): - lines[i] += cls._qubit_line_char * diff + if lines[i].endswith(target_class._qubit_line_char): + lines[i] += target_class._qubit_line_char * diff else: lines[i] += " " first_line = "{:^{width}}{vdelim}\n".format( - col_title, width=len(lines[0]) - 1, vdelim=cls._vdelim + col_title, width=len(lines[0]) - 1, vdelim=target_class._vdelim ) return first_line + "\n".join(lines) - @classmethod + @staticmethod def _create_output( - cls, + target_class: type, symbols: dict[Qubit, str], margins: dict[Qubit, str], qubits: QubitSet, global_phase: float | None, ) -> str: - symbols_width = max([len(symbol) for symbol in symbols.values()]) + cls._box_padding + """ + Creates the ouput for a single colum: + a. If there was one or more gphase gate, create a first line with the total global + phase shift ending with target_class.vdelim, e.g. 0.14| + b. for each qubit, append the text representation produces by target_class.draw_symbol + + Args: + target_class (type): type of the diagram builder + symbols (dict[Qubit, str]): dictionary of the gate name for each qubit + margins (dict[Qubit, str]): map of the qubit interconnections. Specific to the + `target_class.draw_symbol` method. + qubits (QubitSet): set of the circuit qubits + global_phase (float | None): total global phase shift added during the moment + + Returns: + str: a string representing a diagram column. + """ + symbols_width = ( + max([len(symbol) for symbol in symbols.values()]) + target_class._box_padding + ) output = "" if global_phase is not None: @@ -354,9 +346,13 @@ def _create_output( ) symbols_width = max([symbols_width, len(global_phase_str)]) output += "{0:{fill}{align}{width}}{vdelim}\n".format( - global_phase_str, fill=" ", align="^", width=symbols_width, vdelim=cls._vdelim + global_phase_str, + fill=" ", + align="^", + width=symbols_width, + vdelim=target_class._vdelim, ) for qubit in qubits: - output += cls._draw_symbol(symbols[qubit], symbols_width, margins[qubit]) + output += target_class._draw_symbol(symbols[qubit], symbols_width, margins[qubit]) return output From cba7e70a020c320aa47a4b950292fb99282e32ef Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 23 Feb 2024 13:33:00 -0500 Subject: [PATCH 53/62] do not use a TextCircuitDiagramUtilities --- .../ascii_circuit_diagram.py | 6 +- .../box_drawing_circuit_diagram.py | 8 +- .../text_circuit_diagram.py | 612 +++++++++--------- 3 files changed, 307 insertions(+), 319 deletions(-) diff --git a/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py index 219eada48..8eda51105 100644 --- a/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py @@ -22,7 +22,7 @@ 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 TextCircuitDiagramUtilities +from braket.circuits.text_diagram_builders import text_circuit_diagram from braket.registers.qubit_set import QubitSet @@ -46,7 +46,7 @@ def build_diagram(circuit: cir.Circuit) -> str: str: string circuit diagram. """ - return TextCircuitDiagramUtilities._build_diagram_internal(AsciiCircuitDiagram, circuit) + return text_circuit_diagram._build_diagram_internal(AsciiCircuitDiagram, circuit) @classmethod def _duplicate_time_at_bottom(cls, lines: str) -> None: @@ -149,7 +149,7 @@ def _ascii_diagram_column( if target_and_control and qubit != min(target_and_control): margins[qubit] = "|" - output = TextCircuitDiagramUtilities._create_output( + output = text_circuit_diagram._create_output( cls, symbols, margins, circuit_qubits, global_phase ) return output diff --git a/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py index 109dea70b..59566f893 100644 --- a/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py @@ -22,7 +22,7 @@ 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 TextCircuitDiagramUtilities +from braket.circuits.text_diagram_builders import text_circuit_diagram from braket.registers.qubit import Qubit from braket.registers.qubit_set import QubitSet @@ -47,9 +47,7 @@ def build_diagram(circuit: cir.Circuit) -> str: str: string circuit diagram. """ - return TextCircuitDiagramUtilities._build_diagram_internal( - BoxDrawingCircuitDiagram, circuit - ) + return text_circuit_diagram._build_diagram_internal(BoxDrawingCircuitDiagram, circuit) @classmethod def _duplicate_time_at_bottom(cls, lines: str) -> None: @@ -118,7 +116,7 @@ def _ascii_diagram_column( else: symbols[qubit] = "┼" - output = TextCircuitDiagramUtilities._create_output( + output = text_circuit_diagram._create_output( BoxDrawingCircuitDiagram, symbols, connections, circuit_qubits, global_phase ) return output diff --git a/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py index cc9c6b03f..645f70e58 100644 --- a/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py @@ -27,332 +27,322 @@ from braket.registers.qubit_set import QubitSet -class TextCircuitDiagramUtilities: - """Provides helper methods to build string circuit diagrams.""" - - @staticmethod - def _build_diagram_internal(target_class: type, circuit: cir.Circuit) -> str: - """ - Build a text circuit diagram. - - The procedure follows as: - 1. Prepare the first column composed of the qubit identifiers - 2. Construct the circuit as a list of columns by looping through the - time slices. A column is a string with rows separated via '\n' - a. compute the instantaneous global phase - b. create the column corresponding to the current moment - 3. Add result types at the end of the circuit - 4. Join the columns to get a list of qubit lines - 5. Add a list of optional parameters: - a. the total global phase - b. results types that do not have any target such as statevector - c. the list of unassigned parameters - - Args: - target_class (type): type of the diagram builder - circuit (Circuit): Circuit for which to build a diagram. - - Returns: - str: ASCII string circuit diagram. - """ - - if not circuit.instructions: - return "" - - if all(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): - return f"Global phase: {circuit.global_phase}" - - circuit_qubits = circuit.qubits - circuit_qubits.sort() - - y_axis_str, global_phase = TextCircuitDiagramUtilities._prepare_qubit_identifier_column( - target_class, circuit, circuit_qubits +def _build_diagram_internal(target_class: type, circuit: cir.Circuit) -> str: + """ + Build a text circuit diagram. + + The procedure follows as: + 1. Prepare the first column composed of the qubit identifiers + 2. Construct the circuit as a list of columns by looping through the + time slices. A column is a string with rows separated via '\n' + a. compute the instantaneous global phase + b. create the column corresponding to the current moment + 3. Add result types at the end of the circuit + 4. Join the columns to get a list of qubit lines + 5. Add a list of optional parameters: + a. the total global phase + b. results types that do not have any target such as statevector + c. the list of unassigned parameters + + Args: + target_class (type): type of the diagram builder + circuit (Circuit): Circuit for which to build a diagram. + + Returns: + str: ASCII string circuit diagram. + """ + + if not circuit.instructions: + return "" + + if all(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): + return f"Global phase: {circuit.global_phase}" + + circuit_qubits = circuit.qubits + circuit_qubits.sort() + + y_axis_str, global_phase = _prepare_qubit_identifier_column( + target_class, circuit, circuit_qubits + ) + + time_slices = circuit.moments.time_slices() + column_strs = [] + + # Moment columns + for time, instructions in time_slices.items(): + global_phase = _compute_moment_global_phase(global_phase, instructions) + moment_str = _ascii_diagram_column_set( + target_class, str(time), circuit_qubits, instructions, global_phase ) - - time_slices = circuit.moments.time_slices() - column_strs = [] - - # Moment columns - for time, instructions in time_slices.items(): - global_phase = TextCircuitDiagramUtilities._compute_moment_global_phase( - global_phase, instructions - ) - moment_str = TextCircuitDiagramUtilities._ascii_diagram_column_set( - target_class, str(time), circuit_qubits, instructions, global_phase + column_strs.append(moment_str) + + # Result type columns + additional_result_types, target_result_types = _categorize_result_types(circuit.result_types) + if target_result_types: + column_strs.append( + _ascii_diagram_column_set( + target_class, "Result Types", circuit_qubits, target_result_types, global_phase ) - column_strs.append(moment_str) - - # Result type columns - additional_result_types, target_result_types = ( - TextCircuitDiagramUtilities._categorize_result_types(circuit.result_types) ) - if target_result_types: - column_strs.append( - TextCircuitDiagramUtilities._ascii_diagram_column_set( - target_class, "Result Types", circuit_qubits, target_result_types, global_phase - ) - ) - # Unite strings - lines = TextCircuitDiagramUtilities._unite_strings(y_axis_str, column_strs) - target_class._duplicate_time_at_bottom(lines) + # Unite strings + lines = _unite_strings(y_axis_str, column_strs) + target_class._duplicate_time_at_bottom(lines) - if global_phase: - lines.append(f"\nGlobal phase: {global_phase}") + if global_phase: + lines.append(f"\nGlobal phase: {global_phase}") - # Additional result types line on bottom - if additional_result_types: - lines.append(f"\nAdditional result types: {', '.join(additional_result_types)}") + # Additional result types line on bottom + if additional_result_types: + lines.append(f"\nAdditional result types: {', '.join(additional_result_types)}") - # A list of parameters in the circuit to the currently assigned values. - if circuit.parameters: - lines.append( - "\nUnassigned parameters: " - f"{sorted(circuit.parameters, key=lambda param: param.name)}." - ) + # A list of parameters in the circuit to the currently assigned values. + if circuit.parameters: + lines.append( + "\nUnassigned parameters: " + f"{sorted(circuit.parameters, key=lambda param: param.name)}." + ) - return "\n".join(lines) + return "\n".join(lines) - @staticmethod - def _prepare_qubit_identifier_column( - target_class: type, circuit: cir.Circuit, circuit_qubits: QubitSet - ) -> tuple[str, float | None]: - # Y Axis Column - y_axis_width = len(str(int(max(circuit_qubits)))) - y_axis_str = "{0:{width}} : {vdelim}\n".format( - "T", width=y_axis_width + 1, vdelim=target_class._vdelim - ) - global_phase = None - if any(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): - y_axis_str += "{0:{width}} : {vdelim}\n".format( - "GP", width=y_axis_width, vdelim=target_class._vdelim - ) - global_phase = 0 +def _prepare_qubit_identifier_column( + target_class: type, circuit: cir.Circuit, circuit_qubits: QubitSet +) -> tuple[str, float | None]: + # Y Axis Column + y_axis_width = len(str(int(max(circuit_qubits)))) + y_axis_str = "{0:{width}} : {vdelim}\n".format( + "T", width=y_axis_width + 1, vdelim=target_class._vdelim + ) - for qubit in circuit_qubits: - for _ in range(target_class._qubit_line_spacing["before"]): - y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) + global_phase = None + if any(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): + y_axis_str += "{0:{width}} : {vdelim}\n".format( + "GP", width=y_axis_width, vdelim=target_class._vdelim + ) + global_phase = 0 - y_axis_str += "q{0:{width}} : {qubit_line_char}\n".format( - str(int(qubit)), - width=y_axis_width, - qubit_line_char=target_class._qubit_line_char, - ) + for qubit in circuit_qubits: + for _ in range(target_class._qubit_line_spacing["before"]): + y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) + + y_axis_str += "q{0:{width}} : {qubit_line_char}\n".format( + str(int(qubit)), + width=y_axis_width, + qubit_line_char=target_class._qubit_line_char, + ) - for _ in range(target_class._qubit_line_spacing["after"]): - y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) - return y_axis_str, global_phase - - @staticmethod - def _unite_strings(first_column: str, column_strs: list[str]) -> str: - lines = first_column.split("\n") - for col_str in column_strs: - for i, line_in_col in enumerate(col_str.split("\n")): - lines[i] += line_in_col - return lines - - @staticmethod - def _compute_moment_global_phase( - global_phase: float | None, items: list[Instruction] - ) -> float | None: - """ - Compute the integrated phase at a certain moment. - - Args: - global_phase (float | None): The integrated phase up to the computed moment - items (list[Instruction]): list of instructions - - Returns: - float | None: The updated integrated phase. - """ - moment_phase = 0 - for item in items: - if ( - isinstance(item, Instruction) - and isinstance(item.operator, Gate) - and item.operator.name == "GPhase" - ): - moment_phase += item.operator.angle - return global_phase + moment_phase if global_phase is not None else None - - @staticmethod - def _group_items( - circuit_qubits: QubitSet, - items: list[Union[Instruction, ResultType]], - ) -> list[tuple[QubitSet, list[Instruction]]]: - """ - Group instructions in a moment - - Args: - circuit_qubits (QubitSet): set of qubits in circuit - items (list[Union[Instruction, ResultType]]): list of instructions or result types - - Returns: - list[tuple[QubitSet, list[Instruction]]]: list of grouped instructions or result types. - """ - groupings = [] - for item in items: - # Can only print Gate and Noise operators for instructions at the moment - if isinstance(item, Instruction) and not isinstance( - item.operator, (Gate, Noise, CompilerDirective) - ): - continue - - # As a zero-qubit gate, GPhase can be grouped with anything. We set qubit_range - # to an empty list and we just add it to the first group below. - if ( - isinstance(item, Instruction) - and isinstance(item.operator, Gate) - and item.operator.name == "GPhase" - ): - qubit_range = QubitSet() - elif (isinstance(item, ResultType) and not item.target) or ( - isinstance(item, Instruction) and isinstance(item.operator, CompilerDirective) - ): - qubit_range = circuit_qubits + for _ in range(target_class._qubit_line_spacing["after"]): + y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) + return y_axis_str, global_phase + + +def _unite_strings(first_column: str, column_strs: list[str]) -> str: + lines = first_column.split("\n") + for col_str in column_strs: + for i, line_in_col in enumerate(col_str.split("\n")): + lines[i] += line_in_col + return lines + + +def _compute_moment_global_phase( + global_phase: float | None, items: list[Instruction] +) -> float | None: + """ + Compute the integrated phase at a certain moment. + + Args: + global_phase (float | None): The integrated phase up to the computed moment + items (list[Instruction]): list of instructions + + Returns: + float | None: The updated integrated phase. + """ + moment_phase = 0 + for item in items: + if ( + isinstance(item, Instruction) + and isinstance(item.operator, Gate) + and item.operator.name == "GPhase" + ): + moment_phase += item.operator.angle + return global_phase + moment_phase if global_phase is not None else None + + +def _group_items( + circuit_qubits: QubitSet, + items: list[Union[Instruction, ResultType]], +) -> list[tuple[QubitSet, list[Instruction]]]: + """ + Group instructions in a moment + + Args: + circuit_qubits (QubitSet): set of qubits in circuit + items (list[Union[Instruction, ResultType]]): list of instructions or result types + + Returns: + list[tuple[QubitSet, list[Instruction]]]: list of grouped instructions or result types. + """ + groupings = [] + for item in items: + # Can only print Gate and Noise operators for instructions at the moment + if isinstance(item, Instruction) and not isinstance( + item.operator, (Gate, Noise, CompilerDirective) + ): + continue + + # As a zero-qubit gate, GPhase can be grouped with anything. We set qubit_range + # to an empty list and we just add it to the first group below. + if ( + isinstance(item, Instruction) + and isinstance(item.operator, Gate) + and item.operator.name == "GPhase" + ): + qubit_range = QubitSet() + elif (isinstance(item, ResultType) and not item.target) or ( + isinstance(item, Instruction) and isinstance(item.operator, CompilerDirective) + ): + qubit_range = circuit_qubits + else: + if isinstance(item.target, list): + target = reduce(QubitSet.union, map(QubitSet, item.target), QubitSet()) else: - if isinstance(item.target, list): - target = reduce(QubitSet.union, map(QubitSet, item.target), QubitSet()) - else: - target = item.target - control = getattr(item, "control", QubitSet()) - target_and_control = target.union(control) - qubit_range = QubitSet(range(min(target_and_control), max(target_and_control) + 1)) - - found_grouping = False - for group in groupings: - qubits_added = group[0] - instr_group = group[1] - # Take into account overlapping multi-qubit gates - if not qubits_added.intersection(set(qubit_range)): - instr_group.append(item) - qubits_added.update(qubit_range) - found_grouping = True - break - - if not found_grouping: - groupings.append((qubit_range, [item])) - - return groupings - - @staticmethod - def _categorize_result_types( - result_types: list[ResultType], - ) -> tuple[list[str], list[ResultType]]: - """ - Categorize result types into result types with target and those without. - - Args: - result_types (list[ResultType]): list of result types - - Returns: - tuple[list[str], list[ResultType]]: first element is a list of result types - without `target` attribute; second element is a list of result types with - `target` attribute - """ - additional_result_types = [] - target_result_types = [] - for result_type in result_types: - if hasattr(result_type, "target"): - target_result_types.append(result_type) + target = item.target + control = getattr(item, "control", QubitSet()) + target_and_control = target.union(control) + qubit_range = QubitSet(range(min(target_and_control), max(target_and_control) + 1)) + + found_grouping = False + for group in groupings: + qubits_added = group[0] + instr_group = group[1] + # Take into account overlapping multi-qubit gates + if not qubits_added.intersection(set(qubit_range)): + instr_group.append(item) + qubits_added.update(qubit_range) + found_grouping = True + break + + if not found_grouping: + groupings.append((qubit_range, [item])) + + return groupings + + +def _categorize_result_types( + result_types: list[ResultType], +) -> tuple[list[str], list[ResultType]]: + """ + Categorize result types into result types with target and those without. + + Args: + result_types (list[ResultType]): list of result types + + Returns: + tuple[list[str], list[ResultType]]: first element is a list of result types + without `target` attribute; second element is a list of result types with + `target` attribute + """ + additional_result_types = [] + target_result_types = [] + for result_type in result_types: + if hasattr(result_type, "target"): + target_result_types.append(result_type) + else: + additional_result_types.extend(result_type.ascii_symbols) + return additional_result_types, target_result_types + + +def _ascii_diagram_column_set( + target_class: type, + col_title: str, + circuit_qubits: QubitSet, + items: list[Union[Instruction, ResultType]], + global_phase: float | None, +) -> str: + """ + Return a set of columns in the ASCII string diagram of the circuit for a list of items. + + Args: + target_class (type): type of the diagram builder + col_title (str): title of column set + 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 set + + Returns: + str: An ASCII string diagram for the column set. + """ + + # Group items to separate out overlapping multi-qubit items + groupings = _group_items(circuit_qubits, items) + + column_strs = [ + target_class._ascii_diagram_column(circuit_qubits, grouping[1], global_phase) + for grouping in groupings + ] + + # Unite column strings + lines = _unite_strings(column_strs[0], column_strs[1:]) + + # Adjust for column title width + col_title_width = len(col_title) + symbols_width = len(lines[0]) - 1 + if symbols_width < col_title_width: + diff = col_title_width - symbols_width + for i in range(len(lines) - 1): + if lines[i].endswith(target_class._qubit_line_char): + lines[i] += target_class._qubit_line_char * diff else: - additional_result_types.extend(result_type.ascii_symbols) - return additional_result_types, target_result_types - - @staticmethod - def _ascii_diagram_column_set( - target_class: type, - col_title: str, - circuit_qubits: QubitSet, - items: list[Union[Instruction, ResultType]], - global_phase: float | None, - ) -> str: - """ - Return a set of columns in the ASCII string diagram of the circuit for a list of items. - - Args: - target_class (type): type of the diagram builder - col_title (str): title of column set - 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 set - - Returns: - str: An ASCII string diagram for the column set. - """ - - # Group items to separate out overlapping multi-qubit items - groupings = TextCircuitDiagramUtilities._group_items(circuit_qubits, items) - - column_strs = [ - target_class._ascii_diagram_column(circuit_qubits, grouping[1], global_phase) - for grouping in groupings - ] - - # Unite column strings - lines = TextCircuitDiagramUtilities._unite_strings(column_strs[0], column_strs[1:]) - - # Adjust for column title width - col_title_width = len(col_title) - symbols_width = len(lines[0]) - 1 - if symbols_width < col_title_width: - diff = col_title_width - symbols_width - for i in range(len(lines) - 1): - if lines[i].endswith(target_class._qubit_line_char): - lines[i] += target_class._qubit_line_char * diff - else: - lines[i] += " " - - first_line = "{:^{width}}{vdelim}\n".format( - col_title, width=len(lines[0]) - 1, vdelim=target_class._vdelim + lines[i] += " " + + first_line = "{:^{width}}{vdelim}\n".format( + col_title, width=len(lines[0]) - 1, vdelim=target_class._vdelim + ) + + return first_line + "\n".join(lines) + + +def _create_output( + target_class: type, + symbols: dict[Qubit, str], + margins: dict[Qubit, str], + qubits: QubitSet, + global_phase: float | None, +) -> str: + """ + Creates the ouput for a single colum: + a. If there was one or more gphase gate, create a first line with the total global + phase shift ending with target_class.vdelim, e.g. 0.14| + b. for each qubit, append the text representation produces by target_class.draw_symbol + + Args: + target_class (type): type of the diagram builder + symbols (dict[Qubit, str]): dictionary of the gate name for each qubit + margins (dict[Qubit, str]): map of the qubit interconnections. Specific to the + `target_class.draw_symbol` method. + qubits (QubitSet): set of the circuit qubits + global_phase (float | None): total global phase shift added during the moment + + Returns: + str: a string representing a diagram column. + """ + symbols_width = max([len(symbol) for symbol in symbols.values()]) + target_class._box_padding + output = "" + + if global_phase is not None: + global_phase_str = ( + f"{global_phase:.2f}" if isinstance(global_phase, float) else str(global_phase) ) - - return first_line + "\n".join(lines) - - @staticmethod - def _create_output( - target_class: type, - symbols: dict[Qubit, str], - margins: dict[Qubit, str], - qubits: QubitSet, - global_phase: float | None, - ) -> str: - """ - Creates the ouput for a single colum: - a. If there was one or more gphase gate, create a first line with the total global - phase shift ending with target_class.vdelim, e.g. 0.14| - b. for each qubit, append the text representation produces by target_class.draw_symbol - - Args: - target_class (type): type of the diagram builder - symbols (dict[Qubit, str]): dictionary of the gate name for each qubit - margins (dict[Qubit, str]): map of the qubit interconnections. Specific to the - `target_class.draw_symbol` method. - qubits (QubitSet): set of the circuit qubits - global_phase (float | None): total global phase shift added during the moment - - Returns: - str: a string representing a diagram column. - """ - symbols_width = ( - max([len(symbol) for symbol in symbols.values()]) + target_class._box_padding + symbols_width = max([symbols_width, len(global_phase_str)]) + output += "{0:{fill}{align}{width}}{vdelim}\n".format( + global_phase_str, + fill=" ", + align="^", + width=symbols_width, + vdelim=target_class._vdelim, ) - output = "" - - if global_phase is not None: - global_phase_str = ( - f"{global_phase:.2f}" if isinstance(global_phase, float) else str(global_phase) - ) - symbols_width = max([symbols_width, len(global_phase_str)]) - output += "{0:{fill}{align}{width}}{vdelim}\n".format( - global_phase_str, - fill=" ", - align="^", - width=symbols_width, - vdelim=target_class._vdelim, - ) - for qubit in qubits: - output += target_class._draw_symbol(symbols[qubit], symbols_width, margins[qubit]) - return output + for qubit in qubits: + output += target_class._draw_symbol(symbols[qubit], symbols_width, margins[qubit]) + return output From 8aaf579a79d88b05296c9b8c8e756995ac008fb5 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 23 Feb 2024 14:41:31 -0500 Subject: [PATCH 54/62] rename methods --- .../text_diagram_builders/ascii_circuit_diagram.py | 2 +- .../box_drawing_circuit_diagram.py | 8 ++++---- .../text_diagram_builders/text_circuit_diagram.py | 14 +++++++------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py index 8eda51105..96cedaf5a 100644 --- a/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py @@ -54,7 +54,7 @@ def _duplicate_time_at_bottom(cls, lines: str) -> None: lines.append(lines[0]) @classmethod - def _ascii_diagram_column( + def _create_diagram_column( cls, circuit_qubits: QubitSet, items: list[Union[Instruction, ResultType]], diff --git a/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py index 59566f893..97da2083f 100644 --- a/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py @@ -28,7 +28,7 @@ class BoxDrawingCircuitDiagram(CircuitDiagram): - """Builds ASCII string circuit diagrams using box-drawing characters.""" + """Builds string circuit diagrams using box-drawing characters.""" _vdelim = "│" # Character that connects qubits of multi-qubit gates _qubit_line_char = "─" # Character used for the qubit line @@ -56,14 +56,14 @@ def _duplicate_time_at_bottom(cls, lines: str) -> None: lines[-1] = lines[0] @classmethod - def _ascii_diagram_column( + def _create_diagram_column( cls, circuit_qubits: QubitSet, items: list[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. + Return a column in the string diagram of the circuit for a given list of items. Args: circuit_qubits (QubitSet): qubits in circuit @@ -71,7 +71,7 @@ def _ascii_diagram_column( 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. + str: a string diagram for the specified moment in time for a column. """ symbols = {qubit: cls._qubit_line_char for qubit in circuit_qubits} connections = {qubit: "none" for qubit in circuit_qubits} diff --git a/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py index 645f70e58..b5438942e 100644 --- a/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py @@ -49,7 +49,7 @@ def _build_diagram_internal(target_class: type, circuit: cir.Circuit) -> str: circuit (Circuit): Circuit for which to build a diagram. Returns: - str: ASCII string circuit diagram. + str: string circuit diagram. """ if not circuit.instructions: @@ -71,7 +71,7 @@ def _build_diagram_internal(target_class: type, circuit: cir.Circuit) -> str: # Moment columns for time, instructions in time_slices.items(): global_phase = _compute_moment_global_phase(global_phase, instructions) - moment_str = _ascii_diagram_column_set( + moment_str = _create_diagram_column_set( target_class, str(time), circuit_qubits, instructions, global_phase ) column_strs.append(moment_str) @@ -80,7 +80,7 @@ def _build_diagram_internal(target_class: type, circuit: cir.Circuit) -> str: additional_result_types, target_result_types = _categorize_result_types(circuit.result_types) if target_result_types: column_strs.append( - _ascii_diagram_column_set( + _create_diagram_column_set( target_class, "Result Types", circuit_qubits, target_result_types, global_phase ) ) @@ -253,7 +253,7 @@ def _categorize_result_types( return additional_result_types, target_result_types -def _ascii_diagram_column_set( +def _create_diagram_column_set( target_class: type, col_title: str, circuit_qubits: QubitSet, @@ -261,7 +261,7 @@ def _ascii_diagram_column_set( global_phase: float | None, ) -> str: """ - Return a set of columns in the ASCII string diagram of the circuit for a list of items. + Return a set of columns in the string diagram of the circuit for a list of items. Args: target_class (type): type of the diagram builder @@ -271,14 +271,14 @@ def _ascii_diagram_column_set( global_phase (float | None): the integrated global phase up to this set Returns: - str: An ASCII string diagram for the column set. + str: A string diagram for the column set. """ # Group items to separate out overlapping multi-qubit items groupings = _group_items(circuit_qubits, items) column_strs = [ - target_class._ascii_diagram_column(circuit_qubits, grouping[1], global_phase) + target_class._create_diagram_column(circuit_qubits, grouping[1], global_phase) for grouping in groupings ] From cafbf1362bf3fed161c59fea056610e8aa5a7dcc Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 23 Feb 2024 16:00:50 -0500 Subject: [PATCH 55/62] rename to text_circuit_diagram_utils --- .../circuits/text_diagram_builders/ascii_circuit_diagram.py | 6 +++--- .../text_diagram_builders/box_drawing_circuit_diagram.py | 6 +++--- ...ext_circuit_diagram.py => text_circuit_diagram_utils.py} | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) rename src/braket/circuits/text_diagram_builders/{text_circuit_diagram.py => text_circuit_diagram_utils.py} (99%) diff --git a/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py index 96cedaf5a..d55560565 100644 --- a/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py @@ -22,7 +22,7 @@ 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 import text_circuit_diagram +from braket.circuits.text_diagram_builders import text_circuit_diagram_utils from braket.registers.qubit_set import QubitSet @@ -46,7 +46,7 @@ def build_diagram(circuit: cir.Circuit) -> str: str: string circuit diagram. """ - return text_circuit_diagram._build_diagram_internal(AsciiCircuitDiagram, circuit) + return text_circuit_diagram_utils._build_diagram_internal(AsciiCircuitDiagram, circuit) @classmethod def _duplicate_time_at_bottom(cls, lines: str) -> None: @@ -149,7 +149,7 @@ def _create_diagram_column( if target_and_control and qubit != min(target_and_control): margins[qubit] = "|" - output = text_circuit_diagram._create_output( + output = text_circuit_diagram_utils._create_output( cls, symbols, margins, circuit_qubits, global_phase ) return output diff --git a/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py index 97da2083f..d4a91c715 100644 --- a/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py @@ -22,7 +22,7 @@ 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 import text_circuit_diagram +from braket.circuits.text_diagram_builders import text_circuit_diagram_utils from braket.registers.qubit import Qubit from braket.registers.qubit_set import QubitSet @@ -47,7 +47,7 @@ def build_diagram(circuit: cir.Circuit) -> str: str: string circuit diagram. """ - return text_circuit_diagram._build_diagram_internal(BoxDrawingCircuitDiagram, circuit) + return text_circuit_diagram_utils._build_diagram_internal(BoxDrawingCircuitDiagram, circuit) @classmethod def _duplicate_time_at_bottom(cls, lines: str) -> None: @@ -116,7 +116,7 @@ def _create_diagram_column( else: symbols[qubit] = "┼" - output = text_circuit_diagram._create_output( + output = text_circuit_diagram_utils._create_output( BoxDrawingCircuitDiagram, symbols, connections, circuit_qubits, global_phase ) return output diff --git a/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/text_circuit_diagram_utils.py similarity index 99% rename from src/braket/circuits/text_diagram_builders/text_circuit_diagram.py rename to src/braket/circuits/text_diagram_builders/text_circuit_diagram_utils.py index b5438942e..5d775dbbe 100644 --- a/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/text_circuit_diagram_utils.py @@ -311,7 +311,7 @@ def _create_output( global_phase: float | None, ) -> str: """ - Creates the ouput for a single colum: + Creates the ouput for a single column: a. If there was one or more gphase gate, create a first line with the total global phase shift ending with target_class.vdelim, e.g. 0.14| b. for each qubit, append the text representation produces by target_class.draw_symbol From a5d2cf0046db1b165ef8babf1971ab34bb45db15 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Mon, 26 Feb 2024 10:31:25 -0500 Subject: [PATCH 56/62] use cls var --- .../text_diagram_builders/box_drawing_circuit_diagram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py index d4a91c715..528f6b69f 100644 --- a/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py @@ -117,7 +117,7 @@ def _create_diagram_column( symbols[qubit] = "┼" output = text_circuit_diagram_utils._create_output( - BoxDrawingCircuitDiagram, symbols, connections, circuit_qubits, global_phase + cls, symbols, connections, circuit_qubits, global_phase ) return output From 096677d24168e7f5fceece08e69f8d5b087854bb Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 27 Feb 2024 17:08:35 -0500 Subject: [PATCH 57/62] first changes according to PR feedback --- src/braket/circuits/ascii_circuit_diagram.py | 18 ++++++++++++++++++ src/braket/circuits/circuit.py | 9 +++++---- .../unit_tests/braket/circuits/test_circuit.py | 7 ------- 3 files changed, 23 insertions(+), 11 deletions(-) create mode 100644 src/braket/circuits/ascii_circuit_diagram.py diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py new file mode 100644 index 000000000..accbce161 --- /dev/null +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -0,0 +1,18 @@ +# 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. + +# Moving ascii_circuit_diagram.py into the text_diagram_builders folder in order +# to group all classes that print circuits in a text format. +from braket.circuits.text_diagram_builders.ascii_circuit_diagram import ( # noqa: F401 + AsciiCircuitDiagram, +) diff --git a/src/braket/circuits/circuit.py b/src/braket/circuits/circuit.py index c085ebb16..bd94f83b3 100644 --- a/src/braket/circuits/circuit.py +++ b/src/braket/circuits/circuit.py @@ -49,7 +49,9 @@ QubitReferenceType, SerializationProperties, ) -from braket.circuits.text_diagram_builders.ascii_circuit_diagram import AsciiCircuitDiagram +from braket.circuits.text_diagram_builders.box_drawing_circuit_diagram import ( + BoxDrawingCircuitDiagram, +) from braket.circuits.unitary_calculation import calculate_unitary_big_endian from braket.default_simulator.openqasm.interpreter import Interpreter from braket.ir.jaqcd import Program as JaqcdProgram @@ -82,7 +84,6 @@ class Circuit: """ _ALL_QUBITS = "ALL" # Flag to indicate all qubits in _qubit_observable_mapping - default_diagram_builder = AsciiCircuitDiagram @classmethod def register_subroutine(cls, func: SubroutineCallable) -> None: @@ -1084,7 +1085,7 @@ def adjoint(self) -> Circuit: circ.add_result_type(result_type) return circ - def diagram(self, circuit_diagram_class: type = AsciiCircuitDiagram) -> str: + def diagram(self, circuit_diagram_class: type = BoxDrawingCircuitDiagram) -> str: """Get a diagram for the current circuit. Args: @@ -1496,7 +1497,7 @@ def __repr__(self) -> str: ) def __str__(self): - return self.diagram(self.default_diagram_builder) + return self.diagram() def __eq__(self, other: Circuit): if isinstance(other, Circuit): diff --git a/test/unit_tests/braket/circuits/test_circuit.py b/test/unit_tests/braket/circuits/test_circuit.py index 48248cba2..11090b313 100644 --- a/test/unit_tests/braket/circuits/test_circuit.py +++ b/test/unit_tests/braket/circuits/test_circuit.py @@ -18,7 +18,6 @@ import braket.ir.jaqcd as jaqcd from braket.circuits import ( - AsciiCircuitDiagram, BoxDrawingCircuitDiagram, Circuit, FreeParameter, @@ -199,12 +198,6 @@ def test_repr_result_types(cnot_prob): def test_str(h): - expected = AsciiCircuitDiagram.build_diagram(h) - assert str(h) == expected - - -def test_change_diagram_builder(h): - Circuit.default_diagram_builder = BoxDrawingCircuitDiagram expected = BoxDrawingCircuitDiagram.build_diagram(h) assert str(h) == expected From 0ee9ab7588f4ff169b510571add40c5ad2c324f4 Mon Sep 17 00:00:00 2001 From: Milan <30416311+krneta@users.noreply.github.com> Date: Fri, 1 Mar 2024 11:17:12 -0800 Subject: [PATCH 58/62] Attempt at simplification (#898) --- .../ascii_circuit_diagram.py | 32 ++- .../box_drawing_circuit_diagram.py | 34 ++- .../text_circuit_diagram.py | 249 ++++++++++++++++++ .../text_circuit_diagram_utils.py | 187 ++----------- 4 files changed, 316 insertions(+), 186 deletions(-) create mode 100644 src/braket/circuits/text_diagram_builders/text_circuit_diagram.py diff --git a/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py index d55560565..558cc349b 100644 --- a/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py @@ -17,21 +17,20 @@ from typing import Union import braket.circuits.circuit as cir -from braket.circuits.circuit_diagram import CircuitDiagram 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 import text_circuit_diagram_utils +from braket.circuits.text_diagram_builders.text_circuit_diagram import TextCircuitDiagram from braket.registers.qubit_set import QubitSet -class AsciiCircuitDiagram(CircuitDiagram): +class AsciiCircuitDiagram(TextCircuitDiagram): """Builds ASCII string circuit diagrams.""" _vdelim = "|" # Character that connects qubits of multi-qubit gates _qubit_line_char = "-" # Character used for the qubit line - _box_padding = 0 # number of blank space characters around the gate name + _box_pad = 0 # number of blank space characters around the gate name _qubit_line_spacing = {"before": 1, "after": 0} # number of empty lines around the qubit line @staticmethod @@ -45,8 +44,27 @@ def build_diagram(circuit: cir.Circuit) -> str: Returns: str: string circuit diagram. """ + return AsciiCircuitDiagram._build(circuit) - return text_circuit_diagram_utils._build_diagram_internal(AsciiCircuitDiagram, circuit) + @classmethod + def _vertical_delimiter(cls) -> str: + return cls._vdelim + + @classmethod + def _qubit_line_character(cls) -> str: + return cls._qubit_line_char + + @classmethod + def _box_padding(cls) -> int: + return cls._box_pad + + @classmethod + def _qubit_line_spacing_before(cls) -> int: + return cls._qubit_line_spacing["before"] + + @classmethod + def _qubit_line_spacing_after(cls) -> int: + return cls._qubit_line_spacing["after"] @classmethod def _duplicate_time_at_bottom(cls, lines: str) -> None: @@ -149,9 +167,7 @@ def _create_diagram_column( if target_and_control and qubit != min(target_and_control): margins[qubit] = "|" - output = text_circuit_diagram_utils._create_output( - cls, symbols, margins, circuit_qubits, global_phase - ) + output = cls._create_output(symbols, margins, circuit_qubits, global_phase) return output @classmethod diff --git a/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py index 528f6b69f..e585277fe 100644 --- a/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py @@ -17,22 +17,21 @@ from typing import Literal import braket.circuits.circuit as cir -from braket.circuits.circuit_diagram import CircuitDiagram 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 import text_circuit_diagram_utils +from braket.circuits.text_diagram_builders.text_circuit_diagram import TextCircuitDiagram from braket.registers.qubit import Qubit from braket.registers.qubit_set import QubitSet -class BoxDrawingCircuitDiagram(CircuitDiagram): +class BoxDrawingCircuitDiagram(TextCircuitDiagram): """Builds string circuit diagrams using box-drawing characters.""" _vdelim = "│" # Character that connects qubits of multi-qubit gates _qubit_line_char = "─" # Character used for the qubit line - _box_padding = 4 # number of blank space characters around the gate name + _box_pad = 4 # number of blank space characters around the gate name _qubit_line_spacing = {"before": 1, "after": 1} # number of empty lines around the qubit line @staticmethod @@ -46,11 +45,30 @@ def build_diagram(circuit: cir.Circuit) -> str: Returns: str: string circuit diagram. """ + return BoxDrawingCircuitDiagram._build(circuit) - return text_circuit_diagram_utils._build_diagram_internal(BoxDrawingCircuitDiagram, circuit) + @classmethod + def _vertical_delimiter(cls) -> str: + return cls._vdelim + + @classmethod + def _qubit_line_character(cls) -> str: + return cls._qubit_line_char + + @classmethod + def _box_padding(cls) -> int: + return cls._box_pad @classmethod - def _duplicate_time_at_bottom(cls, lines: str) -> None: + def _qubit_line_spacing_before(cls) -> int: + return cls._qubit_line_spacing["before"] + + @classmethod + def _qubit_line_spacing_after(cls) -> int: + return cls._qubit_line_spacing["after"] + + @classmethod + def _duplicate_time_at_bottom(cls, lines: list) -> None: # Do not add a line after the circuit # It is safe to do because the last line is empty: _qubit_line_spacing["after"] = 1 lines[-1] = lines[0] @@ -116,9 +134,7 @@ def _create_diagram_column( else: symbols[qubit] = "┼" - output = text_circuit_diagram_utils._create_output( - cls, symbols, connections, circuit_qubits, global_phase - ) + output = cls._create_output(symbols, connections, circuit_qubits, global_phase) return output @staticmethod diff --git a/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py new file mode 100644 index 000000000..96bd6f1f0 --- /dev/null +++ b/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py @@ -0,0 +1,249 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import Literal, Union + +import braket.circuits.circuit as cir +from braket.circuits.circuit_diagram import CircuitDiagram +from braket.circuits.instruction import Instruction +from braket.circuits.moments import MomentType +from braket.circuits.result_type import ResultType +from braket.circuits.text_diagram_builders.text_circuit_diagram_utils import ( + _add_footers, + _categorize_result_types, + _compute_moment_global_phase, + _group_items, + _prepare_qubit_identifier_column, + _unite_strings, +) +from braket.registers.qubit import Qubit +from braket.registers.qubit_set import QubitSet + + +class TextCircuitDiagram(CircuitDiagram): + @classmethod + @abstractmethod + def _vertical_delimiter(cls) -> str: + """[TODO: add description].""" + + @classmethod + @abstractmethod + def _qubit_line_character(cls) -> str: + """[TODO: add description].""" + + @classmethod + @abstractmethod + def _box_padding(cls) -> int: + """[TODO: add description].""" + + @classmethod + @abstractmethod + def _qubit_line_spacing_before(cls) -> int: + """[TODO: add description].""" + + @classmethod + @abstractmethod + def _qubit_line_spacing_after(cls) -> int: + """[TODO: add description].""" + + @classmethod + @abstractmethod + def _duplicate_time_at_bottom(cls, lines: list[str]) -> None: + """[TODO: add description].""" + + @classmethod + @abstractmethod + def _create_diagram_column( + cls, + circuit_qubits: QubitSet, + items: list[Instruction | ResultType], + global_phase: float | None = None, + ) -> str: + """[TODO: add description].""" + + @classmethod + @abstractmethod + def _draw_symbol( + cls, + symbol: str, + symbols_width: int, + connection: Literal["above", "below", "both", "none"], + ) -> str: + """[TODO: add description].""" + + @classmethod + def _build(cls, circuit: cir.Circuit) -> str: + """ + Build a text circuit diagram. + + The procedure follows as: + 1. Prepare the first column composed of the qubit identifiers + 2. Construct the circuit as a list of columns by looping through the + time slices. A column is a string with rows separated via '\n' + a. compute the instantaneous global phase + b. create the column corresponding to the current moment + 3. Add result types at the end of the circuit + 4. Join the columns to get a list of qubit lines + 5. Add a list of optional parameters: + a. the total global phase + b. results types that do not have any target such as statevector + c. the list of unassigned parameters + + Args: + circuit (Circuit): Circuit for which to build a diagram. + + Returns: + str: string circuit diagram. + """ + if not circuit.instructions: + return "" + + if all(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): + return f"Global phase: {circuit.global_phase}" + + circuit_qubits = circuit.qubits + circuit_qubits.sort() + + y_axis_str, global_phase = _prepare_qubit_identifier_column( + circuit, + circuit_qubits, + cls._vertical_delimiter(), + cls._qubit_line_character(), + cls._qubit_line_spacing_before(), + cls._qubit_line_spacing_after(), + ) + + column_strs = [] + + global_phase, additional_result_types = cls._build_columns( + circuit, circuit_qubits, global_phase, column_strs + ) + + # Unite strings + lines = _unite_strings(y_axis_str, column_strs) + cls._duplicate_time_at_bottom(lines) + + return _add_footers(lines, circuit, global_phase, additional_result_types) + + @classmethod + def _build_columns( + cls, + circuit: cir.Circuit, + circuit_qubits: QubitSet, + global_phase: float | None, + column_strs: list, + ) -> tuple[float | None, list[str]]: + time_slices = circuit.moments.time_slices() + + # Moment columns + for time, instructions in time_slices.items(): + global_phase = _compute_moment_global_phase(global_phase, instructions) + moment_str = cls._create_diagram_column_set( + str(time), circuit_qubits, instructions, global_phase + ) + column_strs.append(moment_str) + + # Result type columns + additional_result_types, target_result_types = _categorize_result_types( + circuit.result_types + ) + if target_result_types: + column_strs.append( + cls._create_diagram_column_set( + "Result Types", circuit_qubits, target_result_types, global_phase + ) + ) + return global_phase, additional_result_types + + @classmethod + def _create_diagram_column_set( + cls, + col_title: str, + circuit_qubits: QubitSet, + items: list[Union[Instruction, ResultType]], + global_phase: float | None, + ) -> str: + """ + Return a set of columns in the string diagram of the circuit for a list of items. + + Args: + col_title (str): title of column set + 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 set + + Returns: + str: A string diagram for the column set. + """ + + # Group items to separate out overlapping multi-qubit items + groupings = _group_items(circuit_qubits, items) + + column_strs = [ + cls._create_diagram_column(circuit_qubits, grouping[1], global_phase) + for grouping in groupings + ] + + # Unite column strings + lines = _unite_strings(column_strs[0], column_strs[1:]) + + # Adjust for column title width + col_title_width = len(col_title) + symbols_width = len(lines[0]) - 1 + if symbols_width < col_title_width: + diff = col_title_width - symbols_width + for i in range(len(lines) - 1): + if lines[i].endswith(cls._qubit_line_character()): + lines[i] += cls._qubit_line_character() * diff + else: + lines[i] += " " + + first_line = "{:^{width}}{vdelim}\n".format( + col_title, width=len(lines[0]) - 1, vdelim=cls._vertical_delimiter() + ) + + return first_line + "\n".join(lines) + + @classmethod + def _create_output( + cls, + symbols: dict[Qubit, str], + margins: dict[Qubit, str], + qubits: QubitSet, + global_phase: float | None, + ) -> str: + """ + Creates the ouput for a single column: + a. If there was one or more gphase gate, create a first line with the total global + phase shift ending with target_class.vdelim, e.g. 0.14| + b. for each qubit, append the text representation produces by target_class.draw_symbol + + Args: + symbols (dict[Qubit, str]): dictionary of the gate name for each qubit + margins (dict[Qubit, str]): map of the qubit interconnections. Specific to the + `target_class.draw_symbol` method. + qubits (QubitSet): set of the circuit qubits + global_phase (float | None): total global phase shift added during the moment + + Returns: + str: a string representing a diagram column. + """ + symbols_width = max([len(symbol) for symbol in symbols.values()]) + cls._box_padding() + output = "" + + if global_phase is not None: + global_phase_str = ( + f"{global_phase:.2f}" if isinstance(global_phase, float) else str(global_phase) + ) + symbols_width = max([symbols_width, len(global_phase_str)]) + output += "{0:{fill}{align}{width}}{vdelim}\n".format( + global_phase_str, + fill=" ", + align="^", + width=symbols_width, + vdelim=cls._vertical_delimiter(), + ) + + for qubit in qubits: + output += cls._draw_symbol(symbols[qubit], symbols_width, margins[qubit]) + return output diff --git a/src/braket/circuits/text_diagram_builders/text_circuit_diagram_utils.py b/src/braket/circuits/text_diagram_builders/text_circuit_diagram_utils.py index 5d775dbbe..9b85c30e1 100644 --- a/src/braket/circuits/text_diagram_builders/text_circuit_diagram_utils.py +++ b/src/braket/circuits/text_diagram_builders/text_circuit_diagram_utils.py @@ -23,72 +23,15 @@ from braket.circuits.moments import MomentType from braket.circuits.noise import Noise from braket.circuits.result_type import ResultType -from braket.registers.qubit import Qubit from braket.registers.qubit_set import QubitSet -def _build_diagram_internal(target_class: type, circuit: cir.Circuit) -> str: - """ - Build a text circuit diagram. - - The procedure follows as: - 1. Prepare the first column composed of the qubit identifiers - 2. Construct the circuit as a list of columns by looping through the - time slices. A column is a string with rows separated via '\n' - a. compute the instantaneous global phase - b. create the column corresponding to the current moment - 3. Add result types at the end of the circuit - 4. Join the columns to get a list of qubit lines - 5. Add a list of optional parameters: - a. the total global phase - b. results types that do not have any target such as statevector - c. the list of unassigned parameters - - Args: - target_class (type): type of the diagram builder - circuit (Circuit): Circuit for which to build a diagram. - - Returns: - str: string circuit diagram. - """ - - if not circuit.instructions: - return "" - - if all(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): - return f"Global phase: {circuit.global_phase}" - - circuit_qubits = circuit.qubits - circuit_qubits.sort() - - y_axis_str, global_phase = _prepare_qubit_identifier_column( - target_class, circuit, circuit_qubits - ) - - time_slices = circuit.moments.time_slices() - column_strs = [] - - # Moment columns - for time, instructions in time_slices.items(): - global_phase = _compute_moment_global_phase(global_phase, instructions) - moment_str = _create_diagram_column_set( - target_class, str(time), circuit_qubits, instructions, global_phase - ) - column_strs.append(moment_str) - - # Result type columns - additional_result_types, target_result_types = _categorize_result_types(circuit.result_types) - if target_result_types: - column_strs.append( - _create_diagram_column_set( - target_class, "Result Types", circuit_qubits, target_result_types, global_phase - ) - ) - - # Unite strings - lines = _unite_strings(y_axis_str, column_strs) - target_class._duplicate_time_at_bottom(lines) - +def _add_footers( + lines: list, + circuit: cir.Circuit, + global_phase: float | None, + additional_result_types: list[str], +) -> str: if global_phase: lines.append(f"\nGlobal phase: {global_phase}") @@ -107,37 +50,38 @@ def _build_diagram_internal(target_class: type, circuit: cir.Circuit) -> str: def _prepare_qubit_identifier_column( - target_class: type, circuit: cir.Circuit, circuit_qubits: QubitSet + circuit: cir.Circuit, + circuit_qubits: QubitSet, + vdelim: str, + qubit_line_char: str, + line_spacing_before: int, + line_spacing_after: int, ) -> tuple[str, float | None]: # Y Axis Column y_axis_width = len(str(int(max(circuit_qubits)))) - y_axis_str = "{0:{width}} : {vdelim}\n".format( - "T", width=y_axis_width + 1, vdelim=target_class._vdelim - ) + y_axis_str = "{0:{width}} : {vdelim}\n".format("T", width=y_axis_width + 1, vdelim=vdelim) global_phase = None if any(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments): - y_axis_str += "{0:{width}} : {vdelim}\n".format( - "GP", width=y_axis_width, vdelim=target_class._vdelim - ) + y_axis_str += "{0:{width}} : {vdelim}\n".format("GP", width=y_axis_width, vdelim=vdelim) global_phase = 0 for qubit in circuit_qubits: - for _ in range(target_class._qubit_line_spacing["before"]): + for _ in range(line_spacing_before): y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) y_axis_str += "q{0:{width}} : {qubit_line_char}\n".format( str(int(qubit)), width=y_axis_width, - qubit_line_char=target_class._qubit_line_char, + qubit_line_char=qubit_line_char, ) - for _ in range(target_class._qubit_line_spacing["after"]): + for _ in range(line_spacing_after): y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5) return y_axis_str, global_phase -def _unite_strings(first_column: str, column_strs: list[str]) -> str: +def _unite_strings(first_column: str, column_strs: list[str]) -> list: lines = first_column.split("\n") for col_str in column_strs: for i, line_in_col in enumerate(col_str.split("\n")): @@ -251,98 +195,3 @@ def _categorize_result_types( else: additional_result_types.extend(result_type.ascii_symbols) return additional_result_types, target_result_types - - -def _create_diagram_column_set( - target_class: type, - col_title: str, - circuit_qubits: QubitSet, - items: list[Union[Instruction, ResultType]], - global_phase: float | None, -) -> str: - """ - Return a set of columns in the string diagram of the circuit for a list of items. - - Args: - target_class (type): type of the diagram builder - col_title (str): title of column set - 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 set - - Returns: - str: A string diagram for the column set. - """ - - # Group items to separate out overlapping multi-qubit items - groupings = _group_items(circuit_qubits, items) - - column_strs = [ - target_class._create_diagram_column(circuit_qubits, grouping[1], global_phase) - for grouping in groupings - ] - - # Unite column strings - lines = _unite_strings(column_strs[0], column_strs[1:]) - - # Adjust for column title width - col_title_width = len(col_title) - symbols_width = len(lines[0]) - 1 - if symbols_width < col_title_width: - diff = col_title_width - symbols_width - for i in range(len(lines) - 1): - if lines[i].endswith(target_class._qubit_line_char): - lines[i] += target_class._qubit_line_char * diff - else: - lines[i] += " " - - first_line = "{:^{width}}{vdelim}\n".format( - col_title, width=len(lines[0]) - 1, vdelim=target_class._vdelim - ) - - return first_line + "\n".join(lines) - - -def _create_output( - target_class: type, - symbols: dict[Qubit, str], - margins: dict[Qubit, str], - qubits: QubitSet, - global_phase: float | None, -) -> str: - """ - Creates the ouput for a single column: - a. If there was one or more gphase gate, create a first line with the total global - phase shift ending with target_class.vdelim, e.g. 0.14| - b. for each qubit, append the text representation produces by target_class.draw_symbol - - Args: - target_class (type): type of the diagram builder - symbols (dict[Qubit, str]): dictionary of the gate name for each qubit - margins (dict[Qubit, str]): map of the qubit interconnections. Specific to the - `target_class.draw_symbol` method. - qubits (QubitSet): set of the circuit qubits - global_phase (float | None): total global phase shift added during the moment - - Returns: - str: a string representing a diagram column. - """ - symbols_width = max([len(symbol) for symbol in symbols.values()]) + target_class._box_padding - output = "" - - if global_phase is not None: - global_phase_str = ( - f"{global_phase:.2f}" if isinstance(global_phase, float) else str(global_phase) - ) - symbols_width = max([symbols_width, len(global_phase_str)]) - output += "{0:{fill}{align}{width}}{vdelim}\n".format( - global_phase_str, - fill=" ", - align="^", - width=symbols_width, - vdelim=target_class._vdelim, - ) - - for qubit in qubits: - output += target_class._draw_symbol(symbols[qubit], symbols_width, margins[qubit]) - return output From e4df2ab2872de0ff61acf67ad56c19dc448d496a Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 1 Mar 2024 15:47:46 -0500 Subject: [PATCH 59/62] add docstrings and rename box_drawing_circuit_diagram after merge --- src/braket/circuits/__init__.py | 4 +- src/braket/circuits/circuit.py | 6 +- .../ascii_circuit_diagram.py | 34 +++++------ .../text_circuit_diagram.py | 61 +++++++++++++------ ..._diagram.py => unicode_circuit_diagram.py} | 58 +++++++++--------- .../braket/circuits/test_circuit.py | 4 +- ...ram.py => test_unicode_circuit_diagram.py} | 8 +-- 7 files changed, 96 insertions(+), 79 deletions(-) rename src/braket/circuits/text_diagram_builders/{box_drawing_circuit_diagram.py => unicode_circuit_diagram.py} (85%) rename test/unit_tests/braket/circuits/{test_box_drawing_circuit_diagram.py => test_unicode_circuit_diagram.py} (99%) diff --git a/src/braket/circuits/__init__.py b/src/braket/circuits/__init__.py index bff514618..a5fb52980 100644 --- a/src/braket/circuits/__init__.py +++ b/src/braket/circuits/__init__.py @@ -40,6 +40,6 @@ from braket.circuits.text_diagram_builders.ascii_circuit_diagram import ( # noqa: F401 AsciiCircuitDiagram, ) -from braket.circuits.text_diagram_builders.box_drawing_circuit_diagram import ( # noqa: F401 - BoxDrawingCircuitDiagram, +from braket.circuits.text_diagram_builders.unicode_circuit_diagram import ( # noqa: F401 + UnicodeCircuitDiagram, ) diff --git a/src/braket/circuits/circuit.py b/src/braket/circuits/circuit.py index bd94f83b3..d5abae046 100644 --- a/src/braket/circuits/circuit.py +++ b/src/braket/circuits/circuit.py @@ -49,9 +49,7 @@ QubitReferenceType, SerializationProperties, ) -from braket.circuits.text_diagram_builders.box_drawing_circuit_diagram import ( - BoxDrawingCircuitDiagram, -) +from braket.circuits.text_diagram_builders.unicode_circuit_diagram import UnicodeCircuitDiagram from braket.circuits.unitary_calculation import calculate_unitary_big_endian from braket.default_simulator.openqasm.interpreter import Interpreter from braket.ir.jaqcd import Program as JaqcdProgram @@ -1085,7 +1083,7 @@ def adjoint(self) -> Circuit: circ.add_result_type(result_type) return circ - def diagram(self, circuit_diagram_class: type = BoxDrawingCircuitDiagram) -> str: + def diagram(self, circuit_diagram_class: type = UnicodeCircuitDiagram) -> str: """Get a diagram for the current circuit. Args: diff --git a/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py index 558cc349b..929f59f73 100644 --- a/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py @@ -28,11 +28,6 @@ class AsciiCircuitDiagram(TextCircuitDiagram): """Builds ASCII string circuit diagrams.""" - _vdelim = "|" # Character that connects qubits of multi-qubit gates - _qubit_line_char = "-" # Character used for the qubit line - _box_pad = 0 # number of blank space characters around the gate name - _qubit_line_spacing = {"before": 1, "after": 0} # number of empty lines around the qubit line - @staticmethod def build_diagram(circuit: cir.Circuit) -> str: """ @@ -48,23 +43,28 @@ def build_diagram(circuit: cir.Circuit) -> str: @classmethod def _vertical_delimiter(cls) -> str: - return cls._vdelim + """Character that connects qubits of multi-qubit gates.""" + return "|" @classmethod def _qubit_line_character(cls) -> str: - return cls._qubit_line_char + """Character used for the qubit line.""" + return "-" @classmethod - def _box_padding(cls) -> int: - return cls._box_pad + def _box_pad(cls) -> int: + """number of blank space characters around the gate name.""" + return 0 @classmethod - def _qubit_line_spacing_before(cls) -> int: - return cls._qubit_line_spacing["before"] + def _qubit_line_spacing_above(cls) -> int: + """number of empty lines above the qubit line.""" + return 1 @classmethod - def _qubit_line_spacing_after(cls) -> int: - return cls._qubit_line_spacing["after"] + 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: @@ -88,7 +88,7 @@ def _create_diagram_column( Returns: str: an ASCII string diagram for the specified moment in time for a column. """ - symbols = {qubit: cls._qubit_line_char for qubit in circuit_qubits} + symbols = {qubit: cls._qubit_line_character() for qubit in circuit_qubits} margins = {qubit: " " for qubit in circuit_qubits} for item in items: @@ -117,7 +117,7 @@ def _create_diagram_column( control_qubits = QubitSet() target_and_control = QubitSet() qubits = circuit_qubits - ascii_symbols = cls._qubit_line_char * len(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()) @@ -178,7 +178,7 @@ def _draw_symbol(cls, symbol: str, symbols_width: int, connection: str) -> str: Args: symbol (str): the gate name symbols_width (int): size of the expected output. The ouput will be filled with - cls._qubit_line_char if needed. + cls._qubit_line_character() if needed. connection (str): character indicating if the gate also involve a qubit with a lower index. @@ -187,6 +187,6 @@ def _draw_symbol(cls, symbol: str, symbols_width: int, connection: str) -> str: """ output = "{0:{width}}\n".format(connection, width=symbols_width + 1) output += "{0:{fill}{align}{width}}\n".format( - symbol, fill=cls._qubit_line_char, align="<", width=symbols_width + 1 + symbol, fill=cls._qubit_line_character(), align="<", width=symbols_width + 1 ) return output diff --git a/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py index 96bd6f1f0..556b099da 100644 --- a/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py @@ -1,6 +1,6 @@ from __future__ import annotations -from abc import abstractmethod +from abc import ABC, abstractmethod from typing import Literal, Union import braket.circuits.circuit as cir @@ -20,36 +20,33 @@ from braket.registers.qubit_set import QubitSet -class TextCircuitDiagram(CircuitDiagram): +class TextCircuitDiagram(CircuitDiagram, ABC): + """Abstract base class for text circuit diagrams.""" + @classmethod @abstractmethod def _vertical_delimiter(cls) -> str: - """[TODO: add description].""" + """Character that connects qubits of multi-qubit gates.""" @classmethod @abstractmethod def _qubit_line_character(cls) -> str: - """[TODO: add description].""" + """Character used for the qubit line.""" @classmethod @abstractmethod - def _box_padding(cls) -> int: - """[TODO: add description].""" + def _box_pad(cls) -> int: + """number of blank space characters around the gate name.""" @classmethod @abstractmethod - def _qubit_line_spacing_before(cls) -> int: - """[TODO: add description].""" + def _qubit_line_spacing_above(cls) -> int: + """number of empty lines above the qubit line.""" @classmethod @abstractmethod - def _qubit_line_spacing_after(cls) -> int: - """[TODO: add description].""" - - @classmethod - @abstractmethod - def _duplicate_time_at_bottom(cls, lines: list[str]) -> None: - """[TODO: add description].""" + def _qubit_line_spacing_below(cls) -> int: + """number of empty lines below the qubit line.""" @classmethod @abstractmethod @@ -59,8 +56,20 @@ def _create_diagram_column( items: list[Instruction | ResultType], global_phase: float | None = None, ) -> str: - """[TODO: add description].""" + """ + Return a column in the string diagram of the circuit for a given list of items. + Args: + circuit_qubits (QubitSet): qubits in circuit + items (list[Instruction | ResultType]): list of instructions or result types + global_phase (float | None): the integrated global phase up to this column + + Returns: + str: a string diagram for the specified moment in time for a column. + """ + + # Ignore flake8 issue caused by Literal["above", "below", "both", "none"] + # flake8: noqa: BCS005 @classmethod @abstractmethod def _draw_symbol( @@ -69,7 +78,19 @@ def _draw_symbol( symbols_width: int, connection: Literal["above", "below", "both", "none"], ) -> str: - """[TODO: add description].""" + """ + Create a string representing the symbol inside a box. + + Args: + symbol (str): the gate name + symbols_width (int): size of the expected output. The ouput will be filled with + cls._qubit_line_character() if needed. + connection (Literal["above", "below", "both", "none"]): specifies if a connection + will be drawn above and/or below the box. + + Returns: + str: a string representing the symbol. + """ @classmethod def _build(cls, circuit: cir.Circuit) -> str: @@ -109,8 +130,8 @@ def _build(cls, circuit: cir.Circuit) -> str: circuit_qubits, cls._vertical_delimiter(), cls._qubit_line_character(), - cls._qubit_line_spacing_before(), - cls._qubit_line_spacing_after(), + cls._qubit_line_spacing_above(), + cls._qubit_line_spacing_below(), ) column_strs = [] @@ -228,7 +249,7 @@ def _create_output( Returns: str: a string representing a diagram column. """ - symbols_width = max([len(symbol) for symbol in symbols.values()]) + cls._box_padding() + symbols_width = max([len(symbol) for symbol in symbols.values()]) + cls._box_pad() output = "" if global_phase is not None: diff --git a/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/unicode_circuit_diagram.py similarity index 85% rename from src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py rename to src/braket/circuits/text_diagram_builders/unicode_circuit_diagram.py index e585277fe..1275bd68f 100644 --- a/src/braket/circuits/text_diagram_builders/box_drawing_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/unicode_circuit_diagram.py @@ -26,14 +26,9 @@ from braket.registers.qubit_set import QubitSet -class BoxDrawingCircuitDiagram(TextCircuitDiagram): +class UnicodeCircuitDiagram(TextCircuitDiagram): """Builds string circuit diagrams using box-drawing characters.""" - _vdelim = "│" # Character that connects qubits of multi-qubit gates - _qubit_line_char = "─" # Character used for the qubit line - _box_pad = 4 # number of blank space characters around the gate name - _qubit_line_spacing = {"before": 1, "after": 1} # number of empty lines around the qubit line - @staticmethod def build_diagram(circuit: cir.Circuit) -> str: """ @@ -45,27 +40,32 @@ def build_diagram(circuit: cir.Circuit) -> str: Returns: str: string circuit diagram. """ - return BoxDrawingCircuitDiagram._build(circuit) + return UnicodeCircuitDiagram._build(circuit) @classmethod def _vertical_delimiter(cls) -> str: - return cls._vdelim + """Character that connects qubits of multi-qubit gates.""" + return "│" @classmethod def _qubit_line_character(cls) -> str: - return cls._qubit_line_char + """Character used for the qubit line.""" + return "─" @classmethod - def _box_padding(cls) -> int: - return cls._box_pad + def _box_pad(cls) -> int: + """number of blank space characters around the gate name.""" + return 4 @classmethod - def _qubit_line_spacing_before(cls) -> int: - return cls._qubit_line_spacing["before"] + def _qubit_line_spacing_above(cls) -> int: + """number of empty lines above the qubit line.""" + return 1 @classmethod - def _qubit_line_spacing_after(cls) -> int: - return cls._qubit_line_spacing["after"] + def _qubit_line_spacing_below(cls) -> int: + """number of empty lines below the qubit line.""" + return 1 @classmethod def _duplicate_time_at_bottom(cls, lines: list) -> None: @@ -91,7 +91,7 @@ def _create_diagram_column( Returns: str: a string diagram for the specified moment in time for a column. """ - symbols = {qubit: cls._qubit_line_char for qubit in circuit_qubits} + symbols = {qubit: cls._qubit_line_character() for qubit in circuit_qubits} connections = {qubit: "none" for qubit in circuit_qubits} for item in items: @@ -150,7 +150,7 @@ def _build_parameters( control_qubits = QubitSet() qubits = circuit_qubits ascii_symbols = [item.ascii_symbols[0]] * len(qubits) - BoxDrawingCircuitDiagram._update_connections(qubits, connections) + UnicodeCircuitDiagram._update_connections(qubits, connections) elif ( isinstance(item, Instruction) and isinstance(item.operator, Gate) @@ -159,7 +159,7 @@ def _build_parameters( target_qubits = circuit_qubits control_qubits = QubitSet() qubits = circuit_qubits - ascii_symbols = BoxDrawingCircuitDiagram._qubit_line_char * len(circuit_qubits) + ascii_symbols = UnicodeCircuitDiagram._qubit_line_character() * len(circuit_qubits) else: if isinstance(item.target, list): target_qubits = reduce(QubitSet.union, map(QubitSet, item.target), QubitSet()) @@ -174,7 +174,7 @@ def _build_parameters( 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 - BoxDrawingCircuitDiagram._update_connections(qubits, connections) + UnicodeCircuitDiagram._update_connections(qubits, connections) return ( target_qubits, @@ -207,7 +207,7 @@ def _draw_symbol( Args: symbol (str): the gate name symbols_width (int): size of the expected output. The ouput will be filled with - cls._qubit_line_char if needed. + cls._qubit_line_character() if needed. connection (Literal["above", "below", "both", "none"]): specifies if a connection will be drawn above and/or below the box. @@ -218,28 +218,26 @@ def _draw_symbol( bottom = "" if symbol in ["C", "N", "SWAP"]: if connection in ["above", "both"]: - top = _fill_symbol(cls._vdelim, " ") + top = _fill_symbol(cls._vertical_delimiter(), " ") if connection in ["below", "both"]: - bottom = _fill_symbol(cls._vdelim, " ") + bottom = _fill_symbol(cls._vertical_delimiter(), " ") new_symbol = {"C": "●", "N": "◯", "SWAP": "x"} # replace SWAP by x # the size of the moment remains as if there was a box with 4 characters inside - symbol = _fill_symbol(new_symbol[symbol], cls._qubit_line_char) + symbol = _fill_symbol(new_symbol[symbol], cls._qubit_line_character()) elif symbol in ["StartVerbatim", "EndVerbatim"]: top, symbol, bottom = cls._build_verbatim_box(symbol, connection) elif symbol == "┼": - top = bottom = _fill_symbol(cls._vdelim, " ") - symbol = _fill_symbol(f"{symbol}", cls._qubit_line_char) - elif symbol == cls._qubit_line_char: + top = bottom = _fill_symbol(cls._vertical_delimiter(), " ") + symbol = _fill_symbol(f"{symbol}", cls._qubit_line_character()) + elif symbol == cls._qubit_line_character(): # We do not box when no gate is applied. pass else: top, symbol, bottom = cls._build_box(symbol, connection) output = f"{_fill_symbol(top, ' ', symbols_width)} \n" - output += ( - f"{_fill_symbol(symbol, cls._qubit_line_char, symbols_width)}{cls._qubit_line_char}\n" - ) + output += f"{_fill_symbol(symbol, cls._qubit_line_character(), symbols_width)}{cls._qubit_line_character()}\n" output += f"{_fill_symbol(bottom, ' ', symbols_width)} \n" return output @@ -272,7 +270,7 @@ def _build_verbatim_box( top = "║" symbol = "╨" top = _fill_symbol(top, " ") - symbol = _fill_symbol(symbol, BoxDrawingCircuitDiagram._qubit_line_char) + symbol = _fill_symbol(symbol, UnicodeCircuitDiagram._qubit_line_character()) bottom = _fill_symbol(bottom, " ") return top, symbol, bottom diff --git a/test/unit_tests/braket/circuits/test_circuit.py b/test/unit_tests/braket/circuits/test_circuit.py index 11090b313..9356d02b7 100644 --- a/test/unit_tests/braket/circuits/test_circuit.py +++ b/test/unit_tests/braket/circuits/test_circuit.py @@ -18,7 +18,6 @@ import braket.ir.jaqcd as jaqcd from braket.circuits import ( - BoxDrawingCircuitDiagram, Circuit, FreeParameter, Gate, @@ -27,6 +26,7 @@ Observable, QubitSet, ResultType, + UnicodeCircuitDiagram, circuit, compiler_directives, gates, @@ -198,7 +198,7 @@ def test_repr_result_types(cnot_prob): def test_str(h): - expected = BoxDrawingCircuitDiagram.build_diagram(h) + expected = UnicodeCircuitDiagram.build_diagram(h) assert str(h) == expected diff --git a/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py b/test/unit_tests/braket/circuits/test_unicode_circuit_diagram.py similarity index 99% rename from test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py rename to test/unit_tests/braket/circuits/test_unicode_circuit_diagram.py index 49220268e..96df634cb 100644 --- a/test/unit_tests/braket/circuits/test_box_drawing_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_unicode_circuit_diagram.py @@ -15,27 +15,27 @@ import pytest from braket.circuits import ( - BoxDrawingCircuitDiagram, Circuit, FreeParameter, Gate, Instruction, Observable, Operator, + UnicodeCircuitDiagram, ) from braket.pulse import Frame, Port, PulseSequence def _assert_correct_diagram(circ, expected): - assert BoxDrawingCircuitDiagram.build_diagram(circ) == "\n".join(expected) + assert UnicodeCircuitDiagram.build_diagram(circ) == "\n".join(expected) def test_empty_circuit(): - assert BoxDrawingCircuitDiagram.build_diagram(Circuit()) == "" + assert UnicodeCircuitDiagram.build_diagram(Circuit()) == "" def test_only_gphase_circuit(): - assert BoxDrawingCircuitDiagram.build_diagram(Circuit().gphase(0.1)) == "Global phase: 0.1" + assert UnicodeCircuitDiagram.build_diagram(Circuit().gphase(0.1)) == "Global phase: 0.1" def test_one_gate_one_qubit(): From 6d649a8609aab4878d1001c7619f205ece6610f4 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 1 Mar 2024 17:19:09 -0500 Subject: [PATCH 60/62] standardize type hints of _draw_symbol --- .../ascii_circuit_diagram.py | 27 ++++++++++--------- .../text_circuit_diagram.py | 15 ++++------- .../unicode_circuit_diagram.py | 9 +++---- 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py index 929f59f73..3106afc47 100644 --- a/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/ascii_circuit_diagram.py @@ -14,7 +14,7 @@ from __future__ import annotations from functools import reduce -from typing import Union +from typing import Literal, Union import braket.circuits.circuit as cir from braket.circuits.compiler_directive import CompilerDirective @@ -30,8 +30,7 @@ class AsciiCircuitDiagram(TextCircuitDiagram): @staticmethod def build_diagram(circuit: cir.Circuit) -> str: - """ - Build a text circuit diagram. + """Build a text circuit diagram. Args: circuit (Circuit): Circuit for which to build a diagram. @@ -89,7 +88,7 @@ def _create_diagram_column( 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} - margins = {qubit: " " 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: @@ -165,27 +164,31 @@ def _create_diagram_column( # Set the margin to be a connector if not on the first qubit if target_and_control and qubit != min(target_and_control): - margins[qubit] = "|" + connections[qubit] = "above" - output = cls._create_output(symbols, margins, circuit_qubits, global_phase) + 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: str) -> str: - """ - Create a string representing the symbol. + 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 ouput will be filled with cls._qubit_line_character() if needed. - connection (str): character indicating if the gate also involve a qubit with a lower - index. + 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. """ - output = "{0:{width}}\n".format(connection, width=symbols_width + 1) + connection_char = cls._vertical_delimiter() if connection in ["above"] else " " + output = "{0:{width}}\n".format(connection_char, width=symbols_width + 1) output += "{0:{fill}{align}{width}}\n".format( symbol, fill=cls._qubit_line_character(), align="<", width=symbols_width + 1 ) diff --git a/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py index 556b099da..5032b9532 100644 --- a/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py @@ -56,8 +56,7 @@ def _create_diagram_column( items: list[Instruction | ResultType], global_phase: float | None = None, ) -> str: - """ - Return a column in the string diagram of the circuit for a given list of items. + """Return a column in the string diagram of the circuit for a given list of items. Args: circuit_qubits (QubitSet): qubits in circuit @@ -78,8 +77,7 @@ def _draw_symbol( symbols_width: int, connection: Literal["above", "below", "both", "none"], ) -> str: - """ - Create a string representing the symbol inside a box. + """Create a string representing the symbol inside a box. Args: symbol (str): the gate name @@ -94,8 +92,7 @@ def _draw_symbol( @classmethod def _build(cls, circuit: cir.Circuit) -> str: - """ - Build a text circuit diagram. + """Build a text circuit diagram. The procedure follows as: 1. Prepare the first column composed of the qubit identifiers @@ -184,8 +181,7 @@ def _create_diagram_column_set( items: list[Union[Instruction, ResultType]], global_phase: float | None, ) -> str: - """ - Return a set of columns in the string diagram of the circuit for a list of items. + """Return a set of columns in the string diagram of the circuit for a list of items. Args: col_title (str): title of column set @@ -233,8 +229,7 @@ def _create_output( qubits: QubitSet, global_phase: float | None, ) -> str: - """ - Creates the ouput for a single column: + """Creates the ouput for a single column: a. If there was one or more gphase gate, create a first line with the total global phase shift ending with target_class.vdelim, e.g. 0.14| b. for each qubit, append the text representation produces by target_class.draw_symbol diff --git a/src/braket/circuits/text_diagram_builders/unicode_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/unicode_circuit_diagram.py index 1275bd68f..730c42476 100644 --- a/src/braket/circuits/text_diagram_builders/unicode_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/unicode_circuit_diagram.py @@ -31,8 +31,7 @@ class UnicodeCircuitDiagram(TextCircuitDiagram): @staticmethod def build_diagram(circuit: cir.Circuit) -> str: - """ - Build a text circuit diagram. + """Build a text circuit diagram. Args: circuit (Circuit): Circuit for which to build a diagram. @@ -80,8 +79,7 @@ def _create_diagram_column( items: list[Instruction | ResultType], global_phase: float | None = None, ) -> str: - """ - Return a column in the string diagram of the circuit for a given list of items. + """Return a column in the string diagram of the circuit for a given list of items. Args: circuit_qubits (QubitSet): qubits in circuit @@ -201,8 +199,7 @@ def _draw_symbol( symbols_width: int, connection: Literal["above", "below", "both", "none"], ) -> str: - """ - Create a string representing the symbol inside a box. + """Create a string representing the symbol inside a box. Args: symbol (str): the gate name From 8a1c0bbeb33c6934b78162b2b172a04a7ee90ca8 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 5 Mar 2024 16:57:35 -0500 Subject: [PATCH 61/62] small changes according to PR feedback. --- .../text_diagram_builders/text_circuit_diagram.py | 6 +++--- .../text_diagram_builders/unicode_circuit_diagram.py | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py index 5032b9532..c8bfa2650 100644 --- a/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/text_circuit_diagram.py @@ -231,13 +231,13 @@ def _create_output( ) -> str: """Creates the ouput for a single column: a. If there was one or more gphase gate, create a first line with the total global - phase shift ending with target_class.vdelim, e.g. 0.14| - b. for each qubit, append the text representation produces by target_class.draw_symbol + phase shift ending with the _vertical_delimiter() class attribute, e.g. 0.14| + b. for each qubit, append the text representation produces by cls._draw_symbol Args: symbols (dict[Qubit, str]): dictionary of the gate name for each qubit margins (dict[Qubit, str]): map of the qubit interconnections. Specific to the - `target_class.draw_symbol` method. + `_draw_symbol` classmethod. qubits (QubitSet): set of the circuit qubits global_phase (float | None): total global phase shift added during the moment diff --git a/src/braket/circuits/text_diagram_builders/unicode_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/unicode_circuit_diagram.py index 730c42476..8136bb7cc 100644 --- a/src/braket/circuits/text_diagram_builders/unicode_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/unicode_circuit_diagram.py @@ -135,9 +135,9 @@ def _create_diagram_column( output = cls._create_output(symbols, connections, circuit_qubits, global_phase) return output - @staticmethod + @classmethod def _build_parameters( - circuit_qubits: QubitSet, item: ResultType | Instruction, connections: dict[Qubit, str] + cls, circuit_qubits: QubitSet, item: ResultType | Instruction, connections: dict[Qubit, str] ) -> tuple: map_control_qubit_states = {} @@ -148,7 +148,7 @@ def _build_parameters( control_qubits = QubitSet() qubits = circuit_qubits ascii_symbols = [item.ascii_symbols[0]] * len(qubits) - UnicodeCircuitDiagram._update_connections(qubits, connections) + cls._update_connections(qubits, connections) elif ( isinstance(item, Instruction) and isinstance(item.operator, Gate) @@ -157,7 +157,7 @@ def _build_parameters( target_qubits = circuit_qubits control_qubits = QubitSet() qubits = circuit_qubits - ascii_symbols = UnicodeCircuitDiagram._qubit_line_character() * len(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()) @@ -172,7 +172,7 @@ def _build_parameters( 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 - UnicodeCircuitDiagram._update_connections(qubits, connections) + cls._update_connections(qubits, connections) return ( target_qubits, From af08fe7f46ab3ed6b33ba6c11a19cccbd2ae6a00 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 6 Mar 2024 11:59:00 -0500 Subject: [PATCH 62/62] change a staticmethod to a classmethod --- .../text_diagram_builders/unicode_circuit_diagram.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/braket/circuits/text_diagram_builders/unicode_circuit_diagram.py b/src/braket/circuits/text_diagram_builders/unicode_circuit_diagram.py index 8136bb7cc..9e1779cb7 100644 --- a/src/braket/circuits/text_diagram_builders/unicode_circuit_diagram.py +++ b/src/braket/circuits/text_diagram_builders/unicode_circuit_diagram.py @@ -251,8 +251,9 @@ def _build_box( symbol = f"┤ {symbol} ├" return top, symbol, bottom - @staticmethod + @classmethod def _build_verbatim_box( + cls, symbol: Literal["StartVerbatim", "EndVerbatim"], connection: Literal["above", "below", "both", "none"], ) -> str: @@ -267,7 +268,7 @@ def _build_verbatim_box( top = "║" symbol = "╨" top = _fill_symbol(top, " ") - symbol = _fill_symbol(symbol, UnicodeCircuitDiagram._qubit_line_character()) + symbol = _fill_symbol(symbol, cls._qubit_line_character()) bottom = _fill_symbol(bottom, " ") return top, symbol, bottom