File Coverage

blib/lib/PDL/FFT.pm
Criterion Covered Total %
statement 145 186 77.9
branch 31 54 57.4
condition 3 3 100.0
subroutine 19 20 95.0
pod 0 9 0.0
total 198 272 72.7


line stmt bran cond sub pod time code
1              
2             #
3             # GENERATED WITH PDL::PP! Don't modify!
4             #
5             package PDL::FFT;
6              
7             @EXPORT_OK = qw( PDL::PP _fft PDL::PP _ifft fft ifft fftnd ifftnd fftconvolve realfft realifft kernctr PDL::PP convmath PDL::PP cmul PDL::PP cdiv );
8             %EXPORT_TAGS = (Func=>[@EXPORT_OK]);
9              
10 3     3   3216 use PDL::Core;
  3         9  
  3         34  
11 3     3   26 use PDL::Exporter;
  3         8  
  3         25  
12 3     3   20 use DynaLoader;
  3         6  
  3         345  
13              
14              
15              
16            
17             @ISA = ( 'PDL::Exporter','DynaLoader' );
18             push @PDL::Core::PP, __PACKAGE__;
19             bootstrap PDL::FFT ;
20              
21              
22              
23              
24             =head1 NAME
25              
26             PDL::FFT - FFTs for PDL
27              
28             =head1 DESCRIPTION
29              
30             !!!!!!!!!!!!!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
31             As of PDL-2.006_04, the direction of the FFT/IFFT has been
32             reversed to match the usage in the FFTW library and the convention
33             in use generally.
34             !!!!!!!!!!!!!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
35              
36             FFTs for PDL. These work for arrays of any dimension, although ones
37             with small prime factors are likely to be the quickest. The forward
38             FFT is unnormalized while the inverse FFT is normalized so that the
39             IFFT of the FFT returns the original values.
40              
41             For historical reasons, these routines work in-place and do not recognize
42             the in-place flag. That should be fixed.
43              
44             =head1 SYNOPSIS
45              
46             use PDL::FFT qw/:Func/;
47              
48             fft($real, $imag);
49             ifft($real, $imag);
50             realfft($real);
51             realifft($real);
52              
53             fftnd($real,$imag);
54             ifftnd($real,$imag);
55              
56             $kernel = kernctr($image,$smallk);
57             fftconvolve($image,$kernel);
58              
59             =head1 DATA TYPES
60              
61             The underlying C library upon which this module is based performs FFTs
62             on both single precision and double precision floating point piddles.
63             Performing FFTs on integer data types is not reliable. Consider the
64             following FFT on piddles of type 'double':
65              
66             $r = pdl(0,1,0,1);
67             $i = zeroes($r);
68             fft($r,$i);
69             print $r,$i;
70             [2 0 -2 0] [0 0 0 0]
71              
72             But if $r and $i are unsigned short integers (ushorts):
73              
74             $r = pdl(ushort,0,1,0,1);
75             $i = zeroes($r);
76             fft($r,$i);
77             print $r,$i;
78             [2 0 65534 0] [0 0 0 0]
79              
80             This used to occur because L converts the ushort
81             piddles to floats or doubles, performs the FFT on them, and then
82             converts them back to ushort, causing the overflow where the amplitude
83             of the frequency should be -2.
84              
85             Therefore, if you pass in a piddle of integer datatype (byte, short,
86             ushort, long) to any of the routines in PDL::FFT, your data will be
87             promoted to a double-precision piddle. If you pass in a float, the
88             single-precision FFT will be performed.
89              
90             =head1 FREQUENCIES
91              
92             For even-sized input arrays, the frequencies are packed like normal
93             for FFTs (where N is the size of the array and D is the physical step
94             size between elements):
95              
96             0, 1/ND, 2/ND, ..., (N/2-1)/ND, 1/2D, -(N/2-1)/ND, ..., -1/ND.
97              
98             which can easily be obtained (taking the Nyquist frequency to be
99             positive) using
100              
101             C<< $kx = $real->xlinvals(-($N/2-1)/$N/$D,1/2/$D)->rotate(-($N/2 -1)); >>
102              
103             For odd-sized input arrays the Nyquist frequency is not directly
104             acessible, and the frequencies are
105              
106             0, 1/ND, 2/ND, ..., (N/2-0.5)/ND, -(N/2-0.5)/ND, ..., -1/ND.
107              
108             which can easily be obtained using
109              
110             C<< $kx = $real->xlinvals(-($N/2-0.5)/$N/$D,($N/2-0.5)/$N/$D)->rotate(-($N-1)/2); >>
111              
112              
113             =head1 ALTERNATIVE FFT PACKAGES
114              
115             Various other modules - such as
116             L and L -
117             contain FFT routines.
118             However, unlike PDL::FFT, these modules are optional,
119             and so may not be installed.
120              
121             =cut
122              
123              
124              
125              
126              
127              
128              
129             =head1 FUNCTIONS
130              
131              
132              
133             =cut
134              
135              
136              
137              
138              
139              
140             *_fft = \&PDL::_fft;
141              
142              
143              
144              
145              
146             *_ifft = \&PDL::_ifft;
147              
148              
149              
150              
151 3     3   24 use Carp;
  3         14  
  3         233  
152 3     3   18 use PDL::Core qw/:Func/;
  3         8  
  3         12  
153 3     3   25 use PDL::Basic qw/:Func/;
  3         6  
  3         31  
154 3     3   22 use PDL::Types;
  3         7  
  3         431  
155 3     3   1297 use PDL::ImageND qw/kernctr/; # moved to ImageND since FFTW uses it too
  3         7  
  3         29  
156 3     3   21 use PDL::Ops qw/ci cimag creal/;
  3         8  
  3         26  
157              
158             END {
159             # tidying up required after using fftn
160 3 50   3   2739 print "Freeing FFT space\n" if $PDL::verbose;
161 3         58 fft_free();
162             }
163              
164             sub todecimal {
165 156     156 0 308 my ($arg) = @_;
166 156 100 100     1075 $arg = $arg->double if (($arg->get_datatype != $PDL_F) &&
167             ($arg->get_datatype != $PDL_D));
168 154         276 $_[0] = $arg;
169 154         279 1;}
170              
171             =head2 fft()
172              
173             =for ref
174              
175             Complex 1-D FFT of the "real" and "imag" arrays [inplace]. A single
176             cfloat/cdouble input piddle can also be used.
177              
178             =for sig
179              
180             Signature: ([o,nc]real(n); [o,nc]imag(n))
181              
182             =for usage
183              
184             fft($real,$imag);
185              
186             =cut
187              
188             *fft = \&PDL::fft;
189              
190             sub PDL::fft {
191             # Convert the first argument to decimal and check for trouble.
192 35     35 0 16730 my $re=$_[0];
193 35         73 my $im=$_[1];
194 35 100       170 if ($re->type =~ m/cdouble|cfloat/) {
195 3         2193 $im=cimag($re);
196 3         2047 $re=creal($re);
197             }
198 35         128 eval { todecimal($re); };
  35         108  
199 35 50       93 if ($@) {
200 0         0 $@ =~ s/ at .*//s;
201 0         0 barf("Error in FFT with first argument: $@");
202             }
203             # Convert the second argument to decimal and check for trouble.
204 35         58 eval { todecimal($im); };
  35         70  
205 35 100       109 if ($@) {
206 1         7 $@ =~ s/ at .*//s;
207 1         5 my $message = "Error in FFT with second argument: $@";
208 1 50       9 $message .= '. Did you forget to supply the second (imaginary) piddle?'
209             if ($message =~ /undefined value/);
210 1         5 barf($message);
211             }
212 34         307142 _fft($re,$im);
213 34 100       526 if ($_[0]->type =~ m/cdouble|cfloat/) {
214 3         16473 $_[0]= $re+ci()*$im;
215             } else {
216 31         357 $_[0]=$re,$_[1]=$im;
217             }
218             }
219              
220              
221             =head2 ifft()
222              
223             =for ref
224              
225             Complex inverse 1-D FFT of the "real" and "imag" arrays [inplace]. A single
226             cfloat/cdouble input piddle can also be used.
227              
228             =for sig
229              
230             Signature: ([o,nc]real(n); [o,nc]imag(n))
231              
232             =for usage
233              
234             ifft($real,$imag);
235              
236             =cut
237              
238             *ifft = \&PDL::ifft;
239              
240             sub PDL::ifft {
241             # Convert the first argument to decimal and check for trouble.
242 22     22 0 80 my $re=$_[0];
243 22         48 my $im=$_[1];
244 22 100       95 if ($re->type =~ m/cdouble|cfloat/) {
245 3         830 $im=cimag($re);
246 3         715 $re=creal($re);
247             }
248 22         68 eval { todecimal($re); };
  22         70  
249 22 50       68 if ($@) {
250 0         0 $@ =~ s/ at .*//s;
251 0         0 barf("Error in FFT with first argument: $@");
252             }
253             # Convert the second argument to decimal and check for trouble.
254 22         35 eval { todecimal($im); };
  22         47  
255 22 100       61 if ($@) {
256 1         7 $@ =~ s/ at .*//s;
257 1         5 my $message = "Error in FFT with second argument: $@";
258 1 50       6 $message .= '. Did you forget to supply the second (imaginary) piddle?'
259             if ($message =~ /undefined value/);
260 1         4 barf($message);
261             }
262 21         216210 _ifft($re,$im);
263 21 100       308 if ($_[0]->type =~ m/cdouble|cfloat/) {
264 3         10913 $_[0]= $re+ci()*$im;
265             } else {
266 18         63 $_[0]=$re,$_[1]=$im;
267             }
268             }
269              
270             =head2 realfft()
271              
272             =for ref
273              
274             One-dimensional FFT of real function [inplace].
275              
276             The real part of the transform ends up in the first half of the array
277             and the imaginary part of the transform ends up in the second half of
278             the array.
279              
280             =for usage
281              
282             realfft($real);
283              
284             =cut
285              
286             *realfft = \&PDL::realfft;
287              
288             sub PDL::realfft {
289 1 50   1 0 15 barf("Usage: realfft(real(*)") if $#_ != 0;
290 1         5 my ($x) = @_;
291 1         6 todecimal($x);
292             # FIX: could eliminate $y
293 1         702 my ($y) = 0*$x;
294 1         18 fft($x,$y);
295 1         7 my ($n) = int((($x->dims)[0]-1)/2); my($t);
  1         3  
296 1         10 ($t=$x->slice("-$n:-1")) .= $y->slice("1:$n");
297 1         15 undef;
298             }
299              
300             =head2 realifft()
301              
302             =for ref
303              
304             Inverse of one-dimensional realfft routine [inplace].
305              
306             =for usage
307              
308             realifft($real);
309              
310             =cut
311              
312             *realifft = \&PDL::realifft;
313              
314             sub PDL::realifft {
315 3     3   26 use PDL::Ufunc 'max';
  3         7  
  3         57  
316 1 50   1 0 11 barf("Usage: realifft(xfm(*)") if $#_ != 0;
317 1         5 my ($x) = @_;
318 1         4 todecimal($x);
319 1         6 my ($n) = int((($x->dims)[0]-1)/2); my($t);
  1         3  
320             # FIX: could eliminate $y
321 1         665 my ($y) = 0*$x;
322 1         16 ($t=$y->slice("1:$n")) .= $x->slice("-$n:-1");
323 1         15 ($t=$x->slice("-$n:-1")) .= $x->slice("$n:1");
324 1         12 ($t=$y->slice("-$n:-1")) .= -$y->slice("$n:1");
325 1         18 ifft($x,$y);
326             # Sanity check -- shouldn't happen
327 1 50       5 carp "Bad inverse transform in realifft" if max(abs($y)) > 1e-6*max(abs($x));
328 1         11 undef;
329             }
330              
331             =head2 fftnd()
332              
333             =for ref
334              
335             N-dimensional FFT over all pdl dims of input (inplace)
336              
337             =for example
338              
339             fftnd($real,$imag);
340              
341             =cut
342              
343             *fftnd = \&PDL::fftnd;
344              
345             sub PDL::fftnd {
346 13 50   13 0 86 barf "Must have real and imaginary parts for fftnd" if $#_ != 1;
347 13         42 my ($r,$i) = @_;
348 13 50       81 if ($r->type =~m/cdouble|cfloat/ ) {
349 0         0 $i=cimag $r;
350 0         0 $r=creal $r;
351             }
352 13         75 my ($n) = $r->getndims;
353 13 50       61 barf "Dimensions of real and imag must be the same for fft"
354             if ($n != $i->getndims);
355 13         24 $n--;
356 13         52 todecimal($r);
357 13         42 todecimal($i);
358             # need the copy in case $r and $i point to same memory
359 13         54 $i = $i->copy;
360 13         88 foreach (0..$n) {
361 27         162 fft($r,$i);
362 27         482 $r = $r->mv(0,$n);
363 27         240 $i = $i->mv(0,$n);
364             }
365 13 50       58 if ($_[0]->type =~m/cdouble|cfloat/ ) {
366 0         0 $_[0]=$r+ci()*$i;
367             } else {
368 13         10324 $_[0] = $r; $_[1] = $i;
  13         90  
369             }
370 13         53 undef;
371             }
372              
373             =head2 ifftnd()
374              
375             =for ref
376              
377             N-dimensional inverse FFT over all pdl dims of input (inplace)
378              
379             =for example
380              
381             ifftnd($real,$imag);
382              
383             =cut
384              
385             *ifftnd = \&PDL::ifftnd;
386              
387             sub PDL::ifftnd {
388 7 50   7 0 42 barf "Must have real and imaginary parts for ifftnd" if $#_ != 1;
389 7         21 my ($r,$i) = @_;
390 7 50       37 if ($r->type =~m/cdouble|cfloat/ ) {
391 0         0 $r=creal $r;
392 0         0 $i=cimag $r;
393             }
394 7         45 my ($n) = $r->getndims;
395 7 50       44 barf "Dimensions of real and imag must be the same for ifft"
396             if ($n != $i->getndims);
397 7         30 todecimal($r);
398 7         14 todecimal($i);
399             # need the copy in case $r and $i point to same memory
400 7         36 $i = $i->copy;
401 7         25 $n--;
402 7         43 foreach (0..$n) {
403 14         60 ifft($r,$i);
404 14         235 $r = $r->mv(0,$n);
405 14         128 $i = $i->mv(0,$n);
406             }
407 7 50       33 if ($_[0]->type =~m/cdouble|cfloat/ ) {
408 0         0 $_[0]=$r+ci()*$i;
409             } else {
410 7         9661 $_[0] = $r; $_[1] = $i;
  7         80  
411             }
412 7         33 undef;
413             }
414              
415              
416              
417              
418             =head2 fftconvolve()
419              
420             =for ref
421              
422             N-dimensional convolution with periodic boundaries (FFT method)
423              
424             =for usage
425              
426             $kernel = kernctr($image,$smallk);
427             fftconvolve($image,$kernel);
428              
429             fftconvolve works inplace, and returns an error array in kernel as an
430             accuracy check -- all the values in it should be negligible.
431              
432             See also L, which
433             performs speed-optimized convolution with a variety of boundary conditions.
434              
435             The sizes of the image and the kernel must be the same.
436             L centres a small kernel to emulate the
437             behaviour of the direct convolution routines.
438              
439             The speed cross-over between using straight convolution
440             (L) and
441             these fft routines is for kernel sizes roughly 7x7.
442              
443             =cut
444              
445             *fftconvolve = \&PDL::fftconvolve;
446              
447             sub PDL::fftconvolve {
448 2 50   2 0 19 barf "Must have image & kernel for fftconvolve" if $#_ != 1;
449 2         12 my ($im, $k) = @_;
450              
451 2         7 my ($ar,$ai,$kr,$ki,$cr,$ci);
452              
453 2         15 $imr = $im->copy;
454 2         20 $imi = $imr->zeros;
455 2         14 fftnd($imr, $imi);
456              
457 2         19 $kr = $k->copy;
458 2         28 $ki = $kr->zeroes;
459 2         19 fftnd($kr,$ki);
460              
461 2         31 $cr = $imr->zeroes;
462 2         16 $ci = $imi->zeroes;
463 2         10274 cmul($imr,$imi,$kr,$ki,$cr,$ci);
464              
465 2         35 ifftnd($cr,$ci);
466 2         12 $_[0] = $cr;
467 2         8 $_[1] = $ci;
468              
469 2         40 ($cr,$ci);
470             }
471              
472             sub PDL::fftconvolve_inplace {
473 0 0   0 0   barf "Must have image & kernel for fftconvolve" if $#_ != 1;
474 0           my ($hr, $hi) = @_;
475 0 0         if ($hr->type =~m/cdouble|cfloat/) {
476 0           $hi=cimag($hr);
477 0           $hr=creal($hr);
478             }
479 0           my ($n) = $hr->getndims;
480 0           todecimal($hr); # Convert to double unless already float or double
481 0           todecimal($hi); # Convert to double unless already float or double
482             # need the copy in case $r and $i point to same memory
483 0           $hi = $hi->copy;
484 0           $hr = $hr->copy;
485 0           fftnd($hr,$hi);
486 0           convmath($hr->clump(-1),$hi->clump(-1));
487 0           my ($str1, $str2, $tmp, $i);
488 0           chop($str1 = '-1:1,' x $n);
489 0           chop($str2 = '1:-1,' x $n);
490              
491             # FIX: do these inplace -- cuts the arithmetic by a factor 2 as well.
492              
493 0           ($tmp = $hr->slice($str2)) += $hr->slice($str1)->copy;
494 0           ($tmp = $hi->slice($str2)) -= $hi->slice($str1)->copy;
495 0           for ($i = 0; $i<$n; $i++) {
496 0           chop ($str1 = ('(0),' x $i).'-1:1,'.('(0),'x($n-$i-1)));
497 0           chop ($str2 = ('(0),' x $i).'1:-1,'.('(0),'x($n-$i-1)));
498 0           ($tmp = $hr->slice($str2)) += $hr->slice($str1)->copy;
499 0           ($tmp = $hi->slice($str2)) -= $hi->slice($str1)->copy;
500             }
501 0           $hr->clump(-1)->set(0,$hr->clump(-1)->at(0)*2);
502 0           $hi->clump(-1)->set(0,0.);
503 0           ifftnd($hr,$hi);
504             # convert back to complex if input was complex
505 0 0         if ($_[0]->type =~m/cdouble|cfloat/) {
506 0           $_[0]=$hr+ci()*$hi;
507 0           return $_[0];
508             } else {
509 0           $_[0] = $hr; $_[1] = $hi;
  0            
510 0           return ($hr,$hi);
511             }
512             }
513              
514              
515              
516              
517              
518             =head2 convmath
519              
520             =for sig
521              
522             Signature: ([o,nc]a(m); [o,nc]b(m))
523              
524             =for ref
525              
526             Internal routine doing maths for convolution
527              
528             =for bad
529              
530             convmath does not process bad values.
531             It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles.
532              
533              
534             =cut
535              
536              
537              
538              
539              
540              
541             *convmath = \&PDL::convmath;
542              
543              
544              
545              
546              
547             =head2 cmul
548              
549             =for sig
550              
551             Signature: (ar(); ai(); br(); bi(); [o]cr(); [o]ci())
552              
553             =for ref
554              
555             Complex multiplication
556              
557             =for bad
558              
559             cmul does not process bad values.
560             It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles.
561              
562              
563             =cut
564              
565              
566              
567              
568              
569              
570             *cmul = \&PDL::cmul;
571              
572              
573              
574              
575              
576             =head2 cdiv
577              
578             =for sig
579              
580             Signature: (ar(); ai(); br(); bi(); [o]cr(); [o]ci())
581              
582             =for ref
583              
584             Complex division
585              
586             =for bad
587              
588             cdiv does not process bad values.
589             It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles.
590              
591              
592             =cut
593              
594              
595              
596              
597              
598              
599             *cdiv = \&PDL::cdiv;
600              
601              
602              
603              
604             1; # OK
605              
606              
607              
608              
609             =head1 BUGS
610              
611             Where the source is marked `FIX', could re-implement using phase-shift
612             factors on the transforms and some real-space bookkeeping, to save
613             some temporary space and redundant transforms.
614              
615             =head1 AUTHOR
616              
617             This file copyright (C) 1997, 1998 R.J.R. Williams
618             (rjrw@ast.leeds.ac.uk), Karl Glazebrook (kgb@aaoepp.aao.gov.au),
619             Tuomas J. Lukka, (lukka@husc.harvard.edu). All rights reserved. There
620             is no warranty. You are allowed to redistribute this software /
621             documentation under certain conditions. For details, see the file
622             COPYING in the PDL distribution. If this file is separated from the
623             PDL distribution, the copyright notice should be included in the file.
624              
625              
626             =cut
627              
628              
629              
630             ;
631              
632              
633              
634             # Exit with OK status
635              
636             1;
637              
638