| 26 | | |
|---|
| 27 | | gid = 0 |
|---|
| 28 | | ncid = 0 |
|---|
| 29 | | gidlist = [] |
|---|
| 30 | | vfilelist = {} |
|---|
| 31 | | spikefilelist = {} |
|---|
| 32 | | dt = 0.1 |
|---|
| 33 | | running = False |
|---|
| 34 | | initialised = False |
|---|
| 35 | | |
|---|
| 36 | | # ============================================================================== |
|---|
| 37 | | # Utility classes and functions |
|---|
| 38 | | # ============================================================================== |
|---|
| 39 | | |
|---|
| 40 | | class ID(common.ID): |
|---|
| 41 | | """ |
|---|
| 42 | | Instead of storing ids as integers, we store them as ID objects, |
|---|
| 43 | | which allows a syntax like: |
|---|
| 44 | | p[3,4].tau_m = 20.0 |
|---|
| 45 | | where p is a Population object. The question is, how big a memory/performance |
|---|
| 46 | | hit is it to replace integers with ID objects? |
|---|
| 47 | | """ |
|---|
| 48 | | |
|---|
| 49 | | def __init__(self, n): |
|---|
| 50 | | common.ID.__init__(self, n) |
|---|
| 51 | | self.hocname = None |
|---|
| 52 | | self.model_obj = None |
|---|
| 53 | | |
|---|
| 54 | | def __getattr__(self, name): |
|---|
| 55 | | """Note that this currently does not translate units.""" |
|---|
| 56 | | if type(self.cellclass) == type and issubclass(self.cellclass, common.StandardCellType): |
|---|
| 57 | | translated_name = self.cellclass.translations[name][0] |
|---|
| 58 | | else: |
|---|
| 59 | | translated_name = name |
|---|
| 60 | | if self.hocname: |
|---|
| 61 | | return getattr(neuron.h, '%s.%s' % (self.hocname, translated_name)) |
|---|
| 62 | | else: |
|---|
| 63 | | return getattr(neuron.h, 'cell%d.%s' % (int(self), translated_name)) |
|---|
| 64 | | |
|---|
| 65 | | def setParameters(self,**parameters): |
|---|
| 66 | | # We perform a call to the low-level function set() of the API. |
|---|
| 67 | | # If the cellclass is not defined in the ID object, we have an error: |
|---|
| 68 | | if (self.cellclass == None): |
|---|
| 69 | | raise Exception("Unknown cellclass") |
|---|
| 70 | | else: |
|---|
| 71 | | #Otherwise we use the ID one. Nevertheless, here we have a small problem in the |
|---|
| 72 | | #parallel framework. Suppose a population is created, distributed among |
|---|
| 73 | | #several nodes. Then a call like cell[i, j].set() should be performed only on the |
|---|
| 74 | | #node who owns the cell. To do that, if the node doesn't have the cell, a call to set() |
|---|
| 75 | | #does nothing... |
|---|
| 76 | | set(self, self.cellclass, parameters) |
|---|
| 77 | | |
|---|
| 78 | | def getParameters(self): |
|---|
| 79 | | params = {} |
|---|
| 80 | | for k in self.cellclass.translations.keys(): |
|---|
| 81 | | params[k] = self.__getattr__(k) |
|---|
| 82 | | return params |
|---|
| 83 | | |
|---|
| 84 | | def list_standard_models(): |
|---|
| 85 | | return [obj for obj in globals().values() if isinstance(obj, type) and issubclass(obj, common.StandardCellType)] |
|---|
| 86 | | |
|---|
| 87 | | # ============================================================================== |
|---|
| 88 | | # Module-specific functions and classes (not part of the common API) |
|---|
| 89 | | # ============================================================================== |
|---|
| 90 | | |
|---|
| 91 | | def _translate_synapse_type(synapse_type, weight=None): |
|---|
| 92 | | """ |
|---|
| 93 | | If synapse_type is given (not None), it is used to determine whether the |
|---|
| 94 | | synapse is excitatory or inhibitory. |
|---|
| 95 | | Otherwise, the synapse type is inferred from the sign of the weight. |
|---|
| 96 | | Much testing needed to check if this behaviour matches nest and pcsim. |
|---|
| 97 | | """ |
|---|
| 98 | | |
|---|
| 99 | | if synapse_type: |
|---|
| 100 | | if synapse_type == 'excitatory': |
|---|
| 101 | | syn_objref = "esyn" |
|---|
| 102 | | elif synapse_type == 'inhibitory': |
|---|
| 103 | | syn_objref = "isyn" |
|---|
| 104 | | else: |
|---|
| 105 | | # More sophisticated treatment needed once we have more sophisticated synapse |
|---|
| 106 | | # models, e.g. NMDA... |
|---|
| 107 | | #raise common.InvalidParameterValueError, synapse_type, "valid types are 'excitatory' or 'inhibitory'" |
|---|
| 108 | | syn_objref = synapse_type |
|---|
| 109 | | else: |
|---|
| 110 | | if weight is None or weight >= 0.0: |
|---|
| 111 | | syn_objref = "esyn" |
|---|
| 112 | | else: |
|---|
| 113 | | syn_objref = "isyn" |
|---|
| 114 | | return syn_objref |
|---|
| 115 | | |
|---|
| 116 | | def checkParams(param, val=None): |
|---|
| 117 | | """Check parameters are of valid types, normalise the different ways of |
|---|
| 118 | | specifying parameters and values by putting everything in a dict. |
|---|
| 119 | | Called by set() and Population.set().""" |
|---|
| 120 | | if isinstance(param, str): |
|---|
| 121 | | if isinstance(val, float) or isinstance(val, int): |
|---|
| 122 | | param_dict = {param:float(val)} |
|---|
| 123 | | elif isinstance(val,(str, list)): |
|---|
| 124 | | param_dict = {param:val} |
|---|
| 125 | | else: |
|---|
| 126 | | raise common.InvalidParameterValueError |
|---|
| 127 | | elif isinstance(param, dict): |
|---|
| 128 | | param_dict = param |
|---|
| 129 | | else: |
|---|
| 130 | | raise common.InvalidParameterValueError |
|---|
| 131 | | return param_dict |
|---|
| 132 | | |
|---|
| | 20 | import operator |
|---|
| | 21 | |
|---|
| | 22 | # Global variables |
|---|
| | 23 | gid_counter = 0 |
|---|
| | 24 | initialised = False |
|---|
| | 25 | running = False |
|---|
| | 26 | quit_on_end = True |
|---|
| | 27 | recorder_list = [] |
|---|
| | 28 | |
|---|
| 143 | | global dt, nhost, myid, _min_delay, logger, initialised, pc |
|---|
| 144 | | dt = timestep |
|---|
| 145 | | _min_delay = min_delay |
|---|
| 146 | | |
|---|
| 147 | | # Initialisation of the log module. To write in the logfile, simply enter |
|---|
| 148 | | # logging.critical(), logging.debug(), logging.info(), logging.warning() |
|---|
| 149 | | if debug: |
|---|
| 150 | | logging.basicConfig(level=logging.DEBUG, |
|---|
| 151 | | format='%(asctime)s %(levelname)s %(message)s', |
|---|
| 152 | | filename='neuron2.log', |
|---|
| 153 | | filemode='w') |
|---|
| 154 | | else: |
|---|
| 155 | | logging.basicConfig(level=logging.INFO, |
|---|
| 156 | | format='%(asctime)s %(levelname)s %(message)s', |
|---|
| 157 | | filename='neuron2.log', |
|---|
| 158 | | filemode='w') |
|---|
| 159 | | |
|---|
| 160 | | logging.info("Initialization of NEURON (use setup(.., debug=True) to see a full logfile)") |
|---|
| 161 | | |
|---|
| 162 | | # All the objects that will be used frequently in the hoc code are declared in the setup |
|---|
| 163 | | neuron.h.dt = dt |
|---|
| 164 | | if initialised: |
|---|
| 165 | | pass |
|---|
| 166 | | else: |
|---|
| 167 | | #hoc_commands = [ |
|---|
| 168 | | # 'tmp = xopen("%s")' % os.path.join(pyNN_path[0],'hoc','standardCells.hoc'), |
|---|
| 169 | | # 'tmp = xopen("%s")' % os.path.join(pyNN_path[0],'hoc','odict.hoc'), |
|---|
| 170 | | # 'objref pc', |
|---|
| 171 | | # 'pc = new ParallelContext()', |
|---|
| 172 | | # 'dt = %f' % dt, |
|---|
| 173 | | # 'create dummy_section', |
|---|
| 174 | | # 'access dummy_section', |
|---|
| 175 | | # 'objref netconlist, nil', |
|---|
| 176 | | # 'netconlist = new List()', |
|---|
| 177 | | # 'strdef cmd', |
|---|
| 178 | | # 'strdef fmt', |
|---|
| 179 | | # 'objref nc', |
|---|
| 180 | | # 'objref rng', |
|---|
| 181 | | # 'objref cell'] |
|---|
| 182 | | neuron.xopen("%s" % os.path.join(pyNN_path[0],'hoc','standardCells.hoc') ) |
|---|
| 183 | | neuron.xopen("%s" % os.path.join(pyNN_path[0],'hoc','odict.hoc') ) # to suppress - odict no longer needed? |
|---|
| | 39 | global initialised, quit_on_end, running, pc |
|---|
| | 40 | if not initialised: |
|---|
| | 41 | h('min_delay = 0') |
|---|
| | 42 | h('tstop = 0') |
|---|
| 245 | | #neuron.h.tstop = simtime |
|---|
| 246 | | print "dt = %f" % dt |
|---|
| 247 | | print "min delay = ", pc.set_maxstep(100) |
|---|
| 248 | | #neuron.finitialize() |
|---|
| 249 | | neuron.init() |
|---|
| 250 | | print "tstop = ", simtime |
|---|
| 251 | | neuron.h('tstop = %g' % simtime) |
|---|
| 252 | | pc.psolve(simtime) |
|---|
| | 74 | local_minimum_delay = pc.set_maxstep(10) |
|---|
| | 75 | h.finitialize() |
|---|
| | 76 | h.tstop = 0 |
|---|
| | 77 | logging.debug("local_minimum_delay on host #%d = %g" % (rank(), local_minimum_delay)) |
|---|
| | 78 | if num_processes() > 1: |
|---|
| | 79 | assert local_minimum_delay >= get_min_delay(),\ |
|---|
| | 80 | "There are connections with delays (%g) shorter than the minimum delay (%g)" % (local_minimum_delay, get_min_delay()) |
|---|
| | 81 | h.tstop = simtime |
|---|
| | 82 | pc.psolve(h.tstop) |
|---|
| | 83 | return get_current_time() |
|---|
| | 84 | |
|---|
| | 85 | # ============================================================================== |
|---|
| | 86 | # Functions returning information about the simulation state |
|---|
| | 87 | # ============================================================================== |
|---|
| | 88 | |
|---|
| | 89 | def get_current_time(): |
|---|
| | 90 | """Return the current time in the simulation.""" |
|---|
| | 91 | return h.t |
|---|
| | 92 | |
|---|
| | 93 | def get_time_step(): |
|---|
| | 94 | return h.dt |
|---|
| | 95 | common.get_time_step = get_time_step |
|---|
| | 96 | |
|---|
| | 97 | def get_min_delay(): |
|---|
| | 98 | return h.min_delay |
|---|
| | 99 | common.get_min_delay = get_min_delay |
|---|
| | 100 | |
|---|
| | 101 | def num_processes(): |
|---|
| | 102 | return int(pc.nhost()) |
|---|
| | 103 | |
|---|
| | 104 | def rank(): |
|---|
| | 105 | """Return the MPI rank.""" |
|---|
| | 106 | return int(pc.id()) |
|---|
| | 107 | |
|---|
| | 108 | def list_standard_models(): |
|---|
| | 109 | return [obj for obj in globals().values() if isinstance(obj, type) and issubclass(obj, common.StandardCellType)] |
|---|
| | 110 | |
|---|
| | 111 | class ID(int, common.IDMixin): |
|---|
| | 112 | |
|---|
| | 113 | def __init__(self, n): |
|---|
| | 114 | int.__init__(n) |
|---|
| | 115 | common.IDMixin.__init__(self) |
|---|
| | 116 | |
|---|
| | 117 | def _build_cell(self, celltype, parent=None): |
|---|
| | 118 | gid = int(self) |
|---|
| | 119 | self._cell = celltype.model(**celltype.parameters) # create the cell object |
|---|
| | 120 | pc.set_gid2node(gid, rank()) # assign the gid to this node |
|---|
| | 121 | nc = neuron.NetCon(self._cell.source, None) # } associate the cell spike source |
|---|
| | 122 | pc.cell(gid, nc.hoc_obj) # } with the gid (using a temporary NetCon) |
|---|
| | 123 | self.parent = parent |
|---|
| | 124 | |
|---|
| | 125 | def get_native_parameters(self): |
|---|
| | 126 | D = {} |
|---|
| | 127 | for name in ('tau_m', 'cm', 'v_rest', 'v_thresh', 't_refrac', |
|---|
| | 128 | 'i_offset', 'v_reset', 'v_init', 'tau_e', 'tau_i'): |
|---|
| | 129 | D[name] = getattr(self._cell, name) |
|---|
| | 130 | return D |
|---|
| | 131 | |
|---|
| | 132 | def set_native_parameters(self, parameters): |
|---|
| | 133 | for name, val in parameters.items(): |
|---|
| | 134 | setattr(self._cell, name, val) |
|---|
| | 135 | |
|---|
| 257 | | |
|---|
| 258 | | def create(cellclass, param_dict=None, n=1): |
|---|
| 259 | | """ |
|---|
| 260 | | Create n cells all of the same type. |
|---|
| 261 | | If n > 1, return a list of cell ids/references. |
|---|
| 262 | | If n==1, return just the single id. |
|---|
| 263 | | """ |
|---|
| 264 | | global gid, gidlist, nhost, myid, pc, nc |
|---|
| 265 | | |
|---|
| 266 | | assert n > 0, 'n must be a positive integer' |
|---|
| 267 | | #if isinstance(cellclass, type): |
|---|
| 268 | | # celltype = cellclass(param_dict) |
|---|
| 269 | | # hoc_name = celltype.hoc_name |
|---|
| 270 | | # hoc_commands, argstr = _hoc_arglist([celltype.parameters]) |
|---|
| 271 | | #elif isinstance(cellclass, str): |
|---|
| 272 | | # hoc_name = cellclass |
|---|
| 273 | | # hoc_commands, argstr = _hoc_arglist([param_dict]) |
|---|
| 274 | | #argstr = argstr.strip().strip(',') |
|---|
| 275 | | |
|---|
| 276 | | # round-robin partitioning |
|---|
| 277 | | newgidlist = [i+myid for i in range(gid, gid+n, nhost) if i < gid+n-myid] |
|---|
| 278 | | cell_list = [] |
|---|
| 279 | | for cell_id in newgidlist: |
|---|
| 280 | | #hoc_commands += ['tmp = pc.set_gid2node(%d,%d)' % (cell_id, myid), |
|---|
| 281 | | # 'objref cell%d' % cell_id, |
|---|
| 282 | | # 'cell%d = new %s(%s)' % (cell_id, hoc_name, argstr), |
|---|
| 283 | | # 'tmp = cell%d.connect2target(nil, nc)' % cell_id, |
|---|
| 284 | | # #'nc = new NetCon(cell%d.source, nil)' % cell_id, |
|---|
| 285 | | # 'tmp = pc.cell(%d, nc)' % cell_id] |
|---|
| 286 | | cell = cellclass(param_dict) # create the cell object |
|---|
| 287 | | cell.gid = cell_id # and assign its gid |
|---|
| 288 | | pc.set_gid2node(cell.gid, myid) # assign this gid to this node |
|---|
| 289 | | nc = neuron.NetCon(cell.source, None) # } associate the cell spike source |
|---|
| 290 | | pc.cell(cell.gid, nc.hoc_obj) # } with the gid (using a temporary NetCon) |
|---|
| 291 | | cell_list.append(cell) |
|---|
| 292 | | |
|---|
| 293 | | gidlist.extend(newgidlist) |
|---|
| 294 | | #cell_list = [ID(i) for i in range(gid, gid+n)] |
|---|
| 295 | | #for id in cell_list: |
|---|
| 296 | | # id.cellclass = cellclass |
|---|
| 297 | | gid = gid+n |
|---|
| 298 | | if n == 1: |
|---|
| 299 | | cell_list = cell_list[0] |
|---|
| 300 | | return cell_list |
|---|
| 301 | | |
|---|
| 302 | | def connect(source, target, weight=None, delay=None, synapse_type=None, p=1, rng=None): |
|---|
| 303 | | """Connect a source of spikes to a synaptic target. source and target can |
|---|
| 304 | | both be individual cells or lists of cells, in which case all possible |
|---|
| 305 | | connections are made with probability p, using either the random number |
|---|
| 306 | | generator supplied, or the default rng otherwise. |
|---|
| 307 | | Weights should be in nA or µS.""" |
|---|
| 308 | | global ncid, gid, gidlist, _min_delay, pc |
|---|
| 309 | | if type(source) != types.ListType: |
|---|
| 310 | | source = [source] |
|---|
| 311 | | if type(target) != types.ListType: |
|---|
| 312 | | target = [target] |
|---|
| 313 | | if weight is None: weight = 0.0 |
|---|
| 314 | | if delay is None: delay = _min_delay |
|---|
| 315 | | syn_objref = _translate_synapse_type(synapse_type, weight) |
|---|
| 316 | | nc_start = ncid |
|---|
| 317 | | hoc_commands = [] |
|---|
| 318 | | conn_list = [] |
|---|
| 319 | | for tgt in target: |
|---|
| 320 | | if tgt.gid > gid or tgt.gid < 0: # or not isinstance(tgt, int): |
|---|
| 321 | | raise common.ConnectionError, "Postsynaptic cell id %s does not exist." % str(tgt.gid) |
|---|
| 322 | | else: |
|---|
| 323 | | if tgt.gid in gidlist: # only create connections to cells that exist on this machine |
|---|
| 324 | | if p < 1: |
|---|
| 325 | | if rng: # use the supplied RNG |
|---|
| 326 | | rarr = self.rng.uniform(0,1, len(source)) |
|---|
| 327 | | else: # use the default RNG |
|---|
| 328 | | rarr = numpy.random.uniform(0,1, len(source)) |
|---|
| 329 | | for j, src in enumerate(source): |
|---|
| 330 | | if src.gid > gid or src.gid < 0: # or not isinstance(src, int): |
|---|
| 331 | | raise common.ConnectionError, "Presynaptic cell id %s does not exist." % str(src.gid) |
|---|
| 332 | | else: |
|---|
| 333 | | if p >= 1.0 or rarr[j] < p: # might be more efficient to vectorise the latter comparison |
|---|
| 334 | | |
|---|
| 335 | | nc = pc.gid_connect(src.gid, |
|---|
| 336 | | getattr(tgt, syn_objref).hoc_obj) |
|---|
| 337 | | nc.delay = delay |
|---|
| 338 | | nc.weight[0] = weight |
|---|
| 339 | | conn_list.append(nc) |
|---|
| 340 | | ncid += 1 |
|---|
| 341 | | print nc, weight, nc.weight[0] |
|---|
| 342 | | #hoc_execute(hoc_commands, "--- connect(%s,%s) ---" % (str(source), str(target))) |
|---|
| 343 | | #return range(nc_start, ncid) # why not return a list of NetCon objects, instead of ids? |
|---|
| 344 | | if len(conn_list) == 1: |
|---|
| 345 | | conn_list = conn_list[0] |
|---|
| 346 | | return conn_list |
|---|
| 347 | | |
|---|
| 348 | | def set(cells, cellclass, param, val=None): |
|---|
| 349 | | """Set one or more parameters of an individual cell or list of cells. |
|---|
| 350 | | param can be a dict, in which case val should not be supplied, or a string |
|---|
| 351 | | giving the parameter name, in which case val is the parameter value. |
|---|
| 352 | | cellclass must be supplied for doing translation of parameter names.""" |
|---|
| 353 | | global gidlist |
|---|
| 354 | | |
|---|
| 355 | | param_dict = checkParams(param, val) |
|---|
| 356 | | |
|---|
| 357 | | if type(cellclass) == type and issubclass(cellclass, common.StandardCellType): |
|---|
| 358 | | param_dict = cellclass({}).translate(param_dict) |
|---|
| 359 | | if not isinstance(cells, list): |
|---|
| 360 | | cells = [cells] |
|---|
| 361 | | hoc_commands = [] |
|---|
| 362 | | for param, val in param_dict.items(): |
|---|
| 363 | | if isinstance(val, str): |
|---|
| 364 | | fmt = 'pc.gid2cell(%d).%s = "%s"' |
|---|
| 365 | | elif isinstance(val, list): |
|---|
| 366 | | cmds, argstr = _hoc_arglist([val]) |
|---|
| 367 | | hoc_commands += cmds |
|---|
| 368 | | fmt = 'pc.gid2cell(%d).%s = %s' |
|---|
| 369 | | val = argstr |
|---|
| 370 | | else: |
|---|
| 371 | | fmt = 'pc.gid2cell(%d).%s = %g' |
|---|
| 372 | | for cell in cells: |
|---|
| 373 | | if cell in gidlist: |
|---|
| 374 | | hoc_commands += [fmt % (cell, param, val), |
|---|
| 375 | | 'tmp = pc.gid2cell(%d).param_update()' % cell] |
|---|
| 376 | | hoc_execute(hoc_commands, "--- set() ---") |
|---|
| 377 | | |
|---|
| 378 | | def record(source, filename): |
|---|
| 379 | | """Record spikes to a file. source can be an individual cell or a list of |
|---|
| 380 | | cells.""" |
|---|
| 381 | | # would actually like to be able to record to an array and choose later |
|---|
| 382 | | # whether to write to a file. |
|---|
| 383 | | global spikefilelist, gidlist |
|---|
| 384 | | if type(source) != types.ListType: |
|---|
| 385 | | source = [source] |
|---|
| 386 | | hoc_commands = [] |
|---|
| 387 | | if not spikefilelist.has_key(filename): |
|---|
| 388 | | spikefilelist[filename] = [] |
|---|
| 389 | | for src in source: |
|---|
| 390 | | if src in gidlist: |
|---|
| 391 | | #hoc_commands += ['tmp = cell%d.record(1)' % src] |
|---|
| 392 | | src.record(1) |
|---|
| 393 | | spikefilelist[filename] += [src] # writing to file is done in end() |
|---|
| 394 | | #hoc_execute(hoc_commands, "---record() ---") |
|---|
| 395 | | |
|---|
| 396 | | def record_v(source, filename): |
|---|
| 397 | | """ |
|---|
| 398 | | Record membrane potential to a file. source can be an individual cell or |
|---|
| 399 | | a list of cells.""" |
|---|
| 400 | | # would actually like to be able to record to an array and |
|---|
| 401 | | # choose later whether to write to a file. |
|---|
| 402 | | global vfilelist, gidlist |
|---|
| 403 | | if type(source) != types.ListType: |
|---|
| 404 | | source = [source] |
|---|
| 405 | | hoc_commands = [] |
|---|
| 406 | | if not vfilelist.has_key(filename): |
|---|
| 407 | | vfilelist[filename] = [] |
|---|
| 408 | | for src in source: |
|---|
| 409 | | if src.gid in gidlist: |
|---|
| 410 | | #hoc_commands += ['tmp = cell%d.record_v(1,%g)' % (src, dt)] |
|---|
| 411 | | src.record_v(1) |
|---|
| 412 | | vfilelist[filename] += [src] # writing to file is done in end() |
|---|
| 413 | | #hoc_execute(hoc_commands, "---record_v() ---") |
|---|
| 445 | | common.Population.__init__(self, dims, cellclass, cellparams, label) |
|---|
| 446 | | #if self.ndim > 1: |
|---|
| 447 | | # for i in range(1, self.ndim): |
|---|
| 448 | | # if self.dim[i] != self.dim[0]: |
|---|
| 449 | | # raise common.InvalidDimensionsError, "All dimensions must be the same size (temporary restriction)." |
|---|
| 450 | | |
|---|
| 451 | | # set the steps list, used by the __getitem__() method. |
|---|
| 452 | | self.steps = [1]*self.ndim |
|---|
| 453 | | for i in xrange(self.ndim-1): |
|---|
| 454 | | for j in range(i+1, self.ndim): |
|---|
| 455 | | self.steps[i] *= self.dim[j] |
|---|
| 456 | | |
|---|
| 457 | | if isinstance(cellclass, type): |
|---|
| 458 | | self.celltype = cellclass(cellparams) |
|---|
| 459 | | self.cellparams = self.celltype.parameters |
|---|
| 460 | | hoc_name = self.celltype.hoc_name |
|---|
| 461 | | elif isinstance(cellclass, str): # not a standard model |
|---|
| 462 | | hoc_name = cellclass |
|---|
| | 177 | # Build the arrays of cell ids |
|---|
| | 178 | # Cells on the local node are represented as ID objects, other cells by integers |
|---|
| | 179 | # All are stored in a single numpy array for easy lookup by address |
|---|
| | 180 | # The local cells are also stored in a list, for easy iteration |
|---|
| | 181 | self._all_ids = numpy.array([id for id in range(self.first_id, self.last_id)], ID) #.reshape(self.dim) |
|---|
| | 182 | # _mask_local is used to extract those elements from arrays that apply to the cells on the current node, e.g. for tset() |
|---|
| | 183 | self._mask_local = self._all_ids%num_processes()==0 # round-robin distribution of cells between nodes |
|---|
| | 184 | ## _map_local is used to map addresses to the index within the list of local cells |
|---|
| | 185 | #self._map_local = numpy.where(self._mask_local, self._all_ids/int(num_processes()), None) |
|---|
| | 186 | for i,(id,is_local) in enumerate(zip(self._all_ids, self._mask_local)): |
|---|
| | 187 | if is_local: |
|---|
| | 188 | self._all_ids[i] = ID(id) |
|---|
| | 189 | # _local_ids is a list containing only those cells that exist on the current node |
|---|
| | 190 | self._local_ids = self._all_ids[self._mask_local] |
|---|
| | 191 | self._all_ids = self._all_ids.reshape(self.dim) |
|---|
| | 192 | self._mask_local = self._mask_local.reshape(self.dim) |
|---|
| 464 | | if self.cellparams is not None: |
|---|
| 465 | | hoc_commands, argstr = _hoc_arglist([self.cellparams]) |
|---|
| 466 | | argstr = argstr.strip().strip(',') |
|---|
| 467 | | else: |
|---|
| 468 | | hoc_commands = [] |
|---|
| 469 | | argstr = '' |
|---|
| 470 | | |
|---|
| 471 | | if not self.label: |
|---|
| 472 | | self.label = 'population%d' % Population.nPop |
|---|
| 473 | | self.hoc_label = self.label.replace(" ","_") |
|---|
| 474 | | |
|---|
| 475 | | self.record_from = { 'spiketimes': [], 'vtrace': [] } |
|---|
| 476 | | |
|---|
| 477 | | |
|---|
| 478 | | # Now the gid and cellclass are stored as instance of the ID class, which will allow a syntax like |
|---|
| 479 | | # p[i, j].set(param, val). But we have also to deal with positions : a population needs to know ALL the positions |
|---|
| 480 | | # of its cells, and not only those of the cells located on a particular node (i.e in self.gidlist). So |
|---|
| 481 | | # each population should store what we call a "fullgidlist" with the ID of all the cells in the populations |
|---|
| 482 | | # (and therefore their positions) |
|---|
| 483 | | self.fullgidlist = numpy.array([ID(i) for i in range(gid, gid+self.size) if i < gid+self.size], ID) |
|---|
| 484 | | self.cell = self.fullgidlist |
|---|
| 485 | | |
|---|
| 486 | | # self.gidlist is now derived from self.fullgidlist since it contains only the cells of the population located on |
|---|
| 487 | | # the node |
|---|
| 488 | | self.gidlist = [self.fullgidlist[i+myid] for i in range(0, len(self.fullgidlist), nhost) if i < len(self.fullgidlist)-myid] |
|---|
| 489 | | self.gid_start = gid |
|---|
| 490 | | |
|---|
| 491 | | # Write hoc commands |
|---|
| 492 | | hoc_commands += ['objref %s' % self.hoc_label, |
|---|
| 493 | | '%s = new List()' % self.hoc_label] |
|---|
| 494 | | |
|---|
| 495 | | for cell_id in self.gidlist: |
|---|
| 496 | | hoc_commands += ['tmp = pc.set_gid2node(%d,%d)' % (cell_id, myid), |
|---|
| 497 | | 'cell = new %s(%s)' % (hoc_name, argstr), |
|---|
| 498 | | #'nc = new NetCon(cell.source, nil)', |
|---|
| 499 | | 'tmp = cell.connect2target(nil, nc)', |
|---|
| 500 | | 'tmp = pc.cell(%d, nc)' % cell_id, |
|---|
| 501 | | 'tmp = %s.append(cell)' %(self.hoc_label)] |
|---|
| 502 | | hoc_execute(hoc_commands, "--- Population[%s].__init__() ---" %self.label) |
|---|
| | 194 | self.celltype = cellclass(cellparams) |
|---|
| | 195 | for id in self._local_ids: |
|---|
| | 196 | id._build_cell(self.celltype, parent=self) |
|---|
| | 197 | gid_counter += self.size |
|---|
| 632 | | if self.dim == valueArray.shape: |
|---|
| 633 | | values = numpy.reshape(valueArray, valueArray.size) |
|---|
| 634 | | values = values.take(numpy.array(self.gidlist)-self.gid_start) # take just the values for cells on this machine |
|---|
| 635 | | assert len(values) == len(self.gidlist) |
|---|
| 636 | | if isinstance(self.celltype, common.StandardCellType): |
|---|
| 637 | | parametername = self.celltype.translate({parametername: values[0]}).keys()[0] |
|---|
| 638 | | hoc_commands = [] |
|---|
| 639 | | fmt = '%s.object(%s).%s = %s' % (self.hoc_label, "%d", parametername, "%g") |
|---|
| 640 | | for i, val in enumerate(values): |
|---|
| 641 | | try: |
|---|
| 642 | | hoc_commands += [fmt % (i, val), |
|---|
| 643 | | 'success = %s.object(%d).param_update()' % (self.hoc_label, i)] |
|---|
| 644 | | except TypeError: |
|---|
| 645 | | raise common.InvalidParameterValueError, "%s is not a numeric value" % str(val) |
|---|
| 646 | | hoc_execute(hoc_commands, "--- Population[%s].__tset()__ ---" %self.label) |
|---|
| 647 | | else: |
|---|
| 648 | | raise common.InvalidDimensionsError |
|---|
| | 299 | if self.dim == value_array.shape: # the values are numbers or non-array objects |
|---|
| | 300 | local_values = value_array[self._mask_local] |
|---|
| | 301 | elif len(value_array.shape) == len(self.dim)+1: # the values are themselves 1D arrays |
|---|
| | 302 | #values = numpy.reshape(value_array, (self.dim, value_array.size/self.size)) |
|---|
| | 303 | local_values = value_array[self._mask_local] # not sure this works |
|---|
| | 304 | else: |
|---|
| | 305 | raise common.InvalidDimensionsError, "Population: %s, value_array: %s" % (str(self.dim), |
|---|
| | 306 | str(value_array.shape)) |
|---|
| | 307 | ##local_indices = numpy.array([cell.gid for cell in self]) - self.first_id |
|---|
| | 308 | ##values = values.take(local_indices) # take just the values for cells on this machine |
|---|
| | 309 | assert local_values.size == self._local_ids.size, "%d != %d" % (local_values.size, self._local_ids.size) |
|---|
| | 310 | |
|---|
| | 311 | logging.info("%s.tset('%s', array(shape=%s, min=%s, max=%s)", |
|---|
| | 312 | self.label, parametername, value_array.shape, |
|---|
| | 313 | value_array.min(), value_array.max()) |
|---|
| | 314 | # Set the values for each cell |
|---|
| | 315 | for cell, val in zip(self, local_values.flat): |
|---|
| | 316 | setattr(cell, parametername, val) |
|---|
| 656 | | if isinstance(self.celltype, common.StandardCellType): |
|---|
| 657 | | parametername = self.celltype.translate({parametername: 0}).keys()[0] |
|---|
| 658 | | paramfmt = "%g,"*len(rand_distr.parameters); paramfmt = paramfmt.strip(',') |
|---|
| 659 | | distr_params = paramfmt % tuple(rand_distr.parameters) |
|---|
| 660 | | hoc_commands = ['rng = new Random(%d)' % 0 or distribution.rng.seed, |
|---|
| 661 | | 'tmp = rng.%s(%s)' % (rand_distr.name, distr_params)] |
|---|
| 662 | | # We do the loop in hoc, to speed up the code |
|---|
| 663 | | loop = "for tmp = 0, %d" %(len(self.gidlist)-1) |
|---|
| 664 | | cmd = '%s.object(tmp).%s = rng.repick()' % (self.hoc_label, parametername) |
|---|
| 665 | | hoc_commands += ['cmd="%s { %s success = %s.object(tmp).param_update()}"' %(loop, cmd, self.hoc_label), |
|---|
| 666 | | 'success = execute1(cmd)'] |
|---|
| 667 | | hoc_execute(hoc_commands, "--- Population[%s].__rset()__ ---" %self.label) |
|---|
| 668 | | else: |
|---|
| 669 | | rarr = rand_distr.next(n=self.size) |
|---|
| 670 | | rarr = rarr.reshape(self.dim) |
|---|
| 671 | | hoc_comment("--- Population[%s].__rset()__ --- " %self.label) |
|---|
| 672 | | self.tset(parametername, rarr) |
|---|
| 673 | | |
|---|
| 674 | | def _call(self, methodname, arguments): |
|---|
| 675 | | """ |
|---|
| 676 | | Calls the method methodname(arguments) for every cell in the population. |
|---|
| 677 | | e.g. p.call("set_background","0.1") if the cell class has a method |
|---|
| 678 | | set_background(). |
|---|
| 679 | | """ |
|---|
| 680 | | raise Exception("Method not yet implemented") |
|---|
| 681 | | ## Not sure this belongs in the API, because cell classes only have |
|---|
| 682 | | ## parameters/attributes, not methods. |
|---|
| 683 | | |
|---|
| 684 | | def _tcall(self, methodname, objarr): |
|---|
| 685 | | """ |
|---|
| 686 | | `Topographic' call. Calls the method methodname() for every cell in the |
|---|
| 687 | | population. The argument to the method depends on the coordinates of the |
|---|
| 688 | | cell. objarr is an array with the same dimensions as the Population. |
|---|
| 689 | | e.g. p.tcall("memb_init", vinitArray) calls |
|---|
| 690 | | p.cell[i][j].memb_init(vInitArray[i][j]) for all i, j. |
|---|
| 691 | | """ |
|---|
| 692 | | raise Exception("Method not yet implemented") |
|---|
| | 328 | rng = h.Random(rand_distr.rng.seed or 0) |
|---|
| | 329 | native_rand_distr = getattr(rng, rand_distr.name) |
|---|
| | 330 | rarr = [native_rand_distr(*rand_distr.parameters)] + [rng.repick() for i in range(self._all_ids.size-1)] |
|---|
| | 331 | else: |
|---|
| | 332 | rarr = rand_distr.next(n=self._all_ids.size) |
|---|
| | 333 | rarr = numpy.array(rarr) |
|---|
| | 334 | logging.info("%s.rset('%s', %s)", self.label, parametername, rand_distr) |
|---|
| | 335 | for cell,val in zip(self, rarr[self._mask_local.flatten()]): |
|---|
| | 336 | setattr(cell, parametername, val) |
|---|
| | 337 | |
|---|
| | 338 | def randomInit(self, rand_distr): |
|---|
| | 339 | """ |
|---|
| | 340 | Set initial membrane potentials for all the cells in the population to |
|---|
| | 341 | random values. |
|---|
| | 342 | """ |
|---|
| | 343 | self.rset('v_init', rand_distr) |
|---|