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
| Level | Passes | Description |
|---|---|---|
| 0 | — | No optimization, passthrough |
| 1 | SWAP decomposition, gate cancellation, rotation merging, constant folding | Standard optimization |
| 2 | Level 1 + repeated cancellation, Pauli twirling, dynamical decoupling | Full 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") # 0Rotation 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 gateBasis 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 gatesSWAP to CNOT Decomposition
qc = sf.Circuit(2).swap(0, 1)
result = sf.compile(qc, level=1)
# SWAP → 3 CNOTs + single-qubit gatesPauli 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.
