sfSuperfermion docs
Reference

Superfermion Architecture

Superfermion is built on a single architectural axiom:

Superfermion Architecture

Design Philosophy: Hexagonal Architecture (Ports & Adapters)

Superfermion is built on a single architectural axiom:

The domain core defines the contracts; the outside world conforms to them — never the reverse.

This is Hexagonal Architecture (Ports & Adapters), coined by Alistair Cockburn. The core is sovereign — nothing in it imports, references, or depends on any external vendor, framework, or cloud provider. All external integrations plug in through well-defined protocols. The result is a kernel architecture: a small, stable, self-contained core surrounded by a growing ecosystem of adapters.

The Three Reinforcing Principles

1. Dependency Inversion (the "D" in SOLID)

High-level policy (sf.run, sf.compile) never imports low-level detail (IBM API calls, IonQ REST endpoints, Qiskit internals). Both depend on abstract Protocols. Dependencies always point inward toward sf.State.

Applications → Runner → Circuit → Rust IR → sf.State
     ↑              ↑          ↑
  Adapters       Adapters   Adapters
(algorithms)  (providers)  (bridges)

2. Open/Closed Principle

Superfermion is open for extension (add a new provider, simulation backend, bridge, or ML layer) but closed for modification (the core never changes when you add a new vendor). Adding GoogleDevice means writing one adapter file — zero lines touched in core.

3. Strategy Pattern at the Architecture Level

Every major subsystem is a family of interchangeable strategies behind a stable interface:

SubsystemInterfaceStrategies
SimulationQuantumStateImpl traitstatevector, MPS, stabilizer, density_matrix, GPU
DevicesDeviceExecutor protocolRustDevice, IBMDevice, IonQDevice, BraketDevice
ProvidersProvider protocolIBM, IonQ, Braket, OpenQuantum, Catstate
CompilationCompilerPass baseSwapDecomp, GateCancel, RotMerge, SABRE, ...
ML bridgesframework-native gradientJAX custom_vjp, PyTorch autograd, TF custom_gradient
TrackingTrackerProtocolLocalTracker, CatstateTracker

Ports (Abstractions SF Owns)

These are the contracts that define the boundary between core and outside world:

PortPurpose
DeviceExecutorAnything that can execute a circuit
ProviderAnything that can submit a job to hardware
TrackerProtocolAnything that can observe experiment lifecycle
HardwareSpecAnything that describes a target topology and basis gate set
NoiseModelAnything that describes quantum channel noise
QuantumStateImplRust trait — anything that is a quantum state

Adapters (Implementations That Plug In)

Adapters conform to SF's ports — SF never conforms to them:

AdapterPort it implements
IBMDevice, IonQDevice, BraketDeviceDeviceExecutor / Provider
from_qiskit(), from_cirq(), from_pennylane()Bridge adapters translating foreign circuits into SF IR
TorchQuantumLayer, TFQuantumLayer, QuantumLayerML framework adapters around sf.State.grad()
LocalTracker, CatstateTrackerTrackerProtocol
RustDevice (statevector, MPS, stabilizer, DM, GPU)DeviceExecutor + QuantumStateImpl

The Kernel Analogy

SystemKernelAdapters
Linuxkernel syscall ABIdevice drivers
PyTorchTensor + autogradbackends (CPU, CUDA, XLA, MPS)
KubernetesAPI server + schedulerCNI, CSI, CRI plugins
Superfermionsf.State + sf-ir + Circuit + compile + runproviders, bridges, ML layers

Design Principle: Python is the API, Rust Does the Work

Superfermion follows a strict two-layer architecture. Python owns the user-facing API surface — circuit construction, configuration, experiment tracking, ML framework bridges. Rust owns all performance-critical computation — statevector simulation, gate application, sampling, gradients, tensor operations.

JAX is reserved for a single purpose: thin custom-gradient bridges in superfermion.nn that connect sf.State.grad() to framework-native autograd (JAX custom_vjp, PyTorch autograd.Function, TF custom_gradient). It is never used for simulation, linear algebra, or observable computation.


Core Concepts

sf.Circuit

The fluent circuit builder. Immutable after construction; returns new circuit on each gate call.

qc = sf.Circuit(2).h(0).cx(0, 1)

sf.State

A Rust-native quantum state handle exposed via PyO3. This is the first-class object for all post-simulation operations:

MethodDescription
state.expectation(obs)Pauli expectation value
state.grad(obs, dag, params)Parameter-shift gradient
state.sample(shots)Measurement sampling
state.numpy()Export as NumPy array
state.entropy()Von Neumann entropy
state.purity()State purity
state.fidelity(other)Fidelity with another state
state.partial_trace(qubits)Partial trace
state.qfim(dag, params)Quantum Fisher Information Matrix

All methods dispatch to Rust. Python never touches raw state data.

sf.run() and sf.simulate()

Two entry points for circuit execution:

result = sf.run(circuit, device="cpu", method="statevector", shots=1024)
# result.counts, result.state, result.metadata

state = sf.simulate(circuit, device="cpu", method="statevector")
# Returns sf.State directly (shots=0 implied)

sf.run() returns a RunResult with counts, state, and metadata. sf.simulate() is shorthand for zero-shot execution that returns the sf.State directly.


Module Map

superfermion/
├── __init__.py          Circuit, run, simulate, State, MethodError, ...
├── circuit.py           Circuit builder, parameter binding
├── runner.py            sf.run() / sf.simulate() dispatch
├── results.py           RunResult dataclass
├── parameters.py        param() symbolic parameter factory
├── _sf_core             Rust PyO3 extension (State, QuantumDAG, ...)

├── devices/
│   ├── rust_device.py   RustDevice — routes to Rust simulation methods
│   ├── ibm.py           IBM Quantum provider adapter
│   ├── ionq.py          IonQ provider adapter
│   ├── braket.py        AWS Braket provider adapter
│   └── openquantum.py   OpenQuantum provider adapter

├── observables/
│   └── core.py          PauliString, SparsePauliOp, Hamiltonian, expval

├── noise/
│   └── __init__.py      NoiseModel, noise channels (depolarizing, damping, ...)

├── nn/
│   ├── quantum_layer.py Flax QuantumLayer (jax.custom_vjp → sf.State.grad)
│   ├── torch_layer.py   TorchQuantumLayer (autograd.Function → sf.State.grad)
│   └── tf_layer.py      TFQuantumLayer (tf.custom_gradient → sf.State.grad)

├── qml/
│   ├── gradient/
│   │   ├── adjoint.py       Adjoint differentiation
│   │   ├── parameter_shift.py Parameter-shift rule
│   │   ├── spsa.py          SPSA stochastic gradient
│   │   ├── qng.py           Quantum Natural Gradient
│   │   ├── riemannian.py    Riemannian gradient (natural gradient via QFIM)
│   │   └── stochastic_reconfig.py  Stochastic Reconfiguration
│   └── encoding/        Angle, amplitude, IQP encoding

├── algorithms/
│   ├── vqe.py           Variational Quantum Eigensolver
│   ├── qaoa.py          QAOA
│   ├── qsvm.py          Quantum SVM
│   ├── qrl.py           Quantum Reinforcement Learning
│   └── qbm.py           Quantum Boltzmann Machine

├── chemistry/           JW/BK transforms, UCCSD, PySCF bridge
├── qec/                 10 codes + 4 decoders
├── compiler/            Gate decomposition, rotation merge, SABRE routing
├── bridge/              Qiskit, Cirq, PennyLane, QASM interop
├── mitigation/          ZNE, readout error mitigation
├── pulse/               Waveforms, schedules, gate calibration
├── viz/                 Circuit drawing, Bloch sphere, histograms
└── experiment/          Experiment tracking protocols

Rust Workspace

crates/
├── sf-ir/           QuantumDAG, QuantumStateImpl trait, simulation engines
│                    (statevector, MPS, stabilizer, density matrix)
├── sf-compiler/     Pass manager, gate cancellation, rotation merge, twirl
├── sf-router/       SABRE routing, hardware topology
├── sf-pulse/        Waveforms, schedules
├── sf-qec/          Stabilizer codes, MWPM/UnionFind decoders
├── sf-gpu/          CUDA statevector simulation (cudarc, sm_75+)
└── sf-bindings/     PyO3 FFI — exposes State, QuantumDAG, compile, etc.

QuantumStateImpl Trait

The Rust trait that all simulation methods implement:

pub trait QuantumStateImpl {
    fn expectation(&self, observable: &[(Vec<u8>, f64, f64)]) -> (f64, f64);
    fn sample(&self, shots: usize) -> Vec<u64>;
    fn numpy(&self) -> Vec<Complex64>;
    fn entropy(&self) -> f64;
    fn purity(&self) -> f64;
    fn fidelity(&self, other: &Self) -> f64;
    fn partial_trace(&self, qubits: &[usize]) -> Vec<Complex64>;
}

The Python sf.State wraps whichever QuantumStateImpl the simulation produced, dispatching method calls to the Rust implementation.


Execution Flow

sf.run(circuit, device="cpu", method="statevector", shots=1024)


runner.py — resolve device, bind parameters, optional compile


RustDevice.execute(dag, method, shots, **kwargs)

    ├── statevector  → dag.simulate()           [Rust, Rayon-parallel]
    ├── mps          → dag.simulate_mps()       [Rust, faer QR/SVD]
    ├── stabilizer   → dag.simulate_stabilizer() [Rust, word-packed tableau]
    ├── density_matrix → dag.simulate_dm_noisy() [Rust, Kraus channels]
    └── gpu          → dag.simulate_gpu()       [CUDA via cudarc]


sf.State (Rust-native handle)


RunResult(counts, state, metadata)

Simulation Methods

MethodEngineMax QubitsBest For
statevectorRust (Rayon)~25 CPU, ~30 GPUExact sim, gradients, QML
mpsRust (faer)200+Low-entanglement circuits
stabilizerRust (word-packed)~1000Clifford circuits, QEC
density_matrixRust (Kraus)~12Noisy simulation

ML Framework Integration

Each ML layer is ~20 lines wrapping sf.State.grad() with the framework's custom gradient mechanism:

FrameworkLayerMechanism
Flax (JAX)QuantumLayerjax.custom_vjp
PyTorchTorchQuantumLayertorch.autograd.Function
TensorFlowTFQuantumLayertf.custom_gradient

All three call sf.State.grad() (Rust parameter-shift) in the backward pass.


Provider / Device Model

Superfermion defines protocols; platform layers implement them:

ProtocolPurpose
DeviceExecutorAnything that can execute a circuit
DeviceCapabilitiesWhat a device supports (methods, max qubits)
ProviderSupplies devices (IBM, IonQ, Catstate, ...)
JobAsync result handle for QPU execution
AlgorithmConfigurable algorithm (VQE, QAOA, ...)
AlgorithmResultAlgorithm output

Local simulation uses RustDevice (implements DeviceExecutor). Cloud providers implement the same protocol.

On this page