Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
cd37e22
add gaussian square echo pulse
miamico Jan 12, 2023
859c451
address some PR comments
miamico Jan 24, 2023
760aa7c
fixed parameters of symbolicpulse
miamico Jan 26, 2023
fca8fd6
adding math description and reference
miamico Jan 31, 2023
2895969
change variable names
miamico Jan 31, 2023
755d16e
fix lint
miamico Jan 31, 2023
6f73a4c
remove width_echo parameter
miamico Jan 31, 2023
e1c731e
fix lint
miamico Jan 31, 2023
b083550
add release notes
miamico Jan 31, 2023
0c1ba46
fix conflicts
miamico Apr 12, 2023
d94fcc7
address some PR comments
miamico Jan 24, 2023
3d310c7
fixed parameters of symbolicpulse
miamico Jan 26, 2023
a1df97f
adding math description and reference
miamico Jan 31, 2023
a331c36
change variable names
miamico Jan 31, 2023
0d695dc
fix lint
miamico Jan 31, 2023
bfd2c5d
remove width_echo parameter
miamico Jan 31, 2023
7d79645
fix lint
miamico Jan 31, 2023
7ff1871
add release notes
miamico Jan 31, 2023
f1f5a90
Merge branch 'main' of github.com:miamico/miamico-gse
miamico Apr 12, 2023
75427a9
Update first paragraph
miamico Apr 12, 2023
310b727
Update amp param to float
miamico Apr 12, 2023
f3c1e50
Update angle def param to float
miamico Apr 12, 2023
b18df11
Update amp constrain
miamico Apr 12, 2023
877b285
drafting tests
miamico Apr 12, 2023
16f2b33
update docstring
miamico Apr 12, 2023
bba03a0
finish up test
miamico Apr 12, 2023
bbeba66
Merge branch 'main' into main
miamico Apr 13, 2023
4ead034
fix missing pulses from init
miamico Apr 13, 2023
d012580
addding Cos
miamico Apr 13, 2023
41af294
Merge branch 'main' of github.com:miamico/miamico-gse
miamico Apr 13, 2023
061aac4
missing sin
miamico Apr 14, 2023
d0d04ce
fixed tests
miamico Apr 14, 2023
050c001
black formatting
miamico Apr 17, 2023
fab388b
break string
miamico Apr 17, 2023
cb0749e
fixing strings
miamico Apr 17, 2023
170370e
reformatting
miamico Apr 17, 2023
92c4b29
fix lint
miamico Apr 17, 2023
17944ca
fixing last things
miamico Apr 17, 2023
ecc70c6
fix tests
miamico Apr 17, 2023
5259ff5
fix black
miamico Apr 17, 2023
686f342
Merge branch 'main' into main
miamico Apr 17, 2023
a0575d3
fix another test
miamico Apr 18, 2023
bff8bab
fix amp validation
miamico Apr 18, 2023
b4851fc
Merge branch 'main' of github.com:miamico/miamico-gse
miamico Apr 18, 2023
a55abd7
Merge branch 'main' into main
miamico Apr 18, 2023
257fd43
fixing docstring
miamico Apr 22, 2023
75183f2
Merge branch 'main' of github.com:miamico/miamico-gse
miamico Apr 22, 2023
97a1e5f
Merge branch 'main' into main
miamico Apr 22, 2023
bb744bd
Update qiskit/pulse/library/symbolic_pulses.py
miamico Apr 27, 2023
8be636a
Merge branch 'main' into main
miamico Apr 27, 2023
769b145
Merge branch 'main' into main
miamico May 2, 2023
88f045f
rename to gaussian_square_echo
miamico May 2, 2023
be39a8f
Merge branch 'main' of github.com:miamico/miamico-gse
miamico May 2, 2023
efb3b07
fix lint
miamico May 5, 2023
41647e1
Update qiskit/qobj/converters/pulse_instruction.py
miamico May 19, 2023
4328ce2
Update releasenotes/notes/gaussian-square-echo-pulse-84306f1a02e2bb28…
miamico May 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions qiskit/pulse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@
Gaussian,
GaussianSquare,
GaussianSquareDrag,
gaussian_square_echo,
Sin,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a merging error. Those pulses should not be removed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool, just pushed a fix for this

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like these are still removed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now only the Sin is still missing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully they're all back in now

Cos,
Sawtooth,
Expand Down
2 changes: 2 additions & 0 deletions qiskit/pulse/library/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
Gaussian
GaussianSquare
GaussianSquareDrag
gaussian_square_echo
Sin
Cos
Sawtooth
Expand Down Expand Up @@ -117,6 +118,7 @@
Gaussian,
GaussianSquare,
GaussianSquareDrag,
gaussian_square_echo,
Drag,
Constant,
Sin,
Expand Down
201 changes: 201 additions & 0 deletions qiskit/pulse/library/symbolic_pulses.py
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,207 @@ def GaussianSquareDrag(
return instance


def gaussian_square_echo(
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[float, ParameterExpression]] = 0.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,
) -> SymbolicPulse:
"""An echoed Gaussian square pulse with an active tone overlaid on it.
Comment thread
miamico marked this conversation as resolved.

The Gaussian Square Echo pulse is composed of three pulses. First, a Gaussian Square pulse
Comment thread
miamico marked this conversation as resolved.
: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)\
Comment thread
miamico marked this conversation as resolved.
& \\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://iopscience.iop.org/article/10.1088/2058-9565/abe519

.. |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 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.
Comment thread
miamico marked this conversation as resolved.
angle: The angle in radians of the complex phase factor uniformly
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.
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 None and risefall_sigma_ratio is not None:
width = duration - 2.0 * risefall_sigma_ratio * sigma

parameters = {
"amp": amp,
"angle": angle,
"sigma": sigma,
"width": width,
"active_amp": active_amp,
"active_angle": active_angle,
}

# Prepare symbolic expressions
(
_t,
_duration,
_amp,
_sigma,
_active_amp,
_width,
_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

_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_active = _duration / 2

_sq_t0_active = _center_active - _width / 2
_sq_t1_active = _center_active + _width / 2

_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_active = (
_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),
)
)

envelop_expr_total = envelope_expr + envelope_expr_active

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) + sym.Abs(_active_amp) <= 1.0)

instance = SymbolicPulse(
pulse_type="gaussian_square_echo",
duration=duration,
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.
Expand Down
1 change: 1 addition & 0 deletions qiskit/qobj/converters/pulse_instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class ParametricPulseShapes(Enum):
gaussian = "Gaussian"
gaussian_square = "GaussianSquare"
gaussian_square_drag = "GaussianSquareDrag"
gaussian_square_echo = "gaussian_square_echo"
drag = "Drag"
constant = "Constant"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
features:
- |
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
the full duration of the pulse and implements correcting single qubit
rotations.
106 changes: 106 additions & 0 deletions test/python/pulse/test_pulse_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
Gaussian,
GaussianSquare,
GaussianSquareDrag,
gaussian_square_echo,
Drag,
Sin,
Cos,
Expand Down Expand Up @@ -288,6 +289,65 @@ 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 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
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
amp = 0.5
width = 100
duration = width + 2 * risefall
active_amp = 0.1
width_echo = (duration - 2 * (duration - width)) / 2

gse = gaussian_square_echo(
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_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):
"""Test gaussian square echo active amp parameter validation."""

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):
gaussian_square_echo(duration=50, width=0, sigma=16, amp=0.1, active_amp=1.1)
with self.assertRaises(PulseError):
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."""
drag = Drag(duration=25, sigma=4, amp=0.5j, beta=1)
Expand Down Expand Up @@ -451,6 +511,22 @@ def test_repr(self):
repr(gsd),
"GaussianSquareDrag(duration=20, sigma=30, width=14.0, beta=1, amp=1.0, angle=0.0)",
)
gse = gaussian_square_echo(duration=20, sigma=30, amp=1.0, width=3)
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)"
),
)
gse = gaussian_square_echo(duration=20, sigma=30, amp=1.0, risefall_sigma_ratio=0.1)
self.assertEqual(
repr(gse),
(
"gaussian_square_echo(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)")
const = Constant(duration=150, amp=0.1, angle=0.3)
Expand All @@ -476,6 +552,17 @@ 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):
gaussian_square_echo(
duration=150,
amp=0.2,
sigma=8,
)
with self.assertRaises(PulseError):
gaussian_square_echo(duration=150, amp=0.2, sigma=8, width=160)
with self.assertRaises(PulseError):
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)
with self.assertRaises(PulseError):
Expand Down Expand Up @@ -536,6 +623,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):
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 = 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):
gaussian_square_echo(duration=1000, sigma=4.0, amp=1.01, width=100)

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)

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):
Expand Down