Skip to content

Sweep a parameter

examples/parameter_sweep.py

Evaluate one circuit at many parameter values, as one job. A variational algorithm evaluates the same circuit at many points, and submitting one job per point queues, schedules and accounts each separately. A PUB carries the whole sweep — one circuit, many rows of parameter values — and the platform runs it as one job with one result.

# `input float[64] theta;` is the whole binding mechanism. `include "stdgates.inc";` is
# required for `ry` to be defined — OpenQASM 3 declares no gates of its own.
ROTATION = """
OPENQASM 3.0;
include "stdgates.inc";
input float[64] theta;
qubit[1] q;
ry(theta) q[0];
"""

The binding is expressed in the circuit itself. An OpenQASM 3 input declaration names a value the program leaves free, and the importer turns it into a bound parameter. Everything about the sweep lives in the circuit text and the parameter rows, which is why a sweep needs no vendor-specific side channel.

Z = Observable(num_qubits=1, terms=(PauliTerm(pauli="Z", qubits=(0,), coefficient=1.0),))
# One row per run, one value per `input`. A published cap bounds how many rows a PUB may
# carry, and a sweep past it is rejected at submit rather than discovered mid-run.
ANGLES = [0.0, math.pi / 2, math.pi]

Rotating by theta about the Y axis walks the state from |0> to |1>. Read with Z, that traces a cosine: +1 at 0, 0 at π/2, −1 at π — a curve you can check by eye.

job = client.jobs.submit(
backend_id=backend_id,
circuit=ROTATION,
observables=[Z],
parameter_values=[[angle] for angle in ANGLES],
shots=SHOTS,
name="ry sweep",
)

parameter_values supplies one row per run, each row holding one value per input in declaration order. This circuit has one input, so each row is a single-element list.

# One expectation value per parameter row, in the order the rows were submitted.
values = client.jobs.result(job.id).values or ()
if len(values) != len(ANGLES):
print(f"expected {len(ANGLES)} values, got {len(values)}", file=sys.stderr)
return 1
print()
for angle, value in zip(ANGLES, values, strict=True):
bar = round(20 * (value + 1))
print(
f" theta={angle:5.3f} <Z>={value:+.4f} (cos={math.cos(angle):+.1f}) {'█' * bar}"
)
return 0

The result carries one expectation value per parameter row, in submission order, so the loop pairs each angle with its value directly.

Terminal window
QUBITRA_API_KEY=qpk_... python examples/parameter_sweep.py

Output from a live run, trimmed to the values:

theta=0.000 <Z>=+1.0000 (cos=+1.0) ████████████████████████████████████████
theta=1.571 <Z>=+0.0000 (cos=+0.0) ████████████████████
theta=3.142 <Z>=-1.0000 (cos=-1.0)

The three values trace the cosine — approximately [1.0, 0.0, -1.0] at angles [0, π/2, π] — and the whole sweep travelled as one PUB in one job.