LAL  7.5.0.1-fe68b98
RealFFTTest.c
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2007 Duncan Brown, Jolien Creighton
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with with program; see the file COPYING. If not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 * MA 02110-1301 USA
18 */
19 
20 /**
21  * \file
22  * \ingroup RealFFT_h
23  *
24  * \brief Tests the routines in \ref RealFFT.h.
25  *
26  * ### Usage ###
27  *
28  * \code
29  * RealFFTTest [options]
30  * Options:
31  * -h print this message
32  * -q quiet: run silently
33  * -v verbose: print extra information
34  * -d level set lalDebugLevel to level
35  * -m trials set number of random trials
36  * -n size set size of FFTs
37  * \endcode
38  *
39  * Use the <tt>-n</tt> option to specify the size of the test transform and
40  * the <tt>-m</tt> option to specify the number of test transforms of that size.
41  * (Default is to test transforms of size 1 to 128 in unit steps and then
42  * powers of two up to 65536.)
43  *
44  * ### Exit codes ###
45  *
46  * <table><tr><th>Code</th><th>Explanation</th></tr>
47  * <tr><td>0</td><td>Success, normal exit.</td></tr>
48  * <tr><td>1</td><td>Subroutine failed.</td></tr>
49  * </table>
50  *
51  * ### Uses ###
52  *
53  *
54  * ### Notes ###
55  *
56  */
57 
58 /** \cond DONT_DOXYGEN */
59 #include <config.h>
60 
61 #include <complex.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <math.h>
65 
66 #include <lal/LALStdlib.h>
67 #include <lal/LALgetopt.h>
68 #include <lal/LALConstants.h>
69 #include <lal/SeqFactories.h>
70 #include <lal/RealFFT.h>
71 #include <lal/VectorOps.h>
72 #include <config.h>
73 
74 #define CODES_(x) #x
75 #define CODES(x) CODES_(x)
76 
77 int verbose = 0;
78 UINT4 m_ = 1; /* number of random trials */
79 UINT4 n_ = 0; /* size of each transform */
80 
81 static void
82 Usage( const char *program, int exitflag );
83 
84 static void
85 ParseOptions( int argc, char *argv[] );
86 
87 static void
88 TestStatus( LALStatus *status, const char *expectedCodes, int exitCode );
89 
90 void LALForwardRealDFT(
93  REAL4Vector *input
94  );
95 
96 int main( int argc, char *argv[] )
97 {
98  static LALStatus status;
99 
100  RealFFTPlan *fwd = NULL;
101  RealFFTPlan *rev = NULL;
102  REAL4Vector *dat = NULL;
103  REAL4Vector *rfft = NULL;
104  REAL4Vector *ans = NULL;
105  COMPLEX8Vector *dft = NULL;
106  COMPLEX8Vector *fft = NULL;
107 #if LAL_CUDA_ENABLED
108  /* The test itself should pass at 1e-4, but it might fail at
109  * some rare cases where accuracy is bad for some numbers. */
110  REAL8 eps = 3e-4;
111 #else
112  /* very conservative floating point precision */
113  REAL8 eps = 1e-6;
114 #endif
115  REAL8 lbn;
116  REAL8 ssq;
117  REAL8 var;
118  REAL8 tol;
119 
120  UINT4 nmax;
121  UINT4 m;
122  UINT4 n;
123  UINT4 i;
124  UINT4 j;
125  UINT4 k;
126  UINT4 s = 0;
127 
128  FILE *fp;
129 
130 
131  ParseOptions( argc, argv );
132  m = m_;
133  n = n_;
134 
135  fp = verbose ? stdout : NULL ;
136 
137  if ( n == 0 )
138  {
139  nmax = 65536;
140  }
141  else
142  {
143  nmax = n--;
144  }
145 
146  while ( n < nmax )
147  {
148  if ( n < 128 )
149  {
150  ++n;
151  }
152  else
153  {
154  n *= 2;
155  }
156 
157  LALSCreateVector( &status, &dat, n );
158  TestStatus( &status, CODES( 0 ), 1 );
159  LALSCreateVector( &status, &rfft, n );
160  TestStatus( &status, CODES( 0 ), 1 );
161  LALSCreateVector( &status, &ans, n );
162  TestStatus( &status, CODES( 0 ), 1 );
163  LALCCreateVector( &status, &dft, n / 2 + 1 );
164  TestStatus( &status, CODES( 0 ), 1 );
165  LALCCreateVector( &status, &fft, n / 2 + 1 );
166  TestStatus( &status, CODES( 0 ), 1 );
167  fwd = XLALCreateForwardREAL4FFTPlan( n, 0 );
168  rev = XLALCreateReverseREAL4FFTPlan( n, 0 );
169  TestStatus( &status, CODES( 0 ), 1 );
170 
171  /*
172  *
173  * Do m trials of random data.
174  *
175  */
176  for ( i = 0; i < m; ++i )
177  {
178  srand( s++ ); /* seed the random number generator */
179 
180  /*
181  *
182  * Create data and compute error tolerance.
183  *
184  * Reference: Kaneko and Liu,
185  * "Accumulation of round-off error in fast fourier tranforms"
186  * J. Asssoc. Comp. Mach, Vol 17 (No 4) 637-654, October 1970.
187  *
188  */
189  srand( i ); /* seed the random number generator */
190  ssq = 0;
191  for ( j = 0; j < n; ++j )
192  {
193  dat->data[j] = 20.0 * rand() / (REAL4)( RAND_MAX + 1.0 ) - 10.0;
194  ssq += dat->data[j] * dat->data[j];
195  fp ? fprintf( fp, "%e\n", dat->data[j] ) : 0;
196  }
197  lbn = log( n ) / log( 2 );
198  var = 2.5 * lbn * eps * eps * ssq / n;
199  tol = 5 * sqrt( var ); /* up to 5 sigma excursions */
200  fp ? fprintf( fp, "\neps = %e \ntol = %e\n", eps, tol ) : 0;
201 
202  /*
203  *
204  * Perform forward FFT and DFT (only if n < 100).
205  *
206  */
207  XLALREAL4ForwardFFT( fft, dat, fwd );
208  XLALREAL4VectorFFT( rfft, dat, fwd );
209  XLALREAL4VectorFFT( ans, rfft, rev );
210  fp ? fprintf( fp, "rfft()\t\trfft(rfft())\trfft(rfft())\n\n" ) : 0;
211  for ( j = 0; j < n; ++j )
212  {
213  fp ? fprintf( fp, "%e\t%e\t%e\n",
214  rfft->data[j], ans->data[j], ans->data[j] / n ) : 0;
215  }
216  if ( n < 128 )
217  {
218  LALForwardRealDFT( &status, dft, dat );
219  TestStatus( &status, CODES( 0 ), 1 );
220 
221  /*
222  *
223  * Check accuracy of FFT vs DFT.
224  *
225  */
226  fp ? fprintf( fp, "\nfftre\t\tfftim\t\t" ) : 0;
227  fp ? fprintf( fp, "dtfre\t\tdftim\n" ) : 0;
228  for ( k = 0; k <= n / 2; ++k )
229  {
230  REAL8 fftre = creal(fft->data[k]);
231  REAL8 fftim = cimag(fft->data[k]);
232  REAL8 dftre = creal(dft->data[k]);
233  REAL8 dftim = cimag(dft->data[k]);
234  REAL8 errre = fabs( dftre - fftre );
235  REAL8 errim = fabs( dftim - fftim );
236  REAL8 avere = fabs( dftre + fftre ) / 2 + eps;
237  REAL8 aveim = fabs( dftim + fftim ) / 2 + eps;
238  REAL8 ferre = errre / avere;
239  REAL8 ferim = errim / aveim;
240  fp ? fprintf( fp, "%e\t%e\t", fftre, fftim ) : 0;
241  fp ? fprintf( fp, "%e\t%e\n", dftre, dftim ) : 0;
242  /* fp ? fprintf( fp, "%e\t%e\t", errre, errim ) : 0; */
243  /* fp ? fprintf( fp, "%e\t%e\n", ferre, ferim ) : 0; */
244  if ( ferre > eps && errre > tol )
245  {
246  fputs( "FAIL: Incorrect result from forward transform\n", stderr );
247  fprintf( stderr, "\tdifference = %e\n", errre );
248  fprintf( stderr, "\ttolerance = %e\n", tol );
249  fprintf( stderr, "\tfrac error = %e\n", ferre );
250  fprintf( stderr, "\tprecision = %e\n", eps );
251  return 1;
252  }
253  if ( ferim > eps && errim > tol )
254  {
255  fputs( "FAIL: Incorrect result from forward transform\n", stderr );
256  fprintf( stderr, "\tdifference = %e\n", errim );
257  fprintf( stderr, "\ttolerance = %e\n", tol );
258  fprintf( stderr, "\tfrac error = %e\n", ferim );
259  fprintf( stderr, "\tprecision = %e\n", eps );
260  return 1;
261  }
262  }
263  }
264 
265  /*
266  *
267  * Perform reverse FFT and check accuracy vs original data.
268  *
269  */
270  XLALREAL4ReverseFFT( ans, fft, rev );
271  TestStatus( &status, CODES( 0 ), 1 );
272  fp ? fprintf( fp, "\ndat->data[j]\tans->data[j] / n\n" ) : 0;
273  for ( j = 0; j < n; ++j )
274  {
275  REAL8 err = fabs( dat->data[j] - ans->data[j] / n );
276  REAL8 ave = fabs( dat->data[j] + ans->data[j] / n ) / 2 + eps;
277  REAL8 fer = err / ave;
278  fp ? fprintf( fp, "%e\t%e\n", dat->data[j], ans->data[j] / n ) : 0;
279  /* fp ? fprintf( fp, "%e\t%e\n", err, fer ) : 0; */
280  if ( fer > eps && err > tol )
281  {
282  fputs( "FAIL: Incorrect result after reverse transform\n", stderr );
283  fprintf( stderr, "\tdifference = %e\n", err );
284  fprintf( stderr, "\ttolerance = %e\n", tol );
285  fprintf( stderr, "\tfrac error = %e\n", fer );
286  fprintf( stderr, "\tprecision = %e\n", eps );
287  return 1;
288  }
289  }
290  }
291 
292  LALSDestroyVector( &status, &dat );
293  TestStatus( &status, CODES( 0 ), 1 );
294  LALSDestroyVector( &status, &rfft );
295  TestStatus( &status, CODES( 0 ), 1 );
296  LALSDestroyVector( &status, &ans );
297  TestStatus( &status, CODES( 0 ), 1 );
298  LALCDestroyVector( &status, &dft );
299  TestStatus( &status, CODES( 0 ), 1 );
300  LALCDestroyVector( &status, &fft );
301  TestStatus( &status, CODES( 0 ), 1 );
304 
305  /* Null pointers should be a no-op */
306  rev = NULL;
307  fwd = NULL;
310 
311  TestStatus( &status, CODES( 0 ), 1 );
312  }
313 
315  return 0;
316 }
317 
318 /*
319  * TestStatus()
320  *
321  * Routine to check that the status code status->statusCode agrees with one of
322  * the codes specified in the space-delimited string ignored; if not,
323  * exit to the system with code exitcode.
324  *
325  */
326 static void
327 TestStatus( LALStatus *status, const char *ignored, int exitcode )
328 {
329  char str[64];
330  char *tok;
331 
332  if ( verbose )
333  {
334  REPORTSTATUS( status );
335  }
336 
337  if ( strncpy( str, ignored, sizeof( str ) ) )
338  {
339  if ( (tok = strtok( str, " " ) ) )
340  {
341  do
342  {
343  if ( status->statusCode == atoi( tok ) )
344  {
345  return;
346  }
347  }
348  while ( ( tok = strtok( NULL, " " ) ) );
349  }
350  else
351  {
352  if ( status->statusCode == atoi( str ) )
353  {
354  return;
355  }
356  }
357  }
358 
359  fprintf( stderr, "\nExiting to system with code %d\n", exitcode );
360  exit( exitcode );
361 }
362 
363 /*
364  *
365  * Usage()
366  *
367  * Prints a usage message for program program and exits with code exitcode.
368  *
369  */
370 static void
371 Usage( const char *program, int exitcode )
372 {
373  fprintf( stderr, "Usage: %s [options]\n", program );
374  fprintf( stderr, "Options:\n" );
375  fprintf( stderr, " -h print this message\n" );
376  fprintf( stderr, " -q quiet: run silently\n" );
377  fprintf( stderr, " -v verbose: print extra information\n" );
378  fprintf( stderr, " -d level set lalDebugLevel to level\n" );
379  fprintf( stderr, " -m trials set number of random trials\n" );
380  fprintf( stderr, " -n size set size of FFTs\n" );
381  exit( exitcode );
382 }
383 
384 
385 /*
386  * ParseOptions()
387  *
388  * Parses the argc - 1 option strings in argv[].
389  *
390  */
391 static void
392 ParseOptions( int argc, char *argv[] )
393 {
394  FILE *fp;
395 
396  while ( 1 )
397  {
398  int c = -1;
399 
400  c = LALgetopt( argc, argv, "hqvd:m:n:" );
401  if ( c == -1 )
402  {
403  break;
404  }
405 
406  switch ( c )
407  {
408  case 'n': /* set FFT size */
409  n_ = atoi( LALoptarg );
410  break;
411 
412  case 'm': /* set number of trials */
413  m_ = atoi( LALoptarg );
414  break;
415 
416  case 'd': /* set debug level */
417  break;
418 
419  case 'v': /* verbose */
420  ++verbose;
421  break;
422 
423  case 'q': /* quiet: run silently (ignore error messages) */
424  fp = freopen( "/dev/null", "w", stderr );
425  if (fp == NULL)
426  {
427  fprintf(stderr, "Error: Unable to open /dev/null\n");
428  exit(1);
429  }
430  fp = freopen( "/dev/null", "w", stdout );
431  if (fp == NULL)
432  {
433  fprintf(stderr, "Error: Unable to open /dev/null\n");
434  exit(1);
435  }
436  break;
437 
438  case 'h':
439  Usage( argv[0], 0 );
440  break;
441 
442  default:
443  Usage( argv[0], 1 );
444  }
445 
446  }
447 
448  if ( LALoptind < argc )
449  {
450  Usage( argv[0], 1 );
451  }
452 
453  return;
454 }
455 
456 
457 static void
458 LALDFT(
459  LALStatus *status,
461  COMPLEX8Vector *input,
462  INT4 sign
463  )
464 {
465  UINT4 n;
466  UINT4 j;
467  UINT4 k;
468 
470 
471  n = output->length;
472 
473  for ( k = 0; k < n; ++k )
474  {
475  REAL8 sre = 0;
476  REAL8 sim = 0;
477  for ( j = 0; j < n; ++j )
478  {
479  REAL8 phi = sign * LAL_TWOPI * (REAL8)(j) * (REAL8)(k) / (REAL8)(n);
480  REAL8 c = cos( phi );
481  REAL8 s = sin( phi );
482  REAL8 re = creal(input->data[j]);
483  REAL8 im = cimag(input->data[j]);
484  sre += c * re - s * im;
485  sim += c * im + s * re;
486  }
487  output->data[k] = sre;
488  output->data[k] += I * sim;
489  }
490 
491  RETURN( status );
492 }
493 
494 void LALForwardRealDFT(
495  LALStatus *status,
497  REAL4Vector *input
498  )
499 {
500  COMPLEX8Vector *a = NULL;
501  COMPLEX8Vector *b = NULL;
502  UINT4 n;
503  UINT4 j;
504  UINT4 k;
505 
508 
509  n = input->length;
510 
511  TRY( LALCCreateVector( status->statusPtr, &a, n ), status );
512  TRY( LALCCreateVector( status->statusPtr, &b, n ), status );
513 
514  for ( j = 0; j < n; ++j )
515  a->data[j] = input->data[j];
516 
517  TRY( LALDFT( status->statusPtr, b, a, -1 ), status );
518 
519  for ( k = 0; k <= n / 2; ++k )
520  output->data[k] = b->data[k];
521 
522  TRY( LALCDestroyVector( status->statusPtr, &a ), status );
523  TRY( LALCDestroyVector( status->statusPtr, &b ), status );
524 
526  RETURN( status );
527 }
528 
529 /** \endcond */
const char * program
void REPORTSTATUS(LALStatus *status)
Definition: LALError.c:322
void LALCheckMemoryLeaks(void)
Definition: LALMalloc.c:784
#define TRY(func, statusptr)
#define ATTATCHSTATUSPTR(statusptr)
#define DETATCHSTATUSPTR(statusptr)
#define INITSTATUS(statusptr)
#define RETURN(statusptr)
static int sign(int s)
Definition: LALStringTest.c:27
int LALgetopt(int argc, char *const *argv, const char *optstring)
Definition: LALgetopt.c:172
int LALoptind
Definition: LALgetopt.c:79
char * LALoptarg
Definition: LALgetopt.c:64
#define fprintf
static void Usage(const char *program, int exitcode)
Definition: XLALChisqTest.c:70
static void ParseOptions(int argc, char *argv[])
Definition: XLALChisqTest.c:88
int main(int argc, char *argv[])
Definition: cache.c:25
#define LAL_TWOPI
2*pi is circumference of a circle divided by its radius
Definition: LALConstants.h:180
double REAL8
Double precision real floating-point number (8 bytes).
uint32_t UINT4
Four-byte unsigned integer.
int32_t INT4
Four-byte signed integer.
float REAL4
Single precision real floating-point number (4 bytes).
static const INT4 m
Definition: Random.c:80
static const REAL4 eps
Definition: Random.c:84
static const INT4 a
Definition: Random.c:79
REAL4FFTPlan * XLALCreateReverseREAL4FFTPlan(UINT4 size, int measurelvl)
Returns a new REAL4FFTPlan for a reverse transform.
Definition: CudaRealFFT.c:130
#define RealFFTPlan
Definition: RealFFT.h:195
REAL4FFTPlan * XLALCreateForwardREAL4FFTPlan(UINT4 size, int measurelvl)
Returns a new REAL4FFTPlan for a forward transform.
Definition: CudaRealFFT.c:120
void XLALDestroyREAL4FFTPlan(REAL4FFTPlan *plan)
Destroys a REAL4FFTPlan.
Definition: CudaRealFFT.c:140
int XLALREAL4VectorFFT(REAL4Vector *_LAL_RESTRICT_ output, const REAL4Vector *_LAL_RESTRICT_ input, const REAL4FFTPlan *plan)
Perform a REAL4Vector to REAL4Vector FFT.
Definition: CudaRealFFT.c:233
int XLALREAL4ReverseFFT(REAL4Vector *output, const COMPLEX8Vector *input, const REAL4FFTPlan *plan)
Performs a reverse FFT of REAL4 data.
Definition: CudaRealFFT.c:198
int XLALREAL4ForwardFFT(COMPLEX8Vector *output, const REAL4Vector *input, const REAL4FFTPlan *plan)
Performs a forward FFT of REAL4 data.
Definition: CudaRealFFT.c:161
void LALCCreateVector(LALStatus *, COMPLEX8Vector **, UINT4)
void LALCDestroyVector(LALStatus *, COMPLEX8Vector **)
void LALSDestroyVector(LALStatus *, REAL4Vector **)
void LALSCreateVector(LALStatus *, REAL4Vector **, UINT4)
Vector of type COMPLEX8, see DATATYPE-Vector types for more details.
Definition: LALDatatypes.h:163
COMPLEX8 * data
Pointer to the data array.
Definition: LALDatatypes.h:168
LAL status structure, see The LALStatus structure for more details.
Definition: LALDatatypes.h:947
Vector of type REAL4, see DATATYPE-Vector types for more details.
Definition: LALDatatypes.h:145
REAL4 * data
Pointer to the data array.
Definition: LALDatatypes.h:150
UINT4 length
Number of elements in array.
Definition: LALDatatypes.h:149
int verbose
Definition: tconvert.c:103
FILE * fp
Definition: tconvert.c:105
void output(int gps_sec, int output_type)
Definition: tconvert.c:440