def test_is_byte_aligned(self): a = empty_aligned(100) self.assertTrue(is_byte_aligned(a, get_expected_alignment(None))) a = empty_aligned(100, n=16) self.assertTrue(is_byte_aligned(a, n=16)) a = empty_aligned(100, n=5) self.assertTrue(is_byte_aligned(a, n=5)) a = empty_aligned(100, dtype="float32", n=16)[1:] self.assertFalse(is_byte_aligned(a, n=16)) self.assertTrue(is_byte_aligned(a, n=4))
def test_is_byte_aligned(self): a = empty_aligned(100) self.assertTrue(is_byte_aligned(a, get_expected_alignment(None))) a = empty_aligned(100, n=16) self.assertTrue(is_byte_aligned(a, n=16)) a = empty_aligned(100, n=5) self.assertTrue(is_byte_aligned(a, n=5)) a = empty_aligned(100, dtype='float32', n=16)[1:] self.assertFalse(is_byte_aligned(a, n=16)) self.assertTrue(is_byte_aligned(a, n=4))
def set_data(self, self_array, array, shape, dtype, copy=True, name=None): """ :param self_array: array owned by the current instance (either self.data_in or self.data_out). :type: numpy.ndarray :param self_array: data to set :type: numpy.ndarray :type tuple shape: shape of the array :param dtype: type of the array :type: numpy.dtype :param bool copy: should we copy the array :param str name: name of the array Copies are avoided when possible. """ self.check_array(array, shape, dtype) if id(self.refs[name]) == id(array): # nothing to do: fft is performed on self.data_in or self.data_out arr_to_use = self.refs[name] if self.check_alignment and not(pyfftw.is_byte_aligned(array)): # If the array is not properly aligned, # create a temp. array copy it to self.data_in or self.data_out self_array[:] = array[:] arr_to_use = self_array else: # If the array is properly aligned, use it directly if copy: arr_to_use = np.copy(array) else: arr_to_use = array return arr_to_use
def check_array(self, array, shape, dtype, copy=True): """ Check that a given array is compatible with the FFTW plans, in terms of alignment and data type. If the provided array does not meet any of the checks, a new array is returned. """ if array.shape != shape: raise ValueError("Invalid data shape: expected %s, got %s" % (shape, array.shape) ) if array.dtype != dtype: raise ValueError("Invalid data type: expected %s, got %s" % (dtype, array.dtype) ) if self.check_alignment and not(pyfftw.is_byte_aligned(array)): array2 = pyfftw.zeros_aligned(self.shape, dtype=self.dtype_in) np.copyto(array2, array) else: if copy: array2 = np.copy(array) else: array2 = array return array2
def check_array(self, array, shape, dtype, copy=True): """ Check that a given array is compatible with the FFTW plans, in terms of alignment and data type. If the provided array does not meet any of the checks, a new array is returned. """ if array.shape != shape: raise ValueError("Invalid data shape: expected %s, got %s" % (shape, array.shape)) if array.dtype != dtype: raise ValueError("Invalid data type: expected %s, got %s" % (dtype, array.dtype)) if self.check_alignment and not (pyfftw.is_byte_aligned(array)): array2 = pyfftw.zeros_aligned(self.shape, dtype=self.dtype_in) np.copyto(array2, array) else: if copy: array2 = np.copy(array) else: array2 = array return array2
def _Xfftn(a, s, axes, overwrite_input, planner_effort, threads, auto_align_input, auto_contiguous, avoid_copy, inverse, real, normalise_idft=True, ortho=False): '''Generic transform interface for all the transforms. No defaults exist. The transform must be specified exactly. ''' a_orig = a invreal = inverse and real if inverse: direction = 'FFTW_BACKWARD' else: direction = 'FFTW_FORWARD' if planner_effort not in _valid_efforts: raise ValueError('Invalid planner effort: ', planner_effort) s, axes = _cook_nd_args(a, s, axes, invreal) input_shape, output_shape = _compute_array_shapes( a, s, axes, inverse, real) a_is_complex = numpy.iscomplexobj(a) # Make the input dtype correct by transforming to an available type if a.dtype.char not in _rc_dtype_pairs: dtype = _default_dtype if a.dtype == numpy.dtype('float16') and '32' in pyfftw._supported_types: # convert half-precision to single precision, if available dtype = numpy.dtype('float32') # warn when losing precision but not when using a higher precision if dtype.itemsize < a.dtype.itemsize: warnings.warn("Narrowing conversion from %s to %s precision" % (a.dtype, dtype)) if not real or inverse: # It's going to be complex dtype = numpy.dtype(_rc_dtype_pairs[dtype.char]) # finally convert the input array a = numpy.asarray(a, dtype=dtype) elif not (real and not inverse) and not a_is_complex: # We need to make it a complex dtype a = numpy.asarray(a, dtype=_rc_dtype_pairs[a.dtype.char]) elif (real and not inverse) and a_is_complex: # It should be real a = numpy.asarray(a, dtype=_rc_dtype_pairs[a.dtype.char]) # Make the output dtype correct if not real: output_dtype = a.dtype else: output_dtype = _rc_dtype_pairs[a.dtype.char] if not avoid_copy: a_copy = a.copy() output_array = pyfftw.empty_aligned(output_shape, output_dtype) flags = [planner_effort] if not auto_align_input: flags.append('FFTW_UNALIGNED') if overwrite_input: flags.append('FFTW_DESTROY_INPUT') if not a.shape == input_shape: if avoid_copy: raise ValueError('Cannot avoid copy: ' 'The transform shape is not the same as the array size. ' '(from avoid_copy flag)') # This means we need to use an _FFTWWrapper object # and so need to create slicers. update_input_array_slicer, FFTW_array_slicer = ( _setup_input_slicers(a.shape, input_shape)) # Also, the input array will be a different shape to the shape of # `a`, so we need to create a new array. input_array = pyfftw.empty_aligned(input_shape, a.dtype) FFTW_object = _FFTWWrapper(input_array, output_array, axes, direction, flags, threads, input_array_slicer=update_input_array_slicer, FFTW_array_slicer=FFTW_array_slicer, normalise_idft=normalise_idft, ortho=ortho) # We copy the data back into the internal FFTW object array internal_array = FFTW_object.input_array internal_array[:] = 0 internal_array[FFTW_array_slicer] = ( a_copy[update_input_array_slicer]) else: # Otherwise we can use `a` as-is input_array = a if auto_contiguous: # We only need to create a new array if it's not already # contiguous if not (a.flags['C_CONTIGUOUS'] or a.flags['F_CONTIGUOUS']): if avoid_copy: raise ValueError('Cannot avoid copy: ' 'The input array is not contiguous and ' 'auto_contiguous is set. (from avoid_copy flag)') input_array = pyfftw.empty_aligned(a.shape, a.dtype) if (auto_align_input and not pyfftw.is_byte_aligned(input_array)): if avoid_copy: raise ValueError('Cannot avoid copy: ' 'The input array is not aligned and ' 'auto_align is set. (from avoid_copy flag)') input_array = pyfftw.byte_align(input_array) FFTW_object = pyfftw.FFTW(input_array, output_array, axes, direction, flags, threads, normalise_idft=normalise_idft, ortho=ortho) if not avoid_copy: # Copy the data back into the (likely) destroyed array FFTW_object.input_array[:] = a_copy return FFTW_object
def _Xfftn(a, s, axes, overwrite_input, planner_effort, threads, auto_align_input, auto_contiguous, avoid_copy, inverse, real, normalise_idft=True, ortho=False, real_direction_flag=None): '''Generic transform interface for all the transforms. No defaults exist. The transform must be specified exactly. The argument ``real_direction_flag`` is a slight exception to this rule: for backwards compatibility this function defaults to standard Fourier transforms (and not the specialized real to real variants). If this flag is set to one of the standard real transform types (e.g., 'FFTW_RODFT00') then the arguments ``inverse`` and ``real`` are ignored. ''' a_orig = a invreal = inverse and real if real_direction_flag is not None: direction = real_direction_flag real_to_real = True elif inverse: direction = 'FFTW_BACKWARD' real_to_real = False else: direction = 'FFTW_FORWARD' real_to_real = False if planner_effort not in _valid_efforts: raise ValueError('Invalid planner effort: ', planner_effort) s, axes = _cook_nd_args(a, s, axes, invreal) input_shape, output_shape = _compute_array_shapes( a, s, axes, inverse, real) a_is_complex = numpy.iscomplexobj(a) # Make the input dtype correct by transforming to an available type if real_to_real: if a.dtype not in _real_to_real_dtypes: a = numpy.asarray(a, dtype=_default_dtype) else: if a.dtype.char not in _rc_dtype_pairs: dtype = _default_dtype if a.dtype == numpy.dtype('float16') and '32' in pyfftw._supported_types: # convert half-precision to single precision, if available dtype = numpy.dtype('float32') # warn when losing precision but not when using a higher precision if dtype.itemsize < a.dtype.itemsize: warnings.warn("Narrowing conversion from %s to %s precision" % (a.dtype, dtype)) if not real or inverse: # It's going to be complex dtype = numpy.dtype(_rc_dtype_pairs[dtype.char]) # finally convert the input array a = numpy.asarray(a, dtype=dtype) elif not (real and not inverse) and not a_is_complex: # We need to make it a complex dtype a = numpy.asarray(a, dtype=_rc_dtype_pairs[a.dtype.char]) elif (real and not inverse) and a_is_complex: # It should be real a = numpy.asarray(a, dtype=_rc_dtype_pairs[a.dtype.char]) # Make the output dtype correct if not real: # 'real' implies c2r or r2c; hence 'not real' means r2r or c2c. output_dtype = a.dtype else: output_dtype = _rc_dtype_pairs[a.dtype.char] if not avoid_copy: a_copy = a.copy() output_array = pyfftw.empty_aligned(output_shape, output_dtype) flags = [planner_effort] if not auto_align_input: flags.append('FFTW_UNALIGNED') if overwrite_input: flags.append('FFTW_DESTROY_INPUT') if not a.shape == input_shape: if avoid_copy: raise ValueError('Cannot avoid copy: ' 'The transform shape is not the same as the array size. ' '(from avoid_copy flag)') # This means we need to use an _FFTWWrapper object # and so need to create slicers. update_input_array_slicer, FFTW_array_slicer = ( _setup_input_slicers(a.shape, input_shape)) # Also, the input array will be a different shape to the shape of # `a`, so we need to create a new array. input_array = pyfftw.empty_aligned(input_shape, a.dtype) FFTW_object = _FFTWWrapper(input_array, output_array, axes, direction, flags, threads, input_array_slicer=update_input_array_slicer, FFTW_array_slicer=FFTW_array_slicer, normalise_idft=normalise_idft, ortho=ortho) # We copy the data back into the internal FFTW object array internal_array = FFTW_object.input_array internal_array[:] = 0 internal_array[FFTW_array_slicer] = ( a_copy[update_input_array_slicer]) else: # Otherwise we can use `a` as-is input_array = a if auto_contiguous: # We only need to create a new array if it's not already # contiguous if not (a.flags['C_CONTIGUOUS'] or a.flags['F_CONTIGUOUS']): if avoid_copy: raise ValueError('Cannot avoid copy: ' 'The input array is not contiguous and ' 'auto_contiguous is set. (from avoid_copy flag)') input_array = pyfftw.empty_aligned(a.shape, a.dtype) if (auto_align_input and not pyfftw.is_byte_aligned(input_array)): if avoid_copy: raise ValueError('Cannot avoid copy: ' 'The input array is not aligned and ' 'auto_align is set. (from avoid_copy flag)') input_array = pyfftw.byte_align(input_array) FFTW_object = pyfftw.FFTW(input_array, output_array, axes, direction, flags, threads, normalise_idft=normalise_idft, ortho=ortho) if not avoid_copy: # Copy the data back into the (likely) destroyed array FFTW_object.input_array[:] = a_copy return FFTW_object
def _Xfftn(a, s, axes, overwrite_input, planner_effort, threads, auto_align_input, auto_contiguous, avoid_copy, inverse, real): '''Generic transform interface for all the transforms. No defaults exist. The transform must be specified exactly. ''' a_orig = a invreal = inverse and real if inverse: direction = 'FFTW_BACKWARD' else: direction = 'FFTW_FORWARD' if planner_effort not in _valid_efforts: raise ValueError('Invalid planner effort: ', planner_effort) s, axes = _cook_nd_args(a, s, axes, invreal) input_shape, output_shape = _compute_array_shapes( a, s, axes, inverse, real) a_is_complex = numpy.iscomplexobj(a) # Make the input dtype correct if a.dtype.char not in _rc_dtype_pairs: if a.dtype == numpy.dtype('float16'): # convert half-precision to single precision rather than double if not real or inverse: a = numpy.asarray( a, dtype=_rc_dtype_pairs[numpy.dtype('float32').char]) else: a = numpy.asarray(a, dtype=numpy.dtype('float32').char) else: # We make it the default dtype if not real or inverse: # It's going to be complex a = numpy.asarray( a, dtype=_rc_dtype_pairs[_default_dtype.char]) else: a = numpy.asarray(a, dtype=_default_dtype) elif not (real and not inverse) and not a_is_complex: # We need to make it a complex dtype a = numpy.asarray(a, dtype=_rc_dtype_pairs[a.dtype.char]) elif (real and not inverse) and a_is_complex: # It should be real a = numpy.asarray(a, dtype=_rc_dtype_pairs[a.dtype.char]) # Make the output dtype correct if not real: output_dtype = a.dtype else: output_dtype = _rc_dtype_pairs[a.dtype.char] if not avoid_copy: a_copy = a.copy() output_array = pyfftw.empty_aligned(output_shape, output_dtype) flags = [planner_effort] if not auto_align_input: flags.append('FFTW_UNALIGNED') if overwrite_input: flags.append('FFTW_DESTROY_INPUT') if not a.shape == input_shape: if avoid_copy: raise ValueError('Cannot avoid copy: ' 'The transform shape is not the same as the array size. ' '(from avoid_copy flag)') # This means we need to use an _FFTWWrapper object # and so need to create slicers. update_input_array_slicer, FFTW_array_slicer = ( _setup_input_slicers(a.shape, input_shape)) # Also, the input array will be a different shape to the shape of # `a`, so we need to create a new array. input_array = pyfftw.empty_aligned(input_shape, a.dtype) FFTW_object = _FFTWWrapper(input_array, output_array, axes, direction, flags, threads, input_array_slicer=update_input_array_slicer, FFTW_array_slicer=FFTW_array_slicer) # We copy the data back into the internal FFTW object array internal_array = FFTW_object.input_array internal_array[:] = 0 internal_array[FFTW_array_slicer] = ( a_copy[update_input_array_slicer]) else: # Otherwise we can use `a` as-is input_array = a if auto_contiguous: # We only need to create a new array if it's not already # contiguous if not (a.flags['C_CONTIGUOUS'] or a.flags['F_CONTIGUOUS']): if avoid_copy: raise ValueError('Cannot avoid copy: ' 'The input array is not contiguous and ' 'auto_contiguous is set. (from avoid_copy flag)') input_array = pyfftw.empty_aligned(a.shape, a.dtype) if (auto_align_input and not pyfftw.is_byte_aligned(input_array)): if avoid_copy: raise ValueError('Cannot avoid copy: ' 'The input array is not aligned and ' 'auto_align is set. (from avoid_copy flag)') input_array = pyfftw.byte_align(input_array) FFTW_object = pyfftw.FFTW(input_array, output_array, axes, direction, flags, threads) if not avoid_copy: # Copy the data back into the (likely) destroyed array FFTW_object.input_array[:] = a_copy return FFTW_object
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()