Skip to main content
This guide walks through composing a hybrid program from layers and running it with haiqu.flow. If you are new to layers and programs, read the overview first.
1

Authenticate and start an experiment

Log in and initialize an experiment so your circuits and jobs are tracked.
from haiqu.sdk import haiqu

haiqu.login()
haiqu.init("Hybrid program demo")
You’re authenticated and ready to build a program. See the login reference for API-key options.
2

Build a program

A program is an ordered list of layers. Start with an InputLayer, end with a DeviceLayer, and add processing steps in between.
from haiqu.sdk.hybrid import HybridProgram, layers

program = HybridProgram(layers=[
    layers.InputLayer(),
    layers.DeviceLayer(device_id="aer_simulator"),
])
The program is validated as you build it — for example, leaving out the DeviceLayer raises an error.
3

Run the flow

Pass the program and your circuit(s) to haiqu.flow.
from qiskit import QuantumCircuit

qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()

job = haiqu.flow(program, circuits=qc, shots=1000)
haiqu.flow returns a job handle you can track and collect results from.
4

Read the results

Call job.result() to retrieve the output.
job.result()
# [{'00': 0.504, '11': 0.496}]
Results come back as a nested list ordered by circuits → observables → parameters. With a single circuit and no observables, that is a one-element list holding the measurement distribution (bitstrings in Qiskit bit-order).
You’ve built and run your first hybrid program.

Worked examples

Measure observables

Supply observables to get expectation values instead of a distribution. A program that takes observables needs a layer that computes them; the simplest choice is the grouped EstimatorLayer, which evaluates the observables and applies error mitigation. The order of Pauli terms in a string follows the Qiskit reversed-order convention (for example, "IZ" measures qubit 0 in the Z basis).
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from haiqu.sdk.hybrid import HybridProgram, layers

program = HybridProgram(layers=[
    layers.InputLayer(),
    layers.EstimatorLayer(),
    layers.DeviceLayer(device_id="aer_simulator"),
])

qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)

obs = [SparsePauliOp("ZZ"), SparsePauliOp("XY")]
job = haiqu.flow(program, circuits=qc, observables=obs)
job.result()
# [[1.0, 0.018]]
Configure the mitigation through the layer’s flags — for example EstimatorLayer(noise_tailoring=True), or EstimatorLayer(mitigation_enabled=False) to turn it off. For full control, compute the observables by hand instead of using the grouped EstimatorLayer: split them with ObservableSplitLayer and evaluate them with QWCComputeLayer.
program = HybridProgram(layers=[
    layers.InputLayer(),
    layers.ObservableSplitLayer(),
    layers.QWCComputeLayer(),
    layers.DeviceLayer(device_id="aer_simulator"),
])
Building a program only validates its structure. A program that passes observables but has no EstimatorLayer (or ObservableSplitLayerQWCComputeLayer) is rejected when you call haiqu.flow, not when you construct it.

A full pipeline

Compose transpilation, packing, and error mitigation into one program. Each layer applies its step in order before the circuits reach the device:
program = HybridProgram(layers=[
    layers.InputLayer(),
    layers.TranspilationLayer(optimization_level=3),
    layers.PackingLayer(pack_size=2),
    layers.EstimatorLayer(
        mitigation_enabled=True,
        readout_mitigation=True,
        dynamical_decoupling=True,
    ),
    layers.DeviceLayer(device_id="fake_torino"),
])

job = haiqu.flow(program, circuits=qc, observables=obs)

Sweep over parameters

Pass parameters to run a parametrized circuit at several values. The extra dimension appears last in the result ordering:
from qiskit import QuantumCircuit
from qiskit.circuit import Parameter
from haiqu.sdk.hybrid import HybridProgram, layers

program = HybridProgram(layers=[
    layers.InputLayer(),
    layers.DeviceLayer(device_id="aer_simulator"),
])

theta = Parameter("θ")
qc = QuantumCircuit(2)
qc.ry(theta, 0)
qc.cx(0, 1)
qc.measure_all()

job = haiqu.flow(program, circuits=qc, parameters=[[0.5], [1.0]])
job.result()
# [[{'00': 0.934, '11': 0.066}, {'00': 0.802, '11': 0.198}]]
Estimate cost before running. Pass dry_run=True to stop just before device execution and estimate the QPU cost. job.result() is then empty; read job.estimated_qpu_cost instead.
job = haiqu.flow(program, circuits=qc, dry_run=True)
job.estimated_qpu_cost
See the haiqu.flow reference for the full signature and every supported combination of circuits, parameters, and observables, and the hybrid program reference for all layers and their parameters.