Repository: ivy-llc/ivy

Issue:
fftn



Discussion:

Resolving patch:
diff --git a/ivy/functional/frontends/numpy/fft/discrete_fourier_transform.py b/ivy/functional/frontends/numpy/fft/discrete_fourier_transform.py
--- a/ivy/functional/frontends/numpy/fft/discrete_fourier_transform.py
+++ b/ivy/functional/frontends/numpy/fft/discrete_fourier_transform.py
@@ -1,6 +1,6 @@
 import ivy
 from ivy.functional.frontends.numpy.func_wrapper import to_ivy_arrays_and_back
-from ivy.func_wrapper import with_unsupported_dtypes
+from ivy.func_wrapper import with_unsupported_dtypes, with_supported_dtypes
 
 
 # --- Helpers --- #
@@ -45,6 +45,38 @@ def fftfreq(n, d=1.0):
     return results * val
 
 
+@with_supported_dtypes(
+    {"1.26.0 and below": ("float32", "float64", "complex64", "complex128")},
+    "numpy",
+)
+@to_ivy_arrays_and_back
+def fftn(a, s=None, axes=None, norm=None):
+    invreal = 0
+    if norm is None:
+        norm = "backward"
+    if s is None:
+        shapeless = 1
+        if axes is None:
+            s = list(a.shape)
+        else:
+            axes = [ax % len(a.shape) for ax in axes]
+            s = ivy.gather(a.shape, ivy.array(axes, dtype="int64"))
+    else:
+        shapeless = 0
+    s = list(s)
+    if axes is None:
+        axes = list(range(-len(s), 0))
+    if len(s) != len(axes):
+        raise ValueError("Shape and axes have different lengths.")
+    if invreal and shapeless:
+        s[-1] = (a.shape[axes[-1]] - 1) * 2
+    itl = list(range(len(axes)))
+    itl.reverse()
+    for ii in itl:
+        a = ivy.fft(a, axes[ii], norm=norm, n=int(s[ii]))
+    return ivy.astype(a, ivy.complex128)
+
+
 @to_ivy_arrays_and_back
 @with_unsupported_dtypes({"1.26.0 and below": ("float16",)}, "numpy")
 def fftshift(x, axes=None):


Test patch:
diff --git a/ivy_tests/test_ivy/test_frontends/test_numpy/test_fft/test_discrete_fourier_transform.py b/ivy_tests/test_ivy/test_frontends/test_numpy/test_fft/test_discrete_fourier_transform.py
--- a/ivy_tests/test_ivy/test_frontends/test_numpy/test_fft/test_discrete_fourier_transform.py
+++ b/ivy_tests/test_ivy/test_frontends/test_numpy/test_fft/test_discrete_fourier_transform.py
@@ -62,6 +62,53 @@ def test_numpy_fftfreq(
     )
 
 
+@handle_frontend_test(
+    fn_tree="numpy.fft.fftn",
+    dtype_values=helpers.dtype_and_values(
+        available_dtypes=helpers.get_dtypes("valid"),
+        min_value=-1e5,
+        max_value=1e5,
+        min_num_dims=2,
+        max_num_dims=3,
+        min_dim_size=2,
+        max_dim_size=3,
+        large_abs_safety_factor=10,
+        small_abs_safety_factor=10,
+        safety_factor_scale="log",
+    ),
+    axes=st.sampled_from([(0, 1), (-1, -2), (1, 0), None]),
+    s=st.tuples(
+        st.integers(min_value=2, max_value=256), st.integers(min_value=2, max_value=256)
+    )
+    | st.none(),
+    norm=st.sampled_from(["backward", "ortho", "forward", None]),
+)
+def test_numpy_fftn(
+    dtype_values,
+    s,
+    axes,
+    norm,
+    frontend,
+    backend_fw,
+    test_flags,
+    fn_tree,
+    on_device,
+):
+    dtype, values = dtype_values
+    helpers.test_frontend_function(
+        input_dtypes=dtype,
+        frontend=frontend,
+        backend_to_test=backend_fw,
+        test_flags=test_flags,
+        fn_tree=fn_tree,
+        on_device=on_device,
+        a=values[0],
+        s=s,
+        axes=axes,
+        norm=norm,
+    )
+
+
 @handle_frontend_test(
     fn_tree="numpy.fft.fftshift",
     dtype_and_x=helpers.dtype_and_values(