From b121b05b7987913963452708d47650919df4debc Mon Sep 17 00:00:00 2001 From: "C.A.P. Linssen" Date: Thu, 13 May 2021 11:04:59 +0200 Subject: [PATCH 1/6] move precise spike time offset from Event to Time class --- models/cont_delay_synapse.h | 2 +- models/iaf_psc_alpha_canon.cpp | 2 +- models/iaf_psc_alpha_ps.cpp | 2 +- models/iaf_psc_delta_ps.cpp | 2 +- models/iaf_psc_exp_ps.cpp | 2 +- models/iaf_psc_exp_ps_lossless.cpp | 2 +- models/multimeter.cpp | 2 +- models/parrot_neuron_ps.cpp | 2 +- nestkernel/event.cpp | 12 ++- nestkernel/event.h | 95 ++++++----------------- nestkernel/event_delivery_manager.cpp | 4 +- nestkernel/event_delivery_manager_impl.h | 2 +- nestkernel/nest_time.h | 58 +++++++++++++- nestkernel/recording_backend_arbor.cpp | 2 +- nestkernel/recording_backend_ascii.cpp | 4 +- nestkernel/recording_backend_memory.cpp | 4 +- nestkernel/recording_backend_screen.cpp | 4 +- nestkernel/recording_backend_sionlib.cpp | 2 +- nestkernel/structural_plasticity_node.cpp | 3 +- 19 files changed, 105 insertions(+), 101 deletions(-) diff --git a/models/cont_delay_synapse.h b/models/cont_delay_synapse.h index 532ed7580e..a6a42b2894 100644 --- a/models/cont_delay_synapse.h +++ b/models/cont_delay_synapse.h @@ -213,7 +213,7 @@ cont_delay_synapse< targetidentifierT >::send( Event& e, thread t, const CommonS e.set_receiver( *get_target( t ) ); e.set_weight( weight_ ); e.set_rport( get_rport() ); - double orig_event_offset = e.get_offset(); + double orig_event_offset = e.get_stamp().get_offset(); double total_offset = orig_event_offset + delay_offset_; // As far as i have seen, offsets are outside of tics regime provided // by the Time-class to allow more precise spike-times, hence comparing diff --git a/models/iaf_psc_alpha_canon.cpp b/models/iaf_psc_alpha_canon.cpp index 8664d2693c..4262d3af71 100644 --- a/models/iaf_psc_alpha_canon.cpp +++ b/models/iaf_psc_alpha_canon.cpp @@ -452,7 +452,7 @@ nest::iaf_psc_alpha_canon::handle( SpikeEvent& e ) const long Tdeliver = e.get_stamp().get_steps() + e.get_delay_steps() - 1; B_.events_.add_spike( e.get_rel_delivery_steps( nest::kernel().simulation_manager.get_slice_origin() ), Tdeliver, - e.get_offset(), + e.get_stamp().get_offset(), e.get_weight() * e.get_multiplicity() ); } diff --git a/models/iaf_psc_alpha_ps.cpp b/models/iaf_psc_alpha_ps.cpp index 34809438de..aff016fe91 100644 --- a/models/iaf_psc_alpha_ps.cpp +++ b/models/iaf_psc_alpha_ps.cpp @@ -476,7 +476,7 @@ nest::iaf_psc_alpha_ps::handle( SpikeEvent& e ) B_.events_.add_spike( e.get_rel_delivery_steps( nest::kernel().simulation_manager.get_slice_origin() ), Tdeliver, - e.get_offset(), + e.get_stamp().get_offset(), e.get_weight() * e.get_multiplicity() ); } diff --git a/models/iaf_psc_delta_ps.cpp b/models/iaf_psc_delta_ps.cpp index 51f80f31c3..b65ba21cab 100644 --- a/models/iaf_psc_delta_ps.cpp +++ b/models/iaf_psc_delta_ps.cpp @@ -527,7 +527,7 @@ iaf_psc_delta_ps::handle( SpikeEvent& e ) const long Tdeliver = e.get_stamp().get_steps() + e.get_delay_steps() - 1; B_.events_.add_spike( e.get_rel_delivery_steps( kernel().simulation_manager.get_slice_origin() ), Tdeliver, - e.get_offset(), + e.get_stamp().get_offset(), e.get_weight() * e.get_multiplicity() ); } diff --git a/models/iaf_psc_exp_ps.cpp b/models/iaf_psc_exp_ps.cpp index 691504558d..989a65ed10 100644 --- a/models/iaf_psc_exp_ps.cpp +++ b/models/iaf_psc_exp_ps.cpp @@ -448,7 +448,7 @@ nest::iaf_psc_exp_ps::handle( SpikeEvent& e ) B_.events_.add_spike( e.get_rel_delivery_steps( nest::kernel().simulation_manager.get_slice_origin() ), Tdeliver, - e.get_offset(), + e.get_stamp().get_offset(), e.get_weight() * e.get_multiplicity() ); } diff --git a/models/iaf_psc_exp_ps_lossless.cpp b/models/iaf_psc_exp_ps_lossless.cpp index 4d370cb0ef..b2b93a6435 100644 --- a/models/iaf_psc_exp_ps_lossless.cpp +++ b/models/iaf_psc_exp_ps_lossless.cpp @@ -492,7 +492,7 @@ nest::iaf_psc_exp_ps_lossless::handle( SpikeEvent& e ) B_.events_.add_spike( e.get_rel_delivery_steps( nest::kernel().simulation_manager.get_slice_origin() ), Tdeliver, - e.get_offset(), + e.get_stamp().get_offset(), e.get_weight() * e.get_multiplicity() ); } diff --git a/models/multimeter.cpp b/models/multimeter.cpp index a24783d40b..6e4b206889 100644 --- a/models/multimeter.cpp +++ b/models/multimeter.cpp @@ -224,7 +224,7 @@ multimeter::handle( DataLoggingReply& reply ) reply.set_stamp( info[ j ].timestamp ); // const index sender = reply.get_sender_node_id(); // const Time stamp = reply.get_stamp(); - // const double offset = reply.get_offset(); + // const double offset = reply.get_stamp().get_offset(); write( reply, info[ j ].data, RecordingBackend::NO_LONG_VALUES ); } diff --git a/models/parrot_neuron_ps.cpp b/models/parrot_neuron_ps.cpp index d5381b740e..15eeb59a97 100644 --- a/models/parrot_neuron_ps.cpp +++ b/models/parrot_neuron_ps.cpp @@ -124,7 +124,7 @@ parrot_neuron_ps::handle( SpikeEvent& e ) // parrot ignores weight of incoming connection, store multiplicity B_.events_.add_spike( e.get_rel_delivery_steps( nest::kernel().simulation_manager.get_slice_origin() ), Tdeliver, - e.get_offset(), + e.get_stamp().get_offset(), static_cast< double >( e.get_multiplicity() ) ); } } diff --git a/nestkernel/event.cpp b/nestkernel/event.cpp index d28e0fb3a5..572bb56be9 100644 --- a/nestkernel/event.cpp +++ b/nestkernel/event.cpp @@ -45,11 +45,15 @@ Event::Event() , d_( 1 ) , stamp_( Time::step( 0 ) ) , stamp_steps_( 0 ) - , offset_( 0.0 ) , w_( 0.0 ) { } +index +Event::get_receiver_node_id() const +{ + return receiver_->get_node_id(); +} void SpikeEvent::operator()() { @@ -121,9 +125,3 @@ void DiffusionConnectionEvent::operator()() receiver_->handle( *this ); } } - -nest::index -nest::Event::get_receiver_node_id( void ) const -{ - return receiver_->get_node_id(); -} diff --git a/nestkernel/event.h b/nestkernel/event.h index e79eeb38dc..e76ac443bf 100644 --- a/nestkernel/event.h +++ b/nestkernel/event.h @@ -106,14 +106,14 @@ class Event virtual void operator()() = 0; /** - * Change pointer to receiving Node. + * Return reference to receiving Node. */ - void set_receiver( Node& ); + Node& get_receiver() const; /** - * Return reference to receiving Node. + * Change pointer to receiving Node. */ - Node& get_receiver() const; + void set_receiver( Node& ); /** * Return node ID of receiving Node. @@ -143,21 +143,15 @@ class Event /** * Return time stamp of the event. * The stamp denotes the time when the event was created. - * The resolution of Stamp is limited by the time base of the - * simulation kernel (@see class nest::Time). - * If this resolution is not fine enough, the creation time - * can be corrected by using the time attribute. */ Time const& get_stamp() const; /** - * Set the transmission delay of the event. - * The delay refers to the time until the event is - * expected to arrive at the receiver. - * @param t delay. + * Set the time stamp of the event. + * The time stamp refers to the time when the event + * was created. */ - - void set_delay_steps( delay ); + void set_stamp( Time const& ); /** * Return transmission delay of the event. @@ -166,6 +160,14 @@ class Event */ delay get_delay_steps() const; + /** + * Set the transmission delay of the event. + * The delay refers to the time until the event is + * expected to arrive at the receiver. + * @param d delay + */ + void set_delay_steps( delay d ); + /** * Relative spike delivery time in steps. * Returns the delivery time of the spike relative to a given @@ -188,14 +190,6 @@ class Event */ port get_port() const; - /** - * Return the receiver port number of the event. - * This function returns the number of the r-port over which the - * Event was sent. - * @note A return value of 0 indicates that the r-port is not used. - */ - rport get_rport() const; - /** * Set the port number. * Each event carries the number of the port over which the event @@ -206,35 +200,24 @@ class Event */ void set_port( port p ); + /** + * Return the receiver port number of the event. + * This function returns the number of the r-port over which the + * Event was sent. + * @note A return value of 0 indicates that the r-port is not used. + */ + rport get_rport() const; + /** * Set the receiver port number (r-port). * When a connection is established, the receiving Node may issue - * a port number (r-port) to distinguish the incomin + * a port number (r-port) to distinguish the incoming * connection. By the default, the r-port is not used and its port * number defaults to zero. * @param p Receiver port number of the connection, or 0 if unused. */ void set_rport( rport p ); - /** - * Return the creation time offset of the Event. - * Each Event carries the exact time of creation. This - * time need not coincide with an integral multiple of the - * temporal resolution. Rather, Events may be created at any point - * in time. - */ - double get_offset() const; - - /** - * Set the creation time of the Event. - * Each Event carries the exact time of creation in realtime. This - * time need not coincide with an integral multiple of the - * temporal resolution. Rather, Events may be created at any point - * in time. - * @param t Creation time in realtime. t has to be in [0, h). - */ - void set_offset( double t ); - /** * Return the weight. */ @@ -272,13 +255,6 @@ class Event */ bool is_valid() const; - /** - * Set the time stamp of the event. - * The time stamp refers to the time when the event - * was created. - */ - void set_stamp( Time const& ); - protected: index sender_node_id_; //!< node ID of sender or -1. /* @@ -336,15 +312,6 @@ class Event */ mutable long stamp_steps_; - /** - * Offset for precise spike times. - * offset_ specifies a correction to the creation time. - * If the resolution of stamp is not sufficiently precise, - * this attribute can be used to correct the creation time. - * offset_ has to be in [0, h). - */ - double offset_; - /** * Weight of the connection. */ @@ -1365,18 +1332,6 @@ Event::set_delay_steps( delay d ) d_ = d; } -inline double -Event::get_offset() const -{ - return offset_; -} - -inline void -Event::set_offset( double t ) -{ - offset_ = t; -} - inline port Event::get_port() const { diff --git a/nestkernel/event_delivery_manager.cpp b/nestkernel/event_delivery_manager.cpp index 0d0f4aa5e4..2b4ced652d 100644 --- a/nestkernel/event_delivery_manager.cpp +++ b/nestkernel/event_delivery_manager.cpp @@ -510,7 +510,7 @@ EventDeliveryManager::collocate_spike_data_buffers_( const thread tid, else { send_buffer[ send_buffer_position.idx( rank ) ].set( - ( *iiit ).get_tid(), ( *iiit ).get_syn_id(), ( *iiit ).get_lcid(), lag, ( *iiit ).get_offset() ); + ( *iiit ).get_tid(), ( *iiit ).get_syn_id(), ( *iiit ).get_lcid(), lag, ( *iiit ).get_stamp().get_offset() ); ( *iiit ).set_status( TARGET_ID_PROCESSED ); // mark entry for removal send_buffer_position.increase( rank ); } @@ -621,7 +621,7 @@ EventDeliveryManager::deliver_events_( const thread tid, const std::vector< Spik const SpikeDataT& spike_data = recv_buffer[ rank * send_recv_count_spike_data_per_rank + i ]; se.set_stamp( prepared_timestamps[ spike_data.get_lag() ] ); - se.set_offset( spike_data.get_offset() ); + se.set_offset( spike_data.get_stamp().get_offset() ); if ( not kernel().connection_manager.use_compressed_spikes() ) { diff --git a/nestkernel/event_delivery_manager_impl.h b/nestkernel/event_delivery_manager_impl.h index a0d03096d7..eeea7d2a86 100644 --- a/nestkernel/event_delivery_manager_impl.h +++ b/nestkernel/event_delivery_manager_impl.h @@ -133,7 +133,7 @@ EventDeliveryManager::send_off_grid_remote( thread tid, SpikeEvent& e, const lon // Unroll spike multiplicity as plastic synapses only handle individual spikes. for ( int i = 0; i < e.get_multiplicity(); ++i ) { - off_grid_spike_register_[ tid ][ assigned_tid ][ lag ].push_back( OffGridTarget( *it, e.get_offset() ) ); + off_grid_spike_register_[ tid ][ assigned_tid ][ lag ].push_back( OffGridTarget( *it, e.get_stamp().get_offset() ) ); } } } diff --git a/nestkernel/nest_time.h b/nestkernel/nest_time.h index 25798d03de..227f31e06c 100644 --- a/nestkernel/nest_time.h +++ b/nestkernel/nest_time.h @@ -172,12 +172,28 @@ class Time static tic_t compute_max(); ///////////////////////////////////////////////////////////// - // The data: longest integer for tics + // The data ///////////////////////////////////////////////////////////// protected: + /** + * Longest integer for tics. + * The resolution of tic_t is limited by the time base of the + * simulation kernel. + * If this resolution is not fine enough, the creation time + * can be corrected by using the offset_ attribute. + */ tic_t tics; + /** + * Offset for precise spike times. + * offset_ specifies a correction to the creation time. + * If the resolution of stamp is not sufficiently precise, + * this attribute can be used to correct the creation time. + * offset_ has to be in [0, h). + */ + double offset_; + ///////////////////////////////////////////////////////////// // Friend declaration for units and binary operators ///////////////////////////////////////////////////////////// @@ -199,6 +215,29 @@ class Time friend Time operator*( const Time& t, long factor ); friend std::ostream&(::operator<<)( std::ostream&, const Time& ); + ///////////////////////////////////////////////////////////// + // Offset + ///////////////////////////////////////////////////////////// + + /** + * Return the creation time offset of the Event. + * Each Event carries the exact time of creation. This + * time need not coincide with an integral multiple of the + * temporal resolution. Rather, Events may be created at any point + * in time. + */ + double get_offset() const; + + /** + * Set the creation time of the Event. + * Each Event carries the exact time of creation in realtime. This + * time need not coincide with an integral multiple of the + * temporal resolution. Rather, Events may be created at any point + * in time. + * @param t Creation time in realtime. t has to be in [0, h). + */ + void set_offset( double t ); + ///////////////////////////////////////////////////////////// // Limits for time, including infinity definitions ///////////////////////////////////////////////////////////// @@ -295,7 +334,8 @@ class Time public: Time() - : tics( 0 ){}; + : tics( 0 ) + , offset_( 0. ) {}; // Default copy constructor: assumes legal time object // Defined by compiler. @@ -514,7 +554,7 @@ class Time { return LIM_NEG_INF_ms; } - return Range::MS_PER_TIC * tics; + return Range::MS_PER_TIC * tics - offset_; } delay @@ -628,6 +668,18 @@ inline Time operator*( const Time& t, long factor ) { return factor * t; } + +inline double +Time::get_offset() const +{ + return offset_; +} + +inline void +Time::set_offset( double t ) +{ + offset_ = t; +} } // namespace std::ostream& operator<<( std::ostream&, const nest::Time& ); diff --git a/nestkernel/recording_backend_arbor.cpp b/nestkernel/recording_backend_arbor.cpp index 2326cd6543..4c4e31c196 100644 --- a/nestkernel/recording_backend_arbor.cpp +++ b/nestkernel/recording_backend_arbor.cpp @@ -250,7 +250,7 @@ nest::RecordingBackendArbor::write( const RecordingDevice& device, const unsigned sender_node_id = event.get_sender_node_id(); const auto step_time = event.get_stamp().get_ms(); - const auto offset = event.get_offset(); + const auto offset = event.get_stamp().get_offset(); const auto time = static_cast< float >( step_time - offset ); buffer.push_back( { { num_arbor_cells_ + sender_node_id, 0 }, time } ); diff --git a/nestkernel/recording_backend_ascii.cpp b/nestkernel/recording_backend_ascii.cpp index 9e4ecfc371..35e55642ce 100644 --- a/nestkernel/recording_backend_ascii.cpp +++ b/nestkernel/recording_backend_ascii.cpp @@ -303,11 +303,11 @@ nest::RecordingBackendASCII::DeviceData::write( const Event& event, if ( time_in_steps_ ) { - file_ << event.get_stamp().get_steps() << "\t" << event.get_offset(); + file_ << event.get_stamp().get_steps() << "\t" << event.get_stamp().get_offset(); } else { - file_ << ( event.get_stamp().get_ms() - event.get_offset() ); + file_ << ( event.get_stamp().get_ms() ); } for ( auto& val : double_values ) diff --git a/nestkernel/recording_backend_memory.cpp b/nestkernel/recording_backend_memory.cpp index 6ff762603f..9e4310eff6 100644 --- a/nestkernel/recording_backend_memory.cpp +++ b/nestkernel/recording_backend_memory.cpp @@ -197,11 +197,11 @@ nest::RecordingBackendMemory::DeviceData::push_back( const Event& event, if ( time_in_steps_ ) { times_steps_.push_back( event.get_stamp().get_steps() ); - times_offset_.push_back( event.get_offset() ); + times_offset_.push_back( event.get_stamp().get_offset() ); } else { - times_ms_.push_back( event.get_stamp().get_ms() - event.get_offset() ); + times_ms_.push_back( event.get_stamp().get_ms() ); } for ( size_t i = 0; i < double_values.size(); ++i ) diff --git a/nestkernel/recording_backend_screen.cpp b/nestkernel/recording_backend_screen.cpp index 71b9657731..c238dfdc61 100644 --- a/nestkernel/recording_backend_screen.cpp +++ b/nestkernel/recording_backend_screen.cpp @@ -199,11 +199,11 @@ nest::RecordingBackendScreen::DeviceData::write( const Event& event, if ( time_in_steps_ ) { - std::cout << event.get_stamp().get_steps() << "\t" << event.get_offset(); + std::cout << event.get_stamp().get_steps() << "\t" << event.get_stamp().get_offset(); } else { - std::cout << event.get_stamp().get_ms() - event.get_offset(); + std::cout << event.get_stamp().get_ms(); } for ( auto& val : double_values ) diff --git a/nestkernel/recording_backend_sionlib.cpp b/nestkernel/recording_backend_sionlib.cpp index 2e1883e462..740599cdcf 100644 --- a/nestkernel/recording_backend_sionlib.cpp +++ b/nestkernel/recording_backend_sionlib.cpp @@ -456,7 +456,7 @@ nest::RecordingBackendSIONlib::write( const RecordingDevice& device, const sion_uint64 sender_node_id = static_cast< sion_uint64 >( event.get_sender_node_id() ); const sion_int64 step = static_cast< sion_int64 >( event.get_stamp().get_steps() ); - const double offset = event.get_offset(); + const double offset = event.get_stamp().get_offset(); if ( P_.sion_collective_ ) { diff --git a/nestkernel/structural_plasticity_node.cpp b/nestkernel/structural_plasticity_node.cpp index fa5d3d607c..45013c2fe7 100644 --- a/nestkernel/structural_plasticity_node.cpp +++ b/nestkernel/structural_plasticity_node.cpp @@ -256,8 +256,7 @@ nest::StructuralPlasticityNode::connect_synaptic_element( Name name, int n ) void nest::StructuralPlasticityNode::set_spiketime( Time const& t_sp, double offset ) { - const double t_sp_ms = t_sp.get_ms() - offset; - update_synaptic_elements( t_sp_ms ); + update_synaptic_elements( t_sp.get_ms() ); Ca_minus_ += beta_Ca_; } From e7a1c01358578cbee5a0df2303a653f9ec954d33 Mon Sep 17 00:00:00 2001 From: "C.A.P. Linssen" Date: Fri, 15 Oct 2021 22:45:42 +0200 Subject: [PATCH 2/6] pull upstream/master --- testsuite/pytests/test_stdp_multiplicity.py | 88 ++--- testsuite/pytests/test_stdp_synapse.py | 360 -------------------- testsuite/pytests/test_visualization.py | 9 +- 3 files changed, 32 insertions(+), 425 deletions(-) delete mode 100644 testsuite/pytests/test_stdp_synapse.py diff --git a/testsuite/pytests/test_stdp_multiplicity.py b/testsuite/pytests/test_stdp_multiplicity.py index 8c3ab0f852..091f64d769 100644 --- a/testsuite/pytests/test_stdp_multiplicity.py +++ b/testsuite/pytests/test_stdp_multiplicity.py @@ -26,13 +26,6 @@ import math import numpy as np -try: - import matplotlib as mpl - import matplotlib.pyplot as plt - DEBUG_PLOTS = True -except Exception: - DEBUG_PLOTS = False - @nest.ll_api.check_stack class StdpSpikeMultiplicity(unittest.TestCase): @@ -58,29 +51,23 @@ class StdpSpikeMultiplicity(unittest.TestCase): delta, since in this case all spikes are at the end of the step, i.e., all spikes have identical times independent of delta. - 2. We choose delta values that are decreased by factors of 2. The + 2. We choose delta values that are decrease by factors of 2. The plasticity rules depend on spike-time differences through - :: - exp(dT / tau) where dT is the time between pre- and postsynaptic spikes. We construct pre- and postsynaptic spike times so that - :: - - dT = pre_post_shift + m * delta + dT = pre_post_shift + m * delta - with ``m * delta < resolution << pre_post_shift``. The time-dependence + with m * delta < resolution << pre_post_shift. The time-dependence of the plasticity rule is therefore to good approximation linear in delta. - We can thus test as follows: Let ``w_pl`` be the weight obtained with the - plain parrot, and ``w_ps_j`` the weight obtained with the precise parrot - for ``delta_j = delta0 / 2^j``. Then, - - :: + We can thus test as follows: Let w_pl be the weight obtained with the + plain parrot, and w_ps_j the weight obtained with the precise parrot + for delta_j = delta0 / 2^j. Then, ( w_ps_{j+1} - w_pl ) / ( w_ps_j - w_pl ) ~ 0.5 for all j @@ -129,8 +116,8 @@ def run_protocol(self, pre_post_shift): assert multiplicity * delta < resolution / 2., "Test inconsistent." nest.ResetKernel() - nest.SetKernelStatus({'tics_per_ms': tics_per_ms, - 'resolution': resolution}) + # For co-dependent properties, we use `set()` instead of kernel attributes + nest.set(resolution=resolution, tics_per_ms=tics_per_ms) pre_times = sorted(t_base - k * delta for t_base in pre_spike_times_base @@ -170,7 +157,8 @@ def run_protocol(self, pre_post_shift): # create spike recorder --- debugging only spikes = nest.Create("spike_recorder") nest.Connect( - pre_parrot + post_parrot + pre_parrot_ps + post_parrot_ps, + pre_parrot + post_parrot + + pre_parrot_ps + post_parrot_ps, spikes ) @@ -206,51 +194,33 @@ def run_protocol(self, pre_post_shift): post_weights['parrot'].append(w_post) post_weights['parrot_ps'].append(w_post_ps) - if DEBUG_PLOTS: - import datetime - fig, ax = plt.subplots(nrows=2) - fig.suptitle("Final obtained weights") - ax[0].plot(post_weights["parrot"], marker="o", label="parrot") - ax[0].plot(post_weights["parrot_ps"], marker="o", label="parrot_ps") - ax[0].set_ylabel("final weight") - ax[0].set_xticklabels([]) - ax[1].semilogy(np.abs(np.array(post_weights["parrot"]) - np.array(post_weights["parrot_ps"])), - marker="o", label="error") - ax[1].set_xticks([i for i in range(len(deltas))]) - ax[1].set_xticklabels(["{0:.1E}".format(d) for d in deltas]) - ax[1].set_xlabel("timestep [ms]") - for _ax in ax: - _ax.grid(True) - _ax.legend() - plt.savefig("/tmp/test_stdp_multiplicity" + str(datetime.datetime.utcnow()) + ".png") - plt.close(fig) - print(post_weights) return post_weights - def _test_stdp_multiplicity(self, pre_post_shift, max_abs_err=1E-6): - """Check that for smaller and smaller timestep, weights obtained from parrot and precise parrot converge. - - Enforce a maximum allowed absolute error ``max_abs_err`` between the final weights for the smallest timestep - tested. + def test_ParrotNeuronSTDPProtocolPotentiation(self): + """Check weight convergence on potentiation.""" - Enforce that the error should strictly decrease with smaller timestep.""" - - post_weights = self.run_protocol(pre_post_shift=pre_post_shift) + post_weights = self.run_protocol(pre_post_shift=10.0) w_plain = np.array(post_weights['parrot']) w_precise = np.array(post_weights['parrot_ps']) - assert all(w_plain == w_plain[0]), 'Plain weights should be independent of timestep!' - abs_err = np.abs(w_precise - w_plain) - assert abs_err[-1] < max_abs_err, 'Final absolute error is ' + '{0:.2E}'.format(abs_err[-1]) + ' but should be <= ' + '{0:.2E}'.format(max_abs_err) - assert np.all(np.diff(abs_err) < 0), 'Error should decrease with smaller timestep!' + assert all(w_plain == w_plain[0]), 'Plain weights differ' + dw = w_precise - w_plain + dwrel = dw[1:] / dw[:-1] + assert all(np.round(dwrel, decimals=3) == + 0.5), 'Precise weights do not converge.' + + def test_ParrotNeuronSTDPProtocolDepression(self): + """Check weight convergence on depression.""" - def test_stdp_multiplicity(self): - """Check weight convergence on potentiation and depression. + post_weights = self.run_protocol(pre_post_shift=-10.0) + w_plain = np.array(post_weights['parrot']) + w_precise = np.array(post_weights['parrot_ps']) - See also: _test_stdp_multiplicity().""" - # XXX: TODO: use ``@pytest.mark.parametrize`` for this - self._test_stdp_multiplicity(pre_post_shift=10.) # test potentiation - self._test_stdp_multiplicity(pre_post_shift=-10.) # test depression + assert all(w_plain == w_plain[0]), 'Plain weights differ' + dw = w_precise - w_plain + dwrel = dw[1:] / dw[:-1] + assert all(np.round(dwrel, decimals=3) == + 0.5), 'Precise weights do not converge.' def suite(): diff --git a/testsuite/pytests/test_stdp_synapse.py b/testsuite/pytests/test_stdp_synapse.py deleted file mode 100644 index a634d90344..0000000000 --- a/testsuite/pytests/test_stdp_synapse.py +++ /dev/null @@ -1,360 +0,0 @@ -# -*- coding: utf-8 -*- -# -# test_stdp_synapse.py -# -# This file is part of NEST. -# -# Copyright (C) 2004 The NEST Initiative -# -# NEST is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# NEST is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with NEST. If not, see . - -import nest -import unittest -from math import exp -import numpy as np - -try: - import matplotlib as mpl - import matplotlib.pyplot as plt - DEBUG_PLOTS = True -except Exception: - DEBUG_PLOTS = False - - -@nest.ll_api.check_stack -class STDPSynapseTest(unittest.TestCase): - """ - Compare the STDP synaptic plasticity model against a self-contained Python reference. - - Random pre and post spike times are generated according to a Poisson distribution; some hard-coded spike times are - added to make sure to test for edge cases such as simultaneous pre- and post spike. - """ - - def init_params(self): - self.resolution = 0.1 # [ms] - self.simulation_duration = 1E3 # [ms] - self.synapse_model = "stdp_synapse" - self.presynaptic_firing_rate = 20. # [ms^-1] - self.postsynaptic_firing_rate = 20. # [ms^-1] - self.tau_pre = 16.8 - self.tau_post = 33.7 - self.init_weight = .5 - self.synapse_parameters = { - "synapse_model": self.synapse_model, - "receptor_type": 0, - "delay": self.dendritic_delay, - # STDP constants - "lambda": 0.01, - "alpha": 0.85, - "mu_plus": 0.0, - "mu_minus": 0.0, - "tau_plus": self.tau_pre, - "Wmax": 15.0, - "weight": self.init_weight - } - self.neuron_parameters = { - "tau_minus": self.tau_post, - } - self.hardcoded_trains_length = 15 - self.hardcoded_pre_times = np.array([1, 5, 6, 7, 9, 11, 12, 13.]) - self.hardcoded_post_times = np.array([2, 3, 4, 8, 9, 10, 12.]) - - def do_nest_simulation_and_compare_to_reproduced_weight(self, fname_snip): - pre_spikes, post_spikes, t_weight_by_nest, weight_by_nest = self.do_the_nest_simulation() - if DEBUG_PLOTS: - self.plot_weight_evolution(pre_spikes, post_spikes, - t_weight_by_nest, - weight_by_nest, - fname_snip=fname_snip, - title_snip=self.nest_neuron_model + " (NEST)") - - t_weight_reproduced_independently, weight_reproduced_independently = self.reproduce_weight_drift( - pre_spikes, post_spikes, - self.init_weight, - fname_snip=fname_snip) - - # ``weight_by_nest`` containts only weight values at pre spike times, ``weight_reproduced_independently`` - # contains the weight at pre *and* post times: check that weights are equal only for pre spike times - assert len(weight_by_nest) > 0 - for idx_pre_spike_nest, t_pre_spike_nest in enumerate(t_weight_by_nest): - idx_pre_spike_reproduced_independently = \ - np.argmin((t_pre_spike_nest - t_weight_reproduced_independently)**2) - np.testing.assert_allclose(t_pre_spike_nest, - t_weight_reproduced_independently[idx_pre_spike_reproduced_independently]) - np.testing.assert_allclose(weight_by_nest[idx_pre_spike_nest], - weight_reproduced_independently[idx_pre_spike_reproduced_independently]) - - def do_the_nest_simulation(self): - """ - This function is where calls to NEST reside. Returns the generated pre- and post spike sequences and the - resulting weight established by STDP. - """ - nest.set_verbosity('M_WARNING') - nest.ResetKernel() - nest.SetKernelStatus({'resolution': self.resolution}) - - neurons = nest.Create( - self.nest_neuron_model, - 2, - params=self.neuron_parameters) - presynaptic_neuron = neurons[0] - postsynaptic_neuron = neurons[1] - - generators = nest.Create( - "poisson_generator", - 2, - params=({"rate": self.presynaptic_firing_rate, - "stop": (self.simulation_duration - self.hardcoded_trains_length)}, - {"rate": self.postsynaptic_firing_rate, - "stop": (self.simulation_duration - self.hardcoded_trains_length)})) - presynaptic_generator = generators[0] - postsynaptic_generator = generators[1] - - wr = nest.Create('weight_recorder') - nest.CopyModel(self.synapse_model, self.synapse_model + "_rec", {"weight_recorder": wr}) - - # While the random sequences, fairly long, would supposedly - # reveal small differences in the weight change between NEST - # and ours, some low-probability events (say, coinciding - # spikes) can well not have occured. To generate and - # test every possible combination of pre/post precedence, we - # append some hardcoded spike sequences: - # pre: 1 5 6 7 9 11 12 13 - # post: 2 3 4 8 9 10 12 - ( - hardcoded_pre_times, - hardcoded_post_times - ) = [ - [ - self.simulation_duration - self.hardcoded_trains_length + float(t) - for t in train - ] for train in ( - self.hardcoded_pre_times, - self.hardcoded_post_times - ) - ] - - spike_senders = nest.Create( - "spike_generator", - 2, - params=({"spike_times": hardcoded_pre_times}, - {"spike_times": hardcoded_post_times}) - ) - pre_spike_generator = spike_senders[0] - post_spike_generator = spike_senders[1] - - # The recorder is to save the randomly generated spike trains. - spike_recorder = nest.Create("spike_recorder") - - nest.Connect(presynaptic_generator + pre_spike_generator, presynaptic_neuron, - syn_spec={"synapse_model": "static_synapse", "weight": 9999.}) - nest.Connect(postsynaptic_generator + post_spike_generator, postsynaptic_neuron, - syn_spec={"synapse_model": "static_synapse", "weight": 9999.}) - nest.Connect(presynaptic_neuron + postsynaptic_neuron, spike_recorder, - syn_spec={"synapse_model": "static_synapse"}) - # The synapse of interest itself - self.synapse_parameters["synapse_model"] += "_rec" - nest.Connect(presynaptic_neuron, postsynaptic_neuron, syn_spec=self.synapse_parameters) - self.synapse_parameters["synapse_model"] = self.synapse_model - - nest.Simulate(self.simulation_duration) - - all_spikes = nest.GetStatus(spike_recorder, keys='events')[0] - pre_spikes = all_spikes['times'][all_spikes['senders'] == presynaptic_neuron.tolist()[0]] - post_spikes = all_spikes['times'][all_spikes['senders'] == postsynaptic_neuron.tolist()[0]] - - t_hist = nest.GetStatus(wr, "events")[0]["times"] - weight = nest.GetStatus(wr, "events")[0]["weights"] - - return pre_spikes, post_spikes, t_hist, weight - - def reproduce_weight_drift(self, pre_spikes, post_spikes, initial_weight, fname_snip=""): - """Independent, self-contained model of STDP""" - def facilitate(w, Kpre, Wmax_=1.): - norm_w = (w / self.synapse_parameters["Wmax"]) + ( - self.synapse_parameters["lambda"] * pow( - 1 - (w / self.synapse_parameters["Wmax"]), self.synapse_parameters["mu_plus"]) * Kpre) - if norm_w < 1.0: - return norm_w * self.synapse_parameters["Wmax"] - else: - return self.synapse_parameters["Wmax"] - - def depress(w, Kpost): - norm_w = (w / self.synapse_parameters["Wmax"]) - ( - self.synapse_parameters["alpha"] * self.synapse_parameters["lambda"] * pow( - w / self.synapse_parameters["Wmax"], self.synapse_parameters["mu_minus"]) * Kpost) - if norm_w > 0.0: - return norm_w * self.synapse_parameters["Wmax"] - else: - return 0. - - def Kpost_at_time(t, spikes, init=1., inclusive=True): - t_curr = 0. - Kpost = 0. - for spike_idx, t_sp in enumerate(spikes): - if t < t_sp: - # integrate to t - Kpost *= exp(-(t - t_curr) / self.tau_post) - return Kpost - # integrate to t_sp - Kpost *= exp(-(t_sp - t_curr) / self.tau_post) - if inclusive: - Kpost += 1. - if t == t_sp: - return Kpost - if not inclusive: - Kpost += 1. - t_curr = t_sp - # if we get here, t > t_last_spike - # integrate to t - Kpost *= exp(-(t - t_curr) / self.tau_post) - return Kpost - - t = 0. - Kpre = 0. - weight = initial_weight - - t_log = [] - w_log = [] - Kpre_log = [] - - # logging - t_log.append(t) - w_log.append(weight) - Kpre_log.append(Kpre) - - post_spikes_delayed = post_spikes + self.dendritic_delay - - while t < self.simulation_duration: - idx_next_pre_spike = -1 - if np.where((pre_spikes - t) > 0)[0].size > 0: - idx_next_pre_spike = np.where((pre_spikes - t) > 0)[0][0] - t_next_pre_spike = pre_spikes[idx_next_pre_spike] - - idx_next_post_spike = -1 - if np.where((post_spikes_delayed - t) > 0)[0].size > 0: - idx_next_post_spike = np.where((post_spikes_delayed - t) > 0)[0][0] - t_next_post_spike = post_spikes_delayed[idx_next_post_spike] - - if idx_next_post_spike >= 0 and t_next_post_spike < t_next_pre_spike: - handle_post_spike = True - handle_pre_spike = False - elif idx_next_pre_spike >= 0 and t_next_post_spike > t_next_pre_spike: - handle_post_spike = False - handle_pre_spike = True - else: - handle_post_spike = idx_next_post_spike >= 0 - handle_pre_spike = idx_next_pre_spike >= 0 - - # integrate to min(t_next_pre_spike, t_next_post_spike) - t_next = t - if handle_pre_spike: - t_next = max(t, t_next_pre_spike) - if handle_post_spike: - t_next = max(t, t_next_post_spike) - - if t_next == t: - # no more spikes to process - t_next = self.simulation_duration - - '''# max timestep - t_next_ = min(t_next, t + 1E-3) - if t_next != t_next_: - t_next = t_next_ - handle_pre_spike = False - handle_post_spike = False''' - - h = t_next - t - Kpre *= exp(-h / self.tau_pre) - t = t_next - - if handle_post_spike: - # Kpost += 1. <-- not necessary, will call Kpost_at_time(t) later to compute Kpost for any value t - weight = facilitate(weight, Kpre) - - if handle_pre_spike: - Kpre += 1. - _Kpost = Kpost_at_time(t - self.dendritic_delay, post_spikes, init=self.init_weight, inclusive=False) - weight = depress(weight, _Kpost) - - # logging - t_log.append(t) - w_log.append(weight) - Kpre_log.append(Kpre) - - Kpost_log = [Kpost_at_time(t - self.dendritic_delay, post_spikes, init=self.init_weight) for t in t_log] - if DEBUG_PLOTS: - self.plot_weight_evolution(pre_spikes, post_spikes, t_log, w_log, Kpre_log, Kpost_log, - fname_snip=fname_snip + "_ref", title_snip="Reference") - - return t_log, w_log - - def plot_weight_evolution(self, pre_spikes, post_spikes, t_log, w_log, Kpre_log=None, Kpost_log=None, - fname_snip="", title_snip=""): - fig, ax = plt.subplots(nrows=3) - - n_spikes = len(pre_spikes) - for i in range(n_spikes): - ax[0].plot(2 * [pre_spikes[i]], [0, 1], linewidth=2, color="blue", alpha=.4) - ax[0].set_ylabel("Pre spikes") - ax0_ = ax[0].twinx() - if Kpre_log: - ax0_.plot(t_log, Kpre_log) - - n_spikes = len(post_spikes) - for i in range(n_spikes): - ax[1].plot(2 * [post_spikes[i]], [0, 1], linewidth=2, color="red", alpha=.4) - ax1_ = ax[1].twinx() - ax[1].set_ylabel("Post spikes") - if Kpost_log: - ax1_.plot(t_log, Kpost_log) - - ax[2].plot(t_log, w_log, marker="o", label="nestml") - ax[2].set_ylabel("w") - - ax[2].set_xlabel("Time [ms]") - for _ax in ax: - _ax.grid(which="major", axis="both") - _ax.grid(which="minor", axis="x", linestyle=":", alpha=.4) - _ax.minorticks_on() - _ax.set_xlim(0., self.simulation_duration) - - fig.suptitle(title_snip) - fig.savefig("/tmp/nest_stdp_synapse_test" + fname_snip + ".png", dpi=300) - plt.close(fig) - - def test_stdp_synapse(self): - self.dendritic_delay = float('nan') - self.init_params() - for self.dendritic_delay in [1., self.resolution]: - self.init_params() - for self.nest_neuron_model in ["iaf_psc_exp", - "iaf_cond_exp", - "iaf_psc_exp_ps"]: - fname_snip = "_[nest_neuron_mdl=" + self.nest_neuron_model + "]" - self.do_nest_simulation_and_compare_to_reproduced_weight(fname_snip=fname_snip) - - -def suite(): - suite = unittest.TestLoader().loadTestsFromTestCase(STDPSynapseTest) - return unittest.TestSuite([suite]) - - -def run(): - runner = unittest.TextTestRunner(verbosity=2) - runner.run(suite()) - - -if __name__ == "__main__": - run() diff --git a/testsuite/pytests/test_visualization.py b/testsuite/pytests/test_visualization.py index fe4401dcf0..b49538b245 100644 --- a/testsuite/pytests/test_visualization.py +++ b/testsuite/pytests/test_visualization.py @@ -95,7 +95,7 @@ def voltage_trace_verify(self, device): @unittest.skipIf(not PLOTTING_POSSIBLE, 'Plotting impossible because matplotlib or display missing') def test_voltage_trace_from_device(self): """Test voltage_trace from device""" - import nest.voltage_trace + import nest.voltage_trace as nvtrace nest.ResetKernel() nodes = nest.Create('iaf_psc_alpha', 2) pg = nest.Create('poisson_generator', 1, {'rate': 1000.}) @@ -105,11 +105,10 @@ def test_voltage_trace_from_device(self): nest.Simulate(100) # Test with data from device - plt.close("all") nest.voltage_trace.from_device(device) self.voltage_trace_verify(device) - # Test with data from file + # Test with fata from file vm = device.get('events') data = np.zeros([len(vm['senders']), 3]) data[:, 0] = vm['senders'] @@ -118,8 +117,6 @@ def test_voltage_trace_from_device(self): filename = os.path.join(self.nest_tmpdir(), 'voltage_trace.txt') self.filenames.append(filename) np.savetxt(filename, data) - - plt.close("all") nest.voltage_trace.from_file(filename) self.voltage_trace_verify(device) @@ -153,7 +150,7 @@ def spike_recorder_raster_verify(self, sr_ref): @unittest.skipIf(not PLOTTING_POSSIBLE, 'Plotting impossible because matplotlib or display missing') def test_raster_plot(self): """Test raster_plot""" - import nest.raster_plot + import nest.raster_plot as nraster sr, sr_to_file = self.spike_recorder_data_setup(to_file=True) spikes = sr.get('events') From ee38b2787074a5758cff4b245a3b7d3f1b031a4d Mon Sep 17 00:00:00 2001 From: "C.A.P. Linssen" Date: Tue, 10 Sep 2024 10:06:46 +0200 Subject: [PATCH 3/6] move precise spike time offset from Event to Time class --- models/cont_delay_synapse.h | 6 ++-- models/iaf_bw_2001.cpp | 4 +-- models/iaf_chxk_2008.cpp | 2 +- models/iaf_psc_alpha_ps.cpp | 4 +-- models/iaf_psc_delta_ps.cpp | 4 +-- models/iaf_psc_exp_ps.cpp | 4 +-- models/iaf_psc_exp_ps_lossless.cpp | 4 +-- models/iaf_tum_2000.cpp | 4 +-- models/parrot_neuron_ps.cpp | 2 +- models/poisson_generator_ps.cpp | 2 +- models/spike_generator.cpp | 2 +- models/spike_train_injector.cpp | 2 +- nestkernel/event.h | 44 +++++++++++++++-------- nestkernel/event_delivery_manager.cpp | 8 ++--- nestkernel/nest_time.h | 50 +++++++++++++++------------ 15 files changed, 80 insertions(+), 62 deletions(-) diff --git a/models/cont_delay_synapse.h b/models/cont_delay_synapse.h index a9816dfca0..340cd6ef5b 100644 --- a/models/cont_delay_synapse.h +++ b/models/cont_delay_synapse.h @@ -231,16 +231,16 @@ cont_delay_synapse< targetidentifierT >::send( Event& e, size_t t, const CommonS if ( total_offset < Time::get_resolution().get_ms() ) { e.set_delay_steps( get_delay_steps() ); - e.set_offset( total_offset ); + e.get_stamp().set_offset( total_offset ); } else { e.set_delay_steps( get_delay_steps() - 1 ); - e.set_offset( total_offset - Time::get_resolution().get_ms() ); + e.get_stamp().set_offset( total_offset - Time::get_resolution().get_ms() ); } e(); // reset offset to original value - e.set_offset( orig_event_offset ); + e.get_stamp().set_offset( orig_event_offset ); return true; } diff --git a/models/iaf_bw_2001.cpp b/models/iaf_bw_2001.cpp index e030579183..ff92528d8f 100644 --- a/models/iaf_bw_2001.cpp +++ b/models/iaf_bw_2001.cpp @@ -474,7 +474,7 @@ nest::iaf_bw_2001::update( Time const& origin, const long from, const long to ) S_.s_NMDA_pre += s_NMDA_delta; SpikeEvent se; - se.set_offset( s_NMDA_delta ); + se.get_stamp().set_offset( s_NMDA_delta ); kernel().event_delivery_manager.send( *this, se, lag ); } @@ -509,7 +509,7 @@ nest::iaf_bw_2001::handle( SpikeEvent& e ) } else { - B_.spikes_[ rport - 1 ].add_value( steps, e.get_weight() * e.get_multiplicity() * e.get_offset() ); + B_.spikes_[ rport - 1 ].add_value( steps, e.get_weight() * e.get_multiplicity() * e.get_stamp().get_offset() ); } } diff --git a/models/iaf_chxk_2008.cpp b/models/iaf_chxk_2008.cpp index 62559536ae..0c75b37698 100644 --- a/models/iaf_chxk_2008.cpp +++ b/models/iaf_chxk_2008.cpp @@ -425,7 +425,7 @@ nest::iaf_chxk_2008::update( Time const& origin, const long from, const long to set_spiketime( Time::step( origin.get_steps() + lag + 1 ) ); SpikeEvent se; - se.set_offset( dt ); + se.get_stamp().set_offset( dt ); kernel().event_delivery_manager.send( *this, se, lag ); } diff --git a/models/iaf_psc_alpha_ps.cpp b/models/iaf_psc_alpha_ps.cpp index cc0e5772aa..99f567ad1f 100644 --- a/models/iaf_psc_alpha_ps.cpp +++ b/models/iaf_psc_alpha_ps.cpp @@ -547,7 +547,7 @@ nest::iaf_psc_alpha_ps::emit_spike_( Time const& origin, const long lag, const d // send spike set_spiketime( Time::step( S_.last_spike_step_ ), S_.last_spike_offset_ ); SpikeEvent se; - se.set_offset( S_.last_spike_offset_ ); + se.get_stamp().set_offset( S_.last_spike_offset_ ); kernel().event_delivery_manager.send( *this, se, lag ); return; @@ -569,7 +569,7 @@ nest::iaf_psc_alpha_ps::emit_instant_spike_( Time const& origin, const long lag, // send spike set_spiketime( Time::step( S_.last_spike_step_ ), S_.last_spike_offset_ ); SpikeEvent se; - se.set_offset( S_.last_spike_offset_ ); + se.get_stamp().set_offset( S_.last_spike_offset_ ); kernel().event_delivery_manager.send( *this, se, lag ); return; diff --git a/models/iaf_psc_delta_ps.cpp b/models/iaf_psc_delta_ps.cpp index 21c8afc0bd..7dda17ca5a 100644 --- a/models/iaf_psc_delta_ps.cpp +++ b/models/iaf_psc_delta_ps.cpp @@ -480,7 +480,7 @@ nest::iaf_psc_delta_ps::emit_spike_( Time const& origin, const long lag, const d // send spike set_spiketime( Time::step( S_.last_spike_step_ ), S_.last_spike_offset_ ); SpikeEvent se; - se.set_offset( S_.last_spike_offset_ ); + se.get_stamp().set_offset( S_.last_spike_offset_ ); kernel().event_delivery_manager.send( *this, se, lag ); } @@ -500,7 +500,7 @@ nest::iaf_psc_delta_ps::emit_instant_spike_( Time const& origin, const long lag, // send spike set_spiketime( Time::step( S_.last_spike_step_ ), S_.last_spike_offset_ ); SpikeEvent se; - se.set_offset( S_.last_spike_offset_ ); + se.get_stamp().set_offset( S_.last_spike_offset_ ); kernel().event_delivery_manager.send( *this, se, lag ); } diff --git a/models/iaf_psc_exp_ps.cpp b/models/iaf_psc_exp_ps.cpp index abfac7adb0..efa2938765 100644 --- a/models/iaf_psc_exp_ps.cpp +++ b/models/iaf_psc_exp_ps.cpp @@ -516,7 +516,7 @@ nest::iaf_psc_exp_ps::emit_spike_( const Time& origin, const long lag, const dou set_spiketime( Time::step( S_.last_spike_step_ ), S_.last_spike_offset_ ); SpikeEvent se; - se.set_offset( S_.last_spike_offset_ ); + se.get_stamp().set_offset( S_.last_spike_offset_ ); kernel().event_delivery_manager.send( *this, se, lag ); } @@ -537,7 +537,7 @@ nest::iaf_psc_exp_ps::emit_instant_spike_( const Time& origin, const long lag, c set_spiketime( Time::step( S_.last_spike_step_ ), S_.last_spike_offset_ ); SpikeEvent se; - se.set_offset( S_.last_spike_offset_ ); + se.get_stamp().set_offset( S_.last_spike_offset_ ); kernel().event_delivery_manager.send( *this, se, lag ); } diff --git a/models/iaf_psc_exp_ps_lossless.cpp b/models/iaf_psc_exp_ps_lossless.cpp index ee03f7f011..c98b70ca65 100644 --- a/models/iaf_psc_exp_ps_lossless.cpp +++ b/models/iaf_psc_exp_ps_lossless.cpp @@ -559,7 +559,7 @@ nest::iaf_psc_exp_ps_lossless::emit_spike_( const Time& origin, const long lag, set_spiketime( Time::step( S_.last_spike_step_ ), S_.last_spike_offset_ ); SpikeEvent se; - se.set_offset( S_.last_spike_offset_ ); + se.get_stamp().set_offset( S_.last_spike_offset_ ); kernel().event_delivery_manager.send( *this, se, lag ); } @@ -580,7 +580,7 @@ nest::iaf_psc_exp_ps_lossless::emit_instant_spike_( const Time& origin, const lo set_spiketime( Time::step( S_.last_spike_step_ ), S_.last_spike_offset_ ); SpikeEvent se; - se.set_offset( S_.last_spike_offset_ ); + se.get_stamp().set_offset( S_.last_spike_offset_ ); kernel().event_delivery_manager.send( *this, se, lag ); } diff --git a/models/iaf_tum_2000.cpp b/models/iaf_tum_2000.cpp index 29ae084558..bbafdd07fe 100644 --- a/models/iaf_tum_2000.cpp +++ b/models/iaf_tum_2000.cpp @@ -429,7 +429,7 @@ nest::iaf_tum_2000::update( const Time& origin, const long from, const long to ) // send spike with datafield SpikeEvent se; - se.set_offset( delta_y_tsp ); + se.get_stamp().set_offset( delta_y_tsp ); kernel().event_delivery_manager.send( *this, se, lag ); } @@ -458,7 +458,7 @@ nest::iaf_tum_2000::handle( SpikeEvent& e ) if ( e.get_rport() == 1 ) { - s *= e.get_offset(); + s *= e.get_stamp().get_offset(); } // separate buffer channels for excitatory and inhibitory inputs diff --git a/models/parrot_neuron_ps.cpp b/models/parrot_neuron_ps.cpp index cc15c174fc..6b9c7e1c85 100644 --- a/models/parrot_neuron_ps.cpp +++ b/models/parrot_neuron_ps.cpp @@ -81,7 +81,7 @@ parrot_neuron_ps::update( Time const& origin, long const from, long const to ) // send spike SpikeEvent se; se.set_multiplicity( multiplicity ); - se.set_offset( ev_offset ); + se.get_stamp().set_offset( ev_offset ); kernel().event_delivery_manager.send( *this, se, lag ); for ( unsigned long i = 0; i < multiplicity; ++i ) diff --git a/models/poisson_generator_ps.cpp b/models/poisson_generator_ps.cpp index 8f587de1a3..d336986ee5 100644 --- a/models/poisson_generator_ps.cpp +++ b/models/poisson_generator_ps.cpp @@ -262,7 +262,7 @@ nest::poisson_generator_ps::event_hook( DSSpikeEvent& e ) while ( nextspk.first <= V_.t_max_active_ ) { e.set_stamp( nextspk.first ); - e.set_offset( nextspk.second ); + e.get_stamp().set_offset( nextspk.second ); e.get_receiver().handle( e ); // Draw time of next spike diff --git a/models/spike_generator.cpp b/models/spike_generator.cpp index c6153ede4c..0294c16072 100644 --- a/models/spike_generator.cpp +++ b/models/spike_generator.cpp @@ -376,7 +376,7 @@ nest::spike_generator::update( Time const& sliceT0, const long from, const long if ( P_.precise_times_ ) { - se->set_offset( P_.spike_offsets_[ S_.position_ ] ); + se->get_stamp().set_offset( P_.spike_offsets_[ S_.position_ ] ); } if ( not P_.spike_multiplicities_.empty() ) diff --git a/models/spike_train_injector.cpp b/models/spike_train_injector.cpp index 9d596a8e5a..78d0b0f72b 100644 --- a/models/spike_train_injector.cpp +++ b/models/spike_train_injector.cpp @@ -368,7 +368,7 @@ spike_train_injector::update( Time const& sliceT0, const long from, const long t if ( P_.precise_times_ ) { - se.set_offset( P_.spike_offsets_[ S_.position_ ] ); + se.get_stamp().set_offset( P_.spike_offsets_[ S_.position_ ] ); } if ( not P_.spike_multiplicities_.empty() ) diff --git a/nestkernel/event.h b/nestkernel/event.h index 040028a1e8..ceaa309894 100644 --- a/nestkernel/event.h +++ b/nestkernel/event.h @@ -171,20 +171,6 @@ class Event */ void set_sender_node_id_info( const size_t tid, const synindex syn_id, const size_t lcid ); - /** - * Return time stamp of the event. - * - * The stamp denotes the time when the event was created. - */ - Time const& get_stamp() const; - - /** - * Set the time stamp of the event. - * The time stamp refers to the time when the event - * was created. - */ - void set_stamp( Time const& ); - /** * Return transmission delay of the event. * @@ -195,11 +181,12 @@ class Event /** * Set the transmission delay of the event. + * * The delay refers to the time until the event is * expected to arrive at the receiver. * @param d delay */ - void set_delay_steps( delay d ); + void set_delay_steps( long d ); /** * Relative spike delivery time in steps. @@ -297,7 +284,28 @@ class Event */ bool is_valid() const; + /** + * Return time stamp of the event. * + * The stamp denotes the time when the event was created. + */ + Time const& get_stamp() const; + + /** + * Return time stamp of the event. + * + * The stamp denotes the time when the event was created. + */ + Time& get_stamp(); + + /** + * Set the time stamp of the event. + * + * The time stamp refers to the time when the event + * was created. + */ + void set_stamp( Time const& ); + protected: size_t sender_node_id_; //!< node ID of sender or 0 SpikeData sender_spike_data_; //!< spike data of sender node, in some cases required to retrieve node ID @@ -931,6 +939,12 @@ Event::get_stamp() const return stamp_; } +inline Time& +Event::get_stamp() +{ + return stamp_; +} + inline void Event::set_stamp( Time const& s ) { diff --git a/nestkernel/event_delivery_manager.cpp b/nestkernel/event_delivery_manager.cpp index dbb98504d5..4d827e6874 100644 --- a/nestkernel/event_delivery_manager.cpp +++ b/nestkernel/event_delivery_manager.cpp @@ -668,7 +668,7 @@ EventDeliveryManager::deliver_events_( const size_t tid, const std::vector< Spik { const SpikeDataT& spike_data = recv_buffer[ rank * spike_buffer_size_per_rank + i * SPIKES_PER_BATCH + j ]; se_batch[ j ].set_stamp( prepared_timestamps[ spike_data.get_lag() ] ); - se_batch[ j ].set_offset( spike_data.get_offset() ); + se_batch[ j ].get_stamp().set_offset( spike_data.get_offset() ); tid_batch[ j ] = spike_data.get_tid(); syn_id_batch[ j ] = spike_data.get_syn_id(); lcid_batch[ j ] = spike_data.get_lcid(); @@ -689,7 +689,7 @@ EventDeliveryManager::deliver_events_( const size_t tid, const std::vector< Spik const SpikeDataT& spike_data = recv_buffer[ rank * spike_buffer_size_per_rank + num_batches * SPIKES_PER_BATCH + j ]; se_batch[ j ].set_stamp( prepared_timestamps[ spike_data.get_lag() ] ); - se_batch[ j ].set_offset( spike_data.get_offset() ); + se_batch[ j ].get_stamp().set_offset( spike_data.get_offset() ); tid_batch[ j ] = spike_data.get_tid(); syn_id_batch[ j ] = spike_data.get_syn_id(); lcid_batch[ j ] = spike_data.get_lcid(); @@ -712,7 +712,7 @@ EventDeliveryManager::deliver_events_( const size_t tid, const std::vector< Spik const SpikeDataT& spike_data = recv_buffer[ rank * spike_buffer_size_per_rank + i * SPIKES_PER_BATCH + j ]; se_batch[ j ].set_stamp( prepared_timestamps[ spike_data.get_lag() ] ); - se_batch[ j ].set_offset( spike_data.get_offset() ); + se_batch[ j ].get_stamp().set_offset( spike_data.get_offset() ); syn_id_batch[ j ] = spike_data.get_syn_id(); // for compressed spikes lcid holds the index in the @@ -750,7 +750,7 @@ EventDeliveryManager::deliver_events_( const size_t tid, const std::vector< Spik const SpikeDataT& spike_data = recv_buffer[ rank * spike_buffer_size_per_rank + num_batches * SPIKES_PER_BATCH + j ]; se_batch[ j ].set_stamp( prepared_timestamps[ spike_data.get_lag() ] ); - se_batch[ j ].set_offset( spike_data.get_offset() ); + se_batch[ j ].get_stamp().set_offset( spike_data.get_offset() ); syn_id_batch[ j ] = spike_data.get_syn_id(); // for compressed spikes lcid holds the index in the // compressed_spike_data structure diff --git a/nestkernel/nest_time.h b/nestkernel/nest_time.h index 5f77890e91..8fe9090678 100644 --- a/nestkernel/nest_time.h +++ b/nestkernel/nest_time.h @@ -169,6 +169,29 @@ class Time public: static tic_t compute_max(); + ///////////////////////////////////////////////////////////// + // Offset + ///////////////////////////////////////////////////////////// + + /** + * Return the creation time offset of the Event. + * Each Event carries the exact time of creation. This + * time need not coincide with an integral multiple of the + * temporal resolution. Rather, Events may be created at any point + * in time. + */ + double get_offset() const; + + /** + * Set the creation time of the Event. + * Each Event carries the exact time of creation in realtime. This + * time need not coincide with an integral multiple of the + * temporal resolution. Rather, Events may be created at any point + * in time. + * @param t Creation time in realtime. t has to be in [0, h). + */ + void set_offset( double t ); + ///////////////////////////////////////////////////////////// // The data ///////////////////////////////////////////////////////////// @@ -213,29 +236,6 @@ class Time friend Time operator*( const Time& t, long factor ); friend std::ostream&( ::operator<< )( std::ostream&, const Time& ); - ///////////////////////////////////////////////////////////// - // Offset - ///////////////////////////////////////////////////////////// - - /** - * Return the creation time offset of the Event. - * Each Event carries the exact time of creation. This - * time need not coincide with an integral multiple of the - * temporal resolution. Rather, Events may be created at any point - * in time. - */ - double get_offset() const; - - /** - * Set the creation time of the Event. - * Each Event carries the exact time of creation in realtime. This - * time need not coincide with an integral multiple of the - * temporal resolution. Rather, Events may be created at any point - * in time. - * @param t Creation time in realtime. t has to be in [0, h). - */ - void set_offset( double t ); - ///////////////////////////////////////////////////////////// // Limits for time, including infinity definitions ///////////////////////////////////////////////////////////// @@ -339,6 +339,7 @@ class Time : tics( ( time_abs( t.t ) < LIM_MAX.tics ) ? t.t : ( t.t < 0 ) ? LIM_NEG_INF.tics : LIM_POS_INF.tics ) + , offset_( 0. ) { } @@ -346,6 +347,7 @@ class Time : tics( ( time_abs( t.t ) < LIM_MAX.steps ) ? t.t * Range::TICS_PER_STEP : ( t.t < 0 ) ? LIM_NEG_INF.tics : LIM_POS_INF.tics ) + , offset_( 0. ) { } @@ -353,12 +355,14 @@ class Time : tics( ( time_abs( t.t ) < LIM_MAX.ms ) ? static_cast< tic_t >( t.t * Range::TICS_PER_MS + 0.5 ) : ( t.t < 0 ) ? LIM_NEG_INF.tics : LIM_POS_INF.tics ) + , offset_( 0. ) { } static tic_t fromstamp( ms_stamp ); Time( ms_stamp t ) : tics( fromstamp( t ) ) + , offset_( 0. ) { } From a8615d11f4dd5dc4809c0e30e5257541fc00c470 Mon Sep 17 00:00:00 2001 From: "C.A.P. Linssen" Date: Tue, 10 Sep 2024 10:28:39 +0200 Subject: [PATCH 4/6] move precise spike time offset from Event to Time class --- models/clopath_synapse.h | 7 ------- models/eprop_synapse_bsshslm_2020.h | 7 ------- models/ht_synapse.h | 7 ------- models/jonke_synapse.h | 7 ------- models/quantal_stp_synapse.h | 7 ------- models/stdp_dopamine_synapse.h | 7 ------- models/stdp_facetshw_synapse_hom.h | 7 ------- models/stdp_nn_pre_centered_synapse.h | 7 ------- models/stdp_nn_restr_synapse.h | 7 ------- models/stdp_nn_symm_synapse.h | 7 ------- models/stdp_pl_synapse_hom.h | 7 ------- models/stdp_synapse.h | 7 ------- models/stdp_synapse_hom.h | 7 ------- models/stdp_triplet_synapse.h | 7 ------- models/tsodyks2_synapse.h | 7 ------- models/tsodyks_synapse.h | 7 ------- models/tsodyks_synapse_hom.h | 7 ------- models/urbanczik_synapse.h | 7 ------- models/vogels_sprekeler_synapse.h | 7 ------- 19 files changed, 133 deletions(-) diff --git a/models/clopath_synapse.h b/models/clopath_synapse.h index 0252e7f019..f707aefb5e 100644 --- a/models/clopath_synapse.h +++ b/models/clopath_synapse.h @@ -61,13 +61,6 @@ synapses can only be connected to neuron models that are capable of doing this archiving. So far, compatible models are ``aeif_psc_delta_clopath`` and ``hh_psc_alpha_clopath``. -.. warning:: - - This synaptic plasticity rule does not take - :ref:`precise spike timing ` into - account. When calculating the weight update, the precise spike time part - of the timestamp is ignored. - See also [2]_, [3]_. Parameters diff --git a/models/eprop_synapse_bsshslm_2020.h b/models/eprop_synapse_bsshslm_2020.h index 52223ca67b..bb8742461d 100644 --- a/models/eprop_synapse_bsshslm_2020.h +++ b/models/eprop_synapse_bsshslm_2020.h @@ -76,13 +76,6 @@ For more information on the optimizers, see the documentation of the weight opti Details on the event-based NEST implementation of e-prop can be found in [2]_. -.. warning:: - - This synaptic plasticity rule does not take - :ref:`precise spike timing ` into - account. When calculating the weight update, the precise spike time part - of the timestamp is ignored. - Parameters ++++++++++ diff --git a/models/ht_synapse.h b/models/ht_synapse.h index 1221e89116..d83c8a0b20 100644 --- a/models/ht_synapse.h +++ b/models/ht_synapse.h @@ -54,13 +54,6 @@ Synaptic dynamics are given by For implementation details see: `HillTononi_model <../model_details/HillTononiModels.ipynb>`_ -.. warning:: - - This synaptic plasticity rule does not take - :ref:`precise spike timing ` into - account. When calculating the weight update, the precise spike time part - of the timestamp is ignored. - Parameters ++++++++++ diff --git a/models/jonke_synapse.h b/models/jonke_synapse.h index e8bb578566..c9bd4e81d2 100644 --- a/models/jonke_synapse.h +++ b/models/jonke_synapse.h @@ -74,13 +74,6 @@ and This makes it possible to implement update rules which approximate the rule stated in [1]_, and for examples, the rules given in [2]_ and [3]_. -.. warning:: - - This synaptic plasticity rule does not take - :ref:`precise spike timing ` into - account. When calculating the weight update, the precise spike time part - of the timestamp is ignored. - Parameters ++++++++++ diff --git a/models/quantal_stp_synapse.h b/models/quantal_stp_synapse.h index 3c866a3674..c5841e1a5f 100644 --- a/models/quantal_stp_synapse.h +++ b/models/quantal_stp_synapse.h @@ -52,13 +52,6 @@ equations is taken from Maass and Markram 2002 [3]_. The connection weight is interpreted as the maximal weight that can be obtained if all n release sites are activated. -.. warning:: - - This synaptic plasticity rule does not take - :ref:`precise spike timing ` into - account. When calculating the weight update, the precise spike time part - of the timestamp is ignored. - Parameters ++++++++++ diff --git a/models/stdp_dopamine_synapse.h b/models/stdp_dopamine_synapse.h index 118c0054ff..1ade54c0a0 100644 --- a/models/stdp_dopamine_synapse.h +++ b/models/stdp_dopamine_synapse.h @@ -54,13 +54,6 @@ of neurons. The spikes emitted by the pool of dopamine neurons are delivered to the synapse via the assigned volume transmitter. The dopaminergic dynamics is calculated in the synapse itself. -.. warning:: - - This synaptic plasticity rule does not take - :ref:`precise spike timing ` into - account. When calculating the weight update, the precise spike time part - of the timestamp is ignored. - Parameters ++++++++++ diff --git a/models/stdp_facetshw_synapse_hom.h b/models/stdp_facetshw_synapse_hom.h index 2b103eed48..0d7928912f 100644 --- a/models/stdp_facetshw_synapse_hom.h +++ b/models/stdp_facetshw_synapse_hom.h @@ -54,13 +54,6 @@ The modified spike pairing scheme requires the calculation of ``tau_minus_`` within this synapse and not at the neuron site via ``Kplus_`` like in ``stdp_synapse_hom``. -.. warning:: - - This synaptic plasticity rule does not take - :ref:`precise spike timing ` into - account. When calculating the weight update, the precise spike time part - of the timestamp is ignored. - The synapse IDs are assigned to each synapse in an ascending order (0,1,2, ...) according their first presynaptic activity and is used to group synapses that are updated at once. It is possible to avoid activity dependent synapse diff --git a/models/stdp_nn_pre_centered_synapse.h b/models/stdp_nn_pre_centered_synapse.h index ec2791efbf..111d1342b2 100644 --- a/models/stdp_nn_pre_centered_synapse.h +++ b/models/stdp_nn_pre_centered_synapse.h @@ -76,13 +76,6 @@ occurrence, and is reset to 0 on a post-spike occurrence. The postsynaptic trace (implemented on the postsynaptic neuron side) decays with the time constant ``tau_minus`` and increases to 1 on a post-spike occurrence. -.. warning:: - - This synaptic plasticity rule does not take - :ref:`precise spike timing ` into - account. When calculating the weight update, the precise spike time part - of the timestamp is ignored. - Parameters ++++++++++ diff --git a/models/stdp_nn_restr_synapse.h b/models/stdp_nn_restr_synapse.h index 7562266dc3..b586546086 100644 --- a/models/stdp_nn_restr_synapse.h +++ b/models/stdp_nn_restr_synapse.h @@ -73,13 +73,6 @@ eligibility trace [1]_ (implemented on the postsynaptic neuron side). It decays exponentially with the time constant ``tau_minus`` and increases to 1 on a post-spike occurrence (instead of increasing by 1 as in ``stdp_synapse``). -.. warning:: - - This synaptic plasticity rule does not take - :ref:`precise spike timing ` into - account. When calculating the weight update, the precise spike time part - of the timestamp is ignored. - Parameters ++++++++++ diff --git a/models/stdp_nn_symm_synapse.h b/models/stdp_nn_symm_synapse.h index 92970ec8ec..4e50792e3b 100644 --- a/models/stdp_nn_symm_synapse.h +++ b/models/stdp_nn_symm_synapse.h @@ -73,13 +73,6 @@ occurrence. The postsynaptic trace (implemented on the postsynaptic neuron side) decays with the time constant ``tau_minus`` and increases to 1 on a post-spike occurrence. -.. warning:: - - This synaptic plasticity rule does not take - :ref:`precise spike timing ` into - account. When calculating the weight update, the precise spike time part - of the timestamp is ignored. - Parameters ++++++++++ diff --git a/models/stdp_pl_synapse_hom.h b/models/stdp_pl_synapse_hom.h index b01a79c79d..e130135bbc 100644 --- a/models/stdp_pl_synapse_hom.h +++ b/models/stdp_pl_synapse_hom.h @@ -60,13 +60,6 @@ Parameters The parameters can only be set by SetDefaults and apply to all synapses of the model. -.. warning:: - - This synaptic plasticity rule does not take - :ref:`precise spike timing ` into - account. When calculating the weight update, the precise spike time part - of the timestamp is ignored. - References ++++++++++ diff --git a/models/stdp_synapse.h b/models/stdp_synapse.h index 61b3cbcafa..a878068314 100644 --- a/models/stdp_synapse.h +++ b/models/stdp_synapse.h @@ -53,13 +53,6 @@ Description dependent plasticity (as defined in [1]_). Here the weight dependence exponent can be set separately for potentiation and depression. -.. warning:: - - This synaptic plasticity rule does not take - :ref:`precise spike timing ` into - account. When calculating the weight update, the precise spike time part - of the timestamp is ignored. - See also [2]_, [3]_, [4]_. Parameters diff --git a/models/stdp_synapse_hom.h b/models/stdp_synapse_hom.h index 803f513585..d86e9bc686 100644 --- a/models/stdp_synapse_hom.h +++ b/models/stdp_synapse_hom.h @@ -56,13 +56,6 @@ exponent can be set separately for potentiation and depression. * Guetig STDP [1]_ mu_plus = mu_minus = [0.0,1.0] * van Rossum STDP [4]_ mu_plus = 0.0 mu_minus = 1.0 -.. warning:: - - This synaptic plasticity rule does not take - :ref:`precise spike timing ` into - account. When calculating the weight update, the precise spike time part - of the timestamp is ignored. - Parameters ++++++++++ diff --git a/models/stdp_triplet_synapse.h b/models/stdp_triplet_synapse.h index f2a8f8ced3..305e4e978d 100644 --- a/models/stdp_triplet_synapse.h +++ b/models/stdp_triplet_synapse.h @@ -57,13 +57,6 @@ plasticity accounting for spike triplet effects (as defined in [1]_). without changing the postsynaptic archiving-node (clip the traces to a maximum of 1). -.. warning:: - - This synaptic plasticity rule does not take - :ref:`precise spike timing ` into - account. When calculating the weight update, the precise spike time part - of the timestamp is ignored. - Parameters ++++++++++ diff --git a/models/tsodyks2_synapse.h b/models/tsodyks2_synapse.h index a57f300244..fc117b21a7 100644 --- a/models/tsodyks2_synapse.h +++ b/models/tsodyks2_synapse.h @@ -54,13 +54,6 @@ The parameter ``A_se`` from the publications is represented by the synaptic weight. The variable x in the synapse properties is the factor that scales the synaptic weight. -.. warning:: - - This synaptic plasticity rule does not take - :ref:`precise spike timing ` into - account. When calculating the weight update, the precise spike time part - of the timestamp is ignored. - See also [3]_. Under identical conditions, the tsodyks2_synapse produces slightly diff --git a/models/tsodyks_synapse.h b/models/tsodyks_synapse.h index bb6c1e3938..54467a3432 100644 --- a/models/tsodyks_synapse.h +++ b/models/tsodyks_synapse.h @@ -87,13 +87,6 @@ neuron, however, might choose to have a synaptic current that is not necessarily identical to the concentration of transmitter ``y(t)`` in the synaptic cleft. It may realize an arbitrary postsynaptic effect depending on ``y(t)``. -.. warning:: - - This synaptic plasticity rule does not take - :ref:`precise spike timing ` into - account. When calculating the weight update, the precise spike time part - of the timestamp is ignored. - Parameters ++++++++++ diff --git a/models/tsodyks_synapse_hom.h b/models/tsodyks_synapse_hom.h index e01c49f04b..cf1c24fb8f 100644 --- a/models/tsodyks_synapse_hom.h +++ b/models/tsodyks_synapse_hom.h @@ -86,13 +86,6 @@ might choose to have a synaptic current that is not necessarily identical to the concentration of transmitter y(t) in the synaptic cleft. It may realize an arbitrary postsynaptic effect depending on y(t). -.. warning:: - - This synaptic plasticity rule does not take - :ref:`precise spike timing ` into - account. When calculating the weight update, the precise spike time part - of the timestamp is ignored. - Parameters ++++++++++ diff --git a/models/urbanczik_synapse.h b/models/urbanczik_synapse.h index 04fcd05d06..a8560148d9 100644 --- a/models/urbanczik_synapse.h +++ b/models/urbanczik_synapse.h @@ -61,13 +61,6 @@ which is continuous in time. Therefore they can only be connected to neuron models that are capable of doing this archiving. So far, the only compatible model is :doc:`pp_cond_exp_mc_urbanczik `. -.. warning:: - - This synaptic plasticity rule does not take - :ref:`precise spike timing ` into - account. When calculating the weight update, the precise spike time part - of the timestamp is ignored. - Parameters ++++++++++ diff --git a/models/vogels_sprekeler_synapse.h b/models/vogels_sprekeler_synapse.h index cf30380486..d37e1c9c57 100644 --- a/models/vogels_sprekeler_synapse.h +++ b/models/vogels_sprekeler_synapse.h @@ -47,13 +47,6 @@ irrespective of the order of the pre- and postsynaptic spikes. Each pre-synaptic spike also causes a constant depression of the synaptic weight which differentiates this rule from other classical STDP rules. -.. warning:: - - This synaptic plasticity rule does not take - :ref:`precise spike timing ` into - account. When calculating the weight update, the precise spike time part - of the timestamp is ignored. - Parameters ++++++++++ From a0e6f0edacb057f8911dfd7316616b75a0288c90 Mon Sep 17 00:00:00 2001 From: "C.A.P. Linssen" Date: Tue, 10 Sep 2024 10:29:24 +0200 Subject: [PATCH 5/6] move precise spike time offset from Event to Time class --- testsuite/pytests/test_stdp_multiplicity.py | 22 +++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/testsuite/pytests/test_stdp_multiplicity.py b/testsuite/pytests/test_stdp_multiplicity.py index 7317b848e6..465ee52f0a 100644 --- a/testsuite/pytests/test_stdp_multiplicity.py +++ b/testsuite/pytests/test_stdp_multiplicity.py @@ -58,23 +58,29 @@ class TestStdpSpikeMultiplicity: delta, since in this case all spikes are at the end of the step, i.e., all spikes have identical times independent of delta. - 2. We choose delta values that are decrease by factors of 2. The + 2. We choose delta values that are decreased by factors of 2. The plasticity rules depend on spike-time differences through + :: + exp(dT / tau) where dT is the time between pre- and postsynaptic spikes. We construct pre- and postsynaptic spike times so that - dT = pre_post_shift + m * delta + :: + + dT = pre_post_shift + m * delta - with m * delta < resolution << pre_post_shift. The time-dependence + with ``m * delta < resolution << pre_post_shift``. The time-dependence of the plasticity rule is therefore to good approximation linear in delta. - We can thus test as follows: Let w_pl be the weight obtained with the - plain parrot, and w_ps_j the weight obtained with the precise parrot - for delta_j = delta0 / 2^j. Then, + We can thus test as follows: Let ``w_pl`` be the weight obtained with the + plain parrot, and ``w_ps_j`` the weight obtained with the precise parrot + for ``delta_j = delta0 / 2^j``. Then, + + :: ( w_ps_{j+1} - w_pl ) / ( w_ps_j - w_pl ) ~ 0.5 for all j @@ -210,12 +216,12 @@ def test_stdp_multiplicity(self, pre_post_shift, max_abs_err=1e-3): Enforce that the error should strictly decrease with smaller timestep.""" - post_weights = self.run_protocol(pre_post_shift=10.0) + post_weights = self.run_protocol(pre_post_shift=pre_post_shift) w_plain = np.array(post_weights["parrot"]) w_precise = np.array(post_weights["parrot_ps"]) assert all(w_plain == w_plain[0]), "Plain weights should be independent of timestep!" - dw = w_precise - w_plain + abs_err = np.abs(w_precise - w_plain) assert abs_err[-1] < max_abs_err, ( "Final absolute error is " + "{0:.2E}".format(abs_err[-1]) From 86a54e2853035942044829c8b7fd5f09434565ff Mon Sep 17 00:00:00 2001 From: "C.A.P. Linssen" Date: Tue, 10 Sep 2024 10:30:02 +0200 Subject: [PATCH 6/6] move precise spike time offset from Event to Time class --- testsuite/pytests/test_visualization.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/testsuite/pytests/test_visualization.py b/testsuite/pytests/test_visualization.py index f052d5744d..7e8be370a5 100644 --- a/testsuite/pytests/test_visualization.py +++ b/testsuite/pytests/test_visualization.py @@ -100,7 +100,7 @@ def voltage_trace_verify(self, device): @pytest.mark.skipif(not PLOTTING_POSSIBLE, reason="Plotting impossible because matplotlib or display missing") def test_voltage_trace_from_device(self): """Test voltage_trace from device""" - import nest.voltage_trace as nvtrace + import nest.voltage_trace nest.ResetKernel() nodes = nest.Create("iaf_psc_alpha", 2) @@ -111,10 +111,11 @@ def test_voltage_trace_from_device(self): nest.Simulate(100) # Test with data from device + plt.close("all") nest.voltage_trace.from_device(device) self.voltage_trace_verify(device) - # Test with fata from file + # Test with data from file vm = device.get("events") data = np.zeros([len(vm["senders"]), 3]) data[:, 0] = vm["senders"] @@ -123,6 +124,8 @@ def test_voltage_trace_from_device(self): filename = os.path.join(self.nest_tmpdir(), "voltage_trace.txt") self.filenames.append(filename) np.savetxt(filename, data) + + plt.close("all") nest.voltage_trace.from_file(filename) self.voltage_trace_verify(device) @@ -156,7 +159,7 @@ def spike_recorder_raster_verify(self, sr_ref): @pytest.mark.skipif(not PLOTTING_POSSIBLE, reason="Plotting impossible because matplotlib or display missing") def test_raster_plot(self): """Test raster_plot""" - import nest.raster_plot as nraster + import nest.raster_plot sr, sr_to_file = self.spike_recorder_data_setup(to_file=True) spikes = sr.get("events")