void nest::iaf_psc_alpha_canon::update( Time const& origin, const long_t from, const long_t to ) { assert( to >= 0 ); assert( static_cast< delay >( from ) < kernel().connection_manager.get_min_delay() ); assert( from < to ); // at start of slice, tell input queue to prepare for delivery if ( from == 0 ) B_.events_.prepare_delivery(); /* Neurons may have been initialized to superthreshold potentials. We need to check for this here and issue spikes at the beginning of the interval. */ if ( S_.y3_ >= P_.U_th_ ) emit_instant_spike_( origin, from, V_.h_ms_ * ( 1 - std::numeric_limits< double_t >::epsilon() ) ); for ( long_t lag = from; lag < to; ++lag ) { // time at start of update step const long_t T = origin.get_steps() + lag; // if neuron returns from refractoriness during this step, place // pseudo-event in queue to mark end of refractory period if ( S_.is_refractory_ && ( T + 1 - S_.last_spike_step_ == V_.refractory_steps_ ) ) B_.events_.add_refractory( T, S_.last_spike_offset_ ); // save state at beginning of interval for spike-time interpolation V_.y0_before_ = S_.y0_; V_.y2_before_ = S_.y2_; V_.y3_before_ = S_.y3_; // get first event double_t ev_offset; double_t ev_weight; bool end_of_refract; if ( !B_.events_.get_next_spike( T, ev_offset, ev_weight, end_of_refract ) ) { // No incoming spikes, handle with fixed propagator matrix. // Handling this case separately improves performance significantly // if there are many steps without input spikes. // update membrane potential if ( !S_.is_refractory_ ) { S_.y3_ = V_.P30_ * ( P_.I_e_ + S_.y0_ ) + V_.P31_ * S_.y1_ + V_.P32_ * S_.y2_ + V_.expm1_tau_m_ * S_.y3_ + S_.y3_; // lower bound of membrane potential S_.y3_ = ( S_.y3_ < P_.U_min_ ? P_.U_min_ : S_.y3_ ); } // update synaptic currents S_.y2_ = V_.expm1_tau_syn_ * V_.h_ms_ * S_.y1_ + V_.expm1_tau_syn_ * S_.y2_ + V_.h_ms_ * S_.y1_ + S_.y2_; S_.y1_ = V_.expm1_tau_syn_ * S_.y1_ + S_.y1_; /* The following must not be moved before the y1_, y2_ update, since the spike-time interpolation within emit_spike_ depends on all state variables having their values at the end of the interval. */ if ( S_.y3_ >= P_.U_th_ ) emit_spike_( origin, lag, 0, V_.h_ms_ ); } else { // We only get here if there is at least on event, // which has been read above. We can therefore use // a do-while loop. // Time within step is measured by offsets, which are h at the beginning // and 0 at the end of the step. double_t last_offset = V_.h_ms_; // start of step do { // time is measured backward: inverse order in difference const double_t ministep = last_offset - ev_offset; propagate_( ministep ); // check for threshold crossing during ministep // this must be done before adding the input, since // interpolation requires continuity if ( S_.y3_ >= P_.U_th_ ) emit_spike_( origin, lag, V_.h_ms_ - last_offset, ministep ); // handle event if ( end_of_refract ) S_.is_refractory_ = false; // return from refractoriness else S_.y1_ += V_.PSCInitialValue_ * ev_weight; // spike input // store state V_.y2_before_ = S_.y2_; V_.y3_before_ = S_.y3_; last_offset = ev_offset; } while ( B_.events_.get_next_spike( T, ev_offset, ev_weight, end_of_refract ) ); // no events remaining, plain update step across remainder // of interval if ( last_offset > 0 ) // not at end of step, do remainder { propagate_( last_offset ); if ( S_.y3_ >= P_.U_th_ ) emit_spike_( origin, lag, V_.h_ms_ - last_offset, last_offset ); } } // else // Set new input current. The current change occurs at the // end of the interval and thus must come AFTER the threshold- // crossing interpolation S_.y0_ = B_.currents_.get_value( lag ); // logging B_.logger_.record_data( origin.get_steps() + lag ); } // from lag = from ... }
void iaf_psc_delta_canon::update( Time const& origin, const long from, const long to ) { assert( to >= 0 ); assert( static_cast< delay >( from ) < kernel().connection_manager.get_min_delay() ); assert( from < to ); // at start of slice, tell input queue to prepare for delivery if ( from == 0 ) { B_.events_.prepare_delivery(); } /* The psc_delta neuron can fire only 1. precisely upon spike arrival 2. in between spike arrivals when threshold is reached due to the DC current. 3. if the membrane potential is superthreshold at the BEGINNING of a slice due to initialization. In case 1, the spike time is known immediately. In case 2, the spike time can be found by solving the membrane equation. In case 3, the spike time is defined to be at from+epsilon. Thus, we can take arbitrary time steps within (from, to]. Since slice_ring_buffer's delivery mechanism is built on time slices, we still need to step through the individual time steps to check for events. In typical network simulations and for typical step sizes, the probability of steps without input spikes is small. So the outer loop is over steps. */ // check for super-threshold at beginning if ( S_.U_ >= P_.U_th_ ) { emit_instant_spike_( origin, from, V_.h_ms_ * ( 1 - std::numeric_limits< double >::epsilon() ) ); } for ( long lag = from; lag < to; ++lag ) { // time at start of update step const long T = origin.get_steps() + lag; // Time within step is measured by offsets, which are h at the beginning // and 0 at the end of the step. double t = V_.h_ms_; // place pseudo-event in queue to mark end of refractory period if ( S_.is_refractory_ && ( T + 1 - S_.last_spike_step_ == V_.refractory_steps_ ) ) { B_.events_.add_refractory( T, S_.last_spike_offset_ ); } // get first event double ev_offset; double ev_weight; bool end_of_refract; if ( not B_.events_.get_next_spike( T, true, ev_offset, ev_weight, end_of_refract ) ) { // No incoming spikes, handle with fixed propagator matrix. // Handling this case separately improves performance significantly // if there are many steps without input spikes. // update membrane potential if ( not S_.is_refractory_ ) { /* The following way of updating U_ is numerically more precise than the more natural approach U_ = exp_t_ * U_ + I_contrib_; particularly when U_ * exp_t_ is close to -I_contrib_. */ // contribution of the stepwise constant current const double I_ext = -V_.expm1_t_ * V_.R_ * ( S_.I_ + P_.I_e_ ); S_.U_ = I_ext + V_.expm1_t_ * S_.U_ + S_.U_; S_.U_ = S_.U_ < P_.U_min_ ? P_.U_min_ : S_.U_; // lower bound on potential if ( S_.U_ >= P_.U_th_ ) { emit_spike_( origin, lag, 0 ); // offset is zero at end of step } // We exploit here that the refractory period must be at least // one time step long. So even if the spike had happened at the // very beginning of the step, the neuron would remain refractory // for the rest of the step. We can thus ignore the time reset // issued by emit_spike_(). } // nothing to do if neuron is refractory } else { // We only get here if there is at least one event, // which has been read above. We can therefore use // a do-while loop. do { if ( S_.is_refractory_ ) { // move time to time of event t = ev_offset; // normal spikes need to be accumulated if ( not end_of_refract ) { if ( S_.with_refr_input_ ) { V_.refr_spikes_buffer_ += ev_weight * std::exp( -( ( S_.last_spike_step_ - T - 1 ) * V_.h_ms_ - ( S_.last_spike_offset_ - ev_offset ) + P_.t_ref_ ) / P_.tau_m_ ); } } else { // return from refractoriness---apply buffered spikes S_.is_refractory_ = false; if ( S_.with_refr_input_ ) { S_.U_ += V_.refr_spikes_buffer_; V_.refr_spikes_buffer_ = 0.0; } // check if buffered spikes cause new spike if ( S_.U_ >= P_.U_th_ ) { emit_instant_spike_( origin, lag, t ); } } // nothing more to do in this loop iteration continue; } // we get here only if the neuron is not refractory // update neuron to time of event // time is measured backward: inverse order in difference propagate_( t - ev_offset ); t = ev_offset; // Check whether we have passed the threshold. If yes, emit a // spike at the precise location of the crossing. // Time within the step need not be reset to the precise time // of the spike, since the neuron will be refractory for the // remainder of the step. // The event cannot be a return-from-refractoriness event, // since that violates the assumption that the neuron was not // refractory. We can thus ignore the event, since the neuron // is refractory after the spike and ignores all input. if ( S_.U_ >= P_.U_th_ ) { emit_spike_( origin, lag, t ); continue; } // neuron is not refractory: add input spike, check for output // spike S_.U_ += ev_weight; if ( S_.U_ >= P_.U_th_ ) { emit_instant_spike_( origin, lag, t ); } } while ( B_.events_.get_next_spike( T, true, ev_offset, ev_weight, end_of_refract ) ); // no events remaining, plain update step across remainder // of interval if ( not S_.is_refractory_ && t > 0 ) // not at end of step, do remainder { propagate_( t ); if ( S_.U_ >= P_.U_th_ ) { emit_spike_( origin, lag, 0 ); } } } // else // voltage logging B_.logger_.record_data( origin.get_steps() + lag ); S_.I_ = B_.currents_.get_value( lag ); } }