NEST main@caf0ae8
 
Loading...
Searching...
No Matches
eprop_synapse_bsshslm_2020.h
Go to the documentation of this file.
1/*
2 * eprop_synapse_bsshslm_2020.h
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#ifndef EPROP_SYNAPSE_BSSHSLM_2020_H
24#define EPROP_SYNAPSE_BSSHSLM_2020_H
25
26// nestkernel
27#include "connection.h"
28#include "connector_base.h"
30#include "target_identifier.h"
31#include "weight_optimizer.h"
32
33namespace nest
34{
35
36/* BeginUserDocs: synapse, abstract, learning, Bellec, e-prop plasticity, 3-factor
37
38Short description
39+++++++++++++++++
40
41Synapse type for e-prop plasticity
42
43Description
44+++++++++++
45
46``eprop_synapse_bsshslm_2020`` is an implementation of a connector model to create synapses between postsynaptic
47neurons :math:`j` and presynaptic neurons :math:`i` for eligibility propagation (e-prop) plasticity.
48
49E-prop plasticity was originally introduced and implemented in TensorFlow in :footcite:p:`Bellec2020`.
50
51The suffix ``_bsshslm_2020`` follows the NEST convention to indicate in the
52model name the paper that introduced it by the first letter of the authors' last
53names and the publication year.
54
55The e-prop synapse collects the presynaptic spikes needed for calculating the
56weight update. When it is time to update, it triggers the calculation of the
57gradient which is specific to the post-synaptic neuron and is thus defined there.
58
59Eventually, it optimizes the weight with the specified optimizer.
60
61E-prop synapses require archiving of continuous quantities. Therefore e-prop
62synapses can only be connected to neuron models that are capable of
63archiving. So far, compatible models are ``eprop_iaf_bsshslm_2020``,
64``eprop_iaf_adapt_bsshslm_2020``, and ``eprop_readout_bsshslm_2020``.
65
66For more information, see the following topics:
67
68* other e-prop plasticity models:
69
70 * :doc:`eprop_iaf_bsshslm_2020<../models/eprop_iaf_bsshslm_2020/>`
71 * :doc:`eprop_iaf_adapt_bsshslm_2020<../models/eprop_iaf_adapt_bsshslm_2020/>`
72 * :doc:`eprop_readout_bsshslm_2020<../models/eprop_readout_bsshslm_2020/>`
73 * :doc:`eprop_learning_signal_connection_bsshslm_2020<../models/eprop_learning_signal_connection_bsshslm_2020/>`
74
75* :doc:`weight optimizer<../models/weight_optimizer/>`
76* triggering synaptic plasticity computations to reduce memory usage via the
77 :ref:`flush event mechanism<flush_event_mechanism>`
78
79Details on the event-based NEST implementation of e-prop can be found in :footcite:p:`KorcsakGorzo2025`.
80
81.. warning::
82
83 This synaptic plasticity rule does not take
84 :ref:`precise spike timing <sim_precise_spike_times>` into
85 account. When calculating the weight update, the precise spike time part
86 of the timestamp is ignored.
87
88Parameters
89++++++++++
90
91The following parameters can be set in the status dictionary.
92
93==================== ======= =============== ========= ======================================================
94**Common e-prop synapse parameters**
95-------------------------------------------------------------------------------------------------------------
96Parameter Unit Math equivalent Default Description
97==================== ======= =============== ========= ======================================================
98``average_gradient`` Boolean ``False`` If ``True``, average the gradient over the learning
99 window
100``optimizer`` {} Dictionary of optimizer parameters
101==================== ======= =============== ========= ======================================================
102
103============= ==== ========================= ======= ================================
104**Individual synapse parameters**
105-------------------------------------------------------------------------------------
106Parameter Unit Math equivalent Default Description
107============= ==== ========================= ======= ================================
108``delay`` ms :math:`d_{ji}` 1.0 Dendritic delay
109``weight`` pA :math:`W_{ji}` 1.0 Initial value of synaptic weight
110============= ==== ========================= ======= ================================
111
112================= ==== ========================= ======= ==============================
113**Individual e-prop synapse parameters**
114---------------------------------------------------------------------------------------
115Parameter Unit Math equivalent Default Description
116================= ==== ========================= ======= ==============================
117``tau_m_readout`` ms :math:`\tau_\text{m,out}` 10.0 Time constant for low-pass
118 filtering of eligibility trace
119================= ==== ========================= ======= ==============================
120
121Recordables
122+++++++++++
123
124The following variables can be recorded.
125
126================== ==== =============== ============= ===============
127**Synapse recordables**
128---------------------------------------------------------------------
129State variable Unit Math equivalent Initial value Description
130================== ==== =============== ============= ===============
131``weight`` pA :math:`B_{jk}` 1.0 Synaptic weight
132================== ==== =============== ============= ===============
133
134Usage
135+++++
136
137This model can only be used in combination with the other e-prop models
138and the network architecture requires specific wiring, input, and output.
139The usage is demonstrated in several
140:doc:`supervised regression and classification tasks <../auto_examples/eprop_plasticity/index>`
141reproducing among others the original proof-of-concept tasks in :footcite:p:`Bellec2020`.
142
143Transmits
144+++++++++
145
146SpikeEvent, DSSpikeEvent
147
148References
149++++++++++
150
151.. footbibliography::
152
153See also
154++++++++
155
156Examples using this model
157+++++++++++++++++++++++++
158
159.. listexamples:: eprop_synapse_bsshslm_2020
160
161EndUserDocs */
162
207
209void register_eprop_synapse_bsshslm_2020( const std::string& name );
210
237template < typename targetidentifierT >
238class eprop_synapse_bsshslm_2020 : public Connection< targetidentifierT >
239{
240
241public:
244
247
249 static constexpr bool supports_flush_event = true;
250
259
262
265
268
271
274
277
282
284 void get_status( Dictionary& d ) const;
285
287 void set_status( const Dictionary& d, ConnectorModel& cm );
288
290 bool send( Event& e, size_t thread, const EpropSynapseBSSHSLM2020CommonProperties& cp );
291
294 {
295 public:
297
298 size_t
300 {
301 return invalid_port;
302 }
303
304 size_t
306 {
307 return invalid_port;
308 }
309 };
310
316 void check_connection( Node& s, Node& t, size_t receptor_type, const CommonPropertiesType& cp );
317
319 void
320 set_weight( const double w )
321 {
322 weight_ = w;
323 }
324
326 void delete_optimizer();
327
328private:
330 double weight_;
331
333 double gradient_;
334
337
340
343
346
349
352
354 double kappa_;
355
358
360 std::vector< long > presyn_isis_;
361
368};
369
370template < typename targetidentifierT >
372
373// Explicitly declare specializations of Connector methods that need to do special things for eprop_synapse_bsshslm_2020
374template <>
375void Connector< eprop_synapse_bsshslm_2020< TargetIdentifierPtrRport > >::disable_connection( const size_t lcid );
376
377template <>
378void Connector< eprop_synapse_bsshslm_2020< TargetIdentifierIndex > >::disable_connection( const size_t lcid );
379
380template <>
382
383template <>
385
386template < typename targetidentifierT >
389 , weight_( 1.0 )
390 , gradient_( 0.0 )
391 , t_spike_previous_( 0 )
392 , previous_was_flush_event_( false )
393 , t_previous_update_( 0 )
394 , t_next_update_( 0 )
395 , t_previous_trigger_spike_( 0 )
396 , tau_m_readout_( 10.0 )
397 , kappa_( std::exp( -Time::get_resolution().get_ms() / tau_m_readout_ ) )
398 , is_recurrent_to_recurrent_conn_( false )
399 , optimizer_( nullptr )
400{
401}
402
403template < typename targetidentifierT >
407
408// This copy constructor is used to create instances from prototypes.
409// Therefore, only parameter values are copied.
410template < typename targetidentifierT >
412 : ConnectionBase( es )
413 , weight_( es.weight_ )
414 , gradient_( es.gradient_ )
415 , t_spike_previous_( 0 )
416 , previous_was_flush_event_( false )
417 , t_previous_update_( 0 )
418 , t_next_update_( kernel().simulation_manager.get_eprop_update_interval().get_steps() )
419 , t_previous_trigger_spike_( 0 )
420 , tau_m_readout_( es.tau_m_readout_ )
421 , kappa_( std::exp( -Time::get_resolution().get_ms() / tau_m_readout_ ) )
422 , is_recurrent_to_recurrent_conn_( es.is_recurrent_to_recurrent_conn_ )
423 , optimizer_( es.optimizer_ )
424{
425}
426
427// This copy assignment operator is used to write a connection into the connection array.
428template < typename targetidentifierT >
431{
432 if ( this == &es )
433 {
434 return *this;
435 }
436
437 ConnectionBase::operator=( es );
438
439 weight_ = es.weight_;
440 gradient_ = es.gradient_;
441 t_spike_previous_ = es.t_spike_previous_;
442 previous_was_flush_event_ = es.previous_was_flush_event_;
443 t_previous_update_ = es.t_previous_update_;
444 t_next_update_ = es.t_next_update_;
445 t_previous_trigger_spike_ = es.t_previous_trigger_spike_;
446 tau_m_readout_ = es.tau_m_readout_;
447 kappa_ = es.kappa_;
448 is_recurrent_to_recurrent_conn_ = es.is_recurrent_to_recurrent_conn_;
449 optimizer_ = es.optimizer_;
450
451 return *this;
452}
453
454template < typename targetidentifierT >
456 : ConnectionBase( es )
457 , weight_( es.weight_ )
458 , gradient_( es.gradient_ )
459 , t_spike_previous_( 0 )
460 , previous_was_flush_event_( false )
461 , t_previous_update_( 0 )
462 , t_next_update_( es.t_next_update_ )
463 , t_previous_trigger_spike_( 0 )
464 , tau_m_readout_( es.tau_m_readout_ )
465 , kappa_( es.kappa_ )
466 , is_recurrent_to_recurrent_conn_( es.is_recurrent_to_recurrent_conn_ )
467 , optimizer_( es.optimizer_ )
468{
469 es.optimizer_ = nullptr;
470}
471
472// This move assignment operator is used to write a connection into the connection array.
473template < typename targetidentifierT >
476{
477 if ( this == &es )
478 {
479 return *this;
480 }
481
482 ConnectionBase::operator=( es );
483
484 weight_ = es.weight_;
485 gradient_ = es.gradient_;
486 t_spike_previous_ = es.t_spike_previous_;
487 previous_was_flush_event_ = es.previous_was_flush_event_;
488 t_previous_update_ = es.t_previous_update_;
489 t_next_update_ = es.t_next_update_;
490 t_previous_trigger_spike_ = es.t_previous_trigger_spike_;
491 tau_m_readout_ = es.tau_m_readout_;
492 kappa_ = es.kappa_;
493 is_recurrent_to_recurrent_conn_ = es.is_recurrent_to_recurrent_conn_;
494
495 optimizer_ = es.optimizer_;
496 es.optimizer_ = nullptr;
497
498 return *this;
499}
500
501template < typename targetidentifierT >
502inline void
504 Node& t,
505 size_t receptor_type,
506 const CommonPropertiesType& cp )
507{
508 // When we get here, delay has been set so we can check it.
509 if ( get_delay_steps() != 1 )
510 {
511 throw IllegalConnection( "eprop synapses currently require a delay of one simulation step" );
512 }
513
514 ConnTestDummyNode dummy_target;
515 ConnectionBase::check_connection_( dummy_target, s, t, receptor_type );
516
519
520 optimizer_ = cp.optimizer_cp_->get_optimizer();
521}
522
523template < typename targetidentifierT >
524inline void
526{
527 delete optimizer_;
528 // do not set to nullptr to allow detection of double deletion
529}
530
531template < typename targetidentifierT >
532bool
534 size_t thread,
536{
537 Node* target = get_target( thread );
538 assert( target );
539
540 const long t_spike = e.get_stamp().get_steps();
541 const bool is_flush_event = e.is_flush_event();
542
543 if ( previous_was_flush_event_ )
544 {
545 if ( is_flush_event )
546 {
547 return false;
548 }
549
550 t_spike_previous_ = t_spike;
551 t_previous_trigger_spike_ = t_spike;
552 }
553
554 const long update_interval = kernel().simulation_manager.get_eprop_update_interval().get_steps();
555 const long shift = target->get_shift();
556
557 const long interval_step = ( t_spike - shift ) % update_interval;
558
559 if ( target->is_eprop_recurrent_node() and interval_step == 0 )
560 {
561 return false;
562 }
563
564 if ( t_previous_trigger_spike_ == 0 )
565 {
566 t_previous_trigger_spike_ = t_spike;
567 }
568
569 if ( t_spike_previous_ > 0 )
570 {
571 const long presyn_isi =
572 std::min( t_spike, t_next_update_ + shift ) - std::min( t_spike_previous_, t_next_update_ + shift );
573
574 if ( presyn_isi > 0 )
575 {
576 presyn_isis_.push_back( presyn_isi );
577 }
578 }
579
580 if ( t_spike > t_next_update_ + shift )
581 {
582 const long idx_current_update = ( t_spike - shift ) / update_interval;
583 const long t_current_update = idx_current_update * update_interval;
584
585 target->write_update_to_history( t_previous_update_, t_current_update, is_flush_event, previous_was_flush_event_ );
586
587 if ( not presyn_isis_.empty() and t_spike_previous_ > 0 )
588 {
589 gradient_ += target->compute_gradient(
590 presyn_isis_, t_previous_update_, t_previous_trigger_spike_, kappa_, cp.average_gradient_ );
591 }
592
593 if ( not is_flush_event )
594 {
595 weight_ = optimizer_->optimized_weight( *cp.optimizer_cp_, idx_current_update, gradient_, weight_ );
596 gradient_ = 0.0;
597 }
598
599 t_previous_update_ = t_current_update;
600 t_next_update_ = t_current_update + update_interval;
601
602 t_previous_trigger_spike_ = t_spike;
603 }
604 else
605 {
606 if ( not is_flush_event and previous_was_flush_event_ )
607 {
608 const long idx_current_update = ( t_spike - shift ) / update_interval;
609 const long t_current_update = idx_current_update * update_interval;
610
611 target->write_update_to_history(
612 t_previous_update_, t_current_update, is_flush_event, previous_was_flush_event_ );
613
614 weight_ = optimizer_->optimized_weight( *cp.optimizer_cp_, idx_current_update, gradient_, weight_ );
615 gradient_ = 0.0;
616 }
617 }
618
619 if ( not is_flush_event )
620 {
621 t_spike_previous_ = t_spike;
622
623 e.set_receiver( *target );
624 e.set_weight( weight_ );
625 e.set_delay_steps( get_delay_steps() );
626 e.set_rport( get_rport() );
627 e();
628 }
629
630 previous_was_flush_event_ = is_flush_event;
631
632 return true;
633}
634
635template < typename targetidentifierT >
636void
638{
639 ConnectionBase::get_status( d );
640 d[ names::weight ] = weight_;
641 d[ names::tau_m_readout ] = tau_m_readout_;
642 d[ names::size_of ] = static_cast< long >( sizeof( *this ) );
643
644 Dictionary optimizer_dict;
645
646 // The default_connection_ has no optimizer, therefore we need to protect it
647 if ( optimizer_ )
648 {
649 optimizer_->get_status( optimizer_dict );
650 d[ names::optimizer ] = optimizer_dict;
651 }
652}
653
654template < typename targetidentifierT >
655void
657{
658 ConnectionBase::set_status( d, cm );
659 if ( d.known( names::optimizer ) and optimizer_ )
660 {
661 // We must pass here if called by SetDefaults. In that case, the user will get an error
662 // message because the parameters for the synapse-specific optimizer have not been accessed.
663 optimizer_->set_status( d.get< Dictionary >( names::optimizer ) );
664 }
665
666 d.update_value( names::weight, weight_ );
667
668 if ( d.update_value( names::tau_m_readout, tau_m_readout_ ) )
669 {
670 if ( tau_m_readout_ <= 0 )
671 {
672 throw BadProperty( "Membrane time constant of readout neuron tau_m_readout > 0 required." );
673 }
674 kappa_ = std::exp( -Time::get_resolution().get_ms() / tau_m_readout_ );
675 }
676
677 const auto& gcm =
679 const CommonPropertiesType& epcp = gcm.get_common_properties();
680 if ( weight_ < epcp.optimizer_cp_->get_Wmin() )
681 {
682 throw BadProperty( "Minimal weight Wmin ≤ weight required." );
683 }
684
685 if ( weight_ > epcp.optimizer_cp_->get_Wmax() )
686 {
687 throw BadProperty( "weight ≤ maximal weight Wmax required." );
688 }
689}
690
691} // namespace nest
692
693#endif // EPROP_SYNAPSE_BSSHSLM_2020_H
Dictionary class for interface to Python and C++ API.
Definition dictionary.h:213
Exception to be thrown if a status parameter is incomplete or inconsistent.
Definition exceptions.h:680
Class containing the common properties for all connections of a certain type.
Definition common_synapse_properties.h:50
Base class for dummy nodes used in connection testing.
Definition connection.h:67
Base class for representing connections.
Definition connection.h:110
long get_delay_steps() const
Return the delay of the connection in steps.
Definition connection.h:181
Node * get_target(const size_t tid) const
Definition connection.h:239
size_t get_rport() const
Definition connection.h:244
double get_delay() const
Return the delay of the connection in ms.
Definition connection.h:172
Definition connector_model.h:69
Homogeneous connector, contains synapses of one particular type (syn_id_).
Definition connector_base.h:221
"Callback request event" for use in Device.
Definition event.h:521
Base class implementing common properties for e-prop synapses.
Definition eprop_synapse_bsshslm_2020.h:177
void get_status(Dictionary &d) const
Get parameter dictionary.
Definition eprop_synapse_bsshslm_2020.cpp:58
~EpropSynapseBSSHSLM2020CommonProperties()
Destructor.
Definition eprop_synapse_bsshslm_2020.cpp:52
WeightOptimizerCommonProperties * optimizer_cp_
Pointer to common properties object for weight optimizer.
Definition eprop_synapse_bsshslm_2020.h:205
EpropSynapseBSSHSLM2020CommonProperties & operator=(const EpropSynapseBSSHSLM2020CommonProperties &)=delete
Assignment operator.
EpropSynapseBSSHSLM2020CommonProperties()
Definition eprop_synapse_bsshslm_2020.cpp:37
void set_status(const Dictionary &d, ConnectorModel &cm)
Update values in parameter dictionary.
Definition eprop_synapse_bsshslm_2020.cpp:68
bool average_gradient_
If True, average the gradient over the learning window.
Definition eprop_synapse_bsshslm_2020.h:198
Encapsulate information sent between nodes.
Definition event.h:103
Definition connector_model.h:152
To be thrown if a connection is not possible.
Definition exceptions.h:490
Base class for all NEST network objects.
Definition node.h:99
virtual void register_eprop_connection()
Registers an eprop connection.
Definition node.cpp:217
virtual void initialize_update_history()
Initializes the update history.
Definition node.cpp:223
Time get_eprop_update_interval() const
Definition simulation_manager.h:343
Event for spike information.
Definition event.h:418
Definition nest_time.h:135
static Time get_resolution()
Definition nest_time.h:325
long get_steps() const
Definition nest_time.h:504
Base class implementing common properties of a weight optimizer model.
Definition weight_optimizer.h:154
virtual WeightOptimizer * get_optimizer() const =0
Get optimizer.
Base class implementing a weight optimizer model.
Definition weight_optimizer.h:238
Dummy node for testing the connection.
Definition eprop_synapse_bsshslm_2020.h:294
size_t handles_test_event(SpikeEvent &, size_t)
Check if the node can handle a particular event and receptor type.
Definition eprop_synapse_bsshslm_2020.h:299
size_t handles_test_event(DSSpikeEvent &, size_t)
Definition eprop_synapse_bsshslm_2020.h:305
Class implementing a synapse model for e-prop plasticity.
Definition eprop_synapse_bsshslm_2020.h:239
double weight_
Synaptic weight.
Definition eprop_synapse_bsshslm_2020.h:330
Connection< targetidentifierT > ConnectionBase
Type of the connection base.
Definition eprop_synapse_bsshslm_2020.h:246
void check_connection(Node &s, Node &t, size_t receptor_type, const CommonPropertiesType &cp)
Check if the target accepts the event and receptor type requested by the sender.
Definition eprop_synapse_bsshslm_2020.h:503
eprop_synapse_bsshslm_2020 & operator=(const eprop_synapse_bsshslm_2020 &)
Assignment operator.
Definition eprop_synapse_bsshslm_2020.h:430
long t_previous_update_
The time step when the previous e-prop update was.
Definition eprop_synapse_bsshslm_2020.h:342
WeightOptimizer * optimizer_
Optimizer.
Definition eprop_synapse_bsshslm_2020.h:367
static constexpr bool supports_flush_event
Whether this connection type supports flush events.
Definition eprop_synapse_bsshslm_2020.h:249
~eprop_synapse_bsshslm_2020()
Destructor.
Definition eprop_synapse_bsshslm_2020.h:404
void set_weight(const double w)
Set the synaptic weight to the provided value.
Definition eprop_synapse_bsshslm_2020.h:320
long t_next_update_
The time step when the next e-prop update will be.
Definition eprop_synapse_bsshslm_2020.h:345
double kappa_
Low-pass filter of the eligibility trace.
Definition eprop_synapse_bsshslm_2020.h:354
bool is_recurrent_to_recurrent_conn_
If this connection is between two recurrent neurons.
Definition eprop_synapse_bsshslm_2020.h:357
void delete_optimizer()
Delete optimizer.
Definition eprop_synapse_bsshslm_2020.h:525
long t_spike_previous_
The time step when the previous spike arrived.
Definition eprop_synapse_bsshslm_2020.h:336
void get_status(Dictionary &d) const
Get parameter dictionary.
Definition eprop_synapse_bsshslm_2020.h:637
static constexpr ConnectionModelProperties properties
Properties of the connection model.
Definition eprop_synapse_bsshslm_2020.h:256
EpropSynapseBSSHSLM2020CommonProperties CommonPropertiesType
Type of the common synapse properties.
Definition eprop_synapse_bsshslm_2020.h:243
bool send(Event &e, size_t thread, const EpropSynapseBSSHSLM2020CommonProperties &cp)
Send the spike event.
Definition eprop_synapse_bsshslm_2020.h:533
eprop_synapse_bsshslm_2020()
Default constructor.
Definition eprop_synapse_bsshslm_2020.h:387
double gradient_
Gradient used for the weight update.
Definition eprop_synapse_bsshslm_2020.h:333
std::vector< long > presyn_isis_
Vector of presynaptic inter-spike-intervals.
Definition eprop_synapse_bsshslm_2020.h:360
double tau_m_readout_
Time constant for low-pass filtering the eligibility trace.
Definition eprop_synapse_bsshslm_2020.h:351
bool previous_was_flush_event_
Previous event was a flush event.
Definition eprop_synapse_bsshslm_2020.h:339
void set_status(const Dictionary &d, ConnectorModel &cm)
Update values in parameter dictionary.
Definition eprop_synapse_bsshslm_2020.h:656
long t_previous_trigger_spike_
The time step when the spike arrived that triggered the previous e-prop update.
Definition eprop_synapse_bsshslm_2020.h:348
SimulationManager simulation_manager
Definition kernel_manager.h:237
virtual size_t handles_test_event(SpikeEvent &, size_t receptor_type)
Check if the node can handle a particular event and receptor type.
Definition node.cpp:271
const std::string optimizer("optimizer")
const std::string weight("weight")
const std::string tau_m_readout("tau_m_readout")
const std::string size_of("sizeof")
Namespace for the NEST simulation kernel.
Definition beta_normalization_factor.h:33
KernelManager & kernel()
Definition kernel_manager.h:311
ConnectionModelProperties
Definition connector_model.h:49
void register_eprop_synapse_bsshslm_2020(const std::string &name)
Register the eprop synapse model.
Definition eprop_synapse_bsshslm_2020.cpp:32
constexpr size_t invalid_port
Value for invalid connection port number.
Definition nest_types.h:141