Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/gsl/dft.py
8817 views
1
r"""
2
Discrete Fourier Transforms
3
4
This file contains functions useful for computing discrete Fourier
5
transforms and probability distribution functions for discrete random
6
variables for sequences of elements of `\QQ` or `\CC`, indexed by a
7
``range(N)``, `\ZZ / N \ZZ`, an abelian group, the conjugacy classes
8
of a permutation group, or the conjugacy classes of a matrix group.
9
10
This file implements:
11
12
- :meth:`__eq__`
13
14
- :meth:`__mul__` (for right multiplication by a scalar)
15
16
- plotting, printing -- :meth:`IndexedSequence.plot`,
17
:meth:`IndexedSequence.plot_histogram`, :meth:`_repr_`, :meth:`__str__`
18
19
- dft -- computes the discrete Fourier transform for the following cases:
20
21
* a sequence (over `\QQ` or :class:`CyclotomicField`) indexed by ``range(N)``
22
or `\ZZ / N \ZZ`
23
* a sequence (as above) indexed by a finite abelian group
24
* a sequence (as above) indexed by a complete set of representatives of
25
the conjugacy classes of a finite permutation group
26
* a sequence (as above) indexed by a complete set of representatives of
27
the conjugacy classes of a finite matrix group
28
29
- idft -- computes the discrete Fourier transform for the following cases:
30
31
* a sequence (over `\QQ` or CyclotomicField) indexed by ``range(N)`` or
32
`\ZZ / N \ZZ`
33
34
- dct, dst (for discrete Fourier/Cosine/Sine transform)
35
36
- convolution (in :meth:`IndexedSequence.convolution` and
37
:meth:`IndexedSequence.convolution_periodic`)
38
39
- fft, ifft -- (fast Fourier transforms) wrapping GSL's
40
``gsl_fft_complex_forward()``, ``gsl_fft_complex_inverse()``,
41
using William Stein's :func:`FastFourierTransform`
42
43
- dwt, idwt -- (fast wavelet transforms) wrapping GSL's ``gsl_dwt_forward()``,
44
``gsl_dwt_backward()`` using Joshua Kantor's :func:`WaveletTransform` class.
45
Allows for wavelets of type:
46
47
* "haar"
48
* "daubechies"
49
* "daubechies_centered"
50
* "haar_centered"
51
* "bspline"
52
* "bspline_centered"
53
54
55
.. TODO::
56
57
- "filtered" DFTs
58
- more idfts
59
- more examples for probability, stats, theory of FTs
60
61
AUTHORS:
62
63
- David Joyner (2006-10)
64
65
- William Stein (2006-11) -- fix many bugs
66
"""
67
68
##########################################################################
69
# Copyright (C) 2006 David Joyner <[email protected]>
70
#
71
# Distributed under the terms of the GNU General Public License (GPL):
72
#
73
# http://www.gnu.org/licenses/
74
##########################################################################
75
76
from sage.rings.number_field.number_field import CyclotomicField
77
from sage.plot.all import polygon, line, text
78
from sage.groups.abelian_gps.abelian_group import AbelianGroup
79
from sage.groups.perm_gps.permgroup_element import is_PermutationGroupElement
80
from sage.rings.integer_ring import ZZ
81
from sage.rings.integer import Integer
82
from sage.rings.arith import factor
83
from sage.rings.rational_field import QQ
84
from sage.rings.real_mpfr import RR
85
from sage.functions.all import sin, cos
86
from sage.gsl.fft import FastFourierTransform
87
from sage.gsl.dwt import WaveletTransform
88
89
from sage.structure.sage_object import SageObject
90
from sage.structure.sequence import Sequence
91
92
class IndexedSequence(SageObject):
93
"""
94
An indexed sequence.
95
96
INPUT:
97
98
- ``L`` -- A list
99
100
- ``index_object`` must be a Sage object with an ``__iter__`` method
101
containing the same number of elements as ``self``, which is a
102
list of elements taken from a field.
103
"""
104
def __init__(self, L, index_object):
105
r"""
106
Initialize ``self``.
107
108
EXAMPLES::
109
110
sage: J = range(10)
111
sage: A = [1/10 for j in J]
112
sage: s = IndexedSequence(A,J)
113
sage: s
114
Indexed sequence: [1/10, 1/10, 1/10, 1/10, 1/10, 1/10, 1/10, 1/10, 1/10, 1/10]
115
indexed by [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
116
sage: s.dict()
117
{0: 1/10,
118
1: 1/10,
119
2: 1/10,
120
3: 1/10,
121
4: 1/10,
122
5: 1/10,
123
6: 1/10,
124
7: 1/10,
125
8: 1/10,
126
9: 1/10}
127
sage: s.list()
128
[1/10, 1/10, 1/10, 1/10, 1/10, 1/10, 1/10, 1/10, 1/10, 1/10]
129
sage: s.index_object()
130
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
131
sage: s.base_ring()
132
Rational Field
133
"""
134
try:
135
ind = index_object.list()
136
except AttributeError:
137
ind = list(index_object)
138
self._index_object = index_object
139
self._list = Sequence(L)
140
self._base_ring = self._list.universe()
141
dict = {}
142
for i in range(len(ind)):
143
dict[ind[i]] = L[i]
144
self._dict = dict
145
146
def dict(self):
147
"""
148
Return a python dict of ``self`` where the keys are elments in the
149
indexing set.
150
151
EXAMPLES::
152
153
sage: J = range(10)
154
sage: A = [1/10 for j in J]
155
sage: s = IndexedSequence(A,J)
156
sage: s.dict()
157
{0: 1/10, 1: 1/10, 2: 1/10, 3: 1/10, 4: 1/10, 5: 1/10, 6: 1/10, 7: 1/10, 8: 1/10, 9: 1/10}
158
"""
159
return self._dict
160
161
def list(self):
162
"""
163
Return the list of ``self``.
164
165
EXAMPLES::
166
167
sage: J = range(10)
168
sage: A = [1/10 for j in J]
169
sage: s = IndexedSequence(A,J)
170
sage: s.list()
171
[1/10, 1/10, 1/10, 1/10, 1/10, 1/10, 1/10, 1/10, 1/10, 1/10]
172
"""
173
return self._list
174
175
def base_ring(self):
176
r"""
177
This just returns the common parent `R` of the `N` list
178
elements. In some applications (say, when computing the
179
discrete Fourier transform, dft), it is more accurate to think
180
of the base_ring as the group ring `\QQ(\zeta_N)[R]`.
181
182
EXAMPLES::
183
184
sage: J = range(10)
185
sage: A = [1/10 for j in J]
186
sage: s = IndexedSequence(A,J)
187
sage: s.base_ring()
188
Rational Field
189
"""
190
return self._base_ring
191
192
def index_object(self):
193
"""
194
Return the indexing object.
195
196
EXAMPLES::
197
198
sage: J = range(10)
199
sage: A = [1/10 for j in J]
200
sage: s = IndexedSequence(A,J)
201
sage: s.index_object()
202
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
203
"""
204
return self._index_object
205
206
def _repr_(self):
207
"""
208
Implements print method.
209
210
EXAMPLES::
211
212
sage: A = [ZZ(i) for i in range(3)]
213
sage: I = range(3)
214
sage: s = IndexedSequence(A,I)
215
sage: s
216
Indexed sequence: [0, 1, 2]
217
indexed by [0, 1, 2]
218
sage: print s
219
Indexed sequence: [0, 1, 2]
220
indexed by [0, 1, 2]
221
sage: I = GF(3)
222
sage: A = [i^2 for i in I]
223
sage: s = IndexedSequence(A,I)
224
sage: s
225
Indexed sequence: [0, 1, 1]
226
indexed by Finite Field of size 3
227
"""
228
return "Indexed sequence: "+str(self.list())+"\n indexed by "+str(self.index_object())
229
230
def plot_histogram(self, clr=(0,0,1), eps = 0.4):
231
r"""
232
Plot the histogram plot of the sequence.
233
234
The sequence is assumed to be real or from a finite field,
235
with a real indexing set ``I`` coercible into `\RR`.
236
237
Options are ``clr``, which is an RGB value, and ``eps``, which
238
is the spacing between the bars.
239
240
EXAMPLES::
241
242
sage: J = range(3)
243
sage: A = [ZZ(i^2)+1 for i in J]
244
sage: s = IndexedSequence(A,J)
245
sage: P = s.plot_histogram()
246
sage: show(P) # Not tested
247
"""
248
# elements must be coercible into RR
249
I = self.index_object()
250
N = len(I)
251
S = self.list()
252
P = [polygon([[RR(I[i])-eps,0],[RR(I[i])-eps,RR(S[i])],[RR(I[i])+eps,RR(S[i])],[RR(I[i])+eps,0],[RR(I[i]),0]], rgbcolor=clr) for i in range(N)]
253
T = [text(str(I[i]),(RR(I[i]),-0.8),fontsize=15,rgbcolor=(1,0,0)) for i in range(N)]
254
return sum(P) + sum(T)
255
256
def plot(self):
257
"""
258
Plot the points of the sequence.
259
260
Elements of the sequence are assumed to be real or from a
261
finite field, with a real indexing set ``I = range(len(self))``.
262
263
EXAMPLES::
264
265
sage: I = range(3)
266
sage: A = [ZZ(i^2)+1 for i in I]
267
sage: s = IndexedSequence(A,I)
268
sage: P = s.plot()
269
sage: show(P) # Not tested
270
"""
271
# elements must be coercible into RR
272
I = self.index_object()
273
S = self.list()
274
return line([[RR(I[i]),RR(S[i])] for i in range(len(I)-1)])
275
276
def dft(self, chi = lambda x: x):
277
"""
278
A discrete Fourier transform "over `\QQ`" using exact
279
`N`-th roots of unity.
280
281
EXAMPLES::
282
283
sage: J = range(6)
284
sage: A = [ZZ(1) for i in J]
285
sage: s = IndexedSequence(A,J)
286
sage: s.dft(lambda x:x^2)
287
Indexed sequence: [6, 0, 0, 6, 0, 0]
288
indexed by [0, 1, 2, 3, 4, 5]
289
sage: s.dft()
290
Indexed sequence: [6, 0, 0, 0, 0, 0]
291
indexed by [0, 1, 2, 3, 4, 5]
292
sage: G = SymmetricGroup(3)
293
sage: J = G.conjugacy_classes_representatives()
294
sage: s = IndexedSequence([1,2,3],J) # 1,2,3 are the values of a class fcn on G
295
sage: s.dft() # the "scalar-valued Fourier transform" of this class fcn
296
Indexed sequence: [8, 2, 2]
297
indexed by [(), (1,2), (1,2,3)]
298
sage: J = AbelianGroup(2,[2,3],names='ab')
299
sage: s = IndexedSequence([1,2,3,4,5,6],J)
300
sage: s.dft() # the precision of output is somewhat random and architecture dependent.
301
Indexed sequence: [21.0000000000000, -2.99999999999997 - 1.73205080756885*I, -2.99999999999999 + 1.73205080756888*I, -9.00000000000000 + 0.0000000000000485744257349999*I, -0.00000000000000976996261670137 - 0.0000000000000159872115546022*I, -0.00000000000000621724893790087 - 0.0000000000000106581410364015*I]
302
indexed by Multiplicative Abelian group isomorphic to C2 x C3
303
sage: J = CyclicPermutationGroup(6)
304
sage: s = IndexedSequence([1,2,3,4,5,6],J)
305
sage: s.dft() # the precision of output is somewhat random and architecture dependent.
306
Indexed sequence: [21.0000000000000, -2.99999999999997 - 1.73205080756885*I, -2.99999999999999 + 1.73205080756888*I, -9.00000000000000 + 0.0000000000000485744257349999*I, -0.00000000000000976996261670137 - 0.0000000000000159872115546022*I, -0.00000000000000621724893790087 - 0.0000000000000106581410364015*I]
307
indexed by Cyclic group of order 6 as a permutation group
308
sage: p = 7; J = range(p); A = [kronecker_symbol(j,p) for j in J]
309
sage: s = IndexedSequence(A,J)
310
sage: Fs = s.dft()
311
sage: c = Fs.list()[1]; [x/c for x in Fs.list()]; s.list()
312
[0, 1, 1, -1, 1, -1, -1]
313
[0, 1, 1, -1, 1, -1, -1]
314
315
The DFT of the values of the quadratic residue symbol is itself, up to
316
a constant factor (denoted c on the last line above).
317
318
.. TODO::
319
320
Read the parent of the elements of S; if `\QQ` or `\CC` leave as
321
is; if AbelianGroup, use abelian_group_dual; if some other
322
implemented Group (permutation, matrix), call .characters()
323
and test if the index list is the set of conjugacy classes.
324
"""
325
J = self.index_object() ## index set of length N
326
N = len(J)
327
S = self.list()
328
F = self.base_ring() ## elements must be coercible into QQ(zeta_N)
329
if not(J[0] in ZZ):
330
G = J[0].parent() ## if J is not a range it is a group G
331
if J[0] in ZZ and F.base_ring().fraction_field()==QQ:
332
## assumes J is range(N)
333
zeta = CyclotomicField(N).gen()
334
FT = [sum([S[i]*chi(zeta**(i*j)) for i in J]) for j in J]
335
elif not(J[0] in ZZ) and G.is_abelian() and F == ZZ or (F.is_field() and F.base_ring()==QQ):
336
if is_PermutationGroupElement(J[0]):
337
## J is a CyclicPermGp
338
n = G.order()
339
a = list(factor(n))
340
invs = [x[0]**x[1] for x in a]
341
G = AbelianGroup(len(a),invs)
342
## assumes J is AbelianGroup(...)
343
Gd = G.dual_group()
344
FT = [sum([S[i]*chid(G.list()[i]) for i in range(N)])
345
for chid in Gd]
346
elif not(J[0] in ZZ) and G.is_finite() and F == ZZ or (F.is_field() and F.base_ring()==QQ):
347
## assumes J is the list of conj class representatives of a
348
## PermuationGroup(...) or Matrixgroup(...)
349
chi = G.character_table()
350
FT = [sum([S[i]*chi[i,j] for i in range(N)]) for j in range(N)]
351
else:
352
raise ValueError("list elements must be in QQ(zeta_"+str(N)+")")
353
return IndexedSequence(FT,J)
354
355
def idft(self):
356
"""
357
A discrete inverse Fourier transform. Only works over `\QQ`.
358
359
EXAMPLES::
360
361
sage: J = range(5)
362
sage: A = [ZZ(1) for i in J]
363
sage: s = IndexedSequence(A,J)
364
sage: fs = s.dft(); fs
365
Indexed sequence: [5, 0, 0, 0, 0]
366
indexed by [0, 1, 2, 3, 4]
367
sage: it = fs.idft(); it
368
Indexed sequence: [1, 1, 1, 1, 1]
369
indexed by [0, 1, 2, 3, 4]
370
sage: it == s
371
True
372
"""
373
F = self.base_ring() ## elements must be coercible into QQ(zeta_N)
374
J = self.index_object() ## must be = range(N)
375
N = len(J)
376
S = self.list()
377
zeta = CyclotomicField(N).gen()
378
iFT = [sum([S[i]*zeta**(-i*j) for i in J]) for j in J]
379
if not(J[0] in ZZ) or F.base_ring().fraction_field() != QQ:
380
raise NotImplementedError("Sorry this type of idft is not implemented yet.")
381
return IndexedSequence(iFT,J)*(Integer(1)/N)
382
383
def dct(self):
384
"""
385
A discrete Cosine transform.
386
387
EXAMPLES::
388
389
sage: J = range(5)
390
sage: A = [exp(-2*pi*i*I/5) for i in J]
391
sage: s = IndexedSequence(A,J)
392
sage: s.dct()
393
<BLANKLINE>
394
Indexed sequence: [1/4*(sqrt(5) - 1)*e^(-2/5*I*pi) - 1/4*(sqrt(5) + 1)*e^(-4/5*I*pi) - 1/4*(sqrt(5) + 1)*e^(-6/5*I*pi) + 1/4*(sqrt(5) - 1)*e^(-8/5*I*pi) + 1, 1/4*(sqrt(5) - 1)*e^(-2/5*I*pi) - 1/4*(sqrt(5) + 1)*e^(-4/5*I*pi) - 1/4*(sqrt(5) + 1)*e^(-6/5*I*pi) + 1/4*(sqrt(5) - 1)*e^(-8/5*I*pi) + 1, 1/4*(sqrt(5) - 1)*e^(-2/5*I*pi) - 1/4*(sqrt(5) + 1)*e^(-4/5*I*pi) - 1/4*(sqrt(5) + 1)*e^(-6/5*I*pi) + 1/4*(sqrt(5) - 1)*e^(-8/5*I*pi) + 1, 1/4*(sqrt(5) - 1)*e^(-2/5*I*pi) - 1/4*(sqrt(5) + 1)*e^(-4/5*I*pi) - 1/4*(sqrt(5) + 1)*e^(-6/5*I*pi) + 1/4*(sqrt(5) - 1)*e^(-8/5*I*pi) + 1, 1/4*(sqrt(5) - 1)*e^(-2/5*I*pi) - 1/4*(sqrt(5) + 1)*e^(-4/5*I*pi) - 1/4*(sqrt(5) + 1)*e^(-6/5*I*pi) + 1/4*(sqrt(5) - 1)*e^(-8/5*I*pi) + 1]
395
indexed by [0, 1, 2, 3, 4]
396
"""
397
from sage.symbolic.constants import pi
398
F = self.base_ring() ## elements must be coercible into RR
399
J = self.index_object() ## must be = range(N)
400
N = len(J)
401
S = self.list()
402
PI = F(pi)
403
FT = [sum([S[i]*cos(2*PI*i/N) for i in J]) for j in J]
404
return IndexedSequence(FT,J)
405
406
def dst(self):
407
"""
408
A discrete Sine transform.
409
410
EXAMPLES::
411
412
sage: J = range(5)
413
sage: I = CC.0; pi = CC(pi)
414
sage: A = [exp(-2*pi*i*I/5) for i in J]
415
sage: s = IndexedSequence(A,J)
416
417
sage: s.dst() # discrete sine
418
Indexed sequence: [1.11022302462516e-16 - 2.50000000000000*I, 1.11022302462516e-16 - 2.50000000000000*I, 1.11022302462516e-16 - 2.50000000000000*I, 1.11022302462516e-16 - 2.50000000000000*I, 1.11022302462516e-16 - 2.50000000000000*I]
419
indexed by [0, 1, 2, 3, 4]
420
"""
421
from sage.symbolic.constants import pi
422
F = self.base_ring() ## elements must be coercible into RR
423
J = self.index_object() ## must be = range(N)
424
N = len(J)
425
S = self.list()
426
PI = F(pi)
427
FT = [sum([S[i]*sin(2*PI*i/N) for i in J]) for j in J]
428
return IndexedSequence(FT,J)
429
430
def convolution(self, other):
431
r"""
432
Convolves two sequences of the same length (automatically expands
433
the shortest one by extending it by 0 if they have different lengths).
434
435
If `\{a_n\}` and `\{b_n\}` are sequences indexed by `(n=0,1,...,N-1)`,
436
extended by zero for all `n` in `\ZZ`, then the convolution is
437
438
.. MATH::
439
440
c_j = \sum_{i=0}^{N-1} a_i b_{j-i}.
441
442
INPUT:
443
444
- ``other`` -- a collection of elements of a ring with
445
index set a finite abelian group (under `+`)
446
447
OUTPUT:
448
449
The Dirichlet convolution of ``self`` and ``other``.
450
451
EXAMPLES::
452
453
sage: J = range(5)
454
sage: A = [ZZ(1) for i in J]
455
sage: B = [ZZ(1) for i in J]
456
sage: s = IndexedSequence(A,J)
457
sage: t = IndexedSequence(B,J)
458
sage: s.convolution(t)
459
[1, 2, 3, 4, 5, 4, 3, 2, 1]
460
461
AUTHOR: David Joyner (2006-09)
462
"""
463
S = self.list()
464
T = other.list()
465
I0 = self.index_object()
466
J0 = other.index_object()
467
F = self.base_ring()
468
E = other.base_ring()
469
if F != E:
470
raise TypeError("IndexedSequences must have same base ring")
471
if I0 != J0:
472
raise TypeError("IndexedSequences must have same index set")
473
M = len(S)
474
N = len(T)
475
if M < N: ## first, extend by 0 if necessary
476
a = [S[i] for i in range(M)]+[F(0) for i in range(2*N)]
477
b = T+[E(0) for i in range(2*M)]
478
if M > N: ## python trick - a[-j] is really j from the *right*
479
b = [T[i] for i in range(N)]+[E(0) for i in range(2*M)]
480
a = S+[F(0) for i in range(2*M)]
481
if M==N: ## so need only extend by 0 to the *right*
482
a = S+[F(0) for i in range(2*M)]
483
b = T+[E(0) for i in range(2*M)]
484
N = max(M,N)
485
c = [sum([a[i]*b[j-i] for i in range(N)]) for j in range(2*N-1)]
486
#print [[b[j-i] for i in range(N)] for j in range(N)]
487
return c
488
489
def convolution_periodic(self, other):
490
"""
491
Convolves two collections indexed by a ``range(...)`` of the same
492
length (automatically expands the shortest one by extending it
493
by 0 if they have different lengths).
494
495
If `\{a_n\}` and `\{b_n\}` are sequences indexed by `(n=0,1,...,N-1)`,
496
extended periodically for all `n` in `\ZZ`, then the convolution is
497
498
.. MATH::
499
500
c_j = \sum_{i=0}^{N-1} a_i b_{j-i}.
501
502
INPUT:
503
504
- ``other`` -- a sequence of elements of `\CC`, `\RR` or `\GF{q}`
505
506
OUTPUT:
507
508
The Dirichlet convolution of ``self`` and ``other``.
509
510
EXAMPLES::
511
512
sage: I = range(5)
513
sage: A = [ZZ(1) for i in I]
514
sage: B = [ZZ(1) for i in I]
515
sage: s = IndexedSequence(A,I)
516
sage: t = IndexedSequence(B,I)
517
sage: s.convolution_periodic(t)
518
[5, 5, 5, 5, 5, 5, 5, 5, 5]
519
520
AUTHOR: David Joyner (2006-09)
521
"""
522
S = self.list()
523
T = other.list()
524
I = self.index_object()
525
J = other.index_object()
526
F = self.base_ring()
527
E = other.base_ring()
528
if F!=E:
529
raise TypeError("IndexedSequences must have same parent")
530
if I!=J:
531
raise TypeError("IndexedSequences must have same index set")
532
M = len(S)
533
N = len(T)
534
if M<N: ## first, extend by 0 if necessary
535
a = [S[i] for i in range(M)]+[F(0) for i in range(N-M)]
536
b = other
537
if M>N:
538
b = [T[i] for i in range(N)]+[E(0) for i in range(M-N)]
539
a = self
540
if M==N:
541
a = S
542
b = T
543
N = max(M,N)
544
c = [sum([a[i]*b[(j-i)%N] for i in range(N)]) for j in range(2*N-1)]
545
return c
546
547
def __mul__(self,other):
548
"""
549
Implements scalar multiplication (on the right).
550
551
EXAMPLES::
552
553
sage: J = range(5)
554
sage: A = [ZZ(1) for i in J]
555
sage: s = IndexedSequence(A,J)
556
sage: s.base_ring()
557
Integer Ring
558
sage: t = s*(1/3); t; t.base_ring()
559
Indexed sequence: [1/3, 1/3, 1/3, 1/3, 1/3]
560
indexed by [0, 1, 2, 3, 4]
561
Rational Field
562
"""
563
S = self.list()
564
J = range(len(self.index_object()))
565
#if not(other in F):
566
# raise TypeError,"The base rings must be consistent"
567
S1 = [S[i]*other for i in J]
568
return IndexedSequence(S1,self.index_object())
569
570
def __eq__(self,other):
571
"""
572
Implements boolean equals.
573
574
EXAMPLES::
575
576
sage: J = range(5)
577
sage: A = [ZZ(1) for i in J]
578
sage: s = IndexedSequence(A,J)
579
sage: t = s*(1/3)
580
sage: t*3 == s
581
1
582
583
.. WARNING::
584
585
** elements are considered different if they differ
586
by ``10^(-8)``, which is pretty arbitrary -- use with CAUTION!! **
587
"""
588
if type(self) != type(other):
589
return False
590
S = self.list()
591
T = other.list()
592
I = self.index_object()
593
J = other.index_object()
594
if I!=J:
595
return False
596
for i in I:
597
try:
598
if abs(S[i]-T[i]) > 10**(-8): ## tests if they differ as reals -- WHY 10^(-8)???
599
return False
600
except TypeError:
601
pass
602
#if F!=E: ## omitted this test since it
603
# return 0 ## doesn't take into account coercions -- WHY???
604
return True
605
606
def fft(self):
607
"""
608
Wraps the gsl ``FastFourierTransform.forward()`` in
609
:mod:`~sage.gsl.fft`.
610
611
If the length is a power of 2 then this automatically uses the
612
radix2 method. If the number of sample points in the input is
613
a power of 2 then the wrapper for the GSL function
614
``gsl_fft_complex_radix2_forward()`` is automatically called.
615
Otherwise, ``gsl_fft_complex_forward()`` is used.
616
617
EXAMPLES::
618
619
sage: J = range(5)
620
sage: A = [RR(1) for i in J]
621
sage: s = IndexedSequence(A,J)
622
sage: t = s.fft(); t
623
Indexed sequence: [5.00000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000]
624
indexed by [0, 1, 2, 3, 4]
625
"""
626
from sage.rings.all import I
627
# elements must be coercible into RR
628
J = self.index_object() ## must be = range(N)
629
N = len(J)
630
S = self.list()
631
a = FastFourierTransform(N)
632
for i in range(N):
633
a[i] = S[i]
634
a.forward_transform()
635
return IndexedSequence([a[j][0]+I*a[j][1] for j in J],J)
636
637
def ifft(self):
638
"""
639
Implements the gsl ``FastFourierTransform.inverse`` in
640
:mod:`~sage.gsl.fft`.
641
642
If the number of sample points in the input is a power of 2
643
then the wrapper for the GSL function
644
``gsl_fft_complex_radix2_inverse()`` is automatically called.
645
Otherwise, ``gsl_fft_complex_inverse()`` is used.
646
647
EXAMPLES::
648
649
sage: J = range(5)
650
sage: A = [RR(1) for i in J]
651
sage: s = IndexedSequence(A,J)
652
sage: t = s.fft(); t
653
Indexed sequence: [5.00000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000]
654
indexed by [0, 1, 2, 3, 4]
655
sage: t.ifft()
656
Indexed sequence: [1.00000000000000, 1.00000000000000, 1.00000000000000, 1.00000000000000, 1.00000000000000]
657
indexed by [0, 1, 2, 3, 4]
658
sage: t.ifft() == s
659
1
660
"""
661
from sage.rings.all import I
662
# elements must be coercible into RR
663
J = self.index_object() ## must be = range(N)
664
N = len(J)
665
S = self.list()
666
a = FastFourierTransform(N)
667
for i in range(N):
668
a[i] = S[i]
669
a.inverse_transform()
670
return IndexedSequence([a[j][0]+I*a[j][1] for j in J],J)
671
672
def dwt(self,other="haar",wavelet_k=2):
673
"""
674
Wraps the gsl ``WaveletTransform.forward`` in :mod:`~sage.gsl.dwt`
675
(written by Joshua Kantor). Assumes the length of the sample is a
676
power of 2. Uses the GSL function ``gsl_wavelet_transform_forward()``.
677
678
INPUT:
679
680
- ``other`` -- the the name of the type of wavelet; valid choices are:
681
682
* ``'daubechies'``
683
* ``'daubechies_centered'``
684
* ``'haar'`` (default)
685
* ``'haar_centered'``
686
* ``'bspline'``
687
* ``'bspline_centered'``
688
689
- ``wavelet_k`` -- For daubechies wavelets, ``wavelet_k`` specifies a
690
daubechie wavelet with `k/2` vanishing moments.
691
`k = 4,6,...,20` for `k` even are the only ones implemented.
692
693
For Haar wavelets, ``wavelet_k`` must be 2.
694
695
For bspline wavelets, ``wavelet_k`` equal to `103,105,202,204,
696
206,208,301,305,307,309` will give biorthogonal B-spline wavelets
697
of order `(i,j)` where ``wavelet_k`` equals `100 \cdot i + j`.
698
699
The wavelet transform uses `J = \log_2(n)` levels.
700
701
EXAMPLES::
702
703
sage: J = range(8)
704
sage: A = [RR(1) for i in J]
705
sage: s = IndexedSequence(A,J)
706
sage: t = s.dwt()
707
sage: t # slightly random output
708
Indexed sequence: [2.82842712474999, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000]
709
indexed by [0, 1, 2, 3, 4, 5, 6, 7]
710
"""
711
# elements must be coercible into RR
712
J = self.index_object() ## must be = range(N)
713
N = len(J) ## must be 1 minus a power of 2
714
S = self.list()
715
if other == "haar" or other == "haar_centered":
716
if wavelet_k in [2]:
717
a = WaveletTransform(N,other,wavelet_k)
718
else:
719
raise ValueError("wavelet_k must be = 2")
720
if other == "debauchies" or other == "debauchies_centered":
721
if wavelet_k in [4,6,8,10,12,14,16,18,20]:
722
a = WaveletTransform(N,other,wavelet_k)
723
else:
724
raise ValueError("wavelet_k must be in {4,6,8,10,12,14,16,18,20}")
725
if other == "bspline" or other == "bspline_centered":
726
if wavelet_k in [103,105,202,204,206,208,301,305,307,309]:
727
a = WaveletTransform(N,other,103)
728
else:
729
raise ValueError("wavelet_k must be in {103,105,202,204,206,208,301,305,307,309}")
730
for i in range(N):
731
a[i] = S[i]
732
a.forward_transform()
733
return IndexedSequence([RR(a[j]) for j in J],J)
734
735
def idwt(self, other="haar", wavelet_k=2):
736
"""
737
Implements the gsl ``WaveletTransform.backward()`` in
738
:mod:`~sage.gsl.dwt`.
739
740
Assumes the length of the sample is a power of 2. Uses the
741
GSL function ``gsl_wavelet_transform_backward()``.
742
743
INPUT:
744
745
- ``other`` -- Must be one of the following:
746
747
* ``"haar"``
748
* ``"daubechies"``
749
* ``"daubechies_centered"``
750
* ``"haar_centered"``
751
* ``"bspline"``
752
* ``"bspline_centered"``
753
754
- ``wavelet_k`` -- For daubechies wavelets, ``wavelet_k`` specifies a
755
daubechie wavelet with `k/2` vanishing moments.
756
`k = 4,6,...,20` for `k` even are the only ones implemented.
757
758
For Haar wavelets, ``wavelet_k`` must be 2.
759
760
For bspline wavelets, ``wavelet_k`` equal to `103,105,202,204,
761
206,208,301,305,307,309` will give biorthogonal B-spline wavelets
762
of order `(i,j)` where ``wavelet_k`` equals `100 \cdot i + j`.
763
764
EXAMPLES::
765
766
sage: J = range(8)
767
sage: A = [RR(1) for i in J]
768
sage: s = IndexedSequence(A,J)
769
sage: t = s.dwt()
770
sage: t # random arch dependent output
771
Indexed sequence: [2.82842712474999, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000]
772
indexed by [0, 1, 2, 3, 4, 5, 6, 7]
773
sage: t.idwt() # random arch dependent output
774
Indexed sequence: [1.00000000000000, 1.00000000000000, 1.00000000000000, 1.00000000000000, 1.00000000000000, 1.00000000000000, 1.00000000000000, 1.00000000000000]
775
indexed by [0, 1, 2, 3, 4, 5, 6, 7]
776
sage: t.idwt() == s
777
True
778
sage: J = range(16)
779
sage: A = [RR(1) for i in J]
780
sage: s = IndexedSequence(A,J)
781
sage: t = s.dwt("bspline", 103)
782
sage: t # random arch dependent output
783
Indexed sequence: [4.00000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000]
784
indexed by [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
785
sage: t.idwt("bspline", 103) == s
786
True
787
"""
788
# elements must be coercible into RR
789
J = self.index_object() ## must be = range(N)
790
N = len(J) ## must be 1 minus a power of 2
791
S = self.list()
792
k = wavelet_k
793
if other=="haar" or other=="haar_centered":
794
if k in [2]:
795
a = WaveletTransform(N,other,wavelet_k)
796
else:
797
raise ValueError("wavelet_k must be = 2")
798
if other=="debauchies" or other=="debauchies_centered":
799
if k in [4,6,8,10,12,14,16,18,20]:
800
a = WaveletTransform(N,other,wavelet_k)
801
else:
802
raise ValueError("wavelet_k must be in {4,6,8,10,12,14,16,18,20}")
803
if other=="bspline" or other=="bspline_centered":
804
if k in [103,105,202,204,206,208,301,305,307,309]:
805
a = WaveletTransform(N,other,103)
806
else:
807
raise ValueError("wavelet_k must be in {103,105,202,204,206,208,301,305,307,309}")
808
for i in range(N):
809
a[i] = S[i]
810
a.backward_transform()
811
return IndexedSequence([RR(a[j]) for j in J],J)
812
813