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 ...
}
Ejemplo n.º 2
0
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 );
  }
}