3
votes

I used to use fftw_plan_dft for multi-dimensional Fourier transformation.

fftw_plan fftw_plan_dft(int rank, const int *n, fftw_complex *in,
                        fftw_complex *out, int sign, unsigned flags);

Now I want to pass 64 bit integer to fftw, it looks like I need to use fftw guru interface.

 fftw_plan fftw_plan_guru64_dft(
     int rank, const fftw_iodim64 *dims,
     int howmany_rank, const fftw_iodim64 *howmany_dims,
     fftw_complex *in, fftw_complex *out,
     int sign, unsigned flags);

But I do not understand what is howmany_rank and howmany_dims mean. The manual of fftw_plan_guru_dft says:

These two functions plan a complex-data, multi-dimensional DFT for the interleaved and split format, respectively. Transform dimensions are given by (rank, dims) over a multi-dimensional vector (loop) of dimensions (howmany_rank, howmany_dims). dims and howmany_dims should point to fftw_iodim arrays of length rank and howmany_rank, respectively.

I do know know what is "multi-dimensional vector (loop) of dimensions (howmany_rank, howmany_dims)" mean. Can you give me an example or explain how to use this guru interface?

1
Since you tagged pyfftw in this, Here's an example. That line is calling a function pointer that points to a guru interface function. Read the python above to work out how to populate the parameters.Henry Gomersall
Thank you @Henry Gomersall . I can see this line is using guru interface. I am trying hard to read this python code. However, it is a lit bit long, is it okay that you explain the meaning of howmany_rank and howmany_dims to me?Hao Shi
I suggest reading the docs and thinking hard about it. I can't see a short cut to that. You'll find you need various bits of information to specify arbitrary transforms and at that point it'll become obvious. Alternatively, you can put some effort into understanding the code I linked to which will make everything clear (it's a good example because it describes the general mapping from shape, strides and transform axes into the FFTW parameters).Henry Gomersall
@Henry Gomersall Thank you.Hao Shi

1 Answers

2
votes

If the sizes and strides of your mulitdimensional arrays are larger than 2^32, the 64 bit guru interface becomes useful.

The prototype of the function creating complex to complex DTFs is:

fftw_plan fftw_plan_guru64_dft(
 int rank, const fftw_iodim64 *dims,
 int howmany_rank, const fftw_iodim64 *howmany_dims,
 fftw_complex *in, fftw_complex *out,
 int sign, unsigned flags);

where:

  • rank is the rank of the FFTW transform to be performed, that is the number of dimensions.
  • dims is an array of size rank. For each dimension i, dims[i].n is the size of the line, dims[i].is is the stride between lines of the input array and dims[i].os is the stride between lines of the output array. For instance, if the array is contiguous in memory, then the documentation of the guru interface suggests to use the recurrence dims[i].is = n[i+1] * dims[i+1].is. The number of transforms to be performed and the offsets between the starting points are given by howmany_rank and howmany_dims.
  • howmany_rank specifies the number of transforms featuring particular offsets.
  • howmany_dims is an array of size howmany_rank. For each transform i, howmany_dims[i].n is the number of transforms to be computed, each featuring offset between inputs howmany_dims[i].is and offset between output howmany_dims[i].os.

More detail about these arguments are provided in Confusion about FFTW3 guru interface: 3 simultaneous complex FFTs

The following code calls fftw_plan_guru64_dft() so that it performs the same thing as fftw_plan_dft_3d(). It can be compiled by gcc main.c -o main -lfftw3 -lm -Wall:

#include<stdlib.h>
#include<complex.h>
#include<math.h>
#include<fftw3.h>

int main(void){

    fftw_plan p;
    unsigned long int N = 10;
    unsigned long int M = 12;
    unsigned long int P = 14;
    fftw_complex *in=fftw_malloc(N*M*P*sizeof(fftw_complex));
    if(in==NULL){fprintf(stderr,"malloc failed\n");exit(1);}
    fftw_complex *out=fftw_malloc(N*M*P*sizeof(fftw_complex));
    if(out==NULL){fprintf(stderr,"malloc failed\n");exit(1);}
    unsigned int i,j,k;

    int rank=3;
    fftw_iodim64 *dims=malloc(rank*sizeof(fftw_iodim64));
    if(dims==NULL){fprintf(stderr,"malloc failed\n");exit(1);}
    dims[0].n=N;
    dims[0].is=P*M;
    dims[0].os=P*M;
    dims[1].n=M;
    dims[1].is=P;
    dims[1].os=P;
    dims[2].n=P;
    dims[2].is=1;
    dims[2].os=1;

    int howmany_rank=1;
    fftw_iodim64 *howmany_dims=malloc(howmany_rank*sizeof(fftw_iodim64));
    if(howmany_dims==NULL){fprintf(stderr,"malloc failed\n");exit(1);}
    howmany_dims[0].n=1;
    howmany_dims[0].is=1;
    howmany_dims[0].os=1;

    printf("sizeof fftw complex %ld\n",sizeof(fftw_complex));
    printf("sizeof fftw_iodim64 %ld\n",sizeof(fftw_iodim64));
    printf("creating the plan\n");
    p=fftw_plan_guru64_dft(rank, dims,howmany_rank, howmany_dims,in, out,FFTW_FORWARD, FFTW_ESTIMATE);
    if (p==NULL){fprintf(stderr,"plan creation failed\n");exit(1);}
    printf("created the plan\n");

    for(i=0;i<N;i++){
        for(j=0;j<M;j++){
            for(k=0;k<P;k++){
                //printf("ijk\n");
                in[(i*M+j)*P+k]=30.+12.*sin(2*3.1415926535*i/((double)N))*sin(2*3.1415926535*j/((double)M))*sin(2*3.1415926535*k/((double)P))*I;
            }
        }
    }

    fftw_execute(p);

    for (i = 0; i < N; i++){
        for (j = 0; j < M; j++){
            for (k = 0; k < P; k++){
                printf("result: %d %d %d %g %gI\n", i,j,k, creal(out[(i*M+j)*P+k]), cimag(out[(i*M+j)*P+k]));
            }
        }
    }


    fftw_destroy_plan(p);
    fftw_free(in);
    fftw_free(out);

    free(dims);
    free(howmany_dims);

    return(0);
}

For instance, the guru interface can be used to compute the DFT of a complex 3D electric field. At each point of the grid, the electric field is a vector of size 3. Hence, I can store the electric field as a 4D array, the last dimension specifying the component of the vector. Finally, the guru interface can be used to perform the three 3D DFTs at once:

#include<stdlib.h>
#include<complex.h>
#include<math.h>
#include<fftw3.h>

int main(void){

    fftw_plan p;
    unsigned long int N = 10;
    unsigned long int M = 12;
    unsigned long int P = 14;
    unsigned long int DOF = 3;
    fftw_complex *in=fftw_malloc(N*M*P*DOF*sizeof(fftw_complex));
    if(in==NULL){fprintf(stderr,"malloc failed\n");exit(1);}
    fftw_complex *out=fftw_malloc(N*M*P*DOF*sizeof(fftw_complex));
    if(out==NULL){fprintf(stderr,"malloc failed\n");exit(1);}
    unsigned int i,j,k;

    int rank=3;
    fftw_iodim64 *dims=malloc(rank*sizeof(fftw_iodim64));
    if(dims==NULL){fprintf(stderr,"malloc failed\n");exit(1);}
    dims[0].n=N;
    dims[0].is=P*M*DOF;
    dims[0].os=P*M*DOF;
    dims[1].n=M;
    dims[1].is=P*DOF;
    dims[1].os=P*DOF;
    dims[2].n=P;
    dims[2].is=DOF;
    dims[2].os=DOF;

    int howmany_rank=1;
    fftw_iodim64 *howmany_dims=malloc(howmany_rank*sizeof(fftw_iodim64));
    if(howmany_dims==NULL){fprintf(stderr,"malloc failed\n");exit(1);}
    howmany_dims[0].n=DOF;
    howmany_dims[0].is=1;
    howmany_dims[0].os=1;

    printf("sizeof fftw complex %ld\n",sizeof(fftw_complex));
    printf("sizeof fftw_iodim64 %ld\n",sizeof(fftw_iodim64));
    printf("creating the plan\n");
    p=fftw_plan_guru64_dft(rank, dims,howmany_rank, howmany_dims,in, out,FFTW_FORWARD, FFTW_ESTIMATE);
    if (p==NULL){fprintf(stderr,"plan creation failed\n");exit(1);}
    printf("created the plan\n");

    for(i=0;i<N;i++){
        for(j=0;j<M;j++){
            for(k=0;k<P;k++){
                //printf("ijk\n");
                in[((i*M+j)*P+k)*DOF]=30.+12.*sin(2*3.1415926535*i/((double)N))*sin(2*3.1415926535*j/((double)M))*sin(2*3.1415926535*k/((double)P))*I;
                in[((i*M+j)*P+k)*DOF+1]=42.0;
                in[((i*M+j)*P+k)*DOF+2]=1.0;
            }
        }
    }

    fftw_execute(p);

    for (i = 0; i < N; i++){
        for (j = 0; j < M; j++){
            for (k = 0; k < P; k++){
                printf("result: %d %d %d || %g %gI | %g %gI | %g %gI\n", i,j,k, creal(out[((i*M+j)*P+k)*DOF]), cimag(out[((i*M+j)*P+k)*DOF]),creal(out[((i*M+j)*P+k)*DOF+1]), cimag(out[((i*M+j)*P+k)*DOF+1]),creal(out[((i*M+j)*P+k)*DOF+2]), cimag(out[((i*M+j)*P+k)*DOF+2]));
            }
        }
    }


    fftw_destroy_plan(p);
    fftw_free(in);
    fftw_free(out);

    free(dims);
    free(howmany_dims);

    return(0);
}