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