SimUnit.H

Go to the documentation of this file.
00001 /*!@file ModelNeuron/SimUnit.H abstract classes for a neural
00002    simulation module, which is anything that can evolve its time
00003    state, recieve input and give output. A SimUnit can also
00004    have sub-modules. */
00005 
00006 // //////////////////////////////////////////////////////////////////// //
00007 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the //
00008 // University of Southern California (USC) and the iLab at USC.         //
00009 // See http://iLab.usc.edu for information about this project.          //
00010 // //////////////////////////////////////////////////////////////////// //
00011 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00012 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00013 // in Visual Environments, and Applications'' by Christof Koch and      //
00014 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00015 // pending; application number 09/912,225 filed July 23, 2001; see      //
00016 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00017 // //////////////////////////////////////////////////////////////////// //
00018 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00019 //                                                                      //
00020 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00021 // redistribute it and/or modify it under the terms of the GNU General  //
00022 // Public License as published by the Free Software Foundation; either  //
00023 // version 2 of the License, or (at your option) any later version.     //
00024 //                                                                      //
00025 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00026 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00027 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00028 // PURPOSE.  See the GNU General Public License for more details.       //
00029 //                                                                      //
00030 // You should have received a copy of the GNU General Public License    //
00031 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00032 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00033 // Boston, MA 02111-1307 USA.                                           //
00034 // //////////////////////////////////////////////////////////////////// //
00035 //
00036 // Primary maintainer for this file: David J. Berg <dberg@usc.edu>
00037 // $HeadURL:svn://ilab.usc.edu/trunk/saliency/src/ModelNeuron/SimUnit.H$
00038 
00039 #ifndef MODELNEURON_SIMUNIT_H_DEFINED
00040 #define MODELNEURON_SIMUNIT_H_DEFINED
00041 
00042 #include "Util/SimTime.H"
00043 #include "Util/log.H"
00044 #include "ModelNeuron/NeuralDecoder.H"
00045 #include <typeinfo>
00046 
00047 // ######################################################################
00048 // ! An abstract interface for a simulation unit. Classes should not
00049 // derive from this class directly, but rather from
00050 // SimUnitDerived, defined below. A simulation module is a
00051 // computational unit that takes input, gives output, and evolves its
00052 // internal state. Users must override doIntegrate(time, excite,
00053 // inhibit) to implement the right-hand-side of their differential
00054 // equation(s)(see Rectify.H for the simplest example, and LowPass.H
00055 // for a more interesting one), and doInit() to perform any derived
00056 // class specific initialization. SimUnits can have other
00057 // SimUnits as sub-modules however for this abstract class
00058 // there is no implementation of this functionality (see
00059 // NeuralColumn.H or IZNeuron.H for SimUnits that implement
00060 // sub-modules). Modules and sub-modules need not be simulated at the
00061 // same time step. SimUnits can also have decoders on the
00062 // input or output to transform the signal before or after processing
00063 // (see NeuralDecoder.H). When evolve(time) is called the module will
00064 // check to see if it needs to be integrated, and if so integrate
00065 // itself to the requested 'world' time. Then any submodules will be
00066 // integrated to the super modules current time. Input to a sub module
00067 // should only come from its super module to ensure that proper
00068 // scaling is performed. Each call to input will segregate positive
00069 // (excitatory) and negative (inhibitory) input and add the input to
00070 // the current totals. Upon the next call to evolve the inputs will be
00071 // pre-processed and rescaled if oversampling is detected (multiple
00072 // calls to evolve(time) without an internal time change), before
00073 // being passed to the user defined doIntegrate function. After a call
00074 // to evolve(time), the modules internal time will be simulated up to
00075 // (or close, depending on the RateType, see below) 'time', itsInput
00076 // will be set to 0.0, and a call to getOutput() will represent the
00077 // output of the system at (or close to) 'time'.
00078 // ######################################################################
00079 class SimUnit
00080 {
00081 public:
00082   //a typdef used to register classes
00083   typedef CreateFunctor<SimUnit, ParamList<SimTime> > Creator;
00084   typedef GenericFactory<SimUnit, std::string, Creator> Factory;
00085   
00086   //For sampling rate control options
00087   enum RateType
00088     {
00089       NORMAL, //Use the sampling rate that is closest to the requsted
00090       //sampling rate (time step) and will result in an integer
00091       //number of integrations after each call to evolve()
00092       
00093       STRICT, //enforce that each call to evolve results in an integer
00094       //number of integrations with exactly the requested time
00095       //step. This may result in a module that does not evolve up to
00096       //the current time step on every call to evolve(), but will be
00097       //less than a time step away.
00098       
00099       OPTIM //choose the time step automatically if available
00100     };
00101   
00102   /*! Constructor with default params
00103     @param timeStep is the integration time step, in milliseconds 
00104     @param name is the name of the module
00105     @param units are the units in SI
00106   */
00107   SimUnit(const SimTime& timestep = SimTime::MSECS(1.0), 
00108           const RateType ratetype = NORMAL,
00109           const std::string& name = "", 
00110           const std::string& units = "");
00111   
00112   //! virtual destructor for propper inheritance
00113   virtual ~SimUnit();
00114   
00115 // ######################################################################
00116 // i/o and simulation functions
00117 // ######################################################################  
00118   
00119   //!input to the computation unit
00120   void input(const double& in);
00121 
00122   //!input excitation to the computation unit
00123   void inputExc(const double& in);
00124 
00125   //!input inhibition to the computation unit
00126   void inputInh(const double& in);
00127   
00128   //!set the voltage (or other state value)
00129   virtual void setV(const double& in);
00130   
00131   //!get the output since the last call to evolve
00132   const double getOutput() const;
00133   
00134   //!get an output for display
00135   virtual const double getDisplayOutput() const;
00136   
00137   //! integrate to time t, updating any decoders and internal variables
00138   void evolve(const SimTime& t);
00139   
00140 // ######################################################################
00141 // sub-module functions
00142 // ######################################################################  
00143 
00144   //!get the number of sub units (default implementation returns 0)
00145   virtual const uint numSubs() const;
00146   
00147   //!Returns a reference to the subcomponent. You should check with
00148   //!numSubs before calling (default implementation produces LFATAL)
00149   //!and should use very carefully to ensure the object will not be
00150   //!destroyed by the owner while in use!
00151   virtual const SimUnit& getSub(const uint i) const;
00152 
00153 // ######################################################################
00154 // change state functions
00155 // ######################################################################  
00156 
00157   //! reset the object to post construction then call
00158   //! doInit() to perform any subclass specific initialization.
00159   void initialize();
00160   
00161   //!hook up a decoder of desired
00162   void setDecoderPre(const NeuralDecoder& nd);
00163   
00164   //!hook up a decoder of desired
00165   void setDecoderPost(const NeuralDecoder& nd);
00166   
00167   //!set the modules name
00168   void setName(const std::string& name);
00169   
00170   //!set the  output units in SI
00171   void setUnits(const std::string& units);
00172 
00173   //!set the initial output
00174   void setOutput(const double& val, const bool recursive = true);
00175 
00176   //!set the current time
00177   void setTime(const SimTime& time, const bool recursive = true);
00178   
00179 // ######################################################################
00180 // get state functions
00181 // ######################################################################  
00182 
00183   //! return our internal time
00184   const SimTime getTime() const;
00185   
00186   //! return our internal time step
00187   const SimTime getTimeStep() const;
00188   
00189   //! return the name of this module
00190   const std::string getName() const;
00191   
00192   //! return the units of this modules output
00193   const std::string getUnits() const;
00194   
00195   //!return this modules rate type
00196   const RateType getRateType() const;
00197   
00198   //!clone the object, like a virtual copy constructor. This function
00199   //!implemented in NeurSimModuleDerived, which other objects should
00200   //!derive from.
00201   SimUnit* clone() const;
00202 
00203   //!Returns a reference to the subcomponent. You should check with
00204   //!numSubs before calling (default implementation produces LFATAL)
00205   //!and should use very carefully to ensure the object will not be
00206   //!destroyed by the owner while in use!
00207   virtual SimUnit& editSub(const uint i);
00208   
00209 protected:
00210   //!restrict copy constructor and assignment, use clone instead so
00211   //!derived classes create the correct objects and we avoid slicing
00212   SimUnit(const SimUnit& rhs);
00213   SimUnit& operator=(const SimUnit& rhs);  
00214   
00215 private:  
00216   //!overide to set the optimal time step 'simtime' for the unit,
00217   //!called every evolution  if the rate type is set to OPTIM.
00218   virtual void setOptimTimeStep(SimTime& simtime) const;
00219   
00220   //! perform any user defined integration steps
00221   virtual const double doIntegrate(const SimTime& dt, const double& exc, const double& inh) = 0; 
00222   
00223   //! perform subclass specific initialization
00224   virtual void doInit();  
00225 
00226   //!actually do the cloning work
00227   virtual SimUnit* doClone() const = 0;
00228   
00229   double itsInpExc; // to hold our excitatory input till next integration
00230   double itsInpInh; // to hold our inhibitory input till next integration
00231   double itsOutput;  // our output after the last integration time step
00232   std::string itsName, itsUnits; //name of module and SI output units
00233   SimTime itsTimeStep;  // time step to use for difference equations (in msecs)
00234   SimTime itsT;         // time of last integration
00235   int itsSampScale; // the number of times evolve gets called but does not need to integrate
00236   RateType itsRateType; //the sampling rate restrictions, if any
00237   
00238   //hooks to decoder
00239   NeuralDecoder *itsDecoderPreE, *itsDecoderPreI,  *itsDecoderPost;
00240 };
00241 
00242 // ######################################################################
00243 //!A class to derive from to create new SimUnits. New
00244 //SimUnit derived types can derive from this class to
00245 //inherit the doClone() function if desired. 
00246 /*
00247   Programmer Note: This class uses the 'quriously recursive template
00248   pattern' to simplify creation of new classes for the programmer. As
00249   such, to create a new simulation module, classes should adhear the
00250   following convention:
00251   
00252   class mymodule : public SimUnitDerived<mymodule>
00253   {
00254       mymodule(//params//) : SimUnit<mymodule>(//params//) { };
00255       //...rest of functions
00256   }
00257 */
00258 // ######################################################################
00259 template <class Derived>
00260 class SimUnitDerived : public SimUnit
00261 {
00262 protected:
00263   SimUnitDerived(const SimTime& timestep = SimTime::MSECS(1.0), 
00264                  const RateType ratetype = NORMAL,
00265                  const std::string& name = "", 
00266                  const std::string& units = "") : 
00267     SimUnit(timestep, ratetype, name, units) { };
00268   
00269   ~SimUnitDerived() { };
00270   
00271 private:
00272   SimUnit* doClone() const 
00273   { return new Derived(dynamic_cast<const Derived&>(*this)); };               
00274 };
00275 
00276 // ######################################################################
00277 //! implementation of inlined SimUnit functions
00278 // ######################################################################
00279 inline
00280 SimUnit::SimUnit(const SimTime& timestep,
00281                  const RateType ratetype,
00282                  const std::string& name, 
00283                  const std::string& units) : 
00284   itsInpExc(0.0), itsInpInh(0.0), itsOutput(0.0), itsName(name), 
00285   itsUnits(units), itsTimeStep(timestep), itsT(SimTime::ZERO()), itsSampScale(1),
00286   itsRateType(ratetype), itsDecoderPreE(NULL), itsDecoderPreI(NULL),
00287   itsDecoderPost(NULL)
00288 { 
00289 }
00290 
00291 // ######################################################################
00292 inline
00293 SimUnit::~SimUnit()
00294 {
00295   delete itsDecoderPreE;
00296   delete itsDecoderPreI;
00297   delete itsDecoderPost;
00298 }
00299 
00300 // ######################################################################
00301 inline
00302 void SimUnit::input(const double& input)
00303 {
00304   if (input > 0.0)
00305     itsInpExc += input; 
00306   else 
00307     itsInpInh += input;
00308 }
00309 
00310 // ######################################################################
00311 inline
00312 void SimUnit::inputExc(const double& input)
00313 {
00314   itsInpExc += input; 
00315 }
00316 
00317 // ######################################################################
00318 inline
00319 void SimUnit::inputInh(const double& input)
00320 {
00321   itsInpInh += input;
00322 }
00323 
00324 // ######################################################################
00325 inline
00326 void SimUnit::setV(const double& input)
00327 { }
00328 
00329 // ######################################################################
00330 inline
00331 const double SimUnit::getOutput() const
00332 {
00333   return itsOutput; 
00334 }
00335 
00336 // ######################################################################
00337 inline
00338 const double SimUnit::getDisplayOutput() const
00339 {
00340   return itsOutput; 
00341 }
00342 
00343 // ######################################################################
00344 inline 
00345 void SimUnit::evolve(const SimTime& t)
00346 {
00347   // we run our difference equations with a time step of itsTimeStep;
00348   // let's here figure out how many iterations we will need to go from
00349   // itsT to t in an equal number of steps. if itsRateType=NORMAL each
00350   // step is as close to itsTimeStep as possible so that we end up at
00351   // time t after iterating for an integer number of time steps. If
00352   // itsRateType=STRICT the we enfoce that the time step is exactly
00353   // itsTimeStep, which may result in not ending up exactly (but <
00354   // itsTimestep away) at t.
00355   const SimTime interval(t - itsT);
00356   const int64 nsteps = interval.nsecs() / itsTimeStep.nsecs();
00357   const int steps = (int)nsteps;  
00358   SimTime currTimeStep;
00359 
00360   if (steps <= 0)
00361     ++itsSampScale;//keep track of how many times we don't integrate
00362   else
00363     {
00364       //set our sampling rate
00365       switch (itsRateType) 
00366         {
00367         case NORMAL:
00368           currTimeStep = SimTime::NSECS(interval.nsecs() / nsteps);  
00369           break;
00370         case OPTIM:
00371           currTimeStep = itsTimeStep;
00372           setOptimTimeStep(currTimeStep);
00373           break;
00374         case STRICT:
00375           currTimeStep = itsTimeStep;
00376           break;
00377         }
00378      
00379       //adjust the input by the number of times it was oversampled
00380       if (itsSampScale > 1)
00381         {
00382           itsInpExc /= itsSampScale;
00383           itsInpInh /= itsSampScale;
00384           itsSampScale = 1;
00385         }
00386       
00387       //loop over our difference equations
00388       for (int ii = 0; ii < steps; ++ii)
00389         {      
00390           double inpe = itsInpExc;
00391           double inpi = itsInpInh;
00392           
00393           //pass our latest input through the decoder if one exists
00394           if (itsDecoderPreE != NULL)
00395             {
00396               itsDecoderPreE->push(inpe);
00397               itsDecoderPreI->push(inpi);
00398               inpe = itsDecoderPreE->getOutput();
00399               inpi = itsDecoderPreI->getOutput();
00400             }
00401           
00402           itsT += currTimeStep;//update our current time
00403           itsOutput = doIntegrate(currTimeStep, inpe, inpi);//update internals
00404           
00405           //pass our newest output through a decoder if one is set
00406           if (itsDecoderPost != NULL)
00407             {
00408               itsDecoderPost->push(itsOutput);
00409               itsOutput = itsDecoderPost->getOutput();
00410             }
00411         }
00412 
00413       //reset our input after we evolve to the current time step
00414       itsInpExc = 0.0;
00415       itsInpInh = 0.0;
00416     }
00417 }
00418 
00419 // ######################################################################
00420 inline
00421 const uint SimUnit::numSubs() const 
00422 {
00423   return 0;
00424 }
00425 
00426 // ######################################################################
00427 inline 
00428 const SimUnit& SimUnit::getSub(const uint i) const
00429 {
00430   LFATAL("Not implemented for this class. Make sure to check the size with "
00431          "numSubs before calling getSub");
00432   return *this;//this will never execute
00433 }
00434 
00435 // ######################################################################
00436 inline 
00437 SimUnit& SimUnit::editSub(const uint i)
00438 {
00439   LFATAL("Not implemented for this class. Make sure to check the size with "
00440          "numSubs before calling getSub");
00441   return *this;//this will never execute
00442 }
00443 
00444 // ######################################################################
00445 inline 
00446 void SimUnit::initialize()
00447 {
00448   itsInpExc = 0.0;
00449   itsInpInh = 0.0;
00450   itsOutput = 0.0;
00451   itsT = SimTime(SimTime::ZERO());
00452   itsSampScale = 1;
00453   itsDecoderPreE->reset();
00454   itsDecoderPreI->reset();
00455   itsDecoderPost->reset();
00456   doInit();
00457 }
00458 
00459 // ######################################################################
00460 inline
00461 void SimUnit::setDecoderPre(const NeuralDecoder& nd)
00462 {
00463   delete itsDecoderPreE;
00464   delete itsDecoderPreI;
00465   itsDecoderPreE = nd.clone();
00466   itsDecoderPreI = nd.clone();
00467 }
00468 
00469 // ######################################################################
00470 inline
00471 void SimUnit::setDecoderPost(const NeuralDecoder& nd)
00472 {
00473   delete itsDecoderPost;
00474   itsDecoderPost = nd.clone();
00475 }
00476 
00477 // ######################################################################
00478 inline
00479 void SimUnit::setName(const std::string& name)
00480 {
00481   itsName = name;
00482 }
00483 
00484 // ######################################################################
00485 inline
00486 void SimUnit::setUnits(const std::string& units)
00487 {
00488   itsUnits = units;
00489 }
00490 
00491 // ######################################################################
00492 inline
00493 void SimUnit::setOutput(const double& val, const bool recursive)
00494 {
00495   itsOutput = val;
00496   if (recursive)
00497     for (uint ii = 0; ii < numSubs(); ++ii)
00498       editSub(ii).setOutput(val, true);
00499 }
00500 
00501 // ######################################################################
00502 inline
00503 void SimUnit::setTime(const SimTime& time, const bool recursive)
00504 {
00505   itsT = time;
00506   if (recursive)
00507     for (uint ii = 0; ii < numSubs(); ++ii)
00508       editSub(ii).setTime(time, true);
00509 }
00510 
00511 // ######################################################################
00512 inline 
00513 const SimTime SimUnit::getTime() const 
00514 { 
00515   return itsT; 
00516 };
00517 
00518 // ######################################################################
00519 inline 
00520 const SimTime SimUnit::getTimeStep() const 
00521 { 
00522   return itsTimeStep; 
00523 }; 
00524 
00525 // ######################################################################
00526 inline
00527 const std::string SimUnit::getName() const
00528 {
00529   return itsName;
00530 }
00531 
00532 // ######################################################################
00533 inline
00534 const std::string SimUnit::getUnits() const
00535 {
00536   return itsUnits;
00537 }
00538 
00539 // ######################################################################
00540 inline
00541 const SimUnit::RateType SimUnit::getRateType() const
00542 {
00543   return itsRateType;
00544 }
00545 
00546 // ######################################################################
00547 inline
00548 SimUnit::SimUnit(const SimUnit& rhs)  :
00549   itsInpExc(rhs.itsInpExc), itsInpInh(rhs.itsInpInh), itsOutput(rhs.itsOutput),
00550   itsName(rhs.itsName), itsUnits(rhs.itsUnits), itsTimeStep(rhs.itsTimeStep), 
00551   itsT(rhs.itsT), itsSampScale(rhs.itsSampScale), itsRateType(rhs.itsRateType),
00552   itsDecoderPreE(NULL), itsDecoderPreI(NULL), itsDecoderPost(NULL)
00553 {
00554   if (rhs.itsDecoderPreE)
00555     {
00556       itsDecoderPreE = rhs.itsDecoderPreE->clone();
00557       itsDecoderPreI = rhs.itsDecoderPreI->clone();
00558     }
00559   
00560   if (rhs.itsDecoderPost)
00561     itsDecoderPost = rhs.itsDecoderPost->clone();
00562 }
00563 
00564 // ######################################################################
00565 inline 
00566 SimUnit& SimUnit::operator=(const SimUnit& rhs)
00567 {
00568   if (this != &rhs)
00569     {
00570       itsInpExc = rhs.itsInpExc;
00571       itsInpInh = rhs.itsInpInh;
00572       itsOutput = rhs.itsOutput;
00573       itsName = rhs.itsName;
00574       itsUnits = rhs.itsUnits;     
00575       itsTimeStep = rhs.itsTimeStep; 
00576       itsT = rhs.itsT;
00577       itsSampScale = rhs.itsSampScale;
00578       itsRateType = rhs.itsRateType;
00579       delete itsDecoderPreE;
00580       delete itsDecoderPreI;
00581       delete itsDecoderPost;
00582 
00583       if (rhs.itsDecoderPreE)
00584         {
00585           itsDecoderPreE = rhs.itsDecoderPreE->clone();
00586           itsDecoderPreI = rhs.itsDecoderPreI->clone();
00587         }
00588       else
00589         {
00590           itsDecoderPreE = NULL;
00591           itsDecoderPreI = NULL;
00592         }
00593       
00594       itsDecoderPost = (rhs.itsDecoderPost) ?
00595         rhs.itsDecoderPost->clone() :
00596         NULL;
00597     }
00598   return *this;
00599 }
00600 
00601 // ######################################################################
00602 inline
00603 SimUnit* SimUnit::clone() const
00604 {
00605   SimUnit* d = doClone();
00606   if ( typeid(*d) != typeid(*this) )
00607     LFATAL("DoClone incorrectly overridden" );
00608   return d;
00609 }
00610 
00611 // ######################################################################
00612 inline
00613 void SimUnit::setOptimTimeStep(SimTime& simtime) const
00614 {
00615   LFATAL("Not Implemented for this class");
00616 }
00617 
00618 // ######################################################################
00619 inline
00620 void SimUnit::doInit()
00621 {
00622 }
00623 
00624 #endif
00625 // ######################################################################
00626 /* So things look consistent in everyone's emacs... */
00627 /* Local Variables: */
00628 /* indent-tabs-mode: nil */
00629 /* End: */
Generated on Sun May 8 08:41:01 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3