#![allow(clippy::empty_line_after_doc_comments)]
#![allow(clippy::doc_lazy_continuation)]
#![allow(clippy::redundant_closure)]
#![allow(clippy::field_reassign_with_default)]
#![allow(clippy::needless_range_loop)]
#![allow(clippy::manual_range_contains)]
#![allow(clippy::manual_is_multiple_of)]
#![allow(clippy::assign_op_pattern)]
pub mod error;
#[cfg(feature = "oxifft")]
pub mod oxifft_backend;
#[cfg(feature = "oxifft")]
pub mod oxifft_plan_cache;
pub use error::{FFTError, FFTResult};
#[cfg(feature = "rustfft-backend")]
pub mod plan_cache;
#[cfg(feature = "rustfft-backend")]
pub use plan_cache::{get_global_cache, init_global_cache, CacheStats, PlanCache};
pub mod worker_pool;
pub use worker_pool::{
get_global_pool, get_workers, init_global_pool, set_workers, with_workers, WorkerConfig,
WorkerPool, WorkerPoolInfo,
};
#[cfg(feature = "rustfft-backend")]
pub mod backend;
#[cfg(feature = "rustfft-backend")]
pub use backend::{
get_backend_info, get_backend_manager, get_backend_name, init_backend_manager, list_backends,
set_backend, BackendContext, BackendInfo, BackendManager, FftBackend,
};
#[cfg(feature = "rustfft-backend")]
pub mod context;
#[cfg(feature = "rustfft-backend")]
pub use context::{
fft_context, with_backend, with_fft_settings, without_cache, FftContext, FftContextBuilder,
FftSettingsGuard,
};
#[cfg(feature = "rustfft-backend")]
pub mod strided_fft;
#[cfg(feature = "rustfft-backend")]
pub use strided_fft::{fft_strided, fft_strided_complex, ifft_strided};
#[cfg(feature = "rustfft-backend")]
pub mod plan_serialization;
#[cfg(feature = "rustfft-backend")]
pub use plan_serialization::{PlanDatabaseStats, PlanInfo, PlanMetrics, PlanSerializationManager};
#[cfg(feature = "rustfft-backend")]
pub mod planning;
#[cfg(feature = "rustfft-backend")]
pub use planning::{
get_global_planner, init_global_planner, plan_ahead_of_time, AdvancedFftPlanner as FftPlanner,
FftPlan, FftPlanExecutor, PlanBuilder, PlannerBackend, PlanningConfig, PlanningStrategy,
};
#[cfg(feature = "rustfft-backend")]
pub mod planning_adaptive;
#[cfg(feature = "rustfft-backend")]
pub mod planning_parallel;
#[cfg(feature = "rustfft-backend")]
pub use planning_parallel::{
ParallelExecutor, ParallelPlanResult, ParallelPlanner, ParallelPlanningConfig,
};
#[cfg(feature = "rustfft-backend")]
pub mod auto_tuning;
#[cfg(feature = "rustfft-backend")]
pub use auto_tuning::{
auto_fft, auto_select_algorithm, AutoTuneConfig, AutoTuner, FftVariant, IntegratedAutoSelector,
SelectionResult, SelectionSource, SizeRange, SizeStep,
};
#[cfg(feature = "rustfft-backend")]
pub mod algorithm_selector;
#[cfg(feature = "rustfft-backend")]
pub use algorithm_selector::{
AlgorithmRecommendation, AlgorithmSelector, CacheInfo, FftAlgorithm, HardwareInfo,
InputCharacteristics, PerformanceEntry, PerformanceHistory, SelectionConfig, SimdCapabilities,
SizeCharacteristic,
};
#[cfg(feature = "rustfft-backend")]
pub mod performance_profiler;
#[cfg(feature = "rustfft-backend")]
pub use performance_profiler::{
estimate_fft_memory, AlgorithmComparison, Measurement, MemoryProfiler, PerformanceProfiler,
PerformanceReport, ProfileConfig, ProfileResult,
};
#[cfg(feature = "rustfft-backend")]
pub mod large_fft;
#[cfg(feature = "rustfft-backend")]
pub use large_fft::{LargeFft, LargeFftConfig, LargeFftMethod, LargeFftNd, LargeFftStats};
pub mod dct;
pub mod dst;
pub mod fft;
pub mod fht;
pub mod hfft;
pub mod rfft;
#[cfg(feature = "rustfft-backend")]
pub mod real_planner;
#[cfg(feature = "rustfft-backend")]
pub use real_planner::{ComplexToReal, RealFftPlanner, RealToComplex};
pub use dct::{dct, dct2, dct2_fft, dctn, idct, idct2, idct2_fft, idctn, DCTType};
pub use dst::{dst, dst2, dst2_fft, dstn, idst, idst2, idst2_fft, idstn, DSTType};
pub use fft::{fft, fft2, fftn, ifft, ifft2, ifftn};
pub use fht::{fht, fht_sample_points, fhtoffset, ifht};
pub use hfft::{hfft, hfft2, hfftn, ihfft, ihfft2, ihfftn};
#[cfg(feature = "parallel")]
pub use fft::{fft2_parallel, ifft2_parallel};
pub use rfft::{irfft, irfft2, irfftn, rfft, rfft2, rfftn};
pub use simd_fft::{
fft2_adaptive, fft2_simd, fft_adaptive, fft_simd, fftn_adaptive, fftn_simd, ifft2_adaptive,
ifft2_simd, ifft_adaptive, ifft_simd, ifftn_adaptive, ifftn_simd, simd_support_available,
};
pub mod simd_rfft;
pub use simd_rfft::{irfft_adaptive, irfft_simd, rfft_adaptive, rfft_simd};
pub mod helper;
pub use helper::{fftfreq, fftshift, ifftshift, next_fast_len, prev_fast_len, rfftfreq};
pub mod frft;
pub mod frft_dft;
pub mod frft_ozaktas;
#[cfg(feature = "rustfft-backend")]
pub mod nufft;
pub mod spectrogram;
pub mod waterfall;
pub use frft::{frft, frft_complex, frft_dft, frft_stable};
pub use spectrogram::{spectrogram, spectrogram_normalized, stft as spectrogram_stft};
pub use waterfall::{
apply_colormap, waterfall_3d, waterfall_lines, waterfall_mesh, waterfall_mesh_colored,
};
#[cfg(feature = "never")]
pub mod distributed;
pub mod gpu_kernel_stub;
#[cfg(feature = "never")]
pub mod optimized_fft;
#[cfg(feature = "never")]
pub mod signal_processing;
pub mod simd_fft;
pub mod sparse_fft;
pub mod sparse_fft_cuda_kernels;
pub mod sparse_fft_cuda_kernels_frequency_pruning;
pub mod sparse_fft_cuda_kernels_iterative;
pub mod sparse_fft_cuda_kernels_spectral_flatness;
pub mod sparse_fft_gpu;
pub mod sparse_fft_gpu_cuda;
pub mod sparse_fft_gpu_kernels;
pub mod sparse_fft_gpu_memory;
#[cfg(feature = "never")]
pub mod time_frequency;
#[cfg(feature = "never")]
pub use distributed::{
CommunicationPattern, DecompositionStrategy, DistributedConfig, DistributedFFT,
};
#[cfg(feature = "never")]
pub use optimized_fft::{OptimizationLevel, OptimizedConfig, OptimizedFFT};
#[cfg(feature = "never")]
pub use signal_processing::{
convolve, cross_correlate, design_fir_filter, fir_filter, frequency_filter, FilterSpec,
FilterType, FilterWindow,
};
pub use sparse_fft::WindowFunction;
pub use sparse_fft::{
adaptive_sparse_fft, frequency_pruning_sparse_fft, reconstruct_filtered,
reconstruct_high_resolution, reconstruct_spectrum, reconstruct_time_domain, sparse_fft,
sparse_fft2, sparse_fftn, spectral_flatness_sparse_fft,
};
pub use sparse_fft_cuda_kernels::{
execute_cuda_compressed_sensing_sparse_fft, execute_cuda_sublinear_sparse_fft,
CUDACompressedSensingSparseFFTKernel, CUDASublinearSparseFFTKernel, CUDAWindowKernel,
};
pub use sparse_fft_cuda_kernels_frequency_pruning::{
execute_cuda_frequency_pruning_sparse_fft, CUDAFrequencyPruningSparseFFTKernel,
};
pub use sparse_fft_cuda_kernels_iterative::{
execute_cuda_iterative_sparse_fft, CUDAIterativeSparseFFTKernel,
};
pub use sparse_fft_cuda_kernels_spectral_flatness::{
execute_cuda_spectral_flatness_sparse_fft, CUDASpectralFlatnessSparseFFTKernel,
};
pub use sparse_fft_gpu::{gpu_batch_sparse_fft, gpu_sparse_fft, GPUBackend};
pub use sparse_fft_gpu_cuda::{
cuda_batch_sparse_fft,
cuda_sparse_fft,
get_cuda_devices,
FftGpuContext,
GpuDeviceInfo,
};
pub use sparse_fft_gpu_kernels::{
execute_sparse_fft_kernel, GPUKernel, KernelConfig, KernelFactory, KernelImplementation,
KernelLauncher, KernelStats,
};
pub use sparse_fft_gpu_memory::{
get_global_memory_manager, init_global_memory_manager, memory_efficient_gpu_sparse_fft,
AllocationStrategy, BufferLocation, BufferType,
};
pub use sparse_fft_gpu_memory::{is_cuda_available, is_hip_available, is_sycl_available};
pub mod sparse_fft_multi_gpu;
pub use sparse_fft_multi_gpu::{
multi_gpu_sparse_fft, GPUDeviceInfo, MultiGPUConfig, MultiGPUSparseFFT, WorkloadDistribution,
};
pub mod sparse_fft_specialized_hardware;
pub use sparse_fft_specialized_hardware::{
specialized_hardware_sparse_fft, AcceleratorCapabilities, AcceleratorInfo, AcceleratorType,
HardwareAbstractionLayer, SpecializedHardwareManager,
};
pub mod sparse_fft_batch;
pub use sparse_fft_batch::{batch_sparse_fft, spectral_flatness_batch_sparse_fft, BatchConfig};
#[cfg(feature = "never")]
pub use time_frequency::{time_frequency_transform, TFConfig, TFTransform, WaveletType};
#[cfg(feature = "rustfft-backend")]
pub mod memory_efficient;
#[cfg(feature = "rustfft-backend")]
pub use memory_efficient::{
fft2_efficient, fft_inplace, fft_streaming, process_in_chunks, FftMode,
};
pub mod ndim_optimized;
pub use ndim_optimized::{fftn_memory_efficient, fftn_optimized, rfftn_optimized};
pub mod hartley;
pub use hartley::{dht, dht2, fht as hartley_fht, idht};
pub mod higher_order_dct_dst;
pub use higher_order_dct_dst::{
dct_v, dct_vi, dct_vii, dct_viii, dst_v, dst_vi, dst_vii, dst_viii, idct_v, idct_vi, idct_vii,
idct_viii, idst_v, idst_vi, idst_vii, idst_viii,
};
pub mod mdct;
pub use mdct::{imdct, imdst, mdct, mdct_overlap_add, mdst};
pub mod window;
pub use window::{apply_window, get_window, Window};
pub mod window_extended;
pub use window_extended::{
analyze_window, compare_windows, get_extended_window, visualize_window, ExtendedWindow,
WindowProperties,
};
pub mod hilbert;
pub use hilbert::{
analytic_signal, envelope, instantaneous_frequency, instantaneous_frequency_central,
instantaneous_phase, instantaneous_phase_unwrapped,
};
pub mod czt;
pub use czt::{czt, czt_points, zoom_fft, CZT};
pub mod czt_enhanced;
pub use czt_enhanced::{adaptive_zoom_fft, czt_convolve, iczt, EnhancedCZT, SpiralContour};
pub mod frft_enhanced;
pub use frft_enhanced::{
frft_eigenvector, frft_multi_angle, frft_omk, frft_omk_complex, optimal_frft_angle,
wvd_projection,
};
pub mod stft_enhanced;
pub use stft_enhanced::{
dolph_chebyshev_window, dpss_window, griffin_lim, istft, reassigned_spectrogram,
spectral_coherence, synchrosqueezing,
};
pub mod dct_dst_enhanced;
pub use dct_dst_enhanced::{
batch_dct2 as fast_batch_dct2, dequantized_idct, fast_dct1, fast_dct2, fast_dct3, fast_dct4,
fast_dst1, fast_dst2, fast_dst3, fast_dst4, imdct_stream, mdct_stream, quantized_dct,
};
pub mod hilbert_enhanced;
pub use hilbert_enhanced::{
analytic_signal_padded, degree_of_stationarity, eemd, emd, hht, hilbert_spectrum,
hilbert_transform, instantaneous_energy, marginal_spectrum, mean_frequency, teager_energy,
teager_esa, EMDConfig, EMDResult, EnvelopeMethod, HHTResult, HilbertSpectrum,
};
pub mod padding;
pub use padding::{
auto_pad_1d, auto_pad_complex, auto_pad_nd, remove_padding_1d, AutoPadConfig, PaddingMode,
};
pub mod bluestein;
pub mod butterfly;
pub use butterfly::{
butterfly2, butterfly4, butterfly8, direct_dft, direct_idft, generate_inverse_twiddle_table,
generate_twiddle_table, split_radix_butterfly,
};
pub mod cache_oblivious;
pub use cache_oblivious::{
cache_oblivious_fft, cache_oblivious_fft_with_config, cache_oblivious_ifft,
cache_oblivious_ifft_with_config, cache_oblivious_rfft, CacheObliviousConfig,
};
pub mod ring_buffer_stft;
pub use ring_buffer_stft::{
RingBufferStft, RingBufferStftConfig, StftFrame, StreamingSpectrogram,
WindowFunction as StftWindowFunction,
};
pub mod streaming;
pub use streaming::{streaming_spectrogram, StreamingFft, StreamingFftConfig, WindowType};
pub mod outofcore;
pub use outofcore::{small_fft2d, OutOfCoreConfig, OutOfCoreFft2D};
pub mod fft_plan;
pub use fft_plan::{
create_plan, deserialize_plan, execute_plan, serialize_plan, FftAlgorithm as PlanAlgorithm,
FftPlan as SerializableFftPlan, FftPlanConfig as SerializablePlanConfig, PlanNode,
};
pub mod adaptive_sparse_fft;
pub mod ambiguity;
pub mod compressed_sensing;
pub mod cyclostationary;
pub mod fractional;
pub mod gpu_fft;
pub mod ndim_fft;
pub mod quantum;
pub mod ramanujan;
pub mod shor;
pub mod wigner_ville;
pub mod scattering;
pub use scattering::{
FeatureNormalization, FilterBank, FilterBankConfig, JointScatteringFeatures, MorletWavelet,
ScatteringCoefficients, ScatteringConfig, ScatteringFeatureExtractor, ScatteringFeatures,
ScatteringOrder, ScatteringResult, ScatteringTransform, TimeFrequencyMode,
};
#[allow(clippy::too_many_arguments)]
#[allow(dead_code)]
pub fn stft<T>(
x: &[T],
window: Window,
nperseg: usize,
noverlap: Option<usize>,
nfft: Option<usize>,
fs: Option<f64>,
detrend: Option<bool>,
boundary: Option<&str>,
) -> FFTResult<(
Vec<f64>,
Vec<f64>,
scirs2_core::ndarray::Array2<scirs2_core::numeric::Complex64>,
)>
where
T: scirs2_core::numeric::NumCast + Copy + std::fmt::Debug,
{
spectrogram::stft(
x,
window,
nperseg,
noverlap,
nfft,
fs,
detrend,
Some(true),
boundary,
)
}
#[allow(dead_code)]
fn try_as_complex<U: 'static + Copy>(val: U) -> Option<scirs2_core::numeric::Complex64> {
use scirs2_core::numeric::Complex64;
use std::any::Any;
if let Some(_complex) = (&val as &dyn Any).downcast_ref::<Complex64>() {
return Some(*_complex);
}
if let Some(complex32) = (&val as &dyn Any).downcast_ref::<scirs2_core::numeric::Complex<f32>>()
{
return Some(Complex64::new(complex32.re as f64, complex32.im as f64));
}
None
}
#[allow(dead_code)]
pub fn hilbert<T>(x: &[T]) -> FFTResult<Vec<scirs2_core::numeric::Complex64>>
where
T: scirs2_core::numeric::NumCast + Copy + std::fmt::Debug + 'static,
{
use scirs2_core::numeric::{Complex64, NumCast};
let n = x.len();
let signal: Vec<f64> = x
.iter()
.map(|&val| {
if let Some(val_f64) = NumCast::from(val) {
return Ok(val_f64);
}
match try_as_complex(val) {
Some(c) => Ok(c.re),
None => Err(FFTError::ValueError(format!(
"Could not convert {val:?} to numeric type"
))),
}
})
.collect::<FFTResult<Vec<_>>>()?;
let spectrum = fft(&signal, None)?;
let mut h = vec![Complex64::new(1.0, 0.0); n];
if n.is_multiple_of(2) {
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| {
*val = Complex64::new(0.0, -2.0); });
h.iter_mut().skip(n / 2 + 1).for_each(|val| {
*val = Complex64::new(0.0, 0.0);
});
} else {
h[0] = Complex64::new(1.0, 0.0);
h.iter_mut().take(n.div_ceil(2)).skip(1).for_each(|val| {
*val = Complex64::new(0.0, -2.0); });
h.iter_mut().skip(n.div_ceil(2)).for_each(|val| {
*val = Complex64::new(0.0, 0.0);
});
}
let filtered_spectrum: Vec<Complex64> = spectrum
.iter()
.zip(h.iter())
.map(|(&s, &h)| s * h)
.collect();
let analytic_signal = ifft(&filtered_spectrum, None)?;
Ok(analytic_signal)
}
#[must_use]
#[allow(dead_code)]
pub fn fft_bounds(shape: &[usize]) -> Vec<(i32, i32)> {
shape
.iter()
.map(|&n| {
let n_i32 = i32::try_from(n).unwrap_or(i32::MAX);
let min = -(n_i32 / 2);
let max = n_i32 - 1 + min;
(min, max)
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
use std::f64::consts::PI;
#[test]
fn test_fft_bounds() {
let bounds = fft_bounds(&[4, 4]);
assert_eq!(bounds, vec![(-2, 1), (-2, 1)]);
let bounds = fft_bounds(&[5, 3]);
assert_eq!(bounds, vec![(-2, 2), (-1, 1)]);
let bounds = fft_bounds(&[6, 7, 8]);
assert_eq!(bounds, vec![(-3, 2), (-3, 3), (-4, 3)]);
}
#[test]
fn test_hilbert_transform() {
let n = 1000;
let freq = 5.0; let sample_rate = 100.0; let dt = 1.0 / sample_rate;
let t: Vec<f64> = (0..n).map(|i| i as f64 * dt).collect();
let signal: Vec<f64> = t.iter().map(|&ti| (2.0 * PI * freq * ti).cos()).collect();
let analytic = hilbert(&signal).expect("Operation failed");
let start_idx = n / 4;
let end_idx = 3 * n / 4;
for i in start_idx..end_idx {
let magnitude = (analytic[i].re.powi(2) + analytic[i].im.powi(2)).sqrt();
assert_relative_eq!(magnitude, 1.0, epsilon = 0.1);
if i > start_idx {
let phase_i = analytic[i].im.atan2(analytic[i].re);
let phase_i_prev = analytic[i - 1].im.atan2(analytic[i - 1].re);
let mut phase_diff = phase_i - phase_i_prev;
if phase_diff > PI {
phase_diff -= 2.0 * PI;
} else if phase_diff < -PI {
phase_diff += 2.0 * PI;
}
assert!(phase_diff > 0.0);
}
}
}
}
#[cfg(test)]
mod arm_fft_test;