Large scale network models often include populations of similar entities. Neosim allows models to be defined at this high level, in terms of populations of cells and projections between populations. Having a high level model description also simplifies efficient implementation on parallel machines.
Example populations could be a layer of one type of neuron in a cortex, or a nucleus of similar cells. ``Views'' of populations can also be created, and treated as new populations; an example could select all cells over a certain size, or in a restricted region. Populations are treated as a special type of shared entity, which allows rapid lookup of elements of the population. Another example population could be a ``GeometryView'' which provides rapid lookup of all entities in a given region of space, an essential optimisation for setting up distance based connections in a large model.
In order to define a population, a modeller provides a name, an entity class, the number of elements, and a function to initialise each entity of the population.
Every entity is member of a Population , and there is a hierarchical naming structure for populations. There are methods to lookup an entity or population given a fully qualified name (e.g. "Cortex/layer3/pyramidal12345"):
EntityHandle lookupEntity( Time t, String name ) Population lookupPopulation( Time t, String name )
A time argument is specified, as populations can change over time, with entities added and deleted. For the script reader, this argument will be the simulation time; for entities it will be the entity's local simulation time.
The population tree structure is available:
It has methods for adding and removing population nodes from the tree, and for navigating the branches.PopulationTree getPopulationTree( Time t )
A basic Population has a few methods:
String getName() PopulationNode getParent() int getId() int getNumEnts() int getIndex( EntityID eid ) boolean isMember( Entity e ) Vector getMemberList() // Vector of all EntityIDs void construct() // Build the population.
A PopulationBuilder extends the basic Population to include methods to instantiate a number of entities of a given class:
EntityInit is a function to initialise entity i of the population.PopulationBuilder( EntityClass, int num, EntityInit ei )
This can contain entity method calls to set the position, initial potentials, etc etc depending on the index in the population. Examples could include:interface EntityInit { void init( Entity e, Population p, int index); }
class InitMesh2D implements EntityInit { InitMesh2D( xsz, ysz, x1, y1, xspc, yspc ); void init( Entity e, Population p, int index ) { e.setPos( index % xsz, index \ xsz ); } }
Views of a population can be constructed from an existing population:
by overriding the buildView() method which is called for each entity in the source population.PopulationView( Population srcpop ) final void addEntityToView( Entity e ); void buildView( Entity e )
An example population view is a selection based on pattern matching of the name:
class RegexpMatch extends PopulationView { RegexpMatch( Population p, String regexp ); void buildView( Entity e ) { if (regexp_match( regexp, e.getName() ) { addEntityToView( e ); } } }
Population views can be used to provide rapid lookup of entities, e.g. a GridDecompositionView which puts entities into grid boxes. This can speed up specification of distance-based connection patterns as connection requests can be sent to nearby entities rather than all entities.
class GridDecompositionView extends PopulationView { GridDecompositionView(double gridxsz, double gridysz, double zsz); Vector getMemberList( int gridx, int gridy, int gridz ); void addEntityToView( Entity e, int x, int y, int z ) //... void buildView( Entity e ) { Pos p = e.getPos(); int gridx = (int) p.getX() / gridxsz; int gridy = (int) p.getY() / gridysz; int gridz = (int) p.getZ() / gridzsz; addEntityToView( e, gridx, gridy, gridz ); } }