| | 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 | |
| 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 |
| 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 |
| 441 | | def shotnoise_generator(self,rate,tau,q,num,t): |
| 442 | | """ |
| 443 | | Generate shotnoise |
| 444 | | |
| 445 | | Inputs: |
| 446 | | rate - the rate (in Hz) of the shotnoise |
| 447 | | tau - the exponential decay of the synapse |
| 448 | | g - the quantal increase for a spike |
| 449 | | |
| 450 | | quantal-increase-"q"-exponential-decay-"tau" synapse |
| 451 | | 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 g |
| | 562 | 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 |
| 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) |
| 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 | |