File Coverage

lib/PDL/FFT.pd
Criterion Covered Total %
statement 95 95 100.0
branch 22 34 64.7
condition 4 6 66.6
subroutine 13 13 100.0
pod 0 6 0.0
total 134 154 87.0


line stmt bran cond sub pod time code
1             use strict;
2             use warnings;
3             use PDL::Types qw(types);
4             my $F = [map $_->ppsym, grep $_->real && !$_->integer, types()];
5              
6             { no warnings 'once'; # pass info back to Makefile.PL
7             $PDL::Core::Dev::EXTRAS{$::PDLMOD}{OBJECT} .= join '', map " $::PDLBASE/$_\$(OBJ_EXT)", qw(fftn);
8             $PDL::Core::Dev::EXTRAS{$::PDLMOD}{DEFINE} .= qq{ -DFFT_FLOAT -DFFT_DOUBLE -DFFT_LDOUBLE};
9             $PDL::Core::Dev::EXTRAS{$::PDLMOD}{INC} .= qq{ "-I$::PDLBASE"};
10             }
11              
12             pp_addpm({At=>'Top'},<<'EOD');
13             =head1 NAME
14              
15             PDL::FFT - FFTs for PDL
16              
17             =head1 DESCRIPTION
18              
19             !!!!!!!!!!!!!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
20             As of PDL-2.006_04, the direction of the FFT/IFFT has been
21             reversed to match the usage in the FFTW library and the convention
22             in use generally.
23             !!!!!!!!!!!!!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
24              
25             FFTs for PDL. These work for arrays of any dimension, although ones
26             with small prime factors are likely to be the quickest. The forward
27             FFT is unnormalized while the inverse FFT is normalized so that the
28             IFFT of the FFT returns the original values.
29              
30             For historical reasons, these routines work in-place and do not recognize
31             the in-place flag. That should be fixed.
32              
33             =head1 SYNOPSIS
34              
35             use PDL::FFT qw/:Func/;
36              
37             fft($real, $imag);
38             ifft($real, $imag);
39             realfft($real);
40             realifft($real);
41              
42             fftnd($real,$imag);
43             ifftnd($real,$imag);
44              
45             $kernel = kernctr($image,$smallk);
46             fftconvolve($image,$kernel);
47              
48             =head1 DATA TYPES
49              
50             The underlying C library upon which this module is based performs FFTs
51             on both single precision and double precision floating point ndarrays.
52             The PP functions are defined to only take those data types.
53             Therefore, if you pass in an ndarray of integer datatype (byte, short,
54             ushort, long) to any of the routines in PDL::FFT, your data will be
55             promoted to a double-precision ndarray. If you pass in a float, the
56             single-precision FFT will be performed.
57              
58             =head1 FREQUENCIES
59              
60             For even-sized input arrays, the frequencies are packed like normal
61             for FFTs (where N is the size of the array and D is the physical step
62             size between elements):
63              
64             0, 1/ND, 2/ND, ..., (N/2-1)/ND, 1/2D, -(N/2-1)/ND, ..., -1/ND.
65              
66             which can easily be obtained (taking the Nyquist frequency to be
67             positive) using
68              
69             C<< $kx = $real->xlinvals(-($N/2-1)/$N/$D,1/2/$D)->rotate(-($N/2 -1)); >>
70              
71             For odd-sized input arrays the Nyquist frequency is not directly
72             acessible, and the frequencies are
73              
74             0, 1/ND, 2/ND, ..., (N/2-0.5)/ND, -(N/2-0.5)/ND, ..., -1/ND.
75              
76             which can easily be obtained using
77              
78             C<< $kx = $real->xlinvals(-($N/2-0.5)/$N/$D,($N/2-0.5)/$N/$D)->rotate(-($N-1)/2); >>
79              
80              
81             =head1 ALTERNATIVE FFT PACKAGES
82              
83             Various other modules - such as L and L -
84             contain FFT routines.
85             However, unlike PDL::FFT, these modules are optional,
86             and so may not be installed.
87              
88             =cut
89              
90             EOD
91              
92             pp_def('fft',
93             Pars => '[io]real(n); [io]imag(n);',
94             GenericTypes => $F,
95             CHeader => qq{#include "fftn.h"\n},
96             Code => 'fftn$TFDE(f,,l)($SIZE(n), NULL, $P(real), $P(imag), -1, 1.);',
97             Doc=><<'EOF',
98             =for ref
99              
100             Complex 1-D FFT of the "real" and "imag" arrays [inplace]. A single
101             cfloat/cdouble input ndarray can also be used.
102             EOF
103             PMCode=><<'EOF',
104             sub PDL::fft {
105             # Convert the first argument to decimal and check for trouble.
106             my ($re, $im) = @_;
107             if (!$re->type->real) {
108             $im=$re->im;
109             $re=$re->re;
110             }
111             eval { todecimal($re); };
112             if ($@) {
113             $@ =~ s/ at .*//s;
114             barf("Error in FFT with first argument: $@");
115             }
116             # Convert the second argument to decimal and check for trouble.
117             eval { todecimal($im); };
118             if ($@) {
119             $@ =~ s/ at .*//s;
120             my $message = "Error in FFT with second argument: $@";
121             $message .= '. Did you forget to supply the second (imaginary) ndarray?'
122             if ($message =~ /undefined value/);
123             barf($message);
124             }
125             PDL::_fft_int($re,$im);
126             if (!$_[0]->type->real) {
127             $_[0]= czip($re, $im);
128             } else {
129             $_[0]=$re,$_[1]=$im;
130             }
131             }
132             EOF
133             );
134              
135             pp_def('ifft',
136             Pars => '[io]real(n); [io]imag(n);',
137             GenericTypes => $F,
138             CHeader => qq{#include "fftn.h"\n},
139             Code => 'fftn$TFDE(f,,l)($SIZE(n), NULL, $P(real), $P(imag), 1, -1.);',
140             Doc=><<'EOF',
141             =for ref
142              
143             Complex inverse 1-D FFT of the "real" and "imag" arrays [inplace]. A single
144             cfloat/cdouble input ndarray can also be used.
145             EOF
146             PMCode=><<'EOF',
147             sub PDL::ifft {
148             # Convert the first argument to decimal and check for trouble.
149             my ($re, $im) = @_;
150             if (!$re->type->real) {
151             $im=$re->im;
152             $re=$re->re;
153             }
154             eval { todecimal($re); };
155             if ($@) {
156             $@ =~ s/ at .*//s;
157             barf("Error in FFT with first argument: $@");
158             }
159             # Convert the second argument to decimal and check for trouble.
160             eval { todecimal($im); };
161             if ($@) {
162             $@ =~ s/ at .*//s;
163             my $message = "Error in FFT with second argument: $@";
164             $message .= '. Did you forget to supply the second (imaginary) ndarray?'
165             if ($message =~ /undefined value/);
166             barf($message);
167             }
168             PDL::_ifft_int($re,$im);
169             if (!$_[0]->type->real) {
170             $_[0]= czip($re, $im);
171             } else {
172             $_[0]=$re,$_[1]=$im;
173             }
174             }
175             EOF
176             );
177              
178             pp_add_exported('',"fftnd ifftnd fftconvolve realfft realifft kernctr");
179              
180             pp_addpm(<<'EOD');
181 5     5   44 use Carp;
  5         9  
  5         585  
182 5     5   50 use PDL::Core qw/:Func/;
  5         10  
  5         34  
183 5     5   43 use PDL::Basic qw/:Func/;
  5         13  
  5         446  
184 5     5   35 use PDL::Types;
  5         13  
  5         868  
185 5     5   2712 use PDL::ImageND qw/kernctr/; # moved to ImageND since FFTW uses it too
  5         17  
  5         38  
186 5     5   36 use PDL::Ops qw/czip/;
  5         7  
  5         40  
187              
188             sub todecimal {
189 182     182 0 330 my ($arg) = @_;
190 182 100       403 $arg = $arg->double if $arg->type->integer;
191 180         290 $_[0] = $arg;
192 180         323 1;}
193              
194             =head2 realfft()
195              
196             =for ref
197              
198             One-dimensional FFT of real function [inplace].
199              
200             The real part of the transform ends up in the first half of the array
201             and the imaginary part of the transform ends up in the second half of
202             the array.
203              
204             =for usage
205              
206             realfft($real);
207              
208             =cut
209              
210             *realfft = \&PDL::realfft;
211              
212             sub PDL::realfft {
213 1 50   1 0 10 barf("Usage: realfft(real(*)") if $#_ != 0;
214 1         2 my ($x) = @_;
215 1         4 todecimal($x);
216             # FIX: could eliminate $y
217 1         7 my ($y) = 0*$x;
218 1         7 fft($x,$y);
219 1         9 my ($n) = int((($x->dims)[0]-1)/2); my($t);
  1         3  
220 1         9 ($t=$x->slice("-$n:-1")) .= $y->slice("1:$n");
221 1         245 undef;
222             }
223              
224             =head2 realifft()
225              
226             =for ref
227              
228             Inverse of one-dimensional realfft routine [inplace].
229              
230             =for usage
231              
232             realifft($real);
233              
234             =cut
235              
236             *realifft = \&PDL::realifft;
237              
238             sub PDL::realifft {
239 5     5   35 use PDL::Ufunc 'max';
  5         38  
  5         41  
240 1 50   1 0 13 barf("Usage: realifft(xfm(*)") if $#_ != 0;
241 1         3 my ($x) = @_;
242 1         5 todecimal($x);
243 1         8 my ($n) = int((($x->dims)[0]-1)/2); my($t);
  1         2  
244             # FIX: could eliminate $y
245 1         6 my ($y) = 0*$x;
246 1         12 ($t=$y->slice("1:$n")) .= $x->slice("-$n:-1");
247 1         11 ($t=$x->slice("-$n:-1")) .= $x->slice("$n:1");
248 1         7 ($t=$y->slice("-$n:-1")) .= -$y->slice("$n:1");
249 1         14 ifft($x,$y);
250             # Sanity check -- shouldn't happen
251 1 50       7 carp "Bad inverse transform in realifft" if max(abs($y)) > 1e-6*max(abs($x));
252 1         436 undef;
253             }
254              
255             =head2 fftnd()
256              
257             =for ref
258              
259             N-dimensional FFT over all pdl dims of input (inplace)
260              
261             =for example
262              
263             fftnd($real,$imag);
264              
265             =cut
266              
267             *fftnd = \&PDL::fftnd;
268              
269             sub PDL::fftnd {
270 16     16 0 3448 my ($r,$i) = @_;
271 16 50 66     84 barf "Must have real and imaginary parts or complex for fftnd"
272             if $r->type->real and @_ != 2;
273 16 100       51 if (!$r->type->real) {
274 5         111 $i=$r->im;
275 5         65 $r=$r->re;
276             }
277 16         107 my ($n) = $r->getndims;
278 16 50       99 barf "Dimensions of real and imag must be the same for fft"
279             if ($n != $i->getndims);
280 16         27 $n--;
281 16         60 todecimal($r);
282 16         42 todecimal($i);
283             # need the copy in case $r and $i point to same memory
284 16         63 $i = $i->copy;
285 16         86 foreach (0..$n) {
286 34         161 fft($r,$i);
287 34 50       745 $r = $r->mv(0,$n) if 0 != $n;
288 34 50       330 $i = $i->mv(0,$n) if 0 != $n;
289             }
290 16 100       81 if (!$_[0]->type->real) {
291 5         104901 $_[0]= czip($r, $i);
292             } else {
293 11         208 $_[0] = $r; $_[1] = $i;
  11         38  
294             }
295 16         1037 undef;
296             }
297              
298             =head2 ifftnd()
299              
300             =for ref
301              
302             N-dimensional inverse FFT over all pdl dims of input (inplace)
303              
304             =for example
305              
306             ifftnd($real,$imag);
307              
308             =cut
309              
310             *ifftnd = \&PDL::ifftnd;
311              
312             sub PDL::ifftnd {
313 8     8 0 34 my ($r,$i) = @_;
314 8 50 66     49 barf "Must have real and imaginary parts or complex for ifftnd"
315             if $r->type->real and @_ != 2;
316 8 100       25 if (!$r->type->real) {
317 3         66 $i=$r->im;
318 3         34 $r=$r->re;
319             }
320 8         73 my ($n) = $r->getndims;
321 8 50       49 barf "Dimensions of real and imag must be the same for ifft"
322             if ($n != $i->getndims);
323 8         68 todecimal($r);
324 8         23 todecimal($i);
325             # need the copy in case $r and $i point to same memory
326 8         34 $i = $i->copy;
327 8         30 $n--;
328 8         47 foreach (0..$n) {
329 16         77 ifft($r,$i);
330 16 50       347 $r = $r->mv(0,$n) if 0 != $n;
331 16 50       172 $i = $i->mv(0,$n) if 0 != $n;
332             }
333 8 100       34 if (!$_[0]->type->real) {
334 3         66683 $_[0]= czip($r, $i);
335             } else {
336 5         66 $_[0] = $r; $_[1] = $i;
  5         35  
337             }
338 8         72 undef;
339             }
340              
341             =head2 fftconvolve()
342              
343             =for ref
344              
345             N-dimensional convolution with periodic boundaries (FFT method)
346              
347             =for usage
348              
349             $kernel = kernctr($image,$smallk);
350             fftconvolve($image,$kernel);
351              
352             fftconvolve works inplace, and returns an error array in kernel as an
353             accuracy check -- all the values in it should be negligible.
354              
355             See also L, which
356             performs speed-optimized convolution with a variety of boundary conditions.
357              
358             The sizes of the image and the kernel must be the same.
359             L centres a small kernel to emulate the
360             behaviour of the direct convolution routines.
361              
362             The speed cross-over between using straight convolution
363             (L) and
364             these fft routines is for kernel sizes roughly 7x7.
365              
366             =cut
367              
368             *fftconvolve = \&PDL::fftconvolve;
369              
370             sub PDL::fftconvolve {
371 2 50   2 0 12 barf "Must have image & kernel for fftconvolve" if $#_ != 1;
372 2         64 my ($im, $k) = map $_->r2C, @_;
373 2         33 fftnd($im);
374 2         15 fftnd($k);
375 2         23 my $c = $im * $k;
376 2         21 ifftnd($c);
377 2         2165 $_[0] = $c->re->sever;
378 2         2878 $_[1] = $c->im->sever;
379 2         3081 @_;
380             }
381             EOD
382              
383             pp_addpm({At=>'Bot'},<<'EOD');
384             =head1 BUGS
385              
386             Where the source is marked `FIX', could re-implement using phase-shift
387             factors on the transforms and some real-space bookkeeping, to save
388             some temporary space and redundant transforms.
389              
390             =head1 AUTHOR
391              
392             This file copyright (C) 1997, 1998 R.J.R. Williams
393             (rjrw@ast.leeds.ac.uk), Karl Glazebrook (kgb@aaoepp.aao.gov.au),
394             Tuomas J. Lukka, (lukka@husc.harvard.edu). All rights reserved. There
395             is no warranty. You are allowed to redistribute this software /
396             documentation under certain conditions. For details, see the file
397             COPYING in the PDL distribution. If this file is separated from the
398             PDL distribution, the copyright notice should be included in the file.
399              
400             =cut
401             EOD
402              
403             pp_done();