root/branches/0.3/common.py

Revision 121, 27.5 kB (checked in by apdavison, 2 years ago)

Fix to a wikidoc import bug.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Revision Id
Line 
1 # encoding: utf-8
2 """
3 Defines the PyNN classes and functions, and hence the FACETS API.
4 The simulator-specific classes should inherit from these and have the same
5 arguments.
6 $Id$
7 """
8 __version__ = "$Revision$"
9
10 import types, time, copy
11
12 class InvalidParameterValueError(Exception): pass
13 class NonExistentParameterError(Exception): pass
14 class InvalidDimensionsError(Exception): pass
15 class ConnectionError(Exception): pass
16
17 dt = 0.1
18
19
20 # ==============================================================================
21 #   Utility classes
22 # ==============================================================================
23
24 class ID(int):
25     """
26     This class is experimental. The idea is that instead of storing ids as
27     integers, we store them as ID objects, which allows a syntax like:
28       p[3,4].set('tau_m',20.0)
29     where p is a Population object. The question is, how big a memory/performance
30     hit is it to replace integers with ID objects?
31     """
32    
33     def __init__(self,n):
34         int.__init__(n)
35         self._position  = None
36         self._cellclass = None
37         self._hocname   = None
38         # The cellclass can be a global attribute of the ID object, but
39         # it may be discussed:
40         # The problem is that a call to the low-level funcitons set() and get() will need
41         # the cellclass to work. So we have to choose if we want to store that information in the ID
42         # object (as an attribute for example) or if we want to type it each time we need a call to set()
43         # or get() : p[2,3].set(SpikeSourceArray, {'spike_train' : {}}).
44
45     def set(self,param,val=None):
46         raise Exception("Not yet implemented")
47    
48     def get(self,param):
49         raise Exception("Not yet implemented")
50
51     def setCellClass(self, cellclass):
52         self._cellclass = cellclass   
53    
54     # Here is a proposal to manage the physical position of the cell, as an
55     # attribute of the ID class. Those positions can be used by functions such
56     # as _distantDependantProbability(), setTopographicDelay()...
57     def setPosition(self,pos):
58         self._position = pos
59        
60     def getPosition(self):
61         return self._position
62
63
64
65 # ==============================================================================
66 #   Standard cells
67 # ==============================================================================
68
69 class StandardCellType(object):
70     """Base class for standardized cell model classes."""
71    
72     translations = {}
73     default_parameters = {}
74    
75     def checkParameters(self, supplied_parameters, with_defaults=False):
76         """Checks that the parameters exist and have values of the correct type."""
77         default_parameters = self.__class__.default_parameters
78         if with_defaults:
79             parameters = copy.copy(default_parameters)
80         else:
81             parameters = {}
82         if supplied_parameters:
83             for k in supplied_parameters.keys():
84                 if default_parameters.has_key(k):
85                     if type(supplied_parameters[k]) == type(default_parameters[k]): # same type
86                         parameters[k] = supplied_parameters[k]
87                     elif type(default_parameters[k]) == types.FloatType: # float and something that can be converted to a float
88                         try:
89                             parameters[k] = float(supplied_parameters[k])
90                         except (ValueError, TypeError):
91                             raise InvalidParameterValueError, (type(supplied_parameters[k]), type(default_parameters[k]))
92                     elif type(default_parameters[k]) == types.ListType: # list and something that can be transformed to a list
93                         try:
94                             parameters[k] = list(supplied_parameters[k])
95                         except TypeError:
96                             raise InvalidParameterValueError, (type(supplied_parameters[k]), type(default_parameters[k]))
97                     else:
98                         raise InvalidParameterValueError, (type(supplied_parameters[k]), type(default_parameters[k]))
99                 else:
100                     raise NonExistentParameterError(k)
101         return parameters
102
103     def translate(self,parameters):
104         """Translate standardized model names to simulator specific names."""
105         parameters = self.checkParameters(parameters)
106         translated_parameters = {}
107         for k in parameters.keys():
108             pname = self.__class__.translations[k][0]
109             pval = eval(self.__class__.translations[k][1])
110             translated_parameters[pname] = pval
111         return translated_parameters
112
113     def __init__(self,parameters):
114         self.parameters = self.checkParameters(parameters, with_defaults=True)
115    
116    
117 class IF_curr_alpha(StandardCellType):
118     """Leaky integrate and fire model with fixed threshold and alpha-function-
119     shaped post-synaptic current."""
120    
121     default_parameters = {
122         'v_rest'     : -65.0,   # Resting membrane potential in mV.
123         'cm'         :   1.0,   # Capacity of the membrane in nF
124         'tau_m'      :  20.0,   # Membrane time constant in ms.
125         'tau_refrac' :   0.0,   # Duration of refractory period in ms.
126         'tau_syn'    :   5.0,   # Rise time of the synaptic alpha function in ms.
127         'i_offset'   :   0.0,   # Offset current in nA
128         'v_reset'    : -65.0,   # Reset potential after a spike in mV.
129         'v_thresh'   : -50.0,   # Spike threshold in mV.
130         'v_init'     : -65.0,   # Membrane potential in mV at t = 0
131     }
132
133 class IF_curr_exp(StandardCellType):
134     """Leaky integrate and fire model with fixed threshold and
135     decaying-exponential post-synaptic current. (Separate synaptic currents for
136     excitatory and inhibitory synapses)."""
137    
138     default_parameters = {
139         'v_rest'     : -65.0,   # Resting membrane potential in mV.
140         'cm'         : 1.0,     # Capacity of the membrane in nF
141         'tau_m'      : 20.0,    # Membrane time constant in ms.
142         'tau_refrac' : 0.0,     # Duration of refractory period in ms.
143         'tau_syn_E'  : 5.0,     # Decay time of excitatory synaptic current in ms.
144         'tau_syn_I'  : 5.0,     # Decay time of inhibitory synaptic current in ms.
145         'i_offset'   : 0.0,     # Offset current in nA
146         'v_reset'    : -65.0,   # Reset potential after a spike in mV.
147         'v_thresh'   : -50.0,   # Spike threshold in mV.
148         'v_init'     : -65.0,   # Membrane potential in mV at t = 0
149     }
150
151 class IF_cond_alpha(StandardCellType):
152     """Leaky integrate and fire model with fixed threshold and alpha-function-
153     shaped post-synaptic conductance."""
154    
155     default_parameters = {
156         'v_rest'     : -65.0,   # Resting membrane potential in mV.
157         'cm'         : 1.0,     # Capacity of the membrane in nF
158         'tau_m'      : 20.0,    # Membrane time constant in ms.
159         'tau_refrac' : 0.0,     # Duration of refractory period in ms.
160         'tau_syn_E'  : 5.0,     # Rise time of the excitatory synaptic alpha function in ms.
161         'tau_syn_I'  : 5.0,     # Rise time of the inhibitory synaptic alpha function in ms.
162         'e_rev_E'    : 0.0,     # Reversal potential for excitatory input in mV
163         'e_rev_I'    : -70.0,   # Reversal potential for inhibitory input in mV
164         'v_thresh'   : -50.0,   # Spike threshold in mV.
165         'v_reset'    : -65.0,   # Reset potential after a spike in mV.
166         'i_offset'   : 0.0,     # Offset current in nA
167         'v_init'     : -65.0,   # Membrane potential in mV at t = 0
168     }
169
170 class SpikeSourcePoisson(StandardCellType):
171     """Spike source, generating spikes according to a Poisson process."""
172
173     default_parameters = {
174         'rate'     : 0.0,       # Mean spike frequency (Hz)
175         'start'    : 0.0,       # Start time (ms)
176         'duration' : 1e9        # Duration of spike sequence (ms)
177     } 
178
179 class SpikeSourceArray(StandardCellType):
180     """Spike source generating spikes at the times given in the spike_times array."""
181    
182     default_parameters = { 'spike_times' : [] } # list or numpy array containing spike times in milliseconds.
183           
184
185 # ==============================================================================
186 #   Functions for simulation set-up and control
187 # ==============================================================================
188
189 def setup(timestep=0.1,min_delay=0.1,max_delay=0.1,debug=False,**extra_params):
190     """
191     Should be called at the very beginning of a script.
192     extra_params contains any keyword arguments that are required by a given
193     simulator but not by others.
194     """
195     dt = timestep
196     pass
197
198 def end(compatible_output=True):
199     """Do any necessary cleaning up before exiting."""
200     pass
201    
202 def run(simtime):
203     """Run the simulation for simtime ms."""
204     pass
205
206 def setRNGseeds(seedList):
207     """Globally set rng seeds."""
208     pass
209
210 # ==============================================================================
211 #   Low-level API for creating, connecting and recording from individual neurons
212 # ==============================================================================
213
214 def create(cellclass,paramDict=None,n=1):
215     """Create n cells all of the same type.
216     If n > 1, return a list of cell ids/references.
217     If n==1, return just the single id.
218     """
219     pass
220
221 def connect(source,target,weight=None,delay=None,synapse_type=None,p=1,rng=None):
222     """Connect a source of spikes to a synaptic target. source and target can
223     both be individual cells or lists of cells, in which case all possible
224     connections are made with probability p, using either the random number
225     generator supplied, or the default rng otherwise.
226     Weights should be in nA or µS."""
227     pass
228
229 def set(cells,cellclass,param,val=None):
230     """Set one or more parameters of an individual cell or list of cells.
231     param can be a dict, in which case val should not be supplied, or a string
232     giving the parameter name, in which case val is the parameter value.
233     cellclass must be supplied for doing translation of parameter names."""
234     pass
235
236 def record(source,filename):
237     """Record spikes to a file. source can be an individual cell or a list of
238     cells."""
239     # would actually like to be able to record to an array and choose later
240     # whether to write to a file.
241     pass
242
243 def record_v(source,filename):
244     """Record membrane potential to a file. source can be an individual cell or
245     a list of cells."""
246     # would actually like to be able to record to an array and choose later
247     # whether to write to a file.
248     pass
249
250 # ==============================================================================
251 #   High-level API for creating, connecting and recording from populations of
252 #   neurons.
253 # ==============================================================================
254
255 class Population:
256     """
257     An array of neurons all of the same type. `Population' is used as a generic
258     term intended to include layers, columns, nuclei, etc., of cells.
259     """
260    
261     def __init__(self,dims,cellclass,cellparams=None,label=None):
262         """
263         dims should be a tuple containing the population dimensions, or a single
264           integer, for a one-dimensional population.
265           e.g., (10,10) will create a two-dimensional population of size 10x10.
266         cellclass should either be a standardized cell class (a class inheriting
267         from common.StandardCellType) or a string giving the name of the
268         simulator-specific model that makes up the population.
269         cellparams should be a dict which is passed to the neuron model
270           constructor.
271         label is an optional name for the population.
272         """
273        
274         self.dim      = dims
275         if isinstance(dims, int): # also allow a single integer, for a 1D population
276             #print "Converting integer dims to tuple"
277             self.dim = (self.dim,)
278         self.label    = label
279         self.celltype = cellclass
280         self.ndim     = len(self.dim)
281         self.cellparams = cellparams
282         self.size = self.dim[0]
283         for i in range(1,self.ndim):
284             self.size *= self.dim[i]
285         self.cell = None # to be defined by child, simulator-specific classes
286     
287     def __getitem__(self,addr):
288         """Returns a representation of the cell with coordinates given by addr,
289            suitable for being passed to other methods that require a cell id.
290            Note that __getitem__ is called when using [] access, e.g.
291              p = Population(...)
292              p[2,3] is equivalent to p.__getitem__((2,3)).
293         """
294         pass
295    
296     def __len__(self):
297         """Returns the total number of cells in the population."""
298         return self.size
299    
300     def set(self,param,val=None):
301         """
302         Set one or more parameters for every cell in the population. param
303         can be a dict, in which case val should not be supplied, or a string
304         giving the parameter name, in which case val is the parameter value.
305         val can be a numeric value, or list of such (e.g. for setting spike times).
306         e.g. p.set("tau_m",20.0)
307              p.set({'tau_m':20,'v_rest':-65})
308         """
309         pass
310
311     def tset(self,parametername,valueArray):
312         """
313         'Topographic' set. Sets the value of parametername to the values in
314         valueArray, which must have the same dimensions as the Population.
315         """
316         pass
317    
318     def rset(self,parametername,rand_distr):
319         """
320         'Random' set. Sets the value of parametername to a value taken from
321         rand_distr, which should be a RandomDistribution object.
322         """
323         pass
324    
325     def _call(self,methodname,arguments):
326         """
327         Calls the method methodname(arguments) for every cell in the population.
328         e.g. p.call("set_background","0.1") if the cell class has a method
329         set_background().
330         """
331         pass
332    
333     def _tcall(self,methodname,objarr):
334         """
335         `Topographic' call. Calls the method methodname() for every cell in the
336         population. The argument to the method depends on the coordinates of the
337         cell. objarr is an array with the same dimensions as the Population.
338         e.g. p.tcall("memb_init",vinitArray) calls
339         p.cell[i][j].memb_init(vInitArray[i][j]) for all i,j.
340         """
341         pass
342
343     def randomInit(self,rand_distr):
344         """
345         Sets initial membrane potentials for all the cells in the population to
346         random values.
347         """
348         pass
349
350     def record(self,record_from=None,rng=None):
351         """
352         If record_from is not given, record spikes from all cells in the Population.
353         record_from can be an integer - the number of cells to record from, chosen
354         at random (in this case a random number generator can also be supplied)
355         - or a list containing the ids of the cells to record.
356         """
357         pass
358
359     def record_v(self,record_from=None,rng=None):
360         """
361         If record_from is not given, record the membrane potential for all cells in
362         the Population.
363         record_from can be an integer - the number of cells to record from, chosen
364         at random (in this case a random number generator can also be supplied)
365         - or a list containing the ids of the cells to record.
366         """
367         pass
368
369     def printSpikes(self,filename,gather=True,compatible_output=True):
370         """
371         Writes spike times to file.
372         If compatible_output is True, the format is "spiketime cell_id",
373         where cell_id is the index of the cell counting along rows and down
374         columns (and the extension of that for 3-D).
375         This allows easy plotting of a `raster' plot of spiketimes, with one
376         line for each cell.
377         The timestep and number of data points per cell is written as a header,
378         indicated by a '#' at the beginning of the line.
379         
380         If compatible_output is False, the raw format produced by the simulator
381         is used. This may be faster, since it avoids any post-processing of the
382         spike files.
383         
384         If gather is True, the file will only be created on the master node,
385         otherwise, a file will be written on each node.
386         """       
387         pass
388    
389     def print_v(self,filename,gather=True, compatible_output=True):
390         """
391         Write membrane potential traces to file.
392         If compatible_output is True, the format is "v cell_id",
393         where cell_id is the index of the cell counting along rows and down
394         columns (and the extension of that for 3-D).
395         This allows easy plotting of a `raster' plot of spiketimes, with one
396         line for each cell.
397         The timestep and number of data points per cell is written as a header,
398         indicated by a '#' at the beginning of the line.
399         
400         If compatible_output is False, the raw format produced by the simulator
401         is used. This may be faster, since it avoids any post-processing of the
402         voltage files.
403         """
404         pass
405    
406     def meanSpikeCount(self,gather=True):
407         """
408         Returns the mean number of spikes per neuron.
409         """
410         # gather is not relevant, but is needed for API consistency
411         pass
412
413 # ==============================================================================
414
415 class Projection:
416     """
417     A container for all the connections between two populations, together with
418     methods to set parameters of those connections, including of plasticity
419     mechanisms.
420     """
421    
422     def __init__(self, presynaptic_population, postsynaptic_population,
423                  method='allToAll', methodParameters=None,
424                  source=None, target=None, label=None, rng=None):
425         """
426         presynaptic_population and postsynaptic_population - Population objects.
427         
428         source - string specifying which attribute of the presynaptic cell signals action potentials.
429         
430         target - string specifying which synapse on the postsynaptic cell to connect to
431         If source and/or target are not given, default values are used.
432         
433         method - string indicating which algorithm to use in determining connections.
434         Allowed methods are 'allToAll', 'oneToOne', 'fixedProbability',
435         'distanceDependentProbability', 'fixedNumberPre', 'fixedNumberPost',
436         'fromFile', 'fromList'.
437         
438         methodParameters - dict containing parameters needed by the connection method,
439         although we should allow this to be a number or string if there is only
440         one parameter.
441         
442         rng - since most of the connection methods need uniform random numbers,
443         it is probably more convenient to specify a RNG object here rather
444         than within methodParameters, particularly since some methods also use
445         random numbers to give variability in the number of connections per cell.
446         """
447        
448         self.pre    = presynaptic_population  # } these really       
449         self.source = source                  # } should be
450         self.post   = postsynaptic_population # } read-only
451         self.target = target                  # }
452         if label:
453             self.label = label
454         self.rng = rng
455         self.connection = None # access individual connections. To be defined by child, simulator-specific classes
456     
457     def __len__(self):
458         """Return the total number of connections."""
459         return self.nconn
460    
461     # --- Connection methods ---------------------------------------------------
462     
463     def _allToAll(self,parameters=None,synapse_type=None):
464         """
465         Connect all cells in the presynaptic population to all cells in the postsynaptic population.
466         """
467         allow_self_connections = True # when pre- and post- are the same population,
468                                       # is a cell allowed to connect to itself?
469         if parameters and parameters.has_key('allow_self_connections'):
470             allow_self_connections = parameters['allow_self_connections']
471    
472     def _oneToOne(self,synapse_type=None):
473         """
474         Where the pre- and postsynaptic populations have the same size, connect
475         cell i in the presynaptic population to cell i in the postsynaptic
476         population for all i.
477         In fact, despite the name, this should probably be generalised to the
478         case where the pre and post populations have different dimensions, e.g.,
479         cell i in a 1D pre population of size n should connect to all cells
480         in row i of a 2D post population of size (n,m).
481         """
482         pass
483    
484     def _fixedProbability(self,parameters,synapse_type=None):
485         """
486         For each pair of pre-post cells, the connection probability is constant.
487         """
488         allow_self_connections = True
489         try:
490             p_connect = float(parameters)
491         except TypeError:
492             p_connect = parameters['p_connect']
493             if parameters.has_key('allow_self_connections'):
494                 allow_self_connections = parameters['allow_self_connections']
495    
496     def _distanceDependentProbability(self,parameters,synapse_type=None):
497         """
498         For each pair of pre-post cells, the connection probability depends on distance.
499         d_expression should be the right-hand side of a valid python expression
500         for probability, involving 'd', e.g. "exp(-abs(d))", or "float(d<3)"
501         """
502         allow_self_connections = True
503         if type(parameters) == types.StringType:
504             d_expression = parameters
505         else:
506             d_expression = parameters['d_expression']
507             if parameters.has_key('allow_self_connections'):
508                 allow_self_connections = parameters['allow_self_connections']
509    
510     def _fixedNumberPre(self,parameters,synapse_type=None):
511         """Each presynaptic cell makes a fixed number of connections."""
512         allow_self_connections = True
513         if type(parameters) == types.IntType:
514             n = parameters
515         elif type(parameters) == types.DictType:
516             if parameters.has_key['n']: # all cells have same number of connections
517                 n = parameters['n']
518             elif parameters.has_key['rng']: # number of connections per cell follows a distribution
519                 rng = parameters['rng']
520             if parameters.has_key('allow_self_connections'):
521                 allow_self_connections = parameters['allow_self_connections']
522         else : # assume parameters is a rng
523             rng = parameters
524    
525     def _fixedNumberPost(self,parameters,synapse_type=None):
526         """Each postsynaptic cell receives a fixed number of connections."""
527         allow_self_connections = True
528         if type(parameters) == types.IntType:
529             n = parameters
530         elif type(parameters) == types.DictType:
531             if parameters.has_key['n']: # all cells have same number of connections
532                 n = parameters['n']
533             elif parameters.has_key['rng']: # number of connections per cell follows a distribution
534                 rng = parameters['rng']
535             if parameters.has_key('allow_self_connections'):
536                 allow_self_connections = parameters['allow_self_connections']
537         else : # assume parameters is a rng
538             rng = parameters
539    
540     def _fromFile(self,parameters,synapse_type=None):
541         """
542         Load connections from a file.
543         """
544         if type(parameters) == types.FileType:
545             fileobj = parameters
546             # check fileobj is already open for reading
547         elif type(parameters) == types.StringType:
548             filename = parameters
549             # now open the file...
550         elif type(parameters) == types.DictType:
551             # dict could have 'filename' key or 'file' key
552             # implement this...
553             pass
554        
555     def _fromList(self,conn_list,synapse_type=None):
556         """
557         Read connections from a list of tuples,
558         containing [pre_addr, post_addr, weight, delay]
559         where pre_addr and post_addr are both neuron addresses, i.e. tuples or
560         lists containing the neuron array coordinates.
561         """
562         # Need to implement parameter parsing here...
563         pass
564    
565     # --- Methods for setting connection parameters ----------------------------
566     
567     def setWeights(self,w):
568         """
569         w can be a single number, in which case all weights are set to this
570         value, or a list/1D array of length equal to the number of connections
571         in the population.
572         Weights should be in nA for current-based and µS for conductance-based
573         synapses.
574         """
575         pass
576    
577     def randomizeWeights(self,rand_distr):
578         """
579         Set weights to random values taken from rand_distr.
580         """
581         # Arguably, we could merge this with set_weights just by detecting the
582         # argument type. It could make for easier-to-read simulation code to
583         # give it a separate name, though. Comments?
584         pass
585    
586     def setDelays(self,d):
587         """
588         d can be a single number, in which case all delays are set to this
589         value, or a list/1D array of length equal to the number of connections
590         in the population.
591         """
592         pass
593    
594     def randomizeDelays(self,rand_distr):
595         """
596         Set delays to random values taken from rand_distr.
597         """
598         pass
599    
600     def setThreshold(self,threshold):
601         """
602         Where the emission of a spike is determined by watching for a
603         threshold crossing, set the value of this threshold.
604         """
605         # This is a bit tricky, because in NEST the spike threshold is a
606         # property of the cell model, whereas in NEURON it is a property of the
607         # connection (NetCon).
608         pass
609    
610    
611     # --- Methods relating to synaptic plasticity ------------------------------
612     
613     def setupSTDP(self,stdp_model,parameterDict):
614         """Set-up STDP."""
615         pass
616    
617     def toggleSTDP(self,onoff):
618         """Turn plasticity on or off."""
619         pass
620    
621     def setMaxWeight(self,wmax):
622         """Note that not all STDP models have maximum or minimum weights."""
623         pass
624    
625     def setMinWeight(self,wmin):
626         """Note that not all STDP models have maximum or minimum weights."""
627         pass
628    
629     # --- Methods for writing/reading information to/from file. ----------------
630     
631     def saveConnections(self,filename,gather=False):
632         """Save connections to file in a format suitable for reading in with the
633         'fromFile' method."""
634         pass
635    
636     def printWeights(self,filename,format=None,gather=True):
637         """Print synaptic weights to file."""
638         pass
639    
640     def weightHistogram(self,min=None,max=None,nbins=10):
641         """
642         Return a histogram of synaptic weights.
643         If min and max are not given, the minimum and maximum weights are
644         calculated automatically.
645         """
646         # it is arguable whether functions operating on the set of weights
647         # should be put here or in an external module.
648         pass
649
650
651 # ==============================================================================
652 #   Utility classes
653 # ==============================================================================
654   
655 class Timer:
656     """For timing script execution."""
657     # Note that this class only has static methods, i.e. there is only one timer.
658     # It might be nice to allow instances to be created, i.e. have the possibility
659     # of multiple independent timers.
660     
661     @staticmethod
662     def start():
663         """Start timing."""
664         global start_time
665         start_time = time.time()
666    
667     @staticmethod
668     def elapsedTime():
669         """Return the elapsed time but keep the clock running."""
670         return time.time() - start_time
671    
672     @staticmethod
673     def reset():
674         """Reset the time to zero, and start the clock."""
675         global start_time
676         start_time = time.time()
677    
678 # ==============================================================================
679
Note: See TracBrowser for help on using the browser.