Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/functions/spike_function.py
8815 views
1
r"""
2
Spike Functions
3
4
AUTHORS:
5
6
- William Stein (2007-07): initial version
7
8
- Karl-Dieter Crisman (2009-09): adding documentation and doctests
9
"""
10
#*****************************************************************************
11
# Copyright (C) 2007 William Stein <[email protected]>
12
# Copyright (C) 2009 Karl-Dieter Crisman <[email protected]>
13
#
14
# Distributed under the terms of the GNU General Public License (GPL)
15
# as published by the Free Software Foundation; either version 2 of
16
# the License, or (at your option) any later version.
17
# http://www.gnu.org/licenses/
18
#*****************************************************************************
19
import math
20
21
from sage.plot.all import line
22
from sage.modules.free_module_element import vector
23
from sage.rings.all import RDF
24
25
class SpikeFunction:
26
"""
27
Base class for spike functions.
28
29
INPUT:
30
31
- ``v`` - list of pairs (x, height)
32
33
- ``eps`` - parameter that determines approximation to a true spike
34
35
OUTPUT:
36
37
a function with spikes at each point ``x`` in ``v`` with the given height.
38
39
EXAMPLES::
40
41
sage: spike_function([(-3,4),(-1,1),(2,3)],0.001)
42
A spike function with spikes at [-3.0, -1.0, 2.0]
43
44
Putting the spikes too close together may delete some::
45
46
sage: spike_function([(1,1),(1.01,4)],0.1)
47
Some overlapping spikes have been deleted.
48
You might want to use a smaller value for eps.
49
A spike function with spikes at [1.0]
50
51
Note this should normally be used indirectly via
52
``spike_function``, but one can use it directly::
53
54
sage: from sage.functions.spike_function import SpikeFunction
55
sage: S = SpikeFunction([(0,1),(1,2),(pi,-5)])
56
sage: S
57
A spike function with spikes at [0.0, 1.0, 3.141592653589793]
58
sage: S.support
59
[0.0, 1.0, 3.141592653589793]
60
"""
61
def __init__(self, v, eps=0.0000001):
62
"""
63
Initializes base class SpikeFunction.
64
65
EXAMPLES::
66
67
sage: S = spike_function([(-3,4),(-1,1),(2,3)],0.001); S
68
A spike function with spikes at [-3.0, -1.0, 2.0]
69
sage: S.height
70
[4.0, 1.0, 3.0]
71
sage: S.eps
72
0.00100000000000000
73
"""
74
if len(v) == 0:
75
v = [(0,0)]
76
v = [(float(x[0]), float(x[1])) for x in v]
77
v.sort() # makes sure spike locations are in ascending order so we don't need an absolute value to catch overlaps
78
notify = False
79
80
for i in reversed(range(len(v)-1)):
81
if v[i+1][0] - v[i][0] <= eps:
82
notify = True
83
del v[i+1]
84
85
if notify:
86
print "Some overlapping spikes have been deleted."
87
print "You might want to use a smaller value for eps."
88
89
self.v = v
90
self.eps = eps
91
self.support = [float(x[0]) for x in self.v]
92
self.height = [float(x[1]) for x in self.v]
93
94
def __repr__(self):
95
"""
96
String representation of a spike function.
97
98
EXAMPLES::
99
100
sage: spike_function([(-3,4),(-1,1),(2,3)],0.001)
101
A spike function with spikes at [-3.0, -1.0, 2.0]
102
"""
103
return "A spike function with spikes at %s"%self.support
104
105
def _eval(self, x):
106
"""
107
Evaluates spike function. Note that when one calls
108
the function within the tolerance, the return value
109
is the full height at that point.
110
111
EXAMPLES::
112
113
sage: S = spike_function([(0,5)],eps=.001)
114
sage: S(0)
115
5.0
116
sage: S(.1)
117
0.0
118
sage: S(.01)
119
0.0
120
sage: S(.001)
121
5.0
122
"""
123
eps = self.eps
124
x = float(x)
125
for i in range(len(self.support)):
126
z = self.support[i]
127
if z - eps <= x and x <= z + eps:
128
return self.height[i], i
129
return float(0), -1
130
131
def __call__(self, x):
132
"""
133
Called when spike function is used as callable function.
134
135
EXAMPLES::
136
137
sage: S = spike_function([(0,5)],eps=.001)
138
sage: S(0)
139
5.0
140
sage: S(.1)
141
0.0
142
sage: S(.01)
143
0.0
144
sage: S(.001)
145
5.0
146
"""
147
return self._eval(x)[0]
148
149
def plot_fft_abs(self, samples=2**12, xmin=None, xmax=None, **kwds):
150
"""
151
Plot of (absolute values of) Fast Fourier Transform of
152
the spike function with given number of samples.
153
154
EXAMPLES::
155
156
sage: S = spike_function([(-3,4),(-1,1),(2,3)]); S
157
A spike function with spikes at [-3.0, -1.0, 2.0]
158
sage: P = S.plot_fft_abs(8)
159
sage: p = P[0]; p.ydata
160
[5.0, 5.0, 3.367958691924177, 3.367958691924177, 4.123105625617661, 4.123105625617661, 4.759921664218055, 4.759921664218055]
161
"""
162
w = self.vector(samples = samples, xmin=xmin, xmax=xmax)
163
xmin, xmax = self._ranges(xmin, xmax)
164
z = w.fft()
165
k = vector(RDF, [abs(z[i]) for i in range(int(len(z)/2))])
166
return k.plot(xmin=0, xmax=1, **kwds)
167
168
def plot_fft_arg(self, samples=2**12, xmin=None, xmax=None, **kwds):
169
"""
170
Plot of (absolute values of) Fast Fourier Transform of
171
the spike function with given number of samples.
172
173
EXAMPLES::
174
175
sage: S = spike_function([(-3,4),(-1,1),(2,3)]); S
176
A spike function with spikes at [-3.0, -1.0, 2.0]
177
sage: P = S.plot_fft_arg(8)
178
sage: p = P[0]; p.ydata
179
[0.0, 0.0, -0.211524990023434..., -0.211524990023434..., 0.244978663126864..., 0.244978663126864..., -0.149106180027477..., -0.149106180027477...]
180
"""
181
w = self.vector(samples = samples, xmin=xmin, xmax=xmax)
182
xmin, xmax = self._ranges(xmin, xmax)
183
z = w.fft()
184
k = vector(RDF, [(z[i]).arg() for i in range(int(len(z)/2))])
185
return k.plot(xmin=0, xmax=1, **kwds)
186
187
def vector(self, samples=2**16, xmin=None, xmax=None):
188
"""
189
Creates a sampling vector of the spike function in question.
190
191
EXAMPLES::
192
193
sage: S = spike_function([(-3,4),(-1,1),(2,3)],0.001); S
194
A spike function with spikes at [-3.0, -1.0, 2.0]
195
sage: S.vector(16)
196
(4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
197
"""
198
v = vector(RDF, samples) # creates vector of zeros of length 2^16
199
xmin, xmax = self._ranges(xmin, xmax)
200
delta = (xmax - xmin)/samples
201
w = int(math.ceil(self.eps/delta))
202
for i in range(len(self.support)):
203
x = self.support[i]
204
if x > xmax:
205
break
206
h = self.height[i]
207
j = int((x - xmin)/delta)
208
for k in range(j, min(samples, j+w)):
209
v[k] = h
210
return v
211
212
def _ranges(self, xmin, xmax):
213
"""
214
Quickly find appropriate plotting interval.
215
216
EXAMPLES::
217
218
sage: S = spike_function([(-1,1),(1,40)])
219
sage: S._ranges(None,None)
220
(-1.0, 1.0)
221
"""
222
width = (self.support[-1] + self.support[0])/float(2)
223
if xmin is None:
224
xmin = self.support[0] - width/float(5)
225
if xmax is None:
226
xmax = self.support[-1] + width/float(5)
227
if xmax <= xmin:
228
xmax = xmin + 1
229
return xmin, xmax
230
231
def plot(self, xmin=None, xmax=None, **kwds):
232
"""
233
Special fast plot method for spike functions.
234
235
EXAMPLES::
236
237
sage: S = spike_function([(-1,1),(1,40)])
238
sage: P = plot(S)
239
sage: P[0]
240
Line defined by 8 points
241
"""
242
v = []
243
xmin, xmax = self._ranges(xmin, xmax)
244
x = xmin
245
eps = self.eps
246
while x < xmax:
247
y, i = self._eval(x)
248
v.append( (x, y) )
249
if i != -1:
250
x0 = self.support[i] + eps
251
v.extend([(x0,y), (x0,0)])
252
if i+1 < len(self.support):
253
x = self.support[i+1] - eps
254
v.append( (x, 0) )
255
else:
256
x = xmax
257
v.append( (xmax, 0) )
258
else:
259
new_x = None
260
for j in range(len(self.support)):
261
if self.support[j] - eps > x:
262
new_x = self.support[j] - eps
263
break
264
if new_x is None:
265
new_x = xmax
266
v.append( (new_x, 0) )
267
x = new_x
268
L = line(v, **kwds)
269
L.xmin(xmin-1); L.xmax(xmax)
270
return L
271
272
273
274
spike_function = SpikeFunction
275
276