Openql
QuTech's quantum compiler for realistic hardware-aware circuit compilation
Quick install
pip install openql Background and History
OpenQL is a quantum programming framework and compiler developed at QuTech, the quantum research institute jointly run by Delft University of Technology and TNO (the Netherlands Organisation for Applied Scientific Research). Development began around 2016 under the leadership of Imran Ashraf and Nader Khammassi, who published the foundational paper “OpenQL: A Portable Quantum Programming Framework for Quantum Accelerators” in 2021. QuTech operates some of Europe’s most advanced superconducting and spin-qubit quantum processors, and OpenQL was designed from the start to bridge high-level quantum programs with the low-level control electronics (specifically the eQASM and CC-Light instruction sets used by QuTech’s hardware).
The key architectural idea in OpenQL is a separation between the quantum program (written in Python or C++) and the hardware platform description (a JSON configuration file). The platform description encodes qubit connectivity, native gate set, gate durations, and control electronics constraints. The compiler reads both and produces hardware-native output. This makes OpenQL unusual among quantum frameworks: it targets real, running hardware at a level of detail typically only seen in vendor-specific tools.
OpenQL’s compiler pipeline includes decomposition of high-level gates into native operations, scheduling (both ASAP and ALAP), resource-constraint analysis, and routing with SWAP insertion for non-adjacent qubit interactions. The scheduler is timing-aware, respecting gate durations in nanoseconds rather than treating all gates as unit-time operations. This matters for QuTech’s hardware, where timing is controlled at the nanosecond level through custom control electronics.
The framework has been used extensively in QuTech research on spin qubits (silicon quantum dots), superconducting transmon qubits, and topological qubit development. It is less widely adopted outside European quantum hardware research than Qiskit or Cirq, but it is the primary compilation tool for groups targeting QuTech’s physical processors. OpenQL is open-source under the Apache 2.0 license and is actively maintained, with the Python API (openql) available on PyPI.
Installation
pip install openql
For development builds from source (requires CMake and a C++17 compiler):
git clone https://github.com/QuTech-Delft/OpenQL.git
cd OpenQL
pip install -e .
Key Concepts
OpenQL organises code around three objects:
- Platform: hardware description loaded from a JSON config file (qubit count, gate set, connectivity, timing)
- Program: top-level container for one or more kernels, compiled against a platform
- Kernel: a sequence of quantum gates applied to specific qubits; the basic compilation unit
Platform Setup
import openql as ql
# Load a platform from a JSON hardware configuration file
platform = ql.Platform("my_platform", "path/to/hardware_config.json")
# Built-in example platform (for testing without real hardware config)
platform = ql.Platform("test_platform", "none")
# Inspect platform properties
print(platform.qubit_number) # number of qubits
print(platform.get_ns_per_cycle()) # nanoseconds per timing cycle
Program and Kernel Construction
import openql as ql
platform = ql.Platform("test", "none")
num_qubits = 3
# Create a program
prog = ql.Program("bell_state", platform, num_qubits)
# Create a kernel (a block of gates)
k = ql.Kernel("main_kernel", platform, num_qubits)
# Apply gates
k.hadamard(0) # H on qubit 0
k.cnot(0, 1) # CNOT control=0, target=1
k.measure(0, 0) # measure qubit 0 into classical bit 0
k.measure(1, 1) # measure qubit 1 into classical bit 1
# Add kernel to program
prog.add_kernel(k)
Gate Reference
# Single-qubit gates
k.identity(q) # Identity
k.hadamard(q) # Hadamard
k.x(q) # Pauli-X
k.y(q) # Pauli-Y
k.z(q) # Pauli-Z
k.s(q) # S gate (sqrt(Z))
k.sdag(q) # S-dagger
k.t(q) # T gate
k.tdag(q) # T-dagger
# Rotation gates (angle in radians)
k.rx(q, angle) # Rx(angle)
k.ry(q, angle) # Ry(angle)
k.rz(q, angle) # Rz(angle)
# Two-qubit gates
k.cnot(control, target) # CNOT
k.cz(q1, q2) # Controlled-Z
k.swap(q1, q2) # SWAP
# Three-qubit gates
k.toffoli(q1, q2, q3) # Toffoli / CCX
# Measurement
k.measure(qubit, cbit) # measure qubit into classical bit cbit
Compilation and Output
# Set compiler options
prog.set_option("log_level", "LOG_WARNING") # suppress verbose output
prog.set_option("output_dir", "output") # directory for generated files
# Compile the program
prog.compile()
# After compilation, the output directory contains:
# - <program>.qasm : OpenQASM 2.0 output
# - <program>.sched.qasm : scheduled QASM
# - <program>.dot : dependency graph (Graphviz)
# - <program>.json : intermediate representation
Parameterised Gates and Sweeps
import openql as ql
import numpy as np
platform = ql.Platform("test", "none")
num_qubits = 1
for angle in np.linspace(0, 2 * np.pi, 8):
prog = ql.Program(f"sweep_{angle:.2f}", platform, num_qubits)
k = ql.Kernel(f"k_{angle:.2f}", platform, num_qubits)
k.rx(0, angle)
k.measure(0, 0)
prog.add_kernel(k)
prog.compile()
Hardware Configuration File Structure
The platform JSON specifies the hardware in detail. A minimal example:
{
"eqasm_compiler": "none",
"hardware_settings": {
"qubit_number": 5,
"cycle_time": 20
},
"topology": {
"connectivity": "all-to-all"
},
"gate_decomposition": {},
"instructions": {
"h": {
"matrix": [],
"duration": 40,
"qubits": []
},
"cnot": {
"matrix": [],
"duration": 80,
"qubits": []
},
"measure": {
"matrix": [],
"duration": 300,
"qubits": []
}
}
}
Real QuTech hardware configs include cc_light_instr entries mapping gates to microwave pulse operations.
Scheduler Options
# ASAP: schedule gates as early as possible (default)
prog.set_option("scheduler", "ASAP")
# ALAP: schedule gates as late as possible
prog.set_option("scheduler", "ALAP")
# Resource-constrained scheduling (respects hardware resource limits)
prog.set_option("scheduler_post179", "yes")
Multi-Kernel Programs
platform = ql.Platform("test", "none")
num_qubits = 4
# Kernel 1: initialise Bell pair on qubits 0,1
k1 = ql.Kernel("prepare", platform, num_qubits)
k1.hadamard(0)
k1.cnot(0, 1)
# Kernel 2: apply operations on qubits 2,3
k2 = ql.Kernel("process", platform, num_qubits)
k2.x(2)
k2.cnot(2, 3)
k2.measure(2, 2)
k2.measure(3, 3)
prog = ql.Program("multi_kernel", platform, num_qubits)
prog.add_kernel(k1)
prog.add_kernel(k2)
prog.compile()
Reading QASM Output
import openql as ql
platform = ql.Platform("test", "none")
prog = ql.Program("readout_test", platform, 2)
k = ql.Kernel("k", platform, 2)
k.hadamard(0)
k.cnot(0, 1)
k.measure(0, 0)
k.measure(1, 1)
prog.add_kernel(k)
prog.set_option("output_dir", "out")
prog.compile()
# Read generated QASM
with open("out/readout_test.qasm") as f:
print(f.read())
Key Differences from Other Frameworks
| Feature | OpenQL | Qiskit | Cirq |
|---|---|---|---|
| Primary target | QuTech hardware | IBM hardware | Google hardware |
| Timing model | Nanosecond-accurate | Abstract cycles | Moments |
| Hardware config | JSON platform file | BackendV2 object | Device spec |
| Output format | eQASM, OpenQASM | OpenQASM, QPY | Cirq JSON |
| Compiler model | Explicit passes | Transpiler passes | Moment-based |
| Community size | Small (European research) | Very large | Medium |
Related QuTech Tools
- cQASM: QuTech’s common QASM dialect, used as OpenQL’s intermediate representation
- Quantumsim: QuTech’s density-matrix simulator for noisy circuit simulation, compatible with OpenQL output
- CC-Light: QuTech’s custom quantum control electronics, targeted by OpenQL’s eQASM backend
- QX Simulator: QuTech’s statevector simulator that can execute OpenQL-generated cQASM circuits