{"cells": [{"cell_type": "markdown", "id": "c760c855", "metadata": {}, "source": ["# ONNX FFTs\n", "\n", "Implementation of a couple of variations of FFT (see [FFT](https://www.tensorflow.org/xla/operation_semantics#fft) in ONNX."]}, {"cell_type": "code", "execution_count": 1, "id": "ddecaddb", "metadata": {}, "outputs": [{"data": {"text/html": ["
run previous cell, wait for 2 seconds
\n", ""], "text/plain": [""]}, "execution_count": 2, "metadata": {}, "output_type": "execute_result"}], "source": ["from jyquickhelper import add_notebook_menu\n", "add_notebook_menu()"]}, {"cell_type": "code", "execution_count": 2, "id": "6f17f4f5", "metadata": {}, "outputs": [], "source": ["%matplotlib inline"]}, {"cell_type": "code", "execution_count": 3, "id": "75f2064c", "metadata": {}, "outputs": [], "source": ["%load_ext mlprodict"]}, {"cell_type": "markdown", "id": "05a249a3", "metadata": {}, "source": ["## Signature\n", "\n", "We try to use function [FFT](https://www.tensorflow.org/xla/operation_semantics#fft) or [torch.fft.fftn](https://pytorch.org/docs/stable/generated/torch.fft.fftn.html#torch.fft.fftn)."]}, {"cell_type": "code", "execution_count": 4, "id": "6e2fc017", "metadata": {}, "outputs": [{"data": {"text/plain": ["1302"]}, "execution_count": 5, "metadata": {}, "output_type": "execute_result"}], "source": ["import numpy\n", "from numpy.testing import assert_almost_equal\n", "\n", "def numpy_fftn(x, fft_type, fft_length, axes):\n", " \"\"\"\n", " Implements FFT\n", "\n", " :param x: input\n", " :param fft_type: string (see below)\n", " :param fft_length: length on each axis of axes\n", " :param axes: axes\n", " :return: result\n", " \n", " * `'FFT`': complex-to-complex FFT. Shape is unchanged.\n", " * `'IFFT`': Inverse complex-to-complex FFT. Shape is unchanged.\n", " * `'RFFT`': Forward real-to-complex FFT.\n", " Shape of the innermost axis is reduced to fft_length[-1] // 2 + 1 if fft_length[-1]\n", " is a non-zero value, omitting the reversed conjugate part of \n", " the transformed signal beyond the Nyquist frequency.\n", " * `'IRFFT`': Inverse real-to-complex FFT (ie takes complex, returns real).\n", " Shape of the innermost axis is expanded to fft_length[-1] if fft_length[-1] \n", " is a non-zero value, inferring the part of the transformed signal beyond the Nyquist\n", " frequency from the reverse conjugate of the 1 to fft_length[-1] // 2 + 1 entries.\n", " \"\"\"\n", " if fft_type == 'FFT':\n", " return numpy.fft.fftn(x, fft_length, axes=axes)\n", " raise NotImplementedError(\"Not implemented for fft_type=%r.\" % fft_type)\n", " \n", "\n", "def test_fct(fct1, fct2, fft_type='FFT', decimal=5):\n", " cases = list(range(4, 20))\n", " dims = [[c] for c in cases] + [[4,4,4,4], [4,5,6,7]]\n", " lengths_axes = [([c], [0]) for c in cases] + [\n", " ([2, 2, 2, 2], None), ([2, 6, 7, 2], None), ([2, 3, 4, 5], None),\n", " ([2], [3]), ([3], [2])]\n", " n_test = 0\n", " for ndim in range(1, 5):\n", " for dim in dims:\n", " for length, axes in lengths_axes:\n", " if axes is None:\n", " axes = range(ndim)\n", " di = dim[:ndim]\n", " axes = [min(len(di) - 1, a) for a in axes]\n", " le = length[:ndim]\n", " if len(length) > len(di):\n", " continue\n", " mat = numpy.random.randn(*di).astype(numpy.float32)\n", " try:\n", " v1 = fct1(mat, fft_type, le, axes=axes)\n", " except Exception as e:\n", " raise AssertionError(\n", " \"Unable to run %r mat.shape=%r ndim=%r di=%r fft_type=%r le=%r \"\n", " \"axes=%r exc=%r\" %(\n", " fct1, mat.shape, ndim, di, fft_type, le, axes, e))\n", " v2 = fct2(mat, fft_type, le, axes=axes)\n", " try:\n", " assert_almost_equal(v1, v2, decimal=decimal)\n", " except AssertionError as e:\n", " raise AssertionError(\n", " \"Failure mat.shape=%r, fft_type=%r, fft_length=%r\" % (\n", " mat.shape, fft_type, le)) from e\n", " n_test += 1\n", " return n_test\n", "\n", "\n", "test_fct(numpy_fftn, numpy_fftn)"]}, {"cell_type": "code", "execution_count": 5, "id": "84993aa6", "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["1.81 s \u00b1 0 ns per loop (mean \u00b1 std. dev. of 1 run, 1 loop each)\n"]}], "source": ["%timeit -n 1 -r 1 test_fct(numpy_fftn, numpy_fftn)"]}, {"cell_type": "code", "execution_count": 6, "id": "9de9a43f", "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["2.07 s \u00b1 0 ns per loop (mean \u00b1 std. dev. of 1 run, 1 loop each)\n"]}], "source": ["import torch\n", "\n", "def torch_fftn(x, fft_type, fft_length, axes):\n", " xt = torch.tensor(x)\n", " if fft_type == 'FFT':\n", " return torch.fft.fftn(xt, fft_length, axes).cpu().detach().numpy()\n", " \n", "%timeit -n 1 -r 1 test_fct(numpy_fftn, torch_fftn)"]}, {"cell_type": "markdown", "id": "e55d6dbf", "metadata": {}, "source": ["## Numpy implementation"]}, {"cell_type": "code", "execution_count": 7, "id": "aa74068d", "metadata": {}, "outputs": [], "source": ["import numpy\n", "\n", "\n", "def _dft_cst(N, fft_length, dtype):\n", " def _arange(dim, dtype, resh):\n", " return numpy.arange(dim).astype(dtype).reshape(resh)\n", "\n", " def _prod(n, k):\n", " return (-2j * numpy.pi * k / fft_length) * n\n", "\n", " def _exp(m):\n", " return numpy.exp(m)\n", " \n", " n = _arange(N, dtype, (-1, 1))\n", " k = _arange(fft_length, dtype, (1, -1))\n", " M = _exp(_prod(n, k))\n", " return M\n", "\n", "\n", "def custom_fft(x, fft_type, length, axis, dft_fct=None):\n", " # https://github.com/numpy/numpy/blob/4adc87dff15a247e417d50f10cc4def8e1c17a03/numpy/fft/_pocketfft.py#L56\n", " if dft_fct is None:\n", " dft_fct = _dft_cst\n", " if fft_type == 'FFT':\n", " if x.shape[axis] > length:\n", " # fft_length > shape on the same axis\n", " # the matrix is shortened\n", " slices = [slice(None)] * len(x.shape)\n", " slices[axis] = slice(0, length)\n", " new_x = x[tuple(slices)]\n", " elif x.shape[axis] == length:\n", " new_x = x\n", " else:\n", " # other, the matrix is completed with zeros\n", " shape = list(x.shape)\n", " shape[axis] = length\n", " slices = [slice(None)] * len(x.shape)\n", " slices[axis] = slice(0, length)\n", " zeros = numpy.zeros(tuple(shape), dtype=x.dtype)\n", " index = [slice(0, i) for i in x.shape]\n", " zeros[tuple(index)] = x\n", " new_x = zeros\n", "\n", " cst = dft_fct(new_x.shape[axis], length, x.dtype)\n", " perm = numpy.arange(len(x.shape)).tolist() \n", " if perm[axis] == perm[-1]:\n", " res = numpy.matmul(new_x, cst).transpose(perm)\n", " else:\n", " perm[axis], perm[-1] = perm[-1], perm[axis] \n", " rest = new_x.transpose(perm)\n", " res = numpy.matmul(rest, cst).transpose(perm)\n", " perm[axis], perm[0] = perm[0], perm[axis]\n", " return res\n", " raise ValueError(\"Unexpected value for fft_type=%r.\" % fft_type)\n", "\n", "\n", "def custom_fftn(x, fft_type, fft_length, axes, dft_fct=None):\n", " if len(axes) != len(fft_length):\n", " raise ValueError(\"Length mismatch axes=%r, fft_length=%r.\" % (\n", " axes, fft_length))\n", " if fft_type == 'FFT':\n", " res = x\n", " for i in range(len(fft_length) - 1, -1, -1):\n", " length = fft_length[i]\n", " axis = axes[i]\n", " res = custom_fft(res, fft_type, length, axis, dft_fct=dft_fct)\n", " return res\n", " raise ValueError(\"Unexpected value for fft_type=%r.\" % fft_type)\n", "\n", " \n", "shape = (4, )\n", "fft_length = [5,]\n", "axes = [0]\n", "rnd = numpy.random.randn(*shape) + numpy.random.randn(*shape) * 1j\n", "custom_fftn(rnd, 'FFT', fft_length, axes), numpy_fftn(rnd, 'FFT', fft_length, axes)\n", "assert_almost_equal(custom_fftn(rnd, 'FFT', fft_length, axes),\n", " numpy_fftn(rnd, 'FFT', fft_length, axes), decimal=5)\n", "\n", "shape = (4, 3)\n", "fft_length = [3, 2]\n", "axes = [0, 1]\n", "rnd = numpy.random.randn(*shape) + numpy.random.randn(*shape) * 1j\n", "custom_fftn(rnd, 'FFT', fft_length, axes), numpy_fftn(rnd, 'FFT', fft_length, axes)\n", "assert_almost_equal(custom_fftn(rnd, 'FFT', fft_length, axes),\n", " numpy_fftn(rnd, 'FFT', fft_length, axes), decimal=5)"]}, {"cell_type": "code", "execution_count": 8, "id": "5c454666", "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["2.35 s \u00b1 0 ns per loop (mean \u00b1 std. dev. of 1 run, 1 loop each)\n"]}], "source": ["%timeit -n 1 -r 1 test_fct(numpy_fftn, custom_fftn, decimal=4)"]}, {"cell_type": "markdown", "id": "f27bd70d", "metadata": {}, "source": ["## Benchmark"]}, {"cell_type": "code", "execution_count": 9, "id": "507d8348", "metadata": {}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 24/24 [00:06<00:00, 3.91it/s]\n"]}, {"data": {"text/html": ["
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
namecustom_fftnnumpy_fftntorch_fftn
length
80.0005850.0009110.003643
160.0016690.0013730.004087
240.0026820.0032730.005745
320.0042880.0032750.004657
400.0048180.0038310.005198
\n", "
"], "text/plain": ["name custom_fftn numpy_fftn torch_fftn\n", "length \n", "8 0.000585 0.000911 0.003643\n", "16 0.001669 0.001373 0.004087\n", "24 0.002682 0.003273 0.005745\n", "32 0.004288 0.003275 0.004657\n", "40 0.004818 0.003831 0.005198"]}, "execution_count": 10, "metadata": {}, "output_type": "execute_result"}], "source": ["from cpyquickhelper.numbers.speed_measure import measure_time\n", "from tqdm import tqdm\n", "from pandas import DataFrame\n", "\n", "def benchmark(fcts, power2=False):\n", " axes = [1]\n", " if power2:\n", " shape = [512, 1024]\n", " lengths = [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]\n", " else:\n", " shape = [512, 150]\n", " lengths = list(range(8, 200, 8))\n", " rnd = numpy.random.randn(*shape) + numpy.random.randn(*shape) * 1j\n", "\n", " data = []\n", " for length in tqdm(lengths):\n", " fft_length = [length]\n", " for name, fct in fcts.items():\n", " obs = measure_time(lambda: fct(rnd, 'FFT', fft_length, axes),\n", " repeat=5, number=5)\n", " obs['name'] = name\n", " obs['length'] = length\n", " data.append(obs)\n", "\n", " df = DataFrame(data)\n", " return df\n", "\n", "\n", "df = benchmark({'numpy_fftn': numpy_fftn, 'custom_fftn': custom_fftn, 'torch_fftn': torch_fftn})\n", "piv = df.pivot(\"length\", \"name\", \"average\")\n", "piv[:5]"]}, {"cell_type": "code", "execution_count": 10, "id": "6f201494", "metadata": {}, "outputs": [{"data": {"image/png": "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\n", "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["piv.plot(logy=True, logx=True, title=\"FFT benchmark\", figsize=(12, 4));"]}, {"cell_type": "code", "execution_count": 11, "id": "9c29380c", "metadata": {}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 10/10 [00:13<00:00, 1.33s/it]\n"]}, {"data": {"text/html": ["
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
namecustom_fftnnumpy_fftntorch_fftn
length
20.0004340.0011670.023980
40.0011170.0016710.022530
80.0014280.0020770.022102
160.0046540.0028740.019792
320.0031720.0026890.017474
640.0069660.0046120.018116
1280.0309040.0116080.023369
2560.1238210.0258530.023532
5120.4768020.0433520.033228
10241.5279170.1098680.052858
\n", "
"], "text/plain": ["name custom_fftn numpy_fftn torch_fftn\n", "length \n", "2 0.000434 0.001167 0.023980\n", "4 0.001117 0.001671 0.022530\n", "8 0.001428 0.002077 0.022102\n", "16 0.004654 0.002874 0.019792\n", "32 0.003172 0.002689 0.017474\n", "64 0.006966 0.004612 0.018116\n", "128 0.030904 0.011608 0.023369\n", "256 0.123821 0.025853 0.023532\n", "512 0.476802 0.043352 0.033228\n", "1024 1.527917 0.109868 0.052858"]}, "execution_count": 12, "metadata": {}, "output_type": "execute_result"}], "source": ["df = benchmark({'numpy_fftn': numpy_fftn, 'custom_fftn': custom_fftn, 'torch_fftn': torch_fftn},\n", " power2=True)\n", "piv = df.pivot(\"length\", \"name\", \"average\")\n", "piv"]}, {"cell_type": "code", "execution_count": 12, "id": "40847bf5", "metadata": {}, "outputs": [{"data": {"image/png": "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cgk3a+1L1tpbEhMTg92VPnHPPff0afuffPIJABs3buSqq67iqaee4s4772Tjxo189tlnvPrqcf8dIyIiIgOEtZa1uyr4xlPrOe3et7j/rZ1MyRrGsuvzeef7i7l1Ya6Ccg/022oYfeE/X9rKtgPVvdrmlKxh3L106lGPWbFiBffddx/GGKZPn47L5eKiiy7iiiuuACA+Pp7a2lpKSkq46qqrqK6uxu128/DDD/PKK6/Q0NDAzJkzmTp1Kk899RS//OUvWbZsGQA33XQTd9xxB4WFhZx//vmceuqprFmzhtmzZ3PDDTdw9913U1paylNPPcWcOXM69a20tJRrrrmGsrIyZs6cyW233caf//xnXnvtNV555RXef/99GhoaWL16NXfeeSfbt29n79697N69m71793LHHXfw7W9/u1ffUxEREek/tU1unt9QzJMfFPH5oVoSYyK4cX4O15w6RhP2jkNIhuVQLsPYunUrP/nJT1izZg1paWlUVlby3e9+t8tjn376ac477zzuuusuPB4P9fX1LFiwgAceeICNGzcCsH79ep544gk+/PBDrLXMnTuXhQsXkpycTEFBAc8++yzLli1j9uzZPP3006xevZoXX3yRe+65h5UrV3a65vDhw3nssce47777ePnllwFYu3atP8wvX76cdevW8cADDwDw4x//mB07dvD2229TU1PDxIkTue2224iIiOiT909ERET6xueHanhybRF/3VCsCXu9KCTDck9Xw/iiEeC+8NZbb3HllVeSlpYGQEpKSrfHzp49mxtvvJGWlhYuvfRSZs6c2emY1atXc9lllxEX5/yld/nll/Pee+9x8cUXM3bsWKZNmwbA1KlTOeusszDGMG3aNAoLC3vtNS1ZsoSoqCiioqIYPnw4hw4dYuTIkb3WvoiIiPSNFo+X17ceYsXaQv+EvYumZ3LdvBxmjEzEmKG5NnJvCsmwPNCEh4fj9XoB8Hq9NDc3A3DGGWfw7rvv8sorr3D99dfz3e9+l+uuu67H7UZFRfkfh4WF+Z+HhYXhdrt7rf+B13G5XL3atoiIiPQ+TdjrPyE5wS+UnXnmmTz77LNUVFQAUFlZSU5ODuvXrwfgxRdfpKWlBYCioiIyMjK4+eabuemmm9iwYQMAERER/mMWLFjAypUrqa+vp66ujueff54FCxb0Wf8TEhKoqanps/ZFRESk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\n", "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["piv.plot(logy=True, logx=True, title=\"FFT benchmark (power2)\", figsize=(12, 4));"]}, {"cell_type": "markdown", "id": "616099d8", "metadata": {}, "source": ["## Profiling"]}, {"cell_type": "code", "execution_count": 13, "id": "531658bb", "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["f -- 1 1 -- 0.01752 0.54515 -- :8:f (f)\n", " custom_fftn -- 100 100 -- 0.00234 0.52763 -- :57:custom_fftn (custom_fftn)\n", " custom_fft -- 100 100 -- 0.19936 0.52516 -- :20:custom_fft (custom_fft)\n", " _dft_cst -- 100 100 -- 0.31917 0.32366 -- :4:_dft_cst (_dft_cst)\n", " _arange -- 200 200 -- 0.00088 0.00449 -- :5:_arange (_arange)\n", " -- 200 200 -- 0.00128 0.00128 -- ~:0: ()\n", " -- 200 200 -- 0.00064 0.00064 -- ~:0: ()\n", " -- 200 200 -- 0.00169 0.00169 -- ~:0: () +++\n", " -- 100 100 -- 0.00011 0.00011 -- ~:0: () +++\n", " -- 100 100 -- 0.00024 0.00024 -- ~:0: ()\n", " -- 100 100 -- 0.00076 0.00076 -- ~:0: ()\n", " -- 100 100 -- 0.00102 0.00102 -- ~:0: () +++\n", " -- 300 300 -- 0.00013 0.00013 -- ~:0: () +++\n", " -- 400 400 -- 0.00024 0.00024 -- ~:0: ()\n", " -- 300 300 -- 0.00271 0.00271 -- ~:0: ()\n"]}], "source": ["from pyquickhelper.pycode.profiling import profile2graph, profile\n", "\n", "shape = [512, 128]\n", "fft_length = [128]\n", "axes = [1]\n", "rnd = numpy.random.randn(*shape) + numpy.random.randn(*shape) * 1j\n", "\n", "def f():\n", " for i in range(100):\n", " custom_fftn(rnd, 'FFT', fft_length, axes)\n", "\n", "stat, text = profile(f)\n", "gr = profile2graph(stat)\n", "print(gr[0].to_text(fct_width=40))"]}, {"cell_type": "markdown", "id": "7690454d", "metadata": {}, "source": ["We can see that function `_dft_cst` is the bottle neck and more precisely the exponential. We need to use the symmetries of the matrix it builds."]}, {"cell_type": "markdown", "id": "4e250ff9", "metadata": {}, "source": ["## Faster _dft_cst\n", "\n", "The function builds the matrix $M_{nk} = \\left( \\exp\\left(\\frac{-2i\\pi nk}{K}\\right) \\right)_{nk}$ where $1 \\leqslant n \\leqslant N$ and $1 \\leqslant k \\leqslant K$. So it computes powers of the unity roots.\n", "\n", "$$\n", "\\exp\\left(\\frac{-2i\\pi nk}{K}\\right) = \\exp\\left(\\frac{-2i\\pi k}{K}\\right)^n = \\exp\\left(\\frac{-2i\\pi}{K}\\right)^{nk}\n", "$$\n", "\n", "We use that expression to reduce the number of exponentiels to compute."]}, {"cell_type": "code", "execution_count": 14, "id": "8b60fd16", "metadata": {}, "outputs": [{"data": {"text/plain": ["((3, 4), dtype('complex64'))"]}, "execution_count": 15, "metadata": {}, "output_type": "execute_result"}], "source": ["import numpy\n", "from numpy.testing import assert_almost_equal\n", "\n", "def _dft_cst(N, fft_length, dtype=numpy.float32):\n", " def _arange(dim, dtype, resh):\n", " return numpy.arange(dim).astype(dtype).reshape(resh)\n", "\n", " n = _arange(N, dtype, (-1, 1))\n", " k = _arange(fft_length, dtype, (1, -1))\n", " M = (-2j * numpy.pi * k / fft_length) * n\n", " numpy.exp(M, out=M)\n", " return M\n", "\n", "\n", "M = _dft_cst(3, 4, numpy.float32)\n", "M.shape, M.dtype"]}, {"cell_type": "code", "execution_count": 15, "id": "0600a293", "metadata": {}, "outputs": [{"data": {"text/plain": ["((4, 3), dtype('complex128'))"]}, "execution_count": 16, "metadata": {}, "output_type": "execute_result"}], "source": ["M = _dft_cst(4, 3, numpy.float64)\n", "M.shape, M.dtype"]}, {"cell_type": "code", "execution_count": 16, "id": "38d760e2", "metadata": {}, "outputs": [{"data": {"text/plain": ["array([[ 1. +0.00000000e+00j, 1. +0.00000000e+00j, 1. +0.00000000e+00j],\n", " [ 1. +0.00000000e+00j, -0.5-8.66025404e-01j, -0.5+8.66025404e-01j],\n", " [ 1. +0.00000000e+00j, -0.5+8.66025404e-01j, -0.5-8.66025404e-01j],\n", " [ 1. +0.00000000e+00j, 1. +2.44929360e-16j, 1. +4.89858720e-16j]])"]}, "execution_count": 17, "metadata": {}, "output_type": "execute_result"}], "source": ["M"]}, {"cell_type": "code", "execution_count": 17, "id": "30466d1b", "metadata": {}, "outputs": [{"data": {"text/plain": ["array([[ 1. +0.00000000e+00j, 1. +0.00000000e+00j, 1. +0.00000000e+00j],\n", " [ 1. +0.00000000e+00j, -0.5-8.66025404e-01j, -0.5+8.66025404e-01j],\n", " [ 1. +0.00000000e+00j, -0.5+8.66025404e-01j, -0.5-8.66025404e-01j],\n", " [ 1. +0.00000000e+00j, 1. +6.10622664e-16j, 1. +1.22124533e-15j]])"]}, "execution_count": 18, "metadata": {}, "output_type": "execute_result"}], "source": ["def _dft_cst_power(N, fft_length, dtype=numpy.float32):\n", " if dtype == numpy.float32:\n", " ctype = numpy.complex64\n", " else:\n", " ctype = numpy.complex128\n", " M = numpy.empty((N, fft_length), dtype=ctype)\n", " M[0, :] = 1\n", " M[1, 0] = 1\n", " root = numpy.exp(numpy.pi / fft_length * (-2j))\n", " current = root\n", " M[1, 1] = root\n", " for i in range(2, M.shape[1]):\n", " current *= root\n", " M[1, i] = current\n", " for i in range(2, M.shape[0]):\n", " numpy.multiply(M[i-1, :], M[1, :], out=M[i, :])\n", " return M\n", "\n", "M_pow = _dft_cst_power(4, 3, numpy.float64)\n", "M_pow"]}, {"cell_type": "code", "execution_count": 18, "id": "965651d7", "metadata": {}, "outputs": [], "source": ["assert_almost_equal(M, M_pow)"]}, {"cell_type": "code", "execution_count": 19, "id": "a7194b92", "metadata": {}, "outputs": [], "source": ["dims = (10, 15)\n", "assert_almost_equal(_dft_cst(*dims, dtype=numpy.float32), \n", " _dft_cst_power(*dims, dtype=numpy.float32),\n", " decimal=5)"]}, {"cell_type": "markdown", "id": "36b1a3d7", "metadata": {}, "source": ["## Benchmark again"]}, {"cell_type": "code", "execution_count": 20, "id": "40a61cb1", "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["1.46 s \u00b1 0 ns per loop (mean \u00b1 std. dev. of 1 run, 1 loop each)\n"]}], "source": ["def custom_fftn_power(*args, **kwargs):\n", " return custom_fftn(*args, dft_fct=_dft_cst_power, **kwargs)\n", "\n", "\n", "%timeit -r 1 -n 1 test_fct(numpy_fftn, custom_fftn_power, decimal=4)"]}, {"cell_type": "code", "execution_count": 21, "id": "3a2707eb", "metadata": {}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 24/24 [00:07<00:00, 3.19it/s]\n"]}, {"data": {"text/html": ["
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
namecustom_fftncustom_fftn_powernumpy_fftntorch_fftn
length
80.0009910.0008370.0011770.007033
160.0027580.0025910.0020690.006228
240.0030870.0028160.0024990.005564
320.0037670.0030680.0033060.005985
400.0047100.0039750.0040440.005733
\n", "
"], "text/plain": ["name custom_fftn custom_fftn_power numpy_fftn torch_fftn\n", "length \n", "8 0.000991 0.000837 0.001177 0.007033\n", "16 0.002758 0.002591 0.002069 0.006228\n", "24 0.003087 0.002816 0.002499 0.005564\n", "32 0.003767 0.003068 0.003306 0.005985\n", "40 0.004710 0.003975 0.004044 0.005733"]}, "execution_count": 22, "metadata": {}, "output_type": "execute_result"}], "source": ["df = benchmark({\n", " 'numpy_fftn': numpy_fftn, 'torch_fftn': torch_fftn, 'custom_fftn': custom_fftn, \n", " 'custom_fftn_power': custom_fftn_power})\n", "piv = df.pivot(\"length\", \"name\", \"average\")\n", "piv[:5]"]}, {"cell_type": "code", "execution_count": 22, "id": "eb2c6d54", "metadata": {}, "outputs": [{"data": {"image/png": "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\n", "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["piv.plot(logy=True, logx=True, title=\"FFT benchmark 2\", figsize=(12, 4));"]}, {"cell_type": "code", "execution_count": 23, "id": "ed6c4d1a", "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["f -- 1 1 -- 0.02624 0.57688 -- :8:f (f)\n", " custom_fftn_power -- 100 100 -- 0.00094 0.55064 -- :1:custom_fftn_power (custom_fftn_power)\n", " custom_fftn -- 100 100 -- 0.00609 0.54970 -- :57:custom_fftn (custom_fftn)\n", " custom_fft -- 100 100 -- 0.46378 0.54342 -- :20:custom_fft (custom_fft)\n", " _dft_cst_power -- 100 100 -- 0.07599 0.07726 -- :1:_dft_cst_power (_dft_cst_power)\n", " -- 100 100 -- 0.00126 0.00126 -- ~:0: ()\n", " -- 100 100 -- 0.00008 0.00008 -- ~:0: () +++\n", " -- 100 100 -- 0.00025 0.00025 -- ~:0: ()\n", " -- 100 100 -- 0.00096 0.00096 -- ~:0: ()\n", " -- 100 100 -- 0.00109 0.00109 -- ~:0: ()\n", " -- 300 300 -- 0.00020 0.00020 -- ~:0: () +++\n", " -- 400 400 -- 0.00027 0.00027 -- ~:0: ()\n"]}], "source": ["from pyquickhelper.pycode.profiling import profile2graph, profile\n", "\n", "shape = [512, 128]\n", "fft_length = [128]\n", "axes = [1]\n", "rnd = numpy.random.randn(*shape) + numpy.random.randn(*shape) * 1j\n", "\n", "def f():\n", " for i in range(100):\n", " custom_fftn_power(rnd, 'FFT', fft_length, axes)\n", "\n", "stat, text = profile(f)\n", "gr = profile2graph(stat)\n", "print(gr[0].to_text(fct_width=40))"]}, {"cell_type": "markdown", "id": "9ef4af25", "metadata": {}, "source": ["## Cooley\u2013Tukey FFT algorithm\n", "\n", "See [Cooley\u2013Tukey FFT algorithm](https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm).\n", "\n", "The FFT matrix is defined by the matrix computation $F_{ak} = X_{an} M_{nk}$, then one coefficient is ($1 \\leqslant n, k \\leqslant K$):\n", "\n", "$$\n", "F_{ak} = \\sum_n X_{an} M_{nk} = \\sum_n X_{an} \\exp\\left(\\frac{-2i\\pi}{K}\\right)^{nk}\n", "$$\n", "\n", "Let's assume K is even, then $\\exp\\left(\\frac{-2i\\pi k}{K}\\right) = -\\exp\\left(\\frac{-2i\\pi \\left(k + \\frac{K}{2}\\right)}{K}\\right)$."]}, {"cell_type": "code", "execution_count": 24, "id": "803fcc3a", "metadata": {}, "outputs": [{"data": {"image/png": "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tbJyP/vZz+jQoUOt+ZS77bbbGl0r85Uf/ehH9erW31hNdX794Q9/oFevXogIX3/9tXu+qvLkk0/Sq1cvIiMj+fjjjyukGzt2LPn5+dWmr66M3377LcOHD+e7776r87EwHnzxy2ucb+YtBEpwPg97BOcbcue4lguQABwBDgBRteVpI4D4r6YY0UJV9Uc/+pEePHhQVVUTEhL0oYceci9bu3atrlixQlVV33vvPd2yZYvGxsZWSP/uu+9qWVmZlpWV6QMPPKCrVq1SVdWysjJ1OBx64cKFBpfNXDlNdX59/PHH+sUXX+itt96qJ0+edM9/9913dcyYMVpWVqa7d++usI2LFy/qwIEDa0xfUxmfe+45TUpKanCZVW0EkMYGxKmq2lVV26pqiKomqupqVV3tWq6q+oSq/ruqRmgNz8rM1eNPI1qAsyt6+d35N998Q3BwsHuZZw+9e+65x+vvuO677z5EBBEhOjraXRYRqbUmZerP386v22+/3et4k3/5y1+YMWMGIsKgQYM4c+YMhYXOhqb09HR3LbW69NWVEWDixIls2LChDkfTVGYjgBjA/0a0AFizZg333XcfISEhrF+/noULFwJQWlpKTk4Offr0qdO+l5SUsH79esaMGeOeZyNa+JY/nl/VKSgooHv377sAhISEUFDg7AKwbdu2CudRfcvYr18/9u7d26BytXYWzFo5fx3RAuD3v/89W7duJT8/n4cffphf/epXAHz44Yf8+Md1/03+3LlzGT58OHfeead7no1o4Rv+fH41RGZmZq2/96upjAEBAVxzzTWcO3fO52Vr6az7USvmzyNanDx5kk8//dQdtH7+85+774jrc3f8m9/8hpMnT/LHP/6xwnwb0aLx/Pn8qkm3bt0qjGuZn59Pt27dOHr0KN27d+eaa66pNY/qygjOnzq0a9euQWVrzaxm1or584gWnTp14ptvvnEPybRjxw7Cw8MBZy/He++9t9b9X7NmDdu3b+eNN96gTZuK/xVsRIvG8+fzqybjx4/ntddeQ1X54IMPuOGGG+jatWudb6JqKmNRURE33XQTbdu2bVDZWjMLZq2YP49oERgYyKuvvspPfvIT+vfvz/r161m+fDknT56kXbt2FZqh7rzzTn7605+SmppKSEgI27dvB2DOnDmcOHGCwYMH43A4WLJkiTuNjWjReP58fgGsXLmSkJAQ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\n", "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["import matplotlib.pyplot as plt\n", "fig, ax = plt.subplots(1, 1, figsize=(6, 3))\n", "a = numpy.arange(0, 12) * (-2 * numpy.pi / 12)\n", "X = numpy.vstack([numpy.cos(a), numpy.sin(a)]).T\n", "ax.plot(X[:, 0], X[:, 1], 'o');\n", "for i in range(0, 12):\n", " ax.text(X[i, 0], X[i, 1], \"exp(-2pi %d/12)\" % i)\n", "ax.set_title('unit roots');"]}, {"cell_type": "markdown", "id": "f257dc6e", "metadata": {}, "source": ["Then:\n", "\n", "$$\n", "\\begin{array}{rcl}\n", "F_{a,k + \\frac{K}{2}} &=& \\sum_{n=1}^{N} X_{an} \\exp\\left(\\frac{-2i\\pi}{K}\\right)^{n\\left(k + \\frac{K}{2}\\right)} \\\\\n", "&=&\\sum_{n=1}^{N} X_{an} (-1)^n \\exp\\left(\\frac{-2i\\pi}{K}\\right)^{nk} \\\\\n", "&=&\\sum_{m=1}^{\\frac{N}{2}} X_{a,2m} \\exp\\left(\\frac{-2i\\pi}{K}\\right)^{2mk} - \\sum_{m=1}^{\\frac{N}{2}} X_{a,2m-1} \\exp\\left(\\frac{-2i\\pi}{K}\\right)^{(2m-1)k} \\\\\n", "&=&\\sum_{m=1}^{\\frac{N}{2}} X_{a,2m} \\exp\\left(\\frac{-2i\\pi}{K}\\right)^{2mk} - \\sum_{m=1}^{\\frac{N}{2}} X_{a,2m-1} \\exp\\left(\\frac{-2i\\pi}{K}\\right)^{2mk} \\exp\\left(\\frac{-2i\\pi}{K}\\right)^{-k}\n", "\\end{array}\n", "$$\n", "\n", "Then:\n", "\n", "$$\n", "\\begin{array}{rcl}\n", "F_{a,k} + F_{a,k+\\frac{K}{2}} &=& 2\\sum_{m=1}^{\\frac{N}{2}} X_{a,2m} \\exp\\left(\\frac{-2i\\pi}{K}\\right)^{2mk}\n", "= 2\\sum_{m=1}^{\\frac{N}{2}} X_{a,2m} \\exp\\left(\\frac{-2i\\pi}{\\frac{K}{2}}\\right)^{mk}\n", "\\end{array}\n", "$$\n", "\n", "Finally:\n", "\n", "$$\n", "\\begin{array}{rcl}\n", "F_{a,k} &=& \\sum_{m=1}^{\\frac{N}{2}} X_{a,2m} \\exp\\left(\\frac{-2i\\pi}{K}\\right)^{2mk} + \\sum_{m=1}^{\\frac{N}{2}} X_{a,2m-1} \\exp\\left(\\frac{-2i\\pi}{K}\\right)^{2mk} \\exp\\left(\\frac{-2i\\pi}{K}\\right)^{-k} \\\\\n", "F_{a,k + \\frac{K}{2}} &=&\\sum_{m=1}^{\\frac{N}{2}} X_{a,2m} \\exp\\left(\\frac{-2i\\pi}{K}\\right)^{2mk} - \\sum_{m=1}^{\\frac{N}{2}} X_{a,2m-1} \\exp\\left(\\frac{-2i\\pi}{K}\\right)^{2mk} \\exp\\left(\\frac{-2i\\pi}{K}\\right)^{-k}\n", "\\end{array}\n", "$$"]}, {"cell_type": "markdown", "id": "356b585d", "metadata": {}, "source": ["Now, what happen when *K* is odd, fallback to the original computation.\n", "\n", "$$\n", "F_{ak} = \\sum_n X_{an} M_{nk} = \\sum_n X_{an} \\exp\\left(\\frac{-2i\\pi}{K}\\right)^{nk}\n", "$$"]}, {"cell_type": "code", "execution_count": 25, "id": "971be7bd", "metadata": {"scrolled": false}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["1.5 s \u00b1 0 ns per loop (mean \u00b1 std. dev. of 1 run, 1 loop each)\n"]}], "source": ["import functools\n", "\n", "\n", "def cooley_fft_2p(x, fft_length):\n", " cst = _dft_cst_power(x.shape[-1], fft_length, x.dtype)\n", " return numpy.matmul(x, cst)\n", "\n", "\n", "@functools.cache\n", "def _build_fact(p2_2, fft_length, dtype):\n", " first = numpy.exp(-2j * numpy.pi / fft_length)\n", " fact = numpy.ones(p2_2, dtype=dtype)\n", " for k in range(1, p2_2):\n", " fact[k] = fact[k-1] * first\n", " return fact.reshape((1, -1))\n", "\n", "\n", "def build_fact(p2_2, fft_length, dtype):\n", " return _build_fact(p2_2, fft_length, dtype)\n", "\n", "\n", "def cooley_fft_recursive(x, fft_length):\n", " if len(x.shape) != 2:\n", " raise RuntimeError(\n", " \"Unexpected x.shape=%r.\" % (x.shape, ))\n", " dtype = numpy.complex128 if x.dtype == numpy.float64 else numpy.complex64\n", " if fft_length == 1:\n", " return x[:, :1].astype(dtype)\n", "\n", " if fft_length % 2 == 0:\n", " def split(x):\n", " even = x[:, ::2]\n", " odd = x[:, 1::2]\n", " return even, odd\n", "\n", " def tmp1(even, odd, fft_length):\n", " p2_2 = fft_length // 2\n", " fft_even = cooley_fft_recursive(even, p2_2)\n", " fft_odd = cooley_fft_recursive(odd, p2_2)\n", " return fft_even, fft_odd, p2_2\n", "\n", " def tmp2(x, fft_even, fft_odd, p2_2):\n", " fact = build_fact(p2_2, fft_length, fft_even.dtype)\n", "\n", " fact_odd = fft_odd * fact\n", " return numpy.hstack([fft_even + fact_odd, fft_even - fact_odd])\n", "\n", " # inplace\n", " # result = numpy.empty((x.shape[0], fft_length), dtype=fft_even.dtype)\n", " # numpy.multiply(fft_odd, fact, out=result[:, :p2_2])\n", " # numpy.subtract(fft_even, result[:, :p2_2], out=result[:, p2_2:])\n", " # numpy.add(fft_even, result[:, :p2_2], out=result[:, :p2_2])\n", " # return result\n", " \n", " even, odd = split(x)\n", " fft_even, fft_odd, p2_2 = tmp1(even, odd, fft_length)\n", " result = tmp2(x, fft_even, fft_odd, p2_2)\n", " else:\n", " result = cooley_fft_2p(x, fft_length)\n", " \n", " return result\n", "\n", "\n", "\n", "def cooley_fft(x, fft_length):\n", " return cooley_fft_recursive(x, fft_length)\n", "\n", "\n", "def custom_fft_cooley(x, fft_type, length, axis):\n", " # https://github.com/numpy/numpy/blob/4adc87dff15a247e417d50f10cc4def8e1c17a03/numpy/fft/_pocketfft.py#L56\n", " if fft_type == 'FFT':\n", " if x.shape[axis] > length:\n", " # fft_length > shape on the same axis\n", " # the matrix is shortened\n", " slices = [slice(None)] * len(x.shape)\n", " slices[axis] = slice(0, length)\n", " new_x = x[tuple(slices)]\n", " elif x.shape[axis] == length:\n", " new_x = x\n", " else:\n", " # other, the matrix is completed with zeros\n", " shape = list(x.shape)\n", " shape[axis] = length\n", " slices = [slice(None)] * len(x.shape)\n", " slices[axis] = slice(0, length)\n", " zeros = numpy.zeros(tuple(shape), dtype=x.dtype)\n", " index = [slice(0, i) for i in x.shape]\n", " zeros[tuple(index)] = x\n", " new_x = zeros\n", "\n", " if axis == len(new_x.shape) - 1:\n", " if len(new_x.shape) != 2:\n", " xt = new_x.reshape((-1, new_x.shape[-1]))\n", " else:\n", " xt = new_x\n", " res = cooley_fft(xt, length)\n", " if len(new_x.shape) != 2:\n", " res = res.reshape(new_x.shape[:-1] + (-1, ))\n", " else:\n", " perm = numpy.arange(len(x.shape)).tolist() \n", " perm[axis], perm[-1] = perm[-1], perm[axis] \n", " rest = new_x.transpose(perm)\n", " shape = rest.shape[:-1]\n", " rest = rest.reshape((-1, rest.shape[-1]))\n", " res = cooley_fft(rest, length)\n", " res = res.reshape(shape + (-1, )).transpose(perm)\n", " perm[axis], perm[0] = perm[0], perm[axis]\n", " return res\n", " raise ValueError(\"Unexpected value for fft_type=%r.\" % fft_type)\n", "\n", "\n", "def custom_fftn_cooley(x, fft_type, fft_length, axes):\n", " if len(axes) != len(fft_length):\n", " raise ValueError(\"Length mismatch axes=%r, fft_length=%r.\" % (\n", " axes, fft_length))\n", " if fft_type == 'FFT':\n", " res = x\n", " for i in range(len(fft_length) - 1, -1, -1):\n", " length = fft_length[i]\n", " axis = axes[i]\n", " res = custom_fft_cooley(res, fft_type, length, axis)\n", " return res\n", " raise ValueError(\"Unexpected value for fft_type=%r.\" % fft_type)\n", " \n", "\n", "shape = (4, )\n", "fft_length = [3,]\n", "axes = [0]\n", "rnd = numpy.random.randn(*shape) + numpy.random.randn(*shape) * 1j\n", "assert_almost_equal(custom_fftn_cooley(rnd, 'FFT', fft_length, axes),\n", " numpy_fftn(rnd, 'FFT', fft_length, axes),\n", " decimal=5)\n", "%timeit -n 1 -r 1 test_fct(numpy_fftn, custom_fftn_cooley)"]}, {"cell_type": "code", "execution_count": 26, "id": "441d6c41", "metadata": {}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 24/24 [00:10<00:00, 2.35it/s]\n"]}, {"data": {"text/html": ["
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
namecustom_fftn_cooleycustom_fftn_powernumpy_fftntorch_fftn
length
80.0028730.0006850.0014820.005463
160.0071970.0021210.0019220.005063
240.0094430.0029030.0027390.005169
320.0127830.0025560.0020030.004076
400.0141420.0039160.0039370.005118
\n", "
"], "text/plain": ["name custom_fftn_cooley custom_fftn_power numpy_fftn torch_fftn\n", "length \n", "8 0.002873 0.000685 0.001482 0.005463\n", "16 0.007197 0.002121 0.001922 0.005063\n", "24 0.009443 0.002903 0.002739 0.005169\n", "32 0.012783 0.002556 0.002003 0.004076\n", "40 0.014142 0.003916 0.003937 0.005118"]}, "execution_count": 27, "metadata": {}, "output_type": "execute_result"}], "source": ["df = benchmark({\n", " 'numpy_fftn': numpy_fftn, 'torch_fftn': torch_fftn,\n", " 'custom_fftn_power': custom_fftn_power, 'custom_fftn_cooley': custom_fftn_cooley})\n", "piv = df.pivot(\"length\", \"name\", \"average\")\n", "piv[:5]"]}, {"cell_type": "code", "execution_count": 27, "id": "6b579149", "metadata": {}, "outputs": [{"data": {"image/png": "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NV/t/Irv9xz42azTKVwzdShf3TgZNycH5r29lv/8uB2TxQpA+t4qXvhlB2fFh3B+cmifv6bjTb+sWe7qahiPfLuFrftqevTeIwd58tCsQy+78v777/Pcc8+hKArx8fHo9XrOOussLrzwQgDc3d2pq6ujsLCQuXPnUlNTg9ls5rXXXmPZsmU0NjaSmJhIXFwcixcv5vnnn+fdd98F4JprruH2228nJyeH008/nQkTJrB69WrGjh3LlVdeyUMPPURJSQmLFy9m3Lhx7fpWUlLCpZdeSmlpKYmJidxwww18+umn/PTTTyxbtoy//vqLxsZGVq1axb333su2bdvIy8tj9+7d5OXlcfvtt3Prrbd2+bV/8MEH5OTkcNVVV1FWVkZAQAALFy4kPDy80+Ot7dq1i5tuuonS0lJcXV156623CA0NJT4+nh07dmAwGKipqSEhIcH2WAghhOjvGprNPP3Ddt5PzWVYgBtf3DCJxMHe7a5bvW81VtXKnuo9GC1GnPROtnOjQr347pYpPPrtVl5dvou/dpXz9Pmjuf2TNAI9nHji3NGHrGkWPaNfjiz359UwtmzZwuOPP87vv//O5s2befHFFzu99qOPPuK0004jPT2dzZs3k5iYyNNPP42Liwvp6eksXryYjRs3snDhQtauXcuaNWt46623bCPDO3fu5F//+hfbt29n+/btfPTRR6xatYrnnnuu09HpwMBA3n77baZOnUp6ejr/+Mc/OPvss3n22Wf5+OOPefTRR5k7d65t1Blg+/bt/PTTT6xbt45HHnkEk8nUrdd+yy23cMUVV5CRkcG8efNsYbuz461dd911vPzyy2zcuJHnnnuOG2+8EQ8PD0444QSWLVsGwCeffML5558vQVkIIcSAkJZXyZkvruSDNblcPSWSZbdO7TAoA6zIXwGARbWws3Jnu/Oujg48fUE8r85LZk9pHWe8uJLcigaen5uIl6v8XOwL/XJkuasONwLcG37//Xdmz56Nv78/AL6+nS/TMnbsWK666ipMJhPnnnsuiYmJ7a5ZtWoV5513Hm5uWoH/+eefz8qVKzn77LOJjIxk9OjRAMTFxXHSSSehKAqjR48mJyenx17TzJkzcXJywsnJicDAQIqLiwkLC2t3XWevPTU1lS+//BKAyy67jLvuuuuQx/erq6tj9erVzJ4923bMaDQC2gj7f/7zH849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\n", "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["piv.plot(logy=True, logx=True, title=\"FFT benchmark 3\", figsize=(12, 4));"]}, {"cell_type": "code", "execution_count": 28, "id": "eb4f30d6", "metadata": {}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 10/10 [00:11<00:00, 1.15s/it]\n"]}, {"data": {"text/html": ["
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
namecustom_fftn_cooleycustom_fftn_powernumpy_fftntorch_fftn
length
20.0005750.0004710.0007220.019371
40.0011530.0003280.0011300.018366
80.0036780.0006240.0017790.019295
160.0068430.0022550.0021920.020169
320.0155740.0030450.0027360.017193
\n", "
"], "text/plain": ["name custom_fftn_cooley custom_fftn_power numpy_fftn torch_fftn\n", "length \n", "2 0.000575 0.000471 0.000722 0.019371\n", "4 0.001153 0.000328 0.001130 0.018366\n", "8 0.003678 0.000624 0.001779 0.019295\n", "16 0.006843 0.002255 0.002192 0.020169\n", "32 0.015574 0.003045 0.002736 0.017193"]}, "execution_count": 29, "metadata": {}, "output_type": "execute_result"}], "source": ["df = benchmark({\n", " 'numpy_fftn': numpy_fftn, 'torch_fftn': torch_fftn,\n", " 'custom_fftn_power': custom_fftn_power, 'custom_fftn_cooley': custom_fftn_cooley},\n", " power2=True)\n", "piv = df.pivot(\"length\", \"name\", \"average\")\n", "piv[:5]"]}, {"cell_type": "code", "execution_count": 29, "id": "769d9e41", "metadata": {}, "outputs": [{"data": {"image/png": "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\n", "text/plain": ["
"]}, "metadata": {"needs_background": "light"}, "output_type": "display_data"}], "source": ["piv.plot(logy=True, logx=True, title=\"FFT benchmark 3 (power2)\", figsize=(12, 4));"]}, {"cell_type": "code", "execution_count": 30, "id": "3f6ef212", "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["cooley_fft_recursive -- 100 51100 -- 0.24497 2.68339 -- :22:cooley_fft_recursive (cooley_fft_recursive)\n", " split -- 25500 25500 -- 0.06264 0.06264 -- :31:split (split)\n", " tmp1 -- 100 25500 -- 0.09438 2.54540 -- :36:tmp1 (tmp1)\n", " cooley_fft_recursive -- 51000 200 -- 0.24336 2.54421 -- :22:cooley_fft_recursive (cooley_fft_recursive) +++\n", " tmp2 -- 25500 25500 -- 0.95948 2.04473 -- :42:tmp2 (tmp2)\n", " hstack -- 25500 25500 -- 0.04799 1.05776 -- <__array_function__ internals>:177:hstack (hstack)\n", " _vhstack_dispatcher -- 25500 25500 -- 0.02712 0.07002 -- C:/Python395_x64/lib/site-packages/numpy/core/shape_base.py:218:_vhstack_dispatcher (_vhstack_dispatcher)\n", " _arrays_for...dispatcher -- 25500 25500 -- 0.02361 0.04290 -- C:/Python395_x64/lib/site-packages/numpy/core/shape_base.py:207:_arrays_for_stack_dispatcher (_arrays_for_stack_dispatcher)\n", " -- 25500 25500 -- 0.01929 0.01929 -- ~:0: ()\n", " -- 25500 25500 -- 0.03753 0.93975 -- ~:0: () +++\n", " build_fact -- 25500 25500 -- 0.02749 0.02749 -- :18:build_fact (build_fact)\n", " -- 51100 51100 -- 0.01521 0.01521 -- ~:0: () +++\n", " -- 25600 25600 -- 0.22146 0.22146 -- ~:0: ()\n", "f -- 1 1 -- 0.01449 2.70167 -- :8:f (f)\n", " custom_fftn_cooley -- 100 100 -- 0.00139 2.68718 -- :112:custom_fftn_cooley (custom_fftn_cooley)\n", " custom_fft_cooley -- 100 100 -- 0.00135 2.68568 -- :69:custom_fft_cooley (custom_fft_cooley)\n", " cooley_fft -- 100 100 -- 0.00082 2.68421 -- :65:cooley_fft (cooley_fft)\n", " cooley_fft_recursive -- 100 100 -- 0.00160 2.68339 -- :22:cooley_fft_recursive (cooley_fft_recursive) +++\n", " -- 300 300 -- 0.00012 0.00012 -- ~:0: () +++\n", " -- 300 300 -- 0.00011 0.00011 -- ~:0: () +++\n", " -- 77200 77200 -- 0.02367 0.02367 -- ~:0: ()\n", " -- 25500 76500 -- 0.58675 0.93975 -- ~:0: ()\n", " atleast_1d -- 25500 25500 -- 0.09562 0.13747 -- C:/Python395_x64/lib/site-packages/numpy/core/shape_base.py:23:atleast_1d (atleast_1d)\n", " -- 51000 51000 -- 0.01708 0.01708 -- ~:0: ()\n", " -- 25500 25500 -- 0.00822 0.00822 -- ~:0: () +++\n", " -- 51000 51000 -- 0.01655 0.01655 -- ~:0: ()\n", " hstack -- 25500 25500 -- 0.09871 0.90222 -- C:/Python395_x64/lib/site-packages/numpy/core/shape_base.py:285:hstack (hstack)\n", " concatenate -- 25500 25500 -- 0.04882 0.57709 -- <__array_function__ internals>:177:concatenate (concatenate)\n", " concatenate -- 25500 25500 -- 0.01049 0.01049 -- C:/Python395_x64/lib/site-packages/numpy/core/multiarray.py:148:concatenate (concatenate)\n", " -- 25500 25500 -- 0.51778 0.51778 -- ~:0: () +++\n", " atleast_1d -- 25500 25500 -- 0.04022 0.21751 -- <__array_function__ internals>:177:atleast_1d (atleast_1d)\n", " _atleast_1d_dispatcher -- 25500 25500 -- 0.00838 0.00838 -- C:/Python395_x64/lib/site-packages/numpy/core/shape_base.py:19:_atleast_1d_dispatcher (_atleast_1d_dispatcher)\n", " -- 25500 25500 -- 0.03144 0.16891 -- ~:0: () +++\n", " -- 25500 25500 -- 0.00892 0.00892 -- ~:0: ()\n"]}], "source": ["from pyquickhelper.pycode.profiling import profile2graph, profile\n", "\n", "shape = [512, 256]\n", "fft_length = [256]\n", "axes = [1]\n", "rnd = numpy.random.randn(*shape) + numpy.random.randn(*shape) * 1j\n", "\n", "def f():\n", " for i in range(100):\n", " custom_fftn_cooley(rnd, 'FFT', fft_length, axes)\n", "\n", "stat, text = profile(f)\n", "gr = profile2graph(stat)\n", "print(gr[0].to_text(fct_width=40))"]}, {"cell_type": "code", "execution_count": 31, "id": "79164246", "metadata": {}, "outputs": [], "source": []}, {"cell_type": "code", "execution_count": 32, "id": "b9a3147b", "metadata": {}, "outputs": [], "source": []}, {"cell_type": "code", "execution_count": 33, "id": "6c1375ae", "metadata": {}, "outputs": [], "source": []}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.5"}}, "nbformat": 4, "nbformat_minor": 5}