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.cat(circuit, mode) Compile once into a reusable execution handle
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 sim1. Circuit Construction
sf.Circuit is a fluent builder. Each gate call returns the circuit for chaining.
import sys
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
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 diagramAvailable Gates
| Category | Gates |
|---|---|
| 1Q Clifford | h, x, y, z, s, sdg, t, tdg, sx, id |
| 1Q Parametric | rx(theta, q), ry(theta, q), rz(theta, q), p(phi, q), u(theta, phi, lam, q) |
| 2Q | cnot(c, t) / cx(c, t), cz, cy, swap, iswap, ecr |
| 2Q Parametric | cp, cu, rzz, rxx, ryy |
| 3Q | ccx / toffoli, cswap / fredkin |
| Custom | unitary(matrix, *qubits) |
| Special | measure(q), measure_all(), barrier(*qubits), reset(q) |
Circuit Properties
| Property | Type | Description |
|---|---|---|
n_qubits | int | Number of qubits |
gate_count | int | Total gate count |
depth | int | Circuit depth |
n_parameters | int | Number of unbound parameters |
parameters | list[str] | Parameter names |
n_cbits | int | Number of classical bits |
Circuit Methods
| Method | Returns | Description |
|---|---|---|
.bind(params) | Circuit | Bind symbolic parameters |
.to_ir() | QuantumDAG | Export to Rust IR |
.to_qasm3() | str | Export to OpenQASM 3.0 |
.to_json() | str | Export to JSON |
.to_unitary() | ndarray | Unitary matrix (exponential cost) |
.to_gate_list() | list | Export as gate list |
.draw() | str | ASCII visualization |
.count_ops() | dict | Count gates by type |
.from_json(s) | Circuit | Import 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) # 4096Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
circuit | Circuit | required | The circuit to execute |
device | str or DeviceExecutor | "cpu" | Where to run |
shots | int | 1000 | Number of measurement shots |
method | str or None | "statevector" | Simulation algorithm |
target | str or None | None | Hardware target for auto-compilation |
tracker | TrackerProtocol or None | None | Explicit tracker |
params | dict or None | None | Auto-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 provider3. 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 samplingsf.State Methods
| Method | Returns | Description |
|---|---|---|
state.expectation(obs) | float | Exact Pauli expectation value |
state.grad(obs, dag, params) | dict | Adjoint/parameter-shift gradient |
state.sample(shots) | list[int] | Measurement sampling |
state.numpy() | ndarray | Export to numpy array |
state.entropy() | float | Von Neumann entropy |
state.purity() | float | State purity |
state.fidelity(other) | float | Fidelity with another state |
state.partial_trace(qubits) | ndarray | Partial trace over qubits |
state.probabilities() | ndarray | Measurement outcome probabilities |
state.qfim(dag, params) | ndarray | Quantum Fisher Information Matrix |
state.shape | tuple | Shape of the statevector |
state.n_qubits | int | Number of qubits |
state.method | str | Simulation method used |
state.device | str | Device used |
state.from_numpy(arr) | State | Create State from numpy (classmethod) |
RunResult Properties
| Property | Type | Description |
|---|---|---|
result.counts | dict[str, int] | Measurement counts |
result.state | State | Rust quantum state |
result.shots | int | Number of shots |
result.circuit | Circuit | Original circuit |
result.metadata | object | Execution metadata |
result.probabilities | dict[str, float] | Outcome probabilities (populated on every backend; exact from the state when available, otherwise normalized counts) |
result.statevector | ndarray | State amplitudes |
RunResult Methods
| Method | Description |
|---|---|
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)MPS shots=0 densifies the tensor network into a full statevector. For more than 26 qubits this raises a MemoryError with guidance — pass shots>0 to sample directly from the tensor network instead, or densify=False to receive only the State handle. MPS sampling and statevector output use the same little-endian qubit order as the statevector backend ("001" means qubit 0 is |1⟩).
With shots>0, method="mps" also runs dynamic (mid-circuit) circuits (measure/reset/c_if) as per-shot trajectories that collapse the tensor network at every mid measure; bond_dim caps the bond (default 64) and is echoed in result.metadata["bond_dim"].
For MPS-native observables and densification, without going through sf.run:
from superfermion import mps_expval, mps_todense
# Expectation value on the tensor network (never densifies to 2^n)
energy = mps_expval(qc, "ZZII", bond_dim=64) # str / dict / SparsePauliOp
# Densify with a parallel (numba) left-to-right sweep
sv = mps_todense(qc, bond_dim=64) # little-endian, length 2^nStabilizer
For Clifford-only circuits. Polynomial time, scales to ~4000 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)State operations that the stabilizer backend does not support (.numpy(), .partial_trace(), .fidelity(), .entropy(), .purity(), .probabilities(), .grad()) raise superfermion.MethodError, which is a subclass of RuntimeError — catching either works.
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)counts/probabilities keys follow the shared little-endian key convention (qubit q = bit q of int(key, 2)); result.metadata["density_matrix"] is big-endian (qubit 0 = most significant). See the execution guide for the full layout notes.
Cat — compile-once handle (sf.cat())
sf.cat(circuit, mode) lowers a circuit once into a reusable handle: gate fusion, qubit mapping and parameter metadata are cached, so repeated runs with fresh parameter values skip the DAG rebuild that sf.run() pays on every call.
qc = sf.Circuit(2).ry(sf.param("t"), 0).cnot(0, 1)
handle = sf.cat(qc, mode="statevector") # compile once
state = handle.simulate({"t": 0.3}) # rebind, no DAG rebuild
print(handle.parameter_names) # ['t']
print([round(abs(a) ** 2, 3) for a in state.numpy()])
# [0.978, 0.0, 0.0, 0.022]
stab = sf.cat(sf.Circuit(2).h(0).cnot(0, 1), mode="stabilizer")
print(stab.sample(1000, seed=7)) # little-endian counts
# {'00': 502, '11': 498}Modes: statevector → .simulate(values); adjoint → .run_gradient(pauli, theta) returns one d⟨P⟩/dθ per parameter (Pauli string "IXYZ…", character i acts on qubit i); stabilizer / clifford → .sample(shots, seed=…). The handle also exposes .n_qubits and .parameter_names. density_matrix is not implemented and raises ValueError at construction rather than returning wrong numbers; a build that does not export CatCircuit makes sf.cat() raise a clear ImportError.
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
| Level | What it does |
|---|---|
| 0 | No optimization (passthrough) |
| 1 | SWAP decomposition, gate cancellation, rotation merging, constant folding |
| 2 | Level 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 = H.expectation(state.numpy())
print(f"Exact <H> = {energy:.6f}")
# Estimate from measurement counts
result = sf.run(sf.Circuit(2).h(0).cnot(0, 1), shots=10000)
obs = [([3, 3], 0.5, 0.0), # ZZ
([1, 0], 0.3, 0.0), # XI
([0, 3], 0.2, 0.0)] # IZ
energy = result.expectation(obs)
print(f"Counts <H> = {energy:.6f}")Observable API
# PauliString
p = sf.PauliString("XZ", coeff=0.5)
print(p.pauli_str) # "XZ"
print(p.coeffs) # 0.5
# Hamiltonian
H = sf.Hamiltonian([p, sf.PauliString("II", coeff=-1.0)])
print(len(H.terms)) # 2
sparse = H.to_sparse_pauli_op()
# SparsePauliOp
op = sf.SparsePauliOp.from_dict({"ZZ": 0.5, "XI": 0.3})
# Convenience Pauli matrices
from superfermion.observables import I, X, Y, Z
# expval shortcut
from superfermion import expval
state = sf.simulate(sf.Circuit(2).h(0).cnot(0, 1))
val = expval(state.numpy(), H)
print(f"<H> = {val:.6f}")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 * XZ7. 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.to_ir() # keep parameters symbolic — .bind() would erase them
grads = state.grad(obs, dag, params)
print(grads) # {'theta': ~0.0, 'phi': -0.932...}5 Gradient Methods
| Method | Module | Scaling | Best For |
|---|---|---|---|
| Adjoint | qml.gradient.adjoint | O(1) | Deep circuits, many params |
| Parameter-shift | qml.gradient.parameter_shift | O(N) | Shallow circuits |
| SPSA | qml.gradient.spsa | O(1) stochastic | Noisy, many params |
| QNG | qml.gradient.qng | O(N²) | Natural gradient |
| Riemannian | qml.gradient.riemannian | O(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 = [([0, 0], -1.0523, 0.0), # II
([0, 3], 0.3979, 0.0), # IZ
([3, 0], -0.3979, 0.0), # ZI
([3, 3], -0.0112, 0.0), # ZZ
([1, 1], 0.1809, 0.0)] # XX
lr = 0.1
for step in range(50):
state = sf.simulate(ansatz, params=params)
energy = state.expectation(obs)
dag = ansatz.to_ir() # keep parameters symbolic — .bind() would erase them
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
import superfermion as sf
import numpy as np
from superfermion.algorithms.variational import VQE
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))
# Use the built-in VQE class
vqe = VQE(ansatz, H)
result = vqe.minimize(iterations=100)
print(f"Energy: {result.optimal_value:.8f}")
print(f"Optimal params: {result.optimal_params}")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 (duration/sigma in dt units)
gaussian = GaussianPulse(duration=64, sigma=16, amp=0.5)
s.add(gaussian, channel="d0", t0=0)
# Add a DRAG pulse for reduced leakage
drag = DRAGPulse(duration=64, sigma=16, amp=0.5, beta=0.1)
s.add(drag, channel="d0", t0=100)
# Add a square pulse for measurement
square = SquarePulse(duration=200, amp=1.0)
s.add(square, channel="m0", t0=200)
print(s)
# Compile a circuit to a schedule
from superfermion.compiler import schedule_circuit
qc = sf.Circuit(1).rx(0.5, 0)
scheduled = schedule_circuit(qc)
print(scheduled)10. Quantum Error Correction
import numpy as np
from superfermion.qec import (
SurfaceCode2D, SteaneCode, RepetitionCode,
MWPMDecoder, UnionFindDecoder, BPOSD_Decoder, NeuralDecoder,
QECManager,
)
# Create a surface code
code = SurfaceCode2D(distance=3)
syndrome_qubit_map = [[i, (i + 1) % code.n_data] for i in range(code.n_ancilla)]
# Decode errors
decoder = MWPMDecoder(code.n_data, syndrome_qubit_map)
correction = decoder.decode(np.zeros(code.n_ancilla, dtype=int))
print(f"Correction: {correction}")
# Full QEC pipeline
manager = QECManager()
result = manager.run_logical_lifecycle("surface_2d")
print(f"Logical state: {result['logical_state']}")
# Repetition code: any length n >= 2, optional repeated rounds
rep = RepetitionCode(n=5)
print(rep.syndrome_map()) # [[0, 1], [1, 2], [2, 3], [3, 4]] (check -> data qubits)
circuit = rep.build(rounds=3) # 3 repeated syndrome-extraction roundsSee the QEC Guide for all 10 codes and 4 decoders.
11. Chemistry
import numpy as np
from superfermion.chemistry.hamiltonians import get_molecular_hamiltonian, FermionicOperator
from superfermion.chemistry.ansatz import uccsd_ansatz
# Pre-built molecule (cached H2 data — no PySCF needed)
h2 = get_molecular_hamiltonian("H2", basis="sto-3g")
print(f"Terms: {len(h2.terms)}")
# Build UCCSD ansatz (n_qubits, n_electrons)
ansatz = uccsd_ansatz(2, n_electrons=2)
print(f"Ansatz: {ansatz.gate_count} gates, params: {ansatz.parameters}")
# Fermionic operators
fop = FermionicOperator.from_coeffs(np.array([[0.0, 1.0], [1.0, 0.0]])) # example h1
qubit_op = fop.jordan_wigner(n_qubits=2)
print(f"JW terms: {len(qubit_op.terms)}")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 dicts13. 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, to_stim,
)
from superfermion.serialization import to_qasm3, from_qasm3
sf_circuit = sf.Circuit(2).h(0).cnot(0, 1)
# 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 — to_pennylane() returns a qfunc; build a tape for the reverse
import pennylane as qml
dev = qml.device("default.qubit", wires=2)
@qml.qnode(dev)
def pl_qnode(x):
qml.RX(x, wires=0)
qml.CNOT(wires=[0, 1])
pl_tape = qml.workflow.construct_tape(pl_qnode)(0.5)
sf_circuit = from_pennylane(pl_tape)
qfunc = to_pennylane(sf_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_circuit = to_braket(sf_circuit)
ionq_circuit = to_ionq(sf_circuit)
# Stim (Clifford-only) — fast tableau sampling + detector ecosystem
stim_circuit, record_cbits = to_stim(sf_circuit)
samples = stim_circuit.compile_sampler().sample(1000)
# JSON round-trip
json_str = sf_circuit.to_json()
sf_circuit = sf.Circuit.from_json(json_str)
print(sf_circuit.gate_count)14. Error Mitigation
from superfermion.mitigation import (
readout_correction,
zne,
zne_with_calibration,
calibration_based_noise_model,
)
qc = sf.Circuit(2).h(0).cnot(0, 1)
# Readout error correction
result = sf.run(qc, device="cpu", shots=8192)
corrected = readout_correction(result.counts)
print(corrected)
# Zero-noise extrapolation
def energy(sv):
return sf.expval(sv, sf.PauliString("ZZ", coeff=1.0))
mitigated = zne(qc, energy, scale_factors=[1, 2, 3])
print(f"ZNE <ZZ> = {mitigated:.6f}")
# Build noise model from calibration data
noise_model = calibration_based_noise_model("ibm_like")
result = sf.run(qc, method="density_matrix", noise_model=noise_model, shots=1000)
print(result.counts)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 pass | Credentials | Tracking |
|---|---|---|
device="cpu" | None | Optional |
device="gpu" | None | Optional |
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 accounts | Via 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
# Data embedding: feature_names route inputs to named circuit params; the
# rest become trainable weights (gradients flow to both).
# layer = TorchQuantumLayer(circuit, obs, feature_names=["x0"])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