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