1#![allow(deprecated)]
2#![allow(clippy::empty_line_after_doc_comments)]
6#![allow(clippy::doc_lazy_continuation)]
7#![allow(clippy::redundant_closure)]
8#![allow(clippy::field_reassign_with_default)]
9#![allow(clippy::needless_range_loop)]
10#![allow(clippy::manual_range_contains)]
11pub mod error;
58pub use error::{FFTError, FFTResult};
59
60pub mod plan_cache;
62pub use plan_cache::{get_global_cache, init_global_cache, CacheStats, PlanCache};
63
64pub mod worker_pool;
66pub use worker_pool::{
67 get_global_pool, get_workers, init_global_pool, set_workers, with_workers, WorkerConfig,
68 WorkerPool, WorkerPoolInfo,
69};
70
71pub mod backend;
73pub use backend::{
74 get_backend_info, get_backend_manager, get_backend_name, init_backend_manager, list_backends,
75 set_backend, BackendContext, BackendInfo, BackendManager, FftBackend,
76};
77
78pub mod context;
80pub use context::{
81 fft_context, with_backend, with_fft_settings, without_cache, FftContext, FftContextBuilder,
82 FftSettingsGuard,
83};
84
85pub mod strided_fft;
87pub use strided_fft::{fft_strided, fft_strided_complex, ifft_strided};
88
89pub mod plan_serialization;
91pub use plan_serialization::{PlanDatabaseStats, PlanInfo, PlanMetrics, PlanSerializationManager};
92
93pub mod planning;
95pub use planning::{
96 get_global_planner, init_global_planner, plan_ahead_of_time, AdvancedFftPlanner as FftPlanner,
97 FftPlan, FftPlanExecutor, PlanBuilder, PlannerBackend, PlanningConfig, PlanningStrategy,
98};
99
100pub mod planning_adaptive;
102
103pub mod planning_parallel;
105pub use planning_parallel::{
106 ParallelExecutor, ParallelPlanResult, ParallelPlanner, ParallelPlanningConfig,
107};
108
109pub mod auto_tuning;
111pub use auto_tuning::{AutoTuneConfig, AutoTuner, FftVariant, SizeRange, SizeStep};
112
113pub mod dct;
124pub mod dst;
125pub mod fft;
126pub mod fht;
127pub mod hfft;
128pub mod rfft;
129
130pub use dct::{dct, dct2, dctn, idct, idct2, idctn, DCTType};
132pub use dst::{dst, dst2, dstn, idst, idst2, idstn, DSTType};
133pub use fft::{fft, fft2, fftn, ifft, ifft2, ifftn};
134pub use fht::{fht, fht_sample_points, fhtoffset, ifht};
135pub use hfft::{hfft, hfft2, hfftn, ihfft, ihfft2, ihfftn};
136
137#[cfg(feature = "parallel")]
139pub use fft::{fft2_parallel, ifft2_parallel};
140pub use rfft::{irfft, irfft2, irfftn, rfft, rfft2, rfftn};
141
142pub use simd_fft::{
144 fft2_adaptive, fft2_simd, fft_adaptive, fft_simd, fftn_adaptive, fftn_simd, ifft2_adaptive,
145 ifft2_simd, ifft_adaptive, ifft_simd, ifftn_adaptive, ifftn_simd, simd_support_available,
146};
147
148pub mod simd_rfft;
150pub use simd_rfft::{irfft_adaptive, irfft_simd, rfft_adaptive, rfft_simd};
151
152pub mod helper;
154pub use helper::{fftfreq, fftshift, ifftshift, next_fast_len, prev_fast_len, rfftfreq};
155
156pub mod frft;
158pub mod frft_dft;
159pub mod frft_ozaktas;
160pub mod nufft;
161pub mod spectrogram;
162pub mod waterfall;
163pub use frft::{frft, frft_complex, frft_dft, frft_stable};
164pub use spectrogram::{spectrogram, spectrogram_normalized, stft as spectrogram_stft};
165pub use waterfall::{
166 apply_colormap, waterfall_3d, waterfall_lines, waterfall_mesh, waterfall_mesh_colored,
167};
168
169#[cfg(feature = "never")]
171pub mod distributed;
172pub mod gpu_kernel_stub;
173#[cfg(feature = "never")]
174pub mod optimized_fft;
175#[cfg(feature = "never")]
176pub mod signal_processing;
177pub mod simd_fft;
178pub mod sparse_fft;
179pub mod sparse_fft_cuda_kernels;
180pub mod sparse_fft_cuda_kernels_frequency_pruning;
181pub mod sparse_fft_cuda_kernels_iterative;
182pub mod sparse_fft_cuda_kernels_spectral_flatness;
183pub mod sparse_fft_gpu;
184pub mod sparse_fft_gpu_cuda;
185pub mod sparse_fft_gpu_kernels;
186pub mod sparse_fft_gpu_memory;
187#[cfg(feature = "never")]
188pub mod time_frequency;
189#[cfg(feature = "never")]
190pub use distributed::{
191 CommunicationPattern, DecompositionStrategy, DistributedConfig, DistributedFFT,
192};
193#[cfg(feature = "never")]
194pub use optimized_fft::{OptimizationLevel, OptimizedConfig, OptimizedFFT};
195#[cfg(feature = "never")]
196pub use signal_processing::{
197 convolve, cross_correlate, design_fir_filter, fir_filter, frequency_filter, FilterSpec,
198 FilterType, FilterWindow,
199};
200pub use sparse_fft::WindowFunction;
201pub use sparse_fft::{
202 adaptive_sparse_fft, frequency_pruning_sparse_fft, reconstruct_filtered,
203 reconstruct_high_resolution, reconstruct_spectrum, reconstruct_time_domain, sparse_fft,
204 sparse_fft2, sparse_fftn, spectral_flatness_sparse_fft,
205};
206pub use sparse_fft_cuda_kernels::{
207 execute_cuda_compressed_sensing_sparse_fft, execute_cuda_sublinear_sparse_fft,
208 CUDACompressedSensingSparseFFTKernel, CUDASublinearSparseFFTKernel, CUDAWindowKernel,
209};
210pub use sparse_fft_cuda_kernels_frequency_pruning::{
211 execute_cuda_frequency_pruning_sparse_fft, CUDAFrequencyPruningSparseFFTKernel,
212};
213pub use sparse_fft_cuda_kernels_iterative::{
214 execute_cuda_iterative_sparse_fft, CUDAIterativeSparseFFTKernel,
215};
216pub use sparse_fft_cuda_kernels_spectral_flatness::{
217 execute_cuda_spectral_flatness_sparse_fft, CUDASpectralFlatnessSparseFFTKernel,
218};
219pub use sparse_fft_gpu::{gpu_batch_sparse_fft, gpu_sparse_fft, GPUBackend};
220pub use sparse_fft_gpu_cuda::{
221 cuda_batch_sparse_fft,
222 cuda_sparse_fft,
223 get_cuda_devices,
224 FftGpuContext,
225 GpuDeviceInfo,
226 };
228pub use sparse_fft_gpu_kernels::{
229 execute_sparse_fft_kernel, GPUKernel, KernelConfig, KernelFactory, KernelImplementation,
230 KernelLauncher, KernelStats,
231};
232pub use sparse_fft_gpu_memory::{
233 get_global_memory_manager, init_global_memory_manager, memory_efficient_gpu_sparse_fft,
234 AllocationStrategy, BufferLocation, BufferType,
235};
236pub use sparse_fft_gpu_memory::{is_cuda_available, is_hip_available, is_sycl_available};
237
238pub mod sparse_fft_multi_gpu;
240pub use sparse_fft_multi_gpu::{
241 multi_gpu_sparse_fft, GPUDeviceInfo, MultiGPUConfig, MultiGPUSparseFFT, WorkloadDistribution,
242};
243
244pub mod sparse_fft_specialized_hardware;
246pub use sparse_fft_specialized_hardware::{
247 specialized_hardware_sparse_fft, AcceleratorCapabilities, AcceleratorInfo, AcceleratorType,
248 HardwareAbstractionLayer, SpecializedHardwareManager,
249};
250pub mod sparse_fft_batch;
252pub use sparse_fft_batch::{batch_sparse_fft, spectral_flatness_batch_sparse_fft, BatchConfig};
253
254#[cfg(feature = "never")]
255pub use time_frequency::{time_frequency_transform, TFConfig, TFTransform, WaveletType};
256
257pub mod memory_efficient;
259pub use memory_efficient::{
260 fft2_efficient, fft_inplace, fft_streaming, process_in_chunks, FftMode,
261};
262
263pub mod ndim_optimized;
265pub use ndim_optimized::{fftn_memory_efficient, fftn_optimized, rfftn_optimized};
266
267pub mod hartley;
269pub use hartley::{dht, dht2, fht as hartley_fht, idht};
270
271pub mod higher_order_dct_dst;
273pub use higher_order_dct_dst::{
274 dct_v, dct_vi, dct_vii, dct_viii, dst_v, dst_vi, dst_vii, dst_viii, idct_v, idct_vi, idct_vii,
275 idct_viii, idst_v, idst_vi, idst_vii, idst_viii,
276};
277
278pub mod mdct;
280pub use mdct::{imdct, imdst, mdct, mdct_overlap_add, mdst};
281
282pub mod window;
284pub use window::{apply_window, get_window, Window};
285
286pub mod window_extended;
288pub use window_extended::{
289 analyze_window, compare_windows, get_extended_window, visualize_window, ExtendedWindow,
290 WindowProperties,
291};
292
293pub mod czt;
295pub use czt::{czt, czt_points, zoom_fft, CZT};
296
297pub mod padding;
299pub use padding::{
300 auto_pad_1d, auto_pad_complex, auto_pad_nd, remove_padding_1d, AutoPadConfig, PaddingMode,
301};
302
303#[allow(clippy::too_many_arguments)]
355#[allow(dead_code)]
356pub fn stft<T>(
357 x: &[T],
358 window: Window,
359 nperseg: usize,
360 noverlap: Option<usize>,
361 nfft: Option<usize>,
362 fs: Option<f64>,
363 detrend: Option<bool>,
364 boundary: Option<&str>,
365) -> FFTResult<(Vec<f64>, Vec<f64>, ndarray::Array2<num_complex::Complex64>)>
366where
367 T: num_traits::NumCast + Copy + std::fmt::Debug,
368{
369 spectrogram::stft(
370 x,
371 window,
372 nperseg,
373 noverlap,
374 nfft,
375 fs,
376 detrend,
377 Some(true),
378 boundary,
379 )
380}
381
382#[allow(dead_code)]
430fn try_as_complex<U: 'static + Copy>(val: U) -> Option<num_complex::Complex64> {
431 use num_complex::Complex64;
432 use std::any::Any;
433
434 if let Some(_complex) = (&val as &dyn Any).downcast_ref::<Complex64>() {
436 return Some(*_complex);
437 }
438
439 if let Some(complex32) = (&val as &dyn Any).downcast_ref::<num_complex::Complex<f32>>() {
441 return Some(Complex64::new(complex32.re as f64, complex32.im as f64));
442 }
443
444 None
445}
446
447#[allow(dead_code)]
448pub fn hilbert<T>(x: &[T]) -> FFTResult<Vec<num_complex::Complex64>>
449where
450 T: num_traits::NumCast + Copy + std::fmt::Debug + 'static,
451{
452 use num_complex::Complex64;
453
454 let n = x.len();
456
457 let signal: Vec<f64> = x
459 .iter()
460 .map(|&val| {
461 if let Some(val_f64) = num_traits::cast::<T, f64>(val) {
463 return Ok(val_f64);
464 }
465
466 match try_as_complex(val) {
469 Some(c) => Ok(c.re),
470 None => Err(FFTError::ValueError(format!(
471 "Could not convert {val:?} to numeric type"
472 ))),
473 }
474 })
475 .collect::<FFTResult<Vec<_>>>()?;
476
477 let spectrum = fft(&signal, None)?;
479
480 let mut h = vec![Complex64::new(1.0, 0.0); n];
486
487 if n % 2 == 0 {
488 h[0] = Complex64::new(1.0, 0.0); h[n / 2] = Complex64::new(1.0, 0.0); h.iter_mut().take(n / 2).skip(1).for_each(|val| {
494 *val = Complex64::new(0.0, -2.0); });
496
497 h.iter_mut().skip(n / 2 + 1).for_each(|val| {
499 *val = Complex64::new(0.0, 0.0);
500 });
501 } else {
502 h[0] = Complex64::new(1.0, 0.0); h.iter_mut().take(n.div_ceil(2)).skip(1).for_each(|val| {
507 *val = Complex64::new(0.0, -2.0); });
509
510 h.iter_mut().skip(n.div_ceil(2)).for_each(|val| {
512 *val = Complex64::new(0.0, 0.0);
513 });
514 }
515
516 let filtered_spectrum: Vec<Complex64> = spectrum
518 .iter()
519 .zip(h.iter())
520 .map(|(&s, &h)| s * h)
521 .collect();
522
523 let analytic_signal = ifft(&filtered_spectrum, None)?;
525
526 Ok(analytic_signal)
527}
528
529#[must_use]
551#[allow(dead_code)]
552pub fn fft_bounds(shape: &[usize]) -> Vec<(i32, i32)> {
553 shape
554 .iter()
555 .map(|&n| {
556 let n_i32 = i32::try_from(n).unwrap_or(i32::MAX);
558 let min = -(n_i32 / 2);
559 let max = n_i32 - 1 + min;
560 (min, max)
561 })
562 .collect()
563}
564
565#[cfg(test)]
566mod tests {
567 use super::*;
568 use approx::assert_relative_eq;
569 use std::f64::consts::PI;
570
571 #[test]
572 fn test_fft_bounds() {
573 let bounds = fft_bounds(&[4, 4]);
575 assert_eq!(bounds, vec![(-2, 1), (-2, 1)]);
576
577 let bounds = fft_bounds(&[5, 3]);
579 assert_eq!(bounds, vec![(-2, 2), (-1, 1)]);
580
581 let bounds = fft_bounds(&[6, 7, 8]);
583 assert_eq!(bounds, vec![(-3, 2), (-3, 3), (-4, 3)]);
584 }
585
586 #[test]
587 fn test_hilbert_transform() {
588 let n = 1000;
590 let freq = 5.0; let sample_rate = 100.0; let dt = 1.0 / sample_rate;
593
594 let t: Vec<f64> = (0..n).map(|i| i as f64 * dt).collect();
596 let signal: Vec<f64> = t.iter().map(|&ti| (2.0 * PI * freq * ti).cos()).collect();
597
598 let analytic = hilbert(&signal).unwrap();
600
601 let start_idx = n / 4;
605 let end_idx = 3 * n / 4;
606
607 for i in start_idx..end_idx {
608 let magnitude = (analytic[i].re.powi(2) + analytic[i].im.powi(2)).sqrt();
609 assert_relative_eq!(magnitude, 1.0, epsilon = 0.1);
610
611 if i > start_idx {
613 let phase_i = analytic[i].im.atan2(analytic[i].re);
614 let phase_i_prev = analytic[i - 1].im.atan2(analytic[i - 1].re);
615
616 let mut phase_diff = phase_i - phase_i_prev;
619 if phase_diff > PI {
620 phase_diff -= 2.0 * PI;
621 } else if phase_diff < -PI {
622 phase_diff += 2.0 * PI;
623 }
624
625 assert!(phase_diff > 0.0);
627 }
628 }
629 }
630}
631
632#[cfg(test)]
634mod arm_fft_test;