1 /*
2  *  clopath_archiving_node.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 "clopath_archiving_node.h"
24 
25 // Includes from nestkernel:
26 #include "kernel_manager.h"
27 
28 // Includes from sli:
29 #include "dictutils.h"
30 
31 namespace nest
32 {
33 
34 // member functions for ClopathArchivingNode
35 
ClopathArchivingNode()36 nest::ClopathArchivingNode::ClopathArchivingNode()
37   : ArchivingNode()
38   , A_LTD_( 14.0e-5 )
39   , A_LTP_( 8.0e-5 )
40   , u_ref_squared_( 60.0 )
41   , theta_plus_( -45.3 )
42   , theta_minus_( -70.6 )
43   , A_LTD_const_( true )
44   , delay_u_bars_( 5.0 )
45   , ltd_hist_len_( 0 )
46   , ltd_hist_current_( 0 )
47 {
48 }
49 
ClopathArchivingNode(const ClopathArchivingNode & n)50 nest::ClopathArchivingNode::ClopathArchivingNode( const ClopathArchivingNode& n )
51   : ArchivingNode( n )
52   , A_LTD_( n.A_LTD_ )
53   , A_LTP_( n.A_LTP_ )
54   , u_ref_squared_( n.u_ref_squared_ )
55   , theta_plus_( n.theta_plus_ )
56   , theta_minus_( n.theta_minus_ )
57   , A_LTD_const_( n.A_LTD_const_ )
58   , delay_u_bars_( n.delay_u_bars_ )
59   , ltd_hist_len_( n.ltd_hist_len_ )
60   , ltd_hist_current_( n.ltd_hist_current_ )
61 {
62 }
63 
64 void
init_clopath_buffers()65 nest::ClopathArchivingNode::init_clopath_buffers()
66 {
67   // Implementation of the delay of the convolved membrane potentials. This
68   // delay is not described in Clopath et al. 2010 but is present in the code
69   // (https://senselab.med.yale.edu/ModelDB/showmodel.cshtml?model=144566) on
70   // ModelDB which was presumably used to create the figures in the paper.
71   // Since we write into the buffer before we read from it, we have to
72   // add 1 to the size of the buffers.
73   delayed_u_bars_idx_ = 0;
74   delay_u_bars_steps_ = Time::delay_ms_to_steps( delay_u_bars_ ) + 1;
75   delayed_u_bar_plus_.resize( delay_u_bars_steps_ );
76   delayed_u_bar_minus_.resize( delay_u_bars_steps_ );
77 
78   // initialize the ltp-history
79   ltd_hist_current_ = 0;
80   ltd_hist_len_ = kernel().connection_manager.get_max_delay() + 1;
81   ltd_history_.resize( ltd_hist_len_, histentry_extended( 0.0, 0.0, 0 ) );
82 }
83 
84 void
get_status(DictionaryDatum & d) const85 nest::ClopathArchivingNode::get_status( DictionaryDatum& d ) const
86 {
87   ArchivingNode::get_status( d );
88 
89   def< double >( d, names::A_LTD, A_LTD_ );
90   def< double >( d, names::A_LTP, A_LTP_ );
91   def< double >( d, names::u_ref_squared, u_ref_squared_ );
92   def< double >( d, names::theta_plus, theta_plus_ );
93   def< double >( d, names::theta_minus, theta_minus_ );
94   def< bool >( d, names::A_LTD_const, A_LTD_const_ );
95   def< double >( d, names::delay_u_bars, delay_u_bars_ );
96 }
97 
98 void
set_status(const DictionaryDatum & d)99 nest::ClopathArchivingNode::set_status( const DictionaryDatum& d )
100 {
101   ArchivingNode::set_status( d );
102 
103   // We need to preserve values in case invalid values are set
104   double new_A_LTD = A_LTD_;
105   double new_A_LTP = A_LTP_;
106   double new_theta_plus = theta_plus_;
107   double new_theta_minus = theta_minus_;
108   double new_u_ref_squared = u_ref_squared_;
109   double new_A_LTD_const = A_LTD_const_;
110   double new_delay_u_bars = delay_u_bars_;
111   updateValue< double >( d, names::A_LTD, new_A_LTD );
112   updateValue< double >( d, names::A_LTP, new_A_LTP );
113   updateValue< double >( d, names::u_ref_squared, new_u_ref_squared );
114   updateValue< double >( d, names::theta_plus, new_theta_plus );
115   updateValue< double >( d, names::theta_minus, new_theta_minus );
116   updateValue< bool >( d, names::A_LTD_const, new_A_LTD_const );
117   updateValue< double >( d, names::delay_u_bars, new_delay_u_bars );
118   A_LTD_ = new_A_LTD;
119   A_LTP_ = new_A_LTP;
120   u_ref_squared_ = new_u_ref_squared;
121 
122   if ( u_ref_squared_ <= 0 )
123   {
124     throw BadProperty( "Ensure that u_ref_squared > 0" );
125   }
126 
127   theta_plus_ = new_theta_plus;
128   theta_minus_ = new_theta_minus;
129   A_LTD_const_ = new_A_LTD_const;
130   delay_u_bars_ = new_delay_u_bars;
131 }
132 
133 double
get_LTD_value(double t)134 nest::ClopathArchivingNode::get_LTD_value( double t )
135 {
136   std::vector< histentry_extended >::iterator runner;
137   if ( ltd_history_.empty() || t < 0.0 )
138   {
139     return 0.0;
140   }
141   else
142   {
143     runner = ltd_history_.begin();
144     while ( runner != ltd_history_.end() )
145     {
146       if ( fabs( t - runner->t_ ) < kernel().connection_manager.get_stdp_eps() )
147       {
148         return runner->dw_;
149       }
150       ( runner->access_counter_ )++;
151       ++runner;
152     }
153   }
154   // Return zero if there is no entry at time t
155   return 0.0;
156 }
157 
158 void
get_LTP_history(double t1,double t2,std::deque<histentry_extended>::iterator * start,std::deque<histentry_extended>::iterator * finish)159 nest::ClopathArchivingNode::get_LTP_history( double t1,
160   double t2,
161   std::deque< histentry_extended >::iterator* start,
162   std::deque< histentry_extended >::iterator* finish )
163 {
164   *finish = ltp_history_.end();
165   if ( ltp_history_.empty() )
166   {
167     *start = *finish;
168     return;
169   }
170   else
171   {
172     std::deque< histentry_extended >::iterator runner = ltp_history_.begin();
173     // To have a well defined discretization of the integral, we make sure
174     // that we exclude the entry at t1 but include the one at t2 by subtracting
175     // a small number so that runner->t_ is never equal to t1 or t2.
176     while ( ( runner != ltp_history_.end() ) && ( runner->t_ - 1.0e-6 < t1 ) )
177     {
178       ++runner;
179     }
180     *start = runner;
181     while ( ( runner != ltp_history_.end() ) && ( runner->t_ - 1.0e-6 < t2 ) )
182     {
183       ( runner->access_counter_ )++;
184       ++runner;
185     }
186     *finish = runner;
187   }
188 }
189 
190 void
write_clopath_history(Time const & t_sp,double u,double u_bar_plus,double u_bar_minus,double u_bar_bar)191 nest::ClopathArchivingNode::write_clopath_history( Time const& t_sp,
192   double u,
193   double u_bar_plus,
194   double u_bar_minus,
195   double u_bar_bar )
196 {
197   const double t_ms = t_sp.get_ms();
198 
199   // write u_bar_[plus/minus] in ring buffer
200   delayed_u_bar_plus_[ delayed_u_bars_idx_ ] = u_bar_plus;
201 
202   delayed_u_bar_minus_[ delayed_u_bars_idx_ ] = u_bar_minus;
203 
204   // increment pointer
205   delayed_u_bars_idx_ = ( delayed_u_bars_idx_ + 1 ) % delay_u_bars_steps_;
206 
207   // read oldest values from buffers
208   double del_u_bar_plus = delayed_u_bar_plus_[ delayed_u_bars_idx_ ];
209   double del_u_bar_minus = delayed_u_bar_minus_[ delayed_u_bars_idx_ ];
210 
211   // save data for Clopath STDP if necessary
212   if ( ( u > theta_plus_ ) && ( del_u_bar_plus > theta_minus_ ) )
213   {
214     write_LTP_history( t_ms, u, del_u_bar_plus );
215   }
216 
217   if ( del_u_bar_minus > theta_minus_ )
218   {
219     write_LTD_history( t_ms, del_u_bar_minus, u_bar_bar );
220   }
221 }
222 
223 void
write_LTD_history(const double t_ltd_ms,double u_bar_minus,double u_bar_bar)224 nest::ClopathArchivingNode::write_LTD_history( const double t_ltd_ms, double u_bar_minus, double u_bar_bar )
225 {
226   if ( n_incoming_ )
227   {
228     const double dw = A_LTD_const_ ? A_LTD_ * ( u_bar_minus - theta_minus_ ) : A_LTD_ * u_bar_bar * u_bar_bar
229         * ( u_bar_minus - theta_minus_ ) / u_ref_squared_;
230     ltd_history_[ ltd_hist_current_ ] = histentry_extended( t_ltd_ms, dw, 0 );
231     ltd_hist_current_ = ( ltd_hist_current_ + 1 ) % ltd_hist_len_;
232   }
233 }
234 
235 void
write_LTP_history(const double t_ltp_ms,double u,double u_bar_plus)236 nest::ClopathArchivingNode::write_LTP_history( const double t_ltp_ms, double u, double u_bar_plus )
237 {
238   if ( n_incoming_ )
239   {
240     // prune all entries from history which are no longer needed
241     // except the penultimate one. we might still need it.
242     while ( ltp_history_.size() > 1 )
243     {
244       if ( ltp_history_.front().access_counter_ >= n_incoming_ )
245       {
246         ltp_history_.pop_front();
247       }
248       else
249       {
250         break;
251       }
252     }
253     // dw is not the change of the synaptic weight since the factor
254     // x_bar is not included (but later in the synapse)
255     const double dw = A_LTP_ * ( u - theta_plus_ ) * ( u_bar_plus - theta_minus_ ) * Time::get_resolution().get_ms();
256     ltp_history_.push_back( histentry_extended( t_ltp_ms, dw, 0 ) );
257   }
258 }
259 
260 } // of namespace nest
261