Path: blob/master/src/sage/functions/spike_function.py
8815 views
r"""1Spike Functions23AUTHORS:45- William Stein (2007-07): initial version67- Karl-Dieter Crisman (2009-09): adding documentation and doctests8"""9#*****************************************************************************10# Copyright (C) 2007 William Stein <[email protected]>11# Copyright (C) 2009 Karl-Dieter Crisman <[email protected]>12#13# Distributed under the terms of the GNU General Public License (GPL)14# as published by the Free Software Foundation; either version 2 of15# the License, or (at your option) any later version.16# http://www.gnu.org/licenses/17#*****************************************************************************18import math1920from sage.plot.all import line21from sage.modules.free_module_element import vector22from sage.rings.all import RDF2324class SpikeFunction:25"""26Base class for spike functions.2728INPUT:2930- ``v`` - list of pairs (x, height)3132- ``eps`` - parameter that determines approximation to a true spike3334OUTPUT:3536a function with spikes at each point ``x`` in ``v`` with the given height.3738EXAMPLES::3940sage: spike_function([(-3,4),(-1,1),(2,3)],0.001)41A spike function with spikes at [-3.0, -1.0, 2.0]4243Putting the spikes too close together may delete some::4445sage: spike_function([(1,1),(1.01,4)],0.1)46Some overlapping spikes have been deleted.47You might want to use a smaller value for eps.48A spike function with spikes at [1.0]4950Note this should normally be used indirectly via51``spike_function``, but one can use it directly::5253sage: from sage.functions.spike_function import SpikeFunction54sage: S = SpikeFunction([(0,1),(1,2),(pi,-5)])55sage: S56A spike function with spikes at [0.0, 1.0, 3.141592653589793]57sage: S.support58[0.0, 1.0, 3.141592653589793]59"""60def __init__(self, v, eps=0.0000001):61"""62Initializes base class SpikeFunction.6364EXAMPLES::6566sage: S = spike_function([(-3,4),(-1,1),(2,3)],0.001); S67A spike function with spikes at [-3.0, -1.0, 2.0]68sage: S.height69[4.0, 1.0, 3.0]70sage: S.eps710.0010000000000000072"""73if len(v) == 0:74v = [(0,0)]75v = [(float(x[0]), float(x[1])) for x in v]76v.sort() # makes sure spike locations are in ascending order so we don't need an absolute value to catch overlaps77notify = False7879for i in reversed(range(len(v)-1)):80if v[i+1][0] - v[i][0] <= eps:81notify = True82del v[i+1]8384if notify:85print "Some overlapping spikes have been deleted."86print "You might want to use a smaller value for eps."8788self.v = v89self.eps = eps90self.support = [float(x[0]) for x in self.v]91self.height = [float(x[1]) for x in self.v]9293def __repr__(self):94"""95String representation of a spike function.9697EXAMPLES::9899sage: spike_function([(-3,4),(-1,1),(2,3)],0.001)100A spike function with spikes at [-3.0, -1.0, 2.0]101"""102return "A spike function with spikes at %s"%self.support103104def _eval(self, x):105"""106Evaluates spike function. Note that when one calls107the function within the tolerance, the return value108is the full height at that point.109110EXAMPLES::111112sage: S = spike_function([(0,5)],eps=.001)113sage: S(0)1145.0115sage: S(.1)1160.0117sage: S(.01)1180.0119sage: S(.001)1205.0121"""122eps = self.eps123x = float(x)124for i in range(len(self.support)):125z = self.support[i]126if z - eps <= x and x <= z + eps:127return self.height[i], i128return float(0), -1129130def __call__(self, x):131"""132Called when spike function is used as callable function.133134EXAMPLES::135136sage: S = spike_function([(0,5)],eps=.001)137sage: S(0)1385.0139sage: S(.1)1400.0141sage: S(.01)1420.0143sage: S(.001)1445.0145"""146return self._eval(x)[0]147148def plot_fft_abs(self, samples=2**12, xmin=None, xmax=None, **kwds):149"""150Plot of (absolute values of) Fast Fourier Transform of151the spike function with given number of samples.152153EXAMPLES::154155sage: S = spike_function([(-3,4),(-1,1),(2,3)]); S156A spike function with spikes at [-3.0, -1.0, 2.0]157sage: P = S.plot_fft_abs(8)158sage: p = P[0]; p.ydata159[5.0, 5.0, 3.367958691924177, 3.367958691924177, 4.123105625617661, 4.123105625617661, 4.759921664218055, 4.759921664218055]160"""161w = self.vector(samples = samples, xmin=xmin, xmax=xmax)162xmin, xmax = self._ranges(xmin, xmax)163z = w.fft()164k = vector(RDF, [abs(z[i]) for i in range(int(len(z)/2))])165return k.plot(xmin=0, xmax=1, **kwds)166167def plot_fft_arg(self, samples=2**12, xmin=None, xmax=None, **kwds):168"""169Plot of (absolute values of) Fast Fourier Transform of170the spike function with given number of samples.171172EXAMPLES::173174sage: S = spike_function([(-3,4),(-1,1),(2,3)]); S175A spike function with spikes at [-3.0, -1.0, 2.0]176sage: P = S.plot_fft_arg(8)177sage: p = P[0]; p.ydata178[0.0, 0.0, -0.211524990023434..., -0.211524990023434..., 0.244978663126864..., 0.244978663126864..., -0.149106180027477..., -0.149106180027477...]179"""180w = self.vector(samples = samples, xmin=xmin, xmax=xmax)181xmin, xmax = self._ranges(xmin, xmax)182z = w.fft()183k = vector(RDF, [(z[i]).arg() for i in range(int(len(z)/2))])184return k.plot(xmin=0, xmax=1, **kwds)185186def vector(self, samples=2**16, xmin=None, xmax=None):187"""188Creates a sampling vector of the spike function in question.189190EXAMPLES::191192sage: S = spike_function([(-3,4),(-1,1),(2,3)],0.001); S193A spike function with spikes at [-3.0, -1.0, 2.0]194sage: S.vector(16)195(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)196"""197v = vector(RDF, samples) # creates vector of zeros of length 2^16198xmin, xmax = self._ranges(xmin, xmax)199delta = (xmax - xmin)/samples200w = int(math.ceil(self.eps/delta))201for i in range(len(self.support)):202x = self.support[i]203if x > xmax:204break205h = self.height[i]206j = int((x - xmin)/delta)207for k in range(j, min(samples, j+w)):208v[k] = h209return v210211def _ranges(self, xmin, xmax):212"""213Quickly find appropriate plotting interval.214215EXAMPLES::216217sage: S = spike_function([(-1,1),(1,40)])218sage: S._ranges(None,None)219(-1.0, 1.0)220"""221width = (self.support[-1] + self.support[0])/float(2)222if xmin is None:223xmin = self.support[0] - width/float(5)224if xmax is None:225xmax = self.support[-1] + width/float(5)226if xmax <= xmin:227xmax = xmin + 1228return xmin, xmax229230def plot(self, xmin=None, xmax=None, **kwds):231"""232Special fast plot method for spike functions.233234EXAMPLES::235236sage: S = spike_function([(-1,1),(1,40)])237sage: P = plot(S)238sage: P[0]239Line defined by 8 points240"""241v = []242xmin, xmax = self._ranges(xmin, xmax)243x = xmin244eps = self.eps245while x < xmax:246y, i = self._eval(x)247v.append( (x, y) )248if i != -1:249x0 = self.support[i] + eps250v.extend([(x0,y), (x0,0)])251if i+1 < len(self.support):252x = self.support[i+1] - eps253v.append( (x, 0) )254else:255x = xmax256v.append( (xmax, 0) )257else:258new_x = None259for j in range(len(self.support)):260if self.support[j] - eps > x:261new_x = self.support[j] - eps262break263if new_x is None:264new_x = xmax265v.append( (new_x, 0) )266x = new_x267L = line(v, **kwds)268L.xmin(xmin-1); L.xmax(xmax)269return L270271272273spike_function = SpikeFunction274275276