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