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

STDP1Synapse.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   SpikeStream STDP1 Synapse                                             *
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 "STDP1Synapse.h"
00025 #include "Debug.h"
00026 #include "SimulationClock.h"
00027 
00028 //Other includes
00029 #include <cmath>
00030 #include <iostream>
00031 using namespace std;
00032 
00033 
00034 //Defines used for debuggging
00035 //#define SYNAPSE_PARAMETERS_DEBUG
00036 //#define LEARNING_DEBUG
00037 
00038 
00039 /*! When this is defined the synapse's weight can be sent to the SpikeStream
00040         Application to draw a live graph. */
00041 #define MONITOR_WEIGHT
00042 
00043 
00044 // Initialise and declare static variables
00045 const string STDP1Synapse::calciumThreshUpLow("CalciumThreshUpLow");
00046 const string STDP1Synapse::calciumThreshUpHigh("CalciumThreshUpHigh");
00047 const string STDP1Synapse::calciumThreshDownLow("CalciumThreshDownLow");
00048 const string STDP1Synapse::calciumThreshDownHigh("CalciumThreshDownHigh");
00049 const string STDP1Synapse::weightChangeThreshold("WeightChangeThreshold");
00050 const string STDP1Synapse::weightIncreaseAmnt("WeightIncreaseAmnt");
00051 const string STDP1Synapse::weightDecreaseAmnt("WeightDecreaseAmnt");
00052 const string STDP1Synapse::driftThreshold("DriftThreshold");
00053 const string STDP1Synapse::positiveDrift("PositiveDrift");
00054 const string STDP1Synapse::negativeDrift("NegativeDrift");
00055 const string STDP1Synapse::maximumDrift("MaximumDrift");
00056 const string STDP1Synapse::minimumDrift("MinimumDrift");
00057 const string STDP1Synapse::learning("Learning");
00058 const string STDP1Synapse::disable("Disable");
00059 
00060 
00061 /*! Function used to create a STDP1Synapse class when library is dynamically loaded. */
00062 extern "C" {
00063         Synapse* getClass(){
00064                 return new STDP1Synapse;
00065         }
00066 }
00067 
00068 
00069 /*! Constructor. */
00070 STDP1Synapse::STDP1Synapse() : Synapse() {
00071         //Initialise variables
00072         spikeTimeStep = -1;
00073         lastUpdateTime = 0.0;
00074         oldLearningMode = false;
00075 
00076         //Initialise MonitorData structure depending on what we are monitoring
00077         int count = 0;
00078         #ifdef MONITOR_WEIGHT
00079                 count++;
00080         #endif//MONITOR_WEIGHT
00081 
00082         monitorData.dataArray = new double[count];
00083         monitorData.length = count;
00084 }
00085 
00086 
00087 /*! Destructor. */
00088 STDP1Synapse::~STDP1Synapse(){
00089         #ifdef MEMORY_DEBUG
00090                 cout<<"DESTROYING STDPSynapse."<<endl;
00091         #endif//MEMORY_DEBUG
00092 
00093         delete [] monitorData.dataArray;
00094 }
00095 
00096 
00097 //---------------------------------------------------------------------------------------
00098 //-------------------------------- PUBLIC METHODS ---------------------------------------
00099 //---------------------------------------------------------------------------------------
00100 
00101 /*! Needs to be implemented by all classes inheriting from Synapse.
00102         Calls calculate weight, which updates the weight in learning mode. 
00103         The weight will already have been updated if a spike has been received during
00104         this time step.*/
00105 void STDP1Synapse::calculateFinalState(){
00106         if((*parameterMap)[learning] == 1.0)
00107                 calculateWeight(false);
00108 }
00109 
00110 
00111 /*! Returns a description of this class for debugging purposes.
00112         Invoking method has the responsibility of deleting the description string. */
00113 const string* STDP1Synapse::getDescription(){
00114         string* tempstr = new string("STDP1 Synapse");
00115         return tempstr;
00116 }
00117 
00118 
00119 /*! Returns a NeuronData structure containing a pointer to an array containing the current values 
00120         of the monitored data items in the same order as they were listed in the XML file. */
00121 MonitorData* STDP1Synapse::getMonitoringData(){
00122         /* Update any variables that change continuously over time. All of the spike processing and
00123                 updating should have been completed by this point, but in event mode the synapse will 
00124                 not have been updated if it has not received a spike*/
00125         if((*parameterMap)[learning] == 1.0)
00126                 calculateWeight(false);
00127 
00128         #ifdef MONITOR_WEIGHT
00129                 monitorData.dataArray[0] = weight;
00130         #endif//MONITOR_WEIGHT
00131 
00132         return &monitorData;
00133 }
00134 
00135 
00136 /*! Returns a string containing the data that is output by this neuron in monitoring mode in XML format. */ 
00137 string STDP1Synapse::getMonitoringInfo(){
00138         string xmlString = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>";
00139         xmlString += "<monitor_info>";
00140 
00141         #ifdef MONITOR_WEIGHT
00142                 xmlString += "<data><description>Weight</description><range_high>1.0</range_high><range_low>-1.0</range_low></data>";
00143         #endif//MONITOR_WEIGHT
00144 
00145         xmlString += "</monitor_info>";
00146         return xmlString;
00147 }
00148 
00149 
00150 /*! Returns the weight as a short between -127 and 127. */
00151 short STDP1Synapse::getShortWeight(){
00152         /* Update any variables that change continuously over time. */
00153         if((*parameterMap)[learning] == 1.0)
00154                 calculateWeight(false);
00155 
00156         //Convert weight to a number between -127 and 127
00157         short shortWeight = (short)rint(weight * 127.0);
00158 
00159         //Run an extra check in debug mode
00160         #ifdef SAVE_WEIGHTS_DEBUG
00161                 cout<<"STDP1Synapse ["<<preSynapticNeuronID<<", "<<postSynapticNeuron->getNeuronID()<<"]: Saving short weight. Weight = "<<weight<<"; short weight = "<<shortWeight<<endl;
00162                 if(shortWeight > MAX_SHORT_WEIGHT || shortWeight < MIN_SHORT_WEIGHT){
00163                         cerr<<"STDP1Synapse ["<<preSynapticNeuronID<<", "<<postSynapticNeuron->getNeuronID()<<"]: BYTE WEIGHT OUT OF RANGE: "<<shortWeight<<endl;
00164                         return 0;
00165                 }
00166         #endif//SAVE_WEIGHTS_DEBUG
00167 
00168         return shortWeight;
00169 }
00170 
00171 
00172 /*! Returns the weight as a double. */
00173 double STDP1Synapse::getWeight(){
00174         /* Update any variables that change continuously over time. */
00175         if((*parameterMap)[learning] == 1.0)
00176                 calculateWeight(false);//Take account of the current time step because no spike has been received
00177 
00178         return weight;
00179 }
00180 
00181 
00182 /*! Called when the parameters have been changed.
00183         This method checks that the new parameters in the parameter map make sense. 
00184         NOTE May need to check for learning mode being switched on and off. */
00185 bool STDP1Synapse::parametersChanged(){
00186         if(checkParameters()){
00187                 if(!oldLearningMode && (*parameterMap)[learning] == 1.0){//Learning has been switched on
00188                         lastUpdateTime = simulationClock->getSimulationTime();//Bring last update time up to date so that the weight drift is not calculated for the period when learning was switched off
00189                         oldLearningMode = true;
00190                 }
00191                 else if (oldLearningMode && (*parameterMap)[learning] == 0.0){//Learning has been switched off
00192                         calculateWeight(false);//Bring weight up to date before learning is switched off
00193                         oldLearningMode = false;
00194                 }
00195                 return true;//Don't care if learning mode has stayed the same
00196         }
00197         else{
00198                 return false;
00199         }
00200 }
00201 
00202 
00203 /*! Processes an incoming spike from the presynaptic neuron. */
00204 void STDP1Synapse::processSpike(){
00205         //First check to see if synapse is disabled.
00206         if((*parameterMap)[disable] == 1.0)
00207                 return;
00208 
00209         /* In learning mode the spike may have parameters that change over time and need to be updated.
00210                 Will also need to record the time step at which the spike was received to save updating 
00211                 every synapse every time updateWeight is called by a neuron that has received a spike. */
00212         if((*parameterMap)[learning] == 1.0){
00213                 /* Update any variables that change continuously over time. */
00214                 calculateWeight(true);//Update the weight, but exclude the current time step because a spike has been received
00215         
00216                 //Record the time step of the spike
00217                 spikeTimeStep = simulationClock->getTimeStep();
00218         }
00219 
00220         //Alter the postsynaptic potential
00221         postSynapticNeuron->changePostSynapticPotential(weight, preSynapticNeuronID);
00222 }
00223 
00224 
00225 /*! Debugging method called by neuron to check that all the linking is working ok. */
00226 bool STDP1Synapse::testFunction(){
00227         cout<<"STDP1Synapse ["<<preSynapticNeuronID<<", "<<postSynapticNeuron->getNeuronID()<<"]: Test function called. "<<endl;
00228         return true;
00229 }
00230 
00231 
00232 /*! Called by the neuron during learning mode when all spikes have been received.
00233         Alters the weight depending on the membrane potential and the calcium concentration. */
00234 void STDP1Synapse::updateWeight(double membranePotential, double calciumConc){
00235         #ifdef LEARNING_DEBUG
00236                 cout<<"STDP1Synapse ["<<preSynapticNeuronID<<", "<<postSynapticNeuron->getNeuronID()<<"]: Updating weight."<<endl;
00237         #endif//LEARNING_DEBUG
00238 
00239         /* NOTE May need to update here if this contains any
00240                 variables that change continuously over time. */
00241 
00242         if((*parameterMap)[learning] == 1.0){//Check that synapse is in learning mode
00243 
00244                 //Only want to update synapses that have received a spike in this timestep
00245                 if(spikeTimeStep != simulationClock->getTimeStep()){
00246                         #ifdef LEARNING_DEBUG
00247                                 cout<<"STDP1Synapse ["<<preSynapticNeuronID<<", "<<postSynapticNeuron->getNeuronID()<<"]: Spike has not received a spike. Applying positive or negative drift and returning."<<endl;
00248                         #endif//LEARNING_DEBUG
00249                         return;
00250                 }
00251 
00252                 /* The weight should be up to date at this point since we are only running this calculation on synapses that have received
00253                         a spike and the process spike method updates the weight. */
00254 
00255                 //Change weight depending on the value of the membrane potential and calcium concentration
00256                 if(membranePotential > (*parameterMap)[weightChangeThreshold]){//Increase weight
00257                         if(calciumConc > (*parameterMap)[calciumThreshUpLow] && calciumConc < (*parameterMap)[calciumThreshUpHigh]){
00258                                 //Increase weight
00259                                 weight += (*parameterMap)[weightIncreaseAmnt];
00260                                 normaliseWeight();
00261                                 return;
00262                         }
00263                 }
00264                 else{//Decrease weight if calcium is within range
00265                         if(calciumConc > (*parameterMap)[calciumThreshDownLow] && calciumConc < (*parameterMap)[calciumThreshDownHigh]){
00266                                 //Decrease weight
00267                                 weight -= (*parameterMap)[weightDecreaseAmnt];
00268                                 normaliseWeight();
00269                                 return;
00270                         }
00271                 }
00272 
00273                 //If we have reached this point, none of the conditions for changing the weight will have applied, so drift positive or negative
00274                 if(weight > (*parameterMap)[driftThreshold] ){
00275                         weight += (*parameterMap)[positiveDrift] * simulationClock->getTimeStepDuration_ms();//Drifts by positive drift each ms, so multiply by the time step duration
00276                         if(weight > (*parameterMap)[maximumDrift])//Prevent weight from exceeding the maximum, which may not be the same as the maximum weight
00277                                 weight = (*parameterMap)[maximumDrift];
00278                 }
00279                 else{
00280                         weight -= (*parameterMap)[negativeDrift] * simulationClock->getTimeStepDuration_ms();//Drifts by positive drift each ms, so multiply by the time step duration
00281                         if(weight < (*parameterMap)[minimumDrift])//Prevent weight from exceeding the minimum, which may not be the same as the minimum weight
00282                                 weight = (*parameterMap)[minimumDrift];
00283                 }
00284 
00285                 //Normalise the weight in case the drift parameters have been set incorrectly.
00286                 normaliseWeight();
00287         }
00288 }
00289 
00290 
00291 //----------------------------------------------------------------------------------------
00292 //----------------------------------- PRIVATE METHODS ------------------------------------
00293 //----------------------------------------------------------------------------------------
00294 
00295 /*! Calculates the current state of the weight. Each time step the weight drifts by a certain amount
00296         but to save calculating this all the time, it is only calculated when a spike is received or when
00297         the weights are viewed or saved or when the synapse is in full update mode.
00298         When a spike is received by this synapse we do not want to drift the weight for the current
00299         time step since the weight will be updated elsewhere, so only calculate the weight change
00300         for the intervening time steps when a spike was not received and the synapse was not updated.
00301         NOTE: This method should only be called in learning mode. */
00302 void STDP1Synapse::calculateWeight(bool spikeReceived){
00303         //Update the current time
00304         currentTime = simulationClock->getSimulationTime();
00305 
00306         if(lastUpdateTime == currentTime)//Check to see if neuron has been updated already
00307                 return;
00308 
00309         if(weight > (*parameterMap)[driftThreshold] ){
00310                 if(spikeReceived)//Do not drift the weight for the current time step because a spike has been received
00311                         weight += (*parameterMap)[positiveDrift] * (currentTime - lastUpdateTime - simulationClock->getTimeStepDuration_ms());
00312                 else//Drift weight for the current time step as well
00313                         weight += (*parameterMap)[positiveDrift] * (currentTime - lastUpdateTime);
00314 
00315                 //Prevent weight from exceeding the maximum, which may not be the same as the maximum weight
00316                 if(weight > (*parameterMap)[maximumDrift])
00317                         weight = (*parameterMap)[maximumDrift];
00318         }
00319         else{
00320                 if(spikeReceived)//Do not drift the weight for the current time step because a spike has been received
00321                         weight -= (*parameterMap)[negativeDrift] * (currentTime - lastUpdateTime - simulationClock->getTimeStepDuration_ms());
00322                 else//Drift weight for the current time step as well
00323                         weight -= (*parameterMap)[negativeDrift] * (currentTime - lastUpdateTime);
00324 
00325                 //Prevent weight from exceeding the minimum, which may not be the same as the minimum weight
00326                 if(weight < (*parameterMap)[minimumDrift])
00327                         weight = (*parameterMap)[minimumDrift];
00328         }
00329 
00330         //Normalise the weight in case the drift parameters have been set incorrectly.
00331         normaliseWeight();
00332 
00333         //Record the last update time
00334         lastUpdateTime = currentTime;
00335 }
00336 
00337 
00338 /*! Check that all the necessary parameters are in the map. */
00339 bool STDP1Synapse::checkParameters(){
00340 
00341         if(parameterMap->count(calciumThreshUpLow) != 1){
00342                 cerr<<"STDP1Synapse ["<<preSynapticNeuronID<<", "<<postSynapticNeuron->getNeuronID()<<"]: PARAMETER calciumThreshUpLow NOT FOUND WITH KEY \""<<calciumThreshUpLow<<"\""<<endl;
00343                 return false;
00344         }
00345 
00346         if(parameterMap->count(calciumThreshUpHigh) != 1){
00347                 cerr<<"STDP1Synapse ["<<preSynapticNeuronID<<", "<<postSynapticNeuron->getNeuronID()<<"]: PARAMETER calciumThreshUpHigh NOT FOUND WITH KEY \""<<calciumThreshUpHigh<<"\""<<endl;
00348                 return false;
00349         }
00350 
00351         if(parameterMap->count(calciumThreshDownLow) != 1){
00352                 cerr<<"STDP1Synapse ["<<preSynapticNeuronID<<", "<<postSynapticNeuron->getNeuronID()<<"]: PARAMETER calciumThreshDownLow NOT FOUND WITH KEY \""<<calciumThreshDownLow<<"\""<<endl;
00353                 return false;
00354         }
00355 
00356         if(parameterMap->count(calciumThreshDownHigh) != 1){
00357                 cerr<<"STDP1Synapse ["<<preSynapticNeuronID<<", "<<postSynapticNeuron->getNeuronID()<<"]: PARAMETER calciumThreshDownHigh NOT FOUND WITH KEY \""<<calciumThreshDownHigh<<"\""<<endl;
00358                 return false;
00359         }
00360 
00361         if(parameterMap->count(weightChangeThreshold) != 1){
00362                 cerr<<"STDP1Synapse ["<<preSynapticNeuronID<<", "<<postSynapticNeuron->getNeuronID()<<"]: PARAMETER weightChangeThreshold NOT FOUND WITH KEY \""<<weightChangeThreshold<<"\""<<endl;
00363                 return false;
00364         }
00365 
00366         if(parameterMap->count(weightIncreaseAmnt) != 1){
00367                 cerr<<"STDP1Synapse ["<<preSynapticNeuronID<<", "<<postSynapticNeuron->getNeuronID()<<"]: PARAMETER weightIncreaseAmnt NOT FOUND WITH KEY "<<weightIncreaseAmnt<<endl;
00368                 return false;
00369         }
00370 
00371         if(parameterMap->count(weightDecreaseAmnt) != 1){
00372                 cerr<<"STDP1Synapse ["<<preSynapticNeuronID<<", "<<postSynapticNeuron->getNeuronID()<<"]: PARAMETER weightDecreaseAmnt NOT FOUND WITH KEY \""<<weightDecreaseAmnt<<"\""<<endl;
00373                 return false;
00374         }
00375 
00376         if(parameterMap->count(driftThreshold) != 1){
00377                 cerr<<"STDP1Synapse ["<<preSynapticNeuronID<<", "<<postSynapticNeuron->getNeuronID()<<"]: PARAMETER driftThreshold NOT FOUND WITH KEY \""<<driftThreshold<<"\""<<endl;
00378                 return false;
00379         }
00380 
00381         if(parameterMap->count(positiveDrift) != 1){
00382                 cerr<<"STDP1Synapse ["<<preSynapticNeuronID<<", "<<postSynapticNeuron->getNeuronID()<<"]: PARAMETER positiveDrift NOT FOUND WITH KEY \""<<positiveDrift<<"\""<<endl;
00383                 return false;
00384         }
00385 
00386         if(parameterMap->count(negativeDrift) != 1){
00387                 cerr<<"STDP1Synapse ["<<preSynapticNeuronID<<", "<<postSynapticNeuron->getNeuronID()<<"]: PARAMETER negativeDrift NOT FOUND WITH KEY \""<<negativeDrift<<"\""<<endl;
00388                 return false;
00389         }
00390 
00391         if(parameterMap->count(maximumDrift) != 1){
00392                 cerr<<"STDP1Synapse ["<<preSynapticNeuronID<<", "<<postSynapticNeuron->getNeuronID()<<"]: PARAMETER maximumDrift NOT FOUND WITH KEY \""<<maximumDrift<<"\""<<endl;
00393                 return false;
00394         }
00395 
00396         if(parameterMap->count(minimumDrift) != 1){
00397                 cerr<<"STDP1Synapse ["<<preSynapticNeuronID<<", "<<postSynapticNeuron->getNeuronID()<<"]: PARAMETER minimumDrift NOT FOUND WITH KEY \""<<minimumDrift<<"\""<<endl;
00398                 return false;
00399         }
00400 
00401         if(parameterMap->count(learning) != 1){
00402                 cerr<<"STDP1Synapse ["<<preSynapticNeuronID<<", "<<postSynapticNeuron->getNeuronID()<<"]: PARAMETER learning NOT FOUND WITH KEY \""<<learning<<"\""<<endl;
00403                 return false;
00404         }
00405 
00406         if(parameterMap->count(disable) != 1){
00407                 cerr<<"STDP1Synapse ["<<preSynapticNeuronID<<", "<<postSynapticNeuron->getNeuronID()<<"]: PARAMETER disable NOT FOUND WITH KEY \""<<disable<<"\""<<endl;
00408                 return false;
00409         }
00410 
00411         //Print parameters in debug mode
00412         #ifdef SYNAPSE_PARAMETERS_DEBUG
00413                 printParameters();
00414         #endif//SYNAPSE_PARAMETERS_DEBUG
00415 
00416         return true;
00417 }
00418 
00419 
00420 /*! Prevent weight from going out of range.
00421         Note that this is flooring the weight at zero rather than MIN_DOUBLE_WEIGHT
00422         because it is assumed that only excitatory connections will be subject to learning
00423         and should not turn into inhibitory connections during the learning process. */
00424 void STDP1Synapse::normaliseWeight(){
00425         if(weight > MAX_DOUBLE_WEIGHT)
00426                 weight = MAX_DOUBLE_WEIGHT;
00427         else if(weight < 0)
00428                 weight = 0;
00429 }
00430 
00431 
00432 /*! Prints out the parameters for debugging. */
00433 void STDP1Synapse::printParameters(){
00434                 cout<<"------------------ SETTING STDP1Synapse PARAMETERS -----------------------"<<endl;
00435                 cout<<"calciumThreshUpLow = "<<(*parameterMap)[calciumThreshUpLow]<<endl;
00436                 cout<<"calciumThreshUpHigh= "<<(*parameterMap)[calciumThreshUpHigh]<<endl;
00437                 cout<<"calciumThreshDownLow = "<<(*parameterMap)[calciumThreshDownLow]<<endl;
00438                 cout<<"calciumThreshDownHigh = "<<(*parameterMap)[calciumThreshDownHigh]<<endl;
00439                 cout<<"weightChangeThreshold = "<<(*parameterMap)[weightChangeThreshold]<<endl;
00440                 cout<<"weightIncreaseAmnt = "<<(*parameterMap)[weightIncreaseAmnt]<<endl;
00441                 cout<<"weightDecreaseAmnt = "<<(*parameterMap)[weightDecreaseAmnt]<<endl;
00442                 cout<<"driftThreshold = "<<(*parameterMap)[driftThreshold]<<endl;
00443                 cout<<"positiveDrift = "<<(*parameterMap)[positiveDrift]<<endl;
00444                 cout<<"negativeDrift = "<<(*parameterMap)[negativeDrift]<<endl;
00445                 cout<<"maximumDrift = "<<(*parameterMap)[maximumDrift]<<endl;
00446                 cout<<"minimumDrift = "<<(*parameterMap)[minimumDrift]<<endl;
00447                 cout<<"Learning mode = "<<(*parameterMap)[learning]<<endl;
00448                 cout<<"Disable = "<<(*parameterMap)[disable]<<endl;
00449                 cout<<"---------------------------------------------------------------------------"<<endl;
00450 }
00451 
00452 

Generated on Mon Sep 3 22:35:32 2007 for STDP1 Synapse by  doxygen 1.4.4