// Template for NetLayer class.
// Intended to be used together with the LayerConn and ObjectArray classes.
// A NetLayer is an array of cells (either artificial cells such as the built-in
// IntFire4, or compartmental models encapsulated in a template) all of the same
// type. `Layer' is used as a generic term intended to include layers, columns,
// nuclei, etc., of cells.This class is intended to make management of large
// neuronal networks easier. The class provides convenient procedures for
// setting parameters of all cells in the layer at the same time. The LayerConn
// class can be used to easily specify synaptic connections between two
// NetLayers.
// SYNTAX:
// nl = new NetLayer(dim,n,"celltype")
// nl = new NetLayer(dim,n,"celltype",arg[,label])
// nl = new NetLayer(dim,n,"celltype",paramvec[,label])
//
// dim is the number of dimensions (1-3) of the cell array.
// n is the number of cells in each dimension. At the moment, all dimensions
// will have the same size, i.e. only square and cubic arrays are possible.
// Allowing n to be different for each dimension would add considerably to the
// complexity of the code, and it didn't seem worthwhile. However, this could
// be added if needed.
// "celltype" is the name of the artificial cell (e.g. IntFire4) or the name in
// the begintemplate declaration (e.g. MyPyramidalCell).
// arg is a numerical or string argument, if the cells are created with a single argument
// paramvec is a vector containing the parameter values for the cell if there
// are two or more (otherwise use arg), e.g. if
// celltype = "IntFire1" it is a 2-element vector containing the values of tau
// and refrac.
// cell
// Address an individual cell in the array, e.g. nl.cell[3][2][6], like x in
// the Vector and Matrix classes.
// set("parametername",value)
// Sets the value of parametername to value for every cell in the array.
// e.g. nl.set("tau",20.0)
// tset("parametername",&valueObj)
// 'Topographic' set. Sets the value of parametername to valueObj.x[...]
// for every cell i in the array. valueObj can be a Vector or a Matrix.
// rset("parametername",randomobj)
// 'Random' set. Sets the value of parametername to a value taken from
// the randomobj Random object.
// call("procname","arguments")
// Calls the procedure procname(arguments) for every cell in the array.
// e.g. nl.call("set_background","0.1") if the cell template defines the
// procedure set_background()
// tcall("procname","objarr")
// `Topographic' call. Calls the procedure procname() for every cell in the
// array. The argument to the procedure depends on the coordinates of the
// cell. objarr is a Vector, a Matrix or an ObjectArray of the same dimension
// and size as the NetLayer.
// e.g. nl.tcall("memb_init",matobj)
// calls nl.cell[i][j].memb_init(matobj.x[i][j]) for all i,j
// and sets the initial membrane potential for each cell to the values stored
// in the Matrix matobj.
// print_spikes(fileobj)
// Assuming the cell object records spiketimes in a Vector spiketimes, this
// procedure prints spike times to file in the two-column format
// "spiketime cell_id" where cell_id is the index of the cell counting along
// rows and down columns (and the extension of that for 3-D). This allows
// easy plotting of a `raster' plot of spiketimes, with one line for each
// cell.
// mean_spike_count()
// Assuming the cell object records spiketimes in a Vector spiketimes, this
// function returns the mean number of spikes per neuron
// dimensions()
// Returns the number of dimensions of the NetLayer.
// size()
// Returns the size of each dimension. If the NetLayer class is extended to
// allow different sizes in different dimensions, this function could take an
// argument specifying which dimension to return the size for.
// label
// Allows a label for the LayerConn to be set or retreived. It doesn't do
// anything at the moment, but could be used in a GUI.
// celltype
// A string containing the type of cell used in the array. Changing this
// string will not change the cell type, however.
// random_init(randobj)
// Sets initial membrane potentials for all the
// cells in the array to random values.
// Andrew Davison, UNIC, CNRS, August 2003-February 2006
begintemplate NetLayer
public cell, tset, set, call, tcall, print_spikes, dimensions
public size, plot_spikes, gui, label, celltype, rset
public mean_spike_count, random_init, print_v
create cell_holder
objref cell, this, cellParams
strdef command, label, celltype, fmt, arg
proc init() { local i,j,k,a4 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// $1: Number of dimensions (max 3)
// $2: Size of each dimension (all dimensions same size)
// $s3: Name of NetworkCell
// $o4: Vector containing list of cell parameters
create cell_holder // for holding artificial cells/point processes
access cell_holder
m = $1 // should catch error if 1>m>3
n = $2
celltype = $s3
if (numarg() > 3) {
a4 = argtype(4)
if (a4==0) { // number
sprint(arg,"%g",$4)
} else if (a4==2) { // string
sprint(arg,"\"%s\"",$s4) // need to add quotes around
//arg = $s4
} else if (a4==1) { // object
cellParams = $o4.c() // copy here so the same vector can be used
// to initialize other layers, but the
// parameters can then be changed independently
arg = "cellParams"
} else { // error
print "Error in NetLayer creation. Incorrect 4th argument"
arg = ""
}
if (numarg() > 4) {
label = $s5
}
} else {
arg = "" // if the `cell' is a NetStim or similar
}
sprint(command,"objref cell")
for i = 0, m-1 {
sprint(command,"%s[%d]",command,n)
}
execute1(command,this) // declare the objref array.
// Create the cells.
for i = 0,n-1 {
if (m > 1) {
for j = 0,n-1 {
if (m > 2) {
for k = 0,n-1 {
sprint(command,"cell[%d][%d][%d] = new %s(%s)",i,j,k,celltype,arg)
execute1(command,this)
}
} else {
sprint(command,"cell[%d][%d] = new %s(%s)",i,j,celltype,arg)
execute1(command,this)
}
}
} else {
sprint(command,"cell[%d] = new %s(%s)",i,celltype,arg)
execute1(command,this)
}
}
}
func dimensions() { // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// number of dimensions (1-3)
return m
}
func size() { // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// size of each dimension
return n
}
proc set() { local i,j,k // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// for each cell in the array, set a parameter (1st argument) to a value
// (2nd argument).
for i = 0, n-1 {
if (m > 1) {
for j = 0,n-1 {
if (m > 2) {
for k = 0,n-1 {
sprint(command,"%s.cell[%d][%d][%d].%s = %g",this,i,j,k,$s1,$2)
execute1(command)
}
} else {
sprint(command,"%s.cell[%d][%d].%s = %g",this,i,j,$s1,$2)
execute1(command)
}
}
} else {
sprint(command,"%s.cell[%d].%s = %g",this,i,$s1,$2)
execute1(command)
}
}
}
proc tset() { local i,j,k // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// for each cell in the array, set a parameter (1st argument) to a value
// (2nd argument).
if (m > 2) {
print "Cannot be used for 3-D Layers"
return
}
for i = 0, n-1 {
if (m > 1) {
for j = 0,n-1 {
sprint(command,"%s.cell[%d][%d].%s = %s.x[%d][%d]",this,i,j,$s1,$s2,i,j)
execute1(command)
}
} else {
sprint(command,"%s.cell[%d].%s = %s.x[%d]",this,i,$s1,$s2,i)
execute1(command)
}
}
}
proc rset() { local i,j,k // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// for each cell in the array, set a parameter (1st argument) to a value
// from the Random object given as the 2nd argument. An optional third
// argument can specify a condition on the current value, e.g. "> 0"
for i = 0, n-1 {
if (m > 1) {
for j = 0,n-1 {
if (m > 2) {
for k = 0,n-1 {
if (numarg() > 2) {
sprint(command,"if (%s.cell[%d][%d][%d].%s %s) { %s.cell[%d][%d][%d].%s = %g }",this,i,j,k,$s1,$s3,this,i,j,k,$s1,$o2.repick())
} else {
sprint(command,"%s.cell[%d][%d][%d].%s = %g",this,i,j,k,$s1,$o2.repick())
}
execute1(command)
}
} else {
if (numarg() > 2) {
sprint(command,"if (%s.cell[%d][%d].%s %s) { %s.cell[%d][%d].%s = %g }",this,i,j,$s1,$s3,this,i,j,$s1,$o2.repick())
} else {
sprint(command,"%s.cell[%d][%d].%s = %g",this,i,j,$s1,$o2.repick())
}
execute1(command)
}
}
} else {
if (numarg() > 2) {
sprint(command,"if (%s.cell[%d].%s %s) { %s.cell[%d].%s = %g }",this,i,$s1,$s3,this,i,$s1,$o2.repick())
} else {
sprint(command,"%s.cell[%d].%s = %g",this,i,$s1,$o2.repick())
}
execute1(command)
}
}
}
proc call() { local i,j,k // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// for each cell in the array, call a procedure (name is 1st argument) with
// arguments given by the 2nd argument string.
for i = 0, n-1 {
if (m > 1) {
for j = 0,n-1 {
if (m > 2) {
for k = 0,n-1 {
sprint(command,"err = %s.cell[%d][%d][%d].%s(%s)",this,i,j,k,$s1,$s2)
execute1(command)
}
} else {
sprint(command,"err = %s.cell[%d][%d].%s(%s)",this,i,j,$s1,$s2)
execute1(command)
}
}
} else {
sprint(command,"err = %s.cell[%d].%s(%s)",this,i,$s1,$s2)
execute1(command)
}
}
}
proc tcall() { local i,j,k // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// t for topographic.
// for each cell in the array, call a procedure (name is 1st argument) with
// argument given by the value of the element of the 2nd argument (Vector,
// Matrix or ObjectArray) at the corresponding location.
for i = 0, n-1 {
if (m > 1) {
for j = 0,n-1 {
if (m > 2) {
for k = 0,n-1 {
sprint(command,"err = %s.cell[%d][%d][%d].%s(%s.x[%d][%d][%d])",this,i,j,k,$s1,$s2,i,j,k)
execute1(command)
}
} else {
sprint(command,"err = %s.cell[%d][%d].%s(%s.x[%d][%d])",this,i,j,$s1,$s2,i,j)
execute1(command)
}
}
} else {
sprint(command,"err = %s.cell[%d].%s(%s.x[%d])",this,i,$s1,$s2,i)
execute1(command)
}
}
}
proc print_spikes() { local i,j,k // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Write spike times to file. Assumes that the cell template defines a
// Vector spiketimes that is used to record spikes.
for i = 0, n-1 {
if (m > 1) {
for j = 0,n-1 {
if (m > 2) {
for k = 0,n-1 {
sprint(fmt,"%s\t%d\t%d\t%d\t%d\n","%.2f",i*n*n+j*n+k,i,j,k)
this.cell[i][j][k].spiketimes.printf($o1,fmt)
}
} else {
sprint(fmt,"%s\t%d\t%d\t%d\n","%.2f",i*n+j,i,j)
this.cell[i][j].spiketimes.printf($o1,fmt)
}
}
} else {
sprint(fmt,"%s\t%d\n","%.2f",i)
this.cell[i].spiketimes.printf($o1,fmt)
}
}
}
func mean_spike_count() { local i,j,k,nspikes
// return the average number of spikes per cell. Assumes that the cell template defines a
// Vector spiketimes that is used to record spikes.
nspikes = 0
for i = 0, n-1 {
if (m > 1) {
for j = 0,n-1 {
if (m > 2) {
for k = 0,n-1 {
nspikes += this.cell[i][j][k].spiketimes.size()
}
} else {
nspikes += this.cell[i][j].spiketimes.size()
}
}
} else {
nspikes += this.cell[i].spiketimes.size()
}
}
return nspikes/(n^m)
}
proc print_v() { local i,j,k // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Write membrane potential traces to file. Assumes that the cell
// template defines a Vector vrecord that is used to record membrane
// potential
for i = 0, n-1 {
if (m > 1) {
for j = 0,n-1 {
if (m > 2) {
for k = 0,n-1 {
sprint(fmt,"%s\t%d\t%d\t%d\t%d\n","%.3g",i*n*n+j*n+k,i,j,k)
this.cell[i][j][k].vrecord.printf($o1,fmt)
}
} else {
sprint(fmt,"%s\t%d\t%d\t%d\n","%.3g",i*n+j,i,j)
this.cell[i][j].vrecord.printf($o1,fmt)
}
}
} else {
sprint(fmt,"%s\t%d\n","%.3g",i)
this.cell[i].vrecord.printf($o1,fmt)
}
}
}
proc random_init() { local r
// randomly set cell membrane initial values
// $o1 = random object
// $2 = lower bound of uniform distribution
// $3 = upper bound of uniform distribution
r = $o1.uniform($2,$3)
for i = 0, n-1 {
if (m > 1) {
for j = 0,n-1 {
if (m > 2) {
for k = 0,n-1 {
r = $o1.repick()
sprint(command,"err = %s.cell[%d][%d][%d].memb_init(%f)",this,i,j,k,r)
execute1(command)
}
} else {
r = $o1.repick()
sprint(command,"err = %s.cell[%d][%d].memb_init(%f)",this,i,j,r)
execute1(command)
}
}
} else {
r = $o1.repick()
sprint(command,"err = %s.cell[%d].memb_init(%f)",this,i,r)
execute1(command)
}
}
}
proc plot_spikes() { local i,j,k,l
// $o1 = Graph
// $s2 = mark symbol, e.g. "o", "|"
// $3 = mark size
// $4 = mark color
// $5 = mark brush
for i = 0, n-1 {
if (m > 1) {
for j = 0,n-1 {
if (m > 2) {
for k = 0,n-1 {
for l = 0, this.cell[i][j][k].spiketimes.size()-1 {
$o1.mark(this.cell[i][j][k].spiketimes.x[l],i*n*n+j*n+k,$s2,$3,$4,$5)
}
}
} else {
for l = 0, this.cell[i][j].spiketimes.size()-1 {
$o1.mark(this.cell[i][j].spiketimes.x[l],i*n+j,$s2,$3,$4,$5)
}
}
}
} else {
for l = 0, this.cell[i].spiketimes.size()-1 {
$o1.mark(this.cell[i].spiketimes.x[l],i,$s2,$3,$4,$5)
}
}
}
}
// TO DO
//proc gui() {} // provide a graphical interface to the NetLayer object.
//proc save() {} // for session files
//proc load() {}
// N.B. if a NetLayer is removed, are all the constituent cells
// removed automatically, or do I need to clean up with unref() ?
endtemplate NetLayer