Superfermion logo
Guides

Hardware Compilation

Gate decomposition, optimization, qubit routing, and noise suppression for hardware targets.

Superfermion's compiler transforms abstract circuits into hardware-executable form. It handles basis translation, gate optimization, qubit routing, and noise suppression — all in Rust.

Quick Start

import superfermion as sf
from superfermion.compiler.specs import HardwareSpec

qc = sf.Circuit(5).h(0).cnot(0, 3).cnot(1, 4).swap(2, 3)

# Optimization only
optimized = sf.compile(qc, level=1)
print(f"Before: {qc.gate_count} gates, After: {optimized.gate_count} gates")

# Compile for a specific device
ibm_target = HardwareSpec(
    name="ibm_device",
    n_qubits=5,
    native_gates=["rz", "sx", "x", "cx"],
    coupling_map=[
        (0, 1), (1, 0), (1, 2), (2, 1), (2, 3),
        (3, 2), (3, 4), (4, 3),
    ],
)
compiled = sf.compile(qc, level=2, target=ibm_target)

Optimization Levels

LevelPassesDescription
0—No optimization, passthrough
1SWAP decomposition, gate cancellation, rotation merging, constant foldingStandard optimization
2Level 1 + repeated cancellation, Pauli twirling, dynamical decouplingFull optimization (requires target)

Compilation preserves the input unitary (up to a global phase) at every level — checkable with to_unitary() or the built-in simulator. For example, a single cz(0, 1) compiles to an equivalent CNOT + U-gate decomposition with fidelity 1.0.

Compiler Passes

Gate Cancellation

Adjacent inverse gates cancel: HH → ∅, CNOT CNOT → ∅, CZ CZ → ∅.

import superfermion as sf

# Gate cancellation is part of level-1 optimization
qc = sf.Circuit(2).h(0).h(0).cnot(0, 1).cnot(0, 1)
print(f"Before: {qc.gate_count} gates")

result = sf.compile(qc, level=1)
print(f"After cancellation: {result.gate_count} gates")  # 0

Rotation Merging

Sequential rotations around the same axis merge: RZ(a) RZ(b) → RZ(a+b).

from superfermion.compiler import apply_noise_suppression

qc = sf.Circuit(1).rz(0.3, 0).rz(0.7, 0).rz(-0.2, 0)
result = sf.compile(qc, level=1)
print(f"After merging: {result.gate_count} gates")  # ~1 gate

Basis Translation

Decompose gates into a target basis set:

from superfermion.compiler import BasisTranslationPass

# Decompose H into RZ+RX basis
qc = sf.Circuit(1).h(0)
result = BasisTranslationPass(["RZ", "RX"]).run(qc)
print(f"H decomposed to: {result.gate_count} native gates")  # 3 gates

SWAP to CNOT Decomposition

qc = sf.Circuit(2).swap(0, 1)
result = sf.compile(qc, level=1)
# SWAP → 3 CNOTs + single-qubit gates

Pauli Twirling

Convert coherent noise into stochastic Pauli noise:

from superfermion.compiler import PauliTwirlingPass

qc = sf.Circuit(2).cnot(0, 1)
twirled = PauliTwirlingPass().run(qc)

Dynamical Decoupling

Insert XY4 / CPMG sequences to suppress decoherence:

from superfermion.compiler import DynamicalDecouplingPass

dd_pass = DynamicalDecouplingPass(sequence="xy4")
result = dd_pass.run(qc)

Qubit Routing

SABRE routing maps logical qubits to physical hardware topology:

from superfermion.compiler.specs import HardwareSpec

# Linear topology
linear = HardwareSpec(
    name="linear_5",
    n_qubits=5,
    native_gates=["rz", "sx", "x", "cx"],
    coupling_map=[(i, i+1) for i in range(4)] + [(i+1, i) for i in range(4)],
)

# Route onto the linear topology
qc = sf.Circuit(5).h(0).cnot(0, 4).cnot(1, 3).swap(2, 3)
compiled = sf.compile(qc, level=2, target=linear)
print(f"SWAPs inserted for routing: {compiled.gate_count - qc.gate_count}")

Built-in Device Specifications

from superfermion.compiler.specs import get_spec, HardwareSpec

# IBM Eagle (127 qubits, heavy-hex)
ibm = get_spec("ibm_eagle")
print(f"ibm_eagle: {ibm.n_qubits} qubits, {len(ibm.coupling_map)} couplings")

# Rigetti Ankaa (84 qubits, grid)
rigetti = get_spec("rigetti_ankaa")
print(f"rigetti_ankaa: {rigetti.n_qubits} qubits, {len(rigetti.coupling_map)} couplings")

# Custom topology
custom = HardwareSpec(
    name="my_chip",
    n_qubits=4,
    native_gates=["rz", "sx", "x", "cz", "iswap"],
    coupling_map=[(0, 1), (1, 2), (2, 3)],
)
print(f"custom: {custom.n_qubits} qubits, {len(custom.coupling_map)} couplings")

Noise Model

Attach a noise model to simulate realistic hardware:

import superfermion as sf

qc = sf.Circuit(1).x(0)

noise = sf.NoiseModel() \
    .add_depolarizing(0.001) \
    .add_amplitude_damping(0.002) \
    .add_phase_damping(0.001)

result = sf.run(qc, method="density_matrix", noise_model=noise, shots=0)
print(f"purity = {result.metadata['purity']:.4f}")

Auto-Compilation via sf.run()

qc = sf.Circuit(5).h(0).cnot(0, 1)

# Let sf.run() handle compilation automatically
result = sf.run(qc, target="linear_5", shots=4096)
print(result.counts)  # {'00000': ~2048, '00011': ~2048}

Internally, sf.run() with a target= string resolves the device spec, compiles the circuit at level 2, and executes.

On this page