API reference¶
Every entry below is generated from a docstring in src/ by mkdocstrings.
Signatures, defaults, and type annotations are read from the code rather than
written out here, and each heading carries a fold with the source it came from.
Members are listed in the order they appear in their file rather than
alphabetically, and private names are hidden apart from __init__, whose
parameters are folded into the class heading. Every heading is anchored by
dotted path, so another page can link to a single member:
api-qprogram.md#qprogram.QProgram.play.
The supported surface is qprogram.__all__, the names that resolve directly on
the package after import qprogram as qp. Two other kinds of name appear here
under a longer dotted path. The waveform, operation, and block classes live in
submodules the top level does not re-export, so they are written
qp.waveforms.Gaussian, qp.operations.Play, and qp.blocks.Sweep; Call and
MeasurementField are the two names from qprogram.operations that the top
level re-exports as well. The rest are extension points an integrator needs and
a program author does not, reached through their submodule:
qp.serialization.register_operation, qp.protocol.validate_tokens,
qp.sweeps.validate_source. For the reasoning behind any of these names, read
the user guide; this page is the lookup.
Top-level¶
QProgram ¶
Top-level container for a pulse-level quantum program.
The fluent builder for the QProgram AST. Methods like play, measure, and the
control-flow context managers (sweep, average, if_) append typed
operation and block nodes to the current active block.
Parameters:
-
label(str, default:'') –Short identifier for the program; surfaced in result metadata and
.qpheaders. -
description(str | None, default:None) –Longer description of what the program does.
-
schema(BusSchema | None, default:None) –Schema backing typed bus references. Passing one turns on schema-aware bus validation and lets the
.qpwriter emit compact bus paths.
Source code in src/qprogram/qprogram.py
schema
property
¶
The attached BusSchema, or None for a program built from raw-string buses.
At most one schema per program — it defines the chip's elements and bus kinds. The .qp
writer uses it to emit bus paths (q[0].drive) rather than quoted strings; plain-string
buses keep working either way.
fragments
property
¶
The Fragment s used by this program, keyed by name.
Populated by call (including each fragment's own dependencies, registered first) and
by loads() for every fragment section in a .qp file. Iteration order is
topological: a fragment always appears before any fragment that calls it.
source_map
property
¶
The .qp source map: structural path → 1-based line in the parsed file.
Filled by loads() / load() for every node in the body: section (paths follow
qprogram.paths — () is the body, ints index elements, arm:<i> /
else / loop:<i> address conditional arms and parallel loop headers). Empty for
programs built in Python and after expand. Because the .qp round-trip
preserves structure, a path computed against a built program
looks up directly in loads(dumps(p)).source_map. Fragment-internal statements are not
mapped (diagnostics always target the expanded body).
variable ¶
variable(
id: str,
*,
label: str | None = None,
units: str | None = None,
description: str | None = None,
) -> Variable
Declare a new Variable on this program.
Parameters:
-
id(str) –Short identifier matching
[A-Za-z_][A-Za-z0-9_]*. Doubles as the.qpidentifier and must be unique within the program. -
label(str | None, default:None) –Human-readable name for plots and results.
-
units(str | None, default:None) –Unit string (e.g.
"Hz","ns"). -
description(str | None, default:None) –Longer free-form description.
Returns:
Raises:
-
ValidationError–If
idalready exists on this program.
Source code in src/qprogram/qprogram.py
measurement_handles ¶
Return the canonical MeasurementHandle for every measurement in the AST.
Walks the body in declaration order and returns op.handle for each
MeasurementOperation. The returned handles are the same Python instances the AST
stores — writing per-measurement values via handle._set_value(...) is immediately visible
to every MeasurementRef, whether the program was built in Python or loaded from a
.qp file.
Returns:
-
list[MeasurementHandle]–One handle per measurement, in declaration order.
Source code in src/qprogram/qprogram.py
register_vendor
classmethod
¶
Register a VendorNamespace subclass under name.
After registration, program.<name> returns the namespace on any QProgram instance.
Re-registering the same namespace class under the same name is a no-op (import-time side-effect modules may run twice); registering a different class under a taken name is an error — silently replacing another vendor's namespace would be a supply-chain hazard.
Parameters:
-
name(str) –Vendor identifier (also used as the dot-prefix in
.qpoperation names). Must not be a reserved keyword, the"core"sentinel, or the name of anyQProgramattribute (which would make the namespace unreachable — vendor lookup happens in__getattr__, after normal attribute resolution). -
namespace_cls(type[VendorNamespace]) –The
VendorNamespacesubclass to instantiate lazily.
Raises:
-
ValueError–If
nameis reserved, shadows aQProgramattribute, or is already registered to a different namespace class.
Source code in src/qprogram/qprogram.py
play ¶
Append a Play op — play a waveform on a bus.
Parameters:
-
bus(str) –Bus to play on.
-
waveform(Waveform | IQWaveform | str) –Concrete waveform, or a string alias resolved later by
with_waveforms.
Raises:
-
ValidationError–If
buscomes from another schema, or a concrete waveform's channel count does not match the bus's (an IQ pulse on a single-channel bus, or vice versa).
Source code in src/qprogram/qprogram.py
measure ¶
measure(
bus: str,
waveform: IQWaveform | str,
weights: IQWaveform | str,
*,
name: str | None = None,
fields: Iterable[MeasurementField] = (
MeasurementField.IQ,
),
) -> MeasurementHandle
Play a readout pulse, acquire the result, and return a stable handle.
Parameters:
-
bus(str) –Readout bus (must have
acquires=True). -
waveform(IQWaveform | str) –Readout pulse — concrete
IQWaveformor a string alias. -
weights(IQWaveform | str) –Integration weights — same shape options as
waveform. -
name(str | None, default:None) –Explicit handle name. When omitted, an auto-name is allocated using the convention described on
_allocate_measurement_name. -
fields(Iterable[MeasurementField], default:(IQ,)) –Which measurement fields to produce — an iterable of
MeasurementFieldmembers (registered field-name strings are also accepted, which is how vendors extend the set). Default(MeasurementField.IQ,);STATErequests classification,RAWthe raw ADC trace. Order and duplicates don't matter — the stored tuple is canonical. An unknown field name raisesValidationErrorhere, at the call site.
Returns:
-
MeasurementHandle–The
MeasurementHandleidentifying this measurement; pass it to -
MeasurementHandle–result.get(...).
Raises:
-
ValidationError–If
bushas no ADC, comes from another schema, a waveform's channel count does not match the bus's,namecollides with another measurement, orfieldsis a bare string, is not iterable, requests nothing, or names something other than a registered field.
Source code in src/qprogram/qprogram.py
wait ¶
Append a Wait — idle on bus for duration ns.
Parameters:
-
bus(str) –Bus to idle on.
-
duration(int | Expression) –Wait duration in nanoseconds. Accepts an
Expressionfor sweeps.
Raises:
-
ValidationError–If
buscomes from another schema.
Source code in src/qprogram/qprogram.py
sync ¶
Append a Sync — synchronize buses to a common time reference.
Parameters:
-
buses(list[str] | None, default:None) –Buses to sync, or
Noneto sync every bus currently active in the program.
Raises:
-
ValidationError–If
busesis an empty list — ambiguous between "sync nothing" and "sync everything"; passNonefor the sync-all form. Also if a listed bus comes from another schema.
Source code in src/qprogram/qprogram.py
set_frequency ¶
Append a SetFrequency — retune the NCO on bus.
Parameters:
-
bus(str) –Bus whose oscillator to retune.
-
frequency(float | Expression) –New frequency in Hz. Accepts an
Expressionfor sweeps.
Raises:
-
ValidationError–If
buscomes from another schema.
Source code in src/qprogram/qprogram.py
set_phase ¶
Append a SetPhase — set the NCO phase on bus.
Parameters:
-
bus(str) –Bus whose oscillator phase to set.
-
phase(float | Expression) –Phase in radians. Accepts an
Expressionfor sweeps.
Raises:
-
ValidationError–If
buscomes from another schema.
Source code in src/qprogram/qprogram.py
reset_phase ¶
Append a ResetPhase — reset the NCO phase on bus to zero.
Parameters:
-
bus(str) –Bus whose oscillator phase to reset.
Raises:
-
ValidationError–If
buscomes from another schema.
Source code in src/qprogram/qprogram.py
set_gain ¶
Append a SetGain — set the output gain on bus.
Parameters:
-
bus(str) –Bus whose output gain to set.
-
gain(float | Expression) –New gain. Accepts an
Expressionfor sweeps.
Raises:
-
ValidationError–If
buscomes from another schema.
Source code in src/qprogram/qprogram.py
set_offset ¶
set_offset(
bus: str,
offset_path0: float | Expression,
offset_path1: float | Expression | None = None,
) -> None
Append a SetOffset — set DC offset on one or both paths of bus.
Parameters:
-
bus(str) –Bus whose DC offset to set.
-
offset_path0(float | Expression) –Offset on path 0 (the only path for single-channel buses, I for IQ buses).
-
offset_path1(float | Expression | None, default:None) –Offset on path 1 (Q for IQ buses).
Noneleaves that path's offset unchanged.
Raises:
-
ValidationError–If
buscomes from another schema.
Source code in src/qprogram/qprogram.py
set_parameter ¶
Append a SetParameter — write a bus-scoped parameter.
A parameter write is platform configuration rather than a real-time instruction, so platforms expose it host-side only.
Parameters:
-
bus(str) –The bus whose parameter is written.
-
parameter(str) –Name of the parameter to write.
-
value(float | Expression) –New value. Accepts an
Expressionfor sweeps.
Raises:
-
ValidationError–If
buscomes from another schema.
Source code in src/qprogram/qprogram.py
get_parameter ¶
Append a GetParameter and return the freshly-declared variable.
Derives a unique variable id from f"{bus}_{parameter}", replacing non-word characters
with underscores and appending a numeric suffix on collision; the original
bus.parameter form is kept on the variable's label for traceability.
Parameters:
-
bus(str) –The bus whose parameter is read.
-
parameter(str) –Name of the parameter to read.
Returns:
Raises:
-
ValidationError–If
buscomes from another schema, or the derived id is not a validVariableid — which is what a bus or parameter name carrying letters or digits outside ASCII produces.
Source code in src/qprogram/qprogram.py
sweep ¶
Open a Sweep binding variable to a source's values.
The DSL's only loop. What varies between a linear ramp, an explicit table, a log-spaced set and a composed pattern is the source, not the block — and there are two equal-billing ways to say which source.
Pick the values fluently, which is the shorter spelling and needs no source class in scope::
with program.sweep(freq).from_range(4e9, 6e9, 1e6):
...
with program.sweep(amp).from_linspace(0.0, 1.0, num=101):
...
with program.sweep(det).from_logspace(1e6, 1e9, num=50):
...
with program.sweep(phi).from_values(calibrated_phases):
...
with program.sweep(phi).from_file("phases.npy"):
...
with program.sweep(phi).from_values(base).rotate(by=1).repeat(3):
...
Or pass the source object, which is what you want when you are computing the source rather
than writing it out — and the form that reaches combinator nestings the fluent
rotate / repeat shortcuts don't::
with program.sweep(freq, qp.Range(4e9, 6e9, 1e6)):
...
with program.sweep(phi, qp.Concat(qp.Rotate(base, by=i) for i in range(4))):
...
with program.sweep(freq, source): # held in a variable, from a scan spec, ...
...
Both build the identical Sweep node and serialize to the identical
.qp line. Every registered source has a fluent builder, vendor sources included — see
_SweepBuilder.
Use | on the returned context manager to compose several sweeps into a
Parallel block that advances them in lockstep.
Parameters:
-
variable(Variable) –The
Variablerebound each iteration. -
source(SweepSource, default:_UNSET) –A
SweepSource. A bare 1-D sequence is accepted as shorthand forValues. Omit it to get a_SweepBuilderand pick the values with afrom_*method instead.
Returns:
-
_SweepBuilder | _LoopContext–A context manager opening the sweep block, or — when
sourceis omitted — the -
_SweepBuilder | _LoopContext–_SweepBuilderthat produces one.
Raises:
-
ValidationError–If
sourceis given but is neither a sweep source nor a 1-D sequence of values.
Source code in src/qprogram/qprogram.py
average ¶
Open an averaging block that repeats its body shots times and averages the results.
Parameters:
-
shots(int) –How many times to repeat the body.
Returns:
-
_AverageContext–The context manager that opens the
Averageblock.
Source code in src/qprogram/qprogram.py
block ¶
Open a generic grouping block — a container that carries no semantics of its own.
Returns:
-
_BlockContext–The context manager that opens the
Block.
Source code in src/qprogram/qprogram.py
if_ ¶
Open an if arm gated on a measurement-state predicate.
Build chains with sequential with blocks::
with program.if_(m.state == 0):
program.play(q[0].drive, "id_pulse")
with program.elif_(m.state == 1):
program.play(q[0].drive, "pi_pulse")
with program.else_():
pass
The producing measurement must request state classification (fields must include
STATE); the validator emits missing-classification
otherwise.
Parameters:
-
condition(Expression) –A
Comparisonbetween aMeasurementRef(fromhandle.state) and anintliteral. That is the only accepted shape.
Returns:
-
_IfContext–The context manager that opens the conditional's first arm.
Raises:
-
ValidationError–If
conditionis anything other than a comparison of a measurement-state reference against anintliteral.
Source code in src/qprogram/qprogram.py
elif_ ¶
Extend the open if_ chain with another arm.
Must appear immediately after the matching if_() / elif_() at the same nesting level;
any other append in between closes the chain. Condition shape is the same as if_.
Parameters:
-
condition(Expression) –The arm's condition, in the shape
if_documents.
Returns:
-
_ElifContext–The context manager that opens the new arm.
Raises:
-
ValidationError–If
conditionhas the wrong shape, no conditional chain is open at this nesting level, or the chain already has anelse_()arm.
Source code in src/qprogram/qprogram.py
else_ ¶
Close the open if_ chain with an unconditional arm.
Must appear immediately after the matching if_() / elif_() at the same nesting level.
At most one else_() per chain.
Returns:
-
_ElseContext–The context manager that opens the
elsebody.
Raises:
-
ValidationError–If no conditional chain is open at this nesting level, or the chain already has an
else_()arm.
Source code in src/qprogram/qprogram.py
call ¶
Instantiate a Fragment at the current position.
Appends a first-class Call node — the fragment definition and
the call site both survive serialization and round-trip through .qp. Use
expand to lower every call into the substituted fragment body.
Arguments bind to the fragment's parameters with the Python calling convention (positional
in declaration order, then keywords). Accepted values: numbers, expressions/variables,
buses (strings or BusRef), and waveforms.
The fragment (and, transitively, any fragment it calls) is registered on this program so
the .qp writer can emit its definition.
Parameters:
-
fragment(Fragment) –The fragment to call.
-
*args(object, default:()) –Positional arguments, bound in parameter declaration order.
-
**kwargs(object, default:{}) –Keyword arguments, bound by parameter name.
Raises:
-
ValidationError–On a non-Fragment argument, a binding error (missing/extra/duplicate parameter, unsupported value type), a name clash with a different fragment already used by this program, a schema mismatch, or a call cycle.
Source code in src/qprogram/qprogram.py
expand ¶
Return a deep copy with every fragment Call inlined.
The canonical lowering from the composed form to a fragment-free program: each call site is
replaced by a plain Block containing the fragment body with parameters substituted
by the bound arguments. Fragment-local variables are hygienically renamed onto this program
({fragment}_{id}, numeric suffix on collision); colliding measurement names gain a
_2 / _3 suffix (the shared handle is renamed, keeping handle.state conditionals
consistent). Nested calls expand recursively; expansion is deterministic, so expanding twice
yields structurally equal programs.
A program with no calls is deep-copied and returned unchanged in structure.
Returns:
-
QProgram–A new, fragment-free
QProgram; the original is untouched. Its fragment -
QProgram–registry and its
.qpsource map are both empty either way, because expansion -
QProgram–restructures the tree the recorded paths address.
Raises:
-
ValidationError–On a fragment call cycle or a binding used in an incompatible position (e.g. a waveform bound to a parameter used inside arithmetic).
Source code in src/qprogram/qprogram.py
rebind ¶
rebind(
*,
schema: BusSchema | None = None,
elements: Mapping[
tuple[str, int | tuple[int, ...]],
tuple[str, int | tuple[int, ...]],
]
| None = None,
naming: BusNaming | None = None,
strings: Mapping[str, str] | None = None,
allow_unported_strings: bool = False,
) -> QProgram
Return a copy of this program with its bus references re-resolved structurally.
Rather than rewriting bus strings, rebind re-resolves every schema-backed
BusRef through a schema factory, so the result stays a typed BusRef
(serializing as a q[1].drive path, not a quoted string) and can re-index a qubit, move to a
different element, swap naming conventions, or move onto another chip's schema — all checked
against the schema (an absent bus kind raises AttributeError).
Auto-allocated measurement names embed the bus (q0/readout/m0); rebind re-derives them for
the rebound buses while leaving user-supplied names untouched (see
MeasurementHandle). Fragment calls are expanded first.
Parameters:
-
schema(BusSchema | None, default:None) –Target schema. Defaults to the program's current schema (re-index within one chip).
-
elements(Mapping[tuple[str, int | tuple[int, ...]], tuple[str, int | tuple[int, ...]]] | None, default:None) –Maps
(element, idx)to(element, idx)— e.g.{("q", 0): ("q", 1)}to port qubit 0's operations onto qubit 1. Unlisted(element, idx)pairs pass through. -
naming(BusNaming | None, default:None) –Re-resolve every ref under a new
BusNaming(cross-platform names). -
strings(Mapping[str, str] | None, default:None) –Escape hatch for raw-string buses (which carry no schema metadata): an old→new map. Map a string to itself to mark it intentionally untouched.
-
allow_unported_strings(bool, default:False) –When
False(default), a raw-string bus not covered bystringsraises — a partial port is a loud choice, not a silent accident. SetTrueto leave uncovered raw-string buses in place.
Returns:
Raises:
-
ValidationError–If
namingis given without a schema, or raw-string buses are left unported withoutallow_unported_strings. -
AttributeError–If a rebound
(element, idx, kind)does not resolve against the target schema (e.g. the target element lacks that bus kind). -
KeyError–If the target schema's naming pattern names a placeholder other than
{element},{index}or{kind}. -
ValueError–If the target schema's naming pattern is not a well-formed format string.
Source code in src/qprogram/qprogram.py
1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 | |
with_waveforms ¶
Return a copy with string waveform names resolved to concrete waveforms, scoped per bus.
For each operation whose waveform attribute is still a string, the name is looked up against
waveforms for that operation's bus — so a shared name like "pi_pulse" can resolve to a
different concrete pulse on q[0].drive than on q[1].drive. Concrete waveforms and names
with no matching entry pass through unchanged. Each replacement re-runs the channel-type check,
so an IQ pulse landing on a single-channel bus is caught here rather than at the hardware compiler.
Parameters:
-
waveforms(WaveformLibrary | Mapping[str, Waveform | IQWaveform]) –A
WaveformLibrary(resolved per bus), or a plain{name: waveform}mapping (one global tier, resolved on every bus).
Returns:
Raises:
-
ValidationError–If a resolved waveform's channel count does not match its bus's, or if
waveformsis a mapping keyed by anything other than non-empty strings.
Source code in src/qprogram/qprogram.py
Sweep builders¶
program.sweep(variable), with the source left out, returns a source builder;
program.sweep(variable, source) returns the loop context straight away. Both
are private classes that user code never constructs, but their methods are part
of the public surface, so they are documented here. Entering a builder before a
from_* call has picked any values raises ValidationError rather than
sweeping nothing. The context managers behind average, block, if_,
elif_, and else_ add nothing to the context-manager protocol, so they have
no entries of their own.
_SweepBuilder ¶
Source picker returned by program.sweep(variable) when no source is passed.
Each from_* builds one SweepSource and returns exactly what the
two-argument sweep(variable, source) form returns — the same Sweep node, the same
.qp line, the same | composition. The only thing it changes is the call site, which does
not have to name a source class::
with program.sweep(freq).from_range(4e9, 6e9, 1e6):
...
with program.sweep(freq).from_range(4e9, 6e9, 1e6) | program.sweep(amp).from_values(table):
...
Both spellings are supported on purpose. Reach for from_* when writing a sweep by hand; pass
the source object when computing one — holding it in a variable, building it in a
comprehension, or composing combinators more deeply than _LoopContext.rotate and
_LoopContext.repeat reach.
Every registered source is reachable here, not just the built-ins: an unknown from_<name>
attribute is resolved against the live sweep-source registry (see __getattr__), so a
vendor source gets its builder with no core change. The five built-ins are additionally written
out as real methods, so editors complete and type-check them.
A builder is not a context manager — it has no values yet. with program.sweep(v): raises
instead of quietly sweeping nothing.
Source code in src/qprogram/qprogram.py
from_range ¶
Sweep a ramp from start to stop in increments of step, both ends inclusive.
Builds Range, which is where the validation rules live.
Parameters:
-
start(float) –First value (inclusive).
-
stop(float) –Final value (inclusive).
-
step(float, default:1) –Increment between consecutive points. Defaults to
1.
Returns:
-
_LoopContext–The context manager that opens the sweep block.
Source code in src/qprogram/qprogram.py
from_linspace ¶
Sweep num evenly spaced points from start to stop, both ends inclusive.
Builds Linspace — the ramp to prefer when you know the point count
rather than the spacing.
Parameters:
-
start(float) –First value (inclusive).
-
stop(float) –Final value (inclusive).
-
num(int) –Number of points.
1yields[start].
Returns:
-
_LoopContext–The context manager that opens the sweep block.
Source code in src/qprogram/qprogram.py
from_logspace ¶
Sweep num points spaced evenly on a log scale between start and stop.
Builds Logspace. Both bounds are actual values, not exponents.
Parameters:
-
start(float) –First value (inclusive). Must be strictly positive.
-
stop(float) –Final value (inclusive). Must be strictly positive.
-
num(int) –Number of points.
Returns:
-
_LoopContext–The context manager that opens the sweep block.
Source code in src/qprogram/qprogram.py
from_values ¶
Sweep an explicit list of points.
Builds Values, which is KIND = "arbitrary" even when the points
happen to be evenly spaced — use from_range or from_linspace when the sweep
really is a ramp and you want a platform to be able to compile it as one.
Parameters:
-
points(ArrayLike) –Sequence of values to iterate through. Anything
numpy.asarrayaccepts.
Returns:
-
_LoopContext–The context manager that opens the sweep block.
Source code in src/qprogram/qprogram.py
from_file ¶
Sweep the points held in the .npy file at path.
Builds File, which stores the path rather than the values — the
file must be readable wherever the program is validated or run.
Parameters:
-
path(str) –Path to a
.npyfile holding a 1-D array.
Returns:
-
_LoopContext–The context manager that opens the sweep block.
Source code in src/qprogram/qprogram.py
__getattr__ ¶
Resolve an unknown from_<source> attribute against the live sweep-source registry.
This is what keeps the fluent form open: whatever
register_sweep_source knows about is spellable here — vendor sources and
the combinators included — matched on the class name with underscores and case ignored, so
from_iq_table finds IQTable. The returned callable forwards its arguments to the
source's constructor, so the source itself still does the validating.
Parameters:
-
attribute(str) –The attribute being looked up.
Returns:
-
Callable[..., _LoopContext]–A callable that constructs the named source from the arguments it is given and returns
-
Callable[..., _LoopContext]–the loop context for the resulting sweep.
Raises:
-
AttributeError–For a
from_*attribute no registered source answers (with a did-you-mean list), for a shaping method that belongs on the sweep rather than on the builder, and for any other attribute, as usual.
Source code in src/qprogram/qprogram.py
_LoopContext ¶
Context manager returned by QProgram.sweep.
Supports | to compose multiple sweeps into a Parallel block. __or__ is pure —
it returns a fresh context with the concatenated list and touches the program only on
__enter__ — so a list of sweeps can be folded programmatically::
functools.reduce(operator.or_, [program.sweep(v, src) for v, src in specs])
repeat and rotate are pure in the same way. They wrap the bound source in the
matching combinator and hand back a fresh context, which is what lets a sweep be shaped inline
without naming Repeat / Rotate::
with program.sweep(phi).from_values(base).rotate(by=1).repeat(3):
...
Source code in src/qprogram/qprogram.py
__or__ ¶
Compose this sweep with other into a Parallel block.
Parameters:
-
other(_LoopContext) –The sweep context to advance in lockstep with this one.
Returns:
-
_LoopContext–A fresh context carrying both operands' sweeps; neither operand is modified.
Source code in src/qprogram/qprogram.py
repeat ¶
Run the bound source's points times times back to back — Repeat.
Each repetition is a distinct sweep point with its own result entry; reach for
QProgram.average when you want the repetitions collapsed instead.
Parameters:
-
times(int) –How many times to run the source. Must be at least 1.
Returns:
-
_LoopContext–A fresh context bound to the wrapped source; this one is left untouched.
Raises:
-
ValidationError–If this context is already a
|composition, which gives the combinator no single source to wrap.
Source code in src/qprogram/qprogram.py
rotate ¶
Cyclically shift the bound source's points left by by — Rotate.
Parameters:
-
by(int, default:1) –Positions to shift left. May be negative (shifts right) or exceed the point count (wraps, as
numpy.rolldoes).
Returns:
-
_LoopContext–A fresh context bound to the wrapped source; this one is left untouched.
Raises:
-
ValidationError–If this context is already a
|composition.
Source code in src/qprogram/qprogram.py
Sweep sources¶
What a Sweep iterates over: a description of the values, never a producer of
them. Every source answers length() and values() without the program
running, declares a KIND of "linear" or "arbitrary" along with its own
sweep.<name> capability token, and compares and hashes structurally over its
public attributes, which are treated as immutable once the source is in a
program. register_sweep_source puts a subclass in the registry under its own
class name, which is what makes it parseable from a .qp file and spellable as
sweep(variable).from_<name>(...); validate_source checks the length() and
values() invariants for a new one.
SweepSource ¶
Bases: ABC
How a Sweep generates the values it binds to its variable.
Subclasses declare KIND and TOKEN, implement length and values,
and store their parameters as public instance attributes (which is what makes the .qp
serialization signature-driven and free).
Equality and hashing are structural over those attributes, so two sources built the same way
compare equal and a program survives deepcopy / loads(dumps(...)) comparison. Sources are
conceptually immutable: once one has been used in a program, do not mutate its attributes.
KIND
class-attribute
¶
Whether the values form an exact start + step * i ramp ("linear") or not
("arbitrary").
This is a claim about compilability, not a description of the numbers: a platform may compile
a linear sweep to a loop register with an increment, while an arbitrary one needs a value table or
a host-side dispatch per point. Two sources can produce identical values and still differ here —
Values listing an even ramp is still "arbitrary", because nothing
about the source proves the regularity to a compiler.
TOKEN
class-attribute
¶
This source's own sweep.<name> capability token, registered with the global registry when
the class is registered. A platform that can generate this source natively declares the token; one
that cannot omits it and the validator reports it, rather than the platform silently materializing
the points into a table.
length
abstractmethod
¶
Return the number of sweep points.
Must be answerable statically — without running the program, and without side effects beyond
reading this source's own parameters. It must also be at least one: a sweep with no points
never executes its body, so a built-in source rejects an empty parameterization as soon as
it can see one, at construction for the sources that carry their values and on first read
for File, which learns the length only when it loads the array.
validate_source holds a subclass to the same rule.
Returns:
-
int–The number of points the sweep iterates through.
Source code in src/qprogram/sweeps/source.py
values
abstractmethod
¶
Return the 1-D array of values this source sweeps, in iteration order.
len(values()) == length() is an invariant; validate_source checks it for the
built-ins' tests and any subclass that wants the same guard.
Returns:
-
ndarray–A 1-D array of
lengthvalues, in the order the sweep binds them.
Source code in src/qprogram/sweeps/source.py
tokens ¶
Return every capability token this source requires.
The source's own TOKEN plus its sweep.<kind> token. Combinators override this to
union their wrapped sources' tokens too — a platform that cannot generate
Logspace also cannot generate a rotation of one.
Returns:
-
set[str]–The capability tokens a platform must declare to generate this source.
Source code in src/qprogram/sweeps/source.py
Range ¶
Bases: SweepSource
A linear ramp from start toward stop in increments of step.
The source a hardware sequencer can usually run without a value table: one loop register plus an
increment. Prefer Linspace when you know the number of points rather than the spacing.
The ramp always begins at start and holds round((stop - start) / step) + 1 points, so it
lands on stop only when step divides stop - start evenly. Otherwise the last point
stops short of stop — Range(0, 1, 0.3) ends at 0.9, Range(0, 0.4, 1) is the single
point 0.0 — or steps past it, as Range(0, 1, 0.6) does by ending at 1.2. Reach for
Linspace when the last point has to land exactly on stop.
Parameters:
-
start(float) –First value, always produced.
-
stop(float) –The value the ramp runs toward, produced only when
stepdividesstop - startevenly. -
step(float, default:1) –Increment between consecutive points. Defaults to
1.
Raises:
-
ValidationError–If any bound is non-numeric or non-finite, if
stepis zero (an infinite sweep), or ifsteppoints away fromstop(an empty sweep).
Source code in src/qprogram/sweeps/builtin.py
length ¶
Return the number of points in the ramp.
The count is round((stop - start) / step) + 1 — the rounding absorbs floating-point
division noise for ranges like (0.0, 1.0, 0.01), and it is also what lets the last point
stop short of stop or step past it when step does not divide stop - start evenly.
Returns:
-
int–The number of points in the ramp, counting the one at
start.
Source code in src/qprogram/sweeps/builtin.py
values ¶
Return the ramp's points, in iteration order.
Returns:
-
ndarray–start + step * arange(length())— consistent withlengthby construction.
Source code in src/qprogram/sweeps/builtin.py
Linspace ¶
Bases: SweepSource
num evenly spaced points from start to stop, both ends inclusive.
The shape most sweeps actually want — you usually know how many points you can afford, not the
spacing that lands on them. Linear, so a platform can compile it as a ramp: the derived step is
(stop - start) / (num - 1).
Parameters:
-
start(float) –First value (inclusive).
-
stop(float) –Final value (inclusive).
-
num(int) –Number of points.
1yields[start].
Raises:
-
ValidationError–If a bound is non-numeric or non-finite, or
numis not an int >= 1.
Source code in src/qprogram/sweeps/builtin.py
length ¶
values ¶
Return the evenly spaced points.
Returns:
-
ndarray–numpy.linspaceover the closed interval[start, stop].
step ¶
Return the spacing this sweep resolves to, for a compiler that wants start/step form.
Returns:
-
float–(stop - start) / (num - 1), or0.0for a single-point sweep, where the spacing is -
float–undefined.
Source code in src/qprogram/sweeps/builtin.py
Logspace ¶
Bases: SweepSource
num points spaced evenly on a log scale between start and stop (both linear values).
Note the argument convention: start and stop are the actual first and last values, not the
exponents numpy.logspace takes — a frequency sweep reads Logspace(1e6, 1e9, num=50)
rather than Logspace(6, 9, num=50).
Parameters:
-
start(float) –First value (inclusive). Must be strictly positive.
-
stop(float) –Final value (inclusive). Must be strictly positive.
-
num(int) –Number of points.
Raises:
-
ValidationError–If a bound is non-positive, non-numeric or non-finite, or
numis not an int >= 1.
Source code in src/qprogram/sweeps/builtin.py
Values ¶
Bases: SweepSource
An explicit list of sweep points.
Use it when the points don't fit a regular pattern: calibrated values, a measured table, the output of a computation
you ran yourself. Note that it is KIND "arbitrary" even when the values happen to be evenly spaced — the
source proves nothing about their regularity to a compiler, so reach for Range or
Linspace when the sweep really is a ramp and you want a platform to be able to compile it as
one.
Parameters:
-
points(ArrayLike) –Sequence of values to iterate through. Anything
numpy.asarrayaccepts. Namedpointsrather thanvaluesso it doesn't collide withvalues; on the wire it is almost always written as the bracket literal[...]anyway.
Raises:
-
ValidationError–If
pointsis empty or not 1-D.
Source code in src/qprogram/sweeps/builtin.py
length ¶
Return the number of points, one per element of the stored array.
Returns:
-
int–The size of the stored array.
values ¶
Return the points given at construction.
Returns:
-
ndarray–The stored
floatarray itself, not a copy — treat it as read-only, since the source -
ndarray–is a value object whose equality and hash are derived from it.
Source code in src/qprogram/sweeps/builtin.py
File ¶
Bases: SweepSource
Sweep points loaded from a .npy file at the given path.
The path is what the AST stores, so a .qp file records where the points came from rather
than inlining them — which keeps the file small and the intent legible. The cost is that the file
must be readable wherever the program is validated or run: length and values both
load it, and neither caches (caching would put the loaded array into the structural equality of
the source, so an already-loaded instance would stop comparing equal to a fresh one).
Parameters:
-
path(str) –Path to a
.npyfile holding a 1-D array.
Raises:
-
ValidationError–If
pathis empty.
Source code in src/qprogram/sweeps/builtin.py
length ¶
Load the file and report its length.
Returns:
-
int–The number of values the file holds.
Raises:
-
ValidationError–If the file holds an array that is not 1-D, or holds no values.
-
OSError–If the path does not exist or cannot be read.
Source code in src/qprogram/sweeps/builtin.py
values ¶
Load the file and return its contents.
Returns:
-
ndarray–The file's contents as a 1-D
floatarray.
Raises:
-
ValidationError–If the file holds an array that is not 1-D, or holds no values.
-
OSError–If the path does not exist or cannot be read.
Source code in src/qprogram/sweeps/builtin.py
Repeat ¶
Bases: SweepSource
An inner source's points repeated times times, back to back.
Repeat(Values([0, 1]), times=3) sweeps 0, 1, 0, 1, 0, 1. Note this is not averaging:
each repetition is a distinct sweep point with its own result entry. Use
average when you want the repetitions collapsed.
Reports KIND = "arbitrary" even around a linear source — see the module docstring for why the
conservative direction is the correct one.
Parameters:
-
source(SweepSource) –The source to repeat. A bare sequence is accepted and wrapped in
Values. -
times(int) –How many times to run it. Must be an int >= 1.
Raises:
-
ValidationError–If
sourceisn't a source or sequence, or iftimesis not anint, is abool, or is less than1.
Source code in src/qprogram/sweeps/combinators.py
length ¶
Return the number of points across every repetition.
Returns:
-
int–source.length() * times.
values ¶
Return the repeated points.
Returns:
-
ndarray–The wrapped source's values tiled
timestimes.
tokens ¶
Return every capability token this source requires.
Returns:
-
set[str]–sweep.repeatandsweep.arbitrary, plus everything the wrapped source needs.
Source code in src/qprogram/sweeps/combinators.py
Rotate ¶
Bases: SweepSource
An inner source's points cyclically shifted left by by positions.
Rotate(Values([0, 1, 2, 3]), by=1) sweeps 1, 2, 3, 0. The point count is unchanged. Use it
with Concat to build the phase-cycling pattern where a sequence is swept once per starting
offset::
Concat(Rotate(base, by=i) for i in range(base.length()))
Parameters:
-
source(SweepSource) –The source to rotate. A bare sequence is accepted and wrapped in
Values. -
by(int, default:1) –Number of positions to shift left. May be negative (shifts right) or exceed the length (wraps, as
numpy.rolldoes). Defaults to1.
Raises:
-
ValidationError–If
sourceisn't a source or sequence, or ifbyis not anintor is abool.
Source code in src/qprogram/sweeps/combinators.py
length ¶
Return the number of points, which rotation leaves unchanged.
Returns:
-
int–The wrapped source's length.
values ¶
Return the rotated points.
Returns:
-
ndarray–The wrapped source's values under
numpy.rollby-by— a left shift, so -
ndarray–by=1starts at the second point.
Source code in src/qprogram/sweeps/combinators.py
tokens ¶
Return every capability token this source requires.
Returns:
-
set[str]–sweep.rotateandsweep.arbitrary, plus everything the wrapped source needs.
Source code in src/qprogram/sweeps/combinators.py
Concat ¶
Bases: SweepSource
Several sources joined end to end into a single sweep.
Concat([Range(0, 1, 0.5), Values([10, 20])]) sweeps 0, 0.5, 1, 10, 20. Accepts any
iterable, so a comprehension or generator expression works directly.
Parameters:
-
sources(Iterable[SweepSource]) –The sources to concatenate, in order. At least one. Bare sequences among them are wrapped in
Values.
Raises:
-
ValidationError–If
sourcesis a single source rather than an iterable of them, is empty, or holds something that is neither a source nor a sequence of values.
Source code in src/qprogram/sweeps/combinators.py
length ¶
Return the number of points across every part.
Returns:
-
int–The sum of the wrapped sources' lengths.
values ¶
Return the concatenated points.
Returns:
-
ndarray–The parts' values joined in order.
tokens ¶
Return every capability token this source requires.
Returns:
-
set[str]–sweep.concatandsweep.arbitrary, plus everything every wrapped source needs.
Source code in src/qprogram/sweeps/combinators.py
register_sweep_source ¶
Register a sweep-source class for .qp serialization, keyed by its class name.
The sweep analogue of register_waveform, and the whole extension step for a new source:
once registered, for <var> in <ClassName>(...) parses and writes itself from the constructor
signature, exactly as a waveform constructor does.
Also registers the class's TOKEN in the capability registry,
so a profile may list it without a separate register_capability_tokens call — mirroring
register_waveform_token.
Same-class re-registration is a no-op; registering a different class under an already-taken name raises — it would silently change how every existing file parses that constructor.
Parameters:
-
cls(type[SweepSource]) –Sweep-source class to register. Its
__name__is the constructor name on the wire, and itsTOKENis added to the capability registry.
Returns:
-
type[SweepSource]–cls, so the function can be used as a decorator.
Raises:
-
ValueError–If
cls.__name__is already registered to a different class.
Source code in src/qprogram/serialization/registry.py
known_sweep_sources ¶
Return the class name of every registered sweep source.
The live registry, so a vendor source registered at import time is included immediately. Both
did-you-mean lists read it: the parser's, for an unknown for x in Name(...) constructor, and
the fluent builder's, for an unknown sweep(var).from_* attribute.
Returns:
-
set[str]–The class name of every sweep source currently registered.
Source code in src/qprogram/serialization/registry.py
validate_source ¶
Assert the SweepSource.length / SweepSource.values invariants.
Not called on the hot path — it materializes the values. Tests and source authors use it to check a new subclass honors the contract.
Parameters:
-
source(SweepSource) –The source to check.
Raises:
-
AssertionError–If the values are not a non-empty 1-D array whose length matches
SweepSource.length.
Source code in src/qprogram/sweeps/source.py
Bus schemas¶
BusSchema ¶
The bus kinds each element kind on a chip exposes.
Three construction modes:
- Presets —
transmon,fluxonium, etc. return fully-typed subclasses with IDE autocomplete. - Dynamic — instantiate
BusSchemadirectly and calladd_elementfor custom topologies. Bus access viaschema.q[0].driveworks at runtime but has no static type. - Custom typed — subclass
BusSchemato expose your own typed accessors; see the user guide for the template.
Schemas compose: schema_a + schema_b (or combine for three or more, or for naming
control) returns a new schema with the union of both element families. Either operand may be a
schema instance or a schema class, e.g. FluxTunableTransmonSchema + RFSwitchSchema. The result
is a plain (dynamic) BusSchema — runtime access like combined.q[0].drive works, but it
carries no static typing (the same trade-off as add_element). Build refs from the
combined schema, not the originals, so their BusRef.schema back-pointer matches the
schema you attach to a program.
Each QProgram holds at most one schema — passed at construction, or adopted
from the first schema-backed ref the program sees. The .qp writer reads program.schema —
not the individual refs' BusRef.schema back-pointers — both to emit the schema: block
and to decide that a bus renders as an element[idx].kind path. A ref's own back-pointer
records which schema produced it, which is what lets a program refuse a ref from a foreign schema
and what routes the bus to its per-bus capability profile.
Parameters:
-
naming(BusNaming | None, default:None) –Naming convention for the bus strings this schema resolves. When omitted, a default
BusNamingis used.
Attributes:
-
KIND(str) –Class-level identifier set by built-in presets (
"transmon","fluxonium", ...). Informational; user subclasses may set their own. A combined schema keeps the base"".
Source code in src/qprogram/buses.py
elements
property
¶
The registered element schemas, keyed by element name.
add_element ¶
Register an element type and its available bus kinds.
For statically-typed schemas, subclass BusSchema and expose @property accessors
rather than using this method.
Parameters:
-
name(str) –Element name (e.g.
"q","resonator"). Registering a name twice replaces the earlier declaration. -
buses(dict[str, tuple[ChannelType, bool]]) –Mapping of bus kind to
(channel, acquires). For example::{"drive": ("IQ", False), "readout": ("IQ", True), "flux": ("single", False)}
Source code in src/qprogram/buses.py
combine
staticmethod
¶
Merge schemas into a new dynamic BusSchema.
The result holds the union of every input schema's elements, so passing a single schema
yields a dynamic copy of it. It is a plain
BusSchema (not a typed subclass), so combined.q[0].drive resolves at runtime but
without static typing — the same trade-off as building a schema with add_element.
The + operator (__add__, and the class-level form via the metaclass) delegates
here; use combine directly when joining three or more schemas in one call or when you need
to pick the naming convention explicitly.
Parameters:
-
*schemas(BusSchema, default:()) –The schemas to merge (at least one). Each must be a
BusSchemainstance. -
naming(BusNaming | None, default:None) –Naming convention for the combined schema. When omitted, every input schema must share the same naming pattern (a combined schema can carry only one) — they usually do, since the default is universal. Pass an explicit
namingto resolve a clash.
Returns:
Raises:
-
ValueError–If no schemas are given, if the inputs disagree on naming and none is given, or if two inputs define the same element name with different buses (an ambiguous merge — rename one element). Re-declaring an identical element is allowed (idempotent).
Source code in src/qprogram/buses.py
transmon
classmethod
¶
Return a schema for fixed-frequency transmon qubits (no couplers).
Qubit buses: drive (IQ), readout (IQ, acquires).
Parameters:
-
naming(BusNaming | None, default:None) –Custom naming convention for the resolved bus strings.
Returns:
-
TransmonSchema–A typed schema exposing a
qqubit accessor.
Source code in src/qprogram/buses.py
transmon_coupled
classmethod
¶
Return a schema for fixed-frequency transmon qubits with couplers.
Qubit buses: drive (IQ), readout (IQ, acquires). Coupler buses: flux (single).
Parameters:
-
naming(BusNaming | None, default:None) –Custom naming convention for the resolved bus strings.
Returns:
-
TransmonCoupledSchema–A typed schema exposing
qqubit andccoupler accessors.
Source code in src/qprogram/buses.py
flux_tunable_transmon
classmethod
¶
Return a schema for flux-tunable transmon qubits (no couplers).
Qubit buses: drive (IQ), readout (IQ, acquires), flux (single).
Parameters:
-
naming(BusNaming | None, default:None) –Custom naming convention for the resolved bus strings.
Returns:
-
FluxTunableTransmonSchema–A typed schema exposing a
qqubit accessor.
Source code in src/qprogram/buses.py
flux_tunable_transmon_coupled
classmethod
¶
flux_tunable_transmon_coupled(
naming: BusNaming | None = None,
) -> FluxTunableTransmonCoupledSchema
Return a schema for flux-tunable transmon qubits with couplers.
Qubit buses: drive (IQ), readout (IQ, acquires), flux (single).
Coupler buses: flux (single).
Parameters:
-
naming(BusNaming | None, default:None) –Custom naming convention for the resolved bus strings.
Returns:
-
FluxTunableTransmonCoupledSchema–A typed schema exposing
qqubit andccoupler accessors.
Source code in src/qprogram/buses.py
fluxonium
classmethod
¶
Return a schema for fluxonium qubits (no couplers).
Qubit buses: drive (IQ), readout (IQ, acquires), flux_x (single), flux_z (single).
Parameters:
-
naming(BusNaming | None, default:None) –Custom naming convention for the resolved bus strings.
Returns:
-
FluxoniumSchema–A typed schema exposing a
qqubit accessor.
Source code in src/qprogram/buses.py
fluxonium_coupled
classmethod
¶
Return a schema for fluxonium qubits with couplers.
Qubit buses: drive (IQ), readout (IQ, acquires), flux_x (single), flux_z (single).
Coupler buses: flux (single).
Parameters:
-
naming(BusNaming | None, default:None) –Custom naming convention for the resolved bus strings.
Returns:
-
FluxoniumCoupledSchema–A typed schema exposing
qqubit andccoupler accessors.
Source code in src/qprogram/buses.py
BusNaming ¶
Configurable bus-name format string.
The default "{element}{index}/{kind}" produces "q0/drive", "coupler0_1/flux", etc.
Platforms with entrenched naming conventions can supply their own, e.g.
"{kind}_{element}{index}_bus" for "drive_q0_bus".
Parameters:
-
pattern(str, default:DEFAULT_PATTERN) –Format string. Supported placeholders:
{element},{index},{kind}.
Source code in src/qprogram/buses.py
resolve ¶
Format a bus name from its component pieces.
Parameters:
-
element(str) –Element name, substituted for
{element}. -
index(int | tuple) –Element index, substituted for
{index}. A tuple is joined with underscores, so(0, 1)becomes0_1. -
kind(str) –Bus kind name, substituted for
{kind}.
Returns:
-
str–The bus name produced by this naming's pattern.
Raises:
-
KeyError–If the pattern names a placeholder other than
{element},{index}or{kind}. -
ValueError–If the pattern is not a well-formed format string, or applies a format specification the substituted text cannot satisfy — every piece is substituted as text, so
{index:d}fails. -
IndexError–If the pattern uses a positional placeholder such as
{0}; the three pieces are supplied by keyword only.
Source code in src/qprogram/buses.py
BusRef ¶
Bases: str
A string that also carries structured bus metadata.
A BusRef is a real str everywhere downstream — operations, serialization, compiler —
but exposes metadata attributes for tooling and validation. The idx attribute is named that way
rather than index to avoid shadowing the inherited str.index method.
The constructor takes the resolved bus name as the string value, followed by the metadata fields
below. Refs normally come from a schema accessor (schema.q[0].drive) or from
resolve_ref; constructing one directly is for buses that live outside any schema.
Attributes:
-
element(str) –Element name (e.g.
"q","coupler"). -
idx(int | tuple[int, ...]) –Element index (e.g.
0or(0, 1)). -
kind(str) –Bus kind name (e.g.
"drive","flux","readout"). -
channel(ChannelType) –"single"for real-valued waveforms,"IQ"for complex I/Q. -
acquires(bool) –Trueif the bus has an ADC and supportsQProgram.measure. -
schema(BusSchema | None) –The
BusSchemathat produced this ref, orNonefor manually-built refs. Used byQProgram._validate_busto reject buses from a different schema than the one attached to the program.
Typed schemas¶
Each preset factory on BusSchema returns one of these subclasses, whose
element properties are declared rather than resolved through __getattr__, so
an editor can complete the bus kinds. The classes are reachable under their own
names for a type annotation or for combine, which takes a class as readily as
an instance.
TransmonSchema ¶
Bases: BusSchema
Typed schema for fixed-frequency transmon qubits — exposes a typed q accessor.
Parameters:
-
naming(BusNaming | None, default:None) –Custom naming convention for the resolved bus strings.
Source code in src/qprogram/buses.py
q
property
¶
The qubit factory: schema.q[0] exposes that qubit's drive and readout buses.
TransmonCoupledSchema ¶
Bases: TransmonSchema
Typed schema for transmon qubits plus couplers — adds a typed c accessor.
Parameters:
-
naming(BusNaming | None, default:None) –Custom naming convention for the resolved bus strings.
Source code in src/qprogram/buses.py
FluxTunableTransmonSchema ¶
Bases: BusSchema
Typed schema for flux-tunable transmon qubits — exposes a typed q accessor.
Parameters:
-
naming(BusNaming | None, default:None) –Custom naming convention for the resolved bus strings.
Source code in src/qprogram/buses.py
q
property
¶
The qubit factory: schema.q[0] exposes that qubit's drive, readout and flux buses.
FluxTunableTransmonCoupledSchema ¶
Bases: FluxTunableTransmonSchema
Typed schema for flux-tunable transmon qubits plus couplers — adds a typed c accessor.
Parameters:
-
naming(BusNaming | None, default:None) –Custom naming convention for the resolved bus strings.
Source code in src/qprogram/buses.py
FluxoniumSchema ¶
Bases: BusSchema
Typed schema for fluxonium qubits — exposes a typed q accessor.
Parameters:
-
naming(BusNaming | None, default:None) –Custom naming convention for the resolved bus strings.
Source code in src/qprogram/buses.py
q
property
¶
The qubit factory: schema.q[0] exposes that qubit's drive, readout and two flux buses.
FluxoniumCoupledSchema ¶
Bases: FluxoniumSchema
Typed schema for fluxonium qubits plus couplers — adds a typed c accessor.
Parameters:
-
naming(BusNaming | None, default:None) –Custom naming convention for the resolved bus strings.
Source code in src/qprogram/buses.py
Typed schema base classes¶
A chip type no preset covers gets a subclass built from the same three pieces
the presets use: an accessor carrying one property per bus kind, a factory that
turns an index into an accessor, and the schema carrying one property per
element. The two base classes hold the machinery for the first two, and
CouplerFactory is reusable as it stands, because a coupler's single flux bus
is the same in every preset that has one. Defining your own typed
schema walks through a
complete class.
_TypedElementAccessor ¶
Base class for typed element accessors. Concrete subclasses add per-bus @property accessors.
Source code in src/qprogram/buses.py
_ref ¶
Source code in src/qprogram/buses.py
_TypedElementFactory ¶
Base class for typed element factories. Concrete subclasses specify _accessor_cls.
Source code in src/qprogram/buses.py
CouplerFactory ¶
Bases: _TypedElementFactory
Subscriptable factory returning CouplerBuses instances. Indices may be tuples.
Source code in src/qprogram/buses.py
Re-resolving a coordinate¶
resolve_ref is the one place an (element, index, kind) coordinate becomes a
BusRef: the .qp parser calls it for every element[i].kind path it reads,
and QProgram.rebind calls it for every ref it rewrites, which is what keeps a
re-indexed or ported program checked against the schema it lands on.
naming_substituted_schema covers the naming-only port, returning a dynamic
copy of the schema with the same elements declared under a new BusNaming.
resolve_ref ¶
Re-resolve an (element, index, kind) coordinate into a typed BusRef.
The single source of truth for turning a structural bus coordinate into a BusRef under
schema's naming. Used both when loading element[i].kind paths
(_resolve_bus_path) and when porting a program to
new indices or a new schema (rebind).
Parameters:
-
schema(BusSchema) –The schema to resolve the coordinate against; it becomes the ref's
BusRef.schemaback-pointer. -
element(str) –Element name, as registered on
schema. -
index(int | tuple[int, ...]) –Element index, a tuple for multi-index elements such as couplers.
-
kind(str) –Bus kind name declared for that element.
Returns:
Raises:
-
AttributeError–If
elementis not an element ofschemaorkindis not one of that element's bus kinds. -
KeyError–If
schema's naming pattern names a placeholder other than{element},{index}or{kind}. -
ValueError–If
schema's naming pattern is not a well-formed format string. -
IndexError–If
schema's naming pattern uses a positional placeholder such as{0}.
Source code in src/qprogram/buses.py
naming_substituted_schema ¶
Return a dynamic copy of schema with every element re-declared under naming.
Used by rebind for naming-only ports: the structural element/bus content
is preserved but BusRef strings (and the serialized schema: block) adopt the new
pattern. The result is a plain (untyped) BusSchema — the same trade-off as
BusSchema.combine.
Parameters:
-
schema(BusSchema) –The schema whose elements and bus kinds are carried over.
-
naming(BusNaming) –The naming convention the copy resolves bus strings through.
Returns:
Source code in src/qprogram/buses.py
Variables and expressions¶
Variable ¶
Variable(
id: str,
*,
label: str | None = None,
units: str | None = None,
description: str | None = None,
)
Bases: Expression
A symbolic variable.
Leaf node of the expression AST, holding a value that starts out UNASSIGNED.
Equality and hashing are structural over id: two variables compare equal when their id matches. Within one
program ids are unique (QProgram.variable rejects duplicates), so id-equality
coincides with identity there; across programs it is what lets a program survive deepcopy /
qp.loads(qp.dumps(...)) and still compare equal to the original.
The runtime sets the value via set_value per loop iteration; reading value (or
calling evaluate) returns the current value or UNASSIGNED.
Parameters:
-
id(str) –Short name matching
[A-Za-z_][A-Za-z0-9_]*. Doubles as the identifier in the.qpformat, so no spaces or punctuation. -
label(str | None, default:None) –Human-readable name for axis labels, plot titles, and the like.
-
units(str | None, default:None) –Unit string, such as
"Hz","ns", or"V". -
description(str | None, default:None) –Longer free-form description.
Raises:
-
InvalidVariableIdError–If
idviolates the pattern or is reserved (seeRESERVED_KEYWORDS).
Source code in src/qprogram/variable.py
label
property
¶
The human-readable name for axis labels and plot titles, or None.
value
property
¶
The current value, or UNASSIGNED if no value has been set.
set_value ¶
Set the variable's current value.
Parameters:
-
value(float) –The value to bind. The runtime writes it once per loop iteration.
reset ¶
evaluate ¶
Return the variable's current value.
Returns:
-
int | float | _UnassignedType–The bound value, or
UNASSIGNEDwhile nothing is bound.
Expression ¶
Bases: ABC
Abstract base for symbolic expressions.
Subclasses split into four families:
- Leaves —
Variable,Constant,MeasurementRef. - Arithmetic —
BinaryOp(+ - * /),UnaryOp(- +). - Comparison & logical —
Comparison,LogicalBinaryOp,LogicalNot. - Math & conditional —
MathFunc,Where.
Operators that map cleanly onto Python syntax build the corresponding AST nodes uniformly across
every subclass. The exceptions are == / != (use eq / ne so Variable
can keep a plain-bool __eq__ for set / dict-key use) and and / or / not (Python
keywords, unoverloadable — use & | ~ or the named helpers and_ / or_ /
not_).
Why __bool__ raises: if freq < 5e9: would otherwise silently test the truthiness of the
Comparison instance (always true) instead of building the conditional the user almost
certainly meant. Mirrors SymPy.
evaluate
abstractmethod
¶
Compute the value of the expression.
Returns:
-
int | float | _UnassignedType–A numeric result for arithmetic / math expressions, a
boolfor comparisons and logical -
int | float | _UnassignedType–expressions (
boolis anintsubclass), orUNASSIGNEDwhen any referenced -
int | float | _UnassignedType–variable is currently unassigned.
Source code in src/qprogram/variable.py
evaluate_or_raise ¶
Compute the value of the expression, raising instead of returning UNASSIGNED.
Returns:
-
int | float–The numeric value.
Raises:
-
UnassignedVariableError–When any referenced variable is unassigned. Use this when the caller (e.g. waveform envelope computation) requires a concrete value.
-
ZeroDivisionError–When the expression divides by an operand that evaluates to zero.
Source code in src/qprogram/variable.py
variables
abstractmethod
¶
Return the free Variable s appearing in this expression.
Every node unions the sets reported by its children, so one call covers the whole subtree.
Returns:
-
set[Variable]–The variables reachable from this node; an empty set for leaves that hold none.
Source code in src/qprogram/variable.py
Constant ¶
Bases: Expression
Concrete numeric leaf with structural equality (Constant(5) == Constant(5)).
Numeric literals appearing in expressions are auto-wrapped to Constant by the operator
methods on Expression.
Parameters:
-
value(float) –The number to hold; an
intis kept as anint.boolis rejected — booleans would silently coerce to 0/1 and obscure intent.
Raises:
-
TypeError–If
valueis not anintorfloat(or is abool).
Source code in src/qprogram/variable.py
BinaryOp ¶
Bases: Expression
Binary arithmetic node — left op right for one of + - * /.
Constructed by the arithmetic operators on Expression. Numeric literals are auto-wrapped
to Constant, so both operands are always Expression instances.
Parameters:
-
op(BinaryOperator) –Operator symbol — one of
+,-,*,/. -
left(Expression) –Left operand.
-
right(Expression) –Right operand.
Source code in src/qprogram/variable.py
evaluate ¶
Apply the arithmetic operator to both operands.
Returns:
-
int | float | _UnassignedType–The numeric result, or
UNASSIGNEDwhen either operand is unassigned.
Raises:
-
ZeroDivisionError–If the operator is
/and the right operand evaluates to zero.
Source code in src/qprogram/variable.py
UnaryOp ¶
Bases: Expression
Unary arithmetic node — op operand for one of - +.
Parameters:
-
op(UnaryOperator) –Operator symbol —
-or+. -
operand(Expression) –The operand.
Source code in src/qprogram/variable.py
evaluate ¶
Apply the sign operator to the operand.
Returns:
-
int | float | _UnassignedType–The negated value for
-and the operand's own value for+, orUNASSIGNED -
int | float | _UnassignedType–when the operand is unassigned.
Source code in src/qprogram/variable.py
Comparison ¶
Bases: Expression
Binary comparison node — left op right for one of == != < <= > >=.
Constructed by the comparison operators on Expression (< <= > >=) and by the helper
functions eq / ne (== !=; see Expression for why these are not
overloaded directly).
evaluate returns bool (an int subclass) or UNASSIGNED if either operand is
unassigned.
Parameters:
-
op(ComparisonOperator) –Operator symbol — one of
==,!=,<,<=,>,>=. -
left(Expression) –Left operand.
-
right(Expression) –Right operand.
Raises:
-
ValueError–If
opis not a recognized comparison operator (defensive — callers should pass a Literal).
Source code in src/qprogram/variable.py
evaluate ¶
Compare the two operands.
Returns:
-
bool | _UnassignedType–The boolean outcome of the comparison, or
UNASSIGNEDwhen either operand is -
bool | _UnassignedType–unassigned.
Source code in src/qprogram/variable.py
LogicalBinaryOp ¶
Bases: Expression
Binary logical node — left op right for and or or.
Constructed by & and | on Expression, or by the named helpers and_ /
or_.
Why this never short-circuits: any UNASSIGNED operand propagates upwards. Matches the rest
of the evaluator and makes unbound-variable diagnostics deterministic rather than position-dependent.
Parameters:
-
op(LogicalBinaryOperator) –Operator symbol —
andoror. -
left(Expression) –Left operand.
-
right(Expression) –Right operand.
Raises:
-
ValueError–If
opis not a recognized logical operator (defensive). -
TypeError–If either operand is not an
Expression.
Source code in src/qprogram/variable.py
evaluate ¶
Combine the truthiness of both operands.
Returns:
-
bool | _UnassignedType–The boolean outcome of
and/or, orUNASSIGNEDwhen either operand is -
bool | _UnassignedType–unassigned. Both operands are always evaluated.
Source code in src/qprogram/variable.py
LogicalNot ¶
Bases: Expression
Logical negation node — not operand.
Constructed by ~ on Expression or by not_.
Parameters:
-
operand(Expression) –The expression to negate.
Raises:
-
TypeError–If
operandis not anExpression.
Source code in src/qprogram/variable.py
evaluate ¶
Negate the truthiness of the operand.
Returns:
-
bool | _UnassignedType–Truewhen the operand is falsy andFalsewhen it is truthy, orUNASSIGNED -
bool | _UnassignedType–when the operand is unassigned.
Source code in src/qprogram/variable.py
MathFunc ¶
Bases: Expression
Math-function application — name(arg1, arg2, ...).
The function name selects the numeric implementation. Recognized names are listed in
_MATH_FUNCTIONS. Each is dispatched via the lazy lookup in _math_eval, keeping
this module free of a direct numpy/math import at the AST layer.
Arity is not enforced beyond requiring at least one operand: minimum and maximum fold
every operand, and every other function reads the first and ignores the rest. The module-level
builders (sin, minimum, ...) are the arity-checked way in.
Parameters:
-
name(str) –Function name, such as
"sin"or"minimum". -
operands(tuple[Expression, ...]) –Operand expressions.
Raises:
-
ValueError–If
nameis not a recognized math function, or ifoperandsis empty.
Source code in src/qprogram/variable.py
evaluate ¶
Evaluate every operand and apply the named function.
Returns:
-
int | float | _UnassignedType–The numeric result, or
UNASSIGNEDas soon as any operand is unassigned.
Source code in src/qprogram/variable.py
Where ¶
Bases: Expression
Ternary conditional expression — where(condition, then, else_).
Short-circuits: the unchosen branch is not evaluated, so it may safely reference unassigned
variables. If condition itself is unassigned the whole expression evaluates to
UNASSIGNED.
Users normally construct via the where helper.
Parameters:
-
condition(Expression) –The predicate expression.
-
then(Expression) –Returned when
conditionevaluates to truthy. -
else_(Expression) –Returned when
conditionevaluates to falsy.
Raises:
-
TypeError–If any argument is not an
Expression.
Source code in src/qprogram/variable.py
evaluate ¶
Evaluate the condition, then the one branch it selects.
Returns:
-
int | float | _UnassignedType–The chosen branch's value.
UNASSIGNEDwhen the condition is unassigned, or when -
int | float | _UnassignedType–the chosen branch is; the branch that is not taken is never evaluated.
Source code in src/qprogram/variable.py
MeasurementRef ¶
Bases: Expression
Reference to a field of a measurement result, used inside conditional expressions.
Built implicitly by the proxy that MeasurementHandle.state returns when the user writes
handle.state == 0; direct construction is rarely needed.
Equality is structural over (handle.name, field): distinct instances built at different sites
refer to the same logical reference whenever their names match. Mirrors Variable's
cross-program equality story so programs survive deepcopy / qp.loads(qp.dumps(...)).
Parameters:
-
handle(MeasurementHandle) –The producing measurement's
MeasurementHandle. -
field(str) –Field name.
"state"is the only accepted value.
Raises:
-
ValueError–If
fieldis not in the allowed set.
Source code in src/qprogram/variable.py
evaluate ¶
Return the current value of the referenced measurement field.
Returns:
-
int | float | _UnassignedType–The value the runtime recorded on the handle for this field, or
UNASSIGNEDbefore -
int | float | _UnassignedType–the measurement has produced one.
Source code in src/qprogram/variable.py
variables ¶
Return the free variables in this expression.
A measurement reference is its own binding kind, distinct from Variable, so it takes
no part in the loop-counter variable walk the rest of the AST feeds.
Returns:
-
set[Variable]–An empty set.
Source code in src/qprogram/variable.py
UNASSIGNED
module-attribute
¶
Sentinel returned by evaluate() when a variable in the expression has no value.
Helper functions¶
Free functions that build expression nodes, all reached as qp.eq, qp.sin,
and so on. eq and ne are the only way to compare two expressions for
equality: Variable.__eq__ compares ids and has to keep returning a bool so
that variables stay usable in sets and as dictionary keys. and_, or_, and
not_ are function forms of &, |, and ~, which Expression does
overload. The math functions, minimum, maximum, and where have no
operator form at all.
eq ¶
eq(
left: Expression | float | _HandleFieldAccess,
right: Expression | float | _HandleFieldAccess,
) -> Comparison
Build an equality Comparison.
Why this exists rather than overloading == on Variable: Variable.__eq__ compares
ids and must keep returning a plain bool, so that variables stay usable in sets (notably the
expression.variables() walk) and as dict keys — which rules out building a
Comparison from ==. Numeric operands are wrapped as Constant;
handle.<field> proxies resolve to MeasurementRef. Equivalent to
handle.state == 0 when one operand is a field-access proxy.
Parameters:
-
left(Expression | float | _HandleFieldAccess) –Left operand — an expression, a number, or a
handle.<field>proxy. -
right(Expression | float | _HandleFieldAccess) –Right operand, same forms as
left.
Returns:
-
Comparison–A
Comparisonnode forleft == right.
Raises:
-
TypeError–If either operand is a
boolor any other type with no expression form.
Source code in src/qprogram/variable.py
ne ¶
ne(
left: Expression | float | _HandleFieldAccess,
right: Expression | float | _HandleFieldAccess,
) -> Comparison
Build an inequality Comparison — counterpart of eq.
Parameters:
-
left(Expression | float | _HandleFieldAccess) –Left operand — an expression, a number, or a
handle.<field>proxy. -
right(Expression | float | _HandleFieldAccess) –Right operand, same forms as
left.
Returns:
-
Comparison–A
Comparisonnode forleft != right.
Raises:
-
TypeError–If either operand is a
boolor any other type with no expression form.
Source code in src/qprogram/variable.py
and_ ¶
Build left and right — function form of the & operator.
Precedence reminder: in Python & binds tighter than comparison operators, so
a < b & c < d parses as a < (b & c) < d. Parenthesize comparisons explicitly:
(a < b) & (c < d).
Parameters:
-
left(Expression) –Left operand.
-
right(Expression) –Right operand.
Returns:
-
LogicalBinaryOp–A
LogicalBinaryOpnode forleft and right.
Raises:
-
TypeError–If either operand is not an
Expression. Numbers are not coerced here — a logical operand must already be an expression.
Source code in src/qprogram/variable.py
or_ ¶
Build left or right — function form of the | operator.
Parameters:
-
left(Expression) –Left operand.
-
right(Expression) –Right operand.
Returns:
-
LogicalBinaryOp–A
LogicalBinaryOpnode forleft or right.
Raises:
-
TypeError–If either operand is not an
Expression.
Source code in src/qprogram/variable.py
not_ ¶
Build not operand — function form of the ~ operator.
Parameters:
-
operand(Expression) –The expression to negate.
Returns:
-
LogicalNot–A
LogicalNotnode wrappingoperand.
Raises:
-
TypeError–If
operandis not anExpression.
Source code in src/qprogram/variable.py
sin ¶
Build a symbolic sin(x).
Parameters:
-
x(Expression | float) –The operand. A number is wrapped as a
Constant.
Returns:
Raises:
-
TypeError–If
xis aboolor any other type with no expression form.
Source code in src/qprogram/variable.py
cos ¶
Build a symbolic cos(x).
Parameters:
-
x(Expression | float) –The operand. A number is wrapped as a
Constant.
Returns:
Raises:
-
TypeError–If
xis aboolor any other type with no expression form.
Source code in src/qprogram/variable.py
tan ¶
Build a symbolic tan(x).
Parameters:
-
x(Expression | float) –The operand. A number is wrapped as a
Constant.
Returns:
Raises:
-
TypeError–If
xis aboolor any other type with no expression form.
Source code in src/qprogram/variable.py
exp ¶
Build a symbolic exp(x).
Parameters:
-
x(Expression | float) –The operand. A number is wrapped as a
Constant.
Returns:
Raises:
-
TypeError–If
xis aboolor any other type with no expression form.
Source code in src/qprogram/variable.py
log ¶
Build a symbolic natural log log(x).
Parameters:
-
x(Expression | float) –The operand. A number is wrapped as a
Constant.
Returns:
Raises:
-
TypeError–If
xis aboolor any other type with no expression form.
Source code in src/qprogram/variable.py
sqrt ¶
Build a symbolic sqrt(x).
Parameters:
-
x(Expression | float) –The operand. A number is wrapped as a
Constant.
Returns:
Raises:
-
TypeError–If
xis aboolor any other type with no expression form.
Source code in src/qprogram/variable.py
minimum ¶
Build a symbolic minimum(a, b, ...).
Why this exists separately from min: the built-in compares with <, which on an
Expression builds a Comparison node instead of a bool, so
min(var_a, var_b) raises TypeError from Expression.__bool__ rather than
building the symbolic minimum.
Parameters:
-
*args(Expression | float, default:()) –At least two operands. Numbers are wrapped as
Constant.
Returns:
Raises:
-
TypeError–If fewer than two arguments are passed, or if an argument is a
boolor any other type with no expression form.
Source code in src/qprogram/variable.py
maximum ¶
Build a symbolic maximum(a, b, ...) — same rationale as minimum.
Parameters:
-
*args(Expression | float, default:()) –At least two operands. Numbers are wrapped as
Constant.
Returns:
Raises:
-
TypeError–If fewer than two arguments are passed, or if an argument is a
boolor any other type with no expression form.
Source code in src/qprogram/variable.py
where ¶
Build a ternary Where expression — where(condition, then, else_).
Only the chosen branch is evaluated, so the unchosen branch may reference variables that happen to be unassigned at evaluation time.
Parameters:
-
condition(Expression) –Boolean expression, typically a
Comparisonor aLogicalBinaryOp. -
then(Expression | float) –Returned when
conditionis truthy. Numeric literals are wrapped asConstant. -
else_(Expression | float) –Returned when
conditionis falsy, wrapped the same way.
Returns:
Raises:
-
TypeError–If
conditionis not anExpression, or if a branch is aboolor any other type with no expression form.
Source code in src/qprogram/variable.py
Waveforms¶
waveforms ¶
Built-in waveform shapes for pulse-level programs.
Single-channel waveforms subclass Waveform; complex (I/Q) waveforms subclass
IQWaveform. Both bases expose envelope(resolution) / get_I() / get_Q()
and get_duration() for platforms that need to render samples.
A shape parameter annotated float | Expression or int | Expression accepts an
Expression in place of a number, so a swept variable can parameterize the shape;
rendering samples then requires every referenced variable to hold a value. Parameters annotated with a
plain numeric type take a number only.
Waveform ¶
Bases: _StructuralEqMixin, ABC
Abstract base for single-channel (real-valued) waveforms.
A concrete shape supplies envelope and get_duration; the analysis and plotting helpers
here are derived from the sampled envelope, so they work for any subclass. Equality and hashing are
structural over the constructor attributes, which lets a waveform be used as a dictionary key and lets
two independently built shapes compare equal.
envelope
abstractmethod
¶
Return the pulse envelope sampled at resolution-ns steps.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
1returns one sample per ns.
Returns:
-
ndarray–A 1-D array of
duration / resolutionsamples. A shape whose parameters are all -
ndarray–floats yields a float array; one built from integers may yield an integer array.
Source code in src/qprogram/waveforms/waveform.py
get_duration
abstractmethod
¶
peak_amplitude ¶
Return max(|envelope|) at the given sample resolution.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
Returns:
-
float–The largest absolute sample value of the envelope.
Raises:
-
UnassignedVariableError–If the envelope depends on a variable that has no value.
Source code in src/qprogram/waveforms/waveform.py
rms_amplitude ¶
Return the root-mean-square amplitude of the envelope at the given resolution.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
Returns:
-
float–The root mean square of the envelope samples.
Raises:
-
UnassignedVariableError–If the envelope depends on a variable that has no value.
Source code in src/qprogram/waveforms/waveform.py
area ¶
Return the integrated envelope ∫ envelope(t) dt in nanosecond-amplitude units.
Useful for pulse-area calibration: a π rotation on a transmon corresponds to a fixed area independent of pulse shape (modulo nonlinear corrections).
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
Returns:
-
float–The trapezoidal integral of the envelope over the pulse.
Raises:
-
UnassignedVariableError–If the envelope depends on a variable that has no value.
Source code in src/qprogram/waveforms/waveform.py
spectrum ¶
Return the one-sided frequency spectrum of the envelope.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
Returns:
-
tuple[ndarray, ndarray]–(frequencies_hz, complex_spectrum)fromnumpy.fft.rfft.
Raises:
-
UnassignedVariableError–If the envelope depends on a variable that has no value.
Source code in src/qprogram/waveforms/waveform.py
plot ¶
Plot the envelope on a matplotlib Axes.
Requires matplotlib, which ships in the viz extra; it is imported inside the call so the
rest of the package stays importable without it.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
-
ax(Axes | None, default:None) –Axes to draw on. A fresh figure+axes is created when
None.
Returns:
-
Axes–The
Axescontaining the plot.
Raises:
-
ModuleNotFoundError–When
matplotlibis not installed — installqprogram[viz]. -
UnassignedVariableError–If the envelope depends on a variable that has no value.
Source code in src/qprogram/waveforms/waveform.py
IQWaveform ¶
Bases: _StructuralEqMixin, ABC
Abstract base for IQ (two-channel, complex-valued) waveforms.
A concrete shape supplies get_I, get_Q and get_duration; the analysis and
plotting helpers here work on the complex envelope I + jQ. Equality and hashing are structural over
the constructor attributes, recursing into the component waveforms.
get_I
abstractmethod
¶
get_Q
abstractmethod
¶
get_duration
abstractmethod
¶
Return the pulse duration in nanoseconds.
Returns:
-
int–The duration in nanoseconds.
peak_amplitude ¶
Return the peak magnitude max(|I + jQ|) at the given sample resolution.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
Returns:
-
float–The largest magnitude of the complex envelope.
Raises:
-
UnassignedVariableError–If either channel's envelope depends on a variable that has no value.
Source code in src/qprogram/waveforms/waveform.py
rms_amplitude ¶
Return the RMS magnitude of the complex envelope at the given resolution.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
Returns:
-
float–The root mean square of the complex envelope magnitudes.
Raises:
-
UnassignedVariableError–If either channel's envelope depends on a variable that has no value.
Source code in src/qprogram/waveforms/waveform.py
area ¶
Return the integrated magnitude ∫ |I + jQ| dt in nanosecond-amplitude units.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
Returns:
-
float–The trapezoidal integral of the complex envelope magnitude over the pulse.
Raises:
-
UnassignedVariableError–If either channel's envelope depends on a variable that has no value.
Source code in src/qprogram/waveforms/waveform.py
spectrum ¶
Return the two-sided frequency spectrum of the complex envelope.
IQ waveforms are complex, so a two-sided numpy.fft.fft is more informative than a
real-only one-sided transform.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
Returns:
-
tuple[ndarray, ndarray]–(frequencies_hz, complex_spectrum), both shifted so zero frequency sits in the middle.
Raises:
-
UnassignedVariableError–If either channel's envelope depends on a variable that has no value.
Source code in src/qprogram/waveforms/waveform.py
plot ¶
Plot the I and Q channels on two stacked matplotlib axes.
Requires matplotlib, which ships in the viz extra; it is imported inside the call so the
rest of the package stays importable without it.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
-
axes(tuple[Axes, Axes] | None, default:None) –Pair of axes to draw on. A fresh figure with two stacked axes is created when
None.
Returns:
-
tuple[Axes, Axes]–The
(I_axes, Q_axes)pair used for the plot.
Raises:
-
ModuleNotFoundError–When
matplotlibis not installed — installqprogram[viz]. -
UnassignedVariableError–If either channel's envelope depends on a variable that has no value.
Source code in src/qprogram/waveforms/waveform.py
Square ¶
Bases: Waveform
Constant-amplitude rectangular pulse.
Parameters:
-
amplitude(float | Expression) –Pulse amplitude. Accepts an
Expressionto be swept by an enclosing loop. -
duration(int | Expression) –Pulse duration in nanoseconds. Accepts an
Expression.
Source code in src/qprogram/waveforms/square.py
envelope ¶
Return the rectangular envelope sampled at resolution-ns steps.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
1returns one sample per ns.
Returns:
-
ndarray–A 1-D array of length
duration / resolution, every sample atamplitude. The dtype -
ndarray–follows
amplitude, so an integer amplitude yields an integer array.
Raises:
-
UnassignedVariableError–If a parameter is an
Expressionwhose variables have no value.
Source code in src/qprogram/waveforms/square.py
get_duration ¶
Return the pulse duration in nanoseconds.
Returns:
-
int–The duration truncated to a whole number of nanoseconds.
Raises:
-
UnassignedVariableError–If
durationis anExpressionwhose variables have no value.
Source code in src/qprogram/waveforms/square.py
Gaussian ¶
Bases: Waveform
Gaussian-shaped pulse, peaked at the midpoint of the duration window.
The envelope is not truncation-corrected: the tails are clipped wherever the window ends, so the first and last samples sit at whatever amplitude the Gaussian reaches there rather than at zero.
Parameters:
-
amplitude(float | Expression) –Peak amplitude. Accepts an
Expression. -
duration(int | Expression) –Pulse duration in nanoseconds. Accepts an
Expression. -
sigma(float | Expression) –Standard deviation in nanoseconds. The truncation ratio
duration / sigmacontrols how steeply the tails are clipped at the window edges. Accepts anExpression.
Source code in src/qprogram/waveforms/gaussian.py
envelope ¶
Return the Gaussian envelope sampled at resolution-ns steps.
The peak sits at the center of the sample window, so an even sample count straddles it between
the two middle samples and the largest sample falls a little below amplitude. sigma is
converted to samples, which keeps the shape the same at any resolution.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
1returns one sample per nanosecond.
Returns:
-
ndarray–A 1-D float array of
duration / resolutionsamples.
Raises:
-
UnassignedVariableError–If
amplitude,duration, orsigmais a symbolic expression whose variables are unassigned.
Source code in src/qprogram/waveforms/gaussian.py
get_duration ¶
Return the pulse duration in nanoseconds.
Returns:
-
int–The duration, truncated to a whole number of nanoseconds.
Raises:
-
UnassignedVariableError–If
durationis a symbolic expression whose variables are unassigned.
Source code in src/qprogram/waveforms/gaussian.py
GaussianDragCorrection ¶
GaussianDragCorrection(
amplitude: float | Expression,
duration: int | Expression,
sigma: float | Expression,
beta: float | Expression,
)
Bases: Gaussian
Derivative-of-Gaussian envelope used as the Q-channel of a DRAG pulse.
On its own, this waveform is rarely emitted directly; it is the Q-channel partner produced by
get_Q.
Only the envelope differs from Gaussian: the duration, and the meaning of
amplitude, duration, and sigma, are inherited unchanged, so the correction always spans the
same window as the Gaussian it partners.
Parameters:
-
amplitude(float | Expression) –Peak amplitude of the underlying Gaussian. Accepts an
Expression. -
duration(int | Expression) –Pulse duration in nanoseconds. Accepts an
Expression. -
sigma(float | Expression) –Standard deviation of the underlying Gaussian in nanoseconds.
-
beta(float | Expression) –DRAG scaling (
βin the Motzoi et al. parameterization). Multiplicative weight on the derivative term.
Source code in src/qprogram/waveforms/gaussian_drag_correction.py
envelope ¶
Return the scaled Gaussian derivative sampled at resolution-ns steps.
The derivative is taken with respect to sample index rather than time, so the correction's
amplitude scales with resolution while the underlying Gaussian's does not. It is antisymmetric
about the pulse center, where it crosses zero.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
1returns one sample per nanosecond.
Returns:
-
ndarray–A 1-D float array of
duration / resolutionsamples.
Raises:
-
UnassignedVariableError–If
amplitude,duration,sigma, orbetais a symbolic expression whose variables are unassigned.
Source code in src/qprogram/waveforms/gaussian_drag_correction.py
Ramp ¶
Ramp(
from_amplitude: float | Expression,
to_amplitude: float | Expression,
duration: int | Expression,
)
Bases: Waveform
Linearly-interpolated ramp between two amplitudes.
Parameters:
-
from_amplitude(float | Expression) –Starting amplitude. Accepts an
Expression. -
to_amplitude(float | Expression) –Ending amplitude. Accepts an
Expression. -
duration(int | Expression) –Ramp duration in nanoseconds. Accepts an
Expression.
Source code in src/qprogram/waveforms/ramp.py
envelope ¶
Return the ramp sampled at resolution-ns steps.
Samples run linearly from from_amplitude to to_amplitude, so the step between samples is
set by the sample count rather than by resolution alone. Both endpoints are included once the
window holds at least two samples: a one-sample window yields from_amplitude alone, and a
window that holds less than one sample yields an empty array.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
1returns one sample per nanosecond.
Returns:
-
ndarray–A 1-D float array of
duration / resolutionsamples running linearly from -
ndarray–from_amplitudetoto_amplitude.
Raises:
-
UnassignedVariableError–If either amplitude or the duration is a symbolic expression whose variables are still unassigned.
-
ValueError–If the duration is negative, which asks for a negative number of samples.
Source code in src/qprogram/waveforms/ramp.py
get_duration ¶
Return the pulse duration in nanoseconds.
Returns:
-
int–The ramp duration, truncated to a whole number of nanoseconds.
Raises:
-
UnassignedVariableError–If
durationis a symbolic expression whose variables are still unassigned.
Source code in src/qprogram/waveforms/ramp.py
FlatTop ¶
FlatTop(
amplitude: float | Expression,
duration: int | Expression,
smooth_duration: int | Expression,
buffer: int = 0,
)
Bases: Waveform
Rectangular pulse with erf-smoothed rising and falling edges.
Useful for fast-acting flat pulses where instantaneous square edges would inject high-frequency content outside the control electronics' bandwidth.
Each edge is an error function of width smooth_duration / 3: the rise crosses half amplitude
smooth_duration ns into the pulse and is flat to within a part in 10⁵ by twice that, with the fall
mirrored about the pulse center. A duration that is not comfortably longer than
2 * smooth_duration therefore never reaches full amplitude.
Parameters:
-
amplitude(float | Expression) –Pulse amplitude. Accepts an
Expression. -
duration(int | Expression) –Pulse duration in nanoseconds, including the smoothed edges but excluding the
bufferpadding. Accepts anExpression. -
smooth_duration(int | Expression) –Length of each edge (rise and fall) in nanoseconds. Accepts an
Expression. -
buffer(int, default:0) –Zero-amplitude padding added on each side of the pulse, in nanoseconds. The total duration is
duration + 2 * buffer.
Source code in src/qprogram/waveforms/flat_top.py
envelope ¶
Return the smoothed rectangular envelope sampled at resolution-ns steps.
The rising and falling error functions are multiplied together rather than spliced, which keeps the
envelope smooth even when the two edges overlap. Padding is emitted as buffer / resolution zero
samples on each side.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
1returns one sample per nanosecond.
Returns:
-
ndarray–A 1-D float array of
(duration + 2 * buffer) / resolutionsamples.
Raises:
-
UnassignedVariableError–If
amplitude,duration, orsmooth_durationis a symbolic expression whose variables are unassigned.
Source code in src/qprogram/waveforms/flat_top.py
get_duration ¶
Return the pulse duration in nanoseconds, padding included.
Returns:
-
int–duration + 2 * buffer, withdurationtruncated to a whole number of nanoseconds.
Raises:
-
UnassignedVariableError–If
durationis a symbolic expression whose variables are unassigned.
Source code in src/qprogram/waveforms/flat_top.py
SuddenNetZero ¶
SuddenNetZero(
amplitude: float | Expression,
duration: int | Expression,
b: float | Expression,
t_phi: int | Expression,
)
Bases: Waveform
Sudden Net Zero (SNZ) pulse for fast, leakage-suppressed two-qubit gates.
The shape is a positive square segment, a zero hold of width t_phi, then a negative square segment
scaled by b. The two segments are meant to cancel, leaving zero net integrated flux: the
cancellation is exact when b is 1 and the samples left over after the hold divide evenly between
the segments, and b is detuned from 1 to null whatever residual the flux line adds.
Parameters:
-
amplitude(float | Expression) –Amplitude of the positive segment. Accepts an
Expression. -
duration(int | Expression) –Total pulse duration in nanoseconds. Accepts an
Expression. -
b(float | Expression) –Ratio of negative-to-positive amplitudes (typically near 1.0). Accepts an
Expression. -
t_phi(int | Expression) –Width of the zero hold between the two segments in nanoseconds. Accepts an
Expression.
Source code in src/qprogram/waveforms/snz.py
envelope ¶
Return the SNZ envelope sampled at resolution-ns steps.
The two square segments split what is left of the pulse once the zero hold is taken out, rounding the positive segment down — so the negative segment carries the extra sample when that remainder is an odd number of samples.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
1returns one sample per ns.
Returns:
-
ndarray–A 1-D float array of length
duration / resolution.
Raises:
-
UnassignedVariableError–If a parameter is an
Expressionwhose variables have no value.
Source code in src/qprogram/waveforms/snz.py
get_duration ¶
Return the pulse duration in nanoseconds.
Returns:
-
int–The duration truncated to a whole number of nanoseconds.
Raises:
-
UnassignedVariableError–If
durationis anExpressionwhose variables have no value.
Source code in src/qprogram/waveforms/snz.py
Sine ¶
Sine(
amplitude: float | Expression,
duration: int | Expression,
frequency: float | Expression,
phase: float | Expression = 0.0,
)
Bases: Waveform
Sinusoidal envelope amplitude · sin(2π·frequency·t + phase).
Useful for parametric drives, sideband cooling tones, and as a building block for amplitude-modulated
waveforms. The envelope does not taper to zero at the endpoints; pair with a window function
(e.g. Tukey) when continuous-wave artifacts matter.
Parameters:
-
amplitude(float | Expression) –Peak amplitude.
-
duration(int | Expression) –Pulse duration in nanoseconds.
-
frequency(float | Expression) –Oscillation frequency in Hz.
-
phase(float | Expression, default:0.0) –Phase offset in radians. Defaults to zero.
Source code in src/qprogram/waveforms/sine.py
envelope ¶
Return the sine envelope sampled at resolution-ns steps.
The sample times are expressed in seconds, so frequency is read as a frequency in Hz.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
1returns one sample per ns.
Returns:
-
ndarray–A 1-D float array of length
duration / resolution.
Raises:
-
UnassignedVariableError–If a parameter is an
Expressionwhose variables have no value.
Source code in src/qprogram/waveforms/sine.py
get_duration ¶
Return the pulse duration in nanoseconds.
Returns:
-
int–The duration truncated to a whole number of nanoseconds.
Raises:
-
UnassignedVariableError–If
durationis anExpressionwhose variables have no value.
Source code in src/qprogram/waveforms/sine.py
Cosine ¶
Cosine(
amplitude: float | Expression,
duration: int | Expression,
frequency: float | Expression,
phase: float | Expression = 0.0,
)
Bases: Waveform
Cosine envelope amplitude · cos(2π·frequency·t + phase).
See Sine for the analogous sine variant and the same caveats.
Parameters:
-
amplitude(float | Expression) –Peak amplitude.
-
duration(int | Expression) –Pulse duration in nanoseconds.
-
frequency(float | Expression) –Oscillation frequency in Hz.
-
phase(float | Expression, default:0.0) –Phase offset in radians. Defaults to zero.
Source code in src/qprogram/waveforms/cosine.py
envelope ¶
Return the cosine envelope sampled at resolution-ns steps.
Every symbolic parameter is resolved to a number first, so an enclosing sweep must have bound its
variables before samples can be rendered. Sample times run from zero, in seconds, which is what
pairs with frequency in Hz.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
1returns one sample per nanosecond.
Returns:
-
ndarray–A 1-D float array of
duration / resolutionsamples.
Raises:
-
UnassignedVariableError–If
amplitude,duration,frequency, orphaseis a symbolic expression whose variables are unassigned.
Source code in src/qprogram/waveforms/cosine.py
get_duration ¶
Return the pulse duration in nanoseconds.
Returns:
-
int–The duration, truncated to a whole number of nanoseconds.
Raises:
-
UnassignedVariableError–If
durationis a symbolic expression whose variables are unassigned.
Source code in src/qprogram/waveforms/cosine.py
Sech ¶
Bases: Waveform
Hyperbolic-secant envelope, centered at the midpoint of the duration window.
The envelope is amplitude * sech((t - center) / tau). sech pulses are the canonical
envelope for chirped adiabatic-passage gates: paired with a quadratic phase ramp they yield
analytically-solvable population transfer.
Parameters:
-
amplitude(float | Expression) –Peak amplitude (at the midpoint).
-
duration(int | Expression) –Pulse duration in nanoseconds.
-
tau(float | Expression) –Width parameter in nanoseconds — analogous to
sigmaonGaussian.
Source code in src/qprogram/waveforms/sech.py
envelope ¶
Return the pulse envelope sampled at resolution-ns steps.
The peak sits at the center of the sample window, and tau is scaled into samples so the shape
is independent of resolution. A window that holds less than one sample yields an empty array.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
1returns one sample per nanosecond.
Returns:
-
ndarray–A 1-D float array of
duration / resolutionsamples.
Raises:
-
UnassignedVariableError–If
amplitude,duration, ortauis a symbolic expression whose variables are still unassigned.
Source code in src/qprogram/waveforms/sech.py
get_duration ¶
Return the pulse duration in nanoseconds.
Returns:
-
int–The pulse duration, truncated to a whole number of nanoseconds.
Raises:
-
UnassignedVariableError–If
durationis a symbolic expression whose variables are still unassigned.
Source code in src/qprogram/waveforms/sech.py
Tukey ¶
Tukey(
amplitude: float | Expression,
duration: int | Expression,
alpha: float | Expression = 0.5,
)
Bases: Waveform
Tukey window: rectangular pulse with cosine-tapered edges.
The alpha parameter controls the fraction of the pulse occupied by the rising and falling
edges combined: alpha=0 produces a pure rectangle, alpha=1 produces a Hann window, and
intermediate values yield a flat top of width (1 - alpha) * duration with cosine ramps on
each side of width (alpha / 2) * duration. Matches the alpha parameter of
scipy.signal.windows.tukey.
Cheaper to compile than FlatTop (no erf evaluation) and commonly used as a
smoothing window in pulse calibration.
Parameters:
-
amplitude(float | Expression) –Peak amplitude (achieved across the flat region).
-
duration(int | Expression) –Total pulse duration in nanoseconds.
-
alpha(float | Expression, default:0.5) –Fraction of the duration occupied by the combined rise + fall, in
[0, 1]. Defaults to0.5.
Source code in src/qprogram/waveforms/tukey.py
envelope ¶
Return the Tukey-windowed envelope sampled at resolution-ns steps.
Three cases skip the taper arithmetic: a window of fewer than two samples and alpha <= 0 are
flat at amplitude, and alpha >= 1 is a full Hann window. Otherwise the taper spans
alpha * (n - 1) / 2 samples at each end, where n is the sample count.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds.
1returns one sample per ns.
Returns:
-
ndarray–A 1-D array of length
duration / resolution. The tapered forms are float; the two flat -
ndarray–forms take their dtype from
amplitude, so an integer amplitude yields an integer array.
Raises:
-
UnassignedVariableError–If a parameter is an
Expressionwhose variables have no value.
Source code in src/qprogram/waveforms/tukey.py
get_duration ¶
Return the pulse duration in nanoseconds.
Returns:
-
int–The duration truncated to a whole number of nanoseconds.
Raises:
-
UnassignedVariableError–If
durationis anExpressionwhose variables have no value.
Source code in src/qprogram/waveforms/tukey.py
Arbitrary ¶
Bases: Waveform
Waveform defined by a user-provided 1-D sample array.
The resolution argument to envelope is ignored — the samples are taken as-is, one per ns.
An array that is already an numpy.ndarray is adopted rather than copied, so a caller holding
a reference to it must not mutate it: waveforms are values, compared and hashed structurally.
envelope hands back a copy for the same reason.
Parameters:
-
samples(Sequence[float] | ndarray) –1-D sequence (list, tuple, or
np.ndarray) of sample values. Converted withnumpy.asarray, so the stored dtype follows the input.
Source code in src/qprogram/waveforms/arbitrary.py
envelope ¶
Return a copy of the stored samples.
Parameters:
-
resolution(int, default:1) –Ignored. Accepted so the signature matches
Waveform.envelope; the stored samples are the envelope already, one per nanosecond.
Returns:
-
ndarray–A copy of the sample array, so writing to it cannot alter the waveform.
Source code in src/qprogram/waveforms/arbitrary.py
get_duration ¶
Return the pulse duration in nanoseconds.
Returns:
-
int–The number of samples, one sample spanning one nanosecond.
Chained ¶
Bases: Waveform
Single-channel waveform built by concatenating other single-channel waveforms in time.
Waveform.__add__ builds these and flattens as it goes, so a + b + c yields one
three-element chain rather than nested pairs. Every child is sampled at whatever resolution the
chain is asked for, and the chain's duration is the sum of the children's.
Parameters:
-
waveforms(list[Waveform]) –Waveforms to play back-to-back, in order.
Source code in src/qprogram/waveforms/chained.py
envelope ¶
Return the children's envelopes concatenated in order.
Parameters:
-
resolution(int, default:1) –Sample period in nanoseconds, passed through to every child.
Returns:
-
ndarray–A 1-D array holding each child's envelope in turn.
Raises:
-
ValueError–If the chain holds no waveforms, leaving nothing to concatenate.
-
UnassignedVariableError–If a child's envelope depends on a variable that has no value.
Source code in src/qprogram/waveforms/chained.py
get_duration ¶
Return the pulse duration in nanoseconds.
Returns:
-
int–The sum of the children's durations, or zero for an empty chain.
Raises:
-
UnassignedVariableError–If a child's duration is a symbolic expression whose variables are unassigned.
Source code in src/qprogram/waveforms/chained.py
IQPair ¶
Bases: IQWaveform
An IQ waveform assembled from two independent single-channel envelopes.
Useful when the desired I and Q envelopes don't fit one of the named DRAG-style classes — for example, a square-on-I, zero-on-Q readout pulse.
Parameters:
-
I(Waveform) –In-phase channel.
-
Q(Waveform) –Quadrature channel. Must have the same duration as
I.
Raises:
-
TypeError–If either argument is not a
Waveforminstance. -
ValidationError–If the two channels have different (concretely-known) durations. The check is best-effort — symbolic durations whose variables are still unassigned are accepted here and left to the platform compiler to verify once values are bound.
Source code in src/qprogram/waveforms/iq_pair.py
get_I ¶
get_Q ¶
get_duration ¶
Return the pulse duration in nanoseconds.
Both channels are required to share a duration, so the I channel answers for the pair.
Returns:
-
int–The duration of the I channel in nanoseconds.
Raises:
-
UnassignedVariableError–If the I channel's duration is a symbolic expression whose variables are still unassigned.
Source code in src/qprogram/waveforms/iq_pair.py
IQDrag ¶
IQDrag(
amplitude: float | Expression,
duration: int | Expression,
sigma: float | Expression,
beta: float | Expression,
)
Bases: IQWaveform
DRAG (Derivative Removal by Adiabatic Gate) pulse.
The I channel carries a Gaussian; the Q channel carries the derivative correction
(GaussianDragCorrection) scaled by beta. Suppresses leakage to
the |2⟩ state during single-qubit rotations on weakly-anharmonic transmons.
The two channels are built on demand from the four stored parameters, so they cannot drift out of step
with each other or with get_duration.
Parameters:
-
amplitude(float | Expression) –Peak amplitude of the I-channel Gaussian. Accepts an
Expression. -
duration(int | Expression) –Pulse duration in nanoseconds. Accepts an
Expression. -
sigma(float | Expression) –Standard deviation of the I-channel Gaussian in nanoseconds.
-
beta(float | Expression) –DRAG scaling (
βin the Motzoi et al. parameterization). Typically a small (< 0.5) value tuned per qubit.
Source code in src/qprogram/waveforms/iq_drag.py
get_I ¶
Return the in-phase component as a single-channel Waveform.
Returns:
Source code in src/qprogram/waveforms/iq_drag.py
get_Q ¶
Return the quadrature component as a single-channel Waveform.
Returns:
-
Waveform–A fresh
GaussianDragCorrectioncarrying this pulse's parameters -
Waveform–together with
beta.
Source code in src/qprogram/waveforms/iq_drag.py
get_duration ¶
Return the pulse duration in nanoseconds.
Returns:
-
int–The duration, truncated to a whole number of nanoseconds. Both channels span it.
Raises:
-
UnassignedVariableError–If
durationis a symbolic expression whose variables are unassigned.
Source code in src/qprogram/waveforms/iq_drag.py
IQRotation ¶
Bases: IQWaveform
An existing IQWaveform rotated in the I/Q plane by phase radians.
Applies the 2x2 rotation::
I_out = I * cos(phase) - Q * sin(phase)
Q_out = I * sin(phase) + Q * cos(phase)
Useful for virtual-Z gates and for applying a software-side phase offset to a calibrated pulse without resampling
the envelope. Materializes both channels as Arbitrary waveforms; for
purely-symbolic rotation, prefer carrying the phase through the underlying envelope's parameters.
Parameters:
-
base(IQWaveform) –The
IQWaveformto rotate. -
phase(float | Expression) –Rotation angle in radians.
Raises:
-
TypeError–If
baseis not anIQWaveforminstance.
Source code in src/qprogram/waveforms/iq_rotation.py
get_I ¶
Return the rotated in-phase channel.
Returns:
-
Waveform–An
Arbitrarywaveform holdingI·cos(phase) - Q·sin(phase), sampled from the base -
Waveform–channels at 1-ns steps.
Raises:
-
UnassignedVariableError–If
phaseor any parameter of the base channels is a symbolic expression whose variables are still unassigned.
Source code in src/qprogram/waveforms/iq_rotation.py
get_Q ¶
Return the rotated quadrature channel.
Returns:
-
Waveform–An
Arbitrarywaveform holdingI·sin(phase) + Q·cos(phase), sampled from the base -
Waveform–channels at 1-ns steps.
Raises:
-
UnassignedVariableError–If
phaseor any parameter of the base channels is a symbolic expression whose variables are still unassigned.
Source code in src/qprogram/waveforms/iq_rotation.py
get_duration ¶
Return the pulse duration in nanoseconds.
A rotation mixes the two channels sample by sample, so the length is the base waveform's.
Returns:
-
int–The duration of the base waveform in nanoseconds.
Raises:
-
UnassignedVariableError–If the base waveform's duration is a symbolic expression whose variables are still unassigned.
Source code in src/qprogram/waveforms/iq_rotation.py
IQZero ¶
Bases: IQWaveform
A real-valued Waveform presented on an IQ bus with a silent Q channel.
Convenience wrapper for IQPair(I=envelope, Q=Square(0.0, duration)). Useful when a calibrated
single-channel pulse has to drive an IQ-typed bus without rewriting the rest of the program.
Parameters:
Raises:
-
TypeError–If
envelopeis not aWaveforminstance.
Source code in src/qprogram/waveforms/iq_zero.py
get_I ¶
get_Q ¶
Return the silent quadrature channel.
Returns:
Raises:
-
UnassignedVariableError–If the envelope's duration is a symbolic expression whose variables are still unassigned.
Source code in src/qprogram/waveforms/iq_zero.py
get_duration ¶
Return the pulse duration in nanoseconds.
Returns:
-
int–The duration of the wrapped envelope in nanoseconds.
Raises:
-
UnassignedVariableError–If the envelope's duration is a symbolic expression whose variables are still unassigned.
Source code in src/qprogram/waveforms/iq_zero.py
Modulated ¶
Bases: IQWaveform
IQ pulse formed by intermediate-frequency modulation of a real envelope.
Produces I = envelope · cos(2π·frequency·t + phase) and
Q = envelope · sin(2π·frequency·t + phase), evaluated at 1-ns resolution. Use this to lift any
single-channel envelope onto an IQ bus for sideband-modulated drive without writing an
IQPair by hand.
The materialized I and Q channels are Arbitrary waveforms — modulation collapses
the enclosing envelope's parametric structure to concrete samples.
Parameters:
-
envelope(Waveform) –Underlying single-channel
Waveformshaping the pulse. -
frequency(float | Expression) –Modulation frequency in Hz.
-
phase(float | Expression, default:0.0) –Phase offset in radians. Defaults to zero.
Raises:
-
TypeError–If
envelopeis not aWaveforminstance.
Source code in src/qprogram/waveforms/modulated.py
get_I ¶
Return the in-phase channel.
Returns:
-
Waveform–An
Arbitrarywaveform holdingenvelope · cos(2π·frequency·t + phase), witht -
Waveform–running over the envelope's samples in seconds at 1-ns steps.
Raises:
-
UnassignedVariableError–If
frequency,phase, or any envelope parameter is a symbolic expression whose variables are still unassigned.
Source code in src/qprogram/waveforms/modulated.py
get_Q ¶
Return the quadrature channel.
Returns:
-
Waveform–An
Arbitrarywaveform holdingenvelope · sin(2π·frequency·t + phase), witht -
Waveform–running over the envelope's samples in seconds at 1-ns steps.
Raises:
-
UnassignedVariableError–If
frequency,phase, or any envelope parameter is a symbolic expression whose variables are still unassigned.
Source code in src/qprogram/waveforms/modulated.py
get_duration ¶
Return the pulse duration in nanoseconds.
Modulation is sample-wise, so the length is the underlying envelope's.
Returns:
-
int–The duration of the underlying envelope in nanoseconds.
Raises:
-
UnassignedVariableError–If the envelope's duration is a symbolic expression whose variables are still unassigned.
Source code in src/qprogram/waveforms/modulated.py
WaveformLibrary ¶
A per-bus store mapping waveform names to concrete waveforms.
Entries are keyed at one of three tiers; get tries them most-specific first:
- exact —
set(name, wf, element="q", idx=0, kind="drive")— onlyq[0].drive. - family —
set(name, wf, element="q", kind="drive")— anyq[*].drive(idx unspecified). - global —
set(name, wf)— any bus. This tier is the only one a raw-string bus can reach, and a baredictpassed toQProgram.with_waveformslands here.
More specific entries shadow less specific ones for a given bus.
Source code in src/qprogram/waveform_library.py
from_mapping
classmethod
¶
Build a global-tier-only library from a plain {name: waveform} mapping.
This is what makes a bare dict usable with QProgram.with_waveforms:
every name in the mapping resolves on every bus, whatever its (element, idx, kind) coordinate.
Parameters:
-
mapping(Mapping[str, Waveform | IQWaveform]) –Waveform names mapped to concrete waveforms.
Returns:
-
WaveformLibrary–A library holding one global-tier entry per mapping key.
Raises:
-
ValidationError–If a key is not a non-empty string.
Source code in src/qprogram/waveform_library.py
set ¶
set(
name: str,
waveform: Waveform | IQWaveform,
*,
element: str | None = None,
idx: int | tuple[int, ...] | None = None,
kind: str | None = None,
) -> None
Register waveform under name at the tier implied by the keyword arguments.
Parameters:
-
name(str) –The waveform name as referenced in the program (the string alias).
-
waveform(Waveform | IQWaveform) –The concrete waveform to resolve to.
-
element(str | None, default:None) –Element kind (e.g.
"q"). Required for the exact and family tiers. -
idx(int | tuple[int, ...] | None, default:None) –Element index — a tuple for a multi-index element such as a coupler. Given (with
elementandkind) → exact tier. -
kind(str | None, default:None) –Bus kind (e.g.
"drive"). Required for the exact and family tiers.
Raises:
-
ValidationError–If
nameis not a non-empty string, or theelement/idx/kindcombination does not match one of the three tiers (exact = all three; family = element + kind; global = none).
Source code in src/qprogram/waveform_library.py
get ¶
Resolve name for bus, trying exact → family → global; None if no entry matches.
A schema-backed BusRef can match all three tiers; a raw-string bus carries no
(element, idx, kind) metadata, so only the global tier is reachable.
Parameters:
-
bus(str) –The bus the name is played on — a
BusRefto reach the exact and family tiers, any string for the global tier alone. -
name(str) –The waveform name to resolve.
Returns:
-
Waveform | IQWaveform | None–The most specific waveform registered for
(bus, name), orNonewhen no tier -
Waveform | IQWaveform | None–matches.
Source code in src/qprogram/waveform_library.py
apply ¶
Return a copy of program with string waveform names resolved against this library.
Platform-free convenience identical to program.with_waveforms(self) — useful for tooling and
tests that resolve without going through a platform.
Parameters:
-
program(QProgram) –The program whose string waveform aliases are resolved. Never mutated.
Returns:
-
QProgram–A copy of
programwith each resolvable alias replaced by its concrete waveform.
Raises:
-
ValidationError–If a resolved waveform does not match the channel kind of the bus it lands on.
Source code in src/qprogram/waveform_library.py
dumps ¶
Serialize the library to the portable .wfl text format.
Each entry is one line — "<name>" [<coord>] = <waveform> — where <coord> is
element[idx].kind (exact tier), element[*].kind (family tier), or absent (global tier),
and <waveform> reuses the same constructor syntax as .qp (e.g.
IQDrag(amplitude=0.5, duration=40, sigma=8, beta=0.1)). Entries are emitted in insertion
order, so loads(dumps(lib)) reproduces the library exactly.
Returns:
-
str–The complete
.wfldocument, header line included, ending in a newline.
Raises:
-
SerializationError–If a stored waveform is not concrete (e.g. carries a
Variable) — a calibration library must hold uploadable pulses, not symbolic ones.
Source code in src/qprogram/waveform_library.py
save ¶
Write the library to path as UTF-8 text.
The .wfl extension is the convention.
Parameters:
-
path(str) –Filesystem path to write. An existing file is overwritten.
Raises:
-
SerializationError–If a stored waveform is not concrete (see
dumps). -
OSError–If the path cannot be written.
Source code in src/qprogram/waveform_library.py
loads
classmethod
¶
Parse a .wfl text document into a WaveformLibrary.
The #!WaveformLibrary header must come first, preceded by blank lines at most — a
comment ahead of it is an error. After the header, blank lines and # comment lines are
skipped and every other line must be an entry.
Parameters:
-
text(str) –The
.wfldocument to parse.
Returns:
-
WaveformLibrary–The reconstructed library, entries in file order.
Raises:
-
ParseError–On a missing or incompatible header, a malformed entry, or an unknown waveform type. Waveform types are looked up in the global serialization registry, so every built-in is always available while a vendor waveform needs its package imported first.
-
ValidationError–If an entry names an empty waveform name.
Source code in src/qprogram/waveform_library.py
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 | |
load
classmethod
¶
Read and parse a .wfl file encoded as UTF-8.
Parameters:
-
path(str) –Filesystem path of the document to read.
Returns:
-
WaveformLibrary–The reconstructed library.
Raises:
-
ParseError–If the file's contents are not a valid
.wfldocument (seeloads). -
OSError–If the path cannot be read.
Source code in src/qprogram/waveform_library.py
Operations¶
The AST leaves, each appended by the matching builder method on QProgram
rather than constructed at a call site. MeasurementField and
normalize_fields are documented here because they live in the same module: the
first is a StrEnum of the field names a measurement can request, and the
second sorts a fields argument into the canonical order those names are
compared, hashed, and serialized in.
operations ¶
Built-in operations — the AST leaf nodes a QProgram is composed of.
Operations are typed nodes appended to the program's active block by the builder methods on
QProgram. Each subclass declares which constructor params hold buses (BUS_ATTRS)
and waveforms (WAVEFORM_ATTRS) so the shared introspection contract works without per-class overrides.
Operation ¶
Base class for all operations in the QProgram AST.
Subclasses customize introspection behavior through two class-attribute conventions:
BUS_ATTRSlists which__init__parameter names hold bus references. The default("bus",)matches every core op exceptSync(which holds a list undertargets) andCall(which lists none — buses reach a call site only as bound argument values).WAVEFORM_ATTRSlists which__init__parameters carry waveform values. Default empty.
Equality and hashing are structural; once an instance has been used as a set / dict key, do
not mutate its attributes. Callers like QProgram.rebind that rewrite operations do
so on a fresh deepcopy.
BROADCASTS_WHEN_NO_BUS
class-attribute
¶
When True and the op's BUS_ATTRS resolve to no buses, the op semantically
touches every bus in the program (e.g. Sync(targets=None)). The validator then routes
it across all program buses instead of the default-bus profile.
AFFECTS_AVERAGING
class-attribute
¶
Whether this op participates in what an Average block averages.
An average block repeats its body and accumulates measurement results; only the ops
that produce those results determine whether the averaging itself can run as a real-time
hardware feature. Ops with AFFECTS_AVERAGING = False are still validated and still execute
inside the body — they just don't gate the Average block's execution domain (see
qprogram.validation.validate). Defaults to False; MeasurementOperation
sets it True, so core measure and vendor acquire opt in automatically. A vendor
adding another averaging-relevant op sets it on its own class.
variables ¶
Return every Variable referenced by this op, transitively.
Walks every public instance attribute, descending into Expression nodes, waveform
parameters, and nested lists. Ops with data hidden in private fields or computed lazily can
override this method.
Source code in src/qprogram/operations/operation.py
buses ¶
Return every bus name this op touches.
Reads the attributes listed in BUS_ATTRS. Plain strings, BusRef
instances, and lists of either are collected.
Source code in src/qprogram/operations/operation.py
waveforms ¶
Return every waveform (concrete or string alias) the op carries.
Reads the attributes listed in WAVEFORM_ATTRS. None values (from optional waveform
params) are skipped.
Source code in src/qprogram/operations/operation.py
walk ¶
Yield self — operations are AST leaves.
Pairs with Block.walk, which recurses through children.
required_capabilities ¶
Return the capability tokens this op needs, in isolation.
Non-recursive: the validator walks the AST and unions per-node sets. Subclasses override to add
their identity token (op.<name>) plus any state-dependent refinement tokens (waveform kind,
expression shape, measure.fields.<name> for the fields a measurement requests, ...).
Source code in src/qprogram/operations/operation.py
Play ¶
Bases: Operation
A waveform played on a bus.
Parameters:
-
bus(str) –Bus to play on.
-
waveform(Waveform | IQWaveform | str) –Either a concrete
Waveform/IQWaveform, or a string alias to be resolved later byQProgram.with_waveforms.
Source code in src/qprogram/operations/play.py
required_capabilities ¶
Return op.play plus the tokens describing the waveform.
A string alias contributes waveform.alias; a concrete waveform contributes its channel
kind (waveform.iq or waveform.single) and, when its class is registered, the
per-class token from qprogram.protocol.waveform_token.
Source code in src/qprogram/operations/play.py
Measure ¶
Measure(
bus: str,
waveform: IQWaveform | str,
weights: IQWaveform | str,
handle: MeasurementHandle,
fields: Iterable[MeasurementField] = (
MeasurementField.IQ,
),
)
Bases: MeasurementOperation
A readout pulse played on a bus, together with the acquisition of the response.
Parameters:
-
bus(str) –Readout bus (must have
acquires=True). -
waveform(IQWaveform | str) –Readout pulse — concrete
IQWaveformor a string alias. -
weights(IQWaveform | str) –Integration weights — concrete
IQWaveformor a string alias. -
handle(MeasurementHandle) –The canonical
MeasurementHandlefor this measurement. The same Python instance is returned to user code, referenced by anyMeasurementRefin conditionals, and listed byQProgram.measurement_handles— the runtime writes per-measurement values onto this single object and every reader sees them. -
fields(Iterable[MeasurementField], default:(IQ,)) –Which measurement fields to produce, as an iterable of
MeasurementFieldmembers. Default(MeasurementField.IQ,).RAWrequests the raw ADC trace;STATErequests a classified outcome (required when the program referenceshandle.statein a conditional). Stored canonically ordered and deduplicated — seenormalize_fields.
Raises:
-
ValidationError–If
fieldsis a bare string, is not iterable, requests no field at all, or names a field that is not registered.
Source code in src/qprogram/operations/measure.py
required_capabilities ¶
Return op.measure plus the waveform and requested-field tokens.
waveform.iq is always required — a readout drives an IQ bus. The pulse and the
integration weights each contribute waveform.alias when given as a string alias, and a
concrete one contributes the per-class token from qprogram.protocol.waveform_token
when its class is registered. The measure.fields.<name> tokens come from
required_capabilities.
Source code in src/qprogram/operations/measure.py
Wait ¶
Bases: Operation
An idle period of duration nanoseconds on bus.
Parameters:
-
bus(str) –Bus to idle on.
-
duration(int | Expression) –Wait duration in nanoseconds. Accepts an
Expressionfor sweeps.
Source code in src/qprogram/operations/wait.py
required_capabilities ¶
Return op.wait plus the tokens contributed by the duration expression.
Source code in src/qprogram/operations/wait.py
Sync ¶
Bases: Operation
An alignment of buses to a common time reference.
The user-facing QProgram.sync exposes a buses= keyword; the AST attribute is named
targets to avoid shadowing Operation.buses (a list attribute named buses would
silently hide the introspection method).
Parameters:
-
targets(list[str] | None, default:None) –Bus names to sync, or
Noneto sync every bus currently active in the program. With no explicit targets the op broadcasts (Operation.BROADCASTS_WHEN_NO_BUS), so the validator intersects the capabilities of every bus the program touches.
Source code in src/qprogram/operations/sync.py
SetFrequency ¶
Bases: Operation
A new NCO / oscillator frequency for a bus.
Parameters:
-
bus(str) –Bus whose oscillator to retune.
-
frequency(float | Expression) –New frequency in Hz. Accepts an
Expressionfor sweeps.
Source code in src/qprogram/operations/set_frequency.py
required_capabilities ¶
Return op.set_frequency plus the tokens contributed by the frequency expression.
Source code in src/qprogram/operations/set_frequency.py
SetPhase ¶
Bases: Operation
A new NCO phase for a bus.
Parameters:
-
bus(str) –Bus whose oscillator phase to set.
-
phase(float | Expression) –Phase in radians. Accepts an
Expression.
Source code in src/qprogram/operations/set_phase.py
required_capabilities ¶
Return op.set_phase plus the tokens contributed by the phase expression.
Source code in src/qprogram/operations/set_phase.py
ResetPhase ¶
Bases: Operation
A reset of the NCO phase on a bus to zero.
Parameters:
-
bus(str) –Bus whose oscillator phase to reset.
Source code in src/qprogram/operations/reset_phase.py
SetGain ¶
Bases: Operation
A new output gain for a bus.
Parameters:
-
bus(str) –Bus whose output gain to set.
-
gain(float | Expression) –New gain. Accepts an
Expressionfor sweeps.
Source code in src/qprogram/operations/set_gain.py
required_capabilities ¶
Return op.set_gain plus the tokens contributed by the gain expression.
Source code in src/qprogram/operations/set_gain.py
SetOffset ¶
SetOffset(
bus: str,
offset_path0: float | Expression,
offset_path1: float | Expression | None = None,
)
Bases: Operation
A new DC offset on one or both signal paths of a bus.
Parameters:
-
bus(str) –Bus whose DC offset to set.
-
offset_path0(float | Expression) –Offset on path 0 (the only path for single-channel buses, I for IQ buses).
-
offset_path1(float | Expression | None, default:None) –Offset on path 1 (Q for IQ buses).
Noneleaves the path's offset unchanged.
Source code in src/qprogram/operations/set_offset.py
required_capabilities ¶
Return op.set_offset plus the tokens contributed by the offset expressions.
An unset offset_path1 contributes nothing.
Source code in src/qprogram/operations/set_offset.py
SetParameter ¶
Bases: Operation
A write to a bus-scoped parameter named by string.
Targets a bus (like SetFrequency and the other set_* ops),
so BUS_ATTRS is ("bus",) and the op routes to that bus's capability slot. Unlike the
real-time set_* ops, a parameter write is a platform-configuration action — not real-time — so
platforms expose op.set_parameter only in a bus slot's host half, making it host-side-only (a
swept value additionally forces its binding loop to host via a predicate).
Parameters:
-
bus(str) –The bus whose parameter is written.
-
parameter(str) –Name of the parameter to set.
-
value(float | Expression) –New value. Accepts an
Expressionfor sweeps.
Source code in src/qprogram/operations/set_parameter.py
required_capabilities ¶
Return op.set_parameter plus the tokens contributed by the value expression.
Source code in src/qprogram/operations/set_parameter.py
GetParameter ¶
Bases: Operation
A read of a bus-scoped parameter into a Variable at runtime.
Targets a bus (BUS_ATTRS = ("bus",)), so the op routes to that bus's capability slot.
Like SetParameter, reading a parameter is a platform-configuration
action — platforms expose op.get_parameter only in a bus slot's host half, making it
host-side-only.
Parameters:
-
variable(Variable) –Destination
Variablefor the read value. -
bus(str) –The bus whose parameter is read.
-
parameter(str) –Name of the parameter to read.
Source code in src/qprogram/operations/get_parameter.py
Call ¶
Bases: Operation
Instantiation of a Fragment at a specific site with bound arguments.
A first-class AST leaf: definitions and call sites survive serialization (.qp emits a
fragment <name>(...): section and a bare <name>(<args>) statement) and structural
equality. QProgram.expand replaces every Call with the substituted fragment body —
compilers and validators consume the expanded, fragment-free program.
Built by QProgram.call; direct construction is rarely needed.
Parameters:
-
fragment(Fragment) –The called
Fragment. -
arguments(dict[str, object]) –Fully-bound
{param_id: value}mapping (one entry per fragment parameter), produced byqprogram.fragments.bind_arguments.
Source code in src/qprogram/operations/call.py
variables ¶
Return host variables referenced by the bound arguments.
The fragment body's own parameters/locals are not reported — they are placeholders that exist only until expansion.
Source code in src/qprogram/operations/call.py
buses ¶
Return every string-valued argument bound at this call site.
A Parameter is untyped — only the position an argument lands in inside
the fragment body decides whether it is a bus, a waveform alias, or a plain value — so a
string argument of any kind is reported here and the result over-approximates the buses the
call touches. Buses named inside the fragment body become visible once
QProgram.expand has substituted the arguments.
Source code in src/qprogram/operations/call.py
required_capabilities ¶
MeasurementField ¶
Bases: StrEnum
Canonical names for the data a measurement can produce.
Members are strings (MeasurementField.IQ == "iq" is True), so they travel unchanged
through capability tokens, the .qp writer, and fields
keys. The enum exists for discoverability (type MeasurementField. and let the editor list
the options) and for static checking — it is not a new value type.
Declaration order is the canonical field order: normalize_fields sorts every
fields tuple this way, so (IQ, STATE) and (STATE, IQ) build the same AST, hash the
same, and serialize to the same .qp line. Vendor-defined fields are plain strings (a vendor
ships its own StrEnum if it wants the same ergonomics) and sort after the core
members, alphabetically among themselves.
Attributes:
-
STATE–Classified outcome — required to reference
handle.statein a conditional. -
IQ–Demodulated, integrated in-phase/quadrature pair. The default, and the
dataattribute of aMeasurementResultwhenever it is requested. -
RAW–Raw ADC trace.
MeasurementOperation ¶
Bases: Operation
Marker base for operations that produce a referenceable measurement.
Why a marker base (rather than duck-typing on the handle attribute): vendor authors opt in
deliberately, and tooling that wants to enumerate measurements has a single isinstance to check.
Why store the canonical MeasurementHandle rather than a name string: every reference
to the measurement — the user's variable, the AST node, every MeasurementRef in
conditionals, the value returned by QProgram.measurement_handles —
becomes the same Python instance, and the runtime writes per-measurement values onto that single object.
Concrete subclasses must set handle and fields.
fields
instance-attribute
¶
Requested measurement fields, canonically ordered by normalize_fields. Typed as
str rather than MeasurementField because vendors may register their own field
names; core fields always compare equal to their enum member.
AFFECTS_AVERAGING
class-attribute
¶
Measurements are exactly what an average block accumulates — see
Operation.AFFECTS_AVERAGING.
required_capabilities ¶
Return one measure.fields.<name> token per entry in fields.
Concrete subclasses union this with their own identity-token set.
Source code in src/qprogram/operations/operation.py
normalize_fields ¶
Coerce a fields argument into the canonical, sorted tuple[str, ...].
Accepts any iterable of MeasurementField members or registered field-name strings —
(MeasurementField.IQ, MeasurementField.STATE) and ["iq", "state"] are equivalent. A bare
string is rejected: iterating one would yield its characters, and there is no comma-separated
"iq,state" spelling.
Every name is checked against the live capability registry, so a typo raises here — at the
measure(...) call site — instead of surfacing later as an unsupported-capability diagnostic.
Vendors widen the accepted set by registering measure.fields.<name> (see
qprogram.protocol.register_capability_tokens).
The result is deduplicated and sorted into canonical order (MeasurementField
declaration order, then vendor names alphabetically), so two measurements requesting the same
data compare equal, hash equal, and serialize identically regardless of argument order.
Parameters:
-
value(Iterable[MeasurementField | str]) –Iterable of
MeasurementFieldmembers or registered field-name strings.
Returns:
-
tuple[str, ...]–Canonical, deduplicated, sorted tuple of field names.
Raises:
-
ValidationError–If
valueis a bare string or not iterable, if any entry is not a string or is empty, if any name is not registered, or if no fields are requested.
Source code in src/qprogram/operations/operation.py
Blocks¶
blocks ¶
AST block types — containers that group operations and other blocks together.
The base Block is a simple ordered container; concrete subclasses
(Average, Conditional, Sweep,
Parallel) carry extra structure that the runtime, validator, and serializer understand.
Sweep is the only loop: it binds a variable to whatever a
SweepSource produces, so a linear ramp, an explicit table, a log-spaced set
and any composition of those are all the same block with a different source.
Block ¶
Generic sequential container for operations and nested blocks.
The base Block is what QProgram.block produces — an unstructured grouping with no
extra semantics. Repeating blocks (Sweep, Average,
Parallel) and Conditional subclass it to add
structure the runtime understands.
Equality and hashing are structural (same class + same children); once a block has been used as a
set / dict key, do not append to it.
Source code in src/qprogram/blocks/block.py
REPEATS
class-attribute
¶
Whether this block re-runs its body, i.e. occupies a repetition level on the sequencer.
True on the three core repeating blocks (Sweep,
Parallel, Average — the last because averaging is
repetition), False on the plain grouping Block and on
Conditional (branching selects a body, it doesn't iterate).
qprogram.validation.validate reads this to compute max_loop_nesting — the only reason
the attribute exists rather than the validator testing concrete classes. A vendor or platform
package contributing a repeating block of its own (see the .qp block registry,
register_vendor_block) sets it True on its subclass and is
then counted correctly against a platform's loop-depth limit, with no core change.
A Parallel counts as one level in total, not one per composed loop: its loop headers live
on loops rather than among its children, and they advance in
lockstep.
elements
property
¶
The contained operations and sub-blocks, in declaration order.
The block's own list rather than a copy; append is the sanctioned way to extend it.
append ¶
Append an operation or sub-block to the end of this block.
Parameters:
variables ¶
Return every Variable referenced by any child.
Loop subclasses override to also include the loop-counter variable they bind.
Returns:
-
set[Variable]–The union of every child's variables.
Source code in src/qprogram/blocks/block.py
buses ¶
Return every bus name referenced by any child.
Returns:
-
set[str]–The union of every child's bus names. A
BusRefis astr, so -
set[str]–schema-backed references appear alongside raw string buses.
Source code in src/qprogram/blocks/block.py
waveforms ¶
Return every waveform (concrete or string alias) referenced by any child.
Returns:
-
set[Waveform | IQWaveform | str]–The union of every child's waveforms.
Source code in src/qprogram/blocks/block.py
walk ¶
Yield this block, then each descendant in pre-order.
Pairs with Operation.walk (which yields just the leaf) so callers can write
for node in program.body.walk(): without recursion.
Yields:
-
Block | Operation–This block first, then every descendant operation and sub-block, in declaration order.
Source code in src/qprogram/blocks/block.py
required_capabilities ¶
Return the capability tokens this block needs, in isolation.
Non-recursive by design — qprogram.validation.validate visits every node and checks
each one's own token set against the slot that node routes to. Recursing here would
double-count. Subclasses override to add their own identity token (block.<name>) and
refinement tokens (sweep shape, etc.).
Returns:
-
set[str]–The identity token
block.blockof a plain grouping block.
Source code in src/qprogram/blocks/block.py
Average ¶
Bases: Block
A body run shots times, with measurement results averaged across the repetitions.
Averaging is repetition, so the block occupies a repetition level on the sequencer
(Block.REPEATS). Unlike a Sweep it contributes no dimension to
the results: the shots are accumulated and divided out, so a state field arrives as the
excited-state population over the shots.
Parameters:
-
shots(int) –Number of times to execute the block body. Must be a positive integer; a
boolis rejected even though it is anint.
Raises:
-
ValidationError–If
shotsis not an integer >= 1.
Source code in src/qprogram/blocks/average.py
Sweep ¶
Bases: Block
A loop binding variable to each value a SweepSource produces.
The DSL's single sweep construct. It carries no notion of how the values are generated — that is
entirely the source's business, which is what lets Range, Values, Logspace, a file, and
any composition of those be peers rather than separate block types.
Parameters:
-
variable(Variable) –The
Variablerebound on each iteration. -
source(SweepSource) –The
SweepSourcedescribing the values. A bare 1-D sequence is accepted as a shorthand forValues.
Raises:
-
ValidationError–If
sourceis neither a source nor a sequence of values.
Source code in src/qprogram/blocks/sweep.py
REPEATS
class-attribute
¶
This block re-runs its body — it occupies a repetition level (see Block.REPEATS).
num_iterations ¶
Return the number of sweep points, delegated to the source.
Answerable without executing the program, which is what lets
Parallel check lockstep lengths at construction time and the executor
size a composed parallel axis before the first shot. Cheap for every built-in source except
File, which reads its array to answer.
Returns:
-
int–The number of points the bound source produces.
Raises:
-
ValidationError–If the bound source cannot describe its points — a
Fileholding an array that is not 1-D, or holding none. -
OSError–If the bound source reads a file whose path does not exist or cannot be read.
Source code in src/qprogram/blocks/sweep.py
variables ¶
required_capabilities ¶
Return block.sweep plus the bound source's own tokens.
A platform therefore declares both the loop and the specific value source it is asked for: the
source's class token and its sweep.<kind>, unioned across everything a combinator wraps.
Returns:
-
set[str]–The identity token
block.sweeptogether withSweepSource.tokens.
Source code in src/qprogram/blocks/sweep.py
Parallel ¶
Bases: Block
Several loops advanced in lockstep, sharing one body.
Created via the | operator on sweep contexts (with sweep(a, src) | sweep(b, src) as p:).
Note the structural quirk: composed loop headers live on self.loops, not in self._elements.
Body operations live in _elements as usual. The introspection overrides below thread loop
variables back into the unioned views so analyzers see them.
The composition occupies a single repetition level (see Block.REPEATS), not one per
composed loop, because the headers advance together rather than nesting.
Parameters:
-
loops(Iterable[Sweep]) –The
Sweepinstances to advance in lockstep. At least two, and all with the same number of iterations — which every source can report statically, so the check happens here at construction rather than at run time.
Raises:
-
ValidationError–If fewer than two loops are given, if their iteration counts differ, or if a bound source cannot describe its points — a
Fileholding an array that is not 1-D, or holding none. -
OSError–If a bound source reads a file whose path does not exist or cannot be read.
Source code in src/qprogram/blocks/parallel.py
REPEATS
class-attribute
¶
This block re-runs its body — it occupies a repetition level (see Block.REPEATS).
variables ¶
Return every Variable in the shared body, plus the ones the loops bind.
The loop headers sit outside _elements, so the inherited walk over the body misses them and
they are unioned in here.
Returns:
-
set[Variable]–The body's variables together with each composed loop's own.
Source code in src/qprogram/blocks/parallel.py
walk ¶
Yield this block, then each composed loop header, then the shared body in pre-order.
The headers come first so a consumer meets the loops that bind the variables before the operations that read them.
Yields:
-
Block | Operation–This block, then each composed
Sweepwith its own descendants, -
Block | Operation–then every node of the shared body.
Source code in src/qprogram/blocks/parallel.py
required_capabilities ¶
Return the capability tokens this block needs, in isolation.
qprogram.validation.validate classifies each composed header as a child block in its
own right and checks its tokens there, so the headers' sweep.* tokens are not repeated
here.
Returns:
-
set[str]–The identity token
block.parallel.
Source code in src/qprogram/blocks/parallel.py
Conditional ¶
Bases: Block
A chain of if / elif / else arms.
Why this doesn't reuse the inherited _elements list: arms each carry an independent body and a
condition expression, so a single ordered list would conflate "arm body" with "shared body" — there
is no shared body in a conditional. append therefore raises; populate via the builder
methods on QProgram.
Attributes:
-
arms(list[tuple[Expression, Block]]) –The
(condition, body)pairs, in source order. The first condition that evaluates truthy selects its body. -
else_body(Block | None) –The terminal
elsebody, orNonewhen the chain has none.
Source code in src/qprogram/blocks/conditional.py
append ¶
Raise ValidationError — populate via QProgram.if_ / elif_ / else_ instead.
Parameters:
Raises:
-
ValidationError–Always.
Source code in src/qprogram/blocks/conditional.py
walk ¶
Yield this block, then every node of each arm body and of the else body.
Arm conditions are expressions rather than AST nodes, so they are not yielded; a consumer that
needs them reads arms directly.
Yields:
-
Block | Operation–This conditional first, then each arm body's nodes in source order, then the
else -
Block | Operation–body's.
Source code in src/qprogram/blocks/conditional.py
variables ¶
Return every Variable referenced by an arm condition or an arm body.
The conditions count: a branch taken on a swept threshold makes that variable part of the conditional even when no operation inside it reads the variable.
Returns:
-
set[Variable]–The union over every arm's condition and body, plus the
elsebody.
Source code in src/qprogram/blocks/conditional.py
buses ¶
Return every bus name referenced from any arm body or from the else body.
Returns:
-
set[str]–The union of the bus names of every arm body and the
elsebody.
Source code in src/qprogram/blocks/conditional.py
waveforms ¶
Return every waveform (concrete or string alias) referenced by any arm body.
Returns:
-
set[Waveform | IQWaveform | str]–The union of the waveforms of every arm body and the
elsebody.
Source code in src/qprogram/blocks/conditional.py
required_capabilities ¶
Return block.conditional plus the expression tokens of every arm condition.
A platform that branches must also evaluate the conditions, so each arm contributes the
expr.* tokens of its condition tree — the comparison, the measurement reference it reads,
and any arithmetic around them. The else arm has no condition and contributes none. Like
every other node's token set this is non-recursive: the arm bodies' own tokens are collected as
the validator walks them.
Returns:
-
set[str]–The identity token
block.conditionaltogether with theexpr.*tokens of the arm -
set[str]–conditions.
Source code in src/qprogram/blocks/conditional.py
Fragments¶
fragment ¶
Build a Fragment from a function — the signature is the parameter list.
The first parameter receives the fragment builder; each remaining parameter becomes a
Parameter (in order); the fragment name is the function's __name__. The body runs
once, at decoration time, to record the AST — Python-level control flow inside it is
evaluated at definition, not per call.
Example::
@fragment
def x_pulse(f, drive, amp):
f.play(drive, Gaussian(amplitude=amp, duration=40, sigma=8))
program.call(x_pulse, "drive_q0", 0.5)
Parameters:
-
func(Callable[..., None]) –The definition function.
*args/**kwargs/ defaults / keyword-only parameters are rejected — the.qpgrammar has no representation for them.
Returns:
Raises:
-
ValidationError–If the object has no
__name__, takes no builder parameter, carries a__name__that is not a valid fragment identifier or is reserved (a lambda's"<lambda>"fails here), or declares a parameter that is not plain positional or that carries a default. -
InvalidVariableIdError–If a parameter's name is reserved (see
RESERVED_KEYWORDS) or falls outside[A-Za-z_][A-Za-z0-9_]*, which a non-ASCII Python identifier does.
Source code in src/qprogram/fragments.py
Fragment ¶
Bases: QProgram
A named, parameterized sub-program — define once, call many times.
Inherits the entire QProgram builder (operations, control flow, vendor
namespaces), so a fragment body is built exactly like a program body. A fragment may call another
fragment that is already defined; cycles are rejected at registration and at expansion.
Parameters:
-
name(str) –Fragment identifier — must match
[A-Za-z_][A-Za-z0-9_]*and not be a reserved keyword. Used verbatim as the.qpdefinition and call name. -
label(str, default:'') –Human-readable label. A
.qpfragment section is headed by the name and the parameter list alone, so the label does not survive serialization. -
description(str | None, default:None) –Longer free-form description. Not serialized either.
Raises:
-
ValidationError–If
nameis not a valid identifier or is reserved.
Source code in src/qprogram/fragments.py
name
property
¶
The fragment's identifier, used verbatim as its .qp definition and call name.
params
property
¶
The declared parameters, in declaration order — the order positional arguments bind in.
parameter ¶
Declare a new Parameter on this fragment.
Parameters:
-
id(str) –Identifier matching
[A-Za-z_][A-Za-z0-9_]*; unique among the fragment's parameters and local variables (they share the identifier namespace inside the body). -
label(str | None, default:None) –Human-readable name for plots and results.
Returns:
-
Parameter–The new
Parameter, usable anywhere in the fragment body a value, bus, or -
Parameter–waveform is accepted.
Raises:
-
ValidationError–If
idcollides with an existing parameter or local variable. -
InvalidVariableIdError–If
idviolates the identifier pattern or is reserved (seeRESERVED_KEYWORDS).
Source code in src/qprogram/fragments.py
variable ¶
variable(
id: str,
*,
label: str | None = None,
units: str | None = None,
description: str | None = None,
) -> Variable
Declare a fragment-local Variable.
Local variables are renamed onto the host program at expansion ({fragment}_{id}, with a
numeric suffix on collision) so repeated calls never clash.
Parameters:
-
id(str) –Short identifier matching
[A-Za-z_][A-Za-z0-9_]*; unique among the fragment's parameters and local variables. -
label(str | None, default:None) –Human-readable name for plots and results.
-
units(str | None, default:None) –Unit string (e.g.
"Hz","ns"). -
description(str | None, default:None) –Longer free-form description.
Returns:
Raises:
-
ValidationError–If
idcollides with a parameter or an existing local variable. -
InvalidVariableIdError–If
idviolates the identifier pattern or is reserved (seeRESERVED_KEYWORDS).
Source code in src/qprogram/fragments.py
Parameter ¶
Parameter(
id: str,
*,
label: str | None = None,
units: str | None = None,
description: str | None = None,
)
Bases: Variable
A fragment parameter — a placeholder substituted with the bound argument at expansion.
Subclasses Variable, so a parameter participates in expressions
(amp * 2), serializes as a bare identifier, and follows the same id rules. Parameters are
untyped: the binding determines the kind (number/expression, bus, or waveform), checked at
expansion with a clear error when a binding is used in an incompatible position.
Source code in src/qprogram/variable.py
Results¶
MeasurementHandle ¶
A reference to a measurement performed by a QProgram.
Equality is structural: two handles with the same name refer to the same measurement. Why this
matters: after a .qp round-trip the original Python objects are gone but names survive in the
AST, so a freshly-constructed MeasurementHandle("q0_m0") compares equal to the original.
Runtime-supplied values (e.g. the classified qubit state when the measurement's fields
includes STATE) live in a private _values dict keyed by
field name. They participate in MeasurementRef
evaluation but do not contribute to handle identity.
Parameters:
-
name(str) –Stable identifier for the measurement. Auto-assigned by
QProgram.measure(q0/readout/m0,m0, ...) or user-supplied. Emitted verbatim into.qp.
Raises:
-
ValidationError–If
nameis not a non-empty string.
Note
_auto_named records whether QProgram.measure allocated the name (True) or the
user supplied it (False). It distinguishes a bus-derived auto-name (q0/readout/m0) that
QProgram.rebind must re-derive when the bus changes from a deliberate user label that
must be preserved — the two are byte-identical once serialized, so the flag is the only sound
signal. It is in-memory state (like the platform parameter store): it is not serialized, so it
defaults to False on a handle reconstructed from .qp (rebind before dumping).
Source code in src/qprogram/result.py
state
property
¶
A proxy for referencing this measurement's classified state in a conditional.
The returned proxy is a throwaway whose == and != operators build
Comparison AST nodes::
with program.if_(handle.state == 0):
...
The producing measurement op must request state classification (its fields must include
STATE); the validator emits missing-classification
otherwise.
MeasurementResult
dataclass
¶
One measurement record produced by the runtime.
Attributes:
-
bus(str) –The bus the measurement was taken on.
-
name(str) –The measurement handle's name, as assigned at program construction.
-
data(DataArray) –The primary result array — the
"iq"field when the measurement requested it, else the first requested field in canonical order (seeMeasurementField). Use it when you want whatever the measurement produced;QProgramResult.getnames a field explicitly instead. -
fields(dict[str, DataArray]) –One array per requested measurement field, keyed by field name. Shapes per spec §8:
iq→(*sweeps, "IQ");state→(*sweeps)(excited-state population under averaging);raw→(*sweeps, "time", "IQ"). This is whatQProgramResult.getreads.
QProgramResult ¶
In-memory result of executing a QProgram.
Each measurement contributes one MeasurementResult holding an xarray.DataArray
per requested field. Dimensions are named after the enclosing loops, outermost first; the iq
field carries a trailing "IQ" dimension with coordinates ["I", "Q"].
Results are stored in construction order and addressable by handle, by name string, or by integer
position via get, which returns the IQ field unless a
different one is named.
Source code in src/qprogram/result.py
measurements
property
¶
All measurement records in construction order.
append_measurement ¶
append_measurement(
bus: str,
name: str,
data: DataArray,
fields: dict[str, DataArray] | None = None,
) -> None
Append a measurement record.
Parameters:
-
bus(str) –The bus the measurement was taken on.
-
name(str) –The measurement handle name as it appears in the AST.
-
data(DataArray) –The primary result data (the
"iq"field when requested). -
fields(dict[str, DataArray] | None, default:None) –Per-measurement-field arrays, keyed by field name. Omitting it records
dataas theIQfield — the field a measurement requests whenfields=is omitted, and the onegetreturns by default. A record whose primary array is notiqmust pass the mapping explicitly, so thatgetnever hands back an array under the wrong field name.
Source code in src/qprogram/result.py
get ¶
get(
measurement: MeasurementHandle | str | int = 0,
bus: str | None = None,
field: MeasurementField | str = MeasurementField.IQ,
) -> xr.DataArray
Retrieve one measurement field's data.
Parameters:
-
measurement(MeasurementHandle | str | int, default:0) –Which measurement to retrieve.
MeasurementHandle: looked up by name.str: looked up by name. Useful after aloads()round-trip when the original handle objects are gone but handles can be reconstructed viaQProgram.measurement_handles.int: positional sugar; returns the N-th measurement in declaration order, or N-th onbuswhen the filter is given. A handle or a name says what it means and survives reordering, so prefer either to a position.
-
bus(str | None, default:None) –Bus name filter — narrows the search before the handle / name / position lookup.
-
field(MeasurementField | str, default:IQ) –Which measurement field to return — a
MeasurementFieldmember or a registered vendor field name (the members are strings, so both spell the same thing). Defaults toIQ, matching the default ofmeasure(..., fields=); a measurement that did not request the field raisesKeyErrorrather than silently substituting another one. Reach forMeasurementResult.datawhen you want the record's primary array whatever the requested fields were.
Returns:
-
DataArray–The field's
xarray.DataArray.
Raises:
-
KeyError–When
measurementis a handle or name with no match in scope, orfieldnames a measurement field the measurement did not request. -
IndexError–When
measurementis an integer position outside the range in scope. -
ValidationError–When
fieldisNone. There is no spelling of "give me the primary array" here — readMeasurementResult.datafor that.
Source code in src/qprogram/result.py
Vendor protocol¶
A vendor extension groups its operations as methods on a VendorNamespace
subclass, registers that subclass under a namespace name with
QProgram.register_vendor, and reaches the program through the two protected
helpers below; program.<vendor>.<operation>(...) then works without core
QProgram knowing the vendor exists.
The registration calls below all run at the extension's import time.
register_operation and register_block add core names unless given a
vendor=, while register_vendor_operation and register_vendor_block forward
to them with the vendor prefix already filled in; register_waveform keys a
waveform class by its own __name__ with no prefix at all.
register_vendor_version records the extension's protocol version, and the
writer raises SerializationError on vendor content whose extension never
called it. try_activate_vendor is the discovery step:
it imports the package behind the qprogram.vendors entry point for a name and
returns False when no installed package claims that name.
VendorNamespace ¶
Base class for vendor operation namespaces.
Vendors subclass this and add typed methods that construct Operation instances
and append them to the program via _append (or _append_measurement for measurement ops).
Parameters:
-
program(QProgram) –The program whose currently active block receives the appended operations. Held for the namespace's lifetime, which the program ties to its own.
Source code in src/qprogram/vendor.py
_append ¶
Append a vendor operation to the program's active block.
BusRef attributes (and lists thereof) are run through
QProgram._validate_bus so vendor ops can't sneak in a bus from a different schema.
Plain-string attributes are not validated.
Parameters:
-
operation(Operation) –The vendor operation instance to append.
Raises:
-
ValidationError–When one of the operation's bus references belongs to a different
BusSchemathan the one attached to the program.
Source code in src/qprogram/vendor.py
_append_measurement ¶
_append_measurement(
op_cls: type[MeasurementOperation],
*,
bus: str,
name: str | None = None,
**kwargs: Any,
) -> MeasurementHandle
Allocate a handle, build a vendor measurement op, append it, and return the handle.
Shares the per-bus name counter with QProgram.measure so vendor and core
measurements on the same bus never collide. Vendor measurement methods call this in place of _append so users
receive a usable MeasurementHandle.
Parameters:
-
op_cls(type[MeasurementOperation]) –The concrete
MeasurementOperationsubclass to instantiate. -
bus(str) –Bus the measurement runs on.
-
name(str | None, default:None) –Explicit handle name; auto-allocated when omitted.
-
**kwargs(Any, default:{}) –Remaining keyword arguments forwarded to
op_cls(...).
Returns:
-
MeasurementHandle–The freshly-allocated
MeasurementHandle.
Raises:
-
ValidationError–When
nameis empty, not a string, or already used by another measurement in the program, or when the operation carries a bus reference from a foreignBusSchema.
Example::
def acquire(self, bus, weights, *, name=None):
return self._append_measurement(Acquire, bus=bus, weights=weights, name=name)
Source code in src/qprogram/vendor.py
register_operation ¶
register_operation(
name: str,
cls: type[Operation],
*,
vendor: str | None = None,
serialize: OperationSerializeFn | None = None,
parse: OperationParseFn | None = None,
) -> type[Operation]
Register an operation for .qp serialization.
Re-registering the same class under the same (vendor, name) is allowed (the owner may
refresh its callbacks; import-time side-effect modules may run twice). Registering a
different class under a taken (vendor, name) raises — silently replacing another
package's operation would corrupt every file using that keyword.
Parameters:
-
name(str) –Keyword used in
.qp(play,measure, ...). Operation names are unrestricted; keyword-led block headers register on the block registry instead. -
cls(type[Operation]) –The
Operationsubclass. -
vendor(str | None, default:None) –Vendor namespace.
Noneregisters a core op (emitted without prefix). Cannot be"core"or anyRESERVED_KEYWORDS. -
serialize(OperationSerializeFn | None, default:None) –Optional override; default uses signature-driven serialization.
-
parse(OperationParseFn | None, default:None) –Optional override; default uses signature-driven parsing.
Returns:
-
type[Operation]–clsunchanged, so the call can stand in for the class at the point of registration. A -
type[Operation]–bare
@register_operationdecoration does not work —clsis the second positional -
type[Operation]–parameter, not the first.
Raises:
-
ValueError–If
vendoris reserved, or(vendor, name)is already registered to a different class.
Source code in src/qprogram/serialization/registry.py
register_vendor_operation ¶
register_vendor_operation(
vendor: str,
name: str,
cls: type[Operation],
*,
serialize: OperationSerializeFn | None = None,
parse: OperationParseFn | None = None,
) -> None
Register an operation under a vendor namespace.
Equivalent to register_operation(name, cls, vendor=vendor, ...) — the spelling vendor
extension packages use at import time. The optional serialize / parse callbacks are
forwarded unchanged: a vendor shipping a measurement operation passes
qprogram.serialization._specs.measurement_op_serialize and
qprogram.serialization._specs.make_measurement_op_parse, so the parser produces the
canonical handle instance shared with every MeasurementRef naming it.
Parameters:
-
vendor(str) –Vendor namespace the operation is emitted under (
<vendor>.<name>). -
name(str) –Operation keyword within that namespace.
-
cls(type[Operation]) –The
Operationsubclass. -
serialize(OperationSerializeFn | None, default:None) –Optional override; default uses signature-driven serialization.
-
parse(OperationParseFn | None, default:None) –Optional override; default uses signature-driven parsing.
Raises:
-
ValueError–If
vendoris reserved, or(vendor, name)is already registered to a different class.
Source code in src/qprogram/serialization/registry.py
register_block ¶
register_block(
name: str,
cls: type[Block],
*,
vendor: str | None = None,
serialize_header: BlockSerializeHeaderFn | None = None,
parse_header: BlockParseHeaderFn | None = None,
) -> type[Block]
Register a keyword-led control-flow block for .qp serialization.
The body parser handles indentation and child statements uniformly. Loops are not registered here — they use the sweep-source registry instead. Same-class re-registration is allowed; claiming a taken keyword with a different class raises.
The registry is keyed by the qualified keyword, so a vendor block is looked up by the same
dotted token the parser reads off the line (myplatform.infinite_loop) and can never collide
with a core keyword.
Parameters:
-
name(str) –Leading keyword in the block header (
average,block, ...), without any vendor prefix. -
cls(type[Block]) –The
Blocksubclass. -
vendor(str | None, default:None) –Vendor namespace.
Noneregisters a core block (emitted without prefix). Cannot be"core"or anyRESERVED_KEYWORDS. Prefer theregister_vendor_blockwrapper from a vendor package. -
serialize_header(BlockSerializeHeaderFn | None, default:None) –Optional header-text override.
-
parse_header(BlockParseHeaderFn | None, default:None) –Optional header-token override.
Returns:
-
type[Block]–clsunchanged, so the call can stand in for the class at the point of registration. A -
type[Block]–bare
@register_blockdecoration does not work —clsis the second positional -
type[Block]–parameter, not the first.
Raises:
-
ValueError–If
vendoris reserved, or the qualified keyword is already registered to a different class.
Source code in src/qprogram/serialization/registry.py
register_vendor_block ¶
register_vendor_block(
vendor: str,
name: str,
cls: type[Block],
*,
serialize_header: BlockSerializeHeaderFn | None = None,
parse_header: BlockParseHeaderFn | None = None,
) -> None
Register a vendor control-flow block — the block analogue of register_vendor_operation.
Equivalent to register_block(name, cls, vendor=vendor, ...). The block's wire form becomes
<vendor>.<name>: followed by an indented suite, and — because the spec records the vendor —
a program containing the block gets a require <vendor> <x.y> line even when it contains no
vendor operations.
A block that repeats its body should also set REPEATS True on
the class so it counts toward max_loop_nesting.
Parameters:
-
vendor(str) –Vendor namespace the block header is emitted under (
<vendor>.<name>:). -
name(str) –Header keyword within that namespace.
-
cls(type[Block]) –The
Blocksubclass. -
serialize_header(BlockSerializeHeaderFn | None, default:None) –Optional header-text override.
-
parse_header(BlockParseHeaderFn | None, default:None) –Optional header-token override.
Raises:
-
ValueError–If
vendoris reserved, or the qualified keyword is already registered to a different class.
Source code in src/qprogram/serialization/registry.py
register_vendor_version ¶
Record the protocol version of an installed vendor extension.
Parameters:
-
vendor(str) –Vendor name as used in the dot-notation operations. Cannot be
"core"or anyRESERVED_KEYWORDS. -
version(str) –Semver string with at least
major.minorinteger components. Major.minor governs compatibility; patch is informational.
Raises:
-
ValueError–If
vendoris reserved orversiondoes not parse asmajor.minor.
Source code in src/qprogram/serialization/registry.py
register_waveform ¶
Register a waveform class for .qp serialization, keyed by its class name.
Same-class re-registration is a no-op; registering a different class under an already-taken name raises — it would silently change how every existing file parses that constructor.
Parameters:
-
cls(type[Waveform | IQWaveform]) –Waveform class to register. Its
__name__is the constructor name on the wire.
Returns:
-
type–cls, so the function can be used as a decorator.
Raises:
-
ValueError–If
cls.__name__is already registered to a different class.
Source code in src/qprogram/serialization/registry.py
try_activate_vendor ¶
Ensure vendor is registered, importing its extension package on demand if needed.
Importing the entry-point target runs the package's registration side effects
(register_vendor / register_vendor_version / register_vendor_operation /
register_profile). Python caches imports, so repeat calls are cheap and idempotent.
Parameters:
-
vendor(str) –Vendor namespace, e.g.
"qblox".
Returns:
-
bool–Truewhen the vendor is registered after the call — either it already was, or its -
bool–qprogram.vendorsentry point was found and imported successfully.Falsewhen no -
bool–installed package claims
vendor; the caller decides whether that is an error.
Raises:
-
VendorActivationError–If an entry point claims
vendorbut its import raises, or it imports without registering a protocol version (a packaging bug in the extension).
Source code in src/qprogram/serialization/registry.py
OperationSpec
dataclass
¶
OperationSpec(
name: str,
vendor: str | None,
cls: type[Operation],
serialize: OperationSerializeFn | None = None,
parse: OperationParseFn | None = None,
)
Serialization metadata for one operation, core or vendor.
Attributes:
-
name(str) –Operation name as it appears in
.qp. -
vendor(str | None) –Vendor name for the dot-prefix (
<vendor>.<name>), orNonefor core. -
cls(type[Operation]) –The
Operationsubclass. -
serialize(OperationSerializeFn | None) –Optional override; default uses
inspect.signature-driven serialization. -
parse(OperationParseFn | None) –Optional override; default uses
inspect.signature-driven parsing.
qualified_name
property
¶
The keyword this operation is written as.
A core operation keeps its bare name; a vendor operation is dotted as
<vendor>.<name>.
BlockSpec
dataclass
¶
BlockSpec(
name: str,
cls: type[Block],
vendor: str | None = None,
serialize_header: BlockSerializeHeaderFn | None = None,
parse_header: BlockParseHeaderFn | None = None,
)
Serialization metadata for a keyword-led control-flow block, core or vendor.
Sweeps are not registered here — for <var> in <Source>(...) is driven by the sweep-source
registry (register_sweep_source) instead.
Attributes:
-
name(str) –Header keyword as it appears in
.qp(e.g."average","block"), without any vendor prefix. -
cls(type[Block]) –The
Blocksubclass. -
vendor(str | None) –Vendor name for the dot-prefix (
<vendor>.<name>), orNonefor a core block. MirrorsOperationSpec.vendor, and is what lets the writer emit arequireline for a file whose only vendor content is a block. -
serialize_header(BlockSerializeHeaderFn | None) –Optional override returning the header text without the trailing
:(e.g."average 1000"). Default emits the bare (qualified) keyword. -
parse_header(BlockParseHeaderFn | None) –Optional override taking the post-keyword tokens; default invokes
cls()with no arguments.
qualified_name
property
¶
The keyword this block header is written as.
A core block keeps its bare name; a vendor block is dotted as <vendor>.<name>, which
is also the key the block registry stores it under.
Serialization¶
dumps and save write .qp; loads and load read it. Those two readers
and ParseError are resolved by the package's module-level __getattr__ on
first access rather than at import time, because the parser imports QProgram
and importing them eagerly would close a cycle. Nothing about that shows at the
call site: qp.loads is read off the package like any other attribute.
dumps ¶
Serialize a QProgram to a .qp-format string.
Parameters:
-
program(QProgram) –Program to serialize.
Returns:
-
str–The full
.qptext: header,requirelines, metadata, schema, fragments, body.
Raises:
-
SerializationError–If the program contains a node or value the format cannot represent faithfully (unregistered operation/block class, vendor without a registered version, attribute value of an unsupported type), or if
programis itself aFragment— fragments serialize as sections of the host program that calls them. The writer never emits lossy output.
Source code in src/qprogram/serialization/writer.py
save ¶
Serialize program and write the result to path.
.qp files are always UTF-8, independent of the platform's locale.
Parameters:
-
program(QProgram) –Program to serialize.
-
path(str) –Destination file path.
Raises:
-
SerializationError–See
dumps.
Source code in src/qprogram/serialization/writer.py
loads ¶
Parse a .qp-format string into a QProgram.
Parameters:
-
text(str) –The
.qpdocument to parse. -
auto_activate(bool, default:True) –Whether a
require <vendor>line whose extension is not imported yet triggers entry-point discovery (theqprogram.vendorsgroup) — the installed package is imported on demand so the file is self-contained. SetFalseto require that vendors be imported explicitly beforehand (no implicit imports).
Returns:
Raises:
-
ParseError–On malformed input, unknown registry entries, or a required vendor that is neither registered nor discoverable (and, when its extension is installed but broken, the wrapped
VendorActivationError). -
ValidationError–If a declaration the grammar accepts is rejected by the program it builds, such as a variable id that is a reserved
.qpkeyword. -
TypeError–If a constructor call in the file does not fit its class's signature — an inline waveform, or a sweep source nested inside a combinator's argument list.
Source code in src/qprogram/serialization/parser.py
load ¶
Read a .qp file and parse it into a QProgram.
.qp files are always UTF-8, independent of the platform's locale.
Parameters:
-
path(str) –Path to the
.qpfile. -
auto_activate(bool, default:True) –See
loads.
Returns:
Raises:
-
ParseError–On malformed input or unknown registry entries.
-
OSError–If the file cannot be read.
-
ValidationError–If a declaration the grammar accepts is rejected by the program it builds, such as a variable id that is a reserved
.qpkeyword. -
TypeError–If a constructor call in the file does not fit its class's signature — an inline waveform, or a sweep source nested inside a combinator's argument list.
Source code in src/qprogram/serialization/parser.py
Platform protocol¶
PlatformProtocol ¶
Bases: ABC
Abstract interface that execution platforms must implement.
Splits into resource discovery (get_*) and capability + execution (capabilities,
validate, plan, explain, execute). The convention is that
execute calls validate first, raises
UnsupportedOperationError on any severity="error" diagnostic, and
surfaces "warning" / "info" diagnostics without raising — concrete platforms aren't
forced to follow it, but skipping the check means cryptic compiler errors in place of
structured diagnostics.
capabilities
abstractmethod
property
¶
The capability descriptor for this platform.
A PlatformCapabilities carries per-(element, bus_kind) bus profiles, a
platform-level profile (block / expression / bus-less ops), and a default-bus-profile fallback
for raw-string buses. Each slot is a BusCapabilities with rt / host halves.
Users introspect this to know what the platform supports; the validator consumes the same
object.
get_bus_schema
abstractmethod
¶
get_buses
abstractmethod
¶
Return the names of every bus this platform exposes.
Returns:
-
list[str]–Every bus name, spelled the way a program would reference it.
get_parameters
abstractmethod
¶
Return the parameter names supported on bus.
Parameters:
-
bus(str) –Bus whose parameters to list.
Returns:
-
list[str]–The parameter names
set_parameter/get_parameteraccept for that bus.
Source code in src/qprogram/platform.py
get_global_parameters
abstractmethod
¶
Return the parameter names that are not bound to any specific bus.
Returns:
-
list[str]–The platform-wide parameter names.
validate ¶
Validate a program against this platform's capabilities.
Default delegates to qprogram.validation.validate and discards the execution-plan
half of the return value. Platforms may override to prepend device-specific predicates or
short-circuit on the first error.
Parameters:
-
qprogram(QProgram) –Program to validate.
Returns:
-
list[Diagnostic]–List of diagnostics; empty when the program is fully supported with no forced-host
-
list[Diagnostic]–fallback events.
Source code in src/qprogram/platform.py
plan ¶
Return the execution-domain plan for qprogram.
Default delegates to qprogram.validation.validate and discards the diagnostic half.
Callers who want both — e.g. execute() implementations that gate on diagnostics and then
compile against the plan — should call qprogram.validation.validate directly to
avoid the duplicated walk.
Parameters:
-
qprogram(QProgram) –Program to classify.
Returns:
-
ExecutionPlan–Mapping from each AST node to its final domain set.
Source code in src/qprogram/platform.py
explain ¶
Render the execution plan for qprogram as a human-readable tree.
Default delegates to qprogram.explain: every body node is shown as its .qp
text with the domain set it will execute in ([rt|host] / [rt] / [host] /
[--]), with errors, warnings (notably forced-host and its reasons), and info
annotated inline. Programs with fragment calls are expanded first.
Parameters:
-
qprogram(QProgram) –Program to classify and render.
Returns:
-
str–The rendered tree.
Source code in src/qprogram/platform.py
execute
abstractmethod
¶
Execute a program and return its results.
By convention the implementation calls validate first and raises
UnsupportedOperationError on any severity="error" diagnostic.
Parameters:
-
qprogram(QProgram) –Program to run.
Returns:
-
QProgramResult–One record per measurement in the program.
Source code in src/qprogram/platform.py
stream ¶
Execute and yield partial results as they become available.
Optional — the default raises NotImplementedError. Platforms that don't support
streaming can leave this alone.
Parameters:
-
qprogram(QProgram) –Program to run.
-
**kwargs(Any, default:{}) –Platform-specific streaming options.
Raises:
-
NotImplementedError–Always, in the default implementation.
Source code in src/qprogram/platform.py
Reference platform¶
The software platform in this repository. It validates a program, interprets
the AST, and returns xarray.DataArray results with one dimension per
enclosing sweep (a Parallel composition contributing one shared dimension)
and none for an averaging block, and it is the semantics a vendor compiler is
tested against: an error diagnostic becomes UnsupportedOperationError, a
warning is raised through warnings.warn with category ExecutionWarning, and
info diagnostics pass silently. simulate(program) is the one-call form,
running the program on a throwaway platform whose measurement model defaults to
a deterministic, all-zero MockMeasurementModel. See
Running programs for the walkthrough.
simulate ¶
simulate(
program: QProgram,
*,
model: MeasurementModel | None = None,
schema: BusSchema | None = None,
parameters: dict[str, float] | None = None,
) -> QProgramResult
Execute program on a one-off ReferencePlatform — the quickest path to results.
The program should already be concrete; resolve any string waveform names first with
program.with_waveforms(library) (or library.apply(program)).
Parameters:
-
program(QProgram) –Program to run.
-
model(MeasurementModel | None, default:None) –Measurement model;
Noneuses a deterministic, all-zeroMockMeasurementModel. -
schema(BusSchema | None, default:None) –Bus schema for the throwaway platform.
-
parameters(dict[str, float] | None, default:None) –Initial parameter store, keyed
"bus.parameter". Copied, so the caller's dict is left untouched.
Returns:
-
QProgramResult–One record per measurement in the program.
Raises:
-
UnsupportedOperationError–When the program uses something the reference platform cannot run.
-
UnassignedVariableError–When an operation's expression references a variable no enclosing loop binds.
-
ValueError–When a measurement requests
rawand the model returns a trace whose shape is not(raw_samples, 2).
Source code in src/qprogram/executor.py
ReferencePlatform ¶
ReferencePlatform(
schema: BusSchema | None = None,
model: MeasurementModel | None = None,
parameters: dict[str, float] | None = None,
vendor_op_handlers: Mapping[
type[Operation], VendorOpHandler
]
| None = None,
)
Bases: PlatformProtocol
The in-tree software platform: validates, interprets, and returns real result xarrays.
Follows the documented convention exactly: execute raises
UnsupportedOperationError on any error diagnostic, surfaces warnings via
warnings (category ExecutionWarning), and passes info through silently.
Fragment calls are expanded before execution. This is the reference semantics vendor
compilers are tested against.
Parameters:
-
schema(BusSchema | None, default:None) –Bus schema reported by
get_bus_schema.Nonemakes that method raise andget_busesreturn nothing. -
model(MeasurementModel | None, default:None) –Measurement model;
Nonebuilds a freshMockMeasurementModel(all-zero response, no noise, ground state). -
parameters(dict[str, float] | None, default:None) –Initial platform parameter store, keyed
"bus.parameter". Copied once, then that copy is read byget_parameter, written byset_parameter, and exposed to the model — so a run's writes persist across calls toexecuteon the same platform. -
vendor_op_handlers(Mapping[type[Operation], VendorOpHandler] | None, default:None) –Map of vendor
Operationclass to aVendorOpHandlerinvoked when that op executes — the seam a platform uses to give its own vendor ops runtime effects on the parameter store (a vendor'sset_parameter/get_parameteroperations, which target an alias rather than a bus). Ops without a handler execute generically (expressions evaluated, then no-op).
Source code in src/qprogram/executor.py
capabilities
property
¶
The permissive descriptor built by reference_capabilities, recomputed per access.
Recomputing is what lets a vendor extension imported after the platform was constructed have its tokens honored.
get_bus_schema ¶
Return the configured schema.
Returns:
-
BusSchema–The schema this platform was constructed with.
Raises:
-
ValueError–When the platform was built without one.
Source code in src/qprogram/executor.py
get_buses ¶
Return the schema's bus names, or an empty list without a schema.
Returns:
-
list[str]–One name per
(element, bus kind)pair the schema declares, with the index position -
list[str]–left as
*because the schema names kinds rather than enumerating indices.
Source code in src/qprogram/executor.py
get_parameters ¶
Return parameter names stored under bus (keys shaped "bus.parameter").
Parameters:
-
bus(str) –Bus whose parameters to list.
Returns:
-
list[str]–The parameter names currently present in the store for that bus. The store grows as
-
list[str]–set_parameterruns, so this reflects what has been set, not what the bus accepts.
Source code in src/qprogram/executor.py
get_global_parameters ¶
Return every known bus.parameter key.
Returns:
-
list[str]–The store's keys, sorted. These are fully qualified, not the bus-less parameters the
-
list[str]–name might suggest — the reference platform keeps one flat store.
Source code in src/qprogram/executor.py
execute ¶
Validate and run qprogram, returning its QProgramResult.
Warning-severity diagnostics are re-emitted through warnings as
ExecutionWarning and do not stop the run; info-severity ones are dropped.
Parameters:
-
qprogram(QProgram) –Program to run. Fragment calls are expanded first, on a copy.
-
**kwargs(object, default:{}) –Accepted and ignored, so callers can pass the platform-specific options a real back-end would take.
Returns:
-
QProgramResult–One record per measurement in the program.
Raises:
-
UnsupportedOperationError–When validation produces any
severity="error"diagnostic (all of them are listed in the message). -
UnassignedVariableError–When an operation's expression references a variable no enclosing loop binds.
-
ValueError–When a measurement requests
rawand the model returns a trace whose shape is not(raw_samples, 2).
Source code in src/qprogram/executor.py
reference_capabilities ¶
Build the reference platform's permissive capability descriptor.
Every token in the live CAPABILITY_REGISTRY is supported — core
and vendor tokens (the reference executor runs vendor operations generically, so importing
a vendor extension makes its programs executable here). Computed fresh on each call so
late-registered vendor tokens are picked up. Each bus slot supports everything host-side while its
rt half excludes the bus-scoped parameter ops (set_parameter / get_parameter), so those
stay host-side-only on every bus — mirroring real platforms, so plans, forced-host warnings,
and explain are meaningful against the reference platform too.
Returns:
-
PlatformCapabilities–A descriptor with an empty per-bus map, a platform slot for blocks and expressions, and a
-
PlatformCapabilities–default bus profile every bus falls back to.
Source code in src/qprogram/executor.py
MeasurementModel ¶
Bases: Protocol
What the executor asks of a measurement back-end — one sample per shot.
env carries the currently bound loop variables (by id) and the platform parameters (by
"bus.parameter"), so a model can shape its response as a function of the sweep.
A model that simulates an ADC also declares raw_samples, an int the executor reads once
at the start of a run (default 16), and returns a raw trace of shape
(raw_samples, 2) from every sample. One that does not can leave
MeasurementSample.raw at its default; a measurement requesting
MeasurementField.RAW then raises rather than accumulating a
broadcast of the wrong trace.
sample ¶
Return one shot's outcomes for a measurement on bus under env.
Parameters:
-
bus(str) –The bus the measurement runs on, as a plain string (
""for a measurement op with no bus attribute). -
env(Mapping[str, float]) –Bound loop variables by id, plus platform parameters keyed
"bus.parameter".
Returns:
-
MeasurementSample–The outcomes for a single shot.
Source code in src/qprogram/executor.py
MockMeasurementModel ¶
MockMeasurementModel(
response: Callable[[str, Mapping[str, float]], complex]
| None = None,
p_excited: Callable[[str, Mapping[str, float]], float]
| None = None,
noise: float = 0.0,
raw_samples: int = 16,
seed: int = 0,
)
Deterministic mock measurement model — the executor's default.
The noiseless IQ point comes from response (default 0j); per-shot gaussian noise is
added on both quadratures. The classified state is a Bernoulli sample of
p_excited (default 0.0). The raw trace replicates the IQ point over
raw_samples time samples with per-sample noise. All randomness flows from one seeded
generator, so identical programs produce identical results.
Parameters:
-
response(Callable[[str, Mapping[str, float]], complex] | None, default:None) –(bus, env) -> complexnoiseless IQ response.envholds the bound loop variables and platform parameters, so e.g. a Rabi oscillation islambda bus, env: np.sin(np.pi * env["g"] / 2) ** 2 + 0j.Noneresponds0j. -
p_excited(Callable[[str, Mapping[str, float]], float] | None, default:None) –(bus, env) -> floatexcited-state probability for the classified outcome.Nonekeeps every shot in the ground state. -
noise(float, default:0.0) –Standard deviation of the gaussian noise added per quadrature, per shot. Zero skips the noise draws entirely rather than drawing zero-width ones.
-
raw_samples(int, default:16) –Number of time samples in the
rawtrace. -
seed(int, default:0) –Seed for the model's private
numpy.random.default_rng.
Source code in src/qprogram/executor.py
sample ¶
Return one deterministic-given-the-seed shot for bus under env.
Parameters:
-
bus(str) –The bus the measurement runs on; forwarded to
responseandp_excited. -
env(Mapping[str, float]) –Bound loop variables by id, plus platform parameters keyed
"bus.parameter"; forwarded toresponseandp_excited.
Returns:
-
MeasurementSample–The shot's IQ point, classified state, and raw trace.
Source code in src/qprogram/executor.py
MeasurementSample
dataclass
¶
One shot's worth of simulated measurement outcomes.
Attributes:
-
i(float) –In-phase value.
-
q(float) –Quadrature value.
-
state(int) –Classified outcome,
0or1. -
raw(ndarray) –Raw trace of shape
(raw_samples, 2)(I and Q per time sample). Defaults to an empty(0, 2)array, which is what a model that simulates no ADC wants; it is read only by a measurement that requestsMeasurementField.RAW, and such a measurement rejects a trace of the wrong shape rather than broadcasting it.
ExecutionWarning ¶
Bases: UserWarning
Category for warning-severity diagnostics surfaced during ReferencePlatform.execute.
Capability protocol¶
The data types and helpers that platforms use to declare which DSL features they support. See Capabilities, diagnostics, and profiles for the narrative tour.
Descriptors and bundles¶
PlatformCapabilities
dataclass
¶
PlatformCapabilities(
bus: Mapping[BusSelector, BusCapabilities],
platform: BusCapabilities,
default_bus_profile: BusCapabilities,
)
The capability descriptor returned by PlatformProtocol.capabilities.
Capabilities split along two grains — per-bus and platform-wide — plus a fallback slot for buses the per-bus mapping does not cover.
Attributes:
-
bus(Mapping[BusSelector, BusCapabilities]) –One slot per
(element_kind, bus_kind). Bus-touching ops (play,wait,measure, ...) route here viafor_bus, and so do the tokens that travel with them:waveform.*andmeasure.fields.*. -
platform(BusCapabilities) –The platform-wide slot. Holds block-structure tokens, expression tokens, and bus-less ops.
-
default_bus_profile(BusCapabilities) –Fallback for raw-string buses lacking schema metadata, and for bus-touching ops whose
(element, kind)key is missing frombus.
for_bus ¶
Resolve the BusCapabilities that applies to bus.
A BusRef carrying schema metadata routes to bus[(element, kind)] when present,
otherwise to default_bus_profile. A plain str or schema-less BusRef always
routes to default_bus_profile.
Parameters:
-
bus(str | BusRef) –The bus a node touches.
Returns:
-
BusCapabilities–The slot whose capabilities apply to that bus.
Source code in src/qprogram/protocol.py
BusCapabilities
dataclass
¶
Two stacked CompilerCapabilities for a single bus or platform slot.
Each half describes what the slot supports in that execution domain. Either may be None
when the bus or platform lacks an engine for that domain — e.g. a flux bus driven only by a
slow DAC has rt=None; a real-time-only bus has host=None.
Attributes:
-
rt(CompilerCapabilities | None) –What the slot supports on the real-time sequencer.
-
host(CompilerCapabilities | None) –What the slot supports under host-side orchestration.
get ¶
Return the CompilerCapabilities for domain, or None if unsupported.
Parameters:
-
domain(Domain) –The execution domain to read.
Returns:
-
CompilerCapabilities | None–The half describing that domain, or
Nonewhen the slot has no engine for it.
Source code in src/qprogram/protocol.py
supported_domains ¶
Return the set of domains this slot has a non-None descriptor for.
Returns:
-
frozenset[Domain]–A subset of
{"rt", "host"}; empty when the slot can run nothing at all.
Source code in src/qprogram/protocol.py
CompilerCapabilities
dataclass
¶
CompilerCapabilities(
profile: str,
version: tuple[int, int, int],
capabilities: frozenset[str],
limits: Mapping[str, float],
predicates: tuple[Predicate, ...],
vendor_versions: Mapping[str, tuple[int, int, int]],
)
The capability descriptor that PlatformProtocol exposes via .capabilities.
Materialized by from_profile, which walks extends and merges parent → child:
capabilities/predicates union, limits replace. A live device may pass limit_overrides= to
further tighten any limit. The same object is what the validator consumes and what users
introspect — there is no separate "advertised vs. enforced" surface.
Attributes:
-
profile(str) –Name of the source profile.
-
version(tuple[int, int, int]) –Source profile version.
-
capabilities(frozenset[str]) –Merged set of capability tokens.
-
limits(Mapping[str, float]) –Merged numeric limits.
-
predicates(tuple[Predicate, ...]) –Merged predicates, parent's first.
-
vendor_versions(Mapping[str, tuple[int, int, int]]) –Merged vendor-extension version expectations.
supports ¶
Return whether token is in this descriptor's capability set.
Parameters:
-
token(str) –The capability token to look for.
Returns:
-
bool–Truewhen this descriptor advertises the token.
Source code in src/qprogram/protocol.py
from_profile
classmethod
¶
from_profile(
profile_name: str,
*,
limit_overrides: Mapping[str, float] | None = None,
extra_predicates: tuple[Predicate, ...] = (),
) -> CompilerCapabilities
Resolve a registered profile and merge it into a capability descriptor.
Parameters:
-
profile_name(str) –Name of a profile registered with
register_profile. -
limit_overrides(Mapping[str, float] | None, default:None) –Per-limit replacements for the merged values — typically supplied by a device that knows its hardware is tighter than the profile defaults.
-
extra_predicates(tuple[Predicate, ...], default:()) –Site-specific predicates run on top of the profile's. Useful for rack-level constraints that don't belong in the vendor-shipped profile.
Returns:
-
CompilerCapabilities–The materialized
CompilerCapabilities.
Raises:
-
KeyError–If
profile_name, or any profile named by anextendslink, is not registered. -
ValueError–If the
extendschain forms a cycle.
Source code in src/qprogram/protocol.py
Profile
dataclass
¶
Profile(
name: str,
version: tuple[int, int, int],
extends: str | None,
capabilities: frozenset[str],
limits: Mapping[str, float] = dict(),
predicates: tuple[Predicate, ...] = (),
vendor_versions: Mapping[
str, tuple[int, int, int]
] = dict(),
)
A named, versioned bundle of capabilities, limits, and predicates.
Vendors register one or more profiles via register_profile. Profiles may extends
another by name — capabilities and predicates accumulate (parent → child), limits inherit and may
be overridden by the child.
Attributes:
-
name(str) –Unique profile name (e.g.
"myvendor-default-v1"). -
version(tuple[int, int, int]) –Profile version as
(major, minor, patch). -
extends(str | None) –Name of a parent profile, or
Nonefor a root profile. -
capabilities(frozenset[str]) –Capability tokens this profile advertises. Validated against
CAPABILITY_REGISTRYat construction. -
limits(Mapping[str, float]) –Numeric thresholds. The validator ignores keys it does not know, so a profile may declare a limit an older validator has no check for.
-
predicates(tuple[Predicate, ...]) –Predicates run against every visited node.
-
vendor_versions(Mapping[str, tuple[int, int, int]]) –Informational record of which vendor extension versions this profile was designed for; mirrors the
.qprequire <vendor> <version>line.
Diagnostic
dataclass
¶
Diagnostic(
severity: Literal["error", "warning", "info"],
code: str,
message: str,
node: Operation | Block | None = None,
path: tuple[int | str, ...] | None = None,
capability: str | None = None,
limit: tuple[str, float] | None = None,
domain: Domain | None = None,
)
One issue found by the validator.
Attributes:
-
severity(Literal['error', 'warning', 'info']) –"error"for hard failures the program cannot execute with (missing capability, exceeded limit, empty execution domain);"warning"for programs that will run but in a degraded or surprising way callers should surface prominently (notably theforced-hostnotice attached to the highest block that lost"rt"from its execution domain);"info"for purely advisory output. The execution convention:execute()raises on errors, surfaces warnings without raising, and passes info through. -
code(str) –Short machine-readable identifier (
"missing-capability","limit-exceeded","empty-domain","forced-host", or a vendor-prefixed code). -
message(str) –Human-readable explanation.
-
node(Operation | Block | None) –The offending AST node when one is available. Capability-missing diagnostics always have one; whole-program checks (total-measurement-count, ...) do not.
-
path(tuple[int | str, ...] | None) –Structural address of
nodeunder the validated program's body (seeqprogram.paths). Stamped byvalidate();Nonewhen there is no node. Because the.qpround-trip preserves structure, the same path resolves againstloads(dumps(p))— whosesource_mapthen maps it to a 1-based.qpline. -
capability(str | None) –The token that was missing, when applicable.
-
limit(tuple[str, float] | None) –(name, observed_value)when a numeric limit was exceeded. The threshold itself lives inCompilerCapabilities.limits. -
domain(Domain | None) –Populated on
"forced-host"diagnostics with the domain the node ended up running in (typically"host").
DomainConstraint
dataclass
¶
A predicate's soft outcome: this node would be supported, except in the listed domains.
The classifier collects these and subtracts exclude from the node's per-domain support set.
Compare to Diagnostic, which is a hard outcome (the node is unsupported outright in the
slot being validated). A predicate may yield zero or more of each type from a single call.
Attributes:
-
node(Operation | Block) –The AST node the constraint applies to. Must be a
Block— the classifier reports an op-targeted constraint asbad-domain-constraint. -
exclude(frozenset[Domain]) –Domains the node cannot run in. Usually a single-element frozenset (
{"rt"}). -
reason(str) –Human-readable explanation surfaced in the eventual
forced-hostdiagnostic, or in theempty-domainerror if the exclusion leaves nothing.
Domain
module-attribute
¶
Execution domain for an AST node — real-time hardware sequencer ("rt") or host-side
orchestration on the lab server ("host").
BusSelector
module-attribute
¶
(element_kind, bus_kind) key into PlatformCapabilities.bus. ("q", "drive") selects
every transmon drive bus on the platform.
ExecutionPlan
module-attribute
¶
Classifier output: each AST node mapped to the set of domains it may execute in.
A frozenset({"rt"}) entry means a real-time hardware path; frozenset({"host"}) means host-side
dispatch; frozenset({"rt", "host"}) means the platform may pick either at compile time. Delivered
by PlatformProtocol.plan.
ValidationContext ¶
ValidationContext(
*,
variable_bindings: Mapping[Variable, Block],
sweep_kinds: Mapping[Variable, SweepKind],
max_loop_nesting: int,
max_parallel_arity: int,
measurement_count: int,
measurement_fields: Mapping[str, tuple[str, ...]]
| None = None,
program_buses: frozenset[str] = frozenset(),
)
Read-only view of program-wide data-flow facts, built once per validate() call.
Predicates use the queries here to answer "in this AST, is X legal?" without re-walking the tree. New queries are added here so predicate authors have a single, discoverable surface; predicates must treat the context as immutable.
Parameters:
-
variable_bindings(Mapping[Variable, Block]) –Each loop-bound variable mapped to the block that binds it.
-
sweep_kinds(Mapping[Variable, SweepKind]) –Each loop-bound variable mapped to its sweep source's
KIND. -
max_loop_nesting(int) –Deepest repetition-level count observed in the program.
-
max_parallel_arity(int) –Largest number of loops composed by any
Parallelblock. -
measurement_count(int) –Total number of measurement operations in the program.
-
measurement_fields(Mapping[str, tuple[str, ...]] | None, default:None) –Requested fields per measurement name;
Noneis read as an empty mapping. -
program_buses(frozenset[str], default:frozenset()) –Every bus referenced anywhere in the program.
Source code in src/qprogram/protocol.py
max_loop_nesting
property
¶
Deepest repetition-level count observed in the program.
A block adds a level when it declares REPEATS, so a
Parallel counts as one level however many loops it composes —
its headers advance in lockstep rather than nesting.
measurement_count
property
¶
Total number of MeasurementOperation instances in the program.
program_buses
property
¶
Every bus name referenced anywhere in the program (QProgram.buses at build time).
Elements may be BusRef instances (which subclass str), so per-bus routing through
PlatformCapabilities.for_bus keeps its schema awareness. Used by the
validator to route broadcast ops (Sync(targets=None)) across every touched bus.
sweep_kind_of ¶
Return how var is loop-bound.
Parameters:
-
var(Variable) –The variable to look up.
Returns:
-
SweepKind | None–The binding
Sweep's sourceKIND—"linear"for an -
SweepKind | None–exact
start + step * iramp,"arbitrary"otherwise — orNonewhen the -
SweepKind | None–variable is not loop-bound (set externally, or unused).
"averaged"belongs to the -
SweepKind | None–vocabulary but no built-in source declares it.
Source code in src/qprogram/protocol.py
binding_loop_of ¶
Return the loop block that binds var, or None if it has no binding.
Parameters:
-
var(Variable) –The variable to look up.
Returns:
-
Block | None–The
Sweepthat bindsvar— a standalone loop or one of a -
Block | None–Parallel's composed headers — orNonewhen nothing binds -
Block | None–it. This is the node a
DomainConstraintaboutvarmust target.
Source code in src/qprogram/protocol.py
measurement_fields ¶
Return the fields tuple of the named measurement, or None if it doesn't exist.
Predicates use this to check that a referenced measurement requested the data they care
about — e.g. that a handle.state reference's source measurement requested
STATE classification.
Parameters:
-
name(str) –Measurement name to look up.
Returns:
-
tuple[str, ...] | None–The measurement's requested field names in canonical order, or
Nonewhen the -
tuple[str, ...] | None–program holds no measurement with that name.
Source code in src/qprogram/protocol.py
known_measurement_names ¶
Return the set of every measurement name in the program.
Returns:
-
set[str]–Every name a measurement in the program carries, whether the author spelled it or the
-
set[str]–builder allocated it.
Source code in src/qprogram/protocol.py
Predicate ¶
Bases: Protocol
Per-node validation predicate consulted while validate() walks the program.
Receives a ValidationContext with cross-op data-flow facts. Returns zero or more
Diagnostic or DomainConstraint objects.
A node is judged against each domain half of each slot it routes to, so a predicate carried by both the rt and
the host half of a profile runs once per (domain, bus) pair: twice for a single-bus node, and twice more for
every additional bus a multi-bus operation such as Sync touches. The validator
discards duplicate outputs, so a predicate should be a cheap, side-effect-free function of (node, ctx).
Motivating examples:
- Flagging a
Waitwhosedurationis bound by an arbitrary-valued sweep — qblox can't run it at all, so the predicate emits aDiagnostic. - Flagging an
IQDragwhosesigmais loop-bound — qblox can't realtime-updatesigma, but the platform can still dispatch one shot per iteration host-side, so the predicate emits aDomainConstraintexcluding"rt".
PredicateFn
module-attribute
¶
PredicateFn = Callable[
["Operation | Block", "ValidationContext"],
Iterable["Diagnostic | DomainConstraint"],
]
QPROGRAM_BASE_V1
module-attribute
¶
QPROGRAM_BASE_V1 = Profile(
name="qprogram-base-v1",
version=(0, 1, 0),
extends=None,
capabilities=_BLOCKS
| _EXPRS
| _SWEEP_KINDS
| _SWEEP_SOURCES,
limits={},
predicates=(),
vendor_versions={},
)
Validator¶
validate(program, caps) returns a list of Diagnostics and an
ExecutionPlan covering every visited node except the root body, keyed by node
identity rather than by structural equality. explain(program, caps) renders
that same classification as a string: a header with the severity counts, then
one row per node carrying its .qp text, its domain, and any diagnostic on it.
optimize(program, caps) applies the one rewrite the validator only reports as
an info hint, lifting a host-side sweep out of an averaging block so that the
averaging itself can run in real time.
validate and explain expand fragment Call nodes before walking, so their
diagnostics reference nodes of that expansion rather than nodes the caller
holds. optimize expands only when the program's own body holds an average;
otherwise it returns a deep copy with the Call nodes intact.
validate ¶
validate(
qprogram: QProgram, caps: PlatformCapabilities
) -> tuple[list[Diagnostic], ExecutionPlan]
Run capability validation + execution-domain classification.
Algorithm:
- Pre-walk to build a
ValidationContext(variable bindings, sweep kinds, …). -
Single recursive post-order walk that, per node, computes:
-
For operations: the available domain set is the slots where the op's required tokens are present (and any predicate-emitted
Diagnosticis absent). The op'ssupportequals itsavailable—DomainConstraintoutputs are not applied to the op; they are routed to the block they target (typically the binding loop of a swept variable). - For blocks:
support = own_available & natural_from_ops - exclude_from_constraints, wherenatural_from_opsis the op-children consensus directly (an all-real-time block is shifted to host-side by the (e2) fallback, not by widening the consensus). For anAverageonly the averaging-relevant op-children enter the consensus. Mixed op-children produce amixed-domainerror. Block-children act as units; they don't constrain the parent's domain but the parent's domain constrains them (no host-side block inside a real-time block). - Whole-program limit checks (loop nesting, parallel arity, measurement count) against the
platform slot's limits;
min_wait_duration_nschecks against the bus slot's limits. - Universal Conditional checks (unknown measurement, missing state classification).
- Emit one
"forced-host"warning per highest-block whosesupportwas reduced from{rt, host}to{host}, with the subtree's constraint reasons in the message. - Stamp each node-bearing diagnostic with its structural
Diagnostic.path.
Parameters:
-
qprogram(QProgram) –Program to validate.
-
caps(PlatformCapabilities) –Platform capability descriptor to check the program against.
Returns:
-
list[Diagnostic]–(diagnostics, plan). The plan covers every visited AST node (excluding the root -
ExecutionPlan–body) and is identity-keyed: each node instance gets its own entry, even when two
-
tuple[list[Diagnostic], ExecutionPlan]–nodes are structurally identical (
plan[node]looks up byid, and iterating the -
tuple[list[Diagnostic], ExecutionPlan]–plan yields every instance).
Note
Programs containing fragment Call nodes are expanded
first (QProgram.expand) — capabilities are checked against the substituted
fragment bodies, and diagnostics reference nodes of that internal expansion. Callers
that need the identity-keyed plan for nodes they hold should expand explicitly and
validate the expanded program.
Source code in src/qprogram/validation.py
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | |
explain ¶
Render the execution plan of program under caps as a tree.
Programs containing fragment calls are expanded first (the header says so) — the plan always describes what would actually execute.
Parameters:
-
program(QProgram) –The program to classify and render.
-
caps(PlatformCapabilities) –The platform capability descriptor to validate against.
Returns:
-
str–A multi-line string: header with severity counts, the body tree with one row per node
-
str–(
.qptext, domain column, inline diagnostics), and a footer for whole-program -
str–diagnostics that have no node.
Source code in src/qprogram/explain.py
optimize ¶
Return a copy of qprogram rewritten to run better under capabilities.
Applies the rewrite the validator only suggests (the "reorderable-averaging" info hint).
For every average that is host-side-only solely because it encloses a host-side sweep —
while its measurement sequence supports real-time hardware — the sweep is lifted to become the
outer loop and the host-side-only setup ops are hoisted out of the average, so the averaging
itself runs as a real-time hardware feature::
average(N): for v in sweep: # host-side (slow DAC step)
for v in sweep: set_offset(flux, v) # hoisted setup
set_offset(flux, v) --> average(N): # REAL-TIME
play(drive, ...) play(drive, ...)
measure(readout) measure(readout)
Only the well-defined pattern above is rewritten — an average whose sole child is a single
flat sweep loop carrying host-side-only setup plus a real-time-capable measurement sequence.
Anything else is left untouched. (Whether a given op is host-side-only is decided by
capabilities: the example assumes a flux bus with no real-time engine; on a platform whose
flux bus has a real-time half, set_offset would not be hoisted and the average would already
be real-time.)
This is opt-in because the reorder is not unconditionally semantics-preserving:
- It groups all shots of one sweep point together rather than interleaving sweep passes; the averaged result is identical for a stationary system but differs under drift.
- Each hoisted op runs once per sweep point instead of once per shot. That is the point of hoisting "setup" (DC-offset / parameter writes are idempotent, so the count change is harmless), but the rewrite hoists every host-side-only op in the leading run — so only apply it when those ops are genuinely idempotent setup. To stay safe, the rewrite refuses to reorder: it only hoists a leading contiguous run of host-side-only ops, never one that sits after a kept op (which would move it past that op and could change results).
Parameters:
-
qprogram(QProgram) –The program to optimize. Never mutated.
-
capabilities(PlatformCapabilities) –The platform descriptor used to classify which ops are host-side-only.
Returns:
-
QProgram–A new
QProgramwith the rewrite applied; the original is untouched. The -
QProgram–search for an
averagescans the program's own body and does not look inside the -
QProgram–fragments it calls, so a program whose body holds no
averageblock comes back a plain -
QProgram–deep copy with its
Callnodes intact — including a program -
QProgram–whose only
averagesits in a fragment body. A program whose body does hold an average -
QProgram–is validated against
capabilitiesto classify its ops, which expands anyCallnodes -
QProgram–first, so such a program is returned in expanded form even if no average ends up rewritten.
Raises:
-
ValidationError–If the program's body holds an average and its fragment calls cannot be expanded (a call cycle, or a binding used in an incompatible position).
Source code in src/qprogram/optimization.py
Diagnostic paths¶
Every node-bearing Diagnostic carries a structural path. AstPath is the
type, node_path builds one for a node, resolve_path walks one back to the
node it names, format_path renders one for display, and iter_child_edges is
the single ordered traversal that node_path, resolve_path, and the
validator all read the AST through.
AstPath
module-attribute
¶
A structural node address: () is program.body; see the module docstring for segments.
node_path ¶
Return the path of node under root, or None if it isn't in the tree.
Matching is by object identity — pass the same instance the tree holds (e.g. a
Diagnostic.node), not a structural twin.
Parameters:
Returns:
-
AstPath | None–The structural address of
node—()whennodeis the root itself — orNone -
AstPath | None–when the tree holds no such instance.
Source code in src/qprogram/paths.py
resolve_path ¶
Return the node addressed by path under root — the inverse of node_path.
Parameters:
Returns:
-
Block | Operation–The addressed block or operation — the root node itself (a program's body, when
rootis -
Block | Operation–a program) for the empty path.
Raises:
-
KeyError–When any segment doesn't exist on the node reached so far (a dangling path — typically a path computed against a structurally different program).
Source code in src/qprogram/paths.py
format_path ¶
Render a path for humans: () → "body"; (1, "arm:0", 2) → "body[1].arm:0[2]".
Parameters:
-
path(AstPath) –The structural address to render.
Returns:
-
str–The path as a single line, always rooted at
body: integer segments in brackets, string -
str–segments after a dot.
Source code in src/qprogram/paths.py
iter_child_edges ¶
Yield (segment, child) for every structural child of node, in document order.
The single canonical child enumeration behind node_path /
resolve_path — Conditional keeps its arm bodies on .arms / .else_body and
Parallel its loop headers on .loops, so a plain elements walk would miss them.
Parameters:
-
node(Block | Operation) –The node whose children to enumerate. An operation is a leaf and yields nothing.
Yields:
-
int | str–A
(segment, child)pair per child, wheresegmentis the path segment that addresses -
Block | Operation–that child under
node.
Source code in src/qprogram/paths.py
Registries and helpers¶
Registration and lookup for the capability tokens and the named profiles.
Profile runs every token it is given through validate_tokens in its
__post_init__, so a token no core definition or registration call has put in
CAPABILITY_REGISTRY is rejected where the profile is defined rather than
surfacing later as a feature the platform silently lacks.
CAPABILITY_REGISTRY
module-attribute
¶
Mutable token registry. Core tokens are added at import time;
vendor packages extend it via register_capability_tokens.
register_profile ¶
Register a profile in PROFILE_REGISTRY under its name.
Idempotent for an equal profile, so an import-time side effect that runs twice is safe even when it rebuilds the bundle each time rather than holding it as a module constant. Only a profile whose content differs raises.
Of an equal pair the registry keeps the first object, so the profile just passed in is not
necessarily the one resolve_profile returns; mutating limits on the object you built will
not be visible through the registry.
Equality is Profile's own, which compares every field with that field's
__eq__. Predicates count as the objects they are, so a profile whose predicates are rebuilt
on each construction — a lambda, a closure, a functools.partial — is never equal to a
second construction of itself and still raises. Hold them as module-level functions, or give
them a type with value equality, to get the idempotency.
Parameters:
-
profile(Profile) –The bundle to register under its own
name.
Raises:
-
ValueError–If a profile with different content is already registered under
profile.name.
Source code in src/qprogram/protocol.py
resolve_profile ¶
Look up a profile by name.
Parameters:
-
name(str) –The registered profile name.
Returns:
Raises:
-
KeyError–If
nameis not registered. The message lists the currently-known names.
Source code in src/qprogram/protocol.py
register_capability_tokens ¶
Register vendor-extension capability tokens.
Idempotent (re-registration is a no-op). Validates the shape of each token but does not enforce
a namespace policy beyond rejecting empty segments and stray dots — each vendor owns its own
vendor.<name>.* prefix.
Parameters:
-
*tokens(str, default:()) –Tokens to register.
Raises:
-
ValueError–If any token is empty, starts/ends with
., or contains...
Source code in src/qprogram/protocol.py
register_waveform_token ¶
Register a waveform class → token mapping.
Also registers token in CAPABILITY_REGISTRY so profiles that list the token don't
have to call both functions.
Parameters:
-
cls(type) –Waveform class to register.
-
token(str) –Canonical capability token (e.g.
"waveform.iq_drag").
Raises:
-
ValueError–If
tokenis empty, starts or ends with., or contains...
Source code in src/qprogram/protocol.py
validate_tokens ¶
Validate that every token in tokens is registered.
Called from Profile's __post_init__, so an unknown token — a typo, or a feature this
build does not have — is rejected at registration rather than during validation.
Parameters:
-
tokens(Iterable[str]) –Capability tokens to check.
Raises:
-
ValueError–If any token is not in
CAPABILITY_REGISTRY.
Source code in src/qprogram/protocol.py
waveform_token ¶
Return the canonical capability token for a waveform value, or None.
String aliases return None (callers add waveform.alias directly). Unknown concrete classes
also return None; the validator skips per-class refinement for them. Channel-kind tokens
(waveform.single / waveform.iq) come from Operation.required_capabilities via
isinstance checks, so they remain present even when no per-class token is registered.
Parameters:
-
wf(object) –The waveform value to classify. Typed
objectbecause the dispatch is purely class-keyed, and vendor packages register their own classes here without subclassingWaveform/IQWaveform.
Returns:
-
str | None–The token registered for the value's class, or
Nonefor a string alias or an -
str | None–unregistered class.
Source code in src/qprogram/protocol.py
expression_tokens ¶
Recursively collect capability tokens contributed by an expression value.
The returned set always describes the Expression node type (and operator name for
MathFunc), not the value. Plain numeric literals contribute nothing — they're not
Expression nodes. Operations call this on each Expression-typed instance attribute they carry.
Parameters:
-
value(object) –Anything that can appear as an expression operand. A value that is not an
Expressioncontributes nothing.
Returns:
-
set[str]–Set of capability tokens contributed by the value and its descendants.
Source code in src/qprogram/protocol.py
measurement_field_token ¶
Return the capability token for a measurement field name ("iq" → "measure.fields.iq").
Parameters:
-
field(str) –A measurement field name, core or vendor-registered.
Returns:
-
str–The token a profile lists to advertise support for that field.
Source code in src/qprogram/protocol.py
known_measurement_fields ¶
Return every currently registered measurement field name (core plus vendor).
Derived from CAPABILITY_REGISTRY rather than from MeasurementField, so vendor
fields are included the moment their token is registered. Used by
qprogram.operations.operation.normalize_fields to reject typos at the measure(...)
call site, and by editor tooling to offer completions.
Returns:
-
set[str]–Every field name that currently has a
measure.fields.<name>token registered.
Source code in src/qprogram/protocol.py
Reserved keywords¶
A frozenset of the identifiers the .qp grammar reserves. Variable ids,
fragment names, and vendor namespaces are all checked against it, and
Reserved keywords lists the words with what each one is kept
for.
RESERVED_KEYWORDS
module-attribute
¶
Errors¶
Every error QProgram raises while a program is built, parsed, or run derives
from QProgramError, so one except covers all of it.
UnsupportedOperationError, BusNotAvailableError, WaveformResolutionError,
CompilationError, and HardwareError are the platform-side half of the
hierarchy, defined here so that user code can catch one class per failure mode
whichever backend is in use; of the five, only UnsupportedOperationError is
raised in this repository, by ReferencePlatform.execute. Errors
covers when each one fires and what it carries.
QProgramError ¶
Bases: Exception
Root of the QProgram exception hierarchy.
Catch this for "QProgram raised something"; prefer a more specific subclass when you do care why.
ValidationError ¶
Bases: QProgramError
A construction-time validation failure.
Raised when a program is being assembled and an operation rejects its arguments — e.g. an IQ
waveform on a single-channel bus, a measure() on a bus without ADC, duplicate variable ids, or
a measurement-handle name collision.
Deliberately not a ValueError: construction validation deserves a catch of its own, and inheriting ValueError
would turn except ValueError into an accidental catch-all for it. Two subclasses do extend ValueError —
InvalidVariableIdError and
UnassignedVariableError — because a malformed identifier and an expression
that has no value really are bad-value errors in the ordinary Python sense.
InvalidVariableIdError ¶
Bases: ValidationError, ValueError
A variable id rejected on its pattern or because it is reserved.
Also a ValueError, since a malformed identifier is a bad value in the ordinary Python
sense — except ValueError around variable construction catches it.
Parameters:
-
id(str) –The offending identifier. Available at catch-time as
id. -
reserved(bool, default:False) –Trueifidmatches the identifier pattern but is one ofRESERVED_KEYWORDS;Falsefor an outright pattern violation. Available at catch-time asreserved.
Source code in src/qprogram/errors.py
UnassignedVariableError ¶
Bases: ValidationError, ValueError
An Expression evaluated while it still references unbound Variable s.
Raised by Expression.evaluate_or_raise. Also a ValueError, since an
expression over unbound variables has no value to give.
Parameters:
-
expression(Expression) –The expression that failed to evaluate. Kept as
expression.
Attributes:
-
free_variables(set[Variable]) –The unbound variables that caused the failure, collected from
expressionat construction.
Source code in src/qprogram/errors.py
ParseError ¶
Bases: QProgramError
Error during .qp file parsing.
Direct child of QProgramError, distinct from
ValidationError — validation runs on in-memory programs, parsing fails on malformed
input text.
Parameters:
-
message(str) –Human-readable error description.
-
line_num(int, default:0) –1-based line number of the offending input, or
0for whole-file errors.
Source code in src/qprogram/serialization/parser.py
SerializationError ¶
Bases: QProgramError
A program the .qp writer cannot faithfully serialize.
Raised instead of emitting lossy or unparseable output — e.g. an operation or block class that
was never registered with the serialization registry, a vendor operation whose extension forgot
to call register_vendor_version, or an attribute value of a type the format
has no representation for. The write-side counterpart of ParseError, but not a guarantee
against one: the writer only refuses what it can see is unrepresentable, so a program can
serialize cleanly and still fail to load. An inline waveform argument holding a math function
is the known case, since Gaussian(amplitude=sin(phi), ...) writes as-is and the grammar
accepts no function call in a constructor argument.
VendorActivationError ¶
Bases: QProgramError
A discovered vendor extension that could not be activated.
Raised by try_activate_vendor (and, during .qp parsing, wrapped into a
ParseError) when a qprogram.vendors entry point's import target raises, or imports
without calling register_vendor_version — i.e. the package is installed but
broken. A vendor that is not installed at all is not this error: discovery reports "no
matching extension".
UnsupportedOperationError ¶
Bases: QProgramError
Platform-side: an operation the backend cannot lower to its hardware.
Typical causes: a vendor op the backend doesn't implement, or a control-flow construct outside the platform's supported feature set.
BusNotAvailableError ¶
Bases: QProgramError
Platform-side: a bus the program references but this backend doesn't expose.
The program is structurally well-formed; the incompatibility is with this particular platform.
Use ValidationError for construction-time bus issues.
WaveformResolutionError ¶
Bases: QProgramError
Platform-side: a string waveform name that reached execution without a concrete waveform.
Resolve names before execution with QProgram.with_waveforms (or
WaveformLibrary.apply); this is raised when one was missed or the
WaveformLibrary used had no entry for it.
CompilationError ¶
Bases: QProgramError
Platform-side: a lowered representation that failed to compile.
Catch-all for backend-internal failures that don't fit the other platform errors (timing constraints, resource over-allocation, code-generation bugs).
HardwareError ¶
Bases: QProgramError
Platform-side: an instrument-level failure during execution.
Covers driver errors, SCPI failures, lost trigger pulses, and anything else surfacing during execution rather than at compile or validate time.