sfSuperfermion docs
Reference

API Reference

Complete API reference: every class, method, and parameter in Superfermion v0.1.0.

Complete API reference. All code examples verified against superfermion==0.1.0.


First-Class Citizens

sf.Circuit(n)          Build circuits
sf.run(circuit, ...)   Execute anywhere
sf.simulate(circuit)   Execute and return sf.State directly
sf.compile(circuit)    Compile for hardware
sf.State               Rust-native quantum state handle
sf.experiment(name)    Scoped experiment tracking
sf.param(name)         Symbolic parameters
sf.Hamiltonian(...)    Observable construction
sf.PauliString(str)    Single Pauli term
sf.NoiseModel()        Noise model for density matrix sim

1. Circuit Construction

sf.Circuit is a fluent builder. Each gate call returns the circuit for chaining.

import superfermion as sf

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

print(qc.n_qubits)     # 2
print(qc.gate_count)   # 2
print(qc.depth)        # 2
print(qc.draw())       # ASCII diagram

Available Gates

CategoryGates
1Q Cliffordh, x, y, z, s, sdg, t, tdg, sx, id
1Q Parametricrx(theta, q), ry(theta, q), rz(theta, q), p(phi, q), u(theta, phi, lam, q)
2Qcnot(c, t) / cx(c, t), cz, cy, swap, iswap, ecr
2Q Parametriccp, cu, rzz, rxx, ryy
3Qccx / toffoli, cswap / fredkin
Customunitary(matrix, *qubits)
Specialmeasure(q), measure_all(), barrier(*qubits), reset(q)

Circuit Properties

PropertyTypeDescription
n_qubitsintNumber of qubits
gate_countintTotal gate count
depthintCircuit depth
n_parametersintNumber of unbound parameters
parameterslist[str]Parameter names
n_cbitsintNumber of classical bits

Circuit Methods

MethodReturnsDescription
.bind(params)CircuitBind symbolic parameters
.to_ir()QuantumDAGExport to Rust IR
.to_qasm3()strExport to OpenQASM 3.0
.to_json()strExport to JSON
.to_unitary()ndarrayUnitary matrix (exponential cost)
.to_gate_list()listExport as gate list
.draw()strASCII visualization
.count_ops()dictCount gates by type
.from_json(s)CircuitImport from JSON (classmethod)

2. Execution — sf.run()

sf.run() is the single universal entry point. It accepts any device (local or QPU) and any simulation method.

import superfermion as sf

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

result = sf.run(qc, shots=4096)
print(result.counts)        # {'00': 2048, '11': 2048}
print(result.state)         # sf.State (Rust handle)
print(result.shots)         # 4096

Parameters

ParameterTypeDefaultDescription
circuitCircuitrequiredThe circuit to execute
devicestr or DeviceExecutor"cpu"Where to run
shotsint1000Number of measurement shots
methodstr or None"statevector"Simulation algorithm
targetstr or NoneNoneHardware target for auto-compilation
trackerTrackerProtocol or NoneNoneExplicit tracker
paramsdict or NoneNoneAuto-bind symbolic parameters

Device Resolution

# Local simulation (builtin shorthands)
sf.run(qc, device="cpu")                  # Rust CPU (Rayon + AVX)
sf.run(qc, device="gpu")                  # Rust GPU (CUDA)

# QPU via provider objects (protocol-based, no registries)
from superfermion.devices.ibm import IBMDevice
ibm = IBMDevice(token="...")
sf.run(qc, device=ibm("ibm_brisbane"))    # explicit object, not magic string

from superfermion.devices.ionq import IonQDevice
ionq = IonQDevice(api_key="...")
sf.run(qc, device=ionq("aria-1"))         # same pattern, different provider

3. Exact Simulation — sf.simulate()

Shortcut that returns sf.State directly instead of RunResult.

state = sf.simulate(qc)

# sf.State is Rust-native — all methods dispatch to Rust
print(state.n_qubits)           # 2
print(state.entropy())          # Von Neumann entropy
print(state.purity())           # state purity
print(state.fidelity(state))    # 1.0

sv = state.numpy()              # export to numpy
samples = state.sample(1000)    # fast Rust sampling

sf.State Methods

MethodReturnsDescription
state.expectation(obs)floatExact Pauli expectation value
state.grad(obs, dag, params)dictAdjoint/parameter-shift gradient
state.sample(shots)list[int]Measurement sampling
state.numpy()ndarrayExport to numpy array
state.entropy()floatVon Neumann entropy
state.purity()floatState purity
state.fidelity(other)floatFidelity with another state
state.partial_trace(qubits)ndarrayPartial trace over qubits
state.probabilities()ndarrayMeasurement outcome probabilities
state.qfim(dag, params)ndarrayQuantum Fisher Information Matrix
state.shapetupleShape of the statevector
state.n_qubitsintNumber of qubits
state.methodstrSimulation method used
state.devicestrDevice used
state.from_numpy(arr)StateCreate State from numpy (classmethod)

RunResult Properties

PropertyTypeDescription
result.countsdict[str, int]Measurement counts
result.stateStateRust quantum state
result.shotsintNumber of shots
result.circuitCircuitOriginal circuit
result.metadataobjectExecution metadata
result.probabilitiesdict[str, float]Outcome probabilities
result.statevectorndarrayState amplitudes

RunResult Methods

MethodDescription
result.expectation(obs)Compute expectation from counts
result.grad(obs, dag, params)Compute gradient from state
result.plot(kind)Plot results ("histogram", etc.)
result.to_dict()Serialize to dict
result.from_dict(d)Import from dict (classmethod)
result.get_probabilities()Get probability distribution
result.probabilities_array()Get probabilities as ndarray

4. Simulation Methods

All methods use the same sf.run() / sf.simulate() API. Only the method= parameter changes.

Statevector (default)

Exact simulation. Supports all sf.State methods including gradients. Up to ~25 qubits on CPU, ~30 on GPU.

result = sf.run(qc, method="statevector", shots=1000)

MPS (Tensor Network)

For weakly entangled circuits. Scales to 200+ qubits.

result = sf.run(qc, method="mps", bond_dim=64, shots=1000)

Stabilizer

For Clifford-only circuits. Polynomial time, scales to ~1000 qubits.

clifford = sf.Circuit(100).h(0)
for i in range(99):
    clifford = clifford.cnot(i, i + 1)

result = sf.run(clifford, method="stabilizer", shots=1000)

Density Matrix

For noisy simulation with Kraus channels.

noise = sf.NoiseModel().add_depolarizing(0.01)
result = sf.run(qc, method="density_matrix", noise_model=noise, shots=0)

5. Compilation — sf.compile()

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 (no hardware target)
optimized = sf.compile(qc, level=1)

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

Optimization Levels

LevelWhat it does
0No optimization (passthrough)
1SWAP decomposition, gate cancellation, rotation merging, constant folding
2Level 1 + repeated cancellation, Pauli twirling, dynamical decoupling (if target set)

Available Compiler Passes

from superfermion.compiler import (
    PassManager,
    BasisTranslationPass,
    PauliTwirlingPass,
    DynamicalDecouplingPass,
    UnitaryDecompositionPass,
    SchedulingPass,
    apply_dynamical_decoupling,
    apply_noise_suppression,
    schedule_circuit,
)

See the Compiler Guide for detailed pass documentation.


6. Observables and Expectation Values

import superfermion as sf

# Build observables
H = sf.Hamiltonian([
    sf.PauliString("ZZ", coeff=0.5),
    sf.PauliString("XI", coeff=0.3),
    sf.PauliString("IZ", coeff=0.2),
])

# Exact expectation from state
state = sf.simulate(sf.Circuit(2).h(0).cnot(0, 1))
energy = state.expectation(H.to_sparse_list())

# Estimate from measurement counts
result = sf.run(sf.Circuit(2).h(0).cnot(0, 1), shots=10000)
energy = result.expectation(H.to_sparse_list())

Observable API

# PauliString
p = sf.PauliString("XZ", coeff=0.5)
print(p.pauli)   # "XZ"
print(p.coeff)   # 0.5

# Hamiltonian
H = sf.Hamiltonian([p, sf.PauliString("II", coeff=-1.0)])
print(H.n_terms)        # 2
print(H.n_qubits)       # 2
sparse = H.to_sparse_list()  # [(pauli_indices, coeff_real, coeff_imag), ...]

# SparsePauliOp
op = sf.SparsePauliOp.from_list(["ZZ", "XI"], [0.5, 0.3])

# Convenience Pauli matrices
from superfermion.observables import I, X, Y, Z

# expval shortcut
from superfermion import expval
val = expval(state.numpy(), H.to_sparse_list())

Observable Format (Rust)

# List of (pauli_indices, coeff_real, coeff_imag)
# Pauli encoding: 0=I, 1=X, 2=Y, 3=Z
zz_obs = [([3, 3], 1.0, 0.0)]     # ZZ with coefficient 1.0
xz_obs = [([1, 3], 0.5, 0.0)]     # 0.5 * XZ

7. Parametric Circuits and Gradients

import superfermion as sf

theta = sf.param("theta")
phi = sf.param("phi")

ansatz = (sf.Circuit(2)
    .ry(theta, 0)
    .ry(phi, 1)
    .cnot(0, 1)
    .rz(theta, 1))

print(ansatz.n_parameters)   # 2
print(ansatz.parameters)     # ['theta', 'phi']

# Bind and compute gradients
params = {"theta": 0.5, "phi": 1.2}
state = sf.simulate(ansatz, params=params)

obs = [([3, 3], 1.0, 0.0)]  # ZZ
dag = ansatz.bind(params).to_ir()
grads = state.grad(obs, dag, params)
print(grads)  # {"theta": -0.23..., "phi": 0.41...}

5 Gradient Methods

MethodModuleScalingBest For
Adjointqml.gradient.adjointO(1)Deep circuits, many params
Parameter-shiftqml.gradient.parameter_shiftO(N)Shallow circuits
SPSAqml.gradient.spsaO(1) stochasticNoisy, many params
QNGqml.gradient.qngO(N²)Natural gradient
Riemannianqml.gradient.riemannianO(N²)QFIM-based natural gradient

8. VQE Optimization

import superfermion as sf
import numpy as np

H = sf.Hamiltonian([
    sf.PauliString("II", coeff=-1.0523),
    sf.PauliString("IZ", coeff=0.3979),
    sf.PauliString("ZI", coeff=-0.3979),
    sf.PauliString("ZZ", coeff=-0.0112),
    sf.PauliString("XX", coeff=0.1809),
])

ansatz = (sf.Circuit(2)
    .ry(sf.param("t0"), 0)
    .ry(sf.param("t1"), 1)
    .cnot(0, 1)
    .ry(sf.param("t2"), 0)
    .ry(sf.param("t3"), 1))

params = {f"t{i}": np.random.uniform(0, np.pi) for i in range(4)}
obs = H.to_sparse_list()
lr = 0.1

for step in range(50):
    state = sf.simulate(ansatz, params=params)
    energy = state.expectation(obs)

    dag = ansatz.bind(params).to_ir()
    grads = state.grad(obs, dag, params)

    for key in params:
        params[key] -= lr * grads.get(key, 0.0)

    if step % 10 == 0:
        print(f"Step {step}: energy = {energy:.6f}")

Built-in VQE Class

from superfermion.algorithms.variational import VQE, QAOA

# Use the built-in VQE class
vqe = VQE(ansatz, obs)
result = vqe.optimize(init_params, max_iterations=100)
print(f"Energy: {result.energy}")

9. Pulse-Level Control

from superfermion.pulse import (
    Schedule, GaussianPulse, DRAGPulse, SquarePulse,
    GaussianSquarePulse, CosinePulse, Waveform,
    Channel, ChannelType, CalibrationSet, GateCalibration,
)

# Build a pulse schedule
s = Schedule()

# Add a Gaussian pulse on the drive channel
gaussian = GaussianPulse(amplitude=0.5, sigma=16e-9, duration=64e-9)
s.add_pulse(gaussian, channel=Channel(0, ChannelType.DRIVE), start=0)

# Add a DRAG pulse for reduced leakage
drag = DRAGPulse(amplitude=0.5, sigma=16e-9, duration=64e-9, beta=0.1)
s.add_pulse(drag, channel=Channel(0, ChannelType.DRIVE), start=100e-9)

# Add a square pulse for measurement
square = SquarePulse(amplitude=1.0, duration=500e-9)
s.add_pulse(square, channel=Channel(0, ChannelType.MEASURE), start=200e-9)

# Compile a circuit to a schedule
from superfermion.compiler import schedule_circuit
qc = sf.Circuit(1).rx(0.5, 0)
scheduled = schedule_circuit(qc, calibration=my_calibration)

10. Quantum Error Correction

from superfermion.qec import (
    SurfaceCode2D, SteaneCode, RepetitionCode,
    MWPMDecoder, UnionFindDecoder, BPOSD_Decoder, NeuralDecoder,
    QECManager,
)

# Create a surface code
code = SurfaceCode2D(distance=3)

# Decode errors
decoder = MWPMDecoder(code)

# Full QEC pipeline
manager = QECManager(code, decoder)
protected = manager.protect(my_logical_circuit, noise_level=0.01)

See the QEC Guide for all 10 codes and 4 decoders.


11. Chemistry

from superfermion.chemistry.hamiltonians import get_molecular_hamiltonian, FermionicOperator
from superfermion.chemistry.ansatz import uccsd_ansatz
from superfermion.chemistry.pyscf_bridge import molecule_to_hamiltonian

# Pre-built molecule
h2 = get_molecular_hamiltonian("H2", bond_length=0.7414)

# Custom molecule via PySCF
H, meta = molecule_to_hamiltonian(atom="H 0 0 0; H 0 0 0.7414", basis="sto-3g")

# Build UCCSD ansatz
ansatz = uccsd_ansatz(H.n_qubits, n_electrons=meta["n_electrons"])

# Fermionic operators
fop = FermionicOperator("0^ 1^ 2 3")  # excitation
qubit_op = fop.to_qubit_operator(mapping="jordan_wigner")

See the Chemistry Guide for full details.


12. Experiment Tracking

Tracking is scoped via context manager using contextvars (thread-safe, no global state).

import superfermion as sf

# Local tracking (no server needed)
with sf.experiment("bell-study") as tracker:
    r1 = sf.run(sf.Circuit(2).h(0).cnot(0, 1), shots=1000)
    r2 = sf.run(sf.Circuit(3).h(0).cnot(0,1).cnot(1,2), shots=1000)

# Runs saved to ~/.superfermion/runs/bell-study/
print(tracker.runs)  # list of run metadata dicts

13. Cross-Framework Interop

from superfermion.bridge import (
    from_qiskit, to_qiskit,
    from_qasm, to_qasm,
    from_cirq, to_cirq,
    from_pennylane, to_pennylane,
    to_braket, to_ionq,
)
from superfermion.serialization import to_qasm3, from_qasm3

# Qiskit interop
qiskit_circuit = to_qiskit(sf_circuit)
sf_circuit = from_qiskit(qiskit_circuit)

# Cirq interop
cirq_circuit = to_cirq(sf_circuit)
sf_circuit = from_cirq(cirq_circuit)

# PennyLane interop
pl_circuit = to_pennylane(sf_circuit)
sf_circuit = from_pennylane(pl_circuit)

# OpenQASM 2.0
qasm_str = to_qasm(sf_circuit)
sf_circuit = from_qasm(qasm_str)      # Rust-accelerated parser

# OpenQASM 3.0
qasm3_str = sf_circuit.to_qasm3()
sf_circuit = from_qasm3(qasm3_str)

# Braket + IonQ export
braket_task = to_braket(sf_circuit)
ionq_circuit = to_ionq(sf_circuit)

# JSON round-trip
json_str = sf_circuit.to_json()
sf_circuit = sf.Circuit.from_json(json_str)

14. Error Mitigation

from superfermion.mitigation import (
    readout_correction,
    zne,
    zne_with_calibration,
    calibration_based_noise_model,
)

# Readout error correction
result = sf.run(qc, device="cpu", shots=8192)
corrected = readout_correction(result)

# Zero-noise extrapolation
mitigated = zne(qc, noise_levels=[1.0, 2.0, 3.0], device="cpu")

# Build noise model from calibration data
noise_model = calibration_based_noise_model(backend_properties)
result = sf.run(qc, method="density_matrix", noise_model=noise_model)

15. QPU Providers — Protocol-Based

SF defines DeviceExecutor as a Python protocol. Any object satisfying it works with sf.run(). No registries, no magic strings for cloud devices.

# Standalone provider usage (no platform needed)
from superfermion.devices.ibm import IBMDevice
ibm = IBMDevice(token="...")
result = sf.run(qc, device=ibm("ibm_brisbane"), shots=8192)

Device Access Tiers

What you passCredentialsTracking
device="cpu"NoneOptional
device="gpu"NoneOptional
device=ibm("brisbane")User's IBM token (local)Optional
device=cs("ibm:brisbane")User's IBM token (stored on Catstate)Via platform
device=cs("best")Catstate's accountsVia platform

See the QPU Providers Guide for detailed setup instructions for each provider.


16. ML Framework Integration

Each layer is a thin wrapper (~20 lines) around sf.State.grad().

# Flax/JAX
from superfermion.nn.quantum_layer import QuantumLayer

# PyTorch
from superfermion.nn.torch_layer import TorchQuantumLayer

# TensorFlow
from superfermion.nn.tf_layer import TFQuantumLayer

See the ML Integration Guide for complete examples with all three frameworks.


API Map

sf.Circuit(n)                     Circuit builder
  .h(q) .x(q) .cnot(c,t) ...     Gate methods (fluent, chainable)
  .rx(theta, q) .ry .rz .p .u    Parametric gates
  .measure(q) .measure_all()      Measurement
  .bind(params) -> Circuit        Parameter binding
  .n_qubits -> int                Qubit count
  .gate_count -> int              Total gate count
  .depth -> int                   Circuit depth
  .n_parameters -> int            Number of unbound parameters
  .parameters -> list[str]        Parameter names
  .to_qasm3() -> str              Export to OpenQASM 3.0
  .to_unitary() -> ndarray        Export to unitary matrix
  .to_ir() -> QuantumDAG          Export to Rust IR
  .to_json() -> str               Export to JSON
  .to_gate_list() -> list         Export to gate list
  .draw() -> str                  ASCII visualization
  .from_json(s) -> Circuit        Import from JSON (classmethod)

sf.run(circuit, ...) -> RunResult   Universal execution
  device="cpu" | "gpu" | executor   Where to run
  method="statevector"|"mps"|...    How to simulate
  shots=1000                        Measurement repetitions
  params={...}                      Auto-bind parameters
  target="device_name"              Auto-compile for hardware
  tracker=tracker_obj               Explicit tracking

sf.simulate(circuit, ...) -> State  Shortcut for sf.run().state

sf.compile(circuit, ...) -> Circuit Hardware compilation
  level=0|1|2                       Optimization aggressiveness
  target=HardwareSpec               Target device specification

sf.State                            Rust-native quantum state
  .expectation(obs) -> float        Exact expectation value
  .grad(obs, dag, params) -> dict   Adjoint differentiation
  .sample(shots) -> list            Measurement sampling
  .numpy() -> ndarray               Export to numpy
  .entropy() -> float               Von Neumann entropy
  .purity() -> float                State purity
  .fidelity(other) -> float         State fidelity
  .partial_trace(qubits) -> ndarray Partial trace
  .probabilities() -> ndarray       Outcome probabilities
  .qfim(dag, params) -> ndarray     Quantum Fisher Information Matrix
  .n_qubits -> int                  Qubit count
  .method -> str                    Simulation method used
  .device -> str                    Device used
  .shape -> tuple                   State shape
  .from_numpy(arr) -> State         Import from numpy (classmethod)

sf.RunResult                        Execution result container
  .counts -> dict[str, int]         Measurement counts
  .state -> State                   Quantum state
  .shots -> int                     Number of shots
  .circuit -> Circuit               Original circuit
  .metadata -> object               Execution metadata
  .statevector -> ndarray           State amplitudes
  .expectation(obs) -> float        Expectation from counts
  .grad(obs, dag, params) -> dict   Gradient from state
  .plot(kind)                       Plot results
  .to_dict() -> dict                Serialize

sf.experiment(name, tracker=None)   Context manager for tracking
sf.param(name) -> SymbolicParameter Symbolic parameter factory
sf.Hamiltonian(terms)               Observable from PauliString list
sf.PauliString(str, coeff=1.0)      Single Pauli term
sf.SparsePauliOp                    Sparse sum of Pauli terms
sf.expval(statevector, observable)  Compute expectation value
sf.NoiseModel()                     Noise model for density matrix sim
sf.DeviceExecutor                   Protocol for device targets
sf.LocalTracker                     File-based experiment tracker

On this page