quchip.control.envelopes

Pulse envelope models for quantum control.

Envelopes define the local complex pulse shape E(t) that a drive line plays between t = 0 and t = duration. Subclasses are auto-registered for serialization and as JAX pytrees (via __init_subclass__), so envelope parameters — duration, amplitude, edge width, DRAG coefficient, anything stored on the instance — remain differentiable end-to-end.

Conventions

  • Times are ns; values are complex, with real and imaginary parts carrying relative I/Q structure.

  • Global phase belongs to scheduling, not to an envelope.

  • value(local_time) stays JAX-traceable; it must not concretize time or stored parameters.

References

  • Motzoi et al., Simple Pulses for Elimination of Leakage, PRL 103, 110501 (2009) — motivates Gaussian envelopes with DRAG corrections for short transmon pulses.

  • Krantz et al., APR 6, 021318 (2019), Sec. IV.C — flat-top (Gaussian-edge) pulses for two-qubit gates.

Examples

>>> from quchip import Gaussian, LinearRamp, Square, SquareWithGaussianEdges
>>> g = Gaussian(duration=20.0, sigmas=3.0, amplitude=0.05)
>>> sq = Square(duration=10.0, amplitude=0.1)
>>> fg = SquareWithGaussianEdges(duration=40.0, amplitude=0.1)
>>> lr = LinearRamp(duration=60.0, ramp_duration=50.0, amplitude=4.0)

Classes

Envelope(**params)

Local complex pulse shape evaluated relative to its scheduled start.

Gaussian(duration[, sigmas, amplitude])

Centered Gaussian pulse.

GaussianDRAG(duration[, sigmas, amplitude, beta])

Gaussian pulse with a derivative quadrature.

GaussianEdge(duration, edge_duration[, ...])

Flat-top pulse with Gaussian ramp-up and ramp-down edges.

LinearRamp(duration, ramp_duration[, amplitude])

Linearly rising ramp that holds at peak amplitude.

Square(duration[, amplitude])

Constant-amplitude pulse.

SquareWithGaussianEdges(duration[, ...])

Flat-top pulse with Gaussian ramp-up and ramp-down edges.

class quchip.control.envelopes.Envelope(**params)[source]

Bases: Registrable, ABC

Local complex pulse shape evaluated relative to its scheduled start.

Parameters:

params (Any)

duration: Any
validate()[source]

Validate relations between concrete parameters.

Return type:

None

abstractmethod value(local_time)[source]

Return complex I/Q shape at time relative to the pulse start.

Parameters:

local_time (Any)

Return type:

Any

sample(local_time, *, real=False)[source]

Evaluate the shape on an array, optionally returning only I.

Parameters:
Return type:

Any

to_dict()[source]

Serialize the concrete type and its declared parameters.

Return type:

dict[str, Any]

class quchip.control.envelopes.Gaussian(duration, sigmas=3, amplitude=1.0)[source]

Bases: Envelope

Centered Gaussian pulse.

\[E(t) = A \exp\!\left[-\frac{(t - \tau/2)^2}{2 \sigma^2}\right], \qquad \sigma = \frac{\tau}{2 N_\sigma}.\]

The sigmas parameter \(N_\sigma\) is the number of standard deviations from the pulse center to its edge at t = 0 or t = duration. Gaussian pulses minimize spectral leakage onto higher transmon levels and are the starting point for DRAG corrections (Motzoi et al., PRL 103, 110501 (2009)).

The scheduled window [0, duration] starts and ends at amplitude * exp(-sigmas**2 / 2), not zero — about 0.011 * amplitude at the default sigmas=3. The pulse turns on and off with that jump; the Gaussian waveform itself is unchanged.

Parameters:
  • duration (Any)

  • sigmas (Any)

  • amplitude (Any)

duration: Any = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=False, kw_only=False, required=True)
sigmas: Any = Parameter(default=3, positive=True, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
amplitude: Any = Parameter(default=1.0, positive=False, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
value(t)[source]

Evaluate the centered Gaussian envelope at time points t.

Parameters:

t (Any)

Return type:

Any

class quchip.control.envelopes.GaussianDRAG(duration, sigmas=3, amplitude=1.0, beta=0.0)[source]

Bases: Envelope

Gaussian pulse with a derivative quadrature.

\[E(t) = I(t) + i\,\beta\,\frac{dI}{dt}, \qquad I(t) = A\exp\!\left[-\frac{(t-\tau/2)^2}{2\sigma^2}\right].\]

beta is signed and measured in ns. Its sign therefore owns the quadrature convention without an additional polarity flag.

Parameters:
duration: Any = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=False, kw_only=False, required=True)
sigmas: Any = Parameter(default=3, positive=True, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
amplitude: Any = Parameter(default=1.0, positive=False, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
beta: Any = Parameter(default=0.0, positive=False, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=False, kw_only=False, required=False)
value(t)[source]

Return complex I/Q shape at time relative to the pulse start.

Parameters:

t (Any)

Return type:

Any

class quchip.control.envelopes.GaussianEdge(duration, edge_duration, sigmas=3, amplitude=1.0)[source]

Bases: Envelope

Flat-top pulse with Gaussian ramp-up and ramp-down edges.

Each edge is a Gaussian of width \(\sigma = \tau_e / (2 N_\sigma)\) where \(\tau_e\) = edge_duration; the plateau between edges holds a constant amplitude \(A\). Total duration includes both edges. Commonly used for two-qubit gates (Krantz et al. 2019, Sec. IV.C) because the flat top sets the gate area while the Gaussian edges suppress spectral leakage.

Parameters:
  • duration (float) – Total pulse length, including both edges, in ns.

  • edge_duration (float) – Ramp time \(\tau_e\) per edge, in ns. Must satisfy 2 * edge_duration <= duration.

  • sigmas (float) – Number of standard deviations spanned by each edge.

  • amplitude (float) – Plateau amplitude \(A\).

See also

SquareWithGaussianEdges

Same shape parameterized by edge_frac (fraction) instead of absolute edge_duration.

References

  • Krantz et al., APR 6, 021318 (2019), Sec. IV.C.

duration: Any = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=False, kw_only=False, required=True)
edge_duration: Any = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=False, kw_only=False, required=True)
sigmas: Any = Parameter(default=3, positive=True, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
amplitude: Any = Parameter(default=1.0, positive=False, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
validate()[source]

Reject edges that overrun the pulse (2 * edge_duration > duration).

Return type:

None

value(t)[source]

Evaluate the flat-top Gaussian-edge envelope at time points t.

Parameters:

t (Any)

Return type:

Any

class quchip.control.envelopes.SquareWithGaussianEdges(duration, amplitude=1.0, edge_frac=0.25, sigmas=3)[source]

Bases: Envelope

Flat-top pulse with Gaussian ramp-up and ramp-down edges.

Each ramp has duration \(\tau_e = f_e \cdot \tau\) with \(f_e\) = edge_frac; the plateau between ramps holds amplitude \(A\). Total duration includes both edges. The Gaussian width is \(\sigma = \tau_e / (2 N_\sigma)\) with \(N_\sigma\) = sigmas.

This is the canonical shape used in Krantz et al. 2019 (Sec. IV.C) for two-qubit gates — the flat top sets the gate area while the Gaussian edges suppress spectral leakage. Parametrizing the ramp as a fraction of the total duration makes the shape shape-invariant under changes of duration.

Parameters:
  • duration (float) – Total pulse length in ns (includes both ramps).

  • amplitude (float) – Plateau amplitude \(A\).

  • edge_frac (float) – Ramp length as a fraction of the total duration. Must satisfy 0 < edge_frac and 2 * edge_frac <= 1.

  • sigmas (float) – Number of standard deviations spanned by each ramp.

duration: Any = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=False, kw_only=False, required=True)
amplitude: Any = Parameter(default=1.0, positive=False, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
edge_frac: Any = Parameter(default=0.25, positive=True, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
sigmas: Any = Parameter(default=3, positive=True, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
validate()[source]

Reject ramps that overrun the pulse (2 * edge_frac > 1).

Return type:

None

property edge_duration: float

Ramp duration in ns (edge_frac * duration).

value(t)[source]

Evaluate the fraction-parameterized Gaussian-edge envelope.

Parameters:

t (Any)

Return type:

Any

class quchip.control.envelopes.LinearRamp(duration, ramp_duration, amplitude=1.0)[source]

Bases: Envelope

Linearly rising ramp that holds at peak amplitude.

The envelope rises linearly from 0 to amplitude over the first ramp_duration nanoseconds, then holds constant at amplitude for the remainder of the pulse.

\[E(t) = A \cdot \min\!\left(\frac{t}{\tau_r},\, 1\right), \qquad 0 \le t \le \tau,\]

where \(\tau_r\) is ramp_duration and \(\tau\) is duration.

Parameters:
  • duration (float) – Total pulse duration in ns. Must be > 0.

  • ramp_duration (float) – Duration of the linear rise in ns. Must satisfy 0 < ramp_duration <= duration.

  • amplitude (float) – Peak amplitude \(A\) (default 1.0).

Notes

For an adiabatic ramp into a Kerr-cat qubit, choose ramp_duration long compared to 1 / (2 * K) (the inverse gap at the bifurcation point). See Grimm et al., Nature 584, 205 (2020).

The waveform is JAX-traceable: ramp_duration and amplitude may be JAX tracers so the ramp parameters are differentiable.

Examples

>>> from quchip.control.envelopes import LinearRamp
>>> ramp = LinearRamp(duration=60.0, ramp_duration=50.0, amplitude=4.0)
>>> import numpy as np
>>> t = np.array([0.0, 25.0, 50.0, 55.0])
>>> np.real(ramp.value(t)).tolist()
[0.0, 2.0, 4.0, 4.0]
duration: Any = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=False, kw_only=False, required=True)
ramp_duration: Any = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=False, kw_only=False, required=True)
amplitude: Any = Parameter(default=1.0, positive=False, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
validate()[source]

Reject ramps longer than the pulse (ramp_duration > duration).

Return type:

None

value(t)[source]

Evaluate the linear-ramp envelope at time points t (ns).

Parameters:

t (array-like) – 1-D array of time points in nanoseconds.

Returns:

Complex-valued waveform: rises linearly over ramp_duration, holds constant at amplitude afterward.

Return type:

array

class quchip.control.envelopes.Square(duration, amplitude=1.0)[source]

Bases: Envelope

Constant-amplitude pulse.

\[E(t) = A, \qquad 0 \le t \le \tau.\]
Parameters:
  • duration (float) – Pulse length \(\tau\) in ns.

  • amplitude (float) – Real amplitude \(A\) applied on top of value().

duration: Any = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=False, kw_only=False, required=True)
amplitude: Any = Parameter(default=1.0, positive=False, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
value(t)[source]

Evaluate the constant envelope at time points t.

Parameters:

t (Any)

Return type:

Any