1 /*
2 * gif_psc_exp_multisynapse.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 "gif_psc_exp_multisynapse.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 "exceptions.h"
34 #include "kernel_manager.h"
35 #include "universal_data_logger_impl.h"
36
37 // Includes from sli:
38 #include "dict.h"
39 #include "integerdatum.h"
40 #include "doubledatum.h"
41 #include "dictutils.h"
42
43 #include "compose.hpp"
44 #include "propagator_stability.h"
45
46 namespace nest
47 {
48 /* ----------------------------------------------------------------
49 * Recordables map
50 * ---------------------------------------------------------------- */
51
52 RecordablesMap< gif_psc_exp_multisynapse > gif_psc_exp_multisynapse::recordablesMap_;
53
54 // Override the create() method with one call to RecordablesMap::insert_()
55 // for each quantity to be recorded.
56 template <>
57 void
create()58 RecordablesMap< gif_psc_exp_multisynapse >::create()
59 {
60 // use standard names wherever you can for consistency!
61 insert_( names::V_m, &gif_psc_exp_multisynapse::get_V_m_ );
62 insert_( names::E_sfa, &gif_psc_exp_multisynapse::get_E_sfa_ );
63 insert_( names::I_stc, &gif_psc_exp_multisynapse::get_I_stc_ );
64 }
65
66 /* ----------------------------------------------------------------
67 * Default constructors defining default parameters and state
68 * ---------------------------------------------------------------- */
69
Parameters_()70 nest::gif_psc_exp_multisynapse::Parameters_::Parameters_()
71 : g_L_( 4.0 ) // nS
72 , E_L_( -70.0 ) // mV
73 , V_reset_( -55.0 ) // mV
74 , Delta_V_( 0.5 ) // mV
75 , V_T_star_( -35 ) // mV
76 , lambda_0_( 0.001 ) // 1/ms
77 , t_ref_( 4.0 ) // ms
78 , c_m_( 80.0 ) // pF
79 , tau_stc_() // ms
80 , q_stc_() // nA
81 , tau_sfa_() // ms
82 , q_sfa_() // mV
83 , tau_syn_( 1, 2.0 ) // ms
84 , has_connections_( false )
85 , I_e_( 0.0 ) // pA
86 {
87 }
88
89
State_()90 nest::gif_psc_exp_multisynapse::State_::State_()
91 : I_stim_( 0.0 )
92 , V_( -70.0 )
93 , sfa_( 0.0 )
94 , stc_( 0.0 )
95 , sfa_elems_()
96 , stc_elems_()
97 , i_syn_()
98 , r_ref_( 0 )
99 {
100 }
101
102 /* ----------------------------------------------------------------
103 * Parameter and state extractions and manipulation functions
104 * ---------------------------------------------------------------- */
105
106 void
get(DictionaryDatum & d) const107 nest::gif_psc_exp_multisynapse::Parameters_::get( DictionaryDatum& d ) const
108 {
109 def< double >( d, names::I_e, I_e_ );
110 def< double >( d, names::E_L, E_L_ );
111 def< double >( d, names::g_L, g_L_ );
112 def< double >( d, names::C_m, c_m_ );
113 def< double >( d, names::V_reset, V_reset_ );
114 def< double >( d, names::Delta_V, Delta_V_ );
115 def< double >( d, names::V_T_star, V_T_star_ );
116 def< double >( d, names::lambda_0, lambda_0_ * 1000.0 ); // convert to 1/s
117 def< double >( d, names::t_ref, t_ref_ );
118
119 def< int >( d, names::n_receptors, n_receptors_() );
120 def< bool >( d, names::has_connections, has_connections_ );
121
122 ArrayDatum tau_syn_ad( tau_syn_ );
123 def< ArrayDatum >( d, names::tau_syn, tau_syn_ad );
124
125 ArrayDatum tau_sfa_list_ad( tau_sfa_ );
126 def< ArrayDatum >( d, names::tau_sfa, tau_sfa_list_ad );
127
128 ArrayDatum q_sfa_list_ad( q_sfa_ );
129 def< ArrayDatum >( d, names::q_sfa, q_sfa_list_ad );
130
131 ArrayDatum tau_stc_list_ad( tau_stc_ );
132 def< ArrayDatum >( d, names::tau_stc, tau_stc_list_ad );
133
134 ArrayDatum q_stc_list_ad( q_stc_ );
135 def< ArrayDatum >( d, names::q_stc, q_stc_list_ad );
136 }
137
138 void
set(const DictionaryDatum & d,Node * node)139 nest::gif_psc_exp_multisynapse::Parameters_::set( const DictionaryDatum& d, Node* node )
140 {
141 updateValueParam< double >( d, names::I_e, I_e_, node );
142 updateValueParam< double >( d, names::E_L, E_L_, node );
143 updateValueParam< double >( d, names::g_L, g_L_, node );
144 updateValueParam< double >( d, names::C_m, c_m_, node );
145 updateValueParam< double >( d, names::V_reset, V_reset_, node );
146 updateValueParam< double >( d, names::Delta_V, Delta_V_, node );
147 updateValueParam< double >( d, names::V_T_star, V_T_star_, node );
148
149 if ( updateValueParam< double >( d, names::lambda_0, lambda_0_, node ) )
150 {
151 lambda_0_ /= 1000.0; // convert to 1/ms
152 }
153
154 updateValueParam< double >( d, names::t_ref, t_ref_, node );
155
156 updateValue< std::vector< double > >( d, names::tau_sfa, tau_sfa_ );
157 updateValue< std::vector< double > >( d, names::q_sfa, q_sfa_ );
158 updateValue< std::vector< double > >( d, names::tau_stc, tau_stc_ );
159 updateValue< std::vector< double > >( d, names::q_stc, q_stc_ );
160
161 if ( tau_sfa_.size() != q_sfa_.size() )
162 {
163 throw BadProperty( String::compose(
164 "'tau_sfa' and 'q_sfa' need to have the same dimensions.\nSize of "
165 "tau_sfa: %1\nSize of q_sfa: %2",
166 tau_sfa_.size(),
167 q_sfa_.size() ) );
168 }
169
170 if ( tau_stc_.size() != q_stc_.size() )
171 {
172 throw BadProperty( String::compose(
173 "'tau_stc' and 'q_stc' need to have the same dimensions.\nSize of "
174 "tau_stc: %1\nSize of q_stc: %2",
175 tau_stc_.size(),
176 q_stc_.size() ) );
177 }
178 if ( g_L_ <= 0 )
179 {
180 throw BadProperty( "Membrane conductance must be strictly positive." );
181 }
182 if ( Delta_V_ <= 0 )
183 {
184 throw BadProperty( "Delta_V must be strictly positive." );
185 }
186 if ( c_m_ <= 0 )
187 {
188 throw BadProperty( "Capacitance must be strictly positive." );
189 }
190 if ( t_ref_ < 0 )
191 {
192 throw BadProperty( "Refractory time must not be negative." );
193 }
194 if ( lambda_0_ < 0 )
195 {
196 throw BadProperty( "lambda_0 must not be negative." );
197 }
198
199 for ( size_t i = 0; i < tau_sfa_.size(); i++ )
200 {
201 if ( tau_sfa_[ i ] <= 0 )
202 {
203 throw BadProperty( "All time constants must be strictly positive." );
204 }
205 }
206
207 for ( size_t i = 0; i < tau_stc_.size(); i++ )
208 {
209 if ( tau_stc_[ i ] <= 0 )
210 {
211 throw BadProperty( "All time constants must be strictly positive." );
212 }
213 }
214
215 std::vector< double > tau_tmp;
216 if ( updateValue< std::vector< double > >( d, names::tau_syn, tau_tmp ) )
217 {
218 if ( has_connections_ && tau_tmp.size() < tau_syn_.size() )
219 {
220 throw BadProperty(
221 "The neuron has connections, "
222 "therefore the number of ports cannot be reduced." );
223 }
224
225 for ( size_t i = 0; i < tau_tmp.size(); ++i )
226 {
227 if ( tau_tmp[ i ] <= 0 )
228 {
229 throw BadProperty( "All synaptic time constants must be > 0." );
230 }
231 }
232
233 tau_syn_ = tau_tmp;
234 }
235 }
236
237 void
get(DictionaryDatum & d,const Parameters_ &) const238 nest::gif_psc_exp_multisynapse::State_::get( DictionaryDatum& d, const Parameters_& ) const
239 {
240 def< double >( d, names::V_m, V_ ); // Membrane potential
241 def< double >( d, names::E_sfa, sfa_ ); // Adaptive threshold potential
242 def< double >( d, names::I_stc, stc_ ); // Spike-triggered current
243 }
244
245 void
set(const DictionaryDatum & d,const Parameters_ &,Node * node)246 nest::gif_psc_exp_multisynapse::State_::set( const DictionaryDatum& d, const Parameters_&, Node* node )
247 {
248 updateValueParam< double >( d, names::V_m, V_, node );
249 }
250
Buffers_(gif_psc_exp_multisynapse & n)251 nest::gif_psc_exp_multisynapse::Buffers_::Buffers_( gif_psc_exp_multisynapse& n )
252 : logger_( n )
253 {
254 }
255
Buffers_(const Buffers_ &,gif_psc_exp_multisynapse & n)256 nest::gif_psc_exp_multisynapse::Buffers_::Buffers_( const Buffers_&, gif_psc_exp_multisynapse& n )
257 : logger_( n )
258 {
259 }
260
261 /* ----------------------------------------------------------------
262 * Default and copy constructor for node
263 * ---------------------------------------------------------------- */
264
gif_psc_exp_multisynapse()265 nest::gif_psc_exp_multisynapse::gif_psc_exp_multisynapse()
266 : ArchivingNode()
267 , P_()
268 , S_()
269 , B_( *this )
270 {
271 recordablesMap_.create();
272 }
273
gif_psc_exp_multisynapse(const gif_psc_exp_multisynapse & n)274 nest::gif_psc_exp_multisynapse::gif_psc_exp_multisynapse( const gif_psc_exp_multisynapse& n )
275 : ArchivingNode( n )
276 , P_( n.P_ )
277 , S_( n.S_ )
278 , B_( n.B_, *this )
279 {
280 }
281
282 /* ----------------------------------------------------------------
283 * Node initialization functions
284 * ---------------------------------------------------------------- */
285
286 void
init_buffers_()287 nest::gif_psc_exp_multisynapse::init_buffers_()
288 {
289 B_.spikes_.clear(); //!< includes resize
290 B_.currents_.clear(); //!< includes resize
291 B_.logger_.reset(); //!< includes resize
292 ArchivingNode::clear_history();
293 }
294
295 void
calibrate()296 nest::gif_psc_exp_multisynapse::calibrate()
297 {
298 B_.logger_.init();
299
300 const double h = Time::get_resolution().get_ms();
301 V_.rng_ = get_vp_specific_rng( get_thread() );
302
303 const double tau_m = P_.c_m_ / P_.g_L_;
304
305 V_.P33_ = std::exp( -h / tau_m );
306 V_.P30_ = -1 / P_.c_m_ * numerics::expm1( -h / tau_m ) * tau_m;
307 V_.P31_ = -numerics::expm1( -h / tau_m );
308
309 V_.RefractoryCounts_ = Time( Time::ms( P_.t_ref_ ) ).get_steps();
310
311 // initializing adaptation (stc/sfa) variables
312 V_.P_sfa_.resize( P_.tau_sfa_.size(), 0.0 );
313 V_.P_stc_.resize( P_.tau_stc_.size(), 0.0 );
314
315 for ( size_t i = 0; i < P_.tau_sfa_.size(); i++ )
316 {
317 V_.P_sfa_[ i ] = std::exp( -h / P_.tau_sfa_[ i ] );
318 }
319 S_.sfa_elems_.resize( P_.tau_sfa_.size(), 0.0 );
320
321 for ( size_t i = 0; i < P_.tau_stc_.size(); i++ )
322 {
323 V_.P_stc_[ i ] = std::exp( -h / P_.tau_stc_[ i ] );
324 }
325 S_.stc_elems_.resize( P_.tau_stc_.size(), 0.0 );
326
327 V_.P11_syn_.resize( P_.n_receptors_() );
328 V_.P21_syn_.resize( P_.n_receptors_() );
329
330 S_.i_syn_.resize( P_.n_receptors_() );
331
332 B_.spikes_.resize( P_.n_receptors_() );
333
334 for ( size_t i = 0; i < P_.n_receptors_(); i++ )
335 {
336 V_.P11_syn_[ i ] = std::exp( -h / P_.tau_syn_[ i ] );
337 V_.P21_syn_[ i ] = propagator_32( P_.tau_syn_[ i ], tau_m, P_.c_m_, h );
338
339 B_.spikes_[ i ].resize();
340 }
341 }
342
343 /* ----------------------------------------------------------------
344 * Update and spike handling functions
345 */
346
347 void
update(Time const & origin,const long from,const long to)348 nest::gif_psc_exp_multisynapse::update( Time const& origin, const long from, const long to )
349 {
350
351 assert( to >= 0 && ( delay ) from < kernel().connection_manager.get_min_delay() );
352 assert( from < to );
353
354 for ( long lag = from; lag < to; ++lag )
355 {
356
357 // exponential decaying stc and sfa elements
358 S_.stc_ = 0.0;
359 for ( size_t i = 0; i < S_.stc_elems_.size(); i++ )
360 {
361 S_.stc_ += S_.stc_elems_[ i ];
362 S_.stc_elems_[ i ] = V_.P_stc_[ i ] * S_.stc_elems_[ i ];
363 }
364
365 S_.sfa_ = P_.V_T_star_;
366 for ( size_t i = 0; i < S_.sfa_elems_.size(); i++ )
367 {
368 S_.sfa_ += S_.sfa_elems_[ i ];
369 S_.sfa_elems_[ i ] = V_.P_sfa_[ i ] * S_.sfa_elems_[ i ];
370 }
371
372 double sum_syn_pot = 0.0;
373 for ( size_t i = 0; i < P_.n_receptors_(); i++ )
374 {
375 // computing effect of synaptic currents on membrane potential
376 sum_syn_pot += V_.P21_syn_[ i ] * S_.i_syn_[ i ];
377 // exponential decaying PSCs
378 S_.i_syn_[ i ] = V_.P11_syn_[ i ] * S_.i_syn_[ i ];
379 S_.i_syn_[ i ] += B_.spikes_[ i ].get_value( lag ); // collecting spikes
380 }
381
382 if ( S_.r_ref_ == 0 ) // neuron is not in refractory period
383 {
384 // effect of synaptic currents (sum_syn_pot) is added here
385 S_.V_ = V_.P30_ * ( S_.I_stim_ + P_.I_e_ - S_.stc_ ) + V_.P33_ * S_.V_ + V_.P31_ * P_.E_L_ + sum_syn_pot;
386
387 const double lambda = P_.lambda_0_ * std::exp( ( S_.V_ - S_.sfa_ ) / P_.Delta_V_ );
388
389 if ( lambda > 0.0 )
390 {
391 // Draw random number and compare to prob to have a spike
392 // hazard function is computed by 1 - exp(- lambda * dt)
393 if ( V_.rng_->drand() < -numerics::expm1( -lambda * Time::get_resolution().get_ms() ) )
394 {
395
396 for ( size_t i = 0; i < S_.stc_elems_.size(); i++ )
397 {
398 S_.stc_elems_[ i ] += P_.q_stc_[ i ];
399 }
400
401 for ( size_t i = 0; i < S_.sfa_elems_.size(); i++ )
402 {
403 S_.sfa_elems_[ i ] += P_.q_sfa_[ i ];
404 }
405
406 S_.r_ref_ = V_.RefractoryCounts_;
407
408 // And send the spike event
409 set_spiketime( Time::step( origin.get_steps() + lag + 1 ) );
410 SpikeEvent se;
411 kernel().event_delivery_manager.send( *this, se, lag );
412 }
413 }
414 }
415 else
416 {
417 --S_.r_ref_; // neuron is absolute refractory
418 S_.V_ = P_.V_reset_; // reset the membrane potential
419 }
420
421 // Set new input current
422 S_.I_stim_ = B_.currents_.get_value( lag );
423
424 // Voltage logging
425 B_.logger_.record_data( origin.get_steps() + lag );
426 }
427 }
428
429
430 void
handle(SpikeEvent & e)431 gif_psc_exp_multisynapse::handle( SpikeEvent& e )
432 {
433 assert( e.get_delay_steps() > 0 );
434 assert( ( e.get_rport() > 0 ) && ( ( size_t ) e.get_rport() <= P_.n_receptors_() ) );
435
436 B_.spikes_[ e.get_rport() - 1 ].add_value(
437 e.get_rel_delivery_steps( kernel().simulation_manager.get_slice_origin() ), e.get_weight() * e.get_multiplicity() );
438 }
439
440 void
handle(CurrentEvent & e)441 nest::gif_psc_exp_multisynapse::handle( CurrentEvent& e )
442 {
443 assert( e.get_delay_steps() > 0 );
444
445 const double c = e.get_current();
446 const double w = e.get_weight();
447
448 // Add weighted current; HEP 2002-10-04
449 B_.currents_.add_value( e.get_rel_delivery_steps( kernel().simulation_manager.get_slice_origin() ), w * c );
450 }
451
452 void
handle(DataLoggingRequest & e)453 nest::gif_psc_exp_multisynapse::handle( DataLoggingRequest& e )
454 {
455 B_.logger_.handle( e );
456 }
457
458 } // namespace
459