Exemplo n.º 1
0
    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
Exemplo n.º 2
0
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))
Exemplo n.º 3
0
    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 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()
Exemplo n.º 5
0
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))
Exemplo n.º 6
0
    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()