// Provide classes for simulating a mouse moving in a maze//// Copyright 2007 John L Baker. All rights reserved.// This software is provided AS IS under the terms of the Open Source// MIT License. See http://www.opensource.org/licenses/mit-license.php.//// File: placecell_baker_2003.cpp//// Release: 1.0.1// Author: John Baker// Updated: 6 March 2007//// Description://// Place cells simulated here are purely phenomenological. They are simulated// as PoissonNeurons with random firing patterns, which can be modulated based// on gamma and theta rhythms. Place field firing probability is determined by// by location within a maze.//// Interneurons are similarly phenomenological. Their firing is modulate by// gamma and theta rhythms but is not location dependent.//// Place cells and interneurons are organized into groups (layers) based// on similar properties or connectivity. Different connection policies// can be applied to create synapses onto a target neuron.//// See the header file for references.#include"placecell_baker_2003.h"#include<iostream>usingnamespace std;
usingnamespace BNSF;
usingnamespace BAKER_2003;
// --------------------------------------------------------------------// PlaceCell class body// --------------------------------------------------------------------// Constructors and destructor
PlaceCell::PlaceCell(Model* m, PlaceCellLayer* pclayer)
: PoissonNeuron(m)
{
usingnamespace UOM;
// Save the associated place cell layer provided
_layer = pclayer;
// Set initial default values (examples only).// Note that the superclass has an initial// firing rate of 0, which is left as is.
_meanFiringRate = 1*Hz;
_distanceEstSD = 1*cm;
_propDistanceEstSD = 0.2f;
_thetaPhase = 0*radiansPerDegree;
_precessionRate = 0*radiansPerDegree/meter;
_isActive = true;
_inactiveFiringRate = 0*Hz;
// Set values indicating that no center has yet been set
_centerX = numeric_limits<Number>::infinity();
_centerY = numeric_limits<Number>::infinity();
_Xvar = 0;
_Yvar = 0;
_hx = 0;
_hy = 0;
_densityMultiplier = 0;
}
PlaceCell::~PlaceCell() {}
// Access the maze subjectMazeSubject* PlaceCell::subject(){
returnlayer()->subject();
}
// Access the maze associated with the subjectMaze* PlaceCell::maze(){
returnlayer()->subject()->maze();
}
// Return the peak firing rateNumber PlaceCell::peakRate(){
returnmeanFiringRate()*_densityMultiplier;
}
// Set the place field center location and update fivoidPlaceCell::setCenter(Number x, Number y){
// Save the values
_centerX = x;
_centerY = y;
// Update field properties if possibleupdateProperties();
}
// Set the factors for computing distance estimate standard deviationvoidPlaceCell::setDistanceEstSD(Number sdall, Number sdprop){
_distanceEstSD = sdall;
_propDistanceEstSD = sdprop;
updateProperties();
}
// Set the flag indicating that this cell is active// in the current environment.voidPlaceCell::isActive(bool b){
_isActive = b;
}
// Return a location in local coordinates. See also the// implementation of this in Maze. Subclasses may want to// override this for a different place field scheme.voidPlaceCell::localCoordinates(
Number locX, // Current location X coord
Number locY, // Current location Y coord
Number& northDist, // Boundary dist along (0,1)
Number& southDist, // Boundary dist along (0,-1)
Number& eastDist, // Boundary dist along (1,0)
Number& westDist)// Boundary dist along (-1,0){
maze()->localCoordinates(locX, locY, northDist,southDist,eastDist,westDist);
}
// Provide a heading defined by local conditions. See also the// implementation of this in Maze. Subclasses may want to// override this for a different place field scheme.voidPlaceCell::localOrientation(
Number locX, // Current location X coord
Number locY, // Current location Y coord
Number& hx, // Unit vector x coord (output)
Number& hy)// Unit vector y coord (output){
maze()->localOrientation(locX, locY, hx, hy);
}
// Get the location of the nearest place field center. By default// there is only one center, but subclasses may support more than one.voidPlaceCell::locateFieldCenter(
Number locX, // Current location X coord
Number locY, // Current location Y coord
Number& ctrX, // Center location X coord
Number& ctrY)// Center location X coord{
ctrX = centerX();
ctrY = centerY();
}
// Check that everything is set before starting the simulationvoidPlaceCell::simulationStarted(){
if (_densityMultiplier==0) {
FatalError("(PlaceCell::simulationStarted) Initialization incomplete.");
return;
}
// Let superclass know simulation is starting
PoissonNeuron::simulationStarted();
}
// Update the firing rate based on current locationNumber PlaceCell::updateFiringRate(){
MazeSubject* sub = subject();
Number locx = subject()->locX();
Number locy = subject()->locY();
Number ctrx,ctry;
Number dx,dy;
Number dxr,dyr;
Number den,rate;
// Get the nearest place field center for all subsequent processing.locateFieldCenter(locx,locy,ctrx,ctry);
// Handle the case of an inactive cell.// Similarly, if the cell has a place field located// in the current inactive region, treat it as inactive.if ( isInactive() || (
layer()->inactiveRegion()!=NULL &&
layer()->inactiveRegion()->containsPoint(ctrx,ctry))) {
firingRate( inactiveFiringRate() );
returninactiveFiringRate();
}
// Otherwise proceed with setting the rate based// on the current location.// Get the difference between the current location and field center
dx = locx - ctrx;
dy = locy - ctry;
// Rotate the difference vector based on the local orientation
dxr = dx*_hx + dy*_hy;
dyr = -dx*_hy + dy*_hx;
// Get the new firing rate from the spatial probability density
den = _densityMultiplier*exp(-((dxr*dxr)/_Xvar+(dyr*dyr)/_Yvar)/2);
firingRate( rate = meanFiringRate()*den );
return rate;
}
// Update field properties if possible (otherwise ignore)voidPlaceCell::updateProperties(){
Number xplus,xminus,yplus,yminus;
Number xsd,ysd;
Number ctrx,ctry;
// Check for cases in which the update is not yet possibleif (layer()==NULL || subject()==NULL || maze()==NULL) {
return;
}
// Get the local orientation for the place field center// as well as local coordinates of the center in NSEW format.locateFieldCenter(subject()->locX(),subject()->locY(),ctrx,ctry);
localOrientation(ctrx, ctry, _hx, _hy);
localCoordinates(ctrx, ctry, yplus,yminus,xplus,xminus);
// For each coordinate pick the smallest distance for the error model// and compute an associated standard deviation.
xsd = distanceEstSD()+propDistanceEstSD()*minval(xplus,xminus);
ysd = distanceEstSD()+propDistanceEstSD()*minval(yplus,yminus);
// Save variances (avoids squaring std deviation each time)
_Xvar = xsd*xsd;
_Yvar = ysd*ysd;
// Compute the density multiplier to scale the probability// density to unity assuming a uniform spatial occupancy.
_densityMultiplier = 1/(2*Pi*xsd*ysd);
}
// Return the spike selection probability applying gamma and// theta modulation as provided by the place cell layer.Number PlaceCell::spikeSelectionProbability(){
// Check for a zero rate and handle directly if soif (firingRate()==0 ) {
return0;
}
// Get values from the subjectconst Number hdx = subject()->headingX();
const Number hdy = subject()->headingY();
const Number locx = subject()->locX();
const Number locy = subject()->locY();
// Get values from the layerconst Number ga = layer()->gammaAmplitude();
const Number gf = layer()->gammaFrequency();
const Number go = layer()->gammaOffset();
const Number ta = layer()->thetaAmplitude();
const Number tf = layer()->thetaFrequency();
const Number to = layer()->thetaOffset();
// Working variables
Number ctrx,ctry; // relevant place field center
Number dx,dy; // distance from center
Number s; // dist to center along heading vector
Number a; // theta phase precession
Number gammaMod,thetaMod; // oscillatory modulation values// Get theta phase precession, but only if active in the current contextif (isActive() && precessionRate()!=0 ) {
// Based on current position and heading determine whether the// the distance to the field center in the current heading direction.// This can be positive or negative depending on whether the direction// is towards or away from the field center.locateFieldCenter(locx,locy,ctrx,ctry);
dx = ctrx - locx;
dy = ctrx - locy;
s = hdx*dx+hdy*dy;
// Phase precession is a linear function of distance.// The amount of precession is arbitrarily limited to [-Pi,Pi].
a = precessionRate() * s;
if (a<-Pi) a =-Pi;
elseif (a>Pi) a = Pi;
}
else {
a = 0;
}
// Get the gamma and theta modulations.
gammaMod = plusval(1+ga*cos(2*Pi*gf*(_nextSpikeTime-go)));
thetaMod = plusval(1+ta*cos(2*Pi*tf*(_nextSpikeTime-to)-(thetaPhase()+a)));
// From all this, get a combined spiking probability.returnisiAdjustedSelProb( gammaMod*thetaMod*firingRate() );
}
// --------------------------------------------------------------------// Interneuron class body// --------------------------------------------------------------------// Constructors and destructor
Interneuron::Interneuron(Model* m, InterneuronLayer* inlayer)
: PoissonNeuron(m)
{
// Save the associated interneuron layer provided
_layer = inlayer;
// Initialize variables
_thetaPhase = 0; // arbitary default
}
Interneuron::~Interneuron() {}
// Return the spike selection probability applying gamma-theta modulation// as provided by the place cell layerNumber Interneuron::spikeSelectionProbability(){
// Get parameters from the layerconst Number gf = layer()->gammaFrequency();
const Number ga = layer()->gammaAmplitude();
const Number go = layer()->gammaOffset();
const Number tf = layer()->thetaFrequency();
const Number ta = layer()->thetaAmplitude();
const Number to = layer()->thetaOffset();
const Number tph = thetaPhase();
MazeSubject* sub = layer()->subject();
// Working variables
Number oscMod,frate;
// Compute the oscillatory modulation value as of the next spike time
oscMod = plusval(1+ga*cos(2*Pi*gf*(_nextSpikeTime-go))) *
plusval(1+ta*cos(2*Pi*tf*(_nextSpikeTime-to) - thetaPhase()));
// Check to see if the subject is in a novelty region.// NOTE: performance would be improved in this case if the// layer were a subscriber of the subject and detected whether// the current location was in a novel region. For simple mazes// there may not be much improvement though.if (layer()->inactiveRegion() != NULL &&
layer()->inactiveRegion()->containsPoint(sub->locX(),sub->locY()) ) {
frate = layer()->inactiveRate();
}
else {
frate = layer()->firingRate();
}
// Get a combined spiking probability for the next candidate spike time.// The overall firing rate comes from the layer rather than this object.returnisiAdjustedSelProb(oscMod*frate);
}
// Get firing rate directly from the layerNumber Interneuron::firingRate(){
returnlayer()->firingRate();
}
// --------------------------------------------------------------------// SawToothInterneuron class body// --------------------------------------------------------------------// Constructors and destructor
SawToothInterneuron::SawToothInterneuron(Model* m, InterneuronLayer* inlayer)
: Interneuron(m, inlayer)
{
// Initialize variables
_contraThetaPhase = 0; // default that must be changed before use.
}
SawToothInterneuron::~SawToothInterneuron() {}
// Return the spike selection probability applying gamma-theta modulation// as provided by the place cell layer. Instead of a cosine wave, a// saw-tooth wave is used with a maximum and minimum phase as specified.Number SawToothInterneuron::spikeSelectionProbability(){
// Get parameters from the layerconst Number gf = layer()->gammaFrequency();
const Number ga = layer()->gammaAmplitude();
const Number go = layer()->gammaOffset();
const Number tf = layer()->thetaFrequency();
const Number ta = layer()->thetaAmplitude();
const Number to = layer()->thetaOffset();
const Number twoPi = 2*Pi;
MazeSubject* sub = layer()->subject();
// Working variables
Number gammaMod,thetaMod,frate,omega,peakPhase,w;
// Compute the oscillatory modulation value as of the next spike time// starting with the gamma modulation, which is sinusoidal.
gammaMod = plusval(1+ga*cos(twoPi*gf*(_nextSpikeTime-go)));
// Get the phase relative to peak phase and work out// a modulator by cases. The value derived (w) is in the// range [0 1] where w=0 at phase ctp and w=1 at tph.// 2w-1 then has range [-1 1] and replaces the cosine function// in the normal formulation of theta modulation.
omega = fmod(Number(twoPi*tf*(_nextSpikeTime-to)-contraThetaPhase()),twoPi);
peakPhase = fmod(Number(thetaPhase()-contraThetaPhase()),twoPi);
// Fix the C-defined bug in fmod when handling negative numbers
omega = omega>=0 ? omega : omega+twoPi;
peakPhase = peakPhase>=0 ? peakPhase : peakPhase + twoPi;
// The saw-tooth function is now a minimum at omega=0 and 2*pi.// Pick the rising or falling regions based on omega vs peak phase.// Try to handle the peakPhase==0 case correctly.
w = omega<peakPhase ? omega/peakPhase : (twoPi-omega)/(twoPi-peakPhase);
thetaMod = plusval(1+ta*(2*w-1));
// Check to see if the subject is in a novelty region.// NOTE: performance would be improved in this case if the// layer were a subscriber of the subject and detected whether// the current location was in a novel region. For simple mazes// there may not be much improvement though.if (layer()->inactiveRegion() != NULL &&
layer()->inactiveRegion()->containsPoint(sub->locX(),sub->locY()) ) {
frate = layer()->inactiveRate();
}
else {
frate = layer()->firingRate();
}
// Get a combined spiking probability for the next candidate spike time.// The overall firing rate comes from the layer rather than this object.returnisiAdjustedSelProb(gammaMod*thetaMod*frate);
}
// --------------------------------------------------------------------// CellLayer class body// --------------------------------------------------------------------// Constructors and destructor
CellLayer::CellLayer(MazeSubject* sub, int numericId, UniformRandom* spkunif)
{
usingnamespace UOM;
// Save the values provided
_subject = sub;
_numericIdentifier = numericId;
// Set initial values
_gammaFrequency = 0;
_gammaAmplitude = 0;
_gammaOffset = 0;
_thetaFrequency = 0;
_thetaAmplitude = 0;
_thetaOffset = 0;
_inactiveRegion = NULL;
// Create a default model and clock solver// with an arbitrary default time step.
ClockSolver* mySolver = new ClockSolver;
Model* myModel = new Model;
model(myModel);
mySolver->model(myModel);
mySolver->timeStep(5*msec);
// Set the spike randomizer in the modelmodel()->uniformRandom(spkunif);
}
CellLayer::~CellLayer()
{
// Tell any subscribers that this object is terminatingchanged(terminatedChange);
// Remove any active change subscriptionsif (_subject != NULL) {
_subject->removeSubscriber(this);
}
// Delete the model and solver if not// already done by the subclass.if (model()!=NULL) {
deletemodel()->solver();
deletemodel();
}
}
// Add a probe to all cellsvoidCellLayer::addProbeToAll(Probe* pr){
int k;
for (k=1;k<=numCells();k++) {
cell(k)->addProbe(pr);
}
}
// Remove a probe from all cellsvoidCellLayer::removeProbeFromAll(Probe* pr){
int k;
for (k=1;k<=numCells();k++) {
cell(k)->removeProbe(pr);
}
}
// Assign a numeric identifier to the layer and associated place cells.voidCellLayer::numericIdentifier(int n){
int k;
int nextId = n;
int ncell = numCells();
// Save the identifier value
_numericIdentifier = nextId++;
// Assign identifier to place cellsfor (k=1;k<=ncell;k++) {
cell(k)->numericIdentifier(nextId++);
}
}
// --------------------------------------------------------------------// PlaceCellLayer class body// --------------------------------------------------------------------// Constructors and destructor
PlaceCellLayer::PlaceCellLayer(
MazeSubject* sub,
int numCells,
int numericId,
UniformRandom* spkunif)
: CellLayer(sub, numericId, spkunif)
{
usingnamespace UOM;
// Set initial values
_numPlaceCells = numCells;
_placeCells = NULL;
_totalFiringRate = 0;
_fractionActive = 1;
// Subscribe to changes from the subjectsubject()->addSubscriber(this);
}
PlaceCellLayer::~PlaceCellLayer()
{
int k;
// Remove any dependenciesif (_subject!=NULL) {
_subject->removeSubscriber(this);
}
// Delete the model here to speed up processingdeletemodel()->solver();
deletemodel();
_model = NULL;
// Delete allocated place cellsif (_placeCells!=NULL) {
for (k=1;k<=numCells();k++) {
deleteplaceCell(k);
}
}
delete[] _placeCells;
}
// Access an place cell by number (n=1 is the first)PlaceCell* PlaceCellLayer::placeCell(int n){
// Make sure n is validif (n<1 || n>numCells() ) {
FatalError("(PlaceCellLayer::placeCell) Invalid cell number requested.");
returnNULL;
}
return _placeCells[n-1];
}
// Receive an update notification from the subjectvoidPlaceCellLayer::updateFrom(ModelComponent* sub, int reason){
int k;
// If the subject is terminating, do not reference it furtherif (reason==terminatedChange) {
_subject = NULL;
}
// If this is a state change, update all firing frequencieselseif (reason==stateChange) {
// Let each place cell update its firing rate// and get the new total.
_totalFiringRate = 0;
for (k=1;k<=numCells();k++) {
_totalFiringRate +=
placeCell(k)->updateFiringRate();
}
// Tell any interested parties about the change in rateschanged(stateChange);
}
// Check for a maze being changedelseif (reason==parameterChange) {
if (maze()!=NULL) {
// Check for a change of maze or the first setting// of the maze value. If so, update place cells// using the new maze coordinates.if (subject()->maze()!=subject()->previousMaze() ) {
// Reset place cell locations based on the new maze.setPlaceFieldCenters();
}
}
}
// Any other change types are ignored -- hence no-opelse {}
}
// Find place field centers with the maze. The results are returned// in the preallocated arrays X and Y, which are assumed to be large enough.void PlaceCellLayer::setPlaceFieldCenters()
{
int k;
Number x,y;
// Set the new centers for each cellfor (k=1;k<=numCells();k++) {
// Have the maze select a random point using the // random number stream reserved for this purpose.maze()->randomPointInMaze(x,y,subject()->locationRandomizer() );
// Set center to the point foundplaceCell(k)->setCenter(x,y);
}
// Set a new active set (if needed)if (fractionActive()>0 && fractionActive()<1) {
setFractionActive( fractionActive() );
}
}
// Set the activity status of range of cellsvoidPlaceCellLayer::setFractionActive(Number activeFraction){
int k,n;
bool defaultActive;
Number prob;
UniformRandom* unif=subject()->networkRandomizer();
// Save the fraction active
_fractionActive = activeFraction;
// Efficiently handle the special case when// active fraction is 1 or 0.if (activeFraction==0 || activeFraction==1.0) {
for (k=1;k<=numCells();k++) {
placeCell(k)->isActive( activeFraction!=0 );
}
return;
}
// Decide whether to select active or inactive cells// depending on which involves less random selection.if (activeFraction>0.5) {
defaultActive = true;
prob = 1 - activeFraction;
}
else {
defaultActive = false;
prob = activeFraction;
}
// Start by setting everying to the default statusfor (k=1;k<=numCells();k++) {
placeCell(k)->isActive(defaultActive);
}
// Now pick cells at random to set to the opposite status.
n=int( activeFraction*numCells()+0.5 );
while(n>0) {
k=int( unif->next()*numCells()+1 );
if (placeCell(k)->isActive() == defaultActive) {
placeCell(k)->isActive( !defaultActive );
n--;
}
}
}
// Set a group of cells active or inactivevoidPlaceCellLayer::setActivity(bool activeState, int from, int to){
int k;
for (k=from;k<=to;k++) {
placeCell(k)->isActive(activeState);
}
}
// Disable theta phase precession in all cellsvoidPlaceCellLayer::disableThetaPhasePrecession(){
int k;
for (k=1;k<=numCells();k++) {
placeCell(k)->precessionRate(0);
}
}
// Print field centers to a filevoidPlaceCellLayer::printPlaceFieldCenters(char* pathName){
usingnamespace UOM;
FILE* out;
int i;
PlaceCell* cell;
// Open the output fileif (pathName!=NULL) {
out = fopen(pathName,"w");
if (out==NULL) {
FatalError("(PlaceCellLayer::printFieldCenters) ""File open failed");
}
}
else {
out = stdout;
}
// Write a header linefprintf(out,"id,x,y,peakrate,isactive\n");
// Write out the field centers in CSV formatfor (i=1;i<=numCells();i++) {
cell = placeCell(i);
fprintf(out,"%d,%g,%g,%g,%d\n",
cell->numericIdentifier(),
cell->centerX()/cm,
cell->centerY()/cm,
cell->peakRate()/Hz,
cell->isActive() ? 1 : 0 );
}
// Close the output fileif (out!=stdout) {
fclose(out);
}
}
// Allocate place cells.voidPlaceCellLayer::allocatePlaceCells(){
int k;
// Make sure everything is ready to proceedif (_placeCells != NULL) {
FatalError("(PlaceCellLayer::allocatePlaceCells) ""Trying to allocate place cells twice.");
return;
}
// Allocate an array of place cells and initialize it
_placeCells = new PlaceCell* [numCells()];
for (k=1;k<=numCells();k++) {
_placeCells[k-1] = newPlaceCell(k);
}
// Locate place field centers at random locations// excluding the currently unexplored novel region.if (maze()!=NULL) {
setPlaceFieldCenters();
}
// Update all numeric identifiersnumericIdentifier( numericIdentifier() );
}
// Allocate a new place cell with a center at the indicated location. // This can be extended by subclasses to reflect layer-specific properties.PlaceCell* PlaceCellLayer::newPlaceCell(int k){
returnnewPlaceCell(model(), this);
}
// --------------------------------------------------------------------// InteneuronLayer class body// --------------------------------------------------------------------// Constructors and destructor
InterneuronLayer::InterneuronLayer(
MazeSubject* sub,
int numCells,
int numericId,
UniformRandom* spkunif)
: CellLayer(sub, numericId, spkunif)
{
// Set initial values
_numInterneurons = numCells;
_interneurons = NULL;
_firingRate = 0;
_inactiveRate = 0;
}
InterneuronLayer::~InterneuronLayer()
{
int k;
// Delete the model here to speed up processingdeletemodel()->solver();
deletemodel();
_model = NULL;
// Delete allocated interneuronsif (_interneurons!=NULL) {
for (k=1;k<=numCells();k++) {
deleteinterneuron(k);
}
}
delete[] _interneurons;
}
// Allocate interneurons.voidInterneuronLayer::allocateInterneurons(){
int k;
// Make sure everything is ready to proceedif (_interneurons != NULL) {
FatalError("(InterneuronLayer::allocateInterneurons) ""Trying to allocate interneurons twice.");
return;
}
// Allocate and initialize interneurons
_interneurons = new Interneuron* [numCells()];
// Build the array of place cellsfor (k=1;k<=numCells();k++) {
_interneurons[k-1] = newInterneuron(k);
}
// Set all numeric identifiersnumericIdentifier( numericIdentifier() );
// Set all firing ratesfiringRate( firingRate() );
}
// Allocate a new interneuron.. This can be extended by // subclasses to reflect layer-specific properties.Interneuron* InterneuronLayer::newInterneuron(int k){
Interneuron* cell = newInterneuron(model(), this);
return cell;
}
// Access an interneuron by number (n=1 is the first)Interneuron* InterneuronLayer::interneuron(int n){
// Make sure n is validif (n<1 || n>numCells() ) {
FatalError("(InterneuronLayer::interneuron) Invalid cell number requested.");
returnNULL;
}
return _interneurons[n-1];
}
// --------------------------------------------------------------------// ConnectionPolicy class body// --------------------------------------------------------------------// Constructors and destructor
ConnectionPolicy::ConnectionPolicy(
CellLayer* source,
UniformRandom* randomizer)
{
usingnamespace UOM;
// Clear current target neuron pointer
_target = NULL;
// Save values provided
_layer = source;
_uniformRandom = randomizer;
// Set starting values for params.// These will be updated as needed in subclasses.
_minLaminarDist = 0;
_maxLaminarDist = 0;
_minBasalDist = 0;
_maxBasalDist = 0;
_enpassantDist = 0;
_minAxonDist = 100*micron;
_maxAxonDist = 100*micron;
_dendriteSynDensity = 0;
_somaSynDensity = 0;
_ISSynDensity = 0;
_synapseWeight = 1;
_synapseWeightAlt = 1;
_synapseType = NullTokenId;
_synapseTypeAlt = NullTokenId;
_connectOrder = sequentialOrder;
// Zero statistics counters
_totalSomaSynapses = 0;
_totalISSynapses = 0;
_totalAxonSynapses = 0;
_totalApicalSynapses = 0;
_totalBasalSynapses = 0;
// Initialize connection index
_nextToConnect = 1;
}
ConnectionPolicy::~ConnectionPolicy() {}
// Return total connected synapsesintConnectionPolicy::totalSynapses(){
return
_totalSomaSynapses +
_totalISSynapses +
_totalAxonSynapses +
_totalApicalSynapses +
_totalBasalSynapses;
}
// Print connection stats for debugvoidConnectionPolicy::printSynapseCounts(){
cerr<<"totalSomaSynapses = " <<totalSomaSynapses()<<endl
<<"totalISSynapses = " <<totalISSynapses()<<endl
<<"totalAxonSynapses = " <<totalAxonSynapses()<<endl
<<"totalDendriteSynapses = "
<<totalApicalSynapses()<<" Apical, "
<<totalBasalSynapses()<<" Basal, "
<<totalApicalSynapses()+totalBasalSynapses()<<" Total"<<endl;
}
// Connect the target neuron with the cell layervoidConnectionPolicy::connectWith(
MorphologicalNeuron* target,
ConnectionSequence connSeq){
// Save target and order for inner functions
_target = target;
_connectOrder = connSeq;
// Connect with various parts of the cellconnectWithSoma();
connectWithIS();
connectWithAxon();
connectWithDendrites();
// Clear the target pointer (just to be clean)
_target = NULL;
}
// Connect with the somavoidConnectionPolicy::connectWithSoma(){
int k,n;
for (k=1;k<=_target->numSomaComp();k++) {
Compartment* soma = _target->somaComp(k);
n= numberToConnect(soma,1,somaSynDensity() );
createSynapses(soma,n);
_totalSomaSynapses += n;
}
}
// Connect with the initial segmentvoidConnectionPolicy::connectWithIS(){
int k,n;
for (k=1;k<=_target->numISComp();k++) {
Compartment* IS = _target->ISComp(k);
n= numberToConnect(IS,1,ISSynDensity() );
createSynapses(IS,n);
_totalISSynapses += n;
}
}
// Connect with the dendritesvoidConnectionPolicy::connectWithDendrites(){
Compartment* pcomp;
int k;
// Start sequential connections with the first cell
_nextToConnect = 1;
// Make connections for each compartment in turnfor (k=1;k<=_target->numDendriteComp(); k++) {
Number f;
int nsyn;
// See how much of a compartment is in range.// Optimize performance when out of range.
f = dendriteFractionInRange(k, minLaminarDist(), maxLaminarDist() );
f += dendriteFractionInRange(k, minBasalDist(), maxBasalDist() );
if (f>0) {
// Create and connect synapses
pcomp = _target->dendriteComp(k);
nsyn = numberToConnect(pcomp, f, dendriteSynDensity() );
createSynapses(pcomp, nsyn);
// Count the synapsesif (_target->isApicalDendrite(k) ) {
_totalApicalSynapses += nsyn;
}
else {
_totalBasalSynapses += nsyn;
}
}
}
}
// Get an estimated fraction of a target dendrite compartment// within a pathway defined by a range of distances from the origin.Number ConnectionPolicy::dendriteFractionInRange(
int dendNbr, // number of target dendrite
Number minDist, // minimum distance
Number maxDist)// maximum distance {
usingnamespace UOM;
// If both min and max are the same then there is nothing to do.// This tyically arises when both are defaults (0).if (minDist==maxDist)
return0;
// Get values from the target neuron
Compartment* pDend = _target->dendriteComp(dendNbr);
MorphologyEntry* pMorph = _target->dendriteMorphologyEntry(dendNbr);
Number orX = _target->orientationX();
Number orY = _target->orientationY();
Number orZ = _target->orientationZ();
// Working variables
Number s1,s2; // distance orthogonal to layer
Number t1,t2; // distances trimmed to boundaries
Number xprox,yprox,zprox; // proximal end dendrite location
Number xdist,ydist,zdist; // distal end dendrite location
Number temp; // temporary register// Ensure that distance specs are right way around just// in case someone missed the lecture on negative numbers.if (minDist>maxDist) {
FatalError("(ConnectionPolicy::dendriteFractionInRange) ""Minimum laminar distance greater than maximum");
return0;
}
// Locate the proximal and distals ends of the compartment // assuming the dendrite is a perfect cylinder. Note that// the dx,dy,dz values of the compartment provide the// location of end points but do not imply the length of// the cylinder (dendrites are not really that straight).// Note that the morphology table is not converted for// units of measure and entries must be converted to microns.
xprox = pMorph->x*micron - pMorph->dx*micron/2;
yprox = pMorph->y*micron - pMorph->dy*micron/2;
zprox = pMorph->z*micron - pMorph->dz*micron/2;
xdist = pMorph->x*micron + pMorph->dx*micron/2;
ydist = pMorph->y*micron + pMorph->dy*micron/2;
zdist = pMorph->z*micron + pMorph->dz*micron/2;
// Using the orientation of the layer, get a distance// aligned with the layer assuming that the layer is// defined by parallel planes (another rough assumption).
s1 = orX*xprox + orY*yprox + orZ*zprox;
s2 = orX*xdist + orY*ydist + orZ*zdist;
// Get the fraction based on the distance along the axis// of the compartment that lies within the layer. Separate cases// are the easiest way to handle this logic.if (s1<minDist && s2<minDist) {
// Dendrite is entirely below the layerreturn0;
}
elseif (s1>maxDist && s2>maxDist) {
// Dendrite is entirely above the layerreturn0;
}
else {
// Dendrite is at least partially within the layerif (s2<s1) {
temp = s2;
s2 = s1;
s1 = temp;
}
t1=maxval(s1,minDist);
t2=minval(s2,maxDist);
// It is possible that the dendrite is exactly// parallel to the layer boundary. If so, handle// as a special case to avoid divide by 0. Otherwise// the membrane area is prorated based on the// fraction of the dendrite (as a straight line)// that is within the layer boundaries.if (s1==s2) {
return1;
}
else {
return (t2-t1)/(s2-s1);
}
}
}
// Find the number of synapses to connect. The number is derived// from a Poisson distribution with a mean of the value determined // by applying synapse density to the implied dendrite area.intConnectionPolicy::numberToConnect(
Compartment* pcomp, // target compartment
Number fraction, // fraction of comp in layer
Number density)// synapse density{
Number numExpected;
int numActual;
numExpected = fraction*density*pcomp->extraShellArea( enpassantDist() );
// Assuming connections occur uniformly, use random round to convert// to an integer number of connections. This avoids the possibility// of consistently rounding to zero or one for small compartments.
numActual = int( numExpected+uniformRandom()->next() );
return numActual;
}
// Create the necessary synapses by selecting at random from // source cells in the place cell layer.voidConnectionPolicy::createSynapses(
Compartment* pcomp, // comparmtent to connect withint numSynapses)// number of synapses{
SynapticResponse* resp1=NULL;
SynapticResponse* resp2=NULL;
Number len;
int k,n;
// If no connections are to be made stop here.// The required synaptic response may not even// exist in this compartment.if (numSynapses==0)
return;
// If connections are requested, make sure that cells // are defined for the source layer.if (numSynapses>0 && _layer->numCells()==0) {
FatalError("(ConnectionPolicy::createSynapses) No cells defined for layer.");
}
// Find the synaptic response(s) to be connected withif (synapseType()!=NullTokenId) {
resp1 = pcomp->findSynapticResponse(synapseType() );
}
if (synapseTypeAlt()!=NullTokenId) {
resp2 = pcomp->findSynapticResponse(synapseTypeAlt() );
}
// Create synapsesfor (k=1;k<=numSynapses;k++) {
// Use the appropriate method for picking a source neuronswitch (_connectOrder) {
case sequentialOrder:
if (_nextToConnect >= _layer->numCells() ) {
_nextToConnect = 1;
}
n = _nextToConnect++;
break;
case randomOrder:
n = int( _layer->numCells()*uniformRandom()->next()+1 );
break;
default:
FatalError("(ConnectionPolicy::createSynapses) Invalid connect order");
}
// Connect with the synaptic response(s)
len = axonLength(pcomp); // get a common lengthif (resp1!=NULL) {
resp1->createSynapse(_layer->cell(n)->axonProcess(),synapseWeight(),len);
}
if (resp2!=NULL) {
resp2->createSynapse(_layer->cell(n)->axonProcess(),synapseWeightAlt(),len);
}
}
}
// Select an axon length for the current connection.// By default this is a uniformly distributed random number// between axon min and max distances.Number ConnectionPolicy::axonLength(Compartment* pcomp){
// If min and max are the same, skip the random number// generation overhead. Otherwise generate a distance.returnminAxonDist()==maxAxonDist()
? minAxonDist()
: uniformRandom()->next(minAxonDist(), maxAxonDist());
}