🐙 Poulpy-CPU-ARM
Poulpy-CPU-ARM is a Rust crate that provides a NEON / ASIMD accelerated CPU backend for Poulpy, targeting AArch64 (Apple Silicon, Neoverse, …).
This backend implements the Poulpy HAL extension traits and can be used by:
poulpy-halpoulpy-corepoulpy-ckks(backend wiring opt-in viaenable-ckks)poulpy-bin-fhethrough the crate's explicitenable-neonintegration feature
🚩 Safety and Requirements
To avoid illegal hardware instructions on non-AArch64 CPUs, this backend is opt-in and only builds when explicitly requested.
| Requirement | Status |
|---|---|
| Cargo feature flag | --features enable-neon must be enabled |
| CPU architecture | aarch64 |
| CPU target features | NEON / ASIMD (part of the AArch64 architectural baseline) |
If enable-neon is enabled but the target is not aarch64, the build fails immediately with a clear error message (compile_error! in lib.rs), rather than generating invalid binaries.
When enable-neon is not enabled, this crate is simply skipped and Poulpy automatically falls back to the portable poulpy-cpu-ref backend. This ensures that Poulpy's workspace remains portable on x86 hosts and CI runners.
⚙️ Building with the NEON backend enabled
NEON / ASIMD is part of the AArch64 baseline, so no RUSTFLAGS target-feature flag is required:
Running an example
Running benchmarks
Running tests
To include CKKS backend wiring in the NEON test build:
Basic Usage
This crate exposes two NEON-accelerated backends:
use ;
use ;
let log_n: usize = 10;
// f64 FFT backend (NEON)
let module: = new;
// Q120 NTT backend (NEON, CRT over four ~30-bit primes)
let module: = new;
Once compiled with enable-neon, both backends are usable anywhere Poulpy expects a backend type in the HAL/core/CKKS layers. poulpy-bin-fhe has a separate enable-neon feature for its current crate-local integration.
Numerical contract
- Integer / modular operations (
Znx*,I128BigOps,Ntt*,NttDFTExecute) are bit-exact againstpoulpy-cpu-ref. - FFT-domain operations (
ReimArith,Reim4*,I64Ops,ReimFFTExecute) match the reference within ULP tolerance — NEON kernels use FMA where the scalar reference does not.
See poulpy-hal/docs/backend_safety_contract.md for the full backend contract.
🤝 Contributors
To implement your own Poulpy backend (SIMD or accelerator):
- Define a backend struct and implement the
Backendtrait frompoulpy-hal. - For each HAL operation family, either call the blanket default or implement the OEP trait directly with a custom dispatch.
- For each
poulpy-coreoperation family, either call the correspondingimpl_*_defaults_full!macro to inherit the portable implementation, or implement the OEP trait directly to override it. - Optionally, do the same for
poulpy-ckksbehind a backend-ownedenable-ckksfeature using theimpl_ckks_*_defaults!macros or direct OEP trait implementations.
At every layer the macro and the direct implementation are mutually exclusive per operation family: the macro opts the backend into the portable default path, while a direct OEP impl replaces it entirely. There is no requirement to use the macros — a backend that needs full control can implement every OEP trait by hand.
Your backend will automatically integrate with the backend-generic layers:
poulpy-halpoulpy-corepoulpy-ckks
No modifications to those crates are required — the HAL provides the extension points. Only operations that need a faster implementation require explicit overrides; everything else is inherited from the default layer for free.
For questions or guidance, feel free to open an issue or discussion in the repository.