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: */