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