From d784d881414dcd23082ce36879c32bc9c4c71106 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 14 Nov 2023 16:35:51 -0500 Subject: [PATCH 01/49] Add U gate --- src/braket/circuits/gates.py | 112 ++++++++++++++++++ src/braket/circuits/translations.py | 1 + test/unit_tests/braket/circuits/test_gates.py | 18 ++- 3 files changed, 130 insertions(+), 1 deletion(-) diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index dbc2e1a0b..279d1faf2 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -1091,6 +1091,118 @@ def phaseshift( Gate.register_gate(PhaseShift) +class U(TripleAngledGate): + """Parameterized primitive gate for OpenQASM simulator + + Args: + angle_1 (Union[FreeParameterExpression, float]): theta angle in radians. + angle_2 (Union[FreeParameterExpression, float]): phi angle in radians. + angle_3 (Union[FreeParameterExpression, float]): lambda angle in radians. + """ + + def __init__( + self, + angle_1: Union[FreeParameterExpression, float], + angle_2: Union[FreeParameterExpression, float], + angle_3: Union[FreeParameterExpression, float], + ): + super().__init__( + angle_1=angle_1, + angle_2=angle_2, + angle_3=angle_3, + qubit_count=None, + ascii_symbols=[_multi_angled_ascii_characters("U", angle_1, angle_2, angle_3)], + ) + + @property + def _qasm_name(self) -> str: + return "U" + + def to_matrix(self) -> np.ndarray: + """ + Generate parameterized Unitary matrix. + https://openqasm.com/language/gates.html#built-in-gates + + Returns: + np.ndarray: U Matrix + """ + theta = self.angle_1 + phi = self.angle_2 + lam = self.angle_3 + return np.array( + [ + [ + np.cos(theta / 2), + -np.exp(1j * lam) * np.sin(theta / 2), + ], + [ + np.exp(1j * phi) * np.sin(theta / 2), + np.exp(1j * (phi + lam)) * np.cos(theta / 2), + ], + ] + ) + + def adjoint(self) -> list[Gate]: + return [U(self.angle_1, np.pi - self.angle_3, np.pi - self.angle_2)] + + @staticmethod + def fixed_qubit_count() -> int: + return 1 + + def bind_values(self, **kwargs) -> U: + return _get_angles(self, **kwargs) + + @staticmethod + @circuit.subroutine(register=True) + def u( + target: QubitInput, + angle_1: Union[FreeParameterExpression, float], + angle_2: Union[FreeParameterExpression, float], + angle_3: Union[FreeParameterExpression, float], + *, + control: Optional[QubitSetInput] = None, + control_state: Optional[BasisStateInput] = None, + power: float = 1, + ) -> Iterable[Instruction]: + """Registers this function into the circuit class. + + Args: + target1 (QubitInput): Target qubit 1 index. + angle_1 (Union[FreeParameterExpression, float]): theta angle in radians. + angle_2 (Union[FreeParameterExpression, float]): phi angle in radians. + angle_3 (Union[FreeParameterExpression, float]): lambda angle in radians. + control (Optional[QubitSetInput]): Control qubit(s). Default None. + control_state (Optional[BasisStateInput]): Quantum state on which to control the + operation. Must be a binary sequence of same length as number of qubits in + `control`. Will be ignored if `control` is not present. May be represented as a + string, list, or int. For example "0101", [0, 1, 0, 1], 5 all represent + controlling on qubits 0 and 2 being in the \\|0⟩ state and qubits 1 and 3 being + in the \\|1⟩ state. Default "1" * len(control). + power (float): Integer or fractional power to raise the gate to. Negative + powers will be split into an inverse, accompanied by the positive power. + Default 1. + + Returns: + Iterable[Instruction]: MS instruction. + + Examples: + >>> circ = Circuit().ms(0, 1, 0.15, 0.34) + """ + return [ + Instruction( + U(angle_1, angle_2, angle_3), + target=qubit, + control=control, + control_state=control_state, + power=power, + ) + for qubit in QubitSet(target) + ] + + +Gate.register_gate(U) + + # Two qubit gates # diff --git a/src/braket/circuits/translations.py b/src/braket/circuits/translations.py index ba536594f..30fc28309 100644 --- a/src/braket/circuits/translations.py +++ b/src/braket/circuits/translations.py @@ -55,6 +55,7 @@ "rx": braket_gates.Rx, "ry": braket_gates.Ry, "rz": braket_gates.Rz, + "U": braket_gates.U, "swap": braket_gates.Swap, "iswap": braket_gates.ISwap, "pswap": braket_gates.PSwap, diff --git a/test/unit_tests/braket/circuits/test_gates.py b/test/unit_tests/braket/circuits/test_gates.py index 05b98ade4..ac317c402 100644 --- a/test/unit_tests/braket/circuits/test_gates.py +++ b/test/unit_tests/braket/circuits/test_gates.py @@ -54,6 +54,7 @@ class TripleAngle: (Gate.Rx, "rx", ir.Rx, [SingleTarget, Angle], {}), (Gate.Ry, "ry", ir.Ry, [SingleTarget, Angle], {}), (Gate.Rz, "rz", ir.Rz, [SingleTarget, Angle], {}), + (Gate.U, "u", None, [SingleTarget, TripleAngle], {}), (Gate.CNot, "cnot", ir.CNot, [SingleTarget, SingleControl], {}), (Gate.CV, "cv", ir.CV, [SingleTarget, SingleControl], {}), (Gate.CCNot, "ccnot", ir.CCNot, [SingleTarget, DoubleControl], {}), @@ -121,6 +122,7 @@ class TripleAngle: Gate.Rx, Gate.Ry, Gate.Rz, + Gate.U, Gate.PhaseShift, Gate.PSwap, Gate.XX, @@ -434,6 +436,18 @@ def test_ir_gate_level(testclass, subroutine_name, irclass, irsubclasses, kwargs OpenQASMSerializationProperties(qubit_reference_type=QubitReferenceType.PHYSICAL), "rz(0.17) $4;", ), + ( + Gate.U(angle_1=0.17, angle_2=3.45, angle_3=5.21), + [4], + OpenQASMSerializationProperties(qubit_reference_type=QubitReferenceType.VIRTUAL), + "U(0.17, 3.45, 5.21) q[4];", + ), + ( + Gate.U(angle_1=0.17, angle_2=3.45, angle_3=5.21), + [4], + OpenQASMSerializationProperties(qubit_reference_type=QubitReferenceType.PHYSICAL), + "U(0.17, 3.45, 5.21) $4;", + ), ( Gate.XX(angle=0.17), [4, 5], @@ -859,6 +873,8 @@ def test_gate_subroutine(testclass, subroutine_name, irclass, irsubclasses, kwar subroutine_input = {"target": multi_targets} if Angle in irsubclasses: subroutine_input.update(angle_valid_input()) + if TripleAngle in irsubclasses: + subroutine_input.update(triple_angle_valid_input()) assert subroutine(**subroutine_input) == Circuit(instruction_list) @@ -925,7 +941,7 @@ def test_large_unitary(): @pytest.mark.parametrize("gate", parameterizable_gates) def test_bind_values(gate): - triple_angled = gate.__name__ in ("MS",) + triple_angled = gate.__name__ in ("MS", "U") num_params = 3 if triple_angled else 1 thetas = [FreeParameter(f"theta_{i}") for i in range(num_params)] mapping = {f"theta_{i}": i for i in range(num_params)} From 9633a2ab62d3d4b3199b07625c1d1e6428498cf5 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 14 Nov 2023 18:02:06 -0500 Subject: [PATCH 02/49] modification according to feedback --- src/braket/circuits/gates.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index 279d1faf2..db7c8e1e6 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -1123,27 +1123,32 @@ def to_matrix(self) -> np.ndarray: Generate parameterized Unitary matrix. https://openqasm.com/language/gates.html#built-in-gates + Unitary matrix: + .. math:: \mathtt{U}(\theta, \phi, \lambda) = \begin{bmatrix} + \cos{(\theta/2)} & -e^{i \lambda} \sin{(\theta/2)} \\ + e^{i \phi} \sin{(\theta/2)} & -e^{i (\phi + \lambda)} \cos{(\theta/2)} \end{bmatrix}. + Returns: np.ndarray: U Matrix """ - theta = self.angle_1 - phi = self.angle_2 - lam = self.angle_3 + _theta = self.angle_1 + _phi = self.angle_2 + _lambda = self.angle_3 return np.array( [ [ - np.cos(theta / 2), - -np.exp(1j * lam) * np.sin(theta / 2), + np.cos(_theta / 2), + -np.exp(1j * _lambda) * np.sin(_theta / 2), ], [ - np.exp(1j * phi) * np.sin(theta / 2), - np.exp(1j * (phi + lam)) * np.cos(theta / 2), + np.exp(1j * _phi) * np.sin(_theta / 2), + np.exp(1j * (_phi + _lambda)) * np.cos(_theta / 2), ], ] ) def adjoint(self) -> list[Gate]: - return [U(self.angle_1, np.pi - self.angle_3, np.pi - self.angle_2)] + return [U(-self.angle_1, -self.angle_3, -self.angle_2)] @staticmethod def fixed_qubit_count() -> int: @@ -1167,7 +1172,7 @@ def u( """Registers this function into the circuit class. Args: - target1 (QubitInput): Target qubit 1 index. + target (QubitSetInput): Target qubit(s) angle_1 (Union[FreeParameterExpression, float]): theta angle in radians. angle_2 (Union[FreeParameterExpression, float]): phi angle in radians. angle_3 (Union[FreeParameterExpression, float]): lambda angle in radians. @@ -1183,10 +1188,10 @@ def u( Default 1. Returns: - Iterable[Instruction]: MS instruction. + Iterable[Instruction]: U instruction. Examples: - >>> circ = Circuit().ms(0, 1, 0.15, 0.34) + >>> circ = Circuit().u(0, 0.15, 0.34, 0.52) """ return [ Instruction( From e68e78cbdef7363c5ad6d848fa166b0ed41cc507 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 14 Nov 2023 18:14:00 -0500 Subject: [PATCH 03/49] fix linters --- src/braket/circuits/gates.py | 79 ++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 3 deletions(-) diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index db7c8e1e6..21a7837e4 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -189,6 +189,78 @@ def i( Gate.register_gate(I) +# class GPhase(AngledGate): +# """Z-axis rotation gate. + +# Args: +# angle (Union[FreeParameterExpression, float]): angle in radians. +# """ + +# def __init__(self, angle: Union[FreeParameterExpression, float]): +# super().__init__( +# angle=angle, +# qubit_count=None, +# ascii_symbols=[angled_ascii_characters("GPhase", angle)], +# ) + +# @property +# def _qasm_name(self) -> str: +# return "gphase" + +# # def to_matrix(self) -> np.ndarray: +# # return np.array( +# # [[np.exp(1j * self.angle / 2), 0], [0, np.exp(1j * self.angle / 2)]], dtype=complex +# # ) + +# def bind_values(self, **kwargs) -> AngledGate: +# return get_angle(self, **kwargs) + +# @staticmethod +# def fixed_qubit_count() -> int: +# return 0 + +# @staticmethod +# @circuit.subroutine(register=True) +# def gphase( +# angle: Union[FreeParameterExpression, float], +# *, +# control: Optional[QubitSetInput] = None, +# control_state: Optional[BasisStateInput] = None, +# power: float = 1, +# ) -> Iterable[Instruction]: +# """Registers this function into the circuit class. + +# Args: +# target (QubitSetInput): Target qubit(s). +# angle (Union[FreeParameterExpression, float]): Angle in radians. +# control (Optional[QubitSetInput]): Control qubit(s). Default None. +# control_state (Optional[BasisStateInput]): Quantum state on which to control the +# operation. Must be a binary sequence of same length as number of qubits in +# `control`. Will be ignored if `control` is not present. May be represented as a +# string, list, or int. For example "0101", [0, 1, 0, 1], 5 all represent +# controlling on qubits 0 and 2 being in the \\|0⟩ state and qubits 1 and 3 being +# in the \\|1⟩ state. Default "1" * len(control). +# power (float): Integer or fractional power to raise the gate to. Negative +# powers will be split into an inverse, accompanied by the positive power. +# Default 1. + +# Returns: +# Iterable[Instruction]: Rx instruction. + +# Examples: +# >>> circ = Circuit().rz(0, 0.15) +# """ +# return [ +# Instruction( +# GPhase(angle), target=[], control=control, control_state=control_state, +# power=power +# ) +# ] + + +# Gate.register_gate(GPhase) + + class X(Gate): """Pauli-X gate.""" @@ -1119,14 +1191,15 @@ def _qasm_name(self) -> str: return "U" def to_matrix(self) -> np.ndarray: - """ + r""" Generate parameterized Unitary matrix. https://openqasm.com/language/gates.html#built-in-gates Unitary matrix: .. math:: \mathtt{U}(\theta, \phi, \lambda) = \begin{bmatrix} \cos{(\theta/2)} & -e^{i \lambda} \sin{(\theta/2)} \\ - e^{i \phi} \sin{(\theta/2)} & -e^{i (\phi + \lambda)} \cos{(\theta/2)} \end{bmatrix}. + e^{i \phi} \sin{(\theta/2)} & -e^{i (\phi + \lambda)} \cos{(\theta/2)} + \end{bmatrix}. Returns: np.ndarray: U Matrix @@ -1160,7 +1233,7 @@ def bind_values(self, **kwargs) -> U: @staticmethod @circuit.subroutine(register=True) def u( - target: QubitInput, + target: QubitSetInput, angle_1: Union[FreeParameterExpression, float], angle_2: Union[FreeParameterExpression, float], angle_3: Union[FreeParameterExpression, float], From 0c2c9e8c9acca6f5a20cbefbcfe08a6617b85780 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 14 Nov 2023 18:20:48 -0500 Subject: [PATCH 04/49] clean commented code --- src/braket/circuits/gates.py | 72 ------------------------------------ 1 file changed, 72 deletions(-) diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index 21a7837e4..6d48d3608 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -189,78 +189,6 @@ def i( Gate.register_gate(I) -# class GPhase(AngledGate): -# """Z-axis rotation gate. - -# Args: -# angle (Union[FreeParameterExpression, float]): angle in radians. -# """ - -# def __init__(self, angle: Union[FreeParameterExpression, float]): -# super().__init__( -# angle=angle, -# qubit_count=None, -# ascii_symbols=[angled_ascii_characters("GPhase", angle)], -# ) - -# @property -# def _qasm_name(self) -> str: -# return "gphase" - -# # def to_matrix(self) -> np.ndarray: -# # return np.array( -# # [[np.exp(1j * self.angle / 2), 0], [0, np.exp(1j * self.angle / 2)]], dtype=complex -# # ) - -# def bind_values(self, **kwargs) -> AngledGate: -# return get_angle(self, **kwargs) - -# @staticmethod -# def fixed_qubit_count() -> int: -# return 0 - -# @staticmethod -# @circuit.subroutine(register=True) -# def gphase( -# angle: Union[FreeParameterExpression, float], -# *, -# control: Optional[QubitSetInput] = None, -# control_state: Optional[BasisStateInput] = None, -# power: float = 1, -# ) -> Iterable[Instruction]: -# """Registers this function into the circuit class. - -# Args: -# target (QubitSetInput): Target qubit(s). -# angle (Union[FreeParameterExpression, float]): Angle in radians. -# control (Optional[QubitSetInput]): Control qubit(s). Default None. -# control_state (Optional[BasisStateInput]): Quantum state on which to control the -# operation. Must be a binary sequence of same length as number of qubits in -# `control`. Will be ignored if `control` is not present. May be represented as a -# string, list, or int. For example "0101", [0, 1, 0, 1], 5 all represent -# controlling on qubits 0 and 2 being in the \\|0⟩ state and qubits 1 and 3 being -# in the \\|1⟩ state. Default "1" * len(control). -# power (float): Integer or fractional power to raise the gate to. Negative -# powers will be split into an inverse, accompanied by the positive power. -# Default 1. - -# Returns: -# Iterable[Instruction]: Rx instruction. - -# Examples: -# >>> circ = Circuit().rz(0, 0.15) -# """ -# return [ -# Instruction( -# GPhase(angle), target=[], control=control, control_state=control_state, -# power=power -# ) -# ] - - -# Gate.register_gate(GPhase) - - class X(Gate): """Pauli-X gate.""" From c3044cbed5ad1535301370a94d1e3dfe4e18d128 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 15 Nov 2023 12:43:50 -0500 Subject: [PATCH 05/49] first version of gphase --- src/braket/circuits/braket_program_context.py | 11 ++- src/braket/circuits/gate.py | 2 +- src/braket/circuits/gates.py | 76 ++++++++++++++++++- src/braket/circuits/translations.py | 1 + test/unit_tests/braket/circuits/test_gates.py | 17 ++++- 5 files changed, 101 insertions(+), 6 deletions(-) diff --git a/src/braket/circuits/braket_program_context.py b/src/braket/circuits/braket_program_context.py index 9d8c69afe..2f3441622 100644 --- a/src/braket/circuits/braket_program_context.py +++ b/src/braket/circuits/braket_program_context.py @@ -56,8 +56,15 @@ def is_builtin_gate(self, name: str) -> bool: user_defined_gate = self.is_user_defined_gate(name) return name in BRAKET_GATES and not user_defined_gate - def add_phase_instruction(self, target: tuple[int], phase_value: int) -> None: - raise NotImplementedError + def add_phase_instruction(self, target: tuple[int], phase_value: float) -> None: + """Add a global phase to the circuit. + + Args: + target (tuple[int]): Unused + phase_value (float): The phase value to be applied + """ + instruction = Instruction(BRAKET_GATES["gphase"](phase_value)) + self._circuit.add_instruction(instruction) def add_gate_instruction( self, gate_name: str, target: tuple[int], *params, ctrl_modifiers: list[int], power: float diff --git a/src/braket/circuits/gate.py b/src/braket/circuits/gate.py index 3c59409e5..2183a2329 100644 --- a/src/braket/circuits/gate.py +++ b/src/braket/circuits/gate.py @@ -198,7 +198,7 @@ def _to_openqasm( return ( f"{inv_prefix}{power_prefix}{control_prefix}" - f"{self._qasm_name}{param_string} {', '.join(qubits)};" + f"{self._qasm_name}{param_string}{','.join([f' {qubit}' for qubit in qubits])};" ) @property diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index 6d48d3608..f5c087309 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -189,6 +189,80 @@ def i( Gate.register_gate(I) +class GPhase(AngledGate): + """Z-axis rotation gate. + + Args: + angle (Union[FreeParameterExpression, float]): angle in radians. + """ + + def __init__(self, angle: Union[FreeParameterExpression, float]): + # Avoid parent constructor because _qubit_count must be zero + self._qubit_count = self.fixed_qubit_count() + self._ascii_symbols = [] + + if angle is None: + raise ValueError("angle must not be None") + if isinstance(angle, FreeParameterExpression): + self._parameters = [angle] + else: + self._parameters = [float(angle)] # explicit casting in case angle is e.g. np.float32 + + @property + def _qasm_name(self) -> str: + return "gphase" + + def adjoint(self) -> list[Gate]: + return [GPhase(-self.angle)] + + def to_matrix(self) -> np.ndarray: + return np.exp(1j * self.angle) * np.eye(1) + + def bind_values(self, **kwargs) -> AngledGate: + return get_angle(self, **kwargs) + + @staticmethod + def fixed_qubit_count() -> int: + return 0 + + @staticmethod + @circuit.subroutine(register=True) + def gphase( + angle: Union[FreeParameterExpression, float], + *, + control: Optional[QubitSetInput] = None, + control_state: Optional[BasisStateInput] = None, + power: float = 1, + ) -> Instruction: + """Registers this function into the circuit class. + + Args: + angle (Union[FreeParameterExpression, float]): Phase in radians. + control (Optional[QubitSetInput]): Control qubit(s). Default None. + control_state (Optional[BasisStateInput]): Quantum state on which to control the + operation. Must be a binary sequence of same length as number of qubits in + `control`. Will be ignored if `control` is not present. May be represented as a + string, list, or int. For example "0101", [0, 1, 0, 1], 5 all represent + controlling on qubits 0 and 2 being in the \\|0⟩ state and qubits 1 and 3 being + in the \\|1⟩ state. Default "1" * len(control). + power (float): Integer or fractional power to raise the gate to. Negative + powers will be split into an inverse, accompanied by the positive power. + Default 1. + + Returns: + Instruction: GPhase instruction d. + + Examples: + >>> circ = Circuit().gphase(0.45) + """ + return [ + Instruction(GPhase(angle), control=control, control_state=control_state, power=power) + ] + + +Gate.register_gate(GPhase) + + class X(Gate): """Pauli-X gate.""" @@ -1155,7 +1229,7 @@ def adjoint(self) -> list[Gate]: def fixed_qubit_count() -> int: return 1 - def bind_values(self, **kwargs) -> U: + def bind_values(self, **kwargs) -> TripleAngledGate: return _get_angles(self, **kwargs) @staticmethod diff --git a/src/braket/circuits/translations.py b/src/braket/circuits/translations.py index 30fc28309..bbb194be3 100644 --- a/src/braket/circuits/translations.py +++ b/src/braket/circuits/translations.py @@ -31,6 +31,7 @@ from braket.ir.jaqcd.program_v1 import Results BRAKET_GATES = { + "gphase": braket_gates.GPhase, "i": braket_gates.I, "h": braket_gates.H, "x": braket_gates.X, diff --git a/test/unit_tests/braket/circuits/test_gates.py b/test/unit_tests/braket/circuits/test_gates.py index ac317c402..56c908550 100644 --- a/test/unit_tests/braket/circuits/test_gates.py +++ b/test/unit_tests/braket/circuits/test_gates.py @@ -35,12 +35,17 @@ from braket.pulse import ArbitraryWaveform, Frame, Port, PulseSequence +class NoTarget: + pass + + class TripleAngle: pass testdata = [ (Gate.H, "h", ir.H, [SingleTarget], {}), + (Gate.GPhase, "gphase", None, [NoTarget, Angle], {}), (Gate.I, "i", ir.I, [SingleTarget], {}), (Gate.X, "x", ir.X, [SingleTarget], {}), (Gate.Y, "y", ir.Y, [SingleTarget], {}), @@ -119,6 +124,7 @@ class TripleAngle: ] parameterizable_gates = [ + Gate.GPhase, Gate.Rx, Gate.Ry, Gate.Rz, @@ -149,6 +155,10 @@ class TripleAngle: ] +def no_target_valid_input(**kwargs): + return {} + + def single_target_valid_input(**kwargs): return {"target": 2} @@ -195,6 +205,7 @@ def two_dimensional_matrix_valid_input(**kwargs): valid_ir_switcher = { + "NoTarget": no_target_valid_input, "SingleTarget": single_target_valid_input, "DoubleTarget": double_target_valid_ir_input, "Angle": angle_valid_input, @@ -236,7 +247,9 @@ def create_valid_target_input(irsubclasses): qubit_set = [] # based on the concept that control goes first in target input for subclass in irsubclasses: - if subclass == SingleTarget: + if subclass == NoTarget: + qubit_set.extend(list(no_target_valid_input().values())) + elif subclass == SingleTarget: qubit_set.extend(list(single_target_valid_input().values())) elif subclass == DoubleTarget: qubit_set.extend(list(double_target_valid_ir_input().values())) @@ -284,7 +297,7 @@ def calculate_qubit_count(irsubclasses): qubit_count += 2 elif subclass == MultiTarget: qubit_count += 3 - elif subclass in (Angle, TwoDimensionalMatrix, TripleAngle): + elif subclass in (NoTarget, Angle, TwoDimensionalMatrix, TripleAngle): pass else: raise ValueError("Invalid subclass") From d6d6886e11c7dff36909cc5eef90c37fbcde02fa Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 15 Nov 2023 16:02:25 -0500 Subject: [PATCH 06/49] handle drawing and control global phase --- src/braket/circuits/ascii_circuit_diagram.py | 5 +-- src/braket/circuits/gates.py | 36 ++++++++++++++++---- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index daef01793..559bb0db6 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -115,8 +115,9 @@ def _ascii_group_items( 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) + if isinstance(item, Instruction) and ( + not isinstance(item.operator, (Gate, Noise, CompilerDirective)) + or item.operator.__class__.__name__ == "GPhase" ): continue diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index f5c087309..fc740c9e7 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -190,7 +190,7 @@ def i( class GPhase(AngledGate): - """Z-axis rotation gate. + """Global phase gate. Args: angle (Union[FreeParameterExpression, float]): angle in radians. @@ -216,7 +216,16 @@ def adjoint(self) -> list[Gate]: return [GPhase(-self.angle)] def to_matrix(self) -> np.ndarray: - return np.exp(1j * self.angle) * np.eye(1) + r""" + Generate a 1x1 matrix containing the global phase. + + Unitary matrix: + .. math:: \mathtt{gphase}(\gamma) = e^(i \gamma) I_m. + + Returns: + np.ndarray: GPhase Matrix + """ + return np.exp(1j * self.angle) * np.eye(1, dtype=complex) def bind_values(self, **kwargs) -> AngledGate: return get_angle(self, **kwargs) @@ -250,14 +259,29 @@ def gphase( Default 1. Returns: - Instruction: GPhase instruction d. + Instruction: GPhase instruction. Examples: >>> circ = Circuit().gphase(0.45) """ - return [ - Instruction(GPhase(angle), control=control, control_state=control_state, power=power) - ] + if control is not None: + if control_state is None: + inv = 0 + elif isinstance(control_state, int): + inv = 1 if control_state % 2 else 0 + control_state = control_state - 1 + elif isinstance(control_state, (str, list)): + inv = 1 - control_state[0] + control_state = control_state[1:] + return PhaseShift.phaseshift( + control[0], + angle * (-1) ** inv, + control=control[1:], + control_state=control_state, + power=power, + ) + + return Instruction(GPhase(angle), control=control, control_state=control_state, power=power) Gate.register_gate(GPhase) From 388367ca076156fd6331c11d884a9c532195cae9 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 17 Nov 2023 23:05:11 +0100 Subject: [PATCH 07/49] Adding a global phase attribute --- src/braket/circuits/ascii_circuit_diagram.py | 23 ++++++++- src/braket/circuits/circuit.py | 8 ++++ src/braket/circuits/gates.py | 49 ++++++++++++++------ src/braket/circuits/moments.py | 3 ++ 4 files changed, 66 insertions(+), 17 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 559bb0db6..45e19eb78 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -23,6 +23,7 @@ from braket.circuits.instruction import Instruction 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 @@ -84,6 +85,9 @@ def build_diagram(circuit: cir.Circuit) -> str: # Time on top and bottom lines.append(lines[0]) + if circuit.global_phase: + lines.append(f"\nGlobal phase: {circuit.global_phase}") + # Additional result types line on bottom if additional_result_types: lines.append(f"\nAdditional result types: {', '.join(additional_result_types)}") @@ -117,7 +121,6 @@ def _ascii_group_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)) - or item.operator.__class__.__name__ == "GPhase" ): continue @@ -259,6 +262,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 + ) + target_and_control = target_qubits.union(control_qubits) qubits = QubitSet(range(min(target_and_control), max(target_and_control) + 1)) @@ -289,7 +296,7 @@ def _ascii_diagram_column( else ascii_symbols[item_qubit_index] ) elif qubit in control_qubits: - symbols[qubit] = "C" + symbols[qubit] = "C" if map_control_qubit_states[qubit] else "N" else: symbols[qubit] = "|" @@ -306,3 +313,15 @@ def _ascii_diagram_column( 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.as_tuple) + } + 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 76e986087..b3d36724c 100644 --- a/src/braket/circuits/circuit.py +++ b/src/braket/circuits/circuit.py @@ -156,6 +156,11 @@ def depth(self) -> int: """int: Get the circuit depth.""" return self._moments.depth + @property + def global_phase(self) -> float: + """int: Get the circuit depth.""" + return self._moments._global_phase + @property def instructions(self) -> list[Instruction]: """Iterable[Instruction]: Get an `iterable` of instructions in the circuit.""" @@ -1196,6 +1201,9 @@ def _to_openqasm( ] ) + if self.global_phase: + ir_instructions.append(f"gphase({self.global_phase});") + if self.result_types: ir_instructions.extend( [ diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index fc740c9e7..3bc3bc3b7 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -30,7 +30,7 @@ angled_ascii_characters, get_angle, ) -from braket.circuits.basis_state import BasisStateInput +from braket.circuits.basis_state import BasisState, BasisStateInput from braket.circuits.free_parameter import FreeParameter from braket.circuits.free_parameter_expression import FreeParameterExpression from braket.circuits.gate import Gate @@ -242,7 +242,7 @@ def gphase( control: Optional[QubitSetInput] = None, control_state: Optional[BasisStateInput] = None, power: float = 1, - ) -> Instruction: + ) -> Instruction | Iterable[Instruction]: """Registers this function into the circuit class. Args: @@ -259,29 +259,48 @@ def gphase( Default 1. Returns: - Instruction: GPhase instruction. + Instruction | Iterable[Instruction]: GPhase instruction. Examples: >>> circ = Circuit().gphase(0.45) """ if control is not None: + control_qubits = QubitSet(control) + if control_state is None: - inv = 0 - elif isinstance(control_state, int): - inv = 1 if control_state % 2 else 0 - control_state = control_state - 1 - elif isinstance(control_state, (str, list)): - inv = 1 - control_state[0] - control_state = control_state[1:] + control_state = len(control_qubits) + control_basis_state = list(BasisState(control_state, len(control_qubits)).as_tuple) + + if not any(control_basis_state): + return [ + X.x(control_qubits[-1]), + PhaseShift.phaseshift( + control_qubits[-1], + angle, + control=control_qubits[:-1], + control_state=control_basis_state[:-1], + power=power, + ), + X.x(control_qubits[-1]), + ] + + highest_control_qubit = max( + [qubit for qubit, state in zip(control_qubits, control_basis_state) if state] + ) + highest_control_qubit_index = control_qubits.index(highest_control_qubit) + + control_qubits.pop(highest_control_qubit_index) + control_basis_state.pop(highest_control_qubit_index) + return PhaseShift.phaseshift( - control[0], - angle * (-1) ** inv, - control=control[1:], - control_state=control_state, + highest_control_qubit, + angle, + control=control_qubits, + control_state=control_basis_state, power=power, ) - return Instruction(GPhase(angle), control=control, control_state=control_state, power=power) + return Instruction(GPhase(angle), power=power) Gate.register_gate(GPhase) diff --git a/src/braket/circuits/moments.py b/src/braket/circuits/moments.py index 7cd8e0a9d..ad0de4e07 100644 --- a/src/braket/circuits/moments.py +++ b/src/braket/circuits/moments.py @@ -106,6 +106,7 @@ def __init__(self, instructions: Iterable[Instruction] | None = None): self._qubits = QubitSet() self._depth = 0 self._time_all_qubits = -1 + self._global_phase = 0 self.add(instructions or []) @@ -181,6 +182,8 @@ def _add(self, instruction: Instruction, noise_index: int = 0) -> None: self._time_all_qubits = time elif isinstance(operator, Noise): self.add_noise(instruction) + elif instruction.target == QubitSet([]): + self._global_phase += operator.angle else: qubit_range = instruction.target.union(instruction.control) time = self._update_qubit_times(qubit_range) From 7a52eb8b6d288190487a29476ffcbd415571b773 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Sun, 19 Nov 2023 00:52:49 +0100 Subject: [PATCH 08/49] add global phase to circuit unitary --- src/braket/circuits/circuit.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/braket/circuits/circuit.py b/src/braket/circuits/circuit.py index b3d36724c..0b6b2caf9 100644 --- a/src/braket/circuits/circuit.py +++ b/src/braket/circuits/circuit.py @@ -1431,7 +1431,9 @@ def to_unitary(self) -> np.ndarray: qubits = self.qubits if not qubits: return np.zeros(0, dtype=complex) - return calculate_unitary_big_endian(self.instructions, qubits) + return calculate_unitary_big_endian(self.instructions, qubits) * np.exp( + 1j * self.global_phase + ) @property def qubits_frozen(self) -> bool: From f35cc38c37f96884f41fc5b4c87017ee77ceb0b6 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Mon, 20 Nov 2023 12:30:01 +0100 Subject: [PATCH 09/49] first draft tests to check coverage --- .../braket/circuits/test_circuit.py | 2 ++ test/unit_tests/braket/circuits/test_gates.py | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/test/unit_tests/braket/circuits/test_circuit.py b/test/unit_tests/braket/circuits/test_circuit.py index 5824967b8..71f84f9b4 100644 --- a/test/unit_tests/braket/circuits/test_circuit.py +++ b/test/unit_tests/braket/circuits/test_circuit.py @@ -2016,6 +2016,8 @@ def test_to_unitary_with_compiler_directives_returns_expected_unitary(): (Circuit().rx(0, 0.15), gates.Rx(0.15).to_matrix()), (Circuit().ry(0, 0.15), gates.Ry(0.15).to_matrix()), (Circuit().rz(0, 0.15), gates.Rz(0.15).to_matrix()), + (Circuit().u(0, 0.15, 0.16, 0.17), gates.U(0.15, 0.16, 0.17).to_matrix()), + (Circuit().gphase(0.15), gates.GPhase(0.15).to_matrix()), (Circuit().phaseshift(0, 0.15), gates.PhaseShift(0.15).to_matrix()), (Circuit().cnot(0, 1), gates.CNot().to_matrix()), (Circuit().cnot(0, 1).add_result_type(ResultType.StateVector()), gates.CNot().to_matrix()), diff --git a/test/unit_tests/braket/circuits/test_gates.py b/test/unit_tests/braket/circuits/test_gates.py index 56c908550..62369c5dd 100644 --- a/test/unit_tests/braket/circuits/test_gates.py +++ b/test/unit_tests/braket/circuits/test_gates.py @@ -891,6 +891,26 @@ def test_gate_subroutine(testclass, subroutine_name, irclass, irsubclasses, kwar assert subroutine(**subroutine_input) == Circuit(instruction_list) +# @pytest.mark.parametrize("testclass,subroutine_name,irclass,irsubclasses,kwargs", testdata) +def test_control_gphase_subroutine(): + subroutine = getattr(Circuit(), "gphase") + assert subroutine(angle=0.123, control=2) == Circuit( + Instruction(**create_valid_instruction_input(Gate.PhaseShift, [SingleTarget, Angle])) + ) + subroutine = getattr(Circuit(), "gphase") + assert subroutine(angle=0.123, control=2, control_state=[0]) == Circuit( + [ + Instruction(operator=Gate.X(), target=2), + Instruction(**create_valid_instruction_input(Gate.PhaseShift, [SingleTarget, Angle])), + Instruction(operator=Gate.X(), target=2), + ] + ) + subroutine = getattr(Circuit(), "gphase") + assert subroutine(angle=0.123, control=[2, 0], control_state=[0, 1]) == Circuit( + Instruction(operator=Gate.PhaseShift(angle=0.123), target=0, control=2, control_state=[0]) + ) + + @pytest.mark.parametrize("testclass,subroutine_name,irclass,irsubclasses,kwargs", testdata) def test_gate_adjoint_expansion_correct(testclass, subroutine_name, irclass, irsubclasses, kwargs): gate = testclass(**create_valid_gate_class_input(irsubclasses, **kwargs)) @@ -1099,6 +1119,13 @@ def test_pulse_gate_to_matrix(): "10", "negctrl @ ctrl @ negctrl @ z q[1], q[2], q[3], q[0];", ), + ( + Gate.GPhase(0.3), + QubitSet([]), + QubitSet([1]), + "1", + "ctrl @ gphase(0.3) q[1];", + ), ), ) def test_gate_control(gate, target, control, control_state, expected_ir): From fa4cb3a24185ef380f1c739a72608a4a7f9fb999 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Mon, 20 Nov 2023 16:13:25 +0100 Subject: [PATCH 10/49] add more tests --- .../braket/circuits/test_angled_gate.py | 2 +- .../circuits/test_ascii_circuit_diagram.py | 14 ++++++++ .../braket/circuits/test_circuit.py | 36 +++++++++++++++++++ test/unit_tests/braket/circuits/test_gates.py | 5 +++ 4 files changed, 56 insertions(+), 1 deletion(-) diff --git a/test/unit_tests/braket/circuits/test_angled_gate.py b/test/unit_tests/braket/circuits/test_angled_gate.py index e76756e29..4e093e5b4 100644 --- a/test/unit_tests/braket/circuits/test_angled_gate.py +++ b/test/unit_tests/braket/circuits/test_angled_gate.py @@ -31,7 +31,7 @@ def test_is_operator(angled_gate): def test_angle_is_none(): - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="angle must not be None"): AngledGate(qubit_count=1, ascii_symbols=["foo"], angle=None) 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 3f3268f83..470ef554c 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -58,6 +58,20 @@ def test_one_gate_one_qubit_rotation_with_parameter(): _assert_correct_diagram(circ, expected) +def test_one_gate_with_global_phase(): + circ = Circuit().x(target=0).gphase(0.15) + expected = ( + "T : |0|", + " ", + "q0 : -X-", + "", + "T : |0|", + "", + "Global phase: 0.15", + ) + _assert_correct_diagram(circ, expected) + + def test_one_gate_one_qubit_rotation_with_unicode(): theta = FreeParameter("\u03B8") circ = Circuit().rx(angle=theta, target=0) diff --git a/test/unit_tests/braket/circuits/test_circuit.py b/test/unit_tests/braket/circuits/test_circuit.py index 71f84f9b4..679306ff8 100644 --- a/test/unit_tests/braket/circuits/test_circuit.py +++ b/test/unit_tests/braket/circuits/test_circuit.py @@ -1718,6 +1718,22 @@ def foo( inputs={}, ), ), + ( + Circuit().gphase(0.15).x(0), + OpenQasmProgram( + source="\n".join( + [ + "OPENQASM 3.0;", + "bit[1] b;", + "qubit[1] q;", + "x q[0];", + "gphase(0.15);", + "b[0] = measure q[0];", + ] + ), + inputs={}, + ), + ), ], ) def test_from_ir(expected_circuit, ir): @@ -3106,3 +3122,23 @@ def test_parametrized_pulse_circuit(user_defined_frame): def test_free_param_float_mix(): Circuit().ms(0, 1, 0.1, FreeParameter("theta")) + + +def test_circuit_with_global_phase(): + circuit = Circuit().gphase(0.15).x(0) + assert circuit.global_phase == 0.15 + + assert circuit.to_ir( + ir_type=IRType.OPENQASM, + serialization_properties=OpenQASMSerializationProperties( + qubit_reference_type=QubitReferenceType.PHYSICAL + ), + ).source == "\n".join( + [ + "OPENQASM 3.0;", + "bit[1] b;", + "x $0;", + "gphase(0.15);", + "b[0] = measure $0;", + ] + ) diff --git a/test/unit_tests/braket/circuits/test_gates.py b/test/unit_tests/braket/circuits/test_gates.py index 62369c5dd..a7ca685d8 100644 --- a/test/unit_tests/braket/circuits/test_gates.py +++ b/test/unit_tests/braket/circuits/test_gates.py @@ -911,6 +911,11 @@ def test_control_gphase_subroutine(): ) +def test_angle_gphase_is_none(): + with pytest.raises(ValueError, match="angle must not be None"): + Gate.GPhase(angle=None) + + @pytest.mark.parametrize("testclass,subroutine_name,irclass,irsubclasses,kwargs", testdata) def test_gate_adjoint_expansion_correct(testclass, subroutine_name, irclass, irsubclasses, kwargs): gate = testclass(**create_valid_gate_class_input(irsubclasses, **kwargs)) From 5ddf96ef8f3a11d42f225999e2375f78c16b4a9b Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Mon, 20 Nov 2023 16:32:54 +0100 Subject: [PATCH 11/49] add test with parametric global phase --- .../circuits/test_ascii_circuit_diagram.py | 17 +++++++++++++++++ 1 file changed, 17 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 470ef554c..a47907924 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -88,6 +88,23 @@ def test_one_gate_one_qubit_rotation_with_unicode(): _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 + 1) + expected = ( + "T : |0|", + " ", + "q0 : -X-", + "", + "T : |0|", + "", + "Global phase: 2*θ + 1", + "", + "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) From 3c0d766bdc6f964874ac7cf2f024b939300b4757 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Mon, 20 Nov 2023 16:42:43 +0100 Subject: [PATCH 12/49] add test for neg control qubit printing --- .../circuits/test_ascii_circuit_diagram.py | 16 ++++++++++++++++ 1 file changed, 16 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 a47907924..911908149 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -218,6 +218,22 @@ def test_connector_across_two_qubits(): _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 : -N-", + " | ", + "q1 : -C-", + " | ", + "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 = ( From 9f8a547bb2899a245a27143b3a6fd1592d5c0b02 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Mon, 20 Nov 2023 16:52:13 +0100 Subject: [PATCH 13/49] clean up --- test/unit_tests/braket/circuits/test_gates.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/unit_tests/braket/circuits/test_gates.py b/test/unit_tests/braket/circuits/test_gates.py index a7ca685d8..509a37792 100644 --- a/test/unit_tests/braket/circuits/test_gates.py +++ b/test/unit_tests/braket/circuits/test_gates.py @@ -891,7 +891,6 @@ def test_gate_subroutine(testclass, subroutine_name, irclass, irsubclasses, kwar assert subroutine(**subroutine_input) == Circuit(instruction_list) -# @pytest.mark.parametrize("testclass,subroutine_name,irclass,irsubclasses,kwargs", testdata) def test_control_gphase_subroutine(): subroutine = getattr(Circuit(), "gphase") assert subroutine(angle=0.123, control=2) == Circuit( From 76989f78499e37e08a4755a130364cbea0b935f9 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 21 Nov 2023 00:09:51 +0100 Subject: [PATCH 14/49] simplify ctrl-gphase transform --- src/braket/circuits/gates.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index 151d96cfb..a11b67bf5 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -310,16 +310,12 @@ def gphase( X.x(control_qubits[-1]), ] - highest_control_qubit = max( - [qubit for qubit, state in zip(control_qubits, control_basis_state) if state] - ) - highest_control_qubit_index = control_qubits.index(highest_control_qubit) - - control_qubits.pop(highest_control_qubit_index) - control_basis_state.pop(highest_control_qubit_index) + rightmost_control_qubit_index = control_basis_state.index(1) + rightmost_control_qubit = control_qubits.pop(rightmost_control_qubit_index) + control_basis_state.pop(rightmost_control_qubit_index) return PhaseShift.phaseshift( - highest_control_qubit, + rightmost_control_qubit, angle, control=control_qubits, control_state=control_basis_state, From f1204d08bf21484e030480d981d2cfeb55e12301 Mon Sep 17 00:00:00 2001 From: Aaron Berdy Date: Mon, 20 Nov 2023 18:37:01 -0800 Subject: [PATCH 15/49] feat: add str, repr and getitem to BasisState --- src/braket/circuits/basis_state.py | 9 ++++ .../braket/circuits/test_basis_state.py | 46 +++++++++++++++++-- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/braket/circuits/basis_state.py b/src/braket/circuits/basis_state.py index 814444e75..825a074fb 100644 --- a/src/braket/circuits/basis_state.py +++ b/src/braket/circuits/basis_state.py @@ -33,6 +33,15 @@ def __iter__(self): def __eq__(self, other): return self.state == other.state + def __str__(self): + return self.as_string + + def __repr__(self): + return f'BasisState("{self.as_string}")' + + def __getitem__(self, item): + return BasisState(self.state[item]) + BasisStateInput = Union[int, list[int], str, BasisState] diff --git a/test/unit_tests/braket/circuits/test_basis_state.py b/test/unit_tests/braket/circuits/test_basis_state.py index 023494fae..3e08e45a6 100644 --- a/test/unit_tests/braket/circuits/test_basis_state.py +++ b/test/unit_tests/braket/circuits/test_basis_state.py @@ -51,6 +51,46 @@ ), ) def test_as_props(basis_state_input, size, as_tuple, as_int, as_string): - assert BasisState(basis_state_input, size).as_tuple == as_tuple - assert BasisState(basis_state_input, size).as_int == as_int - assert BasisState(basis_state_input, size).as_string == as_string + basis_state = BasisState(basis_state_input, size) + assert basis_state.as_tuple == as_tuple + assert basis_state.as_int == as_int + assert basis_state.as_string == as_string == str(basis_state) + + +@pytest.mark.parametrize( + "basis_state_input, index, substate_input", + ( + ( + "1001", + slice(None), + "1001", + ), + ( + "1001", + 3, + "1", + ), + ( + "1010", + slice(None, None, 2), + "11", + ), + ( + "1010", + slice(1, None, 2), + "00", + ), + ( + "1010", + slice(None, -2), + "10", + ), + ( + "1010", + -1, + "0", + ), + ), +) +def test_indexing(basis_state_input, index, substate_input): + assert BasisState(basis_state_input)[index] == BasisState(substate_input) From f5d0511923da821d7d2573e9d48524ed10fee615 Mon Sep 17 00:00:00 2001 From: Aaron Berdy Date: Mon, 20 Nov 2023 18:42:14 -0800 Subject: [PATCH 16/49] add repr coverage --- test/unit_tests/braket/circuits/test_basis_state.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit_tests/braket/circuits/test_basis_state.py b/test/unit_tests/braket/circuits/test_basis_state.py index 3e08e45a6..11707d644 100644 --- a/test/unit_tests/braket/circuits/test_basis_state.py +++ b/test/unit_tests/braket/circuits/test_basis_state.py @@ -55,6 +55,7 @@ def test_as_props(basis_state_input, size, as_tuple, as_int, as_string): assert basis_state.as_tuple == as_tuple assert basis_state.as_int == as_int assert basis_state.as_string == as_string == str(basis_state) + assert repr(basis_state) == f'BasisState("{as_string}")' @pytest.mark.parametrize( From f68b0844fffe71b3868d09505a0fd383aed6c250 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 21 Nov 2023 13:37:05 +0100 Subject: [PATCH 17/49] add index --- src/braket/circuits/basis_state.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/braket/circuits/basis_state.py b/src/braket/circuits/basis_state.py index 825a074fb..96028d1b3 100644 --- a/src/braket/circuits/basis_state.py +++ b/src/braket/circuits/basis_state.py @@ -1,5 +1,5 @@ from functools import singledispatch -from typing import Optional, Union +from typing import Any, Optional, Union import numpy as np @@ -42,6 +42,9 @@ def __repr__(self): def __getitem__(self, item): return BasisState(self.state[item]) + def index(self, value: Any) -> int: + return list(self.state).index(value) + BasisStateInput = Union[int, list[int], str, BasisState] From 5c5672417d23f8fa99e0b8d55e9e319f00e87ebd Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 21 Nov 2023 14:30:32 +0100 Subject: [PATCH 18/49] add pop --- src/braket/circuits/basis_state.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/braket/circuits/basis_state.py b/src/braket/circuits/basis_state.py index 96028d1b3..dc9efa863 100644 --- a/src/braket/circuits/basis_state.py +++ b/src/braket/circuits/basis_state.py @@ -45,6 +45,23 @@ def __getitem__(self, item): def index(self, value: Any) -> int: return list(self.state).index(value) + def pop(self, index: int | None = None) -> int: + """Removes and returns item at index. + + Args: + index (int | None): index of the object to remove (default last). + + Returns: + int: removed item. + """ + if index is None: + item = self.state[-1] + self.state = self.state[:-1] + else: + item = self.state[index] + self.state = self.state[:index] + self.state[index + 1 :] + return item + BasisStateInput = Union[int, list[int], str, BasisState] From dabb79073ea0d00d3f81c4191beae687f0c7b878 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 21 Nov 2023 15:59:53 +0100 Subject: [PATCH 19/49] fix phase target qubit --- src/braket/circuits/ascii_circuit_diagram.py | 2 +- src/braket/circuits/gates.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 45e19eb78..90c2c3c23 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -319,7 +319,7 @@ def _build_map_control_qubits(item: Instruction, control_qubits: QubitSet) -> di 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.as_tuple) + qubit: state for qubit, state in zip(control_qubits, control_state) } else: map_control_qubit_states = {qubit: 1 for qubit in control_qubits} diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index a11b67bf5..923ddb71b 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -295,7 +295,7 @@ def gphase( if control_state is None: control_state = len(control_qubits) - control_basis_state = list(BasisState(control_state, len(control_qubits)).as_tuple) + control_basis_state = BasisState(control_state, len(control_qubits)) if not any(control_basis_state): return [ @@ -310,7 +310,9 @@ def gphase( X.x(control_qubits[-1]), ] - rightmost_control_qubit_index = control_basis_state.index(1) + rightmost_control_qubit_index = ( + len(control_qubits) - control_basis_state[::-1].index(1) - 1 + ) rightmost_control_qubit = control_qubits.pop(rightmost_control_qubit_index) control_basis_state.pop(rightmost_control_qubit_index) From 31a60f8384d9942e6b991730f41d99ccdd1438f5 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 21 Nov 2023 16:06:49 +0100 Subject: [PATCH 20/49] fix typing --- src/braket/circuits/basis_state.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/braket/circuits/basis_state.py b/src/braket/circuits/basis_state.py index dc9efa863..2af4e0c18 100644 --- a/src/braket/circuits/basis_state.py +++ b/src/braket/circuits/basis_state.py @@ -45,11 +45,11 @@ def __getitem__(self, item): def index(self, value: Any) -> int: return list(self.state).index(value) - def pop(self, index: int | None = None) -> int: + def pop(self, index: Optional[int] = None) -> int: """Removes and returns item at index. Args: - index (int | None): index of the object to remove (default last). + index (Optional[int]): index of the object to remove (default last). Returns: int: removed item. From 0b02f14ae1d52f0ed6393e3c446acb024e7cfc28 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 21 Nov 2023 16:16:28 +0100 Subject: [PATCH 21/49] add index and pop tests --- test/unit_tests/braket/circuits/test_basis_state.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/unit_tests/braket/circuits/test_basis_state.py b/test/unit_tests/braket/circuits/test_basis_state.py index 11707d644..7a100f2cb 100644 --- a/test/unit_tests/braket/circuits/test_basis_state.py +++ b/test/unit_tests/braket/circuits/test_basis_state.py @@ -95,3 +95,13 @@ def test_as_props(basis_state_input, size, as_tuple, as_int, as_string): ) def test_indexing(basis_state_input, index, substate_input): assert BasisState(basis_state_input)[index] == BasisState(substate_input) + + +def test_index(): + assert BasisState("1010").index(0) == 1 + + +def test_pop(): + basis_state = BasisState("1010", 4) + assert basis_state.pop(1) == 0 + assert basis_state == BasisState("110", 3) From 0a7985eafcf588f14ca902ffeb886e56c8efc525 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 21 Nov 2023 16:20:55 +0100 Subject: [PATCH 22/49] fix code coverage --- test/unit_tests/braket/circuits/test_basis_state.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/unit_tests/braket/circuits/test_basis_state.py b/test/unit_tests/braket/circuits/test_basis_state.py index 7a100f2cb..4882379d5 100644 --- a/test/unit_tests/braket/circuits/test_basis_state.py +++ b/test/unit_tests/braket/circuits/test_basis_state.py @@ -105,3 +105,6 @@ def test_pop(): basis_state = BasisState("1010", 4) assert basis_state.pop(1) == 0 assert basis_state == BasisState("110", 3) + + assert basis_state.pop() == 0 + assert basis_state == BasisState("11", 2) From c75f331a2b1502204a077c8f5bc6768c74910dbc Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 21 Nov 2023 23:00:48 +0100 Subject: [PATCH 23/49] move unitary matrices --- src/braket/circuits/gates.py | 59 ++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index 923ddb71b..096fedae2 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -216,7 +216,11 @@ def i( class GPhase(AngledGate): - """Global phase gate. + r"""Global phase gate. + + Unitary matrix: + + .. math:: \mathtt{gphase}(\gamma) = e^(i \gamma) I_1. Args: angle (Union[FreeParameterExpression, float]): angle in radians. @@ -242,15 +246,6 @@ def adjoint(self) -> list[Gate]: return [GPhase(-self.angle)] def to_matrix(self) -> np.ndarray: - r""" - Generate a 1x1 matrix containing the global phase. - - Unitary matrix: - .. math:: \mathtt{gphase}(\gamma) = e^(i \gamma) I_m. - - Returns: - np.ndarray: GPhase Matrix - """ return np.exp(1j * self.angle) * np.eye(1, dtype=complex) def bind_values(self, **kwargs) -> AngledGate: @@ -269,7 +264,11 @@ def gphase( control_state: Optional[BasisStateInput] = None, power: float = 1, ) -> Instruction | Iterable[Instruction]: - """Registers this function into the circuit class. + r"""Registers this function into the circuit class. + + Unitary matrix: + + .. math:: \mathtt{gphase}(\gamma) = e^(i \gamma) I_1. Args: angle (Union[FreeParameterExpression, float]): Phase in radians. @@ -294,20 +293,21 @@ def gphase( control_qubits = QubitSet(control) if control_state is None: - control_state = len(control_qubits) + control_state = (1,) * len(control_qubits) control_basis_state = BasisState(control_state, len(control_qubits)) if not any(control_basis_state): + phaseshift_target = control_qubits[-1] return [ - X.x(control_qubits[-1]), + X.x(phaseshift_target), PhaseShift.phaseshift( - control_qubits[-1], + phaseshift_target, angle, control=control_qubits[:-1], control_state=control_basis_state[:-1], power=power, ), - X.x(control_qubits[-1]), + X.x(phaseshift_target), ] rightmost_control_qubit_index = ( @@ -1403,7 +1403,14 @@ def phaseshift( class U(TripleAngledGate): - """Parameterized primitive gate for OpenQASM simulator + r"""Parameterized single-qubit gate. + + Unitary matrix: + + .. math:: \mathtt{U}(\theta, \phi, \lambda) = \begin{bmatrix} + \cos{(\theta/2)} & -e^{i \lambda} \sin{(\theta/2)} \\ + e^{i \phi} \sin{(\theta/2)} & -e^{i (\phi + \lambda)} \cos{(\theta/2)} + \end{bmatrix}. Args: angle_1 (Union[FreeParameterExpression, float]): theta angle in radians. @@ -1430,19 +1437,6 @@ def _qasm_name(self) -> str: return "U" def to_matrix(self) -> np.ndarray: - r""" - Generate parameterized Unitary matrix. - https://openqasm.com/language/gates.html#built-in-gates - - Unitary matrix: - .. math:: \mathtt{U}(\theta, \phi, \lambda) = \begin{bmatrix} - \cos{(\theta/2)} & -e^{i \lambda} \sin{(\theta/2)} \\ - e^{i \phi} \sin{(\theta/2)} & -e^{i (\phi + \lambda)} \cos{(\theta/2)} - \end{bmatrix}. - - Returns: - np.ndarray: U Matrix - """ _theta = self.angle_1 _phi = self.angle_2 _lambda = self.angle_3 @@ -1483,6 +1477,13 @@ def u( ) -> Iterable[Instruction]: """Registers this function into the circuit class. + Unitary matrix: + + .. math:: \mathtt{U}(\theta, \phi, \lambda) = \begin{bmatrix} + \cos{(\theta/2)} & -e^{i \lambda} \sin{(\theta/2)} \\ + e^{i \phi} \sin{(\theta/2)} & -e^{i (\phi + \lambda)} \cos{(\theta/2)} + \end{bmatrix}. + Args: target (QubitSetInput): Target qubit(s) angle_1 (Union[FreeParameterExpression, float]): theta angle in radians. From df1b1a94ece152d0966e8863f9dc81380291f85f Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Thu, 23 Nov 2023 22:15:03 +0100 Subject: [PATCH 24/49] use a subindex in MomentKey --- src/braket/circuits/ascii_circuit_diagram.py | 7 ++++++ src/braket/circuits/circuit.py | 15 +++++++----- src/braket/circuits/gates.py | 22 +++++++++-------- src/braket/circuits/moments.py | 24 ++++++++++++++----- .../braket/circuits/test_circuit.py | 4 ++-- 5 files changed, 48 insertions(+), 24 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 90c2c3c23..2a5b1b9e0 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -124,6 +124,10 @@ def _ascii_group_items( ): continue + # TODO: We should use isinstance(item.operator, GPhase) + if isinstance(item, Instruction) and item.operator.__class__.__name__ == "GPhase": + continue + if (isinstance(item, ResultType) and not item.target) or ( isinstance(item, Instruction) and isinstance(item.operator, CompilerDirective) ): @@ -201,6 +205,9 @@ def _ascii_diagram_column_set( for grouping in groupings ] + if not groupings: + return "" + # Unite column strings lines = column_strs[0].split("\n") for column_str in column_strs[1:]: diff --git a/src/braket/circuits/circuit.py b/src/braket/circuits/circuit.py index 0b6b2caf9..a844ed117 100644 --- a/src/braket/circuits/circuit.py +++ b/src/braket/circuits/circuit.py @@ -26,7 +26,7 @@ from braket.circuits.free_parameter_expression import FreeParameterExpression from braket.circuits.gate import Gate from braket.circuits.instruction import Instruction -from braket.circuits.moments import Moments +from braket.circuits.moments import Moments, MomentType from braket.circuits.noise import Noise from braket.circuits.noise_helpers import ( apply_noise_to_gates, @@ -158,8 +158,14 @@ def depth(self) -> int: @property def global_phase(self) -> float: - """int: Get the circuit depth.""" - return self._moments._global_phase + """float: Get the global phase of the circuit.""" + return sum( + [ + instr.operator.angle + for moment, instr in self._moments.items() + if moment.moment_type == MomentType.GLOBAL_PHASE + ] + ) @property def instructions(self) -> list[Instruction]: @@ -1201,9 +1207,6 @@ def _to_openqasm( ] ) - if self.global_phase: - ir_instructions.append(f"gphase({self.global_phase});") - if self.result_types: ir_instructions.extend( [ diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index 096fedae2..31f9a1de9 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -297,27 +297,25 @@ def gphase( control_basis_state = BasisState(control_state, len(control_qubits)) if not any(control_basis_state): - phaseshift_target = control_qubits[-1] + phaseshift_target = control_qubits[0] return [ X.x(phaseshift_target), PhaseShift.phaseshift( phaseshift_target, angle, - control=control_qubits[:-1], - control_state=control_basis_state[:-1], + control=control_qubits[1:], + control_state=control_basis_state[1:], power=power, ), X.x(phaseshift_target), ] - rightmost_control_qubit_index = ( - len(control_qubits) - control_basis_state[::-1].index(1) - 1 - ) - rightmost_control_qubit = control_qubits.pop(rightmost_control_qubit_index) - control_basis_state.pop(rightmost_control_qubit_index) + leftmost_control_qubit_index = control_basis_state.index(1) + leftmost_control_qubit = control_qubits.pop(leftmost_control_qubit_index) + control_basis_state.pop(leftmost_control_qubit_index) return PhaseShift.phaseshift( - rightmost_control_qubit, + leftmost_control_qubit, angle, control=control_qubits, control_state=control_basis_state, @@ -1437,6 +1435,10 @@ def _qasm_name(self) -> str: return "U" def to_matrix(self) -> np.ndarray: + r"""Returns a matrix representation of this gate. + Returns: + ndarray: The matrix representation of this gate. + """ _theta = self.angle_1 _phi = self.angle_2 _lambda = self.angle_3 @@ -1475,7 +1477,7 @@ def u( control_state: Optional[BasisStateInput] = None, power: float = 1, ) -> Iterable[Instruction]: - """Registers this function into the circuit class. + r"""Registers this function into the circuit class. Unitary matrix: diff --git a/src/braket/circuits/moments.py b/src/braket/circuits/moments.py index ad0de4e07..9bbe657a7 100644 --- a/src/braket/circuits/moments.py +++ b/src/braket/circuits/moments.py @@ -42,6 +42,7 @@ class MomentType(str, Enum): INITIALIZATION_NOISE = "initialization_noise" READOUT_NOISE = "readout_noise" COMPILER_DIRECTIVE = "compiler_directive" + GLOBAL_PHASE = "global_phase" class MomentsKey(NamedTuple): @@ -59,6 +60,7 @@ class MomentsKey(NamedTuple): qubits: QubitSet moment_type: MomentType noise_index: int + subindex: int = 0 class Moments(Mapping[MomentsKey, Instruction]): @@ -106,7 +108,7 @@ def __init__(self, instructions: Iterable[Instruction] | None = None): self._qubits = QubitSet() self._depth = 0 self._time_all_qubits = -1 - self._global_phase = 0 + self._number_gphase_in_current_moment = 0 self.add(instructions or []) @@ -183,7 +185,16 @@ def _add(self, instruction: Instruction, noise_index: int = 0) -> None: elif isinstance(operator, Noise): self.add_noise(instruction) elif instruction.target == QubitSet([]): - self._global_phase += operator.angle + time = self._get_qubit_times(self._max_times.keys()) + 1 + self._number_gphase_in_current_moment += 1 + key = MomentsKey( + time, + QubitSet([]), + MomentType.GLOBAL_PHASE, + 0, + self._number_gphase_in_current_moment, + ) + self._moments[key] = instruction else: qubit_range = instruction.target.union(instruction.control) time = self._update_qubit_times(qubit_range) @@ -191,14 +202,15 @@ def _add(self, instruction: Instruction, noise_index: int = 0) -> None: self._qubits.update(qubit_range) self._depth = max(self._depth, time + 1) + def _get_qubit_times(self, qubits: QubitSet) -> int: + return max([self._max_time_for_qubit(qubit) for qubit in qubits] + [self._time_all_qubits]) + def _update_qubit_times(self, qubits: QubitSet) -> int: - qubit_max_times = [self._max_time_for_qubit(qubit) for qubit in qubits] + [ - self._time_all_qubits - ] - time = max(qubit_max_times) + 1 + time = self._get_qubit_times(qubits) + 1 # Update time for all specified qubits for qubit in qubits: self._max_times[qubit] = time + self._number_gphase_in_current_moment = 0 return time def add_noise( diff --git a/test/unit_tests/braket/circuits/test_circuit.py b/test/unit_tests/braket/circuits/test_circuit.py index 679306ff8..4b1a1f289 100644 --- a/test/unit_tests/braket/circuits/test_circuit.py +++ b/test/unit_tests/braket/circuits/test_circuit.py @@ -1726,8 +1726,8 @@ def foo( "OPENQASM 3.0;", "bit[1] b;", "qubit[1] q;", - "x q[0];", "gphase(0.15);", + "x q[0];", "b[0] = measure q[0];", ] ), @@ -3137,8 +3137,8 @@ def test_circuit_with_global_phase(): [ "OPENQASM 3.0;", "bit[1] b;", - "x $0;", "gphase(0.15);", + "x $0;", "b[0] = measure $0;", ] ) From daa9cd9a96f3f47a412a37818a7f06af68a098d4 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 24 Nov 2023 21:10:42 +0100 Subject: [PATCH 25/49] print global phase integration --- src/braket/circuits/ascii_circuit_diagram.py | 114 +++++++++++++----- .../circuits/test_ascii_circuit_diagram.py | 22 ++-- 2 files changed, 95 insertions(+), 41 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 2a5b1b9e0..09576d44b 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -48,20 +48,16 @@ def build_diagram(circuit: cir.Circuit) -> str: circuit_qubits = circuit.qubits circuit_qubits.sort() - # 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) - 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 = AsciiCircuitDiagram._prepare_y_axis_str(circuit, circuit_qubits) time_slices = circuit.moments.time_slices() column_strs = [] # Moment columns + global_phase = 0 if circuit.global_phase else None for time, instructions in time_slices.items(): - moment_str = AsciiCircuitDiagram._ascii_diagram_column_set( - str(time), circuit_qubits, instructions + moment_str, global_phase = AsciiCircuitDiagram._ascii_diagram_column_set( + str(time), circuit_qubits, instructions, global_phase ) column_strs.append(moment_str) @@ -72,8 +68,8 @@ def build_diagram(circuit: cir.Circuit) -> str: if target_result_types: column_strs.append( AsciiCircuitDiagram._ascii_diagram_column_set( - "Result Types", circuit_qubits, target_result_types - ) + "Result Types", circuit_qubits, target_result_types, global_phase + )[0] ) # Unite strings @@ -101,6 +97,21 @@ def build_diagram(circuit: cir.Circuit) -> str: return "\n".join(lines) + @staticmethod + def _prepare_y_axis_str(circuit: cir.Circuit, circuit_qubits: QubitSet) -> str: + # 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) + + if circuit.global_phase: # FIXME: this won't work if GPHASE(-1), GPHASE(1) + y_axis_str += "{0:{width}} : |\n".format("GP", width=y_axis_width) + + 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 + @staticmethod def _ascii_group_items( circuit_qubits: QubitSet, @@ -126,9 +137,8 @@ def _ascii_group_items( # TODO: We should use isinstance(item.operator, GPhase) if isinstance(item, Instruction) and item.operator.__class__.__name__ == "GPhase": - continue - - if (isinstance(item, ResultType) and not item.target) or ( + qubit_range = QubitSet() + elif (isinstance(item, ResultType) and not item.target) or ( isinstance(item, Instruction) and isinstance(item.operator, CompilerDirective) ): qubit_range = circuit_qubits @@ -146,7 +156,9 @@ def _ascii_group_items( qubits_added = group[0] instr_group = group[1] # Take into account overlapping multi-qubit gates - if not qubits_added.intersection(set(qubit_range)): + if not qubits_added.intersection(set(qubit_range)) or ( + isinstance(item, Instruction) and item.operator.__class__.__name__ == "GPhase" + ): instr_group.append(item) qubits_added.update(qubit_range) found_grouping = True @@ -183,8 +195,11 @@ def _categorize_result_types( @staticmethod def _ascii_diagram_column_set( - col_title: str, circuit_qubits: QubitSet, items: list[Union[Instruction, ResultType]] - ) -> str: + col_title: str, + circuit_qubits: QubitSet, + items: list[Union[Instruction, ResultType]], + global_phase: float | None = None, + ) -> tuple[str, float | None]: """ Return a set of columns in the ASCII string diagram of the circuit for a list of items. @@ -192,21 +207,28 @@ def _ascii_diagram_column_set( 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. + tuple[str, float | None]: A tuple of the ASCII string diagram for the column set + and the integrated global phase. """ # 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]) - for grouping in groupings - ] - - if not groupings: - return "" + column_strs, global_phases = list( + zip( + *[ + AsciiCircuitDiagram._ascii_diagram_column( + circuit_qubits, grouping[1], global_phase + ) + for grouping in groupings + ] + ) + ) + if global_phase is not None: + global_phase = sum(global_phases) # Unite column strings lines = column_strs[0].split("\n") @@ -227,21 +249,25 @@ def _ascii_diagram_column_set( first_line = "{:^{width}}|\n".format(col_title, width=len(lines[0]) - 1) - return first_line + "\n".join(lines) + return first_line + "\n".join(lines), global_phase @staticmethod def _ascii_diagram_column( - circuit_qubits: QubitSet, items: list[Union[Instruction, ResultType]] - ) -> str: + circuit_qubits: QubitSet, + items: list[Union[Instruction, ResultType]], + global_phase: float | None = None, + ) -> tuple[str, float | None]: """ 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. + tuple[str, float | None]: A tuple of the ASCII string diagram for the specified moment + in time for a column and the integrated global phase. """ symbols = {qubit: "-" for qubit in circuit_qubits} margins = {qubit: " " for qubit in circuit_qubits} @@ -263,6 +289,13 @@ def _ascii_diagram_column( 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 item.operator.__class__.__name__ == "GPhase": + target_qubits = circuit_qubits + control_qubits = QubitSet() + target_and_control = QubitSet() + qubits = circuit_qubits + ascii_symbols = symbols + global_phase += item.operator.angle else: if isinstance(item.target, list): target_qubits = reduce(QubitSet.union, map(QubitSet, item.target), QubitSet()) @@ -308,13 +341,32 @@ def _ascii_diagram_column( symbols[qubit] = "|" # Set the margin to be a connector if not on the first qubit - if qubit != min(target_and_control): + if target_and_control and qubit != min(target_and_control): margins[qubit] = "|" - symbols_width = max([len(symbol) for symbol in symbols.values()]) + output = AsciiCircuitDiagram._create_output(symbols, margins, circuit_qubits, global_phase) + return output, global_phase + @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 = "" - for qubit in circuit_qubits: + + if global_phase is not None: + global_phase = ( + round(global_phase, 2) if isinstance(global_phase, float) else global_phase + ) + symbols_width = max([symbols_width, len(str(global_phase))]) + output += "{0:{fill}{align}{width}}|\n".format( + str(global_phase), 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 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 911908149..666f1ecb2 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -61,11 +61,12 @@ def test_one_gate_one_qubit_rotation_with_parameter(): def test_one_gate_with_global_phase(): circ = Circuit().x(target=0).gphase(0.15) expected = ( - "T : |0|", - " ", - "q0 : -X-", + "T : |0| 1 |", + "GP : |0|0.15|", + " ", + "q0 : -X------", "", - "T : |0|", + "T : |0| 1 |", "", "Global phase: 0.15", ) @@ -90,15 +91,16 @@ def test_one_gate_one_qubit_rotation_with_unicode(): def test_one_gate_with_parametric_expression_global_phase_(): theta = FreeParameter("\u03B8") - circ = Circuit().x(target=0).gphase(2 * theta + 1) + circ = Circuit().x(target=0).gphase(2 * theta).x(0).gphase(1) expected = ( - "T : |0|", - " ", - "q0 : -X-", + "T : |0| 1 | 2 |", + "GP : |0|2*θ|2*θ + 1.0|", + " ", + "q0 : -X-X-------------", "", - "T : |0|", + "T : |0| 1 | 2 |", "", - "Global phase: 2*θ + 1", + "Global phase: 2*θ + 1.0", "", "Unassigned parameters: [θ].", ) From 9e5f7dc7a67d26f103b515e44d90e7a05a21e001 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 24 Nov 2023 21:22:56 +0100 Subject: [PATCH 26/49] fix docstrings --- src/braket/circuits/gates.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index 31f9a1de9..b23f6d504 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -264,7 +264,7 @@ def gphase( control_state: Optional[BasisStateInput] = None, power: float = 1, ) -> Instruction | Iterable[Instruction]: - r"""Registers this function into the circuit class. + r"""Global phase gate. Unitary matrix: @@ -1401,7 +1401,7 @@ def phaseshift( class U(TripleAngledGate): - r"""Parameterized single-qubit gate. + r"""Generalized single-qubit rotation gate. Unitary matrix: @@ -1477,7 +1477,7 @@ def u( control_state: Optional[BasisStateInput] = None, power: float = 1, ) -> Iterable[Instruction]: - r"""Registers this function into the circuit class. + r"""Generalized single-qubit rotation gate. Unitary matrix: From aa460640858912af274bba92e7510061b734b98e Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 24 Nov 2023 22:14:58 +0100 Subject: [PATCH 27/49] fix circuits zero total global phase --- src/braket/circuits/ascii_circuit_diagram.py | 25 +++++++++++-------- .../circuits/test_ascii_circuit_diagram.py | 13 ++++++++++ 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 09576d44b..9e66747e1 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -21,6 +21,7 @@ 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 @@ -48,13 +49,12 @@ def build_diagram(circuit: cir.Circuit) -> str: circuit_qubits = circuit.qubits circuit_qubits.sort() - y_axis_str = AsciiCircuitDiagram._prepare_y_axis_str(circuit, circuit_qubits) + y_axis_str, global_phase = AsciiCircuitDiagram._prepare_y_axis_str(circuit, circuit_qubits) time_slices = circuit.moments.time_slices() column_strs = [] # Moment columns - global_phase = 0 if circuit.global_phase else None for time, instructions in time_slices.items(): moment_str, global_phase = AsciiCircuitDiagram._ascii_diagram_column_set( str(time), circuit_qubits, instructions, global_phase @@ -66,11 +66,10 @@ def build_diagram(circuit: cir.Circuit) -> str: circuit.result_types ) if target_result_types: - column_strs.append( - AsciiCircuitDiagram._ascii_diagram_column_set( - "Result Types", circuit_qubits, target_result_types, global_phase - )[0] + result_type_col_str, global_phase = AsciiCircuitDiagram._ascii_diagram_column_set( + "Result Types", circuit_qubits, target_result_types, global_phase ) + column_strs.append(result_type_col_str) # Unite strings lines = y_axis_str.split("\n") @@ -81,8 +80,8 @@ def build_diagram(circuit: cir.Circuit) -> str: # Time on top and bottom lines.append(lines[0]) - if circuit.global_phase: - lines.append(f"\nGlobal phase: {circuit.global_phase}") + if global_phase: + lines.append(f"\nGlobal phase: {global_phase}") # Additional result types line on bottom if additional_result_types: @@ -98,19 +97,23 @@ def build_diagram(circuit: cir.Circuit) -> str: return "\n".join(lines) @staticmethod - def _prepare_y_axis_str(circuit: cir.Circuit, circuit_qubits: QubitSet) -> str: + def _prepare_y_axis_str( + 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) - if circuit.global_phase: # FIXME: this won't work if GPHASE(-1), GPHASE(1) + global_phase = None + if any(m for m in circuit._moments if m.moment_type == MomentType.GLOBAL_PHASE): 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 + return y_axis_str, global_phase @staticmethod def _ascii_group_items( 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 666f1ecb2..e44cb4044 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -73,6 +73,19 @@ def test_one_gate_with_global_phase(): _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.0|", + " ", + "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) From 590d9c81fff80f03c9f123921ccd9057c70ff3bf Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 24 Nov 2023 23:12:42 +0100 Subject: [PATCH 28/49] fix edge cases --- src/braket/circuits/ascii_circuit_diagram.py | 6 ++++-- .../circuits/test_ascii_circuit_diagram.py | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 9e66747e1..8e7e97421 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -43,7 +43,9 @@ def build_diagram(circuit: cir.Circuit) -> str: str: ASCII string circuit diagram. """ - if not circuit.instructions: + if not circuit.instructions or not any( + m for m in circuit._moments if m.moment_type != MomentType.GLOBAL_PHASE + ): return "" circuit_qubits = circuit.qubits @@ -297,7 +299,7 @@ def _ascii_diagram_column( control_qubits = QubitSet() target_and_control = QubitSet() qubits = circuit_qubits - ascii_symbols = symbols + ascii_symbols = "-" * len(circuit_qubits) global_phase += item.operator.angle else: if isinstance(item.target, list): 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 e44cb4044..f00881cc9 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -29,6 +29,10 @@ def test_empty_circuit(): assert AsciiCircuitDiagram.build_diagram(Circuit()) == "" +def test_only_gphase_circuit(): + assert AsciiCircuitDiagram.build_diagram(Circuit().gphase(0.1)) == "" + + def test_one_gate_one_qubit(): circ = Circuit().h(0) expected = ("T : |0|", " ", "q0 : -H-", "", "T : |0|") @@ -73,6 +77,21 @@ def test_one_gate_with_global_phase(): _assert_correct_diagram(circ, expected) +def test_one_gate_other_qubit_with_global_phase(): + circ = Circuit().x(target=1).gphase(0.15) + expected = ( + "T : |0| 1 |", + "GP : |0|0.15|", + " ", + "q1 : -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 = ( From dec59aa629f3da76f6c1988e052d1e0fd3a15590 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Sat, 25 Nov 2023 00:47:59 +0100 Subject: [PATCH 29/49] fix to_unitary --- src/braket/circuits/circuit.py | 4 +--- test/unit_tests/braket/circuits/test_circuit.py | 8 ++++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/braket/circuits/circuit.py b/src/braket/circuits/circuit.py index a844ed117..aeae8585d 100644 --- a/src/braket/circuits/circuit.py +++ b/src/braket/circuits/circuit.py @@ -1434,9 +1434,7 @@ def to_unitary(self) -> np.ndarray: qubits = self.qubits if not qubits: return np.zeros(0, dtype=complex) - return calculate_unitary_big_endian(self.instructions, qubits) * np.exp( - 1j * self.global_phase - ) + return calculate_unitary_big_endian(self.instructions, qubits) @property def qubits_frozen(self) -> bool: diff --git a/test/unit_tests/braket/circuits/test_circuit.py b/test/unit_tests/braket/circuits/test_circuit.py index 4b1a1f289..64e4d7ccf 100644 --- a/test/unit_tests/braket/circuits/test_circuit.py +++ b/test/unit_tests/braket/circuits/test_circuit.py @@ -2013,6 +2013,14 @@ def test_to_unitary_with_compiler_directives_returns_expected_unitary(): ) +def test_to_unitary_with_global_phase(): + circuit = Circuit().x(0) + circuit_unitary = np.array([[0, 1], [1, 0]]) + assert np.allclose(circuit.to_unitary(), circuit_unitary) + circuit = circuit.gphase(np.pi / 2) + assert np.allclose(circuit.to_unitary(), 1j * circuit_unitary) + + @pytest.mark.parametrize( "circuit,expected_unitary", [ From ef91d3d17025d1b4f5099fc278ebfa98d26c6cc0 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Sat, 25 Nov 2023 10:42:53 +0100 Subject: [PATCH 30/49] temporary fix that checks classname --- src/braket/circuits/moments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/braket/circuits/moments.py b/src/braket/circuits/moments.py index 9bbe657a7..ddf608162 100644 --- a/src/braket/circuits/moments.py +++ b/src/braket/circuits/moments.py @@ -184,7 +184,7 @@ def _add(self, instruction: Instruction, noise_index: int = 0) -> None: self._time_all_qubits = time elif isinstance(operator, Noise): self.add_noise(instruction) - elif instruction.target == QubitSet([]): + elif operator.__class__.__name__ == "GPhase": time = self._get_qubit_times(self._max_times.keys()) + 1 self._number_gphase_in_current_moment += 1 key = MomentsKey( From dfdecfe242aa91962336ff394c9e22fdd200a0cf Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Sun, 26 Nov 2023 11:38:27 +0100 Subject: [PATCH 31/49] clean up test conditions --- src/braket/circuits/ascii_circuit_diagram.py | 17 +++++++++++++---- src/braket/circuits/moments.py | 3 ++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 8e7e97421..81a8d1f53 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -140,8 +140,11 @@ def _ascii_group_items( ): continue - # TODO: We should use isinstance(item.operator, GPhase) - if isinstance(item, Instruction) and item.operator.__class__.__name__ == "GPhase": + 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) @@ -162,7 +165,9 @@ def _ascii_group_items( instr_group = group[1] # Take into account overlapping multi-qubit gates if not qubits_added.intersection(set(qubit_range)) or ( - isinstance(item, Instruction) and item.operator.__class__.__name__ == "GPhase" + isinstance(item, Instruction) + and isinstance(item.operator, Gate) + and item.operator.name == "GPhase" ): instr_group.append(item) qubits_added.update(qubit_range) @@ -294,7 +299,11 @@ def _ascii_diagram_column( 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 item.operator.__class__.__name__ == "GPhase": + 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() diff --git a/src/braket/circuits/moments.py b/src/braket/circuits/moments.py index ddf608162..6e87db78d 100644 --- a/src/braket/circuits/moments.py +++ b/src/braket/circuits/moments.py @@ -19,6 +19,7 @@ from typing import Any, NamedTuple, Union from braket.circuits.compiler_directive import CompilerDirective +from braket.circuits.gate import Gate from braket.circuits.instruction import Instruction from braket.circuits.noise import Noise from braket.registers.qubit import Qubit @@ -184,7 +185,7 @@ def _add(self, instruction: Instruction, noise_index: int = 0) -> None: self._time_all_qubits = time elif isinstance(operator, Noise): self.add_noise(instruction) - elif operator.__class__.__name__ == "GPhase": + elif isinstance(operator, Gate) and operator.name == "GPhase": time = self._get_qubit_times(self._max_times.keys()) + 1 self._number_gphase_in_current_moment += 1 key = MomentsKey( From e5eaa212e1258e7666a436705d42e34108476aa6 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 28 Nov 2023 10:38:21 +0100 Subject: [PATCH 32/49] change logic according to feedback --- src/braket/circuits/basis_state.py | 23 ++-------- src/braket/circuits/gates.py | 42 ++++++++----------- .../braket/circuits/test_basis_state.py | 13 ------ 3 files changed, 21 insertions(+), 57 deletions(-) diff --git a/src/braket/circuits/basis_state.py b/src/braket/circuits/basis_state.py index 2af4e0c18..43ffd7ad0 100644 --- a/src/braket/circuits/basis_state.py +++ b/src/braket/circuits/basis_state.py @@ -1,5 +1,5 @@ from functools import singledispatch -from typing import Any, Optional, Union +from typing import Optional, Union import numpy as np @@ -42,25 +42,8 @@ def __repr__(self): def __getitem__(self, item): return BasisState(self.state[item]) - def index(self, value: Any) -> int: - return list(self.state).index(value) - - def pop(self, index: Optional[int] = None) -> int: - """Removes and returns item at index. - - Args: - index (Optional[int]): index of the object to remove (default last). - - Returns: - int: removed item. - """ - if index is None: - item = self.state[-1] - self.state = self.state[:-1] - else: - item = self.state[index] - self.state = self.state[:index] + self.state[index + 1 :] - return item + def __bool__(self): + return any(self.state) BasisStateInput = Union[int, list[int], str, BasisState] diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index b23f6d504..a1c881395 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -292,34 +292,28 @@ def gphase( if control is not None: control_qubits = QubitSet(control) - if control_state is None: - control_state = (1,) * len(control_qubits) - control_basis_state = BasisState(control_state, len(control_qubits)) + control_basis_state = ( + BasisState(control_state, len(control_qubits)) + if control_state is not None + else BasisState((1,) * len(control_qubits), len(control_qubits)) + ) - if not any(control_basis_state): - phaseshift_target = control_qubits[0] - return [ + phaseshift_target = control_qubits[-1] + phaseshift_instruction = PhaseShift.phaseshift( + phaseshift_target, + angle, + control=control_qubits[:-1], + control_state=control_basis_state[:-1], + power=power, + ) + return ( + phaseshift_instruction + if control_basis_state[-1] + else [ X.x(phaseshift_target), - PhaseShift.phaseshift( - phaseshift_target, - angle, - control=control_qubits[1:], - control_state=control_basis_state[1:], - power=power, - ), + phaseshift_instruction, X.x(phaseshift_target), ] - - leftmost_control_qubit_index = control_basis_state.index(1) - leftmost_control_qubit = control_qubits.pop(leftmost_control_qubit_index) - control_basis_state.pop(leftmost_control_qubit_index) - - return PhaseShift.phaseshift( - leftmost_control_qubit, - angle, - control=control_qubits, - control_state=control_basis_state, - power=power, ) return Instruction(GPhase(angle), power=power) diff --git a/test/unit_tests/braket/circuits/test_basis_state.py b/test/unit_tests/braket/circuits/test_basis_state.py index 4882379d5..11707d644 100644 --- a/test/unit_tests/braket/circuits/test_basis_state.py +++ b/test/unit_tests/braket/circuits/test_basis_state.py @@ -95,16 +95,3 @@ def test_as_props(basis_state_input, size, as_tuple, as_int, as_string): ) def test_indexing(basis_state_input, index, substate_input): assert BasisState(basis_state_input)[index] == BasisState(substate_input) - - -def test_index(): - assert BasisState("1010").index(0) == 1 - - -def test_pop(): - basis_state = BasisState("1010", 4) - assert basis_state.pop(1) == 0 - assert basis_state == BasisState("110", 3) - - assert basis_state.pop() == 0 - assert basis_state == BasisState("11", 2) From b8f0f33f01b7ec1b2bf0b99e547a14ab2a99473e Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 28 Nov 2023 11:24:15 +0100 Subject: [PATCH 33/49] update docstring --- src/braket/circuits/gates.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index a1c881395..29d223c26 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -266,6 +266,12 @@ def gphase( ) -> Instruction | Iterable[Instruction]: r"""Global phase gate. + If the gate is applied with control/negative control modifiers, it is translated in an + equivalent gate using the following definition: `phaseshift(λ) = ctrl @ gphase(λ)`. + The rightmost control qubit is used for the translation. If the polarity of the rightmost + control modifier is negative, the following identity is used: + `negctrl @ gphase(λ) q = x q; ctrl @ gphase(λ) q; x q`. + Unitary matrix: .. math:: \mathtt{gphase}(\gamma) = e^(i \gamma) I_1. From 244d708e57e7a70b7f00b75250b4e73237196831 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 28 Nov 2023 15:01:33 +0100 Subject: [PATCH 34/49] clean tests --- test/unit_tests/braket/circuits/test_gates.py | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/test/unit_tests/braket/circuits/test_gates.py b/test/unit_tests/braket/circuits/test_gates.py index 509a37792..c13f9c757 100644 --- a/test/unit_tests/braket/circuits/test_gates.py +++ b/test/unit_tests/braket/circuits/test_gates.py @@ -891,22 +891,38 @@ def test_gate_subroutine(testclass, subroutine_name, irclass, irsubclasses, kwar assert subroutine(**subroutine_input) == Circuit(instruction_list) -def test_control_gphase_subroutine(): - subroutine = getattr(Circuit(), "gphase") - assert subroutine(angle=0.123, control=2) == Circuit( - Instruction(**create_valid_instruction_input(Gate.PhaseShift, [SingleTarget, Angle])) - ) - subroutine = getattr(Circuit(), "gphase") - assert subroutine(angle=0.123, control=2, control_state=[0]) == Circuit( - [ - Instruction(operator=Gate.X(), target=2), +@pytest.mark.parametrize( + "control, control_state, instruction_set", + [ + ( + 2, + None, Instruction(**create_valid_instruction_input(Gate.PhaseShift, [SingleTarget, Angle])), - Instruction(operator=Gate.X(), target=2), - ] - ) + ), + ( + 2, + [0], + [ + Instruction(operator=Gate.X(), target=2), + Instruction( + **create_valid_instruction_input(Gate.PhaseShift, [SingleTarget, Angle]) + ), + Instruction(operator=Gate.X(), target=2), + ], + ), + ( + [2, 0], + [0, 1], + Instruction( + operator=Gate.PhaseShift(angle=0.123), target=0, control=2, control_state=[0] + ), + ), + ], +) +def test_control_gphase_subroutine(control, control_state, instruction_set): subroutine = getattr(Circuit(), "gphase") - assert subroutine(angle=0.123, control=[2, 0], control_state=[0, 1]) == Circuit( - Instruction(operator=Gate.PhaseShift(angle=0.123), target=0, control=2, control_state=[0]) + assert subroutine(angle=0.123, control=control, control_state=control_state) == Circuit( + instruction_set ) From c185e410529db252993c6437fb199ad522b17010 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 28 Nov 2023 15:55:45 +0100 Subject: [PATCH 35/49] update tests --- test/unit_tests/braket/circuits/test_gates.py | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/test/unit_tests/braket/circuits/test_gates.py b/test/unit_tests/braket/circuits/test_gates.py index c13f9c757..fc8fe7787 100644 --- a/test/unit_tests/braket/circuits/test_gates.py +++ b/test/unit_tests/braket/circuits/test_gates.py @@ -43,6 +43,10 @@ class TripleAngle: pass +class SingleNegControlModifier: + pass + + testdata = [ (Gate.H, "h", ir.H, [SingleTarget], {}), (Gate.GPhase, "gphase", None, [NoTarget, Angle], {}), @@ -183,6 +187,10 @@ def single_control_valid_input(**kwargs): return {"control": 0} +def single_neg_control_valid_input(**kwargs): + return {"control": [0], "control_state": [0]} + + def double_control_valid_ir_input(**kwargs): return {"controls": [0, 1]} @@ -211,6 +219,7 @@ def two_dimensional_matrix_valid_input(**kwargs): "Angle": angle_valid_input, "TripleAngle": triple_angle_valid_input, "SingleControl": single_control_valid_input, + "SingleNegControlModifier": single_neg_control_valid_input, "DoubleControl": double_control_valid_ir_input, "MultiTarget": multi_target_valid_input, "TwoDimensionalMatrix": two_dimensional_matrix_valid_ir_input, @@ -245,6 +254,8 @@ def create_valid_subroutine_input(irsubclasses, **kwargs): def create_valid_target_input(irsubclasses): input = {} qubit_set = [] + control_qubit_set = [] + control_state = None # based on the concept that control goes first in target input for subclass in irsubclasses: if subclass == NoTarget: @@ -257,6 +268,9 @@ def create_valid_target_input(irsubclasses): qubit_set.extend(list(multi_target_valid_input().values())) elif subclass == SingleControl: qubit_set = list(single_control_valid_input().values()) + qubit_set + elif subclass == SingleNegControlModifier: + control_qubit_set = list(single_neg_control_valid_input()["control"]) + control_state = list(single_neg_control_valid_input()["control_state"]) elif subclass == DoubleControl: qubit_set = list(double_control_valid_ir_input().values()) + qubit_set elif subclass in (Angle, TwoDimensionalMatrix, TripleAngle): @@ -264,6 +278,8 @@ def create_valid_target_input(irsubclasses): else: raise ValueError("Invalid subclass") input["target"] = QubitSet(qubit_set) + input["control"] = QubitSet(control_qubit_set) + input["control_state"] = control_state return input @@ -911,10 +927,12 @@ def test_gate_subroutine(testclass, subroutine_name, irclass, irsubclasses, kwar ], ), ( - [2, 0], + [0, 2], [0, 1], Instruction( - operator=Gate.PhaseShift(angle=0.123), target=0, control=2, control_state=[0] + **create_valid_instruction_input( + Gate.PhaseShift, [SingleTarget, SingleNegControlModifier, Angle] + ) ), ), ], From 4efb8bc53ba1d6bb42ba3df395a71ec324f7ea0d Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Thu, 30 Nov 2023 23:30:16 +0100 Subject: [PATCH 36/49] replace control symbols --- src/braket/circuits/angled_gate.py | 6 +- src/braket/circuits/ascii_circuit_diagram.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 | 96 +++++++++---------- 6 files changed, 67 insertions(+), 67 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 81a8d1f53..68b99f414 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -43,8 +43,8 @@ def build_diagram(circuit: cir.Circuit) -> str: str: ASCII string circuit diagram. """ - if not circuit.instructions or not any( - m for m in circuit._moments if m.moment_type != MomentType.GLOBAL_PHASE + if not circuit.instructions or all( + m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments ): return "" @@ -340,7 +340,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 "" ) @@ -350,7 +350,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 29d223c26..5ecd8ee29 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -1540,7 +1540,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: @@ -2024,7 +2024,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 @@ -2109,7 +2109,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 @@ -2194,7 +2194,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 @@ -2279,7 +2279,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 @@ -2358,7 +2358,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: @@ -2435,7 +2435,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: @@ -2512,7 +2512,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: @@ -3009,7 +3009,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: @@ -3117,7 +3117,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 f00881cc9..8a42bcfbf 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -199,33 +199,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--", "", @@ -241,7 +241,7 @@ def test_connector_across_two_qubits(): " ", "q2 : -H---", " ", - "q3 : -C-H-", + "q3 : -⏺-H-", " | ", "q4 : -X-H-", " ", @@ -257,9 +257,9 @@ def test_neg_control_qubits(): expected = ( "T : |0|", " ", - "q0 : -N-", + "q0 : -○-", " | ", - "q1 : -C-", + "q1 : -⏺-", " | ", "q2 : -X-", "", @@ -275,9 +275,9 @@ def test_connector_across_three_qubits(): " ", "q2 : -H---", " ", - "q3 : -C-H-", + "q3 : -⏺-H-", " | ", - "q4 : -C-H-", + "q4 : -⏺-H-", " | ", "q5 : -X-H-", "", @@ -291,9 +291,9 @@ def test_overlapping_qubits(): expected = ( "T : | 0 |1|", " ", - "q0 : -C---H-", + "q0 : -⏺---H-", " | ", - "q1 : -|-C---", + "q1 : -|-⏺---", " | | ", "q2 : -X-|---", " | ", @@ -311,7 +311,7 @@ def test_overlapping_qubits_angled_gates(): " ", "q0 : -ZZ(0.15)---H-", " | ", - "q1 : -|--------C---", + "q1 : -|--------⏺---", " | | ", "q2 : -ZZ(0.15)-|---", " | ", @@ -329,7 +329,7 @@ def test_connector_across_gt_two_qubits(): " ", "q2 : -H-----", " ", - "q3 : ---C---", + "q3 : ---⏺---", " | ", "q4 : -H-|-H-", " | ", @@ -345,7 +345,7 @@ def test_connector_across_non_used_qubits(): expected = ( "T : | 0 |1|", " ", - "q3 : ---C---", + "q3 : ---⏺---", " | ", "q4 : -H-|-H-", " | ", @@ -399,7 +399,7 @@ def test_verbatim_2q_no_preceding(): expected = ( "T : | 0 |1|2| 3 |", " ", - "q0 : -StartVerbatim-H-C-EndVerbatim-", + "q0 : -StartVerbatim-H-⏺-EndVerbatim-", " | | | ", "q1 : -*************---X-***********-", "", @@ -413,7 +413,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-***********-", "", @@ -427,7 +427,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-***********---", "", @@ -441,9 +441,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-***********-", "", @@ -457,9 +457,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-***********-", "", @@ -473,9 +473,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-***********---", "", @@ -493,7 +493,7 @@ def test_verbatim_different_qubits(): " | | ", "q1 : -H-|---------------|-------------", " | | ", - "q3 : ---|---------------|-----------C-", + "q3 : ---|---------------|-----------⏺-", " | | | ", "q4 : ---*************---***********-X-", "", @@ -507,11 +507,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-", "", @@ -535,7 +535,7 @@ def to_ir(self, target): " ", "q0 : -H---", " ", - "q1 : -H-C-", + "q1 : -H-⏺-", " | ", "q2 : ---X-", "", @@ -611,9 +611,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)-", " | | ", @@ -639,9 +639,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-|--------------------------", " | ", @@ -674,9 +674,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)-", " | ", @@ -826,7 +826,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)-", " | ", @@ -846,7 +846,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): @@ -860,9 +860,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 ccb81fa641fea45c7b3b8b2a597e7c4804e60b25 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 1 Dec 2023 00:33:36 +0100 Subject: [PATCH 37/49] use box drawing characters --- src/braket/circuits/ascii_circuit_diagram.py | 26 +- .../circuits/test_ascii_circuit_diagram.py | 564 +++++++++--------- 2 files changed, 295 insertions(+), 295 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 68b99f414..1e12d7839 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -104,16 +104,16 @@ def _prepare_y_axis_str( ) -> 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 for m in circuit._moments if m.moment_type == MomentType.GLOBAL_PHASE): - 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 @@ -252,12 +252,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), global_phase @@ -279,7 +279,7 @@ def _ascii_diagram_column( tuple[str, float | None]: A tuple of the ASCII string diagram for the specified moment in time for a column and the integrated global phase. """ - 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: @@ -297,7 +297,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) @@ -308,7 +308,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) global_phase += item.operator.angle else: if isinstance(item.target, list): @@ -352,11 +352,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, global_phase @@ -376,14 +376,14 @@ def _create_output( round(global_phase, 2) if isinstance(global_phase, float) else global_phase ) symbols_width = max([symbols_width, len(str(global_phase))]) - output += "{0:{fill}{align}{width}}|\n".format( + output += "{0:{fill}{align}{width}}│\n".format( str(global_phase), 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 + 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 8a42bcfbf..742a2f6ca 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -35,14 +35,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) @@ -51,11 +51,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].", ) @@ -65,12 +65,12 @@ def test_one_gate_one_qubit_rotation_with_parameter(): def test_one_gate_with_global_phase(): circ = Circuit().x(target=0).gphase(0.15) expected = ( - "T : |0| 1 |", - "GP : |0|0.15|", + "T : │0│ 1 │", + "GP : │0│0.15│", " ", - "q0 : -X------", + "q0 : ─X──────", "", - "T : |0| 1 |", + "T : │0│ 1 │", "", "Global phase: 0.15", ) @@ -80,12 +80,12 @@ def test_one_gate_with_global_phase(): def test_one_gate_other_qubit_with_global_phase(): circ = Circuit().x(target=1).gphase(0.15) expected = ( - "T : |0| 1 |", - "GP : |0|0.15|", + "T : │0│ 1 │", + "GP : │0│0.15│", " ", - "q1 : -X------", + "q1 : ─X──────", "", - "T : |0| 1 |", + "T : │0│ 1 │", "", "Global phase: 0.15", ) @@ -95,12 +95,12 @@ def test_one_gate_other_qubit_with_global_phase(): 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.0|", + "T : │ 0 │ 1 │", + "GP : │-0.15│0.0│", " ", - "q0 : -X---------", + "q0 : ─X─────────", "", - "T : | 0 | 1 |", + "T : │ 0 │ 1 │", ) _assert_correct_diagram(circ, expected) @@ -110,11 +110,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: [θ].", ) @@ -125,12 +125,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", "", @@ -145,11 +145,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) @@ -157,13 +157,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) @@ -178,13 +178,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) @@ -197,39 +197,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) @@ -237,17 +237,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) @@ -255,15 +255,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) @@ -271,17 +271,17 @@ def test_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) @@ -289,17 +289,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) @@ -307,17 +307,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) @@ -325,17 +325,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) @@ -343,17 +343,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) @@ -361,11 +361,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) @@ -373,11 +373,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) @@ -385,11 +385,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) @@ -397,13 +397,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) @@ -411,13 +411,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) @@ -425,13 +425,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) @@ -439,15 +439,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) @@ -455,15 +455,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) @@ -471,15 +471,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) @@ -487,17 +487,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) @@ -505,17 +505,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) @@ -531,15 +531,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) @@ -547,13 +547,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) @@ -567,15 +567,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) @@ -583,15 +583,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)", ) @@ -609,17 +609,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) @@ -637,17 +637,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", ) @@ -672,17 +672,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) @@ -690,13 +690,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) @@ -704,15 +704,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) @@ -720,13 +720,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) @@ -737,13 +737,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].", ) @@ -757,11 +757,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) @@ -773,13 +773,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) @@ -801,13 +801,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) @@ -824,15 +824,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].", ) @@ -858,14 +858,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 4aa754508bb5dd8c78e6358100387c7e1e754c08 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 1 Dec 2023 10:19:16 +0100 Subject: [PATCH 38/49] Revert "use box drawing characters" This reverts commit ccb81fa641fea45c7b3b8b2a597e7c4804e60b25. --- src/braket/circuits/ascii_circuit_diagram.py | 26 +- .../circuits/test_ascii_circuit_diagram.py | 564 +++++++++--------- 2 files changed, 295 insertions(+), 295 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 1e12d7839..68b99f414 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -104,16 +104,16 @@ def _prepare_y_axis_str( ) -> 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 for m in circuit._moments if m.moment_type == MomentType.GLOBAL_PHASE): - 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 @@ -252,12 +252,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), global_phase @@ -279,7 +279,7 @@ def _ascii_diagram_column( tuple[str, float | None]: A tuple of the ASCII string diagram for the specified moment in time for a column and the integrated global phase. """ - 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: @@ -297,7 +297,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) @@ -308,7 +308,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) global_phase += item.operator.angle else: if isinstance(item.target, list): @@ -352,11 +352,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, global_phase @@ -376,14 +376,14 @@ def _create_output( round(global_phase, 2) if isinstance(global_phase, float) else global_phase ) symbols_width = max([symbols_width, len(str(global_phase))]) - output += "{0:{fill}{align}{width}}│\n".format( + output += "{0:{fill}{align}{width}}|\n".format( str(global_phase), 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 + 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 742a2f6ca..8a42bcfbf 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -35,14 +35,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) @@ -51,11 +51,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].", ) @@ -65,12 +65,12 @@ def test_one_gate_one_qubit_rotation_with_parameter(): def test_one_gate_with_global_phase(): circ = Circuit().x(target=0).gphase(0.15) expected = ( - "T : │0│ 1 │", - "GP : │0│0.15│", + "T : |0| 1 |", + "GP : |0|0.15|", " ", - "q0 : ─X──────", + "q0 : -X------", "", - "T : │0│ 1 │", + "T : |0| 1 |", "", "Global phase: 0.15", ) @@ -80,12 +80,12 @@ def test_one_gate_with_global_phase(): def test_one_gate_other_qubit_with_global_phase(): circ = Circuit().x(target=1).gphase(0.15) expected = ( - "T : │0│ 1 │", - "GP : │0│0.15│", + "T : |0| 1 |", + "GP : |0|0.15|", " ", - "q1 : ─X──────", + "q1 : -X------", "", - "T : │0│ 1 │", + "T : |0| 1 |", "", "Global phase: 0.15", ) @@ -95,12 +95,12 @@ def test_one_gate_other_qubit_with_global_phase(): 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.0│", + "T : | 0 | 1 |", + "GP : |-0.15|0.0|", " ", - "q0 : ─X─────────", + "q0 : -X---------", "", - "T : │ 0 │ 1 │", + "T : | 0 | 1 |", ) _assert_correct_diagram(circ, expected) @@ -110,11 +110,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: [θ].", ) @@ -125,12 +125,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", "", @@ -145,11 +145,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) @@ -157,13 +157,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) @@ -178,13 +178,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) @@ -197,39 +197,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) @@ -237,17 +237,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) @@ -255,15 +255,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) @@ -271,17 +271,17 @@ def test_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) @@ -289,17 +289,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) @@ -307,17 +307,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) @@ -325,17 +325,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) @@ -343,17 +343,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) @@ -361,11 +361,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) @@ -373,11 +373,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) @@ -385,11 +385,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) @@ -397,13 +397,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) @@ -411,13 +411,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) @@ -425,13 +425,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) @@ -439,15 +439,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) @@ -455,15 +455,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) @@ -471,15 +471,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) @@ -487,17 +487,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) @@ -505,17 +505,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) @@ -531,15 +531,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) @@ -547,13 +547,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) @@ -567,15 +567,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) @@ -583,15 +583,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)", ) @@ -609,17 +609,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) @@ -637,17 +637,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", ) @@ -672,17 +672,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) @@ -690,13 +690,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) @@ -704,15 +704,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) @@ -720,13 +720,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) @@ -737,13 +737,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].", ) @@ -757,11 +757,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) @@ -773,13 +773,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) @@ -801,13 +801,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) @@ -824,15 +824,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].", ) @@ -858,14 +858,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 c2291f8e1477126c4e91d20fe30e06bfb4791a83 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 1 Dec 2023 10:19:51 +0100 Subject: [PATCH 39/49] Revert "replace control symbols" This reverts commit 4efb8bc53ba1d6bb42ba3df395a71ec324f7ea0d. --- src/braket/circuits/angled_gate.py | 6 +- src/braket/circuits/ascii_circuit_diagram.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 | 96 +++++++++---------- 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..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 68b99f414..81a8d1f53 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -43,8 +43,8 @@ def build_diagram(circuit: cir.Circuit) -> str: str: ASCII string circuit diagram. """ - if not circuit.instructions or all( - m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments + if not circuit.instructions or not any( + m for m in circuit._moments if m.moment_type != MomentType.GLOBAL_PHASE ): return "" @@ -340,7 +340,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 "" ) @@ -350,7 +350,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/gate.py b/src/braket/circuits/gate.py index 5f5f4d609..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 5ecd8ee29..29d223c26 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -1540,7 +1540,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: @@ -2024,7 +2024,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 @@ -2109,7 +2109,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 @@ -2194,7 +2194,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 @@ -2279,7 +2279,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 @@ -2358,7 +2358,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: @@ -2435,7 +2435,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: @@ -2512,7 +2512,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: @@ -3009,7 +3009,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: @@ -3117,7 +3117,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 878bd093d..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 8a42bcfbf..f00881cc9 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -199,33 +199,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--", "", @@ -241,7 +241,7 @@ def test_connector_across_two_qubits(): " ", "q2 : -H---", " ", - "q3 : -⏺-H-", + "q3 : -C-H-", " | ", "q4 : -X-H-", " ", @@ -257,9 +257,9 @@ def test_neg_control_qubits(): expected = ( "T : |0|", " ", - "q0 : -○-", + "q0 : -N-", " | ", - "q1 : -⏺-", + "q1 : -C-", " | ", "q2 : -X-", "", @@ -275,9 +275,9 @@ def test_connector_across_three_qubits(): " ", "q2 : -H---", " ", - "q3 : -⏺-H-", + "q3 : -C-H-", " | ", - "q4 : -⏺-H-", + "q4 : -C-H-", " | ", "q5 : -X-H-", "", @@ -291,9 +291,9 @@ def test_overlapping_qubits(): expected = ( "T : | 0 |1|", " ", - "q0 : -⏺---H-", + "q0 : -C---H-", " | ", - "q1 : -|-⏺---", + "q1 : -|-C---", " | | ", "q2 : -X-|---", " | ", @@ -311,7 +311,7 @@ def test_overlapping_qubits_angled_gates(): " ", "q0 : -ZZ(0.15)---H-", " | ", - "q1 : -|--------⏺---", + "q1 : -|--------C---", " | | ", "q2 : -ZZ(0.15)-|---", " | ", @@ -329,7 +329,7 @@ def test_connector_across_gt_two_qubits(): " ", "q2 : -H-----", " ", - "q3 : ---⏺---", + "q3 : ---C---", " | ", "q4 : -H-|-H-", " | ", @@ -345,7 +345,7 @@ def test_connector_across_non_used_qubits(): expected = ( "T : | 0 |1|", " ", - "q3 : ---⏺---", + "q3 : ---C---", " | ", "q4 : -H-|-H-", " | ", @@ -399,7 +399,7 @@ def test_verbatim_2q_no_preceding(): expected = ( "T : | 0 |1|2| 3 |", " ", - "q0 : -StartVerbatim-H-⏺-EndVerbatim-", + "q0 : -StartVerbatim-H-C-EndVerbatim-", " | | | ", "q1 : -*************---X-***********-", "", @@ -413,7 +413,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-***********-", "", @@ -427,7 +427,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-***********---", "", @@ -441,9 +441,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-***********-", "", @@ -457,9 +457,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-***********-", "", @@ -473,9 +473,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-***********---", "", @@ -493,7 +493,7 @@ def test_verbatim_different_qubits(): " | | ", "q1 : -H-|---------------|-------------", " | | ", - "q3 : ---|---------------|-----------⏺-", + "q3 : ---|---------------|-----------C-", " | | | ", "q4 : ---*************---***********-X-", "", @@ -507,11 +507,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-", "", @@ -535,7 +535,7 @@ def to_ir(self, target): " ", "q0 : -H---", " ", - "q1 : -H-⏺-", + "q1 : -H-C-", " | ", "q2 : ---X-", "", @@ -611,9 +611,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)-", " | | ", @@ -639,9 +639,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-|--------------------------", " | ", @@ -674,9 +674,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)-", " | ", @@ -826,7 +826,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)-", " | ", @@ -846,7 +846,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): @@ -860,9 +860,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)-", "", From 9386bca46180305c6d3ed488f2892c0d4cb575d1 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Fri, 1 Dec 2023 10:21:17 +0100 Subject: [PATCH 40/49] simplify all gphase case --- src/braket/circuits/ascii_circuit_diagram.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 81a8d1f53..e3dfb6bca 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -43,8 +43,8 @@ def build_diagram(circuit: cir.Circuit) -> str: str: ASCII string circuit diagram. """ - if not circuit.instructions or not any( - m for m in circuit._moments if m.moment_type != MomentType.GLOBAL_PHASE + if not circuit.instructions or all( + m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments ): return "" From ecbdff162026bbbe30ab789b063e8d8993c401c4 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Sun, 3 Dec 2023 20:17:30 +0100 Subject: [PATCH 41/49] change preprare_y_axis function name --- src/braket/circuits/ascii_circuit_diagram.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index e3dfb6bca..6b616240b 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -51,7 +51,9 @@ def build_diagram(circuit: cir.Circuit) -> str: circuit_qubits = circuit.qubits circuit_qubits.sort() - y_axis_str, global_phase = AsciiCircuitDiagram._prepare_y_axis_str(circuit, circuit_qubits) + y_axis_str, global_phase = AsciiCircuitDiagram._prepare_diagram_vars( + circuit, circuit_qubits + ) time_slices = circuit.moments.time_slices() column_strs = [] @@ -99,7 +101,7 @@ def build_diagram(circuit: cir.Circuit) -> str: return "\n".join(lines) @staticmethod - def _prepare_y_axis_str( + def _prepare_diagram_vars( circuit: cir.Circuit, circuit_qubits: QubitSet ) -> tuple[str, float | None]: # Y Axis Column @@ -107,7 +109,7 @@ def _prepare_y_axis_str( y_axis_str = "{0:{width}} : |\n".format("T", width=y_axis_width + 1) global_phase = None - if any(m for m in circuit._moments if m.moment_type == MomentType.GLOBAL_PHASE): + 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 From ad1053faff59eff44aba73b4d1ce78918facb75a Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Mon, 11 Dec 2023 23:44:16 +0100 Subject: [PATCH 42/49] create an helper function to compute the global phase --- src/braket/circuits/ascii_circuit_diagram.py | 80 ++++++++++++------- .../circuits/test_ascii_circuit_diagram.py | 2 +- 2 files changed, 50 insertions(+), 32 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 6b616240b..9a6e3f1eb 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -43,11 +43,12 @@ def build_diagram(circuit: cir.Circuit) -> str: str: ASCII string circuit diagram. """ - if not circuit.instructions or all( - m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments - ): + 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() @@ -60,7 +61,10 @@ def build_diagram(circuit: cir.Circuit) -> str: # Moment columns for time, instructions in time_slices.items(): - moment_str, global_phase = AsciiCircuitDiagram._ascii_diagram_column_set( + 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) @@ -70,10 +74,11 @@ def build_diagram(circuit: cir.Circuit) -> str: circuit.result_types ) if target_result_types: - result_type_col_str, global_phase = AsciiCircuitDiagram._ascii_diagram_column_set( - "Result Types", circuit_qubits, target_result_types, global_phase + column_strs.append( + AsciiCircuitDiagram._ascii_diagram_column_set( + "Result Types", circuit_qubits, target_result_types, global_phase + ) ) - column_strs.append(result_type_col_str) # Unite strings lines = y_axis_str.split("\n") @@ -119,6 +124,30 @@ def _prepare_diagram_vars( 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, @@ -137,8 +166,8 @@ def _ascii_group_items( 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)) + if isinstance(item, Instruction) and not isinstance( + item.operator, (Gate, Noise, CompilerDirective) ): continue @@ -210,8 +239,8 @@ def _ascii_diagram_column_set( col_title: str, circuit_qubits: QubitSet, items: list[Union[Instruction, ResultType]], - global_phase: float | None = None, - ) -> tuple[str, float | None]: + global_phase: float | None, + ) -> str: """ Return a set of columns in the ASCII string diagram of the circuit for a list of items. @@ -222,25 +251,16 @@ def _ascii_diagram_column_set( global_phase (float | None): the integrated global phase up to this set Returns: - tuple[str, float | None]: A tuple of the ASCII string diagram for the column set - and the integrated global phase. + 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, global_phases = list( - zip( - *[ - AsciiCircuitDiagram._ascii_diagram_column( - circuit_qubits, grouping[1], global_phase - ) - for grouping in groupings - ] - ) - ) - if global_phase is not None: - global_phase = sum(global_phases) + 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") @@ -261,14 +281,14 @@ def _ascii_diagram_column_set( first_line = "{:^{width}}|\n".format(col_title, width=len(lines[0]) - 1) - return first_line + "\n".join(lines), global_phase + return first_line + "\n".join(lines) @staticmethod def _ascii_diagram_column( circuit_qubits: QubitSet, items: list[Union[Instruction, ResultType]], global_phase: float | None = None, - ) -> tuple[str, float | None]: + ) -> str: """ Return a column in the ASCII string diagram of the circuit for a given list of items. @@ -278,8 +298,7 @@ def _ascii_diagram_column( global_phase (float | None): the integrated global phase up to this column Returns: - tuple[str, float | None]: A tuple of the ASCII string diagram for the specified moment - in time for a column and the integrated global phase. + 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} @@ -311,7 +330,6 @@ def _ascii_diagram_column( target_and_control = QubitSet() qubits = circuit_qubits ascii_symbols = "-" * len(circuit_qubits) - global_phase += item.operator.angle else: if isinstance(item.target, list): target_qubits = reduce(QubitSet.union, map(QubitSet, item.target), QubitSet()) @@ -361,7 +379,7 @@ def _ascii_diagram_column( margins[qubit] = "|" output = AsciiCircuitDiagram._create_output(symbols, margins, circuit_qubits, global_phase) - return output, global_phase + return output @staticmethod def _create_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 f00881cc9..0418b25af 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -30,7 +30,7 @@ def test_empty_circuit(): def test_only_gphase_circuit(): - assert AsciiCircuitDiagram.build_diagram(Circuit().gphase(0.1)) == "" + assert AsciiCircuitDiagram.build_diagram(Circuit().gphase(0.1)) == "Global phase: 0.1" def test_one_gate_one_qubit(): From cb6baac6e2d5f269a86ecbb4bbbd044866a29941 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Mon, 11 Dec 2023 23:51:47 +0100 Subject: [PATCH 43/49] make control_basis_state more explicit --- src/braket/circuits/gates.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/braket/circuits/gates.py b/src/braket/circuits/gates.py index 29d223c26..e955c4bb0 100644 --- a/src/braket/circuits/gates.py +++ b/src/braket/circuits/gates.py @@ -298,11 +298,10 @@ def gphase( if control is not None: control_qubits = QubitSet(control) - control_basis_state = ( - BasisState(control_state, len(control_qubits)) - if control_state is not None - else BasisState((1,) * len(control_qubits), len(control_qubits)) + control_state = ( + control_state if control_state is not None else (1,) * len(control_qubits) ) + control_basis_state = BasisState(control_state, len(control_qubits)) phaseshift_target = control_qubits[-1] phaseshift_instruction = PhaseShift.phaseshift( From 52ce20debf9d97665d5919a2b50e1cd5f3a79941 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 12 Dec 2023 00:10:20 +0100 Subject: [PATCH 44/49] add comment and clean grouping --- src/braket/circuits/ascii_circuit_diagram.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 9a6e3f1eb..eb9b7eaf5 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -171,6 +171,8 @@ def _ascii_group_items( ): 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) @@ -195,11 +197,7 @@ def _ascii_group_items( qubits_added = group[0] instr_group = group[1] # Take into account overlapping multi-qubit gates - if not qubits_added.intersection(set(qubit_range)) or ( - isinstance(item, Instruction) - and isinstance(item.operator, Gate) - and item.operator.name == "GPhase" - ): + if not qubits_added.intersection(set(qubit_range)): instr_group.append(item) qubits_added.update(qubit_range) found_grouping = True From d61a0d1414493a231573918508cfdeb41793583f Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 12 Dec 2023 00:29:05 +0100 Subject: [PATCH 45/49] add test_only_neg_control_qubits --- .../circuits/test_ascii_circuit_diagram.py | 16 ++++++++++++++++ 1 file changed, 16 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 0418b25af..b4a1c2327 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -268,6 +268,22 @@ 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 : -N-", + " | ", + "q1 : -N-", + " | ", + "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 = ( From b14c03b9c278bc10111460ae9c82e70db62e62eb Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 12 Dec 2023 12:52:47 +0100 Subject: [PATCH 46/49] parametrize test_one_gate_with_global_phase --- .../circuits/test_ascii_circuit_diagram.py | 24 ++++--------------- 1 file changed, 5 insertions(+), 19 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 b4a1c2327..2954453e3 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -12,6 +12,7 @@ # language governing permissions and limitations under the License. import numpy as np +import pytest from braket.circuits import ( AsciiCircuitDiagram, @@ -61,29 +62,14 @@ def test_one_gate_one_qubit_rotation_with_parameter(): ) _assert_correct_diagram(circ, expected) - -def test_one_gate_with_global_phase(): - circ = Circuit().x(target=0).gphase(0.15) - expected = ( - "T : |0| 1 |", - "GP : |0|0.15|", - " ", - "q0 : -X------", - "", - "T : |0| 1 |", - "", - "Global phase: 0.15", - ) - _assert_correct_diagram(circ, expected) - - -def test_one_gate_other_qubit_with_global_phase(): - circ = Circuit().x(target=1).gphase(0.15) +@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|", " ", - "q1 : -X------", + f"q{target} : -X------", "", "T : |0| 1 |", "", From 095fb6820a92e35dd5da073316094b2a6181637c Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 12 Dec 2023 12:53:34 +0100 Subject: [PATCH 47/49] reformat --- test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py | 1 + 1 file changed, 1 insertion(+) 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 2954453e3..b258c0ee4 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -62,6 +62,7 @@ def test_one_gate_one_qubit_rotation_with_parameter(): ) _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) From 8ebb0cecd6dc4239cbe74da6d39d091dd8c1b8e7 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Tue, 12 Dec 2023 13:06:51 +0100 Subject: [PATCH 48/49] change to printing with fixed precision --- src/braket/circuits/ascii_circuit_diagram.py | 11 +++++++---- .../braket/circuits/test_ascii_circuit_diagram.py | 10 +++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index eb9b7eaf5..660fb73e8 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -390,12 +390,15 @@ def _create_output( output = "" if global_phase is not None: - global_phase = ( - round(global_phase, 2) if isinstance(global_phase, float) else global_phase + global_phase_str = ( + f"{global_phase:.2f}" if isinstance(global_phase, float) else str(global_phase) ) - symbols_width = max([symbols_width, len(str(global_phase))]) + symbols_width = max([symbols_width, len(global_phase_str)]) output += "{0:{fill}{align}{width}}|\n".format( - str(global_phase), fill=" ", align="^", width=symbols_width + global_phase_str, + fill=" ", + align="^", + width=symbols_width, ) for qubit in qubits: 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 b258c0ee4..916bfb050 100644 --- a/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py +++ b/test/unit_tests/braket/circuits/test_ascii_circuit_diagram.py @@ -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.0|", - " ", - "q0 : -X---------", + "T : | 0 | 1 |", + "GP : |-0.15|0.00|", + " ", + "q0 : -X----------", "", - "T : | 0 | 1 |", + "T : | 0 | 1 |", ) _assert_correct_diagram(circ, expected) From b6e95637c0d8af0bb839820c4b07a20801e5faf2 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Jaskula Date: Wed, 20 Dec 2023 18:16:11 -0500 Subject: [PATCH 49/49] fix docstring --- src/braket/circuits/ascii_circuit_diagram.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/braket/circuits/ascii_circuit_diagram.py b/src/braket/circuits/ascii_circuit_diagram.py index 660fb73e8..2c7024574 100644 --- a/src/braket/circuits/ascii_circuit_diagram.py +++ b/src/braket/circuits/ascii_circuit_diagram.py @@ -132,11 +132,11 @@ def _compute_moment_global_phase( Compute the integrated phase at a certain moment. Args: - global_phase (float|None): The integrated phase up to the computed moment + 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. + float | None: The updated integrated phase. """ moment_phase = 0 for item in items: