Changeset 307
- Timestamp:
- 11/08/08 10:58:36 (2 months ago)
- Files:
-
- trunk/examples/stgen (added)
- trunk/examples/stgen/inh_gamma_psth.py (added)
- trunk/src/__init__.py (modified) (2 diffs)
- trunk/src/stgen.py (modified) (13 diffs)
- trunk/test/test_stgen.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/src/__init__.py
r304 r307 6 6 7 7 # The nice thing would be to gathered every non standard 8 # dependency here, in order to centraliz zthe warning8 # dependency here, in order to centralize the warning 9 9 # messages and the check 10 10 dependencies = {'pylab' : {'website' : 'http://matplotlib.sourceforge.net/', 'is_present' : False, 'check':False}, … … 16 16 'NeuroTools.facets.hdf5' : {'website' : None, 'is_present' : False, 'check':False}, 17 17 'srblib' : {'website' : 'http://www.sdsc.edu/srb/index.php/Python', 'is_present' : False, 'check':False}, 18 'rpy' : {'website' : 'http://rpy.sourceforge.net/', 'is_present' : False, 'check':False}, 19 18 20 ## Add here your extensions ### 19 21 } trunk/src/stgen.py
r305 r307 1 """ 2 NeuroTools.stgen 3 ================ 4 5 A collection of tools for stochastic process generation. 6 7 8 Classes 9 ------- 10 11 StGen - Object to generate stochastic processes of various kinds 12 and return them as SpikeTrain or AnalogSignal objects. 13 14 15 Functions 16 --------- 17 18 shotnoise_fromspikes - Convolves the provided spike train with shot decaying exponential. 19 20 gamma_hazard - Compute the hazard function for a gamma process with parameters a,b. 21 """ 22 23 1 24 # TODO : needs refactoring 2 25 # TODO: needs python gsl wrapper / convert to numpy … … 9 32 10 33 11 def gamma_hazard (x, a, b, dt=1e-4):34 def gamma_hazard_scipy(x, a, b, dt=1e-4): 12 35 """ 13 36 Compute the hazard function for a gamma process with parameters a,b … … 21 44 """ 22 45 23 # TODO: this algorithm has numerical problems 46 # This algorithm is presently not used by 47 # inh_gamma_generator as it has numerical problems 24 48 # Try: 25 49 # plot(stgen.gamma_hazard(arange(0,1000.0,0.1),10.0,1.0/50.0)) … … 39 63 else: 40 64 return val 65 66 67 def gamma_hazard(x, a, b, dt=1e-4): 68 """ 69 Compute the hazard function for a gamma process with parameters a,b 70 where a and b are the parameters of the gamma PDF: 71 y(t) = x^(a-1) \exp(-x/b) / (\Gamma(a)*b^a) 72 73 Inputs: 74 x - in units of seconds 75 a - dimensionless 76 b - in units of seconds 77 """ 78 79 # Used by inh_gamma_generator 80 81 # Ideally, I would like to see an implementation which does not depend on RPy 82 # but the gamma_hazard_scipy above using scipy exhibits numerical problems, as it does not 83 # support directly returning the log. 84 85 if not check_dependency('rpy'): 86 raise ImportError("gamma_hazard requires RPy (http://rpy.sourceforge.net/)") 87 88 from rpy import r 89 90 # scipy.special.gammaincc has numerical problems 91 #Hpre = -log(scipy.special.gammaincc(a,(x-dt)/b)) 92 #Hpost = -log(scipy.special.gammaincc(a,(x+dt)/b)) 93 94 # reverting to the good old r.pgamma 95 Hpre = -r.pgamma(x-dt,shape=a,scale=b,lower=False,log=True) 96 Hpost = -r.pgamma(x+dt,shape=a,scale=b,lower=False,log=True) 97 val = 0.5*(Hpost-Hpre)/dt 98 99 return val 100 101 41 102 42 103 … … 45 106 def __init__(self, rng=None, seed=None): 46 107 """ 47 Spike Train Generator 48 Object to generate spiking random processes with various statistics 49 (inhomogeneous poisson, inhomogeneous gamma, etc.) as SpikeTrain objects. 108 Stochastic Process Generator 109 ============================ 110 111 Object to generate stochastic processes of various kinds 112 and return them as SpikeTrain or AnalogSignal objects. 113 50 114 51 115 Inputs: 52 rng - Seed for the random number generator. Can be None, or116 rng - The random number generator state object (optional). Can be None, or 53 117 a numpy.random.RandomState object, or an object with the same 54 118 interface. 119 120 seed - A seed for the rng (optional). 55 121 56 122 If rng is not None, the provided rng will be used to generate random numbers, … … 60 126 Examples: 61 127 >> x = StGen() 128 129 130 131 StGen Methods: 132 133 Spiking point processes: 134 ------------------------ 135 136 poisson_generator - homogeneous Poisson process 137 inh_poisson_generator - inhomogeneous Poisson process (time varying rate) 138 inh_gamma_generator - inhomogeneous Gamma process (time varying a,b) 139 140 Continuous time processes: 141 -------------------------- 142 143 OU_generator - Ohrnstein-Uhlenbeck process 144 145 146 See also: 147 shotnoise_fromspikes 148 62 149 """ 63 150 … … 75 162 76 163 77 def poisson_generator(self, rate, t_start=0.0, t_stop=1000.0, array=False ):164 def poisson_generator(self, rate, t_start=0.0, t_stop=1000.0, array=False,debug=False): 78 165 """ 79 166 Returns a SpikeList whose spikes are a realization of a Poisson process … … 98 185 """ 99 186 100 number = int((t_stop-t_start)/1000.0*2.0*rate) 187 #number = int((t_stop-t_start)/1000.0*2.0*rate) 188 189 # less wasteful than double length method above 190 n = (t_stop-t_start)/1000.0*rate 191 number = numpy.ceil(n+3*numpy.sqrt(n)) 192 if number<100: 193 number = min(5+numpy.ceil(2*n),100) 194 101 195 if number > 0: 102 196 isi = self.rng.exponential(1.0/rate, number)*1000.0 … … 111 205 i = numpy.searchsorted(spikes, t_stop) 112 206 207 extra_spikes = [] 113 208 if i==len(spikes): 114 raise RuntimeError("Internal ISI buffer overrun. Please file a bug report ticket at http://neuralensemble.org/NeuroTools.") 115 116 if array: 117 return numpy.resize(spikes,(i,)) 118 119 return SpikeTrain(numpy.resize(spikes,(i,)), t_start=t_start,t_stop=t_stop) 209 # ISI buf overrun 210 211 t_last = spikes[-1] + self.rng.exponential(1.0/rate, 1)[0]*1000.0 212 213 while (t_last<t_stop): 214 extra_spikes.append(t_last) 215 t_last += self.rng.exponential(1.0/rate, 1)[0]*1000.0 216 217 spikes = numpy.concatenate((spikes,extra_spikes)) 218 219 if debug: 220 print "ISI buf overrun handled. len(spikes)=%d, len(extra_spikes)=%d" % (len(spikes),len(extra_spikes)) 221 222 223 else: 224 spikes = numpy.resize(spikes,(i,)) 225 226 if not array: 227 spikes = SpikeTrain(spikes, t_start=t_start,t_stop=t_stop) 228 229 230 if debug: 231 return spikes, extra_spikes 232 else: 233 return spikes 120 234 121 235 … … 270 384 # TODO: provide optimized C/weave implementation if possible 271 385 272 inh_gamma_generator = _inh_gamma_generator_python 386 387 def _inh_gamma_generator_unimplemented(self, a, b, t, t_stop, array=False): 388 raise Exception("inh_gamma_generator is disabled as dependency RPy was not found.") 389 390 if check_dependency('rpy'): 391 inh_gamma_generator = _inh_gamma_generator_python 392 else: 393 inh_gamma_generator = _inh_gamma_generator_unimplemented 273 394 274 395 … … 432 553 OU_generator = _OU_generator_python2 433 554 434 # TODO: inhomogeneous OU generator555 # TODO: optimized inhomogeneous OU generator 435 556 436 557 … … 439 560 # TODO fix shotnoise stuff below ... and write tests 440 561 441 def shotnoise_generator(self,rate,tau,q,num,t):442 """443 Generate shotnoise444 445 Inputs:446 rate - the rate (in Hz) of the shotnoise447 tau - the exponential decay of the synapse448 g - the quantal increase for a spike449 450 quantal-increase-"q"-exponential-decay-"tau" synapse451 and a poisson spike train of "rate" and "num" """452 453 g = numpy.zeros(numpy.shape(t),float)454 for i in xrange(num):455 456 spikes = poisson_generator(rate,t[-1])457 dg=exp_conv(spikes,t,q,tau)458 tmp = add(g,dg,g)459 460 return g562 def shotnoise_fromspikes(self,rate,tau,q,num,t): 563 """ 564 Generate shotnoise 565 566 Inputs: 567 rate - the rate (in Hz) of the shotnoise 568 tau - the exponential decay of the synapse 569 g - the quantal increase for a spike 570 571 quantal-increase-"q"-exponential-decay-"tau" synapse 572 and a poisson spike train of "rate" and "num" """ 573 574 g = numpy.zeros(numpy.shape(t),float) 575 for i in xrange(num): 576 577 spikes = poisson_generator(rate,t[-1]) 578 dg=exp_conv(spikes,t,q,tau) 579 tmp = add(g,dg,g) 580 581 return g 461 582 462 583 … … 466 587 467 588 468 def exp_conv(poisson_train,t,q,tau,eps = 1.0e-8):589 def shotnoise_fromspikes(spike_train,q,tau,dt,array=False, eps = 1.0e-8): 469 590 """ 470 Convolve poisson spike trains with shot decaying exponentials 471 t must be equally spaced arrayrange 472 poisson spike times must all in the range of t 473 otherwise unpredicted results. 474 """ 475 476 dt = t[1]-t[0] 591 Convolves the provided spike train with shot decaying exponentials 592 yielding so called shot noise if the spike train is Poisson-like. 593 Returns an AnalogSignal if array=False, otherwise (shotnoise,t) as numpy arrays. 594 595 Inputs: 596 spike_train - a SpikeTrain object 597 q - the shot jump for each spike 598 tau - the shot decay time constant in milliseconds 599 dt - the resolution of the resulting shotnoise in milliseconds 600 array - if True, returns (shotnoise,t) as numpy arrays, otherwise an AnalogSignal. 601 eps - a numerical parameter indicating at what value of 602 the shot kernal the tail is cut. The default is usually fine. 603 604 Examples: 605 >> stg = stgen.StGen() 606 >> st = stg.poisson_generator(10.0,0.0,1000.0) 607 >> g_e = shotnoise_fromspikes(st,2.0,10.0) 608 609 610 See also: 611 poisson_generator, inh_gamma_generator, OU_generator ... 612 """ 613 614 st = spike_train 615 616 t = numpy.arange(st.t_start,st.t_stop,dt) 477 617 478 618 # time of vanishing significance 479 vs_t = -tau*log(eps/q) 480 481 kern = q*exp(-arrayrange(0.0,vs_t,dt)/tau) 482 483 idx = clip(searchsorted(t,poisson_train),0,len(t)-1) 484 485 a = zeros(shape(t),Float) 486 487 put(a,idx,1.0) 488 489 return convolve(a,kern)[0:len(t)] 619 vs_t = -tau*numpy.log(eps/q) 620 621 kern = q*numpy.exp(-numpy.arange(0.0,vs_t,dt)/tau) 622 623 idx = numpy.clip(numpy.searchsorted(t,poisson_train,'right')-1,0,len(t)-1) 624 625 a = numpy.zeros(shape(t),float) 626 627 a[idx] = 1.0 628 629 y = convolve(a,kern)[0:len(t)] 630 631 result = AnalogSignal(y,dt,t_start=0,t_stop=st.t_stop-st.t_start) 632 result.time_offset(st.t_start) 633 return result 634 490 635 491 636 trunk/test/test_stgen.py
r299 r307 41 41 assert isinstance(st, numpy.ndarray) 42 42 43 st = stg.poisson_generator(rate,0.0,t_stop,array=True,debug=True) 44 45 assert isinstance(st[0], numpy.ndarray) 46 assert isinstance(st[1], list) 47 48 st = stg.poisson_generator(rate,0.0,t_stop,debug=True) 49 50 assert isinstance(st[0], signals.SpikeTrain) 51 assert isinstance(st[1], list) 52 43 53 44 54 def testStatsPoisson(self): 55 56 # this is a statistical test with non-zero chance of failure 57 58 def test_poisson(rate,t_start,t_stop): 59 stg = stgen.StGen() 60 dt = t_stop-t_start 61 N = rate*dt/1000.0 62 63 st = stg.poisson_generator(rate,t_start=t_start,t_stop=t_stop,array=True) 64 65 if len(st) in (0,1,2,3): 66 assert N<15 67 return 68 69 70 71 assert st[-1] < t_stop 72 assert st[0] > t_start 73 74 75 # last spike should not be more than 4 ISI away from t_stop 76 err = """ 77 Last spike should not be more than 4 ISI behind t_stop. 78 There is a non-zero chance for this to occur during normal operation. 79 Re-run the test to see if the error persists.""" 80 81 if st[-1] < t_stop-4.0*1.0/rate*1000.0: 82 raise StatisticalError(err) 83 84 85 # first spike should not be more than 4 ISI away from t_start 86 err = """ 87 First spike should not be more than 4 ISI in front of t_start. 88 There is a non-zero chance for this to occur during normal operation. 89 Re-run the test to see if the error persists.""" 90 91 if st[0] > t_start+4.0*1.0/rate*1000.0: 92 raise StatisticalError(err) 93 94 err = """ 95 Number of spikes should be within 3 standard deviations of mean. 96 There is a non-zero chance for this to occur during normal operation. 97 Re-run the test to see if the error persists.""" 98 99 100 if len(st) > N+3.0*numpy.sqrt(N) or len(st) < N-3.0*numpy.sqrt(N): 101 raise StatisticalError(err) 102 103 104 # high rates 105 106 test_poisson(100.0,500.0,1500.0) 107 108 # high rates, short time 109 110 test_poisson(100.0,500.0,550.0) 111 112 # low rates, short time 113 114 test_poisson(2.0,500.0,550.0) 115 116 # low rates, long time 117 118 test_poisson(5.0,500.0,50500.0) 119 120 121 122 123 def testStatsInhPoisson(self): 45 124 46 125 # this is a statistical test with non-zero chance of failure … … 52 131 t_stop = 1500.0 # milliseconds 53 132 54 st = stg.poisson_generator(rate,t_start=t_start,t_stop=t_stop,array=True)55 56 assert st[-1] < t_stop57 assert st[0] > t_start58 59 60 # last spike should not be more than 4 ISI away from t_stop61 err = """62 Last spike should not be more than 4 ISI behind t_stop.63 There is a non-zero chance for this to occur during normal operation.64 Re-run the test to see if the error persists."""65 66 if st[-1] < t_stop-4.0*1.0/rate*1000.0:67 raise StatisticalError(err)68 69 70 # first spike should not be more than 4 ISI away from t_start71 err = """72 First spike should not be more than 4 ISI in front of t_start.73 There is a non-zero chance for this to occur during normal operation.74 Re-run the test to see if the error persists."""75 76 if st[0] > t_start+4.0*1.0/rate*1000.0:77 raise StatisticalError(err)78 79 err = """80 Number of spikes should be within 3 standard deviations of mean.81 There is a non-zero chance for this to occur during normal operation.82 Re-run the test to see if the error persists."""83 84 # time interval is one second85 86 if len(st) > rate+3.0*numpy.sqrt(rate) or len(st) < rate-3.0*numpy.sqrt(rate):87 raise StatisticalError(err)88 89 90 def testStatsInhPoisson(self):91 92 # this is a statistical test with non-zero chance of failure93 94 stg = stgen.StGen()95 96 rate = 100.0 #Hz97 t_start = 500.098 t_stop = 1500.0 # milliseconds99 100 133 st = stg.inh_poisson_generator(numpy.array([rate]),numpy.array([t_start]),t_stop,array=True) 101 134

