Rust FFI Acceleration

July 29, 2026 · View on GitHub

The spo-kernel Rust workspace accelerates selected paths across the engine, coupling, supervisor, SSGF, monitor, extraction, and autotune surfaces. Recorded local speedups vary substantially by operation, size, build, and host. Python classes auto-detect the compiled spo_kernel module and delegate transparently—no application-code change is needed.

For runtime selection across Rust, Python, JAX, and auxiliary research backends, see Backend Fallback Chain.

Prerequisites

  • Rust 1.83+ (workspace MSRV)
  • maturin (pip install maturin)

Building

Use the repository helper so maturin runs through the Python interpreter that owns the target environment:

python tools/install_spo_kernel.py --release

This compiles all Rust crates and installs spo_kernel into the active Python environment. To target the repository virtual environment explicitly:

.venv/bin/python tools/install_spo_kernel.py --release

Verify the selected environment:

python tools/install_spo_kernel.py --check-only

The equivalent raw maturin command is:

python -m maturin develop --release -m spo-kernel/crates/spo-ffi/Cargo.toml

Using python -m maturin is intentional: it prevents a globally installed maturin executable from building into a different interpreter than the one used by spo run, tests, or notebooks.

You can inspect the command without compiling Rust:

python tools/install_spo_kernel.py --dry-run --json

After installation, direct import should work:

import spo_kernel
print(spo_kernel.PyUPDEStepper)

Auto-Delegation

Python classes check for spo_kernel at construction time. If present, hot paths delegate to Rust with no API change:

from scpn_phase_orchestrator import UPDEEngine

engine = UPDEEngine(n_oscillators=64, dt=0.01, method="rk4")
# engine._rust is a PyUPDEStepper if spo_kernel is installed
# engine._rust is None otherwise (pure numpy fallback)

The _compat.HAS_RUST flag controls delegation globally. Set it to False in benchmarks to force the Python path.

Accelerated Modules

Python Class / FunctionRust FFI ClassHot path
UPDEEnginePyUPDEStepperstep(), run()
StuartLandauEnginePyStuartLandauStepperstep(), run()
CouplingBuilderPyCouplingBuilderbuild(), project()
ImprintModelPyImprintModelupdate(), modulate_coupling(), modulate_lag()
compute_order_parameterorder_parametersingle call
compute_plvplvsingle call
modulation_indexpac_modulation_indexsingle call
pac_matrixpac_matrix_computefull NxN
CoherenceMonitorPyCoherenceMonitorcompute_r_good(), compute_r_bad(), detect_phase_lock()
RegimeManagerPyRegimeManagerevaluate(), transition()
ActionProjectorPyActionProjectorproject()
BoundaryObserverPyBoundaryObserverobserve()
SupervisorPolicyPySupervisorPolicydecide()
PhaseQualityScorerPyPhaseQualityScorerscore(), is_collapsed()
LagModelPyLagModelestimate()
NeurocoreBridgePyLIFEnsemblestep() (LIF ensemble, 325x at N=10000)
Physical extractorphysical_extractanalytic signal extraction
Symbolic extractorsring_phase, graph_walk_phase, transition_qualitysingle call
Informational extractorevent_phasetimestamp analysis
SimplicialEnginesimplicial_run3-body coupling run()
HypergraphEnginehypergraph_runk-body coupling run()
GeometricEnginegeometric_runSO(2) exp map run()
extract_envelopeenvelope_rms_rustcumulative-sum RMS
OttAntonsenReductionoa_run_rustrun(), steady_state_R(), predict_from_oscillators()
SplittingEnginesplitting_run_rustStrang split run()
te_adapt_couplingte_adapt_coupling_rustTE-directed coupling update
UniversalPrior.log_probabilityprior_log_probability_rustBayesian log-density
load_hcp_connectomeload_hcp_connectome_rustsynthetic connectome generation
GeometryCarrier.decodecarrier_decode_rustsoftplus(A·z) decode
compute_ethical_costcompute_ethical_cost_rustSEC + CBF ethical cost
classify_sleep_stageclassify_sleep_stage_rustAASM stage classification
EVSMonitor._frequency_specificityfrequency_specificity_rusttarget/control ITPC ratio
PhaseSINDy.fitsindy_fit_rustSTLSQ sparse regression
estimate_coupling(disabled)normal equations (3x slower than LAPACK)
extract_phases(disabled)naive DFT (60x slower than SciPy FFT)

Benchmark Comparison

bench/run_benchmarks.py measures UPDEEngine.step() with RK4, averaged over 1000 steps after 50 warmup iterations. The table is a historical local snapshot. Re-run the benchmark and record host/build metadata before using a number for capacity planning.

NPython (numpy)Rust (spo_kernel)Speedup
16~25 us/step7.3 us/step3.4x
64~180 us/step28 us/step6.4x
256~2.8 ms/step0.32 ms/step8.7x
1024~45 ms/step8.6 ms/step5.2x

The speedup saturates at large N because both paths are O(N^2) in coupling computation; the Rust advantage comes from avoiding Python interpreter overhead and numpy dispatch per operation.

LIF Ensemble (NeurocoreBridge)

The PyLIFEnsemble accelerates the neurocore bridge's spiking neuron simulation. Measured on Windows 11, Python 3.12, Rust 1.93.0 release build, N=10000 neurons (10 layers × 1000), 100 substeps:

BackendTimens/neuron/substepSpeedup vs scalar
Rust (PyLIFEnsemble)0.004 s3-6 ns325×
NumPy (vectorised)0.014 s14 ns93×
Scalar (sc-neurocore per-neuron)1.306 s1,306 ns

The recorded 4 ms per 100-substep result is local throughput evidence. It does not establish a 250 Hz control-loop deadline: end-to-end sensing, scheduling, transport, actuation, jitter, and worst-case latency were not measured.

Crate Structure

spo-kernel/
  Cargo.toml          # workspace root
  crates/
    spo-types/        # Shared types: UPDEState, LayerState, Regime, Knob, ControlAction
    spo-engine/       # 53 modules: UPDE (12 engines), coupling (11), monitors (14), SSGF (3), autotune (4), + support
    spo-oscillators/  # Physical, informational, symbolic, quality extractors
    spo-supervisor/   # Boundaries, coherence, policy, projector, regime manager
    spo-ffi/          # PyO3 bindings (this is what maturin builds)

All pure-logic crates (spo-types, spo-engine, spo-oscillators, spo-supervisor) have #![no_std] aspirations but currently use std for HashMap and Vec. Only spo-ffi depends on PyO3 and numpy.

FFI Numeric Precision Contract

The Python/Rust boundary is a float64 contract:

  • Python passes contiguous numeric arrays after shape, finite-value, and domain validation.
  • Rust kernels return float64-compatible values and Python revalidates shape, finiteness, and physical bounds before accepting the result.
  • Phase outputs are normalised to [0, 2π) unless the documented monitor contract returns a unitless score or matrix.
  • Fixed-point formal manifests are separate review artefacts; they do not change the runtime float64 solver contract.

Any new FFI path must add a module-specific parity test that compares Python and Rust on the same seeded input and documents the tolerated absolute or relative error. Safety-critical gates must fail closed when either backend emits NaN, infinity, shape drift, or a value outside the declared physical range.

Contributing Rust Code

Run all three before submitting:

cargo fmt --all
cargo clippy --workspace -- -D warnings
cargo test --workspace

The CI pipeline runs:

JobMatrixWhat
rust-check3 OS (Linux, macOS, Windows)cargo fmt --check, clippy -D warnings, cargo test
ffi-test3 OS x 2 Python (3.11, 3.12)maturin develop --release, pytest tests/
cargo-auditLinuxcargo audit for known vulnerabilities
cargo-denyLinuxRustSec advisories, banned wildcard dependencies, and source registry policy
rust-miriLinux nightlyMiri smoke tests for pure-Rust type/supervisor crates
rust-msrvLinuxVerify builds on Rust 1.83.0

Numerical Parity

The Rust and Python implementations produce identical results to float64 precision. The CI ffi-test job runs the full Python test suite with spo_kernel installed, confirming parity across all integration methods (euler, rk4, rk45) and both engines (UPDEEngine, StuartLandauEngine).