| 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 |
|---|
| 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) |
|---|
| 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 | |
|---|
| 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 | | |
|---|
| 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 |   |
|---|