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, so envelope parameters stay differentiable end-to-end. These include duration, amplitude, edge width, DRAG coefficient, and all other values stored on the instance.

Conventions

  • Times are in ns. Values are complex, and the real and imaginary parts carry the relative I/Q structure.

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

  • value(local_time) stays JAX-traceable and 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.

Concrete subclasses declare duration and shape parameters with parameter(). Constructors are synthesized from those declarations. Times are in ns and values are complex I/Q amplitudes.

Parameters:

**params (Any) – Declared envelope fields. Concrete subclasses define their names, defaults, units, and validation constraints.

duration: Any
validate()[source]

Validate relations between concrete parameters.

parameter_values()[source]

Return declared numerical fields available for rebinding.

with_params(bindings)[source]

Return an independent envelope after applying parameter bindings.

Parameters:

bindings (mapping[str, Any]) – Declared field names and replacement values. Unknown names raise ValueError.

abstractmethod value(local_time)[source]

Return complex I/Q shape at local time in ns.

Parameters:

local_time (scalar or array-like) – Time relative to the scheduled pulse start.

sampling_times()[source]

Return local feature samples for automatic grids, overridable for narrow shapes.

The default probes 65 evenly spaced points, and built-in envelopes refine their characteristic widths. The QuTiP backend also interpolates the envelope linearly between knots that include these times, with a close knot on each side of each time. List the time of each step, so that the step stays sharp at any pulse start, up to float precision. QuTiP otherwise ramps a step across one interpolation interval of up to 25 ps. The dynamiqs backend evaluates the envelope directly.

area()[source]

Return the complex area ∫₀^τ E(t) dt, in ns times the amplitude unit.

Built-in envelopes return closed forms. Other envelopes integrate value() with 16-point Gauss-Legendre quadrature on each interval between the sorted sampling_times(), so the area stays traceable under JAX. Its accuracy depends on how well these times resolve the shape, so list the time of each step or narrow feature in sampling_times() and check convergence.

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

Evaluate the shape, optionally returning only its real I component.

Parameters:
  • local_time (scalar or array-like) – Local sample times in ns.

  • real (bool, default False) – Return real I values instead of complex I/Q values.

to_dict()[source]

Serialize the concrete type and its declared parameters.

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 at zero. This is approximately 0.011 * amplitude at the default sigmas=3. The pulse turns on and off with that jump, and the Gaussian waveform itself is unchanged.

Parameters:
  • duration (float) – Pulse duration in ns. Must be positive.

  • sigmas (float, default 3) – Number of standard deviations from the pulse center to each window edge, so sigma = duration/(2*sigmas). Must be positive.

  • amplitude (float, default 1.0) – Peak in-phase envelope amplitude. The consuming drive supplies its physical unit (for example GHz for a Hamiltonian drive or 1/sqrt(ns) for an incident field).

References

Motzoi et al., Phys. Rev. Lett. 103, 110501 (2009), https://doi.org/10.1103/PhysRevLett.103.110501.

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)
sampling_times()[source]

Return local feature samples for automatic grids, overridable for narrow shapes.

The default probes 65 evenly spaced points, and built-in envelopes refine their characteristic widths. The QuTiP backend also interpolates the envelope linearly between knots that include these times, with a close knot on each side of each time. List the time of each step, so that the step stays sharp at any pulse start, up to float precision. QuTiP otherwise ramps a step across one interpolation interval of up to 25 ps. The dynamiqs backend evaluates the envelope directly.

area()[source]

Return A σ √(2π) erf(N_σ/√2), the area of the window.

value(t)[source]

Evaluate the centered Gaussian envelope.

Parameters:

t (scalar or array-like) – Local times in ns.

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, so its sign owns the quadrature convention without an additional polarity flag.

Parameters:
  • duration (float) – Pulse duration in ns. Must be positive.

  • sigmas (float, default 3) – Number of standard deviations from the pulse center to each window edge, so sigma = duration/(2*sigmas). Must be positive.

  • amplitude (float, default 1.0) – Peak in-phase envelope amplitude, in units set by the drive.

  • beta (float, default 0.0) – Signed derivative-quadrature coefficient in ns.

References

Motzoi et al., Phys. Rev. Lett. 103, 110501 (2009), https://doi.org/10.1103/PhysRevLett.103.110501.

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)
sampling_times()[source]

Return local feature samples for automatic grids, overridable for narrow shapes.

The default probes 65 evenly spaced points, and built-in envelopes refine their characteristic widths. The QuTiP backend also interpolates the envelope linearly between knots that include these times, with a close knot on each side of each time. List the time of each step, so that the step stays sharp at any pulse start, up to float precision. QuTiP otherwise ramps a step across one interpolation interval of up to 25 ps. The dynamiqs backend evaluates the envelope directly.

area()[source]

Return the area of the in-phase Gaussian. The derivative quadrature integrates to zero.

value(t)[source]

Evaluate the DRAG envelope.

Parameters:

t (scalar or array-like) – Local times in ns.

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. This pulse is common for two-qubit gates (Krantz et al. 2019, Sec. IV.C). The flat top sets the gate area, and 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).

sampling_times()[source]

Return local feature samples for automatic grids, overridable for narrow shapes.

The default probes 65 evenly spaced points, and built-in envelopes refine their characteristic widths. The QuTiP backend also interpolates the envelope linearly between knots that include these times, with a close knot on each side of each time. List the time of each step, so that the step stays sharp at any pulse start, up to float precision. QuTiP otherwise ramps a step across one interpolation interval of up to 25 ps. The dynamiqs backend evaluates the envelope directly.

area()[source]

Return A(τ − 2τ_e) + A σ √(2π) erf(√2 N_σ): the plateau and two half-Gaussian edges.

value(t)[source]

Evaluate the flat-top Gaussian-edge envelope.

Parameters:

t (scalar or array-like) – Local times in ns.

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.

Krantz et al. 2019 (Sec. IV.C) give this as the standard shape for two-qubit gates. The flat top sets the gate area, and the Gaussian edges suppress spectral leakage. The ramp is a fraction of the total duration, so the shape is invariant under changes of duration.

Parameters:
  • duration (float) – Total pulse length in ns, including 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).

property edge_duration: float

Ramp duration in ns (edge_frac * duration).

sampling_times()[source]

Return local feature samples for automatic grids, overridable for narrow shapes.

The default probes 65 evenly spaced points, and built-in envelopes refine their characteristic widths. The QuTiP backend also interpolates the envelope linearly between knots that include these times, with a close knot on each side of each time. List the time of each step, so that the step stays sharp at any pulse start, up to float precision. QuTiP otherwise ramps a step across one interpolation interval of up to 25 ps. The dynamiqs backend evaluates the envelope directly.

area()[source]

Return A(τ − 2τ_e) + A σ √(2π) erf(√2 N_σ): the plateau and two half-Gaussian edges.

value(t)[source]

Evaluate the fraction-parameterized Gaussian-edge envelope.

Parameters:

t (scalar or array-like) – Local times in ns.

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 at amplitude for the rest 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 can 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).

sampling_times()[source]

Return local feature samples for automatic grids, overridable for narrow shapes.

The default probes 65 evenly spaced points, and built-in envelopes refine their characteristic widths. The QuTiP backend also interpolates the envelope linearly between knots that include these times, with a close knot on each side of each time. List the time of each step, so that the step stays sharp at any pulse start, up to float precision. QuTiP otherwise ramps a step across one interpolation interval of up to 25 ps. The dynamiqs backend evaluates the envelope directly.

area()[source]

Return A(τ − τ_r/2): the ramp and the hold.

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. It rises linearly over ramp_duration and then holds constant at amplitude.

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)
sampling_times()[source]

Return local feature samples for automatic grids, overridable for narrow shapes.

The default probes 65 evenly spaced points, and built-in envelopes refine their characteristic widths. The QuTiP backend also interpolates the envelope linearly between knots that include these times, with a close knot on each side of each time. List the time of each step, so that the step stays sharp at any pulse start, up to float precision. QuTiP otherwise ramps a step across one interpolation interval of up to 25 ps. The dynamiqs backend evaluates the envelope directly.

area()[source]

Return A τ.

value(t)[source]

Evaluate the constant envelope.

Parameters:

t (scalar or array-like) – Local times in ns.