Skip to main content

Fast Fourier transforms for the dihedral group over prime finite fields

Project description

fft-dihedral

PyPI License: MIT Python

Fast Fourier transforms for the dihedral group over prime finite fields.

This package computes the Fourier transform of functions on

D_{2n} = <r, s | r^n = s^2 = e, srs^{-1} = r^{-1}>.

The fast path reduces the nonabelian dihedral transform to four cyclic number-theoretic transforms (NTTs). The inverse transform reconstructs two cyclic spectra and applies two inverse NTTs.

Install

From PyPI:

python -m pip install fft-dihedral

From a checkout:

python -m pip install -e ".[plot,dev]"

Quick Start

from fft_dihedral import (
    DEFAULT_MODULUS,
    dihedral_dft_fft,
    dihedral_inverse_fft,
    flatten_transform,
    root_of_unity,
)

n = 16
omega = root_of_unity(n, DEFAULT_MODULUS)
rotations = list(range(n))
reflections = [2 * k for k in range(n)]

transform = dihedral_dft_fft(rotations, reflections, DEFAULT_MODULUS, omega)
assert len(flatten_transform(transform)) == 2 * n
assert dihedral_inverse_fft(transform, omega) == (rotations, reflections)

Conventions

A function f: D_{2n} -> GF(p) is represented by two length-n arrays:

rotations[k]   = f(r^k)
reflections[k] = f(s r^k)

For a primitive nth root of unity omega, the two-dimensional irreducible representations satisfy

rho_j(r^k)   = [[omega^(j k), 0], [0, omega^(-j k)]]
rho_j(s r^k) = [[0, omega^(-j k)], [omega^(j k), 0]]

With the normalized finite-group DFT convention

fhat(rho) = (1 / |D_{2n}|) sum_g f(g) rho(g),

each two-dimensional Fourier coefficient is assembled from four cyclic transforms:

fhat(rho_j) = 1/(2n) * [
  [DFT_omega(rotations)[j],    DFT_omega^{-1}(reflections)[j]],
  [DFT_omega(reflections)[j],  DFT_omega^{-1}(rotations)[j]]
]

The one-dimensional representations are computed by direct character sums.

What The Four NTTs Do

The input vector is really two vectors:

rotations   = [f(1), f(r), ..., f(r^{n-1})]
reflections = [f(s), f(sr), ..., f(sr^{n-1})]

The FFT applies cyclic NTTs to those two vectors with both orientations of the root:

A^+ = NTT_omega(rotations)
A^- = NTT_omega^{-1}(rotations)
B^+ = NTT_omega(reflections)
B^- = NTT_omega^{-1}(reflections)

For each two-dimensional irrep rho_j, the matrix coefficient is assembled as:

1/(2n) * [[A^+[j], B^-[j]],
          [B^+[j], A^-[j]]]

The inverse FFT reverses this packing. The one-dimensional coefficients recover the j = 0 cyclic frequencies, and, for even n, the j = n/2 frequencies. The two-dimensional matrices recover the paired frequencies j and n-j. Once the full A^+ and B^+ spectra have been rebuilt, two inverse NTTs give back the rotation and reflection coefficient vectors.

Group Algebra Multiplication

Elements of the group algebra are represented by the same two arrays:

x = sum_k rotations[k] r^k + sum_k reflections[k] s r^k

The package provides both a quadratic reference product and an FFT product:

from fft_dihedral import (
    DEFAULT_MODULUS,
    dihedral_multiply_fft,
    dihedral_multiply_naive,
    root_of_unity,
)

n = 16
omega = root_of_unity(n, DEFAULT_MODULUS)
lhs_rotations = [1] * n
lhs_reflections = [2] * n
rhs_rotations = [3] * n
rhs_reflections = [4] * n

fast = dihedral_multiply_fft(
    lhs_rotations,
    lhs_reflections,
    rhs_rotations,
    rhs_reflections,
    DEFAULT_MODULUS,
    omega,
)
slow = dihedral_multiply_naive(
    lhs_rotations,
    lhs_reflections,
    rhs_rotations,
    rhs_reflections,
    DEFAULT_MODULUS,
)
assert fast == slow

Because the transform is normalized by 1 / |D_{2n}|, multiplication in Fourier space is

(x y)^hat(rho) = |D_{2n}| xhat(rho) yhat(rho).

So the FFT product computes two transforms, multiplies each scalar or matrix block with the extra factor 2n, and then applies the inverse dihedral FFT.

Supported Fields

The implementation supports prime fields GF(p) when:

  • n is a power of two for the fast transform.
  • p is prime.
  • n | p - 1, so GF(p) contains a primitive nth root of unity.
  • gcd(2n, p) = 1, so the normalization by 1/(2n) is defined.

The default field is GF(2013265921), where

2013265921 = 15 * 2^27 + 1

so it supports power-of-two rotation orders up to 2^27.

True extension fields GF(p^k) are not implemented yet. This is intentionally stricter than integer quotient rings like Z/p^kZ; those are not fields when k > 1.

Arbitrary Compatible Primes

Roots are selected automatically using Sympy:

from fft_dihedral import root_of_unity

omega = root_of_unity(16, 97)

CLI verification over a non-default prime:

fft-dihedral verify --n 16 --modulus 97

You can also pass an explicit root:

fft-dihedral verify --n 16 --modulus 97 --omega 8

Verify

python -m pytest
fft-dihedral verify --n 128
fft-dihedral verify --n 16 --modulus 97

Benchmark

fft-dihedral bench --min-exp 4 --max-exp 15 --repetitions 5 --naive-limit 512
fft-dihedral bench --min-exp 4 --max-exp 6 --modulus 257 --repetitions 3
fft-dihedral bench-mul --min-exp 4 --max-exp 12 --repetitions 5 --naive-limit 512

The benchmark prints FFT time / (2n log2(2n)). For an O(2n log(2n)) algorithm this ratio should stay roughly flat as n grows, modulo Python timing noise and cache effects.

The bench-mul command compares group-algebra multiplication by FFT against the direct quadratic product. The fast path computes two forward dihedral FFTs, multiplies the Fourier blocks, and applies one inverse dihedral FFT.

Plot

Install the plotting extra:

python -m pip install "fft-dihedral[plot]"

Then generate PNG and SVG timing plots:

fft-dihedral-plot --min-exp 4 --max-exp 14 --repetitions 3 --naive-limit 1024

Limitations

  • No true extension-field GF(p^k) implementation yet.
  • The radix-2 NTT is implemented in pure Python, so the Rust crate is much faster for production workloads.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fft_dihedral-0.1.1.tar.gz (17.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fft_dihedral-0.1.1-py3-none-any.whl (14.8 kB view details)

Uploaded Python 3

File details

Details for the file fft_dihedral-0.1.1.tar.gz.

File metadata

  • Download URL: fft_dihedral-0.1.1.tar.gz
  • Upload date:
  • Size: 17.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for fft_dihedral-0.1.1.tar.gz
Algorithm Hash digest
SHA256 4f625a2d008799c2608398a0175fbc99977299932f103caed3b81c39552ddbbb
MD5 60386295afe8b5457bd57cc8de32b9c9
BLAKE2b-256 e101201276da0725a0748bf5070eed33c10872187f71181c583e9d7677638296

See more details on using hashes here.

File details

Details for the file fft_dihedral-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: fft_dihedral-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 14.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for fft_dihedral-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 13aea8a099601069a1c9ddc9c8736083d9edf6ea49bebc314c08aba6ae59f8e8
MD5 1b599673efcec7dff2a5b4b8e9a9c6fe
BLAKE2b-256 876ae85a950c617b8fb18425e97729ec0ca8ad896e68c30664ed601277dd91c6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page