Multidimensional transforms
#include <flint/gr_dft.h>gr_dft_prod_pre_structgr_dft_prod_pre_tA plan for product DFTs with fixed component lengths over one ring. It holds one cyclic plan for each distinct component length.
int gr_dft_prod_precomp_init(gr_dft_prod_pre_t P, const ulong * cyc, slong num, int flags, gr_ctx_t ctx)sourceint gr_dft_prod_precomp_init_root(gr_dft_prod_pre_t P, gr_srcptr w, ulong order, const ulong * cyc, slong num, int flags, gr_ctx_t ctx)sourceInitialise P for the product DFT with the num component lengths cyc. The first function uses the default roots . The second takes a root of the given order, which every component length must divide, and uses . The flags are passed to the component plans. After any return value, P must be cleared with gr_dft_prod_precomp_clear.
Each component plan is made with GR_DFT_ALG_AUTO by gr_dft_precomp_init or gr_dft_precomp_init_root, and their status is returned, so the conditions of those functions apply to each component. gr_dft_prod_precomp_init_root returns GR_DOMAIN if some is zero or does not divide order. With num = 0 the transform is the identity on one entry.
void gr_dft_prod_precomp_clear(gr_dft_prod_pre_t P)sourceFrees the memory used by P.
int gr_dft_prod_precomp(gr_ptr res, gr_srcptr vec, const gr_dft_prod_pre_t P, gr_ctx_t ctx)sourceint gr_dft_prod_inverse_precomp(gr_ptr res, gr_srcptr vec, const gr_dft_prod_pre_t P, gr_ctx_t ctx)sourceint gr_dft_prod_inverse(gr_ptr res, gr_srcptr vec, const ulong * cyc, slong num, gr_ctx_t ctx)sourceSet res to the forward or inverse product DFT of vec, both of length , which may be the same. The last two use a temporary plan with default roots. The inverse divides by once at the end.
void gr_dft_prod_precomp_set_serial_block(gr_dft_prod_pre_t P, slong serial_block)sourceSets the threading granularity of the product transform: the lines of one axis are distributed over at most chunks. It does not change the component plans. Zero restores the default GR_DFT_SERIAL_BLOCK_DEFAULT.
The DFT on a finite abelian group is the -dimensional DFT of an array with entries. These functions compute it with one cyclic plan per distinct component length. They are the basis of the transforms on groups of Dirichlet characters (gr_dft_dirichlet), whose components are given by the Conrey decomposition and often repeat.
Layout and algorithm#
The array is stored in row-major order: the entry with index is at position , so the last index varies fastest. The transform is computed axis by axis: for each axis , every line of entries along that axis is transformed with the cyclic plan of length , in place for the last axis and through a buffer for the others, which are strided. Since
factors over the axes, there are no twiddle factors between the axes, in contrast to the Cooley–Tukey decomposition of a cyclic transform of the same total length. The inverse transform applies the inverse cyclic transforms without their divisions, and divides by once at the end. Axes of length 1 are skipped.
The order of the axes does not matter mathematically, and the product transform is not the cyclic transform of length , even when the are pairwise coprime: the Good–Thomas algorithm reduces the cyclic transform to a product transform only after permuting the input and the output (The prime factor algorithm).
Roots#
gr_dft_prod_precomp_init and the one-shot functions use the default roots , so they need a ring such as acb. Over other rings, gr_dft_prod_precomp_init_root takes one principal root of some order divisible by every , for example , and uses for the axis of length . Each of these is then a principal -th root of unity, and the component plans check them as described in Roots of unity.
Threads#
The lines of one axis are independent, and when the ring is thread-safe and flint_get_num_threads is greater than 1, they are distributed over at most chunks. Threads are requested from the global pool for each axis separately, and only for an axis with at least two lines; an axis with a single line, as in a product with one long axis, is transformed by its component plan, which can then use the threads of the pool itself. The granularity is set with gr_dft_prod_precomp_set_serial_block (default GR_DFT_SERIAL_BLOCK_DEFAULT).
Example#
The transform on over , with the root 3 of order 6: the axes use and . The input is the array with rows and .
#include <flint/flint.h>
#include <flint/gr.h>
#include <flint/gr_vec.h>
#include <flint/gr_dft.h>
/* DFT on Z/2 x Z/3 over Z/7Z, using w = 3 of order 6 */
int main(void)
{
gr_ctx_t ctx;
gr_dft_prod_pre_t P;
gr_ptr x, y, w;
ulong cyc[2] = { 2, 3 };
slong j, n = 6;
gr_ctx_init_nmod(ctx, 7);
x = gr_heap_init_vec(n, ctx);
y = gr_heap_init_vec(n, ctx);
w = gr_heap_init(ctx);
GR_MUST_SUCCEED(gr_set_ui(w, 3, ctx));
GR_MUST_SUCCEED(gr_dft_prod_precomp_init_root(P, w, 6, cyc, 2, 0, ctx));
/* entry 3 j0 + j1 holds x[j0][j1] */
for (j = 0; j < n; j++)
GR_MUST_SUCCEED(gr_set_si(GR_ENTRY(x, j, ctx->sizeof_elem), j + 1, ctx));
GR_MUST_SUCCEED(gr_dft_prod_precomp(y, x, P, ctx));
_gr_vec_print(y, n, ctx);
flint_printf("\n");
GR_MUST_SUCCEED(gr_dft_prod_inverse_precomp(y, y, P, ctx));
_gr_vec_print(y, n, ctx);
flint_printf("\n");
gr_dft_prod_precomp_clear(P);
gr_heap_clear(w, ctx);
gr_heap_clear_vec(x, n, ctx);
gr_heap_clear_vec(y, n, ctx);
gr_ctx_clear(ctx);
return 0;
}[0, 6, 2, 5, 0, 0] [1, 2, 3, 4, 5, 6]
For instance, the entry is .
Guides#
The FLINT team, “Multidimensional transforms (gr_dft.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/gr_dft/prod.html (generated 2026-11-02).
@misc{flint-doc-gr-dft-prod,
author = {{The FLINT team}},
title = {{Multidimensional transforms (gr\_dft.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/gr_dft/prod.html},
note = {Generated 2026-11-02}
}