Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

BUG: Fix fftn failure when both out and s parameters are given #28895

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
Loading
from

Conversation

shibozhou
Copy link

Fixes #28890

Problem

When N-dim FFTs are calculated using fftn, the code performs a loop over all axes, applying 1D FFts in sequence. However, when both out and s are specified, intermediate transforms change the array shape, making the out parameter incompatible with these intermediate results.

Solution

This pr changes the _raw_fftnd to only use the out parameter for the final transformation, not for intermediate steps. The current function works correctly when both out and s have different shapes than the input array.

Test

The added test case verifies the fix with an input array of shape (4, 4, 4) and an output with shape (6, 6, 6)

@shibozhou
Copy link
Author

Looks like a pypy issue. Similar to #28469 what they encountered.

@ngoldbaum ngoldbaum requested a review from mhvk May 5, 2025 15:03
if out is not None and s is not None and any(s[i] != a.shape[axes[i]] for i in range(len(axes))):
intermediate_out = None
else:
intermediate_out = out
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it bring any performance improvement if we pass a itself when possible as out in intermediate steps (when out is originally None)? We might not be able to do so in the first iteration tough.

Copy link
Contributor

@mhvk mhvk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shibozhou - I had some comments and then realized it may be possible to be a bit smarter about intermediate results, and at the same time address @vtavana's comment about using in-place with the input where possible (which should also speed up the out=None case). I'll push to your branch with that, but the tests still need to be improved a bit. Furthermore, the documentation for out is now no longer correct, so that needs adjusting as well.

@@ -734,11 +734,21 @@ def _cook_nd_args(a, s=None, axes=None, invreal=0):

def _raw_fftnd(a, s=None, axes=None, function=fft, norm=None, out=None):
a = asarray(a)
s, axes = _cook_nd_args(a, s, axes)
s, axes = _cook_nd_args(a, s, axes, function is irfft)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change? _raw_fftnd is never called with function=irfft.

s, axes = _cook_nd_args(a, s, axes)
s, axes = _cook_nd_args(a, s, axes, function is irfft)

if out is not None and s is not None and any(s[i] != a.shape[axes[i]] for i in range(len(axes))):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that s is already overridden here.

@@ -465,6 +465,28 @@ def test_irfftn_out_and_s_interaction(self, s):
assert result is out
assert_array_equal(result, expected)

def test_fftn_with_out_and_s(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest to use a pytest.mark.parametrize here to do the two tests and add more possible s tuples, making values both bigger and smaller than the array in various positions. Also, could you move the test right below test_fftn_out_and_s_interaction (and maybe call it test_fftn_out_and_s_interaction2).

def test_fftn_with_out_and_s(self):
# fftn works correctly with both out and s parameters
rng = np.random.RandomState(42)
x = np.random.rand(4, 4, 4) + 1j * np.random.rand(4, 4, 4)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be wise to ensure we never modify the input, by doing after the definition, x.flags.writeable = False.

elif this_out is not None and n < this_out.shape[axis]:
# For an intermediate step where we return fewer elements, we
# can use a smaller view of the previous array.
this_out = this_out[(slice(None,),)*(axis-1) + (slice(n),)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it should be axis

Suggested change
this_out = this_out[(slice(None,),)*(axis-1) + (slice(n),)]
this_out = this_out[(slice(None,),)*axis + (slice(n),)]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you're right - which just goes to show that this really needs additional tests!

@shibozhou - are you up for adding tests, or would you prefer that I do it?

Along a similar vein: I think axis can actually be negative here -- they should be normalized, perhaps best in _cook_nd_args.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

BUG: N-D FFTs fail when both out and s are given
3 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.