quchip.control.sequence¶
Declarative pulse programming for a Chip.
QuantumSequence is the user-facing scheduling API. It tracks
per-endpoint timing cursors, per-device virtual-Z phase frames, and
cross-device barriers, then materializes the schedule into classical
DriveOp and field
CoherentOp records for the engine.
Conventions¶
Times are in ns. Frequencies are in GHz (ordinary, not angular).
Each carrier pulse follows the virtual-Z frame of one device. By default, this is the device that its line drives. Virtual-Z shifts accumulate into later carrier pulses in that frame on any line, but not into baseband flux pulses. This matches the lab-frame semantics of software-Z (McKay et al., PRA 96, 022330 (2017)).
All sweep axes stay JAX-traceable. Pulse parameters, delays, and envelope fields go into
SolveProblemwithout Python-side concretization.
Examples
>>> from quchip import (
... DuffingTransmon, ChargeDrive, Chip, QuantumSequence, Gaussian
... )
>>> q = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3)
>>> drive = ChargeDrive(target=q)
>>> chip = Chip([q])
>>> seq = QuantumSequence(chip)
>>> _ = seq.schedule(q, envelope=Gaussian(duration=20.0, amplitude=0.05))
>>> seq.total_duration
20.0
Classes
|
Declarative pulse sequence builder for a |
- class quchip.control.sequence.QuantumSequence(chip)[source]¶
Bases:
objectDeclarative pulse sequence builder for a
Chip.Tracks per-
(device, drive)timing cursors and per-device virtual-Z phase frames. Append pulses withschedule()(or the conveniencescharge(),phase(),flux()). Synchronize channels withbarrier()anddelay().build_problem()andbuild_batch()materialize the schedule lazily into engine control-operation records.Where a device/drive is expected, you can use the object or its string label.
Simulation uses one consistent verb,
simulate, across three tiers, from most ergonomic to most explicit:simulate()/simulate_batch(): schedule and solve in one call (the example-facing path).solve()/solve_many(): solve aSolveProblem/ batch you already hold.The module-level
simulate()/solve_problem()/solve_many(): the low-level “I already havedrive_ops/ aSolveProblem” tier.
- Parameters:
chip (Chip) – Chip that this sequence schedules against. It supplies the device and coupling maps used to resolve scheduling targets, the wired
ControlEquipmentlines, and the frame/backend settings used bybuild_problem()andsimulate().
Examples
>>> from quchip import ( ... DuffingTransmon, ChargeDrive, Chip, QuantumSequence, Gaussian ... ) >>> q = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3) >>> drive = ChargeDrive(target=q) >>> chip = Chip([q]) >>> chip.wire(drive) >>> seq = QuantumSequence(chip) >>> _ = seq.charge(q, envelope=Gaussian(duration=20.0, amplitude=0.05)) >>> seq.vz(q, angle=0.5) >>> _ = seq.charge(q, envelope=Gaussian(duration=10.0, amplitude=0.03))
- schedule(target, *, envelope, freq=None, start_time=None, phase=0.0, frame=None, detuning=None)[source]¶
Schedule a pulse on target.
- Parameters:
target (str | ControlEndpoint | BaseDevice | BaseCoupling) –
Accepted forms, resolved in this order:
network.expose(...).input: scheduled at that external reference plane without enteringControlEquipment.BaseDrive: scheduled directly on that drive.BaseDevice: uses the device’s first connected drive. If a device has multiple drives, pass the drive object explicitly.BaseCoupling— uses the uniqueCouplingDrivethat targets that coupling. If a coupling has multiple control lines, pass the drive object explicitly.str: a label, resolved in this order. First, a device label. Then, only when the label is absent from the device map, a coupling label (the two label spaces are disjoint). Then, only when the label is absent from both, a control-equipment line label, scheduled directly on that line. This third fallback lets a caller schedule by a drive’s label after its device or coupling target is eliminated. See the retarget registry ofeliminate(), which keeps a converted line’s label. A control-line label that collides with a device/coupling label is shadowed by the device/coupling resolution.
envelope (Envelope) – Pulse envelope.
freq (float, optional) – Optional carrier frequency in GHz. Omitting it leaves the signal carrier-free. Drive-specific conveniences can supply their own explicit default, such as
charge()usingchip.freq(device).start_time (float, optional) – Pulse start time in ns. Defaults to the current cursor; earlier times are rejected.
phase (float) – Per-pulse phase offset, composed with any accumulated virtual-Z.
frame (str or BaseDevice, optional) – Device whose virtual-Z frame the carrier follows.
vz()on that device shifts this pulse’s phase. Defaults to the device that the drive line targets. Coupling-drive and coherent-input pulses have no default frame. A cross-resonance tone on the control’s line names the target device, because its phase sets the axis of the target’s conditional rotation. A frame requiresfreq.detuning (float, optional) – Carrier offset
δin GHz, referenced to the pulse startt0. The pulse deliversE(t - t0) exp(i phase) exp(-2πi freq t) exp(-2πi δ (t - t0)).freqstays the frame frequency, so the pulse is the same in that frame at everyt0. A carrier atfreq = f + δwithoutdetuningis a fixed oscillator instead, with phasephase - 2π δ t0relative to the frame atf.Noneadds no offset term.pulse.<index>.detuningrebinds, sweeps, and differentiates likefreq. A detuning requiresfreq.
- charge(target, *, envelope, freq=None, phase=0.0, frame=None, detuning=None)[source]¶
Schedule a charge-drive pulse.
- Parameters:
target (str or BaseDevice) – Driven device.
envelope (Envelope) – Pulse envelope; time parameters are in ns and amplitudes in GHz.
freq (float or None, default=None) – Carrier frequency in GHz;
Noneuseschip.freq(target).phase (float, default=0.0) – Carrier phase in radians.
frame (str or BaseDevice or None, default=None) – Device whose virtual-Z frame the carrier follows.
Noneuses target. Seeschedule().detuning (float or None, default=None) – Carrier offset in GHz, referenced to the pulse start. See
schedule().
- phase(target, *, envelope, freq, phase=0.0, frame=None, detuning=None)[source]¶
Schedule a phase-drive pulse.
- Parameters:
target (str or BaseDevice) – Driven device.
envelope (Envelope) – Pulse envelope; time parameters are in ns and amplitudes in GHz.
freq (float) – Carrier frequency in GHz.
phase (float, default=0.0) – Carrier phase in radians.
frame (str or BaseDevice or None, default=None) – Device whose virtual-Z frame the carrier follows.
Noneuses target. Seeschedule().detuning (float or None, default=None) – Carrier offset in GHz, referenced to the pulse start. See
schedule().
- flux(target, *, envelope)[source]¶
Schedule a baseband flux-drive pulse.
- Parameters:
target (str or BaseDevice) – Driven flux-tunable device.
envelope (Envelope) – Baseband pulse envelope in GHz versus time in ns.
- pump(coupling, *, envelope, freq=None, start_time=None, phase=0.0)[source]¶
Schedule an edge pump.
- Parameters:
coupling (str or BaseCoupling) – Modulable coupling to pump.
envelope (Envelope) – Pump-amplitude envelope in GHz versus time in ns.
freq (float or None, default=None) – Carrier frequency in GHz;
Noneapplies the envelope at baseband.start_time (float or None, default=None) – Absolute start time in ns;
Noneuses the channel cursor.phase (float, default=0.0) – Carrier phase in radians.
- flux_to(target, *, target_freq, envelope)[source]¶
Schedule a flux-drive frequency-shift pulse to
target_freq.This is not an inverse-SQUID flux calibration: it calculates
δω = target_freq − chip.freq(device)and schedules aFluxDrivepulse whose envelope amplitude is that frequency shift in GHz. The resulting Hamiltonian contribution is the linear detuning termδω(t) n̂. Passenvelopewithamplitude=None(or any placeholder). This method replaces the amplitude with the calculated δω.target_freqcan be a JAX tracer (for examplechip.freq(other_device)).- Parameters:
target (str | BaseDevice) – Device to flux-pulse.
target_freq (float) – Target
0 → 1frequency in GHz.envelope (Envelope) – Envelope template with all timing parameters set. Its
amplitudeis replaced by the calculated δω, so any passed value is ignored.
- Returns:
Handle to the scheduled entry, usable with
.vary().- Return type:
- vz(target, angle)[source]¶
Apply a virtual-Z frame shift of angle rad on target.
The shift is free (no pulse is emitted). It accumulates into the
phase_offsetof every later carrier pulse whose frame is target, on any line. A pulse’s frame defaults to the device that its line drives, andschedule(..., frame=...)names another device. This matches the standard software-Z trick for transmons (McKay et al., PRA 96, 022330 (2017)). Baseband flux pulses are not affected.- Parameters:
target (str or BaseDevice) – Device receiving the virtual frame shift.
angle (float) – Z-frame shift in radians.
- delay(scope, duration)[source]¶
Insert an idle delay on all drive channels of a target.
- Parameters:
scope (str or BaseDevice) – Device label or object whose channels advance.
duration (float) – Delay in ns.
- barrier(*labels)[source]¶
Synchronize channel cursors.
With no arguments, every
(device, drive)cursor on the chip is advanced to the current maximum. With explicit device targets, only those devices’ cursors are aligned. This guarantees that pulses scheduled after the barrier start no earlier than any pulse scheduled before it.- Parameters:
*labels (str or BaseDevice) – Devices to synchronize. If omitted, all device-drive cursors are synchronized.
- property scheduled_ops: tuple[DriveOp | CoherentOp, ...]¶
Materialize scheduled classical-drive and coherent-field operations.
- property channel_cursors: dict[tuple[str, str], Any]¶
Current time cursors keyed by
(target_label, drive_label).
- build_problem(tlist=None, solver=None, options=None, e_ops=None, initial_state=None, approximation=None, frame=None, states=None, dissipation=True, run_args=None, *, duration=None)[source]¶
Build a single
SolveProblemfrom this sequence.- Parameters:
tlist (array-like, optional) – Grid passed to the numerical solver, in ns. Its first time is the initial-state time and its last time ends the calculation. Scheduled signals keep their absolute times in a partial interval.
duration (float, optional) – Interval from zero in ns, with automatic sampling. It can extend the schedule but cannot cut it short, and cannot be combined with
tlist. Without either argument, the scheduled duration is used.solver (str, optional) – Backend solver name; defaults to the backend’s own default solver.
options (dict, optional) – Backend numerical options. Use
statesto select state retention.states ({"all", "final", "none"} or None, default None) – None saves all deterministic states and preserves native stochastic defaults. Explicit values keep the full state history, only the final state, or neither. Requested observable traces are kept independently.
run_args (dict or None, default None) – Native trajectory call keywords: QuTiP seeds, ntraj, heterodyne, target_tol, timeout; Dynamiqs keys, method, gradient, etas. Assembled physics and native options cannot be overridden here.
e_ops (dict, optional) – Expectation operators keyed by device label (or a 2-tuple of device labels for a two-body operator), mapped to a local operator (or a pair of local operators). They are decomposed into per-band terms before reaching the solver.
initial_state (Any, optional) – Initial state; see
simulate()for accepted forms, coordinate conventions, and the meaning ofNone.approximation (Approximation, optional) – Engine approximation override.
Noneuses the chip default.frame (FrameSpec, optional) – Integration-frame override; defaults to the chip’s declared frame.
"auto"derives constraints from delivered scheduled signals and weights them overtlist[-1] - tlist[0].dissipation (bool, default=True) – Include authored collapse channels when true.
- Returns:
Frozen problem (chip, compiled engine result, initial state,
tlist, collapse operators, and expectation operators), ready forsolve()orsolve_problem().- Return type:
- resolve(*, frame=None, approximation=None)[source]¶
Resolve the backend-neutral Hamiltonian and noise description.
- Parameters:
frame (FrameSpec or None, default=None) – Resolution frame, or
Noneto use the chip default.approximation (Approximation or None, default=None) – Engine approximation, or
Noneto use the chip default.
- vary(field, values, *, name=None)[source]¶
Create a
BatchAxisover a public parameter path or state.- Parameters:
field (str) –
"initial_state"or any dotted path exposed byparameters. Pulse handles remain the concise way to vary a pulse field immediately after scheduling it.values (array-like) – Sequence of values for
field, one per batch point.name (str, optional) – Axis name recorded in
SolveBatch.axesandSolveBatch.params_at(). Defaults to field.
- Returns:
Sequence-level axis, consumable by
build_batch()orzip().- Return type:
- zip(*axes)[source]¶
Zip axes into one pairwise dimension.
- Parameters:
*axes (BatchAxis) – Two or more axes to zip, each created by this sequence (
vary(),PulseHandle.vary(), orDelayHandle.vary()). All axes need the same length.- Returns:
Combined axis that
build_batch()treats as a single dimension: pointisupplies pointifrom every zipped axis together, not the outer product.- Return type:
- build_batch(*axes, tlist=None, solver=None, options=None, e_ops=None, initial_state=None, approximation=None, states=None, dissipation=True, run_args=None, duration=None)[source]¶
Build a batched solve request from explicit sweep axes.
- Parameters:
*axes (BatchAxis or ZippedBatchAxis) – Independent or pairwise-zipped sweep axes.
tlist (array-like or None, default=None) – Solver time grid in ns.
solver (str or None, default=None) – Backend solver name;
Noneuses its default.options (dict or None, default=None) – Backend numerical options.
run_args (dict or None, default None) – Native trajectory call keywords: QuTiP seeds, ntraj, heterodyne, target_tol, timeout; Dynamiqs keys, method, gradient, etas. Assembled physics and native options cannot be overridden here.
e_ops (dict or None, default=None) – Local expectation-operator specifications.
initial_state (Any or None, default=None) – State reused at every batch point; see
simulate()for accepted forms and coordinate conventions. Aninitial_stateaxis supplies one state per point.approximation (Approximation or None, default=None) – Engine approximation override.
states ({"all", "final", "none"} or None, default=None) – None saves all deterministic states and preserves native stochastic defaults. Explicit values select state retention; native storage conflicts raise.
dissipation (bool, default=True) – Include authored collapse channels when true.
duration (float or None, default=None) – Automatically sampled interval from zero, in ns.
- simulate(tlist=None, solver=None, options=None, e_ops=None, initial_state=None, *, backend=None, partition=True, approximation=None, states=None, dissipation=True, run_args=None, duration=None)[source]¶
Build and solve one scheduled simulation.
- Parameters:
tlist (array-like, optional) – Sample times in ns. Do not use with
duration. If omitted, quchip builds an automatic grid through the schedule end.solver (str, optional) – Backend solver name, such as
"mesolve"or"sesolve".options (dict, optional) – Backend-specific solver options.
run_args (dict or None, default None) – Native trajectory call keywords: QuTiP seeds, ntraj, heterodyne, target_tol, timeout; Dynamiqs keys, method, gradient, etas. Assembled physics and native options cannot be overridden here.
e_ops (dict, optional) – Named observables to evaluate.
initial_state (object or mapping, optional) – A label of local energy levels, such as
{"q0": 1}, or configured string shorthand names its dressed state: the eigenstate of the solve’s undriven static lab-frame Hamiltonian, as kept by its approximation and excitation cutoff, that is assigned to the label. Its overlap with the bare product is made real and nonnegative, and it is placed in the solve frame attlist[0]as the label target ofoverlap().Noneand{}give the all-ground label. With ordinary couplings under the default RWA, that state is the bare vacuum. When the solve uses the chip’s approximation, a label start is the same physical state aschip.state()before the frame transform. At a nonzero rotating-frame start time, the frame transform is applied attlist[0], so the state need not match the lab-frame vector returned bychip.state().chip.bare_state(...)gives a bare product, and a mapping with a local ket raises. QuTiPQobjand dynamiqsQArraykets or density matrices use resolved solver coordinates. Arrays, symbolic expressions, and callables are authored-space kets projected onto kept levels. ACompactStatereuses captured excitation coordinates and requires the same local energy bases.backend (object or {"qutip", "dynamiqs"}, optional) – Per-call backend override.
partition (bool, default=True) – Solve independent chip components separately when the initial state permits it.
approximation (Approximation, optional) – Override the chip’s approximation for this solve.
states ({"all", "final", "none"} or None, default=None) – None saves all deterministic states and preserves native stochastic defaults. State storage policy for the returned result.
dissipation (bool, default=True) – Include declared collapse channels when
True.duration (float, optional) – Positive simulation end time in ns when
tlistis omitted.
- Returns:
Time samples, requested observables, and states according to
states.- Return type:
- Raises:
ValueError – If
tlistanddurationare both supplied, timing is invalid, or automatic sampling lacks the concrete timing it requires.
Notes
A per-call
backendoutranks the chip and process defaults, and foreign-native initial states are coerced at the solve boundary. Python evaluates an inlineinitial_state=chip.state(...)before this scope opens, so a traced state still requires a JAX-capable surrounding backend.Partitioning returns a
PartitionedSimulationResultand requiresinitial_stateto beNoneor a mapping; string shorthand and concrete states take the joint path.
- simulate_batch(*axes, tlist=None, solver=None, options=None, e_ops=None, initial_state=None, backend=None, progress=True, approximation=None, states=None, dissipation=True, run_args=None, duration=None, max_workers=None)[source]¶
Build and solve a batched sweep over pulse or sequence parameters.
- Parameters:
*axes (BatchAxis or ZippedBatchAxis) – Cartesian sweep axes. A
ZippedBatchAxispairs member axes pointwise and contributes one batch dimension.tlist (Any | None) – As for
simulate(); one initial state is reused for every batch point unless it is a supported mapping.solver (str | None) – As for
simulate(); one initial state is reused for every batch point unless it is a supported mapping.options (dict | None) – As for
simulate(); one initial state is reused for every batch point unless it is a supported mapping.run_args (dict | None) – As for
simulate(); one initial state is reused for every batch point unless it is a supported mapping.e_ops (dict | None) – As for
simulate(); one initial state is reused for every batch point unless it is a supported mapping.initial_state (Any | None) – As for
simulate(); one initial state is reused for every batch point unless it is a supported mapping.backend (Any | None) – As for
simulate(); one initial state is reused for every batch point unless it is a supported mapping.progress (bool, default=True) – Show backend batch progress when supported.
approximation (Approximation, optional) – Per-batch approximation override.
states ({"all", "final", "none"} or None, default=None) – None saves all deterministic states and preserves native stochastic defaults. State storage policy for each batch result.
dissipation (bool, default=True) – Include declared collapse channels.
duration (float, optional) – Positive end time in ns when
tlistis omitted.max_workers (int or None, default=None) – Worker processes for a backend that solves points in parallel, such as QuTiP.
Nonetakes the CPU count that loky reports, which honorsLOKY_MAX_CPU_COUNT, or fewer when the available memory cannot hold them.1solves in this process.
- Returns:
Results indexed by the Cartesian or zipped axis shape.
- Return type:
- Raises:
ValueError – If an axis is invalid, timing is inconsistent, or a sweep changes a structural quantity that cannot vary in one batch.
- subspace_propagator(devices, levels=(0, 1), *, frame='dressed', tlist=None, duration=None, solver=None, options=None, backend=None, approximation=None, dissipation=None, progress=False, max_workers=None)[source]¶
Return the propagator of this sequence on the dressed states of a product of levels.
The inputs are the labels that give each selected device one of
levelsand every other device level 0, in the order ofitertools.product(levels, repeat=len(devices)). One batch starts each input in its dressed state and projects every final state on the label target of every input, asamplitude_array()does. The record converts the matrix toframe. The dressed frame takes the dressed frequencies and the dressed ground energy from the static model of the solve, with its approximation and excitation cutoff. The solve leaves out collapse channels, so the matrix is the propagator of the Hamiltonian alone.- Parameters:
devices (device object, str, or sequence of them) – Selected devices, in the order of the levels of each label.
levels (sequence of int, default=(0, 1)) – Levels of each selected device.
frame ({"dressed", "lab", "integration"}, default="dressed") – Frame of the returned matrix.
tlist (Any | None) – As for
simulate(). The propagator runs fromtlist[0]totlist[-1].duration (Any | None) – As for
simulate(). The propagator runs fromtlist[0]totlist[-1].solver (str | None) – As for
simulate(). The propagator runs fromtlist[0]totlist[-1].options (dict | None) – As for
simulate(). The propagator runs fromtlist[0]totlist[-1].backend (Any | None) – As for
simulate(). The propagator runs fromtlist[0]totlist[-1].approximation (Approximation | None) – As for
simulate(). The propagator runs fromtlist[0]totlist[-1].dissipation (bool or None, default=None) –
Noneleaves out the chip’s collapse channels and warns when it has any.Falseleaves them out without a warning.Trueraises, because the matrix cannot include them. The result’somitted_channelsnames the channels that the solve left out.progress (bool, default=False) – Show backend batch progress when supported.
max_workers (int or None, default=None) – As for
simulate_batch().
- Returns:
Matrix, labels, leakage of each input, frame frequencies, and the batch of solves.
- Return type:
- Raises:
ValueError – If
dissipationisTrue,frameis unknown, or a device or level is missing or repeated.
- active_patch(*, hops=1, method='sw', local=False, order=None)[source]¶
Reduce the chip to this schedule’s active patch (spectators eliminated).
Convenience for
quchip.chip.transformations.active_patch(); see it for the activity rule, elimination order, and validity reporting.- Parameters:
hops (int, default=1) – Coupling-graph expansion beyond scheduled targets.
method ({"sw", "exact"}, default="sw") – Reduction method forwarded to each elimination.
local (bool, default=False) – Forwarded to each elimination. Each step then computes its generator on the patch around the eliminated device.
order (sequence of devices or labels, optional) – Spectators to eliminate, in this order. By default, quchip chooses the order as
eliminate_spectators()does.
- property parameters: Mapping[str, Any]¶
Chip and scheduled-pulse values available to
with_params().
- property settings: Mapping[str, Any]¶
Read-only sequence structure, separate from numerical parameters.
- describe()[source]¶
Plain-text timeline of the scheduled pulses.
One row per pulse shows the resolved time window, drive → device, envelope with its declared parameters, and carrier frequency, followed by a count of delays, barriers, and virtual-Z entries. A pulse whose frame differs from its device also names that frame. Returns a string:
print(seq.describe()).