python v0.11.x

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

FeatureOpenQLQiskitCirq
Primary targetQuTech hardwareIBM hardwareGoogle hardware
Timing modelNanosecond-accurateAbstract cyclesMoments
Hardware configJSON platform fileBackendV2 objectDevice spec
Output formateQASM, OpenQASMOpenQASM, QPYCirq JSON
Compiler modelExplicit passesTranspiler passesMoment-based
Community sizeSmall (European research)Very largeMedium
  • 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