使用 CUDA 进行希尔伯特变换

问题描述 投票:0回答:2

为了对一维数组进行希尔伯特变换,必须:

  • 对数组进行 FFT
  • 将数组的一半加倍,将另一半归零
  • 对结果进行逆 FFT

我使用 PyCuLib 进行 FFTing。到目前为止我的代码

def htransforms(data):
    N = data.shape[0]
    transforms       = nb.cuda.device_array_like(data)          # Allocates memory on GPU with size/dimensions of signal
    transforms.dtype = np.complex64                             # Change GPU array type to complex for FFT 

    pyculib.fft.fft(signal.astype(np.complex64), transforms)    # Do FFT on GPU

    transforms[1:N/2]      *= 2.0      # THIS STEP DOESN'T WORK
    transforms[N/2 + 1: N]  = 0+0j     # NEITHER DOES THIS ONE

    pyculib.fft.ifft_inplace(transforms)                        # Do IFFT on GPU: in place (same memory)    
    envelope_function      = transforms.copy_to_host()          # Copy results to host (computer) memory
    return abs(envelope_function)

我有一种感觉,它可能与 Numba 的 CUDA 接口本身有关...它是否允许像这样修改数组(或数组切片)的单个元素?我认为可能是这样,因为变量

transforms
是一个
numba.cuda.cudadrv.devicearray.DeviceNDArray
,所以我想它可能有一些与 numpy 的
ndarray
相同的操作。

总之,使用Numba的

device_arrays
,对切片进行简单操作最简单的方法是什么?我得到的错误是

*= 不支持的操作数类型:“DeviceNDArray”和“float”

python signal-processing numba
2个回答
1
投票

我会使用pytorch

你的函数使用pytorch(我删除了abs以返回复数值)

def htransforms(data):
    N = data.shape[-1]
    # Allocates memory on GPU with size/dimensions of signal
    transforms       = torch.tensor(data).cuda()
    
    torch.fft.fft(transforms, axis=-1)
    transforms[:, 1:N//2]      *= 2.0      # THIS STEP DOESN'T WORK
    transforms[:, N//2 + 1: N]  = 0+0j     # NEITHER DOES THIS ONE

    # Do IFFT on GPU: in place (same memory)
    return torch.abs(torch.fft.ifft(transforms)).cpu() 

但是你的变换实际上与我在wikipedia

上找到的不同

维基百科版本

def htransforms_wikipedia(data):
    N = data.shape[-1]
    # Allocates memory on GPU with size/dimensions of signal
    transforms       = torch.tensor(data).cuda()
    
    transforms = torch.fft.fft(transforms, axis=-1)
    transforms[:, 1:N//2]      *= -1j      # positive frequency
    transforms[:, (N+2)//2 + 1: N] *= +1j # negative frequency
    transforms[:,0] = 0; # DC signal
    if N % 2 == 0:
        transforms[:, N//2] = 0; # the (-1)**n term
    
    # Do IFFT on GPU: in place (same memory)
    return torch.fft.ifft(transforms).cpu() 
data = torch.zeros((1, 2**10))
data[:, 2**9] = 1;
tdata = htransforms(data).data;
plt.plot(tdata.real.T, '-')
plt.plot(tdata.imag.T, '-')
plt.xlim([500, 525])
plt.legend(['real', 'imaginary'])
plt.title('inpulse response of your version')

result with your definition

data = torch.zeros((1, 2**10))
data[:, 2**9] = 1;
tdata = htransforms_wikipedia(data).data;
plt.plot(tdata.real.T, '-');
plt.plot(tdata.imag.T, '-');
plt.xlim([500, 525])
plt.legend(['real', 'imaginary'])
plt.title('inpulse response of Wikipedia version')

result for wikipedia definition

您的版本的脉冲响应是

1 + 1j * h[k]
,其中
h[k]
是维基百科版本的脉冲响应。如果您正在使用真实数据,维基百科版本很好,因为您可以使用 rfftirfft 生成一个线性版本

def real_htransforms_wikipedia(data):
    N = data.shape[-1]
    # Allocates memory on GPU with size/dimensions of signal
    transforms       = torch.tensor(data).cuda()
    transforms = -1j * torch.fft.rfft(transforms, axis=-1)
    transforms[0] = 0;
    return torch.fft.irfft(transforms, axis=-1)

0
投票

@Bob 给出的答案仅返回变换的虚部。如果你想返回一个复数(更类似于 scipy hilbert 函数),你可以像这样返回一个复张量。

def hilbert_transform(data):
    # Allocates memory on GPU with size/dimensions of signal
    data = torch.from_numpy(data).to('cuda')
    transforms = -1j * torch.fft.rfft(data, axis=-1)
    transforms[0] = 0;
    imaginary = torch.fft.irfft(transforms, axis=-1)
    real = data
    return torch.complex(real, imaginary)

这将与 torch.angle 等函数很好地配合使用实部和虚部来获取信号相位和 torch.abs 来获取包络。

© www.soinside.com 2019 - 2024. All rights reserved.