Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/gsl/fft.pyx
8817 views
1
"""
2
Fast Fourier Transforms Using GSL
3
4
AUTHORS:
5
6
- William Stein (2006-9): initial file (radix2)
7
- D. Joyner (2006-10): Minor modifications (from radix2 to general case\
8
and some documentation).
9
- M. Hansen (2013-3): Fix radix2 backwards transformation
10
- L.F. Tabera Alonso (2013-3): Documentation
11
"""
12
13
#*****************************************************************************
14
# Copyright (C) 2006 William Stein <[email protected]>
15
#
16
# Distributed under the terms of the GNU General Public License (GPL)
17
#
18
# This code is distributed in the hope that it will be useful,
19
# but WITHOUT ANY WARRANTY; without even the implied warranty of
20
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21
# General Public License for more details.
22
#
23
# The full text of the GPL is available at:
24
#
25
# http://www.gnu.org/licenses/
26
#*****************************************************************************
27
28
include 'sage/ext/stdsage.pxi'
29
30
import sage.plot.all
31
import sage.libs.pari.all
32
from sage.rings.integer import Integer
33
from sage.rings.complex_number import ComplexNumber
34
35
def FastFourierTransform(size, base_ring=None):
36
"""
37
Create an array for fast Fourier transform conversion using gsl.
38
39
INPUT:
40
41
- ``size`` -- The size of the array
42
- ``base_ring`` -- Unused (2013-03)
43
44
EXAMPLES:
45
46
We create an array of the desired size::
47
48
sage: a = FastFourierTransform(8)
49
sage: a
50
[(0.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0)]
51
52
Now, set the values of the array::
53
54
sage: for i in range(8): a[i] = i + 1
55
sage: a
56
[(1.0, 0.0), (2.0, 0.0), (3.0, 0.0), (4.0, 0.0), (5.0, 0.0), (6.0, 0.0), (7.0, 0.0), (8.0, 0.0)]
57
58
We can perform the forward Fourier transform on the array::
59
60
sage: a.forward_transform()
61
sage: a #abs tol 1e-2
62
[(36.0, 0.0), (-4.00, 9.65), (-4.0, 4.0), (-3.99, 1.65), (-4.0, 0.0), (-4.0, -1.65), (-4.0, -4.0), (-3.99, -9.65)]
63
64
And backwards::
65
66
sage: a.backward_transform()
67
sage: a #abs tol 1e-2
68
[(8.0, 0.0), (16.0, 0.0), (24.0, 0.0), (32.0, 0.0), (40.0, 0.0), (48.0, 0.0), (56.0, 0.0), (64.0, 0.0)]
69
70
Other example::
71
72
sage: a = FastFourierTransform(128)
73
sage: for i in range(1, 11):
74
....: a[i] = 1
75
....: a[128-i] = 1
76
sage: a[:6:2]
77
[(0.0, 0.0), (1.0, 0.0), (1.0, 0.0)]
78
sage: a.plot().show(ymin=0)
79
sage: a.forward_transform()
80
sage: a.plot().show()
81
82
"""
83
return FastFourierTransform_complex(int(size))
84
85
FFT = FastFourierTransform
86
87
cdef class FastFourierTransform_base:
88
pass
89
90
cdef class FastFourierTransform_complex(FastFourierTransform_base):
91
"""
92
Wrapper class for GSL's fast Fourier transform.
93
"""
94
95
def __init__(self, size_t n, size_t stride=1):
96
"""
97
Create an array-like object of fixed size that will contain the vector to
98
apply the Fast Fourier Transform.
99
100
INPUT:
101
102
- ``n`` -- An integer, the size of the array
103
- ``stride`` -- The stride to be applied when manipulating the array.
104
105
EXAMPLES::
106
107
sage: a = FastFourierTransform(1) # indirect doctest
108
sage: a
109
[(0.0, 0.0)]
110
111
"""
112
self.n = n
113
self.stride = stride
114
self.data = <double*> sage_malloc(sizeof(double)*(2*n))
115
cdef int i
116
for i from 0 <= i < 2*n:
117
self.data[i] = 0
118
119
def __dealloc__(self):
120
"""
121
Frees allocated memory.
122
123
EXAMPLE::
124
125
sage: a = FastFourierTransform(128)
126
sage: del a
127
128
"""
129
sage_free(self.data)
130
131
def __len__(self):
132
"""
133
Return the size of the array.
134
135
OUTPUT: The size of the array.
136
137
EXAMPLE::
138
139
sage: a = FastFourierTransform(48)
140
sage: len(a)
141
48
142
143
"""
144
return self.n
145
146
def __setitem__(self, size_t i, xy):
147
"""
148
Assign a value to an index of the array. Currently the input has to be
149
en element that can be coerced to ``float` or a ``ComplexNumber`` element.
150
151
INPUT:
152
153
- ``i`` -- An integer peresenting the index.
154
- ``xy`` -- An object to store as `i`-th element of the array ``self[i]``.
155
156
EXAMPLE::
157
158
sage: I = CC(I)
159
sage: a = FastFourierTransform(4)
160
sage: a[0] = 1
161
sage: a[1] = I
162
sage: a[2] = 1+I
163
sage: a[3] = (2,2)
164
sage: a
165
[(1.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, 2.0)]
166
sage: I = CDF(I)
167
sage: a[1] = I
168
Traceback (most recent call last):
169
...
170
TypeError: Unable to convert 1.0*I to float; use abs() or real_part() as desired
171
172
"""
173
# just set real for now
174
if i < 0 or i >= self.n:
175
raise IndexError
176
if isinstance(xy, (tuple, ComplexNumber)):
177
self.data[2*i] = xy[0]
178
self.data[2*i+1] = xy[1]
179
else:
180
self.data[2*i] = xy
181
182
def __getitem__(self, i):
183
"""
184
Gets the `i`-th element of the array.
185
186
INPUT:
187
188
- ``i``: An integer.
189
190
OUTPUT:
191
192
- The `i`-th element of the array ``self[i]``.
193
194
EXAMPLES::
195
196
sage: a = FastFourierTransform(4)
197
sage: a[0]
198
(0.0, 0.0)
199
sage: a[0] = 1
200
sage: a[0] == (1,0)
201
True
202
203
"""
204
if isinstance(i, slice):
205
start, stop, step = i.indices(self.n)
206
return list(self)[start:stop:step]
207
else:
208
if i < 0 or i >= self.n:
209
raise IndexError
210
return self.data[2*i], self.data[2*i+1]
211
212
def __repr__(self):
213
"""
214
String representation of the array.
215
216
OUTPUT:
217
218
- A string representing this array. The complex numbers are
219
presented as a tuple of two float elements.
220
221
EXAMPLES::
222
223
sage: a = FastFourierTransform(4)
224
sage: for i in range(4): a[i] = i
225
sage: a
226
[(0.0, 0.0), (1.0, 0.0), (2.0, 0.0), (3.0, 0.0)]
227
228
"""
229
return str(list(self))
230
231
def _plot_polar(self, xmin, xmax, **args):
232
"""
233
Plot a slice of the array using polar coordinates.
234
235
INPUT:
236
237
- ``xmin`` -- The lower bound of the slice to plot.
238
- ``xmax`` -- The upper bound of the slice to plot.
239
- ``**args`` -- passed on to the line plotting function.
240
241
OUTPUT:
242
243
- A plot of the array interpreting each element as polar coordinates.
244
245
This method should not be called directly. See :meth:`plot` for the details.
246
247
EXAMPLE::
248
249
sage: a = FastFourierTransform(4)
250
sage: a._plot_polar(0,2)
251
252
"""
253
cdef int i
254
v = []
255
256
point = sage.plot.all.point
257
pi = sage.symbolic.constants.pi.n()
258
I = sage.symbolic.constants.I.n()
259
s = 1/(3*pi) # so arg gets scaled between -1/3 and 1/3.
260
261
for i from xmin <= i < xmax:
262
z = self.data[2*i] + I*self.data[2*i+1]
263
mag = z.abs()
264
arg = z.arg()*s
265
v.append(point((i,mag), hue=arg, **args))
266
return sum(v)
267
268
def _plot_rect(self, xmin, xmax, **args):
269
"""
270
Plot a slice of the array.
271
272
INPUT:
273
274
- ``xmin`` -- The lower bound of the slice to plot.
275
- ``xmax`` -- The upper bound of the slice to plot.
276
- ``**args`` -- passed on to the line plotting function.
277
278
OUTPUT:
279
280
- A plot of the array.
281
282
This method should not be called directly. See :meth:`plot` for the details.
283
284
EXAMPLE::
285
286
sage: a = FastFourierTransform(4)
287
sage: a._plot_rect(0,3)
288
289
"""
290
cdef int i
291
cdef double pr_x, x, h
292
v = []
293
294
point = sage.plot.all.point
295
296
for i from xmin <= i < xmax:
297
x = self.data[2*i]
298
h = self.data[2*i+1]
299
v.append(point((i,x), hue=h, **args))
300
return sum(v)
301
302
def plot(self, style='rect', xmin=None, xmax=None, **args):
303
"""
304
Plot a slice of the array.
305
306
- ``style`` -- Style of the plot, options are ``"rect"`` or ``"polar"``
307
- ``rect`` -- height represents real part, color represents
308
imaginary part.
309
- ``polar`` -- height represents absolute value, color
310
represents argument.
311
- ``xmin`` -- The lower bound of the slice to plot. 0 by default.
312
- ``xmax`` -- The upper bound of the slice to plot. ``len(self)`` by default.
313
- ``**args`` -- passed on to the line plotting function.
314
315
OUTPUT:
316
317
- A plot of the array.
318
319
EXAMPLE::
320
321
sage: a = FastFourierTransform(16)
322
sage: for i in range(16): a[i] = (random(),random())
323
sage: A = plot(a)
324
sage: B = plot(a, style='polar')
325
sage: type(A)
326
<class 'sage.plot.graphics.Graphics'>
327
sage: type(B)
328
<class 'sage.plot.graphics.Graphics'>
329
sage: a = FastFourierTransform(125)
330
sage: b = FastFourierTransform(125)
331
sage: for i in range(1, 60): a[i]=1
332
sage: for i in range(1, 60): b[i]=1
333
sage: a.forward_transform()
334
sage: a.inverse_transform()
335
sage: (a.plot()+b.plot())
336
337
"""
338
if xmin is None:
339
xmin = 0
340
else:
341
xmin = int(xmin)
342
if xmax is None:
343
xmax = self.n
344
else:
345
xmax = int(xmax)
346
if style == 'rect':
347
return self._plot_rect(xmin, xmax, **args)
348
elif style == 'polar':
349
return self._plot_polar(xmin, xmax, **args)
350
else:
351
raise ValueError, "unknown style '%s'"%style
352
353
def forward_transform(self):
354
"""
355
Compute the in-place forward Fourier transform of this data
356
using the Cooley-Tukey algorithm.
357
358
OUTPUT:
359
360
- None, the transformation is done in-place.
361
362
If the number of sample points in the input is a power of 2 then the
363
gsl function ``gsl_fft_complex_radix2_forward`` is automatically called.
364
Otherwise, ``gsl_fft_complex_forward`` is called.
365
366
EXAMPLES::
367
368
sage: a = FastFourierTransform(4)
369
sage: for i in range(4): a[i] = i
370
sage: a.forward_transform()
371
sage: a #abs tol 1e-2
372
[(6.0, 0.0), (-2.0, 2.0), (-2.0, 0.0), (-2.0, -2.0)]
373
374
"""
375
cdef gsl_fft_complex_wavetable * wt
376
cdef gsl_fft_complex_workspace * mem
377
N = Integer(self.n)
378
e = N.exact_log(2)
379
if N==2**e:
380
gsl_fft_complex_radix2_forward(self.data, self.stride, self.n)
381
else:
382
mem = gsl_fft_complex_workspace_alloc(self.n)
383
wt = gsl_fft_complex_wavetable_alloc(self.n)
384
gsl_fft_complex_forward(self.data, self.stride, self.n, wt, mem)
385
gsl_fft_complex_workspace_free(mem)
386
gsl_fft_complex_wavetable_free(wt)
387
388
def inverse_transform(self):
389
"""
390
Compute the in-place inverse Fourier transform of this data
391
using the Cooley-Tukey algorithm.
392
393
OUTPUT:
394
395
- None, the transformation is done in-place.
396
397
If the number of sample points in the input is a power of 2 then the
398
function ``gsl_fft_complex_radix2_inverse`` is automatically called.
399
Otherwise, ``gsl_fft_complex_inverse`` is called.
400
401
This transform is normalized so ``f.forward_transform().inverse_transform() == f``
402
modulo round-off errors. See also :meth:`backward_transform`.
403
404
EXAMPLES::
405
406
sage: a = FastFourierTransform(125)
407
sage: b = FastFourierTransform(125)
408
sage: for i in range(1, 60): a[i]=1
409
sage: for i in range(1, 60): b[i]=1
410
sage: a.forward_transform()
411
sage: a.inverse_transform()
412
sage: (a.plot()+b.plot())
413
sage: abs(sum([CDF(a[i])-CDF(b[i]) for i in range(125)])) < 2**-16
414
True
415
416
Here we check it with a power of two::
417
418
sage: a = FastFourierTransform(128)
419
sage: b = FastFourierTransform(128)
420
sage: for i in range(1, 60): a[i]=1
421
sage: for i in range(1, 60): b[i]=1
422
sage: a.forward_transform()
423
sage: a.inverse_transform()
424
sage: (a.plot()+b.plot())
425
426
"""
427
cdef gsl_fft_complex_wavetable * wt
428
cdef gsl_fft_complex_workspace * mem
429
N = Integer(self.n)
430
e = N.exact_log(2)
431
if N==2**e:
432
gsl_fft_complex_radix2_inverse(self.data, self.stride, self.n)
433
else:
434
mem = gsl_fft_complex_workspace_alloc(self.n)
435
wt = gsl_fft_complex_wavetable_alloc(self.n)
436
gsl_fft_complex_inverse(self.data, self.stride, self.n, wt, mem)
437
gsl_fft_complex_workspace_free(mem)
438
gsl_fft_complex_wavetable_free(wt)
439
440
def backward_transform(self):
441
"""
442
Compute the in-place backwards Fourier transform of this data
443
using the Cooley-Tukey algorithm.
444
445
OUTPUT:
446
447
- None, the transformation is done in-place.
448
449
This is the same as :meth:`inverse_transform` but lacks normalization
450
so that ``f.forward_transform().backward_transform() == n*f``. Where
451
``n`` is the size of the array.
452
453
EXAMPLES::
454
455
sage: a = FastFourierTransform(125)
456
sage: b = FastFourierTransform(125)
457
sage: for i in range(1, 60): a[i]=1
458
sage: for i in range(1, 60): b[i]=1
459
sage: a.forward_transform()
460
sage: a.backward_transform()
461
sage: (a.plot() + b.plot()).show(ymin=0) # long time (2s on sage.math, 2011)
462
sage: abs(sum([CDF(a[i])/125-CDF(b[i]) for i in range(125)])) < 2**-16
463
True
464
465
Here we check it with a power of two::
466
467
sage: a = FastFourierTransform(128)
468
sage: b = FastFourierTransform(128)
469
sage: for i in range(1, 60): a[i]=1
470
sage: for i in range(1, 60): b[i]=1
471
sage: a.forward_transform()
472
sage: a.backward_transform()
473
sage: (a.plot() + b.plot()).show(ymin=0)
474
"""
475
cdef gsl_fft_complex_wavetable * wt
476
cdef gsl_fft_complex_workspace * mem
477
N = Integer(self.n)
478
e = N.exact_log(2)
479
if N==2**e:
480
gsl_fft_complex_radix2_backward(self.data, self.stride, self.n)
481
else:
482
mem = gsl_fft_complex_workspace_alloc(self.n)
483
wt = gsl_fft_complex_wavetable_alloc(self.n)
484
gsl_fft_complex_backward(self.data, self.stride, self.n, wt, mem)
485
gsl_fft_complex_workspace_free(mem)
486
gsl_fft_complex_wavetable_free(wt)
487
488
cdef class FourierTransform_complex:
489
pass
490
491
cdef class FourierTransform_real:
492
pass
493
494
495
496