1#![allow(clippy::empty_line_after_doc_comments)]
2#![allow(clippy::doc_lazy_continuation)]
3#![allow(clippy::redundant_closure)]
4#![allow(clippy::field_reassign_with_default)]
5#![allow(clippy::needless_range_loop)]
6#![allow(clippy::manual_range_contains)]
7#![allow(clippy::manual_is_multiple_of)]
8#![allow(clippy::assign_op_pattern)]
9pub mod error;
168
169#[cfg(feature = "oxifft")]
171pub mod oxifft_backend;
172
173#[cfg(feature = "oxifft")]
175pub mod oxifft_plan_cache;
176
177pub use error::{FFTError, FFTResult};
178
179pub mod plan_cache;
181pub use plan_cache::{get_global_cache, init_global_cache, CacheStats, PlanCache};
182
183pub mod worker_pool;
185pub use worker_pool::{
186 get_global_pool, get_workers, init_global_pool, set_workers, with_workers, WorkerConfig,
187 WorkerPool, WorkerPoolInfo,
188};
189
190pub mod backend;
192pub use backend::{
193 get_backend_info, get_backend_manager, get_backend_name, init_backend_manager, list_backends,
194 set_backend, BackendContext, BackendInfo, BackendManager, FftBackend,
195};
196
197pub mod context;
199pub use context::{
200 fft_context, with_backend, with_fft_settings, without_cache, FftContext, FftContextBuilder,
201 FftSettingsGuard,
202};
203
204pub mod strided_fft;
206pub use strided_fft::{fft_strided, fft_strided_complex, ifft_strided};
207
208pub mod plan_serialization;
210pub use plan_serialization::{PlanDatabaseStats, PlanInfo, PlanMetrics, PlanSerializationManager};
211
212pub mod planning;
214pub use planning::{
215 get_global_planner, init_global_planner, plan_ahead_of_time, AdvancedFftPlanner as FftPlanner,
216 FftPlan, FftPlanExecutor, PlanBuilder, PlannerBackend, PlanningConfig, PlanningStrategy,
217};
218
219pub mod planning_adaptive;
221
222pub mod planning_parallel;
224pub use planning_parallel::{
225 ParallelExecutor, ParallelPlanResult, ParallelPlanner, ParallelPlanningConfig,
226};
227
228pub mod auto_tuning;
230pub use auto_tuning::{
231 auto_fft, auto_select_algorithm, AutoTuneConfig, AutoTuner, FftVariant, IntegratedAutoSelector,
232 SelectionResult, SelectionSource, SizeRange, SizeStep,
233};
234
235pub mod algorithm_selector;
237pub use algorithm_selector::{
238 AlgorithmRecommendation, AlgorithmSelector, CacheInfo, FftAlgorithm, HardwareInfo,
239 InputCharacteristics, PerformanceEntry, PerformanceHistory, SelectionConfig, SimdCapabilities,
240 SizeCharacteristic,
241};
242
243pub mod performance_profiler;
245pub use performance_profiler::{
246 estimate_fft_memory, AlgorithmComparison, Measurement, MemoryProfiler, PerformanceProfiler,
247 PerformanceReport, ProfileConfig, ProfileResult,
248};
249
250pub mod large_fft;
252pub use large_fft::{LargeFft, LargeFftConfig, LargeFftMethod, LargeFftNd, LargeFftStats};
253
254pub mod dct;
265pub mod dst;
266pub mod fft;
267pub mod fht;
268pub mod hfft;
269pub mod rfft;
270
271pub mod real_planner;
273pub use real_planner::{ComplexToReal, RealFftPlanner, RealToComplex};
274
275pub use dct::{dct, dct2, dct2_fft, dctn, idct, idct2, idct2_fft, idctn, DCTType};
277pub use dst::{dst, dst2, dst2_fft, dstn, idst, idst2, idst2_fft, idstn, DSTType};
278pub use fft::{fft, fft2, fftn, ifft, ifft2, ifftn};
279pub use fht::{fht, fht_sample_points, fhtoffset, ifht};
280pub use hfft::{hfft, hfft2, hfftn, ihfft, ihfft2, ihfftn};
281
282#[cfg(feature = "parallel")]
284pub use fft::{fft2_parallel, ifft2_parallel};
285pub use rfft::{irfft, irfft2, irfftn, rfft, rfft2, rfftn};
286
287pub use simd_fft::{
289 fft2_adaptive, fft2_simd, fft_adaptive, fft_simd, fftn_adaptive, fftn_simd, ifft2_adaptive,
290 ifft2_simd, ifft_adaptive, ifft_simd, ifftn_adaptive, ifftn_simd, simd_support_available,
291};
292
293pub mod simd_rfft;
295pub use simd_rfft::{irfft_adaptive, irfft_simd, rfft_adaptive, rfft_simd};
296
297pub mod helper;
299pub use helper::{fftfreq, fftshift, ifftshift, next_fast_len, prev_fast_len, rfftfreq};
300
301pub mod frft;
303pub mod frft_dft;
304pub mod frft_ozaktas;
305pub mod nufft;
306pub mod spectrogram;
307pub mod waterfall;
308pub use frft::{frft, frft_complex, frft_dft, frft_stable};
309pub use spectrogram::{spectrogram, spectrogram_normalized, stft as spectrogram_stft};
310pub use waterfall::{
311 apply_colormap, waterfall_3d, waterfall_lines, waterfall_mesh, waterfall_mesh_colored,
312};
313
314#[cfg(feature = "never")]
316pub mod distributed;
317pub mod gpu_kernel_stub;
318#[cfg(feature = "never")]
319pub mod optimized_fft;
320#[cfg(feature = "never")]
321pub mod signal_processing;
322pub mod simd_fft;
323pub mod sparse_fft;
324pub mod sparse_fft_cuda_kernels;
325pub mod sparse_fft_cuda_kernels_frequency_pruning;
326pub mod sparse_fft_cuda_kernels_iterative;
327pub mod sparse_fft_cuda_kernels_spectral_flatness;
328pub mod sparse_fft_gpu;
329pub mod sparse_fft_gpu_cuda;
330pub mod sparse_fft_gpu_kernels;
331pub mod sparse_fft_gpu_memory;
332#[cfg(feature = "never")]
333pub mod time_frequency;
334#[cfg(feature = "never")]
335pub use distributed::{
336 CommunicationPattern, DecompositionStrategy, DistributedConfig, DistributedFFT,
337};
338#[cfg(feature = "never")]
339pub use optimized_fft::{OptimizationLevel, OptimizedConfig, OptimizedFFT};
340#[cfg(feature = "never")]
341pub use signal_processing::{
342 convolve, cross_correlate, design_fir_filter, fir_filter, frequency_filter, FilterSpec,
343 FilterType, FilterWindow,
344};
345pub use sparse_fft::WindowFunction;
346pub use sparse_fft::{
347 adaptive_sparse_fft, frequency_pruning_sparse_fft, reconstruct_filtered,
348 reconstruct_high_resolution, reconstruct_spectrum, reconstruct_time_domain, sparse_fft,
349 sparse_fft2, sparse_fftn, spectral_flatness_sparse_fft,
350};
351pub use sparse_fft_cuda_kernels::{
352 execute_cuda_compressed_sensing_sparse_fft, execute_cuda_sublinear_sparse_fft,
353 CUDACompressedSensingSparseFFTKernel, CUDASublinearSparseFFTKernel, CUDAWindowKernel,
354};
355pub use sparse_fft_cuda_kernels_frequency_pruning::{
356 execute_cuda_frequency_pruning_sparse_fft, CUDAFrequencyPruningSparseFFTKernel,
357};
358pub use sparse_fft_cuda_kernels_iterative::{
359 execute_cuda_iterative_sparse_fft, CUDAIterativeSparseFFTKernel,
360};
361pub use sparse_fft_cuda_kernels_spectral_flatness::{
362 execute_cuda_spectral_flatness_sparse_fft, CUDASpectralFlatnessSparseFFTKernel,
363};
364pub use sparse_fft_gpu::{gpu_batch_sparse_fft, gpu_sparse_fft, GPUBackend};
365pub use sparse_fft_gpu_cuda::{
366 cuda_batch_sparse_fft,
367 cuda_sparse_fft,
368 get_cuda_devices,
369 FftGpuContext,
370 GpuDeviceInfo,
371 };
373pub use sparse_fft_gpu_kernels::{
374 execute_sparse_fft_kernel, GPUKernel, KernelConfig, KernelFactory, KernelImplementation,
375 KernelLauncher, KernelStats,
376};
377pub use sparse_fft_gpu_memory::{
378 get_global_memory_manager, init_global_memory_manager, memory_efficient_gpu_sparse_fft,
379 AllocationStrategy, BufferLocation, BufferType,
380};
381pub use sparse_fft_gpu_memory::{is_cuda_available, is_hip_available, is_sycl_available};
382
383pub mod sparse_fft_multi_gpu;
385pub use sparse_fft_multi_gpu::{
386 multi_gpu_sparse_fft, GPUDeviceInfo, MultiGPUConfig, MultiGPUSparseFFT, WorkloadDistribution,
387};
388
389pub mod sparse_fft_specialized_hardware;
391pub use sparse_fft_specialized_hardware::{
392 specialized_hardware_sparse_fft, AcceleratorCapabilities, AcceleratorInfo, AcceleratorType,
393 HardwareAbstractionLayer, SpecializedHardwareManager,
394};
395pub mod sparse_fft_batch;
397pub use sparse_fft_batch::{batch_sparse_fft, spectral_flatness_batch_sparse_fft, BatchConfig};
398
399#[cfg(feature = "never")]
400pub use time_frequency::{time_frequency_transform, TFConfig, TFTransform, WaveletType};
401
402pub mod memory_efficient;
404pub use memory_efficient::{
405 fft2_efficient, fft_inplace, fft_streaming, process_in_chunks, FftMode,
406};
407
408pub mod ndim_optimized;
410pub use ndim_optimized::{fftn_memory_efficient, fftn_optimized, rfftn_optimized};
411
412pub mod ndim;
414pub use ndim::{fftshift2, ifftshift2};
415
416pub mod hartley;
418pub use hartley::{dht, dht2, fht as hartley_fht, idht};
419
420pub mod higher_order_dct_dst;
422pub use higher_order_dct_dst::{
423 dct_v, dct_vi, dct_vii, dct_viii, dst_v, dst_vi, dst_vii, dst_viii, idct_v, idct_vi, idct_vii,
424 idct_viii, idst_v, idst_vi, idst_vii, idst_viii,
425};
426
427pub mod mdct;
429pub use mdct::{imdct, imdst, mdct, mdct_overlap_add, mdst};
430
431pub mod window;
433pub use window::{apply_window, get_window, Window};
434
435pub mod window_extended;
437pub use window_extended::{
438 analyze_window, compare_windows, get_extended_window, visualize_window, ExtendedWindow,
439 WindowProperties,
440};
441
442pub mod hilbert;
444pub use hilbert::{
445 analytic_signal, envelope, instantaneous_frequency, instantaneous_frequency_central,
446 instantaneous_phase, instantaneous_phase_unwrapped,
447};
448
449pub mod czt;
451pub use czt::{czt, czt_points, zoom_fft, CZT};
452
453pub mod czt_enhanced;
455pub use czt_enhanced::{adaptive_zoom_fft, czt_convolve, iczt, EnhancedCZT, SpiralContour};
456
457pub mod frft_enhanced;
459pub use frft_enhanced::{
460 frft_eigenvector, frft_multi_angle, frft_omk, frft_omk_complex, optimal_frft_angle,
461 wvd_projection,
462};
463
464pub mod stft_enhanced;
466pub use stft_enhanced::{
467 dolph_chebyshev_window, dpss_window, griffin_lim, istft, reassigned_spectrogram,
468 spectral_coherence, synchrosqueezing,
469};
470
471pub mod dct_dst_enhanced;
473pub use dct_dst_enhanced::{
474 batch_dct2 as fast_batch_dct2, dequantized_idct, fast_dct1, fast_dct2, fast_dct3, fast_dct4,
475 fast_dst1, fast_dst2, fast_dst3, fast_dst4, imdct_stream, mdct_stream, quantized_dct,
476};
477
478pub mod hilbert_enhanced;
480pub use hilbert_enhanced::{
481 analytic_signal_padded, degree_of_stationarity, eemd, emd, hht, hilbert_spectrum,
482 hilbert_transform, instantaneous_energy, marginal_spectrum, mean_frequency, teager_energy,
483 teager_esa, EMDConfig, EMDResult, EnvelopeMethod, HHTResult, HilbertSpectrum,
484};
485
486pub mod padding;
488pub use padding::{
489 auto_pad_1d, auto_pad_complex, auto_pad_nd, remove_padding_1d, AutoPadConfig, PaddingMode,
490};
491
492pub mod bluestein;
494
495pub mod butterfly;
497pub use butterfly::{
498 butterfly2, butterfly4, butterfly8, direct_dft, direct_idft, generate_inverse_twiddle_table,
499 generate_twiddle_table, split_radix_butterfly,
500};
501
502pub mod cache_oblivious;
504pub use cache_oblivious::{
505 cache_oblivious_fft, cache_oblivious_fft_with_config, cache_oblivious_ifft,
506 cache_oblivious_ifft_with_config, cache_oblivious_rfft, CacheObliviousConfig,
507};
508
509pub mod ring_buffer_stft;
511pub use ring_buffer_stft::{
512 RingBufferStft, RingBufferStftConfig, StftFrame, StreamingSpectrogram,
513 WindowFunction as StftWindowFunction,
514};
515
516pub mod streaming;
518pub use streaming::{streaming_spectrogram, StreamingFft, StreamingFftConfig, WindowType};
519
520pub mod outofcore;
522pub use outofcore::{small_fft2d, OutOfCoreConfig, OutOfCoreFft2D};
523
524pub mod fft_plan;
526pub use fft_plan::{
527 create_plan, deserialize_plan, execute_plan, serialize_plan, FftAlgorithm as PlanAlgorithm,
528 FftPlan as SerializableFftPlan, FftPlanConfig as SerializablePlanConfig, PlanNode,
529};
530
531pub mod adaptive_sparse_fft;
533pub mod ambiguity;
535pub mod compressed_sensing;
537pub mod cyclostationary;
539pub mod fractional;
541pub mod gpu_fft;
543pub mod ndim_fft;
545pub mod quantum;
547pub mod ramanujan;
549pub mod shor;
551pub mod wigner_ville;
553
554pub mod wavelet_packets;
556pub use wavelet_packets::{
557 best_basis as wp_best_basis, wp_reconstruct, wpd, Wavelet as WaveletPacket, WaveletPacketNode,
558 WaveletPacketTree,
559};
560
561pub mod scattering;
563pub use scattering::{
564 FeatureNormalization, FilterBank, FilterBankConfig, JointScatteringFeatures, MorletWavelet,
565 ScatteringCoefficients, ScatteringConfig, ScatteringFeatureExtractor, ScatteringFeatures,
566 ScatteringOrder, ScatteringResult, ScatteringTransform, TimeFrequencyMode,
567};
568
569#[allow(clippy::too_many_arguments)]
621#[allow(dead_code)]
622pub fn stft<T>(
623 x: &[T],
624 window: Window,
625 nperseg: usize,
626 noverlap: Option<usize>,
627 nfft: Option<usize>,
628 fs: Option<f64>,
629 detrend: Option<bool>,
630 boundary: Option<&str>,
631) -> FFTResult<(
632 Vec<f64>,
633 Vec<f64>,
634 scirs2_core::ndarray::Array2<scirs2_core::numeric::Complex64>,
635)>
636where
637 T: scirs2_core::numeric::NumCast + Copy + std::fmt::Debug,
638{
639 spectrogram::stft(
640 x,
641 window,
642 nperseg,
643 noverlap,
644 nfft,
645 fs,
646 detrend,
647 Some(true),
648 boundary,
649 )
650}
651
652#[allow(dead_code)]
700fn try_as_complex<U: 'static + Copy>(val: U) -> Option<scirs2_core::numeric::Complex64> {
701 use scirs2_core::numeric::Complex64;
702 use std::any::Any;
703
704 if let Some(_complex) = (&val as &dyn Any).downcast_ref::<Complex64>() {
706 return Some(*_complex);
707 }
708
709 if let Some(complex32) = (&val as &dyn Any).downcast_ref::<scirs2_core::numeric::Complex<f32>>()
711 {
712 return Some(Complex64::new(complex32.re as f64, complex32.im as f64));
713 }
714
715 None
716}
717
718#[allow(dead_code)]
719pub fn hilbert<T>(x: &[T]) -> FFTResult<Vec<scirs2_core::numeric::Complex64>>
720where
721 T: scirs2_core::numeric::NumCast + Copy + std::fmt::Debug + 'static,
722{
723 use scirs2_core::numeric::{Complex64, NumCast};
724
725 let n = x.len();
727
728 let signal: Vec<f64> = x
730 .iter()
731 .map(|&val| {
732 if let Some(val_f64) = NumCast::from(val) {
734 return Ok(val_f64);
735 }
736
737 match try_as_complex(val) {
740 Some(c) => Ok(c.re),
741 None => Err(FFTError::ValueError(format!(
742 "Could not convert {val:?} to numeric type"
743 ))),
744 }
745 })
746 .collect::<FFTResult<Vec<_>>>()?;
747
748 let spectrum = fft(&signal, None)?;
750
751 let mut h = vec![Complex64::new(1.0, 0.0); n];
757
758 if n.is_multiple_of(2) {
759 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| {
765 *val = Complex64::new(0.0, -2.0); });
767
768 h.iter_mut().skip(n / 2 + 1).for_each(|val| {
770 *val = Complex64::new(0.0, 0.0);
771 });
772 } else {
773 h[0] = Complex64::new(1.0, 0.0); h.iter_mut().take(n.div_ceil(2)).skip(1).for_each(|val| {
778 *val = Complex64::new(0.0, -2.0); });
780
781 h.iter_mut().skip(n.div_ceil(2)).for_each(|val| {
783 *val = Complex64::new(0.0, 0.0);
784 });
785 }
786
787 let filtered_spectrum: Vec<Complex64> = spectrum
789 .iter()
790 .zip(h.iter())
791 .map(|(&s, &h)| s * h)
792 .collect();
793
794 let analytic_signal = ifft(&filtered_spectrum, None)?;
796
797 Ok(analytic_signal)
798}
799
800#[must_use]
822#[allow(dead_code)]
823pub fn fft_bounds(shape: &[usize]) -> Vec<(i32, i32)> {
824 shape
825 .iter()
826 .map(|&n| {
827 let n_i32 = i32::try_from(n).unwrap_or(i32::MAX);
829 let min = -(n_i32 / 2);
830 let max = n_i32 - 1 + min;
831 (min, max)
832 })
833 .collect()
834}
835
836#[cfg(test)]
837mod tests {
838 use super::*;
839 use approx::assert_relative_eq;
840 use std::f64::consts::PI;
841
842 #[test]
843 fn test_fft_bounds() {
844 let bounds = fft_bounds(&[4, 4]);
846 assert_eq!(bounds, vec![(-2, 1), (-2, 1)]);
847
848 let bounds = fft_bounds(&[5, 3]);
850 assert_eq!(bounds, vec![(-2, 2), (-1, 1)]);
851
852 let bounds = fft_bounds(&[6, 7, 8]);
854 assert_eq!(bounds, vec![(-3, 2), (-3, 3), (-4, 3)]);
855 }
856
857 #[test]
858 fn test_hilbert_transform() {
859 let n = 1000;
861 let freq = 5.0; let sample_rate = 100.0; let dt = 1.0 / sample_rate;
864
865 let t: Vec<f64> = (0..n).map(|i| i as f64 * dt).collect();
867 let signal: Vec<f64> = t.iter().map(|&ti| (2.0 * PI * freq * ti).cos()).collect();
868
869 let analytic = hilbert(&signal).expect("Operation failed");
871
872 let start_idx = n / 4;
876 let end_idx = 3 * n / 4;
877
878 for i in start_idx..end_idx {
879 let magnitude = (analytic[i].re.powi(2) + analytic[i].im.powi(2)).sqrt();
880 assert_relative_eq!(magnitude, 1.0, epsilon = 0.1);
881
882 if i > start_idx {
884 let phase_i = analytic[i].im.atan2(analytic[i].re);
885 let phase_i_prev = analytic[i - 1].im.atan2(analytic[i - 1].re);
886
887 let mut phase_diff = phase_i - phase_i_prev;
890 if phase_diff > PI {
891 phase_diff -= 2.0 * PI;
892 } else if phase_diff < -PI {
893 phase_diff += 2.0 * PI;
894 }
895
896 assert!(phase_diff > 0.0);
898 }
899 }
900 }
901}
902
903#[cfg(test)]
905mod arm_fft_test;