对周期的向量值函数使用FFT

首先介绍一下我的主要目的. 考虑一个未知的函数 f(x,y)=(f_1(x,y),f_2(x,y)),(x,y)\in\mathbb{R}^2,假设 f_1,f_2 关于两个变量都是1-周期的, 也就是说 f_i(x+1,y)=f_i(x,y+1)=f_i(x,y). 假设已知了这个向量函数在 [0,1]\times[0,1] 上的一个离散分布数据, 现在我需要计算这个函数的 Fourier 系数.

暂时我是这么考虑的. 假设这个未知函数为 f(x,y)=(\sin(2\pi x)\cos(2\pi y), \cos(2\pi x)\sin(2\pi y)), 首先很容易可以创建这个函数在 [0,1]\times[0,1] 上的离散数据.

function f(x)
    [sin(2*pi*x[1])*cos(2*pi*x[2]), cos(2*pi*x[1])*sin(2*pi*x[2])]
end
N=200
data = [f([i/N,j/N]) for i in 0:N-1, j in 0:N-1]

数据 data 是一个 N\times N 的矩阵, 矩阵的每一个元素都是一个 2\times1 的矢量. 根据离散Fourier变换的定义, 可以通过离散Fourier变换和数据 data 来计算 f 的Fourier系数. Julia 实现离散Fourier变换的包是FFTW.jl, 但我不知道这个包能不能实现向量值函数的离散Fourier变换. 当然, 我可以写如下比较丑陋的代码: 将两个维度的数据拆分为两个矩阵, 分别进行离散Fourier变换, 再拼接一下, 代码如下

function v_fft(A::Matrix{Vector{Float64}})
    n=size(A,2)
    b=Matrix{Float64}(undef, n, n)
    c=Matrix{Float64}(undef, n, n)
    @inbounds @simd for i in 1:n^2
        b[i]=A[i][1]
        c[i]=A[i][2]        
    end
    fb=fft(b)
    fc=fft(c)
    result=Matrix{Vector{ComplexF64}}(undef, n, n)
    @inbounds @simd for i in 1:n^2
        result[i]=[fb[i],fc[i]]       
    end
    result
end

但如上所说, 这样写太丑陋了, 实际上应该并不需要这么做. 但是这个包的文档写得太简略了, 没有多少数学定义, 我看不太懂. 请问有没有大佬可以解决一下这个问题, 即不用这么麻烦的方式直接得到结果.

听描述感觉你要的就是最简单的 2D DFT,FFTW.jl 是 Steven Johnson 对他写的FFTW 库的封装。看他写的文章就可以找到数学定义:

谢谢你给出参考文献. 其实高维DFT定义我自然是知道的, 但是我不知道怎么在FFTW.jl中调用fft函数来计算高维DFT, FFTW.jl 的API文档看不太懂.

julia> fft(randn(4, 4), 1)  # FFT over the first dimension
4×4 Matrix{ComplexF64}:
 -0.169743+0.0im         1.38273+0.0im         2.7259+0.0im        1.73068+0.0im
  -1.29979+0.593027im   -1.57606+1.05342im  -0.825811-0.636893im  0.292878+0.408652im
   0.48972+0.0im       -0.957822+0.0im      -0.573209+0.0im        2.40699+0.0im
  -1.29979-0.593027im   -1.57606-1.05342im  -0.825811+0.636893im  0.292878-0.408652im

julia> fft(randn(4, 4))   # 2D FFT
4×4 Matrix{ComplexF64}:
 1.07097+0.0im       0.502392-1.92969im   -4.29076+0.0im        0.502392+1.92969im
 -3.4929-2.11008im   -2.20324-1.97117im    5.33781+0.329706im  -0.722525+0.459805im
 8.37998+0.0im       -4.41875-2.98268im    2.09503+0.0im        -4.41875+2.98268im
 -3.4929+2.11008im  -0.722525-0.459805im   5.33781-0.329706im   -2.20324+1.97117im

help?> fft
  fft(A [, dims])

  Performs a multidimensional FFT of the array A. The optional dims argument specifies an iterable subset of
  dimensions (e.g. an integer, range, tuple, or array) to transform along. Most efficient if the size of A along
  the transformed dimensions is a product of small primes; see Base.nextprod. See also plan_fft() for even greater
  efficiency.

  A one-dimensional FFT computes the one-dimensional discrete Fourier transform (DFT) as defined by

  \operatorname{DFT}(A)[k] =
  \sum_{n=1}^{\operatorname{length}(A)}
  \exp\left(-i\frac{2\pi
  (n-1)(k-1)}{\operatorname{length}(A)} \right) A[n].

  A multidimensional FFT simply performs this operation along each transformed dimension of A.

  │ Note
  │
  │  This performs a multidimensional FFT by default. FFT libraries in other languages such as Python and
  │  Octave perform a one-dimensional FFT along the first non-singleton dimension of the array. This is
  │  worth noting while performing comparisons.

我在英文论坛上得到了一个还算满意的答案, 请参见这里.

1 个赞

更自然的方法是将数据构建成 N\times N\times 2 的 Array. 具体如下:

function f(x)
    [sin(2*pi*x[1])*cos(2*pi*x[2]), cos(2*pi*x[1])*sin(2*pi*x[2])]
end
N=200
data = [f([i/N,j/N])[k] for i in 0:N-1, j in 0:N-1, k in 1:2]

using FFTW
fft(data, 1:2) # 沿着前两个维度进行 Fourier 变换, 即分别会对 data[:,:,1],data[:,:,2] 进行 Fourier 变换.

一个相关的包是 GitHub - JuliaArrays/FFTViews.jl: Julia package for fast fourier transforms and periodic views