Changeset 821
- Timestamp:
- 11/16/10 19:26:42 (3 years ago)
- Location:
- trunk
- Files:
-
- 3 modified
-
src/common.py (modified) (78 diffs)
-
src/nest/__init__.py (modified) (1 diff)
-
test/unittests/test_space.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/common.py
r820 r821 12 12 check_weight() 13 13 check_delay() 14 14 15 15 Accessing individual neurons: 16 16 IDMixin 17 17 18 18 Common API implementation/base classes: 19 19 1. Simulation set-up and control: … … 49 49 50 50 if not 'simulator' in locals(): 51 simulator = None # should be set by simulator-specific modules51 simulator = None # should be set by simulator-specific modules 52 52 53 53 DEFAULT_WEIGHT = 0.0 … … 59 59 logger = logging.getLogger("PyNN") 60 60 61 # ============================================================================= =61 # ============================================================================= 62 62 # Utility functions and classes 63 # ============================================================================= =63 # ============================================================================= 64 64 65 65 66 66 def is_conductance(target_cell): 67 67 """ 68 Returns True if the target cell uses conductance-based synapses, False if it 69 uses current-based synapses, and None if the synapse-basis cannot be determined. 68 Returns True if the target cell uses conductance-based synapses, False if 69 it uses current-based synapses, and None if the synapse-basis cannot be 70 determined. 70 71 """ 71 72 if hasattr(target_cell, 'local') and target_cell.local and hasattr(target_cell, 'cellclass'): 72 73 if isinstance(target_cell.cellclass, type): 73 74 is_conductance = target_cell.cellclass.conductance_based 74 else: # where cellclass is a string, i.e. for native cell types in NEST75 else: # where cellclass is a string, i.e. for native cell types in NEST 75 76 is_conductance = "cond" in target_cell.cellclass 76 77 else: … … 78 79 return is_conductance 79 80 81 80 82 def check_weight(weight, synapse_type, is_conductance): 81 #print "check_weight: node=%s, weight=%s, synapse_type=%s, is_conductance=%s" % (rank(), weight, synapse_type, is_conductance)82 83 if weight is None: 83 84 weight = DEFAULT_WEIGHT 84 85 if core.is_listlike(weight): 85 86 weight = numpy.array(weight) 86 nan_filter = (1 -numpy.isnan(weight)).astype(bool)# weight arrays may contain NaN, which should be ignored87 nan_filter = (1 - numpy.isnan(weight)).astype(bool) # weight arrays may contain NaN, which should be ignored 87 88 filtered_weight = weight[nan_filter] 88 all_negative = (filtered_weight <=0).all()89 all_positive = (filtered_weight >=0).all()89 all_negative = (filtered_weight <= 0).all() 90 all_positive = (filtered_weight >= 0).all() 90 91 if not (all_negative or all_positive): 91 92 raise errors.InvalidWeightError("Weights must be either all positive or all negative") … … 98 99 if not all_positive: 99 100 raise errors.InvalidWeightError("Weights must be positive for conductance-based and/or excitatory synapses") 100 elif is_conductance ==False and synapse_type == 'inhibitory':101 elif is_conductance == False and synapse_type == 'inhibitory': 101 102 if not all_negative: 102 103 raise errors.InvalidWeightError("Weights must be negative for current-based, inhibitory synapses") 103 else: # is_conductance is None. This happens if the cell does not exist on the current node.104 else: # is_conductance is None. This happens if the cell does not exist on the current node. 104 105 logger.debug("Can't check weight, conductance status unknown.") 105 106 return weight 107 106 108 107 109 def check_delay(delay): … … 110 112 # If the delay is too small , we have to throw an error 111 113 if delay < get_min_delay() or delay > get_max_delay(): 112 raise errors.ConnectionError("delay (%s) is out of range [%s,%s]" % (delay, get_min_delay(), get_max_delay())) 114 raise errors.ConnectionError("delay (%s) is out of range [%s,%s]" % \ 115 (delay, get_min_delay(), get_max_delay())) 113 116 return delay 114 117 115 118 116 # ============================================================================= =119 # ============================================================================= 117 120 # Accessing individual neurons 118 # ============================================================================= =121 # ============================================================================= 119 122 120 123 class IDMixin(object): … … 137 140 val = self.get_parameters()[name] 138 141 except KeyError: 139 raise errors.NonExistentParameterError(name, self.cellclass.__name__, 142 raise errors.NonExistentParameterError(name, 143 self.cellclass.__name__, 140 144 self.cellclass.get_parameter_names()) 141 145 return val 142 146 143 147 def __setattr__(self, name, value): 144 148 if name == "parent": 145 149 object.__setattr__(self, name, value) 146 150 elif self.cellclass.has_parameter(name): 147 self.set_parameters(**{name: value})151 self.set_parameters(**{name: value}) 148 152 else: 149 153 object.__setattr__(self, name, value) 150 154 151 155 def set_parameters(self, **parameters): 152 """Set cell parameters, given as a sequence of parameter=value arguments.""" 156 """ 157 Set cell parameters, given as a sequence of parameter=value arguments. 158 """ 153 159 # if some of the parameters are computed from the values of other 154 160 # parameters, need to get and translate all parameters … … 156 162 if self.is_standard_cell: 157 163 computed_parameters = self.cellclass.computed_parameters() 158 have_computed_parameters = numpy.any([p_name in computed_parameters for p_name in parameters]) 159 if have_computed_parameters: 164 have_computed_parameters = numpy.any([p_name in computed_parameters 165 for p_name in parameters]) 166 if have_computed_parameters: 160 167 all_parameters = self.get_parameters() 161 168 all_parameters.update(parameters) … … 165 172 else: 166 173 raise errors.NotLocalError("Cannot set parameters for a cell that does not exist on this node.") 167 174 168 175 def get_parameters(self): 169 176 """Return a dict of all cell parameters.""" … … 185 192 else: 186 193 return celltype 187 194 188 195 @property 189 196 def is_standard_cell(self): 190 return (type(self.cellclass) == type and issubclass(self.cellclass, standardmodels.StandardCellType)) 191 197 return (type(self.cellclass) == type and 198 issubclass(self.cellclass, standardmodels.StandardCellType)) 199 192 200 def _set_position(self, pos): 193 201 """ 194 202 Set the cell position in 3D space. 195 203 196 204 Cell positions are stored in an array in the parent Population. 197 205 """ … … 199 207 assert len(pos) == 3 200 208 self.parent._set_cell_position(self, pos) 201 209 202 210 def _get_position(self): 203 211 """ 204 212 Return the cell position in 3D space. 205 213 206 214 Cell positions are stored in an array in the parent Population, if any, 207 215 or within the ID object otherwise. Positions are generated the first … … 211 219 212 220 position = property(_get_position, _set_position) 213 221 214 222 @property 215 223 def local(self): 216 224 return self.parent.is_local(self) 217 225 218 226 def inject(self, current_source): 219 227 """Inject current from a current source object into the cell.""" … … 223 231 """Get the initial value of a state variable of the cell.""" 224 232 return self.parent._get_cell_initial_value(self, variable) 225 233 226 234 def set_initial_value(self, variable, value): 227 235 """Set the initial value of a state variable of the cell.""" 228 236 self.parent._set_cell_initial_value(self, variable, value) 229 237 230 # ============================================================================== 238 239 # ============================================================================= 231 240 # Functions for simulation set-up and control 232 # ============================================================================== 241 # ============================================================================= 242 233 243 234 244 def setup(timestep=DEFAULT_TIMESTEP, min_delay=DEFAULT_MIN_DELAY, … … 247 257 if min_delay < timestep: 248 258 "min_delay (%g) must be greater than timestep (%g)" % (min_delay, timestep) 249 259 250 260 def end(compatible_output=True): 251 261 """Do any necessary cleaning up before exiting.""" 252 262 raise NotImplementedError 253 263 254 264 def run(simtime): 255 265 """Run the simulation for simtime ms.""" … … 259 269 """ 260 270 Reset the time to zero, neuron membrane potentials and synaptic weights to 261 their initial values, and delete any recorded data. The network structure is 262 not changed, nor is the specification of which neurons to record from.""" 271 their initial values, and delete any recorded data. The network structure 272 is not changed, nor is the specification of which neurons to record from. 273 """ 263 274 simulator.reset() 264 275 … … 291 302 return simulator.state.mpi_rank 292 303 293 # ============================================================================= =294 # Low-level API for creating, connecting and recording from individual neurons295 # ============================================================================= =304 # ============================================================================= 305 # Low-level API for creating, connecting and recording from individual neurons 306 # ============================================================================= 296 307 297 308 def build_create(population_class): … … 299 310 """ 300 311 Create n cells all of the same type. 301 312 302 313 If n > 1, return a list of cell ids/references. 303 314 If n==1, return just the single id. 304 315 """ 305 return population_class(n, cellclass, cellparams) # return the Population or Population.all_cells?316 return population_class(n, cellclass, cellparams) # return the Population or Population.all_cells? 306 317 return create 307 318 308 319 def build_connect(projection_class, connector_class): 309 def connect(source, target, weight=0.0, delay=None, synapse_type=None, p=1, rng=None): 320 def connect(source, target, weight=0.0, delay=None, synapse_type=None, 321 p=1, rng=None): 310 322 """ 311 323 Connect a source of spikes to a synaptic target. 312 313 source and target can both be individual cells or lists of cells, in which314 case all possible connections are made with probability p, using either the315 random number generator supplied, or the default rng otherwise.316 Weights should be in nA or µS.324 325 source and target can both be individual cells or lists of cells, in 326 which case all possible connections are made with probability p, using 327 either the random number generator supplied, or the default rng 328 otherwise. Weights should be in nA or µS. 317 329 """ 318 330 if isinstance(source, IDMixin): … … 327 339 """ 328 340 Set one or more parameters of an individual cell or list of cells. 329 341 330 342 param can be a dict, in which case val should not be supplied, or a string 331 343 giving the parameter name, in which case val is the parameter value. … … 344 356 assert isinstance(source, (BasePopulation, Assembly)) 345 357 source._record(variable, to_file=filename) 346 simulator.recorder_list.append(source.recorders[variable]) # this is a bit hackish - better to add to Population.__del__?358 simulator.recorder_list.append(source.recorders[variable]) # this is a bit hackish - better to add to Population.__del__? 347 359 if variable == 'v': 348 360 record.__doc__ = """ … … 356 368 357 369 358 # ============================================================================= =370 # ============================================================================= 359 371 # High-level API for creating, connecting and recording from populations of 360 372 # neurons. 361 # ============================================================================= =373 # ============================================================================= 362 374 363 375 class BasePopulation(object): 364 376 record_filter = None 365 377 366 378 def __getitem__(self, index): 367 379 """ … … 383 395 else: 384 396 raise TypeError("indices must be integers, slices, lists, arrays or tuples, not %s" % type(index).__name__) 385 397 386 398 def __len__(self): 387 399 """Return the total number of cells in the population (all nodes).""" 388 400 return self.size 389 401 390 402 def __iter__(self): 391 403 """Iterator over cell ids on the local node.""" 392 404 return iter(self.local_cells) 393 405 394 406 def is_local(self, id): 395 407 assert id.parent is self 396 408 index = self.id_to_index(id) 397 409 return self._mask_local[index] 398 410 399 411 def all(self): 400 412 """Iterator over cell ids on all nodes.""" … … 408 420 index = self.id_to_index(id) 409 421 return self.positions[:, index] 410 422 411 423 def _set_cell_position(self, id, pos): 412 424 index = self.id_to_index(id) … … 425 437 def nearest(self, position): 426 438 """Return the neuron closest to the specified position.""" 427 # doesn't always work correctly if a position is equidistant between two428 # neurons, i.e. 0.5 should be rounded up, but it isn't always.439 # doesn't always work correctly if a position is equidistant between 440 # two neurons, i.e. 0.5 should be rounded up, but it isn't always. 429 441 # also doesn't take account of periodic boundary conditions 430 pos = numpy.array([position] *self.positions.shape[1]).transpose()442 pos = numpy.array([position] * self.positions.shape[1]).transpose() 431 443 dist_arr = (self.positions - pos)**2 432 444 distances = dist_arr.sum(axis=0) 433 445 nearest = distances.argmin() 434 446 return self[nearest] 435 436 def get(self, parameter_name): # would be nice to add a 'gather' argument447 448 def get(self, parameter_name): # would be nice to add a 'gather' argument 437 449 """ 438 450 Get the values of a parameter for every local cell in the population. … … 443 455 return self._get_array(parameter_name) 444 456 else: 445 return [getattr(cell, parameter_name) for cell in self] # list or array?457 return [getattr(cell, parameter_name) for cell in self] # list or array? 446 458 447 459 def set(self, param, val=None): … … 450 462 can be a dict, in which case val should not be supplied, or a string 451 463 giving the parameter name, in which case val is the parameter value. 452 val can be a numeric value, or list of such (e.g. for setting spike times). 464 val can be a numeric value, or list of such (e.g. for setting spike 465 times). 453 466 e.g. p.set("tau_m",20.0). 454 467 p.set({'tau_m':20,'v_rest':-65}) … … 457 470 # -- Proposed change to arguments -- 458 471 #Set one or more parameters for every cell in the population. 459 # 472 # 460 473 #Each value may be a single number or a list/array of numbers of the same 461 474 #size as the population. If the parameter itself takes lists/arrays as 462 475 #values (e.g. spike times), then the value provided may be either a 463 476 #single lists/1D array, a list of lists/1D arrays, or a 2D array. 464 # 477 # 465 478 #e.g. p.set(tau_m=20.0). 466 479 # p.set(tau_m=20, v_rest=[-65.0, -65.3, ... , -67.2]) … … 476 489 param_dict[name] = float(val) 477 490 elif isinstance(val, (list, numpy.ndarray)): 478 pass # ought to check list/array only contains numeric types491 pass # ought to check list/array only contains numeric types 479 492 else: 480 493 raise errors.InvalidParameterValueError … … 496 509 #accepts arguments x,y,z and returns a single value. 497 510 #""" 498 if (self.size,) == value_array.shape: # the values are numbers or non-array objects511 if (self.size,) == value_array.shape: # the values are numbers or non-array objects 499 512 local_values = value_array[self._mask_local] 500 513 assert local_values.size == self.local_cells.size, "%d != %d" % (local_values.size, self.local_cells.size) 501 elif len(value_array.shape) == 2: # the values are themselves 1D arrays514 elif len(value_array.shape) == 2: # the values are themselves 1D arrays 502 515 if value_array.shape[0] != self.size: 503 raise errors.InvalidDimensionsError ,"Population: %d, value_array first dimension: %s" % (self.size,504 value_array.shape[0])505 local_values = value_array[self._mask_local] # not sure this works506 else: 507 raise errors.InvalidDimensionsError ,"Population: %d, value_array: %s" % (self.size,508 str(value_array.shape))516 raise errors.InvalidDimensionsError("Population: %d, value_array first dimension: %s" % (self.size, 517 value_array.shape[0])) 518 local_values = value_array[self._mask_local] # not sure this works 519 else: 520 raise errors.InvalidDimensionsError("Population: %d, value_array: %s" % (self.size, 521 str(value_array.shape))) 509 522 assert local_values.shape[0] == self.local_cells.size, "%d != %d" % (local_values.size, self.local_cells.size) 510 523 511 524 try: 512 525 logger.debug("%s.tset('%s', array(shape=%s, min=%s, max=%s))", 513 526 self.label, parametername, value_array.shape, 514 527 value_array.min(), value_array.max()) 515 except TypeError: # min() and max() won't work for non-numeric values528 except TypeError: # min() and max() won't work for non-numeric values 516 529 logger.debug("%s.tset('%s', non_numeric_array(shape=%s))", 517 530 self.label, parametername, value_array.shape) 518 531 519 532 # Set the values for each cell 520 533 if hasattr(self, "_set_array"): … … 537 550 self._native_rset(parametername, rand_distr) 538 551 else: 539 rarr = rand_distr.next(n=self.all_cells.size, mask_local=False) #self._mask_local)540 rarr = numpy.array(rarr) # isn't rarr already an array?552 rarr = rand_distr.next(n=self.all_cells.size, mask_local=False) 553 rarr = numpy.array(rarr) # isn't rarr already an array? 541 554 assert rarr.size == self.size, "%s != %s" % (rarr.size, self.size) 542 555 self.tset(parametername, rarr) … … 549 562 """ 550 563 raise NotImplementedError() 551 564 552 565 def _tcall(self, methodname, objarr): 553 566 """ 554 `Topographic' call. Call the method methodname() for every cell in the 555 population. The argument to the method depends on the coordinates of the 556 cell. objarr is an array with the same dimensions as the Population. 567 `Topographic' call. Call the method methodname() for every cell in the 568 population. The argument to the method depends on the coordinates of 569 the cell. objarr is an array with the same dimensions as the 570 Population. 557 571 e.g. p.tcall("memb_init", vinitArray) calls 558 572 p.cell[i][j].memb_init(vInitArray[i][j]) for all i,j. … … 571 585 """ 572 586 Set initial values of state variables, e.g. the membrane potential. 573 587 574 588 `value` may either be a numeric value (all neurons set to the same 575 589 value) or a `RandomDistribution` object (each neuron gets a … … 586 600 self._set_initial_value_array(variable, value) 587 601 else: 588 for cell in self: # only on local node602 for cell in self: # only on local node 589 603 cell.set_initial_value(variable, value) 590 604 591 605 def can_record(self, variable): 592 606 """Determine whether `variable` can be recorded from this population.""" … … 594 608 return (variable in self.celltype.recordable) 595 609 else: 596 return True # for now, not able to check for native cells, although it should be possible in principle610 return True # for now, not able to check for native cells, although it should be possible in principle 597 611 598 612 def _record(self, variable, record_from=None, rng=None, to_file=True): … … 602 616 if not self.can_record(variable): 603 617 raise errors.RecordingError(variable, self.celltype) 604 if isinstance(record_from, list): #record from the fixed list specified by user618 if isinstance(record_from, list): # record from the fixed list specified by user 605 619 pass 606 elif record_from is None: # record from all cells:620 elif record_from is None: # record from all cells: 607 621 record_from = self.all_cells 608 elif isinstance(record_from, int): # record from a number of cells, selected at random622 elif isinstance(record_from, int): # record from a number of cells, selected at random 609 623 nrec = record_from 610 624 if not rng: … … 620 634 if isinstance(to_file, basestring): 621 635 self.recorders[variable].file = to_file 622 623 636 624 637 def record(self, record_from=None, rng=None, to_file=True): 625 638 """ 626 If record_from is not given, record spikes from all cells in the Population. 627 record_from can be an integer - the number of cells to record from, chosen 628 at random (in this case a random number generator can also be supplied) 629 - or a list containing the ids of the cells to record. 639 If record_from is not given, record spikes from all cells in the 640 Population. record_from can be an integer - the number of cells to 641 record from, chosen at random (in this case a random number generator 642 can also be supplied) - or a list containing the ids of the cells to 643 record. 630 644 """ 631 645 self._record('spikes', record_from, rng, to_file) … … 633 647 def record_v(self, record_from=None, rng=None, to_file=True): 634 648 """ 635 If record_from is not given, record the membrane potential for all cells in636 the Population.637 record_from can be an integer - the number of cells to record from, chosen638 at random (in this case a random number generator can also be supplied)639 - or a list containing the ids of the cells to record.649 If record_from is not given, record the membrane potential for all 650 cells in the Population. 651 record_from can be an integer - the number of cells to record from, 652 chosen at random (in this case a random number generator can also be 653 supplied) - or a list containing the ids of the cells to record. 640 654 """ 641 655 self._record('v', record_from, rng, to_file) … … 645 659 If record_from is not given, record synaptic conductances 646 660 for all cells in the Population. 647 record_from can be an integer - the number of cells to record from, chosen648 at random (in this case a random number generator can also be supplied)649 - or a list containing the ids of the cells to record.661 record_from can be an integer - the number of cells to record from, 662 chosen at random (in this case a random number generator can also be 663 supplied) - or a list containing the ids of the cells to record. 650 664 """ 651 665 self._record('gsyn', record_from, rng, to_file) … … 654 668 """ 655 669 Write spike times to file. 656 670 657 671 file should be either a filename or a PyNN File object. 658 672 659 673 If compatible_output is True, the format is "spiketime cell_id", 660 674 where cell_id is the index of the cell counting along rows and down … … 664 678 The timestep, first id, last id, and number of data points per cell are 665 679 written in a header, indicated by a '#' at the beginning of the line. 666 680 667 681 If compatible_output is False, the raw format produced by the simulator 668 682 is used. This may be faster, since it avoids any post-processing of the 669 683 spike files. 670 684 671 685 For parallel simulators, if gather is True, all data will be gathered 672 686 to the master node and a single output file created there. Otherwise, a 673 687 file will be written on each node, containing only the cells simulated 674 688 on that node. 675 """ 689 """ 676 690 self.recorders['spikes'].write(file, gather, compatible_output, self.record_filter) 677 691 678 692 def getSpikes(self, gather=True, compatible_output=True): 679 693 """ … … 688 702 """ 689 703 Write membrane potential traces to file. 690 704 691 705 file should be either a filename or a PyNN File object. 692 706 693 707 If compatible_output is True, the format is "v cell_id", 694 708 where cell_id is the index of the cell counting along rows and down … … 696 710 The timestep, first id, last id, and number of data points per cell are 697 711 written in a header, indicated by a '#' at the beginning of the line. 698 712 699 713 If compatible_output is False, the raw format produced by the simulator 700 714 is used. This may be faster, since it avoids any post-processing of the 701 715 voltage files. 702 716 703 717 For parallel simulators, if gather is True, all data will be gathered 704 718 to the master node and a single output file created there. Otherwise, a … … 707 721 """ 708 722 self.recorders['v'].write(file, gather, compatible_output, self.record_filter) 709 723 710 724 def get_v(self, gather=True, compatible_output=True): 711 725 """ … … 714 728 """ 715 729 return self.recorders['v'].get(gather, compatible_output, self.record_filter) 716 730 717 731 def print_gsyn(self, file, gather=True, compatible_output=True): 718 732 """ 719 733 Write synaptic conductance traces to file. 720 734 721 735 file should be either a filename or a PyNN File object. 722 736 723 737 If compatible_output is True, the format is "t g cell_id", 724 738 where cell_id is the index of the cell counting along rows and down … … 732 746 """ 733 747 self.recorders['gsyn'].write(file, gather, compatible_output, self.record_filter) 734 748 735 749 def get_gsyn(self, gather=True, compatible_output=True): 736 750 """ … … 745 759 """ 746 760 return self.recorders['spikes'].count(gather) 747 761 748 762 def meanSpikeCount(self, gather=True): 749 763 """ … … 752 766 spike_counts = self.recorders['spikes'].count(gather) 753 767 total_spikes = sum(spike_counts.values()) 754 if rank() == 0 or not gather: # should maybe use allgather, and get the numbers on all nodes768 if rank() == 0 or not gather: # should maybe use allgather, and get the numbers on all nodes 755 769 return float(total_spikes)/len(spike_counts) 756 770 … … 762 776 raise TypeError("Can't inject current into a spike source.") 763 777 current_source.inject_into(self) 764 778 765 779 def save_positions(self, filename): 766 780 """ … … 768 782 """ 769 783 # this should be rewritten to use self.positions and recording.files 770 fmt = "%s\t%s\t%s\t%s\n" % ("%d", "%g", "%g", "%g")784 fmt = "%s\t%s\t%s\t%s\n" % ("%d", "%g", "%g", "%g") 771 785 lines = [] 772 for cell in self.all(): 773 x, y,z = cell.position774 line = fmt % (cell, x, y, z)786 for cell in self.all(): 787 x, y, z = cell.position 788 line = fmt % (cell, x, y, z) 775 789 lines.append(line) 776 790 if rank() == 0: … … 785 799 """ 786 800 nPop = 0 787 801 788 802 def __init__(self, size, cellclass, cellparams=None, structure=None, 789 803 label=None): 790 804 """ 791 805 Create a population of neurons all of the same type. 792 793 size - number of cells in the Population. For backwards-compatibility, n794 may also be a tuple giving the dimensions of a grid, e.g. n=(10,10)795 is equivalent to n=100 with structure=Grid2D()806 807 size - number of cells in the Population. For backwards-compatibility, 808 n may also be a tuple giving the dimensions of a grid, 809 e.g. n=(10,10) is equivalent to n=100 with structure=Grid2D() 796 810 cellclass should either be a standardized cell class (a class inheriting 797 from common.standardmodels.StandardCellType) or a string giving the name of the798 simulator-specific model that makes up the population.811 from common.standardmodels.StandardCellType) or a string giving the 812 name of the simulator-specific model that makes up the population. 799 813 cellparams should be a dict which is passed to the neuron model 800 814 constructor … … 802 816 label is an optional name for the population. 803 817 """ 804 if not isinstance(size, int): # also allow a single integer, for a 1D population818 if not isinstance(size, int): # also allow a single integer, for a 1D population 805 819 assert isinstance(size, tuple), "`size` must be an integer or a tuple of ints. You have supplied a %s" % type(size) 806 820 # check the things inside are ints 807 821 for e in size: 808 822 assert isinstance(e, int), "`size` must be an integer or a tuple of ints. Element '%s' is not an int" % str(e) 809 823 810 824 assert structure is None, "If you specify `size` as a tuple you may not specify structure." 811 825 if len(size) == 1: 812 826 structure = space.Line() 813 827 elif len(size) == 2: 814 nx, ny = size828 nx, ny = size 815 829 structure = space.Grid2D(nx/float(ny)) 816 830 elif len(size) == 3: 817 nx, ny,nz = size831 nx, ny, nz = size 818 832 structure = space.Grid3D(nx/float(ny), nx/float(nz)) 819 833 else: … … 821 835 size = reduce(operator.mul, size) 822 836 self.size = size 823 self.label = label or 'population%d' % Population.nPop 837 self.label = label or 'population%d' % Population.nPop 824 838 if isinstance(cellclass, type) and issubclass(cellclass, standardmodels.StandardCellType): 825 839 self.celltype = cellclass(cellparams) 826 840 else: 827 self.celltype = cellclass 841 self.celltype = cellclass 828 842 self._structure = structure or space.Line() 829 843 self._positions = None … … 842 856 'gsyn' : self.recorder_class('gsyn', population=self)} 843 857 Population.nPop += 1 844 858 845 859 @property 846 860 def local_cells(self): 847 861 return self.all_cells[self._mask_local] 848 862 849 863 @property 850 864 def cell(self): 851 warn("The `Population.cell` attribute is not an official part of the API, and its use is deprecated. \ 852 It will be removed in a future release. All uses of `cell` may be replaced by `all_cells`") 865 warn("The `Population.cell` attribute is not an official part of the \ 866 API, and its use is deprecated. It will be removed in a future \ 867 release. All uses of `cell` may be replaced by `all_cells`") 853 868 return self.all_cells 854 869 855 870 def id_to_index(self, id): 856 871 """ 857 Given the ID(s) of cell(s) in the Population, return its (their) index (order in the858 Population).872 Given the ID(s) of cell(s) in the Population, return its (their) index 873 (order in the Population). 859 874 >>> assert p.id_to_index(p[5]) == 5 860 875 >>> assert p.id_to_index(p.index([1,2,3])) == [1,2,3] 861 876 """ 862 if not self.first_id <= id <= self.last_id: # this test assumes id is a single ID, supposed to support id being a list/array of IDs877 if not self.first_id <= id <= self.last_id: # this test assumes id is a single ID, supposed to support id being a list/array of IDs 863 878 raise IndexError("id should be in the range [%d,%d], actually %d" % (self.first_id, self.last_id, id)) 864 return id - self.first_id # this assumes ids are consecutive865 879 return id - self.first_id # this assumes ids are consecutive 880 866 881 def id_to_local_index(self, id): 867 882 if num_processes() > 1: 868 return self.local_cells.tolist().index(id) # probably very slow883 return self.local_cells.tolist().index(id) # probably very slow 869 884 else: 870 885 return self.id_to_index(id) 871 886 872 887 def _get_structure(self): 873 888 return self._structure 874 889 875 890 def _set_structure(self, structure): 876 891 assert isinstance(structure, space.BaseStructure) 877 892 if structure != self._structure: 878 self._positions = None # setting a new structure invalidates previously calculated positions893 self._positions = None # setting a new structure invalidates previously calculated positions 879 894 self._structure = structure 880 895 structure = property(fget=_get_structure, fset=_set_structure) 881 896 # arguably structure should be read-only, i.e. it is not possible to change it after Population creation 882 897 883 898 def _get_positions(self): 884 899 """ 885 Try to return self._positions. If it does not exist, create it and then return it 900 Try to return self._positions. If it does not exist, create it and then 901 return it. 886 902 """ 887 903 if self._positions is None: … … 892 908 assert isinstance(pos_array, numpy.ndarray) 893 909 assert pos_array.shape == (3, self.size), "%s != %s" % (pos_array.shape, (3, self.size)) 894 self._positions = pos_array.copy() # take a copy in case pos_array is changed later895 self._structure = None # explicitly setting positions destroys any previous structure910 self._positions = pos_array.copy() # take a copy in case pos_array is changed later 911 self._structure = None # explicitly setting positions destroys any previous structure 896 912 897 913 positions = property(_get_positions, _set_positions, … … 899 915 giving the x,y,z coordinates of all the neurons (soma, in the 900 916 case of non-point models).""") 901 917 902 918 def describe(self, template='population_default.txt', engine='default'): 903 919 """ 904 920 Returns a human-readable description of the population. 905 921 906 922 The output may be customized by specifying a different template 907 923 togther with an associated template engine (see ``pyNN.descriptions``). 908 924 909 925 If template is None, then a dictionary containing the template context 910 926 will be returned. … … 931 947 932 948 class PopulationView(BasePopulation): 933 949 934 950 def __init__(self, parent, selector, label=None): 935 951 self.parent = parent 936 self.mask = selector # later we can have fancier selectors, for now we just have numpy masks952 self.mask = selector # later we can have fancier selectors, for now we just have numpy masks 937 953 self.label = label or "view of %s with mask %s" % (parent.label, self.mask) 938 954 # maybe just redefine __getattr__ instead of the following... 939 955 self.celltype = self.parent.celltype 940 956 self.cellparams = self.parent.cellparams 941 self.all_cells = self.parent.all_cells[self.mask] # do we need to ensure this is ordered?957 self.all_cells = self.parent.all_cells[self.mask] # do we need to ensure this is ordered? 942 958 self.size = len(self.all_cells) 943 959 self._mask_local = self.parent._mask_local[self.mask] 944 960 self.local_cells = self.all_cells[self._mask_local] 945 self.first_id = self.all_cells[0] # only works if we assume all_cells is sorted, otherwise could use min()961 self.first_id = self.all_cells[0] # only works if we assume all_cells is sorted, otherwise could use min() 946 962 self.last_id = self.all_cells[-1] 947 963 self.recorders = self.parent.recorders 948 964 self.record_filter = self.all_cells 949 965 950 966 @property 951 967 def initial_values(self): … … 973 989 >>> assert id_to_index(p.index([1,2,3])) == [1,2,3] 974 990 """ 975 index, = numpy.where(self.all_cells ==id)991 index, = numpy.where(self.all_cells == id) 976 992 if index.size == 1: 977 993 return index.item() … … 984 1000 """ 985 1001 Returns a human-readable description of the population view. 986 1002 987 1003 The output may be customized by specifying a different template 988 1004 togther with an associated template engine (see ``pyNN.descriptions``). 989 1005 990 1006 If template is None, then a dictionary containing the template context 991 1007 will be returned. … … 998 1014 999 1015 1000 # ============================================================================= =1016 # ============================================================================= 1001 1017 1002 1018 class Assembly(object): … … 1013 1029 if not isinstance(p, BasePopulation): 1014 1030 raise TypeError("argument is a %s, not a Population." % type(p).__name__) 1015 self.populations = list(populations) # should this be a set?1031 self.populations = list(populations) # should this be a set? 1016 1032 self.label = kwargs.get('label', 'assembly%d' % Assembly.count) 1017 1033 assert isinstance(self.label, basestring), "label must be a string or unicode" … … 1020 1036 @property 1021 1037 def local_cells(self): 1022 return numpy.append(self.populations[0].local_cells, [p.local_cells for p in self.populations[1:]]) 1038 return numpy.append(self.populations[0].local_cells, 1039 [p.local_cells for p in self.populations[1:]]) 1023 1040 1024 1041 @property 1025 1042 def all_cells(self): 1026 return numpy.append(self.populations[0].all_cells, [p.all_cells for p in self.populations[1:]]) 1043 return numpy.append(self.populations[0].all_cells, 1044 [p.all_cells for p in self.populations[1:]]) 1027 1045 1028 1046 @property 1029 1047 def size(self): 1030 1048 return sum(p.size for p in self.populations) 1031 1049 1032 1050 def __iter__(self): 1033 1051 return chain(iter(p) for p in self.populations) 1034 1052 1035 1053 def __len__(self): 1036 1054 """Return the total number of cells in the population (all nodes).""" 1037 1055 return self.size 1038 1056 1039 1057 def __add__(self, other): 1040 1058 if isinstance(other, BasePopulation): … … 1044 1062 else: 1045 1063 raise TypeError("can only add a Population or another Assembly to an Assembly") 1046 1064 1047 1065 def __iadd__(self, other): 1048 1066 if isinstance(other, BasePopulation): … … 1053 1071 raise TypeError("can only add a Population or another Assembly to an Assembly") 1054 1072 return self 1055 1073 1056 1074 def initialize(self, variable, value): 1057 1075 for p in self.populations: … … 1075 1093 """ 1076 1094 Returns a human-readable description of the assembly. 1077 1095 1078 1096 The output may be customized by specifying a different template 1079 1097 togther with an associated template engine (see ``pyNN.descriptions``). 1080 1098 1081 1099 If template is None, then a dictionary containing the template context 1082 1100 will be returned. … … 1086 1104 return descriptions.render(engine, template, context) 1087 1105 1088 # ============================================================================== 1106 # ============================================================================= 1107 1089 1108 1090 1109 class Projection(object): 1091 1110 """ 1092 1111 A container for all the connections of a given type (same synapse type and 1093 plasticity mechanisms) between two populations, together with methods to set1094 parameters of those connections, including of plasticity mechanisms.1095 """ 1096 1112 plasticity mechanisms) between two populations, together with methods to 1113 set parameters of those connections, including of plasticity mechanisms. 1114 """ 1115 1097 1116 def __init__(self, presynaptic_neurons, postsynaptic_neurons, method, 1098 1117 source=None, target=None, synapse_dynamics=None, … … 1101 1120 presynaptic_neurons and postsynaptic_neurons - Population, PopulationView 1102 1121 or Assembly objects. 1103 1122 1104 1123 source - string specifying which attribute of the presynaptic cell 1105 1124 signals action potentials. This is only needed for 1106 multicompartmental cells with branching axons or dendrodendritic1107 synapses. All standard cells have a single source, and this1108 is the default.1109 1125 multicompartmental cells with branching axons or 1126 dendrodendriticsynapses. All standard cells have a single 1127 source, and this is the default. 1128 1110 1129 target - string specifying which synapse on the postsynaptic cell to 1111 1130 connect to. For standard cells, this can be 'excitatory' or 1112 1131 'inhibitory'. For non-standard cells, it could be 'NMDA', etc. 1113 If target is not given, the default values of 'excitatory' is used. 1114 1132 If target is not given, the default values of 'excitatory' is 1133 used. 1134 1115 1135 method - a Connector object, encapsulating the algorithm to use for 1116 1136 connecting the neurons. 1117 1118 synapse_dynamics - a `standardmodels.SynapseDynamics` object specifying which1119 synaptic plasticity mechanisms to use.1120 1137 1138 synapse_dynamics - a `standardmodels.SynapseDynamics` object specifying 1139 which synaptic plasticity mechanisms to use. 1140 1121 1141 rng - specify an RNG object to be used by the Connector. 1122 1142 """ 1123 for prefix, pop in zip(("pre", "post"), (presynaptic_neurons, postsynaptic_neurons)): 1143 for prefix, pop in zip(("pre", "post"), 1144 (presynaptic_neurons, postsynaptic_neurons)): 1124 1145 if not isinstance(pop, (BasePopulation, Assembly)): 1125 1146 raise errors.ConnectionError("%ssynaptic_neurons must be a Population, PopulationView or Assembly, not a %s" % (prefix, type(pop))) 1126 self.pre = presynaptic_neurons # } these really1127 self.source = source # } should be1128 self.post = postsynaptic_neurons # } read-only1129 self.target = target # }1147 self.pre = presynaptic_neurons # } these really 1148 self.source = source # } should be 1149 self.post = postsynaptic_neurons # } read-only 1150 self.target = target # } 1130 1151 self.label = label 1131 1152 if isinstance(rng, random.AbstractRNG): … … 1149 1170 """Return the total number of local connections.""" 1150 1171 return len(self.connection_manager) 1151 1172 1152 1173 def size(self, gather=True): 1153 1174 """ … … 1161 1182 else: 1162 1183 return len(self) 1163 1184 1164 1185 def __repr__(self): 1165 1186 return 'Projection("%s")' % self.label 1166 1167 1187 1168 1188 def __getitem__(self, i): 1169 1189 return self.connection_manager[i] 1170 1171 # --- Methods for setting connection parameters --------------------------- -1172 1190 1191 # --- Methods for setting connection parameters --------------------------- 1192 1173 1193 def setWeights(self, w): 1174 1194 """ … … 1184 1204 w = check_weight(w, self.synapse_type, is_conductance(self.post.local_cells[0])) 1185 1205 self.connection_manager.set('weight', w) 1186 1206 1187 1207 def randomizeWeights(self, rand_distr): 1188 1208 """ … … 1193 1213 # give it a separate name, though. Comments? 1194 1214 self.setWeights(rand_distr.next(len(self))) 1195 1215 1196 1216 def setDelays(self, d): 1197 1217 """ … … 1202 1222 """ 1203 1223 self.connection_manager.set('delay', d) 1204 1224 1205 1225 def randomizeDelays(self, rand_distr): 1206 1226 """ … … 1221 1241 """ 1222 1242 self.setSynapseDynamics(param, rand_distr.next(len(self))) 1223 1224 # --- Methods for writing/reading information to/from file. --------------- -1225 1243 1244 # --- Methods for writing/reading information to/from file. --------------- 1245 1226 1246 def getWeights(self, format='list', gather=True): 1227 1247 """ 1228 1248 Get synaptic weights for all connections in this Projection. 1229 1249 1230 1250 Possible formats are: a list of length equal to the number of connections 1231 1251 in the projection, a 2D weight array (with NaN for non-existent … … 1236 1256 logger.error("getWeights() with gather=True not yet implemented") 1237 1257 return self.connection_manager.get('weight', format, offset=(self.pre.first_id, self.post.first_id)) 1238 1258 1239 1259 def getDelays(self, format='list', gather=True): 1240 1260 """ 1241 1261 Get synaptic delays for all connections in this Projection. 1242 1262 1243 1263 Possible formats are: a list of length equal to the number of connections 1244 1264 in the projection, a 2D delay array (with NaN for non-existent … … 1257 1277 logger.error("getstandardmodels.SynapseDynamics() with gather=True not yet implemented") 1258 1278 return self.connection_manager.get(parameter_name, format, offset=(self.pre.first_id, self.post.first_id)) 1259 1279 1260 1280 def saveConnections(self, filename, gather=True, compatible_output=True): 1261 1281 """ 1262 1282 Save connections to file in a format suitable for reading in with a 1263 1283 FromFileConnector. 1264 """ 1284 """ 1265 1285 fmt = "%d\t%d\t%g\t%g\n" 1266 1286 lines = [] 1267 1287 if not compatible_output: 1268 for c in self.connections: 1269 line = fmt % (c.source, 1288 for c in self.connections: 1289 line = fmt % (c.source, 1270 1290 c.target, 1271 1291 c.weight, … … 1273 1293 lines.append(line) 1274 1294 else: 1275 for c in self.connections: 1295 for c in self.connections: 1276 1296 line = fmt % (self.pre.id_to_index(c.source), 1277 1297 self.post.id_to_index(c.target), … … 1292 1312 f.writelines(lines) 1293 1313 f.close() 1294 1314 1295 1315 def printWeights(self, filename, format='list', gather=True): 1296 """Print synaptic weights to file. In the array format, zeros are printed 1297 for non-existent connections.""" 1316 """ 1317 Print synaptic weights to file. In the array format, zeros are printed 1318 for non-existent connections. 1319 """ 1298 1320 weights = self.getWeights(format=format, gather=gather) 1299 1321 f = open(filename, 'w', DEFAULT_BUFFER_SIZE) … … 1307 1329 # f.write(fmt % tuple(row)) 1308 1330 f.close() 1309 1331 1310 1332 def weightHistogram(self, min=None, max=None, nbins=10): 1311 1333 """ … … 1317 1339 # should be put here or in an external module. 1318 1340 bins = numpy.arange(min, max, float(max-min)/nbins) 1319 return numpy.histogram(self.getWeights(format='list', gather=True), bins) # returns n, bins1320 1341 return numpy.histogram(self.getWeights(format='list', gather=True), bins) # returns n, bins 1342 1321 1343 def describe(self, template='projection_default.txt', engine='default'): 1322 1344 """ 1323 1345 Returns a human-readable description of the projection. 1324 1346 1325 1347 The output may be customized by specifying a different template 1326 1348 togther with an associated template engine (see ``pyNN.descriptions``). 1327 1349 1328 1350 If template is None, then a dictionary containing the template context 1329 1351 will be returned. … … 1345 1367 1346 1368 1347 # ============================================================================= =1369 # ============================================================================= -
trunk/src/nest/__init__.py
r820 r821 284 284 assert len(rarr) == len(self.local_cells), "%d != %d" % (len(rarr), len(self.local_cells)) 285 285 nest.SetStatus(self.local_cells.tolist(), STATE_VARIABLE_MAP[variable], value) 286 self.initial_values[variable] = core.LazyArray(value, self.size)286 self.initial_values[variable] = core.LazyArray(value, (self.size,)) 287 287 288 288 def _record(self, variable, record_from=None, rng=None, to_file=True): -
trunk/test/unittests/test_space.py
r812 r821 232 232 rs = space.RandomStructure(boundary=s, origin=(1.0, 1.0, 1.0)) 233 233 positions = rs.generate_positions(n) 234 assert_equal(positions.shape, ( n,3))234 assert_equal(positions.shape, (3,n)) 235 235 for axis in range(2): 236 236 assert 3 < max(positions[:,axis]) < 3.5
