Numerical derivatives

def ifft_dif_recursive(f):
    f = np.asarray(f)
    N = f.shape[0]
    W = np.exp(2. * np.pi * 1.0j / N)
    if N == 1:
        return f
    else:
        indrange = np.arange(N // 2)
        f1,f2 = f[:N//2], f[N//2:]
        f_even = f1 + f2
        f_odd = (f1 - f2) * (W**(indrange))
        F1 = ifft_dif_recursive(f_even)
        F2 = ifft_dif_recursive(f_odd)

        F = np.zeros(N, dtype=complex)
        indrange = np.arange(N)
        F[indrange[::2]] = F1
        F[indrange[1::2]] = F2
        return F
def gauss(x):
    return np.exp(-x**2)
def dgauss(x):
    return -2*x*np.exp(-x**2)

x = np.linspace(-3, 3, 32)
y = gauss(x)
dy = dgauss(x)
N = x.shape[0]
dx = x[1] - x[0]

print(N)
plt.plot(x, y, color='red', lw=3)
plt.plot(x, dy, color='blue', lw=3)
plt.xlabel('x')
plt.ylabel('y(x)')

F = fft_dif_recursive(y)
freq = (2 * np.pi / N/ dx) * np.concatenate((np.arange(N // 2), np.arange(-N // 2, 0, 1)))
F = 1.0j * F * freq
dy2 = ifft_dif_recursive(F) / (N)
plt.plot(x, dy2.real, 'o')

Comparison with dft2

Ntest = 10
timings3 = []
dimension = []
for i in range(1, Ntest):
    f = [random.gauss(0, 1) for n in range(2**i)]
    timing = %timeit -o -n 1 fft_dif_recursive(f)
    dimension.append(2**i)
    timings3.append(timing.average)
plt.subplot(121)    
plt.plot(np.array(dimension), np.array(timings3), 'o', markersize=12, color='red')
plt.plot(np.array(dimension), np.array(timings3), color = 'red')
plt.plot(np.array(dimension2), np.array(timings2), 'o', markersize=12, color='blue')
plt.plot(np.array(dimension2), np.array(timings2), color='blue')


plt.xlabel("$N$")
plt.ylabel("Timing / s")
plt.show()