root/trunk/examples/retina/retina.py
| Revision 340, 11.9 kB (checked in by LaurentPerrinet, 3 weeks ago) | |
|---|---|
| |
| Line | |
|---|---|
| 1 | #!/usr/bin/env python |
| 2 | # -*- coding: utf8 -*- |
| 3 | """ |
| 4 | |
| 5 | retina.py |
| 6 | ========= |
| 7 | |
| 8 | The retina for 'benchmark one'. This should feed the 'Hyper-Column' (see |
| 9 | https://facets.kip.uni-heidelberg.de/private/wiki/index.php/V1_hypercolumn ) |
| 10 | |
| 11 | |
| 12 | For this retina, it consists of 2 layers of neurons on a rectangular |
| 13 | grid connected in a one to one fashion. Here, we more use primate 'Phasic' |
| 14 | responses originate with morphologically larger ganglion cell types with fast |
| 15 | optic nerve fiber conduction velocities (~4 m/s, Gouras, 1969). Microelectrodes |
| 16 | staining of such cells shows that they are 'parasol' types (Dacey and Lee, 1994). |
| 17 | ON types branch low in the inner plexiform layer (sublamina b), while OFF types |
| 18 | branch high in the inner plexiform layer (sublamina a) following the classic |
| 19 | branching pattern for ON and OFF center cells (Nelson et al, 1978; Dacey and Lee, |
| 20 | 1994). Phasic cells are often referred to as the 'magnocellular' or 'M-cell' |
| 21 | pathway because their fibers terminate in the magnocellular layer of the lateral |
| 22 | geniculate nucleus of the thalamus. Near the fovea receptive fields of phasic |
| 23 | cells are 2-3 times larger than those of tonic cells and may be 10 times larger |
| 24 | in peripheral retina. |
| 25 | |
| 26 | The data times outputted by the model are the input to the LGN ( |
| 27 | http://en.wikipedia.org/wiki/Lateral_geniculate_nucleus ). Time for travelling |
| 28 | through the retina being compensated : |
| 29 | |
| 30 | |
| 31 | @article{Stanford87, |
| 32 | Author = {Stanford, L R}, |
| 33 | Journal = {Science}, |
| 34 | Month = {Oct}, |
| 35 | Number = {4825}, |
| 36 | Pages = {358-360}, |
| 37 | Title = {Conduction velocity variations minimize conduction time differences |
| 38 | among retinal ganglion cell axons}, |
| 39 | Volume = {238}, |
| 40 | Year = {1987}} |
| 41 | |
| 42 | To remember (in the cat) 5° of visual angle ~ 1 mm of retina |
| 43 | |
| 44 | See : |
| 45 | Data for parameters http://webvision.med.utah.edu/GCPHYS1.HTM (TODO LUP: define |
| 46 | different sets for different animals (cat, monkey, human)) |
| 47 | Morphology : http://webvision.med.utah.edu/GC1.html |
| 48 | Topics on CVonline http://homepages.inf.ed.ac.uk/cgi/rbf/CVONLINE/phase3entries.pl?TAG59 |
| 49 | |
| 50 | TODO (LuP) : use Ringach 07 for setting the neurons centers |
| 51 | TODO (LuP) : the grid is rectangular, but should be hexagonal / log-polar conformation |
| 52 | / define boudaries in biological coordinates |
| 53 | |
| 54 | from scipy.interpolate import interpolate |
| 55 | from numpy.random import randn |
| 56 | from numpy import * |
| 57 | |
| 58 | data = arange(16) |
| 59 | data = data+1 |
| 60 | data = data.reshape(4,4) |
| 61 | xrange = arange(4) |
| 62 | yrange = arange(4) |
| 63 | X,Y = meshgrid(xrange,yrange) |
| 64 | |
| 65 | outgrid = interpolate.interp2d(X,Y,data,kind='linear') |
| 66 | xi = array([0,0.5,1,1.5,2,2.5,3]) |
| 67 | yi = xi |
| 68 | |
| 69 | z = outgrid(xi,yi) |
| 70 | |
| 71 | |
| 72 | |
| 73 | TODO (LuP) : allow the use of moving images / |
| 74 | TODO (LuP) : justify or not SFA (since we are interested in synchrony effect, we should |
| 75 | rather use a simpler model) / noise model |
| 76 | TODO (LuP) : use real delays |
| 77 | TODO (LuP) : get significative statistics to the retina into that file |
| 78 | TODO (Lup) : use another cell type? |
| 79 | |
| 80 | TODO (Lup) : use nest2 definitively |
| 81 | |
| 82 | $ Id $ |
| 83 | |
| 84 | """ |
| 85 | import datetime |
| 86 | |
| 87 | import pyNN.nest2 as sim |
| 88 | from NeuroTools.signals import load_spikelist |
| 89 | import os, tempfile |
| 90 | import numpy |
| 91 | from NeuroTools.parameters import ParameterSet |
| 92 | |
| 93 | |
| 94 | class Retina(object): |
| 95 | """ |
| 96 | Model class for the retina. |
| 97 | |
| 98 | """ |
| 99 | def __init__(self,N=1000): |
| 100 | """ |
| 101 | Default parameters for the retina of size NxN. |
| 102 | |
| 103 | Sets up parameters and the whole structure of the dictionary / HDF file in url. |
| 104 | It contains all relevant parameters and stores them to a dictionary for |
| 105 | clarity &future compatibility with XML exports. |
| 106 | |
| 107 | """ |
| 108 | #try : |
| 109 | # url = "https://neuralensemble.org/svn/NeuroTools/trunk/examples/retina/retina.param" |
| 110 | # self.params = ParameterSet(url) |
| 111 | # print "Loaded parameters from SVN" |
| 112 | #except : |
| 113 | params = {} # a dictionary containing all parameters |
| 114 | # === Define parameters ======================================================== |
| 115 | # LUP: get running file name and include script in HDF5? |
| 116 | |
| 117 | params = {'description' : 'default retina', |
| 118 | 'N': N, # integer; square of total number of Ganglion Cells LUP: how do we include types and units in parameters? (or by default it is a float in ISO standards) TODO: go rectangular |
| 119 | 'N_ret': .2, # float; diameter of Ganglion Cell's RF (max: 1) |
| 120 | 'K_ret': 4.0, # float; ratio of center vs. surround in DOG |
| 121 | 'dt' : 0.1,# discretization step in simulations (ms) |
| 122 | 'simtime' : 40000*0.1, # float; (ms) |
| 123 | 'syn_delay' : 1.0, # float; (ms) |
| 124 | 'noise_std' : 5.0, # (nA) standard deviation of the internal noise |
| 125 | 'snr' : 2.0, # (nA) maximum signal |
| 126 | 'weight' : 1.0, # |
| 127 | #'threads' : 2, 'kernelseeds' : [43210987, 394780234], # array with one element per thread |
| 128 | #'threads' : 1, |
| 129 | 'kernelseed' : 4321097, # array with one element per thread |
| 130 | # seed for random generator used when building connections |
| 131 | 'connectseed' : 12345789, # seed for random generator(s) used during simulation |
| 132 | 'initialized' : datetime.datetime.now().isoformat() # the date in ISO 8601 format to avoid overriding old simulations |
| 133 | } |
| 134 | |
| 135 | # retinal neurons parameters |
| 136 | params['parameters_gc'] = {'Theta':-57.0, 'Vreset': -70.0,'Vinit': -70.0, |
| 137 | 'TauR': 0.5, 'gL':28.95, |
| 138 | 'C':289.5, 'V_reversal_E': 0.0, 'V_reversal_I': -75.0, 'TauSyn_E':1.5, |
| 139 | 'TauSyn_I': 10.0, 'V_reversal_sfa': -70.0, 'q_sfa': 0., #14.48, |
| 140 | 'Tau_sfa':110.0, 'V_reversal_relref': -70.0, 'q_relref': 3214.0, |
| 141 | 'Tau_relref': 1.97}#,'python':True} |
| 142 | |
| 143 | ## default input image # TODO add start and stop time |
| 144 | # define the center of every neuron in normalized visual angle / retinal space |
| 145 | |
| 146 | #X,Y = numpy.meshgrid(numpy.linspace(-N/2, N/2,N),numpy.linspace(-N/2,N/2,N)) |
| 147 | X,Y = numpy.random.rand(N)*2-1, numpy.random.rand(N)*2 -1 |
| 148 | |
| 149 | # Generate a DOG excitation on the input layer of the GC |
| 150 | # Based on the assumptions of the DOG model (Enroth-Cugell and Robson, 1966) that ganglion cells linearly add signals from both center and surround mechanisms for all points in space |
| 151 | # this is the impulse response to a discrete dirac in the center to some specific luminance / excentricity value |
| 152 | # easy : extend to input images (simply by convoluting the image with this kernel) |
| 153 | # hard : extend to time varrying movies (not only a step) |
| 154 | params['position'] = [X,Y] |
| 155 | R2= X**2 + Y**2 |
| 156 | N, N_ret, K_ret = params['N'], params['N_ret'], params['K_ret'] |
| 157 | amplitude = (numpy.exp(-R2 / ( 2 * N_ret**2) ) - 1/K_ret**2 * numpy.exp(-R2 / ( 2 * N_ret**2 * K_ret**2) )) / (1 - 1/K_ret**2) |
| 158 | #amplitude *= params['noise_std'] #Â scaled by the noise variance |
| 159 | #file.setStructure({'amplitude' : amplitude }, "/build", createparents=True) |
| 160 | params['amplitude'] = amplitude |
| 161 | |
| 162 | self.params = ParameterSet(params) |
| 163 | #self.params.save('file://' + os.getcwd() + '/retina.param') |
| 164 | |
| 165 | |
| 166 | def run(self,params,verbose=True): |
| 167 | """ |
| 168 | params are the parameters to use |
| 169 | |
| 170 | """ |
| 171 | tmpdir = tempfile.mkdtemp() |
| 172 | Timer = sim.Timer() |
| 173 | # === Build the network ======================================================== |
| 174 | if verbose: print "Setting up simulation" |
| 175 | Timer.start() # start timer on construction |
| 176 | sim.setup(timestep=params['dt'],max_delay=params['syn_delay']) |
| 177 | N = params['N'] |
| 178 | #dc_generator |
| 179 | phr_ON = sim.Population((N,),'dc_generator') |
| 180 | phr_OFF = sim.Population((N,),'dc_generator') |
| 181 | |
| 182 | |
| 183 | for factor, phr in [(-params['snr'],phr_OFF),(params['snr'],phr_ON)]: |
| 184 | phr.tset('amplitude', params['amplitude'] * factor ) |
| 185 | phr.set({ 'start' : params['simtime']/4, 'stop' : params['simtime']/4*3}) |
| 186 | |
| 187 | # internal noise model (see benchmark_noise) |
| 188 | noise_ON = sim.Population((N,),'noise_generator',{'mean':0.,'std':params['noise_std']}) |
| 189 | noise_OFF = sim.Population((N,),'noise_generator',{'mean':0.,'std':params['noise_std']}) |
| 190 | |
| 191 | |
| 192 | # target ON and OFF populations (what about a tridimensional Population?) |
| 193 | out_ON = sim.Population((N,), sim.IF_cond_alpha) #IF_curr_alpha)#'iaf_sfa_neuron')# EIF_cond_alpha_isfa_ista, IF_cond_exp_gsfa_grr,sim.IF_cond_alpha)#'iaf_sfa_neuron',params['parameters_gc'])#'iaf_cond_neuron')# IF_cond_alpha) # |
| 194 | out_OFF = sim.Population((N,), sim.IF_cond_alpha) #IF_curr_alpha)#'iaf_sfa_neuron')#sim.IF_curr_alpha)#,params['parameters_gc']) |
| 195 | |
| 196 | |
| 197 | # initialize membrane potential TODO: and conductances? |
| 198 | from pyNN.random import RandomDistribution, NumpyRNG |
| 199 | rng = NumpyRNG(seed=params['kernelseed']) |
| 200 | vinit_distr = RandomDistribution(distribution='uniform',parameters=[-70,-55], rng=rng) |
| 201 | for out_ in [out_ON, out_OFF]: |
| 202 | out_.randomInit(vinit_distr) |
| 203 | |
| 204 | |
| 205 | retina_proj_ON = sim.Projection(phr_ON, out_ON, 'oneToOne') |
| 206 | retina_proj_ON.setWeights(params['weight']) |
| 207 | # TODO fix setWeight, add setDelays to 10 ms (relative to stimulus onset) |
| 208 | retina_proj_OFF = sim.Projection(phr_OFF, out_OFF, 'oneToOne') |
| 209 | retina_proj_OFF.setWeights(params['weight']) |
| 210 | |
| 211 | noise_proj_ON = sim.Projection(noise_ON, out_ON, 'oneToOne') |
| 212 | noise_proj_ON.setWeights(params['weight']) |
| 213 | noise_proj_OFF = sim.Projection(noise_OFF, out_OFF, 'oneToOne') # implication if ON and OFF have the same noise input? |
| 214 | noise_proj_OFF.setWeights(params['weight']) |
| 215 | |
| 216 | |
| 217 | out_ON.record() |
| 218 | out_OFF.record() |
| 219 | |
| 220 | # reads out time used for building |
| 221 | buildCPUTime= Timer.elapsedTime() |
| 222 | |
| 223 | # === Run simulation =========================================================== |
| 224 | if verbose: print "Running simulation" |
| 225 | |
| 226 | Timer.reset() # start timer on construction |
| 227 | sim.run(params['simtime']) |
| 228 | simCPUTime = Timer.elapsedTime() |
| 229 | |
| 230 | Timer.reset() # start timer on construction |
| 231 | # TODO LUP use something like "for pop in [phr, out]" ? |
| 232 | out_ON_filename=os.path.join(tmpdir,'out_on.gdf') |
| 233 | out_OFF_filename=os.path.join(tmpdir,'out_off.gdf') |
| 234 | out_ON.printSpikes(out_ON_filename)# |
| 235 | out_OFF.printSpikes(out_OFF_filename)# |
| 236 | |
| 237 | # TODO LUP get out_ON_DATA on a 2D grid independantly of out_ON.cell.astype(int) |
| 238 | out_ON_DATA = load_spikelist(out_ON_filename,range(N), |
| 239 | t_start=0.0, t_stop=params['simtime']) |
| 240 | out_OFF_DATA = load_spikelist(out_OFF_filename,range(N), |
| 241 | t_start=0.0, t_stop=params['simtime']) |
| 242 | |
| 243 | out = {'out_ON_DATA':out_ON_DATA, |
| 244 | 'out_OFF_DATA':out_OFF_DATA}#,'out_ON_pos':out_ON} |
| 245 | # cleans up |
| 246 | os.remove(out_ON_filename) |
| 247 | os.remove(out_OFF_filename) |
| 248 | os.rmdir(tmpdir) |
| 249 | writeCPUTime = Timer.elapsedTime() |
| 250 | |
| 251 | if verbose: |
| 252 | print "\nRetina Network Simulation:" |
| 253 | print(params['description']) |
| 254 | print "Number of Neurons : ", N |
| 255 | print "Output rate (ON) : ", out_ON_DATA.mean_rate(), "Hz/neuron in ", params['simtime'], "ms" |
| 256 | print "Output rate (OFF) : ", out_OFF_DATA.mean_rate(), "Hz/neuron in ",params['simtime'], "ms" |
| 257 | print("Build time : %g s" % buildCPUTime) |
| 258 | print("Simulation time : %g s" % simCPUTime) |
| 259 | print("Writing time : %g s" % writeCPUTime) |
| 260 | |
| 261 | return out |
| 262 | |
| 263 | |
| 264 | if __name__ == '__main__': |
| 265 | |
| 266 | ret = Retina(100) |
| 267 | out = ret.run(ret.params) |
| 268 | # plotting |
| 269 | import pylab |
| 270 | fig = pylab.figure(1) |
| 271 | z = pylab.subplot(121) |
| 272 | out['out_ON_DATA'].raster_plot(display=z, id_list=range(20), kwargs={'color':'r'}) |
| 273 | z = pylab.subplot(122) |
| 274 | out['out_OFF_DATA'].raster_plot(display=z, id_list=range(20), kwargs={'color':'b'}) |
| 275 | fig = pylab.figure(2) |
| 276 | z = pylab.subplot(111) |
| 277 | out['out_ON_DATA'].firing_rate(ret.params.simtime/100, display=z, kwargs={'label':'ON','color':'r'}) |
| 278 | out['out_OFF_DATA'].firing_rate(ret.params.simtime/100, display=z, kwargs={'label':'OFF','color':'b'}) |
| 279 | pylab.legend() |
| 280 |
Note: See TracBrowser for help on using the browser.

