Changeset 6

Show
Ignore:
Timestamp:
04/16/07 17:24:06 (2 years ago)
Author:
davison
Message:

Fixed indentation and a few API inconsistencies in pcsim module.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/checkAPI.py

    r5 r6  
    1414import re, string, types, getopt, sys, shutil, os 
    1515shutil.copy('dummy_hoc.py','hoc.py') 
    16 import common, neuron, nest, neuron2 
     16import common, neuron, nest, neuron2, pcsim 
    1717os.remove('hoc.py'); os.remove('hoc.pyc') 
    1818 
    1919 
     20red     = 0010; green  = 0020; yellow = 0030; blue = 0040; 
     21magenta = 0050; cyan   = 0060; bright = 0100 
    2022try: 
    2123    import ll.ansistyle 
    2224    coloured = True 
    23     red     = 0010; green  = 0020; yellow = 0030; blue = 0040; 
    24     magenta = 0050; cyan   = 0060; bright = 0100 
    2525except ImportError: 
    2626    coloured = False 
     
    4747                ] 
    4848 
    49 module_list = [neuron, nest, neuron2
     49module_list = [neuron, nest, neuron2, pcsim
    5050 
    5151if coloured: 
     
    7979    global inconsistency 
    8080    if str1 and str2: 
    81         str1 = str1.strip() 
    82         str2 = str2.strip(
     81        str1 = ' '.join(str1.strip().split()) # ignore differences in white space 
     82        str2 = ' '.join(str2.strip().split()
    8383        nchar1 = len(str1) 
    8484        if nchar1 <= len(str2) and str2[0:nchar1] == str1: 
  • trunk/pcsim.py

    r4 r6  
    1515import types 
    1616import sys 
    17 from numpy import import
     17from numpy import
    1818from pypcsim import * 
    1919from tables import * 
     
    5454        raise common.InvalidParameterValueError 
    5555    return paramDict 
     56 
     57class ID(common.ID): 
     58    pass 
    5659 
    5760# Implementation of the NativeRNG 
     
    238241class IF_curr_exp(common.IF_curr_exp): 
    239242    """Leaky integrate and fire model with fixed threshold and 
    240        decaying-exponential post-synaptic current. (Separate synaptic currents for 
    241        excitatory and inhibitory synapses.""" 
     243    decaying-exponential post-synaptic current. (Separate synaptic currents for 
     244    excitatory and inhibitory synapses.""" 
    242245     
    243246    translations = { 
     
    506509 
    507510class Population(common.Population): 
    508      """ 
    509      An array of neurons all of the same type. `Population' is used as a generic 
    510      term intended to include layers, columns, nuclei, etc., of cells. 
    511      All cells have both an address (a tuple) and an id (an integer). If p is a 
    512      Population object, the address and id can be inter-converted using : 
    513      id = p[address] 
    514      address = p.locate(id) 
    515      """ 
    516      nPop = 0 
     511    """ 
     512    An array of neurons all of the same type. `Population' is used as a generic 
     513    term intended to include layers, columns, nuclei, etc., of cells. 
     514    All cells have both an address (a tuple) and an id (an integer). If p is a 
     515    Population object, the address and id can be inter-converted using : 
     516    id = p[address] 
     517    address = p.locate(id) 
     518    """ 
     519    nPop = 0 
     520     
     521    def __init__(self, dims, cellclass, cellparams=None, label=None): 
     522        """ 
     523        dims should be a tuple containing the population dimensions, or a single 
     524          integer, for a one-dimensional population. 
     525          e.g., (10,10) will create a two-dimensional population of size 10x10. 
     526        cellclass should either be a standardized cell class (a class inheriting 
     527        from common.StandardCellType) or a string giving the name of the 
     528        simulator-specific model that makes up the population. 
     529        cellparams should be a dict which is passed to the neuron model 
     530          constructor 
     531        label is an optional name for the population. 
     532        """ 
     533        global gid, myid, nhost 
     534         
     535        if len(dims) > 3: 
     536            raise exceptions.AttributeError('PCSIM does not support populations with more than 3 dimensions') 
     537         
     538        self.actual_ndim = len(dims) 
     539         
     540        while len(dims) < 3: 
     541            dims += (1,) 
     542         
     543        common.Population.__init__(self, dims, cellclass, cellparams, label) 
     544         
     545         
     546                  
     547        # set the steps list, used by the __getitem__() method. 
     548        self.steps = [1]*self.ndim 
     549        for i in range(self.ndim-1): 
     550            for j in range(i+1, self.ndim): 
     551                self.steps[i] *= self.dim[j] 
     552         
     553        if isinstance(cellclass, str): 
     554            if not cellclass in globals(): 
     555                raise exceptions.AttributeError('Trying to create non-existent cellclass ' + cellclass.__name__ ) 
     556            cellclass = eval(cellclass) 
     557        self.celltype = cellclass 
     558        if issubclass(cellclass, common.StandardCellType): 
     559            self.cellfactory = cellclass(cellparams).simObjFactory 
     560        else: 
     561            if issubclass(cellclass, SimObject): 
     562                self.cellfactory = apply(cellclass, (), cellparams) 
     563            else: 
     564                raise exceptions.AttributeError('Trying to create non-existent cellclass ' + cellclass.__name__ ) 
     565         
     566             
     567        # CuboidGridPopulation(SimNetwork &net, GridPoint3D origin, Volume3DSize dims, SimObjectFactory &objFactory) 
     568        self.pcsim_population = CuboidGridObjectPopulation(pcsim_globals.net, GridPoint3D(0,0,0), Volume3DSize(dims[0], dims[1], dims[2]), self.cellfactory) 
     569         
     570        if not self.label: 
     571            self.label = 'population%d' % Population.nPop          
     572        self.record_from = { 'spiketimes': [], 'vtrace': [] }         
     573        Population.nPop += 1 
     574          
     575          
     576    def __getitem__(self, addr): 
     577        """Returns a representation of the cell with coordinates given by addr, 
     578           suitable for being passed to other methods that require a cell id. 
     579           Note that __getitem__ is called when using [] access, e.g. 
     580             p = Population(...) 
     581             p[2,3] is equivalent to p.__getitem__((2,3)). 
     582        """ 
     583        # What we actually pass around are gids. 
     584        orig_addr = addr; 
     585        if isinstance(addr, int): 
     586            addr = (addr,) 
     587        while len(addr) < 3: 
     588            addr += (0,)                   
     589        assert len(addr) == len(self.dim) 
     590         
     591        index = 0 
     592        for i, s in zip(addr, self.steps): 
     593            index += i*s 
     594        id = index  
     595        pcsim_index = self.pcsim_population.getIndex(addr[0],addr[1],addr[2]) 
     596        assert id == pcsim_index, " id = %s, pcsim_index = %s" % (id, pcsim_index) 
     597        assert orig_addr == self.locate(id), 'index=%s addr=%s id=%s locate(id)=%s' % (index, orig_addr, id, self.locate(id)) 
     598        return id 
     599          
     600          
     601    def locate(self, id): 
     602        """Given an element id in a Population, return the coordinates. 
     603               e.g. for  4 6  , element 2 has coordinates (1,0) and value 7 
     604                         7 9 
     605        """ 
     606        # id should be a gid 
     607        assert isinstance(id, int)          
     608        if self.ndim == 3: 
     609            rows = self.dim[0]; cols = self.dim[1] 
     610            i = id/(rows*cols); remainder = id%(rows*cols) 
     611            j = remainder/cols; k = remainder%cols 
     612            coords = (k, j, i) 
     613        elif self.ndim == 2: 
     614            cols = self.dim[1] 
     615            i = id/cols; j = id%cols 
     616            coords = (i, j) 
     617        elif self.ndim == 1: 
     618            coords = (id,) 
     619        else: 
     620            raise common.InvalidDimensionsError 
     621        if self.actual_ndim == 1: 
     622            coords = (coords[0],) 
     623        elif self.actual_ndim == 2: 
     624            coords = (coords[0],coords[1],) 
     625        pcsim_coords = self.pcsim_population.getLocation(id) 
     626        pcsim_coords = (pcsim_coords.x(), pcsim_coords.y(), pcsim_coords.z()) 
     627        if self.actual_ndim == 1: 
     628            pcsim_coords = (pcsim_coords[0],) 
     629        elif self.actual_ndim == 2: 
     630            pcsim_coords = (pcsim_coords[0],pcsim_coords[1],)     
     631        # assert coords == pcsim_coords, " coords = %s, pcsim_coords = %s " % (coords, pcsim_coords) 
     632        return pcsim_coords 
    517633      
    518      def __init__(self, dims, cellclass, cellparams=None, label=None): 
    519          """ 
    520          dims should be a tuple containing the population dimensions, or a single 
    521            integer, for a one-dimensional population. 
    522            e.g., (10,10) will create a two-dimensional population of size 10x10. 
    523          cellclass should either be a standardized cell class (a class inheriting 
    524          from common.StandardCellType) or a string giving the name of the 
    525          simulator-specific model that makes up the population. 
    526          cellparams should be a dict which is passed to the neuron model 
    527            constructor 
    528          label is an optional name for the population. 
    529          """ 
    530          global gid, myid, nhost 
    531           
    532          if len(dims) > 3: 
    533              raise exceptions.AttributeError('PCSIM does not support populations with more than 3 dimensions') 
    534           
    535          self.actual_ndim = len(dims) 
    536           
    537          while len(dims) < 3: 
    538              dims += (1,) 
    539           
    540          common.Population.__init__(self, dims, cellclass, cellparams, label) 
    541           
    542           
    543                    
    544          # set the steps list, used by the __getitem__() method. 
    545          self.steps = [1]*self.ndim 
    546          for i in range(self.ndim-1): 
    547              for j in range(i+1, self.ndim): 
    548                  self.steps[i] *= self.dim[j] 
    549           
    550          if isinstance(cellclass, str): 
    551              if not cellclass in globals(): 
    552                  raise exceptions.AttributeError('Trying to create non-existent cellclass ' + cellclass.__name__ ) 
    553              cellclass = eval(cellclass) 
    554          self.celltype = cellclass 
    555          if issubclass(cellclass, common.StandardCellType): 
    556              self.cellfactory = cellclass(cellparams).simObjFactory 
    557          else: 
    558              if issubclass(cellclass, SimObject): 
    559                  self.cellfactory = apply(cellclass, (), cellparams) 
    560              else: 
    561                  raise exceptions.AttributeError('Trying to create non-existent cellclass ' + cellclass.__name__ ) 
    562           
    563               
    564          # CuboidGridPopulation(SimNetwork &net, GridPoint3D origin, Volume3DSize dims, SimObjectFactory &objFactory) 
    565          self.pcsim_population = CuboidGridObjectPopulation(pcsim_globals.net, GridPoint3D(0,0,0), Volume3DSize(dims[0], dims[1], dims[2]), self.cellfactory) 
    566           
    567          if not self.label: 
    568              self.label = 'population%d' % Population.nPop          
    569          self.record_from = { 'spiketimes': [], 'vtrace': [] }         
    570          Population.nPop += 1 
    571           
    572           
    573      def __getitem__(self, addr): 
    574          """Returns a representation of the cell with coordinates given by addr, 
    575             suitable for being passed to other methods that require a cell id. 
    576             Note that __getitem__ is called when using [] access, e.g. 
    577               p = Population(...) 
    578               p[2,3] is equivalent to p.__getitem__((2,3)). 
    579          """ 
    580          # What we actually pass around are gids. 
    581          orig_addr = addr; 
    582          if isinstance(addr, int): 
    583              addr = (addr,) 
    584          while len(addr) < 3: 
    585              addr += (0,)                   
    586          assert len(addr) == len(self.dim) 
    587           
    588          index = 0 
    589          for i, s in zip(addr, self.steps): 
    590              index += i*s 
    591          id = index  
    592          pcsim_index = self.pcsim_population.getIndex(addr[0],addr[1],addr[2]) 
    593          assert id == pcsim_index, " id = %s, pcsim_index = %s" % (id, pcsim_index) 
    594          assert orig_addr == self.locate(id), 'index=%s addr=%s id=%s locate(id)=%s' % (index, orig_addr, id, self.locate(id)) 
    595          return id 
    596           
    597           
    598      def locate(self, id): 
    599          """Given an element id in a Population, return the coordinates. 
    600                 e.g. for  4 6  , element 2 has coordinates (1,0) and value 7 
    601                           7 9 
    602          """ 
    603          # id should be a gid 
    604          assert isinstance(id, int)          
    605          if self.ndim == 3: 
    606              rows = self.dim[0]; cols = self.dim[1] 
    607              i = id/(rows*cols); remainder = id%(rows*cols) 
    608              j = remainder/cols; k = remainder%cols 
    609              coords = (k, j, i) 
    610          elif self.ndim == 2: 
    611              cols = self.dim[1] 
    612              i = id/cols; j = id%cols 
    613              coords = (i, j) 
    614          elif self.ndim == 1: 
    615              coords = (id,) 
    616          else: 
    617              raise common.InvalidDimensionsError 
    618          if self.actual_ndim == 1: 
    619              coords = (coords[0],) 
    620          elif self.actual_ndim == 2: 
    621              coords = (coords[0],coords[1],) 
    622          pcsim_coords = self.pcsim_population.getLocation(id) 
    623          pcsim_coords = (pcsim_coords.x(), pcsim_coords.y(), pcsim_coords.z()) 
    624          if self.actual_ndim == 1: 
    625              pcsim_coords = (pcsim_coords[0],) 
    626          elif self.actual_ndim == 2: 
    627              pcsim_coords = (pcsim_coords[0],pcsim_coords[1],)     
    628          # assert coords == pcsim_coords, " coords = %s, pcsim_coords = %s " % (coords, pcsim_coords) 
    629          return pcsim_coords 
     634    def getObjectID(self, index): 
     635        return self.pcsim_population[index] 
     636     
     637    def __len__(self): 
     638        """Returns the total number of cells in the population.""" 
     639        return self.pcsim_population.size() 
     640         
     641    def set(self, param, val=None): 
     642        """ 
     643        Set one or more parameters for every cell in the population. param 
     644        can be a dict, in which case val should not be supplied, or a string 
     645        giving the parameter name, in which case val is the parameter value. 
     646        val can be a numeric value, or list of such. 
     647        e.g. p.set("tau_m",20.0). 
     648             p.set({'tau_m':20,'v_rest':-65}) 
     649        """ 
     650        """PCSIM: iteration through all elements """ 
     651        paramDict = checkParams(param, val) 
     652        if issubclass(self.celltype, common.StandardCellType): 
     653            paramDict = self.celltype({}).translate(paramDict) 
     654                  
     655        for index in range(0,len(self)): 
     656            obj = pcsim_globals.net.object(self.pcsim_population[index]) 
     657            if obj: 
     658                for param,value in paramDict.items(): 
     659                    setattr( obj, param, value ) 
     660          
     661          
     662    def tset(self, parametername, valueArray): 
     663        """ 
     664        'Topographic' set. Sets the value of parametername to the values in 
     665        valueArray, which must have the same dimensions as the Population. 
     666        """ 
     667        """PCSIM: iteration and set """ 
     668        if self.dim[0:self.actual_ndim] == valueArray.shape: 
     669            values = numpy.reshape(valueArray, valueArray.size)                           
     670            if issubclass(self.celltype, common.StandardCellType): 
     671                parametername = self.celltype({}).translate({parametername: values[0]}).keys()[0]              
     672            for i, val in enumerate(values): 
     673                try: 
     674                    obj = pcsim_globals.net.object(self.pcsim_population[i])                  
     675                    if obj: setattr(obj, parametername, val) 
     676                except TypeError: 
     677                    raise common.InvalidParameterValueError, "%s is not a numeric value" % str(val)              
     678        else: 
     679            raise common.InvalidDimensionsError 
     680          
     681    def rset(self, parametername, rand_distr): 
     682        """ 
     683        'Random' set. Sets the value of parametername to a value taken from 
     684        rand_distr, which should be a RandomDistribution object. 
     685        """ 
     686        """ 
     687            Will be implemented in the future more efficiently for  
     688            NativeRNGs. 
     689        """          
     690        rarr = numpy.array(rand_distr.next(n=self.size)) 
     691        rarr = rarr.reshape(self.dim[0:self.actual_ndim])          
     692        self.tset(parametername, rarr) 
    630693      
    631      def getObjectID(self, index): 
    632          return self.pcsim_population[index] 
     694    def _call(self, methodname, arguments): 
     695        """ 
     696        Calls the method methodname(arguments) for every cell in the population. 
     697        e.g. p.call("set_background","0.1") if the cell class has a method 
     698        set_background(). 
     699        """ 
     700        """ This works nicely for PCSIM for simulator specific cells,  
     701            because cells (SimObject classes) are directly wrapped in python """ 
     702        for i in xrange(0,len(self)): 
     703            obj = pcsim_globals.net.object(self.pcsim_population[i]) 
     704            if obj: apply( obj, methodname, (), arguments) 
     705          
     706          
    633707      
    634      def __len__(self): 
    635          """Returns the total number of cells in the population.""" 
    636          return self.pcsim_population.size() 
    637           
    638      def set(self, param, val=None): 
    639          """PCSIM: iteration through all elements """ 
    640          """ 
    641          Set one or more parameters for every cell in the population. param 
    642          can be a dict, in which case val should not be supplied, or a string 
    643          giving the parameter name, in which case val is the parameter value. 
    644          e.g. p.set("tau_m",20.0). 
    645               p.set({'tau_m':20,'v_rest':-65}) 
    646          """ 
    647          paramDict = checkParams(param, val) 
    648          if issubclass(self.celltype, common.StandardCellType): 
    649              paramDict = self.celltype({}).translate(paramDict) 
    650                    
    651          for index in range(0,len(self)): 
    652              obj = pcsim_globals.net.object(self.pcsim_population[index]) 
    653              if obj: 
    654                  for param,value in paramDict.items(): 
    655                      setattr( obj, param, value ) 
    656           
    657           
    658      def tset(self, parametername, valueArray): 
    659          """PCSIM: iteration and set """ 
    660          """ 
    661          'Topographic' set. Sets the value of parametername to the values in 
    662          valueArray, which must have the same dimensions as the Population. 
    663          """ 
    664          if self.dim[0:self.actual_ndim] == valueArray.shape: 
    665              values = numpy.reshape(valueArray, valueArray.size)                           
    666              if issubclass(self.celltype, common.StandardCellType): 
    667                  parametername = self.celltype({}).translate({parametername: values[0]}).keys()[0]              
    668              for i, val in enumerate(values): 
    669                  try: 
    670                      obj = pcsim_globals.net.object(self.pcsim_population[i])                  
    671                      if obj: setattr(obj, parametername, val) 
    672                  except TypeError: 
    673                      raise common.InvalidParameterValueError, "%s is not a numeric value" % str(val)              
    674          else: 
    675              raise common.InvalidDimensionsError 
    676           
    677      def rset(self, parametername, rand_distr): 
    678          """ 
    679          'Random' set. Sets the value of parametername to a value taken from 
    680          rand_distr, which should be a RandomDistribution object. 
    681          """ 
    682          """ 
    683              Will be implemented in the future more efficiently for  
    684              NativeRNGs. 
    685          """          
    686          rarr = numpy.array(rand_distr.next(n=self.size)) 
    687          rarr = rarr.reshape(self.dim[0:self.actual_ndim])          
    688          self.tset(parametername, rarr) 
     708    def _tcall(self, methodname, objarr): 
     709        """ 
     710        `Topographic' call. Calls the method methodname() for every cell in the  
     711        population. The argument to the method depends on the coordinates of the 
     712        cell. objarr is an array with the same dimensions as the Population. 
     713        e.g. p.tcall("memb_init",vinitArray) calls 
     714        p.cell[i][j].memb_init(vInitArray[i][j]) for all i,j. 
     715        """ 
     716        """ PCSIM: iteration at the python level and apply""" 
     717        for i in xrange(0,len(self)): 
     718            obj = pcsim_globals.net.object(self.pcsim_population[i]) 
     719            if obj: apply( obj, methodname, (), arguments) 
     720          
     721 
     722    def record(self, record_from=None, rng=None): 
     723        """ 
     724        If record_from is not given, record spikes from all cells in the Population. 
     725        record_from can be an integer - the number of cells to record from, chosen 
     726        at random (in this case a random number generator can also be supplied) 
     727        - or a list containing the ids (e.g., (i,j,k) tuple for a 3D population) 
     728        of the cells to record. 
     729        """ 
     730        """ PCSIM: IMPLEMENTED by an array of recorders at python level""" 
     731        """ 
     732          The current implementation allows only one invocation of this method per population 
     733        """ 
     734        if isinstance(record_from, int): 
     735            if not rng:   rng = pyNN.random.RandomDistribution(NativeRNG(seed = datetime.today().microsecond), 'UniformInteger', (0,len(self)-1))              
     736            src_indices = [ int(i) for i in rng.next(record_from) ]             
     737        elif record_from: 
     738            src_indices = record_from 
     739        else: 
     740            src_indices  = range(self.pcsim_population.size()) 
     741        sources = [ self.pcsim_population[i] for i in src_indices ] 
     742        self.spike_rec = SpikesMultiChannelRecorder(sources, None, src_indices) 
     743          
     744    def record_v(self, record_from=None, rng=None): 
     745        """ 
     746        If record_from is not given, record the membrane potential for all cells in 
     747        the Population. 
     748        record_from can be an integer - the number of cells to record from, chosen 
     749        at random (in this case a random number generator can also be supplied) 
     750        - or a list containing the ids of the cells to record.          
     751        """ 
     752        """ PCSIM: IMPLEMENTED by an array of recorders """ 
     753        if isinstance(record_from, int):              
     754            if not rng:   rng = pyNN.random.RandomDistribution(NativeRNG(seed = datetime.today().microsecond), 'UniformInteger', (0,len(self)-1))             
     755            src_indices = [ int(i) for i in rng.next(record_from) ]              
     756        elif record_from: 
     757            src_indices = record_from 
     758        else: 
     759            src_indices = range(self.pcsim_population.size()) 
     760        sources = [ self.pcsim_population[i] for i in src_indices ] 
     761        self.vm_rec = FieldMultiChannelRecorder(sources, None, src_indices) 
     762     
     763    def printSpikes(self, filename, gather=True): 
     764        """ 
     765        Prints spike times to file in the two-column format 
     766        "spiketime cell_id" where cell_id is the index of the cell counting 
     767        along rows and down columns (and the extension of that for 3-D). 
     768        This allows easy plotting of a `raster' plot of spiketimes, with one 
     769        line for each cell. This method requires that the cell class records 
     770        spikes in a vector spiketimes. 
     771        If gather is True, the file will only be created on the master node, 
     772        otherwise, a file will be written on each node. 
     773        """ 
     774        """PCSIM: implemented by corresponding recorders at python level """ 
     775        self.spike_rec.saveSpikesText(filename) 
     776          
     777          
     778    def print_v(self, filename, gather=True): 
     779        """ 
     780        Write membrane potential traces to file. 
     781        """ 
     782        """PCSIM: will be implemented by corresponding analog recorders at python level object  """ 
     783        self.spike_rec.saveValuesText(filename) 
     784          
    689785      
    690      def _call(self, methodname, arguments): 
    691          """ 
    692          Calls the method methodname(arguments) for every cell in the population. 
    693          e.g. p.call("set_background","0.1") if the cell class has a method 
    694          set_background(). 
    695          """ 
    696          """ This works nicely for PCSIM for simulator specific cells,  
    697              because cells (SimObject classes) are directly wrapped in python """ 
    698          for i in xrange(0,len(self)): 
    699              obj = pcsim_globals.net.object(self.pcsim_population[i]) 
    700              if obj: apply( obj, methodname, (), arguments) 
    701           
    702           
     786    def meanSpikeCount(self, gather=True):          
     787        """ 
     788            Returns the mean number of spikes per neuron. 
     789            NOTE: This method works in PCSIM only if you invoke the record 
     790                  during setup of the population. And the mean spike count 
     791                  takes into account only the neurons that are recorded, not all neurons. 
     792                  Implemented in this way because cells in PCSIM don't have 
     793                  actual internal recording mechanisms. All recordings are done with  
     794                  SpikeTimeRecorder SimObjects and spike messages between cells and  
     795                  recorders.  
     796        """ 
     797        if self.spike_rec: 
     798            return self.spike_rec.meanSpikeCount() 
     799        return 0; 
     800 
     801    def randomInit(self, rand_distr): 
     802        """ 
     803        Sets initial membrane potentials for all the cells in the population to 
     804        random values. 
     805        """ 
     806        """ PCSIM: can be reduced to rset() where parameterName is Vinit""" 
     807        self.rset("v_init", rand_distr) 
    703808      
    704      def _tcall(self, methodname, objarr): 
    705          """ PCSIM: iteration at the python level and apply""" 
    706          """ 
    707          `Topographic' call. Calls the method methodname() for every cell in the  
    708          population. The argument to the method depends on the coordinates of the 
    709          cell. objarr is an array with the same dimensions as the Population. 
    710          e.g. p.tcall("memb_init",vinitArray) calls 
    711          p.cell[i][j].memb_init(vInitArray[i][j]) for all i,j. 
    712          """ 
    713          for i in xrange(0,len(self)): 
    714              obj = pcsim_globals.net.object(self.pcsim_population[i]) 
    715              if obj: apply( obj, methodname, (), arguments) 
    716           
    717  
    718      def record(self, record_from=None, rng=None): 
    719          """ PCSIM: IMPLEMENTED by an array of recorders at python level""" 
    720          """ 
    721          If record_from is not given, record spikes from all cells in the Population. 
    722          record_from can be an integer - the number of cells to record from, chosen 
    723          at random (in this case a random number generator can also be supplied) 
    724          - or a list containing the ids (e.g., (i,j,k) tuple for a 3D population) 
    725          of the cells to record. 
    726          """          
    727          """ 
    728            The current implementation allows only one invocation of this method per population 
    729          """ 
    730          if isinstance(record_from, int): 
    731              if not rng:   rng = pyNN.random.RandomDistribution(NativeRNG(seed = datetime.today().microsecond), 'UniformInteger', (0,len(self)-1))              
    732              src_indices = [ int(i) for i in rng.next(record_from) ]             
    733          elif record_from: 
    734              src_indices = record_from 
    735          else: 
    736              src_indices  = range(self.pcsim_population.size()) 
    737          sources = [ self.pcsim_population[i] for i in src_indices ] 
    738          self.spike_rec = SpikesMultiChannelRecorder(sources, None, src_indices) 
    739           
    740      def record_v(self, record_from=None, rng=None): 
    741          """ PCSIM: IMPLEMENTED by an array of recorders """ 
    742          """ 
    743          If record_from is not given, record the membrane potential for all cells in 
    744          the Population. 
    745          record_from can be an integer - the number of cells to record from, chosen 
    746          at random (in this case a random number generator can also be supplied) 
    747          - or a list containing the ids of the cells to record.          
    748          """ 
    749          if isinstance(record_from, int):              
    750              if not rng:   rng = pyNN.random.RandomDistribution(NativeRNG(seed = datetime.today().microsecond), 'UniformInteger', (0,len(self)-1))             
    751              src_indices = [ int(i) for i in rng.next(record_from) ]              
    752          elif record_from: 
    753              src_indices = record_from 
    754          else: 
    755              src_indices = range(self.pcsim_population.size()) 
    756          sources = [ self.pcsim_population[i] for i in src_indices ] 
    757          self.vm_rec = FieldMultiChannelRecorder(sources, None, src_indices) 
    758       
    759      def printSpikes(self, filename, gather=True): 
    760          """PCSIM: implemented by corresponding recorders at python level """ 
    761          """ 
    762          Prints spike times to file in the two-column format 
    763          "spiketime cell_id" where cell_id is the index of the cell counting 
    764          along rows and down columns (and the extension of that for 3-D). 
    765          This allows easy plotting of a `raster' plot of spiketimes, with one 
    766          line for each cell. This method requires that the cell class records 
    767          spikes in a vector spiketimes. 
    768          If gather is True, the file will only be created on the master node, 
    769          otherwise, a file will be written on each node. 
    770          """ 
    771          self.spike_rec.saveSpikesText(filename) 
    772           
    773           
    774      def print_v(self, filename, gather=True): 
    775          """PCSIM: will be implemented by corresponding analog recorders at python level object  """ 
    776          """ 
    777          Write membrane potential traces to file. 
    778          """ 
    779          self.spike_rec.saveValuesText(filename) 
    780           
    781       
    782      def meanSpikeCount(self, gather=True):          
    783          """ 
    784              Returns the mean number of spikes per neuron. 
    785              NOTE: This method works in PCSIM only if you invoke the record 
    786                    during setup of the population. And the mean spike count 
    787                    takes into account only the neurons that are recorded, not all neurons. 
    788                    Implemented in this way because cells in PCSIM don't have 
    789                    actual internal recording mechanisms. All recordings are done with  
    790                    SpikeTimeRecorder SimObjects and spike messages between cells and  
    791                    recorders.  
    792          """ 
    793          if self.spike_rec: 
    794              return self.spike_rec.meanSpikeCount() 
    795          return 0; 
    796  
    797      def randomInit(self, rand_distr): 
    798          """ PCSIM: can be reduced to rset() where parameterName is Vinit""" 
    799          """ 
    800          Sets initial membrane potentials for all the cells in the population to 
    801          random values. 
    802          """          
    803          self.rset("v_init", rand_distr) 
    804       
    805809 
    806810 
    807811class Projection(common.Projection): 
    808      """ 
    809      A container for all the connections between two populations, together with 
    810      methods to set parameters of those connections, including of plasticity 
    811      mechanisms. 
    812      """ 
    813       
    814      nProj = 0 
    815       
    816      #class ConnectionDict: 
    817      #         
    818      #    def __init__(self,parent): 
    819      #        self.parent = parent 
    820      # 
    821      #    def __getitem__(self,id): 
    822      #        """Returns a connection id. 
    823      #        Suppose we have a 2D Population (5x3) projecting to a 3D Population (4x5x7). 
    824      #        Total number of possible connections is 5x3x4x5x7 = 2100. 
    825      #        Therefore valid calls are: 
    826      #        connection[2099] - 2099th possible connection (may not exist) 
    827      #        connection[14,139] - connection between 14th pre- and 139th postsynaptic neuron (may not exist) 
    828      #        connection[(4,2),(3,4,6)] - connection between presynaptic neuron with address (4,2) 
    829      #        and post-synaptic neuron with address (3,4,6) (may not exist). 
    830      #        """ 
    831      #        if isinstance(id, int): # linear mapping 
    832      #            preID = id/self.parent.post.size; postID = id%self.parent.post.size 
    833      #            return self.__getitem__((preID,postID)) 
    834      #        elif isinstance(id, tuple): # (pre,post) 
    835      #            if len(id) == 2: 
    836      #                pre = id[0] 
    837      #                post = id[1] 
    838      #                if isinstance(pre,int) and isinstance(post,int): 
    839      #                    pre_coords = self.parent.pre.locate(pre) 
    840      #                    post_coords = self.parent.post.locate(post) 
    841      #                    return self.__getitem__((pre_coords,post_coords)) 
    842      #                elif isinstance(pre,tuple) and isinstance(post,tuple): # should also allow lists 
    843      #                    if len(pre) == self.parent.pre.ndim and len(post) == self.parent.post.ndim: 
    844      #                        fmt = "[%d]"*(len(pre)+len(post)) 
    845      #                        address = fmt % (pre+post) 
    846      #                    else: 
    847      #                        raise common.InvalidDimensionsError 
    848      #                else: 
    849      #                    raise KeyError 
    850      #            else: 
    851      #                raise common.InvalidDimensionsError 
    852      #        else: 
    853      #            raise KeyError #most appropriate? 
    854      #         
    855      #        return address 
    856      # 
    857       
    858      def __init__(self, presynaptic_population, postsynaptic_population, method='allToAll', methodParameters=None, source=None, target=None, label=None, rng=None): 
    859          """ 
    860          presynaptic_population and postsynaptic_population - Population objects. 
    861           
    862          source - string specifying which attribute of the presynaptic cell signals action potentials 
    863           
    864          target - string specifying which synapse on the postsynaptic cell to connect to 
    865          If source and/or target are not given, default values are used. 
    866           
    867          method - string indicating which algorithm to use in determining connections. 
    868          Allowed methods are 'allToAll', 'oneToOne', 'fixedProbability', 
    869          'distanceDependentProbability', 'fixedNumberPre', 'fixedNumberPost', 
    870          'fromFile', 'fromList' 
    871           
    872          methodParameters - dict containing parameters needed by the connection method, 
    873          although we should allow this to be a number or string if there is only 
    874          one parameter. 
    875           
    876          rng - since most of the connection methods need uniform random numbers, 
    877          it is probably more convenient to specify a RNG object here rather 
    878          than within methodParameters, particularly since some methods also use 
    879          random numbers to give variability in the number of connections per cell. 
    880          """ 
    881          """ 
    882             PCSIM implementation specific comments: 
    883                 - source parameter does not have any meaning in context of PyPCSIM interface. Action potential 
    884                 signals are predefined by the neuron model and each cell has only one source,  
    885                 so there is no need to name a source since is implicitly known.  
    886                 - rng parameter is also not currently not applicable. For connection making only internal 
    887                 random number generators can be used. 
    888                 - The semantics of the target parameter is slightly changed: 
    889                     If it is a string then it represents a pcsim synapse class. 
    890                     If it is an integer then it represents which target(synapse) on the postsynaptic cell 
    891                     to connect to. 
    892                     It can be also a pcsim SimObjectFactory object which will be used for creation  
    893                     of the synapse objects associated to the created connections. 
    894                      
    895          """ 
    896          global pcsim_globals 
    897          common.Projection.__init__(self, presynaptic_population, postsynaptic_population, method, methodParameters, source, target, label, rng) 
    898           
    899          # Determine connection decider 
    900          if method == 'allToAll': 
    901              decider = RandomConnections(1) 
    902              wiring_method = DistributedSyncWiringMethod(pcsim_globals.net) 
    903          elif method == 'fixedProbability': 
    904              decider = RandomConnections(float(methodParameters)) 
    905              wiring_method = DistributedSyncWiringMethod(pcsim_globals.net) 
    906          elif method == 'distanceDependentProbability': 
    907              decider = EuclideanDistanceRandomConnections(methodParameters[0], methodParameters[1]) 
    908              wiring_method = DistributedSyncWiringMethod(pcsim_globals.net) 
    909          elif method == 'fixedNumberPre': 
    910              decider = DegreeDistributionConnections(ConstantNumber(parameters), DegreeDistributionConnections.incoming) 
    911              wiring_method = SimpleAllToAllWiringMethod(pcsim_globals.net) 
    912          elif method == 'fixedNumberPost': 
    913              decider = DegreeDistributionConnections(ConstantNumber(parameters), DegreeDistributionConnections.outgoing) 
    914              wiring_method = SimpleAllToAllWiringMethod(pcsim_globals.net) 
    915          elif method == 'oneToOne': 
    916              decider = RandomConnections(1) 
    917              wiring_method = OneToOneWiringMethod(pcsim_globals.net)  
    918          else: 
    919              raise Exception("METHOD NOT YET IMPLEMENTED") 
    920               
    921          if not target: 
    922              self.syn_factory = SimpleScalingSpikingSynapse(1, 1, pcsim_globals.minDelay/1000) 
    923          elif isinstance(target, int): 
    924              self.syn_factory = SimpleScalingSpikingSynapse(target, 1, pcsim_globals.minDelay/1000) 
    925          else: 
    926              if isinstance(target, str): 
    927                  target = eval(target) 
    928                  self.syn_factory = target({}) 
    929              else: 
    930                  self.syn_factory = target 
    931               
    932          self.pcsim_projection = ConnectionsProjection(self.pre.pcsim_population, self.post.pcsim_population,  
    933                                                        self.syn_factory, decider, wiring_method, collectIDs = True) 
    934  
    935          if not label: 
    936              self.label = 'projection%d' % Projection.nProj 
    937          if not rng: 
    938              self.rng = numpy.random.RandomState() 
    939          Projection.nProj += 1 
    940  
    941      def __len__(self): 
    942          """Return the total number of connections.""" 
    943          return self.pcsim_projection.size() 
    944       
    945      def __getitem__(self, n): 
    946          return self.pcsim_projection[n] 
    947  
    948       
    949      # --- Methods for setting connection parameters ---------------------------- 
    950       
    951      def setWeights(self, w): 
    952          """ 
    953          w can be a single number, in which case all weights are set to this 
    954          value, or an array with the same dimensions as the Projection array. 
    955          """ 
    956          if isinstance(w, float) or isinstance(w, int): 
    957              for i in range(len(self)): 
    958                  pcsim_globals.net.object(self.pcsim_projection[i]).W = w 
    959          else: 
    960              for i in range(len(self)): 
    961                  pcsim_globals.net.object(self.pcsim_projection[i]).W = w[i] 
    962       
    963      def randomizeWeights(self, rng): 
    964          """ 
    965          Set weights to random values taken from rng. 
    966          """ 
    967          # Arguably, we could merge this with set_weights just by detecting the 
    968          # argument type. It could make for easier-to-read simulation code to 
    969          # give it a separate name, though. Comments? 
    970          weights = rng.next(len(self)) 
    971          self.setWeights(weights) 
    972       
    973      def setDelays(self, d): 
    974          """ 
    975          d can be a single number, in which case all delays are set to this 
    976          value, or an array with the same dimensions as the Projection array. 
    977          """ 
    978          raise Exception("METHOD NOT YET IMPLEMENTED!") 
    979       
    980      def randomizeDelays(self, rng): 
    981          """ 
    982          Set delays to random values taken from rng. 
    983          """ 
    984          raise Exception("Method not yet implemented!") 
    985       
    986      def setThreshold(self, threshold): 
    987          """ 
    988          Where the emission of a spike is determined by watching for a 
    989          threshold crossing, set the value of this threshold. 
    990          """ 
    991          # This is a bit tricky, because in NEST and PCSIM the spike threshold is a 
    992          # property of the cell model, whereas in NEURON it is a property of the 
    993          # connection (NetCon). 
    994          raise Exception("Method not applicable to PCSIM") 
    995       
    996       
    997      # --- Methods relating to synaptic plasticity ------------------------------ 
    998       
    999      def setupSTDP(self, stdp_model, parameterDict): 
    1000          """Set-up STDP.""" 
    1001          raise Exception("Method not yet implemented") 
    1002       
    1003      def toggleSTDP(self, onoff): 
    1004          """Turn plasticity on or off.""" 
    1005          raise Exception("Method not yet implemented") 
    1006       
    1007      def setMaxWeight(self, wmax): 
    1008          """Note that not all STDP models have maximum or minimum weights.""" 
    1009          raise Exception("Method not yet implemented") 
    1010       
    1011      def setMinWeight(self, wmin): 
    1012          """Note that not all STDP models have maximum or minimum weights.""" 
    1013          raise Exception("Method not yet implemented") 
    1014       
    1015      # --- Methods for writing/reading information to/from file. ---------------- 
    1016       
    1017      def saveConnections(self, filename): 
    1018          """Save connections to file in a format suitable for reading in with the 
    1019          'fromFile' method.""" 
    1020          # should think about adding a 'gather' option. 
    1021          raise Exception("Method not yet implemented") 
    1022           
    1023       
    1024      def printWeights(self, filename, format=None): 
    1025          """Print synaptic weights to file.""" 
    1026          raise Exception("Method not yet implemented") 
    1027       
    1028      def weightHistogram(self, min=None, max=None, nbins=10): 
    1029          """ 
    1030          Return a histogram of synaptic weights. 
    1031          If min and max are not given, the minimum and maximum weights are 
    1032          calculated automatically. 
    1033          """ 
    1034          # it is arguable whether functions operating on the set of weights 
    1035          # should be put here or in an external module. 
    1036          raise Exception("Method not yet implemented") 
     812    """ 
     813    A container for all the connections between two populations, together with 
     814    methods to set parameters of those connections, including of plasticity 
     815    mechanisms. 
     816    """ 
     817     
     818    nProj = 0 
     819     
     820    def __init__(self, presynaptic_population, postsynaptic_population, method='allToAll', methodParameters=None, source=None, target=None, label=None, rng=None): 
     821        """ 
     822        presynaptic_population and postsynaptic_population - Population objects. 
     823         
     824        source - string specifying which attribute of the presynaptic cell signals action potentials 
     825         
     826        target - string specifying which synapse on the postsynaptic cell to connect to 
     827        If source and/or target are not given, default values are used. 
     828         
     829        method - string indicating which algorithm to use in determining connections. 
     830        Allowed methods are 'allToAll', 'oneToOne', 'fixedProbability', 
     831        'distanceDependentProbability', 'fixedNumberPre', 'fixedNumberPost', 
     832        'fromFile', 'fromList' 
     833         
     834        methodParameters - dict containing parameters needed by the connection method, 
     835        although we should allow this to be a number or string if there is only 
     836        one parameter. 
     837         
     838        rng - since most of the connection methods need uniform random numbers, 
     839        it is probably more convenient to specify a RNG object here rather 
     840        than within methodParameters, particularly since some methods also use 
     841        random numbers to give variability in the number of connections per cell. 
     842        """ 
     843        """ 
     844           PCSIM implementation specific comments: 
     845