1 /*
2  *  izhikevich.cpp
3  *
4  *  This file is part of NEST.
5  *
6  *  Copyright (C) 2004 The NEST Initiative
7  *
8  *  NEST is free software: you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation, either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  NEST is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with NEST.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22 
23 #include "izhikevich.h"
24 
25 // C++ includes:
26 #include <limits>
27 
28 // Includes from libnestutil:
29 #include "dict_util.h"
30 #include "numerics.h"
31 
32 // Includes from nestkernel:
33 #include "event_delivery_manager_impl.h"
34 #include "exceptions.h"
35 #include "kernel_manager.h"
36 #include "universal_data_logger_impl.h"
37 
38 // Includes from sli:
39 #include "dict.h"
40 #include "dictutils.h"
41 #include "doubledatum.h"
42 #include "integerdatum.h"
43 
44 /* ----------------------------------------------------------------
45  * Recordables map
46  * ---------------------------------------------------------------- */
47 
48 nest::RecordablesMap< nest::izhikevich > nest::izhikevich::recordablesMap_;
49 
50 namespace nest
51 {
52 // Override the create() method with one call to RecordablesMap::insert_()
53 // for each quantity to be recorded.
54 template <>
55 void
create()56 RecordablesMap< izhikevich >::create()
57 {
58   // use standard names whereever you can for consistency!
59   insert_( names::V_m, &izhikevich::get_V_m_ );
60   insert_( names::U_m, &izhikevich::get_U_m_ );
61 }
62 }
63 
64 /* ----------------------------------------------------------------
65  * Default constructors defining default parameters and state
66  * ---------------------------------------------------------------- */
67 
Parameters_()68 nest::izhikevich::Parameters_::Parameters_()
69   : a_( 0.02 )                                      // a
70   , b_( 0.2 )                                       // b
71   , c_( -65.0 )                                     // c without unit
72   , d_( 8.0 )                                       // d
73   , I_e_( 0.0 )                                     // pA
74   , V_th_( 30.0 )                                   // mV
75   , V_min_( -std::numeric_limits< double >::max() ) // mV
76   , consistent_integration_( true )
77 {
78 }
79 
State_()80 nest::izhikevich::State_::State_()
81   : v_( -65.0 )       // membrane potential
82   , u_( 0.2 * -65.0 ) // membrane recovery variable (b * V_m_init)
83   , I_( 0.0 )         // input current
84 {
85 }
86 
87 /* ----------------------------------------------------------------
88  * Parameter and state extractions and manipulation functions
89  * ---------------------------------------------------------------- */
90 
91 void
get(DictionaryDatum & d) const92 nest::izhikevich::Parameters_::get( DictionaryDatum& d ) const
93 {
94   def< double >( d, names::I_e, I_e_ );
95   def< double >( d, names::V_th, V_th_ ); // threshold value
96   def< double >( d, names::V_min, V_min_ );
97   def< double >( d, names::a, a_ );
98   def< double >( d, names::b, b_ );
99   def< double >( d, names::c, c_ );
100   def< double >( d, names::d, d_ );
101   def< bool >( d, names::consistent_integration, consistent_integration_ );
102 }
103 
104 void
set(const DictionaryDatum & d,Node * node)105 nest::izhikevich::Parameters_::set( const DictionaryDatum& d, Node* node )
106 {
107 
108   updateValueParam< double >( d, names::V_th, V_th_, node );
109   updateValueParam< double >( d, names::V_min, V_min_, node );
110   updateValueParam< double >( d, names::I_e, I_e_, node );
111   updateValueParam< double >( d, names::a, a_, node );
112   updateValueParam< double >( d, names::b, b_, node );
113   updateValueParam< double >( d, names::c, c_, node );
114   updateValueParam< double >( d, names::d, d_, node );
115   updateValue< bool >( d, names::consistent_integration, consistent_integration_ );
116   const double h = Time::get_resolution().get_ms();
117   if ( not consistent_integration_ && h != 1.0 )
118   {
119     LOG( M_INFO, "Parameters_::set", "Use 1.0 ms as resolution for consistency." );
120   }
121 }
122 
123 void
get(DictionaryDatum & d,const Parameters_ &) const124 nest::izhikevich::State_::get( DictionaryDatum& d, const Parameters_& ) const
125 {
126   def< double >( d, names::U_m, u_ ); // Membrane potential recovery variable
127   def< double >( d, names::V_m, v_ ); // Membrane potential
128 }
129 
130 void
set(const DictionaryDatum & d,const Parameters_ &,Node * node)131 nest::izhikevich::State_::set( const DictionaryDatum& d, const Parameters_&, Node* node )
132 {
133   updateValueParam< double >( d, names::U_m, u_, node );
134   updateValueParam< double >( d, names::V_m, v_, node );
135 }
136 
Buffers_(izhikevich & n)137 nest::izhikevich::Buffers_::Buffers_( izhikevich& n )
138   : logger_( n )
139 {
140 }
141 
Buffers_(const Buffers_ &,izhikevich & n)142 nest::izhikevich::Buffers_::Buffers_( const Buffers_&, izhikevich& n )
143   : logger_( n )
144 {
145 }
146 
147 /* ----------------------------------------------------------------
148  * Default and copy constructor for node
149  * ---------------------------------------------------------------- */
150 
izhikevich()151 nest::izhikevich::izhikevich()
152   : ArchivingNode()
153   , P_()
154   , S_()
155   , B_( *this )
156 {
157   recordablesMap_.create();
158 }
159 
izhikevich(const izhikevich & n)160 nest::izhikevich::izhikevich( const izhikevich& n )
161   : ArchivingNode( n )
162   , P_( n.P_ )
163   , S_( n.S_ )
164   , B_( n.B_, *this )
165 {
166 }
167 
168 /* ----------------------------------------------------------------
169  * Node initialization functions
170  * ---------------------------------------------------------------- */
171 
172 void
init_buffers_()173 nest::izhikevich::init_buffers_()
174 {
175   B_.spikes_.clear();   // includes resize
176   B_.currents_.clear(); // includes resize
177   B_.logger_.reset();   // includes resize
178   ArchivingNode::clear_history();
179 }
180 
181 void
calibrate()182 nest::izhikevich::calibrate()
183 {
184   B_.logger_.init();
185 }
186 
187 /* ----------------------------------------------------------------
188  * Update and spike handling functions
189  */
190 
191 void
update(Time const & origin,const long from,const long to)192 nest::izhikevich::update( Time const& origin, const long from, const long to )
193 {
194   assert( to >= 0 && ( delay ) from < kernel().connection_manager.get_min_delay() );
195   assert( from < to );
196 
197   const double h = Time::get_resolution().get_ms();
198   double v_old, u_old;
199 
200   for ( long lag = from; lag < to; ++lag )
201   {
202     // neuron is never refractory
203     // use standard forward Euler numerics in this case
204     if ( P_.consistent_integration_ )
205     {
206       v_old = S_.v_;
207       u_old = S_.u_;
208       S_.v_ +=
209         h * ( 0.04 * v_old * v_old + 5.0 * v_old + 140.0 - u_old + S_.I_ + P_.I_e_ ) + B_.spikes_.get_value( lag );
210       S_.u_ += h * P_.a_ * ( P_.b_ * v_old - u_old );
211     }
212     // use numerics published in Izhikevich (2003) in this case (not
213     // recommended)
214     else
215     {
216       double I_syn = B_.spikes_.get_value( lag );
217       S_.v_ += h * 0.5 * ( 0.04 * S_.v_ * S_.v_ + 5.0 * S_.v_ + 140.0 - S_.u_ + S_.I_ + P_.I_e_ + I_syn );
218       S_.v_ += h * 0.5 * ( 0.04 * S_.v_ * S_.v_ + 5.0 * S_.v_ + 140.0 - S_.u_ + S_.I_ + P_.I_e_ + I_syn );
219       S_.u_ += h * P_.a_ * ( P_.b_ * S_.v_ - S_.u_ );
220     }
221 
222     // lower bound of membrane potential
223     S_.v_ = ( S_.v_ < P_.V_min_ ? P_.V_min_ : S_.v_ );
224 
225     // threshold crossing
226     if ( S_.v_ >= P_.V_th_ )
227     {
228       S_.v_ = P_.c_;
229       S_.u_ = S_.u_ + P_.d_;
230 
231       // compute spike time
232       set_spiketime( Time::step( origin.get_steps() + lag + 1 ) );
233 
234       SpikeEvent se;
235       kernel().event_delivery_manager.send( *this, se, lag );
236     }
237 
238     // set new input current
239     S_.I_ = B_.currents_.get_value( lag );
240 
241     // voltage logging
242     B_.logger_.record_data( origin.get_steps() + lag );
243   }
244 }
245 
246 void
handle(SpikeEvent & e)247 nest::izhikevich::handle( SpikeEvent& e )
248 {
249   assert( e.get_delay_steps() > 0 );
250   B_.spikes_.add_value(
251     e.get_rel_delivery_steps( kernel().simulation_manager.get_slice_origin() ), e.get_weight() * e.get_multiplicity() );
252 }
253 
254 void
handle(CurrentEvent & e)255 nest::izhikevich::handle( CurrentEvent& e )
256 {
257   assert( e.get_delay_steps() > 0 );
258 
259   const double c = e.get_current();
260   const double w = e.get_weight();
261   B_.currents_.add_value( e.get_rel_delivery_steps( kernel().simulation_manager.get_slice_origin() ), w * c );
262 }
263 
264 void
handle(DataLoggingRequest & e)265 nest::izhikevich::handle( DataLoggingRequest& e )
266 {
267   B_.logger_.handle( e );
268 }
269