Superfermion logo

Getting Started

Install Superfermion and run your first quantum circuit in 5 minutes.

Welcome. This page gets you from zero to a running quantum circuit with gradients.

1. Install

pip install superfermion

That's it. Python 3.10–3.13 on Linux, macOS, or Windows. Pre-built wheels for all three platforms — no Rust toolchain required.

Optional extras

pip install "superfermion[all]"        # everything
pip install "superfermion[qml]"        # JAX + Flax + Optax
pip install "superfermion[chemistry]"  # PySCF for molecular simulation
pip install "superfermion[qpu]"        # IBM + AWS Braket SDKs
pip install "superfermion[ml]"         # PyTorch + TensorFlow + Cirq

2. Verify

import superfermion as sf
print(sf.__version__)  # e.g. '0.1.5'

3. Build a circuit

import superfermion as sf
import sys

# Unicode diagrams on Windows consoles: reconfigure stdout first
sys.stdout.reconfigure(encoding="utf-8", errors="replace")

# Bell state: entangle two qubits
qc = sf.Circuit(2).h(0).cx(0, 1)
print(qc.draw())
# q0: ── H ── ● ──
# q1: ────── X ──

Every gate method returns the circuit — chain as many as you want.

4. Run it

result = sf.run(qc, shots=1024)
print(result.counts)  # {'00': ~512, '11': ~512}

sf.run() works everywhere — local CPU, GPU, IBM Quantum, IonQ. Same API.

5. Get the quantum state

state = sf.simulate(qc)       # returns sf.State (Rust-native)
print(state.numpy())           # [0.707+0j, 0, 0, 0.707+0j]
print(state.entropy())         # 0.0
print(state.purity())          # 1.0

sf.State lives in Rust. All methods — expectation(), grad(), sample() — dispatch to compiled Rust.

6. Compute an expectation value

from superfermion import Hamiltonian, PauliString

H = Hamiltonian([PauliString("ZZ", coeff=1.0)])
energy = H.expectation(state.numpy())
print(energy)  # 1.0

# Or pass the Rust observable format directly to State:
# state.expectation([([3, 3], 1.0, 0.0)])   # ZZ: 0=I 1=X 2=Y 3=Z (LSB-first)

7. Take a gradient

theta = sf.param("theta")
qc = sf.Circuit(1).ry(theta, 0)

state = sf.simulate(qc, params={"theta": 0.5})
obs = [([3], 1.0, 0.0)]      # Pauli Z
dag = qc.to_ir()  # keep parameters symbolic — .bind() would erase them
grads = state.grad(obs, dag, {"theta": 0.5})

print(grads)  # {"theta": -0.479...}

The adjoint method computes gradients in one backward pass regardless of parameter count. On 20-qubit SU(2) circuits it is ~77x faster than parameter-shift at 40 parameters and ~73x at 80, because parameter-shift needs two circuit runs per parameter. Full grid and correctness checks: gradient benchmarks.

8. Try a different simulation method

# MPS — for weakly entangled circuits, scales to 200+ qubits
qc_mps = sf.Circuit(8).h(0)
for i in range(7):
    qc_mps = qc_mps.cnot(i, i + 1)
result = sf.run(qc_mps, method="mps", bond_dim=64, shots=1024)
print(result.counts)  # {'00000000': ~512, '11111111': ~512}

# Stabilizer — for Clifford-only circuits, scales to 1000+ qubits
qc_stab = sf.Circuit(100).h(0)
for i in range(99):
    qc_stab = qc_stab.cnot(i, i + 1)
result = sf.run(qc_stab, method="stabilizer", shots=1024)
print(list(result.counts.items())[:2])

# Density matrix — for noisy simulation
qc_noisy = sf.Circuit(1).x(0)
noise = sf.NoiseModel().add_depolarizing(0.01)
result = sf.run(qc_noisy, method="density_matrix", noise_model=noise, shots=0)
print(f"purity = {result.metadata['purity']:.4f}")

Where next

Now that you've run your first circuit, dive into the guides:

From source (development)

git clone https://github.com/Catstate101/superfermion.git
cd superfermion
pip install maturin
cd crates/sf-bindings && maturin develop --release && cd ../..
pip install -e ".[dev]"

On this page