Main Page | Namespace List | Alphabetical List | Class List | Directories | File List | Class Members | File Members

STDP1Neuron.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   SpikeStream STDP1 Neuron                                              *
00003  *   Copyright (C) 2007 by David Gamez                                     *
00004  *   david@davidgamez.eu                                                   *
00005  *   Version 0.1                                                           *
00006  *                                                                         *
00007  *   This program is free software; you can redistribute it and/or modify  *
00008  *   it under the terms of the GNU General Public License as published by  *
00009  *   the Free Software Foundation; either version 2 of the License, or     *
00010  *   (at your option) any later version.                                   *
00011  *                                                                         *
00012  *   This program is distributed in the hope that it will be useful,       *
00013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00015  *   GNU General Public License for more details.                          *
00016  *                                                                         *
00017  *   You should have received a copy of the GNU General Public License     *
00018  *   along with this program; if not, write to the                         *
00019  *   Free Software Foundation, Inc.,                                       *
00020  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
00021  ***************************************************************************/
00022 
00023 //SpikeStream includes
00024 #include "STDP1Neuron.h"
00025 #include "SimulationClock.h"
00026 #include "Debug.h"
00027 #include "STDP1Synapse.h"
00028 
00029 //Other includes
00030 #include <cmath>
00031 #include <iostream>
00032 using namespace std;
00033 
00034 
00035 //Defines for debugging
00036 //#define NEURON_MODEL_DEBUG
00037 //#define NEURON_METHOD_DEBUG
00038 //#define NEURON_PARAMETERS_DEBUG
00039 
00040 
00041 /*! Defines whether we sent membrane potential to SpikeStream Application
00042         to draw a real time graph. */
00043 #define MONITOR_MEMBRANE_POTENTIAL
00044 
00045 /*! Defines whether we sent calcium concentration to SpikeStream Application
00046         to draw a real time graph. */
00047 #define MONITOR_CALCIUM_CONC
00048 
00049 
00050 //List static variables here so they can be accessed and initialise them
00051 double STDP1Neuron::threshold = 0.0;
00052 double STDP1Neuron::refractoryPeriod_millisec = 0.0;
00053 double STDP1Neuron::membraneTimeConstant_millisec = 0.0;
00054 double STDP1Neuron::refractoryParam_m = 0.0;
00055 double STDP1Neuron::refractoryParam_n = 0.0;
00056 bool STDP1Neuron::learningMode = false;
00057 double STDP1Neuron::calciumIncreaseAmnt = 0.0;
00058 double STDP1Neuron::calciumDecayRate = 0.0;
00059 double STDP1Neuron::minPostsynapticPotential = 0.0;
00060 
00061 
00062 /*! Function used to create a Neuron class when library is dynamically loaded. */
00063 extern "C" {
00064         Neuron* getClass(){
00065                 return new STDP1Neuron;
00066         }
00067 }
00068 
00069 
00070 /*! Constructor. */
00071 STDP1Neuron::STDP1Neuron() : Neuron() {
00072         //Initialise dynamic variables and set default values
00073         pspTotal = 0;
00074         membranePotential = 0;
00075         currentTime = 0.0;
00076         lastUpdateTime = 0.0;
00077         timeSinceLastFire = 0.0;
00078         oldLearningMode = false;
00079         calciumConc = 0.0;
00080         finalStateUpdateTime = -1.0;
00081 
00082         //Initialise MonitorData structure depending on what we are monitoring
00083         int count = 0;
00084         #ifdef MONITOR_MEMBRANE_POTENTIAL
00085                 count++;
00086         #endif//MONITOR_MEMBRANE_POTENTIAL
00087         #ifdef MONITOR_CALCIUM_CONC
00088                 count++;
00089         #endif//MONITOR_CALCIUM_CONC
00090 
00091         monitorData.dataArray = new double[count];
00092         monitorData.length = count;
00093 }
00094 
00095 
00096 /*! Destructor. */
00097 STDP1Neuron::~STDP1Neuron(){
00098         #ifdef MEMORY_DEBUG
00099                 cout<<"DELETING STDP1 NEURON"<<endl;
00100         #endif//MEMORY_DEBUG
00101 
00102         delete [] monitorData.dataArray;
00103 }
00104 
00105 
00106 //---------------------------------------------------------------------------------------
00107 //-------------------------------- PUBLIC METHODS ---------------------------------------
00108 //---------------------------------------------------------------------------------------
00109 
00110 /*! Called to calculate the final membrane state and decide whether the neuron should fire or not. 
00111         In event-based updating mode, this has to be called after all spikes have been received because 
00112         a set of excitatory messages could be received from one layer and later a set of inhibitory messages 
00113         from another layer. In full neuron update mode, this method is called at each timestep. */
00114 void STDP1Neuron::calculateFinalState(){
00115         #ifdef NEURON_MODEL_DEBUG
00116                 cout<<"STDP1Neuron ["<<neuronID<<"]: Calculating final state."<<endl;
00117         #endif//NEURON_METHOD_DEBUG
00118 
00119         //Update the neuron if it has not been updated already
00120         update();
00121 
00122         //Calculate the membrane potential taking refractory period into account
00123         calculateMembranePotential();
00124 
00125         /* Call all the synapses that connect to this neuron and instruct them to carry out STDP learning
00126                 This needs to be carried out at this point after all the spikes have arrived in the timestep
00127                 and the membrane potential is final for this timestep and before the neuron fires, which
00128                 reduces the membrane potential back to zero. Synapses do not connect to more than one neuron
00129                 so there is no danger of calling the same method twice on the same synapse. This method will not
00130                 be called on all synapses in a layer if all the neurons are not updated in a layer. */
00131         if(learningMode){
00132                 for(vector<void*>::iterator iter = preSynapseVector.begin(); iter != preSynapseVector.end(); ++iter){
00133                         ((STDP1Synapse*)*iter)->updateWeight(membranePotential, calciumConc);
00134                 }
00135         }
00136 
00137         //Fire neuron if membrane potential is greater than the threshold
00138         if(membranePotential > threshold){
00139                 //Call method to send spikes etc.
00140                 fireNeuron();
00141 
00142                 //Once neuron has fired, it clears its history
00143                 pspTotal = 0;
00144                 membranePotential = 0;
00145 
00146                 //Increase calcium concentration
00147                 calciumConc += calciumIncreaseAmnt;
00148         }
00149 
00150         /* Record time at which final state has been calculated. This enables us to call this method
00151                 when requesting monitor data so that the monitor data is up to date. */
00152         finalStateUpdateTime = simulationClock->getSimulationTime();
00153 }
00154 
00155 
00156 /*! Called by a synapse when it receives a spike and decides to pass it on to the neuron 
00157         Will be using dynamic synapses, so spikes may not always be passed on. */
00158 void STDP1Neuron::changePostSynapticPotential(double amount, unsigned int preSynapticNeuronID){
00159 
00160         //Update any changes in the neuron's state.
00161         update();
00162 
00163         /* Add new spike to the total. 
00164                 This spike has just arrived, so it has not decayed and is multiplied by 1 */
00165         pspTotal += amount;
00166 
00167         /* Make sure that this has not made the pspTotal less than the minimum */
00168         if(pspTotal < minPostsynapticPotential)
00169                 pspTotal = minPostsynapticPotential;
00170 
00171         #ifdef NEURON_MODEL_DEBUG
00172                 cout<<"STDP1Neuron ["<<neuronID<<"]: Changing post synaptic potential at time "<<simulationClock->getSimulationTime()<<" with timestep "<<simulationClock->getTimeStep()<<". pspTotal = "<<pspTotal<<endl;
00173         #endif//NEURON_METHOD_DEBUG
00174 }
00175 
00176 
00177 /*! Returns a description of this class for debugging purposes. 
00178         Invoking method has the responsibility of deleting the description string. */
00179 const string* STDP1Neuron::getDescription(){
00180         string* tempstr = new string("STDP1 Neuron");
00181         return tempstr;
00182 }
00183 
00184 
00185 /*! Returns a NeuronData structure containing a pointer to an array containing the current values 
00186         of the monitored data items in the same order as they were listed in the XML file. */
00187 MonitorData* STDP1Neuron::getMonitoringData(){
00188 
00189         //Check that the final state has been calculated for this neuron
00190         if(finalStateUpdateTime != simulationClock->getSimulationTime())
00191                 calculateFinalState();
00192 
00193         #ifdef MONITOR_MEMBRANE_POTENTIAL
00194                 monitorData.dataArray[0] = membranePotential;
00195         #endif//MONITOR_MEMBRANE_POTENTIAL
00196 
00197         #ifdef MONITOR_CALCIUM_CONC
00198                 monitorData.dataArray[1] = calciumConc;
00199         #endif//MONITOR_CALCIUM_CONC
00200 
00201         return &monitorData;
00202 }
00203 
00204 
00205 /*! Returns a string containing the data that is output by this neuron in monitoring mode in XML format. */ 
00206 string STDP1Neuron::getMonitoringInfo(){
00207         string xmlString = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>";
00208         xmlString += "<monitor_info>";
00209 
00210         #ifdef MONITOR_MEMBRANE_POTENTIAL
00211                 xmlString += "<data><description>Membrane Potential</description><range_high>2</range_high><range_low>-8</range_low></data>";
00212         #endif//MONITOR_MEMBRANE_POTENTIAL
00213 
00214         #ifdef MONITOR_CALCIUM_CONC
00215                 xmlString += "<data><description>Calcium Concentration</description><range_high>50</range_high><range_low>0</range_low></data>";
00216         #endif//MONITOR_CALCIUM_CONC
00217 
00218         xmlString += "</monitor_info>";
00219         return xmlString;
00220 }
00221 
00222 
00223 /*! Called when the parameters have been statically changed. 
00224         In this neuron implementation it resets the calcium to zero when it is turned on. */
00225 void STDP1Neuron::parametersChanged(){
00226         #ifdef NEURON_METHOD_DEBUG
00227                 cout<<"STDP1Neuron ["<<neuronID<<"]: Parameters changed."<<endl;//Should be called on every neuron
00228         #endif//NEURON_METHOD_DEBUG
00229 
00230         //Reset calcium concentration if learning mode is being turned on
00231         if(learningMode == true && oldLearningMode == false){//Learning is being turned on
00232                 calciumConc = 0.0;//Reset calcium concentration
00233         }
00234         oldLearningMode = learningMode;
00235 }
00236 
00237 
00238 /*! Sets the neuron parameters. */
00239 bool STDP1Neuron::setParameters(map<string, double> paramMap){
00240         #ifdef NEURON_METHOD_DEBUG
00241                 cout<<"STDP1Neuron ["<<neuronID<<"]: Setting parameters"<<endl;//Should be called on a single neuron
00242         #endif//NEURON_METHOD_DEBUG
00243 
00244         if(paramMap.count(string("Threshold")))
00245                 threshold = paramMap[string("Threshold")];
00246         else{
00247                 cerr<<"STDP1Neuron ["<<neuronID<<"]: CANNOT FIND \"Threshold\" IN PARAMETER MAP."<<endl;
00248                 return false;
00249         }
00250 
00251         if(paramMap.count(string("RefractoryPeriod")))
00252                 refractoryPeriod_millisec = paramMap[string("RefractoryPeriod")];
00253         else{
00254                 cerr<<"STDP1Neuron ["<<neuronID<<"]: CANNOT FIND \"RefractoryPeriod\" IN PARAMETER MAP"<<endl;
00255                 return false;
00256         }
00257 
00258         if(paramMap.count(string("MembraneTimeConstant")))
00259                 membraneTimeConstant_millisec = paramMap[string("MembraneTimeConstant")];
00260         else{
00261                 cerr<<"STDP1Neuron ["<<neuronID<<"]: CANNOT FIND \"MembraneTimeConstant\" IN PARAMETER MAP"<<endl;
00262                 return false;
00263         }
00264 
00265         if(paramMap.count(string("RefractoryParamM")))
00266                 refractoryParam_m = paramMap[string("RefractoryParamM")];
00267         else{
00268                 cerr<<"STDP1Neuron ["<<neuronID<<"]: CANNOT FIND \"RefractoryParamM\" IN PARAMETER MAP"<<endl;
00269                 return false;
00270         }
00271 
00272         if(paramMap.count(string("RefractoryParamN")))
00273                 refractoryParam_n = paramMap[string("RefractoryParamN")];
00274         else{
00275                 cerr<<"STDP1Neuron ["<<neuronID<<"]: CANNOT FIND \"RefractoryParamN\" IN PARAMETER MAP"<<endl;
00276                 return false;
00277         }
00278 
00279         if(paramMap.count(string("CalciumIncreaseAmnt")))
00280                 calciumIncreaseAmnt = paramMap[string("CalciumIncreaseAmnt")];
00281         else{
00282                 cerr<<"STDP1Neuron ["<<neuronID<<"]: CANNOT FIND \"CalciumIncreaseAmnt\" IN PARAMETER MAP"<<endl;
00283                 return false;
00284         }
00285 
00286         if(paramMap.count(string("CalciumDecayRate")))
00287                 calciumDecayRate = paramMap[string("CalciumDecayRate")];
00288         else{
00289                 cerr<<"STDP1Neuron ["<<neuronID<<"]: CANNOT FIND \"CalciumDecayRate\" IN PARAMETER MAP"<<endl;
00290                 return false;
00291         }
00292 
00293         if(paramMap.count(string("MinPostsynapticPotential")))
00294                 minPostsynapticPotential = paramMap[string("MinPostsynapticPotential")];
00295         else{
00296                 cerr<<"STDP1Neuron ["<<neuronID<<"]: CANNOT FIND \"MinPostsynapticPotential\" IN PARAMETER MAP"<<endl;
00297                 return false;
00298         }
00299 
00300         if(paramMap.count(string("Learning"))){
00301                 if(paramMap[string("Learning")] == 1.0)
00302                         learningMode = true;
00303                 else if(paramMap[string("Learning")] == 0.0)
00304                         learningMode = false;
00305                 else{
00306                         cerr<<"STDP1Neuron ["<<neuronID<<"]: \"Learning\" HAS INCORRECT VALUE IN PARAMETER MAP: "<<paramMap[string("Learning")]<<endl;
00307                         return false;
00308                 }
00309         }
00310         else{
00311                 cerr<<"STDP1Neuron ["<<neuronID<<"]: CANNOT FIND Learning IN PARAMETER MAP"<<endl;
00312                 return false;
00313         }
00314 
00315         #ifdef NEURON_PARAMETERS_DEBUG
00316                 printParameters();
00317         #endif//NEURON_PARAMETERS_DEBUG
00318 
00319         //All parameters have been set ok
00320         return true;
00321 }
00322 
00323 
00324 //----------------------------------------------------------------------------------------
00325 //----------------------------------- PRIVATE METHODS ------------------------------------
00326 //----------------------------------------------------------------------------------------
00327 
00328 /*! Calculates the current state of the membrane potential, taking the refractory period into
00329         account. This is generally called just before calculating the final state of the neuron
00330         to evaluate whether it should fire, but in learning mode, need to calculate the membrane
00331         potential prior to processing the spike if it is a voltage dependent learning rule.
00332         NOTE this may create problems if a large number of positive synapses are always processed
00333         prior to a large number of negative synapses. */
00334 void STDP1Neuron::calculateMembranePotential(){
00335         #ifdef NEURON_METHOD_DEBUG
00336                 cout<<"STDP1Neuron ["<<neuronID<<"]:Calculating membrane potential."<<endl;
00337         #endif//NEURON_METHOD_DEBUG
00338 
00339         /* In refractory period neuron cannot fire so membrane potential is zero
00340                 Ignoring spikes that arrive within refractory period by resetting pspTotal. */
00341         if(timeSinceLastFire <= refractoryPeriod_millisec){
00342                 #ifdef NEURON_MODEL_DEBUG
00343                         cout<<"Time "<<currentTime<<" In refractory period: NeuronID = "<<neuronID<<"; timeSinceLastFire = "<<timeSinceLastFire<<endl;
00344                 #endif//NEURON_MODEL_DEBUG
00345 
00346                 //Reset state of neuron during refractory period
00347                 pspTotal = 0;
00348                 membranePotential = 0;
00349 
00350                 return;
00351         }
00352 
00353         /* Calculate membrane potential. This is the total contributions of previous spikes combined
00354                 with the refractory nature of the neuron */
00355         membranePotential  = pspTotal - exp ( refractoryParam_n - pow(timeSinceLastFire, refractoryParam_m));
00356 
00357 
00358         //Output debugging information
00359         #ifdef NEURON_MODEL_DEBUG
00360                 cout<<"Time "<<currentTime<<" Calculating membrane potential: NeuronID = "<<neuronID<<"; timeSinceLastFire = "<<timeSinceLastFire<<" pspTotal = " <<pspTotal<<" membranePotential = "<<membranePotential<<"; refractory period = "<<refractoryPeriod_millisec<<"; param N = "<<refractoryParam_n<<" param M = "<<refractoryParam_m<<"; threshold = "<<threshold;
00361                 cout<<" Last update time "<<lastUpdateTime<<" current time "<<currentTime<<endl;
00362         #endif//NEURON_MODEL_DEBUG
00363 }
00364 
00365 
00366 /*! Prints out the parameters for debugging. */
00367 void STDP1Neuron::printParameters(){
00368         cout<<"============================ STDP1 PARAMETERS =================================="<<endl;
00369         cout<<"Threshold = "<<threshold<<endl;
00370         cout<<"Refractory period = "<<refractoryPeriod_millisec<<endl;
00371         cout<<"Membrane time constant = "<<membraneTimeConstant_millisec<<endl;
00372         cout<<"Refractory parameter M = "<<refractoryParam_m<<endl;
00373         cout<<"Refractory parameter N = "<<refractoryParam_n<<endl;
00374         cout<<"CalciumIncreaseAmnt = "<<calciumIncreaseAmnt<<endl;
00375         cout<<"CalciumDecayRate = "<<calciumDecayRate<<endl;
00376         cout<<"Learning mode = "<<learningMode<<endl;
00377         cout<<"================================================================================="<<endl;
00378 }
00379 
00380 
00381 /*! Called to retrospectively calculate how the neuron has changed since its last update.
00382         Should be called before carrying out any changes on the neuron's state and before
00383         reporting any parameters of the neuron. */
00384 void STDP1Neuron::update(){
00385         #ifdef NEURON_METHOD_DEBUG
00386                 cout<<"STDP1Neuron ["<<neuronID<<"]: Updating."<<endl;
00387         #endif//NEURON_METHOD_DEBUG
00388 
00389         //Update the current time
00390         currentTime = simulationClock->getSimulationTime();
00391 
00392         if(lastUpdateTime == currentTime)//Check to see if neuron has been updated already
00393                 return;
00394 
00395 
00396         /* Update the time interval since the neuron last fired */
00397         timeSinceLastFire = currentTime - neuronFireTime;
00398         //In debug mode run an extra check that the time interval is not negative
00399         #ifdef NEURON_MODEL_DEFBUG
00400                 if(timeSinceLastFire < 0.0){
00401                         cerr<<"STDP1Neuron ["<<neuronID<<"]: TIME SINCE LAST NEURON FIRE SHOULD NOT BE LESS THAN ZERO"<<endl;
00402                         return;
00403                 }
00404         #endif//NEURON_MODEL_DEBUG
00405 
00406 
00407         /* Decay post synaptic total from spikes received in previous time steps
00408                 -1 * (currentTime - lastUpdateTime) is the same as (lastUpdateTime - currentTime), 
00409                 which should be a negative number */
00410         pspTotal = pspTotal * exp( (lastUpdateTime - currentTime) / membraneTimeConstant_millisec );
00411 
00412         //Calculate the current calcium potential
00413         calciumConc *= exp( (lastUpdateTime - currentTime) / calciumDecayRate );
00414 
00415         //Record the last update time
00416         lastUpdateTime = currentTime;
00417 }
00418 
00419 

Generated on Mon Sep 3 22:33:18 2007 for STDP1 Neuron by  doxygen 1.4.4