PDL-FFTW3-0.203/0000755000175000017500000000000014742523116012717 5ustar osboxesosboxesPDL-FFTW3-0.203/META.json0000644000175000017500000000304414742523116014341 0ustar osboxesosboxes{ "abstract" : "PDL interface to the Fastest Fourier Transform in the West", "author" : [ "Dima Kogan , Craig DeForest " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.44, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "PDL-FFTW3", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "PDL::PP" : "0" } }, "configure" : { "requires" : { "IPC::Run" : "0", "PDL" : "2.097" } }, "develop" : { "requires" : { "CPAN::Changes" : "0" } }, "runtime" : { "requires" : { "PDL" : "2.097", "perl" : "5.016" } }, "test" : { "requires" : { "Test::More" : "0.98" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/PDLPorters/pdl-fftw3/issues" }, "repository" : { "type" : "git", "url" : "git://github.com/PDLPorters/pdl-fftw3.git", "web" : "https://github.com/PDLPorters/pdl-fftw3" }, "x_IRC" : "irc://irc.perl.org/#pdl" }, "version" : "0.203", "x_serialization_backend" : "JSON::PP version 4.04" } PDL-FFTW3-0.203/README.pod0000644000175000017500000002631414742515074014372 0ustar osboxesosboxes=head1 NAME PDL::FFTW3 - PDL interface to the Fastest Fourier Transform in the West v3 =head1 SYNOPSIS use PDL; use PDL::FFTW3; use PDL::Graphics::Gnuplot; # Basic functionality my $x = sin( sequence(100) * 2.0 ) + 2.0 * cos( sequence(100) / 3.0 ); my $F = rfft1( $x ); gplot( with => 'lines', $F->abs ); =====> 8000 ++------------+-------------+------------+-------------+------------++ + + + + + + | | | * | 7000 ++ * ++ | * | | * | | * | | * | 6000 ++ * ++ | * | | * | | * | 5000 ++ * ++ | * | | * | | ** | 4000 ++ ** ++ | ** | | * * | | * * | | * * | 3000 ++ * * ++ | * * | | * * | | * * * | 2000 ++ * * * ++ | * * * | | * * ** | | * * ** | | * * ** | 1000 ++ * * * * ++ | * * * * | | ** * * * | + * * + + + * * + + 0 ****-------*********************************--************************ 0 10 20 30 40 50 # Correlation of two real signals # two signals offset by 30 units my $x = sequence(100); my $y1 = exp( 0.2*($x - 20.5) ** (-2.0) ); my $y2 = exp( 0.2*($x - 50.5) ** (-2.0) ); # compute the correlation my $F12 = rfft1( cat($y1,$y2) ); my $corr = irfft1( $F12(:,(1)) * $F12(:,(0))->conj ); # and find the peak say maximum_ind($corr); =====> 30 =head1 DESCRIPTION This is a PDL binding to version 3 of the FFTW library. Supported are complex <-> complex and real <-> complex FFTs. =head2 NB to install wget http://www.fftw.org/fftw-3.3.4.tar.gz tar xvf fftw-3.3.4.tar.gz cd fftw-3.3.4/ ./configure --prefix=/usr --enable-threads --enable-float --enable-shared --with-pic make all install install-pkgconfigDATA make clean ./configure --prefix=/usr --enable-threads --enable-shared --with-pic make all install install-pkgconfigDATA This will give you both fftw3f (first chunk) and fftw3 (second). =head2 Supported operations This module computes the Discrete Fourier Transform. In its most basic form, this transform converts a vector of complex numbers in the time domain into another vector of complex numbers in the frequency domain. These complex <-> complex transforms are supported with C functions for a rank-C transform. The opposite effect (transform data in the frequency domain back to the time domain) can be achieved with the C functions. A common use case is to transform purely-real data. This data has 0 for its complex component, and FFTW can take advantage of this to compute the FFT faster and using less memory. Since a Fourier Transform of a real signal has an even real part and an odd imaginary part, only 1/2 of the spectrum is needed. These forward real -> complex transforms are supported with the C functions. The backward version of this transform is complex -> real and is supported with the C functions. =head2 Basic usage details Arbitrary C-dimensional transforms are supported. All functions exported by this module have the C in their name, so for instance a complex <-> complex 3D forward transform is computed with the C function. The rank I be specified in this way; there is no function called simply C. In-place operation is supported for complex <-> complex functions, but not the real ones (real function don't have mathing dimensionality of the input and output). An in-place transform of C<$x> can be computed with fft1( $x->inplace ); All the functions in this module support PDL threading. For instance, if we have 4 different image ndarrays C<$a>, C<$b>, C<$c>, C<$d> and we want to compute their 2D FFTs at the same time, we can say my $ABCD_transformed = rfft2( PDL::cat( $a, $b, $c, $d) ); This takes advantage of PDL's automatic parallelization, if appropriate (See L). =head2 Data formats FFTW supports single and double-precision floating point numbers directly. If possible, the PDL input will be used as-is. If not, a type conversion will be made to use the lowest-common type. So as an example, the following will perform a single-precision floating point transform (and return data of that type). fft1( $x->byte ) As of 0.20, this module expects complex numbers to be stored as "native complex" types (C, C). Complex outputs will also be native complex. Generally, the sizes of the input and the output must match. This is completely true for the complex <-> complex transforms: the output will have the same size and the input, and an error will result if this isn't possible for some reason. This is a little bit more involved for the real <-> complex transforms. If I'm transforming a real 3D vector of dimensions C, I will get a complex output of dimensions C. The C is there because the input was real. The first dimension is always the one that gets the C. This is described in detail in section 2.4 of the FFTW manual. Note that given a real input, the dimensionality of the complex transformed output is unambiguous. However, this is I true for the backward transform. For instance, a 1D inverse transform of a vector of 10 complex numbers can produce real output of either 18 or 19 elements (because C and C). I. Thus Cdim(0) == 18> is true. If we want the odd-sized output, we have to explicitly pass this into the function like this: irfft1( sequence(cdouble,10), zeros(19) ) Here I create a new output ndarray with the C function; C then fills in this ndarray with the result of the computation. This module validates all of its input, so only 18 and 19 are valid here. An error will be thrown if you try to pass in C. This all means that the following will produce surprising results if C<$x-Edim(0)> isn't even irfft1( rfft1( $x ) ) =head2 FFT normalization Following the widest-used convention for discrete Fourier transforms, this module normalizes the inverse transform (but not the forward transform) by dividing by the number of elements in the data set, so that ifft1( fft1( $x ) ) is a slow approximate no-op, if C<$x> is well-behaved. This is different from the behavior of the underlying FFTW3 library itself, but more consistent with other FFT packages for popular analysis languages including PDL. =head1 FUNCTIONS =head2 fftX (fft1, fft2, fft3, ..., fftn) The basic complex <-> complex FFT. You can pass in the rank as a parameter with the C form, or append the rank to the function name for ranks up to 9. These functions all take one input ndarray and one output ndarray. The dimensions of the input and the output are identical. The output parameter is optional and, if present, must be the last argument. If the output ndarray is passed in, the user I make sure the dimensions match. As of 0.20, inputs must be "native complex" data. Any type other than C or C will be converted in the normal PP way. The fftn form takes a minimum of two arguments: the PDL to transform, and the number of dimensions to transform as a separate argument. The following are equivalent: $X = fftn( $x, 1 ); $X = fft1( $x ); fft1( $x, my $X = $x->zeros ); =head2 ifftX (ifft1, ifft2, ifft3, ..., ifftn) The basic, properly normalized, complex <-> complex backward FFT. Everything is exactly like in the C functions, except the inverse transform is computed and normalized, so that (for example) ifft1( fft1 ( $x ) ) is a good approximation of C<$x> itself. =head2 rfftX (rfft1, rfft2, rfft3, ..., rfftn) The real -> complex FFT. You can pass in the rank with the C form, or append the rank to the function name for ranks up to 9. These functions all take one input ndarray and one output ndarray. The dimensions of the input and the output are not identical, but are related as described in L. The output can be passed in as the last argument, if desired. If the output ndarray is passed in, the user I make sure the dimensions match. In the C form, the rank is the second argument. The following are equivalent: $X = rfftn( $x, 1 ); $X = rfft1( $x ); rfft1( $x, my $X = $x->zeroes ); As of 0.20, only returns native-complex data. Support for PDL::Complex has been removed. =head2 rNfftX (rNfft1, rNfft2, rNfft3, ..., rNfftn) As of 0.20, just an alias for rfftX, etc. =head2 irfftX (irfft1, irfft2, irfft3, ..., irfftn) The complex -> real inverse FFT. You can pass in the rank with the C form, or append the rank to the function name for ranks up to 9. Argument passing and interpretation is as described in C above. Please read L for details about dimension interpretation. There's an ambiguity about the output dimensionality, which is described in that section. =head1 AUTHOR Dima Kogan, C<< >>; contributions from Craig DeForest, C<< >>. =head1 LICENSE AND COPYRIGHT Copyright 2013 Dima Kogan and Craig DeForest. This program is free software; you can redistribute it and/or modify it under the same terms as PDL. =cut PDL-FFTW3-0.203/t/0000755000175000017500000000000014742523116013162 5ustar osboxesosboxesPDL-FFTW3-0.203/t/threads.t0000644000175000017500000000230514103742616015000 0ustar osboxesosboxes#!/usr/bin/env perl use strict; use warnings; use PDL::LiteF; my $can_use_threads = eval 'use threads; 1'; ## no critic (eval) use Test::More; if( ! $can_use_threads ) { plan skip_all => "perl does not support threads"; } else { plan tests => 1; $PDL::no_clone_skip_warning = 1; require PDL::FFTW3; PDL::FFTW3->import('rfft1'); require Thread::Semaphore; } my $s = Thread::Semaphore->new(); sub run_fft { note "Running in thread #" . threads->self->tid; for my $size_factor (1..10) { my $xvals = sequence($size_factor * 100); my $x = sin( $xvals * 2.0 ) + 2.0 * cos( $xvals / 3.0 ); my $F = rfft1( $x ); } $s->down; return 1; } subtest "Running FFTW in threads" => sub { eval { local $SIG{ALRM} = sub { die "alarm\n" }; alarm 4; # timeout in 4 seconds my @threads; for (0..7) { push @threads, threads->create(\&run_fft); $s->up; } $s->down; for my $thr (@threads) { my ($return) = $thr->join; if( ! $return ) { fail "Thread #@{[ $thr->tid ]} did not complete successfully"; return; } } alarm 0; }; if ($@) { if($@ eq "alarm\n") { fail "Threads hanging"; } else { die $@; } } pass "All threads returned successfully"; }; done_testing; PDL-FFTW3-0.203/t/fftw.t0000644000175000017500000016403614723310735014327 0ustar osboxesosboxes#!/usr/bin/perl use strict; use warnings; use Test::More; use PDL::LiteF; use PDL::Types; use PDL::FFTW3; # Please be careful about rearranging these tests, since they depend on the # global FFTW plan cache, and thus order can matter. # # Here I use GNU octave to compute the reference results. To transform 'format # long' octave output to the PDL input that appears here I use this: # # perl -ane 'if(/Column/) { print "],\n[" } if( @F == 3 ) { $F[2] =~ s/i//; print "[" . "$F[0],$F[1]$F[2]" . "],"; } END {print "],\n"} ' # I turn on the pthreading to stress things out a bit more, and to make sure it # works set_autopthread_targ(2); set_autopthread_size(0); # With potentially-unaligned input I can no longer predict when a new plan will # be created, so I disable plan-creation checks. When PDL itself produces only # aligned data buffers this should be re-enabled. my $do_check_plan_creations = undef; use constant approx_eps_double => 1e-8; use constant approx_eps_single => 1e-3; my $Nplans = 0; # 1D basic test { my $x = pdl '0 1+1i 2+4i 3+9i 4+16i 5+25i 6+36i 7+49i 8+64i 9+81i'; # from octave: conj( fft( (0:9) + i* ((0:9).**2) )' ) my $Xref = pdl('45.0+285i -158.8841768587627+17.7490974608742i -73.8190960235587-28.6459544426446i -41.3271264002680-38.7279671349711i -21.2459848116453-42.8475374738350i -5.0-45i 11.2459848116453-46.0967344361641i 31.3271264002680-45.9933924150247i 63.8190960235587-42.4097736473563i 148.8841768587627-13.0277379108784i'); ok_should_make_plan( all( approx( fft1($x), $Xref, approx_eps_double) ), "Basic 1D complex FFT - double precision" ); ok_should_reuse_plan( all( approx( $x->fft1, $Xref, approx_eps_double) ), "Basic 1D complex FFT as a method - double precision" ); ok_should_make_plan( all( approx( fft1(cfloat $x), cfloat($Xref), approx_eps_single) ), "Basic 1D complex FFT - single precision" ); ok_should_make_plan( all( approx( ifft1(fft1($x)), $x, approx_eps_double) ), "Basic 1D complex FFT - inverse(forward) should be the same (normalized)" ); } # 2D basic test { my $x = pdl ' 0 1+0.01i 2+0.04i 3+0.09i 4+0.16i; 5+0.25i 6+0.36i 7+0.49i 8+0.64i 9+0.81i; 10+i 11+1.21i 12+1.44i 13+1.69i 14+1.96i; 15+2.25i 16+2.56i 17+2.89i 18+3.24i 19+3.61i; 20+4i 21+4.41i 22+4.84i 23+5.29i 24+5.76i; 25+6.25i 26+6.76i 27+7.29i 28+7.84i 29+8.41i; 30+9i 31+9.61i 32+10.24i 33+10.89i 34+11.56i '; # from octave: fft2( reshape(0:34,5,7)' + i* (1e-2*reshape(0:34,5,7)' .** 2) ) my $Xref = pdl ' 595+136.85i -25.9303392628859+18.4682083666705i -19.4901331394265-0.24543007434912i -15.5098668605735-11.6176194425008i -9.06966073711407-29.7051588498206i; -158.361292658031+170.810364558179i 3.02129040241307-1.62582569424951i 2.10126095620458+0.284635136279005i 1.532651487797+1.46536486372099i 0.612622041588519+3.37582569424951i; -114.713779395612+42.8112631783535i 1.90212339568438-0.0854244602002882i 0.982093949475899+0.648274540139187i 0.413484481068306+1.10172545986082i -0.506544965140175+1.83542446020029i; -95.2888085635639-9.5507800001045i 1.40404722050367+0.60011858233589i 0.484017774295172+0.810109299679749i -0.0845916941124162+0.939890700320246i -1.00462114032089+1.14988141766411i; -79.7111914364361-49.4933880183808i 1.0046211403209+1.14988141766412i 0.0845916941124141+0.939890700320234i -0.484017774295178+0.810109299679762i -1.40404722050366+0.600118582335895i; -60.286220604388-96.7465798760672i 0.50654496514017+1.83542446020029i -0.413484481068314+1.10172545986081i -0.982093949475905+0.648274540139193i -1.90212339568438-0.085424460200287i; -16.638707341969-192.58087984198i -0.612622041588522+3.3758256942495i -1.53265148779701+1.46536486372098i -2.10126095620459+0.28463513627902i -3.02129040241307-1.6258256942495i '; ok_should_make_plan( all( approx( fft2($x), $Xref, approx_eps_double) ), "Basic 2D native complex FFT - double precision" ); ok_should_make_plan( all( approx( fft2(cfloat $x), cfloat($Xref), approx_eps_single) ), "Basic 2D complex FFT - single precision" ); ok_should_make_plan( all( approx( ifft2(fft2($x)), $x, approx_eps_double) ), "Basic 2D complex FFT - inverse(forward) should be the same (normalized)" ); } # lots of 1D ffts threaded in a 2d array { my $x = pdl ' 4.24264068711928i 1+4.12310562561766i 2+4i 3+3.87298334620742i 4+3.74165738677394i 5+3.60555127546399i; 6+3.46410161513775i 7+3.3166247903554i 8+3.16227766016838i 9+3i 10+2.82842712474619i 11+2.64575131106459i; 12+2.44948974278318i 13+2.23606797749979i 14+2i 15+1.73205080756888i 16+1.4142135623731i 17+i; 18 19+i 20+1.4142135623731i 21+1.73205080756888i 22+2i 23+2.23606797749979i; 24+2.44948974278318i 25+2.64575131106459i 26+2.82842712474619i 27+3i 28+3.16227766016838i 29+3.3166247903554i '; # octave result: fft( reshape(0:29,6,5) + i*sqrt(abs(reshape(18:-1:-11,6,5))) ) my $Xref = pdl ' 15+23.5859383211823i -2.32805351899395+5.55930952077235i -2.7755160508616+2.11251769696778i -3+0.38265782660416i -3.2244839491384-1.35158391816997i -3.67194648100605-4.83299532464091i; 51+18.4171825014723i -2.12988347946625+5.6460896960971i -2.70812956895156+2.21961197953935i -3+0.49243029863233i -3.29187043104844-1.2444896355984i -3.87011652053375-4.74621514931617i; 87+10.8318220902249i -1.42222779450344+5.82451856548428i -2.43683966685802+2.58845058798449i -3+0.89558452008761i -3.56316033314198-0.87565102715326i -4.57777220549656-4.56778627992898i; 123+8.38233234744176i -4.57777220549656+3.3750288227011i -3.56316033314198+0.13896084520131i -3-1.55390522269557i -2.43683966685802-3.32514076993644i -1.42222779450344-7.01727602271216i; 159+17.4025706291177i -3.87011652053375+4.63147782374252i -3.29187043104844+1.20500010718478i -3-0.52218157372224i -2.70812956895156-2.25910150795298i -2.12988347946625-5.76082702167074i '; ok_should_make_plan( all( approx( fft1($x), $Xref, approx_eps_double) ), "1D FFTs threaded inside a 2D ndarray" ); ok_should_make_plan( all( approx( ifft1(fft1($x)), $x , approx_eps_double) ), "1D FFTs threaded inside a 2D ndarray - inverse(forward) should be the same" ); } # lots of 1D ffts threaded in a 3d array { my $x = PDL::czip(sequence(6,5,4)**1.1, (abs(sequence(6,5,4) - 10))**0.8); # octave reference code (3d array collapsed to 2d) # re = reshape(0:119,6,4*5) .** 1.1 # im = (abs(reshape(0:119,6,4*5) - 10)) .** 0.8 # fft(re + i*im) my $Xref = PDL::czip(reshape( pdl( [[1.69598045826025e+01,+2.99472886475101e+01],[-4.57128799578198e-01,+7.88558765388318e+00],[-2.51287898808965e+00,+3.70301251723728e+00],[-3.48312389248108e+00,+1.61384695353585e+00],[-4.40181702820775e+00,-4.91751648931500e-01],[-6.10485587424586e+00,-4.80054345442333e+00]], [[6.33118711499336e+01,+9.18075894489374e+00],[-1.28375178011374e+00,+9.98129736230945e+00],[-4.36076655499263e+00,+3.82708231830186e+00],[-4.08027791431578e+00,+3.64309574332352e-01],[-3.78420764599829e+00,-9.13541864133209e-01],[-6.73854409586578e+00,-4.25130753757941e+00]], [[1.13759697869505e+02,+1.97408963697151e+01],[-7.29388611445114e+00,+5.57273237248353e+00],[-5.32061252013794e+00,+6.73706431548140e-01],[-4.30915950819925e+00,-1.80990242523002e+00],[-3.28801256905250e+00,-4.31960446632102e+00],[-1.23762967061300e+00,-9.41122152264220e+00]], [[1.66435716068732e+02,+3.92801435749690e+01],[-7.00620607474377e+00,+6.20403184755403e+00],[-5.31853826280121e+00,+1.07388595520872e+00],[-4.46232098197304e+00,-1.50418919374508e+00],[-3.59897150704107e+00,-4.09148521833613e+00],[-1.85441208781864e+00,-9.29419710710112e+00]], [[2.20709491043481e+02,+5.64630711694275e+01],[-6.91312575495935e+00,+6.55086880329684e+00],[-5.36308316775101e+00,+1.27431097299403e+00],[-4.57890644153963e+00,-1.37165712546438e+00],[-3.78905073734364e+00,-4.02301895420251e+00],[-2.19365625629182e+00,-9.34243162811467e+00]], [[2.76241513888165e+02,+7.23930056605892e+01],[-6.87318460924704e+00,+6.80271191149501e+00],[-5.41174690593730e+00,+1.41218875469242e+00],[-4.67359735938111e+00,-1.28842511961541e+00],[-3.93071118249889e+00,-3.99277491384701e+00],[-2.43142506404472e+00,-9.41307369494718e+00]], [[3.32816672889238e+02,+8.74786737952202e+01],[-6.85538501192059e+00,+7.00327114642510e+00],[-5.45840640198368e+00,+1.51804628653698e+00],[-4.75361170451194e+00,-1.22862468678152e+00],[-4.04474354399512e+00,-3.97811872251649e+00],[-2.61520470809143e+00,-9.48587494925505e+00]], [[3.90285534816727e+02,+1.01934524303480e+02],[-6.84870160277348e+00,+7.17094139230458e+00],[-5.50194078556953e+00,+1.60420335500904e+00],[-4.82306119868588e+00,-1.18240994885097e+00],[-4.14060148105651e+00,-3.97127361702190e+00],[-2.76521775751010e+00,-9.55598548492096e+00]], [[4.48537844122192e+02,+1.15891995850952e+02],[-6.84822636641758e+00,+7.31547544486356e+00],[-5.54234829173794e+00,+1.67694646900063e+00],[-4.88452106175259e+00,-1.14500594148577e+00],[-4.22349575457002e+00,-3.96881837596082e+00],[-2.89204567772393e+00,-9.62221002325373e+00]], [[5.07488694175294e+02,+1.29439468309100e+02],[-6.85145842410931e+00,+7.44274887104558e+00],[-5.57990116405240e+00,+1.73994109240401e+00],[-4.93971551455435e+00,-1.11374817715806e+00],[-4.29663683461546e+00,-3.96901565750555e+00],[-3.00194446700983e+00,-9.68443780878244e+00]], [[5.67070568283683e+02,+1.42640190784627e+02],[-6.85700195115374e+00,+7.55660524048573e+00],[-5.61491735058256e+00,+1.79551966393514e+00],[-4.98985787485722e+00,-1.08700470396320e+00],[-4.36215484055982e+00,-3.97089501060820e+00],[-3.09892860515759e+00,-9.74290039627863e+00]], [[6.27228420472482e+02,+1.55541513973010e+02],[-6.86402517554169e+00,+7.65970907217783e+00],[-5.64769495207659e+00,+1.84526159054989e+00],[-5.03583553656148e+00,-1.06370689880151e+00],[-4.42154056842631e+00,-3.97387614329543e+00],[-3.18573117267543e+00,-9.79792420309997e+00]], [[6.87916464776208e+02,+1.68180119564361e+02],[-6.87200754524014e+00,+7.75398935566485e+00],[-5.67849584768631e+00,+1.89028747465175e+00],[-5.07831785783708e+00,-1.04311878827984e+00],[-4.47588053315932e+00,-3.97759390422351e+00],[-3.26429827802791e+00,-9.84984260267090e+00]], [[7.49095987297811e+02,+1.80585198360161e+02],[-6.88061078534553e+00,+7.84089054155888e+00],[-5.70754494005455e+00,+1.93142189375022e+00],[-5.11782264695134e+00,-1.02471305032201e+00],[-4.52599231404356e+00,-3.98180928875154e+00],[-3.33606544685574e+00,-9.89896527117856e+00]], [[8.10733802053247e+02,+1.92780489751580e+02],[-6.88960853349083e+00,+7.92152398109467e+00],[-5.73503408146089e+00,+1.96928962600128e+00],[-5.15475900259173e+00,-1.00809962679505e+00],[-4.57250726514005e+00,-3.98636092807034e+00],[-3.40212222387915e+00,-9.94556934146171e+00]], [[8.72801129019100e+02,+2.04785648596696e+02],[-6.89884580787876e+00,+7.99676402590117e+00],[-5.76112695592524e+00,+2.00437559511875e+00],[-5.18945595634560e+00,-9.92982320347998e-01],[-4.61592350811448e+00,-3.99113714088274e+00],[-3.46331527512791e+00,-9.98989898003569e+00]], [[9.35272758698146e+02,+2.16617194443934e+02],[-6.90821460405405e+00,+8.06731155157295e+00],[-5.78596367678119e+00,+2.03706383976585e+00],[-5.22218222758994e+00,-9.79131204378469e-01],[-4.65664122235683e+00,-3.99605908789967e+00],[-3.52031568871528e+00,-1.00321680493426e+01]], [[9.98126416838528e+02,+2.28289190542651e+02],[-6.91763865128097e+00,+8.13373739118195e+00],[-5.80966474975681e+00,+2.06766375929783e+00],[-5.25316021545925e+00,-9.66364422864565e-01],[-4.69498691572975e+00,-4.00107022982894e+00],[-3.57366444372853e+00,-1.00725636708949e+01]], [[1.06134227249634e+03,+2.39813741913757e+02],[-6.92706358972381e+00,+8.19651289056715e+00],[-5.83233436936525e+00,+2.09642832609833e+00],[-5.28257613941830e+00,-9.54535801950115e-01],[-4.73123057917205e+00,-4.00612951750744e+00],[-3.62380403823212e+00,-1.01112498044903e+01]], [[1.12490255099778e+03,+2.51201368661019e+02],[-6.93645047026591e+00,+8.25603193408341e+00],[-5.85406312069293e+00,+2.12356705077917e+00],[-5.31058753759640e+00,-9.43526187589065e-01],[-4.76559809982867e+00,-4.01120687194793e+00],[-3.67110104753344e+00,-1.01483705104741e+01]] ), 2,6,5,4 )->using(0,1)); my $f = fft1($x); is( get_autopthread_actual(), 2, "1D FFTs threaded inside a 3D ndarray - CPU threading should work" ); ok_should_reuse_plan( all( approx( $f, $Xref, approx_eps_double) ), "1D FFTs threaded inside a 3D ndarray" ); } # try out some different ways of calling the module, make sure the argument # verification works { eval { fft1( ) }; ok( $@, "Calling fft1 with no arguments should fail" ); eval { fft1(sequence(2,5), sequence(2,5), sequence(2,5) ) }; ok( $@, "Calling fft1 with too many arguments should fail" ); eval { fft1(null) }; ok( $@, "Calling fft1(null) should fail."); eval { fft1( {} ) }; ok( $@, "Calling fft1( {} ) should fail (want ndarray input)."); eval { fft1(sequence(2,5), sequence(3,5) ) }; ok( $@, "Calling fft1 with mismatched arguments should fail" ); # should be able to ask for output in the arglist my $x = random(10)->r2C; my $f1 = fft1( $x ); my $f2; eval { fft1( $x, $f2 ) }; ok( $@, "Calling fft1 with undef argument should fail" ); $f2 = null; eval { fft1( $x, $f2 ) }; ok_should_reuse_plan( !$@ && all( approx( $f1, $f2 ), approx_eps_double), "Should be able to ask for output in the arglist" ); eval { fft4( sequence(5,3,4,4)->r2C ); }; ok_should_make_plan( ! $@, "dimensionality baseline" ); eval { fft4( sequence(5,4,4)->r2C ); }; ok( $@, "too few dimensions should fail" ); } # inplace checks { my $xorig = PDL::czip(sequence(10), sequence(10)**2); # from octave: conj( fft( (0:9) + i* ((0:9).**2) )' ) my $Xref = PDL::czip(pdl( [45.0000000000000,+285.0000000000000], [-158.8841768587627,+17.7490974608742], [-73.8190960235587,-28.6459544426446], [-41.3271264002680,-38.7279671349711], [-21.2459848116453,-42.8475374738350], [-5.0000000000000,-45.0000000000000], [11.2459848116453,-46.0967344361641], [31.3271264002680,-45.9933924150247], [63.8190960235587,-42.4097736473563], [148.8841768587627,-13.0277379108784] )->using(0,1)); my $x = $xorig->copy; fft1($x, $x); ok_should_make_plan( all( approx( $x, $Xref, approx_eps_double) ), 'In-place test: fft1($x,$x)' ); $x = $xorig->copy; fft1( $x->inplace ); ok_should_reuse_plan( all( approx( $x, $Xref, approx_eps_double) ), 'In-place test: fft1( $x->inplace )' ); ok( !$x->is_inplace, "After computation the in-place flag should be cleared" ); } # lots of 2D ffts threaded in a 3d array { my $x = PDL::czip( sequence(6,5,4)**1.1, (abs(sequence(6,5,4) - 10))**0.8 ); # in octave I compute 4 slices separately # re = reshape(0:119,6,4*5) .** 1.1 # im = (abs(reshape(0:119,6,4*5) - 10)) .** 0.8 # slice1 = fft2(re(:, 1:5 ) + i*im(:, 1:5 )) # slice2 = fft2(re(:, 6:10) + i*im(:, 6:10)) # slice3 = fft2(re(:,11:15) + i*im(:,11:15)) # slice4 = fft2(re(:,16:20) + i*im(:,16:20)) my $Xref = PDL::czip(pdl( [ [[581.176580714253419,+154.612158706515430],[-22.954098523846195,+36.194518039527026],[-22.875879493772423,+10.551998195290025],[-20.913788738508782,-2.707592216571274],[-18.862059487643258,-13.839402151924361],[-18.129097984835099,-37.099701249860743]], [[-178.408651850856160,+183.139542083867042],[11.470377256968625,-1.718053156591536],[5.282150186239193,+2.913557271028725],[2.408626479527148,+3.419465916129992],[1.652807905147736,+4.600098529135501],[-1.590381734378646,+10.091671271587607]], [[-135.442076986364611,+37.496948236259065],[4.372069715046538,-5.432458423609452],[3.947276983624399,-0.475185262726981],[2.122893057072843,+1.257276333468205],[1.641541660378284,+1.195733119702670],[3.269737048112678,+3.674743286679739]], [[-117.024242102743898,-47.339310480754413],[-0.861443876013559,+1.732467533240662],[0.185127597800303,+0.707054055400392],[-0.499359313992656,+1.552118887461319],[-2.447776485966577,+0.609947466490766],[-2.937831933834375,-2.841282678209592]], [[-65.502586861276143,-178.172895308336507],[5.687451429953602,+8.651464276849186],[0.896929785660302,+4.817638327194231],[-0.533990946503966,+4.547965847191024],[-3.993598732954924,+4.974864791937925],[-11.136704766293862,+2.171852097686326]] ], [ [[1.95537025989162e+03,+5.07137667919341e+02],[-3.42769560144680e+01,+3.57351487661338e+01],[-2.74943435492808e+01,+7.95132595764308e+00],[-2.40745068388859e+01,-5.95821387389173e+00],[-2.06361887967360e+01,-1.98800012868518e+01],[-1.37058376743800e+01,-4.77615819611594e+01]], [[-1.90823299051261e+02,+1.63561853708853e+02],[-5.30707810726989e-01,-4.48966067048194e-01],[-1.41545809310629e-01,-3.74843067838806e-01],[5.33592439251135e-02,-3.42459575841451e-01],[2.48256646407656e-01,-3.13193921558001e-01],[6.37372950089715e-01,-2.63898986015401e-01]], [[-1.55758721083882e+02,+1.14830386090883e+01],[-1.37557316201933e-01,-4.05958779660870e-01],[4.43148065741885e-02,-2.42630352946638e-01],[1.36711806861231e-01,-1.63554000296091e-01],[2.29943537575820e-01,-8.62535289493658e-02],[4.18482577171305e-01,+6.29473735169093e-02]], [[-1.32979537236871e+02,-8.30537608826256e+01],[1.04159629258482e-01,-4.09670807209363e-01],[1.66802170317476e-01,-1.76664338294510e-01],[2.00610593363786e-01,-6.16794569397552e-02],[2.35974686283565e-01,+5.21905158295986e-02],[3.11024493644906e-01,+2.76346187116616e-01]], [[-9.46011330787756e+01,-2.37163771051710e+02],[4.75138465903229e-01,-4.56993554740357e-01],[3.66037852013303e-01,-9.62444251010284e-02],[3.15838397830188e-01,+8.37813088919830e-02],[2.68458013974529e-01,+2.63383652294485e-01],[1.81832333250489e-01,+6.20818911805349e-01]]], [ [[3.44204524288343e+03,+8.39727512433739e+02],[-3.43632539907719e+01,+3.87327181909819e+01],[-2.83836871718609e+01,+9.43178024888828e+00],[-2.53765929187988e+01,-5.22664306816161e+00],[-2.23580755213291e+01,-1.98905352749490e+01],[-1.62871457265958e+01,-4.92352018146898e+01]], [[-1.93850055487056e+02,+1.78604950208849e+02],[-2.81086565720529e-01,-2.74439891768760e-01],[-6.30094446091169e-02,-2.17686087051074e-01],[4.62218954441780e-02,-1.90640300235732e-01],[1.55551122009561e-01,-1.64483923137678e-01],[3.74411612383404e-01,-1.14838044041313e-01]], [[-1.62294627938734e+02,+1.82927539954391e+01],[-5.11143306149971e-02,-2.36649532393143e-01],[4.16430850746093e-02,-1.33253277381126e-01],[8.85453597042875e-02,-8.22033677083361e-02],[1.35780317473130e-01,-3.15956544938805e-02],[2.31197389980492e-01,+6.82673228827312e-02]], [[-1.42113448440296e+02,-8.10603929075218e+01],[9.13716757316451e-02,-2.22938826506865e-01],[1.09204470637664e-01,-8.58346301593518e-02],[1.18905834471482e-01,-1.75430229641282e-02],[1.29121710583797e-01,+5.05580698311880e-02],[1.51066988141103e-01,+1.86140992994433e-01]], [[-1.08434269598928e+02,-2.42363869807370e+02],[3.19073455607117e-01,-2.15663737884522e-01],[2.21262307844932e-01,-1.74079346210403e-02],[1.73630454892792e-01,+8.20062392537970e-02],[1.26848168463476e-01,+1.81581729708371e-01],[3.58267103028470e-02,+3.81129561460766e-01]]], [ [[4.99244512804990e+03,+1.14070714415806e+03],[-3.45882131232035e+01,+4.06503577933066e+01],[-2.90431528725214e+01,+1.03290985710599e+01],[-2.62579620764095e+01,-4.83653993713021e+00],[-2.34643803252018e+01,-2.00056028480667e+01],[-1.78522004933373e+01,-5.03542510152376e+01]], [[-1.96380323533266e+02,+1.88152304723188e+02],[-1.92865464527335e-01,-2.02889176717224e-01],[-3.86947461251954e-02,-1.56831303578437e-01],[3.85138755642588e-02,-1.34443894895275e-01],[1.15794735723374e-01,-1.12485367180954e-01],[2.70543304787344e-01,-6.98569500483150e-02]], [[-1.66872451417648e+02,+2.23039858589711e+01],[-2.77487657486899e-02,-1.70406376180963e-01],[3.47883788886858e-02,-9.33380414940140e-02],[6.63337828406928e-02,-5.50977611340899e-02],[9.80588055564104e-02,-1.70571735903083e-02],[1.62032394212931e-01,+5.84144732089472e-02]], [[-1.48137315640352e+02,-8.03755018459729e+01],[7.46992540366458e-02,-1.55140422934376e-01],[8.17655449197855e-02,-5.64025227538078e-02],[8.56906163946222e-02,-7.12349856999575e-03],[8.98748544483531e-02,+4.20897732548463e-02],[9.90136072387319e-02,+1.40302117404749e-01]], [[-1.17049392363129e+02,-2.46859689910763e+02],[2.39899060049070e-01,-1.38101687968226e-01],[1.59658915211960e-01,-6.48727639912472e-04],[1.20144019881925e-01,+6.82934899895806e-02],[8.10343889012709e-02,+1.37369911169460e-01],[4.03481145874271e-03,+2.75896474493798e-01]]] )->using(0,1)); ok_should_make_plan( all( approx( fft2($x), $Xref, approx_eps_double) ), "2D FFTs threaded inside a 3D ndarray" ); ok_should_make_plan( all( approx( fft2(cfloat $x), cfloat($Xref), approx_eps_single) ), "2D FFTs threaded inside a 3D ndarray - single precision" ); # Now I take a slice of the input matrix, and fft that. This makes sure the # nembed logic works correctly. It turns out that $P() in PP physicalizes the # data, so no nembed logic currently exists, and stuff works anyway my $x_slice1_connected = $x->slice('0:4'); my $x_slice1_severed = $x_slice1_connected->copy; # octave ref; x is the same, but I slice it differently # slice1 = fft2(re(1:5, 1:5 ) + i*im(1:5, 1:5 )) # slice2 = fft2(re(1:5, 6:10) + i*im(1:5, 6:10)) # slice3 = fft2(re(1:5,11:15) + i*im(1:5,11:15)) # slice4 = fft2(re(1:5,16:20) + i*im(1:5,16:20)) my $X_slice1_ref = PDL::czip(pdl( [ [[466.673919234444952,+126.917907968545364],[-17.870670056332415,+22.924773314027945],[-17.419713146828549,+4.981405529320815],[-17.185596880754357,-6.395001262195541],[-16.329892079156576,-25.335769447218507]], [[-146.569840424967879,+155.523541145286003],[7.951065332965691,-1.600154624557469],[4.722562521481950,+2.138095446385981],[2.501374281282990,+4.563007814082358],[-1.259388175222825,+8.080745148100860]], [[-111.129976688165755,+32.155437078172781],[2.298297449575815,-3.696832692971159],[2.653255527169226,-1.211338065207176],[2.961744281698524,+0.603384484335323],[3.142880661280621,+3.580230270481282]], [[-98.149556168903700,-38.308859759070558],[-1.167503315289326,+2.243222101185434],[-1.415433176872478,+0.465280753105545],[-1.265331912942163,-0.563640202617083],[-0.990113854951290,-1.818507239579064]], [[-55.390996616595444,-144.671074787325381],[4.429337544491914,+7.214235144120515],[-0.507891051226907,+5.073209513809378],[-3.336844872242201,+3.567780589580991],[-7.345688413939850,+1.308257900249694]]], [ [[1.60938166615551e+03,+4.17670759558360e+02],[-2.68270274976814e+01,+2.26046920161292e+01],[-2.16496476842982e+01,+1.53675022177060e+00],[-1.84235786296893e+01,-1.15007886011593e+01],[-1.31627231622858e+01,-3.26244585940880e+01]], [[-1.58974908440522e+02,+1.36021676093101e+02],[-3.47634511617340e-01,-3.58080303406544e-01],[-4.81671641422727e-02,-3.04113508354926e-01],[1.37085201618827e-01,-2.75199098858623e-01],[4.36488388932893e-01,-2.35539773688715e-01]], [[-1.29686452688029e+02,+9.43606648956182e+00],[-7.01487711558949e-02,-2.99496693116574e-01],[7.08541090235068e-02,-1.75726626102805e-01],[1.59295212504510e-01,-1.01725734277373e-01],[3.04097411071888e-01,+1.38736645617663e-02]], [[-1.10651829493696e+02,-6.92608523091722e+01],[1.02487962542310e-01,-2.85298738134981e-01],[1.52660684703708e-01,-1.07199757251788e-01],[1.85962069038061e-01,+1.35561877936201e-03],[2.43247390743176e-01,+1.74296200677137e-01]], [[-7.85760340725247e+01,-1.97565816638999e+02],[3.69940012121665e-01,-2.93442198214098e-01],[2.89796229462924e-01,-1.61887501790537e-02],[2.44337576650966e-01,+1.54687612811214e-01],[1.77103574452566e-01,+4.29905675781014e-01]]], [ [[2.84720460273767e+03,+6.95426680279720e+02],[-2.71001945282257e+01,+2.47316707441501e+01],[-2.25465424823261e+01,+2.50812248540162e+00],[-1.97162495543114e+01,-1.12342767103843e+01],[-1.51112098777464e+01,-3.34825045357138e+01]], [[-1.61503358924060e+02,+1.48680117938248e+02],[-1.79689739185806e-01,-2.15121551579987e-01],[-1.29520991006925e-02,-1.72606756571795e-01],[9.02589918624682e-02,-1.47576720159049e-01],[2.57436047526738e-01,-1.09094607758747e-01]], [[-1.35172303389293e+02,+1.51762198152973e+01],[-1.92334762886415e-02,-1.71645475592242e-01],[5.19956985158810e-02,-9.30825631541810e-02],[9.64968448637267e-02,-4.51405104074254e-02],[1.69230891271143e-01,+3.14149724776526e-02]], [[-1.18329666591565e+02,-6.75646155694635e+01],[8.08712125581526e-02,-1.51645886509917e-01],[9.50510458629830e-02,-4.70696243011893e-02],[1.04542849160088e-01,+1.73074187214384e-02],[1.21053350041535e-01,+1.21015057947666e-01]], [[-9.02186573536845e+01,-2.01901820815281e+02],[2.41850811057668e-01,-1.30135033446000e-01],[1.68066917394274e-01,+2.15612297762638e-02],[1.23657088647714e-01,+1.15562097610201e-01],[5.37254517374679e-02,+2.67979230130139e-01]]], [ [[4.13847519887461e+03,+9.46564617413697e+02],[-2.73984844843996e+01,+2.60748739526840e+01],[-2.31796005168213e+01,+3.07463500922328e+00],[-2.05604995190013e+01,-1.11449862512929e+01],[-1.63039317216984e+01,-3.41606345226538e+01]], [[-1.63618296453467e+02,+1.56682279811098e+02],[-1.21816276616064e-01,-1.57768013235654e-01],[-4.20394797933782e-03,-1.23055796103499e-01],[6.85931647323148e-02,-1.02200070987194e-01],[1.86530323460939e-01,-6.94242384647662e-02]], [[-1.39005402614916e+02,+1.85410801291960e+01],[-7.24932070775660e-03,-1.22713508583900e-01],[4.06094617668798e-02,-6.41494708720088e-02],[7.04423276692341e-02,-2.82307037179682e-02],[1.19111127939139e-01,+2.94310371284693e-02]], [[-1.23376794373435e+02,-6.69854082776712e+01],[6.41877749756504e-02,-1.04443436094924e-01],[6.98384223826521e-02,-2.92264510229911e-02],[7.36940494064069e-02,+1.71728447983243e-02],[8.05143239080554e-02,+9.20916536812421e-02]], [[-9.74417295843736e+01,-2.05659722932842e+02],[1.79920562610709e-01,-8.03744824636155e-02],[1.19169692096844e-01,+2.45697427068684e-02],[8.21878607839186e-02,+8.96232824108548e-02],[2.32671625274674e-02,+1.95170681256241e-01]]] )->using(0,1)); ok_should_make_plan( all( approx( fft2($x_slice1_severed), $X_slice1_ref, approx_eps_double) ), "2D FFTs threaded inside a 3D ndarray - slice1 - severed" ); ok_should_reuse_plan( all( approx( fft2($x_slice1_connected), $X_slice1_ref, approx_eps_double) ), "2D FFTs threaded inside a 3D ndarray - slice1 - connected" ); # now go again with single precision. If $P() didn't physicalize its input, # this would no longer be aligned like before, since the input has shifted by # 4 bytes ok_should_make_plan( all( approx( fft2(cfloat $x_slice1_connected), cfloat($X_slice1_ref), approx_eps_single) ), "2D FFTs threaded inside a 3D ndarray - slice1 - connected - single precision" ); # Similar, but slicing from 1 my $x_slice2_connected = $x->slice('1:5'); my $x_slice2_severed = $x_slice2_connected->copy; # octave ref; x is the same, but I slice it differently # slice1 = fft2(re(2:6, 1:5 ) + i*im(2:6, 1:5 )) # slice2 = fft2(re(2:6, 6:10) + i*im(2:6, 6:10)) # slice3 = fft2(re(2:6,11:15) + i*im(2:6,11:15)) # slice4 = fft2(re(2:6,16:20) + i*im(2:6,16:20)) my $X_slice2_ref = PDL::czip(pdl( [ [[501.602971299978947,+129.993495486019384],[-19.456482657442123,+24.258038724117910],[-18.901134436232283,+3.773486895387808],[-16.305859228821138,-7.743879151484332],[-15.423234561310013,-24.567626816829364]], [[-151.877806557964107,+149.398495098007515],[8.163865009246718,+0.126518424791354],[2.817072235106799,+2.881419182368629],[1.352437467320572,+2.913388776160406],[-0.169443715571585,+6.850269367688697]], [[-115.427317232676344,+31.210772021296826],[3.796588391538631,-3.335504645848345],[2.597359558283342,+0.777874238982427],[0.879920386805304,+1.061145569333210],[2.149823747278043,+1.912391035534666]], [[-96.426654416952132,-39.742809611359242],[-0.598036090192128,+0.778294180312916],[0.320622108376032,+0.964395257139944],[-1.544335750271782,+1.347131498949573],[-2.866826597745860,-1.701987219366836]], [[-53.072170179373629,-152.671376980423531],[2.832891540353520,+6.174977250984833],[0.255424405994367,+3.432508834397477],[-1.781776614496669,+4.184361527846315],[-7.917898111232422,+2.712874425873987]]], [ [[1.64950652205531e+03,+4.27600276999139e+02],[-2.68326169419681e+01,+2.27006099751444e+01],[-2.16864604518378e+01,+1.58304952104838e+00],[-1.84802721265835e+01,-1.14845056637592e+01],[-1.32524144503064e+01,-3.26556345359360e+01]], [[-1.59063871746115e+02,+1.36592105027094e+02],[-3.36871694070668e-01,-3.49609863474421e-01],[-4.55953991858456e-02,-2.96056972955683e-01],[1.34600311442187e-01,-2.67131294666781e-01],[4.25889674867783e-01,-2.27029553320656e-01]], [[-1.29914250138566e+02,+9.70844038896290e+00],[-6.62751362095687e-02,-2.91745099839182e-01],[7.01619885190578e-02,-1.70438268145247e-01],[1.55723949511221e-01,-9.77926603796963e-02],[2.95815595456073e-01,+1.58897872028383e-02]], [[-1.10986042959537e+02,-6.91582210856051e+01],[1.02120011495870e-01,-2.76831416225680e-01],[1.49564602040562e-01,-1.03018025874890e-01],[1.81059831817083e-01,+3.01286202283804e-03],[2.35263568251515e-01,+1.72089158783488e-01]], [[-7.91021617428083e+01,-1.97705600191950e+02],[3.63757532989153e-01,-2.82417409570807e-01],[2.82874371475266e-01,-1.27278916830977e-02],[2.36733361180215e-01,+1.53574423567642e-01],[1.68068158380308e-01,+4.21594307082665e-01]]], [ [[2.88949916162442e+03,+7.04137573981104e+02],[-2.71104520320376e+01,+2.47850199832838e+01],[-2.25708531361947e+01,+2.53121804927141e+00],[-1.97494432727707e+01,-1.12297375097047e+01],[-1.51590850953107e+01,-3.35077499355859e+01]], [[-1.61580394342465e+02,+1.48997806548413e+02],[-1.76879506003449e-01,-2.12465169857628e-01],[-1.24755429736926e-02,-1.70267020812346e-01],[8.92911645313911e-02,-1.45396031420977e-01],[2.54131545443049e-01,-1.07112102202037e-01]], [[-1.35319865252548e+02,+1.53132007477149e+01],[-1.85746548309280e-02,-1.69343727504296e-01],[5.15118373088666e-02,-9.16869090114727e-02],[9.52949133188648e-02,-4.42851974580498e-02],[1.66850120576454e-01,+3.14287260996196e-02]], [[-1.18527818813508e+02,-6.75353911868008e+01],[8.01879439510876e-02,-1.49371193378786e-01],[9.38997734474098e-02,-4.61642470396344e-02],[1.03082280030402e-01,+1.73771906993944e-02],[1.19061337332500e-01,+1.19751924894898e-01]], [[-9.05079981819587e+01,-2.02038499149128e+02],[2.39079369215351e-01,-1.27615555152443e-01],[1.65776790935290e-01,+2.18486772332205e-02],[1.21629148805613e-01,+1.14469852669599e-01],[5.20654175199115e-02,+2.64657747522687e-01]]], [ [[4.18223859152336e+03,+9.54625443037726e+02],[-2.74079447865556e+01,+2.61124638323619e+01],[-2.31978971811230e+01,+3.09007255890941e+00],[-2.05843589930420e+01,-1.11431800313476e+01],[-1.63369532442610e+01,-3.41807837482412e+01]], [[-1.63682484895292e+02,+1.56906338384727e+02],[-1.20524688463036e-01,-1.56416089806520e-01],[-4.03733743270347e-03,-1.21913001592987e-01],[6.80631350314347e-02,-1.01174630711859e-01],[1.84871712650214e-01,-6.85694062663622e-02]], [[-1.39115953613999e+02,+1.86329023623412e+01],[-7.02295195511970e-03,-1.21580690846917e-01],[4.03182507468609e-02,-6.34983391169523e-02],[6.98267599630536e-02,-2.78713424589018e-02],[1.17963938106273e-01,+2.93281548087439e-02]], [[-1.23519603679800e+02,-6.69735391127112e+01],[6.37479744806300e-02,-1.03380542771702e-01],[6.92370420039889e-02,-2.88488700507459e-02],[7.29858824833397e-02,+1.71294493033826e-02],[7.96224057648701e-02,+9.13717087078728e-02]], [[-9.76419555018576e+01,-2.05773543168977e+02],[1.78415452819879e-01,-7.93210083326257e-02],[1.18035987634609e-01,+2.45615120500224e-02],[8.12721813043989e-02,+8.89567757119387e-02],[2.26856083517065e-02,+1.93436447059624e-01]]] )->using(0,1)); ok_should_reuse_plan( all( approx( fft2($x_slice2_severed), $X_slice2_ref, approx_eps_double) ), "2D FFTs threaded inside a 3D ndarray - slice2 - severed" ); ok_should_reuse_plan( all( approx( fft2($x_slice2_connected), $X_slice2_ref, approx_eps_double) ), "2D FFTs threaded inside a 3D ndarray - slice2 - connected" ); # now go again with single precision. If $P() didn't physicalize its input, # this would no longer be aligned like before, since the input has shifted by # 4 bytes ok_should_reuse_plan( all( approx( fft2(cfloat $x_slice2_connected), cfloat($X_slice2_ref), approx_eps_single) ), "2D FFTs threaded inside a 3D ndarray - slice2 - connected - single precision" ); # I now try to fft INTO a slice. The out-of-slice pieces shouldn't be touched my $x_orig = $x->copy; fft2( $x_slice2_severed, $x_slice2_connected ); is( get_autopthread_actual(), 2, "1D FFTs threaded inside a 3D ndarray - slice2 - connected - in-slice correct - CPU threading should work" ); ok_should_reuse_plan( all( approx( $x_slice2_connected, $X_slice2_ref, approx_eps_double) ), "2D FFTs threaded inside a 3D ndarray - slice2 - connected - in-slice correct" ); my $X_partialfft_ref = $x_orig->copy; $X_partialfft_ref->slice('1:5') .= $x_slice2_connected; ok( all( approx( $x, $X_partialfft_ref, approx_eps_double) ), "2D FFTs threaded inside a 3D ndarray - slice2 - connected - out-of-slice correct" ); } # check the type logic { my $x = PDL::czip(sequence(10), sequence(10)**2); # from octave: conj( fft( (0:9) + i* ((0:9).**2) )' ) my $Xref = PDL::czip(pdl( [45.0000000000000,+285.0000000000000], [-158.8841768587627,+17.7490974608742], [-73.8190960235587,-28.6459544426446], [-41.3271264002680,-38.7279671349711], [-21.2459848116453,-42.8475374738350], [-5.0000000000000,-45.0000000000000], [11.2459848116453,-46.0967344361641], [31.3271264002680,-45.9933924150247], [63.8190960235587,-42.4097736473563], [148.8841768587627,-13.0277379108784] )->using(0,1)); my $f = null; my $F; fft1($x, $f); ok_should_reuse_plan( all( approx( $f, $Xref, approx_eps_double) ), "Type-checking baseline" ); ok( $PDL::FFTW3::_last_do_double_precision, "Unspecified FFTs should be double-precision" ); $f = null; $F = fft1($x->cfloat, $f); ok_should_reuse_plan( all( approx( $f, $Xref, approx_eps_single) ), "Float input/Unspecified output baseline" ); ok( ! $PDL::FFTW3::_last_do_double_precision, "Float input/Unspecified output should do a float FFT" ); is( $F->type, cfloat, "CFloat input/Unspecified output should return a cfloat" ); $f = $x->zeros; fft1($x, $f); ok_should_reuse_plan( all( approx( $f, $Xref, approx_eps_double) ), "double input/double output baseline" ); ok( $PDL::FFTW3::_last_do_double_precision, "double input/double output should do a double FFT" ); $f = $x->zeros->cfloat; fft1($x, $f); ok_should_reuse_plan( all( approx( $f, $Xref, approx_eps_single) ), "double input/float output baseline" ); ok( ! $PDL::FFTW3::_last_do_double_precision, "double input/cfloat output should do a float FFT" ); $f = $x->zeros->byte; eval { fft1($x, $f) }; ok( $@, "Output to 'byte' should fail"); $f = $x->zeros; fft1($x->cfloat, $f->cdouble ); ok_should_reuse_plan( all( approx( $f, $Xref, approx_eps_double) ), "float input/double output baseline" ); ok( $PDL::FFTW3::_last_do_double_precision, "float input/double output should do a double FFT" ); } # real fft basic checks { my $x6 = sequence(6)**2; my $x7 = sequence(7)**2; # octave refs parse with this: # perl -ane '@F[2] =~ s/i//g; print "[$F[0],$F[1]$F[2]],\n";' # octave reference: conj(fft( (0:5).**2 )') and conj(fft( (0:6).**2 )') my $fx6_ref = PDL::czip(pdl( [55.00000000000000,+0.00000000000000], [-6.00000000000000,+31.17691453623979], [-14.00000000000000,+10.39230484541326], [-15.00000000000000,+0.00000000000000], [-14.00000000000000,-10.39230484541326], [-6.00000000000000,-31.17691453623979] )->using(0,1)); my $fx7_ref = PDL::czip(pdl( [91.00000000000000,+0.00000000000000], [-5.90820611352046,+50.87477421602225], [-18.77412667908545,+19.53809802761889], [-20.81766720739410,+5.59196512255867], [-20.81766720739410,-5.59196512255867], [-18.77412667908545,-19.53809802761889], [-5.90820611352046,-50.87477421602225] )->using(0,1)); my $fx6 = rNfft1($x6); my $fx6_ref_input = $fx6_ref->slice('0:3'); ok_should_make_plan( all( approx( $fx6, $fx6_ref_input, approx_eps_double) ), "rfft basic test - native forward - 6long" ); $fx6 = rNfft1($x6->float); ok_should_make_plan( all( approx( $fx6, $fx6_ref_input->cfloat, approx_eps_single) ), "rNfft - native float forward - 6long" ) or diag "Got: $fx6\nExpected: ", $fx6_ref_input->cfloat; my $fx7 = rNfft1($x7); ok_should_make_plan( all( approx( $fx7, $fx7_ref->slice('0:3'), approx_eps_double) ), "rfft basic test - forward - 7long" ); my $x6_back = irfft1($fx6_ref_input, zeros(6) ); ok_should_make_plan( all( approx( $x6_back, $x6, approx_eps_double) ), "rfft basic test - native backward - 6long - output in arglist" ) or diag "Got: ($x6_back)\nExpected ($x6)"; $x6_back = irfft1($fx6_ref_input); ok_should_reuse_plan( all( approx( $x6_back, $x6, approx_eps_double) ), "rfft basic test - native backward - 6long - output returned" ) or diag "Got: ($x6_back)\nExpected ($x6)"; my $x7_back = irfft1($fx7_ref->slice('0:3'), zeros(7) ); ok_should_make_plan( all( approx( $x7, $x7_back, approx_eps_double) ), "rfft basic test - backward - 7long" ); # Currently a single plan is made for ALL the thread slices. These tests are # meant to exercise cases where this is a bad assumption. I.e. where some # slices have different alignment than others. For some reason I'm not seeing # these tests trigger any failures, so I'm not adding any plan-per-slice # logic. I'm leaving these tests here so that if error DO show up, we'll know # about them. # # I have two 1d threaded real ffts with odd size. I do this both with # single-precision and with double-precision # # octave code: # conj(fft((0:4).**2)') and conj(fft((5+(0:4)).**2)') my $x5_double = (sequence(10)**2)->reshape(5,2); my $x5_single = $x5_double->float; my $fx5_ref = PDL::czip(pdl( [ [30.00000000000000, + 0.00000000000000], [-5.26393202250021, +17.20477400588967], [-9.73606797749979, + 4.06149620291133], [-9.73606797749979, - 4.06149620291133], [-5.26393202250021, -17.20477400588967]], [ [255.0000000000000, + 0.0000000000000], [-30.2639320225002, +51.6143220176690], [-34.7360679774998, +12.1844886087340], [-34.7360679774998, -12.1844886087340], [-30.2639320225002, -51.6143220176690]])->using(0,1)); my $fx5_double = rfft1($x5_double); ok_should_make_plan( all( approx( $fx5_double, $fx5_ref->slice('0:2'), approx_eps_double) ), "rfft threaded double precision, odd number. May need 2 plans" ); my $fx5_single = rfft1($x5_single); ok_should_make_plan( all( approx( $fx5_single, $fx5_ref->slice('0:2'), approx_eps_single) ), "rfft threaded single precision, odd number. May need 2 plans" ) or diag "got=$fx5_single\nexpected=", $fx5_ref->slice('0:2'); } # real fft 2D checks { my $x64 = sequence(6,4)**1.1; my $x65 = sequence(6,5)**1.1; my $x74 = sequence(7,4)**1.1; my $x75 = sequence(7,5)**1.1; my $x64_ref = PDL::czip(pdl( [[360.467089670772225,+0.000000000000000],[-96.799893286902019,+103.123844918798000],[-99.028084766558067,+0.000000000000000],[-96.799893286902019,-103.123844918798000]], [[-15.988207248715064,+28.700459428988125],[0.351953528236895,-1.568072536701052],[0.894706789826859,-1.030374425555896],[1.617577583003193,-0.729750250118165]], [[-16.292902538160519,+9.547035210008918],[0.634591040891635,-0.785541150275863],[0.769581432672688,-0.358960145970985],[1.059338032001399,-0.013005581424496]], [[-16.334882296969155,+0.000000000000000],[0.826035615718165,-0.382043067657264],[0.750315495608504,+0.000000000000000],[0.826035615718165,+0.382043067657264]], [[-16.292902538160519,-9.547035210008918],[1.059338032001399,+0.013005581424496],[0.769581432672688,+0.358960145970985],[0.634591040891635,+0.785541150275863]], [[-15.988207248715064,-28.700459428988125],[1.617577583003193,+0.729750250118165],[0.894706789826859,+1.030374425555896],[0.351953528236895,+1.568072536701052]] )->using(0,1))->t; my $x65_ref = PDL::czip(pdl( [[581.176580714253532,+0.000000000000000],[-121.955619356066137,+180.656218696101774],[-126.233159544554283,+42.418129358506746],[-126.233159544554283,-42.418129358506746],[-121.955619356066137,-180.656218696101774]], [[-20.541598254340641,+36.647109644693884],[0.166836245337382,-1.944952627138933],[0.717118890606079,-1.295587872699933],[1.204146586049559,-0.971137876719541],[2.048534847787472,-0.720103497369214]], [[-20.868969490707826,+12.195700173607197],[0.644275726642125,-1.030653760454608],[0.749750248828911,-0.542566364608868],[0.913334629089297,-0.244339532151135],[1.274868845404011,+0.108769899029373]], [[-20.913788738508799,+0.000000000000000],[0.937317766511587,-0.564249965530514],[0.811766871540105,-0.147421276996557],[0.811766871540105,+0.147421276996557],[0.937317766511587,+0.564249965530514]], [[-20.868969490707826,-12.195700173607197],[1.274868845404011,-0.108769899029373],[0.913334629089297,+0.244339532151135],[0.749750248828911,+0.542566364608868],[0.644275726642125,+1.030653760454608]], [[-20.541598254340641,-36.647109644693884],[2.048534847787472,+0.720103497369214],[1.204146586049559,+0.971137876719541],[0.717118890606079,+1.295587872699933],[0.166836245337382,+1.944952627138933]] )->using(0,1))->t; my $x74_ref = PDL::czip(pdl( [[501.493394074383787,+0.000000000000000],[-133.959755823278442,+142.617956550047921],[-137.025115324985222,+0.000000000000000],[-133.959755823278442,-142.617956550047921]], [[-18.865669184773111,+40.763000085858437],[0.303558154663907,-2.139893880507619],[1.092794692703986,-1.466640717037794],[2.101733986260737,-1.130389132503664]], [[-19.293372490503472,+15.621007669166248],[0.660940909046470,-1.114601274898615],[0.916525999732684,-0.589874650055651],[1.356292422245748,-0.197237286031954]], [[-19.369109179404475,+4.468560279899923],[0.870724656176023,-0.640911587295642],[0.881225310656959,-0.170789696788245],[1.069971171773709,+0.261394699800439]], [[-19.369109179404475,-4.468560279899923],[1.069971171773709,-0.261394699800439],[0.881225310656959,+0.170789696788245],[0.870724656176023,+0.640911587295642]], [[-19.293372490503472,-15.621007669166248],[1.356292422245748,+0.197237286031954],[0.916525999732684,+0.589874650055651],[0.660940909046470,+1.114601274898615]], [[-18.865669184773111,-40.763000085858437],[2.101733986260737,+1.130389132503664],[1.092794692703986,+1.466640717037794],[0.303558154663907,+2.139893880507619]] )->using(0,1))->t; my $x75_ref = PDL::czip(pdl( [[807.475069006401100,+0.000000000000000],[-168.753063158798682,+249.823189325119387],[-174.641491905125804,+58.661407474458414],[-174.641491905125804,-58.661407474458414],[-168.753063158798682,-249.823189325119387]], [[-24.254907872104781,+52.050559234805071],[0.014162346363465,-2.632174401873477],[0.825892556600018,-1.808402549148552],[1.517875094286842,-1.417505033220904],[2.687499935923872,-1.159881805800431]], [[-24.714356335554129,+19.955552964153426],[0.626228017088200,-1.433208444983354],[0.856153542379253,-0.823419842535374],[1.123969929564233,-0.469253829892776],[1.658488397174277,-0.080552774016870]], [[-24.795144499563577,+5.709126404576390],[0.953872055406139,-0.885288945105194],[0.915476598274172,-0.361029355303055],[0.992228878787710,-0.012896599792635],[1.249581916098234,+0.447905615145083]], [[-24.795144499563577,-5.709126404576390],[1.249581916098234,-0.447905615145083],[0.992228878787710,+0.012896599792635],[0.915476598274172,+0.361029355303055],[0.953872055406139,+0.885288945105194]], [[-24.714356335554129,-19.955552964153426],[1.658488397174277,+0.080552774016870],[1.123969929564233,+0.469253829892776],[0.856153542379253,+0.823419842535374],[0.626228017088200,+1.433208444983354]], [[-24.254907872104781,-52.050559234805071],[2.687499935923872,+1.159881805800431],[1.517875094286842,+1.417505033220904],[0.825892556600018,+1.808402549148552],[0.014162346363465,+2.632174401873477]] )->using(0,1))->t; # forward my $fx64 = rfft2($x64); ok_should_make_plan( all( approx( $fx64, $x64_ref->slice('0:3'), approx_eps_double) ), "rfft 2d test - forward - 6,4" ); my $fx65 = rfft2($x65); ok_should_make_plan( all( approx( $fx65, $x65_ref->slice('0:3'), approx_eps_double) ), "rfft 2d test - forward - 6,5" ); my $fx74 = rfft2($x74); ok_should_make_plan( all( approx( $fx74, $x74_ref->slice('0:3'), approx_eps_double) ), "rfft 2d test - forward - 7,4" ); my $fx75 = rfft2($x75); ok_should_make_plan( all( approx( $fx75, $x75_ref->slice('0:3'), approx_eps_double) ), "rfft 2d test - forward - 7,5" ); # backward my $x64_back = irfft2($x64_ref->slice('0:3') ); ok_should_make_plan( all( approx( $x64, $x64_back, approx_eps_double) ), "rfft 2d test - backward - 6,4 - output returned" ); $x64_back = zeros(6,4); irfft2($x64_ref->slice('0:3'), $x64_back ); ok_should_reuse_plan( all( approx( $x64_back, $x64, approx_eps_double) ), "rfft 2d test - backward - 6,4 - output in arglist" ); my $x65_back = irfft2($x65_ref->slice('0:3') ); ok_should_make_plan( all( approx( $x65_back, $x65, approx_eps_double) ), "rfft 2d test - backward - 6,5 - output returned" ); $x65_back = zeros(6,5); irfft2($x65_ref->slice('0:3'), $x65_back ); ok_should_reuse_plan( all( approx( $x65_back, $x65, approx_eps_double) ), "rfft 2d test - backward - 6,5 - output in arglist" ); my $x74_back = irfft2($x74_ref->slice('0:3'), zeros(7,4) ); ok_should_make_plan( all( approx( $x74_back, $x74, approx_eps_double) ), "rfft 2d test - backward - 7,4" ); my $x75_back = irfft2($x75_ref->slice('0:3'), zeros(7,5) ); ok_should_make_plan( all( approx( $x75_back, $x75, approx_eps_double) ), "rfft 2d test - backward - 7,5" ); } # real fft sanity checks { { eval { rfft1( sequence(6) ) }; ok_should_reuse_plan( ! $@, "real ffts shouldn't work inplace - baseline forward" ); eval { rfft1( inplace sequence(6) ) }; ok( $@, "real ffts shouldn't work inplace - forward" ); eval { rNfft1( sequence(6) ) }; ok_should_reuse_plan( ! $@, "real->N ffts shouldn't work inplace - baseline forward" ); eval { rNfft1( inplace sequence(6) ) }; like $@, qr/in-place real FFTs are not supported/, "real->N ffts shouldn't work inplace - forward"; } { eval { irfft1( sequence(cdouble,4) ) }; ok_should_reuse_plan( ! $@, "real ffts shouldn't work inplace - baseline backward" ); eval { irfft1( inplace sequence(cdouble,4) ) }; like $@, qr/in-place real FFTs are not supported/, "real ffts shouldn't work inplace - backward"; } { eval { rfft1( sequence(7), sequence(cdouble,4) ) }; ok_should_reuse_plan( ! $@, "real fft dims - baseline"); eval { rfft1( sequence(7), sequence(cdouble,5) ) }; like $@, qr/mismatched first dimension/, "real fft dimensionality 1"; eval { rfft1( sequence(7), sequence(cdouble,3) ) }; like $@, qr/mismatched first dimension/, "real fft dimensionality 2"; eval { rNfft1( sequence(7), sequence(4) ) }; like $@, qr/produces complex output/, "real->N fft type"; eval { rNfft1( sequence(7), sequence(cdouble,4) ) }; ok_should_reuse_plan( ! $@, "real fft dims - baseline"); eval { rNfft1( sequence(7), sequence(cdouble,5) ) }; like $@, qr/mismatched first dimension/, "real->N fft dimensionality 1"; eval { rNfft1( sequence(7), sequence(cdouble,3) ) }; like $@, qr/mismatched first dimension/, "real->N fft dimensionality 2"; } { eval { rfft4( sequence(5,3,4,4) ) }; ok_should_make_plan( ! $@, "real dimensionality baseline" ); eval { rfft4( sequence(5,4,4) ) }; ok( $@, "real dimensionality: too few dimensions should fail" ); eval { irfft4( sequence(cdouble,5,3,4,4) ) }; ok_should_make_plan( ! $@, "real-backward dimensionality baseline" ); eval { irfft4( sequence(5,4,4) ) }; ok( $@, "real-backward dimensionality: too few dimensions should fail" ); } { eval { rfft4( sequence(5,3,4,4), sequence(cdouble,3,3,4,4) ) }; ok_should_reuse_plan( ! $@, "real dimensionality baseline - more explicit" ); eval { rfft4( sequence(5,3,4,4), sequence(cdouble,3,3,4,4,3) ) }; ok_should_reuse_plan( ! $@, "real dimensionality baseline - extra dims should be ok" ); eval { rfft4( sequence(5,3,4,4), sequence(cdouble,3,3,3,4,3) ) }; ok( $@, "real dimensionality - output should look like complex numbers" ); eval { rfft4( sequence(5,3,4,4), sequence(cdouble,3,3,4,3) ) }; ok( $@, "real dimensionality - different dims should break 1" ); eval { rfft4( sequence(5,3,4,4), sequence(cdouble,3,3,4,5) ) }; ok( $@, "real dimensionality - different dims should break 2" ); eval { rfft4( sequence(4,3,4,4), sequence(cdouble,3,3,4,4) ) }; ok_should_make_plan( !$@, "real dimensionality - slightly different complex dims still ok" ); eval { rfft4( sequence(6,3,4,4), sequence(cdouble,3,3,4,4) ) }; ok( $@, "real dimensionality - too different complex dims should break" ); } { eval { irfft4( sequence(cdouble,3,3,4,4) ) }; ok_should_make_plan( ! $@, "real-backward dimensionality baseline 1" ); eval { irfft4( sequence(cdouble,3,3,4,4), sequence(5,3,4,4) ) }; ok_should_make_plan( ! $@, "real-backward dimensionality baseline 2" ); eval { irfft4( sequence(cdouble,3,3,4,4), sequence(5,3,4,4,3) ) }; ok_should_reuse_plan( ! $@, "real-backward dimensionality baseline - extra dims should be ok" ); eval { irfft4( sequence(3,3,3,4,4,3), sequence(5,3,4,4) ) }; ok( $@, "real-backward dimensionality - input should look like complex numbers" ); eval { irfft4( sequence(cdouble,3,3,4,3), sequence(5,3,4,4) ) }; ok( $@, "real-backward dimensionality - different dims should break 1" ); eval { irfft4( sequence(cdouble,3,3,4,5), sequence(5,3,4,4) ) }; ok( $@, "real-backward dimensionality - different dims should break 2" ); eval { irfft4( sequence(cdouble,3,3,4,4), sequence(4,3,4,4) ) }; ok_should_reuse_plan( !$@, "real-backward dimensionality - slightly different complex dims still ok" ); eval { irfft4( sequence(cdouble,3,3,4,4), sequence(6,3,4,4) ) }; ok( $@, "real-backward dimensionality - too different complex dims should break" ); } } # make sure the routines do not damage their input { # I use all-new size to make sure a planning phase takes place. This checks # the planning phase for preserving its input also my $xorig = sequence(6,7,8); my $Xorig = PDL::czip(sequence(2,6,7,8)->using(0,1)); my $X = $Xorig->copy; fft2($X); ok_should_make_plan( all( approx( $X, $Xorig, approx_eps_double) ), "2D forward FFT preserves its input" ); $X = $Xorig->copy; ifft2($X); ok_should_make_plan( all( approx( $X, $Xorig, approx_eps_double) ), "2D backward FFT preserves its input" ); my $x = $xorig->copy; rfft3($x); ok_should_make_plan( all( approx( $x, $xorig, approx_eps_double) ), "3D real forward FFT preserves its input" ); $X = $Xorig->copy; irfft3($X); ok_should_make_plan( all( approx( $X, $Xorig, approx_eps_double) ), "3D real backward FFT preserves its input" ); } # Check parameterized operation { my $a = PDL::czip((sequence(2,4,4,4)==0)->using(0,1)); my $b = fftn($a,1); my $btemplate = zeroes($a); $btemplate->re->slice(':,(0),(0)') .= 1; ok_should_make_plan( all( approx( $b, $btemplate, approx_eps_double ) ), "parameterized forward complex FFT works (1d on a 1+3d var)" ); ok_should_reuse_plan( all( approx( $a->fftn(1), $btemplate, approx_eps_double ) ), "parameterized forward complex FFT works (1d on a 1+3d var) - method" ); $b = fftn($a,2); $btemplate .= 0; $btemplate->re->slice(':,:,(0)') .= 1; ok_should_make_plan( all( approx( $b, $btemplate, approx_eps_double ) ), "parameterized forward complex FFT works (2d on a 1+3d var)" ); $b = fftn($a,3); $btemplate .= 1; ok_should_make_plan( all( approx( $b, $btemplate, approx_eps_double ) ), "parameterized forward complex FFT works (3d on a 1+3d var)" ); # inverse on 2d -- should leave the 3rd dimension alone my $c = ifftn($b,2); my $ctemplate= zeroes($c); $ctemplate->re->slice('(0),(0)') .= 1; ok_should_make_plan( all( approx( $c, $ctemplate, approx_eps_double ) ), "parameterized reverse complex FFT works (2d on a 1+3d var)" ); $a = sequence(4,4,4)==0; $b = rfftn($a,1); $btemplate = zeroes($b); $btemplate->slice(':,(0),(0)') .= 1; ok_should_make_plan( all( approx( $b, $btemplate, approx_eps_double ) ), "parameterized forward real FFT works (1d on a 3d var)" ); # inverse on 2d -- should be a normalized forward on the non-transformed direction $c = irfftn($b,2); $ctemplate = zeroes($a); $ctemplate->slice('(0),:,(0)') .= 0.25; ok_should_make_plan( all( approx( $c, $ctemplate, approx_eps_double) ), "parameterized reverse real FFT works (2d on a 3d var)" ); } # Alignment checks. Here I try to fft purposely-unaligned ndarrays to make sure # that PDL::FFTW3 both makes a new plan and computes the data correctly. I only # create an input offset # # This test isn't written perfectly. It assumes that $x->copy is aligned at 16 # bytes, which is true on my amd64 box, but is not true in general. # Additionally, FFTW crashes if alignment is particularly bad, so I disable # these tests. if(0) { my $x = PDL::czip(sequence(10), sequence(10)**2); # from octave: conj( fft( (0:9) + i* ((0:9).**2) )' ) my $Xref = PDL::czip(pdl( [45.0000000000000,+285.0000000000000], [-158.8841768587627,+17.7490974608742], [-73.8190960235587,-28.6459544426446], [-41.3271264002680,-38.7279671349711], [-21.2459848116453,-42.8475374738350], [-5.0000000000000,-45.0000000000000], [11.2459848116453,-46.0967344361641], [31.3271264002680,-45.9933924150247], [63.8190960235587,-42.4097736473563], [148.8841768587627,-13.0277379108784] )->using(0,1)); my $length = length ${$x->get_dataref}; for my $offset (4,8) { # this assumes a 16-aligned ndarray is created. This is true on my amd64 box my $x_offset = $x->copy; # create an offset in the data inside the ndarray ${$x_offset->get_dataref} .= ' ' x $offset; substr(${$x_offset->get_dataref}, 0, $offset) = ""; substr(${$x_offset->get_dataref}, 0) = ${$x->get_dataref}; $x_offset->upd_data(); ok_should_make_plan( all( approx( fft1($x_offset), $Xref, approx_eps_double) ), "Basic 1D complex FFT - unaligned $offset bytes" ); } } done_testing; sub ok_should_make_plan { my ($value, $planname) = @_; local $Test::Builder::Level = $Test::Builder::Level + 1; my $ret = ok( $value, $planname ); check_new_plan( $planname ); $ret; } sub ok_should_reuse_plan { my ($value, $planname) = @_; local $Test::Builder::Level = $Test::Builder::Level + 1; my $ret = ok( $value, $planname ); check_reused_plan( $planname ); $ret; } sub check_new_plan { my $planname = shift; SKIP: { skip "Plan creation checks disabled because pdl memory may be unaligned", 1 unless $do_check_plan_creations; local $Test::Builder::Level = $Test::Builder::Level + 1; ok( $PDL::FFTW3::_Nplans == $Nplans+1, "$planname: should make a new plan" ); } $Nplans = $PDL::FFTW3::_Nplans; } sub check_reused_plan { my $planname = shift; SKIP: { skip "Plan creation checks disabled because pdl memory may be unaligned", 1 unless $do_check_plan_creations; local $Test::Builder::Level = $Test::Builder::Level + 1; ok( $PDL::FFTW3::_Nplans == $Nplans, "$planname: should reuse an existing plan" ); } $Nplans = $PDL::FFTW3::_Nplans; } PDL-FFTW3-0.203/GENERATED/0000755000175000017500000000000014742523116014215 5ustar osboxesosboxesPDL-FFTW3-0.203/GENERATED/PDL/0000755000175000017500000000000014742523117014635 5ustar osboxesosboxesPDL-FFTW3-0.203/GENERATED/PDL/FFTW3.pm0000644000175000017500000007137014742523117016034 0ustar osboxesosboxes# # GENERATED WITH PDL::PP from fftw3.pd! Don't modify! # package PDL::FFTW3; our @EXPORT_OK = qw( fft1 ifft1 rfft1 rNfft1 irfft1 fft2 ifft2 rfft2 rNfft2 irfft2 fft3 ifft3 rfft3 rNfft3 irfft3 fft4 ifft4 rfft4 rNfft4 irfft4 fft5 ifft5 rfft5 rNfft5 irfft5 fft6 ifft6 rfft6 rNfft6 irfft6 fft7 ifft7 rfft7 rNfft7 irfft7 fft8 ifft8 rfft8 rNfft8 irfft8 fft9 ifft9 rfft9 rNfft9 irfft9 fft10 ifft10 rfft10 rNfft10 irfft10 fftn ifftn rfftn irfftn ); our %EXPORT_TAGS = (Func=>\@EXPORT_OK); use PDL::Core; use PDL::Exporter; use DynaLoader; our $VERSION = '0.203'; our @ISA = ( 'PDL::Exporter','DynaLoader' ); push @PDL::Core::PP, __PACKAGE__; bootstrap PDL::FFTW3 $VERSION; #line 25 "fftw3.pd" #line 0 "README.pod" =head1 NAME PDL::FFTW3 - PDL interface to the Fastest Fourier Transform in the West v3 =head1 SYNOPSIS use PDL; use PDL::FFTW3; use PDL::Graphics::Gnuplot; # Basic functionality my $x = sin( sequence(100) * 2.0 ) + 2.0 * cos( sequence(100) / 3.0 ); my $F = rfft1( $x ); gplot( with => 'lines', $F->abs ); =====> 8000 ++------------+-------------+------------+-------------+------------++ + + + + + + | | | * | 7000 ++ * ++ | * | | * | | * | | * | 6000 ++ * ++ | * | | * | | * | 5000 ++ * ++ | * | | * | | ** | 4000 ++ ** ++ | ** | | * * | | * * | | * * | 3000 ++ * * ++ | * * | | * * | | * * * | 2000 ++ * * * ++ | * * * | | * * ** | | * * ** | | * * ** | 1000 ++ * * * * ++ | * * * * | | ** * * * | + * * + + + * * + + 0 ****-------*********************************--************************ 0 10 20 30 40 50 # Correlation of two real signals # two signals offset by 30 units my $x = sequence(100); my $y1 = exp( 0.2*($x - 20.5) ** (-2.0) ); my $y2 = exp( 0.2*($x - 50.5) ** (-2.0) ); # compute the correlation my $F12 = rfft1( cat($y1,$y2) ); my $corr = irfft1( $F12(:,(1)) * $F12(:,(0))->conj ); # and find the peak say maximum_ind($corr); =====> 30 =head1 DESCRIPTION This is a PDL binding to version 3 of the FFTW library. Supported are complex <-> complex and real <-> complex FFTs. =head2 NB to install wget http://www.fftw.org/fftw-3.3.4.tar.gz tar xvf fftw-3.3.4.tar.gz cd fftw-3.3.4/ ./configure --prefix=/usr --enable-threads --enable-float --enable-shared --with-pic make all install install-pkgconfigDATA make clean ./configure --prefix=/usr --enable-threads --enable-shared --with-pic make all install install-pkgconfigDATA This will give you both fftw3f (first chunk) and fftw3 (second). =head2 Supported operations This module computes the Discrete Fourier Transform. In its most basic form, this transform converts a vector of complex numbers in the time domain into another vector of complex numbers in the frequency domain. These complex <-> complex transforms are supported with C functions for a rank-C transform. The opposite effect (transform data in the frequency domain back to the time domain) can be achieved with the C functions. A common use case is to transform purely-real data. This data has 0 for its complex component, and FFTW can take advantage of this to compute the FFT faster and using less memory. Since a Fourier Transform of a real signal has an even real part and an odd imaginary part, only 1/2 of the spectrum is needed. These forward real -> complex transforms are supported with the C functions. The backward version of this transform is complex -> real and is supported with the C functions. =head2 Basic usage details Arbitrary C-dimensional transforms are supported. All functions exported by this module have the C in their name, so for instance a complex <-> complex 3D forward transform is computed with the C function. The rank I be specified in this way; there is no function called simply C. In-place operation is supported for complex <-> complex functions, but not the real ones (real function don't have mathing dimensionality of the input and output). An in-place transform of C<$x> can be computed with fft1( $x->inplace ); All the functions in this module support PDL threading. For instance, if we have 4 different image ndarrays C<$a>, C<$b>, C<$c>, C<$d> and we want to compute their 2D FFTs at the same time, we can say my $ABCD_transformed = rfft2( PDL::cat( $a, $b, $c, $d) ); This takes advantage of PDL's automatic parallelization, if appropriate (See L). =head2 Data formats FFTW supports single and double-precision floating point numbers directly. If possible, the PDL input will be used as-is. If not, a type conversion will be made to use the lowest-common type. So as an example, the following will perform a single-precision floating point transform (and return data of that type). fft1( $x->byte ) As of 0.20, this module expects complex numbers to be stored as "native complex" types (C, C). Complex outputs will also be native complex. Generally, the sizes of the input and the output must match. This is completely true for the complex <-> complex transforms: the output will have the same size and the input, and an error will result if this isn't possible for some reason. This is a little bit more involved for the real <-> complex transforms. If I'm transforming a real 3D vector of dimensions C, I will get a complex output of dimensions C. The C is there because the input was real. The first dimension is always the one that gets the C. This is described in detail in section 2.4 of the FFTW manual. Note that given a real input, the dimensionality of the complex transformed output is unambiguous. However, this is I true for the backward transform. For instance, a 1D inverse transform of a vector of 10 complex numbers can produce real output of either 18 or 19 elements (because C and C). I. Thus Cdim(0) == 18> is true. If we want the odd-sized output, we have to explicitly pass this into the function like this: irfft1( sequence(cdouble,10), zeros(19) ) Here I create a new output ndarray with the C function; C then fills in this ndarray with the result of the computation. This module validates all of its input, so only 18 and 19 are valid here. An error will be thrown if you try to pass in C. This all means that the following will produce surprising results if C<$x-Edim(0)> isn't even irfft1( rfft1( $x ) ) =head2 FFT normalization Following the widest-used convention for discrete Fourier transforms, this module normalizes the inverse transform (but not the forward transform) by dividing by the number of elements in the data set, so that ifft1( fft1( $x ) ) is a slow approximate no-op, if C<$x> is well-behaved. This is different from the behavior of the underlying FFTW3 library itself, but more consistent with other FFT packages for popular analysis languages including PDL. =head1 FUNCTIONS =head2 fftX (fft1, fft2, fft3, ..., fftn) The basic complex <-> complex FFT. You can pass in the rank as a parameter with the C form, or append the rank to the function name for ranks up to 9. These functions all take one input ndarray and one output ndarray. The dimensions of the input and the output are identical. The output parameter is optional and, if present, must be the last argument. If the output ndarray is passed in, the user I make sure the dimensions match. As of 0.20, inputs must be "native complex" data. Any type other than C or C will be converted in the normal PP way. The fftn form takes a minimum of two arguments: the PDL to transform, and the number of dimensions to transform as a separate argument. The following are equivalent: $X = fftn( $x, 1 ); $X = fft1( $x ); fft1( $x, my $X = $x->zeros ); =head2 ifftX (ifft1, ifft2, ifft3, ..., ifftn) The basic, properly normalized, complex <-> complex backward FFT. Everything is exactly like in the C functions, except the inverse transform is computed and normalized, so that (for example) ifft1( fft1 ( $x ) ) is a good approximation of C<$x> itself. =head2 rfftX (rfft1, rfft2, rfft3, ..., rfftn) The real -> complex FFT. You can pass in the rank with the C form, or append the rank to the function name for ranks up to 9. These functions all take one input ndarray and one output ndarray. The dimensions of the input and the output are not identical, but are related as described in L. The output can be passed in as the last argument, if desired. If the output ndarray is passed in, the user I make sure the dimensions match. In the C form, the rank is the second argument. The following are equivalent: $X = rfftn( $x, 1 ); $X = rfft1( $x ); rfft1( $x, my $X = $x->zeroes ); As of 0.20, only returns native-complex data. Support for PDL::Complex has been removed. =head2 rNfftX (rNfft1, rNfft2, rNfft3, ..., rNfftn) As of 0.20, just an alias for rfftX, etc. =head2 irfftX (irfft1, irfft2, irfft3, ..., irfftn) The complex -> real inverse FFT. You can pass in the rank with the C form, or append the rank to the function name for ranks up to 9. Argument passing and interpretation is as described in C above. Please read L for details about dimension interpretation. There's an ambiguity about the output dimensionality, which is described in that section. =head1 AUTHOR Dima Kogan, C<< >>; contributions from Craig DeForest, C<< >>. =head1 LICENSE AND COPYRIGHT Copyright 2013 Dima Kogan and Craig DeForest. This program is free software; you can redistribute it and/or modify it under the same terms as PDL. =cut use strict; use warnings; #line 226 "fftw3.pd" use PDL::Types; use List::Util 'reduce'; use threads::shared; # When I compute an FFTW plan, it goes here. # This is :shared so that it can be used with Perl threads. my %existingPlans :shared; # these are for the unit tests our $_Nplans = 0; our $_last_do_double_precision; # This is a function that sits between the user's call into this module and the # PP-generated internals. Specifically, this function is called BEFORE any PDL # threading happens. Here I make sure the FFTW plan exists, or if it doesn't, I # make it. Thus the PP-based internals can safely assume that the plan exists sub __fft_internal { my $thisfunction = shift; my ($do_inverse_fft, $is_real_fft, $rank) = $thisfunction =~ /^(i?)(r?)N?.*fft([0-9]+)/; # first I parse the variables. This is a very direct translation of what PP # does normally. Plan-creation has to be outside of PP, so I must re-do this # here my $Nargs = scalar @_; my ($in, $out); if ( $Nargs == 2 ) { # all variables on stack, read in output and temp vars ($in, $out) = map {defined $_ ? PDL::Core::topdl($_) : $_} @_; } elsif ( $Nargs == 1 ) { $in = PDL::Core::topdl $_[0]; if ( $in->is_inplace ) { barf <set_inplace(0); } else { $out = PDL::null(); } } else { barf( <isnull ) { my ($type, @dims) = getOutArgs($in, $is_real_fft, $do_inverse_fft); $out->set_datatype($type->enum); $out->setdims(\@dims); $out->make_physical; } validateArguments( $rank, $is_real_fft, $do_inverse_fft, $thisfunction, $in, $out ); # I need to physical-ize the ndarrays before I make a plan. Again, normally PP # does this, but to make sure alignments match, I need to do this myself, now $in->make_physical; $out->make_physical; my $plan = getPlan( $thisfunction, $rank, $is_real_fft, $do_inverse_fft, $in, $out ); barf "$thisfunction couldn't make a plan. Giving up\n" unless defined $plan; my $is_native = !$in->type->real; # native complex # I now have the arguments and the plan. Go! my $internal_function = 'PDL::__'; $internal_function .= !$is_real_fft ? 'N' : ($is_native && $do_inverse_fft) ? 'irN' : $do_inverse_fft ? barf("irfft no longer supports PDL::Complex") : 'rN'; $internal_function .= "fft$rank"; eval { no strict 'refs'; $internal_function->( $in, $out, $plan ) }; barf $@ if $@; $out; } sub getOutArgs { my ($in, $is_real_fft, $do_inverse_fft) = @_; my @dims = $in->dims; my $is_native = !$in->type->real; my $out_type = $in->type; if ( !$is_real_fft ) { # complex fft. Output is the same size as the input. } elsif ( !$do_inverse_fft ) { # forward real fft $dims[0] = int($dims[0]/2)+1; $out_type = typeWithComplexity(getPrecision($out_type), 1); } else { # backward real fft # # there's an ambiguity here. I want int($out->dim(0)/2) + 1 == $in->dim(1), # however this could mean that # $out->dim(0) = 2*$in->dim(1) - 2 # or # $out->dim(0) = 2*$in->dim(1) - 1 # # WITHOUT ANY OTHER INFORMATION, I ASSUME EVEN INPUT SIZES, SO I ASSUME # $out->dim(0) = 2*$in->dim(1) - 2 if ($is_native) { $out_type = ($out_type == cfloat) ? float : double; } else { shift @dims; } $dims[0] = 2*($dims[0]-1); } ($out_type, @dims); } sub validateArguments { my ($rank, $is_real_fft, $do_inverse_fft, $thisfunction, $in, $out) = @_; for my $arg ( $in, $out ) { barf <isa('PDL'); $thisfunction arguments must be of type 'PDL'. Instead I got an arg of type '$type'. Giving up. EOF } # validate dimensionality of the ndarrays my @inout = ($in, $out); for my $iarg ( 0..1 ) { my $arg = $inout[$iarg]; if( $arg->isnull ) { barf "$thisfunction: don't know what to do with a null input. Giving up"; } if( !$is_real_fft ) { validateArgumentDimensions_complex( $rank, $thisfunction, $arg); } else { validateArgumentDimensions_real( $rank, $do_inverse_fft, $thisfunction, $iarg, $arg); } } # we have an explicit output ndarray we're filling in. Make sure the # input/output dimensions match up if ( !$is_real_fft ) { matchDimensions_complex($thisfunction, $rank, $in, $out); } else { matchDimensions_real($thisfunction, $rank, $do_inverse_fft, $in, $out); } } sub validateArgumentDimensions_complex { my ( $rank, $thisfunction, $arg ) = @_; barf "Tried to compute a complex FFT, but non-native-complex argument given" if $arg->type->real; my $dims_cmp = $arg->ndims; barf <type->real; # native complex # real FFT. Forward transform takes in real and spits out complex; # backward transform does the reverse if (!!$do_inverse_fft == !!($iarg == 0)) { # need complex for this my ($verb, $var, $reason) = ($iarg == 0) ? qw(takes input) : qw(produces output); if ( ($iarg == 1 && !$is_native) || ($iarg == 0 && !$is_native) ) { $reason = "\$$var should be native-complex"; } elsif (!$is_native && $arg->dim(0) != 2) { $reason = "\$$var->dim(0) == 2 should be true"; } barf <info]}: $arg). Giving up. EOF } my ($min_dimensionality, $var) = ($rank, $iarg == 0 ? 'input' : 'output'); if ( $arg->ndims < $min_dimensionality ) { barf <dim($idim) != $out->dim($idim) ) { barf <dim(1) should be int($input->dim(0)/2) + 1 (Section 2.4 of # the FFTW3 documentation) ($varname1, $varname2, $var1, $var2) = (qw(input output), $in, $out); } else { # Backward FFT. The input is complex, the output is real. ($varname1, $varname2, $var1, $var2) = (qw(output input), $out, $in); } barf <dim(0)/2) + 1 != $var2->dim(0); $thisfunction: mismatched first dimension: \$$varname2->dim(0) == int(\$$varname1->dim(0)/2) + 1 wasn't true. $varname1: @{[$var1->info]} $varname2: @{[$var2->info]} Giving up. EOF for my $idim (1..$rank-1) { if ( $var1->dim($idim) != $var2->dim($idim) ) { barf <isnull ) { if( $$in->type < float ) { forceType( $in, (float) ); } } else { # I'm given an output. Make sure this is of a type I can work with, # otherwise give up my $out_type = $$out->type; barf <type; my $in_precision = getPrecision($in_type); my $out_precision = getPrecision($out_type); return if $in_precision == $out_precision; forceType( $in, typeWithComplexity($out_precision, !$in_type->real) ); forceType( $out, typeWithComplexity($out_precision, !$out_type->real) ); } } sub typeWithComplexity { my ($precision, $complex) = @_; $complex ? ($precision == 1 ? cfloat : cdouble) : $precision == 1 ? float : double; } sub getPrecision { my ($type) = @_; ($type <= float || $type == cfloat) ? 1 : # float 2; # double } sub forceType { my ($x, $type) = @_; $$x = convert( $$x, $type ) unless $$x->type == $type; } sub getPlan { my ($thisfunction, $rank, $is_real_fft, $do_inverse_fft, $in, $out) = @_; # I get the plan ID, check if I already have a plan, and make a new plan if I # don't already have one my @dims = ((!$is_real_fft || !$do_inverse_fft) ? $in : $out)->dims; # FFT dimensionality my $Nslices = reduce {$a*$b} 1, splice(@dims, $rank); my $do_double_precision = ($in->get_datatype == $PDL_F || $in->get_datatype == $PDL_CF) ? 0 : 1; $_last_do_double_precision = $do_double_precision; my $do_inplace = is_same_data( $in, $out ); # I compute a single plan for the whole set of thread slices. I make a # worst-case plan, so I find the worst-aligned thread slice and plan off of # it. So if $Nslices>1 then the worst-case alignment is the worse of (1st, # 2nd) slices my $in_alignment = get_data_alignment_pdl( $in ); my $out_alignment = get_data_alignment_pdl( $out ); my $stride_bytes = ($do_double_precision ? 8 : 4) * reduce {$a*$b} @dims; if( $Nslices > 1 ) { my $in_alignment_2nd = get_data_alignment_int($in_alignment + $stride_bytes); my $out_alignment_2nd = get_data_alignment_int($out_alignment + $stride_bytes); $in_alignment = $in_alignment_2nd if $in_alignment_2nd < $in_alignment; $out_alignment = $out_alignment_2nd if $out_alignment_2nd < $out_alignment; } my $planID = join('_', $thisfunction, $do_double_precision, $do_inplace, $in_alignment, $out_alignment, @dims); if ( !exists $existingPlans{$planID} ) { lock(%existingPlans); $existingPlans{$planID} = compute_plan( \@dims, $do_double_precision, $is_real_fft, $do_inverse_fft, $in, $out, $in_alignment, $out_alignment ); $_Nplans++; } return $existingPlans{$planID}; } #line 580 "fftw3.pd" sub fft1 { __fft_internal( "fft1",@_ ); } *PDL::fft1 = \&fft1; sub ifft1 { my $a = __fft_internal( "ifft1", @_ ); $a /= $a->shape->slice('0:0')->prodover; $a; } *PDL::ifft1 = \&ifft1; sub rfft1 { __fft_internal( "rfft1", @_ ); } *PDL::rfft1 = \&rfft1; sub rNfft1 { __fft_internal( "rNfft1", @_ ); } *PDL::rNfft1 = \&rNfft1; sub irfft1 { my $a = __fft_internal( "irfft1", @_ ); $a /= $a->shape->slice('0:0')->prodover; $a; } *PDL::irfft1 = \&irfft1; #line 580 "fftw3.pd" sub fft2 { __fft_internal( "fft2",@_ ); } *PDL::fft2 = \&fft2; sub ifft2 { my $a = __fft_internal( "ifft2", @_ ); $a /= $a->shape->slice('0:1')->prodover; $a; } *PDL::ifft2 = \&ifft2; sub rfft2 { __fft_internal( "rfft2", @_ ); } *PDL::rfft2 = \&rfft2; sub rNfft2 { __fft_internal( "rNfft2", @_ ); } *PDL::rNfft2 = \&rNfft2; sub irfft2 { my $a = __fft_internal( "irfft2", @_ ); $a /= $a->shape->slice('0:1')->prodover; $a; } *PDL::irfft2 = \&irfft2; #line 580 "fftw3.pd" sub fft3 { __fft_internal( "fft3",@_ ); } *PDL::fft3 = \&fft3; sub ifft3 { my $a = __fft_internal( "ifft3", @_ ); $a /= $a->shape->slice('0:2')->prodover; $a; } *PDL::ifft3 = \&ifft3; sub rfft3 { __fft_internal( "rfft3", @_ ); } *PDL::rfft3 = \&rfft3; sub rNfft3 { __fft_internal( "rNfft3", @_ ); } *PDL::rNfft3 = \&rNfft3; sub irfft3 { my $a = __fft_internal( "irfft3", @_ ); $a /= $a->shape->slice('0:2')->prodover; $a; } *PDL::irfft3 = \&irfft3; #line 580 "fftw3.pd" sub fft4 { __fft_internal( "fft4",@_ ); } *PDL::fft4 = \&fft4; sub ifft4 { my $a = __fft_internal( "ifft4", @_ ); $a /= $a->shape->slice('0:3')->prodover; $a; } *PDL::ifft4 = \&ifft4; sub rfft4 { __fft_internal( "rfft4", @_ ); } *PDL::rfft4 = \&rfft4; sub rNfft4 { __fft_internal( "rNfft4", @_ ); } *PDL::rNfft4 = \&rNfft4; sub irfft4 { my $a = __fft_internal( "irfft4", @_ ); $a /= $a->shape->slice('0:3')->prodover; $a; } *PDL::irfft4 = \&irfft4; #line 580 "fftw3.pd" sub fft5 { __fft_internal( "fft5",@_ ); } *PDL::fft5 = \&fft5; sub ifft5 { my $a = __fft_internal( "ifft5", @_ ); $a /= $a->shape->slice('0:4')->prodover; $a; } *PDL::ifft5 = \&ifft5; sub rfft5 { __fft_internal( "rfft5", @_ ); } *PDL::rfft5 = \&rfft5; sub rNfft5 { __fft_internal( "rNfft5", @_ ); } *PDL::rNfft5 = \&rNfft5; sub irfft5 { my $a = __fft_internal( "irfft5", @_ ); $a /= $a->shape->slice('0:4')->prodover; $a; } *PDL::irfft5 = \&irfft5; #line 580 "fftw3.pd" sub fft6 { __fft_internal( "fft6",@_ ); } *PDL::fft6 = \&fft6; sub ifft6 { my $a = __fft_internal( "ifft6", @_ ); $a /= $a->shape->slice('0:5')->prodover; $a; } *PDL::ifft6 = \&ifft6; sub rfft6 { __fft_internal( "rfft6", @_ ); } *PDL::rfft6 = \&rfft6; sub rNfft6 { __fft_internal( "rNfft6", @_ ); } *PDL::rNfft6 = \&rNfft6; sub irfft6 { my $a = __fft_internal( "irfft6", @_ ); $a /= $a->shape->slice('0:5')->prodover; $a; } *PDL::irfft6 = \&irfft6; #line 580 "fftw3.pd" sub fft7 { __fft_internal( "fft7",@_ ); } *PDL::fft7 = \&fft7; sub ifft7 { my $a = __fft_internal( "ifft7", @_ ); $a /= $a->shape->slice('0:6')->prodover; $a; } *PDL::ifft7 = \&ifft7; sub rfft7 { __fft_internal( "rfft7", @_ ); } *PDL::rfft7 = \&rfft7; sub rNfft7 { __fft_internal( "rNfft7", @_ ); } *PDL::rNfft7 = \&rNfft7; sub irfft7 { my $a = __fft_internal( "irfft7", @_ ); $a /= $a->shape->slice('0:6')->prodover; $a; } *PDL::irfft7 = \&irfft7; #line 580 "fftw3.pd" sub fft8 { __fft_internal( "fft8",@_ ); } *PDL::fft8 = \&fft8; sub ifft8 { my $a = __fft_internal( "ifft8", @_ ); $a /= $a->shape->slice('0:7')->prodover; $a; } *PDL::ifft8 = \&ifft8; sub rfft8 { __fft_internal( "rfft8", @_ ); } *PDL::rfft8 = \&rfft8; sub rNfft8 { __fft_internal( "rNfft8", @_ ); } *PDL::rNfft8 = \&rNfft8; sub irfft8 { my $a = __fft_internal( "irfft8", @_ ); $a /= $a->shape->slice('0:7')->prodover; $a; } *PDL::irfft8 = \&irfft8; #line 580 "fftw3.pd" sub fft9 { __fft_internal( "fft9",@_ ); } *PDL::fft9 = \&fft9; sub ifft9 { my $a = __fft_internal( "ifft9", @_ ); $a /= $a->shape->slice('0:8')->prodover; $a; } *PDL::ifft9 = \&ifft9; sub rfft9 { __fft_internal( "rfft9", @_ ); } *PDL::rfft9 = \&rfft9; sub rNfft9 { __fft_internal( "rNfft9", @_ ); } *PDL::rNfft9 = \&rNfft9; sub irfft9 { my $a = __fft_internal( "irfft9", @_ ); $a /= $a->shape->slice('0:8')->prodover; $a; } *PDL::irfft9 = \&irfft9; #line 580 "fftw3.pd" sub fft10 { __fft_internal( "fft10",@_ ); } *PDL::fft10 = \&fft10; sub ifft10 { my $a = __fft_internal( "ifft10", @_ ); $a /= $a->shape->slice('0:9')->prodover; $a; } *PDL::ifft10 = \&ifft10; sub rfft10 { __fft_internal( "rfft10", @_ ); } *PDL::rfft10 = \&rfft10; sub rNfft10 { __fft_internal( "rNfft10", @_ ); } *PDL::rNfft10 = \&rNfft10; sub irfft10 { my $a = __fft_internal( "irfft10", @_ ); $a /= $a->shape->slice('0:9')->prodover; $a; } *PDL::irfft10 = \&irfft10; #line 608 "fftw3.pd" sub _rank_springboard { my ($name, $source, $rank, @rest) = @_; my $inverse = ($name =~ m/^i/); unless(defined $rank) { die "${name}n: second argument must be the rank of the transform you want"; } $rank = 0+$rank; # force numeric context unless($rank>=1 ) { die "${name}n: second argument (rank) must be between 1 and 10"; } my $active_lo = 0; my $active_hi = $rank-1; unless($source->ndims > $active_hi) { die "${name}n: rank is $rank but input has only ".($active_hi-$active_lo)." active dims!"; } my $out = __fft_internal( $name.$rank, $source, @rest ); if($inverse) { $out /= $out->shape->slice("$active_lo:$active_hi")->prodover; } return $out; } sub fftn { _rank_springboard( "fft", @_ ) } sub ifftn { _rank_springboard( "ifft", @_ ) } sub rfftn { _rank_springboard( "rfft", @_ ) } sub irfftn { _rank_springboard( "irfft", @_ ) } *PDL::fftn = \&fftn; *PDL::ifftn = \&ifftn; *PDL::rfftn = \&rfftn; *PDL::irfftn = \&irfftn; #line 890 "FFTW3.pm" # Exit with OK status 1; PDL-FFTW3-0.203/TODO0000644000175000017500000000034013562111342013375 0ustar osboxesosboxes-*- org -*- - check on alignment issues. plain PDL alloc seems ok... - can embedding be made to work? - Do something smarter than FFTW_ESTIMATE - support for reading/writing the wisdom - Support DCT, DST, Hartley transform PDL-FFTW3-0.203/fftw3.pd0000644000175000017500000005212414742523014014276 0ustar osboxesosboxes=head1 NAME PDL::FFTW3 - PDL interface to the Fastest Fourier Transform in the West v3 =cut # -*- cperl -*- ##### General layout of the module ##### # # Each type of transform that is supported by this module has a plain, # unthreaded perl entry point the user calls. This entry point makes sure the # FFTW plan exists (or makes it). Then it calls the THREADED PP function to # actually compute the transform use strict; use warnings; # I generate code for up to 10-dimensional FFTs my $maxrank = 10; our $VERSION = '0.203'; pp_setversion($VERSION); pp_addpm( {At => 'Top'}, slurp('README.pod') . <<'EOF' ); use strict; use warnings; EOF pp_addhdr( ' #include /* the Linux kernel does something similar to assert at compile time */ #define static_assert_fftw(x) (void)( sizeof( int[ 1 - 2* !(x) ]) ) ' ); # I want to be able to say $X = fft1($x); rank is required. 'fft()' is ambiguous # about whether threading is desired or if a large fft is desired. Old PDL::FFTW # did one thing, matlab does another, so I do not include this function at all my $TEMPLATE_REAL_R2C = <<'EOF'; // make sure the PDL data type I'm using matches the FFTW data type static_assert_fftw(sizeof($GENERIC())*2 == sizeof($TFD(fftwf_,fftw_)complex)); $TFD(fftwf_,fftw_)plan plan = INT2PTR($TFD(fftwf_,fftw_)plan, $COMP(plan)); $TFD(fftwf_,fftw_)execute_dft_r2c(plan, (void*)$P(real), (void*)$P(complexv)); EOF my $TEMPLATE_REAL_C2R = <<'EOF'; // make sure the PDL data type I'm using matches the FFTW data type static_assert_fftw(sizeof($GENERIC()) == sizeof($TGC(fftwf_,fftw_)complex)); $TGC(fftwf_,fftw_)plan plan = INT2PTR($TGC(fftwf_,fftw_)plan, $COMP(plan)); // FFTW inverse real transforms clobber their input. I thus make a new // buffer and transform from there PDL_Indx i, nbytes = sizeof($GENERIC()); PDL_Indx rank = $PRIV(vtable)->par_realdims[0], *dims = $PDL(complexv)->dims; for (i=0; idata); if( in_alignment < 16 ) in_data |= in_alignment; UVTYPE out_data = PTR2UV(out_pdl->data); if( out_alignment < 16 ) out_data |= out_alignment; void* plan; if( !is_real_fft ) { int direction = do_inverse_fft ? FFTW_BACKWARD : FFTW_FORWARD; // complex-complex FFT. Input/output have identical dimensions if( !do_double_precision ) plan = fftwf_plan_dft( rank, dims_row_first, NUM2PTR(void*, in_data), NUM2PTR(void*, out_data), direction, FFTW_ESTIMATE); else plan = fftw_plan_dft( rank, dims_row_first, NUM2PTR(void*, in_data), NUM2PTR(void*, out_data), direction, FFTW_ESTIMATE); } else { // real-complex FFT. Input/output have different dimensions if( !do_double_precision) { if( !do_inverse_fft ) plan = fftwf_plan_dft_r2c( rank, dims_row_first, NUM2PTR(void*, in_data), NUM2PTR(void*, out_data), FFTW_ESTIMATE ); else plan = fftwf_plan_dft_c2r( rank, dims_row_first, NUM2PTR(void*, in_data), NUM2PTR(void*, out_data), FFTW_ESTIMATE ); } else { if( !do_inverse_fft ) plan = fftw_plan_dft_r2c( rank, dims_row_first, NUM2PTR(void*, in_data), NUM2PTR(void*, out_data), FFTW_ESTIMATE ); else plan = fftw_plan_dft_c2r( rank, dims_row_first, NUM2PTR(void*, in_data), NUM2PTR(void*, out_data), FFTW_ESTIMATE ); } } if( plan == NULL ) XSRETURN_UNDEF; else RETVAL = PTR2IV(plan); } OUTPUT: RETVAL int is_same_data( in, out ) pdl* in pdl* out CODE: { RETVAL = (in->data == out->data) ? 1 : 0; } OUTPUT: RETVAL #define _get_data_alignment_int( x ) \ ( (x & 0xF) == 0 ) ? 16 : \ ( (x & 0x7) == 0 ) ? 8 : \ ( (x & 0x3) == 0 ) ? 4 : \ ( (x & 0x1) == 0 ) ? 2 : 1; int get_data_alignment_int( x ) UV x CODE: { RETVAL = _get_data_alignment_int( x ); } OUTPUT: RETVAL int get_data_alignment_pdl( in ) pdl* in CODE: { RETVAL = _get_data_alignment_int( PTR2UV(in->data) ); } OUTPUT: RETVAL EOXS pp_addpm( {At => 'Middle'}, <<'EOINCLUDE' ); use PDL::Types; use List::Util 'reduce'; use threads::shared; # When I compute an FFTW plan, it goes here. # This is :shared so that it can be used with Perl threads. my %existingPlans :shared; # these are for the unit tests our $_Nplans = 0; our $_last_do_double_precision; # This is a function that sits between the user's call into this module and the # PP-generated internals. Specifically, this function is called BEFORE any PDL # threading happens. Here I make sure the FFTW plan exists, or if it doesn't, I # make it. Thus the PP-based internals can safely assume that the plan exists sub __fft_internal { my $thisfunction = shift; my ($do_inverse_fft, $is_real_fft, $rank) = $thisfunction =~ /^(i?)(r?)N?.*fft([0-9]+)/; # first I parse the variables. This is a very direct translation of what PP # does normally. Plan-creation has to be outside of PP, so I must re-do this # here my $Nargs = scalar @_; my ($in, $out); if ( $Nargs == 2 ) { # all variables on stack, read in output and temp vars ($in, $out) = map {defined $_ ? PDL::Core::topdl($_) : $_} @_; } elsif ( $Nargs == 1 ) { $in = PDL::Core::topdl $_[0]; if ( $in->is_inplace ) { barf <set_inplace(0); } else { $out = PDL::null(); } } else { barf( <isnull ) { my ($type, @dims) = getOutArgs($in, $is_real_fft, $do_inverse_fft); $out->set_datatype($type->enum); $out->setdims(\@dims); $out->make_physical; } validateArguments( $rank, $is_real_fft, $do_inverse_fft, $thisfunction, $in, $out ); # I need to physical-ize the ndarrays before I make a plan. Again, normally PP # does this, but to make sure alignments match, I need to do this myself, now $in->make_physical; $out->make_physical; my $plan = getPlan( $thisfunction, $rank, $is_real_fft, $do_inverse_fft, $in, $out ); barf "$thisfunction couldn't make a plan. Giving up\n" unless defined $plan; my $is_native = !$in->type->real; # native complex # I now have the arguments and the plan. Go! my $internal_function = 'PDL::__'; $internal_function .= !$is_real_fft ? 'N' : ($is_native && $do_inverse_fft) ? 'irN' : $do_inverse_fft ? barf("irfft no longer supports PDL::Complex") : 'rN'; $internal_function .= "fft$rank"; eval { no strict 'refs'; $internal_function->( $in, $out, $plan ) }; barf $@ if $@; $out; } sub getOutArgs { my ($in, $is_real_fft, $do_inverse_fft) = @_; my @dims = $in->dims; my $is_native = !$in->type->real; my $out_type = $in->type; if ( !$is_real_fft ) { # complex fft. Output is the same size as the input. } elsif ( !$do_inverse_fft ) { # forward real fft $dims[0] = int($dims[0]/2)+1; $out_type = typeWithComplexity(getPrecision($out_type), 1); } else { # backward real fft # # there's an ambiguity here. I want int($out->dim(0)/2) + 1 == $in->dim(1), # however this could mean that # $out->dim(0) = 2*$in->dim(1) - 2 # or # $out->dim(0) = 2*$in->dim(1) - 1 # # WITHOUT ANY OTHER INFORMATION, I ASSUME EVEN INPUT SIZES, SO I ASSUME # $out->dim(0) = 2*$in->dim(1) - 2 if ($is_native) { $out_type = ($out_type == cfloat) ? float : double; } else { shift @dims; } $dims[0] = 2*($dims[0]-1); } ($out_type, @dims); } sub validateArguments { my ($rank, $is_real_fft, $do_inverse_fft, $thisfunction, $in, $out) = @_; for my $arg ( $in, $out ) { barf <isa('PDL'); $thisfunction arguments must be of type 'PDL'. Instead I got an arg of type '$type'. Giving up. EOF } # validate dimensionality of the ndarrays my @inout = ($in, $out); for my $iarg ( 0..1 ) { my $arg = $inout[$iarg]; if( $arg->isnull ) { barf "$thisfunction: don't know what to do with a null input. Giving up"; } if( !$is_real_fft ) { validateArgumentDimensions_complex( $rank, $thisfunction, $arg); } else { validateArgumentDimensions_real( $rank, $do_inverse_fft, $thisfunction, $iarg, $arg); } } # we have an explicit output ndarray we're filling in. Make sure the # input/output dimensions match up if ( !$is_real_fft ) { matchDimensions_complex($thisfunction, $rank, $in, $out); } else { matchDimensions_real($thisfunction, $rank, $do_inverse_fft, $in, $out); } } sub validateArgumentDimensions_complex { my ( $rank, $thisfunction, $arg ) = @_; barf "Tried to compute a complex FFT, but non-native-complex argument given" if $arg->type->real; my $dims_cmp = $arg->ndims; barf <type->real; # native complex # real FFT. Forward transform takes in real and spits out complex; # backward transform does the reverse if (!!$do_inverse_fft == !!($iarg == 0)) { # need complex for this my ($verb, $var, $reason) = ($iarg == 0) ? qw(takes input) : qw(produces output); if ( ($iarg == 1 && !$is_native) || ($iarg == 0 && !$is_native) ) { $reason = "\$$var should be native-complex"; } elsif (!$is_native && $arg->dim(0) != 2) { $reason = "\$$var->dim(0) == 2 should be true"; } barf <info]}: $arg). Giving up. EOF } my ($min_dimensionality, $var) = ($rank, $iarg == 0 ? 'input' : 'output'); if ( $arg->ndims < $min_dimensionality ) { barf <dim($idim) != $out->dim($idim) ) { barf <dim(1) should be int($input->dim(0)/2) + 1 (Section 2.4 of # the FFTW3 documentation) ($varname1, $varname2, $var1, $var2) = (qw(input output), $in, $out); } else { # Backward FFT. The input is complex, the output is real. ($varname1, $varname2, $var1, $var2) = (qw(output input), $out, $in); } barf <dim(0)/2) + 1 != $var2->dim(0); $thisfunction: mismatched first dimension: \$$varname2->dim(0) == int(\$$varname1->dim(0)/2) + 1 wasn't true. $varname1: @{[$var1->info]} $varname2: @{[$var2->info]} Giving up. EOF for my $idim (1..$rank-1) { if ( $var1->dim($idim) != $var2->dim($idim) ) { barf <isnull ) { if( $$in->type < float ) { forceType( $in, (float) ); } } else { # I'm given an output. Make sure this is of a type I can work with, # otherwise give up my $out_type = $$out->type; barf <type; my $in_precision = getPrecision($in_type); my $out_precision = getPrecision($out_type); return if $in_precision == $out_precision; forceType( $in, typeWithComplexity($out_precision, !$in_type->real) ); forceType( $out, typeWithComplexity($out_precision, !$out_type->real) ); } } sub typeWithComplexity { my ($precision, $complex) = @_; $complex ? ($precision == 1 ? cfloat : cdouble) : $precision == 1 ? float : double; } sub getPrecision { my ($type) = @_; ($type <= float || $type == cfloat) ? 1 : # float 2; # double } sub forceType { my ($x, $type) = @_; $$x = convert( $$x, $type ) unless $$x->type == $type; } sub getPlan { my ($thisfunction, $rank, $is_real_fft, $do_inverse_fft, $in, $out) = @_; # I get the plan ID, check if I already have a plan, and make a new plan if I # don't already have one my @dims = ((!$is_real_fft || !$do_inverse_fft) ? $in : $out)->dims; # FFT dimensionality my $Nslices = reduce {$a*$b} 1, splice(@dims, $rank); my $do_double_precision = ($in->get_datatype == $PDL_F || $in->get_datatype == $PDL_CF) ? 0 : 1; $_last_do_double_precision = $do_double_precision; my $do_inplace = is_same_data( $in, $out ); # I compute a single plan for the whole set of thread slices. I make a # worst-case plan, so I find the worst-aligned thread slice and plan off of # it. So if $Nslices>1 then the worst-case alignment is the worse of (1st, # 2nd) slices my $in_alignment = get_data_alignment_pdl( $in ); my $out_alignment = get_data_alignment_pdl( $out ); my $stride_bytes = ($do_double_precision ? 8 : 4) * reduce {$a*$b} @dims; if( $Nslices > 1 ) { my $in_alignment_2nd = get_data_alignment_int($in_alignment + $stride_bytes); my $out_alignment_2nd = get_data_alignment_int($out_alignment + $stride_bytes); $in_alignment = $in_alignment_2nd if $in_alignment_2nd < $in_alignment; $out_alignment = $out_alignment_2nd if $out_alignment_2nd < $out_alignment; } my $planID = join('_', $thisfunction, $do_double_precision, $do_inplace, $in_alignment, $out_alignment, @dims); if ( !exists $existingPlans{$planID} ) { lock(%existingPlans); $existingPlans{$planID} = compute_plan( \@dims, $do_double_precision, $is_real_fft, $do_inverse_fft, $in, $out, $in_alignment, $out_alignment ); $_Nplans++; } return $existingPlans{$planID}; } EOINCLUDE for my $rank (1..$maxrank) { my $shapestr = sprintf(q{$a->shape->slice('0:%d')->prodover},$rank-1); pp_addpm({At => 'Bot'}, pp_line_numbers(__LINE__, < 'Bot'}, pp_line_numbers(__LINE__, sprintf <<'EOF', $maxrank)); sub _rank_springboard { my ($name, $source, $rank, @rest) = @_; my $inverse = ($name =~ m/^i/); unless(defined $rank) { die "${name}n: second argument must be the rank of the transform you want"; } $rank = 0+$rank; # force numeric context unless($rank>=1 ) { die "${name}n: second argument (rank) must be between 1 and %d"; } my $active_lo = 0; my $active_hi = $rank-1; unless($source->ndims > $active_hi) { die "${name}n: rank is $rank but input has only ".($active_hi-$active_lo)." active dims!"; } my $out = __fft_internal( $name.$rank, $source, @rest ); if($inverse) { $out /= $out->shape->slice("$active_lo:$active_hi")->prodover; } return $out; } sub fftn { _rank_springboard( "fft", @_ ) } sub ifftn { _rank_springboard( "ifft", @_ ) } sub rfftn { _rank_springboard( "rfft", @_ ) } sub irfftn { _rank_springboard( "irfft", @_ ) } *PDL::fftn = \&fftn; *PDL::ifftn = \&ifftn; *PDL::rfftn = \&rfftn; *PDL::irfftn = \&irfftn; EOF pp_add_exported( map "${_}fftn", '','i','r','ir' ); pp_done(); sub generateDefinitions { my $rank = shift; my %pp_def = ( HandleBad => 0, OtherPars => 'IV plan', # comes from pre-fft code not user # this is a private function so I don't want to create # user-visible documentation or exports Doc => undef, PMFunc => '' ); ################################################################################ ####### first I generate the definitions for the simple complex-complex FFT case # make dimension string 'n1,n2,n3,n4...'. my @dims_real = my @dims_complex = my @dims = map "n$_", 1..$rank; my $dims_string = join(',', @dims); $pp_def{Pars} = "in($dims_string); [o]out($dims_string);"; $pp_def{GenericTypes} = [qw(G C)]; pp_def( "__Nfft$rank", %pp_def, Code => $TEMPLATE_COMPLEX ); ################################################################################## ####### real-native complex and native complex-real $dims_complex[0] = 'nhalf'; # first complex dim is real->dim(0)/2+1 my $dims_real_string = join(',', @dims_real); my $dims_complex_string = join(',', @dims_complex); # backward $pp_def{RedoDimsCode} = <<'EOF'; if( $PDL(real)->dims[0] <= 0 ) $SIZE(n1) = 2*$PDL(complexv)->dims[0] - 2; EOF $pp_def{Pars} = "complexv($dims_complex_string); real [o]real($dims_real_string);"; pp_def( "__irNfft$rank", %pp_def, Code => $TEMPLATE_REAL_C2R ); # forward $pp_def{RedoDimsCode} = <<'EOF'; if( $PDL(complexv)->ndims <= 1 || $PDL(complexv)->dims[1] <= 0 ) $SIZE(nhalf) = (int)( $PDL(real)->dims[0]/2 ) + 1; EOF $pp_def{Pars} = "real($dims_real_string); complex [o]complexv($dims_complex_string);"; $pp_def{GenericTypes} = [qw(F D)]; pp_def("__rNfft$rank", %pp_def, Code => $TEMPLATE_REAL_R2C, ); } sub slurp { my $filename = shift; open my $fh, '<', $filename or die "open '$filename' for reading: $!"; local $/ = undef; return qq{\n#line 0 "$filename"\n\n} . <$fh>; } PDL-FFTW3-0.203/META.yml0000644000175000017500000000145614742523116014176 0ustar osboxesosboxes--- abstract: 'PDL interface to the Fastest Fourier Transform in the West' author: - 'Dima Kogan , Craig DeForest ' build_requires: PDL::PP: '0' Test::More: '0.98' configure_requires: IPC::Run: '0' PDL: '2.097' dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.44, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: PDL-FFTW3 no_index: directory: - t - inc requires: PDL: '2.097' perl: '5.016' resources: IRC: irc://irc.perl.org/#pdl bugtracker: https://github.com/PDLPorters/pdl-fftw3/issues repository: git://github.com/PDLPorters/pdl-fftw3.git version: '0.203' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' PDL-FFTW3-0.203/Changes0000644000175000017500000000326514742523023014215 0ustar osboxesosboxes0.203 2025-01-17 - fix metadata and LICENSE file to reflect new licence 0.202 2025-01-17 - relicense to same terms as PDL itself 0.201 2025-01-03 - remove workaround for type-selection fixed in PDL 2.097 0.20 2024-12-02 - remove support for PDL::Complex entirely - rfft* now same as rNfft* 0.19 2024-02-24 - fix test to not broadcast over output 0.18 2021-10-04 - opt in to upcoming PDL multi-C feature 0.17 2021-10-01 - whitespace fix for #line directive 0.16 2021-08-08 - planning now works on threaded Perl - thanks @zmughal 0.15 2021-05-08 - fix ifftn bug with native complex 0.14 2021-04-01 - real->native complex - rNfft 0.13 2021-03-31 - native complex in irfft 0.12 2021-03-23 - PDL::Complex outputs where appropriate - thanks @wlmb 0.11 2021-03-23 - support PDL 2.027+ "native complex" inputs for fftn and ifftn 0.10 2021-03-21 - fix metadata gremlin 0.09 2021-03-21 - fix setting VERSION so visible to module-loaders 0.08 2021-03-20 - allow complex inputs that are PDL::Complex 0.07 2021-02-21 - change param names from "complex" which clashes with complex.h 0.06 2019-12-09 - add licence info - thanks @manwar 0.05 2019-11-10 - update metadata to point at GitHub 0.04 2015-06-17 - updated to dep on PDL::Core 0.03 16 Jun 2015 - Moved into own repo and CPAN distro 0.02.2_02 1 Feb 2014 - portability: change `cat` to slurp - threading improvements 0.02.2_01 20 Jan 2014 - reduce string-eval use - check validateArguments in/out types match - support compilers other than GCC 0.02.2 2 Nov 2013 - use CONFIGURE_REQUIRES 0.02.1 26 Oct 2013 - add deps on IPC::Run, PDL::Core::Dev, PDL::PP - typo fix in metadata - use parseVersion not VERSION_FROM 0.02 26 Oct 2013 - first CPAN release PDL-FFTW3-0.203/MANIFEST0000644000175000017500000000055314742523117014054 0ustar osboxesosboxesChanges fftw3.pd LICENSE Makefile.PL MANIFEST This list of files README.pod t/fftw.t t/threads.t TODO META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) GENERATED/PDL/FFTW3.pm mod=PDL::FFTW3 pd=fftw3.pd (added by pdlpp_mkgen) PDL-FFTW3-0.203/Makefile.PL0000644000175000017500000000602114742522276014676 0ustar osboxesosboxesuse strict; use warnings; use ExtUtils::MakeMaker; use Config; use PDL::Core::Dev; ############################## # Try to use Alien::FFTW3 - but if it's not present # fall back to pkg-config. This is so that # a Debian package won't have to include Alien::FFTW3. my $cflags; my $libs; if( eval "require Alien::FFTW3" ) { ## Ensure at least version 3.3; die if we can't get it. Alien::FFTW3->VERSION(3.3); my $p = Alien::FFTW3->precision; unless($p->{'d'} and $p->{'f'}) { die "PDL::FFTW3 - needs both double-precision and single-precision fftw3 libraries\n\t(libfftw3 and libfftw3f). Alien::FFTW3 found only ".(join(",",keys %$p))."\n"; } $cflags = Alien::FFTW3->cflags; $libs = Alien::FFTW3->libs; } else { print "Alien::FFTW3 not found. Using pkg-config instead...\n"; require IPC::Run; $cflags = ''; $libs = ''; my $err = ''; IPC::Run::run( ['pkg-config', '--cflags', 'fftw3f >= 3.3', 'fftw3 >= 3.3'], \undef, \$cflags, \$err ) or die "Couldn't get the fftw flags: '$err'"; IPC::Run::run( ['pkg-config', '--libs', 'fftw3f >= 3.3', 'fftw3 >= 3.3'], \undef, \$libs, \$err ) or die "Couldn't get the fftw libs: '$err'"; chomp($cflags, $libs); } my @package = (qw(fftw3.pd FFTW3 PDL::FFTW3), undef, 1); my %descriptor = pdlpp_stdargs(\@package); $descriptor{VERSION_FROM} = 'fftw3.pd'; # I support single and double precision FFTW calls, so both fftw and fftw3f push @{$descriptor{LIBS} }, $libs; $descriptor{INC} = '' unless defined $descriptor{INC}; $descriptor{INC} .= " $cflags"; $descriptor{PREREQ_PM} = { 'PDL' => '2.097', # fixed type-selecting }; $descriptor{CONFIGURE_REQUIRES} = { 'PDL' => '2.097', 'IPC::Run' =>0, # 'Alien::FFTW3' =>0, }; $descriptor{BUILD_REQUIRES} = {'PDL::PP'=>0}; $descriptor{TEST_REQUIRES} = {'Test::More'=>'0.88'}; $descriptor{AUTHOR} = "Dima Kogan , Craig DeForest "; $descriptor{ABSTRACT} = "PDL interface to the Fastest Fourier Transform in the West"; $descriptor{LICENSE} = "perl"; $descriptor{MIN_PERL_VERSION} = "5.016"; $descriptor{META_MERGE} = { "meta-spec" => { version => 2 }, resources => { bugtracker => { web => 'https://github.com/PDLPorters/pdl-fftw3/issues' }, repository => { web => 'https://github.com/PDLPorters/pdl-fftw3', url => 'git://github.com/PDLPorters/pdl-fftw3.git', type => 'git', }, x_IRC => 'irc://irc.perl.org/#pdl', }, prereqs => { develop => { requires => { 'CPAN::Changes' => 0, }, }, test => { requires => { 'Test::More' => '0.98', }, }, }, }; WriteMakefile( %descriptor ); sub MY::postamble { return <<'FOO' . pdlpp_postamble(\@package); install :: @echo "Updating PDL documentation database..."; @$(PERL) -e "exit if $$ENV{DESTDIR}; use PDL::Doc; eval { PDL::Doc::add_module(q{PDL::FFTW3}); }; "; FOO } PDL-FFTW3-0.203/LICENSE0000644000175000017500000000041414742522667013735 0ustar osboxesosboxesThis program is free software; you can redistribute it and/or modify it under the terms of either: a) the GNU General Public License as published by the Free Software Foundation, either version 1, or (at your option) any later version; or b) the "Artistic License".