semiflow

Status: beta (0.15.0-beta). The API is stabilising toward 1.0; minor
versions may still make breaking changes. Bug reports and feedback are welcome.
SemiFlow solves evolution equations ∂ₜu = Lu (heat, diffusion,
Schrödinger, graph and manifold PDEs, …) by Chernoff approximation of operator
semigroups: no matrix exponentials, no linear solves, a flat memory footprint.
The core is a no_std + alloc Rust library with 3 runtime
dependencies (num-traits, libm, num-complex); the same engines are available from Python,
JavaScript/WebAssembly and C.
The method evaluates e^{tL}f by iterating an explicit step operator S(τ):
(S(t/n))ⁿ f → e^{tL} f as n → ∞. Each step is an explicit stencil over a
Vec-backed grid with reused scratch buffers, so steady-state evolution does
not allocate.
Mathematical foundation: Theorem 6 of I. D. Remizov (2025), Vladikavkaz
Math. J. 27(4), 124–135 (doi:10.46698/a3908-1212-5385-q).
Honest performance note. The primary measured advantage is memory
frugality: a flat ~3 MB working set across the 1D/2D/3D families versus
50–418 MB for heavy frameworks. Wall-clock speed is not a general
strength — for matched-accuracy PDE solving SemiFlow is slower than adaptive
ODE solvers and spectral methods, and parallelism pays off only on large 3D
grids. Two measured niches: tail-latency-sensitive pricing
(Diffusion4thChernoff ≈ 41 ns p99.9 per tick) and L1-resident low-rank
carriers (TtChernoff, ReverseChernoff). Treat memory and latency as
measured properties of the concrete grid types, not guarantees of the traits.
The 2026-10 overhaul (docs/perf/overhaul-v0_15.md)
made the grid engines 2.6–39× faster bit-for-bit, the stiff Chebyshev graph
action O(√(λt)) instead of O(λt) (559× on issue #16), ETDRK4 2.1× and the
general Taylor action 2.1× faster; Richardson extrapolation (richardson=)
raises any engine's order by levels − 1. Head-to-head comparisons quoted in
the user guide predate it.
Install
[dependencies]
semiflow = "0.15.0-beta"
MSRV: Rust 1.81. The default build is #![no_std] + alloc with
AVX2/NEON kernels (feature simd, scalar lanes elsewhere); std is needed
only for parallel. See below.
Quickstart
Solve ∂ₜu = ½·∂ₓₓu from u₀(x) = e^{-x²} to t = 1 and compare with the
closed-form solution u(1,x) = 3^{-1/2}·e^{-x²/3}:
use semiflow::{ChernoffSemigroup, DiffusionChernoff, Grid1D, GridFn1D, State};
let grid = Grid1D::new(-10.0, 10.0, 1000).expect("valid grid");
let u0 = GridFn1D::from_fn(grid, |x| (-x * x).exp());
let diffusion = DiffusionChernoff::new(|_| 0.5_f64, |_| 0.0, |_| 0.0, 0.5, grid);
let semigroup = ChernoffSemigroup::new(diffusion, 100).expect("n >= 1");
let u1 = semigroup.evolve(1.0, &u0).expect("evolve");
let exact = GridFn1D::from_fn(grid, |x| (-x * x / 3.0).exp() / 3.0_f64.sqrt());
let mut err = u1.clone();
err.axpy(-1.0, &exact);
assert!(err.norm_sup() < 1e-5, "sup-norm error {}", err.norm_sup());
More: Quickstart, User Guide and
the runnable examples
(cargo run -p semiflow --example heat_2d_demo).
no_std
[dependencies]
semiflow = "0.15.0-beta"
The crate is #![no_std] by default and needs only alloc; the std
feature is needed only for parallel. What that means in practice:
- Your binary provides a
#[global_allocator] and a #[panic_handler], as
for any no_std + alloc program (a std program already has both).
- The target needs pointer-sized atomics (the engines share coefficient
closures through
alloc::sync::Arc): Cortex-M3/M4/M7/M33, RISC-V with the
a extension, x86_64-unknown-none and similar work; thumbv6m does not.
SemiflowError implements Debug, Display and core::error::Error
(the same trait as std::error::Error) in every build.
f32/f64 transcendentals come from the pure-Rust libm
in every build, and the SIMD kernels use the same lane arithmetic with or
without intrinsics, so std, no_std, simd and non-simd builds give
bit-identical results on every CPU.
simd works without std (the intrinsics come from core::arch);
parallel requires std.
This is verified on every pull request, not just type-checked:
| CI job |
What it proves |
no-std |
the default and the featureless build for the host, thumbv7em-none-eabihf, thumbv7m-none-eabi, riscv32imac-unknown-none-elf, and at MSRV 1.81 |
no-std-test |
the full semiflow test suite passes in the default (no_std) configuration |
no-std-libm |
semiflow-nostd-check — closed-form oracle checks plus per-scenario output digests: the default no_std build (the job fails if anything enables num-traits/std), the std build and its AVX2 lanes must all produce the committed bits |
no-std-libm-neon |
the same digests from the std build on aarch64 (NEON lanes) |
no-std-qemu |
the same checks and digests as a bare-metal #![no_main] binary on Cortex-M3 (soft float) and Cortex-M4F (hard float) under QEMU — see nostd-qemu |
Feature flags
| Feature |
Default |
Effect |
simd |
yes |
AVX2 (x86_64) / NEON (aarch64) kernels via core::arch, scalar lanes elsewhere. Works in no_std. Results are bit-identical to a non-simd build. |
std |
no |
Links std. Needed only by parallel; math is libm either way, so results do not change. Without it the crate is no_std + alloc (the default). |
parallel |
no |
Multi-threaded apply for 1D kernels and 2D/3D compositions via std::thread::scope; bit-identical across thread counts. Implies std; not for wasm32. |
linear-interp |
no |
Enables InterpKind::Linear grid sampling (otherwise it returns SemiflowError::Unsupported). |
s3-poc |
no |
Experimental S³ evolvers with a proven-boundary API (ADR-0169). |
slow-tests |
no |
Internal: enables the long convergence gates. Not needed by users. |
diff-scipy |
no |
Internal: SciPy differential test (needs Python). Not needed by users. |
tracking-alloc |
no |
Internal: allocation counting in the latency_tail example. Not needed by users. |
Engine catalogue
1D Chernoff functions
| Type |
Order |
Description |
DiffusionChernoff |
2 |
5-point formula for a(x)∂²_x |
Diffusion4thChernoff |
4 |
4th-order spatial diffusion |
Diffusion6thChernoff |
6 |
6th-order spatial diffusion |
Diffusion4thZeta4Chernoff |
4 |
ζ⁴ Richardson |
Diffusion6thZeta6Chernoff |
6 |
ζ⁶ nested-Richardson |
Diffusion8thZeta8Chernoff |
8 |
ζ⁸ Chebyshev M=64 |
DiffusionExpmvChernoff<F> |
— |
Al-Mohy–Higham expmv action |
TruncatedExpDiffusionChernoff |
2 |
Truncated-exponential diffusion |
TruncatedExp4thDiffusionChernoff |
4 |
Truncated-exponential 4th-order |
DriftReactionChernoff |
exact |
Characteristic-flow formula for b(x)∂_x + c(x) |
DriftReactionZeta4Chernoff |
4 |
ζ⁴ WITH drift via Richardson |
ShiftChernoff1D |
2 |
Formula (6) of Theorem 6, Remizov 2025 |
Compositions
| Type |
Description |
StrangSplit<D, R> |
Strang 2nd-order splitting: D(τ/2) ∘ R(τ) ∘ D(τ/2) |
AxisLift<C> |
Lift a 1D ChernoffFunction to a 2D grid row or column |
2D tensor-product
| Type |
Description |
Strang2D<X, Y> |
Palindromic Strang: Sx(τ/2) ∘ Sy(τ) ∘ Sx(τ/2), global order 2 |
NonSeparable2DChernoff |
Non-separable isotropic 2D operator |
NonSeparable2DAnisotropicChernoff |
Non-separable anisotropic 2D operator |
3D tensor-product
| Type |
Description |
AxisLift3D<C> |
Lift a 1D ChernoffFunction to a 3D grid along Axis::X/Y/Z |
Strang3D<X, Y, Z> |
3D palindromic Strang |
Adaptive
| Type |
Description |
AdaptivePI<C> |
PI step-size controller wrapping any ChernoffFunction |
Boundary conditions
| Type |
Description |
BoundaryPolicy::Dirichlet { value } |
Fixed-value stencil BC |
BoundaryPolicy::Neumann |
Clamp-to-boundary stencil BC |
BoundaryPolicy::Robin { alpha, beta } |
Robin stencil BC |
BoundaryPolicy::OddReflect |
Odd-image stencil BC (used by DirichletHeat2ndChernoff) |
KillingChernoff<C, R, F> |
Operator-level Dirichlet via Feynman-Kac killing |
Killing2ndChernoff<C, K, F> |
Order-2 soft-killing e^{-τκ/2}·C(τ)·e^{-τκ/2} |
ReflectedHeatChernoff<C, R, F> |
Neumann via Walsh 1986 image method |
DirichletHeat2ndChernoff<C, R, F> |
Order-2 absorbing wall via odd-image method (math §21.9) |
ObstacleChernoff<C, O, F> |
Projective-splitting obstacle / variational-inequality evolver |
Resolvent and nonautonomous
| Type |
Description |
LaplaceChernoffResolvent<C, F> |
(λI − A)⁻¹ g via Gauss-Laguerre 32-pt quadrature — unique to Remizov |
HowlandLift<C, F> |
Nonautonomous Howland lift on L²([0,T], X) |
HdrSnapshot<F> |
NIST nearest-rank percentile library (no_std + alloc) |
Riemannian manifold
| Type |
Description |
ManifoldChernoff<M, F> |
Curvature-corrected Gaussian on T_xM; order 1 (base) or 2 (with R/12) |
Torus<F, D> |
Flat torus backend |
Sphere2<F> |
Round 2-sphere backend |
Hyperbolic2<F> |
Poincaré disk backend (also the SABR volatility manifold) |
FubiniStudyCp1 |
Kähler CP¹ / Fubini-Study backend |
High-dimensional and sparse-grid
| Type |
Description |
AnisotropicShiftChernoffND<F, D> |
d-D Gaussian shift via Gauss-Hermite tensor quadrature |
AnisotropicShiftZeta2ND |
Order-2 ζ²-correction variant |
AnisotropicShiftAdaptiveQ |
Adaptive per-point GH quadrature (41% fewer nodes/axis) |
SmolyakGridND<F, const D> |
Smolyak sparse-grid backend for D ≥ 5 |
Matrix-valued operators
| Type |
Description |
MatrixDiffusionChernoff<F, M> |
Coupled M-component 1D diffusion; Padé[13/13] expm |
MatrixDiffusionChernoff2D<F, M> |
Palindromic Strang 2D for M-component diffusion |
MatrixDiffusionChernoff3D<F, M> |
Same for 3D |
MatrixDiffusionChernoffComplex<F, M> |
Complex-valued Padé[13/13] coupled diffusion |
Quantum graphs
| Type |
Description |
QuantumGraphHeatChernoff<F> |
Heat on quantum graphs with Kirchhoff vertex condition |
QuantumSchrödingerChernoff<C> |
Complex Schrödinger on quantum graphs |
Reverse-mode AD
| Type |
Description |
ReverseChernoff<F> |
Reverse-mode AD over (F_θ(τ))ⁿ u₀ via binomial checkpointing |
CheckpointSchedule |
O(√n) default schedule (Griewank-Walther) |
RegionMap |
DoF-aligned region partition enabling K>1 per-region parameter sensitivity |
Scope: constant-a DiffusionChernoff<F> only. Multi-parameter (K>1)
sensitivity via ReverseChernoff::with_region_map. Variable-coefficient
kernels are deferred.
Tensor-train carrier
| Type |
Description |
TtChernoff<F> |
TT-Chernoff evolver; storage O(d·n·r²) |
TtState<F> |
Tensor-train state u(i₁,…,i_d) = G₁[i₁]·…·G_d[i_d] |
VarCoefTt<F> |
Variable-coefficient TT carrier for separable diagonal a_j(x_j) |
Scope: linear diagonal-A Gaussian class (TtChernoff); separable diagonal
variable coefficients (VarCoefTt). Non-separable variable coefficients and
off-diagonal A are research-track. VarCoefTt::new returns
SemiflowError::VarCoefOutOfClass when inputs fall outside the separable
diagonal class.
Gridless / particle-ensemble
| Type |
Description |
GridlessChernoff<F, const D> |
Particle-ensemble Chernoff; implements ChernoffFunction<F> |
MeasureState<F, D> |
Particle-ensemble state; exposes first_moment, variance, variance_per_axis diagnostics (math §38.12) |
ParticleReduction |
Particle cap policy: WeightedVoronoi { cap } or GaussianBackground |
Schrödinger, graph and quantum-graph operators
| Type |
Description |
SchrödingerChernoffComplex / SchrodingerChernoff |
Schrödinger equation on a native complex (or real-pair) carrier |
GraphHeatChernoff |
Heat semigroup of a graph Laplacian |
GraphKrylovChernoff, graph_expmv_frechet |
Depth-independent e^{−tL}·v (Chebyshev / Lanczos) and its all-parameter Fréchet gradient (graded-mesh Duhamel quadrature: quadrature error independent of λ_max·t; f64 conditioning floor ≈ (r+n)·u·ρ̄t; ImplicitEuler path keeps its O(Δt) bias) |
QuantumGraphHeatChernoff / QuantumSchrödingerChernoff |
Metric graphs with Kirchhoff vertex conditions |
Conservative, stiff and semilinear problems
| Type |
Description |
ConservativeDiffusionChernoff, assemble_conservative_csr_1d, assemble_conservative_csr_nd |
Divergence-form ∂ₓ(k(x)∂ₓu) with harmonic-mean faces (sharp material interfaces, optional contact resistance) |
MultilayerStack, multilayer_evolve, MassWeightedConservativeChernoff |
Stiff multilayer conduction in one depth-flat Krylov action |
SymmetricOperator, MassKOperator, EntrySensitivity, mass_lumped_evolve |
Externally assembled symmetric PSD sparse operators (FEM), the (M, K) problem without forming M⁻¹K, entry-wise gradients |
general_operator::GeneralOperator |
Possibly non-symmetric CSR operators via Taylor expmv or adaptive Arnoldi (expmv_krylov) |
SpdResolvent, SpdSolver |
(λM + A)⁻¹b: exact O(N) tridiagonal LDLᵀ, PCG with IC(0) or Jacobi; factor once, solve many (steady states, implicit steps) |
RationalKrylov, RationalKrylovChernoff, rational_krylov_expmv |
Shift-and-invert Krylov e^{−tM⁻¹A}v for very stiff SPD operators: solve count independent of λ_max t |
phi_action, phi_action_batched, phi_combination, Etdrk4, Nonlinearity |
Semilinear ∂ₜu = Lu + N(u) by exponential time differencing (order 4) |
phi_combination_krylov, PhiSolver, KrylovPhiOptions |
Adaptive Arnoldi φ-actions for non-symmetric generators (Etdrk4::with_phi_solver): 33× fewer generator applications than Taylor at τ‖G‖ = 4·10³ |
ReactionDiffusion, Species, ReactionSolver, RosenbrockOptions |
Multi-species ∂ₜu = D Δu + f(t, x, u) by Strang splitting on any grid engine; stiff kinetics by adaptive L-stable Rosenbrock (ROS2) |
CrossDiffusion |
Constant K×K cross-diffusion matrix D by exact modal splitting (D = PΛP⁻¹ verified; complex, negative or defective D rejected), with ReactionDiffusion::with_cross_diffusion |
SubordinatedChernoff |
Bochner–Phillips subordination of any engine |
HypoellipticChernoff |
Kolmogorov, Heisenberg and Engel (step-2/3 Carnot) operators |
Executor
ChernoffSemigroup<C, S> — wraps a ChernoffFunction and a step count;
call .evolve(t, &f) to compute (S(t/n))ⁿ f.
Examples
| Example |
Description |
heat_2d_demo.rs |
2D tensor heat equation (Gaussian oracle, convergence table) |
strang_advdiff_demo.rs |
Strang splitting for 1D advection-diffusion |
cev_european_call.rs |
CEV European option pricing vs Schroder (1989) closed form |
boundary_demo.rs |
Boundary policy showcase (Dirichlet, Neumann, periodic) |
latency_tail.rs |
HFT-style p99.9 latency benchmark |
resolvent_perf.rs |
LaplaceChernoffResolvent L-gate bench harness |
heston_pricer.rs |
Heston ρ→0 pricer via palindromic Strang |
sabr_pricer.rs |
SABR-on-H² via ManifoldChernoff<Hyperbolic2> |
graph_par_speedup.rs |
Parallel graph-heat speedup (--features parallel) |
rough_heston_pricer.rs |
Oracle-validated risk-neutral rough-Heston pricer (--rate/--price); honest scope: solver of a 4-factor Markov approximation, not validated vs true rough-Heston |
cargo run -p semiflow --example heat_2d_demo
Bindings
| Language |
Package |
Notes |
| Rust |
semiflow (docs.rs) |
The full engine catalogue; no_std + alloc |
| Python |
semiflow-pde |
90 classes and the functions version(), heisenberg_heat_kernel(), sample_gridfn2d(), edge_weight_grad(), edge_weight_grad_batched(), graph_expmv_frechet(), symmetric_op_expmv_frechet(), assemble_conservative_csr_1d(), mass_lumped_evolve(), phi_action(), phi_action_batched(), phi_combination(), shift1d_coeff_grad(), richardson_weights(); NumPy in/out; complete .pyi stubs; see the PyPI page |
| JavaScript / WASM |
@semiflow/wasm |
Lite build on npm; the heavy-grid engines need a --features full build; see the npm page |
| C / C++ |
semiflow-ffi |
extern "C" ABI with catch_unwind on every entry point; header semiflow.h |
The bindings mirror the user-facing engines, not the internal composition types
(AxisLift, StrangSplit, … are Rust-side building blocks). Variable
coefficients cross the language boundary as pre-sampled arrays (evaluated in
Rust, no call back into the host language during evolve); some classes also
accept a host-language callback (e.g. Heat1D.with_a_function in Python,
Heat1D.withAFunction in JS, smf_state_new_with_closure in C).
The Bindings guide has side-by-side examples.
Documentation
Accuracy and design
Every numerical claim is gated in CI against a closed-form or high-order
reference oracle (convergence-order and sup-norm tests). Design principles:
no_std + alloc core; 3 runtime dependencies; SIMD hot
paths isolated to src/simd/ (AVX2 on x86_64, NEON on aarch64, scalar
fallback elsewhere); bit-reproducible parallelism across thread counts; no
unsafe in the math kernels. Builds with and without simd agree to rounding
but not always bit for bit, so golden files are tied to one configuration.
How to cite
If you use SemiFlow in academic work, cite both the software and the theorem.
@software{volkov2026semiflow,
author = {Volkov, Ilia},
title = {{SemiFlow}: {Chernoff} Approximation of Operator Semigroups},
year = {2026},
version = {0.15.0-beta},
doi = {10.5281/zenodo.20837851},
url = {https://doi.org/10.5281/zenodo.20837851}
}
@article{Remizov2025,
author = {I. D. Remizov},
title = {Chernoff Approximations of the Solution of Linear ODE with Variable Coefficients},
journal = {Vladikavkaz Math. J.},
volume = {27},
number = {4},
pages = {124--135},
year = {2025},
doi = {10.46698/a3908-1212-5385-q}
}
The concept DOI 10.5281/zenodo.20837851
always resolves to the latest version; CITATION.cff has the
full entry (GitHub's "Cite this repository" reads it).
License
Dual-licensed under MIT or Apache-2.0, at your
option.
Author, contributing, security
SemiFlow is created and maintained by Ilia Volkov. See
CONTRIBUTING.md and CODE_OF_CONDUCT.md.
Report vulnerabilities privately as described in SECURITY.md —
not in public issues.