def create_test_arrays(self, input_shape, output_shape, axes=None): a = self.input_dtype(numpy.random.randn(*input_shape) +1j*numpy.random.randn(*input_shape)) b = self.output_dtype(numpy.random.randn(*output_shape)) # We fill a by doing the forward FFT from b. # This means that the relevant bits that should be purely # real will be (for example the zero freq component). # This is easier than writing a complicate system to work it out. try: if axes == None: fft = FFTW(b,a,direction='FFTW_FORWARD') else: fft = FFTW(b,a,direction='FFTW_FORWARD', axes=axes) b[:] = self.output_dtype(numpy.random.randn(*output_shape)) fft.execute() scaling = numpy.prod(numpy.array(a.shape)) a = self.input_dtype(a/scaling) except ValueError: # In this case, we assume that it was meant to error, # so we can return what we want. pass b = self.output_dtype(numpy.random.randn(*output_shape)) return a, b
def create_test_arrays(self, input_shape, output_shape, axes=None): a = self.input_dtype( numpy.random.randn(*input_shape) + 1j * numpy.random.randn(*input_shape)) b = self.output_dtype(numpy.random.randn(*output_shape)) # We fill a by doing the forward FFT from b. # This means that the relevant bits that should be purely # real will be (for example the zero freq component). # This is easier than writing a complicate system to work it out. try: if axes == None: fft = FFTW(b, a, direction='FFTW_FORWARD') else: fft = FFTW(b, a, direction='FFTW_FORWARD', axes=axes) b[:] = self.output_dtype(numpy.random.randn(*output_shape)) fft.execute() scaling = numpy.prod(numpy.array(a.shape)) a = self.input_dtype(a / scaling) except ValueError: # In this case, we assume that it was meant to error, # so we can return what we want. pass b = self.output_dtype(numpy.random.randn(*output_shape)) return a, b
def test_default_args(self): in_shape = self.input_shapes['2d'] out_shape = self.output_shapes['2d'] a, b = self.create_test_arrays(in_shape, out_shape) fft = FFTW(a, b) fft.execute() ref_b = self.reference_fftn(a, axes=(-1, )) self.assertTrue(numpy.allclose(b, ref_b, rtol=1e-2, atol=1e-3))
def test_default_args(self): in_shape = self.input_shapes['2d'] out_shape = self.output_shapes['2d'] a, b = self.create_test_arrays(in_shape, out_shape) fft = FFTW(a,b) fft.execute() ref_b = self.reference_fftn(a, axes=(-1,)) self.assertTrue(numpy.allclose(b, ref_b, rtol=1e-2, atol=1e-3))
def run_validate_fft(self, a, b, axes, fft=None, ifft=None, force_unaligned_data=False, create_array_copies=True, threads=1, flags=('FFTW_ESTIMATE',)): ''' *** EVERYTHING IS FLIPPED AROUND BECAUSE WE ARE VALIDATING AN INVERSE FFT *** Run a validation of the FFTW routines for the passed pair of arrays, a and b, and the axes argument. a and b are assumed to be the same shape (but not necessarily the same layout in memory). fft and ifft, if passed, should be instantiated FFTW objects. If force_unaligned_data is True, the flag FFTW_UNALIGNED will be passed to the fftw routines. ''' if create_array_copies: # Don't corrupt the original mutable arrays a = a.copy() b = b.copy() a_orig = a.copy() flags = list(flags) if force_unaligned_data: flags.append('FFTW_UNALIGNED') if ifft == None: ifft = FFTW(a, b, axes=axes, direction='FFTW_BACKWARD', flags=flags, threads=threads) else: ifft.update_arrays(a,b) if fft == None: fft = FFTW(b, a, axes=axes, direction='FFTW_FORWARD', flags=flags, threads=threads) else: fft.update_arrays(b,a) a[:] = a_orig # Test the inverse FFT by comparing it to the result from numpy.fft ifft.execute() a[:] = a_orig ref_b = self.reference_fftn(a, axes=axes) # The scaling is the product of the lengths of the fft along # the axes along which the fft is taken. scaling = numpy.prod(numpy.array(b.shape)[list(axes)]) self.assertEqual(ifft.N, scaling) self.assertEqual(fft.N, scaling) # This is actually quite a poor relative error, but it still # sometimes fails. I assume that numpy.fft has different internals # to fftw. self.assertTrue(numpy.allclose(b/scaling, ref_b, rtol=1e-2, atol=1e-3)) # Test the FFT by comparing the result to the starting # value (which is scaled as per FFTW being unnormalised). fft.execute() self.assertTrue(numpy.allclose(a/scaling, a_orig, rtol=1e-2, atol=1e-3)) return fft, ifft
class FFTWCallTest(unittest.TestCase): def __init__(self, *args, **kwargs): super(FFTWCallTest, self).__init__(*args, **kwargs) if not hasattr(self, 'assertRaisesRegex'): self.assertRaisesRegex = self.assertRaisesRegexp def setUp(self): self.input_array = empty_aligned((256, 512), dtype='complex128', n=16) self.output_array = empty_aligned((256, 512), dtype='complex128', n=16) self.fft = FFTW(self.input_array, self.output_array) self.input_array[:] = ( numpy.random.randn(*self.input_array.shape) + 1j * numpy.random.randn(*self.input_array.shape)) def test_call(self): '''Test a call to an instance of the class. ''' self.input_array[:] = ( numpy.random.randn(*self.input_array.shape) + 1j * numpy.random.randn(*self.input_array.shape)) output_array = self.fft() self.assertTrue(numpy.alltrue(output_array == self.output_array)) def test_call_with_positional_input_update(self): '''Test the class call with a positional input update. ''' input_array = byte_align( (numpy.random.randn(*self.input_array.shape) + 1j * numpy.random.randn(*self.input_array.shape)), n=16) output_array = self.fft(byte_align(input_array.copy(), n=16)).copy() self.fft.update_arrays(input_array, self.output_array) self.fft.execute() self.assertTrue(numpy.alltrue(output_array == self.output_array)) def test_call_with_keyword_input_update(self): '''Test the class call with a keyword input update. ''' input_array = byte_align( numpy.random.randn(*self.input_array.shape) + 1j * numpy.random.randn(*self.input_array.shape), n=16) output_array = self.fft( input_array=byte_align(input_array.copy(), n=16)).copy() self.fft.update_arrays(input_array, self.output_array) self.fft.execute() self.assertTrue(numpy.alltrue(output_array == self.output_array)) def test_call_with_keyword_output_update(self): '''Test the class call with a keyword output update. ''' output_array = byte_align( (numpy.random.randn(*self.output_array.shape) + 1j * numpy.random.randn(*self.output_array.shape)), n=16) returned_output_array = self.fft( output_array=byte_align(output_array.copy(), n=16)).copy() self.fft.update_arrays(self.input_array, output_array) self.fft.execute() self.assertTrue(numpy.alltrue(returned_output_array == output_array)) def test_call_with_positional_updates(self): '''Test the class call with a positional array updates. ''' input_array = byte_align( (numpy.random.randn(*self.input_array.shape) + 1j * numpy.random.randn(*self.input_array.shape)), n=16) output_array = byte_align( (numpy.random.randn(*self.output_array.shape) + 1j * numpy.random.randn(*self.output_array.shape)), n=16) returned_output_array = self.fft(byte_align(input_array.copy(), n=16), byte_align(output_array.copy(), n=16)).copy() self.fft.update_arrays(input_array, output_array) self.fft.execute() self.assertTrue(numpy.alltrue(returned_output_array == output_array)) def test_call_with_keyword_updates(self): '''Test the class call with a positional output update. ''' input_array = byte_align( (numpy.random.randn(*self.input_array.shape) + 1j * numpy.random.randn(*self.input_array.shape)), n=16) output_array = byte_align( (numpy.random.randn(*self.output_array.shape) + 1j * numpy.random.randn(*self.output_array.shape)), n=16) returned_output_array = self.fft( output_array=byte_align(output_array.copy(), n=16), input_array=byte_align(input_array.copy(), n=16)).copy() self.fft.update_arrays(input_array, output_array) self.fft.execute() self.assertTrue(numpy.alltrue(returned_output_array == output_array)) def test_call_with_different_input_dtype(self): '''Test the class call with an array with a different input dtype ''' input_array = byte_align(numpy.complex64( numpy.random.randn(*self.input_array.shape) + 1j * numpy.random.randn(*self.input_array.shape)), n=16) output_array = self.fft(byte_align(input_array.copy(), n=16)).copy() _input_array = byte_align(numpy.asarray(input_array, dtype=self.input_array.dtype), n=16) self.assertTrue(_input_array.dtype != input_array.dtype) self.fft.update_arrays(_input_array, self.output_array) self.fft.execute() self.assertTrue(numpy.alltrue(output_array == self.output_array)) def test_call_with_list_input(self): '''Test the class call with a list rather than an array ''' output_array = self.fft().copy() test_output_array = self.fft(self.input_array.tolist()).copy() self.assertTrue(numpy.alltrue(output_array == test_output_array)) def test_call_with_invalid_update(self): '''Test the class call with an invalid update. ''' new_shape = self.input_array.shape + (2, ) invalid_array = (numpy.random.randn(*new_shape) + 1j * numpy.random.randn(*new_shape)) self.assertRaises(ValueError, self.fft, *(), **{'output_array': invalid_array}) self.assertRaises(ValueError, self.fft, *(), **{'input_array': invalid_array}) def test_call_with_auto_input_alignment(self): '''Test the class call with a keyword input update. ''' input_array = (numpy.random.randn(*self.input_array.shape) + 1j * numpy.random.randn(*self.input_array.shape)) output_array = self.fft( input_array=byte_align(input_array.copy(), n=16)).copy() # Offset by one from 16 byte aligned to guarantee it's not # 16 byte aligned a = input_array a__ = empty_aligned(numpy.prod(a.shape) * a.itemsize + 1, dtype='int8', n=16) a_ = a__[1:].view(dtype=a.dtype).reshape(*a.shape) a_[:] = a # Just confirm that a usual update will fail self.assertRaisesRegex(ValueError, 'Invalid input alignment', self.fft.update_arrays, *(a_, self.output_array)) self.fft(a_, self.output_array) self.assertTrue(numpy.alltrue(output_array == self.output_array)) # now try with a single byte offset and SIMD off ar, ai = numpy.float32(numpy.random.randn(2, 257)) a = ar[1:] + 1j * ai[1:] b = a.copy() a_size = len(a.ravel()) * a.itemsize update_array = numpy.frombuffer(numpy.zeros(a_size + 1, dtype='int8')[1:].data, dtype=a.dtype).reshape(a.shape) fft = FFTW(a, b, flags=('FFTW_UNALIGNED', )) # Confirm that a usual update will fail (it's not on the # byte boundary) self.assertRaisesRegex(ValueError, 'Invalid input alignment', fft.update_arrays, *(update_array, b)) fft(update_array, b) def test_call_with_invalid_output_striding(self): '''Test the class call with an invalid strided output update. ''' # Add an extra dimension to bugger up the striding new_shape = self.output_array.shape + (2, ) output_array = byte_align(numpy.random.randn(*new_shape) + 1j * numpy.random.randn(*new_shape), n=16) self.assertRaisesRegex(ValueError, 'Invalid output striding', self.fft, **{'output_array': output_array[:, :, 1]}) def test_call_with_different_striding(self): '''Test the input update with different strides to internal array. ''' shape = self.input_array.shape + (2, ) input_array = byte_align(numpy.random.randn(*shape) + 1j * numpy.random.randn(*shape), n=16) fft = FFTW(input_array[:, :, 0], self.output_array) test_output_array = fft().copy() new_input_array = byte_align(input_array[:, :, 0].copy(), n=16) new_output = fft(new_input_array).copy() # Test the test! self.assertTrue( new_input_array.strides != input_array[:, :, 0].strides) self.assertTrue(numpy.alltrue(test_output_array == new_output)) def test_call_with_copy_with_missized_array_error(self): '''Force an input copy with a missized array. ''' shape = list(self.input_array.shape + (2, )) shape[0] += 1 input_array = byte_align(numpy.random.randn(*shape) + 1j * numpy.random.randn(*shape), n=16) fft = FFTW(self.input_array, self.output_array) self.assertRaisesRegex(ValueError, 'Invalid input shape', self.fft, **{'input_array': input_array[:, :, 0]}) def test_call_with_unaligned(self): '''Make sure the right thing happens with unaligned data. ''' input_array = (numpy.random.randn(*self.input_array.shape) + 1j * numpy.random.randn(*self.input_array.shape)) output_array = self.fft( input_array=byte_align(input_array.copy(), n=16)).copy() input_array = byte_align(input_array, n=16) output_array = byte_align(output_array, n=16) # Offset by one from 16 byte aligned to guarantee it's not # 16 byte aligned a = byte_align(input_array.copy(), n=16) a__ = empty_aligned(numpy.prod(a.shape) * a.itemsize + 1, dtype='int8', n=16) a_ = a__[1:].view(dtype=a.dtype).reshape(*a.shape) a_[:] = a # Create a different second array the same way b = byte_align(output_array.copy(), n=16) b__ = empty_aligned(numpy.prod(b.shape) * a.itemsize + 1, dtype='int8', n=16) b_ = b__[1:].view(dtype=b.dtype).reshape(*b.shape) b_[:] = a # Set up for the first array fft = FFTW(input_array, output_array) a_[:] = a output_array = fft().copy() # Check a_ is not aligned... self.assertRaisesRegex(ValueError, 'Invalid input alignment', self.fft.update_arrays, *(a_, output_array)) # and b_ too self.assertRaisesRegex(ValueError, 'Invalid output alignment', self.fft.update_arrays, *(input_array, b_)) # But it should still work with the a_ fft(a_) # However, trying to update the output will raise an error self.assertRaisesRegex(ValueError, 'Invalid output alignment', self.fft.update_arrays, *(input_array, b_)) # Same with SIMD off fft = FFTW(input_array, output_array, flags=('FFTW_UNALIGNED', )) fft(a_) self.assertRaisesRegex(ValueError, 'Invalid output alignment', self.fft.update_arrays, *(input_array, b_)) def test_call_with_normalisation_on(self): _input_array = empty_aligned((256, 512), dtype='complex128', n=16) ifft = FFTW(self.output_array, _input_array, direction='FFTW_BACKWARD') self.fft(normalise_idft=True) # Shouldn't make any difference ifft(normalise_idft=True) self.assertTrue(numpy.allclose(self.input_array, _input_array)) def test_call_with_normalisation_off(self): _input_array = empty_aligned((256, 512), dtype='complex128', n=16) ifft = FFTW(self.output_array, _input_array, direction='FFTW_BACKWARD') self.fft(normalise_idft=True) # Shouldn't make any difference ifft(normalise_idft=False) _input_array /= ifft.N self.assertTrue(numpy.allclose(self.input_array, _input_array)) def test_call_with_normalisation_default(self): _input_array = empty_aligned((256, 512), dtype='complex128', n=16) ifft = FFTW(self.output_array, _input_array, direction='FFTW_BACKWARD') self.fft() ifft() # Scaling is performed by default self.assertTrue(numpy.allclose(self.input_array, _input_array)) def test_call_with_normalisation_precision(self): '''The normalisation should use a double precision scaling. ''' # Should be the case for double inputs... _input_array = empty_aligned((256, 512), dtype='complex128', n=16) ifft = FFTW(self.output_array, _input_array, direction='FFTW_BACKWARD') ref_output = ifft(normalise_idft=False).copy() / numpy.float64(ifft.N) test_output = ifft(normalise_idft=True).copy() self.assertTrue(numpy.alltrue(ref_output == test_output)) # ... and single inputs. _input_array = empty_aligned((256, 512), dtype='complex64', n=16) ifft = FFTW(numpy.array(self.output_array, _input_array.dtype), _input_array, direction='FFTW_BACKWARD') ref_output = ifft(normalise_idft=False).copy() / numpy.float64(ifft.N) test_output = ifft(normalise_idft=True).copy() self.assertTrue(numpy.alltrue(ref_output == test_output))
def run_validate_fft(self, a, b, axes, fft=None, ifft=None, force_unaligned_data=False, create_array_copies=True, threads=1, flags=('FFTW_ESTIMATE', )): ''' Run a validation of the FFTW routines for the passed pair of arrays, a and b, and the axes argument. a and b are assumed to be the same shape (but not necessarily the same layout in memory). fft and ifft, if passed, should be instantiated FFTW objects. If force_unaligned_data is True, the flag FFTW_UNALIGNED will be passed to the fftw routines. The threads argument runs the validation with multiple threads. flags is passed to the creation of the FFTW object. ''' if create_array_copies: # Don't corrupt the original mutable arrays a = a.copy() b = b.copy() a_orig = a.copy() flags = list(flags) if force_unaligned_data: flags.append('FFTW_UNALIGNED') if fft == None: fft = FFTW(a, b, axes=axes, direction='FFTW_FORWARD', flags=flags, threads=threads) else: fft.update_arrays(a, b) if ifft == None: ifft = FFTW(b, a, axes=axes, direction='FFTW_BACKWARD', flags=flags, threads=threads) else: ifft.update_arrays(b, a) a[:] = a_orig # Test the forward FFT by comparing it to the result from numpy.fft fft.execute() ref_b = self.reference_fftn(a, axes=axes) # This is actually quite a poor relative error, but it still # sometimes fails. I assume that numpy.fft has different internals # to fftw. self.assertTrue(numpy.allclose(b, ref_b, rtol=1e-2, atol=1e-3)) # Test the inverse FFT by comparing the result to the starting # value (which is scaled as per FFTW being unnormalised). ifft.execute() # The scaling is the product of the lengths of the fft along # the axes along which the fft is taken. scaling = numpy.prod(numpy.array(a.shape)[list(axes)]) self.assertEqual(ifft.N, scaling) self.assertEqual(fft.N, scaling) self.assertTrue( numpy.allclose(a / scaling, a_orig, rtol=1e-2, atol=1e-3)) return fft, ifft
def do_fft(self, inputa): outputa = np.zeros(self.fft_size, dtype=complex) fft_plan = FFTW(inputa, outputa, direction='FFTW_FORWARD') #fft_plan = FFTW.Plan(inputa, outputa, direction='forward', flags=['estimate']) fft_plan.execute() return (np.log10(np.abs(outputa)) * 20)[:self.fft_size/2]
def test_differing_aligned_arrays_update(self): '''Test to see if the alignment code is working as expected ''' # Start by creating arrays that are only on various byte # alignments (4, 16 and 32) _input_array = n_byte_align_empty( len(self.input_array.ravel())*2+5, 32, dtype='float32') _output_array = n_byte_align_empty( len(self.output_array.ravel())*2+5, 32, dtype='float32') _input_array[:] = 0 _output_array[:] = 0 input_array_4 = ( numpy.frombuffer(_input_array[1:-4].data, dtype='complex64') .reshape(self.input_array.shape)) output_array_4 = ( numpy.frombuffer(_output_array[1:-4].data, dtype='complex64') .reshape(self.output_array.shape)) input_array_16 = ( numpy.frombuffer(_input_array[4:-1].data, dtype='complex64') .reshape(self.input_array.shape)) output_array_16 = ( numpy.frombuffer(_output_array[4:-1].data, dtype='complex64') .reshape(self.output_array.shape)) input_array_32 = ( numpy.frombuffer(_input_array[:-5].data, dtype='complex64') .reshape(self.input_array.shape)) output_array_32 = ( numpy.frombuffer(_output_array[:-5].data, dtype='complex64') .reshape(self.output_array.shape)) input_arrays = {4: input_array_4, 16: input_array_16, 32: input_array_32} output_arrays = {4: output_array_4, 16: output_array_16, 32: output_array_32} alignments = (4, 16, 32) # Test the arrays are aligned on 4 bytes... self.assertTrue(is_n_byte_aligned(input_arrays[4], 4)) self.assertTrue(is_n_byte_aligned(output_arrays[4], 4)) # ...and on 16... self.assertFalse(is_n_byte_aligned(input_arrays[4], 16)) self.assertFalse(is_n_byte_aligned(output_arrays[4], 16)) self.assertTrue(is_n_byte_aligned(input_arrays[16], 16)) self.assertTrue(is_n_byte_aligned(output_arrays[16], 16)) # ...and on 32... self.assertFalse(is_n_byte_aligned(input_arrays[16], 32)) self.assertFalse(is_n_byte_aligned(output_arrays[16], 32)) self.assertTrue(is_n_byte_aligned(input_arrays[32], 32)) self.assertTrue(is_n_byte_aligned(output_arrays[32], 32)) if len(pyfftw.pyfftw._valid_simd_alignments) > 0: max_align = pyfftw.pyfftw._valid_simd_alignments[0] else: max_align = simd_alignment for in_align in alignments: for out_align in alignments: expected_align = min(in_align, out_align, max_align) fft = FFTW(input_arrays[in_align], output_arrays[out_align]) self.assertTrue(fft.input_alignment == expected_align) self.assertTrue(fft.output_alignment == expected_align) for update_align in alignments: if update_align < expected_align: # This should fail (not aligned properly) self.assertRaisesRegex(ValueError, 'Invalid input alignment', fft.update_arrays, input_arrays[update_align], output_arrays[out_align]) self.assertRaisesRegex(ValueError, 'Invalid output alignment', fft.update_arrays, input_arrays[in_align], output_arrays[update_align]) else: # This should work (and not segfault!) fft.update_arrays(input_arrays[update_align], output_arrays[out_align]) fft.update_arrays(input_arrays[in_align], output_arrays[update_align]) fft.execute()
class FFTWCallTest(unittest.TestCase): def __init__(self, *args, **kwargs): super(FFTWCallTest, self).__init__(*args, **kwargs) if not hasattr(self, "assertRaisesRegex"): self.assertRaisesRegex = self.assertRaisesRegexp def setUp(self): self.input_array = empty_aligned((256, 512), dtype="complex128", n=16) self.output_array = empty_aligned((256, 512), dtype="complex128", n=16) self.fft = FFTW(self.input_array, self.output_array) self.input_array[:] = numpy.random.randn(*self.input_array.shape) + 1j * numpy.random.randn( *self.input_array.shape ) def test_call(self): """Test a call to an instance of the class. """ self.input_array[:] = numpy.random.randn(*self.input_array.shape) + 1j * numpy.random.randn( *self.input_array.shape ) output_array = self.fft() self.assertTrue(numpy.alltrue(output_array == self.output_array)) def test_call_with_positional_input_update(self): """Test the class call with a positional input update. """ input_array = byte_align( (numpy.random.randn(*self.input_array.shape) + 1j * numpy.random.randn(*self.input_array.shape)), n=16 ) output_array = self.fft(byte_align(input_array.copy(), n=16)).copy() self.fft.update_arrays(input_array, self.output_array) self.fft.execute() self.assertTrue(numpy.alltrue(output_array == self.output_array)) def test_call_with_keyword_input_update(self): """Test the class call with a keyword input update. """ input_array = byte_align( numpy.random.randn(*self.input_array.shape) + 1j * numpy.random.randn(*self.input_array.shape), n=16 ) output_array = self.fft(input_array=byte_align(input_array.copy(), n=16)).copy() self.fft.update_arrays(input_array, self.output_array) self.fft.execute() self.assertTrue(numpy.alltrue(output_array == self.output_array)) def test_call_with_keyword_output_update(self): """Test the class call with a keyword output update. """ output_array = byte_align( (numpy.random.randn(*self.output_array.shape) + 1j * numpy.random.randn(*self.output_array.shape)), n=16 ) returned_output_array = self.fft(output_array=byte_align(output_array.copy(), n=16)).copy() self.fft.update_arrays(self.input_array, output_array) self.fft.execute() self.assertTrue(numpy.alltrue(returned_output_array == output_array)) def test_call_with_positional_updates(self): """Test the class call with a positional array updates. """ input_array = byte_align( (numpy.random.randn(*self.input_array.shape) + 1j * numpy.random.randn(*self.input_array.shape)), n=16 ) output_array = byte_align( (numpy.random.randn(*self.output_array.shape) + 1j * numpy.random.randn(*self.output_array.shape)), n=16 ) returned_output_array = self.fft( byte_align(input_array.copy(), n=16), byte_align(output_array.copy(), n=16) ).copy() self.fft.update_arrays(input_array, output_array) self.fft.execute() self.assertTrue(numpy.alltrue(returned_output_array == output_array)) def test_call_with_keyword_updates(self): """Test the class call with a positional output update. """ input_array = byte_align( (numpy.random.randn(*self.input_array.shape) + 1j * numpy.random.randn(*self.input_array.shape)), n=16 ) output_array = byte_align( (numpy.random.randn(*self.output_array.shape) + 1j * numpy.random.randn(*self.output_array.shape)), n=16 ) returned_output_array = self.fft( output_array=byte_align(output_array.copy(), n=16), input_array=byte_align(input_array.copy(), n=16) ).copy() self.fft.update_arrays(input_array, output_array) self.fft.execute() self.assertTrue(numpy.alltrue(returned_output_array == output_array)) def test_call_with_different_input_dtype(self): """Test the class call with an array with a different input dtype """ input_array = byte_align( numpy.complex64( numpy.random.randn(*self.input_array.shape) + 1j * numpy.random.randn(*self.input_array.shape) ), n=16, ) output_array = self.fft(byte_align(input_array.copy(), n=16)).copy() _input_array = byte_align(numpy.asarray(input_array, dtype=self.input_array.dtype), n=16) self.assertTrue(_input_array.dtype != input_array.dtype) self.fft.update_arrays(_input_array, self.output_array) self.fft.execute() self.assertTrue(numpy.alltrue(output_array == self.output_array)) def test_call_with_list_input(self): """Test the class call with a list rather than an array """ output_array = self.fft().copy() test_output_array = self.fft(self.input_array.tolist()).copy() self.assertTrue(numpy.alltrue(output_array == test_output_array)) def test_call_with_invalid_update(self): """Test the class call with an invalid update. """ new_shape = self.input_array.shape + (2,) invalid_array = numpy.random.randn(*new_shape) + 1j * numpy.random.randn(*new_shape) self.assertRaises(ValueError, self.fft, *(), **{"output_array": invalid_array}) self.assertRaises(ValueError, self.fft, *(), **{"input_array": invalid_array}) def test_call_with_auto_input_alignment(self): """Test the class call with a keyword input update. """ input_array = numpy.random.randn(*self.input_array.shape) + 1j * numpy.random.randn(*self.input_array.shape) output_array = self.fft(input_array=byte_align(input_array.copy(), n=16)).copy() # Offset by one from 16 byte aligned to guarantee it's not # 16 byte aligned a = input_array a__ = empty_aligned(numpy.prod(a.shape) * a.itemsize + 1, dtype="int8", n=16) a_ = a__[1:].view(dtype=a.dtype).reshape(*a.shape) a_[:] = a # Just confirm that a usual update will fail self.assertRaisesRegex(ValueError, "Invalid input alignment", self.fft.update_arrays, *(a_, self.output_array)) self.fft(a_, self.output_array) self.assertTrue(numpy.alltrue(output_array == self.output_array)) # now try with a single byte offset and SIMD off ar, ai = numpy.float32(numpy.random.randn(2, 257)) a = ar[1:] + 1j * ai[1:] b = a.copy() a_size = len(a.ravel()) * a.itemsize update_array = numpy.frombuffer(numpy.zeros(a_size + 1, dtype="int8")[1:].data, dtype=a.dtype).reshape(a.shape) fft = FFTW(a, b, flags=("FFTW_UNALIGNED",)) # Confirm that a usual update will fail (it's not on the # byte boundary) self.assertRaisesRegex(ValueError, "Invalid input alignment", fft.update_arrays, *(update_array, b)) fft(update_array, b) def test_call_with_invalid_output_striding(self): """Test the class call with an invalid strided output update. """ # Add an extra dimension to bugger up the striding new_shape = self.output_array.shape + (2,) output_array = byte_align(numpy.random.randn(*new_shape) + 1j * numpy.random.randn(*new_shape), n=16) self.assertRaisesRegex( ValueError, "Invalid output striding", self.fft, **{"output_array": output_array[:, :, 1]} ) def test_call_with_different_striding(self): """Test the input update with different strides to internal array. """ shape = self.input_array.shape + (2,) input_array = byte_align(numpy.random.randn(*shape) + 1j * numpy.random.randn(*shape), n=16) fft = FFTW(input_array[:, :, 0], self.output_array) test_output_array = fft().copy() new_input_array = byte_align(input_array[:, :, 0].copy(), n=16) new_output = fft(new_input_array).copy() # Test the test! self.assertTrue(new_input_array.strides != input_array[:, :, 0].strides) self.assertTrue(numpy.alltrue(test_output_array == new_output)) def test_call_with_copy_with_missized_array_error(self): """Force an input copy with a missized array. """ shape = list(self.input_array.shape + (2,)) shape[0] += 1 input_array = byte_align(numpy.random.randn(*shape) + 1j * numpy.random.randn(*shape), n=16) fft = FFTW(self.input_array, self.output_array) self.assertRaisesRegex(ValueError, "Invalid input shape", self.fft, **{"input_array": input_array[:, :, 0]}) def test_call_with_unaligned(self): """Make sure the right thing happens with unaligned data. """ input_array = numpy.random.randn(*self.input_array.shape) + 1j * numpy.random.randn(*self.input_array.shape) output_array = self.fft(input_array=byte_align(input_array.copy(), n=16)).copy() input_array = byte_align(input_array, n=16) output_array = byte_align(output_array, n=16) # Offset by one from 16 byte aligned to guarantee it's not # 16 byte aligned a = byte_align(input_array.copy(), n=16) a__ = empty_aligned(numpy.prod(a.shape) * a.itemsize + 1, dtype="int8", n=16) a_ = a__[1:].view(dtype=a.dtype).reshape(*a.shape) a_[:] = a # Create a different second array the same way b = byte_align(output_array.copy(), n=16) b__ = empty_aligned(numpy.prod(b.shape) * a.itemsize + 1, dtype="int8", n=16) b_ = b__[1:].view(dtype=b.dtype).reshape(*b.shape) b_[:] = a # Set up for the first array fft = FFTW(input_array, output_array) a_[:] = a output_array = fft().copy() # Check a_ is not aligned... self.assertRaisesRegex(ValueError, "Invalid input alignment", self.fft.update_arrays, *(a_, output_array)) # and b_ too self.assertRaisesRegex(ValueError, "Invalid output alignment", self.fft.update_arrays, *(input_array, b_)) # But it should still work with the a_ fft(a_) # However, trying to update the output will raise an error self.assertRaisesRegex(ValueError, "Invalid output alignment", self.fft.update_arrays, *(input_array, b_)) # Same with SIMD off fft = FFTW(input_array, output_array, flags=("FFTW_UNALIGNED",)) fft(a_) self.assertRaisesRegex(ValueError, "Invalid output alignment", self.fft.update_arrays, *(input_array, b_)) def test_call_with_normalisation_on(self): _input_array = empty_aligned((256, 512), dtype="complex128", n=16) ifft = FFTW(self.output_array, _input_array, direction="FFTW_BACKWARD") self.fft(normalise_idft=True) # Shouldn't make any difference ifft(normalise_idft=True) self.assertTrue(numpy.allclose(self.input_array, _input_array)) def test_call_with_normalisation_off(self): _input_array = empty_aligned((256, 512), dtype="complex128", n=16) ifft = FFTW(self.output_array, _input_array, direction="FFTW_BACKWARD") self.fft(normalise_idft=True) # Shouldn't make any difference ifft(normalise_idft=False) _input_array /= ifft.N self.assertTrue(numpy.allclose(self.input_array, _input_array)) def test_call_with_normalisation_default(self): _input_array = empty_aligned((256, 512), dtype="complex128", n=16) ifft = FFTW(self.output_array, _input_array, direction="FFTW_BACKWARD") self.fft() ifft() # Scaling is performed by default self.assertTrue(numpy.allclose(self.input_array, _input_array)) def test_call_with_normalisation_precision(self): """The normalisation should use a double precision scaling. """ # Should be the case for double inputs... _input_array = empty_aligned((256, 512), dtype="complex128", n=16) ifft = FFTW(self.output_array, _input_array, direction="FFTW_BACKWARD") ref_output = ifft(normalise_idft=False).copy() / numpy.float64(ifft.N) test_output = ifft(normalise_idft=True).copy() self.assertTrue(numpy.alltrue(ref_output == test_output)) # ... and single inputs. _input_array = empty_aligned((256, 512), dtype="complex64", n=16) ifft = FFTW(numpy.array(self.output_array, _input_array.dtype), _input_array, direction="FFTW_BACKWARD") ref_output = ifft(normalise_idft=False).copy() / numpy.float64(ifft.N) test_output = ifft(normalise_idft=True).copy() self.assertTrue(numpy.alltrue(ref_output == test_output))
def test_differing_aligned_arrays_update(self): '''Test to see if the alignment code is working as expected ''' # Start by creating arrays that are only on various byte # alignments (4, 16 and 32) _input_array = empty_aligned(len(self.input_array.ravel())*2+5, dtype='float32', n=32) _output_array = empty_aligned(len(self.output_array.ravel())*2+5, dtype='float32', n=32) _input_array[:] = 0 _output_array[:] = 0 input_array_4 = ( numpy.frombuffer(_input_array[1:-4].data, dtype='complex64') .reshape(self.input_array.shape)) output_array_4 = ( numpy.frombuffer(_output_array[1:-4].data, dtype='complex64') .reshape(self.output_array.shape)) input_array_16 = ( numpy.frombuffer(_input_array[4:-1].data, dtype='complex64') .reshape(self.input_array.shape)) output_array_16 = ( numpy.frombuffer(_output_array[4:-1].data, dtype='complex64') .reshape(self.output_array.shape)) input_array_32 = ( numpy.frombuffer(_input_array[:-5].data, dtype='complex64') .reshape(self.input_array.shape)) output_array_32 = ( numpy.frombuffer(_output_array[:-5].data, dtype='complex64') .reshape(self.output_array.shape)) input_arrays = {4: input_array_4, 16: input_array_16, 32: input_array_32} output_arrays = {4: output_array_4, 16: output_array_16, 32: output_array_32} alignments = (4, 16, 32) # Test the arrays are aligned on 4 bytes... self.assertTrue(is_byte_aligned(input_arrays[4], n=4)) self.assertTrue(is_byte_aligned(output_arrays[4], n=4)) # ...and on 16... self.assertFalse(is_byte_aligned(input_arrays[4], n=16)) self.assertFalse(is_byte_aligned(output_arrays[4], n=16)) self.assertTrue(is_byte_aligned(input_arrays[16], n=16)) self.assertTrue(is_byte_aligned(output_arrays[16], n=16)) # ...and on 32... self.assertFalse(is_byte_aligned(input_arrays[16], n=32)) self.assertFalse(is_byte_aligned(output_arrays[16], n=32)) self.assertTrue(is_byte_aligned(input_arrays[32], n=32)) self.assertTrue(is_byte_aligned(output_arrays[32], n=32)) if len(pyfftw.pyfftw._valid_simd_alignments) > 0: max_align = pyfftw.pyfftw._valid_simd_alignments[0] else: max_align = simd_alignment for in_align in alignments: for out_align in alignments: expected_align = min(in_align, out_align, max_align) fft = FFTW(input_arrays[in_align], output_arrays[out_align]) self.assertTrue(fft.input_alignment == expected_align) self.assertTrue(fft.output_alignment == expected_align) for update_align in alignments: if update_align < expected_align: # This should fail (not aligned properly) self.assertRaisesRegex(ValueError, 'Invalid input alignment', fft.update_arrays, input_arrays[update_align], output_arrays[out_align]) self.assertRaisesRegex(ValueError, 'Invalid output alignment', fft.update_arrays, input_arrays[in_align], output_arrays[update_align]) else: # This should work (and not segfault!) fft.update_arrays(input_arrays[update_align], output_arrays[out_align]) fft.update_arrays(input_arrays[in_align], output_arrays[update_align]) fft.execute()