ezmsg.sigproc.filter#
Core IIR/FIR filtering infrastructure with BA and SOS coefficient support.
Functions
Classes
- class BaseFilterByDesignTransformerUnit(*args, settings=None, **kwargs)[source]#
Bases:
BaseTransformerUnit[SettingsType,AxisArray,AxisArray,FilterByDesignTransformer],Generic[SettingsType,TransformerType]- Parameters:
settings (Settings | None)
- class Filter(*args, settings=None, **kwargs)[source]#
Bases:
BaseTransformerUnit[FilterSettings,AxisArray,AxisArray,FilterTransformer]- Parameters:
settings (Settings | None)
- SETTINGS#
alias of
FilterSettings
- class FilterBaseSettings(axis: str | None = None, coef_type: str = 'ba', use_fast_sosfilt: bool = True, use_mlx_metal: bool = True, mlx_metal_chunk_sizes: tuple[int, ...] = (512,), thread_min_bytes: int = 1048576, fir_fft_min_taps: int = 64)[source]#
Bases:
Settings- Parameters:
- use_fast_sosfilt: bool = True#
If True (default), numpy SOS filtering calls scipy’s Cython kernel directly instead of going through
scipy.signal.sosfilt.Output is bit-identical – same kernel, same dtype promotion, same order of operations – so this is purely a latency optimization. It removes scipy’s fixed ~12-18 us of per-call validation and bookkeeping, which is invisible on offline-sized chunks but dominant online: measured 59.9% of the call at 16 channels x 30 samples, 51.0% at 32x30, 16.6% at 256x30, 4.1% at 256x128.
The fast path depends on a private scipy entry point. It is verified against the public function once per process, and falls back automatically if the private kernel is missing or disagrees. Set False to force the public path.
- use_mlx_metal: bool = True#
If True (default), SOS filtering on MLX inputs runs on the GPU via the bundled Metal kernel (
sosfilt_mlx_metal) instead of round-tripping through scipy. Set to False to fall back to the scipy path (bit-exact with numpy at the cost of CPU round-trips and ~5-8x slowdown).
- mlx_metal_chunk_sizes: tuple[int, ...] = (512,)#
Allowable compile-time chunk sizes for SOS Metal kernels. The smallest size that fits the remaining samples is selected on each launch; otherwise the largest size is repeated. Specializations compile lazily on first use. Values must be in
[1, 512].
- thread_min_bytes: int = 1048576#
Chunk size (bytes) at or above which scipy IIR filtering is split across threads on a non-sample axis. Channels are independent recurrences, so the result is bit-identical to the single-threaded call.
Only large chunks benefit: scipy’s filters are single-threaded C loops, and below ~1 MB the dispatch cost makes threading a net loss (measured 0.23x at 30 samples x 256 channels, 4.5x at 8192). The default keeps online-sized chunks single-threaded while letting offline blocks use the cores. Set to 0 to disable threading entirely.
- fir_fft_min_taps: int = 64#
Tap count at or above which a numpy FIR filter is applied by FFT convolution instead of scipy.ndimage’s C-level correlation.
Neither wins everywhere. Measured on 256 channels (best FFT vs ndimage), the FFT is 1.3-6.8x faster at 129-513 taps while ndimage is 1.6-3.0x faster at 17-33 taps, with the crossover near 64 taps.
The two differ in more than speed, and the difference matters if you rely on an offline run reproducing an online one exactly:
The time-domain path is chunk-size invariant – filtering in chunks of 250, chunks of 333, or one shot gives bit-identical output.
The FFT path is not. The transform length depends on the chunk length, so a different chunking gives a different rounding: measured 2.6e-07 relative in float32 (~7e-16 in float64).
Both match scipy’s
lfilterto roundoff, so this is about reproducibility across chunkings rather than accuracy. Set this very high to force the time-domain path everywhere and keep FIR output chunk-invariant.
- __init__(axis=None, coef_type='ba', use_fast_sosfilt=True, use_mlx_metal=True, mlx_metal_chunk_sizes=(512,), thread_min_bytes=1048576, fir_fft_min_taps=64)#
- class FilterByDesignTransformer(*args, **kwargs)[source]#
Bases:
BaseStatefulTransformer[SettingsType,AxisArray,AxisArray,FilterByDesignState],ABC,Generic[SettingsType,FilterCoefsType]Abstract base class for filter design transformers.
- class FilterSettings(axis: str | None = None, coef_type: str = 'ba', use_fast_sosfilt: bool = True, use_mlx_metal: bool = True, mlx_metal_chunk_sizes: tuple[int, ...] = (512,), thread_min_bytes: int = 1048576, fir_fft_min_taps: int = 64, coefs: FilterCoefficients | None = None)[source]#
Bases:
FilterBaseSettings- Parameters:
- coefs: FilterCoefficients | None = None#
The pre-calculated filter coefficients.
- __init__(axis=None, coef_type='ba', use_fast_sosfilt=True, use_mlx_metal=True, mlx_metal_chunk_sizes=(512,), thread_min_bytes=1048576, fir_fft_min_taps=64, coefs=None)#
- class FilterTransformer(*args, **kwargs)[source]#
Bases:
BaseStatefulTransformer[FilterSettings,AxisArray,AxisArray,FilterState]Filter data using the provided coefficients.