# Copyright (C) 2012 Josh Willis, Andrew Miller # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ============================================================================= # # Preamble # # ============================================================================= # """ This is a helper module that does most of the work for the unit-tests of the pycbc.fft subpackage. Its class and many private variables are called by the three test scripts test_fft_unthreaded, test_fftw_pthreads, and test_fftw_openmp. For both forward and reverse complex FFT, and forward R2C and reverse C2R FFT, these tests validate that the FFT produces the correct output and does not overwrite its input on small test cases where the answer may be hand-computed. They also verify that an FFT of (larger sized) random data followed by the reverse FFT gives back the original input (modulo the scaling factor of the length of the array, when testing just the basic Array class; that factor is not present for Time/FrequencySeries transformations). For Time and Frequency series these tests also compare their outputs on large, random input to direct calls to the XLAL *TimeFreqFFT and *FreqTimeFFT functions. A similar comparison is *not* made for the array tests, since the underlying LAL functions on a vector input are what the pycbc fft routines use (on the CPU), so there is little additional gain from the comparison to the tests already done. Finally, for all three classes of pycbc.types these unit tests also check that the correct exceptions are raised when several different kinds of erroneous inputs are given. For the R2C (resp. C2R), the tests are run with input arrays (resp. output arrays) that are both even and odd in length, since the length of those output arrays is not the same as that of the input. However these unit tests no longer check that the imaginary parts of DC (and for even-length also Nyquist) outputs are exactly zero, both because that complicates the testing framework considerably, and because it will not in general hold for GPU algorithms (and those cannot be made to enforce that, without unacceptable computational overhead). All of these tests are performed for inputs from each of the three basic types (Array, TimeSeries, and FrequencySeries) and for each precision (single and double). They are also checked for each possible backend of the current scheme, which means in particular that whichever backend is the default will be tested twice, once as 'Default' and once under its own name. """ import pycbc import pycbc.scheme import pycbc.types from pycbc.types import Array as ar, TimeSeries as ts, FrequencySeries as fs import numpy from numpy import dtype, float32, float64, complex64, complex128, zeros, real from numpy.random import randn import pycbc.fft import unittest import sys from utils import parse_args_all_schemes, simple_exit from lal import LIGOTimeGPS as LTG import lal as _lal # Because we run many similar tests where we only vary dtypes, precisions, # or Array/TimeSeries/FrequencySeries, it is helpful to define the following # dictionarys and functions and then call those from within the actual tests. # Map the same kind (real or complex) to the other precision _other_prec = {float32:float64, float64:float32, complex64:complex128, complex128:complex64} # Map the same precision to the other kind _other_kind = {float32:complex64, float64:complex128, complex64:float32, complex128:float64} # Map the dtype of a valid *forward* fft *input* array to the wrong kind, # but correct precision, dtype of the *output* array. This is either R2R or C2R. # The same mapping also send the dtype of a valid *output* array for an inverse # fft to the wrong kind but correct precision *input* array, corresponding to # a R2R or R2C transform. _bad_dtype = {float32: float32, float64: float64, complex64: float32, complex128: float64} # Several dicts for our direct comparisons with LAL's Time/Freq FFT routines. _fwd_plan_dict = {float32: _lal.CreateForwardREAL4FFTPlan, float64: _lal.CreateForwardREAL8FFTPlan, complex64: _lal.CreateForwardCOMPLEX8FFTPlan, complex128: _lal.CreateForwardCOMPLEX16FFTPlan} _rev_plan_dict = {float32: _lal.CreateReverseREAL4FFTPlan, float64: _lal.CreateReverseREAL8FFTPlan, complex64: _lal.CreateReverseCOMPLEX8FFTPlan, complex128: _lal.CreateReverseCOMPLEX16FFTPlan} _fwd_lalfft_dict = {float32: _lal.REAL4TimeFreqFFT, float64: _lal.REAL8TimeFreqFFT, complex64: _lal.COMPLEX8TimeFreqFFT, complex128: _lal.COMPLEX16TimeFreqFFT} _rev_lalfft_dict = {float32: _lal.REAL4FreqTimeFFT, float64: _lal.REAL8FreqTimeFFT, complex64: _lal.COMPLEX8FreqTimeFFT, complex128: _lal.COMPLEX16FreqTimeFFT} # Our actual helper functions. Note that these perform the necessary operations # within the appropriate context, so they should not themselves be called inside # of a context block. def _test_fft(test_case,inarr,expec,tol): # Basic test to see that the forward FFT doesn't # overwrite its input and computes its output to # within the required accuracy. tc = test_case inty = type(inarr) in_pristine = inty(inarr) outty = type(expec) # Make a copy... outarr = outty(expec) # Clear it and change values (so we test something meaningful): outarr.clear() if hasattr(outarr,'_epoch'): outarr._epoch *= 5*tol if hasattr(outarr,'delta_t'): outarr._delta_t *= 5*tol if hasattr(outarr,'delta_f'): outarr._delta_f *= 5*tol with tc.context: pycbc.fft.fft(inarr,outarr,tc.backends) # First, verify that the input hasn't been overwritten emsg = 'FFT overwrote input array' tc.assertEqual(inarr,in_pristine,emsg) # Next, check that the output is correct to within tolerance. # That will require exact equality of all other meta-data emsg = 'FFT output differs by more than a factor of {0} from expected'.format(tol) if isinstance(outarr,ts) or isinstance(outarr,fs): tc.assertTrue(outarr.almost_equal_norm(expec,tol=tol,dtol=tol),msg=emsg) else: tc.assertTrue(outarr.almost_equal_norm(expec,tol=tol),msg=emsg) def _test_ifft(test_case,inarr,expec,tol): # Basic test to see that the reverse FFT doesn't # overwrite its input and computes its output to # within the required accuracy. tc = test_case inty = type(inarr) in_pristine = inty(inarr) outty = type(expec) # Make a copy... outarr = outty(expec) # Clear it and change values (so we test something meaningful): outarr.clear() if hasattr(outarr,'_epoch'): outarr._epoch *= 5*tol if hasattr(outarr,'delta_t'): outarr._delta_t *= 5*tol if hasattr(outarr,'delta_f'): outarr._delta_f *= 5*tol with tc.context: pycbc.fft.ifft(inarr,outarr,tc.backends) # First, verify that the input hasn't been overwritten emsg = 'Inverse FFT overwrote input array' tc.assertEqual(inarr,in_pristine,emsg) # Next, check that the output is correct to within tolerance. # That will require exact equality of all other meta-data emsg = 'Inverse FFT output differs by more than a factor of {0} from expected'.format(tol) if isinstance(outarr,ts) or isinstance(outarr,fs): tc.assertTrue(outarr.almost_equal_norm(expec,tol=tol,dtol=tol),msg=emsg) else: tc.assertTrue(outarr.almost_equal_norm(expec,tol=tol),msg=emsg) def _test_random(test_case,inarr,outarr,tol): tc = test_case # Test that applying a transform and its inverse to reasonably long, random # input gives back the (appropriately scaled) input. We must allow for numerical # error, and it seems more reliable to check using normwise error (than elementwise). # # First test IFFT(FFT(random)) # The numpy randn(n) provides an array of n numbers drawn from standard normal if dtype(inarr).kind == 'c': inarr._data[:] = randn(len(inarr)) +1j*randn(len(inarr)) # If we're going to do a HC2R transform we must worry about DC/Nyquist imaginary if dtype(outarr).kind == 'f': inarr._data[0] = real(inarr[0]) if (len(outarr)%2)==0: inarr._data[len(inarr)-1] = real(inarr[len(inarr)-1]) else: inarr._data[:] = randn(len(inarr)) incopy = type(inarr)(inarr) outarr.clear() # An FFT followed by IFFT gives Array scaled by len(Array), but for # Time/FrequencySeries there should be no scaling. if type(inarr) == pycbc.types.Array: incopy *= len(inarr) with tc.context: pycbc.fft.fft(inarr,outarr,tc.backends) pycbc.fft.ifft(outarr,inarr,tc.backends) emsg="IFFT(FFT(random)) did not reproduce original array to within tolerance {0}".format(tol) if isinstance(incopy,ts) or isinstance(incopy,fs): tc.assertTrue(incopy.almost_equal_norm(inarr,tol=tol,dtol=tol), msg=emsg) else: tc.assertTrue(incopy.almost_equal_norm(inarr,tol=tol), msg=emsg) # Perform arithmetic on outarr and inarr to pull them off of the GPU: outarr *= 1.0 inarr *= 1.0 # Now the same for FFT(IFFT(random)) if dtype(outarr).kind == 'c': outarr._data[:] = randn(len(outarr))+1j*randn(len(outarr)) # If we're going to do a HC2R transform we must worry about DC/Nyquist imaginary if dtype(inarr).kind == 'f': outarr._data[0] = real(outarr[0]) if (len(inarr)%2)==0: outarr._data[len(outarr)-1] = real(outarr[len(outarr)-1]) else: outarr._data[:] = randn(len(outarr)) inarr.clear() outcopy = type(outarr)(outarr) if type(outarr) == pycbc.types.Array: outcopy *= len(inarr) with tc.context: pycbc.fft.ifft(outarr,inarr,tc.backends) pycbc.fft.fft(inarr,outarr,tc.backends) emsg="FFT(IFFT(random)) did not reproduce original array to within tolerance {0}".format(tol) if isinstance(outcopy,ts) or isinstance(outcopy,fs): tc.assertTrue(outcopy.almost_equal_norm(outarr,tol=tol,dtol=tol), msg=emsg) else: tc.assertTrue(outcopy.almost_equal_norm(outarr,tol=tol), msg=emsg) def _test_lal_tf_fft(test_case,inarr,outarr,tol): tc = test_case # Make sure input and output have been moved back to CPU if needed inarr *= 1.0 outarr *=1.0 # Fill input array with random, clear output, and get lal handles for each. if dtype(inarr).kind == 'c': inarr._data[:] = randn(len(inarr)) +1j*randn(len(inarr)) else: inarr._data[:] = randn(len(inarr)) outarr.clear() inlal = inarr.lal() outlal = outarr.lal() # Calculate the pycbc fft: with tc.context: pycbc.fft.fft(inarr,outarr,tc.backends) fwdplan = _fwd_plan_dict[dtype(inarr).type](len(inarr),0) # Call the lal function directly (see above for dict). Note that # lal functions want *output* given first. _fwd_lalfft_dict[dtype(inarr).type](outlal,inlal,fwdplan) # Make a pycbc type from outlal. Some hackiness because we don't know the # type of outlal. if hasattr(outlal,'deltaT'): cmparr = ts(outlal.data.data,epoch=outlal.epoch,delta_t=outlal.deltaT) else: cmparr = fs(outlal.data.data,epoch=outlal.epoch,delta_f=outlal.deltaF) emsg = "Direct call to LAL TimeFreqFFT() did not agree with fft() to within precision {0}".format(tol) tc.assertTrue(outarr.almost_equal_norm(cmparr,tol=tol),msg=emsg) def _test_lal_tf_ifft(test_case,inarr,outarr,tol): tc = test_case # Fill input array with random, clear output, and get lal handles for each. if dtype(inarr).kind == 'c': inarr._data[:] = randn(len(inarr)) +1j*randn(len(inarr)) # We must worry about DC/Nyquist imag parts if this is HC2R transform: if dtype(outarr).kind == 'f': inarr._data[0] = real(inarr._data[0]) if (len(outarr)%2)==0: inarr._data[len(inarr)-1] = real(inarr[len(inarr)-1]) else: inarr._data[:] = randn(len(outarr)) outarr.clear() inlal = inarr.lal() outlal = outarr.lal() # Calculate the pycbc fft: with tc.context: pycbc.fft.ifft(inarr,outarr,tc.backends) revplan = _rev_plan_dict[dtype(outarr).type](len(outarr),0) # Call the lal function directly (see above for dict). Note that # lal functions want *output* given first. _rev_lalfft_dict[dtype(outarr).type](outlal,inlal,revplan) # Make a pycbc type from outlal. Some hackiness because we don't know the # type of outlal. if hasattr(outlal,'deltaT'): cmparr = ts(outlal.data.data,epoch=outlal.epoch,delta_t=outlal.deltaT) else: cmparr = fs(outlal.data.data,epoch=outlal.epoch,delta_f=outlal.deltaF) emsg = "Direct call to LAL TimeFreqFFT() did not agree with fft() to within precision {0}".format(tol) tc.assertTrue(outarr.almost_equal_norm(cmparr,tol=tol),msg=emsg) def _test_raise_excep_fft(test_case,inarr,outarr,other_args={}): # As far as can be told from the unittest module documentation, the # 'assertRaises' tests do not permit a custom message. So more # comments than usual here, to help diagnose and test failures. # # The 'other_args' argument is needed to pass additional keywords to # the constructors of some types (T/F series); we cannot simply copy since # the whole point is to vary the input/output in some way that should cause # an exception. tc = test_case with tc.context: outty = type(outarr) outzer = pycbc.types.zeros(len(outarr)) # If we give an output array that is wrong only in length, raise ValueError: out_badlen = outty(pycbc.types.zeros(len(outarr)+1),dtype=outarr.dtype,**other_args) args = [inarr,out_badlen,tc.backends] tc.assertRaises(ValueError,pycbc.fft.fft,*args) # If we give an output array that has the wrong precision, raise ValueError: out_badprec = outty(outzer,dtype=_other_prec[dtype(outarr).type],**other_args) args = [inarr,out_badprec,tc.backends] tc.assertRaises(ValueError,pycbc.fft.fft,*args) # If we give an output array that has the wrong kind (real or complex) but # correct precision, then raise a ValueError. This only makes sense if we try # to do either C2R or R2R. out_badkind = outty(outzer,dtype=_bad_dtype[dtype(inarr).type],**other_args) args = [inarr,out_badkind,tc.backends] tc.assertRaises(ValueError,pycbc.fft.fft,*args) # If we give an output array that isn't a PyCBC type, raise TypeError: out_badtype = numpy.zeros(len(outarr),dtype=outarr.dtype) args = [inarr,out_badtype,tc.backends] tc.assertRaises(TypeError,pycbc.fft.fft,*args) # If we give an input array that isn't a PyCBC type, raise TypeError: in_badtype = numpy.zeros(len(inarr),dtype=inarr.dtype) args = [in_badtype,outarr,tc.backends] tc.assertRaises(TypeError,pycbc.fft.fft,*args) def _test_raise_excep_ifft(test_case,inarr,outarr,other_args={}): # As far as can be told from the unittest module documentation, the # 'assertRaises' tests do not permit a custom message. So more # comments than usual here, to help diagnose and test failures. # # The 'other_args' argument is needed to pass additional keywords to # the constructors of some types (T/F series); we cannot simply copy since # the whole point is to vary the input/output in some way that should cause # an exception. tc = test_case with tc.context: outty = type(outarr) outzer = pycbc.types.zeros(len(outarr)) # If we give an output array that is wrong only in length, raise ValueError: out_badlen = outty(pycbc.types.zeros(len(outarr)+1),dtype=outarr.dtype,**other_args) args = [inarr,out_badlen,tc.backends] tc.assertRaises(ValueError,pycbc.fft.ifft,*args) # If we give an output array that has the wrong precision, raise ValueError: out_badprec = outty(outzer,dtype=_other_prec[dtype(outarr).type],**other_args) args = [inarr,out_badprec,tc.backends] tc.assertRaises(ValueError,pycbc.fft.ifft,*args) # If we give an output array that has the wrong kind (real or complex) but # correct precision, then raise a ValueError. Here we must adjust the kind # of the *input* array, not output. But that makes it hard, because the 'other_args' # parameter will be wrong for that. Very hacky, but oh well... new_args = other_args.copy() if new_args != {}: try: delta = new_args.pop('delta_t') new_args.update({'delta_f' : delta}) except KeyError: delta = new_args.pop('delta_f') new_args.update({'delta_t' : delta}) in_badkind = type(inarr)(pycbc.types.zeros(len(inarr)),dtype=_bad_dtype[dtype(outarr).type], **new_args) args = [in_badkind,outarr,tc.backends] tc.assertRaises(ValueError,pycbc.fft.ifft,*args) # If we give an output array that isn't a PyCBC type, raise TypeError: out_badtype = numpy.zeros(len(outarr),dtype=outarr.dtype) args = [inarr,out_badtype,tc.backends] tc.assertRaises(TypeError,pycbc.fft.ifft,*args) # If we give an input array that isn't a PyCBC type, raise TypeError: in_badtype = numpy.zeros(len(inarr),dtype=inarr.dtype) args = [in_badtype,outarr,tc.backends] tc.assertRaises(TypeError,pycbc.fft.ifft,*args) # The following isn't a helper function, called by several test functions, but it only applies # to the 'lalfft' backend, so we only add it to that TestCase class, rather than putting it in # the main class definition def _test_lalfft(test_case): import pycbc.fft.lalfft as lfft tc = test_case tc.assertEquals(lfft._default_measurelvl,1, msg="Default lalfft measure level not initialized to 1") tc.assertRaises(ValueError,lfft.set_measure_level,5) tc.assertEquals(lfft.get_measure_level(),1, msg="lalfft.get_measure_level() did not return _default_measurelvl") plan1 = lfft._get_fwd_plan('single','complex','complex',4) lfft.set_measure_level(2) plan2 = lfft._get_fwd_plan('single','complex','complex',4) tc.assertTrue(plan1 is not plan2,msg="Increasing measure level did not result in new plan") lfft.set_measure_level(0) plan0 = lfft._get_fwd_plan('single','complex','complex',4) tc.assertTrue(plan2 is plan0,msg="Decreasing measure level *did* result in new plan") # Restore default lfft.set_measure_level(1) class _BaseTestFFTClass(unittest.TestCase): """ This is the base class from which unit tests for all FFT backends are derived. """ def setUp(self): # Dictionary to convert a dtype to a relative precision to test self.tdict = { float32: 1e-6, float64: 1e-14, complex64: 1e-6, complex128: 1e-14} # Next we set up various lists that are used to build our 'known' # test, which are repeated for a variety of different precisions # and basic types. All of the lists should be consistent with a # direct calculation in the formulas of the "What FFTW actually # computes" section of the FFTW manual. # First, R2C transforms. We have both even and odd length inputs, # since the appropriate values for the output lengths vary. self.in_r2c_e = [1.0,-1.0,2.0,-2.0] self.out_r2c_e = [0.0+0.0j,-1.0-1.0j,6.0+0.0j] self.in_r2c_o = [1.0,2.0,2.0] self.out_r2c_o = [5.0+0.0j,-1.0+0.0j] # Next, C2R transforms, again for both even and odd lengths self.in_c2r_e = [0.0+0.0j,-1.0-1.0j,6.0+0.0j] self.out_c2r_e = [4.0, -4.0, 8.0, -8.0] self.in_c2r_o = [5.0+0.0j,-1.0+0.0j] self.out_c2r_o = [3.0,6.0,6.0] # Finally, C2C transforms, where we don't do both even and odd, # but do have different lists for fwd and rev (i.e., fft and ifft) self.in_c2c_fwd = [1.0+1.0j,2.0-2.0j] self.out_c2c_fwd = [3.0-1.0j,-1.0+3.0j] self.in_c2c_rev = [3.0-1.0j,-1.0+3.0j] self.out_c2c_rev = [2.0+2.0j,4.0-4.0j] # For Time/FrequencySeries, we want to test with a non-trivial epoch self.epoch = LTG(3,4) # When we need a delta_t or delta_f for input, use this. # Output-appropriate variable is computed. self.delta = 1.0/4096.0 # Length of our random arrays, for both real and complex arrays. self.rand_len_r = 2046 self.rand_len_c = 1024 def test_fwd_real_arr(self): for fwd_dtype in [float32,float64]: # Even input inarr = ar(self.in_r2c_e,dtype=fwd_dtype) outexp = ar(self.out_r2c_e,dtype=_other_kind[fwd_dtype]) _test_fft(self,inarr,outexp,self.tdict[fwd_dtype]) # Odd input inarr = ar(self.in_r2c_o,dtype=fwd_dtype) outexp = ar(self.out_r2c_o,dtype=_other_kind[fwd_dtype]) _test_fft(self,inarr,outexp,self.tdict[fwd_dtype]) # Random rand_inarr = ar(zeros(self.rand_len_r,dtype=fwd_dtype)) rand_outarr = ar(zeros(self.rand_len_c,dtype=_other_kind[fwd_dtype])) _test_random(self,rand_inarr,rand_outarr,self.tdict[fwd_dtype]) # Clean these up since they could be big: del rand_inarr del rand_outarr # Check that exceptions are raised. Need input and # output arrays; just reuse inarr and outexp (values won't # matter, we're just checking exceptions). _test_raise_excep_fft(self,inarr,outexp) def test_fwd_real_ts(self): for fwd_dtype in [float32,float64]: delta_t = self.delta # Even input inarr = ts(self.in_r2c_e,dtype=fwd_dtype,delta_t=delta_t,epoch=self.epoch) delta_f = 1.0/(inarr.delta_t * len(inarr)) outexp = fs(self.out_r2c_e,dtype=_other_kind[fwd_dtype],delta_f=delta_f,epoch=self.epoch) outexp *= delta_t _test_fft(self,inarr,outexp,self.tdict[fwd_dtype]) # Odd input inarr = ts(self.in_r2c_o,dtype=fwd_dtype,delta_t=delta_t,epoch=self.epoch) delta_f = 1.0/(inarr.delta_t * len(inarr)) outexp = fs(self.out_r2c_o,dtype=_other_kind[fwd_dtype],delta_f=delta_f,epoch=self.epoch) outexp *= delta_t _test_fft(self,inarr,outexp,self.tdict[fwd_dtype]) # Random rand_inarr = ts(zeros(self.rand_len_r,dtype=fwd_dtype),epoch=self.epoch,delta_t=delta_t) delta_f = 1.0/(rand_inarr.delta_t * len(rand_inarr)) rand_outarr = fs(zeros(self.rand_len_c,dtype=_other_kind[fwd_dtype]),epoch=self.epoch, delta_f=delta_f) _test_random(self,rand_inarr,rand_outarr,self.tdict[fwd_dtype]) # Reuse random arrays for the LAL tests: _test_lal_tf_fft(self,rand_inarr,rand_outarr,self.tdict[fwd_dtype]) # Clean these up since they could be big: del rand_inarr del rand_outarr # Check that exceptions are raised. Need input and # output arrays; just reuse inarr and outexp (values won't # matter, we're just checking exceptions). output_args = {"delta_f": self.delta, "epoch": self.epoch} _test_raise_excep_fft(self,inarr,outexp,output_args) def test_fwd_real_fs(self): for fwd_dtype in [float32,float64]: delta_f = self.delta # Even input inarr = fs(self.in_r2c_e,dtype=fwd_dtype,delta_f=delta_f,epoch=self.epoch) delta_t = 1.0/(inarr.delta_f * len(inarr)) outexp = ts(self.out_r2c_e,dtype=_other_kind[fwd_dtype],delta_t=delta_t,epoch=self.epoch) outexp *= delta_f _test_fft(self,inarr,outexp,self.tdict[fwd_dtype]) # Odd input inarr = fs(self.in_r2c_o,dtype=fwd_dtype,delta_f=delta_f,epoch=self.epoch) delta_t = 1.0/(inarr.delta_f * len(inarr)) outexp = ts(self.out_r2c_o,dtype=_other_kind[fwd_dtype],delta_t=delta_t,epoch=self.epoch) outexp *= delta_f _test_fft(self,inarr,outexp,self.tdict[fwd_dtype]) # Random rand_inarr = fs(zeros(self.rand_len_r,dtype=fwd_dtype),epoch=self.epoch,delta_f=delta_f) delta_t = 1.0/(rand_inarr.delta_f * len(rand_inarr)) rand_outarr = ts(zeros(self.rand_len_c,dtype=_other_kind[fwd_dtype]),epoch=self.epoch, delta_t=delta_t) _test_random(self,rand_inarr,rand_outarr,self.tdict[fwd_dtype]) # LAL doesn't have forward FFT funcs starting from a FS, so skip _test_lal # Clean these up since they could be big: del rand_inarr del rand_outarr # Check that exceptions are raised. Need input and # output arrays; just reuse inarr and outexp (values won't # matter, we're just checking exceptions). output_args = {"delta_t": self.delta, "epoch": self.epoch} _test_raise_excep_fft(self,inarr,outexp,output_args) def test_rev_real_arr(self): for rev_dtype in [float32,float64]: # Even input inarr = ar(self.in_c2r_e,dtype=_other_kind[rev_dtype]) outexp = ar(self.out_c2r_e,dtype=rev_dtype) _test_ifft(self,inarr,outexp,self.tdict[rev_dtype]) # Odd input inarr = ar(self.in_c2r_o,dtype=_other_kind[rev_dtype]) outexp = ar(self.out_c2r_o,dtype=rev_dtype) _test_ifft(self,inarr,outexp,self.tdict[rev_dtype]) # Random---we don't do that in 'reverse' tests, since both # directions are already tested in forward, and if we just passed # in arrays in the other order we'd only get exceptions # # Check that exceptions are raised. Need input and # output arrays; just reuse inarr and outexp (values won't # matter, we're just checking exceptions). _test_raise_excep_ifft(self,inarr,outexp) def test_rev_real_ts(self): for rev_dtype in [float32,float64]: delta_t = self.delta # Even input inarr = ts(self.in_c2r_e,dtype=_other_kind[rev_dtype],delta_t=delta_t,epoch=self.epoch) delta_f = 1.0/(delta_t*len(self.out_c2r_e)) outexp = fs(self.out_c2r_e,dtype=rev_dtype,delta_f=delta_f,epoch=self.epoch) outexp *= delta_t _test_ifft(self,inarr,outexp,self.tdict[rev_dtype]) # Odd input inarr = ts(self.in_c2r_o,dtype=_other_kind[rev_dtype],delta_t=delta_t,epoch=self.epoch) delta_f = 1.0/(delta_t*len(self.out_c2r_o)) outexp = fs(self.out_c2r_o,dtype=rev_dtype,delta_f=delta_f,epoch=self.epoch) outexp *= delta_t _test_ifft(self,inarr,outexp,self.tdict[rev_dtype]) # Random---we don't do that in 'reverse' tests, since both # directions are already tested in forward, and if we just passed # in arrays in the other order we'd only get exceptions # # LAL doesn't have reverse FFT funcs starting from a TimeSeries, so # we skip those tests as well. # # Check that exceptions are raised. Need input and # output arrays; just reuse inarr and outexp (values won't # matter, we're just checking exceptions). output_args = {"delta_f": self.delta, "epoch": self.epoch} _test_raise_excep_ifft(self,inarr,outexp,output_args) def test_rev_real_fs(self): for rev_dtype in [float32,float64]: delta_f = self.delta # Even input inarr = fs(self.in_c2r_e,dtype=_other_kind[rev_dtype],delta_f=delta_f,epoch=self.epoch) delta_t = 1.0/(delta_f*len(self.out_c2r_e)) outexp = ts(self.out_c2r_e,dtype=rev_dtype,delta_t=delta_t,epoch=self.epoch) outexp *= delta_f _test_ifft(self,inarr,outexp,self.tdict[rev_dtype]) # Odd input inarr = fs(self.in_c2r_o,dtype=_other_kind[rev_dtype],delta_f=delta_f,epoch=self.epoch) delta_t = 1.0/(delta_f*len(self.out_c2r_o)) outexp = ts(self.out_c2r_o,dtype=rev_dtype,delta_t=delta_t,epoch=self.epoch) outexp *= delta_f _test_ifft(self,inarr,outexp,self.tdict[rev_dtype]) # Random---we don't do that in 'reverse' tests, since both # directions are already tested in forward, and if we just passed # in arrays in the other order we'd only get exceptions # # However, we do still generate the arrays for T/F series, so that we may # do the LAL comparison test. As usual, we then delete those arrays. rand_inarr = fs(zeros(self.rand_len_c,dtype=_other_kind[rev_dtype]),epoch=self.epoch, delta_f=self.delta) rand_outarr = ts(zeros(self.rand_len_r,dtype=rev_dtype),epoch=self.epoch, delta_t=self.delta) _test_lal_tf_ifft(self,rand_inarr,rand_outarr,self.tdict[rev_dtype]) del rand_inarr del rand_outarr # Check that exceptions are raised. Need input and # output arrays; just reuse inarr and outexp (values won't # matter, we're just checking exceptions). output_args = {"delta_t": self.delta, "epoch": self.epoch} _test_raise_excep_ifft(self,inarr,outexp,output_args) def test_fwd_complex_arr(self): for fwd_dtype in [complex64,complex128]: # Don't do separate even/odd tests for complex inarr = ar(self.in_c2c_fwd,dtype=fwd_dtype) outexp = ar(self.out_c2c_fwd,dtype=fwd_dtype) _test_fft(self,inarr,outexp,self.tdict[fwd_dtype]) # Random rand_inarr = ar(zeros(self.rand_len_c,dtype=fwd_dtype)) rand_outarr = ar(zeros(self.rand_len_c,dtype=fwd_dtype)) _test_random(self,rand_inarr,rand_outarr,self.tdict[fwd_dtype]) # Clean these up since they could be big: del rand_inarr del rand_outarr # Check that exceptions are raised. Need input and # output arrays; just reuse inarr and outexp (values won't # matter, we're just checking exceptions). _test_raise_excep_fft(self,inarr,outexp) def test_fwd_complex_ts(self): for fwd_dtype in [complex64,complex128]: delta_t = self.delta # Don't do separate even/odd tests for complex inarr = ts(self.in_c2c_fwd,dtype=fwd_dtype,delta_t=delta_t,epoch=self.epoch) delta_f = 1.0/(delta_t * len(inarr)) outexp = fs(self.out_c2c_fwd,dtype=fwd_dtype,delta_f=delta_f,epoch=self.epoch) outexp *= delta_t _test_fft(self,inarr,outexp,self.tdict[fwd_dtype]) # Random rand_inarr = ts(zeros(self.rand_len_c,dtype=fwd_dtype),delta_t=delta_t,epoch=self.epoch) delta_f = 1.0/(delta_t*len(rand_inarr)) rand_outarr = fs(zeros(self.rand_len_c,dtype=fwd_dtype),delta_f=delta_f,epoch=self.epoch) _test_random(self,rand_inarr,rand_outarr,self.tdict[fwd_dtype]) # Reuse random arrays for the LAL tests: # COMMENTED OUT: The LAL Complex TimeFreqFFT and FreqTimeFFT functions perform # a repacking of data because they seem to assume that the array represents both # positive and negative frequencies. We don't do this, so we don't compare. #_test_lal_tf_fft(self,rand_inarr,rand_outarr,self.tdict[fwd_dtype]) # Clean these up since they could be big: del rand_inarr del rand_outarr # Check that exceptions are raised. Need input and # output arrays; just reuse inarr and outexp (values won't # matter, we're just checking exceptions). output_args = {"delta_f": self.delta, "epoch": self.epoch} _test_raise_excep_fft(self,inarr,outexp,output_args) def test_fwd_complex_fs(self): for fwd_dtype in [complex64,complex128]: delta_f = self.delta # Don't do separate even/odd tests for complex inarr = fs(self.in_c2c_fwd,dtype=fwd_dtype,delta_f=delta_f,epoch=self.epoch) delta_t = 1.0/(delta_f * len(inarr)) outexp = ts(self.out_c2c_fwd,dtype=fwd_dtype,delta_t=delta_t,epoch=self.epoch) outexp *= delta_f _test_fft(self,inarr,outexp,self.tdict[fwd_dtype]) # Random rand_inarr = fs(zeros(self.rand_len_c,dtype=fwd_dtype),delta_f=delta_f,epoch=self.epoch) delta_t = 1.0/(delta_t*len(rand_inarr)) rand_outarr = ts(zeros(self.rand_len_c,dtype=fwd_dtype),delta_t=delta_t,epoch=self.epoch) _test_random(self,rand_inarr,rand_outarr,self.tdict[fwd_dtype]) # LAL doesn't have forward FFT funcs starting from a FS, so skip _test_lal # Clean these up since they could be big: del rand_inarr del rand_outarr # Check that exceptions are raised. Need input and # output arrays; just reuse inarr and outexp (values won't # matter, we're just checking exceptions). output_args = {"delta_t": self.delta, "epoch": self.epoch} _test_raise_excep_fft(self,inarr,outexp,output_args) def test_rev_complex_arr(self): for rev_dtype in [complex64,complex128]: # Don't do separate even/odd tests for complex inarr = ar(self.in_c2c_rev,dtype=rev_dtype) outexp = ar(self.out_c2c_rev,dtype=rev_dtype) _test_ifft(self,inarr,outexp,self.tdict[rev_dtype]) # Random---we don't do that in 'reverse' tests, since both # directions are already tested in forward, and if we just passed # in arrays in the other order we'd only get exceptions # # Check that exceptions are raised. Need input and # output arrays; just reuse inarr and outexp (values won't # matter, we're just checking exceptions). _test_raise_excep_ifft(self,inarr,outexp) def test_rev_complex_ts(self): for rev_dtype in [complex64,complex128]: delta_t = self.delta # Don't do separate even/odd tests for complex inarr = ts(self.in_c2c_rev,dtype=rev_dtype,delta_t=delta_t,epoch=self.epoch) delta_f = 1.0/(delta_t*len(self.out_c2c_rev)) outexp = fs(self.out_c2c_rev,dtype=rev_dtype,delta_f=delta_f,epoch=self.epoch) outexp *= delta_t _test_ifft(self,inarr,outexp,self.tdict[rev_dtype]) # Random---we don't do that in 'reverse' tests, since both # directions are already tested in forward, and if we just passed # in arrays in the other order we'd only get exceptions # # LAL doesn't have reverse FFT funcs starting from a TimeSeries, so # we skip those tests as well. # # Check that exceptions are raised. Need input and # output arrays; just reuse inarr and outexp (values won't # matter, we're just checking exceptions). output_args = {"delta_f": self.delta, "epoch": self.epoch} _test_raise_excep_ifft(self,inarr,outexp,output_args) def test_rev_complex_fs(self): for rev_dtype in [complex64,complex128]: delta_f = self.delta # Don't do separate even/odd tests for complex inarr = fs(self.in_c2c_rev,dtype=rev_dtype,delta_f=delta_f,epoch=self.epoch) delta_t = 1.0/(delta_f*len(self.out_c2c_rev)) outexp = ts(self.out_c2c_rev,dtype=rev_dtype,delta_t=delta_t,epoch=self.epoch) outexp *= delta_f _test_ifft(self,inarr,outexp,self.tdict[rev_dtype]) # Random---we don't do that in 'reverse' tests, since both # directions are already tested in forward, and if we just passed # in arrays in the other order we'd only get exceptions # # However, we do still generate the arrays for T/F series, so that we may # do the LAL comparison test. As usual, we then delete those arrays. # # COMMENTED OUT: The LAL Complex TimeFreqFFT and FreqTimeFFT functions perform # a repacking of data because they seem to assume that the array represents both # positive and negative frequencies. We don't do this, so we don't compare. #rand_inarr = fs(zeros(self.rand_len_c,dtype=rev_dtype),epoch=self.epoch, # delta_f=self.delta) #rand_outarr = ts(zeros(self.rand_len_c,dtype=rev_dtype),epoch=self.epoch, # delta_t=self.delta) #_test_lal_tf_ifft(self,rand_inarr,rand_outarr,self.tdict[rev_dtype]) #del rand_inarr #del rand_outarr # # Check that exceptions are raised. Need input and # output arrays; just reuse inarr and outexp (values won't # matter, we're just checking exceptions). output_args = {"delta_t": self.delta, "epoch": self.epoch} _test_raise_excep_ifft(self,inarr,outexp,output_args)