pub mod c2c_inplace;
pub mod r2r_inplace;
pub use c2c_inplace::{C2CDirection, C2CPlan};
pub use r2r_inplace::{R2RPlan, R2RPlanKind};
use std::{
ffi::c_void,
marker::PhantomData,
ops::Deref,
sync::{LazyLock, LockResult, Mutex, PoisonError, RwLockReadGuard, RwLockWriteGuard},
};
use crate::{
Array, BaseItem, BorrowAtRuntime, BorrowAtRuntimeMut, RawAccessMut, RlstScalar, Shape,
StableRawAccessMut, Stride, c32, c64, dense::array::shared_array::ArrayAtomicSharedRef,
};
use fftw_sys;
use num::Complex;
static FFTW_PLAN_INTERFACE: LazyLock<Mutex<FftwPlanInterface>> =
LazyLock::new(|| Mutex::new(FftwPlanInterface::default()));
#[derive(Copy, Clone, Debug)]
pub enum FftPrecision {
Single,
Double,
}
#[derive(Copy, Clone, Debug)]
#[repr(u32)]
pub enum FftwPlanFlags {
Estimate = fftw_sys::FFTW_ESTIMATE,
Measure = fftw_sys::FFTW_MEASURE,
Patient = fftw_sys::FFTW_PATIENT,
Exhaustive = fftw_sys::FFTW_EXHAUSTIVE,
DestroyInput = fftw_sys::FFTW_DESTROY_INPUT,
WisdomOnly = fftw_sys::FFTW_WISDOM_ONLY,
Unaligned = fftw_sys::FFTW_UNALIGNED,
}
pub trait FftPlanInplace {
fn execute(&mut self);
}
struct FftwPlanPtrType<T> {
_marker: PhantomData<T>,
}
trait FftwPlanPtrTypeTrait {
type PtrType;
fn get_fftw_ptr(ptr: *mut c_void) -> Self::PtrType;
}
impl FftwPlanPtrTypeTrait for FftwPlanPtrType<f64> {
type PtrType = fftw_sys::fftw_plan;
fn get_fftw_ptr(ptr: *mut c_void) -> Self::PtrType {
ptr as fftw_sys::fftw_plan
}
}
impl FftwPlanPtrTypeTrait for FftwPlanPtrType<f32> {
type PtrType = fftw_sys::fftwf_plan;
fn get_fftw_ptr(ptr: *mut c_void) -> Self::PtrType {
ptr as fftw_sys::fftwf_plan
}
}
struct FftwPlanPtr<T>
where
T: 'static,
FftwPlanPtrType<T>: FftwPlanPtrTypeTrait,
{
ptr: *mut c_void,
_marker: std::marker::PhantomData<T>,
}
trait PlanInterfaceTrait {
type PlanPtr;
type RealType;
unsafe fn destroy_plan(&self, plan_ptr: Self::PlanPtr);
unsafe fn create_c2c(
&self,
rank: i32,
shape: *const i32,
arr_in: *mut Complex<Self::RealType>,
arr_out: *mut Complex<Self::RealType>,
sign: i32,
flags: u32,
) -> Self::PlanPtr;
unsafe fn create_r2r(
&self,
rank: i32,
shape: *const i32,
arr_in: *mut Self::RealType,
arr_out: *mut Self::RealType,
kind: &[R2RPlanKind],
flags: u32,
) -> Self::PlanPtr;
}
impl<T> Drop for FftwPlanPtr<T>
where
T: 'static,
FftwPlanPtrType<T>: FftwPlanPtrTypeTrait,
{
fn drop(&mut self) {
if coe::is_same::<T, f64>() {
let ptr = FftwPlanPtrType::<f64>::get_fftw_ptr(self.ptr);
unsafe {
FFTW_PLAN_INTERFACE
.lock()
.unwrap()
.fftw_double
.destroy_plan(ptr);
};
} else if coe::is_same::<T, f32>() {
let ptr = FftwPlanPtrType::<f32>::get_fftw_ptr(self.ptr);
unsafe {
FFTW_PLAN_INTERFACE
.lock()
.unwrap()
.fftw_single
.destroy_plan(ptr)
};
}
}
}
struct ConcretePlanInterface<T> {
_marker: PhantomData<T>,
}
impl<T> Default for ConcretePlanInterface<T> {
fn default() -> Self {
Self {
_marker: Default::default(),
}
}
}
impl PlanInterfaceTrait for ConcretePlanInterface<f64> {
type PlanPtr = fftw_sys::fftw_plan;
type RealType = f64;
unsafe fn destroy_plan(&self, plan_ptr: Self::PlanPtr) {
unsafe {
fftw_sys::fftw_destroy_plan(plan_ptr);
}
}
unsafe fn create_c2c(
&self,
rank: i32,
shape: *const i32,
arr_in: *mut Complex<Self::RealType>,
arr_out: *mut Complex<Self::RealType>,
sign: i32,
flags: u32,
) -> Self::PlanPtr {
unsafe { fftw_sys::fftw_plan_dft(rank, shape, arr_in, arr_out, sign, flags) }
}
unsafe fn create_r2r(
&self,
rank: i32,
shape: *const i32,
arr_in: *mut Self::RealType,
arr_out: *mut Self::RealType,
kind: &[R2RPlanKind],
flags: u32,
) -> Self::PlanPtr {
unsafe { fftw_sys::fftw_plan_r2r(rank, shape, arr_in, arr_out, kind.as_ptr(), flags) }
}
}
impl PlanInterfaceTrait for ConcretePlanInterface<f32> {
type PlanPtr = fftw_sys::fftwf_plan;
type RealType = f32;
unsafe fn destroy_plan(&self, plan_ptr: Self::PlanPtr) {
unsafe {
fftw_sys::fftwf_destroy_plan(plan_ptr);
}
}
unsafe fn create_c2c(
&self,
rank: i32,
shape: *const i32,
arr_in: *mut Complex<Self::RealType>,
arr_out: *mut Complex<Self::RealType>,
sign: i32,
flags: u32,
) -> Self::PlanPtr {
unsafe { fftw_sys::fftwf_plan_dft(rank, shape, arr_in, arr_out, sign, flags) }
}
unsafe fn create_r2r(
&self,
rank: i32,
shape: *const i32,
arr_in: *mut Self::RealType,
arr_out: *mut Self::RealType,
kind: &[R2RPlanKind],
flags: u32,
) -> Self::PlanPtr {
unsafe { fftw_sys::fftwf_plan_r2r(rank, shape, arr_in, arr_out, kind.as_ptr(), flags) }
}
}
#[derive(Default)]
struct FftwPlanInterface {
pub fftw_single: ConcretePlanInterface<f32>,
pub fftw_double: ConcretePlanInterface<f64>,
}
#[allow(private_bounds)]
pub struct PlanInplace<Item, ArrayImpl, const NDIM: usize>
where
Item: RlstScalar + 'static,
FftwPlanPtrType<Item::Real>: FftwPlanPtrTypeTrait,
{
shared_ref: ArrayAtomicSharedRef<ArrayImpl, NDIM>,
plan: FftwPlanPtr<Item::Real>,
_marker: std::marker::PhantomData<Item>,
}
#[allow(private_interfaces, private_bounds)]
impl<Item, ArrayImpl, const NDIM: usize> PlanInplace<Item, ArrayImpl, NDIM>
where
Item: RlstScalar + 'static,
FftwPlanPtrType<Item::Real>: FftwPlanPtrTypeTrait,
{
pub fn new(
shared_ref: ArrayAtomicSharedRef<ArrayImpl, NDIM>,
plan: FftwPlanPtr<Item::Real>,
) -> Self {
Self {
shared_ref,
plan,
_marker: PhantomData,
}
}
}
macro_rules! impl_plan_inplace {
($dtype:ty, $execute:ident) => {
impl<ArrayImpl, const NDIM: usize> FftPlanInplace for PlanInplace<$dtype, ArrayImpl, NDIM>
where
ArrayImpl: BaseItem<Item = $dtype>,
{
fn execute(&mut self) {
let mut _lock = self.shared_ref.borrow_mut().unwrap();
unsafe {
fftw_sys::$execute(
<FftwPlanPtrType<<$dtype as RlstScalar>::Real> as FftwPlanPtrTypeTrait>::get_fftw_ptr(
self.plan.ptr,
),
);
}
}
}
};
}
impl_plan_inplace!(f32, fftwf_execute);
impl_plan_inplace!(f64, fftw_execute);
impl_plan_inplace!(c32, fftwf_execute);
impl_plan_inplace!(c64, fftw_execute);
pub struct Fft<ArrayImpl, const NDIM: usize> {
shared_ref: ArrayAtomicSharedRef<ArrayImpl, NDIM>,
}
pub struct FftMutGuard<'a, ArrayImpl, const NDIM: usize> {
guard: RwLockWriteGuard<'a, Array<ArrayImpl, NDIM>>,
}
impl<ArrayImpl, const NDIM: usize> Deref for FftMutGuard<'_, ArrayImpl, NDIM> {
type Target = Array<ArrayImpl, NDIM>;
fn deref(&self) -> &Self::Target {
&self.guard
}
}
impl<ArrayImpl, const NDIM: usize> FftMutGuard<'_, ArrayImpl, NDIM>
where
ArrayImpl: RawAccessMut,
{
pub fn data_mut(&mut self) -> Option<&mut [ArrayImpl::Item]> {
self.guard.data_mut()
}
pub(crate) fn array_mut(&mut self) -> &mut Array<ArrayImpl, NDIM> {
&mut self.guard
}
}
#[allow(private_bounds)]
impl<Item, ArrayImpl, const NDIM: usize> Array<ArrayImpl, NDIM>
where
Item: RlstScalar + 'static,
FftwPlanPtrType<Item::Real>: FftwPlanPtrTypeTrait,
ArrayImpl: StableRawAccessMut<Item = Item> + Shape<NDIM> + Stride<NDIM>,
{
pub fn into_fft(self) -> Fft<ArrayImpl, NDIM> {
Fft::from_arr(self)
}
}
#[allow(private_bounds)]
impl<Item, ArrayImpl, const NDIM: usize> Fft<ArrayImpl, NDIM>
where
Item: RlstScalar + 'static,
FftwPlanPtrType<Item::Real>: FftwPlanPtrTypeTrait,
ArrayImpl: StableRawAccessMut<Item = Item> + Shape<NDIM> + Stride<NDIM>,
{
pub fn from_arr(arr: Array<ArrayImpl, NDIM>) -> Self {
Self {
shared_ref: arr.into_shared_atomic(),
}
}
pub fn borrow(&self) -> LockResult<RwLockReadGuard<'_, Array<ArrayImpl, NDIM>>> {
self.shared_ref.borrow()
}
pub fn borrow_mut(&self) -> LockResult<FftMutGuard<'_, ArrayImpl, NDIM>> {
match self.shared_ref.borrow_mut() {
Ok(guard) => Ok(FftMutGuard { guard }),
Err(error) => Err(PoisonError::new(FftMutGuard {
guard: error.into_inner(),
})),
}
}
}