Skip to content

Add arbitrary-N real FFT via generalized Bruun (z^N-1 factorization)#50

Merged
falseywinchnet merged 1 commit into
mainfrom
feature/arbitrary-n-genbruun
Jun 24, 2026
Merged

Add arbitrary-N real FFT via generalized Bruun (z^N-1 factorization)#50
falseywinchnet merged 1 commit into
mainfrom
feature/arbitrary-n-genbruun

Conversation

@falseywinchnet

Copy link
Copy Markdown
Owner

Extend bfft.rfft/irfft to any N >= 2. Power-of-two stays on the native Bruun kernel; every other size uses a new generalized Bruun plan that factors z^N-1 over the reals -- the 2-adic part reuses the existing power-of-two SIMD cascade, and each odd prime factor peels off through a condition-1 real radix-p codelet (z^(pM)-1 = (z^M-1) * prod_{j}(z^(2M) - 2cos(2pij/p) z^M + 1)). FFT-grade for all N including primes (0.33-2.0x of NumPy/FFTW). Not Bluestein/Rader/mixed-radix.

  • src/detail/genbruun_kernel.hpp: bruun::GenBruun (plan-time op schedule, owns scratch, forward + inverse; rooted pow2 cascades reuse norm_q_fwd/norm_q_inv)
  • src/bfft.cpp: bfft_plan is now a tagged {RFFT | GenBruun}; bfft_plan_create accepts non-pow2; forward/inverse/size/bins dispatch; pow2-only entry points (native/magnitude/residue/f32) guarded -> BFFT_ERROR_INVALID_ARGUMENT
  • bfft/_core.py, bfft/init.py: relax N check to N>=2; docstrings
  • tests/genbruun_correctness.cpp (+ Makefile): make test now covers arbitrary N
  • benchmarks/odd_prime_power_fftw_benchmark.cpp: FFTW-baseline benchmark, all N
  • README "Arbitrary-N support"; notes/genbruun_port_handoff.md; src/detail/genbruun_acceleration_plan.md
  • scratch_genbruun*.{cpp,py}: reference implementations

Perf: the arbitrary-N odd-radix projections are not yet SIMD, so non-pow2 sizes are ~3.5-5x slower than FFTW on prime powers (pow2 path unaffected). The acceleration plan documents the AVX2/NEON codelet work.

Extend bfft.rfft/irfft to any N >= 2. Power-of-two stays on the native Bruun
kernel; every other size uses a new generalized Bruun plan that factors z^N-1
over the reals -- the 2-adic part reuses the existing power-of-two SIMD cascade,
and each odd prime factor peels off through a condition-1 real radix-p codelet
(z^(pM)-1 = (z^M-1) * prod_{j}(z^(2M) - 2cos(2*pi*j/p) z^M + 1)). FFT-grade for
all N including primes (0.33-2.0x of NumPy/FFTW). Not Bluestein/Rader/mixed-radix.

- src/detail/genbruun_kernel.hpp: bruun::GenBruun (plan-time op schedule, owns
  scratch, forward + inverse; rooted pow2 cascades reuse norm_q_fwd/norm_q_inv)
- src/bfft.cpp: bfft_plan is now a tagged {RFFT | GenBruun}; bfft_plan_create
  accepts non-pow2; forward/inverse/size/bins dispatch; pow2-only entry points
  (native/magnitude/residue/f32) guarded -> BFFT_ERROR_INVALID_ARGUMENT
- bfft/_core.py, bfft/__init__.py: relax N check to N>=2; docstrings
- tests/genbruun_correctness.cpp (+ Makefile): make test now covers arbitrary N
- benchmarks/odd_prime_power_fftw_benchmark.cpp: FFTW-baseline benchmark, all N
- README "Arbitrary-N support"; notes/genbruun_port_handoff.md;
  src/detail/genbruun_acceleration_plan.md
- scratch_genbruun*.{cpp,py}: reference implementations

Perf: the arbitrary-N odd-radix projections are not yet SIMD, so non-pow2 sizes
are ~3.5-5x slower than FFTW on prime powers (pow2 path unaffected). The
acceleration plan documents the AVX2/NEON codelet work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@falseywinchnet falseywinchnet merged commit 40ae18c into main Jun 24, 2026
4 checks passed
@falseywinchnet falseywinchnet deleted the feature/arbitrary-n-genbruun branch June 24, 2026 22:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant