b01t programs are hardware-agnostic until you compile them to a backend. The Qiskit backend takes a b01t IRProgram and produces a Qiskit QuantumCircuit that you can simulate locally, transpile, or submit to real quantum hardware. For programs built with @coherent, there is a two-step path: first lower the ExactProgram to IRProgram, then emit to QuantumCircuit. For @parametric and @adaptive programs, skip the first step — they already produce IRProgram directly from .build().
Prerequisites
Install Qiskit before using the backend:
If Qiskit is not installed, calling QiskitBackend().emit() raises:
DSLValidationError: Qiskit is required for backend emission.
Install with 'pip install qiskit'.
Compiling a @coherent program
@coherent programs produce an ExactProgram. Before passing to the Qiskit backend, lower it to IRProgram with lower_exact_program():
from b01t import coherent, QReg, cx, z, Certification
from b01t import ancilla, compute, phase, uncompute
from b01t import lower_exact_program, QiskitBackend
@coherent
def oracle(sys: QReg):
with ancilla(1) as anc:
compute(lambda: cx(sys[0], anc[0]))
phase(lambda: z(anc[0]))
uncompute()
# Step 1: build the certified ExactProgram
prog = oracle.build_exact(("sys", 1))
assert prog.certification == Certification.SAFE
# Step 2: lower to broad IR
ir = lower_exact_program(prog)
# Step 3: emit to Qiskit QuantumCircuit
circuit = QiskitBackend().emit(ir)
print(circuit)
lower_exact_program() handles ancilla coalescing — sequential ancilla registers share physical qubits to minimize qubit count. The returned IRProgram carries is_safe=True from the original certification.
Do not run is_safe_program() on an IR produced by lower_exact_program(). The broad-path safety checker does not understand the APPLY-pattern ancilla blocks that lowering may produce, and will give a false negative. Safety was already proven by the exact path.
Compiling @parametric and @adaptive programs
@parametric and @adaptive programs produce IRProgram directly from .build(), so you can pass them straight to the backend:
from b01t import adaptive, QReg, h, measure_all, QiskitBackend
from b01t.kit import repeat
@adaptive
def grover_search(sys: QReg):
for q in sys:
h(q)
# ... oracle and diffusion iterations ...
return measure_all(sys)
# Build IRProgram directly
ir = grover_search.build(("sys", 2))
# Emit to Qiskit QuantumCircuit
circuit = QiskitBackend().emit(ir)
print(circuit)
Complete example
Here is a full walkthrough from definition to printed circuit, using the Bernstein-Vazirani oracle as an example:
from b01t import coherent, QReg, cx
from b01t import lower_exact_program, QiskitBackend, Certification
@coherent
def bv_oracle(inp: QReg, tgt: QReg):
"""Oracle for hidden string [1, 0, 1]: CX on bits 0 and 2."""
cx(inp[0], tgt[0])
cx(inp[2], tgt[0])
# Build the certified program
prog = bv_oracle.build_exact(("inp", 3), ("tgt", 1))
assert prog.certification == Certification.SAFE
# Lower and emit
ir = lower_exact_program(prog)
circuit = QiskitBackend().emit(ir)
# Inspect
print(f"Qubits: {circuit.num_qubits}")
print(f"Gates: {circuit.size()}")
print(circuit.draw())
Supported gates
The Qiskit backend maps b01t gate names to Qiskit circuit methods as follows:
| b01t gate | Qiskit operation | Arity |
|---|
x | circuit.x | 1 |
h | circuit.h | 1 |
z | circuit.z | 1 |
s | circuit.s | 1 |
sdg | circuit.sdg | 1 |
t | circuit.t | 1 |
tdg | circuit.tdg | 1 |
rx(θ) | circuit.rx | 1 |
ry(θ) | circuit.ry | 1 |
rz(θ) | circuit.rz | 1 |
cx | circuit.cx | 2 |
cz | circuit.cz | 2 |
swap | circuit.swap | 2 |
cry(θ) | circuit.cry | 2 |
crz(θ) | circuit.crz | 2 |
ccx | circuit.ccx | 3 |
ccz | circuit.ccz (or H+CCX+H decomposition on older Qiskit) | 3 |
mcx | circuit.mcx | n |
mcz | H + circuit.mcx + H decomposition | n |
Measurement operations (measure, measure_all) add classical registers automatically. Classical if_then is not supported in the current backend — attempting to emit an IfOp raises DSLValidationError.
Running the circuit
Once you have a QuantumCircuit, use any Qiskit-compatible runner:
from qiskit import transpile
from qiskit_aer import AerSimulator
simulator = AerSimulator()
transpiled = transpile(circuit, simulator)
job = simulator.run(transpiled, shots=1024)
result = job.result()
counts = result.get_counts()
print(counts)
Or submit to IBM Quantum hardware using qiskit-ibm-runtime. The QuantumCircuit produced by b01t is a standard Qiskit circuit — any Qiskit tutorial or documentation applies directly.
Use circuit.draw("mpl") to render a matplotlib diagram of your circuit, or circuit.draw("text") for a terminal-friendly ASCII diagram.