From cd37e2290f3817eb5cd591911d3283a1e3a58264 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Thu, 12 Jan 2023 16:15:41 -0700 Subject: [PATCH 01/45] add gaussian square echo pulse --- qiskit/pulse/__init__.py | 1 + qiskit/pulse/library/__init__.py | 1 + qiskit/pulse/library/symbolic_pulses.py | 147 ++++++++++++++++++++ qiskit/qobj/converters/pulse_instruction.py | 1 + 4 files changed, 150 insertions(+) diff --git a/qiskit/pulse/__init__.py b/qiskit/pulse/__init__.py index 8902b12c5429..a4b23f66b585 100644 --- a/qiskit/pulse/__init__.py +++ b/qiskit/pulse/__init__.py @@ -142,6 +142,7 @@ Gaussian, GaussianSquare, GaussianSquareDrag, + GaussianSquareEcho, ParametricPulse, SymbolicPulse, ScalableSymbolicPulse, diff --git a/qiskit/pulse/library/__init__.py b/qiskit/pulse/library/__init__.py index bb68dca4a9fa..9692055e207e 100644 --- a/qiskit/pulse/library/__init__.py +++ b/qiskit/pulse/library/__init__.py @@ -118,6 +118,7 @@ Gaussian, GaussianSquare, GaussianSquareDrag, + GaussianSquareEcho, Drag, Constant, ) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index 1f65b4572516..e65d52bf94e7 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1063,6 +1063,153 @@ def GaussianSquareDrag( return instance +def GaussianSquareEcho( + duration: Union[int, ParameterExpression], + amp: Union[float, ParameterExpression], + sigma: Union[float, ParameterExpression], + width: Optional[Union[float, ParameterExpression]] = None, + angle: Optional[Union[float, ParameterExpression]] = 0.0, + active_amp: Optional[Union[complex, ParameterExpression]] = 0, + risefall_sigma_ratio: Optional[Union[float, ParameterExpression]] = None, + name: Optional[str] = None, + limit_amplitude: Optional[bool] = None, +) -> SymbolicPulse: + """An echoed Gaussian square pulse with an active tone overlaid on it. + Exactly one of the ``risefall_sigma_ratio`` and ``width`` parameters has to be specified. + If ``risefall_sigma_ratio`` is not ``None`` and ``width`` is ``None``: + .. math:: + \\text{risefall} &= \\text{risefall_sigma_ratio} \\times \\text{sigma}\\\\ + \\text{width} &= \\text{duration} - 2 \\times \\text{risefall} + If ``width`` is not None and ``risefall_sigma_ratio`` is None: + .. math:: \\text{risefall} = \\frac{\\text{duration} - \\text{width}}{2} + Gaussian :math:`g(x, c, σ)` and lifted gaussian :math:`g'(x, c, σ)` curves + can be written as: + .. math:: + g(x, c, σ) &= \\exp\\Bigl(-\\frac12 \\frac{(x - c)^2}{σ^2}\\Bigr)\\\\ + g'(x, c, σ) &= \\frac{g(x, c, σ)-g(-1, c, σ)}{1-g(-1, c, σ)} + + Args: + duration: Pulse length in terms of the sampling period `dt`. + amp: The amplitude of the DRAG rise and fall and of the square pulse. + sigma: A measure of how wide or narrow the DRAG risefall is; see the class + docstring for more details. + width: The duration of the embedded square pulse. + angle: The angle in radians of the complex phase factor uniformly + scaling the pulse. Default value 0. + active_amp: The amplitude of the active cancellation tone. + risefall_sigma_ratio: The ratio of each risefall duration to sigma. + name: Display name for this pulse envelope. + limit_amplitude: If ``True``, then limit the amplitude of the + waveform to 1. The default is ``True`` and the amplitude is constrained to 1. + Returns: + ScalableSymbolicPulse instance. + Raises: + PulseError: When width and risefall_sigma_ratio are both empty or both non-empty. + """ + # Convert risefall_sigma_ratio into width which is defined in OpenPulse spec + if width is None and risefall_sigma_ratio is None: + raise PulseError( + "Either the pulse width or the risefall_sigma_ratio parameter must be specified." + ) + if width is not None and risefall_sigma_ratio is not None: + raise PulseError( + "Either the pulse width or the risefall_sigma_ratio parameter can be specified" + " but not both." + ) + + if width is not None and risefall_sigma_ratio is None: + total_risefall = duration - width + echo_risefall = 2*total_risefall + width_echo = (duration - echo_risefall)/2 + + if width is None and risefall_sigma_ratio is not None: + width = duration - 2.0 * risefall_sigma_ratio * sigma + width_echo = (duration - 4.0 * risefall_sigma_ratio * sigma)/2 + + + + parameters = {"sigma": sigma, "width": width, "width_echo": width_echo, "active_amp": active_amp} + + # Prepare symbolic expressions + _t, _duration, _amp, _sigma, _active_amp, _width, _width_echo, _angle = sym.symbols( + "t, duration, amp, sigma, active_amp, width, width_echo, angle" + ) + + # gaussian square echo for rotary tone + _center = _duration / 4 + + _sq_t0 = _center - _width_echo / 2 + _sq_t1 = _center + _width_echo / 2 + + _gaussian_ledge = _lifted_gaussian(_t, _sq_t0, -1, _sigma) + _gaussian_redge = _lifted_gaussian(_t, _sq_t1, _duration/2 + 1, _sigma) + + envelope_expr_p = ( + _amp + * sym.exp(sym.I * _angle) + * sym.Piecewise( + (_gaussian_ledge, _t <= _sq_t0), + (_gaussian_redge, _t >= _sq_t1), + (1, True), + ) + ) + + _center_echo = _duration/2 + _duration / 4 + + _sq_t0_echo = _center_echo - _width_echo / 2 + _sq_t1_echo = _center_echo + _width_echo / 2 + + _gaussian_ledge_echo = _lifted_gaussian(_t, _sq_t0_echo, _duration/2-1, _sigma) + _gaussian_redge_echo = _lifted_gaussian(_t, _sq_t1_echo, _duration + 1, _sigma) + + envelope_expr_echo = ( + -1*_amp + * sym.exp(sym.I * _angle) + * sym.Piecewise( + (_gaussian_ledge_echo, _t <= _sq_t0_echo), + (_gaussian_redge_echo, _t >= _sq_t1_echo), + (1, True), + ) + ) + + envelope_expr = sym.Piecewise( (envelope_expr_p, _t <= _duration/2), (envelope_expr_echo, _t >= _duration/2), (0, True) ) + + # gaussian square for active cancellation tone + _center_xy = _duration/2 + + _sq_t0_xy = _center_xy - _width / 2 + _sq_t1_xy = _center_xy + _width / 2 + + _gaussian_ledge_xy = _lifted_gaussian(_t, _sq_t0_xy, -1, _sigma) + _gaussian_redge_xy = _lifted_gaussian(_t, _sq_t1_xy, _duration + 1, _sigma) + + envelope_expr_xy = _active_amp * sym.Piecewise( + (_gaussian_ledge_xy, _t <= _sq_t0_xy), (_gaussian_redge_xy, _t >= _sq_t1_xy), (1, True) + ) + + envelop_expr_total = envelope_expr + envelope_expr_xy + + + consts_expr = sym.And(_sigma > 0, _width >= 0, _duration >= _width, _duration/2 >= _width_echo) + valid_amp_conditions_expr = sym.And(sym.Abs(_amp) <= 1.0) + + instance = ScalableSymbolicPulse( + pulse_type="GaussianSquareEcho", + duration=duration, + amp=amp, + angle=angle, + parameters=parameters, + name=name, + limit_amplitude=limit_amplitude, + envelope=envelop_expr_total, + constraints=consts_expr, + valid_amp_conditions=valid_amp_conditions_expr, + ) + instance.validate_parameters() + + return instance + + class Drag(metaclass=_PulseType): """The Derivative Removal by Adiabatic Gate (DRAG) pulse is a standard Gaussian pulse with an additional Gaussian derivative component and lifting applied. diff --git a/qiskit/qobj/converters/pulse_instruction.py b/qiskit/qobj/converters/pulse_instruction.py index 5715223336e3..76d54d41e024 100644 --- a/qiskit/qobj/converters/pulse_instruction.py +++ b/qiskit/qobj/converters/pulse_instruction.py @@ -48,6 +48,7 @@ class ParametricPulseShapes(Enum): gaussian = "Gaussian" gaussian_square = "GaussianSquare" gaussian_square_drag = "GaussianSquareDrag" + gaussian_square_echo = "GaussianSquareEcho" drag = "Drag" constant = "Constant" From 859c45124171c5e2779c2aec77c29d45fc144d2e Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Tue, 24 Jan 2023 15:20:11 -0700 Subject: [PATCH 02/45] address some PR comments --- qiskit/pulse/library/symbolic_pulses.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index e65d52bf94e7..a3b9b2852f34 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1069,7 +1069,8 @@ def GaussianSquareEcho( sigma: Union[float, ParameterExpression], width: Optional[Union[float, ParameterExpression]] = None, angle: Optional[Union[float, ParameterExpression]] = 0.0, - active_amp: Optional[Union[complex, ParameterExpression]] = 0, + active_amp: Optional[Union[float, ParameterExpression]] = 0, + active_angle: Optional[Union[float, ParameterExpression]] = 0, risefall_sigma_ratio: Optional[Union[float, ParameterExpression]] = None, name: Optional[str] = None, limit_amplitude: Optional[bool] = None, @@ -1090,13 +1091,15 @@ def GaussianSquareEcho( Args: duration: Pulse length in terms of the sampling period `dt`. - amp: The amplitude of the DRAG rise and fall and of the square pulse. - sigma: A measure of how wide or narrow the DRAG risefall is; see the class + amp: The amplitude of the rise and fall and of the square pulse. + sigma: A measure of how wide or narrow the risefall is; see the class docstring for more details. width: The duration of the embedded square pulse. angle: The angle in radians of the complex phase factor uniformly scaling the pulse. Default value 0. active_amp: The amplitude of the active cancellation tone. + active_angle: The angle in radian of the complex phase factor uniformly + scaling the active pulse. Default value 0. risefall_sigma_ratio: The ratio of each risefall duration to sigma. name: Display name for this pulse envelope. limit_amplitude: If ``True``, then limit the amplitude of the @@ -1128,11 +1131,12 @@ def GaussianSquareEcho( - parameters = {"sigma": sigma, "width": width, "width_echo": width_echo, "active_amp": active_amp} + parameters = {"sigma": sigma, "width": width, "width_echo": width_echo, + "active_amp": active_amp, "active_angle": active_angle} # Prepare symbolic expressions - _t, _duration, _amp, _sigma, _active_amp, _width, _width_echo, _angle = sym.symbols( - "t, duration, amp, sigma, active_amp, width, width_echo, angle" + _t, _duration, _amp, _sigma, _active_amp, _width, _width_echo, _angle, _active_angle = sym.symbols( + "t, duration, amp, sigma, active_amp, width, width_echo, angle, active_angle" ) # gaussian square echo for rotary tone @@ -1183,7 +1187,7 @@ def GaussianSquareEcho( _gaussian_ledge_xy = _lifted_gaussian(_t, _sq_t0_xy, -1, _sigma) _gaussian_redge_xy = _lifted_gaussian(_t, _sq_t1_xy, _duration + 1, _sigma) - envelope_expr_xy = _active_amp * sym.Piecewise( + envelope_expr_xy = _active_amp * sym.exp(sym.I * _active_angle) * sym.Piecewise( (_gaussian_ledge_xy, _t <= _sq_t0_xy), (_gaussian_redge_xy, _t >= _sq_t1_xy), (1, True) ) @@ -1191,9 +1195,12 @@ def GaussianSquareEcho( consts_expr = sym.And(_sigma > 0, _width >= 0, _duration >= _width, _duration/2 >= _width_echo) + + # Check validity of amplitudes valid_amp_conditions_expr = sym.And(sym.Abs(_amp) <= 1.0) + valid_amp_conditions_expr = sym.And(sym.Abs(_active_amp) <= 1.0) - instance = ScalableSymbolicPulse( + instance = SymbolicPulse( pulse_type="GaussianSquareEcho", duration=duration, amp=amp, From 760aa7caa499ea1ef6c0031c6eba7978d5e6023b Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Wed, 25 Jan 2023 22:00:06 -0700 Subject: [PATCH 03/45] fixed parameters of symbolicpulse --- qiskit/pulse/library/symbolic_pulses.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index a3b9b2852f34..6496911e457d 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1131,7 +1131,7 @@ def GaussianSquareEcho( - parameters = {"sigma": sigma, "width": width, "width_echo": width_echo, + parameters = {"amp": amp, "angle": angle, "sigma": sigma, "width": width, "width_echo": width_echo, "active_amp": active_amp, "active_angle": active_angle} # Prepare symbolic expressions @@ -1203,8 +1203,6 @@ def GaussianSquareEcho( instance = SymbolicPulse( pulse_type="GaussianSquareEcho", duration=duration, - amp=amp, - angle=angle, parameters=parameters, name=name, limit_amplitude=limit_amplitude, From fca8fd6e040e411767e7b24ad4077a75f9b0d49d Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Tue, 31 Jan 2023 10:12:05 -0700 Subject: [PATCH 04/45] adding math description and reference --- qiskit/pulse/library/symbolic_pulses.py | 89 ++++++++++++++++++------- 1 file changed, 64 insertions(+), 25 deletions(-) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index 6496911e457d..351a767a1415 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1083,11 +1083,29 @@ def GaussianSquareEcho( \\text{width} &= \\text{duration} - 2 \\times \\text{risefall} If ``width`` is not None and ``risefall_sigma_ratio`` is None: .. math:: \\text{risefall} = \\frac{\\text{duration} - \\text{width}}{2} - Gaussian :math:`g(x, c, σ)` and lifted gaussian :math:`g'(x, c, σ)` curves - can be written as: + The Gaussian Square Echo pulse is composed of three pulses. First, a Gaussian Square pulse + :math: `f_{echo}(x)` with amplitude ``amp`` and phase ``angle`` playing for half duration, followed by a second + Gaussian Square pulse :math: `-f_{echo}(x)` with opposite amplitude and same phase playing for the rest of the + duration. Third a Gaussian Square pulse :math: `f_{active}(x)` with amplitude ``active_amp`` and phase ``active_angle`` + playing for the entire duration. The Gaussian Square Echo pulse :math: `g_e()` can be written as: .. math:: - g(x, c, σ) &= \\exp\\Bigl(-\\frac12 \\frac{(x - c)^2}{σ^2}\\Bigr)\\\\ - g'(x, c, σ) &= \\frac{g(x, c, σ)-g(-1, c, σ)}{1-g(-1, c, σ)} + + g_e(x) &= \\begin{cases}\ + f_{\\text{active}} + f_{\\text{echo}}(x)\ + & x < \\frac{\\text{duration}}{2}\\\\ + f_{\\text{active}} - f_{\\text{echo}}(x)\ + & \\frac{\\text{duration}}{2} < x\\\\ + + + References: + 1. |citation1|_ + + .. _citation1: https://journals.aps.org/prxquantum/abstract/10.1103/PRXQuantum.1.020318 + + .. |citation1| replace:: *Sundaresan, N., Lauer, I., Pritchett, E., + Magesan, E., Jurcevic, P. & Gambetta, J. M. + Reducing Unitary and Spectator Errors in Cross Resonance with + Optimized Rotary Echoes. PRX Quantum 1, 020318 (2020).* Args: duration: Pulse length in terms of the sampling period `dt`. @@ -1122,22 +1140,35 @@ def GaussianSquareEcho( if width is not None and risefall_sigma_ratio is None: total_risefall = duration - width - echo_risefall = 2*total_risefall - width_echo = (duration - echo_risefall)/2 + echo_risefall = 2 * total_risefall + width_echo = (duration - echo_risefall) / 2 if width is None and risefall_sigma_ratio is not None: width = duration - 2.0 * risefall_sigma_ratio * sigma - width_echo = (duration - 4.0 * risefall_sigma_ratio * sigma)/2 - - - - parameters = {"amp": amp, "angle": angle, "sigma": sigma, "width": width, "width_echo": width_echo, - "active_amp": active_amp, "active_angle": active_angle} + width_echo = (duration - 4.0 * risefall_sigma_ratio * sigma) / 2 + + parameters = { + "amp": amp, + "angle": angle, + "sigma": sigma, + "width": width, + "width_echo": width_echo, + "active_amp": active_amp, + "active_angle": active_angle, + } # Prepare symbolic expressions - _t, _duration, _amp, _sigma, _active_amp, _width, _width_echo, _angle, _active_angle = sym.symbols( - "t, duration, amp, sigma, active_amp, width, width_echo, angle, active_angle" - ) + ( + _t, + _duration, + _amp, + _sigma, + _active_amp, + _width, + _width_echo, + _angle, + _active_angle, + ) = sym.symbols("t, duration, amp, sigma, active_amp, width, width_echo, angle, active_angle") # gaussian square echo for rotary tone _center = _duration / 4 @@ -1146,7 +1177,7 @@ def GaussianSquareEcho( _sq_t1 = _center + _width_echo / 2 _gaussian_ledge = _lifted_gaussian(_t, _sq_t0, -1, _sigma) - _gaussian_redge = _lifted_gaussian(_t, _sq_t1, _duration/2 + 1, _sigma) + _gaussian_redge = _lifted_gaussian(_t, _sq_t1, _duration / 2 + 1, _sigma) envelope_expr_p = ( _amp @@ -1158,16 +1189,17 @@ def GaussianSquareEcho( ) ) - _center_echo = _duration/2 + _duration / 4 + _center_echo = _duration / 2 + _duration / 4 _sq_t0_echo = _center_echo - _width_echo / 2 _sq_t1_echo = _center_echo + _width_echo / 2 - _gaussian_ledge_echo = _lifted_gaussian(_t, _sq_t0_echo, _duration/2-1, _sigma) + _gaussian_ledge_echo = _lifted_gaussian(_t, _sq_t0_echo, _duration / 2 - 1, _sigma) _gaussian_redge_echo = _lifted_gaussian(_t, _sq_t1_echo, _duration + 1, _sigma) envelope_expr_echo = ( - -1*_amp + -1 + * _amp * sym.exp(sym.I * _angle) * sym.Piecewise( (_gaussian_ledge_echo, _t <= _sq_t0_echo), @@ -1176,10 +1208,12 @@ def GaussianSquareEcho( ) ) - envelope_expr = sym.Piecewise( (envelope_expr_p, _t <= _duration/2), (envelope_expr_echo, _t >= _duration/2), (0, True) ) + envelope_expr = sym.Piecewise( + (envelope_expr_p, _t <= _duration / 2), (envelope_expr_echo, _t >= _duration / 2), (0, True) + ) # gaussian square for active cancellation tone - _center_xy = _duration/2 + _center_xy = _duration / 2 _sq_t0_xy = _center_xy - _width / 2 _sq_t1_xy = _center_xy + _width / 2 @@ -1187,14 +1221,19 @@ def GaussianSquareEcho( _gaussian_ledge_xy = _lifted_gaussian(_t, _sq_t0_xy, -1, _sigma) _gaussian_redge_xy = _lifted_gaussian(_t, _sq_t1_xy, _duration + 1, _sigma) - envelope_expr_xy = _active_amp * sym.exp(sym.I * _active_angle) * sym.Piecewise( - (_gaussian_ledge_xy, _t <= _sq_t0_xy), (_gaussian_redge_xy, _t >= _sq_t1_xy), (1, True) + envelope_expr_xy = ( + _active_amp + * sym.exp(sym.I * _active_angle) + * sym.Piecewise( + (_gaussian_ledge_xy, _t <= _sq_t0_xy), (_gaussian_redge_xy, _t >= _sq_t1_xy), (1, True) + ) ) envelop_expr_total = envelope_expr + envelope_expr_xy - - consts_expr = sym.And(_sigma > 0, _width >= 0, _duration >= _width, _duration/2 >= _width_echo) + consts_expr = sym.And( + _sigma > 0, _width >= 0, _duration >= _width, _duration / 2 >= _width_echo + ) # Check validity of amplitudes valid_amp_conditions_expr = sym.And(sym.Abs(_amp) <= 1.0) From 28959698d89ccce529552c851a1c32670d060718 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Tue, 31 Jan 2023 10:43:30 -0700 Subject: [PATCH 05/45] change variable names --- qiskit/pulse/library/symbolic_pulses.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index 351a767a1415..ea4f100cc45b 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1097,6 +1097,7 @@ def GaussianSquareEcho( & \\frac{\\text{duration}}{2} < x\\\\ + References: 1. |citation1|_ @@ -1213,23 +1214,23 @@ def GaussianSquareEcho( ) # gaussian square for active cancellation tone - _center_xy = _duration / 2 + _center_active = _duration / 2 - _sq_t0_xy = _center_xy - _width / 2 - _sq_t1_xy = _center_xy + _width / 2 + _sq_t0_active = _center_active - _width / 2 + _sq_t1_active = _center_active + _width / 2 - _gaussian_ledge_xy = _lifted_gaussian(_t, _sq_t0_xy, -1, _sigma) - _gaussian_redge_xy = _lifted_gaussian(_t, _sq_t1_xy, _duration + 1, _sigma) + _gaussian_ledge_active = _lifted_gaussian(_t, _sq_t0_active, -1, _sigma) + _gaussian_redge_active = _lifted_gaussian(_t, _sq_t1_active, _duration + 1, _sigma) - envelope_expr_xy = ( + envelope_expr_active = ( _active_amp * sym.exp(sym.I * _active_angle) * sym.Piecewise( - (_gaussian_ledge_xy, _t <= _sq_t0_xy), (_gaussian_redge_xy, _t >= _sq_t1_xy), (1, True) + (_gaussian_ledge_active, _t <= _sq_t0_active), (_gaussian_redge_active, _t >= _sq_t1_active), (1, True) ) ) - envelop_expr_total = envelope_expr + envelope_expr_xy + envelop_expr_total = envelope_expr + envelope_expr_active consts_expr = sym.And( _sigma > 0, _width >= 0, _duration >= _width, _duration / 2 >= _width_echo From 755d16e58cd322c4ec77e15e69f47f45fe9a34ff Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Tue, 31 Jan 2023 10:56:15 -0700 Subject: [PATCH 06/45] fix lint --- qiskit/pulse/library/symbolic_pulses.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index ea4f100cc45b..b5926052de83 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1075,7 +1075,7 @@ def GaussianSquareEcho( name: Optional[str] = None, limit_amplitude: Optional[bool] = None, ) -> SymbolicPulse: - """An echoed Gaussian square pulse with an active tone overlaid on it. + """An echoed Gaussian square pulse with an active tone overlaid on it. Exactly one of the ``risefall_sigma_ratio`` and ``width`` parameters has to be specified. If ``risefall_sigma_ratio`` is not ``None`` and ``width`` is ``None``: .. math:: @@ -1084,17 +1084,19 @@ def GaussianSquareEcho( If ``width`` is not None and ``risefall_sigma_ratio`` is None: .. math:: \\text{risefall} = \\frac{\\text{duration} - \\text{width}}{2} The Gaussian Square Echo pulse is composed of three pulses. First, a Gaussian Square pulse - :math: `f_{echo}(x)` with amplitude ``amp`` and phase ``angle`` playing for half duration, followed by a second - Gaussian Square pulse :math: `-f_{echo}(x)` with opposite amplitude and same phase playing for the rest of the - duration. Third a Gaussian Square pulse :math: `f_{active}(x)` with amplitude ``active_amp`` and phase ``active_angle`` - playing for the entire duration. The Gaussian Square Echo pulse :math: `g_e()` can be written as: + :math: `f_{echo}(x)` with amplitude ``amp`` and phase ``angle`` playing for half duration, + followed by a second Gaussian Square pulse :math: `-f_{echo}(x)` with opposite amplitude + and same phase playing for the rest of the duration. Third a Gaussian Square pulse + :math: `f_{active}(x)` with amplitude ``active_amp`` and phase ``active_angle`` + playing for the entire duration. The Gaussian Square Echo pulse :math: `g_e()` + can be written as: .. math:: g_e(x) &= \\begin{cases}\ f_{\\text{active}} + f_{\\text{echo}}(x)\ & x < \\frac{\\text{duration}}{2}\\\\ f_{\\text{active}} - f_{\\text{echo}}(x)\ - & \\frac{\\text{duration}}{2} < x\\\\ + & \\frac{\\text{duration}}{2} < x\\\\ @@ -1103,11 +1105,10 @@ def GaussianSquareEcho( .. _citation1: https://journals.aps.org/prxquantum/abstract/10.1103/PRXQuantum.1.020318 - .. |citation1| replace:: *Sundaresan, N., Lauer, I., Pritchett, E., + .. |citation1| replace:: *Sundaresan, N., Lauer, I., Pritchett, E., Magesan, E., Jurcevic, P. & Gambetta, J. M. Reducing Unitary and Spectator Errors in Cross Resonance with Optimized Rotary Echoes. PRX Quantum 1, 020318 (2020).* - Args: duration: Pulse length in terms of the sampling period `dt`. amp: The amplitude of the rise and fall and of the square pulse. @@ -1226,7 +1227,9 @@ def GaussianSquareEcho( _active_amp * sym.exp(sym.I * _active_angle) * sym.Piecewise( - (_gaussian_ledge_active, _t <= _sq_t0_active), (_gaussian_redge_active, _t >= _sq_t1_active), (1, True) + (_gaussian_ledge_active, _t <= _sq_t0_active), + (_gaussian_redge_active, _t >= _sq_t1_active), + (1, True), ) ) From 6f73a4cec633b6acb881677c4fd027b566440314 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Tue, 31 Jan 2023 12:18:11 -0700 Subject: [PATCH 07/45] remove width_echo parameter --- qiskit/pulse/library/symbolic_pulses.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index b5926052de83..5471613af837 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1140,21 +1140,14 @@ def GaussianSquareEcho( " but not both." ) - if width is not None and risefall_sigma_ratio is None: - total_risefall = duration - width - echo_risefall = 2 * total_risefall - width_echo = (duration - echo_risefall) / 2 - if width is None and risefall_sigma_ratio is not None: width = duration - 2.0 * risefall_sigma_ratio * sigma - width_echo = (duration - 4.0 * risefall_sigma_ratio * sigma) / 2 parameters = { "amp": amp, "angle": angle, "sigma": sigma, "width": width, - "width_echo": width_echo, "active_amp": active_amp, "active_angle": active_angle, } @@ -1167,14 +1160,15 @@ def GaussianSquareEcho( _sigma, _active_amp, _width, - _width_echo, _angle, _active_angle, - ) = sym.symbols("t, duration, amp, sigma, active_amp, width, width_echo, angle, active_angle") + ) = sym.symbols("t, duration, amp, sigma, active_amp, width, angle, active_angle") # gaussian square echo for rotary tone _center = _duration / 4 + _width_echo = (_duration - 2*(_duration - _width))/2 + _sq_t0 = _center - _width_echo / 2 _sq_t1 = _center + _width_echo / 2 From e1c731ec5c1b33ccc873376847507cb6a859342e Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Tue, 31 Jan 2023 13:35:23 -0700 Subject: [PATCH 08/45] fix lint --- qiskit/pulse/library/symbolic_pulses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index 5471613af837..1c6576ab4408 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1167,7 +1167,7 @@ def GaussianSquareEcho( # gaussian square echo for rotary tone _center = _duration / 4 - _width_echo = (_duration - 2*(_duration - _width))/2 + _width_echo = (_duration - 2 * (_duration - _width)) / 2 _sq_t0 = _center - _width_echo / 2 _sq_t1 = _center + _width_echo / 2 From b083550e1ef0a20d480f11ed1dd86234a985507b Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Tue, 31 Jan 2023 14:13:37 -0700 Subject: [PATCH 09/45] add release notes --- .../gaussian-square-echo-pulse-84306f1a02e2bb28.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 releasenotes/notes/gaussian-square-echo-pulse-84306f1a02e2bb28.yaml diff --git a/releasenotes/notes/gaussian-square-echo-pulse-84306f1a02e2bb28.yaml b/releasenotes/notes/gaussian-square-echo-pulse-84306f1a02e2bb28.yaml new file mode 100644 index 000000000000..e209ae8010fa --- /dev/null +++ b/releasenotes/notes/gaussian-square-echo-pulse-84306f1a02e2bb28.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + Add new :meth:`~qiskit.pulse.GaussianSquareEcho` pulse shape. This pulse + is composed by three :class:`~qiskit.pulse.GaussianSquare` pulses. The + first two are echo pulses with duration half of the total duration and + implement rotary tones. The third pulse is a cancellation tone that lasts + the full duration of the pulse and implements correcting single qubit + rotations. \ No newline at end of file From 0c1ba461d52ad7473e385596e9d4624323bd5590 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Wed, 12 Apr 2023 12:56:49 -0600 Subject: [PATCH 10/45] fix conflicts --- qiskit/pulse/__init__.py | 5 +- qiskit/pulse/library/__init__.py | 1 + qiskit/pulse/library/symbolic_pulses.py | 147 ++++++++++++++++++++ qiskit/qobj/converters/pulse_instruction.py | 1 + 4 files changed, 150 insertions(+), 4 deletions(-) diff --git a/qiskit/pulse/__init__.py b/qiskit/pulse/__init__.py index d5432eaa7be7..a4b23f66b585 100644 --- a/qiskit/pulse/__init__.py +++ b/qiskit/pulse/__init__.py @@ -142,10 +142,7 @@ Gaussian, GaussianSquare, GaussianSquareDrag, - Sin, - Cos, - Sawtooth, - Triangle, + GaussianSquareEcho, ParametricPulse, SymbolicPulse, ScalableSymbolicPulse, diff --git a/qiskit/pulse/library/__init__.py b/qiskit/pulse/library/__init__.py index 19446c10f1b0..81b9bc793e60 100644 --- a/qiskit/pulse/library/__init__.py +++ b/qiskit/pulse/library/__init__.py @@ -122,6 +122,7 @@ Gaussian, GaussianSquare, GaussianSquareDrag, + GaussianSquareEcho, Drag, Constant, Sin, diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index 9102df2355e3..2beedd19a6d2 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1068,6 +1068,153 @@ def GaussianSquareDrag( return instance +def GaussianSquareEcho( + duration: Union[int, ParameterExpression], + amp: Union[float, ParameterExpression], + sigma: Union[float, ParameterExpression], + width: Optional[Union[float, ParameterExpression]] = None, + angle: Optional[Union[float, ParameterExpression]] = 0.0, + active_amp: Optional[Union[complex, ParameterExpression]] = 0, + risefall_sigma_ratio: Optional[Union[float, ParameterExpression]] = None, + name: Optional[str] = None, + limit_amplitude: Optional[bool] = None, +) -> SymbolicPulse: + """An echoed Gaussian square pulse with an active tone overlaid on it. + Exactly one of the ``risefall_sigma_ratio`` and ``width`` parameters has to be specified. + If ``risefall_sigma_ratio`` is not ``None`` and ``width`` is ``None``: + .. math:: + \\text{risefall} &= \\text{risefall_sigma_ratio} \\times \\text{sigma}\\\\ + \\text{width} &= \\text{duration} - 2 \\times \\text{risefall} + If ``width`` is not None and ``risefall_sigma_ratio`` is None: + .. math:: \\text{risefall} = \\frac{\\text{duration} - \\text{width}}{2} + Gaussian :math:`g(x, c, σ)` and lifted gaussian :math:`g'(x, c, σ)` curves + can be written as: + .. math:: + g(x, c, σ) &= \\exp\\Bigl(-\\frac12 \\frac{(x - c)^2}{σ^2}\\Bigr)\\\\ + g'(x, c, σ) &= \\frac{g(x, c, σ)-g(-1, c, σ)}{1-g(-1, c, σ)} + + Args: + duration: Pulse length in terms of the sampling period `dt`. + amp: The amplitude of the DRAG rise and fall and of the square pulse. + sigma: A measure of how wide or narrow the DRAG risefall is; see the class + docstring for more details. + width: The duration of the embedded square pulse. + angle: The angle in radians of the complex phase factor uniformly + scaling the pulse. Default value 0. + active_amp: The amplitude of the active cancellation tone. + risefall_sigma_ratio: The ratio of each risefall duration to sigma. + name: Display name for this pulse envelope. + limit_amplitude: If ``True``, then limit the amplitude of the + waveform to 1. The default is ``True`` and the amplitude is constrained to 1. + Returns: + ScalableSymbolicPulse instance. + Raises: + PulseError: When width and risefall_sigma_ratio are both empty or both non-empty. + """ + # Convert risefall_sigma_ratio into width which is defined in OpenPulse spec + if width is None and risefall_sigma_ratio is None: + raise PulseError( + "Either the pulse width or the risefall_sigma_ratio parameter must be specified." + ) + if width is not None and risefall_sigma_ratio is not None: + raise PulseError( + "Either the pulse width or the risefall_sigma_ratio parameter can be specified" + " but not both." + ) + + if width is not None and risefall_sigma_ratio is None: + total_risefall = duration - width + echo_risefall = 2*total_risefall + width_echo = (duration - echo_risefall)/2 + + if width is None and risefall_sigma_ratio is not None: + width = duration - 2.0 * risefall_sigma_ratio * sigma + width_echo = (duration - 4.0 * risefall_sigma_ratio * sigma)/2 + + + + parameters = {"sigma": sigma, "width": width, "width_echo": width_echo, "active_amp": active_amp} + + # Prepare symbolic expressions + _t, _duration, _amp, _sigma, _active_amp, _width, _width_echo, _angle = sym.symbols( + "t, duration, amp, sigma, active_amp, width, width_echo, angle" + ) + + # gaussian square echo for rotary tone + _center = _duration / 4 + + _sq_t0 = _center - _width_echo / 2 + _sq_t1 = _center + _width_echo / 2 + + _gaussian_ledge = _lifted_gaussian(_t, _sq_t0, -1, _sigma) + _gaussian_redge = _lifted_gaussian(_t, _sq_t1, _duration/2 + 1, _sigma) + + envelope_expr_p = ( + _amp + * sym.exp(sym.I * _angle) + * sym.Piecewise( + (_gaussian_ledge, _t <= _sq_t0), + (_gaussian_redge, _t >= _sq_t1), + (1, True), + ) + ) + + _center_echo = _duration/2 + _duration / 4 + + _sq_t0_echo = _center_echo - _width_echo / 2 + _sq_t1_echo = _center_echo + _width_echo / 2 + + _gaussian_ledge_echo = _lifted_gaussian(_t, _sq_t0_echo, _duration/2-1, _sigma) + _gaussian_redge_echo = _lifted_gaussian(_t, _sq_t1_echo, _duration + 1, _sigma) + + envelope_expr_echo = ( + -1*_amp + * sym.exp(sym.I * _angle) + * sym.Piecewise( + (_gaussian_ledge_echo, _t <= _sq_t0_echo), + (_gaussian_redge_echo, _t >= _sq_t1_echo), + (1, True), + ) + ) + + envelope_expr = sym.Piecewise( (envelope_expr_p, _t <= _duration/2), (envelope_expr_echo, _t >= _duration/2), (0, True) ) + + # gaussian square for active cancellation tone + _center_xy = _duration/2 + + _sq_t0_xy = _center_xy - _width / 2 + _sq_t1_xy = _center_xy + _width / 2 + + _gaussian_ledge_xy = _lifted_gaussian(_t, _sq_t0_xy, -1, _sigma) + _gaussian_redge_xy = _lifted_gaussian(_t, _sq_t1_xy, _duration + 1, _sigma) + + envelope_expr_xy = _active_amp * sym.Piecewise( + (_gaussian_ledge_xy, _t <= _sq_t0_xy), (_gaussian_redge_xy, _t >= _sq_t1_xy), (1, True) + ) + + envelop_expr_total = envelope_expr + envelope_expr_xy + + + consts_expr = sym.And(_sigma > 0, _width >= 0, _duration >= _width, _duration/2 >= _width_echo) + valid_amp_conditions_expr = sym.And(sym.Abs(_amp) <= 1.0) + + instance = ScalableSymbolicPulse( + pulse_type="GaussianSquareEcho", + duration=duration, + amp=amp, + angle=angle, + parameters=parameters, + name=name, + limit_amplitude=limit_amplitude, + envelope=envelop_expr_total, + constraints=consts_expr, + valid_amp_conditions=valid_amp_conditions_expr, + ) + instance.validate_parameters() + + return instance + + class Drag(metaclass=_PulseType): """The Derivative Removal by Adiabatic Gate (DRAG) pulse is a standard Gaussian pulse with an additional Gaussian derivative component and lifting applied. diff --git a/qiskit/qobj/converters/pulse_instruction.py b/qiskit/qobj/converters/pulse_instruction.py index 4ea888cc8a67..4cf4461d24f6 100644 --- a/qiskit/qobj/converters/pulse_instruction.py +++ b/qiskit/qobj/converters/pulse_instruction.py @@ -48,6 +48,7 @@ class ParametricPulseShapes(Enum): gaussian = "Gaussian" gaussian_square = "GaussianSquare" gaussian_square_drag = "GaussianSquareDrag" + gaussian_square_echo = "GaussianSquareEcho" drag = "Drag" constant = "Constant" From d94fcc7d0ae5f8716a3047f89706b46be57c53f5 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Tue, 24 Jan 2023 15:20:11 -0700 Subject: [PATCH 11/45] address some PR comments --- qiskit/pulse/library/symbolic_pulses.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index 2beedd19a6d2..3dfeb44cb0c0 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1074,7 +1074,8 @@ def GaussianSquareEcho( sigma: Union[float, ParameterExpression], width: Optional[Union[float, ParameterExpression]] = None, angle: Optional[Union[float, ParameterExpression]] = 0.0, - active_amp: Optional[Union[complex, ParameterExpression]] = 0, + active_amp: Optional[Union[float, ParameterExpression]] = 0, + active_angle: Optional[Union[float, ParameterExpression]] = 0, risefall_sigma_ratio: Optional[Union[float, ParameterExpression]] = None, name: Optional[str] = None, limit_amplitude: Optional[bool] = None, @@ -1095,13 +1096,15 @@ def GaussianSquareEcho( Args: duration: Pulse length in terms of the sampling period `dt`. - amp: The amplitude of the DRAG rise and fall and of the square pulse. - sigma: A measure of how wide or narrow the DRAG risefall is; see the class + amp: The amplitude of the rise and fall and of the square pulse. + sigma: A measure of how wide or narrow the risefall is; see the class docstring for more details. width: The duration of the embedded square pulse. angle: The angle in radians of the complex phase factor uniformly scaling the pulse. Default value 0. active_amp: The amplitude of the active cancellation tone. + active_angle: The angle in radian of the complex phase factor uniformly + scaling the active pulse. Default value 0. risefall_sigma_ratio: The ratio of each risefall duration to sigma. name: Display name for this pulse envelope. limit_amplitude: If ``True``, then limit the amplitude of the @@ -1133,11 +1136,12 @@ def GaussianSquareEcho( - parameters = {"sigma": sigma, "width": width, "width_echo": width_echo, "active_amp": active_amp} + parameters = {"sigma": sigma, "width": width, "width_echo": width_echo, + "active_amp": active_amp, "active_angle": active_angle} # Prepare symbolic expressions - _t, _duration, _amp, _sigma, _active_amp, _width, _width_echo, _angle = sym.symbols( - "t, duration, amp, sigma, active_amp, width, width_echo, angle" + _t, _duration, _amp, _sigma, _active_amp, _width, _width_echo, _angle, _active_angle = sym.symbols( + "t, duration, amp, sigma, active_amp, width, width_echo, angle, active_angle" ) # gaussian square echo for rotary tone @@ -1188,7 +1192,7 @@ def GaussianSquareEcho( _gaussian_ledge_xy = _lifted_gaussian(_t, _sq_t0_xy, -1, _sigma) _gaussian_redge_xy = _lifted_gaussian(_t, _sq_t1_xy, _duration + 1, _sigma) - envelope_expr_xy = _active_amp * sym.Piecewise( + envelope_expr_xy = _active_amp * sym.exp(sym.I * _active_angle) * sym.Piecewise( (_gaussian_ledge_xy, _t <= _sq_t0_xy), (_gaussian_redge_xy, _t >= _sq_t1_xy), (1, True) ) @@ -1196,9 +1200,12 @@ def GaussianSquareEcho( consts_expr = sym.And(_sigma > 0, _width >= 0, _duration >= _width, _duration/2 >= _width_echo) + + # Check validity of amplitudes valid_amp_conditions_expr = sym.And(sym.Abs(_amp) <= 1.0) + valid_amp_conditions_expr = sym.And(sym.Abs(_active_amp) <= 1.0) - instance = ScalableSymbolicPulse( + instance = SymbolicPulse( pulse_type="GaussianSquareEcho", duration=duration, amp=amp, From 3d310c7ac58cd233acc18f481436e2a58b8ca323 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Wed, 25 Jan 2023 22:00:06 -0700 Subject: [PATCH 12/45] fixed parameters of symbolicpulse --- qiskit/pulse/library/symbolic_pulses.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index 3dfeb44cb0c0..b44d1886055d 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1136,7 +1136,7 @@ def GaussianSquareEcho( - parameters = {"sigma": sigma, "width": width, "width_echo": width_echo, + parameters = {"amp": amp, "angle": angle, "sigma": sigma, "width": width, "width_echo": width_echo, "active_amp": active_amp, "active_angle": active_angle} # Prepare symbolic expressions @@ -1208,8 +1208,6 @@ def GaussianSquareEcho( instance = SymbolicPulse( pulse_type="GaussianSquareEcho", duration=duration, - amp=amp, - angle=angle, parameters=parameters, name=name, limit_amplitude=limit_amplitude, From a1df97fd23a033ddb59de1229ccbcf87fcc6ae38 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Tue, 31 Jan 2023 10:12:05 -0700 Subject: [PATCH 13/45] adding math description and reference --- qiskit/pulse/library/symbolic_pulses.py | 89 ++++++++++++++++++------- 1 file changed, 64 insertions(+), 25 deletions(-) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index b44d1886055d..60dc781b3833 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1088,11 +1088,29 @@ def GaussianSquareEcho( \\text{width} &= \\text{duration} - 2 \\times \\text{risefall} If ``width`` is not None and ``risefall_sigma_ratio`` is None: .. math:: \\text{risefall} = \\frac{\\text{duration} - \\text{width}}{2} - Gaussian :math:`g(x, c, σ)` and lifted gaussian :math:`g'(x, c, σ)` curves - can be written as: + The Gaussian Square Echo pulse is composed of three pulses. First, a Gaussian Square pulse + :math: `f_{echo}(x)` with amplitude ``amp`` and phase ``angle`` playing for half duration, followed by a second + Gaussian Square pulse :math: `-f_{echo}(x)` with opposite amplitude and same phase playing for the rest of the + duration. Third a Gaussian Square pulse :math: `f_{active}(x)` with amplitude ``active_amp`` and phase ``active_angle`` + playing for the entire duration. The Gaussian Square Echo pulse :math: `g_e()` can be written as: .. math:: - g(x, c, σ) &= \\exp\\Bigl(-\\frac12 \\frac{(x - c)^2}{σ^2}\\Bigr)\\\\ - g'(x, c, σ) &= \\frac{g(x, c, σ)-g(-1, c, σ)}{1-g(-1, c, σ)} + + g_e(x) &= \\begin{cases}\ + f_{\\text{active}} + f_{\\text{echo}}(x)\ + & x < \\frac{\\text{duration}}{2}\\\\ + f_{\\text{active}} - f_{\\text{echo}}(x)\ + & \\frac{\\text{duration}}{2} < x\\\\ + + + References: + 1. |citation1|_ + + .. _citation1: https://journals.aps.org/prxquantum/abstract/10.1103/PRXQuantum.1.020318 + + .. |citation1| replace:: *Sundaresan, N., Lauer, I., Pritchett, E., + Magesan, E., Jurcevic, P. & Gambetta, J. M. + Reducing Unitary and Spectator Errors in Cross Resonance with + Optimized Rotary Echoes. PRX Quantum 1, 020318 (2020).* Args: duration: Pulse length in terms of the sampling period `dt`. @@ -1127,22 +1145,35 @@ def GaussianSquareEcho( if width is not None and risefall_sigma_ratio is None: total_risefall = duration - width - echo_risefall = 2*total_risefall - width_echo = (duration - echo_risefall)/2 + echo_risefall = 2 * total_risefall + width_echo = (duration - echo_risefall) / 2 if width is None and risefall_sigma_ratio is not None: width = duration - 2.0 * risefall_sigma_ratio * sigma - width_echo = (duration - 4.0 * risefall_sigma_ratio * sigma)/2 - - - - parameters = {"amp": amp, "angle": angle, "sigma": sigma, "width": width, "width_echo": width_echo, - "active_amp": active_amp, "active_angle": active_angle} + width_echo = (duration - 4.0 * risefall_sigma_ratio * sigma) / 2 + + parameters = { + "amp": amp, + "angle": angle, + "sigma": sigma, + "width": width, + "width_echo": width_echo, + "active_amp": active_amp, + "active_angle": active_angle, + } # Prepare symbolic expressions - _t, _duration, _amp, _sigma, _active_amp, _width, _width_echo, _angle, _active_angle = sym.symbols( - "t, duration, amp, sigma, active_amp, width, width_echo, angle, active_angle" - ) + ( + _t, + _duration, + _amp, + _sigma, + _active_amp, + _width, + _width_echo, + _angle, + _active_angle, + ) = sym.symbols("t, duration, amp, sigma, active_amp, width, width_echo, angle, active_angle") # gaussian square echo for rotary tone _center = _duration / 4 @@ -1151,7 +1182,7 @@ def GaussianSquareEcho( _sq_t1 = _center + _width_echo / 2 _gaussian_ledge = _lifted_gaussian(_t, _sq_t0, -1, _sigma) - _gaussian_redge = _lifted_gaussian(_t, _sq_t1, _duration/2 + 1, _sigma) + _gaussian_redge = _lifted_gaussian(_t, _sq_t1, _duration / 2 + 1, _sigma) envelope_expr_p = ( _amp @@ -1163,16 +1194,17 @@ def GaussianSquareEcho( ) ) - _center_echo = _duration/2 + _duration / 4 + _center_echo = _duration / 2 + _duration / 4 _sq_t0_echo = _center_echo - _width_echo / 2 _sq_t1_echo = _center_echo + _width_echo / 2 - _gaussian_ledge_echo = _lifted_gaussian(_t, _sq_t0_echo, _duration/2-1, _sigma) + _gaussian_ledge_echo = _lifted_gaussian(_t, _sq_t0_echo, _duration / 2 - 1, _sigma) _gaussian_redge_echo = _lifted_gaussian(_t, _sq_t1_echo, _duration + 1, _sigma) envelope_expr_echo = ( - -1*_amp + -1 + * _amp * sym.exp(sym.I * _angle) * sym.Piecewise( (_gaussian_ledge_echo, _t <= _sq_t0_echo), @@ -1181,10 +1213,12 @@ def GaussianSquareEcho( ) ) - envelope_expr = sym.Piecewise( (envelope_expr_p, _t <= _duration/2), (envelope_expr_echo, _t >= _duration/2), (0, True) ) + envelope_expr = sym.Piecewise( + (envelope_expr_p, _t <= _duration / 2), (envelope_expr_echo, _t >= _duration / 2), (0, True) + ) # gaussian square for active cancellation tone - _center_xy = _duration/2 + _center_xy = _duration / 2 _sq_t0_xy = _center_xy - _width / 2 _sq_t1_xy = _center_xy + _width / 2 @@ -1192,14 +1226,19 @@ def GaussianSquareEcho( _gaussian_ledge_xy = _lifted_gaussian(_t, _sq_t0_xy, -1, _sigma) _gaussian_redge_xy = _lifted_gaussian(_t, _sq_t1_xy, _duration + 1, _sigma) - envelope_expr_xy = _active_amp * sym.exp(sym.I * _active_angle) * sym.Piecewise( - (_gaussian_ledge_xy, _t <= _sq_t0_xy), (_gaussian_redge_xy, _t >= _sq_t1_xy), (1, True) + envelope_expr_xy = ( + _active_amp + * sym.exp(sym.I * _active_angle) + * sym.Piecewise( + (_gaussian_ledge_xy, _t <= _sq_t0_xy), (_gaussian_redge_xy, _t >= _sq_t1_xy), (1, True) + ) ) envelop_expr_total = envelope_expr + envelope_expr_xy - - consts_expr = sym.And(_sigma > 0, _width >= 0, _duration >= _width, _duration/2 >= _width_echo) + consts_expr = sym.And( + _sigma > 0, _width >= 0, _duration >= _width, _duration / 2 >= _width_echo + ) # Check validity of amplitudes valid_amp_conditions_expr = sym.And(sym.Abs(_amp) <= 1.0) From a331c361f0b8a096aa3cda60d7b57e5819f8a75c Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Tue, 31 Jan 2023 10:43:30 -0700 Subject: [PATCH 14/45] change variable names --- qiskit/pulse/library/symbolic_pulses.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index 60dc781b3833..390f49fe1417 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1102,6 +1102,7 @@ def GaussianSquareEcho( & \\frac{\\text{duration}}{2} < x\\\\ + References: 1. |citation1|_ @@ -1218,23 +1219,23 @@ def GaussianSquareEcho( ) # gaussian square for active cancellation tone - _center_xy = _duration / 2 + _center_active = _duration / 2 - _sq_t0_xy = _center_xy - _width / 2 - _sq_t1_xy = _center_xy + _width / 2 + _sq_t0_active = _center_active - _width / 2 + _sq_t1_active = _center_active + _width / 2 - _gaussian_ledge_xy = _lifted_gaussian(_t, _sq_t0_xy, -1, _sigma) - _gaussian_redge_xy = _lifted_gaussian(_t, _sq_t1_xy, _duration + 1, _sigma) + _gaussian_ledge_active = _lifted_gaussian(_t, _sq_t0_active, -1, _sigma) + _gaussian_redge_active = _lifted_gaussian(_t, _sq_t1_active, _duration + 1, _sigma) - envelope_expr_xy = ( + envelope_expr_active = ( _active_amp * sym.exp(sym.I * _active_angle) * sym.Piecewise( - (_gaussian_ledge_xy, _t <= _sq_t0_xy), (_gaussian_redge_xy, _t >= _sq_t1_xy), (1, True) + (_gaussian_ledge_active, _t <= _sq_t0_active), (_gaussian_redge_active, _t >= _sq_t1_active), (1, True) ) ) - envelop_expr_total = envelope_expr + envelope_expr_xy + envelop_expr_total = envelope_expr + envelope_expr_active consts_expr = sym.And( _sigma > 0, _width >= 0, _duration >= _width, _duration / 2 >= _width_echo From 0d695dcb8be9d49b989af0236b99690efeb858f1 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Tue, 31 Jan 2023 10:56:15 -0700 Subject: [PATCH 15/45] fix lint --- qiskit/pulse/library/symbolic_pulses.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index 390f49fe1417..7f17ba02ca9a 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1080,7 +1080,7 @@ def GaussianSquareEcho( name: Optional[str] = None, limit_amplitude: Optional[bool] = None, ) -> SymbolicPulse: - """An echoed Gaussian square pulse with an active tone overlaid on it. + """An echoed Gaussian square pulse with an active tone overlaid on it. Exactly one of the ``risefall_sigma_ratio`` and ``width`` parameters has to be specified. If ``risefall_sigma_ratio`` is not ``None`` and ``width`` is ``None``: .. math:: @@ -1089,17 +1089,19 @@ def GaussianSquareEcho( If ``width`` is not None and ``risefall_sigma_ratio`` is None: .. math:: \\text{risefall} = \\frac{\\text{duration} - \\text{width}}{2} The Gaussian Square Echo pulse is composed of three pulses. First, a Gaussian Square pulse - :math: `f_{echo}(x)` with amplitude ``amp`` and phase ``angle`` playing for half duration, followed by a second - Gaussian Square pulse :math: `-f_{echo}(x)` with opposite amplitude and same phase playing for the rest of the - duration. Third a Gaussian Square pulse :math: `f_{active}(x)` with amplitude ``active_amp`` and phase ``active_angle`` - playing for the entire duration. The Gaussian Square Echo pulse :math: `g_e()` can be written as: + :math: `f_{echo}(x)` with amplitude ``amp`` and phase ``angle`` playing for half duration, + followed by a second Gaussian Square pulse :math: `-f_{echo}(x)` with opposite amplitude + and same phase playing for the rest of the duration. Third a Gaussian Square pulse + :math: `f_{active}(x)` with amplitude ``active_amp`` and phase ``active_angle`` + playing for the entire duration. The Gaussian Square Echo pulse :math: `g_e()` + can be written as: .. math:: g_e(x) &= \\begin{cases}\ f_{\\text{active}} + f_{\\text{echo}}(x)\ & x < \\frac{\\text{duration}}{2}\\\\ f_{\\text{active}} - f_{\\text{echo}}(x)\ - & \\frac{\\text{duration}}{2} < x\\\\ + & \\frac{\\text{duration}}{2} < x\\\\ @@ -1108,11 +1110,10 @@ def GaussianSquareEcho( .. _citation1: https://journals.aps.org/prxquantum/abstract/10.1103/PRXQuantum.1.020318 - .. |citation1| replace:: *Sundaresan, N., Lauer, I., Pritchett, E., + .. |citation1| replace:: *Sundaresan, N., Lauer, I., Pritchett, E., Magesan, E., Jurcevic, P. & Gambetta, J. M. Reducing Unitary and Spectator Errors in Cross Resonance with Optimized Rotary Echoes. PRX Quantum 1, 020318 (2020).* - Args: duration: Pulse length in terms of the sampling period `dt`. amp: The amplitude of the rise and fall and of the square pulse. @@ -1231,7 +1232,9 @@ def GaussianSquareEcho( _active_amp * sym.exp(sym.I * _active_angle) * sym.Piecewise( - (_gaussian_ledge_active, _t <= _sq_t0_active), (_gaussian_redge_active, _t >= _sq_t1_active), (1, True) + (_gaussian_ledge_active, _t <= _sq_t0_active), + (_gaussian_redge_active, _t >= _sq_t1_active), + (1, True), ) ) From bfd2c5d44c73130fecf1a810f628ebb6fab12d23 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Tue, 31 Jan 2023 12:18:11 -0700 Subject: [PATCH 16/45] remove width_echo parameter --- qiskit/pulse/library/symbolic_pulses.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index 7f17ba02ca9a..137e7dd5358a 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1145,21 +1145,14 @@ def GaussianSquareEcho( " but not both." ) - if width is not None and risefall_sigma_ratio is None: - total_risefall = duration - width - echo_risefall = 2 * total_risefall - width_echo = (duration - echo_risefall) / 2 - if width is None and risefall_sigma_ratio is not None: width = duration - 2.0 * risefall_sigma_ratio * sigma - width_echo = (duration - 4.0 * risefall_sigma_ratio * sigma) / 2 parameters = { "amp": amp, "angle": angle, "sigma": sigma, "width": width, - "width_echo": width_echo, "active_amp": active_amp, "active_angle": active_angle, } @@ -1172,14 +1165,15 @@ def GaussianSquareEcho( _sigma, _active_amp, _width, - _width_echo, _angle, _active_angle, - ) = sym.symbols("t, duration, amp, sigma, active_amp, width, width_echo, angle, active_angle") + ) = sym.symbols("t, duration, amp, sigma, active_amp, width, angle, active_angle") # gaussian square echo for rotary tone _center = _duration / 4 + _width_echo = (_duration - 2*(_duration - _width))/2 + _sq_t0 = _center - _width_echo / 2 _sq_t1 = _center + _width_echo / 2 From 7d79645fad1750e4b5dd9a1016f33fa131dc0253 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Tue, 31 Jan 2023 13:35:23 -0700 Subject: [PATCH 17/45] fix lint --- qiskit/pulse/library/symbolic_pulses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index 137e7dd5358a..35524291cbd9 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1172,7 +1172,7 @@ def GaussianSquareEcho( # gaussian square echo for rotary tone _center = _duration / 4 - _width_echo = (_duration - 2*(_duration - _width))/2 + _width_echo = (_duration - 2 * (_duration - _width)) / 2 _sq_t0 = _center - _width_echo / 2 _sq_t1 = _center + _width_echo / 2 From 7ff1871cd97e5a019f79d3de110ce3c964875722 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Tue, 31 Jan 2023 14:13:37 -0700 Subject: [PATCH 18/45] add release notes --- .../gaussian-square-echo-pulse-84306f1a02e2bb28.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 releasenotes/notes/gaussian-square-echo-pulse-84306f1a02e2bb28.yaml diff --git a/releasenotes/notes/gaussian-square-echo-pulse-84306f1a02e2bb28.yaml b/releasenotes/notes/gaussian-square-echo-pulse-84306f1a02e2bb28.yaml new file mode 100644 index 000000000000..e209ae8010fa --- /dev/null +++ b/releasenotes/notes/gaussian-square-echo-pulse-84306f1a02e2bb28.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + Add new :meth:`~qiskit.pulse.GaussianSquareEcho` pulse shape. This pulse + is composed by three :class:`~qiskit.pulse.GaussianSquare` pulses. The + first two are echo pulses with duration half of the total duration and + implement rotary tones. The third pulse is a cancellation tone that lasts + the full duration of the pulse and implements correcting single qubit + rotations. \ No newline at end of file From 75427a9d608971224675314eb2bcc6164f40d123 Mon Sep 17 00:00:00 2001 From: Mirko Amico <31739486+miamico@users.noreply.github.com> Date: Wed, 12 Apr 2023 15:01:31 -0700 Subject: [PATCH 19/45] Update first paragraph Co-authored-by: Naoki Kanazawa --- qiskit/pulse/library/symbolic_pulses.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index 35524291cbd9..5cf1c14aabd3 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1081,6 +1081,7 @@ def GaussianSquareEcho( limit_amplitude: Optional[bool] = None, ) -> SymbolicPulse: """An echoed Gaussian square pulse with an active tone overlaid on it. + Exactly one of the ``risefall_sigma_ratio`` and ``width`` parameters has to be specified. If ``risefall_sigma_ratio`` is not ``None`` and ``width`` is ``None``: .. math:: From 310b7274e1a41fed0107b2cb26325229c6342ffd Mon Sep 17 00:00:00 2001 From: Mirko Amico <31739486+miamico@users.noreply.github.com> Date: Wed, 12 Apr 2023 15:14:22 -0700 Subject: [PATCH 20/45] Update amp param to float Co-authored-by: Naoki Kanazawa --- qiskit/pulse/library/symbolic_pulses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index 5cf1c14aabd3..c2b6c9a9114f 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1074,7 +1074,7 @@ def GaussianSquareEcho( sigma: Union[float, ParameterExpression], width: Optional[Union[float, ParameterExpression]] = None, angle: Optional[Union[float, ParameterExpression]] = 0.0, - active_amp: Optional[Union[float, ParameterExpression]] = 0, + active_amp: Optional[Union[float, ParameterExpression]] = 0.0, active_angle: Optional[Union[float, ParameterExpression]] = 0, risefall_sigma_ratio: Optional[Union[float, ParameterExpression]] = None, name: Optional[str] = None, From f3c1e50083b4292b2e7635eb026acf839fa5b05a Mon Sep 17 00:00:00 2001 From: Mirko Amico <31739486+miamico@users.noreply.github.com> Date: Wed, 12 Apr 2023 15:14:50 -0700 Subject: [PATCH 21/45] Update angle def param to float Co-authored-by: Naoki Kanazawa --- qiskit/pulse/library/symbolic_pulses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index c2b6c9a9114f..41ee69bbfe94 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1075,7 +1075,7 @@ def GaussianSquareEcho( width: Optional[Union[float, ParameterExpression]] = None, angle: Optional[Union[float, ParameterExpression]] = 0.0, active_amp: Optional[Union[float, ParameterExpression]] = 0.0, - active_angle: Optional[Union[float, ParameterExpression]] = 0, + active_angle: Optional[Union[float, ParameterExpression]] = 0.0, risefall_sigma_ratio: Optional[Union[float, ParameterExpression]] = None, name: Optional[str] = None, limit_amplitude: Optional[bool] = None, From b18df1156f546d09cbc3df789ce8e6c968196b79 Mon Sep 17 00:00:00 2001 From: Mirko Amico <31739486+miamico@users.noreply.github.com> Date: Wed, 12 Apr 2023 15:15:37 -0700 Subject: [PATCH 22/45] Update amp constrain Co-authored-by: Naoki Kanazawa --- qiskit/pulse/library/symbolic_pulses.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index 41ee69bbfe94..440e5ef6e2bb 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1240,8 +1240,7 @@ def GaussianSquareEcho( ) # Check validity of amplitudes - valid_amp_conditions_expr = sym.And(sym.Abs(_amp) <= 1.0) - valid_amp_conditions_expr = sym.And(sym.Abs(_active_amp) <= 1.0) + valid_amp_conditions_expr = sym.And(sym.Abs(_amp) + sym.Abs(_active_amp) <= 1.0) instance = SymbolicPulse( pulse_type="GaussianSquareEcho", From 877b285eb5b54d4a93358433a2dc90fedfa5cf47 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Wed, 12 Apr 2023 16:38:47 -0600 Subject: [PATCH 23/45] drafting tests --- test/python/pulse/test_pulse_lib.py | 118 ++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index a16aee744aa3..92d9d77391a4 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -24,6 +24,7 @@ Gaussian, GaussianSquare, GaussianSquareDrag, + GaussianSquareEcho, Drag, Sin, Cos, @@ -278,6 +279,86 @@ def test_gaussian_square_drag_validation(self): with self.assertRaises(PulseError): GaussianSquareDrag(duration=50, width=0, sigma=4, amp=0.8, beta=-20) + def test_gaussian_square_echo_pulse(self): + """Test that GaussianSquareEcho sample pulse matches expectations. + + Test that the real part of the envelop matches GaussianSquare and that + the rise and fall match Drag. + """ + risefall = 32 + sigma = 4 + amp = 0.5 + width = 100 + duration = width + 2 * risefall + + gse = GaussianSquareEcho(duration=duration, sigma=sigma, amp=amp, width=width) + gse_samples = gse.get_waveform().samples + + + ### CONSTRUCT GAUSSIAN SQUARE ECHO PULSE FROM EXISTING PULSES AND COMPARE + # gs_pulse = GaussianSquare(duration=duration, sigma=sigma, amp=amp, width=width) + # np.testing.assert_almost_equal( + # np.real(gse_samples), + # np.real(gs_pulse.get_waveform().samples), + # ) + # gsd2 = GaussianSquareEcho( + # duration=duration, + # sigma=sigma, + # amp=amp, + # beta=beta, + # risefall_sigma_ratio=risefall / sigma, + # ) + # np.testing.assert_almost_equal( + # gsd_samples, + # gsd2.get_waveform().samples, + # ) + + # drag_pulse = Drag(duration=2 * risefall, amp=amp, sigma=sigma, beta=beta) + # np.testing.assert_almost_equal( + # gsd_samples[:risefall], + # drag_pulse.get_waveform().samples[:risefall], + # ) + # np.testing.assert_almost_equal( + # gsd_samples[-risefall:], + # drag_pulse.get_waveform().samples[-risefall:], + # ) + + + def test_gaussian_square_echo_active_amp_validation(self): + """Test gaussian square echo active amp parameter validation.""" + + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=.2) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=.4) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.5, active_amp=.8) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-1, active_amp=.2) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=-.2) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=.6) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-0.5, angle=1.5, active_amp=.25) + with self.assertRaises(PulseError): + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=.2) + with self.assertRaises(PulseError): + GaussianSquareEcho(duration=50, width=0, sigma=4, amp=0.8, active_amp=.2) + with self.assertRaises(PulseError): + GaussianSquareEcho(duration=50, width=0, sigma=4, amp=0.8, active_amp=-.2) + + + def test_gaussian_square_echo_active_angle_validation(self): + """Test gaussian square echo active angle parameter validation.""" + + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=.2, active_angle=20) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=.4, active_angle=40) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.5, active_amp=.8, active_angle=270) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-1, active_amp=.2, active_angle=210) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=-.2, active_angle=120) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=.6, active_angle=350) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-0.5, angle=1.5, active_amp=.25, active_angle=500) + with self.assertRaises(PulseError): + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=.2, active_angle=20) + with self.assertRaises(PulseError): + GaussianSquareEcho(duration=50, width=0, sigma=4, amp=0.8, active_amp=.2, active_angle=210) + with self.assertRaises(PulseError): + GaussianSquareEcho(duration=50, width=0, sigma=4, amp=0.8, active_amp=-.2, active_angle=320) + def test_drag_pulse(self): """Test that the Drag sample pulse matches the pulse library.""" drag = Drag(duration=25, sigma=4, amp=0.5j, beta=1) @@ -441,6 +522,16 @@ def test_repr(self): repr(gsd), "GaussianSquareDrag(duration=20, sigma=30, width=14.0, beta=1, amp=1.0, angle=0.0)", ) + gse = GaussianSquareEcho(duration=20, sigma=30, amp=1.0, width=3) + self.assertEqual( + repr(gse), + "GaussianSquareEcho(duration=20, sigma=30, width=3, amp=1.0, angle=0.0, active_amp=0.0, active_angle=0.0)", + ) + gse = GaussianSquareEcho(duration=20, sigma=30, amp=1.0, risefall_sigma_ratio=0.1) + self.assertEqual( + repr(gse), + "GaussianSquareEcho(duration=20, sigma=30, width=14.0, amp=1.0, angle=0.0, active_amp=0.0, active_angle=0.0)", + ) drag = Drag(duration=5, amp=0.5, sigma=7, beta=1) self.assertEqual(repr(drag), "Drag(duration=5, sigma=7, beta=1, amp=0.5, angle=0)") const = Constant(duration=150, amp=0.1, angle=0.3) @@ -466,6 +557,14 @@ def test_param_validation(self): with self.assertRaises(PulseError): GaussianSquareDrag(duration=150, amp=0.2, sigma=8, risefall_sigma_ratio=10, beta=1) + + with self.assertRaises(PulseError): + GaussianSquareEcho(duration=150, amp=0.2, sigma=8,) + with self.assertRaises(PulseError): + GaussianSquareEcho(duration=150, amp=0.2, sigma=8, width=160) + with self.assertRaises(PulseError): + GaussianSquareEcho(duration=150, amp=0.2, sigma=8, risefall_sigma_ratio=10) + with self.assertRaises(PulseError): Constant(duration=150, amp=0.9 + 0.8j) with self.assertRaises(PulseError): @@ -526,6 +625,25 @@ def test_gaussian_square_drag_limit_amplitude_per_instance(self): ) self.assertGreater(np.abs(waveform.amp), 1.0) + def test_gaussian_square_echo_limit_amplitude(self): + """Test that the check for amplitude less than or equal to 1 can be disabled.""" + with self.assertRaises(PulseError): + GaussianSquareEcho(duration=100, sigma=1.0, amp=1.1, width=10) + + with patch("qiskit.pulse.library.pulse.Pulse.limit_amplitude", new=False): + waveform = GaussianSquareEcho(duration=100, sigma=1.0, amp=1.1, width=10) + self.assertGreater(np.abs(waveform.amp), 1.0) + + def test_gaussian_square_echo_limit_amplitude_per_instance(self): + """Test that the check for amplitude per instance.""" + with self.assertRaises(PulseError): + GaussianSquareEcho(duration=100, sigma=1.0, amp=1.1, width=10) + + waveform = GaussianSquareEcho( + duration=100, sigma=1.0, amp=1.1, width=10, limit_amplitude=False + ) + self.assertGreater(np.abs(waveform.amp), 1.0) + def test_drag_limit_amplitude(self): """Test that the check for amplitude less than or equal to 1 can be disabled.""" with self.assertRaises(PulseError): From 16f2b33f4e800cfb166a40c9f2e4efc66b71b9cb Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Wed, 12 Apr 2023 16:42:39 -0600 Subject: [PATCH 24/45] update docstring --- test/python/pulse/test_pulse_lib.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index 92d9d77391a4..5d15493f2c39 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -282,8 +282,11 @@ def test_gaussian_square_drag_validation(self): def test_gaussian_square_echo_pulse(self): """Test that GaussianSquareEcho sample pulse matches expectations. - Test that the real part of the envelop matches GaussianSquare and that - the rise and fall match Drag. + Test that the real part of the envelop matches GaussianSquare with + given amplitude and phase active for half duration with another + GaussianSquare active for the other half duration with opposite + amplitude and a GaussianSquare active on the entire duration with + its own amplitude and phase """ risefall = 32 sigma = 4 From bba03a0fc7ed7414d3a3e095d77349cdfa36594d Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Wed, 12 Apr 2023 16:56:24 -0600 Subject: [PATCH 25/45] finish up test --- test/python/pulse/test_pulse_lib.py | 44 +++++++++++------------------ 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index 5d15493f2c39..cff51fd0e7be 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -293,38 +293,26 @@ def test_gaussian_square_echo_pulse(self): amp = 0.5 width = 100 duration = width + 2 * risefall + active_amp = 0.1 + width_echo = (duration - 2 * (duration - width)) / 2 - gse = GaussianSquareEcho(duration=duration, sigma=sigma, amp=amp, width=width) + gse = GaussianSquareEcho(duration=duration, sigma=sigma, amp=amp, width=width, active_amp=active_amp) gse_samples = gse.get_waveform().samples - ### CONSTRUCT GAUSSIAN SQUARE ECHO PULSE FROM EXISTING PULSES AND COMPARE - # gs_pulse = GaussianSquare(duration=duration, sigma=sigma, amp=amp, width=width) - # np.testing.assert_almost_equal( - # np.real(gse_samples), - # np.real(gs_pulse.get_waveform().samples), - # ) - # gsd2 = GaussianSquareEcho( - # duration=duration, - # sigma=sigma, - # amp=amp, - # beta=beta, - # risefall_sigma_ratio=risefall / sigma, - # ) - # np.testing.assert_almost_equal( - # gsd_samples, - # gsd2.get_waveform().samples, - # ) - - # drag_pulse = Drag(duration=2 * risefall, amp=amp, sigma=sigma, beta=beta) - # np.testing.assert_almost_equal( - # gsd_samples[:risefall], - # drag_pulse.get_waveform().samples[:risefall], - # ) - # np.testing.assert_almost_equal( - # gsd_samples[-risefall:], - # drag_pulse.get_waveform().samples[-risefall:], - # ) + gs_echo_pulse_pos = GaussianSquare(duration=duration/2, sigma=sigma, amp=amp, width=width_echo) + gs_echo_pulse_neg = GaussianSquare(duration=duration/2, sigma=sigma, amp=amp, width=width_echo) + gs_active_pulse = GaussianSquare(duration=duration, sigma=sigma, amp=active_amp, width=width) + gs_echo_pulse_pos_samples = np.array(gs_echo_pulse_pos.get_waveform().samples.tolist() + [0] * int(duration/2)) + gs_echo_pulse_neg_samples = np.array([0] * int(duration/2) + gs_echo_pulse_neg.get_waveform().samples.tolist()) + gs_active_pulse_samples = gs_active_pulse.get_waveform().samples + + + + np.testing.assert_almost_equal( + gse_samples, + gs_echo_pulse_pos_samples+gs_echo_pulse_neg_samples+gs_active_pulse_samples, + ) def test_gaussian_square_echo_active_amp_validation(self): From 4ead034ed17bc2737d0035a00bce444cad1f6e12 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Thu, 13 Apr 2023 09:25:25 -0600 Subject: [PATCH 26/45] fix missing pulses from init --- qiskit/pulse/__init__.py | 2 ++ qiskit/pulse/library/__init__.py | 1 + 2 files changed, 3 insertions(+) diff --git a/qiskit/pulse/__init__.py b/qiskit/pulse/__init__.py index a4b23f66b585..9dc12a82a6d2 100644 --- a/qiskit/pulse/__init__.py +++ b/qiskit/pulse/__init__.py @@ -143,6 +143,8 @@ GaussianSquare, GaussianSquareDrag, GaussianSquareEcho, + Sawtooth, + Triangle, ParametricPulse, SymbolicPulse, ScalableSymbolicPulse, diff --git a/qiskit/pulse/library/__init__.py b/qiskit/pulse/library/__init__.py index 81b9bc793e60..8815c6397be3 100644 --- a/qiskit/pulse/library/__init__.py +++ b/qiskit/pulse/library/__init__.py @@ -93,6 +93,7 @@ Gaussian GaussianSquare GaussianSquareDrag + GaussianSquareEcho Sin Cos Sawtooth From d0125800e6d9014b58298aef70e7b9ad8b0eba17 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Thu, 13 Apr 2023 11:30:34 -0600 Subject: [PATCH 27/45] addding Cos --- qiskit/pulse/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qiskit/pulse/__init__.py b/qiskit/pulse/__init__.py index 9dc12a82a6d2..a4535818d484 100644 --- a/qiskit/pulse/__init__.py +++ b/qiskit/pulse/__init__.py @@ -143,6 +143,7 @@ GaussianSquare, GaussianSquareDrag, GaussianSquareEcho, + Cos, Sawtooth, Triangle, ParametricPulse, From 061aac4486831e7829ccc3a72dc8201024c37f17 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Fri, 14 Apr 2023 06:43:37 -0600 Subject: [PATCH 28/45] missing sin --- qiskit/pulse/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qiskit/pulse/__init__.py b/qiskit/pulse/__init__.py index a4535818d484..edf47334a8b2 100644 --- a/qiskit/pulse/__init__.py +++ b/qiskit/pulse/__init__.py @@ -143,6 +143,7 @@ GaussianSquare, GaussianSquareDrag, GaussianSquareEcho, + Sin, Cos, Sawtooth, Triangle, From d0d04ce3cf9d5a56badca1215826152cc7dd060d Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Fri, 14 Apr 2023 15:55:15 -0600 Subject: [PATCH 29/45] fixed tests --- test/python/pulse/test_pulse_lib.py | 35 +++++++---------------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index cff51fd0e7be..a807a5139e79 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -301,7 +301,7 @@ def test_gaussian_square_echo_pulse(self): gs_echo_pulse_pos = GaussianSquare(duration=duration/2, sigma=sigma, amp=amp, width=width_echo) - gs_echo_pulse_neg = GaussianSquare(duration=duration/2, sigma=sigma, amp=amp, width=width_echo) + gs_echo_pulse_neg = GaussianSquare(duration=duration/2, sigma=sigma, amp=-amp, width=width_echo) gs_active_pulse = GaussianSquare(duration=duration, sigma=sigma, amp=active_amp, width=width) gs_echo_pulse_pos_samples = np.array(gs_echo_pulse_pos.get_waveform().samples.tolist() + [0] * int(duration/2)) gs_echo_pulse_neg_samples = np.array([0] * int(duration/2) + gs_echo_pulse_neg.get_waveform().samples.tolist()) @@ -326,29 +326,10 @@ def test_gaussian_square_echo_active_amp_validation(self): GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=.6) GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-0.5, angle=1.5, active_amp=.25) with self.assertRaises(PulseError): - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=.2) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=.1, active_amp=1.1) with self.assertRaises(PulseError): - GaussianSquareEcho(duration=50, width=0, sigma=4, amp=0.8, active_amp=.2) - with self.assertRaises(PulseError): - GaussianSquareEcho(duration=50, width=0, sigma=4, amp=0.8, active_amp=-.2) - - - def test_gaussian_square_echo_active_angle_validation(self): - """Test gaussian square echo active angle parameter validation.""" + GaussianSquareEcho(duration=50, width=0, sigma=4, amp=-0.8, active_amp=-.3) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=.2, active_angle=20) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=.4, active_angle=40) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.5, active_amp=.8, active_angle=270) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-1, active_amp=.2, active_angle=210) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=-.2, active_angle=120) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=.6, active_angle=350) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-0.5, angle=1.5, active_amp=.25, active_angle=500) - with self.assertRaises(PulseError): - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=.2, active_angle=20) - with self.assertRaises(PulseError): - GaussianSquareEcho(duration=50, width=0, sigma=4, amp=0.8, active_amp=.2, active_angle=210) - with self.assertRaises(PulseError): - GaussianSquareEcho(duration=50, width=0, sigma=4, amp=0.8, active_amp=-.2, active_angle=320) def test_drag_pulse(self): """Test that the Drag sample pulse matches the pulse library.""" @@ -516,12 +497,12 @@ def test_repr(self): gse = GaussianSquareEcho(duration=20, sigma=30, amp=1.0, width=3) self.assertEqual( repr(gse), - "GaussianSquareEcho(duration=20, sigma=30, width=3, amp=1.0, angle=0.0, active_amp=0.0, active_angle=0.0)", + "GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=3, active_amp=0.0, active_angle=0.0)", ) gse = GaussianSquareEcho(duration=20, sigma=30, amp=1.0, risefall_sigma_ratio=0.1) self.assertEqual( repr(gse), - "GaussianSquareEcho(duration=20, sigma=30, width=14.0, amp=1.0, angle=0.0, active_amp=0.0, active_angle=0.0)", + "GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=14.0, active_amp=0.0, active_angle=0.0)", ) drag = Drag(duration=5, amp=0.5, sigma=7, beta=1) self.assertEqual(repr(drag), "Drag(duration=5, sigma=7, beta=1, amp=0.5, angle=0)") @@ -619,7 +600,7 @@ def test_gaussian_square_drag_limit_amplitude_per_instance(self): def test_gaussian_square_echo_limit_amplitude(self): """Test that the check for amplitude less than or equal to 1 can be disabled.""" with self.assertRaises(PulseError): - GaussianSquareEcho(duration=100, sigma=1.0, amp=1.1, width=10) + GaussianSquareEcho(duration=1000, sigma=4.0, amp=1.01, width=100) with patch("qiskit.pulse.library.pulse.Pulse.limit_amplitude", new=False): waveform = GaussianSquareEcho(duration=100, sigma=1.0, amp=1.1, width=10) @@ -628,10 +609,10 @@ def test_gaussian_square_echo_limit_amplitude(self): def test_gaussian_square_echo_limit_amplitude_per_instance(self): """Test that the check for amplitude per instance.""" with self.assertRaises(PulseError): - GaussianSquareEcho(duration=100, sigma=1.0, amp=1.1, width=10) + GaussianSquareEcho(duration=1000, sigma=4.0, amp=1.01, width=100) waveform = GaussianSquareEcho( - duration=100, sigma=1.0, amp=1.1, width=10, limit_amplitude=False + duration=1000, sigma=4.0, amp=1.01, width=100, limit_amplitude=False ) self.assertGreater(np.abs(waveform.amp), 1.0) From 050c00170ab9ad9dbc09b3260c153fbf821c9b57 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Mon, 17 Apr 2023 10:12:48 -0600 Subject: [PATCH 30/45] black formatting --- test/python/pulse/test_pulse_lib.py | 66 +++++++++++++++++------------ 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index a807a5139e79..1c58bcfe2390 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -283,10 +283,10 @@ def test_gaussian_square_echo_pulse(self): """Test that GaussianSquareEcho sample pulse matches expectations. Test that the real part of the envelop matches GaussianSquare with - given amplitude and phase active for half duration with another - GaussianSquare active for the other half duration with opposite + given amplitude and phase active for half duration with another + GaussianSquare active for the other half duration with opposite amplitude and a GaussianSquare active on the entire duration with - its own amplitude and phase + its own amplitude and phase """ risefall = 32 sigma = 4 @@ -294,42 +294,49 @@ def test_gaussian_square_echo_pulse(self): width = 100 duration = width + 2 * risefall active_amp = 0.1 - width_echo = (duration - 2 * (duration - width)) / 2 + width_echo = (duration - 2 * (duration - width)) / 2 - gse = GaussianSquareEcho(duration=duration, sigma=sigma, amp=amp, width=width, active_amp=active_amp) + gse = GaussianSquareEcho( + duration=duration, sigma=sigma, amp=amp, width=width, active_amp=active_amp + ) gse_samples = gse.get_waveform().samples - - gs_echo_pulse_pos = GaussianSquare(duration=duration/2, sigma=sigma, amp=amp, width=width_echo) - gs_echo_pulse_neg = GaussianSquare(duration=duration/2, sigma=sigma, amp=-amp, width=width_echo) - gs_active_pulse = GaussianSquare(duration=duration, sigma=sigma, amp=active_amp, width=width) - gs_echo_pulse_pos_samples = np.array(gs_echo_pulse_pos.get_waveform().samples.tolist() + [0] * int(duration/2)) - gs_echo_pulse_neg_samples = np.array([0] * int(duration/2) + gs_echo_pulse_neg.get_waveform().samples.tolist()) + gs_echo_pulse_pos = GaussianSquare( + duration=duration / 2, sigma=sigma, amp=amp, width=width_echo + ) + gs_echo_pulse_neg = GaussianSquare( + duration=duration / 2, sigma=sigma, amp=-amp, width=width_echo + ) + gs_active_pulse = GaussianSquare( + duration=duration, sigma=sigma, amp=active_amp, width=width + ) + gs_echo_pulse_pos_samples = np.array( + gs_echo_pulse_pos.get_waveform().samples.tolist() + [0] * int(duration / 2) + ) + gs_echo_pulse_neg_samples = np.array( + [0] * int(duration / 2) + gs_echo_pulse_neg.get_waveform().samples.tolist() + ) gs_active_pulse_samples = gs_active_pulse.get_waveform().samples - - np.testing.assert_almost_equal( gse_samples, - gs_echo_pulse_pos_samples+gs_echo_pulse_neg_samples+gs_active_pulse_samples, + gs_echo_pulse_pos_samples + gs_echo_pulse_neg_samples + gs_active_pulse_samples, ) - def test_gaussian_square_echo_active_amp_validation(self): """Test gaussian square echo active amp parameter validation.""" - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=.2) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=.4) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.5, active_amp=.8) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-1, active_amp=.2) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=-.2) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=.6) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-0.5, angle=1.5, active_amp=.25) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=0.2) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=0.4) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.5, active_amp=0.8) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-1, active_amp=0.2) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=-0.2) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=0.6) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-0.5, angle=1.5, active_amp=0.25) with self.assertRaises(PulseError): - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=.1, active_amp=1.1) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.1, active_amp=1.1) with self.assertRaises(PulseError): - GaussianSquareEcho(duration=50, width=0, sigma=4, amp=-0.8, active_amp=-.3) - + GaussianSquareEcho(duration=50, width=0, sigma=4, amp=-0.8, active_amp=-0.3) def test_drag_pulse(self): """Test that the Drag sample pulse matches the pulse library.""" @@ -529,9 +536,12 @@ def test_param_validation(self): with self.assertRaises(PulseError): GaussianSquareDrag(duration=150, amp=0.2, sigma=8, risefall_sigma_ratio=10, beta=1) - with self.assertRaises(PulseError): - GaussianSquareEcho(duration=150, amp=0.2, sigma=8,) + GaussianSquareEcho( + duration=150, + amp=0.2, + sigma=8, + ) with self.assertRaises(PulseError): GaussianSquareEcho(duration=150, amp=0.2, sigma=8, width=160) with self.assertRaises(PulseError): @@ -603,7 +613,7 @@ def test_gaussian_square_echo_limit_amplitude(self): GaussianSquareEcho(duration=1000, sigma=4.0, amp=1.01, width=100) with patch("qiskit.pulse.library.pulse.Pulse.limit_amplitude", new=False): - waveform = GaussianSquareEcho(duration=100, sigma=1.0, amp=1.1, width=10) + waveform = GaussianSquareEcho(duration=100, sigma=1.0, amp=1.1, width=10) self.assertGreater(np.abs(waveform.amp), 1.0) def test_gaussian_square_echo_limit_amplitude_per_instance(self): From fab388bac9ce9dca7df7391e58238808600e21a9 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Mon, 17 Apr 2023 12:16:26 -0600 Subject: [PATCH 31/45] break string --- test/python/pulse/test_pulse_lib.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index 1c58bcfe2390..62348974070a 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -504,12 +504,14 @@ def test_repr(self): gse = GaussianSquareEcho(duration=20, sigma=30, amp=1.0, width=3) self.assertEqual( repr(gse), - "GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=3, active_amp=0.0, active_angle=0.0)", + """GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=3, active_amp=0.0, + active_angle=0.0)""", ) gse = GaussianSquareEcho(duration=20, sigma=30, amp=1.0, risefall_sigma_ratio=0.1) self.assertEqual( repr(gse), - "GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=14.0, active_amp=0.0, active_angle=0.0)", + """GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=14.0, active_amp=0.0, + active_angle=0.0)""", ) drag = Drag(duration=5, amp=0.5, sigma=7, beta=1) self.assertEqual(repr(drag), "Drag(duration=5, sigma=7, beta=1, amp=0.5, angle=0)") From cb0749e3399f80ad8b2439997d44f643da3ae0cf Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Mon, 17 Apr 2023 12:18:39 -0600 Subject: [PATCH 32/45] fixing strings --- test/python/pulse/test_pulse_lib.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index 62348974070a..8bc276765493 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -504,14 +504,14 @@ def test_repr(self): gse = GaussianSquareEcho(duration=20, sigma=30, amp=1.0, width=3) self.assertEqual( repr(gse), - """GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=3, active_amp=0.0, - active_angle=0.0)""", + ("GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=3, active_amp=0.0," + "active_angle=0.0)"), ) gse = GaussianSquareEcho(duration=20, sigma=30, amp=1.0, risefall_sigma_ratio=0.1) self.assertEqual( repr(gse), - """GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=14.0, active_amp=0.0, - active_angle=0.0)""", + ("GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=14.0, active_amp=0.0," + "active_angle=0.0)"), ) drag = Drag(duration=5, amp=0.5, sigma=7, beta=1) self.assertEqual(repr(drag), "Drag(duration=5, sigma=7, beta=1, amp=0.5, angle=0)") From 170370ed5fbe52b2043ae0c2eb14da0e17b26140 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Mon, 17 Apr 2023 12:20:16 -0600 Subject: [PATCH 33/45] reformatting --- test/python/pulse/test_pulse_lib.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index 8bc276765493..9d413723efe0 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -504,14 +504,18 @@ def test_repr(self): gse = GaussianSquareEcho(duration=20, sigma=30, amp=1.0, width=3) self.assertEqual( repr(gse), - ("GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=3, active_amp=0.0," - "active_angle=0.0)"), + ( + "GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=3, active_amp=0.0," + "active_angle=0.0)" + ), ) gse = GaussianSquareEcho(duration=20, sigma=30, amp=1.0, risefall_sigma_ratio=0.1) self.assertEqual( repr(gse), - ("GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=14.0, active_amp=0.0," - "active_angle=0.0)"), + ( + "GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=14.0, active_amp=0.0," + "active_angle=0.0)" + ), ) drag = Drag(duration=5, amp=0.5, sigma=7, beta=1) self.assertEqual(repr(drag), "Drag(duration=5, sigma=7, beta=1, amp=0.5, angle=0)") From 92c4b291bea689da8fee33ccaede30d869551473 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Mon, 17 Apr 2023 12:22:22 -0600 Subject: [PATCH 34/45] fix lint --- test/python/pulse/test_pulse_lib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index 9d413723efe0..4518ef2203c3 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -513,8 +513,8 @@ def test_repr(self): self.assertEqual( repr(gse), ( - "GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=14.0, active_amp=0.0," - "active_angle=0.0)" + "GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=14.0," + "active_amp=0.0, active_angle=0.0)" ), ) drag = Drag(duration=5, amp=0.5, sigma=7, beta=1) From 17944ca1a51ae51337d96672edf404e1ba039d0b Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Mon, 17 Apr 2023 12:24:25 -0600 Subject: [PATCH 35/45] fixing last things --- test/python/pulse/test_pulse_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index 4518ef2203c3..2f4d333de86c 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -513,7 +513,7 @@ def test_repr(self): self.assertEqual( repr(gse), ( - "GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=14.0," + "GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=14.0," "active_amp=0.0, active_angle=0.0)" ), ) From ecc70c619311f2073a6e3c4f30b10deb8507cddd Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Mon, 17 Apr 2023 12:59:37 -0600 Subject: [PATCH 36/45] fix tests --- test/python/pulse/test_pulse_lib.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index 2f4d333de86c..5cd0cffa3af5 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -326,12 +326,12 @@ def test_gaussian_square_echo_pulse(self): def test_gaussian_square_echo_active_amp_validation(self): """Test gaussian square echo active amp parameter validation.""" - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=0.2) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=0.4) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=.1, active_amp=0.2) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=.1, active_amp=0.4) GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.5, active_amp=0.8) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-1, active_amp=0.2) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=-0.2) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=1, active_amp=0.6) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-.1, active_amp=0.2) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=.1, active_amp=-0.2) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=.1, active_amp=0.6) GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-0.5, angle=1.5, active_amp=0.25) with self.assertRaises(PulseError): GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.1, active_amp=1.1) @@ -506,7 +506,7 @@ def test_repr(self): repr(gse), ( "GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=3, active_amp=0.0," - "active_angle=0.0)" + " active_angle=0.0)" ), ) gse = GaussianSquareEcho(duration=20, sigma=30, amp=1.0, risefall_sigma_ratio=0.1) From 5259ff502d2339e62dd4ca13a24a8f269fcd6c11 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Mon, 17 Apr 2023 14:12:09 -0600 Subject: [PATCH 37/45] fix black --- test/python/pulse/test_pulse_lib.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index 5cd0cffa3af5..7fd0da9d3a5f 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -326,12 +326,12 @@ def test_gaussian_square_echo_pulse(self): def test_gaussian_square_echo_active_amp_validation(self): """Test gaussian square echo active amp parameter validation.""" - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=.1, active_amp=0.2) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=.1, active_amp=0.4) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.1, active_amp=0.2) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.1, active_amp=0.4) GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.5, active_amp=0.8) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-.1, active_amp=0.2) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=.1, active_amp=-0.2) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=.1, active_amp=0.6) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-0.1, active_amp=0.2) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.1, active_amp=-0.2) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.1, active_amp=0.6) GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-0.5, angle=1.5, active_amp=0.25) with self.assertRaises(PulseError): GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.1, active_amp=1.1) From a0575d317bbefcaea3b5dec41d583618cc197a36 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Tue, 18 Apr 2023 06:04:08 -0600 Subject: [PATCH 38/45] fix another test --- test/python/pulse/test_pulse_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index 7fd0da9d3a5f..189e1554a1dc 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -514,7 +514,7 @@ def test_repr(self): repr(gse), ( "GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=14.0," - "active_amp=0.0, active_angle=0.0)" + " active_amp=0.0, active_angle=0.0)" ), ) drag = Drag(duration=5, amp=0.5, sigma=7, beta=1) From bff8bab551e4782d5a4d5579b6d6132d7aada8bf Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Tue, 18 Apr 2023 06:05:59 -0600 Subject: [PATCH 39/45] fix amp validation --- test/python/pulse/test_pulse_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index 189e1554a1dc..1f455e738bed 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -328,7 +328,7 @@ def test_gaussian_square_echo_active_amp_validation(self): GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.1, active_amp=0.2) GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.1, active_amp=0.4) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.5, active_amp=0.8) + GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.5, active_amp=0.3) GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-0.1, active_amp=0.2) GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.1, active_amp=-0.2) GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.1, active_amp=0.6) From 257fd43770ecc0843ae02c2595a9d92b7b9581c6 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Sat, 22 Apr 2023 14:29:29 -0600 Subject: [PATCH 40/45] fixing docstring --- qiskit/pulse/library/symbolic_pulses.py | 61 +++++++++++++++---------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index 440e5ef6e2bb..d35dd327ff29 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1080,50 +1080,65 @@ def GaussianSquareEcho( name: Optional[str] = None, limit_amplitude: Optional[bool] = None, ) -> SymbolicPulse: - """An echoed Gaussian square pulse with an active tone overlaid on it. - - Exactly one of the ``risefall_sigma_ratio`` and ``width`` parameters has to be specified. - If ``risefall_sigma_ratio`` is not ``None`` and ``width`` is ``None``: - .. math:: - \\text{risefall} &= \\text{risefall_sigma_ratio} \\times \\text{sigma}\\\\ - \\text{width} &= \\text{duration} - 2 \\times \\text{risefall} - If ``width`` is not None and ``risefall_sigma_ratio`` is None: - .. math:: \\text{risefall} = \\frac{\\text{duration} - \\text{width}}{2} + """An echoed Gaussian square pulse with an active tone overlaid on it. The ``amp`` and + ``angle`` parameters are used to define the amplitude and phase of the echoed Gaussian + square pulse while the ``active_amp`` and ``active_angle`` parameters are used to + define the active tone. + The Gaussian Square Echo pulse is composed of three pulses. First, a Gaussian Square pulse - :math: `f_{echo}(x)` with amplitude ``amp`` and phase ``angle`` playing for half duration, - followed by a second Gaussian Square pulse :math: `-f_{echo}(x)` with opposite amplitude - and same phase playing for the rest of the duration. Third a Gaussian Square pulse - :math: `f_{active}(x)` with amplitude ``active_amp`` and phase ``active_angle`` - playing for the entire duration. The Gaussian Square Echo pulse :math: `g_e()` + :math:`f_{echo}(x)` with amplitude ``amp`` and phase ``angle`` playing for half duration, + followed by a second Gaussian Square pulse :math:`-f_{echo}(x)` with opposite amplitude + and same phase playing for the rest of the duration. Third a Gaussian Square pulse + :math:`f_{active}(x)` with amplitude ``active_amp`` and phase ``active_angle`` + playing for the entire duration. The Gaussian Square Echo pulse :math:`g_e()` can be written as: + .. math:: g_e(x) &= \\begin{cases}\ f_{\\text{active}} + f_{\\text{echo}}(x)\ & x < \\frac{\\text{duration}}{2}\\\\ f_{\\text{active}} - f_{\\text{echo}}(x)\ - & \\frac{\\text{duration}}{2} < x\\\\ + & \\frac{\\text{duration}}{2} < x\ + \\end{cases}\\\\ + + One case where this pulse can be used is when implementing a direct CNOT gate with + a cross-resonance superconducting qubit architecture. When applying this pulse to + the target qubit, the active portion can be used to cancel IX terms from the + cross-resonance drive while the echo portion can reduce the impact of a static ZZ coupling. + + Exactly one of the ``risefall_sigma_ratio`` and ``width`` parameters has to be specified. + + If ``risefall_sigma_ratio`` is not ``None`` and ``width`` is ``None``: + + .. math:: + \\text{risefall} &= \\text{risefall_sigma_ratio} \\times \\text{sigma}\\\\ + \\text{width} &= \\text{duration} - 2 \\times \\text{risefall} + + If ``width`` is not None and ``risefall_sigma_ratio`` is None: + .. math:: \\text{risefall} = \\frac{\\text{duration} - \\text{width}}{2} References: 1. |citation1|_ - .. _citation1: https://journals.aps.org/prxquantum/abstract/10.1103/PRXQuantum.1.020318 + .. _citation1: https://iopscience.iop.org/article/10.1088/2058-9565/abe519 - .. |citation1| replace:: *Sundaresan, N., Lauer, I., Pritchett, E., - Magesan, E., Jurcevic, P. & Gambetta, J. M. - Reducing Unitary and Spectator Errors in Cross Resonance with - Optimized Rotary Echoes. PRX Quantum 1, 020318 (2020).* + .. |citation1| replace:: *Jurcevic, P., Javadi-Abhari, A., Bishop, L. S., + Lauer, I., Bogorin, D. F., Brink, M., Capelluto, L., G{\"u}nl{\"u}k, O., + Itoko, T., Kanazawa, N. & others + Demonstration of quantum volume 64 on a superconducting quantum + computing system. (Section V)* Args: duration: Pulse length in terms of the sampling period `dt`. - amp: The amplitude of the rise and fall and of the square pulse. + amp: The amplitude of the rise and fall and of the echoed pulse. sigma: A measure of how wide or narrow the risefall is; see the class docstring for more details. width: The duration of the embedded square pulse. angle: The angle in radians of the complex phase factor uniformly - scaling the pulse. Default value 0. - active_amp: The amplitude of the active cancellation tone. + scaling the echoed pulse. Default value 0. + active_amp: The amplitude of the active pulse. active_angle: The angle in radian of the complex phase factor uniformly scaling the active pulse. Default value 0. risefall_sigma_ratio: The ratio of each risefall duration to sigma. From bb744bd1477452d47bf7350dd374724d9391fc79 Mon Sep 17 00:00:00 2001 From: Mirko Amico <31739486+miamico@users.noreply.github.com> Date: Thu, 27 Apr 2023 11:18:21 -0700 Subject: [PATCH 41/45] Update qiskit/pulse/library/symbolic_pulses.py Co-authored-by: Will Shanks --- qiskit/pulse/library/symbolic_pulses.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index d35dd327ff29..aec4cb0efcf7 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1080,10 +1080,7 @@ def GaussianSquareEcho( name: Optional[str] = None, limit_amplitude: Optional[bool] = None, ) -> SymbolicPulse: - """An echoed Gaussian square pulse with an active tone overlaid on it. The ``amp`` and - ``angle`` parameters are used to define the amplitude and phase of the echoed Gaussian - square pulse while the ``active_amp`` and ``active_angle`` parameters are used to - define the active tone. + """An echoed Gaussian square pulse with an active tone overlaid on it. The Gaussian Square Echo pulse is composed of three pulses. First, a Gaussian Square pulse :math:`f_{echo}(x)` with amplitude ``amp`` and phase ``angle`` playing for half duration, From 88f045f83ffa5e43988f076bc4931af6dca1d682 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Tue, 2 May 2023 14:03:55 -0600 Subject: [PATCH 42/45] rename to gaussian_square_echo --- qiskit/pulse/__init__.py | 2 +- qiskit/pulse/library/__init__.py | 4 +-- qiskit/pulse/library/symbolic_pulses.py | 4 +-- test/python/pulse/test_pulse_lib.py | 46 ++++++++++++------------- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/qiskit/pulse/__init__.py b/qiskit/pulse/__init__.py index edf47334a8b2..5fe159b947fb 100644 --- a/qiskit/pulse/__init__.py +++ b/qiskit/pulse/__init__.py @@ -142,7 +142,7 @@ Gaussian, GaussianSquare, GaussianSquareDrag, - GaussianSquareEcho, + gaussian_square_echo, Sin, Cos, Sawtooth, diff --git a/qiskit/pulse/library/__init__.py b/qiskit/pulse/library/__init__.py index 8815c6397be3..b5bc41fb218f 100644 --- a/qiskit/pulse/library/__init__.py +++ b/qiskit/pulse/library/__init__.py @@ -93,7 +93,7 @@ Gaussian GaussianSquare GaussianSquareDrag - GaussianSquareEcho + gaussian_square_echo Sin Cos Sawtooth @@ -123,7 +123,7 @@ Gaussian, GaussianSquare, GaussianSquareDrag, - GaussianSquareEcho, + gaussian_square_echo, Drag, Constant, Sin, diff --git a/qiskit/pulse/library/symbolic_pulses.py b/qiskit/pulse/library/symbolic_pulses.py index d35dd327ff29..963c151ea4fd 100644 --- a/qiskit/pulse/library/symbolic_pulses.py +++ b/qiskit/pulse/library/symbolic_pulses.py @@ -1068,7 +1068,7 @@ def GaussianSquareDrag( return instance -def GaussianSquareEcho( +def gaussian_square_echo( duration: Union[int, ParameterExpression], amp: Union[float, ParameterExpression], sigma: Union[float, ParameterExpression], @@ -1258,7 +1258,7 @@ def GaussianSquareEcho( valid_amp_conditions_expr = sym.And(sym.Abs(_amp) + sym.Abs(_active_amp) <= 1.0) instance = SymbolicPulse( - pulse_type="GaussianSquareEcho", + pulse_type="gaussian_square_echo", duration=duration, parameters=parameters, name=name, diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index 1f455e738bed..b5cdb59685c9 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -24,7 +24,7 @@ Gaussian, GaussianSquare, GaussianSquareDrag, - GaussianSquareEcho, + gaussian_square_echo, Drag, Sin, Cos, @@ -280,7 +280,7 @@ def test_gaussian_square_drag_validation(self): GaussianSquareDrag(duration=50, width=0, sigma=4, amp=0.8, beta=-20) def test_gaussian_square_echo_pulse(self): - """Test that GaussianSquareEcho sample pulse matches expectations. + """Test that gaussian_square_echo sample pulse matches expectations. Test that the real part of the envelop matches GaussianSquare with given amplitude and phase active for half duration with another @@ -296,7 +296,7 @@ def test_gaussian_square_echo_pulse(self): active_amp = 0.1 width_echo = (duration - 2 * (duration - width)) / 2 - gse = GaussianSquareEcho( + gse = gaussian_square_echo( duration=duration, sigma=sigma, amp=amp, width=width, active_amp=active_amp ) gse_samples = gse.get_waveform().samples @@ -326,17 +326,17 @@ def test_gaussian_square_echo_pulse(self): def test_gaussian_square_echo_active_amp_validation(self): """Test gaussian square echo active amp parameter validation.""" - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.1, active_amp=0.2) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.1, active_amp=0.4) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.5, active_amp=0.3) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-0.1, active_amp=0.2) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.1, active_amp=-0.2) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.1, active_amp=0.6) - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=-0.5, angle=1.5, active_amp=0.25) + gaussian_square_echo(duration=50, width=0, sigma=16, amp=0.1, active_amp=0.2) + gaussian_square_echo(duration=50, width=0, sigma=16, amp=0.1, active_amp=0.4) + gaussian_square_echo(duration=50, width=0, sigma=16, amp=0.5, active_amp=0.3) + gaussian_square_echo(duration=50, width=0, sigma=16, amp=-0.1, active_amp=0.2) + gaussian_square_echo(duration=50, width=0, sigma=16, amp=0.1, active_amp=-0.2) + gaussian_square_echo(duration=50, width=0, sigma=16, amp=0.1, active_amp=0.6) + gaussian_square_echo(duration=50, width=0, sigma=16, amp=-0.5, angle=1.5, active_amp=0.25) with self.assertRaises(PulseError): - GaussianSquareEcho(duration=50, width=0, sigma=16, amp=0.1, active_amp=1.1) + gaussian_square_echo(duration=50, width=0, sigma=16, amp=0.1, active_amp=1.1) with self.assertRaises(PulseError): - GaussianSquareEcho(duration=50, width=0, sigma=4, amp=-0.8, active_amp=-0.3) + gaussian_square_echo(duration=50, width=0, sigma=4, amp=-0.8, active_amp=-0.3) def test_drag_pulse(self): """Test that the Drag sample pulse matches the pulse library.""" @@ -501,19 +501,19 @@ def test_repr(self): repr(gsd), "GaussianSquareDrag(duration=20, sigma=30, width=14.0, beta=1, amp=1.0, angle=0.0)", ) - gse = GaussianSquareEcho(duration=20, sigma=30, amp=1.0, width=3) + gse = gaussian_square_echo(duration=20, sigma=30, amp=1.0, width=3) self.assertEqual( repr(gse), ( - "GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=3, active_amp=0.0," + "gaussian_square_echo(duration=20, amp=1.0, angle=0.0, sigma=30, width=3, active_amp=0.0," " active_angle=0.0)" ), ) - gse = GaussianSquareEcho(duration=20, sigma=30, amp=1.0, risefall_sigma_ratio=0.1) + gse = gaussian_square_echo(duration=20, sigma=30, amp=1.0, risefall_sigma_ratio=0.1) self.assertEqual( repr(gse), ( - "GaussianSquareEcho(duration=20, amp=1.0, angle=0.0, sigma=30, width=14.0," + "gaussian_square_echo(duration=20, amp=1.0, angle=0.0, sigma=30, width=14.0," " active_amp=0.0, active_angle=0.0)" ), ) @@ -543,15 +543,15 @@ def test_param_validation(self): GaussianSquareDrag(duration=150, amp=0.2, sigma=8, risefall_sigma_ratio=10, beta=1) with self.assertRaises(PulseError): - GaussianSquareEcho( + gaussian_square_echo( duration=150, amp=0.2, sigma=8, ) with self.assertRaises(PulseError): - GaussianSquareEcho(duration=150, amp=0.2, sigma=8, width=160) + gaussian_square_echo(duration=150, amp=0.2, sigma=8, width=160) with self.assertRaises(PulseError): - GaussianSquareEcho(duration=150, amp=0.2, sigma=8, risefall_sigma_ratio=10) + gaussian_square_echo(duration=150, amp=0.2, sigma=8, risefall_sigma_ratio=10) with self.assertRaises(PulseError): Constant(duration=150, amp=0.9 + 0.8j) @@ -616,18 +616,18 @@ def test_gaussian_square_drag_limit_amplitude_per_instance(self): def test_gaussian_square_echo_limit_amplitude(self): """Test that the check for amplitude less than or equal to 1 can be disabled.""" with self.assertRaises(PulseError): - GaussianSquareEcho(duration=1000, sigma=4.0, amp=1.01, width=100) + gaussian_square_echo(duration=1000, sigma=4.0, amp=1.01, width=100) with patch("qiskit.pulse.library.pulse.Pulse.limit_amplitude", new=False): - waveform = GaussianSquareEcho(duration=100, sigma=1.0, amp=1.1, width=10) + waveform = gaussian_square_echo(duration=100, sigma=1.0, amp=1.1, width=10) self.assertGreater(np.abs(waveform.amp), 1.0) def test_gaussian_square_echo_limit_amplitude_per_instance(self): """Test that the check for amplitude per instance.""" with self.assertRaises(PulseError): - GaussianSquareEcho(duration=1000, sigma=4.0, amp=1.01, width=100) + gaussian_square_echo(duration=1000, sigma=4.0, amp=1.01, width=100) - waveform = GaussianSquareEcho( + waveform = gaussian_square_echo( duration=1000, sigma=4.0, amp=1.01, width=100, limit_amplitude=False ) self.assertGreater(np.abs(waveform.amp), 1.0) From efb3b07e67fe93c693b44e78a7663cff1f2c3cc9 Mon Sep 17 00:00:00 2001 From: Mirko Amico Date: Fri, 5 May 2023 11:22:18 -0600 Subject: [PATCH 43/45] fix lint --- test/python/pulse/test_pulse_lib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/python/pulse/test_pulse_lib.py b/test/python/pulse/test_pulse_lib.py index b44f36ffbf60..a289ad018955 100644 --- a/test/python/pulse/test_pulse_lib.py +++ b/test/python/pulse/test_pulse_lib.py @@ -515,8 +515,8 @@ def test_repr(self): self.assertEqual( repr(gse), ( - "gaussian_square_echo(duration=20, amp=1.0, angle=0.0, sigma=30, width=3, active_amp=0.0," - " active_angle=0.0)" + "gaussian_square_echo(duration=20, amp=1.0, angle=0.0, sigma=30, width=3," + " active_amp=0.0, active_angle=0.0)" ), ) gse = gaussian_square_echo(duration=20, sigma=30, amp=1.0, risefall_sigma_ratio=0.1) From 41647e1089d27e75707185e26e4c8b529bc31b99 Mon Sep 17 00:00:00 2001 From: Mirko Amico <31739486+miamico@users.noreply.github.com> Date: Fri, 19 May 2023 14:19:15 -0700 Subject: [PATCH 44/45] Update qiskit/qobj/converters/pulse_instruction.py Co-authored-by: Will Shanks --- qiskit/qobj/converters/pulse_instruction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/qobj/converters/pulse_instruction.py b/qiskit/qobj/converters/pulse_instruction.py index 4cf4461d24f6..22575f111cc7 100644 --- a/qiskit/qobj/converters/pulse_instruction.py +++ b/qiskit/qobj/converters/pulse_instruction.py @@ -48,7 +48,7 @@ class ParametricPulseShapes(Enum): gaussian = "Gaussian" gaussian_square = "GaussianSquare" gaussian_square_drag = "GaussianSquareDrag" - gaussian_square_echo = "GaussianSquareEcho" + gaussian_square_echo = "gaussian_square_echo" drag = "Drag" constant = "Constant" From 4328ce2870b930ba5c71884c6ecb0209df30028f Mon Sep 17 00:00:00 2001 From: Mirko Amico <31739486+miamico@users.noreply.github.com> Date: Fri, 19 May 2023 14:19:22 -0700 Subject: [PATCH 45/45] Update releasenotes/notes/gaussian-square-echo-pulse-84306f1a02e2bb28.yaml Co-authored-by: Will Shanks --- .../notes/gaussian-square-echo-pulse-84306f1a02e2bb28.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/notes/gaussian-square-echo-pulse-84306f1a02e2bb28.yaml b/releasenotes/notes/gaussian-square-echo-pulse-84306f1a02e2bb28.yaml index e209ae8010fa..b733f6ba6228 100644 --- a/releasenotes/notes/gaussian-square-echo-pulse-84306f1a02e2bb28.yaml +++ b/releasenotes/notes/gaussian-square-echo-pulse-84306f1a02e2bb28.yaml @@ -1,7 +1,7 @@ --- features: - | - Add new :meth:`~qiskit.pulse.GaussianSquareEcho` pulse shape. This pulse + Add new :meth:`~qiskit.pulse.gaussian_square_echo` pulse shape. This pulse is composed by three :class:`~qiskit.pulse.GaussianSquare` pulses. The first two are echo pulses with duration half of the total duration and implement rotary tones. The third pulse is a cancellation tone that lasts