From 1ccdbc7008f09083b3e4e47b2717654e63f87748 Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Mon, 23 Jan 2017 13:31:04 +0100 Subject: [PATCH 01/48] models implementing time-reversal state space spike detection algroithms on precise version of exp-neuron in --- precise/CMakeLists.txt | 1 + precise/iaf_psc_exp_ps_time_reversal.cpp | 720 +++++++++++++++++++++++ precise/iaf_psc_exp_ps_time_reversal.h | 616 +++++++++++++++++++ precise/precisemodule.cpp | 3 + 4 files changed, 1340 insertions(+) create mode 100644 precise/iaf_psc_exp_ps_time_reversal.cpp create mode 100644 precise/iaf_psc_exp_ps_time_reversal.h diff --git a/precise/CMakeLists.txt b/precise/CMakeLists.txt index 0e6eeaeab0..4d5b781dee 100644 --- a/precise/CMakeLists.txt +++ b/precise/CMakeLists.txt @@ -25,6 +25,7 @@ set( precise_sources iaf_psc_exp_ps.cpp iaf_psc_exp_ps.h poisson_generator_ps.cpp poisson_generator_ps.h parrot_neuron_ps.cpp parrot_neuron_ps.h + iaf_psc_exp_ps_time_reversal.cpp iaf_psc_exp_ps_time_reversal.h precisemodule.cpp precisemodule.h ) diff --git a/precise/iaf_psc_exp_ps_time_reversal.cpp b/precise/iaf_psc_exp_ps_time_reversal.cpp new file mode 100644 index 0000000000..386b5e2e57 --- /dev/null +++ b/precise/iaf_psc_exp_ps_time_reversal.cpp @@ -0,0 +1,720 @@ +/* + * iaf_psc_exp_ps_time_reversal.cpp + * + * 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 . + * + */ +#include "iaf_psc_exp_ps_time_reversal.h" + +#include "exceptions.h" +#include "dict.h" +#include "integerdatum.h" +#include "doubledatum.h" +#include "dictutils.h" +#include "universal_data_logger_impl.h" +#include "arraydatum.h" + +#include + +/* ---------------------------------------------------------------- + * Recordables map + * ---------------------------------------------------------------- */ + +nest::RecordablesMap nest::iaf_psc_exp_ps_time_reversal::recordablesMap_; + +namespace nest +{ + // Override the create() method with one call to RecordablesMap::insert_() + // for each quantity to be recorded. + template <> + void RecordablesMap::create() + { + // use standard names whereever you can for consistency! + insert_(names::V_m, & iaf_psc_exp_ps_time_reversal::get_V_m_); + insert_(names::I_syn, & iaf_psc_exp_ps_time_reversal::get_I_syn_); + insert_(names::y1_ex, & iaf_psc_exp_ps_time_reversal::get_y1_ex_); + insert_(names::y1_in, &iaf_psc_exp_ps_time_reversal::get_y1_in_); + insert_(names::y0, &iaf_psc_exp_ps_time_reversal::get_y0_); + + } +} + +/* ---------------------------------------------------------------- + * Default constructors defining default parameters and state + * ---------------------------------------------------------------- */ + +nest::iaf_psc_exp_ps_time_reversal::Parameters_::Parameters_() + : tau_m_ ( 10.0 ), // ms + tau_ex_ ( 2.0 ), // ms + tau_in_ ( 2.0 ), // ms + c_m_ (250.0 ), // pF + t_ref_ ( 2.0 ), // ms + E_L_ ( 0.0 ), // mV + I_e_ ( 0.0 ), // pA + U_th_ ( -55.0-E_L_), // mV, rel to E_L_ + U_min_ (-std::numeric_limits::infinity()), // mV + U_reset_( -70.0-E_L_) // mV, rel to E_L_ +{ + calc_const_spike_test_(); +} + +nest::iaf_psc_exp_ps_time_reversal::State_::State_() + : y0_(0.0), + y1_ex_(0.0), + y1_in_(0.0), + y2_(0.0), + is_refractory_(false), + last_spike_step_(-1), + last_spike_offset_(0.0), + dhaene_quick1(0), + dhaene_quick2(0), + dhaene_tmax_lt_t1(0), + dhaene_max(0), + dhaene_det_spikes(0), + c0(0), + c1a(0), + c1b(0), + c3a(0), + c3b(0), + c4(0), + det_spikes(0) +{} + +nest::iaf_psc_exp_ps_time_reversal::Buffers_::Buffers_(iaf_psc_exp_ps_time_reversal & n) + : logger_(n) +{} + +nest::iaf_psc_exp_ps_time_reversal::Buffers_::Buffers_(const Buffers_ &, iaf_psc_exp_ps_time_reversal & n) + : logger_(n) +{} + +/* ---------------------------------------------------------------- + * Parameter and state extractions and manipulation functions + * ---------------------------------------------------------------- */ + + +//constants for time-reversal state space spike-detection algorithm + +void nest::iaf_psc_exp_ps_time_reversal::Parameters_::calc_const_spike_test_() +{ + //line corresponding to the final timestep i.e t_right: continuation + // of the curved boundary: a + I*b + a1_ =tau_m_ * tau_ex_; + a2_ =tau_m_ * (tau_m_ - tau_ex_); + a3_ =c_m_ * U_th_ * (tau_m_ - tau_ex_); + a4_ =c_m_ * (tau_m_ - tau_ex_); + + //line joining endpoints of the envelope: \alpha I + \beta + b1_ =-tau_m_ * tau_m_; + b2_ =tau_m_ * tau_ex_; + b3_ =tau_m_*(tau_m_ - tau_ex_)- tau_m_*tau_m_ + tau_m_* tau_ex_; + b4_ =-tau_m_*tau_m_; //b1 + b5_ =tau_m_*c_m_*U_th_; + b6_ =tau_m_*(tau_m_- tau_ex_); + b7_ =-c_m_*(tau_m_ - tau_ex_); + + //envelope or curved boundary + c1_ =tau_m_/c_m_; + c2_ =(-tau_m_ * tau_ex_)/(c_m_*(tau_m_ - tau_ex_)); + c3_ =(tau_m_ * tau_m_)/ (c_m_ * (tau_m_ - tau_ex_)); + c4_ =tau_ex_/tau_m_; + c5_ =(c_m_ * U_th_)/tau_m_; + c6_ =1-(tau_ex_/tau_m_); + + //parallel line + + d1_ = tau_m_ * c_m_ ; + d2_ = tau_m_ * tau_ex_; + d3_ = c_m_ * (tau_m_ - tau_ex_); +} + +void nest::iaf_psc_exp_ps_time_reversal::Parameters_::get(DictionaryDatum & d) const +{ + def(d, names::E_L, E_L_); + def(d, names::I_e, I_e_); + def(d, names::V_th, U_th_+E_L_); + def(d, names::V_min, U_min_+E_L_); + def(d, names::V_reset, U_reset_+E_L_); + def(d, names::C_m, c_m_); + def(d, names::tau_m, tau_m_); + def(d, names::tau_syn_ex, tau_ex_); + def(d, names::tau_syn_in, tau_in_); + def(d, names::t_ref, t_ref_); + +} + +double nest::iaf_psc_exp_ps_time_reversal::Parameters_::set(const DictionaryDatum & d) +{ + + updateValue(d, names::tau_m, tau_m_); + updateValue(d, names::tau_syn_ex, tau_ex_); + updateValue(d, names::tau_syn_in, tau_in_); + updateValue(d, names::C_m, c_m_); + updateValue(d, names::t_ref, t_ref_); + updateValue(d, names::I_e, I_e_); + + + // if U0_ is changed, we need to adjust all variables defined relative to U0_ + const double ELold = E_L_; + updateValue(d, names::E_L, E_L_); + const double delta_EL = E_L_ - ELold; + + if(updateValue(d, names::V_reset, U_reset_)) + U_reset_ -= E_L_; + else + U_reset_ -= delta_EL; + + if (updateValue(d, names::V_th, U_th_)) + U_th_ -= E_L_; + else + U_th_ -= delta_EL; + + if (updateValue(d, names::V_min, U_min_)) + U_min_ -= E_L_; + else + U_min_ -= delta_EL; + + if ( U_reset_ >= U_th_ ) + throw BadProperty("Reset potential must be smaller than threshold."); + + if ( U_reset_ < U_min_ ) + throw BadProperty("Reset potential must be greater equal minimum potential."); + + if ( c_m_ <= 0 ) + throw BadProperty("Capacitance must be strictly positive."); + + if ( t_ref_ < 0 ) + throw BadProperty("Refractory time must not be negative."); + + if ( tau_m_ <= 0 || tau_ex_ <= 0 || tau_in_ <= 0 ) + throw BadProperty("All time constants must be strictly positive."); + + if ( tau_m_ == tau_ex_ || tau_m_ == tau_in_ ) + throw BadProperty("Membrane and synapse time constant(s) must differ." + "See note in documentation."); + + calc_const_spike_test_(); + + return delta_EL; +} + +void nest::iaf_psc_exp_ps_time_reversal::State_::get(DictionaryDatum & d, + const Parameters_ & p) const +{ + def(d, names::V_m, y2_ + p.E_L_); // Membrane potential + def(d, names::is_refractory, is_refractory_); + def(d, names::t_spike, Time(Time::step(last_spike_step_)).get_ms()); + def(d, names::offset, last_spike_offset_); + def(d, names::y1_ex, y1_ex_); + def(d, names::y1_in, y1_in_); // y1 state + def(d, names::y2, y2_); // y2 state + def(d, names::I_syn, y1_ex_ + y1_in_); + + + // these entries will break ticket-459 + // because they chane depending on E_L (which is correct) + // since they are only used for debugging we will comment them + // out for the time being + //def(d, names::pot_spikes, c0); + //def(d, names::dhaene_quick1, (dhaene_quick1*100.0/c0)); + //def(d, names::dhaene_quick2, (dhaene_quick2*100.0/c0)); + //def(d, names::dhaene_tmax_lt_t1, (dhaene_tmax_lt_t1*100.0/c0)); + //def(d, names::dhaene_max_geq_V_th, (dhaene_max*100.0/c0)); + //def(d, names::dhaene_det_spikes, (dhaene_det_spikes*100.0/c0)); + + //def(d, names::eq7, (c1a*100.0/c0)); + //def(d, names::eq9, (c1b*100.0/c0)); + //def(d, names::eqs7and9, (c2*100.0/c0)); + //def(d, names::lin_left_geq_V_th, (c3a*100.0/c0)); + //def(d, names::lin_max_geq_V_th, (c3b*100.0/c0)); + //def(d, names::eq13, (c4*100.0/c0)); + //def(d, names::eq12, (det_spikes*100.0/c0)); +} + +void nest::iaf_psc_exp_ps_time_reversal::State_::set(const DictionaryDatum & d, const Parameters_ & p, double delta_EL) +{ + if ( updateValue(d, names::V_m, y2_) ) + y2_ -= p.E_L_; + else + y2_ -= delta_EL; + + //double_t dv; + //updateValue(d, names::dhaene_quick1, dv); + //updateValue(d, names::dhaene_quick2, dv); + //updateValue(d, names::dhaene_tmax_lt_t1, dv); + //updateValue(d, names::dhaene_max_geq_V_th, dv); + //updateValue(d, names::dhaene_det_spikes, dv); + //updateValue(d, names::eq7, dv); + //updateValue(d, names::eq9, dv); + //updateValue(d, names::eqs7and9, dv); + //updateValue(d, names::lin_left_geq_V_th, dv); + //updateValue(d, names::lin_max_geq_V_th, dv); + //updateValue(d, names::eq13, dv); + //updateValue(d, names::eq12, dv); + updateValue(d, names::y1_ex, y1_ex_ ); + updateValue(d, names::y1_in, y1_in_); + updateValue(d, names::y0, y0_); + + +} + +/* ---------------------------------------------------------------- + * Default and copy constructor for node + * ---------------------------------------------------------------- */ + +nest::iaf_psc_exp_ps_time_reversal::iaf_psc_exp_ps_time_reversal() + : Node(), + P_(), + S_(), + B_(*this) +{ + recordablesMap_.create(); +} + +nest::iaf_psc_exp_ps_time_reversal::iaf_psc_exp_ps_time_reversal(const iaf_psc_exp_ps_time_reversal & n) + : Node(n), + P_(n.P_), + S_(n.S_), + B_(n.B_, *this) +{} + +/* ---------------------------------------------------------------- + * Node initialization functions + * ---------------------------------------------------------------- + +void nest::iaf_psc_exp_ps_time_reversal::init_node_(const Node & proto) +{ + const iaf_psc_exp_ps_time_reversal & pr = downcast(proto); + + P_ = pr.P_; + S_ = pr.S_; +}*/ + +void nest::iaf_psc_exp_ps_time_reversal::init_state_(const Node & proto) +{ + const iaf_psc_exp_ps_time_reversal & pr = downcast(proto); + + S_ = pr.S_; +} + +void nest::iaf_psc_exp_ps_time_reversal::init_buffers_() +{ + B_.events_.resize(); + B_.events_.clear(); + B_.currents_.clear(); // includes resize + B_.logger_.reset(); +} + +void nest::iaf_psc_exp_ps_time_reversal::calibrate() +{ + B_.logger_.init(); // ensures initialization in case mm connected after Simulate + + V_.h_ms_ = Time::get_resolution().get_ms(); + + V_.expm1_tau_m_ = numerics::expm1(-V_.h_ms_/P_.tau_m_); + V_.expm1_tau_ex_ = numerics::expm1(-V_.h_ms_/P_.tau_ex_); + V_.expm1_tau_in_ = numerics::expm1(-V_.h_ms_/P_.tau_in_); + V_.P20_ = -P_.tau_m_ / P_.c_m_ * V_.expm1_tau_m_; + V_.P21_ex_ = -P_.tau_m_*P_.tau_ex_ / (P_.tau_m_-P_.tau_ex_) / P_.c_m_ * (V_.expm1_tau_ex_-V_.expm1_tau_m_); + V_.P21_in_ = -P_.tau_m_*P_.tau_in_ / (P_.tau_m_-P_.tau_in_) / P_.c_m_ * (V_.expm1_tau_in_-V_.expm1_tau_m_); + + V_.refractory_steps_ = Time(Time::ms(P_.t_ref_)).get_steps(); + assert( V_.refractory_steps_ >= 0 ); // since t_ref_ >= 0, this can only fail in error +} + +/* ---------------------------------------------------------------- + * Update and spike handling functions + * ---------------------------------------------------------------- */ + +void nest::iaf_psc_exp_ps_time_reversal::update(const Time & 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(); + + /* 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_.y2_ >= P_.U_th_ ) + emit_instant_spike_(origin, from, + V_.h_ms_*(1.0-std::numeric_limits::epsilon())); + + for ( long lag = from; lag < to; ++lag ) + { + // time at start of update step + const long 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 approximation + V_.y0_before_ = S_.y0_; + V_.y1_ex_before_ = S_.y1_ex_; + V_.y1_in_before_ = S_.y1_in_; + V_.y2_before_ = S_.y2_; + + // 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_.y2_ = V_.P20_*(P_.I_e_+S_.y0_) + V_.P21_ex_*S_.y1_ex_ + V_.P21_in_*S_.y1_in_ + V_.expm1_tau_m_*S_.y2_ + S_.y2_; + + // lower bound of membrane potential + S_.y2_ = ( S_.y2_ < P_.U_min_ ? P_.U_min_ : S_.y2_ ); + } + + // update synaptic currents + S_.y1_ex_ = S_.y1_ex_*V_.expm1_tau_ex_ + S_.y1_ex_; + S_.y1_in_ = S_.y1_in_*V_.expm1_tau_in_ + S_.y1_in_; + + /* 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. + */ + // the state space test takes argument dt and + // returns true, spike: if (V(t_{right}) > V_(\theta)); + // returns false: ( (V(t_{right} < V_(\theta) or initial conditions in no-spike region); + // returns true, spike: missed spike excursion, compute t_{max} = dt and find point of + // threshold crossing t_{\theta} using emit_spike_. + V_.bisection_step = V_.h_ms_; + + if (is_spike_(V_.h_ms_)) + emit_spike_(origin, lag, 0, V_.bisection_step); + + } + 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 + + V_.bisection_step = ministep; + + + if (is_spike_(ministep)) + { + emit_spike_(origin, lag, V_.h_ms_-last_offset, V_.bisection_step); + } + + + + // handle event + if ( end_of_refract ) + S_.is_refractory_ = false; // return from refractoriness + else + { + if ( ev_weight >= 0.0 ) + S_.y1_ex_ += ev_weight; // exc. spike input + else + S_.y1_in_ += ev_weight; // inh. spike input + } + + // store state + V_.y1_ex_before_ = S_.y1_ex_; + V_.y1_in_before_ = S_.y1_in_; + V_.y2_before_ = S_.y2_; + 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 + { + + + V_.bisection_step = last_offset; + propagate_(last_offset); + + if (is_spike_(last_offset)) + emit_spike_(origin, lag, V_.h_ms_-last_offset, V_.bisection_step); + + + + } + } // else + + // Set new input current. The current change occurs at the + // end of the interval and thus must come AFTER the threshold- + // crossing approximation + S_.y0_ = B_.currents_.get_value(lag); + + // log state data + B_.logger_.record_data(origin.get_steps() + lag); + } // for +} + +// function handles exact spike times +void nest::iaf_psc_exp_ps_time_reversal::handle(SpikeEvent & e) +{ + assert( e.get_delay() > 0 ); + + /* We need to compute the absolute time stamp of the delivery time + of the spike, since spikes might spend longer than min_delay_ + in the queue. The time is computed according to Time Memo, Rule 3. + */ + const long Tdeliver = e.get_stamp().get_steps() + e.get_delay() - 1; + + B_.events_.add_spike(e.get_rel_delivery_steps(nest::kernel().simulation_manager.get_slice_origin()), + Tdeliver, e.get_offset(), e.get_weight() * e.get_multiplicity()); +} + +void nest::iaf_psc_exp_ps_time_reversal::handle(CurrentEvent & e) +{ + assert( e.get_delay() > 0 ); + + const double_t c = e.get_current(); + const double_t w = e.get_weight(); + + // add weighted current; HEP 2002-10-04 + B_.currents_.add_value(e.get_rel_delivery_steps(nest::kernel().simulation_manager.get_slice_origin() ), w * c); +} + +void nest::iaf_psc_exp_ps_time_reversal::handle(DataLoggingRequest &e) +{ + B_.logger_.handle(e); +} + +// auxiliary functions --------------------------------------------- + +inline +void nest::iaf_psc_exp_ps_time_reversal::set_spiketime(const Time & now) +{ + S_.last_spike_step_ = now.get_steps(); +} + +void nest::iaf_psc_exp_ps_time_reversal::propagate_(const double_t dt) +{ + const double_t expm1_tau_ex = numerics::expm1(-dt/P_.tau_ex_); + const double_t expm1_tau_in = numerics::expm1(-dt/P_.tau_in_); + + if ( !S_.is_refractory_ ) + { + const double_t expm1_tau_m = numerics::expm1(-dt/P_.tau_m_); + + const double_t P20 = -P_.tau_m_ / P_.c_m_ * expm1_tau_m; + const double_t P21_ex = -P_.tau_m_*P_.tau_ex_ / (P_.tau_m_-P_.tau_ex_) / P_.c_m_ * (expm1_tau_ex-expm1_tau_m); + const double_t P21_in = -P_.tau_m_*P_.tau_in_ / (P_.tau_m_-P_.tau_in_) / P_.c_m_ * (expm1_tau_in-expm1_tau_m); + + S_.y2_ = P20*(P_.I_e_+S_.y0_) + P21_ex*S_.y1_ex_ + P21_in*S_.y1_in_ + expm1_tau_m*S_.y2_ + S_.y2_; + } + S_.y1_ex_ = S_.y1_ex_*expm1_tau_ex + S_.y1_ex_; + S_.y1_in_ = S_.y1_in_*expm1_tau_in + S_.y1_in_; +} + +void nest::iaf_psc_exp_ps_time_reversal::emit_spike_(const Time & origin, const long lag, const double_t t0, const double_t dt) +{ + // we know that the potential is subthreshold at t0, super at t0+dt + + // compute spike time relative to beginning of step + const double_t spike_offset = V_.h_ms_ - (t0 + bisectioning_(dt)); + + set_spiketime(Time::step(origin.get_steps() + lag + 1)); + S_.last_spike_offset_ = spike_offset; + + // reset neuron and make it refractory + S_.y2_ = P_.U_reset_; + S_.is_refractory_ = true; + + // send spike + SpikeEvent se; + + se.set_offset(spike_offset); + kernel().event_delivery_manager.send(*this, se, lag); +} + +void nest::iaf_psc_exp_ps_time_reversal::emit_instant_spike_(const Time & origin, const long lag, + const double_t spike_offs) +{ + assert( S_.y2_ >= P_.U_th_ ); // ensure we are superthreshold + + // set stamp and offset for spike + set_spiketime(Time::step(origin.get_steps() + lag + 1)); + S_.last_spike_offset_ = spike_offs; + + // reset neuron and make it refractory + S_.y2_ = P_.U_reset_; + S_.is_refractory_ = true; + + // send spike + SpikeEvent se; + + se.set_offset(S_.last_spike_offset_); + kernel().event_delivery_manager.send(*this, se, lag); +} + +inline double nest::iaf_psc_exp_ps_time_reversal::bisectioning_(const double dt) const +{ + + double_t root = 0.0; + + double_t y2_root = V_.y2_before_; + + double_t div = 2.0; + while ( fabs(P_.U_th_-y2_root) > 1e-14 and (dt/div > 0.0) ) + { + if ( y2_root > P_.U_th_ ) + root -= dt/div; + else + root += dt/div; + + div *= 2.0; + + const double_t expm1_tau_ex = numerics::expm1(-root/P_.tau_ex_); + const double_t expm1_tau_in = numerics::expm1(-root/P_.tau_in_); + const double_t expm1_tau_m = numerics::expm1(-root/P_.tau_m_); + + const double_t P20 = -P_.tau_m_ / P_.c_m_ * expm1_tau_m; + const double_t P21_ex = -P_.tau_m_*P_.tau_ex_ / (P_.tau_m_-P_.tau_ex_) / P_.c_m_ * (expm1_tau_ex-expm1_tau_m); + const double_t P21_in = -P_.tau_m_*P_.tau_in_ / (P_.tau_m_-P_.tau_in_) / P_.c_m_ * (expm1_tau_in-expm1_tau_m); + + y2_root = P20*(P_.I_e_+V_.y0_before_) + P21_ex*V_.y1_ex_before_ + P21_in*V_.y1_in_before_ + expm1_tau_m*V_.y2_before_ + V_.y2_before_; + } + return root; +} + +void nest::iaf_psc_exp_ps_time_reversal::spike_test_count_(const double_t t1) +{ + S_.c0++; // V(t1) < V_th + + // we assume that P_.tau_ex_=P_.tau_in_ + double_t const I_0 = V_.y1_ex_before_ + V_.y1_in_before_; + double_t const V_0 = V_.y2_before_; + double_t const I_t1 = S_.y1_ex_ + S_.y1_in_; + double_t const V_t1 = S_.y2_; + double_t const tau = P_.tau_ex_; + double_t const tau_m = P_.tau_m_; + double_t const I_x = P_.I_e_; + double_t const C_m = P_.c_m_; + double_t const V_th = P_.U_th_; + + double_t const tauC_m = tau_m/C_m; + + double_t const Vdot_0 = -V_0/tau_m + (I_0+I_x)/C_m; + double_t const Vdot_t1 = -V_t1/tau_m + (I_t1+I_x)/C_m; + + // iaflossless tests + if ( Vdot_t1 < 0.0 ) + S_.c1b++; + if ( Vdot_0 > 0.0 ) + { + S_.c1a++; + if ( Vdot_t1 < 0.0 ) + { + S_.c2++; + + if ( Vdot_0*t1 + V_0 >= V_th ) + S_.c3a++; + if ( V_0 + Vdot_0 * (V_0 - V_t1 + Vdot_t1*t1) / (Vdot_t1-Vdot_0) >= V_th ) + S_.c3b++; + + double_t const expm1_tau_syn = numerics::expm1(t1/tau); // positive exponent! + double_t const expm1_tau_m = numerics::expm1(t1/tau_m); // positive exponent! + + double_t const V_0_bar = V_0 - tauC_m*I_x; + //double_t const V_t1_bar = V_t1 - tauC_m*I_x; + double_t const V_th_bar = V_th - tauC_m*I_x; + double_t const V_right_bar = (tau_m*expm1_tau_m - tau*expm1_tau_syn) * V_th_bar / (tau_m-tau); + double_t const I_left = V_th_bar/tauC_m; + double_t const m = (V_right_bar - V_th_bar) / (expm1_tau_syn*I_left); // V_left_bar = V_th_bar + + if ( V_0_bar >= m * (I_0 - I_left) + V_th ) + { + S_.c4++; + + double_t const y = V_th_bar/tauC_m / I_0; + + if ( V_0 >= tau_m/(tau_m-tau) * (-tau/C_m * I_0 + V_th_bar * pow(y, (-tau/tau_m))) ) + S_.det_spikes++; + } + } + } + + // D'Haene tests + double_t const minus_taus = -tau_m*tau / (tau_m-tau); + double_t const V_syn = minus_taus / C_m * I_0; + double_t const V_m = V_0 - tauC_m*I_x - V_syn; + + if ( V_m > 0.0 && V_syn < 0.0 ) + { + S_.dhaene_quick1++; + + double_t const quot = -tau*V_m / (tau_m*V_syn); + + if ( quot <= 1.0 ) + { + S_.dhaene_quick2++; + + double_t const t_max = minus_taus * log(quot); + + if ( t_max < t1 ) + S_.dhaene_tmax_lt_t1++; + + double_t const expm1_tau_syn = numerics::expm1(-t_max/tau); + double_t const expm1_tau_m = numerics::expm1(-t_max/tau_m); + + double_t const P20 = -tau_m*expm1_tau_m / C_m; + double_t const P21 = minus_taus / C_m * (expm1_tau_syn-expm1_tau_m); + + if ( (P20*I_x + P21*I_0 + expm1_tau_m*V_0 + V_0) >= V_th ) + { + S_.dhaene_max++; + if ( t_max <= t1 ) + S_.dhaene_det_spikes++; + } + } + } +} + diff --git a/precise/iaf_psc_exp_ps_time_reversal.h b/precise/iaf_psc_exp_ps_time_reversal.h new file mode 100644 index 0000000000..afc5c0ae14 --- /dev/null +++ b/precise/iaf_psc_exp_ps_time_reversal.h @@ -0,0 +1,616 @@ +/* + * iaf_psc_exp_ps_time_reversal.h + * + * 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 . + * + */ + + +#ifndef IAF_PSC_EXP_PS_TIME_REVERSAL_H +#define IAF_PSC_EXP_PS_TIME_REVERSAL_H + +#include "config.h" + +#include "archiving_node.h" +#include "nest_types.h" +#include "event.h" +#include "ring_buffer.h" +#include "slice_ring_buffer.h" +#include "connection.h" +#include "universal_data_logger.h" +#include "stopwatch.h" +#include "arraydatum.h" + +#include + +/*BeginDocumentation +Name: iaf_psc_exp_ps_time_reversal - Leaky integrate-and-fire neuron +with exponential postsynaptic currents; canoncial implementation; +bisectioning method for approximation of threshold crossing. + +Description: +iaf_psc_exp_ps_time_reversal is the "canonical" implementation of the leaky +integrate-and-fire model neuron with exponential postsynaptic currents +that uses the bisectioning method to approximate the timing of a threshold +crossing [1,2]. This is the most exact implementation available. + +The canonical implementation handles neuronal dynamics in a locally +event-based manner with in coarse time grid defined by the minimum +delay in the network, see [1,2]. Incoming spikes are applied at the +precise moment of their arrival, while the precise time of outgoing +spikes is determined by bisectioning once a threshold crossing has +been detected. Return from refractoriness occurs precisely at spike +time plus refractory period. + +This implementation is more complex than the plain iaf_psc_exp +neuron, but achieves much higher precision. In particular, it does not +suffer any binning of spike times to grid points. Depending on your +application, the canonical application with bisectioning may provide +superior overall performance given an accuracy goal; see [1,2] for +details. Subthreshold dynamics are integrated using exact integration +between events [3]. + +Parameters: + The following parameters can be set in the status dictionary. + E_L double - Resting membrane potential in mV. + C_m double - Specific capacitance of the membrane in pF/mum^2. + tau_m double - Membrane time constant in ms. + tau_syn_ex double - Excitatory synaptic time constant in ms. + tau_syn_in double - Inhibitory synaptic time constant in ms. + t_ref double - Duration of refractory period in ms. + V_th double - Spike threshold in mV. + I_e double - Constant input current in pA. + V_min double - Absolute lower value for the membrane potential. + V_reset double - Reset value for the membrane potential. + +Remarks: + Please note that this node is capable of sending precise spike times + to target nodes (on-grid spike time plus offset). If this node is + connected to a spike_detector, the property "precise_times" of the + spike_detector has to be set to true in order to record the offsets + in addition to the on-grid spike times. + +References: + [1] Morrison A, Straube S, Plesser HE & Diesmann M (2007) Exact subthreshold + integration with continuous spike times in discrete time neural network + simulations. Neural Comput 19, 47–79 + [2] Hanuschkin A, Kunkel S, Helias M, Morrison A and Diesmann M (2010) A + general and efficient method for incorporating precise spike times in + globally timedriven simulations. Front Neuroinform 4:113 + [3] Rotter S & Diesmann M (1999) Exact simulation of time-invariant linear + systems with applications to neuronal modeling. Biol Cybern 81:381-402 + +Author: Kunkel + +Sends: SpikeEvent + +Receives: SpikeEvent, CurrentEvent, DataLoggingRequest + +SeeAlso: iaf_psc_exp, iaf_psc_alpha_canon +*/ + +namespace nest +{ + + /** + * Leaky iaf neuron, exponential PSC synapses, canonical implementation. + * @note Inherit privately from Node, so no classes can be derived + * from this one. + * @todo Implement current input in consistent way. + */ + class iaf_psc_exp_ps_time_reversal : public Node + { + + class Network; + + public: + + /** Basic constructor. + This constructor should only be used by GenericModel to create + model prototype instances. + */ + iaf_psc_exp_ps_time_reversal(); + + /** Copy constructor. + GenericModel::allocate_() uses the copy constructor to clone + actual model instances from the prototype instance. + + @note The copy constructor MUST NOT be used to create nodes based + on nodes that have been placed in the network. + */ + iaf_psc_exp_ps_time_reversal(const iaf_psc_exp_ps_time_reversal &); + + /** + * Import sets of overloaded virtual functions. + * We need to explicitly include sets of overloaded + * virtual functions into the current scope. + * According to the SUN C++ FAQ, this is the correct + * way of doing things, although all other compilers + * happily live without. + */ + using Node::handles_test_event; + using Node::handle; + + port send_test_event( Node&, rport, synindex, bool ); + + void handle(SpikeEvent &); + void handle(CurrentEvent &); + void handle(DataLoggingRequest &); + + port handles_test_event(SpikeEvent &, port); + port handles_test_event(CurrentEvent &, port); + port handles_test_event(DataLoggingRequest &, port); + + bool is_off_grid() const // uses off_grid events + { + return true; + } + + void get_status(DictionaryDatum &) const; + void set_status(const DictionaryDatum &); + + private: + + /** @name Interface functions + * @note These functions are private, so that they can be accessed + * only through a Node*. + */ + //@{ + void init_node_(const Node & proto); + void init_state_(const Node & proto); + void init_buffers_(); + void calibrate(); + + /** + * Time Evolution Operator. + * + * update() promotes the state of the neuron from origin+from to origin+to. + * It does so in steps of the resolution h. Within each step, time is + * advanced from event to event, as retrieved from the spike queue. + * + * Return from refractoriness is handled as a special event in the + * queue, which is marked by a weight that is GSL_NAN. This greatly simplifies + * the code. + * + * For steps, during which no events occur, the precomputed propagator matrix + * is used. For other steps, the propagator matrix is computed as needed. + * + * While the neuron is refractory, membrane potential (y2_) is + * clamped to U_reset_. + */ + void update(Time const & origin, const long from, const long to); + //@} + + // The next two classes need to be friends to access the State_ class/member + friend class RecordablesMap; + friend class UniversalDataLogger; + + void set_spiketime(Time const &); + + /** + * Propagate neuron state. + * Propagate the neuron's state by dt. + * @param dt Interval over which to propagate + */ + void propagate_(const double_t dt); + + /** + * Emit a single spike caused by DC current in absence of spike input. + * Emits a single spike and reset neuron given that the membrane + * potential was below threshold at the beginning of a mini-timestep + * and above afterwards. + * + * @param origin Time stamp at beginning of slice + * @param lag Time step within slice + * @param t0 Beginning of mini-timestep + * @param dt Duration of mini-timestep + */ + void emit_spike_(const Time & origin, const long lag, + const double_t t0, const double_t dt); + + /** + * Emit a single spike at a precisely given time. + * + * @param origin Time stamp at beginning of slice + * @param lag Time step within slice + * @param spike_offset Time offset for spike + */ + void emit_instant_spike_(const Time & origin, const long lag, + const double_t spike_offset); + + /** + * Localize threshold crossing by bisectioning. + * @param double_t length of interval since previous event + * @returns time from previous event to threshold crossing + */ + double_t bisectioning_(const double_t dt) const; + + void spike_test_count_(const double_t); + void spike_test_(const double_t); + bool is_spike_(const double_t); + + // ---------------------------------------------------------------- + + /** + * Independent parameters of the model. + */ + struct Parameters_ + { + /** Membrane time constant in ms. */ + double_t tau_m_; + + /** Time constant of exc. synaptic current in ms. */ + double_t tau_ex_; + + /** Time constant of inh. synaptic current in ms. */ + double_t tau_in_; + + /** Membrane capacitance in pF. */ + double_t c_m_; + + /** Refractory period in ms. */ + double_t t_ref_; + + /** Resting potential in mV. */ + double_t E_L_; + + /** External DC current [pA] */ + double_t I_e_; + + /** Threshold, RELATIVE TO RESTING POTENTAIL(!). + I.e. the real threshold is U_th_ + E_L_. */ + double_t U_th_; + + /** Lower bound, RELATIVE TO RESTING POTENTAIL(!). + I.e. the real lower bound is U_min_+E_L_. */ + double_t U_min_; + + /** Reset potential. + At threshold crossing, the membrane potential is reset to this value. + Relative to resting potential. */ + double_t U_reset_; + + double_t a1_; + double_t a2_; + double_t a3_; + double_t a4_; + + double_t b1_; + double_t b2_; + double_t b3_; + double_t b4_; + double_t b5_; + double_t b6_; + double_t b7_; + + double_t c1_; + double_t c2_; + double_t c3_; + double_t c4_; + double_t c5_; + double_t c6_; + + + double_t d1_; + double_t d2_; + double_t d3_; + + + Parameters_(); //!< Sets default parameter values + void calc_const_spike_test_(); + + void get(DictionaryDatum &) const; //!< Store current values in dictionary + double set(const DictionaryDatum &); //!< Set values from dicitonary + + }; + + // ---------------------------------------------------------------- + + /** + * State variables of the model. + */ + struct State_ + { + double_t y0_; //!< External input current + double_t y1_ex_; //!< Exc. exponetial current + double_t y1_in_; //!< Inh. exponetial current + double_t y2_; //!< Membrane potential (relative to resting potential) + + bool is_refractory_; //!< True while refractory + long last_spike_step_; //!< Time stamp of most recent spike + double_t last_spike_offset_; //!< Offset of most recent spike + + long dhaene_quick1; + long dhaene_quick2; + long dhaene_tmax_lt_t1; + long dhaene_max; + long dhaene_det_spikes; + + long c0; + long c1a; + long c1b; + long c2; + long c3a; + long c3b; + long c4; + long det_spikes; + long state_space_test_spikes; + + State_(); //!< Default initialization + + void get(DictionaryDatum &, const Parameters_ &) const; + void set(const DictionaryDatum &, const Parameters_ &, double delta_EL); + + + + + + }; + + // ---------------------------------------------------------------- + + /** + * Buffers of the model. + */ + struct Buffers_ + { + Buffers_(iaf_psc_exp_ps_time_reversal &); + Buffers_(const Buffers_ &, iaf_psc_exp_ps_time_reversal &); + + /** + * Queue for incoming events. + * @note Handles also pseudo-events marking return from refractoriness. + */ + SliceRingBuffer events_; + RingBuffer currents_; + + //! Logger for all analog data + UniversalDataLogger logger_; + }; + + // ---------------------------------------------------------------- + + /** + * Internal variables of the model. + */ + struct Variables_ + { + double_t h_ms_; //!< Time resolution [ms] + long refractory_steps_; //!< Refractory time in steps + double_t expm1_tau_m_; //!< exp(-h/tau_m) - 1 + double_t expm1_tau_ex_; //!< exp(-h/tau_ex) - 1 + double_t expm1_tau_in_; //!< exp(-h/tau_in) - 1 + double_t P20_; //!< Progagator matrix element, 2nd row + double_t P21_in_; //!< Progagator matrix element, 2nd row + double_t P21_ex_; //!< Progagator matrix element, 2nd row + double_t y0_before_; //!< y0_ at beginning of ministep + double_t y1_ex_before_; //!< y1_ at beginning of ministep + double_t y1_in_before_; //!< y1_ at beginning of ministep + double_t y2_before_; //!< y2_ at beginning of ministep + double_t bisection_step; + }; + + // Access functions for UniversalDataLogger ------------------------------- + + //! Read out the real membrane potential + double_t get_V_m_() const { return S_.y2_ + P_.E_L_; } + double_t get_I_syn_() const { return S_.y1_ex_ + S_.y1_in_; } + double_t get_y1_ex_() const { return S_.y1_ex_; } + double_t get_y1_in_() const { return S_.y1_in_; } + double_t get_y0_() const {return S_.y0_;} + + + // ---------------------------------------------------------------- + + /** + * @defgroup iaf_psc_exp_ps_time_reversal_data + * Instances of private data structures for the different types + * of data pertaining to the model. + * @note The order of definitions is important for speed. + * @{ + */ + Parameters_ P_; + State_ S_; + Variables_ V_; + Buffers_ B_; + /** @} */ + + //! Mapping of recordables names to access functions + static RecordablesMap recordablesMap_; + }; + +inline +port iaf_psc_exp_ps_time_reversal::send_test_event( Node& target, rport receptor_type, synindex, bool ) +{ + SpikeEvent e; + + e.set_sender(*this); + //c.check_event(e); + return target.handles_test_event( e, receptor_type ); +} + +inline +port iaf_psc_exp_ps_time_reversal::handles_test_event(SpikeEvent &, port receptor_type) +{ + if (receptor_type != 0) + throw UnknownReceptorType(receptor_type, get_name()); + return 0; +} + +inline +port iaf_psc_exp_ps_time_reversal::handles_test_event(CurrentEvent &, port receptor_type) +{ + if (receptor_type != 0) + throw UnknownReceptorType(receptor_type, get_name()); + return 0; +} + +inline +port iaf_psc_exp_ps_time_reversal::handles_test_event(DataLoggingRequest & dlr, + port receptor_type) +{ + if (receptor_type != 0) + throw UnknownReceptorType(receptor_type, get_name()); + return B_.logger_.connect_logging_device(dlr, recordablesMap_); +} + +inline +void iaf_psc_exp_ps_time_reversal::get_status(DictionaryDatum & d) const +{ + P_.get(d); + S_.get(d, P_); + (*d)[names::recordables] = recordablesMap_.get_list(); + +} + +inline +void iaf_psc_exp_ps_time_reversal::set_status(const DictionaryDatum & d) +{ + Parameters_ ptmp = P_; // temporary copy in case of errors + double_t delta_EL = ptmp.set(d); // throws if BadProperty + State_ stmp = S_; // temporary copy in case of errors + stmp.set(d, ptmp, delta_EL); // throws if BadProperty + + // if we get here, temporaries contain consistent set of properties + P_ = ptmp; + S_ = stmp; +} + + +// inline +// void iaf_psc_exp_ps_time_reversal::print_stopwatch() +// { +// regime1.print("branch-1 (no-spike) took: "); +// regime2.print("branch-2 (no-spike) took: "); +// regime3.print("branch-3 (spike) took: "); + +// } + +inline +void nest::iaf_psc_exp_ps_time_reversal::spike_test_(const double_t t1) +{ + // we assume that P_.tau_ex_=P_.tau_in_ + double_t const I_0 = V_.y1_ex_before_ + V_.y1_in_before_; + double_t const V_0 = V_.y2_before_; + double_t const I_t1 = S_.y1_ex_ + S_.y1_in_; + double_t const V_t1 = S_.y2_; + double_t const tau = P_.tau_ex_; + double_t const tau_m = P_.tau_m_; + double_t const I_x = P_.I_e_; + double_t const C_m = P_.c_m_; + double_t const V_th = P_.U_th_; + + double_t const tauC_m = tau_m/C_m; + + double_t const Vdot_t1 = -V_t1/tau_m + (I_t1+I_x)/C_m; + + + // iaflossless tests + if ( Vdot_t1 < 0.0 ) + { + double_t const Vdot_0 = -V_0/tau_m + (I_0+I_x)/C_m; + + if ( Vdot_0 > 0.0 ) + { + if ( Vdot_0*t1 + V_0 >= V_th ) + { + //if ( V_0 + Vdot_0 * (V_0 - V_t1 + Vdot_t1*t1) / (Vdot_t1-Vdot_0) >= V_th ) + //{ + /* //final iaflossles test */ + /* double_t const V_th_bar = V_th - tauC_m*I_x; */ + /* double_t const y = V_th_bar/tauC_m / I_0; */ + + /* if ( V_0 >= tau_m/(tau_m-tau) * (-tau/C_m * I_0 + V_th_bar * pow(y, (-tau/tau_m))) ) */ + /* S_.det_spikes++; */ + + // D'Haene tests + double_t const minus_taus = -tau_m*tau / (tau_m-tau); + double_t const V_syn = minus_taus / C_m * I_0; + double_t const V_m = V_0 - tauC_m*I_x - V_syn; + double_t const quot = -tau*V_m / (tau_m*V_syn); + + double_t const t_max = minus_taus * log(quot); + + double_t const expm1_tau_syn = numerics::expm1(-t_max/tau); + double_t const expm1_tau_m = numerics::expm1(-t_max/tau_m); + + double_t const P20 = -tau_m*expm1_tau_m / C_m; + double_t const P21 = minus_taus / C_m * (expm1_tau_syn-expm1_tau_m); + + if ( (P20*I_x + P21*I_0 + expm1_tau_m*V_0 + V_0) >= V_th ) + S_.dhaene_det_spikes++; + + //} + } + } + } +} + +//time-reversal state space analysis test +//looks for the no-spike region first +//the state space test takes argument dt and +//returns true, spike: if (V(t_{right}) > V_(\theta)); +//returns false: ( (V(t_{right} < V_(\theta) or initial conditions in no-spike region); +//returns true, spike: missed spike excursion, compute t_{max} = dt and find point of +//threshold crossing t_{\theta} using emit_spike_. + +inline +bool iaf_psc_exp_ps_time_reversal::is_spike_(double_t dt) +{ + double_t const I_0 = V_.y1_ex_before_ + V_.y1_in_before_; + double_t const V_0 = V_.y2_before_; + const double_t exp_tau_s = numerics::expm1(dt/P_.tau_ex_) ; //inequalities are adjusted such that backward propagation (negative time) is already accounted for here + const double_t exp_tau_m = numerics::expm1(dt/P_.tau_m_) ; + const double_t exp_tau_m_s = numerics::expm1(dt/P_.tau_m_ - dt/P_.tau_ex_); + + //pre-compute g + double_t g = ((P_.a1_ * I_0 * exp_tau_m_s + exp_tau_m * (P_.a3_ - P_.I_e_ * P_.a2_) + P_.a3_)/P_.a4_) ; + + //no-spike + // intersecting line + if((V_0 <= (((I_0 + P_.I_e_)*(P_.b1_ * exp_tau_m + P_.b2_* exp_tau_s) + P_.b5_*(exp_tau_m - exp_tau_s))/( P_.b7_ * exp_tau_s))) + //continuation line + && (V_0 < g)) + { + return false; + } + + //spike + else if (V_0 >= g ) + { + return true; + } + //no-spike + else if(V_0 < (P_.c1_ * P_.I_e_ + P_.c2_ * I_0 + P_.c3_* pow(I_0, P_.c4_) * pow((P_.c5_ - P_.I_e_), P_.c6_))) + { + return false; + } + else + //spike + { + V_.bisection_step = (P_.a1_ / P_.tau_m_ * P_.tau_ex_ ) * log ( P_.b1_ * I_0 / (P_.a2_ * P_.I_e_ - P_.a1_ * I_0 - P_.a4_ * V_0 ) ); + return true; + } + +} + +} // namespace + +#endif // IAF_PSC_EXP_PS_TIME_REVERSAL_H + + diff --git a/precise/precisemodule.cpp b/precise/precisemodule.cpp index f143b6df03..1942ed6977 100644 --- a/precise/precisemodule.cpp +++ b/precise/precisemodule.cpp @@ -53,6 +53,7 @@ #include "iaf_psc_exp_ps.h" #include "parrot_neuron_ps.h" #include "poisson_generator_ps.h" +#include "iaf_psc_exp_ps_time_reversal.h" namespace nest { @@ -101,6 +102,8 @@ PreciseModule::init( SLIInterpreter* ) "poisson_generator_ps" ); kernel().model_manager.register_node_model< parrot_neuron_ps >( "parrot_neuron_ps" ); + kernel().model_manager.register_node_model< iaf_psc_exp_ps_time_reversal >( + "iaf_psc_exp_ps_time_reversal" ); } // PreciseModule::init() From 891f591fc6000efcabc4aadf97d9aeabceeb7c80 Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Mon, 23 Jan 2017 13:32:25 +0100 Subject: [PATCH 02/48] associated variables for time reversal model included in nestkernel --- nestkernel/nest_names.cpp | 28 ++++++++++++++++++++++++++++ nestkernel/nest_names.h | 26 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/nestkernel/nest_names.cpp b/nestkernel/nest_names.cpp index 556841d334..bbeeb52507 100644 --- a/nestkernel/nest_names.cpp +++ b/nestkernel/nest_names.cpp @@ -366,5 +366,33 @@ const Name xs( "xs" ); const Name z( "z" ); const Name z_connected( "z_connected" ); + +//time-reversal state-space-spike detection algorithm for precise +//neuron models + +const Name y1_ex("y1_ex"); +const Name y1_in("y1_in"); +const Name y0("y0"); + +const Name count_case1("count_case1"); +const Name count_case2("count_case2"); +const Name count_case3("count_case3"); +const Name color("color"); + +const Name count_sstest("count_sstest"); +const Name count_old("count_old"); + +const Name count_regime1("count_regime1"); +const Name count_regime2("count_regime2"); +const Name count_regime3("count_regime3"); +const Name count_regime4("count_regime4"); + +const Name time_branch1("time_branch1"); +const Name time_branch2("time_branch2"); +const Name time_branch3("time_branch3"); +const Name time_branch4("time_branch4"); + +const Name y2("y2"); + } } diff --git a/nestkernel/nest_names.h b/nestkernel/nest_names.h index 50b1d15417..03d30ff983 100644 --- a/nestkernel/nest_names.h +++ b/nestkernel/nest_names.h @@ -439,6 +439,32 @@ extern const Name xs; //!< current scaling factor of the synaptic weight [0...1] extern const Name z; //!< Number of available synaptic elements per node extern const Name z_connected; //!< Number of connected synaptic elements //!< per node + +// time-reversal state-space spike detection algorithm +extern const Name y1_ex; +extern const Name y1_in; +extern const Name y0; + +extern const Name count_case1; +extern const Name count_case2; +extern const Name count_case3; +extern const Name color; + +extern const Name count_regime1; +extern const Name count_regime2; +extern const Name count_regime3; +extern const Name count_regime4; + +extern const Name time_branch1; +extern const Name time_branch2; +extern const Name time_branch3; +extern const Name time_branch4; + +extern const Name count_sstest; +extern const Name count_old; + +extern const Name y2; + } } From d865f90cd83e571473204f0453c8bbcda463ec96 Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Mon, 23 Jan 2017 13:48:41 +0100 Subject: [PATCH 03/48] added description for the time reversal model --- precise/iaf_psc_exp_ps_time_reversal.h | 60 +++++++++----------------- 1 file changed, 20 insertions(+), 40 deletions(-) diff --git a/precise/iaf_psc_exp_ps_time_reversal.h b/precise/iaf_psc_exp_ps_time_reversal.h index afc5c0ae14..f576d19d1b 100644 --- a/precise/iaf_psc_exp_ps_time_reversal.h +++ b/precise/iaf_psc_exp_ps_time_reversal.h @@ -40,30 +40,22 @@ /*BeginDocumentation Name: iaf_psc_exp_ps_time_reversal - Leaky integrate-and-fire neuron -with exponential postsynaptic currents; canoncial implementation; -bisectioning method for approximation of threshold crossing. +with exponential postsynaptic currents; precise implementation; +applies state space analysis to predict exact number of spikes in a +simulation for any given resolution. Description: -iaf_psc_exp_ps_time_reversal is the "canonical" implementation of the leaky +iaf_psc_exp_ps_time_reversal is the precise state space implementation of the leaky integrate-and-fire model neuron with exponential postsynaptic currents -that uses the bisectioning method to approximate the timing of a threshold -crossing [1,2]. This is the most exact implementation available. - -The canonical implementation handles neuronal dynamics in a locally -event-based manner with in coarse time grid defined by the minimum -delay in the network, see [1,2]. Incoming spikes are applied at the -precise moment of their arrival, while the precise time of outgoing -spikes is determined by bisectioning once a threshold crossing has -been detected. Return from refractoriness occurs precisely at spike -time plus refractory period. - -This implementation is more complex than the plain iaf_psc_exp -neuron, but achieves much higher precision. In particular, it does not -suffer any binning of spike times to grid points. Depending on your -application, the canonical application with bisectioning may provide -superior overall performance given an accuracy goal; see [1,2] for -details. Subthreshold dynamics are integrated using exact integration -between events [3]. +that uses time reversal to detect spikes [1]. This is the most exact +implementation available. + +Time-reversed state space analysis provides a general method to solve the +threshold-detection problem for an integrable, affine or linear time +evolution. This method is based on the idea of propagating the threshold +backwards in time, and see whether it meets the initial state, rather +than propagating the initial state forward in time and see whether it +meets the threshold. Parameters: The following parameters can be set in the status dictionary. @@ -77,31 +69,19 @@ between events [3]. I_e double - Constant input current in pA. V_min double - Absolute lower value for the membrane potential. V_reset double - Reset value for the membrane potential. - -Remarks: - Please note that this node is capable of sending precise spike times - to target nodes (on-grid spike time plus offset). If this node is - connected to a spike_detector, the property "precise_times" of the - spike_detector has to be set to true in order to record the offsets - in addition to the on-grid spike times. - + References: - [1] Morrison A, Straube S, Plesser HE & Diesmann M (2007) Exact subthreshold - integration with continuous spike times in discrete time neural network - simulations. Neural Comput 19, 47–79 - [2] Hanuschkin A, Kunkel S, Helias M, Morrison A and Diesmann M (2010) A - general and efficient method for incorporating precise spike times in - globally timedriven simulations. Front Neuroinform 4:113 - [3] Rotter S & Diesmann M (1999) Exact simulation of time-invariant linear - systems with applications to neuronal modeling. Biol Cybern 81:381-402 - -Author: Kunkel + [1] J.Krishnan, P.G.L.Porta Mana, M.Helias, M.Diesmann, E.Di.Napoli +(2017) Perfect spike detection via time reversal (to be submitted to Front. +Neuroinform.) + +Author: Krishnan Sends: SpikeEvent Receives: SpikeEvent, CurrentEvent, DataLoggingRequest -SeeAlso: iaf_psc_exp, iaf_psc_alpha_canon +SeeAlso: iaf_psc_exp_ps */ namespace nest From 564a5b8f3a27c3e15af10f456b49a8df30b24dfd Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Mon, 30 Jan 2017 12:31:37 +0100 Subject: [PATCH 04/48] modify function description --- precise/iaf_psc_exp_ps_time_reversal.h | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/precise/iaf_psc_exp_ps_time_reversal.h b/precise/iaf_psc_exp_ps_time_reversal.h index f576d19d1b..1e2eead3ae 100644 --- a/precise/iaf_psc_exp_ps_time_reversal.h +++ b/precise/iaf_psc_exp_ps_time_reversal.h @@ -509,14 +509,6 @@ void nest::iaf_psc_exp_ps_time_reversal::spike_test_(const double_t t1) { if ( Vdot_0*t1 + V_0 >= V_th ) { - //if ( V_0 + Vdot_0 * (V_0 - V_t1 + Vdot_t1*t1) / (Vdot_t1-Vdot_0) >= V_th ) - //{ - /* //final iaflossles test */ - /* double_t const V_th_bar = V_th - tauC_m*I_x; */ - /* double_t const y = V_th_bar/tauC_m / I_0; */ - - /* if ( V_0 >= tau_m/(tau_m-tau) * (-tau/C_m * I_0 + V_th_bar * pow(y, (-tau/tau_m))) ) */ - /* S_.det_spikes++; */ // D'Haene tests double_t const minus_taus = -tau_m*tau / (tau_m-tau); @@ -541,20 +533,22 @@ void nest::iaf_psc_exp_ps_time_reversal::spike_test_(const double_t t1) } } -//time-reversal state space analysis test -//looks for the no-spike region first -//the state space test takes argument dt and -//returns true, spike: if (V(t_{right}) > V_(\theta)); -//returns false: ( (V(t_{right} < V_(\theta) or initial conditions in no-spike region); -//returns true, spike: missed spike excursion, compute t_{max} = dt and find point of -//threshold crossing t_{\theta} using emit_spike_. + +/* Conventional spike detection algorithms propagate the initial state forwards in time and see whether it meets the threshold. +This function implements a general method to solve the threshold-detection problem for an integrable, affine or linear +time evolution by applying geometric analysis. The idea is to propagate the threshold backwards in time and see whether +it meets the initial state. In state space spanned by voltage and current, this clearly separates the spiking region and +non-spiking region. is_spike_ takes argument dt which corresponds to the time window at which this spike prediction occurs. +returns true, spike: if (V(t_{right}) > V_(\theta)); returns false: (V(t_{right} < V_(\theta) or initial conditions in no-spike region; +returns true, spike: missed spike excursion, compute t_{max} = dt and find point of threshold crossing t_{\theta} using emit_spike_. +inequalities are adjusted such that backward propagation (negative time) is already accounted for here */ inline bool iaf_psc_exp_ps_time_reversal::is_spike_(double_t dt) { double_t const I_0 = V_.y1_ex_before_ + V_.y1_in_before_; double_t const V_0 = V_.y2_before_; - const double_t exp_tau_s = numerics::expm1(dt/P_.tau_ex_) ; //inequalities are adjusted such that backward propagation (negative time) is already accounted for here + const double_t exp_tau_s = numerics::expm1(dt/P_.tau_ex_) ; const double_t exp_tau_m = numerics::expm1(dt/P_.tau_m_) ; const double_t exp_tau_m_s = numerics::expm1(dt/P_.tau_m_ - dt/P_.tau_ex_); From d1f19dcae7d676aa6f2a02fea92fefa6c6fd7f4e Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Mon, 30 Jan 2017 12:55:11 +0100 Subject: [PATCH 05/48] removed temp vars --- nestkernel/nest_names.cpp | 20 +------------------- nestkernel/nest_names.h | 20 ++------------------ 2 files changed, 3 insertions(+), 37 deletions(-) diff --git a/nestkernel/nest_names.cpp b/nestkernel/nest_names.cpp index bbeeb52507..9619a3693a 100644 --- a/nestkernel/nest_names.cpp +++ b/nestkernel/nest_names.cpp @@ -373,26 +373,8 @@ const Name z_connected( "z_connected" ); const Name y1_ex("y1_ex"); const Name y1_in("y1_in"); const Name y0("y0"); - -const Name count_case1("count_case1"); -const Name count_case2("count_case2"); -const Name count_case3("count_case3"); -const Name color("color"); - -const Name count_sstest("count_sstest"); -const Name count_old("count_old"); - -const Name count_regime1("count_regime1"); -const Name count_regime2("count_regime2"); -const Name count_regime3("count_regime3"); -const Name count_regime4("count_regime4"); - -const Name time_branch1("time_branch1"); -const Name time_branch2("time_branch2"); -const Name time_branch3("time_branch3"); -const Name time_branch4("time_branch4"); - const Name y2("y2"); + } } diff --git a/nestkernel/nest_names.h b/nestkernel/nest_names.h index 03d30ff983..ce7b9e9722 100644 --- a/nestkernel/nest_names.h +++ b/nestkernel/nest_names.h @@ -440,30 +440,14 @@ extern const Name z; //!< Number of available synaptic elements per node extern const Name z_connected; //!< Number of connected synaptic elements //!< per node -// time-reversal state-space spike detection algorithm +//time-reversal state-space spike detection algorithm extern const Name y1_ex; extern const Name y1_in; extern const Name y0; +extern const Name y2; -extern const Name count_case1; -extern const Name count_case2; -extern const Name count_case3; -extern const Name color; - -extern const Name count_regime1; -extern const Name count_regime2; -extern const Name count_regime3; -extern const Name count_regime4; - -extern const Name time_branch1; -extern const Name time_branch2; -extern const Name time_branch3; -extern const Name time_branch4; -extern const Name count_sstest; -extern const Name count_old; -extern const Name y2; } } From 17b73e1ac4615d0cd76b68b10a6aa95659f0cc93 Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Mon, 30 Jan 2017 13:25:23 +0100 Subject: [PATCH 06/48] more comments --- precise/iaf_psc_exp_ps_time_reversal.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/precise/iaf_psc_exp_ps_time_reversal.h b/precise/iaf_psc_exp_ps_time_reversal.h index 1e2eead3ae..4ed5dd9840 100644 --- a/precise/iaf_psc_exp_ps_time_reversal.h +++ b/precise/iaf_psc_exp_ps_time_reversal.h @@ -552,10 +552,9 @@ bool iaf_psc_exp_ps_time_reversal::is_spike_(double_t dt) const double_t exp_tau_m = numerics::expm1(dt/P_.tau_m_) ; const double_t exp_tau_m_s = numerics::expm1(dt/P_.tau_m_ - dt/P_.tau_ex_); - //pre-compute g double_t g = ((P_.a1_ * I_0 * exp_tau_m_s + exp_tau_m * (P_.a3_ - P_.I_e_ * P_.a2_) + P_.a3_)/P_.a4_) ; - //no-spike + //no-spike, NS_1 // intersecting line if((V_0 <= (((I_0 + P_.I_e_)*(P_.b1_ * exp_tau_m + P_.b2_* exp_tau_s) + P_.b5_*(exp_tau_m - exp_tau_s))/( P_.b7_ * exp_tau_s))) //continuation line @@ -564,18 +563,18 @@ bool iaf_psc_exp_ps_time_reversal::is_spike_(double_t dt) return false; } - //spike + //spike, S_1 else if (V_0 >= g ) { return true; } - //no-spike + //no-spike, NS_2 else if(V_0 < (P_.c1_ * P_.I_e_ + P_.c2_ * I_0 + P_.c3_* pow(I_0, P_.c4_) * pow((P_.c5_ - P_.I_e_), P_.c6_))) { return false; } else - //spike + //spike, S_2 { V_.bisection_step = (P_.a1_ / P_.tau_m_ * P_.tau_ex_ ) * log ( P_.b1_ * I_0 / (P_.a2_ * P_.I_e_ - P_.a1_ * I_0 - P_.a4_ * V_0 ) ); return true; From 49e6ff4f51716ff2d3716c13a957217451144615 Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Thu, 24 Aug 2017 11:43:33 +0200 Subject: [PATCH 07/48] addressing hep's comments --- nestkernel/nest_names.cpp | 8 - nestkernel/nest_names.h | 9 - precise/iaf_psc_exp_ps_time_reversal.cpp | 248 ++++++----------------- precise/iaf_psc_exp_ps_time_reversal.h | 161 ++++----------- 4 files changed, 105 insertions(+), 321 deletions(-) diff --git a/nestkernel/nest_names.cpp b/nestkernel/nest_names.cpp index 9619a3693a..9f22c70746 100644 --- a/nestkernel/nest_names.cpp +++ b/nestkernel/nest_names.cpp @@ -367,14 +367,6 @@ const Name xs( "xs" ); const Name z( "z" ); const Name z_connected( "z_connected" ); -//time-reversal state-space-spike detection algorithm for precise -//neuron models - -const Name y1_ex("y1_ex"); -const Name y1_in("y1_in"); -const Name y0("y0"); -const Name y2("y2"); - } } diff --git a/nestkernel/nest_names.h b/nestkernel/nest_names.h index ce7b9e9722..12059d0bc0 100644 --- a/nestkernel/nest_names.h +++ b/nestkernel/nest_names.h @@ -440,15 +440,6 @@ extern const Name z; //!< Number of available synaptic elements per node extern const Name z_connected; //!< Number of connected synaptic elements //!< per node -//time-reversal state-space spike detection algorithm -extern const Name y1_ex; -extern const Name y1_in; -extern const Name y0; -extern const Name y2; - - - - } } diff --git a/precise/iaf_psc_exp_ps_time_reversal.cpp b/precise/iaf_psc_exp_ps_time_reversal.cpp index 386b5e2e57..00ce9c7697 100644 --- a/precise/iaf_psc_exp_ps_time_reversal.cpp +++ b/precise/iaf_psc_exp_ps_time_reversal.cpp @@ -47,10 +47,8 @@ namespace nest // use standard names whereever you can for consistency! insert_(names::V_m, & iaf_psc_exp_ps_time_reversal::get_V_m_); insert_(names::I_syn, & iaf_psc_exp_ps_time_reversal::get_I_syn_); - insert_(names::y1_ex, & iaf_psc_exp_ps_time_reversal::get_y1_ex_); - insert_(names::y1_in, &iaf_psc_exp_ps_time_reversal::get_y1_in_); - insert_(names::y0, &iaf_psc_exp_ps_time_reversal::get_y0_); - + insert_(names::I_syn_ex, & iaf_psc_exp_ps_time_reversal::get_I_syn_ex_); + insert_(names::I_syn_in, &iaf_psc_exp_ps_time_reversal::get_I_syn_in_); } } @@ -75,8 +73,8 @@ nest::iaf_psc_exp_ps_time_reversal::Parameters_::Parameters_() nest::iaf_psc_exp_ps_time_reversal::State_::State_() : y0_(0.0), - y1_ex_(0.0), - y1_in_(0.0), + I_syn_ex_(0.0), + I_syn_in_(0.0), y2_(0.0), is_refractory_(false), last_spike_step_(-1), @@ -112,35 +110,34 @@ nest::iaf_psc_exp_ps_time_reversal::Buffers_::Buffers_(const Buffers_ &, iaf_psc void nest::iaf_psc_exp_ps_time_reversal::Parameters_::calc_const_spike_test_() { - //line corresponding to the final timestep i.e t_right: continuation + // line corresponding to the final timestep i.e t_right: continuation // of the curved boundary: a + I*b - a1_ =tau_m_ * tau_ex_; - a2_ =tau_m_ * (tau_m_ - tau_ex_); - a3_ =c_m_ * U_th_ * (tau_m_ - tau_ex_); - a4_ =c_m_ * (tau_m_ - tau_ex_); - - //line joining endpoints of the envelope: \alpha I + \beta - b1_ =-tau_m_ * tau_m_; - b2_ =tau_m_ * tau_ex_; - b3_ =tau_m_*(tau_m_ - tau_ex_)- tau_m_*tau_m_ + tau_m_* tau_ex_; - b4_ =-tau_m_*tau_m_; //b1 - b5_ =tau_m_*c_m_*U_th_; - b6_ =tau_m_*(tau_m_- tau_ex_); - b7_ =-c_m_*(tau_m_ - tau_ex_); - - //envelope or curved boundary - c1_ =tau_m_/c_m_; - c2_ =(-tau_m_ * tau_ex_)/(c_m_*(tau_m_ - tau_ex_)); - c3_ =(tau_m_ * tau_m_)/ (c_m_ * (tau_m_ - tau_ex_)); - c4_ =tau_ex_/tau_m_; - c5_ =(c_m_ * U_th_)/tau_m_; - c6_ =1-(tau_ex_/tau_m_); - - //parallel line - - d1_ = tau_m_ * c_m_ ; - d2_ = tau_m_ * tau_ex_; - d3_ = c_m_ * (tau_m_ - tau_ex_); + a1_ =tau_m_ * tau_ex_; + a2_ =tau_m_ * (tau_m_ - tau_ex_); + a3_ =c_m_ * U_th_ * (tau_m_ - tau_ex_); + a4_ =c_m_ * (tau_m_ - tau_ex_); + + // line joining endpoints of the envelope: \alpha I + \beta + b1_ =-tau_m_ * tau_m_; + b2_ =tau_m_ * tau_ex_; + b3_ =tau_m_*(tau_m_ - tau_ex_)- tau_m_*tau_m_ + tau_m_* tau_ex_; + b4_ =-tau_m_*tau_m_; //b1 + b5_ =tau_m_*c_m_*U_th_; + b6_ =tau_m_*(tau_m_- tau_ex_); + b7_ =-c_m_*(tau_m_ - tau_ex_); + + // envelope or curved boundary + c1_ =tau_m_/c_m_; + c2_ =(-tau_m_ * tau_ex_)/(c_m_*(tau_m_ - tau_ex_)); + c3_ =(tau_m_ * tau_m_)/ (c_m_ * (tau_m_ - tau_ex_)); + c4_ =tau_ex_/tau_m_; + c5_ =(c_m_ * U_th_)/tau_m_; + c6_ =1-(tau_ex_/tau_m_); + + // parallel line + d1_ = tau_m_ * c_m_ ; + d2_ = tau_m_ * tau_ex_; + d3_ = c_m_ * (tau_m_ - tau_ex_); } void nest::iaf_psc_exp_ps_time_reversal::Parameters_::get(DictionaryDatum & d) const @@ -155,12 +152,10 @@ void nest::iaf_psc_exp_ps_time_reversal::Parameters_::get(DictionaryDatum & d) c def(d, names::tau_syn_ex, tau_ex_); def(d, names::tau_syn_in, tau_in_); def(d, names::t_ref, t_ref_); - } double nest::iaf_psc_exp_ps_time_reversal::Parameters_::set(const DictionaryDatum & d) { - updateValue(d, names::tau_m, tau_m_); updateValue(d, names::tau_syn_ex, tau_ex_); updateValue(d, names::tau_syn_in, tau_in_); @@ -168,26 +163,37 @@ double nest::iaf_psc_exp_ps_time_reversal::Parameters_::set(const DictionaryDatu updateValue(d, names::t_ref, t_ref_); updateValue(d, names::I_e, I_e_); - // if U0_ is changed, we need to adjust all variables defined relative to U0_ const double ELold = E_L_; updateValue(d, names::E_L, E_L_); const double delta_EL = E_L_ - ELold; if(updateValue(d, names::V_reset, U_reset_)) + { U_reset_ -= E_L_; + } else + { U_reset_ -= delta_EL; + } if (updateValue(d, names::V_th, U_th_)) + { U_th_ -= E_L_; + } else + { U_th_ -= delta_EL; + } if (updateValue(d, names::V_min, U_min_)) + { U_min_ -= E_L_; + } else + { U_min_ -= delta_EL; + } if ( U_reset_ >= U_th_ ) throw BadProperty("Reset potential must be smaller than threshold."); @@ -220,30 +226,9 @@ void nest::iaf_psc_exp_ps_time_reversal::State_::get(DictionaryDatum & d, def(d, names::is_refractory, is_refractory_); def(d, names::t_spike, Time(Time::step(last_spike_step_)).get_ms()); def(d, names::offset, last_spike_offset_); - def(d, names::y1_ex, y1_ex_); - def(d, names::y1_in, y1_in_); // y1 state - def(d, names::y2, y2_); // y2 state - def(d, names::I_syn, y1_ex_ + y1_in_); - - - // these entries will break ticket-459 - // because they chane depending on E_L (which is correct) - // since they are only used for debugging we will comment them - // out for the time being - //def(d, names::pot_spikes, c0); - //def(d, names::dhaene_quick1, (dhaene_quick1*100.0/c0)); - //def(d, names::dhaene_quick2, (dhaene_quick2*100.0/c0)); - //def(d, names::dhaene_tmax_lt_t1, (dhaene_tmax_lt_t1*100.0/c0)); - //def(d, names::dhaene_max_geq_V_th, (dhaene_max*100.0/c0)); - //def(d, names::dhaene_det_spikes, (dhaene_det_spikes*100.0/c0)); - - //def(d, names::eq7, (c1a*100.0/c0)); - //def(d, names::eq9, (c1b*100.0/c0)); - //def(d, names::eqs7and9, (c2*100.0/c0)); - //def(d, names::lin_left_geq_V_th, (c3a*100.0/c0)); - //def(d, names::lin_max_geq_V_th, (c3b*100.0/c0)); - //def(d, names::eq13, (c4*100.0/c0)); - //def(d, names::eq12, (det_spikes*100.0/c0)); + def(d, names::I_syn_ex, I_syn_ex_); + def(d, names::I_syn_in, I_syn_in_); // y1 state + def(d, names::I_syn, I_syn_ex_ + I_syn_in_); } void nest::iaf_psc_exp_ps_time_reversal::State_::set(const DictionaryDatum & d, const Parameters_ & p, double delta_EL) @@ -253,24 +238,8 @@ void nest::iaf_psc_exp_ps_time_reversal::State_::set(const DictionaryDatum & d, else y2_ -= delta_EL; - //double_t dv; - //updateValue(d, names::dhaene_quick1, dv); - //updateValue(d, names::dhaene_quick2, dv); - //updateValue(d, names::dhaene_tmax_lt_t1, dv); - //updateValue(d, names::dhaene_max_geq_V_th, dv); - //updateValue(d, names::dhaene_det_spikes, dv); - //updateValue(d, names::eq7, dv); - //updateValue(d, names::eq9, dv); - //updateValue(d, names::eqs7and9, dv); - //updateValue(d, names::lin_left_geq_V_th, dv); - //updateValue(d, names::lin_max_geq_V_th, dv); - //updateValue(d, names::eq13, dv); - //updateValue(d, names::eq12, dv); - updateValue(d, names::y1_ex, y1_ex_ ); - updateValue(d, names::y1_in, y1_in_); - updateValue(d, names::y0, y0_); - - + updateValue(d, names::I_syn_ex, I_syn_ex_ ); + updateValue(d, names::I_syn_in, I_syn_in_); } /* ---------------------------------------------------------------- @@ -295,7 +264,7 @@ nest::iaf_psc_exp_ps_time_reversal::iaf_psc_exp_ps_time_reversal(const iaf_psc_e /* ---------------------------------------------------------------- * Node initialization functions - * ---------------------------------------------------------------- + * ---------------------------------------------------------------- */ void nest::iaf_psc_exp_ps_time_reversal::init_node_(const Node & proto) { @@ -303,7 +272,7 @@ void nest::iaf_psc_exp_ps_time_reversal::init_node_(const Node & proto) P_ = pr.P_; S_ = pr.S_; -}*/ +} void nest::iaf_psc_exp_ps_time_reversal::init_state_(const Node & proto) { @@ -374,8 +343,8 @@ void nest::iaf_psc_exp_ps_time_reversal::update(const Time & origin, // save state at beginning of interval for spike-time approximation V_.y0_before_ = S_.y0_; - V_.y1_ex_before_ = S_.y1_ex_; - V_.y1_in_before_ = S_.y1_in_; + V_.I_syn_ex_before_ = S_.I_syn_ex_; + V_.I_syn_in_before_ = S_.I_syn_in_; V_.y2_before_ = S_.y2_; // get first event @@ -392,15 +361,15 @@ void nest::iaf_psc_exp_ps_time_reversal::update(const Time & origin, // update membrane potential if ( !S_.is_refractory_ ) { - S_.y2_ = V_.P20_*(P_.I_e_+S_.y0_) + V_.P21_ex_*S_.y1_ex_ + V_.P21_in_*S_.y1_in_ + V_.expm1_tau_m_*S_.y2_ + S_.y2_; + S_.y2_ = V_.P20_*(P_.I_e_+S_.y0_) + V_.P21_ex_*S_.I_syn_ex_ + V_.P21_in_*S_.I_syn_in_ + V_.expm1_tau_m_*S_.y2_ + S_.y2_; // lower bound of membrane potential S_.y2_ = ( S_.y2_ < P_.U_min_ ? P_.U_min_ : S_.y2_ ); } // update synaptic currents - S_.y1_ex_ = S_.y1_ex_*V_.expm1_tau_ex_ + S_.y1_ex_; - S_.y1_in_ = S_.y1_in_*V_.expm1_tau_in_ + S_.y1_in_; + S_.I_syn_ex_ = S_.I_syn_ex_*V_.expm1_tau_ex_ + S_.I_syn_ex_; + S_.I_syn_in_ = S_.I_syn_in_*V_.expm1_tau_in_ + S_.I_syn_in_; /* The following must not be moved before the y1_, y2_ update, since the spike-time interpolation within emit_spike_ depends @@ -455,14 +424,14 @@ void nest::iaf_psc_exp_ps_time_reversal::update(const Time & origin, else { if ( ev_weight >= 0.0 ) - S_.y1_ex_ += ev_weight; // exc. spike input + S_.I_syn_ex_ += ev_weight; // exc. spike input else - S_.y1_in_ += ev_weight; // inh. spike input + S_.I_syn_in_ += ev_weight; // inh. spike input } // store state - V_.y1_ex_before_ = S_.y1_ex_; - V_.y1_in_before_ = S_.y1_in_; + V_.I_syn_ex_before_ = S_.I_syn_ex_; + V_.I_syn_in_before_ = S_.I_syn_in_; V_.y2_before_ = S_.y2_; last_offset = ev_offset; } @@ -548,10 +517,10 @@ void nest::iaf_psc_exp_ps_time_reversal::propagate_(const double_t dt) const double_t P21_ex = -P_.tau_m_*P_.tau_ex_ / (P_.tau_m_-P_.tau_ex_) / P_.c_m_ * (expm1_tau_ex-expm1_tau_m); const double_t P21_in = -P_.tau_m_*P_.tau_in_ / (P_.tau_m_-P_.tau_in_) / P_.c_m_ * (expm1_tau_in-expm1_tau_m); - S_.y2_ = P20*(P_.I_e_+S_.y0_) + P21_ex*S_.y1_ex_ + P21_in*S_.y1_in_ + expm1_tau_m*S_.y2_ + S_.y2_; + S_.y2_ = P20*(P_.I_e_+S_.y0_) + P21_ex*S_.I_syn_ex_ + P21_in*S_.I_syn_in_ + expm1_tau_m*S_.y2_ + S_.y2_; } - S_.y1_ex_ = S_.y1_ex_*expm1_tau_ex + S_.y1_ex_; - S_.y1_in_ = S_.y1_in_*expm1_tau_in + S_.y1_in_; + S_.I_syn_ex_ = S_.I_syn_ex_*expm1_tau_ex + S_.I_syn_ex_; + S_.I_syn_in_ = S_.I_syn_in_*expm1_tau_in + S_.I_syn_in_; } void nest::iaf_psc_exp_ps_time_reversal::emit_spike_(const Time & origin, const long lag, const double_t t0, const double_t dt) @@ -620,101 +589,10 @@ inline double nest::iaf_psc_exp_ps_time_reversal::bisectioning_(const double dt) const double_t P21_ex = -P_.tau_m_*P_.tau_ex_ / (P_.tau_m_-P_.tau_ex_) / P_.c_m_ * (expm1_tau_ex-expm1_tau_m); const double_t P21_in = -P_.tau_m_*P_.tau_in_ / (P_.tau_m_-P_.tau_in_) / P_.c_m_ * (expm1_tau_in-expm1_tau_m); - y2_root = P20*(P_.I_e_+V_.y0_before_) + P21_ex*V_.y1_ex_before_ + P21_in*V_.y1_in_before_ + expm1_tau_m*V_.y2_before_ + V_.y2_before_; + y2_root = P20*(P_.I_e_+V_.y0_before_) + P21_ex*V_.I_syn_ex_before_ + P21_in*V_.I_syn_in_before_ + expm1_tau_m*V_.y2_before_ + V_.y2_before_; } return root; } -void nest::iaf_psc_exp_ps_time_reversal::spike_test_count_(const double_t t1) -{ - S_.c0++; // V(t1) < V_th - - // we assume that P_.tau_ex_=P_.tau_in_ - double_t const I_0 = V_.y1_ex_before_ + V_.y1_in_before_; - double_t const V_0 = V_.y2_before_; - double_t const I_t1 = S_.y1_ex_ + S_.y1_in_; - double_t const V_t1 = S_.y2_; - double_t const tau = P_.tau_ex_; - double_t const tau_m = P_.tau_m_; - double_t const I_x = P_.I_e_; - double_t const C_m = P_.c_m_; - double_t const V_th = P_.U_th_; - - double_t const tauC_m = tau_m/C_m; - - double_t const Vdot_0 = -V_0/tau_m + (I_0+I_x)/C_m; - double_t const Vdot_t1 = -V_t1/tau_m + (I_t1+I_x)/C_m; - - // iaflossless tests - if ( Vdot_t1 < 0.0 ) - S_.c1b++; - if ( Vdot_0 > 0.0 ) - { - S_.c1a++; - if ( Vdot_t1 < 0.0 ) - { - S_.c2++; - - if ( Vdot_0*t1 + V_0 >= V_th ) - S_.c3a++; - if ( V_0 + Vdot_0 * (V_0 - V_t1 + Vdot_t1*t1) / (Vdot_t1-Vdot_0) >= V_th ) - S_.c3b++; - double_t const expm1_tau_syn = numerics::expm1(t1/tau); // positive exponent! - double_t const expm1_tau_m = numerics::expm1(t1/tau_m); // positive exponent! - - double_t const V_0_bar = V_0 - tauC_m*I_x; - //double_t const V_t1_bar = V_t1 - tauC_m*I_x; - double_t const V_th_bar = V_th - tauC_m*I_x; - double_t const V_right_bar = (tau_m*expm1_tau_m - tau*expm1_tau_syn) * V_th_bar / (tau_m-tau); - double_t const I_left = V_th_bar/tauC_m; - double_t const m = (V_right_bar - V_th_bar) / (expm1_tau_syn*I_left); // V_left_bar = V_th_bar - - if ( V_0_bar >= m * (I_0 - I_left) + V_th ) - { - S_.c4++; - - double_t const y = V_th_bar/tauC_m / I_0; - - if ( V_0 >= tau_m/(tau_m-tau) * (-tau/C_m * I_0 + V_th_bar * pow(y, (-tau/tau_m))) ) - S_.det_spikes++; - } - } - } - - // D'Haene tests - double_t const minus_taus = -tau_m*tau / (tau_m-tau); - double_t const V_syn = minus_taus / C_m * I_0; - double_t const V_m = V_0 - tauC_m*I_x - V_syn; - - if ( V_m > 0.0 && V_syn < 0.0 ) - { - S_.dhaene_quick1++; - - double_t const quot = -tau*V_m / (tau_m*V_syn); - - if ( quot <= 1.0 ) - { - S_.dhaene_quick2++; - - double_t const t_max = minus_taus * log(quot); - - if ( t_max < t1 ) - S_.dhaene_tmax_lt_t1++; - - double_t const expm1_tau_syn = numerics::expm1(-t_max/tau); - double_t const expm1_tau_m = numerics::expm1(-t_max/tau_m); - - double_t const P20 = -tau_m*expm1_tau_m / C_m; - double_t const P21 = minus_taus / C_m * (expm1_tau_syn-expm1_tau_m); - - if ( (P20*I_x + P21*I_0 + expm1_tau_m*V_0 + V_0) >= V_th ) - { - S_.dhaene_max++; - if ( t_max <= t1 ) - S_.dhaene_det_spikes++; - } - } - } -} diff --git a/precise/iaf_psc_exp_ps_time_reversal.h b/precise/iaf_psc_exp_ps_time_reversal.h index 4ed5dd9840..ad1f33ac99 100644 --- a/precise/iaf_psc_exp_ps_time_reversal.h +++ b/precise/iaf_psc_exp_ps_time_reversal.h @@ -39,7 +39,7 @@ #include /*BeginDocumentation -Name: iaf_psc_exp_ps_time_reversal - Leaky integrate-and-fire neuron +Name: iaf_psc_exp_ps_lossless - Leaky integrate-and-fire neuron with exponential postsynaptic currents; precise implementation; applies state space analysis to predict exact number of spikes in a simulation for any given resolution. @@ -75,7 +75,7 @@ meets the threshold. (2017) Perfect spike detection via time reversal (to be submitted to Front. Neuroinform.) -Author: Krishnan +Author: Jeyashree Krishnan Sends: SpikeEvent @@ -86,7 +86,6 @@ SeeAlso: iaf_psc_exp_ps namespace nest { - /** * Leaky iaf neuron, exponential PSC synapses, canonical implementation. * @note Inherit privately from Node, so no classes can be derived @@ -220,8 +219,6 @@ namespace nest */ double_t bisectioning_(const double_t dt) const; - void spike_test_count_(const double_t); - void spike_test_(const double_t); bool is_spike_(const double_t); // ---------------------------------------------------------------- @@ -232,64 +229,59 @@ namespace nest struct Parameters_ { /** Membrane time constant in ms. */ - double_t tau_m_; + double tau_m_; /** Time constant of exc. synaptic current in ms. */ - double_t tau_ex_; + double tau_ex_; /** Time constant of inh. synaptic current in ms. */ - double_t tau_in_; + double tau_in_; /** Membrane capacitance in pF. */ - double_t c_m_; + double c_m_; /** Refractory period in ms. */ - double_t t_ref_; + double t_ref_; /** Resting potential in mV. */ - double_t E_L_; + double E_L_; /** External DC current [pA] */ - double_t I_e_; + double I_e_; /** Threshold, RELATIVE TO RESTING POTENTAIL(!). I.e. the real threshold is U_th_ + E_L_. */ - double_t U_th_; + double U_th_; /** Lower bound, RELATIVE TO RESTING POTENTAIL(!). I.e. the real lower bound is U_min_+E_L_. */ - double_t U_min_; + double U_min_; /** Reset potential. At threshold crossing, the membrane potential is reset to this value. Relative to resting potential. */ - double_t U_reset_; - - double_t a1_; - double_t a2_; - double_t a3_; - double_t a4_; - - double_t b1_; - double_t b2_; - double_t b3_; - double_t b4_; - double_t b5_; - double_t b6_; - double_t b7_; - - double_t c1_; - double_t c2_; - double_t c3_; - double_t c4_; - double_t c5_; - double_t c6_; - - - double_t d1_; - double_t d2_; - double_t d3_; - + double U_reset_; + + double a1_; + double a2_; + double a3_; + double a4_; + double b1_; + double b2_; + double b3_; + double b4_; + double b5_; + double b6_; + double b7_; + double c1_; + double c2_; + double c3_; + double c4_; + double c5_; + double c6_; + double d1_; + double d2_; + double d3_; Parameters_(); //!< Sets default parameter values void calc_const_spike_test_(); @@ -307,8 +299,8 @@ namespace nest struct State_ { double_t y0_; //!< External input current - double_t y1_ex_; //!< Exc. exponetial current - double_t y1_in_; //!< Inh. exponetial current + double_t I_syn_ex_; //!< Exc. exponetial current + double_t I_syn_in_; //!< Inh. exponetial current double_t y2_; //!< Membrane potential (relative to resting potential) bool is_refractory_; //!< True while refractory @@ -335,11 +327,6 @@ namespace nest void get(DictionaryDatum &, const Parameters_ &) const; void set(const DictionaryDatum &, const Parameters_ &, double delta_EL); - - - - - }; // ---------------------------------------------------------------- @@ -379,20 +366,19 @@ namespace nest double_t P21_in_; //!< Progagator matrix element, 2nd row double_t P21_ex_; //!< Progagator matrix element, 2nd row double_t y0_before_; //!< y0_ at beginning of ministep - double_t y1_ex_before_; //!< y1_ at beginning of ministep - double_t y1_in_before_; //!< y1_ at beginning of ministep + double_t I_syn_ex_before_; //!< y1_ at beginning of ministep + double_t I_syn_in_before_; //!< y1_ at beginning of ministep double_t y2_before_; //!< y2_ at beginning of ministep double_t bisection_step; - }; + }; // Access functions for UniversalDataLogger ------------------------------- //! Read out the real membrane potential double_t get_V_m_() const { return S_.y2_ + P_.E_L_; } - double_t get_I_syn_() const { return S_.y1_ex_ + S_.y1_in_; } - double_t get_y1_ex_() const { return S_.y1_ex_; } - double_t get_y1_in_() const { return S_.y1_in_; } - double_t get_y0_() const {return S_.y0_;} + double_t get_I_syn_() const { return S_.I_syn_ex_ + S_.I_syn_in_; } + double_t get_I_syn_ex_() const { return S_.I_syn_ex_; } + double_t get_I_syn_in_() const { return S_.I_syn_in_; } // ---------------------------------------------------------------- @@ -471,69 +457,6 @@ void iaf_psc_exp_ps_time_reversal::set_status(const DictionaryDatum & d) S_ = stmp; } - -// inline -// void iaf_psc_exp_ps_time_reversal::print_stopwatch() -// { -// regime1.print("branch-1 (no-spike) took: "); -// regime2.print("branch-2 (no-spike) took: "); -// regime3.print("branch-3 (spike) took: "); - -// } - -inline -void nest::iaf_psc_exp_ps_time_reversal::spike_test_(const double_t t1) -{ - // we assume that P_.tau_ex_=P_.tau_in_ - double_t const I_0 = V_.y1_ex_before_ + V_.y1_in_before_; - double_t const V_0 = V_.y2_before_; - double_t const I_t1 = S_.y1_ex_ + S_.y1_in_; - double_t const V_t1 = S_.y2_; - double_t const tau = P_.tau_ex_; - double_t const tau_m = P_.tau_m_; - double_t const I_x = P_.I_e_; - double_t const C_m = P_.c_m_; - double_t const V_th = P_.U_th_; - - double_t const tauC_m = tau_m/C_m; - - double_t const Vdot_t1 = -V_t1/tau_m + (I_t1+I_x)/C_m; - - - // iaflossless tests - if ( Vdot_t1 < 0.0 ) - { - double_t const Vdot_0 = -V_0/tau_m + (I_0+I_x)/C_m; - - if ( Vdot_0 > 0.0 ) - { - if ( Vdot_0*t1 + V_0 >= V_th ) - { - - // D'Haene tests - double_t const minus_taus = -tau_m*tau / (tau_m-tau); - double_t const V_syn = minus_taus / C_m * I_0; - double_t const V_m = V_0 - tauC_m*I_x - V_syn; - double_t const quot = -tau*V_m / (tau_m*V_syn); - - double_t const t_max = minus_taus * log(quot); - - double_t const expm1_tau_syn = numerics::expm1(-t_max/tau); - double_t const expm1_tau_m = numerics::expm1(-t_max/tau_m); - - double_t const P20 = -tau_m*expm1_tau_m / C_m; - double_t const P21 = minus_taus / C_m * (expm1_tau_syn-expm1_tau_m); - - if ( (P20*I_x + P21*I_0 + expm1_tau_m*V_0 + V_0) >= V_th ) - S_.dhaene_det_spikes++; - - //} - } - } - } -} - - /* Conventional spike detection algorithms propagate the initial state forwards in time and see whether it meets the threshold. This function implements a general method to solve the threshold-detection problem for an integrable, affine or linear time evolution by applying geometric analysis. The idea is to propagate the threshold backwards in time and see whether @@ -546,7 +469,7 @@ inequalities are adjusted such that backward propagation (negative time) is alre inline bool iaf_psc_exp_ps_time_reversal::is_spike_(double_t dt) { - double_t const I_0 = V_.y1_ex_before_ + V_.y1_in_before_; + double_t const I_0 = V_.I_syn_ex_before_ + V_.I_syn_in_before_; double_t const V_0 = V_.y2_before_; const double_t exp_tau_s = numerics::expm1(dt/P_.tau_ex_) ; const double_t exp_tau_m = numerics::expm1(dt/P_.tau_m_) ; From 4194ad79a9f240e8e83231d5e79d27083bb58b66 Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Thu, 24 Aug 2017 12:45:12 +0200 Subject: [PATCH 08/48] addressing hep's comments --- ...versal.cpp => iaf_psc_exp_ps_lossless.cpp} | 68 +++++++++---------- ...e_reversal.h => iaf_psc_exp_ps_lossless.h} | 44 ++++++------ precise/precisemodule.cpp | 6 +- 3 files changed, 59 insertions(+), 59 deletions(-) rename precise/{iaf_psc_exp_ps_time_reversal.cpp => iaf_psc_exp_ps_lossless.cpp} (86%) rename precise/{iaf_psc_exp_ps_time_reversal.h => iaf_psc_exp_ps_lossless.h} (91%) diff --git a/precise/iaf_psc_exp_ps_time_reversal.cpp b/precise/iaf_psc_exp_ps_lossless.cpp similarity index 86% rename from precise/iaf_psc_exp_ps_time_reversal.cpp rename to precise/iaf_psc_exp_ps_lossless.cpp index 00ce9c7697..78fcb12b74 100644 --- a/precise/iaf_psc_exp_ps_time_reversal.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -1,5 +1,5 @@ /* - * iaf_psc_exp_ps_time_reversal.cpp + * iaf_psc_exp_ps_lossless.cpp * * This file is part of NEST. * @@ -19,7 +19,7 @@ * along with NEST. If not, see . * */ -#include "iaf_psc_exp_ps_time_reversal.h" +#include "iaf_psc_exp_ps_lossless.h" #include "exceptions.h" #include "dict.h" @@ -35,20 +35,20 @@ * Recordables map * ---------------------------------------------------------------- */ -nest::RecordablesMap nest::iaf_psc_exp_ps_time_reversal::recordablesMap_; +nest::RecordablesMap nest::iaf_psc_exp_ps_lossless::recordablesMap_; namespace nest { // Override the create() method with one call to RecordablesMap::insert_() // for each quantity to be recorded. template <> - void RecordablesMap::create() + void RecordablesMap::create() { // use standard names whereever you can for consistency! - insert_(names::V_m, & iaf_psc_exp_ps_time_reversal::get_V_m_); - insert_(names::I_syn, & iaf_psc_exp_ps_time_reversal::get_I_syn_); - insert_(names::I_syn_ex, & iaf_psc_exp_ps_time_reversal::get_I_syn_ex_); - insert_(names::I_syn_in, &iaf_psc_exp_ps_time_reversal::get_I_syn_in_); + insert_(names::V_m, & iaf_psc_exp_ps_lossless::get_V_m_); + insert_(names::I_syn, & iaf_psc_exp_ps_lossless::get_I_syn_); + insert_(names::I_syn_ex, & iaf_psc_exp_ps_lossless::get_I_syn_ex_); + insert_(names::I_syn_in, &iaf_psc_exp_ps_lossless::get_I_syn_in_); } } @@ -56,7 +56,7 @@ namespace nest * Default constructors defining default parameters and state * ---------------------------------------------------------------- */ -nest::iaf_psc_exp_ps_time_reversal::Parameters_::Parameters_() +nest::iaf_psc_exp_ps_lossless::Parameters_::Parameters_() : tau_m_ ( 10.0 ), // ms tau_ex_ ( 2.0 ), // ms tau_in_ ( 2.0 ), // ms @@ -71,7 +71,7 @@ nest::iaf_psc_exp_ps_time_reversal::Parameters_::Parameters_() calc_const_spike_test_(); } -nest::iaf_psc_exp_ps_time_reversal::State_::State_() +nest::iaf_psc_exp_ps_lossless::State_::State_() : y0_(0.0), I_syn_ex_(0.0), I_syn_in_(0.0), @@ -93,11 +93,11 @@ nest::iaf_psc_exp_ps_time_reversal::State_::State_() det_spikes(0) {} -nest::iaf_psc_exp_ps_time_reversal::Buffers_::Buffers_(iaf_psc_exp_ps_time_reversal & n) +nest::iaf_psc_exp_ps_lossless::Buffers_::Buffers_(iaf_psc_exp_ps_lossless & n) : logger_(n) {} -nest::iaf_psc_exp_ps_time_reversal::Buffers_::Buffers_(const Buffers_ &, iaf_psc_exp_ps_time_reversal & n) +nest::iaf_psc_exp_ps_lossless::Buffers_::Buffers_(const Buffers_ &, iaf_psc_exp_ps_lossless & n) : logger_(n) {} @@ -108,7 +108,7 @@ nest::iaf_psc_exp_ps_time_reversal::Buffers_::Buffers_(const Buffers_ &, iaf_psc //constants for time-reversal state space spike-detection algorithm -void nest::iaf_psc_exp_ps_time_reversal::Parameters_::calc_const_spike_test_() +void nest::iaf_psc_exp_ps_lossless::Parameters_::calc_const_spike_test_() { // line corresponding to the final timestep i.e t_right: continuation // of the curved boundary: a + I*b @@ -140,7 +140,7 @@ void nest::iaf_psc_exp_ps_time_reversal::Parameters_::calc_const_spike_test_() d3_ = c_m_ * (tau_m_ - tau_ex_); } -void nest::iaf_psc_exp_ps_time_reversal::Parameters_::get(DictionaryDatum & d) const +void nest::iaf_psc_exp_ps_lossless::Parameters_::get(DictionaryDatum & d) const { def(d, names::E_L, E_L_); def(d, names::I_e, I_e_); @@ -154,7 +154,7 @@ void nest::iaf_psc_exp_ps_time_reversal::Parameters_::get(DictionaryDatum & d) c def(d, names::t_ref, t_ref_); } -double nest::iaf_psc_exp_ps_time_reversal::Parameters_::set(const DictionaryDatum & d) +double nest::iaf_psc_exp_ps_lossless::Parameters_::set(const DictionaryDatum & d) { updateValue(d, names::tau_m, tau_m_); updateValue(d, names::tau_syn_ex, tau_ex_); @@ -219,7 +219,7 @@ double nest::iaf_psc_exp_ps_time_reversal::Parameters_::set(const DictionaryDatu return delta_EL; } -void nest::iaf_psc_exp_ps_time_reversal::State_::get(DictionaryDatum & d, +void nest::iaf_psc_exp_ps_lossless::State_::get(DictionaryDatum & d, const Parameters_ & p) const { def(d, names::V_m, y2_ + p.E_L_); // Membrane potential @@ -231,7 +231,7 @@ void nest::iaf_psc_exp_ps_time_reversal::State_::get(DictionaryDatum & d, def(d, names::I_syn, I_syn_ex_ + I_syn_in_); } -void nest::iaf_psc_exp_ps_time_reversal::State_::set(const DictionaryDatum & d, const Parameters_ & p, double delta_EL) +void nest::iaf_psc_exp_ps_lossless::State_::set(const DictionaryDatum & d, const Parameters_ & p, double delta_EL) { if ( updateValue(d, names::V_m, y2_) ) y2_ -= p.E_L_; @@ -246,7 +246,7 @@ void nest::iaf_psc_exp_ps_time_reversal::State_::set(const DictionaryDatum & d, * Default and copy constructor for node * ---------------------------------------------------------------- */ -nest::iaf_psc_exp_ps_time_reversal::iaf_psc_exp_ps_time_reversal() +nest::iaf_psc_exp_ps_lossless::iaf_psc_exp_ps_lossless() : Node(), P_(), S_(), @@ -255,7 +255,7 @@ nest::iaf_psc_exp_ps_time_reversal::iaf_psc_exp_ps_time_reversal() recordablesMap_.create(); } -nest::iaf_psc_exp_ps_time_reversal::iaf_psc_exp_ps_time_reversal(const iaf_psc_exp_ps_time_reversal & n) +nest::iaf_psc_exp_ps_lossless::iaf_psc_exp_ps_lossless(const iaf_psc_exp_ps_lossless & n) : Node(n), P_(n.P_), S_(n.S_), @@ -266,22 +266,22 @@ nest::iaf_psc_exp_ps_time_reversal::iaf_psc_exp_ps_time_reversal(const iaf_psc_e * Node initialization functions * ---------------------------------------------------------------- */ -void nest::iaf_psc_exp_ps_time_reversal::init_node_(const Node & proto) +void nest::iaf_psc_exp_ps_lossless::init_node_(const Node & proto) { - const iaf_psc_exp_ps_time_reversal & pr = downcast(proto); + const iaf_psc_exp_ps_lossless & pr = downcast(proto); P_ = pr.P_; S_ = pr.S_; } -void nest::iaf_psc_exp_ps_time_reversal::init_state_(const Node & proto) +void nest::iaf_psc_exp_ps_lossless::init_state_(const Node & proto) { - const iaf_psc_exp_ps_time_reversal & pr = downcast(proto); + const iaf_psc_exp_ps_lossless & pr = downcast(proto); S_ = pr.S_; } -void nest::iaf_psc_exp_ps_time_reversal::init_buffers_() +void nest::iaf_psc_exp_ps_lossless::init_buffers_() { B_.events_.resize(); B_.events_.clear(); @@ -289,7 +289,7 @@ void nest::iaf_psc_exp_ps_time_reversal::init_buffers_() B_.logger_.reset(); } -void nest::iaf_psc_exp_ps_time_reversal::calibrate() +void nest::iaf_psc_exp_ps_lossless::calibrate() { B_.logger_.init(); // ensures initialization in case mm connected after Simulate @@ -310,7 +310,7 @@ void nest::iaf_psc_exp_ps_time_reversal::calibrate() * Update and spike handling functions * ---------------------------------------------------------------- */ -void nest::iaf_psc_exp_ps_time_reversal::update(const Time & origin, +void nest::iaf_psc_exp_ps_lossless::update(const Time & origin, const long from, const long to) { assert ( to >= 0 ); @@ -466,7 +466,7 @@ void nest::iaf_psc_exp_ps_time_reversal::update(const Time & origin, } // function handles exact spike times -void nest::iaf_psc_exp_ps_time_reversal::handle(SpikeEvent & e) +void nest::iaf_psc_exp_ps_lossless::handle(SpikeEvent & e) { assert( e.get_delay() > 0 ); @@ -480,7 +480,7 @@ void nest::iaf_psc_exp_ps_time_reversal::handle(SpikeEvent & e) Tdeliver, e.get_offset(), e.get_weight() * e.get_multiplicity()); } -void nest::iaf_psc_exp_ps_time_reversal::handle(CurrentEvent & e) +void nest::iaf_psc_exp_ps_lossless::handle(CurrentEvent & e) { assert( e.get_delay() > 0 ); @@ -491,7 +491,7 @@ void nest::iaf_psc_exp_ps_time_reversal::handle(CurrentEvent & e) B_.currents_.add_value(e.get_rel_delivery_steps(nest::kernel().simulation_manager.get_slice_origin() ), w * c); } -void nest::iaf_psc_exp_ps_time_reversal::handle(DataLoggingRequest &e) +void nest::iaf_psc_exp_ps_lossless::handle(DataLoggingRequest &e) { B_.logger_.handle(e); } @@ -499,12 +499,12 @@ void nest::iaf_psc_exp_ps_time_reversal::handle(DataLoggingRequest &e) // auxiliary functions --------------------------------------------- inline -void nest::iaf_psc_exp_ps_time_reversal::set_spiketime(const Time & now) +void nest::iaf_psc_exp_ps_lossless::set_spiketime(const Time & now) { S_.last_spike_step_ = now.get_steps(); } -void nest::iaf_psc_exp_ps_time_reversal::propagate_(const double_t dt) +void nest::iaf_psc_exp_ps_lossless::propagate_(const double_t dt) { const double_t expm1_tau_ex = numerics::expm1(-dt/P_.tau_ex_); const double_t expm1_tau_in = numerics::expm1(-dt/P_.tau_in_); @@ -523,7 +523,7 @@ void nest::iaf_psc_exp_ps_time_reversal::propagate_(const double_t dt) S_.I_syn_in_ = S_.I_syn_in_*expm1_tau_in + S_.I_syn_in_; } -void nest::iaf_psc_exp_ps_time_reversal::emit_spike_(const Time & origin, const long lag, const double_t t0, const double_t dt) +void nest::iaf_psc_exp_ps_lossless::emit_spike_(const Time & origin, const long lag, const double_t t0, const double_t dt) { // we know that the potential is subthreshold at t0, super at t0+dt @@ -544,7 +544,7 @@ void nest::iaf_psc_exp_ps_time_reversal::emit_spike_(const Time & origin, const kernel().event_delivery_manager.send(*this, se, lag); } -void nest::iaf_psc_exp_ps_time_reversal::emit_instant_spike_(const Time & origin, const long lag, +void nest::iaf_psc_exp_ps_lossless::emit_instant_spike_(const Time & origin, const long lag, const double_t spike_offs) { assert( S_.y2_ >= P_.U_th_ ); // ensure we are superthreshold @@ -564,7 +564,7 @@ void nest::iaf_psc_exp_ps_time_reversal::emit_instant_spike_(const Time & origin kernel().event_delivery_manager.send(*this, se, lag); } -inline double nest::iaf_psc_exp_ps_time_reversal::bisectioning_(const double dt) const +inline double nest::iaf_psc_exp_ps_lossless::bisectioning_(const double dt) const { double_t root = 0.0; diff --git a/precise/iaf_psc_exp_ps_time_reversal.h b/precise/iaf_psc_exp_ps_lossless.h similarity index 91% rename from precise/iaf_psc_exp_ps_time_reversal.h rename to precise/iaf_psc_exp_ps_lossless.h index ad1f33ac99..3f6df32f2e 100644 --- a/precise/iaf_psc_exp_ps_time_reversal.h +++ b/precise/iaf_psc_exp_ps_lossless.h @@ -1,5 +1,5 @@ /* - * iaf_psc_exp_ps_time_reversal.h + * iaf_psc_exp_ps_lossless.h * * This file is part of NEST. * @@ -21,8 +21,8 @@ */ -#ifndef IAF_PSC_EXP_PS_TIME_REVERSAL_H -#define IAF_PSC_EXP_PS_TIME_REVERSAL_H +#ifndef IAF_PSC_EXP_PS_LOSSLESS_H +#define IAF_PSC_EXP_PS_LOSSLESS_H #include "config.h" @@ -45,7 +45,7 @@ applies state space analysis to predict exact number of spikes in a simulation for any given resolution. Description: -iaf_psc_exp_ps_time_reversal is the precise state space implementation of the leaky +iaf_psc_exp_ps_lossless is the precise state space implementation of the leaky integrate-and-fire model neuron with exponential postsynaptic currents that uses time reversal to detect spikes [1]. This is the most exact implementation available. @@ -92,7 +92,7 @@ namespace nest * from this one. * @todo Implement current input in consistent way. */ - class iaf_psc_exp_ps_time_reversal : public Node + class iaf_psc_exp_ps_lossless : public Node { class Network; @@ -103,7 +103,7 @@ namespace nest This constructor should only be used by GenericModel to create model prototype instances. */ - iaf_psc_exp_ps_time_reversal(); + iaf_psc_exp_ps_lossless(); /** Copy constructor. GenericModel::allocate_() uses the copy constructor to clone @@ -112,7 +112,7 @@ namespace nest @note The copy constructor MUST NOT be used to create nodes based on nodes that have been placed in the network. */ - iaf_psc_exp_ps_time_reversal(const iaf_psc_exp_ps_time_reversal &); + iaf_psc_exp_ps_lossless(const iaf_psc_exp_ps_lossless &); /** * Import sets of overloaded virtual functions. @@ -176,8 +176,8 @@ namespace nest //@} // The next two classes need to be friends to access the State_ class/member - friend class RecordablesMap; - friend class UniversalDataLogger; + friend class RecordablesMap; + friend class UniversalDataLogger; void set_spiketime(Time const &); @@ -336,8 +336,8 @@ namespace nest */ struct Buffers_ { - Buffers_(iaf_psc_exp_ps_time_reversal &); - Buffers_(const Buffers_ &, iaf_psc_exp_ps_time_reversal &); + Buffers_(iaf_psc_exp_ps_lossless &); + Buffers_(const Buffers_ &, iaf_psc_exp_ps_lossless &); /** * Queue for incoming events. @@ -347,7 +347,7 @@ namespace nest RingBuffer currents_; //! Logger for all analog data - UniversalDataLogger logger_; + UniversalDataLogger logger_; }; // ---------------------------------------------------------------- @@ -384,7 +384,7 @@ namespace nest // ---------------------------------------------------------------- /** - * @defgroup iaf_psc_exp_ps_time_reversal_data + * @defgroup iaf_psc_exp_ps_lossless_data * Instances of private data structures for the different types * of data pertaining to the model. * @note The order of definitions is important for speed. @@ -397,11 +397,11 @@ namespace nest /** @} */ //! Mapping of recordables names to access functions - static RecordablesMap recordablesMap_; + static RecordablesMap recordablesMap_; }; inline -port iaf_psc_exp_ps_time_reversal::send_test_event( Node& target, rport receptor_type, synindex, bool ) +port iaf_psc_exp_ps_lossless::send_test_event( Node& target, rport receptor_type, synindex, bool ) { SpikeEvent e; @@ -411,7 +411,7 @@ port iaf_psc_exp_ps_time_reversal::send_test_event( Node& target, rport receptor } inline -port iaf_psc_exp_ps_time_reversal::handles_test_event(SpikeEvent &, port receptor_type) +port iaf_psc_exp_ps_lossless::handles_test_event(SpikeEvent &, port receptor_type) { if (receptor_type != 0) throw UnknownReceptorType(receptor_type, get_name()); @@ -419,7 +419,7 @@ port iaf_psc_exp_ps_time_reversal::handles_test_event(SpikeEvent &, port recepto } inline -port iaf_psc_exp_ps_time_reversal::handles_test_event(CurrentEvent &, port receptor_type) +port iaf_psc_exp_ps_lossless::handles_test_event(CurrentEvent &, port receptor_type) { if (receptor_type != 0) throw UnknownReceptorType(receptor_type, get_name()); @@ -427,7 +427,7 @@ port iaf_psc_exp_ps_time_reversal::handles_test_event(CurrentEvent &, port recep } inline -port iaf_psc_exp_ps_time_reversal::handles_test_event(DataLoggingRequest & dlr, +port iaf_psc_exp_ps_lossless::handles_test_event(DataLoggingRequest & dlr, port receptor_type) { if (receptor_type != 0) @@ -436,7 +436,7 @@ port iaf_psc_exp_ps_time_reversal::handles_test_event(DataLoggingRequest & dlr, } inline -void iaf_psc_exp_ps_time_reversal::get_status(DictionaryDatum & d) const +void iaf_psc_exp_ps_lossless::get_status(DictionaryDatum & d) const { P_.get(d); S_.get(d, P_); @@ -445,7 +445,7 @@ void iaf_psc_exp_ps_time_reversal::get_status(DictionaryDatum & d) const } inline -void iaf_psc_exp_ps_time_reversal::set_status(const DictionaryDatum & d) +void iaf_psc_exp_ps_lossless::set_status(const DictionaryDatum & d) { Parameters_ ptmp = P_; // temporary copy in case of errors double_t delta_EL = ptmp.set(d); // throws if BadProperty @@ -467,7 +467,7 @@ returns true, spike: missed spike excursion, compute t_{max} = dt and find point inequalities are adjusted such that backward propagation (negative time) is already accounted for here */ inline -bool iaf_psc_exp_ps_time_reversal::is_spike_(double_t dt) +bool iaf_psc_exp_ps_lossless::is_spike_(double_t dt) { double_t const I_0 = V_.I_syn_ex_before_ + V_.I_syn_in_before_; double_t const V_0 = V_.y2_before_; @@ -507,6 +507,6 @@ bool iaf_psc_exp_ps_time_reversal::is_spike_(double_t dt) } // namespace -#endif // IAF_PSC_EXP_PS_TIME_REVERSAL_H +#endif // IAF_PSC_EXP_PS_LOSSLESS_H diff --git a/precise/precisemodule.cpp b/precise/precisemodule.cpp index 1942ed6977..a65c93792e 100644 --- a/precise/precisemodule.cpp +++ b/precise/precisemodule.cpp @@ -53,7 +53,7 @@ #include "iaf_psc_exp_ps.h" #include "parrot_neuron_ps.h" #include "poisson_generator_ps.h" -#include "iaf_psc_exp_ps_time_reversal.h" +#include "iaf_psc_exp_ps_lossless.h" namespace nest { @@ -102,8 +102,8 @@ PreciseModule::init( SLIInterpreter* ) "poisson_generator_ps" ); kernel().model_manager.register_node_model< parrot_neuron_ps >( "parrot_neuron_ps" ); - kernel().model_manager.register_node_model< iaf_psc_exp_ps_time_reversal >( - "iaf_psc_exp_ps_time_reversal" ); + kernel().model_manager.register_node_model< iaf_psc_exp_ps_lossless >( + "iaf_psc_exp_ps_lossless" ); } // PreciseModule::init() From 22ca54d5622c9092fc57ae517a0b22eeee23c054 Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Thu, 24 Aug 2017 13:09:03 +0200 Subject: [PATCH 09/48] addressing hep's comments --- precise/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/precise/CMakeLists.txt b/precise/CMakeLists.txt index 4d5b781dee..f8ba49892e 100644 --- a/precise/CMakeLists.txt +++ b/precise/CMakeLists.txt @@ -25,7 +25,7 @@ set( precise_sources iaf_psc_exp_ps.cpp iaf_psc_exp_ps.h poisson_generator_ps.cpp poisson_generator_ps.h parrot_neuron_ps.cpp parrot_neuron_ps.h - iaf_psc_exp_ps_time_reversal.cpp iaf_psc_exp_ps_time_reversal.h + iaf_psc_exp_ps_lossless.cpp iaf_psc_exp_ps_lossless.h precisemodule.cpp precisemodule.h ) From 22b781570add6d7a49f506f5f8b42fa90a192078 Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Thu, 24 Aug 2017 13:12:59 +0200 Subject: [PATCH 10/48] addressing hep's comments --- precise/iaf_psc_exp_ps_lossless.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.h b/precise/iaf_psc_exp_ps_lossless.h index 3f6df32f2e..c6e78dcbd7 100644 --- a/precise/iaf_psc_exp_ps_lossless.h +++ b/precise/iaf_psc_exp_ps_lossless.h @@ -38,11 +38,10 @@ #include -/*BeginDocumentation +/*Begin Documentation Name: iaf_psc_exp_ps_lossless - Leaky integrate-and-fire neuron with exponential postsynaptic currents; precise implementation; -applies state space analysis to predict exact number of spikes in a -simulation for any given resolution. +predicts exact number of spikes by applying state space analysis Description: iaf_psc_exp_ps_lossless is the precise state space implementation of the leaky From 969272ebe721994c9525658ddc7a8306aabf561f Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Thu, 24 Aug 2017 13:16:08 +0200 Subject: [PATCH 11/48] addressing hep's comments --- precise/iaf_psc_exp_ps_lossless.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.h b/precise/iaf_psc_exp_ps_lossless.h index c6e78dcbd7..1af2319388 100644 --- a/precise/iaf_psc_exp_ps_lossless.h +++ b/precise/iaf_psc_exp_ps_lossless.h @@ -70,9 +70,9 @@ meets the threshold. V_reset double - Reset value for the membrane potential. References: - [1] J.Krishnan, P.G.L.Porta Mana, M.Helias, M.Diesmann, E.Di.Napoli -(2017) Perfect spike detection via time reversal (to be submitted to Front. -Neuroinform.) +[1] J.Krishnan, P.G.L.Porta Mana, M.Helias, M.Diesmann, E.Di.Napoli +(2017) Perfect spike detection via time reversal, arXiv:1706.05702, +submitted to Front. Neuroinformatics. Author: Jeyashree Krishnan From 2423fa0408bd844e436bd4a69744d167934e9760 Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Thu, 24 Aug 2017 13:33:20 +0200 Subject: [PATCH 12/48] addressing hep's comments --- precise/iaf_psc_exp_ps_lossless.cpp | 54 +++++++++++----------- precise/iaf_psc_exp_ps_lossless.h | 70 ++++++++++++++--------------- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index 78fcb12b74..66aeac2ad5 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -65,7 +65,7 @@ nest::iaf_psc_exp_ps_lossless::Parameters_::Parameters_() E_L_ ( 0.0 ), // mV I_e_ ( 0.0 ), // pA U_th_ ( -55.0-E_L_), // mV, rel to E_L_ - U_min_ (-std::numeric_limits::infinity()), // mV + U_min_ (-std::numeric_limits::infinity()), // mV U_reset_( -70.0-E_L_) // mV, rel to E_L_ { calc_const_spike_test_(); @@ -329,7 +329,7 @@ void nest::iaf_psc_exp_ps_lossless::update(const Time & origin, if ( S_.y2_ >= P_.U_th_ ) emit_instant_spike_(origin, from, - V_.h_ms_*(1.0-std::numeric_limits::epsilon())); + V_.h_ms_*(1.0-std::numeric_limits::epsilon())); for ( long lag = from; lag < to; ++lag ) { @@ -348,8 +348,8 @@ void nest::iaf_psc_exp_ps_lossless::update(const Time & origin, V_.y2_before_ = S_.y2_; // get first event - double_t ev_offset; - double_t ev_weight; + double ev_offset; + double ev_weight; bool end_of_refract; if ( !B_.events_.get_next_spike(T, ev_offset, ev_weight, end_of_refract) ) @@ -395,12 +395,12 @@ void nest::iaf_psc_exp_ps_lossless::update(const Time & origin, // 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 + double 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; + const double ministep = last_offset - ev_offset; propagate_(ministep); @@ -484,8 +484,8 @@ void nest::iaf_psc_exp_ps_lossless::handle(CurrentEvent & e) { assert( e.get_delay() > 0 ); - const double_t c = e.get_current(); - const double_t w = e.get_weight(); + const double c = e.get_current(); + const double w = e.get_weight(); // add weighted current; HEP 2002-10-04 B_.currents_.add_value(e.get_rel_delivery_steps(nest::kernel().simulation_manager.get_slice_origin() ), w * c); @@ -504,18 +504,18 @@ void nest::iaf_psc_exp_ps_lossless::set_spiketime(const Time & now) S_.last_spike_step_ = now.get_steps(); } -void nest::iaf_psc_exp_ps_lossless::propagate_(const double_t dt) +void nest::iaf_psc_exp_ps_lossless::propagate_(const double dt) { - const double_t expm1_tau_ex = numerics::expm1(-dt/P_.tau_ex_); - const double_t expm1_tau_in = numerics::expm1(-dt/P_.tau_in_); + const double expm1_tau_ex = numerics::expm1(-dt/P_.tau_ex_); + const double expm1_tau_in = numerics::expm1(-dt/P_.tau_in_); if ( !S_.is_refractory_ ) { - const double_t expm1_tau_m = numerics::expm1(-dt/P_.tau_m_); + const double expm1_tau_m = numerics::expm1(-dt/P_.tau_m_); - const double_t P20 = -P_.tau_m_ / P_.c_m_ * expm1_tau_m; - const double_t P21_ex = -P_.tau_m_*P_.tau_ex_ / (P_.tau_m_-P_.tau_ex_) / P_.c_m_ * (expm1_tau_ex-expm1_tau_m); - const double_t P21_in = -P_.tau_m_*P_.tau_in_ / (P_.tau_m_-P_.tau_in_) / P_.c_m_ * (expm1_tau_in-expm1_tau_m); + const double P20 = -P_.tau_m_ / P_.c_m_ * expm1_tau_m; + const double P21_ex = -P_.tau_m_*P_.tau_ex_ / (P_.tau_m_-P_.tau_ex_) / P_.c_m_ * (expm1_tau_ex-expm1_tau_m); + const double P21_in = -P_.tau_m_*P_.tau_in_ / (P_.tau_m_-P_.tau_in_) / P_.c_m_ * (expm1_tau_in-expm1_tau_m); S_.y2_ = P20*(P_.I_e_+S_.y0_) + P21_ex*S_.I_syn_ex_ + P21_in*S_.I_syn_in_ + expm1_tau_m*S_.y2_ + S_.y2_; } @@ -523,12 +523,12 @@ void nest::iaf_psc_exp_ps_lossless::propagate_(const double_t dt) S_.I_syn_in_ = S_.I_syn_in_*expm1_tau_in + S_.I_syn_in_; } -void nest::iaf_psc_exp_ps_lossless::emit_spike_(const Time & origin, const long lag, const double_t t0, const double_t dt) +void nest::iaf_psc_exp_ps_lossless::emit_spike_(const Time & origin, const long lag, const double t0, const double dt) { // we know that the potential is subthreshold at t0, super at t0+dt // compute spike time relative to beginning of step - const double_t spike_offset = V_.h_ms_ - (t0 + bisectioning_(dt)); + const double spike_offset = V_.h_ms_ - (t0 + bisectioning_(dt)); set_spiketime(Time::step(origin.get_steps() + lag + 1)); S_.last_spike_offset_ = spike_offset; @@ -545,7 +545,7 @@ void nest::iaf_psc_exp_ps_lossless::emit_spike_(const Time & origin, const long } void nest::iaf_psc_exp_ps_lossless::emit_instant_spike_(const Time & origin, const long lag, - const double_t spike_offs) + const double spike_offs) { assert( S_.y2_ >= P_.U_th_ ); // ensure we are superthreshold @@ -567,11 +567,11 @@ void nest::iaf_psc_exp_ps_lossless::emit_instant_spike_(const Time & origin, con inline double nest::iaf_psc_exp_ps_lossless::bisectioning_(const double dt) const { - double_t root = 0.0; + double root = 0.0; - double_t y2_root = V_.y2_before_; + double y2_root = V_.y2_before_; - double_t div = 2.0; + double div = 2.0; while ( fabs(P_.U_th_-y2_root) > 1e-14 and (dt/div > 0.0) ) { if ( y2_root > P_.U_th_ ) @@ -581,13 +581,13 @@ inline double nest::iaf_psc_exp_ps_lossless::bisectioning_(const double dt) cons div *= 2.0; - const double_t expm1_tau_ex = numerics::expm1(-root/P_.tau_ex_); - const double_t expm1_tau_in = numerics::expm1(-root/P_.tau_in_); - const double_t expm1_tau_m = numerics::expm1(-root/P_.tau_m_); + const double expm1_tau_ex = numerics::expm1(-root/P_.tau_ex_); + const double expm1_tau_in = numerics::expm1(-root/P_.tau_in_); + const double expm1_tau_m = numerics::expm1(-root/P_.tau_m_); - const double_t P20 = -P_.tau_m_ / P_.c_m_ * expm1_tau_m; - const double_t P21_ex = -P_.tau_m_*P_.tau_ex_ / (P_.tau_m_-P_.tau_ex_) / P_.c_m_ * (expm1_tau_ex-expm1_tau_m); - const double_t P21_in = -P_.tau_m_*P_.tau_in_ / (P_.tau_m_-P_.tau_in_) / P_.c_m_ * (expm1_tau_in-expm1_tau_m); + const double P20 = -P_.tau_m_ / P_.c_m_ * expm1_tau_m; + const double P21_ex = -P_.tau_m_*P_.tau_ex_ / (P_.tau_m_-P_.tau_ex_) / P_.c_m_ * (expm1_tau_ex-expm1_tau_m); + const double P21_in = -P_.tau_m_*P_.tau_in_ / (P_.tau_m_-P_.tau_in_) / P_.c_m_ * (expm1_tau_in-expm1_tau_m); y2_root = P20*(P_.I_e_+V_.y0_before_) + P21_ex*V_.I_syn_ex_before_ + P21_in*V_.I_syn_in_before_ + expm1_tau_m*V_.y2_before_ + V_.y2_before_; } diff --git a/precise/iaf_psc_exp_ps_lossless.h b/precise/iaf_psc_exp_ps_lossless.h index 1af2319388..f15de50d07 100644 --- a/precise/iaf_psc_exp_ps_lossless.h +++ b/precise/iaf_psc_exp_ps_lossless.h @@ -185,7 +185,7 @@ namespace nest * Propagate the neuron's state by dt. * @param dt Interval over which to propagate */ - void propagate_(const double_t dt); + void propagate_(const double dt); /** * Emit a single spike caused by DC current in absence of spike input. @@ -199,7 +199,7 @@ namespace nest * @param dt Duration of mini-timestep */ void emit_spike_(const Time & origin, const long lag, - const double_t t0, const double_t dt); + const double t0, const double dt); /** * Emit a single spike at a precisely given time. @@ -209,16 +209,16 @@ namespace nest * @param spike_offset Time offset for spike */ void emit_instant_spike_(const Time & origin, const long lag, - const double_t spike_offset); + const double spike_offset); /** * Localize threshold crossing by bisectioning. - * @param double_t length of interval since previous event + * @param double length of interval since previous event * @returns time from previous event to threshold crossing */ - double_t bisectioning_(const double_t dt) const; + double bisectioning_(const double dt) const; - bool is_spike_(const double_t); + bool is_spike_(const double); // ---------------------------------------------------------------- @@ -297,14 +297,14 @@ namespace nest */ struct State_ { - double_t y0_; //!< External input current - double_t I_syn_ex_; //!< Exc. exponetial current - double_t I_syn_in_; //!< Inh. exponetial current - double_t y2_; //!< Membrane potential (relative to resting potential) + double y0_; //!< External input current + double I_syn_ex_; //!< Exc. exponetial current + double I_syn_in_; //!< Inh. exponetial current + double y2_; //!< Membrane potential (relative to resting potential) bool is_refractory_; //!< True while refractory long last_spike_step_; //!< Time stamp of most recent spike - double_t last_spike_offset_; //!< Offset of most recent spike + double last_spike_offset_; //!< Offset of most recent spike long dhaene_quick1; long dhaene_quick2; @@ -356,28 +356,28 @@ namespace nest */ struct Variables_ { - double_t h_ms_; //!< Time resolution [ms] + double h_ms_; //!< Time resolution [ms] long refractory_steps_; //!< Refractory time in steps - double_t expm1_tau_m_; //!< exp(-h/tau_m) - 1 - double_t expm1_tau_ex_; //!< exp(-h/tau_ex) - 1 - double_t expm1_tau_in_; //!< exp(-h/tau_in) - 1 - double_t P20_; //!< Progagator matrix element, 2nd row - double_t P21_in_; //!< Progagator matrix element, 2nd row - double_t P21_ex_; //!< Progagator matrix element, 2nd row - double_t y0_before_; //!< y0_ at beginning of ministep - double_t I_syn_ex_before_; //!< y1_ at beginning of ministep - double_t I_syn_in_before_; //!< y1_ at beginning of ministep - double_t y2_before_; //!< y2_ at beginning of ministep - double_t bisection_step; + double expm1_tau_m_; //!< exp(-h/tau_m) - 1 + double expm1_tau_ex_; //!< exp(-h/tau_ex) - 1 + double expm1_tau_in_; //!< exp(-h/tau_in) - 1 + double P20_; //!< Progagator matrix element, 2nd row + double P21_in_; //!< Progagator matrix element, 2nd row + double P21_ex_; //!< Progagator matrix element, 2nd row + double y0_before_; //!< y0_ at beginning of ministep + double I_syn_ex_before_; //!< y1_ at beginning of ministep + double I_syn_in_before_; //!< y1_ at beginning of ministep + double y2_before_; //!< y2_ at beginning of ministep + double bisection_step; }; // Access functions for UniversalDataLogger ------------------------------- //! Read out the real membrane potential - double_t get_V_m_() const { return S_.y2_ + P_.E_L_; } - double_t get_I_syn_() const { return S_.I_syn_ex_ + S_.I_syn_in_; } - double_t get_I_syn_ex_() const { return S_.I_syn_ex_; } - double_t get_I_syn_in_() const { return S_.I_syn_in_; } + double get_V_m_() const { return S_.y2_ + P_.E_L_; } + double get_I_syn_() const { return S_.I_syn_ex_ + S_.I_syn_in_; } + double get_I_syn_ex_() const { return S_.I_syn_ex_; } + double get_I_syn_in_() const { return S_.I_syn_in_; } // ---------------------------------------------------------------- @@ -447,7 +447,7 @@ inline void iaf_psc_exp_ps_lossless::set_status(const DictionaryDatum & d) { Parameters_ ptmp = P_; // temporary copy in case of errors - double_t delta_EL = ptmp.set(d); // throws if BadProperty + double delta_EL = ptmp.set(d); // throws if BadProperty State_ stmp = S_; // temporary copy in case of errors stmp.set(d, ptmp, delta_EL); // throws if BadProperty @@ -466,15 +466,15 @@ returns true, spike: missed spike excursion, compute t_{max} = dt and find point inequalities are adjusted such that backward propagation (negative time) is already accounted for here */ inline -bool iaf_psc_exp_ps_lossless::is_spike_(double_t dt) +bool iaf_psc_exp_ps_lossless::is_spike_(double dt) { - double_t const I_0 = V_.I_syn_ex_before_ + V_.I_syn_in_before_; - double_t const V_0 = V_.y2_before_; - const double_t exp_tau_s = numerics::expm1(dt/P_.tau_ex_) ; - const double_t exp_tau_m = numerics::expm1(dt/P_.tau_m_) ; - const double_t exp_tau_m_s = numerics::expm1(dt/P_.tau_m_ - dt/P_.tau_ex_); + double const I_0 = V_.I_syn_ex_before_ + V_.I_syn_in_before_; + double const V_0 = V_.y2_before_; + const double exp_tau_s = numerics::expm1(dt/P_.tau_ex_) ; + const double exp_tau_m = numerics::expm1(dt/P_.tau_m_) ; + const double exp_tau_m_s = numerics::expm1(dt/P_.tau_m_ - dt/P_.tau_ex_); - double_t g = ((P_.a1_ * I_0 * exp_tau_m_s + exp_tau_m * (P_.a3_ - P_.I_e_ * P_.a2_) + P_.a3_)/P_.a4_) ; + double g = ((P_.a1_ * I_0 * exp_tau_m_s + exp_tau_m * (P_.a3_ - P_.I_e_ * P_.a2_) + P_.a3_)/P_.a4_) ; //no-spike, NS_1 // intersecting line From da1ae0d311d4050f2f979989222451d607e9d0d2 Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Thu, 24 Aug 2017 13:43:59 +0200 Subject: [PATCH 13/48] addressing hep's comments --- precise/iaf_psc_exp_ps_lossless.cpp | 14 +------------- precise/iaf_psc_exp_ps_lossless.h | 20 ++------------------ 2 files changed, 3 insertions(+), 31 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index 66aeac2ad5..8a58cb6a19 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -78,19 +78,7 @@ nest::iaf_psc_exp_ps_lossless::State_::State_() y2_(0.0), is_refractory_(false), last_spike_step_(-1), - last_spike_offset_(0.0), - dhaene_quick1(0), - dhaene_quick2(0), - dhaene_tmax_lt_t1(0), - dhaene_max(0), - dhaene_det_spikes(0), - c0(0), - c1a(0), - c1b(0), - c3a(0), - c3b(0), - c4(0), - det_spikes(0) + last_spike_offset_(0.0) {} nest::iaf_psc_exp_ps_lossless::Buffers_::Buffers_(iaf_psc_exp_ps_lossless & n) diff --git a/precise/iaf_psc_exp_ps_lossless.h b/precise/iaf_psc_exp_ps_lossless.h index f15de50d07..dd3fb4a66e 100644 --- a/precise/iaf_psc_exp_ps_lossless.h +++ b/precise/iaf_psc_exp_ps_lossless.h @@ -306,22 +306,6 @@ namespace nest long last_spike_step_; //!< Time stamp of most recent spike double last_spike_offset_; //!< Offset of most recent spike - long dhaene_quick1; - long dhaene_quick2; - long dhaene_tmax_lt_t1; - long dhaene_max; - long dhaene_det_spikes; - - long c0; - long c1a; - long c1b; - long c2; - long c3a; - long c3b; - long c4; - long det_spikes; - long state_space_test_spikes; - State_(); //!< Default initialization void get(DictionaryDatum &, const Parameters_ &) const; @@ -468,8 +452,8 @@ inequalities are adjusted such that backward propagation (negative time) is alre inline bool iaf_psc_exp_ps_lossless::is_spike_(double dt) { - double const I_0 = V_.I_syn_ex_before_ + V_.I_syn_in_before_; - double const V_0 = V_.y2_before_; + const double I_0 = V_.I_syn_ex_before_ + V_.I_syn_in_before_; + const double V_0 = V_.y2_before_; const double exp_tau_s = numerics::expm1(dt/P_.tau_ex_) ; const double exp_tau_m = numerics::expm1(dt/P_.tau_m_) ; const double exp_tau_m_s = numerics::expm1(dt/P_.tau_m_ - dt/P_.tau_ex_); From 171a858ed393137901f6248b5dfa26cdf7026610 Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Thu, 24 Aug 2017 13:50:58 +0200 Subject: [PATCH 14/48] addressing hep's comments --- precise/iaf_psc_exp_ps_lossless.cpp | 47 ++++++++++++++++++++++++++++ precise/iaf_psc_exp_ps_lossless.h | 48 ----------------------------- 2 files changed, 47 insertions(+), 48 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index 8a58cb6a19..7081a33b8c 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -582,5 +582,52 @@ inline double nest::iaf_psc_exp_ps_lossless::bisectioning_(const double dt) cons return root; } +/* Conventional spike detection algorithms propagate the initial state forwards in time and see whether it meets the threshold. +This function implements a general method to solve the threshold-detection problem for an integrable, affine or linear +time evolution by applying geometric analysis. The idea is to propagate the threshold backwards in time and see whether +it meets the initial state. In state space spanned by voltage and current, this clearly separates the spiking region and +non-spiking region. is_spike_ takes argument dt which corresponds to the time window at which this spike prediction occurs. +returns true, spike: if (V(t_{right}) > V_(\theta)); returns false: (V(t_{right} < V_(\theta) or initial conditions in no-spike region; +returns true, spike: missed spike excursion, compute t_{max} = dt and find point of threshold crossing t_{\theta} using emit_spike_. +inequalities are adjusted such that backward propagation (negative time) is already accounted for here */ + +inline bool nest::iaf_psc_exp_ps_lossless::is_spike_(const double dt) +{ + const double I_0 = V_.I_syn_ex_before_ + V_.I_syn_in_before_; + const double V_0 = V_.y2_before_; + const double exp_tau_s = numerics::expm1(dt/P_.tau_ex_) ; + const double exp_tau_m = numerics::expm1(dt/P_.tau_m_) ; + const double exp_tau_m_s = numerics::expm1(dt/P_.tau_m_ - dt/P_.tau_ex_); + + double g = ((P_.a1_ * I_0 * exp_tau_m_s + exp_tau_m * (P_.a3_ - P_.I_e_ * P_.a2_) + P_.a3_)/P_.a4_) ; + + //no-spike, NS_1 + // intersecting line + if((V_0 <= (((I_0 + P_.I_e_)*(P_.b1_ * exp_tau_m + P_.b2_* exp_tau_s) + P_.b5_*(exp_tau_m - exp_tau_s))/( P_.b7_ * exp_tau_s))) + //continuation line + && (V_0 < g)) + { + return false; + } + + //spike, S_1 + else if (V_0 >= g ) + { + return true; + } + //no-spike, NS_2 + else if(V_0 < (P_.c1_ * P_.I_e_ + P_.c2_ * I_0 + P_.c3_* pow(I_0, P_.c4_) * pow((P_.c5_ - P_.I_e_), P_.c6_))) + { + return false; + } + else + //spike, S_2 + { + V_.bisection_step = (P_.a1_ / P_.tau_m_ * P_.tau_ex_ ) * log ( P_.b1_ * I_0 / (P_.a2_ * P_.I_e_ - P_.a1_ * I_0 - P_.a4_ * V_0 ) ); + return true; + } + +} + diff --git a/precise/iaf_psc_exp_ps_lossless.h b/precise/iaf_psc_exp_ps_lossless.h index dd3fb4a66e..5ce552effe 100644 --- a/precise/iaf_psc_exp_ps_lossless.h +++ b/precise/iaf_psc_exp_ps_lossless.h @@ -439,54 +439,6 @@ void iaf_psc_exp_ps_lossless::set_status(const DictionaryDatum & d) P_ = ptmp; S_ = stmp; } - -/* Conventional spike detection algorithms propagate the initial state forwards in time and see whether it meets the threshold. -This function implements a general method to solve the threshold-detection problem for an integrable, affine or linear -time evolution by applying geometric analysis. The idea is to propagate the threshold backwards in time and see whether -it meets the initial state. In state space spanned by voltage and current, this clearly separates the spiking region and -non-spiking region. is_spike_ takes argument dt which corresponds to the time window at which this spike prediction occurs. -returns true, spike: if (V(t_{right}) > V_(\theta)); returns false: (V(t_{right} < V_(\theta) or initial conditions in no-spike region; -returns true, spike: missed spike excursion, compute t_{max} = dt and find point of threshold crossing t_{\theta} using emit_spike_. -inequalities are adjusted such that backward propagation (negative time) is already accounted for here */ - -inline -bool iaf_psc_exp_ps_lossless::is_spike_(double dt) -{ - const double I_0 = V_.I_syn_ex_before_ + V_.I_syn_in_before_; - const double V_0 = V_.y2_before_; - const double exp_tau_s = numerics::expm1(dt/P_.tau_ex_) ; - const double exp_tau_m = numerics::expm1(dt/P_.tau_m_) ; - const double exp_tau_m_s = numerics::expm1(dt/P_.tau_m_ - dt/P_.tau_ex_); - - double g = ((P_.a1_ * I_0 * exp_tau_m_s + exp_tau_m * (P_.a3_ - P_.I_e_ * P_.a2_) + P_.a3_)/P_.a4_) ; - - //no-spike, NS_1 - // intersecting line - if((V_0 <= (((I_0 + P_.I_e_)*(P_.b1_ * exp_tau_m + P_.b2_* exp_tau_s) + P_.b5_*(exp_tau_m - exp_tau_s))/( P_.b7_ * exp_tau_s))) - //continuation line - && (V_0 < g)) - { - return false; - } - - //spike, S_1 - else if (V_0 >= g ) - { - return true; - } - //no-spike, NS_2 - else if(V_0 < (P_.c1_ * P_.I_e_ + P_.c2_ * I_0 + P_.c3_* pow(I_0, P_.c4_) * pow((P_.c5_ - P_.I_e_), P_.c6_))) - { - return false; - } - else - //spike, S_2 - { - V_.bisection_step = (P_.a1_ / P_.tau_m_ * P_.tau_ex_ ) * log ( P_.b1_ * I_0 / (P_.a2_ * P_.I_e_ - P_.a1_ * I_0 - P_.a4_ * V_0 ) ); - return true; - } - -} } // namespace From 0bda5c4befd0b2a25f15085b69543473da416acf Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Thu, 24 Aug 2017 14:32:44 +0200 Subject: [PATCH 15/48] addressing hep's comments --- precise/iaf_psc_exp_ps_lossless.cpp | 8 ++++---- precise/iaf_psc_exp_ps_lossless.h | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index 7081a33b8c..8d4b4d1b16 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -68,7 +68,7 @@ nest::iaf_psc_exp_ps_lossless::Parameters_::Parameters_() U_min_ (-std::numeric_limits::infinity()), // mV U_reset_( -70.0-E_L_) // mV, rel to E_L_ { - calc_const_spike_test_(); + calc_const_is_spike_(); } nest::iaf_psc_exp_ps_lossless::State_::State_() @@ -96,7 +96,7 @@ nest::iaf_psc_exp_ps_lossless::Buffers_::Buffers_(const Buffers_ &, iaf_psc_exp_ //constants for time-reversal state space spike-detection algorithm -void nest::iaf_psc_exp_ps_lossless::Parameters_::calc_const_spike_test_() +void nest::iaf_psc_exp_ps_lossless::Parameters_::calc_const_is_spike_() { // line corresponding to the final timestep i.e t_right: continuation // of the curved boundary: a + I*b @@ -202,7 +202,7 @@ double nest::iaf_psc_exp_ps_lossless::Parameters_::set(const DictionaryDatum & d throw BadProperty("Membrane and synapse time constant(s) must differ." "See note in documentation."); - calc_const_spike_test_(); + calc_const_is_spike_(); return delta_EL; } @@ -591,7 +591,7 @@ returns true, spike: if (V(t_{right}) > V_(\theta)); returns false: (V(t_{right} returns true, spike: missed spike excursion, compute t_{max} = dt and find point of threshold crossing t_{\theta} using emit_spike_. inequalities are adjusted such that backward propagation (negative time) is already accounted for here */ -inline bool nest::iaf_psc_exp_ps_lossless::is_spike_(const double dt) +inline bool nest::iaf_psc_exp_ps_lossless::is_spike_(const double dt) { const double I_0 = V_.I_syn_ex_before_ + V_.I_syn_in_before_; const double V_0 = V_.y2_before_; diff --git a/precise/iaf_psc_exp_ps_lossless.h b/precise/iaf_psc_exp_ps_lossless.h index 5ce552effe..88acf8faae 100644 --- a/precise/iaf_psc_exp_ps_lossless.h +++ b/precise/iaf_psc_exp_ps_lossless.h @@ -283,7 +283,7 @@ namespace nest double d3_; Parameters_(); //!< Sets default parameter values - void calc_const_spike_test_(); + void calc_const_is_spike_(); void get(DictionaryDatum &) const; //!< Store current values in dictionary double set(const DictionaryDatum &); //!< Set values from dicitonary @@ -439,6 +439,8 @@ void iaf_psc_exp_ps_lossless::set_status(const DictionaryDatum & d) P_ = ptmp; S_ = stmp; } + + } // namespace From a7596398f18da29a88084b0108c50d54a7212d8c Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Thu, 24 Aug 2017 15:13:32 +0200 Subject: [PATCH 16/48] addressing hep's comments --- precise/iaf_psc_exp_ps_lossless.cpp | 58 ++++++++++++++++------------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index 8d4b4d1b16..3b55adb307 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -94,38 +94,34 @@ nest::iaf_psc_exp_ps_lossless::Buffers_::Buffers_(const Buffers_ &, iaf_psc_exp_ * ---------------------------------------------------------------- */ -//constants for time-reversal state space spike-detection algorithm +// calc_const_is_spike_ precomputes constants for the system of inequalities +// in the _is_spike function. a1_, a2_, a3_, a4_ are constants that appear +// in inequality V < g(h, I_e). b1_, b2_, b3_, b4_, in the line corresponding +// to the final timestep, V < f(h, I). c1_, c2_, c3_, c4_, c5_, c6_ +// in the envelope, V < b(I_e). void nest::iaf_psc_exp_ps_lossless::Parameters_::calc_const_is_spike_() { - // line corresponding to the final timestep i.e t_right: continuation - // of the curved boundary: a + I*b + a1_ =tau_m_ * tau_ex_; a2_ =tau_m_ * (tau_m_ - tau_ex_); a3_ =c_m_ * U_th_ * (tau_m_ - tau_ex_); a4_ =c_m_ * (tau_m_ - tau_ex_); - // line joining endpoints of the envelope: \alpha I + \beta b1_ =-tau_m_ * tau_m_; b2_ =tau_m_ * tau_ex_; b3_ =tau_m_*(tau_m_ - tau_ex_)- tau_m_*tau_m_ + tau_m_* tau_ex_; - b4_ =-tau_m_*tau_m_; //b1 + b4_ =-tau_m_*tau_m_; b5_ =tau_m_*c_m_*U_th_; b6_ =tau_m_*(tau_m_- tau_ex_); b7_ =-c_m_*(tau_m_ - tau_ex_); - // envelope or curved boundary c1_ =tau_m_/c_m_; c2_ =(-tau_m_ * tau_ex_)/(c_m_*(tau_m_ - tau_ex_)); c3_ =(tau_m_ * tau_m_)/ (c_m_ * (tau_m_ - tau_ex_)); c4_ =tau_ex_/tau_m_; c5_ =(c_m_ * U_th_)/tau_m_; c6_ =1-(tau_ex_/tau_m_); - - // parallel line - d1_ = tau_m_ * c_m_ ; - d2_ = tau_m_ * tau_ex_; - d3_ = c_m_ * (tau_m_ - tau_ex_); } void nest::iaf_psc_exp_ps_lossless::Parameters_::get(DictionaryDatum & d) const @@ -582,14 +578,28 @@ inline double nest::iaf_psc_exp_ps_lossless::bisectioning_(const double dt) cons return root; } -/* Conventional spike detection algorithms propagate the initial state forwards in time and see whether it meets the threshold. -This function implements a general method to solve the threshold-detection problem for an integrable, affine or linear -time evolution by applying geometric analysis. The idea is to propagate the threshold backwards in time and see whether -it meets the initial state. In state space spanned by voltage and current, this clearly separates the spiking region and -non-spiking region. is_spike_ takes argument dt which corresponds to the time window at which this spike prediction occurs. -returns true, spike: if (V(t_{right}) > V_(\theta)); returns false: (V(t_{right} < V_(\theta) or initial conditions in no-spike region; -returns true, spike: missed spike excursion, compute t_{max} = dt and find point of threshold crossing t_{\theta} using emit_spike_. -inequalities are adjusted such that backward propagation (negative time) is already accounted for here */ +/* Conventional spike detection algorithms propagate the initial state forwards in time +and see whether it meets the threshold.This function implements a general method to solve +the threshold-detection problem for an integrable, affine or lineartime evolution by +applying geometric analysis. The idea is to propagate the threshold backwards in time +and see whether it meets the initial state. In state space spanned by voltage and current, +this separates the spiking region and non-spiking region. is_spike_ takes argument dt +which corresponds to the time window at which this spike prediction occurs. + +returns true, spike: if (V(t_{right}) > V_(\theta)); returns false: (V(t_{right} < V_(\theta) +or initial conditions in no-spike region; +returns true, spike: missed spike excursion, compute bisection_step, where t_{max} = dt and +find point of threshold crossing t_{\theta} using bisectioning method. +Inequalities are adjusted such that backward propagation (negative time) is already accounted +for here. + +The state space spanning the non-spiking region is bound by the following system of inequalities: +threshold line V < \theta, envelope, V < b(I_e), line corresponding to the final timestep +V < f(h, I) (or) linear approximation of the envelope, V < g(h, I_e). +The state space spanning the spiking region is bound by the following system of inequalities: +threshold line V < \theta, envelope, V > b(I_e) and line corresponding to the final timestep +V > f(h, I) (or) linear approximation of the envelope, V < g(h, I_e). +*/ inline bool nest::iaf_psc_exp_ps_lossless::is_spike_(const double dt) { @@ -601,27 +611,25 @@ inline bool nest::iaf_psc_exp_ps_lossless::is_spike_(const double dt) double g = ((P_.a1_ * I_0 * exp_tau_m_s + exp_tau_m * (P_.a3_ - P_.I_e_ * P_.a2_) + P_.a3_)/P_.a4_) ; - //no-spike, NS_1 - // intersecting line + //no-spike if((V_0 <= (((I_0 + P_.I_e_)*(P_.b1_ * exp_tau_m + P_.b2_* exp_tau_s) + P_.b5_*(exp_tau_m - exp_tau_s))/( P_.b7_ * exp_tau_s))) - //continuation line && (V_0 < g)) { return false; } - //spike, S_1 + //spike else if (V_0 >= g ) { return true; } - //no-spike, NS_2 + //no-spike else if(V_0 < (P_.c1_ * P_.I_e_ + P_.c2_ * I_0 + P_.c3_* pow(I_0, P_.c4_) * pow((P_.c5_ - P_.I_e_), P_.c6_))) { return false; } else - //spike, S_2 + //spike { V_.bisection_step = (P_.a1_ / P_.tau_m_ * P_.tau_ex_ ) * log ( P_.b1_ * I_0 / (P_.a2_ * P_.I_e_ - P_.a1_ * I_0 - P_.a4_ * V_0 ) ); return true; From 4c53f428097158118b83310c3c3840192a874f3c Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Thu, 24 Aug 2017 16:00:13 +0200 Subject: [PATCH 17/48] addressing hep's comments --- precise/iaf_psc_exp_ps_lossless.cpp | 65 ++++++++++------------------- precise/iaf_psc_exp_ps_lossless.h | 46 ++++++++++---------- 2 files changed, 46 insertions(+), 65 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index 3b55adb307..4092f03645 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -67,9 +67,7 @@ nest::iaf_psc_exp_ps_lossless::Parameters_::Parameters_() U_th_ ( -55.0-E_L_), // mV, rel to E_L_ U_min_ (-std::numeric_limits::infinity()), // mV U_reset_( -70.0-E_L_) // mV, rel to E_L_ -{ - calc_const_is_spike_(); -} +{} nest::iaf_psc_exp_ps_lossless::State_::State_() : y0_(0.0), @@ -92,38 +90,6 @@ nest::iaf_psc_exp_ps_lossless::Buffers_::Buffers_(const Buffers_ &, iaf_psc_exp_ /* ---------------------------------------------------------------- * Parameter and state extractions and manipulation functions * ---------------------------------------------------------------- */ - - -// calc_const_is_spike_ precomputes constants for the system of inequalities -// in the _is_spike function. a1_, a2_, a3_, a4_ are constants that appear -// in inequality V < g(h, I_e). b1_, b2_, b3_, b4_, in the line corresponding -// to the final timestep, V < f(h, I). c1_, c2_, c3_, c4_, c5_, c6_ -// in the envelope, V < b(I_e). - -void nest::iaf_psc_exp_ps_lossless::Parameters_::calc_const_is_spike_() -{ - - a1_ =tau_m_ * tau_ex_; - a2_ =tau_m_ * (tau_m_ - tau_ex_); - a3_ =c_m_ * U_th_ * (tau_m_ - tau_ex_); - a4_ =c_m_ * (tau_m_ - tau_ex_); - - b1_ =-tau_m_ * tau_m_; - b2_ =tau_m_ * tau_ex_; - b3_ =tau_m_*(tau_m_ - tau_ex_)- tau_m_*tau_m_ + tau_m_* tau_ex_; - b4_ =-tau_m_*tau_m_; - b5_ =tau_m_*c_m_*U_th_; - b6_ =tau_m_*(tau_m_- tau_ex_); - b7_ =-c_m_*(tau_m_ - tau_ex_); - - c1_ =tau_m_/c_m_; - c2_ =(-tau_m_ * tau_ex_)/(c_m_*(tau_m_ - tau_ex_)); - c3_ =(tau_m_ * tau_m_)/ (c_m_ * (tau_m_ - tau_ex_)); - c4_ =tau_ex_/tau_m_; - c5_ =(c_m_ * U_th_)/tau_m_; - c6_ =1-(tau_ex_/tau_m_); -} - void nest::iaf_psc_exp_ps_lossless::Parameters_::get(DictionaryDatum & d) const { def(d, names::E_L, E_L_); @@ -198,8 +164,6 @@ double nest::iaf_psc_exp_ps_lossless::Parameters_::set(const DictionaryDatum & d throw BadProperty("Membrane and synapse time constant(s) must differ." "See note in documentation."); - calc_const_is_spike_(); - return delta_EL; } @@ -285,8 +249,25 @@ void nest::iaf_psc_exp_ps_lossless::calibrate() V_.P20_ = -P_.tau_m_ / P_.c_m_ * V_.expm1_tau_m_; V_.P21_ex_ = -P_.tau_m_*P_.tau_ex_ / (P_.tau_m_-P_.tau_ex_) / P_.c_m_ * (V_.expm1_tau_ex_-V_.expm1_tau_m_); V_.P21_in_ = -P_.tau_m_*P_.tau_in_ / (P_.tau_m_-P_.tau_in_) / P_.c_m_ * (V_.expm1_tau_in_-V_.expm1_tau_m_); - V_.refractory_steps_ = Time(Time::ms(P_.t_ref_)).get_steps(); + + V_.a1_ = P_.tau_m_ * P_.tau_ex_; + V_.a2_ = P_.tau_m_ * (P_.tau_m_ - P_.tau_ex_); + V_.a3_ = P_.c_m_ * P_.U_th_ * (P_.tau_m_ - P_.tau_ex_); + V_.a4_ = P_.c_m_ * (P_.tau_m_ - P_.tau_ex_); + + V_.b1_ = -P_.tau_m_ * P_.tau_m_; + V_.b2_ = P_.tau_m_ * P_.tau_ex_; + V_.b3_ = P_.tau_m_*P_.c_m_*P_.U_th_; + V_.b4_ = -P_.c_m_*(P_.tau_m_ - P_.tau_ex_); + + V_.c1_ = P_.tau_m_/P_.c_m_; + V_.c2_ = (-P_.tau_m_ * P_.tau_ex_)/(P_.c_m_*(P_.tau_m_ - P_.tau_ex_)); + V_.c3_ = (P_.tau_m_ * P_.tau_m_)/ (P_.c_m_ * (P_.tau_m_ - P_.tau_ex_)); + V_.c4_ = P_.tau_ex_/P_.tau_m_; + V_.c5_ = (P_.c_m_ * P_.U_th_)/P_.tau_m_; + V_.c6_ = 1-(P_.tau_ex_/P_.tau_m_); + assert( V_.refractory_steps_ >= 0 ); // since t_ref_ >= 0, this can only fail in error } @@ -609,10 +590,10 @@ inline bool nest::iaf_psc_exp_ps_lossless::is_spike_(const double dt) const double exp_tau_m = numerics::expm1(dt/P_.tau_m_) ; const double exp_tau_m_s = numerics::expm1(dt/P_.tau_m_ - dt/P_.tau_ex_); - double g = ((P_.a1_ * I_0 * exp_tau_m_s + exp_tau_m * (P_.a3_ - P_.I_e_ * P_.a2_) + P_.a3_)/P_.a4_) ; + double g = ((V_.a1_ * I_0 * exp_tau_m_s + exp_tau_m * (V_.a3_ - P_.I_e_ * V_.a2_) + V_.a3_)/V_.a4_) ; //no-spike - if((V_0 <= (((I_0 + P_.I_e_)*(P_.b1_ * exp_tau_m + P_.b2_* exp_tau_s) + P_.b5_*(exp_tau_m - exp_tau_s))/( P_.b7_ * exp_tau_s))) + if((V_0 <= (((I_0 + P_.I_e_)*(V_.b1_ * exp_tau_m + V_.b2_* exp_tau_s) + V_.b3_*(exp_tau_m - exp_tau_s))/( V_.b4_ * exp_tau_s))) && (V_0 < g)) { return false; @@ -624,14 +605,14 @@ inline bool nest::iaf_psc_exp_ps_lossless::is_spike_(const double dt) return true; } //no-spike - else if(V_0 < (P_.c1_ * P_.I_e_ + P_.c2_ * I_0 + P_.c3_* pow(I_0, P_.c4_) * pow((P_.c5_ - P_.I_e_), P_.c6_))) + else if(V_0 < (V_.c1_ * P_.I_e_ + V_.c2_ * I_0 + V_.c3_* pow(I_0, V_.c4_) * pow((V_.c5_ - P_.I_e_), V_.c6_))) { return false; } else //spike { - V_.bisection_step = (P_.a1_ / P_.tau_m_ * P_.tau_ex_ ) * log ( P_.b1_ * I_0 / (P_.a2_ * P_.I_e_ - P_.a1_ * I_0 - P_.a4_ * V_0 ) ); + V_.bisection_step = (V_.a1_ / P_.tau_m_ * P_.tau_ex_ ) * log ( V_.b1_ * I_0 / (V_.a2_ * P_.I_e_ - V_.a1_ * I_0 - V_.a4_ * V_0 ) ); return true; } diff --git a/precise/iaf_psc_exp_ps_lossless.h b/precise/iaf_psc_exp_ps_lossless.h index 88acf8faae..03039e25ea 100644 --- a/precise/iaf_psc_exp_ps_lossless.h +++ b/precise/iaf_psc_exp_ps_lossless.h @@ -260,30 +260,9 @@ namespace nest At threshold crossing, the membrane potential is reset to this value. Relative to resting potential. */ double U_reset_; - - double a1_; - double a2_; - double a3_; - double a4_; - double b1_; - double b2_; - double b3_; - double b4_; - double b5_; - double b6_; - double b7_; - double c1_; - double c2_; - double c3_; - double c4_; - double c5_; - double c6_; - double d1_; - double d2_; - double d3_; Parameters_(); //!< Sets default parameter values - void calc_const_is_spike_(); + // void calc_const_is_spike_(); void get(DictionaryDatum &) const; //!< Store current values in dictionary double set(const DictionaryDatum &); //!< Set values from dicitonary @@ -352,7 +331,28 @@ namespace nest double I_syn_ex_before_; //!< y1_ at beginning of ministep double I_syn_in_before_; //!< y1_ at beginning of ministep double y2_before_; //!< y2_ at beginning of ministep - double bisection_step; + double bisection_step; // if missed spike is detected, calculate time to emit spike + + // The following are variables that are precomputed for the _is_spike function. + // a1_, a2_, a3_, a4_ are constants that appear in the inequality V < g(h, I_e). + double a1_; + double a2_; + double a3_; + double a4_; + + // b1_, b2_, b3_, b4_, in the line corresponding to the final timestep, V < f(h, I). + double b1_; + double b2_; + double b3_; + double b4_; + + //c1_, c2_, c3_, c4_, c5_, c6_ in the envelope, V < b(I_e). + double c1_; + double c2_; + double c3_; + double c4_; + double c5_; + double c6_; }; // Access functions for UniversalDataLogger ------------------------------- From 5744488c55435520a68c5d6c5d586b68f039a649 Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Fri, 25 Aug 2017 09:57:41 +0200 Subject: [PATCH 18/48] addressing hep's comments --- nestkernel/nest_names.h | 1 - 1 file changed, 1 deletion(-) diff --git a/nestkernel/nest_names.h b/nestkernel/nest_names.h index 12059d0bc0..50b1d15417 100644 --- a/nestkernel/nest_names.h +++ b/nestkernel/nest_names.h @@ -439,7 +439,6 @@ extern const Name xs; //!< current scaling factor of the synaptic weight [0...1] extern const Name z; //!< Number of available synaptic elements per node extern const Name z_connected; //!< Number of connected synaptic elements //!< per node - } } From 0c98af5a2731c71f5bf8c783029de24262448b68 Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Fri, 25 Aug 2017 10:46:22 +0200 Subject: [PATCH 19/48] addressing hep's comments --- precise/iaf_psc_exp_ps_lossless.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index 4092f03645..621949d22c 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -592,25 +592,25 @@ inline bool nest::iaf_psc_exp_ps_lossless::is_spike_(const double dt) double g = ((V_.a1_ * I_0 * exp_tau_m_s + exp_tau_m * (V_.a3_ - P_.I_e_ * V_.a2_) + V_.a3_)/V_.a4_) ; - //no-spike + //no-spike, NS_1, (V <= f_h,I_e(I) and V < g_h,I_e(I)) if((V_0 <= (((I_0 + P_.I_e_)*(V_.b1_ * exp_tau_m + V_.b2_* exp_tau_s) + V_.b3_*(exp_tau_m - exp_tau_s))/( V_.b4_ * exp_tau_s))) && (V_0 < g)) { return false; } - //spike + //spike, S_1, V >= g_h,I_e(I) else if (V_0 >= g ) { return true; } - //no-spike + //no-spike, NS_2, V < b(I) else if(V_0 < (V_.c1_ * P_.I_e_ + V_.c2_ * I_0 + V_.c3_* pow(I_0, V_.c4_) * pow((V_.c5_ - P_.I_e_), V_.c6_))) { return false; } else - //spike + //missed spike detected, S_2 { V_.bisection_step = (V_.a1_ / P_.tau_m_ * P_.tau_ex_ ) * log ( V_.b1_ * I_0 / (V_.a2_ * P_.I_e_ - V_.a1_ * I_0 - V_.a4_ * V_0 ) ); return true; From 53c2f7ce71bf95887c087fcdf4834a393197dba3 Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Tue, 5 Sep 2017 12:54:51 +0200 Subject: [PATCH 20/48] code formatting issues --- nestkernel/nest_names.cpp | 2 - precise/CMakeLists.txt | 2 +- precise/iaf_psc_exp_ps_lossless.cpp | 194 +++++++++++++--------------- precise/iaf_psc_exp_ps_lossless.h | 68 ++++++---- precise/precisemodule.cpp | 4 +- 5 files changed, 135 insertions(+), 135 deletions(-) diff --git a/nestkernel/nest_names.cpp b/nestkernel/nest_names.cpp index 9f22c70746..556841d334 100644 --- a/nestkernel/nest_names.cpp +++ b/nestkernel/nest_names.cpp @@ -366,7 +366,5 @@ const Name xs( "xs" ); const Name z( "z" ); const Name z_connected( "z_connected" ); - - } } diff --git a/precise/CMakeLists.txt b/precise/CMakeLists.txt index f8ba49892e..29f3b9bbdb 100644 --- a/precise/CMakeLists.txt +++ b/precise/CMakeLists.txt @@ -23,9 +23,9 @@ set( precise_sources iaf_psc_alpha_canon.cpp iaf_psc_alpha_canon.h iaf_psc_alpha_presc.cpp iaf_psc_alpha_presc.h iaf_psc_exp_ps.cpp iaf_psc_exp_ps.h + iaf_psc_exp_ps_lossless.cpp iaf_psc_exp_ps_lossless.h poisson_generator_ps.cpp poisson_generator_ps.h parrot_neuron_ps.cpp parrot_neuron_ps.h - iaf_psc_exp_ps_lossless.cpp iaf_psc_exp_ps_lossless.h precisemodule.cpp precisemodule.h ) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index 621949d22c..4bb904a4ea 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -19,17 +19,23 @@ * along with NEST. If not, see . * */ + #include "iaf_psc_exp_ps_lossless.h" +// C++ includes: +#include + +// Includes from nestkernel: #include "exceptions.h" +#include "universal_data_logger_impl.h" + +// Includes from sli: #include "dict.h" +#include "dictutils.h" #include "integerdatum.h" #include "doubledatum.h" -#include "dictutils.h" -#include "universal_data_logger_impl.h" #include "arraydatum.h" -#include /* ---------------------------------------------------------------- * Recordables map @@ -57,17 +63,18 @@ namespace nest * ---------------------------------------------------------------- */ nest::iaf_psc_exp_ps_lossless::Parameters_::Parameters_() - : tau_m_ ( 10.0 ), // ms - tau_ex_ ( 2.0 ), // ms - tau_in_ ( 2.0 ), // ms - c_m_ (250.0 ), // pF - t_ref_ ( 2.0 ), // ms - E_L_ ( 0.0 ), // mV - I_e_ ( 0.0 ), // pA - U_th_ ( -55.0-E_L_), // mV, rel to E_L_ - U_min_ (-std::numeric_limits::infinity()), // mV - U_reset_( -70.0-E_L_) // mV, rel to E_L_ -{} + : tau_m_( 10.0 ) // ms + , tau_ex_( 2.0 ) // ms + , tau_in_( 2.0 ) // ms + , c_m_( 250.0 ) // pF + , t_ref_( 2.0 ) // ms + , E_L_( 0.0 ) // mV + , I_e_( 0.0 ) // pA + , U_th_( -55.0 - E_L_ ) // mV, rel to E_L_ + , U_min_( -std::numeric_limits< double >::infinity() ) // mV + , U_reset_( -70.0 - E_L_ ) // mV, rel to E_L_ +{ +} nest::iaf_psc_exp_ps_lossless::State_::State_() : y0_(0.0), @@ -182,9 +189,13 @@ void nest::iaf_psc_exp_ps_lossless::State_::get(DictionaryDatum & d, void nest::iaf_psc_exp_ps_lossless::State_::set(const DictionaryDatum & d, const Parameters_ & p, double delta_EL) { if ( updateValue(d, names::V_m, y2_) ) + { y2_ -= p.E_L_; + } else + { y2_ -= delta_EL; + } updateValue(d, names::I_syn_ex, I_syn_ex_ ); updateValue(d, names::I_syn_in, I_syn_in_); @@ -214,7 +225,7 @@ nest::iaf_psc_exp_ps_lossless::iaf_psc_exp_ps_lossless(const iaf_psc_exp_ps_loss * Node initialization functions * ---------------------------------------------------------------- */ -void nest::iaf_psc_exp_ps_lossless::init_node_(const Node & proto) +void nest::iaf_psc_exp_ps_lossless::init_node_(const Node& proto) { const iaf_psc_exp_ps_lossless & pr = downcast(proto); @@ -222,7 +233,7 @@ void nest::iaf_psc_exp_ps_lossless::init_node_(const Node & proto) S_ = pr.S_; } -void nest::iaf_psc_exp_ps_lossless::init_state_(const Node & proto) +void nest::iaf_psc_exp_ps_lossless::init_state_(const Node& proto) { const iaf_psc_exp_ps_lossless & pr = downcast(proto); @@ -250,6 +261,7 @@ void nest::iaf_psc_exp_ps_lossless::calibrate() V_.P21_ex_ = -P_.tau_m_*P_.tau_ex_ / (P_.tau_m_-P_.tau_ex_) / P_.c_m_ * (V_.expm1_tau_ex_-V_.expm1_tau_m_); V_.P21_in_ = -P_.tau_m_*P_.tau_in_ / (P_.tau_m_-P_.tau_in_) / P_.c_m_ * (V_.expm1_tau_in_-V_.expm1_tau_m_); V_.refractory_steps_ = Time(Time::ms(P_.t_ref_)).get_steps(); + assert( V_.refractory_steps_ >= 0 ); // since t_ref_ >= 0, this can only fail in error V_.a1_ = P_.tau_m_ * P_.tau_ex_; V_.a2_ = P_.tau_m_ * (P_.tau_m_ - P_.tau_ex_); @@ -267,8 +279,6 @@ void nest::iaf_psc_exp_ps_lossless::calibrate() V_.c4_ = P_.tau_ex_/P_.tau_m_; V_.c5_ = (P_.c_m_ * P_.U_th_)/P_.tau_m_; V_.c6_ = 1-(P_.tau_ex_/P_.tau_m_); - - assert( V_.refractory_steps_ >= 0 ); // since t_ref_ >= 0, this can only fail in error } /* ---------------------------------------------------------------- @@ -291,10 +301,11 @@ void nest::iaf_psc_exp_ps_lossless::update(const Time & origin, We need to check for this here and issue spikes at the beginning of the interval. */ - if ( S_.y2_ >= P_.U_th_ ) + { emit_instant_spike_(origin, from, V_.h_ms_*(1.0-std::numeric_limits::epsilon())); + } for ( long lag = from; lag < to; ++lag ) { @@ -317,7 +328,7 @@ void nest::iaf_psc_exp_ps_lossless::update(const Time & origin, double ev_weight; bool end_of_refract; - if ( !B_.events_.get_next_spike(T, ev_offset, ev_weight, 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 @@ -341,15 +352,13 @@ void nest::iaf_psc_exp_ps_lossless::update(const Time & origin, on all state variables having their values at the end of the interval. */ - // the state space test takes argument dt and - // returns true, spike: if (V(t_{right}) > V_(\theta)); - // returns false: ( (V(t_{right} < V_(\theta) or initial conditions in no-spike region); - // returns true, spike: missed spike excursion, compute t_{max} = dt and find point of - // threshold crossing t_{\theta} using emit_spike_. - V_.bisection_step = V_.h_ms_; + + V_.bisection_step_ = V_.h_ms_; if (is_spike_(V_.h_ms_)) - emit_spike_(origin, lag, 0, V_.bisection_step); + { + emit_spike_(origin, lag, 0, V_.bisection_step_); + } } else @@ -364,41 +373,44 @@ void nest::iaf_psc_exp_ps_lossless::update(const Time & origin, do { - // time is measured backward: inverse order in difference - const double 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 - - V_.bisection_step = ministep; - - - if (is_spike_(ministep)) - { - emit_spike_(origin, lag, V_.h_ms_-last_offset, V_.bisection_step); - } - - - - // handle event - if ( end_of_refract ) - S_.is_refractory_ = false; // return from refractoriness - else - { - if ( ev_weight >= 0.0 ) - S_.I_syn_ex_ += ev_weight; // exc. spike input - else - S_.I_syn_in_ += ev_weight; // inh. spike input - } - - // store state - V_.I_syn_ex_before_ = S_.I_syn_ex_; - V_.I_syn_in_before_ = S_.I_syn_in_; - V_.y2_before_ = S_.y2_; - last_offset = ev_offset; + // time is measured backward: inverse order in difference + const double 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 + + V_.bisection_step_ = ministep; + + if (is_spike_(ministep)) + { + emit_spike_(origin, lag, V_.h_ms_-last_offset, V_.bisection_step_); + } + + // handle event + if ( end_of_refract ) + { + S_.is_refractory_ = false; // return from refractoriness + } + else + { + if ( ev_weight >= 0.0 ) + { + S_.I_syn_ex_ += ev_weight; // exc. spike input + } + else + { + S_.I_syn_in_ += ev_weight; // inh. spike input + } + } + + // store state + V_.I_syn_ex_before_ = S_.I_syn_ex_; + V_.I_syn_in_before_ = S_.I_syn_in_; + V_.y2_before_ = S_.y2_; + last_offset = ev_offset; } while ( B_.events_.get_next_spike(T, ev_offset, ev_weight, end_of_refract) ); @@ -407,16 +419,12 @@ void nest::iaf_psc_exp_ps_lossless::update(const Time & origin, // of interval if ( last_offset > 0 ) // not at end of step, do remainder { - - - V_.bisection_step = last_offset; + V_.bisection_step_ = last_offset; propagate_(last_offset); - - if (is_spike_(last_offset)) - emit_spike_(origin, lag, V_.h_ms_-last_offset, V_.bisection_step); - - - + if (is_spike_(last_offset)) + { + emit_spike_(origin, lag, V_.h_ms_-last_offset, V_.bisection_step_); + } } } // else @@ -474,7 +482,7 @@ void nest::iaf_psc_exp_ps_lossless::propagate_(const double dt) const double expm1_tau_ex = numerics::expm1(-dt/P_.tau_ex_); const double expm1_tau_in = numerics::expm1(-dt/P_.tau_in_); - if ( !S_.is_refractory_ ) + if ( not S_.is_refractory_ ) { const double expm1_tau_m = numerics::expm1(-dt/P_.tau_m_); @@ -484,6 +492,7 @@ void nest::iaf_psc_exp_ps_lossless::propagate_(const double dt) S_.y2_ = P20*(P_.I_e_+S_.y0_) + P21_ex*S_.I_syn_ex_ + P21_in*S_.I_syn_in_ + expm1_tau_m*S_.y2_ + S_.y2_; } + S_.I_syn_ex_ = S_.I_syn_ex_*expm1_tau_ex + S_.I_syn_ex_; S_.I_syn_in_ = S_.I_syn_in_*expm1_tau_in + S_.I_syn_in_; } @@ -530,20 +539,21 @@ void nest::iaf_psc_exp_ps_lossless::emit_instant_spike_(const Time & origin, con } inline double nest::iaf_psc_exp_ps_lossless::bisectioning_(const double dt) const -{ - +{ double root = 0.0; - double y2_root = V_.y2_before_; - double div = 2.0; while ( fabs(P_.U_th_-y2_root) > 1e-14 and (dt/div > 0.0) ) { if ( y2_root > P_.U_th_ ) + { root -= dt/div; + } else + { root += dt/div; - + } + div *= 2.0; const double expm1_tau_ex = numerics::expm1(-root/P_.tau_ex_); @@ -559,29 +569,6 @@ inline double nest::iaf_psc_exp_ps_lossless::bisectioning_(const double dt) cons return root; } -/* Conventional spike detection algorithms propagate the initial state forwards in time -and see whether it meets the threshold.This function implements a general method to solve -the threshold-detection problem for an integrable, affine or lineartime evolution by -applying geometric analysis. The idea is to propagate the threshold backwards in time -and see whether it meets the initial state. In state space spanned by voltage and current, -this separates the spiking region and non-spiking region. is_spike_ takes argument dt -which corresponds to the time window at which this spike prediction occurs. - -returns true, spike: if (V(t_{right}) > V_(\theta)); returns false: (V(t_{right} < V_(\theta) -or initial conditions in no-spike region; -returns true, spike: missed spike excursion, compute bisection_step, where t_{max} = dt and -find point of threshold crossing t_{\theta} using bisectioning method. -Inequalities are adjusted such that backward propagation (negative time) is already accounted -for here. - -The state space spanning the non-spiking region is bound by the following system of inequalities: -threshold line V < \theta, envelope, V < b(I_e), line corresponding to the final timestep -V < f(h, I) (or) linear approximation of the envelope, V < g(h, I_e). -The state space spanning the spiking region is bound by the following system of inequalities: -threshold line V < \theta, envelope, V > b(I_e) and line corresponding to the final timestep -V > f(h, I) (or) linear approximation of the envelope, V < g(h, I_e). -*/ - inline bool nest::iaf_psc_exp_ps_lossless::is_spike_(const double dt) { const double I_0 = V_.I_syn_ex_before_ + V_.I_syn_in_before_; @@ -594,7 +581,7 @@ inline bool nest::iaf_psc_exp_ps_lossless::is_spike_(const double dt) //no-spike, NS_1, (V <= f_h,I_e(I) and V < g_h,I_e(I)) if((V_0 <= (((I_0 + P_.I_e_)*(V_.b1_ * exp_tau_m + V_.b2_* exp_tau_s) + V_.b3_*(exp_tau_m - exp_tau_s))/( V_.b4_ * exp_tau_s))) - && (V_0 < g)) + and (V_0 < g)) { return false; } @@ -605,17 +592,16 @@ inline bool nest::iaf_psc_exp_ps_lossless::is_spike_(const double dt) return true; } //no-spike, NS_2, V < b(I) - else if(V_0 < (V_.c1_ * P_.I_e_ + V_.c2_ * I_0 + V_.c3_* pow(I_0, V_.c4_) * pow((V_.c5_ - P_.I_e_), V_.c6_))) + else if(V_0 < (V_.c1_ * P_.I_e_ + V_.c2_ * I_0 + V_.c3_* std::pow(I_0, V_.c4_) * std::pow((V_.c5_ - P_.I_e_), V_.c6_))) { return false; } else //missed spike detected, S_2 { - V_.bisection_step = (V_.a1_ / P_.tau_m_ * P_.tau_ex_ ) * log ( V_.b1_ * I_0 / (V_.a2_ * P_.I_e_ - V_.a1_ * I_0 - V_.a4_ * V_0 ) ); + V_.bisection_step_ = (V_.a1_ / P_.tau_m_ * P_.tau_ex_ ) * std::log ( V_.b1_ * I_0 / (V_.a2_ * P_.I_e_ - V_.a1_ * I_0 - V_.a4_ * V_0 ) ); return true; } - } diff --git a/precise/iaf_psc_exp_ps_lossless.h b/precise/iaf_psc_exp_ps_lossless.h index 03039e25ea..d9df806059 100644 --- a/precise/iaf_psc_exp_ps_lossless.h +++ b/precise/iaf_psc_exp_ps_lossless.h @@ -24,19 +24,25 @@ #ifndef IAF_PSC_EXP_PS_LOSSLESS_H #define IAF_PSC_EXP_PS_LOSSLESS_H +// C++ includes: +#include + +// Generated includes: #include "config.h" +// Includes from nestkernel: #include "archiving_node.h" -#include "nest_types.h" +#include "connection.h" #include "event.h" +#include "nest_types.h" #include "ring_buffer.h" -#include "slice_ring_buffer.h" -#include "connection.h" #include "universal_data_logger.h" #include "stopwatch.h" #include "arraydatum.h" -#include +// Includes from precise: +#include "slice_ring_buffer.h" + /*Begin Documentation Name: iaf_psc_exp_ps_lossless - Leaky integrate-and-fire neuron @@ -71,8 +77,7 @@ meets the threshold. References: [1] J.Krishnan, P.G.L.Porta Mana, M.Helias, M.Diesmann, E.Di.Napoli -(2017) Perfect spike detection via time reversal, arXiv:1706.05702, -submitted to Front. Neuroinformatics. +(2017) Perfect spike detection via time reversal, arXiv:1706.05702. Author: Jeyashree Krishnan @@ -218,6 +223,17 @@ namespace nest */ double bisectioning_(const double dt) const; + /** + * Retrospective spike detection by state space analysis. + * The state space spanning the non-spiking region is bound by the following system of inequalities: + * threshold line V < \theta, envelope, V < b(I_e), line corresponding to the final timestep + * V < f(h, I) (or) linear approximation of the envelope, V < g(h, I_e). + * The state space spanning the spiking region is bound by the following system of inequalities: + * threshold line V < \theta, envelope, V > b(I_e) and line corresponding to the final timestep + * V > f(h, I) (or) linear approximation of the envelope, V < g(h, I_e). + * Propagate the neuron's state by dt. + * @returns time to emit spike. + */ bool is_spike_(const double); // ---------------------------------------------------------------- @@ -262,11 +278,9 @@ namespace nest double U_reset_; Parameters_(); //!< Sets default parameter values - // void calc_const_is_spike_(); void get(DictionaryDatum &) const; //!< Store current values in dictionary - double set(const DictionaryDatum &); //!< Set values from dicitonary - + double set(const DictionaryDatum &); //!< Set values from dicitonary }; // ---------------------------------------------------------------- @@ -331,29 +345,40 @@ namespace nest double I_syn_ex_before_; //!< y1_ at beginning of ministep double I_syn_in_before_; //!< y1_ at beginning of ministep double y2_before_; //!< y2_ at beginning of ministep - double bisection_step; // if missed spike is detected, calculate time to emit spike + double bisection_step_; //!< if missed spike is detected, calculate time to emit spike - // The following are variables that are precomputed for the _is_spike function. - // a1_, a2_, a3_, a4_ are constants that appear in the inequality V < g(h, I_e). + /** + * Pre-computed constants for inequality V < g(h, I_e) + */ + //@{ double a1_; double a2_; double a3_; double a4_; + //@} - // b1_, b2_, b3_, b4_, in the line corresponding to the final timestep, V < f(h, I). + /** + * Pre-computed constants for inequality V < f(h, I) + */ + //@{ double b1_; double b2_; double b3_; double b4_; + //@} - //c1_, c2_, c3_, c4_, c5_, c6_ in the envelope, V < b(I_e). + /** + * Pre-computed constants for inequality V < b(I_e) + */ + //@{ double c1_; double c2_; double c3_; double c4_; double c5_; double c6_; - }; + //@} + }; // Access functions for UniversalDataLogger ------------------------------- @@ -362,8 +387,6 @@ namespace nest double get_I_syn_() const { return S_.I_syn_ex_ + S_.I_syn_in_; } double get_I_syn_ex_() const { return S_.I_syn_ex_; } double get_I_syn_in_() const { return S_.I_syn_in_; } - - // ---------------------------------------------------------------- /** @@ -386,10 +409,8 @@ namespace nest inline port iaf_psc_exp_ps_lossless::send_test_event( Node& target, rport receptor_type, synindex, bool ) { - SpikeEvent e; - + SpikeEvent e; e.set_sender(*this); - //c.check_event(e); return target.handles_test_event( e, receptor_type ); } @@ -424,7 +445,6 @@ void iaf_psc_exp_ps_lossless::get_status(DictionaryDatum & d) const P_.get(d); S_.get(d, P_); (*d)[names::recordables] = recordablesMap_.get_list(); - } inline @@ -438,12 +458,8 @@ void iaf_psc_exp_ps_lossless::set_status(const DictionaryDatum & d) // if we get here, temporaries contain consistent set of properties P_ = ptmp; S_ = stmp; -} - - - +} } // namespace - #endif // IAF_PSC_EXP_PS_LOSSLESS_H diff --git a/precise/precisemodule.cpp b/precise/precisemodule.cpp index a65c93792e..7ba1972fc7 100644 --- a/precise/precisemodule.cpp +++ b/precise/precisemodule.cpp @@ -98,12 +98,12 @@ PreciseModule::init( SLIInterpreter* ) "iaf_psc_alpha_presc" ); kernel().model_manager.register_node_model< iaf_psc_exp_ps >( "iaf_psc_exp_ps" ); + kernel().model_manager.register_node_model< iaf_psc_exp_ps_lossless >( + "iaf_psc_exp_ps_lossless" ); kernel().model_manager.register_node_model< poisson_generator_ps >( "poisson_generator_ps" ); kernel().model_manager.register_node_model< parrot_neuron_ps >( "parrot_neuron_ps" ); - kernel().model_manager.register_node_model< iaf_psc_exp_ps_lossless >( - "iaf_psc_exp_ps_lossless" ); } // PreciseModule::init() From 5080ccd1e5dea58b5c05d962f9e93ebe9f273942 Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Tue, 5 Sep 2017 14:02:42 +0200 Subject: [PATCH 21/48] code formatting --- precise/iaf_psc_exp_ps_lossless.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index 4bb904a4ea..cf7010e7bf 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -328,7 +328,7 @@ void nest::iaf_psc_exp_ps_lossless::update(const Time & origin, double ev_weight; bool end_of_refract; - if ( not B_.events_.get_next_spike(T, true, ev_offset, ev_weight, end_of_refract) ) + if ( not 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 From e044cfed9ccc90ef89bf1941b0452b8cc6c612d3 Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Tue, 5 Sep 2017 14:17:04 +0200 Subject: [PATCH 22/48] handle get_next_spike --- precise/iaf_psc_alpha_canon.cpp | 4 ++-- precise/slice_ring_buffer.h | 32 +++++++++++++++++++++----------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/precise/iaf_psc_alpha_canon.cpp b/precise/iaf_psc_alpha_canon.cpp index c5300e4189..ea756ad3e9 100644 --- a/precise/iaf_psc_alpha_canon.cpp +++ b/precise/iaf_psc_alpha_canon.cpp @@ -325,7 +325,7 @@ nest::iaf_psc_alpha_canon::update( Time const& origin, double ev_weight; bool end_of_refract; - if ( !B_.events_.get_next_spike( T, ev_offset, ev_weight, end_of_refract ) ) + if ( !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. @@ -388,7 +388,7 @@ nest::iaf_psc_alpha_canon::update( Time const& origin, last_offset = ev_offset; } while ( - B_.events_.get_next_spike( T, ev_offset, ev_weight, end_of_refract ) ); + B_.events_.get_next_spike( T, true, ev_offset, ev_weight, end_of_refract ) ); // no events remaining, plain update step across remainder // of interval diff --git a/precise/slice_ring_buffer.h b/precise/slice_ring_buffer.h index 910c5af325..957275554f 100644 --- a/precise/slice_ring_buffer.h +++ b/precise/slice_ring_buffer.h @@ -98,6 +98,9 @@ class SliceRingBuffer * should never contain spikes with smaller * stamps. Spikes with larger stamps are * left in queue. + * @param accumulate_simultaneous If true, return summed weight + * of simultaneous input spikes, otherwise return + * one spike at a time. * @param ps_offset PS-sense offset of spike time * @param weight Spike weight * @param end_of_refract True if spike is pseudo-spike marking @@ -105,10 +108,9 @@ class SliceRingBuffer * @returns true if spike available, false otherwise * @note If return from refractoriness coincides with * a spike, return from refractoriness is returned. - * If several spikes coincide, the sum of their - * weights is returned a single spike. */ bool get_next_spike( const long req_stamp, + bool accumulate_simultaneous, double& ps_offset, double& weight, bool& end_of_refract ); @@ -180,12 +182,14 @@ SliceRingBuffer::add_refractory( const long stamp, const double ps_offset ) inline bool SliceRingBuffer::get_next_spike( const long req_stamp, + bool accumulate_simultaneous, double& ps_offset, double& weight, bool& end_of_refract ) { end_of_refract = false; if ( deliver_->empty() || refract_ <= deliver_->back() ) + { if ( refract_.stamp_ == req_stamp ) { // if relies on stamp_==long::max() if not refractory // return from refractoriness @@ -198,20 +202,26 @@ SliceRingBuffer::get_next_spike( const long req_stamp, return true; } else + { return false; + } + } else if ( deliver_->back().stamp_ == req_stamp ) { - // we have an event to deliver, register its offset + // we have an event to deliver ps_offset = deliver_->back().ps_offset_; + weight = deliver_->back().weight_; + deliver_->pop_back(); - // accumulate weights of all spikes with same stamp - // AND offset - weight = 0.0; // accumulate weights of all - while ( !deliver_->empty() && deliver_->back().ps_offset_ == ps_offset - && deliver_->back().stamp_ == req_stamp ) + if ( accumulate_simultaneous ) { - weight += deliver_->back().weight_; - deliver_->pop_back(); + // add weights of all spikes with same stamp and offset + while ( not deliver_->empty() and deliver_->back().ps_offset_ == ps_offset + and deliver_->back().stamp_ == req_stamp ) + { + weight += deliver_->back().weight_; + deliver_->pop_back(); + } } return true; @@ -240,7 +250,7 @@ inline bool SliceRingBuffer::SpikeInfo::operator<( const SpikeInfo& b ) const inline bool SliceRingBuffer::SpikeInfo::operator<=( const SpikeInfo& b ) const { - return !( *this > b ); + return not( *this > b ); } inline bool SliceRingBuffer::SpikeInfo::operator>( const SpikeInfo& b ) const From d1b53fe46108a8cba33a35ad0bcdbf7ad3ae6b6c Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Tue, 5 Sep 2017 14:50:23 +0200 Subject: [PATCH 23/48] handle next spike --- nestkernel/nest_names.cpp | 6 + nestkernel/nest_names.h | 6 + precise/iaf_psc_delta_canon.cpp | 63 +++++-- precise/iaf_psc_exp_ps.cpp | 77 ++++++-- precise/iaf_psc_exp_ps_lossless.cpp | 4 +- precise/slice_ring_buffer.h | 262 ---------------------------- 6 files changed, 131 insertions(+), 287 deletions(-) delete mode 100644 precise/slice_ring_buffer.h diff --git a/nestkernel/nest_names.cpp b/nestkernel/nest_names.cpp index 556841d334..399ab42ad6 100644 --- a/nestkernel/nest_names.cpp +++ b/nestkernel/nest_names.cpp @@ -364,6 +364,12 @@ const Name withweight( "withweight" ); const Name x( "x" ); const Name xs( "xs" ); +const Name y( "y" ); +const Name y_0( "y_0" ); +const Name y_1( "y_1" ); +const Name y1( "y1" ); +const Name y2( "y2" ); + const Name z( "z" ); const Name z_connected( "z_connected" ); } diff --git a/nestkernel/nest_names.h b/nestkernel/nest_names.h index 50b1d15417..d255b2e5d5 100644 --- a/nestkernel/nest_names.h +++ b/nestkernel/nest_names.h @@ -436,6 +436,12 @@ extern const Name x; //!< current scaling factor of the synaptic weight [0...1] extern const Name xs; //!< current scaling factor of the synaptic weight [0...1] //(property arrays) +extern const Name y; //!< Used by stdp_connection_facetshw_hom +extern const Name y_0; //!< Used by ac_generator +extern const Name y_1; //!< Used by ac_generator +extern const Name y1; //!< Used in iaf_psc_alpha_canon +extern const Name y2; //!< Used in iaf_psc_alpha_canon + extern const Name z; //!< Number of available synaptic elements per node extern const Name z_connected; //!< Number of connected synaptic elements //!< per node diff --git a/precise/iaf_psc_delta_canon.cpp b/precise/iaf_psc_delta_canon.cpp index 0d3881ea4a..39f3576b28 100644 --- a/precise/iaf_psc_delta_canon.cpp +++ b/precise/iaf_psc_delta_canon.cpp @@ -120,35 +120,53 @@ nest::iaf_psc_delta_canon::Parameters_::set( const DictionaryDatum& d ) updateValue< double >( d, names::I_e, I_e_ ); if ( updateValue< double >( d, names::V_th, U_th_ ) ) + { U_th_ -= E_L_; + } else + { U_th_ -= delta_EL; + } if ( updateValue< double >( d, names::V_min, U_min_ ) ) + { U_min_ -= E_L_; + } else + { U_min_ -= delta_EL; + } if ( updateValue< double >( d, names::V_reset, U_reset_ ) ) + { U_reset_ -= E_L_; + } else + { U_reset_ -= delta_EL; - + } if ( U_reset_ >= U_th_ ) + { throw BadProperty( "Reset potential must be smaller than threshold." ); - + } if ( U_reset_ < U_min_ ) + { throw BadProperty( "Reset potential must be greater equal minimum potential." ); - + } if ( c_m_ <= 0 ) + { throw BadProperty( "Capacitance must be strictly positive." ); + } if ( Time( Time::ms( t_ref_ ) ).get_steps() < 1 ) + { throw BadProperty( "Refractory time must be at least one time step." ); - + } if ( tau_m_ <= 0 ) + { throw BadProperty( "All time constants must be strictly positive." ); + } return delta_EL; } @@ -168,9 +186,13 @@ nest::iaf_psc_delta_canon::State_::set( const DictionaryDatum& d, double delta_EL ) { if ( updateValue< double >( d, names::V_m, U_ ) ) + { U_ -= p.E_L_; + } else + { U_ -= delta_EL; + } } nest::iaf_psc_delta_canon::Buffers_::Buffers_( iaf_psc_delta_canon& n ) @@ -259,7 +281,9 @@ iaf_psc_delta_canon::update( Time const& origin, // 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 @@ -282,9 +306,11 @@ iaf_psc_delta_canon::update( Time const& origin, // 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 ) { @@ -298,20 +324,23 @@ iaf_psc_delta_canon::update( Time const& origin, // 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 ( !B_.events_.get_next_spike( T, ev_offset, ev_weight, 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 ( !S_.is_refractory_ ) + if ( not S_.is_refractory_ ) { /* The following way of updating U_ is numerically more precise than the more natural approach @@ -330,7 +359,9 @@ iaf_psc_delta_canon::update( Time const& origin, 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 @@ -355,13 +386,15 @@ iaf_psc_delta_canon::update( Time const& origin, t = ev_offset; // normal spikes need to be accumulated - if ( !end_of_refract ) + 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 { @@ -376,7 +409,9 @@ iaf_psc_delta_canon::update( Time const& origin, // 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 @@ -408,18 +443,22 @@ iaf_psc_delta_canon::update( Time const& origin, // spike S_.U_ += ev_weight; if ( S_.U_ >= P_.U_th_ ) + { emit_instant_spike_( origin, lag, t ); + } - } while ( - B_.events_.get_next_spike( T, ev_offset, ev_weight, end_of_refract ) ); + } 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 ( !S_.is_refractory_ && t > 0 ) // not at end of step, do remainder + 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 @@ -434,8 +473,8 @@ iaf_psc_delta_canon::update( Time const& origin, void nest::iaf_psc_delta_canon::propagate_( const double dt ) { - assert( !S_.is_refractory_ ); // should not be called if neuron is - // refractory + assert( not S_.is_refractory_ ); // should not be called if neuron is + // refractory // see comment on regular update above const double expm1_dt = numerics::expm1( -dt / P_.tau_m_ ); diff --git a/precise/iaf_psc_exp_ps.cpp b/precise/iaf_psc_exp_ps.cpp index 5209f64af9..98aac5c6ba 100644 --- a/precise/iaf_psc_exp_ps.cpp +++ b/precise/iaf_psc_exp_ps.cpp @@ -135,35 +135,53 @@ nest::iaf_psc_exp_ps::Parameters_::set( const DictionaryDatum& d ) updateValue< double >( d, names::I_e, I_e_ ); if ( updateValue< double >( d, names::V_th, U_th_ ) ) + { U_th_ -= E_L_; + } else + { U_th_ -= delta_EL; + } if ( updateValue< double >( d, names::V_min, U_min_ ) ) + { U_min_ -= E_L_; + } else + { U_min_ -= delta_EL; + } if ( updateValue< double >( d, names::V_reset, U_reset_ ) ) + { U_reset_ -= E_L_; + } else + { U_reset_ -= delta_EL; - + } if ( U_reset_ >= U_th_ ) + { throw BadProperty( "Reset potential must be smaller than threshold." ); - + } if ( U_reset_ < U_min_ ) + { throw BadProperty( "Reset potential must be greater equal minimum potential." ); - + } if ( c_m_ <= 0 ) + { throw BadProperty( "Capacitance must be strictly positive." ); + } if ( Time( Time::ms( t_ref_ ) ).get_steps() < 1 ) + { throw BadProperty( "Refractory time must be at least one time step." ); - + } if ( tau_m_ <= 0 || tau_ex_ <= 0 || tau_in_ <= 0 ) + { throw BadProperty( "All time constants must be strictly positive." ); + } return delta_EL; } @@ -182,9 +200,13 @@ nest::iaf_psc_exp_ps::State_::set( const DictionaryDatum& d, double delta_EL ) { if ( updateValue< double >( d, names::V_m, y2_ ) ) + { y2_ -= p.E_L_; + } else + { y2_ -= delta_EL; + } } /* ---------------------------------------------------------------- @@ -269,16 +291,20 @@ nest::iaf_psc_exp_ps::update( const Time& origin, // 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_.y2_ >= P_.U_th_ ) + { emit_instant_spike_( origin, from, V_.h_ms_ * ( 1.0 - std::numeric_limits< double >::epsilon() ) ); + } for ( long lag = from; lag < to; ++lag ) { @@ -289,7 +315,9 @@ nest::iaf_psc_exp_ps::update( const Time& origin, // 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 approximation V_.y0_before_ = S_.y0_; @@ -302,14 +330,15 @@ nest::iaf_psc_exp_ps::update( const Time& origin, double ev_weight; bool end_of_refract; - if ( !B_.events_.get_next_spike( T, ev_offset, ev_weight, end_of_refract ) ) + if ( not B_.events_.get_next_spike( + T, false, 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_ ) + if ( not S_.is_refractory_ ) { S_.y2_ = V_.P20_ * ( P_.I_e_ + S_.y0_ ) + V_.P21_ex_ * S_.y1_ex_ + V_.P21_in_ * S_.y1_in_ + V_.expm1_tau_m_ * S_.y2_ + S_.y2_; @@ -328,7 +357,9 @@ nest::iaf_psc_exp_ps::update( const Time& origin, interval. */ if ( S_.y2_ >= P_.U_th_ ) + { emit_spike_( origin, lag, 0, V_.h_ms_ ); + } } else { @@ -351,17 +382,25 @@ nest::iaf_psc_exp_ps::update( const Time& origin, // this must be done before adding the input, since // interpolation requires continuity if ( S_.y2_ >= 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 + { + S_.is_refractory_ = false; + } // return from refractoriness else { if ( ev_weight >= 0.0 ) + { S_.y1_ex_ += ev_weight; // exc. spike input + } else - S_.y1_in_ += ev_weight; // inh. spike input + { + S_.y1_in_ += ev_weight; + } // inh. spike input } // store state @@ -369,8 +408,8 @@ nest::iaf_psc_exp_ps::update( const Time& origin, V_.y1_in_before_ = S_.y1_in_; V_.y2_before_ = S_.y2_; last_offset = ev_offset; - } while ( - B_.events_.get_next_spike( T, ev_offset, ev_weight, end_of_refract ) ); + } while ( B_.events_.get_next_spike( + T, false, ev_offset, ev_weight, end_of_refract ) ); // no events remaining, plain update step across remainder // of interval @@ -378,7 +417,9 @@ nest::iaf_psc_exp_ps::update( const Time& origin, { propagate_( last_offset ); if ( S_.y2_ >= P_.U_th_ ) + { emit_spike_( origin, lag, V_.h_ms_ - last_offset, last_offset ); + } } } // else @@ -438,10 +479,15 @@ nest::iaf_psc_exp_ps::handle( DataLoggingRequest& e ) void nest::iaf_psc_exp_ps::propagate_( const double dt ) { + if ( dt == 0 ) + { + return; // if two input spikes arrived simultaneously (#368) + } + const double expm1_tau_ex = numerics::expm1( -dt / P_.tau_ex_ ); const double expm1_tau_in = numerics::expm1( -dt / P_.tau_in_ ); - if ( !S_.is_refractory_ ) + if ( not S_.is_refractory_ ) { const double expm1_tau_m = numerics::expm1( -dt / P_.tau_m_ ); @@ -464,6 +510,11 @@ nest::iaf_psc_exp_ps::emit_spike_( const Time& origin, const double t0, const double dt ) { + // dt == 0 if two input spikes arrived simultaneously, + // but threshold cannot be crossed during empty interval, + // so emit_spike_() should not be called then (#368) + assert( dt > 0 ); + // we know that the potential is subthreshold at t0, super at t0+dt // compute spike time relative to beginning of step @@ -517,9 +568,13 @@ nest::iaf_psc_exp_ps::bisectioning_( const double dt ) const while ( fabs( P_.U_th_ - y2_root ) > 1e-14 ) { if ( y2_root > P_.U_th_ ) + { root -= dt / div; + } else + { root += dt / div; + } div *= 2.0; diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index cf7010e7bf..6427a7ae54 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -328,7 +328,7 @@ void nest::iaf_psc_exp_ps_lossless::update(const Time & origin, double ev_weight; bool end_of_refract; - if ( not B_.events_.get_next_spike(T, ev_offset, ev_weight, 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 @@ -412,7 +412,7 @@ void nest::iaf_psc_exp_ps_lossless::update(const Time & origin, V_.y2_before_ = S_.y2_; last_offset = ev_offset; } - while ( B_.events_.get_next_spike(T, ev_offset, ev_weight, + while ( B_.events_.get_next_spike(T, true, ev_offset, ev_weight, end_of_refract) ); // no events remaining, plain update step across remainder diff --git a/precise/slice_ring_buffer.h b/precise/slice_ring_buffer.h deleted file mode 100644 index 957275554f..0000000000 --- a/precise/slice_ring_buffer.h +++ /dev/null @@ -1,262 +0,0 @@ -/* - * slice_ring_buffer.h - * - * 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 . - * - */ - -#ifndef SLICE_RING_BUFFER_H -#define SLICE_RING_BUFFER_H - -// C++ includes: -#include -#include -#include -#include - -// Generated includes: -#include "config.h" - -// Includes from nestkernel: -#include "kernel_manager.h" -#include "nest_types.h" - -namespace nest -{ -/** - * Queue for all spikes arriving into a neuron. - * Spikes are stored unsorted on arrival, but are sorted when - * prepare_delivery() is called. They can then be retrieved - * one by one in correct temporal order. Coinciding spikes - * are combined into one, see get_next_spike(). - * - * Data is organized as follows: - * - The time of the next return from refractoriness is - * stored in a separate variable and checked explicitly; - * otherwise, we'd have to re-sort data during updating. - * - We have a pseudo-ring of Nbuff=ceil((min_del+max_del)/min_del) elements. - * Each element is a vector storing incoming spikes that - * are due during a given time slice. - * - * @note The following assumptions underlie the handling of - * pseudo-events for return from refractoriness: - * - There is at most one such event per time step (value of time stamp). - */ -class SliceRingBuffer -{ -public: - SliceRingBuffer(); - - /** - * Add spike to queue. - * @param rel_delivery relative delivery time - * @param stamp Delivery time - * @param ps_offset Precise timing offset of spike time - * @param weight Weight of spike. - */ - void add_spike( const delay rel_delivery, - const long stamp, - const double ps_offset, - const double weight ); - - /** - * Add refractory event to queue. - * The refractory event is actually stored as a pseudo-event. - * @param stamp Delivery time - * @param ps_offset Precise timing offset of spike time - */ - void add_refractory( const long stamp, const double ps_offset ); - - /** - * Prepare for spike delivery in current slice by sorting. - */ - void prepare_delivery(); - - /** - * Discard all events in current slice. - */ - void discard_events(); - - /** - * Return next spike. - * @param req_stamp Request spike with this stamp. Queue - * should never contain spikes with smaller - * stamps. Spikes with larger stamps are - * left in queue. - * @param accumulate_simultaneous If true, return summed weight - * of simultaneous input spikes, otherwise return - * one spike at a time. - * @param ps_offset PS-sense offset of spike time - * @param weight Spike weight - * @param end_of_refract True if spike is pseudo-spike marking - * end of refractory period - * @returns true if spike available, false otherwise - * @note If return from refractoriness coincides with - * a spike, return from refractoriness is returned. - */ - bool get_next_spike( const long req_stamp, - bool accumulate_simultaneous, - double& ps_offset, - double& weight, - bool& end_of_refract ); - - /** - * Clear buffer - */ - void clear(); - - /** - * Resize the buffer according to min_delay and max_delay. - */ - void resize(); - -private: - /** - * Information about spike. - */ - struct SpikeInfo - { - SpikeInfo( long stamp, double ps_offset, double weight ); - - bool operator<( const SpikeInfo& b ) const; - bool operator<=( const SpikeInfo& b ) const; - bool operator>( const SpikeInfo& b ) const; - - // data elements must not be const, since heap implementation - // in DEC STL uses operator=(). - long stamp_; // > queue_; - - //! slot to deliver from - std::vector< SpikeInfo >* deliver_; - - SpikeInfo refract_; //!< pseudo-event for return from refractoriness -}; - -inline void -SliceRingBuffer::add_spike( const delay rel_delivery, - const long stamp, - const double ps_offset, - const double weight ) -{ - const delay idx = - kernel().event_delivery_manager.get_slice_modulo( rel_delivery ); - assert( ( size_t ) idx < queue_.size() ); - assert( ps_offset >= 0 ); - - queue_[ idx ].push_back( SpikeInfo( stamp, ps_offset, weight ) ); -} - -inline void -SliceRingBuffer::add_refractory( const long stamp, const double ps_offset ) -{ - // We require that only one refractory-return pseudo-event is stored per - // time step. We guard against violation using assert(): refract_.stamp_ must - // be equal to the marker value for non-refractoriness. All else would mean - // that a refractory neuron fired. - assert( refract_.stamp_ == std::numeric_limits< long >::max() ); - - refract_.stamp_ = stamp; - refract_.ps_offset_ = ps_offset; -} - -inline bool -SliceRingBuffer::get_next_spike( const long req_stamp, - bool accumulate_simultaneous, - double& ps_offset, - double& weight, - bool& end_of_refract ) -{ - end_of_refract = false; - if ( deliver_->empty() || refract_ <= deliver_->back() ) - { - if ( refract_.stamp_ == req_stamp ) - { // if relies on stamp_==long::max() if not refractory - // return from refractoriness - ps_offset = refract_.ps_offset_; - weight = 0; - end_of_refract = true; - - // mark as non-refractory - refract_.stamp_ = std::numeric_limits< long >::max(); - return true; - } - else - { - return false; - } - } - else if ( deliver_->back().stamp_ == req_stamp ) - { - // we have an event to deliver - ps_offset = deliver_->back().ps_offset_; - weight = deliver_->back().weight_; - deliver_->pop_back(); - - if ( accumulate_simultaneous ) - { - // add weights of all spikes with same stamp and offset - while ( not deliver_->empty() and deliver_->back().ps_offset_ == ps_offset - and deliver_->back().stamp_ == req_stamp ) - { - weight += deliver_->back().weight_; - deliver_->pop_back(); - } - } - - return true; - } - else - { - // ensure that we are not blocked by spike from the past, cf #404 - assert( deliver_->back().stamp_ > req_stamp ); - return false; - } -} - -inline SliceRingBuffer::SpikeInfo::SpikeInfo( long stamp, - double ps_offset, - double weight ) - : stamp_( stamp ) - , ps_offset_( ps_offset ) - , weight_( weight ) -{ -} - -inline bool SliceRingBuffer::SpikeInfo::operator<( const SpikeInfo& b ) const -{ - return stamp_ == b.stamp_ ? ps_offset_ > b.ps_offset_ : stamp_ < b.stamp_; -} - -inline bool SliceRingBuffer::SpikeInfo::operator<=( const SpikeInfo& b ) const -{ - return not( *this > b ); -} - -inline bool SliceRingBuffer::SpikeInfo::operator>( const SpikeInfo& b ) const -{ - return stamp_ == b.stamp_ ? ps_offset_ < b.ps_offset_ : stamp_ > b.stamp_; -} -} - -#endif From 3ae028fff2ba4dd4928320ef9b1f2116cd15bb58 Mon Sep 17 00:00:00 2001 From: JKrishnan Date: Wed, 6 Sep 2017 10:52:39 +0200 Subject: [PATCH 24/48] resolve conflicts --- precise/iaf_psc_alpha_canon.cpp | 658 -------------------------------- precise/iaf_psc_alpha_canon.h | 520 ------------------------- 2 files changed, 1178 deletions(-) delete mode 100644 precise/iaf_psc_alpha_canon.cpp delete mode 100644 precise/iaf_psc_alpha_canon.h diff --git a/precise/iaf_psc_alpha_canon.cpp b/precise/iaf_psc_alpha_canon.cpp deleted file mode 100644 index ea756ad3e9..0000000000 --- a/precise/iaf_psc_alpha_canon.cpp +++ /dev/null @@ -1,658 +0,0 @@ -/* - * iaf_psc_alpha_canon.cpp - * - * 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 . - * - */ - -#include "iaf_psc_alpha_canon.h" - -// C++ includes: -#include - -// Includes from libnestutil: -#include "numerics.h" -#include "propagator_stability.h" - -// Includes from nestkernel: -#include "exceptions.h" -#include "kernel_manager.h" -#include "universal_data_logger_impl.h" - -// Includes from sli: -#include "dict.h" -#include "dictutils.h" -#include "doubledatum.h" -#include "integerdatum.h" - -/* ---------------------------------------------------------------- - * Recordables map - * ---------------------------------------------------------------- */ - -nest::RecordablesMap< nest::iaf_psc_alpha_canon > - nest::iaf_psc_alpha_canon::recordablesMap_; - -namespace nest -{ -/* - * Override the create() method with one call to RecordablesMap::insert_() - * for each quantity to be recorded. - */ -template <> -void -RecordablesMap< iaf_psc_alpha_canon >::create() -{ - // use standard names wherever you can for consistency! - insert_( names::V_m, &iaf_psc_alpha_canon::get_V_m_ ); -} -} - -/* ---------------------------------------------------------------- - * Default constructors defining default parameters and state - * ---------------------------------------------------------------- */ - -nest::iaf_psc_alpha_canon::Parameters_::Parameters_() - : tau_m_( 10.0 ) // ms - , tau_syn_( 2.0 ) // ms - , c_m_( 250.0 ) // pF - , t_ref_( 2.0 ) // ms - , E_L_( -70.0 ) // mV - , I_e_( 0.0 ) // pA - , U_th_( -55.0 - E_L_ ) // mV, rel to E_L_ - , U_min_( -std::numeric_limits< double >::infinity() ) // mV - , U_reset_( -70.0 - E_L_ ) // mV, rel to E_L_ - , Interpol_( iaf_psc_alpha_canon::LINEAR ) -{ -} - -nest::iaf_psc_alpha_canon::State_::State_() - : y0_( 0.0 ) - , y1_( 0.0 ) - , y2_( 0.0 ) - , y3_( 0.0 ) - , is_refractory_( false ) - , last_spike_step_( -1 ) - , last_spike_offset_( 0.0 ) -{ -} - -/* ---------------------------------------------------------------- - * Parameter and state extractions and manipulation functions - * ---------------------------------------------------------------- */ - -void -nest::iaf_psc_alpha_canon::Parameters_::get( DictionaryDatum& d ) const -{ - def< double >( d, names::E_L, E_L_ ); - def< double >( d, names::I_e, I_e_ ); - def< double >( d, names::V_th, U_th_ + E_L_ ); - def< double >( d, names::V_min, U_min_ + E_L_ ); - def< double >( d, names::V_reset, U_reset_ + E_L_ ); - def< double >( d, names::C_m, c_m_ ); - def< double >( d, names::tau_m, tau_m_ ); - def< double >( d, names::tau_syn, tau_syn_ ); - def< double >( d, names::t_ref, t_ref_ ); - def< long >( d, names::Interpol_Order, Interpol_ ); -} - -double -nest::iaf_psc_alpha_canon::Parameters_::set( const DictionaryDatum& d ) -{ - // if E_L_ is changed, we need to adjust all variables defined relative to - // E_L_ - const double ELold = E_L_; - updateValue< double >( d, names::E_L, E_L_ ); - const double delta_EL = E_L_ - ELold; - - updateValue< double >( d, names::tau_m, tau_m_ ); - updateValue< double >( d, names::tau_syn, tau_syn_ ); - updateValue< double >( d, names::C_m, c_m_ ); - updateValue< double >( d, names::t_ref, t_ref_ ); - updateValue< double >( d, names::I_e, I_e_ ); - - if ( updateValue< double >( d, names::V_th, U_th_ ) ) - U_th_ -= E_L_; - else - U_th_ -= delta_EL; - - if ( updateValue< double >( d, names::V_min, U_min_ ) ) - U_min_ -= E_L_; - else - U_min_ -= delta_EL; - - if ( updateValue< double >( d, names::V_reset, U_reset_ ) ) - U_reset_ -= E_L_; - else - U_reset_ -= delta_EL; - - long tmp; - if ( updateValue< long >( d, names::Interpol_Order, tmp ) ) - { - if ( NO_INTERPOL <= tmp && tmp < END_INTERP_ORDER ) - Interpol_ = static_cast< interpOrder >( tmp ); - else - throw BadProperty( - "Invalid interpolation order. " - "Valid orders are 0, 1, 2, 3." ); - } - - if ( U_reset_ >= U_th_ ) - throw BadProperty( "Reset potential must be smaller than threshold." ); - - if ( U_reset_ < U_min_ ) - throw BadProperty( - "Reset potential must be greater equal minimum potential." ); - - if ( c_m_ <= 0 ) - throw BadProperty( "Capacitance must be strictly positive." ); - - if ( Time( Time::ms( t_ref_ ) ).get_steps() < 1 ) - throw BadProperty( "Refractory time must be at least one time step." ); - - if ( tau_m_ <= 0 || tau_syn_ <= 0 ) - throw BadProperty( "All time constants must be strictly positive." ); - - return delta_EL; -} - -void -nest::iaf_psc_alpha_canon::State_::get( DictionaryDatum& d, - const Parameters_& p ) const -{ - def< double >( d, names::V_m, y3_ + p.E_L_ ); // Membrane potential - def< double >( d, "y1", y1_ ); // y1 state - def< double >( d, "y2", y2_ ); // y2 state - def< bool >( d, names::is_refractory, is_refractory_ ); -} - -void -nest::iaf_psc_alpha_canon::State_::set( const DictionaryDatum& d, - const Parameters_& p, - double delta_EL ) -{ - if ( updateValue< double >( d, names::V_m, y3_ ) ) - y3_ -= p.E_L_; - else - y3_ -= delta_EL; - - updateValue< double >( d, "y1", y1_ ); - updateValue< double >( d, "y2", y2_ ); -} - -nest::iaf_psc_alpha_canon::Buffers_::Buffers_( iaf_psc_alpha_canon& n ) - : logger_( n ) -{ -} - -nest::iaf_psc_alpha_canon::Buffers_::Buffers_( const Buffers_&, - iaf_psc_alpha_canon& n ) - : logger_( n ) -{ -} - - -/* ---------------------------------------------------------------- - * Default and copy constructor for node - * ---------------------------------------------------------------- */ - -nest::iaf_psc_alpha_canon::iaf_psc_alpha_canon() - : Archiving_Node() - , P_() - , S_() - , B_( *this ) -{ - recordablesMap_.create(); -} - -nest::iaf_psc_alpha_canon::iaf_psc_alpha_canon( const iaf_psc_alpha_canon& n ) - : Archiving_Node( n ) - , P_( n.P_ ) - , S_( n.S_ ) - , B_( n.B_, *this ) -{ -} - -/* ---------------------------------------------------------------- - * Node initialization functions - * ---------------------------------------------------------------- */ - -void -nest::iaf_psc_alpha_canon::init_state_( const Node& proto ) -{ - const iaf_psc_alpha_canon& pr = downcast< iaf_psc_alpha_canon >( proto ); - S_ = pr.S_; -} - -void -nest::iaf_psc_alpha_canon::init_buffers_() -{ - B_.events_.resize(); - B_.events_.clear(); - B_.currents_.clear(); // includes resize - B_.logger_.reset(); - - Archiving_Node::clear_history(); -} - -void -nest::iaf_psc_alpha_canon::calibrate() -{ - B_.logger_.init(); - - V_.h_ms_ = Time::get_resolution().get_ms(); - - V_.PSCInitialValue_ = 1.0 * numerics::e / P_.tau_syn_; - - V_.gamma_ = 1 / P_.c_m_ / ( 1 / P_.tau_syn_ - 1 / P_.tau_m_ ); - V_.gamma_sq_ = 1 / P_.c_m_ / ( ( 1 / P_.tau_syn_ - 1 / P_.tau_m_ ) - * ( 1 / P_.tau_syn_ - 1 / P_.tau_m_ ) ); - - // pre-compute matrix for full time step - V_.expm1_tau_m_ = numerics::expm1( -V_.h_ms_ / P_.tau_m_ ); - V_.expm1_tau_syn_ = numerics::expm1( -V_.h_ms_ / P_.tau_syn_ ); - V_.P30_ = -P_.tau_m_ / P_.c_m_ * V_.expm1_tau_m_; - // these are determined according to a numeric stability criterion - V_.P31_ = propagator_31( P_.tau_syn_, P_.tau_m_, P_.c_m_, V_.h_ms_ ); - V_.P32_ = propagator_32( P_.tau_syn_, P_.tau_m_, P_.c_m_, V_.h_ms_ ); - - // t_ref_ is the refractory period in ms - // refractory_steps_ is the duration of the refractory period in whole - // steps, rounded down - V_.refractory_steps_ = Time( Time::ms( P_.t_ref_ ) ).get_steps(); - // since t_ref_ >= sim step size, this can only fail in error - assert( V_.refractory_steps_ >= 1 ); -} - -/* ---------------------------------------------------------------- - * Update and spike handling functions - * ---------------------------------------------------------------- */ - -void -nest::iaf_psc_alpha_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(); - - /* 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 >::epsilon() ) ); - - for ( long lag = from; lag < to; ++lag ) - { - // time at start of update step - const long 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 ev_offset; - double ev_weight; - bool end_of_refract; - - if ( !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 ( !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 last_offset = V_.h_ms_; // start of step - - do - { - // time is measured backward: inverse order in difference - const double 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, true, 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 ... -} - - -// function handles exact spike times -void -nest::iaf_psc_alpha_canon::handle( SpikeEvent& e ) -{ - assert( e.get_delay() > 0 ); - - /* We need to compute the absolute time stamp of the delivery time - of the spike, since spikes might spend longer than min_delay_ - in the queue. The time is computed according to Time Memo, Rule 3. - */ - const long Tdeliver = e.get_stamp().get_steps() + e.get_delay() - 1; - B_.events_.add_spike( - e.get_rel_delivery_steps( - nest::kernel().simulation_manager.get_slice_origin() ), - Tdeliver, - e.get_offset(), - e.get_weight() * e.get_multiplicity() ); -} - -void -nest::iaf_psc_alpha_canon::handle( CurrentEvent& e ) -{ - assert( e.get_delay() > 0 ); - - const double c = e.get_current(); - const double w = e.get_weight(); - - // add weighted current; HEP 2002-10-04 - B_.currents_.add_value( - e.get_rel_delivery_steps( - nest::kernel().simulation_manager.get_slice_origin() ), - w * c ); -} - -void -nest::iaf_psc_alpha_canon::handle( DataLoggingRequest& e ) -{ - B_.logger_.handle( e ); -} - -// auxiliary functions --------------------------------------------- - -void -nest::iaf_psc_alpha_canon::propagate_( const double dt ) -{ - // needed in any case - const double ps_e_TauSyn = numerics::expm1( -dt / P_.tau_syn_ ); - - // y3_ remains unchanged at 0.0 while neuron is refractory - if ( !S_.is_refractory_ ) - { - const double ps_e_Tau = numerics::expm1( -dt / P_.tau_m_ ); - const double ps_P30 = -P_.tau_m_ / P_.c_m_ * ps_e_Tau; - const double ps_P31 = V_.gamma_sq_ * ps_e_Tau - V_.gamma_sq_ * ps_e_TauSyn - - dt * V_.gamma_ * ps_e_TauSyn - dt * V_.gamma_; - const double ps_P32 = V_.gamma_ * ps_e_Tau - V_.gamma_ * ps_e_TauSyn; - S_.y3_ = ps_P30 * ( P_.I_e_ + S_.y0_ ) + ps_P31 * S_.y1_ + ps_P32 * S_.y2_ - + ps_e_Tau * S_.y3_ + S_.y3_; - - // lower bound of membrane potential - S_.y3_ = ( S_.y3_ < P_.U_min_ ? P_.U_min_ : S_.y3_ ); - } - - // now the synaptic components - S_.y2_ = - ps_e_TauSyn * dt * S_.y1_ + ps_e_TauSyn * S_.y2_ + dt * S_.y1_ + S_.y2_; - S_.y1_ = ps_e_TauSyn * S_.y1_ + S_.y1_; - - return; -} - -void -nest::iaf_psc_alpha_canon::emit_spike_( Time const& origin, - const long lag, - const double t0, - const double dt ) -{ - // we know that the potential is subthreshold at t0, super at t0+dt - - // compute spike time relative to beginning of step - S_.last_spike_step_ = origin.get_steps() + lag + 1; - S_.last_spike_offset_ = V_.h_ms_ - ( t0 + thresh_find_( dt ) ); - - // reset neuron and make it refractory - S_.y3_ = P_.U_reset_; - S_.is_refractory_ = true; - - // send spike - set_spiketime( Time::step( S_.last_spike_step_ ), S_.last_spike_offset_ ); - SpikeEvent se; - se.set_offset( S_.last_spike_offset_ ); - kernel().event_delivery_manager.send( *this, se, lag ); - - return; -} - -void -nest::iaf_psc_alpha_canon::emit_instant_spike_( Time const& origin, - const long lag, - const double spike_offs ) -{ - assert( S_.y3_ >= P_.U_th_ ); // ensure we are superthreshold - - // set stamp and offset for spike - S_.last_spike_step_ = origin.get_steps() + lag + 1; - S_.last_spike_offset_ = spike_offs; - - // reset neuron and make it refractory - S_.y3_ = P_.U_reset_; - S_.is_refractory_ = true; - - // send spike - set_spiketime( Time::step( S_.last_spike_step_ ), S_.last_spike_offset_ ); - SpikeEvent se; - se.set_offset( S_.last_spike_offset_ ); - kernel().event_delivery_manager.send( *this, se, lag ); - - return; -} - -// finds threshpassing -inline double -nest::iaf_psc_alpha_canon::thresh_find_( double const dt ) const -{ - switch ( P_.Interpol_ ) - { - case NO_INTERPOL: - return dt; - case LINEAR: - return thresh_find1_( dt ); - case QUADRATIC: - return thresh_find2_( dt ); - case CUBIC: - return thresh_find3_( dt ); - default: - throw BadProperty( "Invalid interpolation order in iaf_psc_alpha_canon." ); - } - return 0; -} - -// finds threshpassing via linear interpolation -double -nest::iaf_psc_alpha_canon::thresh_find1_( double const dt ) const -{ - double tau = ( P_.U_th_ - V_.y3_before_ ) * dt / ( S_.y3_ - V_.y3_before_ ); - return tau; -} - -// finds threshpassing via quadratic interpolation -double -nest::iaf_psc_alpha_canon::thresh_find2_( double const dt ) const -{ - const double h_sq = dt * dt; - const double derivative = -V_.y3_before_ / P_.tau_m_ - + ( P_.I_e_ + V_.y0_before_ + V_.y2_before_ ) / P_.c_m_; - - const double a = - ( -V_.y3_before_ / h_sq ) + ( S_.y3_ / h_sq ) - ( derivative / dt ); - const double b = derivative; - const double c = V_.y3_before_; - - const double sqr_ = std::sqrt( b * b - 4 * a * c + 4 * a * P_.U_th_ ); - const double tau1 = ( -b + sqr_ ) / ( 2 * a ); - const double tau2 = ( -b - sqr_ ) / ( 2 * a ); - - if ( tau1 >= 0 ) - return tau1; - else if ( tau2 >= 0 ) - return tau2; - else - return thresh_find1_( dt ); -} - -double -nest::iaf_psc_alpha_canon::thresh_find3_( double const dt ) const -{ - const double h_ms = dt; - const double h_sq = h_ms * h_ms; - const double h_cb = h_sq * h_ms; - - const double deriv_t1 = -V_.y3_before_ / P_.tau_m_ - + ( P_.I_e_ + V_.y0_before_ + V_.y2_before_ ) / P_.c_m_; - const double deriv_t2 = - -S_.y3_ / P_.tau_m_ + ( P_.I_e_ + S_.y0_ + S_.y2_ ) / P_.c_m_; - - const double w3_ = ( 2 * V_.y3_before_ / h_cb ) - ( 2 * S_.y3_ / h_cb ) - + ( deriv_t1 / h_sq ) + ( deriv_t2 / h_sq ); - const double w2_ = -( 3 * V_.y3_before_ / h_sq ) + ( 3 * S_.y3_ / h_sq ) - - ( 2 * deriv_t1 / h_ms ) - ( deriv_t2 / h_ms ); - const double w1_ = deriv_t1; - const double w0_ = V_.y3_before_; - - // normal form : x^3 + r*x^2 + s*x + t with coefficients : r, s, t - const double r = w2_ / w3_; - const double s = w1_ / w3_; - const double t = ( w0_ - P_.U_th_ ) / w3_; - const double r_sq = r * r; - - // substitution y = x + r/3 : y^3 + p*y + q == 0 - const double p = -r_sq / 3 + s; - const double q = 2 * ( r_sq * r ) / 27 - r * s / 3 + t; - - // discriminante - const double D = std::pow( ( p / 3 ), 3 ) + std::pow( ( q / 2 ), 2 ); - - double tau1; - double tau2; - double tau3; - - if ( D < 0 ) - { - const double roh = std::sqrt( -( p * p * p ) / 27 ); - const double phi = std::acos( -q / ( 2 * roh ) ); - const double a = 2 * std::pow( roh, ( 1.0 / 3.0 ) ); - tau1 = ( a * std::cos( phi / 3 ) ) - r / 3; - tau2 = ( a * std::cos( phi / 3 + 2 * numerics::pi / 3 ) ) - r / 3; - tau3 = ( a * std::cos( phi / 3 + 4 * numerics::pi / 3 ) ) - r / 3; - } - else - { - const double sgnq = ( q >= 0 ? 1 : -1 ); - const double u = - -sgnq * std::pow( std::fabs( q ) / 2.0 + std::sqrt( D ), 1.0 / 3.0 ); - const double v = -p / ( 3 * u ); - tau1 = ( u + v ) - r / 3; - if ( tau1 >= 0 ) - { - return tau1; - } - else - { - return thresh_find2_( dt ); - } - } - - // set tau to the smallest root above 0 - - double tau = ( tau1 >= 0 ) ? tau1 : 2 * h_ms; - if ( ( tau2 >= 0 ) && ( tau2 < tau ) ) - tau = tau2; - if ( ( tau3 >= 0 ) && ( tau3 < tau ) ) - tau = tau3; - return ( tau <= V_.h_ms_ ) ? tau : thresh_find2_( dt ); -} diff --git a/precise/iaf_psc_alpha_canon.h b/precise/iaf_psc_alpha_canon.h deleted file mode 100644 index 85b14131f7..0000000000 --- a/precise/iaf_psc_alpha_canon.h +++ /dev/null @@ -1,520 +0,0 @@ -/* - * iaf_psc_alpha_canon.h - * - * 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 . - * - */ - -#ifndef IAF_PSC_ALPHA_CANON_H -#define IAF_PSC_ALPHA_CANON_H - -// C++ includes: -#include - -// Generated includes: -#include "config.h" - -// Includes from nestkernel: -#include "archiving_node.h" -#include "connection.h" -#include "event.h" -#include "nest_types.h" -#include "ring_buffer.h" -#include "universal_data_logger.h" - -// Includes from precise: -#include "slice_ring_buffer.h" - -/*BeginDocumentation -Name: iaf_psc_alpha_canon - Leaky integrate-and-fire neuron -with alpha-shape postsynaptic currents; canoncial implementation. - -Description: -iaf_psc_alpha_canon is the "canonical" implementatoin of the leaky -integrate-and-fire model neuron with alpha-shaped postsynaptic -currents in the sense of [1]. This is the most exact implementation -available. - -PSCs are normalized to an amplitude of 1pA. - -The canonical implementation handles neuronal dynamics in a locally -event-based manner with in coarse time grid defined by the minimum -delay in the network, see [1]. Incoming spikes are applied at the -precise moment of their arrival, while the precise time of outgoing -spikes is determined by interpolation once a threshold crossing has -been detected. Return from refractoriness occurs precisly at spike -time plus refractory period. - -This implementation is more complex than the plain iaf_psc_alpha -neuron, but achieves much higher precision. In particular, it does not -suffer any binning of spike times to grid points. Depending on your -application, the canonical application may provide superior overall -performance given an accuracy goal; see [1] for details. Subthreshold -dynamics are integrated using exact integration between events [2]. - -Remarks: -The iaf_psc_delta_canon neuron does not accept CurrentEvent connections. -This is because the present method for transmitting CurrentEvents in -NEST (sending the current to be applied) is not compatible with off-grid -currents, if more than one CurrentEvent-connection exists. Once CurrentEvents -are changed to transmit change-of-current-strength, this problem will -disappear and the canonical neuron will also be able to handle CurrentEvents. -For now, the only way to inject a current is the built-in current I_e. - -Please note that this node is capable of sending precise spike times -to target nodes (on-grid spike time plus offset). If this node is -connected to a spike_detector, the property "precise_times" of the -spike_detector has to be set to true in order to record the offsets -in addition to the on-grid spike times. - -A further improvement of precise simulation is implemented in iaf_psc_exp_ps -based on [3]. - -Parameters: -The following parameters can be set in the status dictionary. - - V_m double - Membrane potential in mV - E_L double - Resting membrane potential in mV. - V_min double - Absolute lower value for the membrane potential. - C_m double - Capacity of the membrane in pF - tau_m double - Membrane time constant in ms. - t_ref double - Duration of refractory period in ms. - V_th double - Spike threshold in mV. - V_reset double - Reset potential of the membrane in mV. - tau_syn double - Rise time of the synaptic alpha function in ms. - I_e double - Constant external input current in pA. - Interpol_Order int - Interpolation order for spike time: - 0-none, 1-linear, 2-quadratic, 3-cubic - -Remarks: -If tau_m is very close to tau_syn_ex or tau_syn_in, the model -will numerically behave as if tau_m is equal to tau_syn_ex or -tau_syn_in, respectively, to avoid numerical instabilities. -For details, please see IAF_Neruons_Singularity.ipynb in -the NEST source code (docs/model_details). - -References: -[1] Morrison A, Straube S, Plesser H E, & Diesmann M (2006) Exact Subthreshold - Integration with Continuous Spike Times in Discrete Time Neural Network - Simulations. To appear in Neural Computation. -[2] Rotter S & Diesmann M (1999) Exact simulation of time-invariant linear - systems with applications to neuronal modeling. Biologial Cybernetics - 81:381-402. -[3] Hanuschkin A, Kunkel S, Helias M, Morrison A & Diesmann M (2010) - A general and efficient method for incorporating exact spike times in - globally time-driven simulations Front Neuroinformatics, 4:113 - -Author: Diesmann, Eppler, Morrison, Plesser, Straube - -Sends: SpikeEvent - -Receives: SpikeEvent, CurrentEvent, DataLoggingRequest - -SeeAlso: iaf_psc_alpha, iaf_psc_alpha_presc, iaf_psc_exp_ps - -*/ - -namespace nest -{ - -/** - * Leaky iaf neuron, alpha PSC synapses, canonical implementation. - * @note Inherit privately from Node, so no classes can be derived - * from this one. - * @todo Implement current input in consistent way. - */ -class iaf_psc_alpha_canon : public Archiving_Node -{ -public: - /** Basic constructor. - This constructor should only be used by GenericModel to create - model prototype instances. - */ - iaf_psc_alpha_canon(); - - /** Copy constructor. - GenericModel::allocate_() uses the copy constructor to clone - actual model instances from the prototype instance. - - @note The copy constructor MUST NOT be used to create nodes based - on nodes that have been placed in the network. - */ - iaf_psc_alpha_canon( const iaf_psc_alpha_canon& ); - - /** - * Import sets of overloaded virtual functions. - * @see Technical Issues / Virtual Functions: Overriding, Overloading, and - * Hiding - */ - using Node::handle; - using Node::handles_test_event; - - port send_test_event( Node&, rport, synindex, bool ); - - port handles_test_event( SpikeEvent&, rport ); - port handles_test_event( CurrentEvent&, rport ); - port handles_test_event( DataLoggingRequest&, rport ); - - void handle( SpikeEvent& ); - void handle( CurrentEvent& ); - void handle( DataLoggingRequest& ); - - bool - is_off_grid() const - { - return true; - } // uses off_grid events - - void get_status( DictionaryDatum& ) const; - void set_status( const DictionaryDatum& ); - -private: - /** @name Interface functions - * @note These functions are private, so that they can be accessed - * only through a Node*. - */ - //@{ - void init_state_( const Node& proto ); - void init_buffers_(); - void calibrate(); - - /** - * Time Evolution Operator. - * - * update() promotes the state of the neuron from origin+from to origin+to. - * It does so in steps of the resolution h. Within each step, time is - * advanced from event to event, as retrieved from the spike queue. - * - * Return from refractoriness is handled as a special event in the - * queue, which is marked by a weight that is GSL_NAN. This greatly - * simplifies the code. - * - * For steps, during which no events occur, the precomputed propagator matrix - * is used. For other steps, the propagator matrix is computed as needed. - * - * While the neuron is refractory, membrane potential (y3_) is - * clamped to U_reset_. - */ - void update( Time const& origin, const long from, const long to ); - - //@} - - /** - * Propagate neuron state. - * Propagate the neuron's state by dt. - * @param dt Interval over which to propagate - */ - void propagate_( const double dt ); - - /** - * Trigger interpolation method to find the precise spike time - * within the mini-timestep (t0,t0+dt] assuming that the membrane - * potential was below threshold at t0 and above at t0+dt. Emit - * the spike and reset the neuron. - * - * @param origin Time stamp at beginning of slice - * @param lag Time step within slice - * @param t0 Beginning of mini-timestep - * @param dt Duration of mini-timestep - */ - void emit_spike_( Time const& origin, - const long lag, - const double t0, - const double dt ); - - /** - * Instantaneously emit a spike at the precise time defined by - * origin, lag and spike_offset and reset the neuron. - * - * @param origin Time stamp at beginning of slice - * @param lag Time step within slice - * @param spike_offset Time offset for spike - */ - void emit_instant_spike_( Time const& origin, - const long lag, - const double spike_offset ); - - /** @name Threshold-crossing interpolation - * These functions determine the time of threshold crossing using - * interpolation, one function per interpolation - * order. thresh_find() is the driver function and the only one to - * be called directly. - */ - //@{ - - /** Interpolation orders. */ - enum interpOrder - { - NO_INTERPOL, - LINEAR, - QUADRATIC, - CUBIC, - END_INTERP_ORDER - }; - - /** - * Localize threshold crossing. - * Driver function to invoke the correct interpolation function - * for the chosen interpolation order. - * @param double length of interval since previous event - * @returns time from previous event to threshold crossing - */ - double thresh_find_( double const ) const; - double thresh_find1_( double const ) const; - double thresh_find2_( double const ) const; - double thresh_find3_( double const ) const; - //@} - - - // The next two classes need to be friends to access the State_ class/member - friend class RecordablesMap< iaf_psc_alpha_canon >; - friend class UniversalDataLogger< iaf_psc_alpha_canon >; - - // ---------------------------------------------------------------- - - /** - * Independent parameters of the model. - */ - struct Parameters_ - { - - /** Membrane time constant in ms. */ - double tau_m_; - - /** Time constant of synaptic current in ms. */ - double tau_syn_; - - /** Membrane capacitance in pF. */ - double c_m_; - - /** Refractory period in ms. */ - double t_ref_; - - /** Resting potential in mV. */ - double E_L_; - - /** External DC current [pA] */ - double I_e_; - - /** Threshold, RELATIVE TO RESTING POTENTAIL(!). - I.e. the real threshold is U_th_ + E_L_. */ - double U_th_; - - /** Lower bound, RELATIVE TO RESTING POTENTAIL(!). - I.e. the real lower bound is U_min_+E_L_. */ - double U_min_; - - /** Reset potential. - At threshold crossing, the membrane potential is reset to this - value. Relative to resting potential. - */ - double U_reset_; - - /** Interpolation order */ - interpOrder Interpol_; - - Parameters_(); //!< Sets default parameter values - - void get( DictionaryDatum& ) const; //!< Store current values in dictionary - - /** Set values from dictionary. - * @returns Change in reversal potential E_L, to be passed to State_::set() - */ - double set( const DictionaryDatum& ); - }; - - // ---------------------------------------------------------------- - - /** - * State variables of the model. - */ - struct State_ - { - double y0_; //!< external input current - double y1_; //!< alpha current, first component - double y2_; //!< alpha current, second component - double y3_; //!< Membrane pot. rel. to resting pot. E_L_. - bool is_refractory_; //!< true while refractory - long last_spike_step_; //!< time stamp of most recent spike - double last_spike_offset_; //!< offset of most recent spike - - State_(); //!< Default initialization - - void get( DictionaryDatum&, const Parameters_& ) const; - - /** Set values from dictionary. - * @param dictionary to take data from - * @param current parameters - * @param Change in reversal potential E_L specified by this dict - */ - void set( const DictionaryDatum&, const Parameters_&, double ); - }; - - // ---------------------------------------------------------------- - - /** - * Buffers of the model. - */ - struct Buffers_ - { - Buffers_( iaf_psc_alpha_canon& ); - Buffers_( const Buffers_&, iaf_psc_alpha_canon& ); - - /** - * Queue for incoming events. - * @note Handles also pseudo-events marking return from refractoriness. - */ - SliceRingBuffer events_; - RingBuffer currents_; - - //! Logger for all analog data - UniversalDataLogger< iaf_psc_alpha_canon > logger_; - }; - - // ---------------------------------------------------------------- - - /** - * Internal variables of the model. - */ - struct Variables_ - { - double h_ms_; //!< time resolution in ms - double PSCInitialValue_; //!< e / tau_syn - long refractory_steps_; //!< refractory time in steps - double gamma_; //!< 1/c_m * 1/(1/tau_syn - 1/tau_m) - double gamma_sq_; //!< 1/c_m * 1/(1/tau_syn - 1/tau_m)^2 - double expm1_tau_m_; //!< exp(-h/tau_m) - 1 - double expm1_tau_syn_; //!< exp(-h/tau_syn) - 1 - double P30_; //!< progagator matrix elem, 3rd row - double P31_; //!< progagator matrix elem, 3rd row - double P32_; //!< progagator matrix elem, 3rd row - double y0_before_; //!< y0_ at beginning of mini-step, forinterpolation - double y2_before_; //!< y2_ at beginning of mini-step, for interpolation - double y3_before_; //!< y3_ at beginning of mini-step, for interpolation - }; - - // Access functions for UniversalDataLogger ------------------------------- - - //! Read out the real membrane potential - double - get_V_m_() const - { - return S_.y3_ + P_.E_L_; - } - - //! Read out state variable y1 - double - get_y1_() const - { - return S_.y1_; - } - - //! Read out state variable y2 - double - get_y2_() const - { - return S_.y2_; - } - - // ---------------------------------------------------------------- - - /** - * @defgroup iaf_psc_alpha_data - * Instances of private data structures for the different types - * of data pertaining to the model. - * @note The order of definitions is important for speed. - * @{ - */ - Parameters_ P_; - State_ S_; - Variables_ V_; - Buffers_ B_; - /** @} */ - - //! Mapping of recordables names to access functions - static RecordablesMap< iaf_psc_alpha_canon > recordablesMap_; -}; - -inline port -nest::iaf_psc_alpha_canon::send_test_event( Node& target, - rport receptor_type, - synindex, - bool ) -{ - SpikeEvent e; - e.set_sender( *this ); - return target.handles_test_event( e, receptor_type ); -} - -inline port -iaf_psc_alpha_canon::handles_test_event( SpikeEvent&, rport receptor_type ) -{ - if ( receptor_type != 0 ) - throw UnknownReceptorType( receptor_type, get_name() ); - return 0; -} - -inline port -iaf_psc_alpha_canon::handles_test_event( CurrentEvent&, rport receptor_type ) -{ - if ( receptor_type != 0 ) - throw UnknownReceptorType( receptor_type, get_name() ); - return 0; -} - -inline port -iaf_psc_alpha_canon::handles_test_event( DataLoggingRequest& dlr, - rport receptor_type ) -{ - if ( receptor_type != 0 ) - throw UnknownReceptorType( receptor_type, get_name() ); - return B_.logger_.connect_logging_device( dlr, recordablesMap_ ); -} - -inline void -iaf_psc_alpha_canon::get_status( DictionaryDatum& d ) const -{ - P_.get( d ); - S_.get( d, P_ ); - Archiving_Node::get_status( d ); - - ( *d )[ names::recordables ] = recordablesMap_.get_list(); -} - -inline void -iaf_psc_alpha_canon::set_status( const DictionaryDatum& d ) -{ - Parameters_ ptmp = P_; // temporary copy in case of errors - const double delta_EL = ptmp.set( d ); // throws if BadProperty - State_ stmp = S_; // temporary copy in case of errors - stmp.set( d, ptmp, delta_EL ); // throws if BadProperty - - // We now know that (ptmp, stmp) are consistent. We do not - // write them back to (P_, S_) before we are also sure that - // the properties to be set in the parent class are internally - // consistent. - Archiving_Node::set_status( d ); - - // if we get here, temporaries contain consistent set of properties - P_ = ptmp; - S_ = stmp; -} - -} // namespace - -#endif // IAF_PSC_ALPHA_CANON_H From 9ad07124b2363484f8089c2fde3efbb8640fad90 Mon Sep 17 00:00:00 2001 From: jk075370 Date: Mon, 23 Oct 2017 09:59:17 +0200 Subject: [PATCH 25/48] addressing hep's comments; add unit test --- precise/iaf_psc_exp_ps_lossless.cpp | 30 ++- precise/iaf_psc_exp_ps_lossless.h | 2 +- .../test_iaf_psc_exp_ps_lossless.sli | 185 ++++++++++++++++++ 3 files changed, 200 insertions(+), 17 deletions(-) create mode 100644 testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index 6427a7ae54..7680f730c9 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -353,11 +353,10 @@ void nest::iaf_psc_exp_ps_lossless::update(const Time & origin, interval. */ - V_.bisection_step_ = V_.h_ms_; - - if (is_spike_(V_.h_ms_)) + const double spike_time_max = is_spike_( V_.h_ms_ ); + if ( not numerics::is_nan( spike_time_max ) ) { - emit_spike_(origin, lag, 0, V_.bisection_step_); + emit_spike_(origin, lag, 0, spike_time_max); } } @@ -382,11 +381,11 @@ void nest::iaf_psc_exp_ps_lossless::update(const Time & origin, // this must be done before adding the input, since // interpolation requires continuity - V_.bisection_step_ = ministep; + const double spike_time_max = is_spike_( ministep ); - if (is_spike_(ministep)) + if ( not numerics::is_nan( spike_time_max ) ) { - emit_spike_(origin, lag, V_.h_ms_-last_offset, V_.bisection_step_); + emit_spike_(origin, lag, V_.h_ms_-last_offset, spike_time_max ); } // handle event @@ -419,11 +418,11 @@ void nest::iaf_psc_exp_ps_lossless::update(const Time & origin, // of interval if ( last_offset > 0 ) // not at end of step, do remainder { - V_.bisection_step_ = last_offset; + const double spike_time_max = is_spike_( last_offset ); propagate_(last_offset); - if (is_spike_(last_offset)) + if ( not numerics::is_nan( spike_time_max ) ) { - emit_spike_(origin, lag, V_.h_ms_-last_offset, V_.bisection_step_); + emit_spike_(origin, lag, V_.h_ms_-last_offset, spike_time_max); } } } // else @@ -569,7 +568,7 @@ inline double nest::iaf_psc_exp_ps_lossless::bisectioning_(const double dt) cons return root; } -inline bool nest::iaf_psc_exp_ps_lossless::is_spike_(const double dt) +double nest::iaf_psc_exp_ps_lossless::is_spike_(const double dt) { const double I_0 = V_.I_syn_ex_before_ + V_.I_syn_in_before_; const double V_0 = V_.y2_before_; @@ -583,24 +582,23 @@ inline bool nest::iaf_psc_exp_ps_lossless::is_spike_(const double dt) if((V_0 <= (((I_0 + P_.I_e_)*(V_.b1_ * exp_tau_m + V_.b2_* exp_tau_s) + V_.b3_*(exp_tau_m - exp_tau_s))/( V_.b4_ * exp_tau_s))) and (V_0 < g)) { - return false; + return numerics::nan; } //spike, S_1, V >= g_h,I_e(I) else if (V_0 >= g ) { - return true; + return dt; } //no-spike, NS_2, V < b(I) else if(V_0 < (V_.c1_ * P_.I_e_ + V_.c2_ * I_0 + V_.c3_* std::pow(I_0, V_.c4_) * std::pow((V_.c5_ - P_.I_e_), V_.c6_))) { - return false; + return numerics::nan; } else //missed spike detected, S_2 { - V_.bisection_step_ = (V_.a1_ / P_.tau_m_ * P_.tau_ex_ ) * std::log ( V_.b1_ * I_0 / (V_.a2_ * P_.I_e_ - V_.a1_ * I_0 - V_.a4_ * V_0 ) ); - return true; + return (V_.a1_ / P_.tau_m_ * P_.tau_ex_ ) * std::log ( V_.b1_ * I_0 / (V_.a2_ * P_.I_e_ - V_.a1_ * I_0 - V_.a4_ * V_0 ) ); } } diff --git a/precise/iaf_psc_exp_ps_lossless.h b/precise/iaf_psc_exp_ps_lossless.h index d9df806059..5c52186281 100644 --- a/precise/iaf_psc_exp_ps_lossless.h +++ b/precise/iaf_psc_exp_ps_lossless.h @@ -234,7 +234,7 @@ namespace nest * Propagate the neuron's state by dt. * @returns time to emit spike. */ - bool is_spike_(const double); + double is_spike_(const double); // ---------------------------------------------------------------- diff --git a/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli b/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli new file mode 100644 index 0000000000..0f1d663d2e --- /dev/null +++ b/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli @@ -0,0 +1,185 @@ +/* + * test_iaf_psc_exp_ps_lossless.sli + * + * 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 . + * + */ + + /* BeginDocumentation + Name: testsuite::test_iaf_psc_exp_ps_lossless - sli script for overall test of iaf_psc_exp model + + Synopsis: (test_iaf_psc_exp_ps_lossless) run -> compares response to current step with reference data + + Description: + test_iaf_psc_exp_ps_lossless.sli is an overall test of the iaf_psc_exp_ps_lossless model connected + to some useful devices. + + A DC current is injected into the neuron using a current generator + device. The membrane potential as well as the spiking activity are + recorded by corresponding devices. + + It can be observed how the current charges the membrane, a spike + is emitted, the neuron becomes absolute refractory, and finally + starts to recover. + + The timing of the various events on the simulation grid is of + particular interest and crucial for the consistency of the + simulation scheme. + + Although 0.1 cannot be represented in the IEEE double data type, it + is safe to simulate with a resolution (computation step size) of 0.1 + ms because by default nest is built with a timebase enabling exact + representation of 0.1 ms. + + The expected output is documented and briefly commented at the end of + the script. The textual output of the voltmeter documented in this file + can be regenerated by setting adding /to_screen true to the SetStatus + call of vm below. + + Other test programs discuss the various aspects of this script in detail, + see the SeeAlso key below. + + Author: Jeyashree Krishnan, 2017 + SeeAlso: iaf_psc_exp, testsuite::test_iaf_i0, testsuite::test_iaf_i0_refractory, testsuite::test_iaf_dc +*/ + + +(unittest) run +/unittest using + +0.1 /h Set + +ResetKernel + +0 << + /local_num_threads 1 + /resolution h + >> SetStatus + +/iaf_psc_exp_ps_lossless Create /neuron Set + +/dc_generator Create /dc_gen Set +dc_gen << /amplitude 1000. >> SetStatus + +/voltmeter Create /vm Set +vm << /withtime true /time_in_steps true /interval h >> SetStatus + +/spike_detector Create /sp_det Set +sp_det << /withtime true /withgid true /time_in_steps true >> SetStatus + + +dc_gen neuron 1.0 h Connect +vm neuron 1.0 h Connect +neuron sp_det 1.0 h Connect + +8 Simulate + + + +{ % reference data + dup Transpose First /test_times Set % times of reference + + vm [/events [/times /V_m]] get cva % array of recorded data + 6 ToUnitTestPrecision % to precision of reference + Transpose % all recorded tuples + {First test_times exch MemberQ } Select % those with reference + eq % compare +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Expected output of this program: +% +% The output send to std::cout is a superposition of the output of +% the voltmeter and the spike detector. Both, voltmeter and spike +% detector are connected to the same neuron. +% +% time (in steps) voltage (in mV) +[ +[ 1 -70 ] %<----- The earliest time dc_gen can be switched on. +[ 2 -70 ] %<----- The DC current arrives at the neuron, it is +[ 3 -70 ] % reflected in the neuron's state variable y0, +[ 4 -70 ] % (initial condition) but has not yet affected +% % the membrane potential. +% ... +% +[ 20 -70 ] +[ 21 -68.9055 ] % the effect of the DC current is visible in the +[ 22 -67.8219 ] % membrane potential +[ 23 -66.749 ] +[ 24 -65.6868 ] +[ 25 -64.6352 ] +[ 26 -63.5941 ] +[ 27 -62.5633 ] +[ 28 -61.5428 ] +[ 29 -60.5324 ] +[ 30 -59.5321 ] +[ 31 -58.5418 ] +[ 32 -57.5612 ] +[ 33 -56.5905 ] +[ 34 -55.6294 ] +[ 35 -54.6779 ] %<---- The membrane potential crossed threshold in the +[ 36 -70 ] % step 3.5 ms -> 3.6 ms. The membrane potential is +[ 37 -70 ] % reset (no super-threshold values can be observed) +% % The spike is reported at 3.6 ms +% ... +% +[ 50 -70 ] +[ 51 -70 ] +[ 52 -70 ] +[ 53 -70 ] +[ 54 -70 ] +[ 55 -70 ] +[ 56 -68.9055 ] %<---- The membrane potential starts to increase again +[ 57 -67.8219 ] % and the neuron generates spikes again +[ 58 -66.749 ] +[ 59 -65.6868 ] +[ 60 -64.6352 ] +[ 61 -63.5941 ] +[ 62 -62.5633 ] +[ 63 -61.5428 ] +[ 64 -60.5324 ] +[ 65 -59.5321 ] +[ 66 -58.5418 ] +[ 67 -57.5612 ] +[ 68 -56.5905 ] +[ 69 -55.6294 ] +[ 70 -54.6779 ] +[ 71 -70 ] +[ 72 -70 ] +[ 73 -70 ] +[ 74 -70 ] +[ 75 -70 ] +[ 76 -70 ] +[ 77 -70 ] +[ 78 -70 ] +[ 79 -70 ] %<---- The last point in time in which the membrane potential +] % is clamped. + % The simulation was run for 8.0 ms. However, in the step + % 7.9 ms -> 8.0 ms the voltmeter necessarily receives the + % voltages that occurred at time 7.9 ms (delay h). This + % results in different end times of the recorded voltage + % traces at different resolutions. In the current + % simulation kernel there is no general cure for this + % problem. One workaround is to end the simulation script + % with "h Simulate", thereby making the script resolution + % dependent. + + +exch assert_or_die + From 5955541f77e725705b3a5e3660f37bc6529e3ad6 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Wed, 7 Mar 2018 14:11:51 +0100 Subject: [PATCH 26/48] Revert "resolve conflicts" This reverts commit 3ae028fff2ba4dd4928320ef9b1f2116cd15bb58. --- precise/iaf_psc_alpha_canon.cpp | 658 ++++++++++++++++++++++++++++++++ precise/iaf_psc_alpha_canon.h | 520 +++++++++++++++++++++++++ 2 files changed, 1178 insertions(+) create mode 100644 precise/iaf_psc_alpha_canon.cpp create mode 100644 precise/iaf_psc_alpha_canon.h diff --git a/precise/iaf_psc_alpha_canon.cpp b/precise/iaf_psc_alpha_canon.cpp new file mode 100644 index 0000000000..ea756ad3e9 --- /dev/null +++ b/precise/iaf_psc_alpha_canon.cpp @@ -0,0 +1,658 @@ +/* + * iaf_psc_alpha_canon.cpp + * + * 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 . + * + */ + +#include "iaf_psc_alpha_canon.h" + +// C++ includes: +#include + +// Includes from libnestutil: +#include "numerics.h" +#include "propagator_stability.h" + +// Includes from nestkernel: +#include "exceptions.h" +#include "kernel_manager.h" +#include "universal_data_logger_impl.h" + +// Includes from sli: +#include "dict.h" +#include "dictutils.h" +#include "doubledatum.h" +#include "integerdatum.h" + +/* ---------------------------------------------------------------- + * Recordables map + * ---------------------------------------------------------------- */ + +nest::RecordablesMap< nest::iaf_psc_alpha_canon > + nest::iaf_psc_alpha_canon::recordablesMap_; + +namespace nest +{ +/* + * Override the create() method with one call to RecordablesMap::insert_() + * for each quantity to be recorded. + */ +template <> +void +RecordablesMap< iaf_psc_alpha_canon >::create() +{ + // use standard names wherever you can for consistency! + insert_( names::V_m, &iaf_psc_alpha_canon::get_V_m_ ); +} +} + +/* ---------------------------------------------------------------- + * Default constructors defining default parameters and state + * ---------------------------------------------------------------- */ + +nest::iaf_psc_alpha_canon::Parameters_::Parameters_() + : tau_m_( 10.0 ) // ms + , tau_syn_( 2.0 ) // ms + , c_m_( 250.0 ) // pF + , t_ref_( 2.0 ) // ms + , E_L_( -70.0 ) // mV + , I_e_( 0.0 ) // pA + , U_th_( -55.0 - E_L_ ) // mV, rel to E_L_ + , U_min_( -std::numeric_limits< double >::infinity() ) // mV + , U_reset_( -70.0 - E_L_ ) // mV, rel to E_L_ + , Interpol_( iaf_psc_alpha_canon::LINEAR ) +{ +} + +nest::iaf_psc_alpha_canon::State_::State_() + : y0_( 0.0 ) + , y1_( 0.0 ) + , y2_( 0.0 ) + , y3_( 0.0 ) + , is_refractory_( false ) + , last_spike_step_( -1 ) + , last_spike_offset_( 0.0 ) +{ +} + +/* ---------------------------------------------------------------- + * Parameter and state extractions and manipulation functions + * ---------------------------------------------------------------- */ + +void +nest::iaf_psc_alpha_canon::Parameters_::get( DictionaryDatum& d ) const +{ + def< double >( d, names::E_L, E_L_ ); + def< double >( d, names::I_e, I_e_ ); + def< double >( d, names::V_th, U_th_ + E_L_ ); + def< double >( d, names::V_min, U_min_ + E_L_ ); + def< double >( d, names::V_reset, U_reset_ + E_L_ ); + def< double >( d, names::C_m, c_m_ ); + def< double >( d, names::tau_m, tau_m_ ); + def< double >( d, names::tau_syn, tau_syn_ ); + def< double >( d, names::t_ref, t_ref_ ); + def< long >( d, names::Interpol_Order, Interpol_ ); +} + +double +nest::iaf_psc_alpha_canon::Parameters_::set( const DictionaryDatum& d ) +{ + // if E_L_ is changed, we need to adjust all variables defined relative to + // E_L_ + const double ELold = E_L_; + updateValue< double >( d, names::E_L, E_L_ ); + const double delta_EL = E_L_ - ELold; + + updateValue< double >( d, names::tau_m, tau_m_ ); + updateValue< double >( d, names::tau_syn, tau_syn_ ); + updateValue< double >( d, names::C_m, c_m_ ); + updateValue< double >( d, names::t_ref, t_ref_ ); + updateValue< double >( d, names::I_e, I_e_ ); + + if ( updateValue< double >( d, names::V_th, U_th_ ) ) + U_th_ -= E_L_; + else + U_th_ -= delta_EL; + + if ( updateValue< double >( d, names::V_min, U_min_ ) ) + U_min_ -= E_L_; + else + U_min_ -= delta_EL; + + if ( updateValue< double >( d, names::V_reset, U_reset_ ) ) + U_reset_ -= E_L_; + else + U_reset_ -= delta_EL; + + long tmp; + if ( updateValue< long >( d, names::Interpol_Order, tmp ) ) + { + if ( NO_INTERPOL <= tmp && tmp < END_INTERP_ORDER ) + Interpol_ = static_cast< interpOrder >( tmp ); + else + throw BadProperty( + "Invalid interpolation order. " + "Valid orders are 0, 1, 2, 3." ); + } + + if ( U_reset_ >= U_th_ ) + throw BadProperty( "Reset potential must be smaller than threshold." ); + + if ( U_reset_ < U_min_ ) + throw BadProperty( + "Reset potential must be greater equal minimum potential." ); + + if ( c_m_ <= 0 ) + throw BadProperty( "Capacitance must be strictly positive." ); + + if ( Time( Time::ms( t_ref_ ) ).get_steps() < 1 ) + throw BadProperty( "Refractory time must be at least one time step." ); + + if ( tau_m_ <= 0 || tau_syn_ <= 0 ) + throw BadProperty( "All time constants must be strictly positive." ); + + return delta_EL; +} + +void +nest::iaf_psc_alpha_canon::State_::get( DictionaryDatum& d, + const Parameters_& p ) const +{ + def< double >( d, names::V_m, y3_ + p.E_L_ ); // Membrane potential + def< double >( d, "y1", y1_ ); // y1 state + def< double >( d, "y2", y2_ ); // y2 state + def< bool >( d, names::is_refractory, is_refractory_ ); +} + +void +nest::iaf_psc_alpha_canon::State_::set( const DictionaryDatum& d, + const Parameters_& p, + double delta_EL ) +{ + if ( updateValue< double >( d, names::V_m, y3_ ) ) + y3_ -= p.E_L_; + else + y3_ -= delta_EL; + + updateValue< double >( d, "y1", y1_ ); + updateValue< double >( d, "y2", y2_ ); +} + +nest::iaf_psc_alpha_canon::Buffers_::Buffers_( iaf_psc_alpha_canon& n ) + : logger_( n ) +{ +} + +nest::iaf_psc_alpha_canon::Buffers_::Buffers_( const Buffers_&, + iaf_psc_alpha_canon& n ) + : logger_( n ) +{ +} + + +/* ---------------------------------------------------------------- + * Default and copy constructor for node + * ---------------------------------------------------------------- */ + +nest::iaf_psc_alpha_canon::iaf_psc_alpha_canon() + : Archiving_Node() + , P_() + , S_() + , B_( *this ) +{ + recordablesMap_.create(); +} + +nest::iaf_psc_alpha_canon::iaf_psc_alpha_canon( const iaf_psc_alpha_canon& n ) + : Archiving_Node( n ) + , P_( n.P_ ) + , S_( n.S_ ) + , B_( n.B_, *this ) +{ +} + +/* ---------------------------------------------------------------- + * Node initialization functions + * ---------------------------------------------------------------- */ + +void +nest::iaf_psc_alpha_canon::init_state_( const Node& proto ) +{ + const iaf_psc_alpha_canon& pr = downcast< iaf_psc_alpha_canon >( proto ); + S_ = pr.S_; +} + +void +nest::iaf_psc_alpha_canon::init_buffers_() +{ + B_.events_.resize(); + B_.events_.clear(); + B_.currents_.clear(); // includes resize + B_.logger_.reset(); + + Archiving_Node::clear_history(); +} + +void +nest::iaf_psc_alpha_canon::calibrate() +{ + B_.logger_.init(); + + V_.h_ms_ = Time::get_resolution().get_ms(); + + V_.PSCInitialValue_ = 1.0 * numerics::e / P_.tau_syn_; + + V_.gamma_ = 1 / P_.c_m_ / ( 1 / P_.tau_syn_ - 1 / P_.tau_m_ ); + V_.gamma_sq_ = 1 / P_.c_m_ / ( ( 1 / P_.tau_syn_ - 1 / P_.tau_m_ ) + * ( 1 / P_.tau_syn_ - 1 / P_.tau_m_ ) ); + + // pre-compute matrix for full time step + V_.expm1_tau_m_ = numerics::expm1( -V_.h_ms_ / P_.tau_m_ ); + V_.expm1_tau_syn_ = numerics::expm1( -V_.h_ms_ / P_.tau_syn_ ); + V_.P30_ = -P_.tau_m_ / P_.c_m_ * V_.expm1_tau_m_; + // these are determined according to a numeric stability criterion + V_.P31_ = propagator_31( P_.tau_syn_, P_.tau_m_, P_.c_m_, V_.h_ms_ ); + V_.P32_ = propagator_32( P_.tau_syn_, P_.tau_m_, P_.c_m_, V_.h_ms_ ); + + // t_ref_ is the refractory period in ms + // refractory_steps_ is the duration of the refractory period in whole + // steps, rounded down + V_.refractory_steps_ = Time( Time::ms( P_.t_ref_ ) ).get_steps(); + // since t_ref_ >= sim step size, this can only fail in error + assert( V_.refractory_steps_ >= 1 ); +} + +/* ---------------------------------------------------------------- + * Update and spike handling functions + * ---------------------------------------------------------------- */ + +void +nest::iaf_psc_alpha_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(); + + /* 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 >::epsilon() ) ); + + for ( long lag = from; lag < to; ++lag ) + { + // time at start of update step + const long 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 ev_offset; + double ev_weight; + bool end_of_refract; + + if ( !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 ( !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 last_offset = V_.h_ms_; // start of step + + do + { + // time is measured backward: inverse order in difference + const double 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, true, 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 ... +} + + +// function handles exact spike times +void +nest::iaf_psc_alpha_canon::handle( SpikeEvent& e ) +{ + assert( e.get_delay() > 0 ); + + /* We need to compute the absolute time stamp of the delivery time + of the spike, since spikes might spend longer than min_delay_ + in the queue. The time is computed according to Time Memo, Rule 3. + */ + const long Tdeliver = e.get_stamp().get_steps() + e.get_delay() - 1; + B_.events_.add_spike( + e.get_rel_delivery_steps( + nest::kernel().simulation_manager.get_slice_origin() ), + Tdeliver, + e.get_offset(), + e.get_weight() * e.get_multiplicity() ); +} + +void +nest::iaf_psc_alpha_canon::handle( CurrentEvent& e ) +{ + assert( e.get_delay() > 0 ); + + const double c = e.get_current(); + const double w = e.get_weight(); + + // add weighted current; HEP 2002-10-04 + B_.currents_.add_value( + e.get_rel_delivery_steps( + nest::kernel().simulation_manager.get_slice_origin() ), + w * c ); +} + +void +nest::iaf_psc_alpha_canon::handle( DataLoggingRequest& e ) +{ + B_.logger_.handle( e ); +} + +// auxiliary functions --------------------------------------------- + +void +nest::iaf_psc_alpha_canon::propagate_( const double dt ) +{ + // needed in any case + const double ps_e_TauSyn = numerics::expm1( -dt / P_.tau_syn_ ); + + // y3_ remains unchanged at 0.0 while neuron is refractory + if ( !S_.is_refractory_ ) + { + const double ps_e_Tau = numerics::expm1( -dt / P_.tau_m_ ); + const double ps_P30 = -P_.tau_m_ / P_.c_m_ * ps_e_Tau; + const double ps_P31 = V_.gamma_sq_ * ps_e_Tau - V_.gamma_sq_ * ps_e_TauSyn + - dt * V_.gamma_ * ps_e_TauSyn - dt * V_.gamma_; + const double ps_P32 = V_.gamma_ * ps_e_Tau - V_.gamma_ * ps_e_TauSyn; + S_.y3_ = ps_P30 * ( P_.I_e_ + S_.y0_ ) + ps_P31 * S_.y1_ + ps_P32 * S_.y2_ + + ps_e_Tau * S_.y3_ + S_.y3_; + + // lower bound of membrane potential + S_.y3_ = ( S_.y3_ < P_.U_min_ ? P_.U_min_ : S_.y3_ ); + } + + // now the synaptic components + S_.y2_ = + ps_e_TauSyn * dt * S_.y1_ + ps_e_TauSyn * S_.y2_ + dt * S_.y1_ + S_.y2_; + S_.y1_ = ps_e_TauSyn * S_.y1_ + S_.y1_; + + return; +} + +void +nest::iaf_psc_alpha_canon::emit_spike_( Time const& origin, + const long lag, + const double t0, + const double dt ) +{ + // we know that the potential is subthreshold at t0, super at t0+dt + + // compute spike time relative to beginning of step + S_.last_spike_step_ = origin.get_steps() + lag + 1; + S_.last_spike_offset_ = V_.h_ms_ - ( t0 + thresh_find_( dt ) ); + + // reset neuron and make it refractory + S_.y3_ = P_.U_reset_; + S_.is_refractory_ = true; + + // send spike + set_spiketime( Time::step( S_.last_spike_step_ ), S_.last_spike_offset_ ); + SpikeEvent se; + se.set_offset( S_.last_spike_offset_ ); + kernel().event_delivery_manager.send( *this, se, lag ); + + return; +} + +void +nest::iaf_psc_alpha_canon::emit_instant_spike_( Time const& origin, + const long lag, + const double spike_offs ) +{ + assert( S_.y3_ >= P_.U_th_ ); // ensure we are superthreshold + + // set stamp and offset for spike + S_.last_spike_step_ = origin.get_steps() + lag + 1; + S_.last_spike_offset_ = spike_offs; + + // reset neuron and make it refractory + S_.y3_ = P_.U_reset_; + S_.is_refractory_ = true; + + // send spike + set_spiketime( Time::step( S_.last_spike_step_ ), S_.last_spike_offset_ ); + SpikeEvent se; + se.set_offset( S_.last_spike_offset_ ); + kernel().event_delivery_manager.send( *this, se, lag ); + + return; +} + +// finds threshpassing +inline double +nest::iaf_psc_alpha_canon::thresh_find_( double const dt ) const +{ + switch ( P_.Interpol_ ) + { + case NO_INTERPOL: + return dt; + case LINEAR: + return thresh_find1_( dt ); + case QUADRATIC: + return thresh_find2_( dt ); + case CUBIC: + return thresh_find3_( dt ); + default: + throw BadProperty( "Invalid interpolation order in iaf_psc_alpha_canon." ); + } + return 0; +} + +// finds threshpassing via linear interpolation +double +nest::iaf_psc_alpha_canon::thresh_find1_( double const dt ) const +{ + double tau = ( P_.U_th_ - V_.y3_before_ ) * dt / ( S_.y3_ - V_.y3_before_ ); + return tau; +} + +// finds threshpassing via quadratic interpolation +double +nest::iaf_psc_alpha_canon::thresh_find2_( double const dt ) const +{ + const double h_sq = dt * dt; + const double derivative = -V_.y3_before_ / P_.tau_m_ + + ( P_.I_e_ + V_.y0_before_ + V_.y2_before_ ) / P_.c_m_; + + const double a = + ( -V_.y3_before_ / h_sq ) + ( S_.y3_ / h_sq ) - ( derivative / dt ); + const double b = derivative; + const double c = V_.y3_before_; + + const double sqr_ = std::sqrt( b * b - 4 * a * c + 4 * a * P_.U_th_ ); + const double tau1 = ( -b + sqr_ ) / ( 2 * a ); + const double tau2 = ( -b - sqr_ ) / ( 2 * a ); + + if ( tau1 >= 0 ) + return tau1; + else if ( tau2 >= 0 ) + return tau2; + else + return thresh_find1_( dt ); +} + +double +nest::iaf_psc_alpha_canon::thresh_find3_( double const dt ) const +{ + const double h_ms = dt; + const double h_sq = h_ms * h_ms; + const double h_cb = h_sq * h_ms; + + const double deriv_t1 = -V_.y3_before_ / P_.tau_m_ + + ( P_.I_e_ + V_.y0_before_ + V_.y2_before_ ) / P_.c_m_; + const double deriv_t2 = + -S_.y3_ / P_.tau_m_ + ( P_.I_e_ + S_.y0_ + S_.y2_ ) / P_.c_m_; + + const double w3_ = ( 2 * V_.y3_before_ / h_cb ) - ( 2 * S_.y3_ / h_cb ) + + ( deriv_t1 / h_sq ) + ( deriv_t2 / h_sq ); + const double w2_ = -( 3 * V_.y3_before_ / h_sq ) + ( 3 * S_.y3_ / h_sq ) + - ( 2 * deriv_t1 / h_ms ) - ( deriv_t2 / h_ms ); + const double w1_ = deriv_t1; + const double w0_ = V_.y3_before_; + + // normal form : x^3 + r*x^2 + s*x + t with coefficients : r, s, t + const double r = w2_ / w3_; + const double s = w1_ / w3_; + const double t = ( w0_ - P_.U_th_ ) / w3_; + const double r_sq = r * r; + + // substitution y = x + r/3 : y^3 + p*y + q == 0 + const double p = -r_sq / 3 + s; + const double q = 2 * ( r_sq * r ) / 27 - r * s / 3 + t; + + // discriminante + const double D = std::pow( ( p / 3 ), 3 ) + std::pow( ( q / 2 ), 2 ); + + double tau1; + double tau2; + double tau3; + + if ( D < 0 ) + { + const double roh = std::sqrt( -( p * p * p ) / 27 ); + const double phi = std::acos( -q / ( 2 * roh ) ); + const double a = 2 * std::pow( roh, ( 1.0 / 3.0 ) ); + tau1 = ( a * std::cos( phi / 3 ) ) - r / 3; + tau2 = ( a * std::cos( phi / 3 + 2 * numerics::pi / 3 ) ) - r / 3; + tau3 = ( a * std::cos( phi / 3 + 4 * numerics::pi / 3 ) ) - r / 3; + } + else + { + const double sgnq = ( q >= 0 ? 1 : -1 ); + const double u = + -sgnq * std::pow( std::fabs( q ) / 2.0 + std::sqrt( D ), 1.0 / 3.0 ); + const double v = -p / ( 3 * u ); + tau1 = ( u + v ) - r / 3; + if ( tau1 >= 0 ) + { + return tau1; + } + else + { + return thresh_find2_( dt ); + } + } + + // set tau to the smallest root above 0 + + double tau = ( tau1 >= 0 ) ? tau1 : 2 * h_ms; + if ( ( tau2 >= 0 ) && ( tau2 < tau ) ) + tau = tau2; + if ( ( tau3 >= 0 ) && ( tau3 < tau ) ) + tau = tau3; + return ( tau <= V_.h_ms_ ) ? tau : thresh_find2_( dt ); +} diff --git a/precise/iaf_psc_alpha_canon.h b/precise/iaf_psc_alpha_canon.h new file mode 100644 index 0000000000..85b14131f7 --- /dev/null +++ b/precise/iaf_psc_alpha_canon.h @@ -0,0 +1,520 @@ +/* + * iaf_psc_alpha_canon.h + * + * 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 . + * + */ + +#ifndef IAF_PSC_ALPHA_CANON_H +#define IAF_PSC_ALPHA_CANON_H + +// C++ includes: +#include + +// Generated includes: +#include "config.h" + +// Includes from nestkernel: +#include "archiving_node.h" +#include "connection.h" +#include "event.h" +#include "nest_types.h" +#include "ring_buffer.h" +#include "universal_data_logger.h" + +// Includes from precise: +#include "slice_ring_buffer.h" + +/*BeginDocumentation +Name: iaf_psc_alpha_canon - Leaky integrate-and-fire neuron +with alpha-shape postsynaptic currents; canoncial implementation. + +Description: +iaf_psc_alpha_canon is the "canonical" implementatoin of the leaky +integrate-and-fire model neuron with alpha-shaped postsynaptic +currents in the sense of [1]. This is the most exact implementation +available. + +PSCs are normalized to an amplitude of 1pA. + +The canonical implementation handles neuronal dynamics in a locally +event-based manner with in coarse time grid defined by the minimum +delay in the network, see [1]. Incoming spikes are applied at the +precise moment of their arrival, while the precise time of outgoing +spikes is determined by interpolation once a threshold crossing has +been detected. Return from refractoriness occurs precisly at spike +time plus refractory period. + +This implementation is more complex than the plain iaf_psc_alpha +neuron, but achieves much higher precision. In particular, it does not +suffer any binning of spike times to grid points. Depending on your +application, the canonical application may provide superior overall +performance given an accuracy goal; see [1] for details. Subthreshold +dynamics are integrated using exact integration between events [2]. + +Remarks: +The iaf_psc_delta_canon neuron does not accept CurrentEvent connections. +This is because the present method for transmitting CurrentEvents in +NEST (sending the current to be applied) is not compatible with off-grid +currents, if more than one CurrentEvent-connection exists. Once CurrentEvents +are changed to transmit change-of-current-strength, this problem will +disappear and the canonical neuron will also be able to handle CurrentEvents. +For now, the only way to inject a current is the built-in current I_e. + +Please note that this node is capable of sending precise spike times +to target nodes (on-grid spike time plus offset). If this node is +connected to a spike_detector, the property "precise_times" of the +spike_detector has to be set to true in order to record the offsets +in addition to the on-grid spike times. + +A further improvement of precise simulation is implemented in iaf_psc_exp_ps +based on [3]. + +Parameters: +The following parameters can be set in the status dictionary. + + V_m double - Membrane potential in mV + E_L double - Resting membrane potential in mV. + V_min double - Absolute lower value for the membrane potential. + C_m double - Capacity of the membrane in pF + tau_m double - Membrane time constant in ms. + t_ref double - Duration of refractory period in ms. + V_th double - Spike threshold in mV. + V_reset double - Reset potential of the membrane in mV. + tau_syn double - Rise time of the synaptic alpha function in ms. + I_e double - Constant external input current in pA. + Interpol_Order int - Interpolation order for spike time: + 0-none, 1-linear, 2-quadratic, 3-cubic + +Remarks: +If tau_m is very close to tau_syn_ex or tau_syn_in, the model +will numerically behave as if tau_m is equal to tau_syn_ex or +tau_syn_in, respectively, to avoid numerical instabilities. +For details, please see IAF_Neruons_Singularity.ipynb in +the NEST source code (docs/model_details). + +References: +[1] Morrison A, Straube S, Plesser H E, & Diesmann M (2006) Exact Subthreshold + Integration with Continuous Spike Times in Discrete Time Neural Network + Simulations. To appear in Neural Computation. +[2] Rotter S & Diesmann M (1999) Exact simulation of time-invariant linear + systems with applications to neuronal modeling. Biologial Cybernetics + 81:381-402. +[3] Hanuschkin A, Kunkel S, Helias M, Morrison A & Diesmann M (2010) + A general and efficient method for incorporating exact spike times in + globally time-driven simulations Front Neuroinformatics, 4:113 + +Author: Diesmann, Eppler, Morrison, Plesser, Straube + +Sends: SpikeEvent + +Receives: SpikeEvent, CurrentEvent, DataLoggingRequest + +SeeAlso: iaf_psc_alpha, iaf_psc_alpha_presc, iaf_psc_exp_ps + +*/ + +namespace nest +{ + +/** + * Leaky iaf neuron, alpha PSC synapses, canonical implementation. + * @note Inherit privately from Node, so no classes can be derived + * from this one. + * @todo Implement current input in consistent way. + */ +class iaf_psc_alpha_canon : public Archiving_Node +{ +public: + /** Basic constructor. + This constructor should only be used by GenericModel to create + model prototype instances. + */ + iaf_psc_alpha_canon(); + + /** Copy constructor. + GenericModel::allocate_() uses the copy constructor to clone + actual model instances from the prototype instance. + + @note The copy constructor MUST NOT be used to create nodes based + on nodes that have been placed in the network. + */ + iaf_psc_alpha_canon( const iaf_psc_alpha_canon& ); + + /** + * Import sets of overloaded virtual functions. + * @see Technical Issues / Virtual Functions: Overriding, Overloading, and + * Hiding + */ + using Node::handle; + using Node::handles_test_event; + + port send_test_event( Node&, rport, synindex, bool ); + + port handles_test_event( SpikeEvent&, rport ); + port handles_test_event( CurrentEvent&, rport ); + port handles_test_event( DataLoggingRequest&, rport ); + + void handle( SpikeEvent& ); + void handle( CurrentEvent& ); + void handle( DataLoggingRequest& ); + + bool + is_off_grid() const + { + return true; + } // uses off_grid events + + void get_status( DictionaryDatum& ) const; + void set_status( const DictionaryDatum& ); + +private: + /** @name Interface functions + * @note These functions are private, so that they can be accessed + * only through a Node*. + */ + //@{ + void init_state_( const Node& proto ); + void init_buffers_(); + void calibrate(); + + /** + * Time Evolution Operator. + * + * update() promotes the state of the neuron from origin+from to origin+to. + * It does so in steps of the resolution h. Within each step, time is + * advanced from event to event, as retrieved from the spike queue. + * + * Return from refractoriness is handled as a special event in the + * queue, which is marked by a weight that is GSL_NAN. This greatly + * simplifies the code. + * + * For steps, during which no events occur, the precomputed propagator matrix + * is used. For other steps, the propagator matrix is computed as needed. + * + * While the neuron is refractory, membrane potential (y3_) is + * clamped to U_reset_. + */ + void update( Time const& origin, const long from, const long to ); + + //@} + + /** + * Propagate neuron state. + * Propagate the neuron's state by dt. + * @param dt Interval over which to propagate + */ + void propagate_( const double dt ); + + /** + * Trigger interpolation method to find the precise spike time + * within the mini-timestep (t0,t0+dt] assuming that the membrane + * potential was below threshold at t0 and above at t0+dt. Emit + * the spike and reset the neuron. + * + * @param origin Time stamp at beginning of slice + * @param lag Time step within slice + * @param t0 Beginning of mini-timestep + * @param dt Duration of mini-timestep + */ + void emit_spike_( Time const& origin, + const long lag, + const double t0, + const double dt ); + + /** + * Instantaneously emit a spike at the precise time defined by + * origin, lag and spike_offset and reset the neuron. + * + * @param origin Time stamp at beginning of slice + * @param lag Time step within slice + * @param spike_offset Time offset for spike + */ + void emit_instant_spike_( Time const& origin, + const long lag, + const double spike_offset ); + + /** @name Threshold-crossing interpolation + * These functions determine the time of threshold crossing using + * interpolation, one function per interpolation + * order. thresh_find() is the driver function and the only one to + * be called directly. + */ + //@{ + + /** Interpolation orders. */ + enum interpOrder + { + NO_INTERPOL, + LINEAR, + QUADRATIC, + CUBIC, + END_INTERP_ORDER + }; + + /** + * Localize threshold crossing. + * Driver function to invoke the correct interpolation function + * for the chosen interpolation order. + * @param double length of interval since previous event + * @returns time from previous event to threshold crossing + */ + double thresh_find_( double const ) const; + double thresh_find1_( double const ) const; + double thresh_find2_( double const ) const; + double thresh_find3_( double const ) const; + //@} + + + // The next two classes need to be friends to access the State_ class/member + friend class RecordablesMap< iaf_psc_alpha_canon >; + friend class UniversalDataLogger< iaf_psc_alpha_canon >; + + // ---------------------------------------------------------------- + + /** + * Independent parameters of the model. + */ + struct Parameters_ + { + + /** Membrane time constant in ms. */ + double tau_m_; + + /** Time constant of synaptic current in ms. */ + double tau_syn_; + + /** Membrane capacitance in pF. */ + double c_m_; + + /** Refractory period in ms. */ + double t_ref_; + + /** Resting potential in mV. */ + double E_L_; + + /** External DC current [pA] */ + double I_e_; + + /** Threshold, RELATIVE TO RESTING POTENTAIL(!). + I.e. the real threshold is U_th_ + E_L_. */ + double U_th_; + + /** Lower bound, RELATIVE TO RESTING POTENTAIL(!). + I.e. the real lower bound is U_min_+E_L_. */ + double U_min_; + + /** Reset potential. + At threshold crossing, the membrane potential is reset to this + value. Relative to resting potential. + */ + double U_reset_; + + /** Interpolation order */ + interpOrder Interpol_; + + Parameters_(); //!< Sets default parameter values + + void get( DictionaryDatum& ) const; //!< Store current values in dictionary + + /** Set values from dictionary. + * @returns Change in reversal potential E_L, to be passed to State_::set() + */ + double set( const DictionaryDatum& ); + }; + + // ---------------------------------------------------------------- + + /** + * State variables of the model. + */ + struct State_ + { + double y0_; //!< external input current + double y1_; //!< alpha current, first component + double y2_; //!< alpha current, second component + double y3_; //!< Membrane pot. rel. to resting pot. E_L_. + bool is_refractory_; //!< true while refractory + long last_spike_step_; //!< time stamp of most recent spike + double last_spike_offset_; //!< offset of most recent spike + + State_(); //!< Default initialization + + void get( DictionaryDatum&, const Parameters_& ) const; + + /** Set values from dictionary. + * @param dictionary to take data from + * @param current parameters + * @param Change in reversal potential E_L specified by this dict + */ + void set( const DictionaryDatum&, const Parameters_&, double ); + }; + + // ---------------------------------------------------------------- + + /** + * Buffers of the model. + */ + struct Buffers_ + { + Buffers_( iaf_psc_alpha_canon& ); + Buffers_( const Buffers_&, iaf_psc_alpha_canon& ); + + /** + * Queue for incoming events. + * @note Handles also pseudo-events marking return from refractoriness. + */ + SliceRingBuffer events_; + RingBuffer currents_; + + //! Logger for all analog data + UniversalDataLogger< iaf_psc_alpha_canon > logger_; + }; + + // ---------------------------------------------------------------- + + /** + * Internal variables of the model. + */ + struct Variables_ + { + double h_ms_; //!< time resolution in ms + double PSCInitialValue_; //!< e / tau_syn + long refractory_steps_; //!< refractory time in steps + double gamma_; //!< 1/c_m * 1/(1/tau_syn - 1/tau_m) + double gamma_sq_; //!< 1/c_m * 1/(1/tau_syn - 1/tau_m)^2 + double expm1_tau_m_; //!< exp(-h/tau_m) - 1 + double expm1_tau_syn_; //!< exp(-h/tau_syn) - 1 + double P30_; //!< progagator matrix elem, 3rd row + double P31_; //!< progagator matrix elem, 3rd row + double P32_; //!< progagator matrix elem, 3rd row + double y0_before_; //!< y0_ at beginning of mini-step, forinterpolation + double y2_before_; //!< y2_ at beginning of mini-step, for interpolation + double y3_before_; //!< y3_ at beginning of mini-step, for interpolation + }; + + // Access functions for UniversalDataLogger ------------------------------- + + //! Read out the real membrane potential + double + get_V_m_() const + { + return S_.y3_ + P_.E_L_; + } + + //! Read out state variable y1 + double + get_y1_() const + { + return S_.y1_; + } + + //! Read out state variable y2 + double + get_y2_() const + { + return S_.y2_; + } + + // ---------------------------------------------------------------- + + /** + * @defgroup iaf_psc_alpha_data + * Instances of private data structures for the different types + * of data pertaining to the model. + * @note The order of definitions is important for speed. + * @{ + */ + Parameters_ P_; + State_ S_; + Variables_ V_; + Buffers_ B_; + /** @} */ + + //! Mapping of recordables names to access functions + static RecordablesMap< iaf_psc_alpha_canon > recordablesMap_; +}; + +inline port +nest::iaf_psc_alpha_canon::send_test_event( Node& target, + rport receptor_type, + synindex, + bool ) +{ + SpikeEvent e; + e.set_sender( *this ); + return target.handles_test_event( e, receptor_type ); +} + +inline port +iaf_psc_alpha_canon::handles_test_event( SpikeEvent&, rport receptor_type ) +{ + if ( receptor_type != 0 ) + throw UnknownReceptorType( receptor_type, get_name() ); + return 0; +} + +inline port +iaf_psc_alpha_canon::handles_test_event( CurrentEvent&, rport receptor_type ) +{ + if ( receptor_type != 0 ) + throw UnknownReceptorType( receptor_type, get_name() ); + return 0; +} + +inline port +iaf_psc_alpha_canon::handles_test_event( DataLoggingRequest& dlr, + rport receptor_type ) +{ + if ( receptor_type != 0 ) + throw UnknownReceptorType( receptor_type, get_name() ); + return B_.logger_.connect_logging_device( dlr, recordablesMap_ ); +} + +inline void +iaf_psc_alpha_canon::get_status( DictionaryDatum& d ) const +{ + P_.get( d ); + S_.get( d, P_ ); + Archiving_Node::get_status( d ); + + ( *d )[ names::recordables ] = recordablesMap_.get_list(); +} + +inline void +iaf_psc_alpha_canon::set_status( const DictionaryDatum& d ) +{ + Parameters_ ptmp = P_; // temporary copy in case of errors + const double delta_EL = ptmp.set( d ); // throws if BadProperty + State_ stmp = S_; // temporary copy in case of errors + stmp.set( d, ptmp, delta_EL ); // throws if BadProperty + + // We now know that (ptmp, stmp) are consistent. We do not + // write them back to (P_, S_) before we are also sure that + // the properties to be set in the parent class are internally + // consistent. + Archiving_Node::set_status( d ); + + // if we get here, temporaries contain consistent set of properties + P_ = ptmp; + S_ = stmp; +} + +} // namespace + +#endif // IAF_PSC_ALPHA_CANON_H From 40ec4aa754b39be158d053658cea30f40d8e09d8 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Wed, 7 Mar 2018 14:16:14 +0100 Subject: [PATCH 27/48] Restore slice_ring_buffer.h. --- precise/slice_ring_buffer.h | 262 ++++++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 precise/slice_ring_buffer.h diff --git a/precise/slice_ring_buffer.h b/precise/slice_ring_buffer.h new file mode 100644 index 0000000000..957275554f --- /dev/null +++ b/precise/slice_ring_buffer.h @@ -0,0 +1,262 @@ +/* + * slice_ring_buffer.h + * + * 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 . + * + */ + +#ifndef SLICE_RING_BUFFER_H +#define SLICE_RING_BUFFER_H + +// C++ includes: +#include +#include +#include +#include + +// Generated includes: +#include "config.h" + +// Includes from nestkernel: +#include "kernel_manager.h" +#include "nest_types.h" + +namespace nest +{ +/** + * Queue for all spikes arriving into a neuron. + * Spikes are stored unsorted on arrival, but are sorted when + * prepare_delivery() is called. They can then be retrieved + * one by one in correct temporal order. Coinciding spikes + * are combined into one, see get_next_spike(). + * + * Data is organized as follows: + * - The time of the next return from refractoriness is + * stored in a separate variable and checked explicitly; + * otherwise, we'd have to re-sort data during updating. + * - We have a pseudo-ring of Nbuff=ceil((min_del+max_del)/min_del) elements. + * Each element is a vector storing incoming spikes that + * are due during a given time slice. + * + * @note The following assumptions underlie the handling of + * pseudo-events for return from refractoriness: + * - There is at most one such event per time step (value of time stamp). + */ +class SliceRingBuffer +{ +public: + SliceRingBuffer(); + + /** + * Add spike to queue. + * @param rel_delivery relative delivery time + * @param stamp Delivery time + * @param ps_offset Precise timing offset of spike time + * @param weight Weight of spike. + */ + void add_spike( const delay rel_delivery, + const long stamp, + const double ps_offset, + const double weight ); + + /** + * Add refractory event to queue. + * The refractory event is actually stored as a pseudo-event. + * @param stamp Delivery time + * @param ps_offset Precise timing offset of spike time + */ + void add_refractory( const long stamp, const double ps_offset ); + + /** + * Prepare for spike delivery in current slice by sorting. + */ + void prepare_delivery(); + + /** + * Discard all events in current slice. + */ + void discard_events(); + + /** + * Return next spike. + * @param req_stamp Request spike with this stamp. Queue + * should never contain spikes with smaller + * stamps. Spikes with larger stamps are + * left in queue. + * @param accumulate_simultaneous If true, return summed weight + * of simultaneous input spikes, otherwise return + * one spike at a time. + * @param ps_offset PS-sense offset of spike time + * @param weight Spike weight + * @param end_of_refract True if spike is pseudo-spike marking + * end of refractory period + * @returns true if spike available, false otherwise + * @note If return from refractoriness coincides with + * a spike, return from refractoriness is returned. + */ + bool get_next_spike( const long req_stamp, + bool accumulate_simultaneous, + double& ps_offset, + double& weight, + bool& end_of_refract ); + + /** + * Clear buffer + */ + void clear(); + + /** + * Resize the buffer according to min_delay and max_delay. + */ + void resize(); + +private: + /** + * Information about spike. + */ + struct SpikeInfo + { + SpikeInfo( long stamp, double ps_offset, double weight ); + + bool operator<( const SpikeInfo& b ) const; + bool operator<=( const SpikeInfo& b ) const; + bool operator>( const SpikeInfo& b ) const; + + // data elements must not be const, since heap implementation + // in DEC STL uses operator=(). + long stamp_; // > queue_; + + //! slot to deliver from + std::vector< SpikeInfo >* deliver_; + + SpikeInfo refract_; //!< pseudo-event for return from refractoriness +}; + +inline void +SliceRingBuffer::add_spike( const delay rel_delivery, + const long stamp, + const double ps_offset, + const double weight ) +{ + const delay idx = + kernel().event_delivery_manager.get_slice_modulo( rel_delivery ); + assert( ( size_t ) idx < queue_.size() ); + assert( ps_offset >= 0 ); + + queue_[ idx ].push_back( SpikeInfo( stamp, ps_offset, weight ) ); +} + +inline void +SliceRingBuffer::add_refractory( const long stamp, const double ps_offset ) +{ + // We require that only one refractory-return pseudo-event is stored per + // time step. We guard against violation using assert(): refract_.stamp_ must + // be equal to the marker value for non-refractoriness. All else would mean + // that a refractory neuron fired. + assert( refract_.stamp_ == std::numeric_limits< long >::max() ); + + refract_.stamp_ = stamp; + refract_.ps_offset_ = ps_offset; +} + +inline bool +SliceRingBuffer::get_next_spike( const long req_stamp, + bool accumulate_simultaneous, + double& ps_offset, + double& weight, + bool& end_of_refract ) +{ + end_of_refract = false; + if ( deliver_->empty() || refract_ <= deliver_->back() ) + { + if ( refract_.stamp_ == req_stamp ) + { // if relies on stamp_==long::max() if not refractory + // return from refractoriness + ps_offset = refract_.ps_offset_; + weight = 0; + end_of_refract = true; + + // mark as non-refractory + refract_.stamp_ = std::numeric_limits< long >::max(); + return true; + } + else + { + return false; + } + } + else if ( deliver_->back().stamp_ == req_stamp ) + { + // we have an event to deliver + ps_offset = deliver_->back().ps_offset_; + weight = deliver_->back().weight_; + deliver_->pop_back(); + + if ( accumulate_simultaneous ) + { + // add weights of all spikes with same stamp and offset + while ( not deliver_->empty() and deliver_->back().ps_offset_ == ps_offset + and deliver_->back().stamp_ == req_stamp ) + { + weight += deliver_->back().weight_; + deliver_->pop_back(); + } + } + + return true; + } + else + { + // ensure that we are not blocked by spike from the past, cf #404 + assert( deliver_->back().stamp_ > req_stamp ); + return false; + } +} + +inline SliceRingBuffer::SpikeInfo::SpikeInfo( long stamp, + double ps_offset, + double weight ) + : stamp_( stamp ) + , ps_offset_( ps_offset ) + , weight_( weight ) +{ +} + +inline bool SliceRingBuffer::SpikeInfo::operator<( const SpikeInfo& b ) const +{ + return stamp_ == b.stamp_ ? ps_offset_ > b.ps_offset_ : stamp_ < b.stamp_; +} + +inline bool SliceRingBuffer::SpikeInfo::operator<=( const SpikeInfo& b ) const +{ + return not( *this > b ); +} + +inline bool SliceRingBuffer::SpikeInfo::operator>( const SpikeInfo& b ) const +{ + return stamp_ == b.stamp_ ? ps_offset_ < b.ps_offset_ : stamp_ > b.stamp_; +} +} + +#endif From 48955e3e45dfa950d70a4798679323185bc8214a Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Wed, 7 Mar 2018 15:19:37 +0100 Subject: [PATCH 28/48] Fixes for iaf_psc_exp_ps_lossless model. --- precise/iaf_psc_exp_ps_lossless.cpp | 31 ++--- precise/parrot_neuron_ps.cpp | 2 +- testsuite/unittests/test_dcgen_versus_I_e.sli | 108 +++++++++++------- 3 files changed, 81 insertions(+), 60 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index 7680f730c9..f419402580 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -113,6 +113,12 @@ void nest::iaf_psc_exp_ps_lossless::Parameters_::get(DictionaryDatum & d) const double nest::iaf_psc_exp_ps_lossless::Parameters_::set(const DictionaryDatum & d) { + // if E_L_ is changed, we need to adjust all variables defined relative to + // E_L_ + const double ELold = E_L_; + updateValue< double >( d, names::E_L, E_L_ ); + const double delta_EL = E_L_ - ELold; + updateValue(d, names::tau_m, tau_m_); updateValue(d, names::tau_syn_ex, tau_ex_); updateValue(d, names::tau_syn_in, tau_in_); @@ -120,20 +126,6 @@ double nest::iaf_psc_exp_ps_lossless::Parameters_::set(const DictionaryDatum & d updateValue(d, names::t_ref, t_ref_); updateValue(d, names::I_e, I_e_); - // if U0_ is changed, we need to adjust all variables defined relative to U0_ - const double ELold = E_L_; - updateValue(d, names::E_L, E_L_); - const double delta_EL = E_L_ - ELold; - - if(updateValue(d, names::V_reset, U_reset_)) - { - U_reset_ -= E_L_; - } - else - { - U_reset_ -= delta_EL; - } - if (updateValue(d, names::V_th, U_th_)) { U_th_ -= E_L_; @@ -151,7 +143,16 @@ double nest::iaf_psc_exp_ps_lossless::Parameters_::set(const DictionaryDatum & d { U_min_ -= delta_EL; } - + + if(updateValue(d, names::V_reset, U_reset_)) + { + U_reset_ -= E_L_; + } + else + { + U_reset_ -= delta_EL; + } + if ( U_reset_ >= U_th_ ) throw BadProperty("Reset potential must be smaller than threshold."); diff --git a/precise/parrot_neuron_ps.cpp b/precise/parrot_neuron_ps.cpp index 3387f42203..ec2e6ec455 100644 --- a/precise/parrot_neuron_ps.cpp +++ b/precise/parrot_neuron_ps.cpp @@ -77,7 +77,7 @@ parrot_neuron_ps::update( Time const& origin, long const from, long const to ) bool end_of_refract; while ( B_.events_.get_next_spike( - T, ev_offset, ev_multiplicity, end_of_refract ) ) + T, false, ev_offset, ev_multiplicity, end_of_refract ) ) { const unsigned long multiplicity = static_cast< unsigned long >( ev_multiplicity ); diff --git a/testsuite/unittests/test_dcgen_versus_I_e.sli b/testsuite/unittests/test_dcgen_versus_I_e.sli index cf2e6eec11..3008d99be1 100644 --- a/testsuite/unittests/test_dcgen_versus_I_e.sli +++ b/testsuite/unittests/test_dcgen_versus_I_e.sli @@ -49,54 +49,74 @@ M_ERROR setverbosity modeldict keys{ - /model Set - - % check if both membrane potential can be measured.... - /record_VM model GetDefaults /recordables known def - record_VM { model GetDefaults /recordables get { /V_m eq {true} if } forall count 1 eq {; /record_VM true def} {/record_VM false def} ifelse } if - - % .... and I_e can be set - model GetDefaults /I_e known record_VM and - { - - ResetKernel - - % special case for Poisson Neuron - model /pp_psc_delta eq { model << /c_2 0.0 >> SetDefaults} if - - model =only (\t: ) =only - - %executive + /model Set + + % check if both membrane potential can be measured.... + /model_defaults model GetDefaults def + + model_defaults /recordables known + { + /recordable_VM + model GetDefaults /recordables get /V_m MemberQ + def + } + { + /recordable_VM false def + } + ifelse + + % .... and I_e can be set + /has_IE model GetDefaults /I_e known def + + recordable_VM has_IE and + { + model =only (\t: ) =only + + ResetKernel + + % special case + model /pp_psc_delta eq { model << /c_2 0.0 >> SetDefaults} if - model Create /n1 Set - model Create /n2 Set - - %take into account synaptic delay - /dc_generator <> Create /dc Set - - % todo: make sure that this procedure is 'stopped' if and only if current events are not supported! - {dc n1 Connect - - dc << /amplitude amp >> SetStatus - - 100 Simulate - n2 << /I_e amp >> SetStatus - - 300 Simulate - - % compare final membrane potentials - n1 GetStatus /V_m get n2 GetStatus /V_m get eq dup {(pass) =}{ (failed: unequal voltages) = executive } ifelse res exch append /res Set - - } stopped { clear (DC not allowed) = errordict /newerror false put } if % skip models that don't support current event - } - if % model GetDefaults /I_e known record_VM and + model Create /n1 Set + model Create /n2 Set + + %take into account synaptic delay + /dc_generator <> Create /dc Set + + % todo: make sure that this procedure is 'stopped' if and + % only if current events are not supported! + { + dc n1 Connect + dc << /amplitude amp >> SetStatus + 100 Simulate + + n2 << /I_e amp >> SetStatus + 300 Simulate + + % compare final membrane potentials + /V1 n1 GetStatus /V_m get def + /V2 n2 GetStatus /V_m get def + V1 V2 eq + dup + { (pass) = V1 =only ( vs ) =only V2 = } + { (failed: unequal voltages ) =only + V1 =only ( vs ) =only V2 = } + ifelse + res exch append /res Set + } + stopped + { + clear (DC not allowed) = + errordict /newerror false put + } + if % skip models that don't support current event + } + if % recordable_VM has_IE and } forall % modeldict keys % combine results, one bool left on stack res First res Rest { and } Fold +} +assert_or_die - -} assert_or_die endusing - - From 7beb90a2af3ccaab5904ecb835de9134972ac75b Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Wed, 7 Mar 2018 16:25:09 +0100 Subject: [PATCH 29/48] Fixed inconsistent E_L initialization. --- precise/iaf_psc_exp_ps_lossless.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index f419402580..a03cb198a1 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -68,7 +68,7 @@ nest::iaf_psc_exp_ps_lossless::Parameters_::Parameters_() , tau_in_( 2.0 ) // ms , c_m_( 250.0 ) // pF , t_ref_( 2.0 ) // ms - , E_L_( 0.0 ) // mV + , E_L_( -70.0 ) // mV , I_e_( 0.0 ) // pA , U_th_( -55.0 - E_L_ ) // mV, rel to E_L_ , U_min_( -std::numeric_limits< double >::infinity() ) // mV From eb39ca21af8c77ec11ba2a842fd4493ddba0b696 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Wed, 7 Mar 2018 16:25:37 +0100 Subject: [PATCH 30/48] Brought code structure more in line with iaf_psc_exp_ps. --- precise/iaf_psc_exp_ps_lossless.h | 35 ++++++++++++------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.h b/precise/iaf_psc_exp_ps_lossless.h index 5c52186281..c3e52771f4 100644 --- a/precise/iaf_psc_exp_ps_lossless.h +++ b/precise/iaf_psc_exp_ps_lossless.h @@ -20,7 +20,6 @@ * */ - #ifndef IAF_PSC_EXP_PS_LOSSLESS_H #define IAF_PSC_EXP_PS_LOSSLESS_H @@ -35,10 +34,9 @@ #include "connection.h" #include "event.h" #include "nest_types.h" +#include "recordables_map.h" #include "ring_buffer.h" #include "universal_data_logger.h" -#include "stopwatch.h" -#include "arraydatum.h" // Includes from precise: #include "slice_ring_buffer.h" @@ -98,11 +96,7 @@ namespace nest */ class iaf_psc_exp_ps_lossless : public Node { - - class Network; - public: - /** Basic constructor. This constructor should only be used by GenericModel to create model prototype instances. @@ -120,24 +114,21 @@ namespace nest /** * Import sets of overloaded virtual functions. - * We need to explicitly include sets of overloaded - * virtual functions into the current scope. - * According to the SUN C++ FAQ, this is the correct - * way of doing things, although all other compilers - * happily live without. + * @see Technical Issues / Virtual Functions: Overriding, Overloading, and + * Hiding */ - using Node::handles_test_event; using Node::handle; + using Node::handles_test_event; port send_test_event( Node&, rport, synindex, bool ); - void handle(SpikeEvent &); - void handle(CurrentEvent &); - void handle(DataLoggingRequest &); - - port handles_test_event(SpikeEvent &, port); - port handles_test_event(CurrentEvent &, port); - port handles_test_event(DataLoggingRequest &, port); + port handles_test_event( SpikeEvent&, port ); + port handles_test_event( CurrentEvent&, port ); + port handles_test_event( DataLoggingRequest&, port ); + + void handle( SpikeEvent & ); + void handle( CurrentEvent & ); + void handle( DataLoggingRequest & ); bool is_off_grid() const // uses off_grid events { @@ -291,8 +282,8 @@ namespace nest struct State_ { double y0_; //!< External input current - double I_syn_ex_; //!< Exc. exponetial current - double I_syn_in_; //!< Inh. exponetial current + double I_syn_ex_; //!< Exc. exponential current + double I_syn_in_; //!< Inh. exponential current double y2_; //!< Membrane potential (relative to resting potential) bool is_refractory_; //!< True while refractory From c6a074d207fe86e86d5a1813d8671e02082ccfd3 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Wed, 7 Mar 2018 16:44:35 +0100 Subject: [PATCH 31/48] Converted iaf_psc_exp_ps_lossless to Archiving_Node. --- precise/iaf_psc_exp_ps_lossless.cpp | 32 ++++++++--------------------- precise/iaf_psc_exp_ps_lossless.h | 5 +---- 2 files changed, 10 insertions(+), 27 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index a03cb198a1..d9e5c2dd4b 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -207,7 +207,7 @@ void nest::iaf_psc_exp_ps_lossless::State_::set(const DictionaryDatum & d, const * ---------------------------------------------------------------- */ nest::iaf_psc_exp_ps_lossless::iaf_psc_exp_ps_lossless() - : Node(), + : Archiving_Node(), P_(), S_(), B_(*this) @@ -216,7 +216,7 @@ nest::iaf_psc_exp_ps_lossless::iaf_psc_exp_ps_lossless() } nest::iaf_psc_exp_ps_lossless::iaf_psc_exp_ps_lossless(const iaf_psc_exp_ps_lossless & n) - : Node(n), + : Archiving_Node(n), P_(n.P_), S_(n.S_), B_(n.B_, *this) @@ -226,14 +226,6 @@ nest::iaf_psc_exp_ps_lossless::iaf_psc_exp_ps_lossless(const iaf_psc_exp_ps_loss * Node initialization functions * ---------------------------------------------------------------- */ -void nest::iaf_psc_exp_ps_lossless::init_node_(const Node& proto) -{ - const iaf_psc_exp_ps_lossless & pr = downcast(proto); - - P_ = pr.P_; - S_ = pr.S_; -} - void nest::iaf_psc_exp_ps_lossless::init_state_(const Node& proto) { const iaf_psc_exp_ps_lossless & pr = downcast(proto); @@ -471,12 +463,6 @@ void nest::iaf_psc_exp_ps_lossless::handle(DataLoggingRequest &e) // auxiliary functions --------------------------------------------- -inline -void nest::iaf_psc_exp_ps_lossless::set_spiketime(const Time & now) -{ - S_.last_spike_step_ = now.get_steps(); -} - void nest::iaf_psc_exp_ps_lossless::propagate_(const double dt) { const double expm1_tau_ex = numerics::expm1(-dt/P_.tau_ex_); @@ -502,19 +488,18 @@ void nest::iaf_psc_exp_ps_lossless::emit_spike_(const Time & origin, const long // we know that the potential is subthreshold at t0, super at t0+dt // compute spike time relative to beginning of step - const double spike_offset = V_.h_ms_ - (t0 + bisectioning_(dt)); - - set_spiketime(Time::step(origin.get_steps() + lag + 1)); - S_.last_spike_offset_ = spike_offset; + S_.last_spike_step_ = origin.get_steps() + lag + 1; + S_.last_spike_offset_ = V_.h_ms_ - (t0 + bisectioning_(dt)); // reset neuron and make it refractory S_.y2_ = P_.U_reset_; S_.is_refractory_ = true; // send spike + set_spiketime( Time::step( S_.last_spike_step_ ), S_.last_spike_offset_ ); SpikeEvent se; - se.set_offset(spike_offset); + se.set_offset( S_.last_spike_offset_ ); kernel().event_delivery_manager.send(*this, se, lag); } @@ -524,7 +509,7 @@ void nest::iaf_psc_exp_ps_lossless::emit_instant_spike_(const Time & origin, con assert( S_.y2_ >= P_.U_th_ ); // ensure we are superthreshold // set stamp and offset for spike - set_spiketime(Time::step(origin.get_steps() + lag + 1)); + S_.last_spike_step_ = origin.get_steps() + lag + 1; S_.last_spike_offset_ = spike_offs; // reset neuron and make it refractory @@ -532,9 +517,10 @@ void nest::iaf_psc_exp_ps_lossless::emit_instant_spike_(const Time & origin, con S_.is_refractory_ = true; // send spike + set_spiketime( Time::step( S_.last_spike_step_ ), S_.last_spike_offset_ ); SpikeEvent se; - se.set_offset(S_.last_spike_offset_); + se.set_offset( S_.last_spike_offset_ ); kernel().event_delivery_manager.send(*this, se, lag); } diff --git a/precise/iaf_psc_exp_ps_lossless.h b/precise/iaf_psc_exp_ps_lossless.h index c3e52771f4..23002abf74 100644 --- a/precise/iaf_psc_exp_ps_lossless.h +++ b/precise/iaf_psc_exp_ps_lossless.h @@ -94,7 +94,7 @@ namespace nest * from this one. * @todo Implement current input in consistent way. */ - class iaf_psc_exp_ps_lossless : public Node + class iaf_psc_exp_ps_lossless : public Archiving_Node { public: /** Basic constructor. @@ -145,7 +145,6 @@ namespace nest * only through a Node*. */ //@{ - void init_node_(const Node & proto); void init_state_(const Node & proto); void init_buffers_(); void calibrate(); @@ -174,8 +173,6 @@ namespace nest friend class RecordablesMap; friend class UniversalDataLogger; - void set_spiketime(Time const &); - /** * Propagate neuron state. * Propagate the neuron's state by dt. From b23c975569b475e6ca6e10ac6e3e93fd2a1ccf4b Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Wed, 7 Mar 2018 16:57:26 +0100 Subject: [PATCH 32/48] Test for lossless neuron now uses reference data from iaf_psc_exp. --- .../test_iaf_psc_exp_ps_lossless.sli | 121 ++++++++---------- 1 file changed, 56 insertions(+), 65 deletions(-) diff --git a/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli b/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli index 0f1d663d2e..abf88fb79b 100644 --- a/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli +++ b/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli @@ -96,7 +96,7 @@ neuron sp_det 1.0 h Connect vm [/events [/times /V_m]] get cva % array of recorded data 6 ToUnitTestPrecision % to precision of reference - Transpose % all recorded tuples + Transpose dup == % all recorded tuples {First test_times exch MemberQ } Select % those with reference eq % compare } @@ -113,73 +113,64 @@ neuron sp_det 1.0 h Connect [ [ 1 -70 ] %<----- The earliest time dc_gen can be switched on. [ 2 -70 ] %<----- The DC current arrives at the neuron, it is -[ 3 -70 ] % reflected in the neuron's state variable y0, -[ 4 -70 ] % (initial condition) but has not yet affected -% % the membrane potential. -% ... +[ 3 -69.602 ] %<- reflected in the neuron's state variable y0, +[ 4 -69.2079 ] % | (initial condition) but has not yet affected +[ 5 -68.8178 ] % | the membrane potential. +[ 6 -68.4316 ] % | +[ 7 -68.0492 ] % --- the effect of the DC current is visible in the +[ 8 -67.6706 ] % membrane potential +[ 9 -67.2958 ] % +[ 10 -66.9247 ] % % -[ 20 -70 ] -[ 21 -68.9055 ] % the effect of the DC current is visible in the -[ 22 -67.8219 ] % membrane potential -[ 23 -66.749 ] -[ 24 -65.6868 ] -[ 25 -64.6352 ] -[ 26 -63.5941 ] -[ 27 -62.5633 ] -[ 28 -61.5428 ] -[ 29 -60.5324 ] -[ 30 -59.5321 ] -[ 31 -58.5418 ] -[ 32 -57.5612 ] -[ 33 -56.5905 ] -[ 34 -55.6294 ] -[ 35 -54.6779 ] %<---- The membrane potential crossed threshold in the -[ 36 -70 ] % step 3.5 ms -> 3.6 ms. The membrane potential is -[ 37 -70 ] % reset (no super-threshold values can be observed) -% % The spike is reported at 3.6 ms % ... % -[ 50 -70 ] -[ 51 -70 ] -[ 52 -70 ] -[ 53 -70 ] -[ 54 -70 ] -[ 55 -70 ] -[ 56 -68.9055 ] %<---- The membrane potential starts to increase again -[ 57 -67.8219 ] % and the neuron generates spikes again -[ 58 -66.749 ] -[ 59 -65.6868 ] -[ 60 -64.6352 ] -[ 61 -63.5941 ] -[ 62 -62.5633 ] -[ 63 -61.5428 ] -[ 64 -60.5324 ] -[ 65 -59.5321 ] -[ 66 -58.5418 ] -[ 67 -57.5612 ] -[ 68 -56.5905 ] -[ 69 -55.6294 ] -[ 70 -54.6779 ] -[ 71 -70 ] -[ 72 -70 ] -[ 73 -70 ] -[ 74 -70 ] -[ 75 -70 ] -[ 76 -70 ] -[ 77 -70 ] -[ 78 -70 ] -[ 79 -70 ] %<---- The last point in time in which the membrane potential -] % is clamped. - % The simulation was run for 8.0 ms. However, in the step - % 7.9 ms -> 8.0 ms the voltmeter necessarily receives the - % voltages that occurred at time 7.9 ms (delay h). This - % results in different end times of the recorded voltage - % traces at different resolutions. In the current - % simulation kernel there is no general cure for this - % problem. One workaround is to end the simulation script - % with "h Simulate", thereby making the script resolution - % dependent. - +[ 45 -56.0204 ] % +[ 46 -55.7615 ] % +[ 47 -55.5051 ] % +[ 48 -55.2513 ] % +[ 49 -55.0001 ] % +[ 50 -70 ] % <---- The membrane potential crossed threshold in the +[ 51 -70 ] % step 4.9 ms -> 5.0 ms. The membrane potential is +[ 52 -70 ] % reset (no super-threshold values can be observed). +[ 53 -70 ] % The spike is reported at 5.0 ms +[ 54 -70 ] % +[ 55 -70 ] % +[ 56 -70 ] % +[ 57 -70 ] % +[ 58 -70 ] % +[ 59 -70 ] % +[ 60 -70 ] % +[ 61 -70 ] % +[ 62 -70 ] % +[ 63 -70 ] % +[ 64 -70 ] % +[ 65 -70 ] % +[ 66 -70 ] % +[ 67 -70 ] % +[ 68 -70 ] % +[ 69 -70 ] % +[ 70 -70 ] % <---- The last point in time at which the membrane potential +[ 71 -69.602 ] % <- is clamped. The fact that the neuron is not refractory +[ 72 -69.2079 ] % | anymore is reflected in the state variable r==0. +[ 73 -68.8178 ] % | The neuron was refractory for 2.0 ms. +[ 74 -68.4316 ] % | +[ 75 -68.0492 ] % --- The membrane potential starts to increase +[ 76 -67.6706 ] % immediately afterwards and the neuron can generate +[ 77 -67.2958 ] % spikes again (at this resolution reported with time +[ 78 -66.9247 ] % stamp 7.1 ms on the grid) +[ 79 -66.5572 ] % <-- +] % | +% | +% - The simulation was run for 8.0 ms. However, in the step +% 7.9 ms -> 8.0 ms the voltmeter necessarily receives the +% voltages that occurred at time 7.9 ms (delay h). This +% results in different end times of the recorded voltage +% traces at different resolutions. In the current +% simulation kernel there is no general cure for this +% problem. One workaround is to end the simulation script +% with "h Simulate", thereby making the script resolution +% dependent. +% exch assert_or_die From b4f8926f4e444112d2da81247839189df7abb4fc Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Wed, 7 Mar 2018 17:01:13 +0100 Subject: [PATCH 33/48] Updated literature reference. --- precise/iaf_psc_exp_ps_lossless.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.h b/precise/iaf_psc_exp_ps_lossless.h index 23002abf74..84027bcc0a 100644 --- a/precise/iaf_psc_exp_ps_lossless.h +++ b/precise/iaf_psc_exp_ps_lossless.h @@ -74,8 +74,10 @@ meets the threshold. V_reset double - Reset value for the membrane potential. References: -[1] J.Krishnan, P.G.L.Porta Mana, M.Helias, M.Diesmann, E.Di.Napoli -(2017) Perfect spike detection via time reversal, arXiv:1706.05702. +[1] Krishnan J, Porta Mana P, Helias M, Diesmann M and Di Napoli E + (2018) Perfect Detection of Spikes in the Linear Sub-threshold + Dynamics of Point Neurons. Front. Neuroinform. 11:75. + doi: 10.3389/fninf.2017.00075 Author: Jeyashree Krishnan From f566c040d92e05a46dbf7173cd6e041134235a6b Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Wed, 7 Mar 2018 17:07:01 +0100 Subject: [PATCH 34/48] Fixed formatting issues. --- precise/iaf_psc_exp_ps_lossless.cpp | 650 ++++++++++++++------------ precise/iaf_psc_exp_ps_lossless.h | 679 +++++++++++++++------------- precise/precisemodule.cpp | 2 +- 3 files changed, 720 insertions(+), 611 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index d9e5c2dd4b..3cfbcf1198 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -37,31 +37,33 @@ #include "arraydatum.h" -/* ---------------------------------------------------------------- +/* ---------------------------------------------------------------- * Recordables map * ---------------------------------------------------------------- */ -nest::RecordablesMap nest::iaf_psc_exp_ps_lossless::recordablesMap_; +nest::RecordablesMap< nest::iaf_psc_exp_ps_lossless > + nest::iaf_psc_exp_ps_lossless::recordablesMap_; namespace nest { - // Override the create() method with one call to RecordablesMap::insert_() - // for each quantity to be recorded. - template <> - void RecordablesMap::create() - { - // use standard names whereever you can for consistency! - insert_(names::V_m, & iaf_psc_exp_ps_lossless::get_V_m_); - insert_(names::I_syn, & iaf_psc_exp_ps_lossless::get_I_syn_); - insert_(names::I_syn_ex, & iaf_psc_exp_ps_lossless::get_I_syn_ex_); - insert_(names::I_syn_in, &iaf_psc_exp_ps_lossless::get_I_syn_in_); - } +// Override the create() method with one call to RecordablesMap::insert_() +// for each quantity to be recorded. +template <> +void +RecordablesMap< iaf_psc_exp_ps_lossless >::create() +{ + // use standard names whereever you can for consistency! + insert_( names::V_m, &iaf_psc_exp_ps_lossless::get_V_m_ ); + insert_( names::I_syn, &iaf_psc_exp_ps_lossless::get_I_syn_ ); + insert_( names::I_syn_ex, &iaf_psc_exp_ps_lossless::get_I_syn_ex_ ); + insert_( names::I_syn_in, &iaf_psc_exp_ps_lossless::get_I_syn_in_ ); +} } -/* ---------------------------------------------------------------- +/* ---------------------------------------------------------------- * Default constructors defining default parameters and state * ---------------------------------------------------------------- */ - + nest::iaf_psc_exp_ps_lossless::Parameters_::Parameters_() : tau_m_( 10.0 ) // ms , tau_ex_( 2.0 ) // ms @@ -77,41 +79,47 @@ nest::iaf_psc_exp_ps_lossless::Parameters_::Parameters_() } nest::iaf_psc_exp_ps_lossless::State_::State_() - : y0_(0.0), - I_syn_ex_(0.0), - I_syn_in_(0.0), - y2_(0.0), - is_refractory_(false), - last_spike_step_(-1), - last_spike_offset_(0.0) -{} - -nest::iaf_psc_exp_ps_lossless::Buffers_::Buffers_(iaf_psc_exp_ps_lossless & n) - : logger_(n) -{} - -nest::iaf_psc_exp_ps_lossless::Buffers_::Buffers_(const Buffers_ &, iaf_psc_exp_ps_lossless & n) - : logger_(n) -{} - -/* ---------------------------------------------------------------- + : y0_( 0.0 ) + , I_syn_ex_( 0.0 ) + , I_syn_in_( 0.0 ) + , y2_( 0.0 ) + , is_refractory_( false ) + , last_spike_step_( -1 ) + , last_spike_offset_( 0.0 ) +{ +} + +nest::iaf_psc_exp_ps_lossless::Buffers_::Buffers_( iaf_psc_exp_ps_lossless& n ) + : logger_( n ) +{ +} + +nest::iaf_psc_exp_ps_lossless::Buffers_::Buffers_( const Buffers_&, + iaf_psc_exp_ps_lossless& n ) + : logger_( n ) +{ +} + +/* ---------------------------------------------------------------- * Parameter and state extractions and manipulation functions * ---------------------------------------------------------------- */ -void nest::iaf_psc_exp_ps_lossless::Parameters_::get(DictionaryDatum & d) const +void +nest::iaf_psc_exp_ps_lossless::Parameters_::get( DictionaryDatum& d ) const { - def(d, names::E_L, E_L_); - def(d, names::I_e, I_e_); - def(d, names::V_th, U_th_+E_L_); - def(d, names::V_min, U_min_+E_L_); - def(d, names::V_reset, U_reset_+E_L_); - def(d, names::C_m, c_m_); - def(d, names::tau_m, tau_m_); - def(d, names::tau_syn_ex, tau_ex_); - def(d, names::tau_syn_in, tau_in_); - def(d, names::t_ref, t_ref_); + def< double >( d, names::E_L, E_L_ ); + def< double >( d, names::I_e, I_e_ ); + def< double >( d, names::V_th, U_th_ + E_L_ ); + def< double >( d, names::V_min, U_min_ + E_L_ ); + def< double >( d, names::V_reset, U_reset_ + E_L_ ); + def< double >( d, names::C_m, c_m_ ); + def< double >( d, names::tau_m, tau_m_ ); + def< double >( d, names::tau_syn_ex, tau_ex_ ); + def< double >( d, names::tau_syn_in, tau_in_ ); + def< double >( d, names::t_ref, t_ref_ ); } -double nest::iaf_psc_exp_ps_lossless::Parameters_::set(const DictionaryDatum & d) +double +nest::iaf_psc_exp_ps_lossless::Parameters_::set( const DictionaryDatum& d ) { // if E_L_ is changed, we need to adjust all variables defined relative to // E_L_ @@ -119,14 +127,14 @@ double nest::iaf_psc_exp_ps_lossless::Parameters_::set(const DictionaryDatum & d updateValue< double >( d, names::E_L, E_L_ ); const double delta_EL = E_L_ - ELold; - updateValue(d, names::tau_m, tau_m_); - updateValue(d, names::tau_syn_ex, tau_ex_); - updateValue(d, names::tau_syn_in, tau_in_); - updateValue(d, names::C_m, c_m_); - updateValue(d, names::t_ref, t_ref_); - updateValue(d, names::I_e, I_e_); + updateValue< double >( d, names::tau_m, tau_m_ ); + updateValue< double >( d, names::tau_syn_ex, tau_ex_ ); + updateValue< double >( d, names::tau_syn_in, tau_in_ ); + updateValue< double >( d, names::C_m, c_m_ ); + updateValue< double >( d, names::t_ref, t_ref_ ); + updateValue< double >( d, names::I_e, I_e_ ); - if (updateValue(d, names::V_th, U_th_)) + if ( updateValue< double >( d, names::V_th, U_th_ ) ) { U_th_ -= E_L_; } @@ -135,7 +143,7 @@ double nest::iaf_psc_exp_ps_lossless::Parameters_::set(const DictionaryDatum & d U_th_ -= delta_EL; } - if (updateValue(d, names::V_min, U_min_)) + if ( updateValue< double >( d, names::V_min, U_min_ ) ) { U_min_ -= E_L_; } @@ -144,7 +152,7 @@ double nest::iaf_psc_exp_ps_lossless::Parameters_::set(const DictionaryDatum & d U_min_ -= delta_EL; } - if(updateValue(d, names::V_reset, U_reset_)) + if ( updateValue< double >( d, names::V_reset, U_reset_ ) ) { U_reset_ -= E_L_; } @@ -154,42 +162,61 @@ double nest::iaf_psc_exp_ps_lossless::Parameters_::set(const DictionaryDatum & d } if ( U_reset_ >= U_th_ ) - throw BadProperty("Reset potential must be smaller than threshold."); - + { + throw BadProperty( "Reset potential must be smaller than threshold." ); + } + if ( U_reset_ < U_min_ ) - throw BadProperty("Reset potential must be greater equal minimum potential."); - + { + throw BadProperty( + "Reset potential must be greater equal minimum potential." ); + } + if ( c_m_ <= 0 ) - throw BadProperty("Capacitance must be strictly positive."); - + { + throw BadProperty( "Capacitance must be strictly positive." ); + } + if ( t_ref_ < 0 ) - throw BadProperty("Refractory time must not be negative."); - + { + throw BadProperty( "Refractory time must not be negative." ); + } + if ( tau_m_ <= 0 || tau_ex_ <= 0 || tau_in_ <= 0 ) - throw BadProperty("All time constants must be strictly positive."); + { + throw BadProperty( "All time constants must be strictly positive." ); + } - if ( tau_m_ == tau_ex_ || tau_m_ == tau_in_ ) - throw BadProperty("Membrane and synapse time constant(s) must differ." - "See note in documentation."); + if ( tau_m_ == tau_ex_ || tau_m_ == tau_in_ ) + { + throw BadProperty( + "Membrane and synapse time constant(s) must differ." + "See note in documentation." ); + } - return delta_EL; + return delta_EL; } -void nest::iaf_psc_exp_ps_lossless::State_::get(DictionaryDatum & d, - const Parameters_ & p) const +void +nest::iaf_psc_exp_ps_lossless::State_::get( DictionaryDatum& d, + const Parameters_& p ) const { - def(d, names::V_m, y2_ + p.E_L_); // Membrane potential - def(d, names::is_refractory, is_refractory_); - def(d, names::t_spike, Time(Time::step(last_spike_step_)).get_ms()); - def(d, names::offset, last_spike_offset_); - def(d, names::I_syn_ex, I_syn_ex_); - def(d, names::I_syn_in, I_syn_in_); // y1 state - def(d, names::I_syn, I_syn_ex_ + I_syn_in_); + def< double >( d, names::V_m, y2_ + p.E_L_ ); // Membrane potential + def< bool >( d, names::is_refractory, is_refractory_ ); + def< double >( + d, names::t_spike, Time( Time::step( last_spike_step_ ) ).get_ms() ); + def< double >( d, names::offset, last_spike_offset_ ); + def< double >( d, names::I_syn_ex, I_syn_ex_ ); + def< double >( d, names::I_syn_in, I_syn_in_ ); // y1 state + def< double >( d, names::I_syn, I_syn_ex_ + I_syn_in_ ); } -void nest::iaf_psc_exp_ps_lossless::State_::set(const DictionaryDatum & d, const Parameters_ & p, double delta_EL) +void +nest::iaf_psc_exp_ps_lossless::State_::set( const DictionaryDatum& d, + const Parameters_& p, + double delta_EL ) { - if ( updateValue(d, names::V_m, y2_) ) + if ( updateValue< double >( d, names::V_m, y2_ ) ) { y2_ -= p.E_L_; } @@ -198,97 +225,112 @@ void nest::iaf_psc_exp_ps_lossless::State_::set(const DictionaryDatum & d, const y2_ -= delta_EL; } - updateValue(d, names::I_syn_ex, I_syn_ex_ ); - updateValue(d, names::I_syn_in, I_syn_in_); + updateValue< double >( d, names::I_syn_ex, I_syn_ex_ ); + updateValue< double >( d, names::I_syn_in, I_syn_in_ ); } -/* ---------------------------------------------------------------- +/* ---------------------------------------------------------------- * Default and copy constructor for node * ---------------------------------------------------------------- */ nest::iaf_psc_exp_ps_lossless::iaf_psc_exp_ps_lossless() - : Archiving_Node(), - P_(), - S_(), - B_(*this) + : Archiving_Node() + , P_() + , S_() + , B_( *this ) { recordablesMap_.create(); } -nest::iaf_psc_exp_ps_lossless::iaf_psc_exp_ps_lossless(const iaf_psc_exp_ps_lossless & n) - : Archiving_Node(n), - P_(n.P_), - S_(n.S_), - B_(n.B_, *this) -{} +nest::iaf_psc_exp_ps_lossless::iaf_psc_exp_ps_lossless( + const iaf_psc_exp_ps_lossless& n ) + : Archiving_Node( n ) + , P_( n.P_ ) + , S_( n.S_ ) + , B_( n.B_, *this ) +{ +} -/* ---------------------------------------------------------------- +/* ---------------------------------------------------------------- * Node initialization functions * ---------------------------------------------------------------- */ -void nest::iaf_psc_exp_ps_lossless::init_state_(const Node& proto) +void +nest::iaf_psc_exp_ps_lossless::init_state_( const Node& proto ) { - const iaf_psc_exp_ps_lossless & pr = downcast(proto); + const iaf_psc_exp_ps_lossless& pr = + downcast< iaf_psc_exp_ps_lossless >( proto ); S_ = pr.S_; } -void nest::iaf_psc_exp_ps_lossless::init_buffers_() +void +nest::iaf_psc_exp_ps_lossless::init_buffers_() { B_.events_.resize(); - B_.events_.clear(); - B_.currents_.clear(); // includes resize + B_.events_.clear(); + B_.currents_.clear(); // includes resize B_.logger_.reset(); } -void nest::iaf_psc_exp_ps_lossless::calibrate() +void +nest::iaf_psc_exp_ps_lossless::calibrate() { - B_.logger_.init(); // ensures initialization in case mm connected after Simulate - + B_.logger_ + .init(); // ensures initialization in case mm connected after Simulate + V_.h_ms_ = Time::get_resolution().get_ms(); - - V_.expm1_tau_m_ = numerics::expm1(-V_.h_ms_/P_.tau_m_); - V_.expm1_tau_ex_ = numerics::expm1(-V_.h_ms_/P_.tau_ex_); - V_.expm1_tau_in_ = numerics::expm1(-V_.h_ms_/P_.tau_in_); - V_.P20_ = -P_.tau_m_ / P_.c_m_ * V_.expm1_tau_m_; - V_.P21_ex_ = -P_.tau_m_*P_.tau_ex_ / (P_.tau_m_-P_.tau_ex_) / P_.c_m_ * (V_.expm1_tau_ex_-V_.expm1_tau_m_); - V_.P21_in_ = -P_.tau_m_*P_.tau_in_ / (P_.tau_m_-P_.tau_in_) / P_.c_m_ * (V_.expm1_tau_in_-V_.expm1_tau_m_); - V_.refractory_steps_ = Time(Time::ms(P_.t_ref_)).get_steps(); - assert( V_.refractory_steps_ >= 0 ); // since t_ref_ >= 0, this can only fail in error + + V_.expm1_tau_m_ = numerics::expm1( -V_.h_ms_ / P_.tau_m_ ); + V_.expm1_tau_ex_ = numerics::expm1( -V_.h_ms_ / P_.tau_ex_ ); + V_.expm1_tau_in_ = numerics::expm1( -V_.h_ms_ / P_.tau_in_ ); + V_.P20_ = -P_.tau_m_ / P_.c_m_ * V_.expm1_tau_m_; + V_.P21_ex_ = -P_.tau_m_ * P_.tau_ex_ / ( P_.tau_m_ - P_.tau_ex_ ) / P_.c_m_ + * ( V_.expm1_tau_ex_ - V_.expm1_tau_m_ ); + V_.P21_in_ = -P_.tau_m_ * P_.tau_in_ / ( P_.tau_m_ - P_.tau_in_ ) / P_.c_m_ + * ( V_.expm1_tau_in_ - V_.expm1_tau_m_ ); + V_.refractory_steps_ = Time( Time::ms( P_.t_ref_ ) ).get_steps(); + assert( V_.refractory_steps_ + >= 0 ); // since t_ref_ >= 0, this can only fail in error V_.a1_ = P_.tau_m_ * P_.tau_ex_; - V_.a2_ = P_.tau_m_ * (P_.tau_m_ - P_.tau_ex_); - V_.a3_ = P_.c_m_ * P_.U_th_ * (P_.tau_m_ - P_.tau_ex_); - V_.a4_ = P_.c_m_ * (P_.tau_m_ - P_.tau_ex_); + V_.a2_ = P_.tau_m_ * ( P_.tau_m_ - P_.tau_ex_ ); + V_.a3_ = P_.c_m_ * P_.U_th_ * ( P_.tau_m_ - P_.tau_ex_ ); + V_.a4_ = P_.c_m_ * ( P_.tau_m_ - P_.tau_ex_ ); V_.b1_ = -P_.tau_m_ * P_.tau_m_; V_.b2_ = P_.tau_m_ * P_.tau_ex_; - V_.b3_ = P_.tau_m_*P_.c_m_*P_.U_th_; - V_.b4_ = -P_.c_m_*(P_.tau_m_ - P_.tau_ex_); - - V_.c1_ = P_.tau_m_/P_.c_m_; - V_.c2_ = (-P_.tau_m_ * P_.tau_ex_)/(P_.c_m_*(P_.tau_m_ - P_.tau_ex_)); - V_.c3_ = (P_.tau_m_ * P_.tau_m_)/ (P_.c_m_ * (P_.tau_m_ - P_.tau_ex_)); - V_.c4_ = P_.tau_ex_/P_.tau_m_; - V_.c5_ = (P_.c_m_ * P_.U_th_)/P_.tau_m_; - V_.c6_ = 1-(P_.tau_ex_/P_.tau_m_); + V_.b3_ = P_.tau_m_ * P_.c_m_ * P_.U_th_; + V_.b4_ = -P_.c_m_ * ( P_.tau_m_ - P_.tau_ex_ ); + + V_.c1_ = P_.tau_m_ / P_.c_m_; + V_.c2_ = + ( -P_.tau_m_ * P_.tau_ex_ ) / ( P_.c_m_ * ( P_.tau_m_ - P_.tau_ex_ ) ); + V_.c3_ = ( P_.tau_m_ * P_.tau_m_ ) / ( P_.c_m_ * ( P_.tau_m_ - P_.tau_ex_ ) ); + V_.c4_ = P_.tau_ex_ / P_.tau_m_; + V_.c5_ = ( P_.c_m_ * P_.U_th_ ) / P_.tau_m_; + V_.c6_ = 1 - ( P_.tau_ex_ / P_.tau_m_ ); } -/* ---------------------------------------------------------------- +/* ---------------------------------------------------------------- * Update and spike handling functions * ---------------------------------------------------------------- */ -void nest::iaf_psc_exp_ps_lossless::update(const Time & origin, - const long from, const long to) +void +nest::iaf_psc_exp_ps_lossless::update( const Time& origin, + const long from, + const long to ) { - assert ( to >= 0 ); + 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 @@ -296,142 +338,147 @@ void nest::iaf_psc_exp_ps_lossless::update(const Time & origin, */ if ( S_.y2_ >= P_.U_th_ ) { - emit_instant_spike_(origin, from, - V_.h_ms_*(1.0-std::numeric_limits::epsilon())); + emit_instant_spike_( origin, + from, + V_.h_ms_ * ( 1.0 - 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; - + // 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_); - + 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 approximation - V_.y0_before_ = S_.y0_; + V_.y0_before_ = S_.y0_; V_.I_syn_ex_before_ = S_.I_syn_ex_; V_.I_syn_in_before_ = S_.I_syn_in_; - V_.y2_before_ = S_.y2_; - + V_.y2_before_ = S_.y2_; + // 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) ) + 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 ( !S_.is_refractory_ ) + if ( not S_.is_refractory_ ) { - S_.y2_ = V_.P20_*(P_.I_e_+S_.y0_) + V_.P21_ex_*S_.I_syn_ex_ + V_.P21_in_*S_.I_syn_in_ + V_.expm1_tau_m_*S_.y2_ + S_.y2_; - - // lower bound of membrane potential - S_.y2_ = ( S_.y2_ < P_.U_min_ ? P_.U_min_ : S_.y2_ ); + S_.y2_ = V_.P20_ * ( P_.I_e_ + S_.y0_ ) + V_.P21_ex_ * S_.I_syn_ex_ + + V_.P21_in_ * S_.I_syn_in_ + V_.expm1_tau_m_ * S_.y2_ + S_.y2_; + + // lower bound of membrane potential + S_.y2_ = ( S_.y2_ < P_.U_min_ ? P_.U_min_ : S_.y2_ ); } - + // update synaptic currents - S_.I_syn_ex_ = S_.I_syn_ex_*V_.expm1_tau_ex_ + S_.I_syn_ex_; - S_.I_syn_in_ = S_.I_syn_in_*V_.expm1_tau_in_ + S_.I_syn_in_; - + S_.I_syn_ex_ = S_.I_syn_ex_ * V_.expm1_tau_ex_ + S_.I_syn_ex_; + S_.I_syn_in_ = S_.I_syn_in_ * V_.expm1_tau_in_ + S_.I_syn_in_; + /* 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. + since the spike-time interpolation within emit_spike_ depends + on all state variables having their values at the end of the + interval. */ const double spike_time_max = is_spike_( V_.h_ms_ ); if ( not numerics::is_nan( spike_time_max ) ) { - emit_spike_(origin, lag, 0, spike_time_max); + emit_spike_( origin, lag, 0, spike_time_max ); } - } else { - // We only get here if there is at least on event, - // which has been read above. We can therefore use + // 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 last_offset = V_.h_ms_; // start of step - + double last_offset = V_.h_ms_; // start of step + do { - // time is measured backward: inverse order in difference - const double 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 + // time is measured backward: inverse order in difference + const double 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 const double spike_time_max = is_spike_( ministep ); - - if ( not numerics::is_nan( spike_time_max ) ) - { - emit_spike_(origin, lag, V_.h_ms_-last_offset, spike_time_max ); - } - - // handle event - if ( end_of_refract ) + + if ( not numerics::is_nan( spike_time_max ) ) + { + emit_spike_( origin, lag, V_.h_ms_ - last_offset, spike_time_max ); + } + + // handle event + if ( end_of_refract ) { - S_.is_refractory_ = false; // return from refractoriness + S_.is_refractory_ = false; // return from refractoriness } - else - { - if ( ev_weight >= 0.0 ) + else + { + if ( ev_weight >= 0.0 ) { - S_.I_syn_ex_ += ev_weight; // exc. spike input + S_.I_syn_ex_ += ev_weight; // exc. spike input } - else + else { - S_.I_syn_in_ += ev_weight; // inh. spike input + S_.I_syn_in_ += ev_weight; // inh. spike input } - } - - // store state - V_.I_syn_ex_before_ = S_.I_syn_ex_; - V_.I_syn_in_before_ = S_.I_syn_in_; - V_.y2_before_ = S_.y2_; - last_offset = ev_offset; - } - while ( B_.events_.get_next_spike(T, true, ev_offset, ev_weight, - end_of_refract) ); - - // no events remaining, plain update step across remainder + } + + // store state + V_.I_syn_ex_before_ = S_.I_syn_ex_; + V_.I_syn_in_before_ = S_.I_syn_in_; + V_.y2_before_ = S_.y2_; + last_offset = ev_offset; + } 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 ( last_offset > 0 ) // not at end of step, do remainder + if ( last_offset > 0 ) // not at end of step, do remainder { const double spike_time_max = is_spike_( last_offset ); - propagate_(last_offset); - if ( not numerics::is_nan( spike_time_max ) ) - { - emit_spike_(origin, lag, V_.h_ms_-last_offset, spike_time_max); - } + propagate_( last_offset ); + if ( not numerics::is_nan( spike_time_max ) ) + { + emit_spike_( origin, lag, V_.h_ms_ - last_offset, spike_time_max ); + } } - } // else - + } // else + // Set new input current. The current change occurs at the // end of the interval and thus must come AFTER the threshold- // crossing approximation - S_.y0_ = B_.currents_.get_value(lag); - + S_.y0_ = B_.currents_.get_value( lag ); + // log state data - B_.logger_.record_data(origin.get_steps() + lag); - } // for + B_.logger_.record_data( origin.get_steps() + lag ); + } // for } // function handles exact spike times -void nest::iaf_psc_exp_ps_lossless::handle(SpikeEvent & e) +void +nest::iaf_psc_exp_ps_lossless::handle( SpikeEvent& e ) { assert( e.get_delay() > 0 ); @@ -441,11 +488,16 @@ void nest::iaf_psc_exp_ps_lossless::handle(SpikeEvent & e) */ const long Tdeliver = e.get_stamp().get_steps() + e.get_delay() - 1; - B_.events_.add_spike(e.get_rel_delivery_steps(nest::kernel().simulation_manager.get_slice_origin()), - Tdeliver, e.get_offset(), e.get_weight() * e.get_multiplicity()); + B_.events_.add_spike( + e.get_rel_delivery_steps( + nest::kernel().simulation_manager.get_slice_origin() ), + Tdeliver, + e.get_offset(), + e.get_weight() * e.get_multiplicity() ); } -void nest::iaf_psc_exp_ps_lossless::handle(CurrentEvent & e) +void +nest::iaf_psc_exp_ps_lossless::handle( CurrentEvent& e ) { assert( e.get_delay() > 0 ); @@ -453,141 +505,167 @@ void nest::iaf_psc_exp_ps_lossless::handle(CurrentEvent & e) const double w = e.get_weight(); // add weighted current; HEP 2002-10-04 - B_.currents_.add_value(e.get_rel_delivery_steps(nest::kernel().simulation_manager.get_slice_origin() ), w * c); + B_.currents_.add_value( + e.get_rel_delivery_steps( + nest::kernel().simulation_manager.get_slice_origin() ), + w * c ); } -void nest::iaf_psc_exp_ps_lossless::handle(DataLoggingRequest &e) +void +nest::iaf_psc_exp_ps_lossless::handle( DataLoggingRequest& e ) { - B_.logger_.handle(e); + B_.logger_.handle( e ); } // auxiliary functions --------------------------------------------- -void nest::iaf_psc_exp_ps_lossless::propagate_(const double dt) +void +nest::iaf_psc_exp_ps_lossless::propagate_( const double dt ) { - const double expm1_tau_ex = numerics::expm1(-dt/P_.tau_ex_); - const double expm1_tau_in = numerics::expm1(-dt/P_.tau_in_); + const double expm1_tau_ex = numerics::expm1( -dt / P_.tau_ex_ ); + const double expm1_tau_in = numerics::expm1( -dt / P_.tau_in_ ); if ( not S_.is_refractory_ ) { - const double expm1_tau_m = numerics::expm1(-dt/P_.tau_m_); - - const double P20 = -P_.tau_m_ / P_.c_m_ * expm1_tau_m; - const double P21_ex = -P_.tau_m_*P_.tau_ex_ / (P_.tau_m_-P_.tau_ex_) / P_.c_m_ * (expm1_tau_ex-expm1_tau_m); - const double P21_in = -P_.tau_m_*P_.tau_in_ / (P_.tau_m_-P_.tau_in_) / P_.c_m_ * (expm1_tau_in-expm1_tau_m); - - S_.y2_ = P20*(P_.I_e_+S_.y0_) + P21_ex*S_.I_syn_ex_ + P21_in*S_.I_syn_in_ + expm1_tau_m*S_.y2_ + S_.y2_; + const double expm1_tau_m = numerics::expm1( -dt / P_.tau_m_ ); + + const double P20 = -P_.tau_m_ / P_.c_m_ * expm1_tau_m; + const double P21_ex = -P_.tau_m_ * P_.tau_ex_ / ( P_.tau_m_ - P_.tau_ex_ ) + / P_.c_m_ * ( expm1_tau_ex - expm1_tau_m ); + const double P21_in = -P_.tau_m_ * P_.tau_in_ / ( P_.tau_m_ - P_.tau_in_ ) + / P_.c_m_ * ( expm1_tau_in - expm1_tau_m ); + + S_.y2_ = P20 * ( P_.I_e_ + S_.y0_ ) + P21_ex * S_.I_syn_ex_ + + P21_in * S_.I_syn_in_ + expm1_tau_m * S_.y2_ + S_.y2_; } - S_.I_syn_ex_ = S_.I_syn_ex_*expm1_tau_ex + S_.I_syn_ex_; - S_.I_syn_in_ = S_.I_syn_in_*expm1_tau_in + S_.I_syn_in_; + S_.I_syn_ex_ = S_.I_syn_ex_ * expm1_tau_ex + S_.I_syn_ex_; + S_.I_syn_in_ = S_.I_syn_in_ * expm1_tau_in + S_.I_syn_in_; } -void nest::iaf_psc_exp_ps_lossless::emit_spike_(const Time & origin, const long lag, const double t0, const double dt) +void +nest::iaf_psc_exp_ps_lossless::emit_spike_( const Time& origin, + const long lag, + const double t0, + const double dt ) { // we know that the potential is subthreshold at t0, super at t0+dt - + // compute spike time relative to beginning of step S_.last_spike_step_ = origin.get_steps() + lag + 1; - S_.last_spike_offset_ = V_.h_ms_ - (t0 + bisectioning_(dt)); - + S_.last_spike_offset_ = V_.h_ms_ - ( t0 + bisectioning_( dt ) ); + // reset neuron and make it refractory S_.y2_ = P_.U_reset_; S_.is_refractory_ = true; - + // send spike set_spiketime( Time::step( S_.last_spike_step_ ), S_.last_spike_offset_ ); SpikeEvent se; - + se.set_offset( S_.last_spike_offset_ ); - kernel().event_delivery_manager.send(*this, se, lag); + kernel().event_delivery_manager.send( *this, se, lag ); } -void nest::iaf_psc_exp_ps_lossless::emit_instant_spike_(const Time & origin, const long lag, - const double spike_offs) +void +nest::iaf_psc_exp_ps_lossless::emit_instant_spike_( const Time& origin, + const long lag, + const double spike_offs ) { - assert( S_.y2_ >= P_.U_th_ ); // ensure we are superthreshold - + assert( S_.y2_ >= P_.U_th_ ); // ensure we are superthreshold + // set stamp and offset for spike S_.last_spike_step_ = origin.get_steps() + lag + 1; S_.last_spike_offset_ = spike_offs; - + // reset neuron and make it refractory S_.y2_ = P_.U_reset_; S_.is_refractory_ = true; - + // send spike set_spiketime( Time::step( S_.last_spike_step_ ), S_.last_spike_offset_ ); SpikeEvent se; - + se.set_offset( S_.last_spike_offset_ ); - kernel().event_delivery_manager.send(*this, se, lag); + kernel().event_delivery_manager.send( *this, se, lag ); } -inline double nest::iaf_psc_exp_ps_lossless::bisectioning_(const double dt) const -{ +inline double +nest::iaf_psc_exp_ps_lossless::bisectioning_( const double dt ) const +{ double root = 0.0; double y2_root = V_.y2_before_; double div = 2.0; - while ( fabs(P_.U_th_-y2_root) > 1e-14 and (dt/div > 0.0) ) + while ( fabs( P_.U_th_ - y2_root ) > 1e-14 and ( dt / div > 0.0 ) ) { if ( y2_root > P_.U_th_ ) { - root -= dt/div; + root -= dt / div; } else { - root += dt/div; + root += dt / div; } div *= 2.0; - - const double expm1_tau_ex = numerics::expm1(-root/P_.tau_ex_); - const double expm1_tau_in = numerics::expm1(-root/P_.tau_in_); - const double expm1_tau_m = numerics::expm1(-root/P_.tau_m_); - - const double P20 = -P_.tau_m_ / P_.c_m_ * expm1_tau_m; - const double P21_ex = -P_.tau_m_*P_.tau_ex_ / (P_.tau_m_-P_.tau_ex_) / P_.c_m_ * (expm1_tau_ex-expm1_tau_m); - const double P21_in = -P_.tau_m_*P_.tau_in_ / (P_.tau_m_-P_.tau_in_) / P_.c_m_ * (expm1_tau_in-expm1_tau_m); - - y2_root = P20*(P_.I_e_+V_.y0_before_) + P21_ex*V_.I_syn_ex_before_ + P21_in*V_.I_syn_in_before_ + expm1_tau_m*V_.y2_before_ + V_.y2_before_; + + const double expm1_tau_ex = numerics::expm1( -root / P_.tau_ex_ ); + const double expm1_tau_in = numerics::expm1( -root / P_.tau_in_ ); + const double expm1_tau_m = numerics::expm1( -root / P_.tau_m_ ); + + const double P20 = -P_.tau_m_ / P_.c_m_ * expm1_tau_m; + const double P21_ex = -P_.tau_m_ * P_.tau_ex_ / ( P_.tau_m_ - P_.tau_ex_ ) + / P_.c_m_ * ( expm1_tau_ex - expm1_tau_m ); + const double P21_in = -P_.tau_m_ * P_.tau_in_ / ( P_.tau_m_ - P_.tau_in_ ) + / P_.c_m_ * ( expm1_tau_in - expm1_tau_m ); + + y2_root = P20 * ( P_.I_e_ + V_.y0_before_ ) + P21_ex * V_.I_syn_ex_before_ + + P21_in * V_.I_syn_in_before_ + expm1_tau_m * V_.y2_before_ + + V_.y2_before_; } return root; } -double nest::iaf_psc_exp_ps_lossless::is_spike_(const double dt) +double +nest::iaf_psc_exp_ps_lossless::is_spike_( const double dt ) { - const double I_0 = V_.I_syn_ex_before_ + V_.I_syn_in_before_; - const double V_0 = V_.y2_before_; - const double exp_tau_s = numerics::expm1(dt/P_.tau_ex_) ; - const double exp_tau_m = numerics::expm1(dt/P_.tau_m_) ; - const double exp_tau_m_s = numerics::expm1(dt/P_.tau_m_ - dt/P_.tau_ex_); - - double g = ((V_.a1_ * I_0 * exp_tau_m_s + exp_tau_m * (V_.a3_ - P_.I_e_ * V_.a2_) + V_.a3_)/V_.a4_) ; - - //no-spike, NS_1, (V <= f_h,I_e(I) and V < g_h,I_e(I)) - if((V_0 <= (((I_0 + P_.I_e_)*(V_.b1_ * exp_tau_m + V_.b2_* exp_tau_s) + V_.b3_*(exp_tau_m - exp_tau_s))/( V_.b4_ * exp_tau_s))) - and (V_0 < g)) - { - return numerics::nan; - } - - //spike, S_1, V >= g_h,I_e(I) - else if (V_0 >= g ) - { - return dt; - } - //no-spike, NS_2, V < b(I) - else if(V_0 < (V_.c1_ * P_.I_e_ + V_.c2_ * I_0 + V_.c3_* std::pow(I_0, V_.c4_) * std::pow((V_.c5_ - P_.I_e_), V_.c6_))) - { - return numerics::nan; - } + const double I_0 = V_.I_syn_ex_before_ + V_.I_syn_in_before_; + const double V_0 = V_.y2_before_; + const double exp_tau_s = numerics::expm1( dt / P_.tau_ex_ ); + const double exp_tau_m = numerics::expm1( dt / P_.tau_m_ ); + const double exp_tau_m_s = + numerics::expm1( dt / P_.tau_m_ - dt / P_.tau_ex_ ); + + double g = + ( ( V_.a1_ * I_0 * exp_tau_m_s + exp_tau_m * ( V_.a3_ - P_.I_e_ * V_.a2_ ) + + V_.a3_ ) / V_.a4_ ); + + // no-spike, NS_1, (V <= f_h,I_e(I) and V < g_h,I_e(I)) + if ( ( V_0 + <= ( ( ( I_0 + P_.I_e_ ) * ( V_.b1_ * exp_tau_m + V_.b2_ * exp_tau_s ) + + V_.b3_ * ( exp_tau_m - exp_tau_s ) ) + / ( V_.b4_ * exp_tau_s ) ) ) and ( V_0 < g ) ) + { + return numerics::nan; + } + + // spike, S_1, V >= g_h,I_e(I) + else if ( V_0 >= g ) + { + return dt; + } + // no-spike, NS_2, V < b(I) + else if ( V_0 < ( V_.c1_ * P_.I_e_ + V_.c2_ * I_0 + + V_.c3_ * std::pow( I_0, V_.c4_ ) + * std::pow( ( V_.c5_ - P_.I_e_ ), V_.c6_ ) ) ) + { + return numerics::nan; + } else - //missed spike detected, S_2 - { - return (V_.a1_ / P_.tau_m_ * P_.tau_ex_ ) * std::log ( V_.b1_ * I_0 / (V_.a2_ * P_.I_e_ - V_.a1_ * I_0 - V_.a4_ * V_0 ) ); - } + // missed spike detected, S_2 + { + return ( V_.a1_ / P_.tau_m_ * P_.tau_ex_ ) + * std::log( V_.b1_ * I_0 + / ( V_.a2_ * P_.I_e_ - V_.a1_ * I_0 - V_.a4_ * V_0 ) ); + } } - - - diff --git a/precise/iaf_psc_exp_ps_lossless.h b/precise/iaf_psc_exp_ps_lossless.h index 84027bcc0a..89a06fcc90 100644 --- a/precise/iaf_psc_exp_ps_lossless.h +++ b/precise/iaf_psc_exp_ps_lossless.h @@ -50,7 +50,7 @@ predicts exact number of spikes by applying state space analysis Description: iaf_psc_exp_ps_lossless is the precise state space implementation of the leaky integrate-and-fire model neuron with exponential postsynaptic currents -that uses time reversal to detect spikes [1]. This is the most exact +that uses time reversal to detect spikes [1]. This is the most exact implementation available. Time-reversed state space analysis provides a general method to solve the @@ -58,9 +58,9 @@ threshold-detection problem for an integrable, affine or linear time evolution. This method is based on the idea of propagating the threshold backwards in time, and see whether it meets the initial state, rather than propagating the initial state forward in time and see whether it -meets the threshold. +meets the threshold. -Parameters: +Parameters: The following parameters can be set in the status dictionary. E_L double - Resting membrane potential in mV. C_m double - Specific capacitance of the membrane in pF/mum^2. @@ -72,7 +72,7 @@ meets the threshold. I_e double - Constant input current in pA. V_min double - Absolute lower value for the membrane potential. V_reset double - Reset value for the membrane potential. - + References: [1] Krishnan J, Porta Mana P, Helias M, Diesmann M and Di Napoli E (2018) Perfect Detection of Spikes in the Linear Sub-threshold @@ -86,370 +86,401 @@ Sends: SpikeEvent Receives: SpikeEvent, CurrentEvent, DataLoggingRequest SeeAlso: iaf_psc_exp_ps -*/ +*/ namespace nest { +/** + * Leaky iaf neuron, exponential PSC synapses, canonical implementation. + * @note Inherit privately from Node, so no classes can be derived + * from this one. + * @todo Implement current input in consistent way. + */ +class iaf_psc_exp_ps_lossless : public Archiving_Node +{ +public: + /** Basic constructor. + This constructor should only be used by GenericModel to create + model prototype instances. + */ + iaf_psc_exp_ps_lossless(); + + /** Copy constructor. + GenericModel::allocate_() uses the copy constructor to clone + actual model instances from the prototype instance. + + @note The copy constructor MUST NOT be used to create nodes based + on nodes that have been placed in the network. + */ + iaf_psc_exp_ps_lossless( const iaf_psc_exp_ps_lossless& ); + /** - * Leaky iaf neuron, exponential PSC synapses, canonical implementation. - * @note Inherit privately from Node, so no classes can be derived - * from this one. - * @todo Implement current input in consistent way. + * Import sets of overloaded virtual functions. + * @see Technical Issues / Virtual Functions: Overriding, Overloading, and + * Hiding */ - class iaf_psc_exp_ps_lossless : public Archiving_Node + using Node::handle; + using Node::handles_test_event; + + port send_test_event( Node&, rport, synindex, bool ); + + port handles_test_event( SpikeEvent&, port ); + port handles_test_event( CurrentEvent&, port ); + port handles_test_event( DataLoggingRequest&, port ); + + void handle( SpikeEvent& ); + void handle( CurrentEvent& ); + void handle( DataLoggingRequest& ); + + bool is_off_grid() const // uses off_grid events { - public: - /** Basic constructor. - This constructor should only be used by GenericModel to create - model prototype instances. - */ - iaf_psc_exp_ps_lossless(); - - /** Copy constructor. - GenericModel::allocate_() uses the copy constructor to clone - actual model instances from the prototype instance. - - @note The copy constructor MUST NOT be used to create nodes based - on nodes that have been placed in the network. - */ - iaf_psc_exp_ps_lossless(const iaf_psc_exp_ps_lossless &); - - /** - * Import sets of overloaded virtual functions. - * @see Technical Issues / Virtual Functions: Overriding, Overloading, and - * Hiding - */ - using Node::handle; - using Node::handles_test_event; - - port send_test_event( Node&, rport, synindex, bool ); - - port handles_test_event( SpikeEvent&, port ); - port handles_test_event( CurrentEvent&, port ); - port handles_test_event( DataLoggingRequest&, port ); - - void handle( SpikeEvent & ); - void handle( CurrentEvent & ); - void handle( DataLoggingRequest & ); - - bool is_off_grid() const // uses off_grid events - { - return true; - } - - void get_status(DictionaryDatum &) const; - void set_status(const DictionaryDatum &); - - private: - - /** @name Interface functions - * @note These functions are private, so that they can be accessed - * only through a Node*. - */ - //@{ - void init_state_(const Node & proto); - void init_buffers_(); - void calibrate(); + return true; + } - /** - * Time Evolution Operator. - * - * update() promotes the state of the neuron from origin+from to origin+to. - * It does so in steps of the resolution h. Within each step, time is - * advanced from event to event, as retrieved from the spike queue. - * - * Return from refractoriness is handled as a special event in the - * queue, which is marked by a weight that is GSL_NAN. This greatly simplifies - * the code. - * - * For steps, during which no events occur, the precomputed propagator matrix - * is used. For other steps, the propagator matrix is computed as needed. - * - * While the neuron is refractory, membrane potential (y2_) is - * clamped to U_reset_. - */ - void update(Time const & origin, const long from, const long to); - //@} - - // The next two classes need to be friends to access the State_ class/member - friend class RecordablesMap; - friend class UniversalDataLogger; - - /** - * Propagate neuron state. - * Propagate the neuron's state by dt. - * @param dt Interval over which to propagate - */ - void propagate_(const double dt); - - /** - * Emit a single spike caused by DC current in absence of spike input. - * Emits a single spike and reset neuron given that the membrane - * potential was below threshold at the beginning of a mini-timestep - * and above afterwards. - * - * @param origin Time stamp at beginning of slice - * @param lag Time step within slice - * @param t0 Beginning of mini-timestep - * @param dt Duration of mini-timestep - */ - void emit_spike_(const Time & origin, const long lag, - const double t0, const double dt); - - /** - * Emit a single spike at a precisely given time. - * - * @param origin Time stamp at beginning of slice - * @param lag Time step within slice - * @param spike_offset Time offset for spike - */ - void emit_instant_spike_(const Time & origin, const long lag, - const double spike_offset); - - /** - * Localize threshold crossing by bisectioning. - * @param double length of interval since previous event - * @returns time from previous event to threshold crossing - */ - double bisectioning_(const double dt) const; + void get_status( DictionaryDatum& ) const; + void set_status( const DictionaryDatum& ); + +private: + /** @name Interface functions + * @note These functions are private, so that they can be accessed + * only through a Node*. + */ + //@{ + void init_state_( const Node& proto ); + void init_buffers_(); + void calibrate(); + + /** + * Time Evolution Operator. + * + * update() promotes the state of the neuron from origin+from to origin+to. + * It does so in steps of the resolution h. Within each step, time is + * advanced from event to event, as retrieved from the spike queue. + * + * Return from refractoriness is handled as a special event in the + * queue, which is marked by a weight that is GSL_NAN. This greatly + *simplifies + * the code. + * + * For steps, during which no events occur, the precomputed propagator matrix + * is used. For other steps, the propagator matrix is computed as needed. + * + * While the neuron is refractory, membrane potential (y2_) is + * clamped to U_reset_. + */ + void update( Time const& origin, const long from, const long to ); + //@} + + // The next two classes need to be friends to access the State_ class/member + friend class RecordablesMap< iaf_psc_exp_ps_lossless >; + friend class UniversalDataLogger< iaf_psc_exp_ps_lossless >; + + /** + * Propagate neuron state. + * Propagate the neuron's state by dt. + * @param dt Interval over which to propagate + */ + void propagate_( const double dt ); + + /** + * Emit a single spike caused by DC current in absence of spike input. + * Emits a single spike and reset neuron given that the membrane + * potential was below threshold at the beginning of a mini-timestep + * and above afterwards. + * + * @param origin Time stamp at beginning of slice + * @param lag Time step within slice + * @param t0 Beginning of mini-timestep + * @param dt Duration of mini-timestep + */ + void emit_spike_( const Time& origin, + const long lag, + const double t0, + const double dt ); + + /** + * Emit a single spike at a precisely given time. + * + * @param origin Time stamp at beginning of slice + * @param lag Time step within slice + * @param spike_offset Time offset for spike + */ + void emit_instant_spike_( const Time& origin, + const long lag, + const double spike_offset ); + + /** + * Localize threshold crossing by bisectioning. + * @param double length of interval since previous event + * @returns time from previous event to threshold crossing + */ + double bisectioning_( const double dt ) const; + + /** + * Retrospective spike detection by state space analysis. + * The state space spanning the non-spiking region is bound by the following + * system of inequalities: + * threshold line V < \theta, envelope, V < b(I_e), line corresponding to the + * final timestep + * V < f(h, I) (or) linear approximation of the envelope, V < g(h, I_e). + * The state space spanning the spiking region is bound by the following + * system of inequalities: + * threshold line V < \theta, envelope, V > b(I_e) and line corresponding to + * the final timestep + * V > f(h, I) (or) linear approximation of the envelope, V < g(h, I_e). + * Propagate the neuron's state by dt. + * @returns time to emit spike. + */ + double is_spike_( const double ); + + // ---------------------------------------------------------------- + + /** + * Independent parameters of the model. + */ + struct Parameters_ + { + /** Membrane time constant in ms. */ + double tau_m_; + + /** Time constant of exc. synaptic current in ms. */ + double tau_ex_; + + /** Time constant of inh. synaptic current in ms. */ + double tau_in_; + + /** Membrane capacitance in pF. */ + double c_m_; + + /** Refractory period in ms. */ + double t_ref_; + + /** Resting potential in mV. */ + double E_L_; + + /** External DC current [pA] */ + double I_e_; + + /** Threshold, RELATIVE TO RESTING POTENTAIL(!). + I.e. the real threshold is U_th_ + E_L_. */ + double U_th_; + + /** Lower bound, RELATIVE TO RESTING POTENTAIL(!). + I.e. the real lower bound is U_min_+E_L_. */ + double U_min_; + + /** Reset potential. + At threshold crossing, the membrane potential is reset to this value. + Relative to resting potential. */ + double U_reset_; + + Parameters_(); //!< Sets default parameter values + + void get( DictionaryDatum& ) const; //!< Store current values in dictionary + double set( const DictionaryDatum& ); //!< Set values from dicitonary + }; + + // ---------------------------------------------------------------- + + /** + * State variables of the model. + */ + struct State_ + { + double y0_; //!< External input current + double I_syn_ex_; //!< Exc. exponential current + double I_syn_in_; //!< Inh. exponential current + double y2_; //!< Membrane potential (relative to resting potential) + + bool is_refractory_; //!< True while refractory + long last_spike_step_; //!< Time stamp of most recent spike + double last_spike_offset_; //!< Offset of most recent spike + + State_(); //!< Default initialization + + void get( DictionaryDatum&, const Parameters_& ) const; + void set( const DictionaryDatum&, const Parameters_&, double delta_EL ); + }; + + // ---------------------------------------------------------------- + + /** + * Buffers of the model. + */ + struct Buffers_ + { + Buffers_( iaf_psc_exp_ps_lossless& ); + Buffers_( const Buffers_&, iaf_psc_exp_ps_lossless& ); /** - * Retrospective spike detection by state space analysis. - * The state space spanning the non-spiking region is bound by the following system of inequalities: - * threshold line V < \theta, envelope, V < b(I_e), line corresponding to the final timestep - * V < f(h, I) (or) linear approximation of the envelope, V < g(h, I_e). - * The state space spanning the spiking region is bound by the following system of inequalities: - * threshold line V < \theta, envelope, V > b(I_e) and line corresponding to the final timestep - * V > f(h, I) (or) linear approximation of the envelope, V < g(h, I_e). - * Propagate the neuron's state by dt. - * @returns time to emit spike. + * Queue for incoming events. + * @note Handles also pseudo-events marking return from refractoriness. */ - double is_spike_(const double); + SliceRingBuffer events_; + RingBuffer currents_; - // ---------------------------------------------------------------- + //! Logger for all analog data + UniversalDataLogger< iaf_psc_exp_ps_lossless > logger_; + }; - /** - * Independent parameters of the model. - */ - struct Parameters_ - { - /** Membrane time constant in ms. */ - double tau_m_; - - /** Time constant of exc. synaptic current in ms. */ - double tau_ex_; - - /** Time constant of inh. synaptic current in ms. */ - double tau_in_; - - /** Membrane capacitance in pF. */ - double c_m_; - - /** Refractory period in ms. */ - double t_ref_; - - /** Resting potential in mV. */ - double E_L_; - - /** External DC current [pA] */ - double I_e_; - - /** Threshold, RELATIVE TO RESTING POTENTAIL(!). - I.e. the real threshold is U_th_ + E_L_. */ - double U_th_; - - /** Lower bound, RELATIVE TO RESTING POTENTAIL(!). - I.e. the real lower bound is U_min_+E_L_. */ - double U_min_; - - /** Reset potential. - At threshold crossing, the membrane potential is reset to this value. - Relative to resting potential. */ - double U_reset_; - - Parameters_(); //!< Sets default parameter values - - void get(DictionaryDatum &) const; //!< Store current values in dictionary - double set(const DictionaryDatum &); //!< Set values from dicitonary - }; - - // ---------------------------------------------------------------- + // ---------------------------------------------------------------- + + /** + * Internal variables of the model. + */ + struct Variables_ + { + double h_ms_; //!< Time resolution [ms] + long refractory_steps_; //!< Refractory time in steps + double expm1_tau_m_; //!< exp(-h/tau_m) - 1 + double expm1_tau_ex_; //!< exp(-h/tau_ex) - 1 + double expm1_tau_in_; //!< exp(-h/tau_in) - 1 + double P20_; //!< Progagator matrix element, 2nd row + double P21_in_; //!< Progagator matrix element, 2nd row + double P21_ex_; //!< Progagator matrix element, 2nd row + double y0_before_; //!< y0_ at beginning of ministep + double I_syn_ex_before_; //!< y1_ at beginning of ministep + double I_syn_in_before_; //!< y1_ at beginning of ministep + double y2_before_; //!< y2_ at beginning of ministep + double bisection_step_; //!< if missed spike is detected, + //!< calculate time to emit spike /** - * State variables of the model. + * Pre-computed constants for inequality V < g(h, I_e) */ - struct State_ - { - double y0_; //!< External input current - double I_syn_ex_; //!< Exc. exponential current - double I_syn_in_; //!< Inh. exponential current - double y2_; //!< Membrane potential (relative to resting potential) - - bool is_refractory_; //!< True while refractory - long last_spike_step_; //!< Time stamp of most recent spike - double last_spike_offset_; //!< Offset of most recent spike - - State_(); //!< Default initialization - - void get(DictionaryDatum &, const Parameters_ &) const; - void set(const DictionaryDatum &, const Parameters_ &, double delta_EL); - }; - - // ---------------------------------------------------------------- + //@{ + double a1_; + double a2_; + double a3_; + double a4_; + //@} /** - * Buffers of the model. + * Pre-computed constants for inequality V < f(h, I) */ - struct Buffers_ - { - Buffers_(iaf_psc_exp_ps_lossless &); - Buffers_(const Buffers_ &, iaf_psc_exp_ps_lossless &); - - /** - * Queue for incoming events. - * @note Handles also pseudo-events marking return from refractoriness. - */ - SliceRingBuffer events_; - RingBuffer currents_; - - //! Logger for all analog data - UniversalDataLogger logger_; - }; - - // ---------------------------------------------------------------- + //@{ + double b1_; + double b2_; + double b3_; + double b4_; + //@} /** - * Internal variables of the model. + * Pre-computed constants for inequality V < b(I_e) */ - struct Variables_ - { - double h_ms_; //!< Time resolution [ms] - long refractory_steps_; //!< Refractory time in steps - double expm1_tau_m_; //!< exp(-h/tau_m) - 1 - double expm1_tau_ex_; //!< exp(-h/tau_ex) - 1 - double expm1_tau_in_; //!< exp(-h/tau_in) - 1 - double P20_; //!< Progagator matrix element, 2nd row - double P21_in_; //!< Progagator matrix element, 2nd row - double P21_ex_; //!< Progagator matrix element, 2nd row - double y0_before_; //!< y0_ at beginning of ministep - double I_syn_ex_before_; //!< y1_ at beginning of ministep - double I_syn_in_before_; //!< y1_ at beginning of ministep - double y2_before_; //!< y2_ at beginning of ministep - double bisection_step_; //!< if missed spike is detected, calculate time to emit spike - - /** - * Pre-computed constants for inequality V < g(h, I_e) - */ - //@{ - double a1_; - double a2_; - double a3_; - double a4_; - //@} - - /** - * Pre-computed constants for inequality V < f(h, I) - */ - //@{ - double b1_; - double b2_; - double b3_; - double b4_; - //@} - - /** - * Pre-computed constants for inequality V < b(I_e) - */ - //@{ - double c1_; - double c2_; - double c3_; - double c4_; - double c5_; - double c6_; - //@} - }; - - // Access functions for UniversalDataLogger ------------------------------- - - //! Read out the real membrane potential - double get_V_m_() const { return S_.y2_ + P_.E_L_; } - double get_I_syn_() const { return S_.I_syn_ex_ + S_.I_syn_in_; } - double get_I_syn_ex_() const { return S_.I_syn_ex_; } - double get_I_syn_in_() const { return S_.I_syn_in_; } - // ---------------------------------------------------------------- - - /** - * @defgroup iaf_psc_exp_ps_lossless_data - * Instances of private data structures for the different types - * of data pertaining to the model. - * @note The order of definitions is important for speed. - * @{ - */ - Parameters_ P_; - State_ S_; - Variables_ V_; - Buffers_ B_; - /** @} */ - - //! Mapping of recordables names to access functions - static RecordablesMap recordablesMap_; + //@{ + double c1_; + double c2_; + double c3_; + double c4_; + double c5_; + double c6_; + //@} }; - -inline -port iaf_psc_exp_ps_lossless::send_test_event( Node& target, rport receptor_type, synindex, bool ) + + // Access functions for UniversalDataLogger ------------------------------- + + //! Read out the real membrane potential + double + get_V_m_() const + { + return S_.y2_ + P_.E_L_; + } + double + get_I_syn_() const + { + return S_.I_syn_ex_ + S_.I_syn_in_; + } + double + get_I_syn_ex_() const + { + return S_.I_syn_ex_; + } + double + get_I_syn_in_() const + { + return S_.I_syn_in_; + } + // ---------------------------------------------------------------- + + /** + * @defgroup iaf_psc_exp_ps_lossless_data + * Instances of private data structures for the different types + * of data pertaining to the model. + * @note The order of definitions is important for speed. + * @{ + */ + Parameters_ P_; + State_ S_; + Variables_ V_; + Buffers_ B_; + /** @} */ + + //! Mapping of recordables names to access functions + static RecordablesMap< iaf_psc_exp_ps_lossless > recordablesMap_; +}; + +inline port +iaf_psc_exp_ps_lossless::send_test_event( Node& target, + rport receptor_type, + synindex, + bool ) { - SpikeEvent e; - e.set_sender(*this); + SpikeEvent e; + e.set_sender( *this ); return target.handles_test_event( e, receptor_type ); } -inline -port iaf_psc_exp_ps_lossless::handles_test_event(SpikeEvent &, port receptor_type) +inline port +iaf_psc_exp_ps_lossless::handles_test_event( SpikeEvent&, port receptor_type ) { - if (receptor_type != 0) - throw UnknownReceptorType(receptor_type, get_name()); + if ( receptor_type != 0 ) + { + throw UnknownReceptorType( receptor_type, get_name() ); + } return 0; } -inline -port iaf_psc_exp_ps_lossless::handles_test_event(CurrentEvent &, port receptor_type) +inline port +iaf_psc_exp_ps_lossless::handles_test_event( CurrentEvent&, port receptor_type ) { - if (receptor_type != 0) - throw UnknownReceptorType(receptor_type, get_name()); + if ( receptor_type != 0 ) + { + throw UnknownReceptorType( receptor_type, get_name() ); + } return 0; } -inline -port iaf_psc_exp_ps_lossless::handles_test_event(DataLoggingRequest & dlr, - port receptor_type) +inline port +iaf_psc_exp_ps_lossless::handles_test_event( DataLoggingRequest& dlr, + port receptor_type ) { - if (receptor_type != 0) - throw UnknownReceptorType(receptor_type, get_name()); - return B_.logger_.connect_logging_device(dlr, recordablesMap_); + if ( receptor_type != 0 ) + { + throw UnknownReceptorType( receptor_type, get_name() ); + } + return B_.logger_.connect_logging_device( dlr, recordablesMap_ ); } -inline -void iaf_psc_exp_ps_lossless::get_status(DictionaryDatum & d) const +inline void +iaf_psc_exp_ps_lossless::get_status( DictionaryDatum& d ) const { - P_.get(d); - S_.get(d, P_); - (*d)[names::recordables] = recordablesMap_.get_list(); + P_.get( d ); + S_.get( d, P_ ); + ( *d )[ names::recordables ] = recordablesMap_.get_list(); } -inline -void iaf_psc_exp_ps_lossless::set_status(const DictionaryDatum & d) +inline void +iaf_psc_exp_ps_lossless::set_status( const DictionaryDatum& d ) { - Parameters_ ptmp = P_; // temporary copy in case of errors - double delta_EL = ptmp.set(d); // throws if BadProperty - State_ stmp = S_; // temporary copy in case of errors - stmp.set(d, ptmp, delta_EL); // throws if BadProperty + Parameters_ ptmp = P_; // temporary copy in case of errors + double delta_EL = ptmp.set( d ); // throws if BadProperty + State_ stmp = S_; // temporary copy in case of errors + stmp.set( d, ptmp, delta_EL ); // throws if BadProperty // if we get here, temporaries contain consistent set of properties P_ = ptmp; S_ = stmp; -} +} } // namespace #endif // IAF_PSC_EXP_PS_LOSSLESS_H - - diff --git a/precise/precisemodule.cpp b/precise/precisemodule.cpp index c38102f416..7aae7e463f 100644 --- a/precise/precisemodule.cpp +++ b/precise/precisemodule.cpp @@ -54,7 +54,7 @@ #include "iaf_psc_exp_ps.h" #include "parrot_neuron_ps.h" #include "poisson_generator_ps.h" -#include "iaf_psc_exp_ps_lossless.h" +#include "iaf_psc_exp_ps_lossless.h" namespace nest { From b39e3a25533113db22cc0771a699f3da5dc9e5bc Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Fri, 9 Mar 2018 10:27:04 +0100 Subject: [PATCH 35/48] Corrected comments and improved variable names. --- precise/iaf_psc_alpha_canon.h | 5 ++-- precise/iaf_psc_alpha_presc.h | 5 ++-- precise/iaf_psc_exp_ps_lossless.cpp | 24 +++++++++---------- precise/iaf_psc_exp_ps_lossless.h | 12 ++++------ .../test_iaf_psc_exp_ps_lossless.sli | 2 +- 5 files changed, 21 insertions(+), 27 deletions(-) diff --git a/precise/iaf_psc_alpha_canon.h b/precise/iaf_psc_alpha_canon.h index a85a176157..c346552001 100644 --- a/precise/iaf_psc_alpha_canon.h +++ b/precise/iaf_psc_alpha_canon.h @@ -102,9 +102,8 @@ The following parameters can be set in the status dictionary. 0-none, 1-linear, 2-quadratic, 3-cubic Remarks: -If tau_m is very close to tau_syn_ex or tau_syn_in, the model -will numerically behave as if tau_m is equal to tau_syn_ex or -tau_syn_in, respectively, to avoid numerical instabilities. +If tau_m is very close to tau_syn, the model will numerically behave as +if tau_m is equal to tau_syn, to avoid numerical instabilities. For details, please see IAF_Neruons_Singularity.ipynb in the NEST source code (docs/model_details). diff --git a/precise/iaf_psc_alpha_presc.h b/precise/iaf_psc_alpha_presc.h index c3ee1641c0..84cfbe2b20 100644 --- a/precise/iaf_psc_alpha_presc.h +++ b/precise/iaf_psc_alpha_presc.h @@ -81,9 +81,8 @@ in addition to the on-grid spike times. Remarks: - If tau_m is very close to tau_syn_ex or tau_syn_in, the model - will numerically behave as if tau_m is equal to tau_syn_ex or - tau_syn_in, respectively, to avoid numerical instabilities. + If tau_m is very close to tau_syn, the model will numerically behave + as if tau_m is equal to tau_syn, to avoid numerical instabilities. For details, please see IAF_Neruons_Singularity.ipynb in the NEST source code (docs/model_details). diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index 3cfbcf1198..65f6b2c9e2 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -123,9 +123,9 @@ nest::iaf_psc_exp_ps_lossless::Parameters_::set( const DictionaryDatum& d ) { // if E_L_ is changed, we need to adjust all variables defined relative to // E_L_ - const double ELold = E_L_; + const double E_L_old = E_L_; updateValue< double >( d, names::E_L, E_L_ ); - const double delta_EL = E_L_ - ELold; + const double delta_E_L = E_L_ - E_L_old; updateValue< double >( d, names::tau_m, tau_m_ ); updateValue< double >( d, names::tau_syn_ex, tau_ex_ ); @@ -140,7 +140,7 @@ nest::iaf_psc_exp_ps_lossless::Parameters_::set( const DictionaryDatum& d ) } else { - U_th_ -= delta_EL; + U_th_ -= delta_E_L; } if ( updateValue< double >( d, names::V_min, U_min_ ) ) @@ -149,7 +149,7 @@ nest::iaf_psc_exp_ps_lossless::Parameters_::set( const DictionaryDatum& d ) } else { - U_min_ -= delta_EL; + U_min_ -= delta_E_L; } if ( updateValue< double >( d, names::V_reset, U_reset_ ) ) @@ -158,7 +158,7 @@ nest::iaf_psc_exp_ps_lossless::Parameters_::set( const DictionaryDatum& d ) } else { - U_reset_ -= delta_EL; + U_reset_ -= delta_E_L; } if ( U_reset_ >= U_th_ ) @@ -169,7 +169,7 @@ nest::iaf_psc_exp_ps_lossless::Parameters_::set( const DictionaryDatum& d ) if ( U_reset_ < U_min_ ) { throw BadProperty( - "Reset potential must be greater equal minimum potential." ); + "Reset potential must be greater than or equal to minimum potential." ); } if ( c_m_ <= 0 ) @@ -194,7 +194,7 @@ nest::iaf_psc_exp_ps_lossless::Parameters_::set( const DictionaryDatum& d ) "See note in documentation." ); } - return delta_EL; + return delta_E_L; } void @@ -207,7 +207,7 @@ nest::iaf_psc_exp_ps_lossless::State_::get( DictionaryDatum& d, d, names::t_spike, Time( Time::step( last_spike_step_ ) ).get_ms() ); def< double >( d, names::offset, last_spike_offset_ ); def< double >( d, names::I_syn_ex, I_syn_ex_ ); - def< double >( d, names::I_syn_in, I_syn_in_ ); // y1 state + def< double >( d, names::I_syn_in, I_syn_in_ ); def< double >( d, names::I_syn, I_syn_ex_ + I_syn_in_ ); } @@ -276,8 +276,8 @@ nest::iaf_psc_exp_ps_lossless::init_buffers_() void nest::iaf_psc_exp_ps_lossless::calibrate() { - B_.logger_ - .init(); // ensures initialization in case mm connected after Simulate + // ensures initialization in case mm connected after Simulate + B_.logger_.init(); V_.h_ms_ = Time::get_resolution().get_ms(); @@ -368,7 +368,7 @@ nest::iaf_psc_exp_ps_lossless::update( const Time& origin, bool end_of_refract; if ( not B_.events_.get_next_spike( - T, true, ev_offset, ev_weight, end_of_refract ) ) + T, false, ev_offset, ev_weight, end_of_refract ) ) { // No incoming spikes, handle with fixed propagator matrix. // Handling this case separately improves performance significantly @@ -451,7 +451,7 @@ nest::iaf_psc_exp_ps_lossless::update( const Time& origin, V_.y2_before_ = S_.y2_; last_offset = ev_offset; } while ( B_.events_.get_next_spike( - T, true, ev_offset, ev_weight, end_of_refract ) ); + T, false, ev_offset, ev_weight, end_of_refract ) ); // no events remaining, plain update step across remainder // of interval diff --git a/precise/iaf_psc_exp_ps_lossless.h b/precise/iaf_psc_exp_ps_lossless.h index 89a06fcc90..d2a842d28d 100644 --- a/precise/iaf_psc_exp_ps_lossless.h +++ b/precise/iaf_psc_exp_ps_lossless.h @@ -91,9 +91,7 @@ SeeAlso: iaf_psc_exp_ps namespace nest { /** - * Leaky iaf neuron, exponential PSC synapses, canonical implementation. - * @note Inherit privately from Node, so no classes can be derived - * from this one. + * Leaky iaf neuron, exponential PSC synapses, lossless implementation. * @todo Implement current input in consistent way. */ class iaf_psc_exp_ps_lossless : public Archiving_Node @@ -159,8 +157,7 @@ class iaf_psc_exp_ps_lossless : public Archiving_Node * * Return from refractoriness is handled as a special event in the * queue, which is marked by a weight that is GSL_NAN. This greatly - *simplifies - * the code. + * simplifies the code. * * For steps, during which no events occur, the precomputed propagator matrix * is used. For other steps, the propagator matrix is computed as needed. @@ -177,7 +174,6 @@ class iaf_psc_exp_ps_lossless : public Archiving_Node /** * Propagate neuron state. - * Propagate the neuron's state by dt. * @param dt Interval over which to propagate */ void propagate_( const double dt ); @@ -339,8 +335,8 @@ class iaf_psc_exp_ps_lossless : public Archiving_Node double P21_in_; //!< Progagator matrix element, 2nd row double P21_ex_; //!< Progagator matrix element, 2nd row double y0_before_; //!< y0_ at beginning of ministep - double I_syn_ex_before_; //!< y1_ at beginning of ministep - double I_syn_in_before_; //!< y1_ at beginning of ministep + double I_syn_ex_before_; //!< I_syn_ex_ at beginning of ministep + double I_syn_in_before_; //!< I_syn_in_ at beginning of ministep double y2_before_; //!< y2_ at beginning of ministep double bisection_step_; //!< if missed spike is detected, //!< calculate time to emit spike diff --git a/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli b/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli index abf88fb79b..491eed0111 100644 --- a/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli +++ b/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli @@ -21,7 +21,7 @@ */ /* BeginDocumentation - Name: testsuite::test_iaf_psc_exp_ps_lossless - sli script for overall test of iaf_psc_exp model + Name: testsuite::test_iaf_psc_exp_ps_lossless - sli script for overall test of iaf_psc_exp_ps_lossless model Synopsis: (test_iaf_psc_exp_ps_lossless) run -> compares response to current step with reference data From effae6c144729773cab5d04332447e5dda9fe8af Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Wed, 14 Mar 2018 15:43:04 +0100 Subject: [PATCH 36/48] Removed trailing whitespace. --- testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli b/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli index 491eed0111..8373060075 100644 --- a/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli +++ b/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli @@ -1,5 +1,5 @@ /* - * test_iaf_psc_exp_ps_lossless.sli + * test_iaf_psc_exp_ps_lossless.sli * * This file is part of NEST. * From c9aeeab6167f3c575e96a41211d0fe2d7772d5c0 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Tue, 20 Mar 2018 15:35:21 +0100 Subject: [PATCH 37/48] Skip membrane potential propagation and spike check if two spikes arrive simultaneously. --- precise/iaf_psc_exp_ps_lossless.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index 65f6b2c9e2..ae0c3b0beb 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -414,18 +414,20 @@ nest::iaf_psc_exp_ps_lossless::update( const Time& origin, { // time is measured backward: inverse order in difference const double ministep = last_offset - ev_offset; + assert( ministep >= 0.0 ); + if ( ministep > 0.0 ) + { + propagate_( ministep ); - propagate_( ministep ); - - // check for threshold crossing during ministep - // this must be done before adding the input, since - // interpolation requires continuity - - const double spike_time_max = is_spike_( ministep ); + // check for threshold crossing during ministep + // this must be done before adding the input, since + // interpolation requires continuity + const double spike_time_max = is_spike_( ministep ); - if ( not numerics::is_nan( spike_time_max ) ) - { - emit_spike_( origin, lag, V_.h_ms_ - last_offset, spike_time_max ); + if ( not numerics::is_nan( spike_time_max ) ) + { + emit_spike_( origin, lag, V_.h_ms_ - last_offset, spike_time_max ); + } } // handle event From 5f6470fff58bfc8cb51d252f5d1dc05682bab158 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Wed, 21 Mar 2018 13:40:32 +0100 Subject: [PATCH 38/48] Also iaf_psc_exp_ps now skips propagation by branch in update(). --- precise/iaf_psc_exp_ps.cpp | 27 ++++++++++++++++----------- precise/iaf_psc_exp_ps_lossless.cpp | 17 ++++++++++++++++- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/precise/iaf_psc_exp_ps.cpp b/precise/iaf_psc_exp_ps.cpp index 98aac5c6ba..6b4bc7adf3 100644 --- a/precise/iaf_psc_exp_ps.cpp +++ b/precise/iaf_psc_exp_ps.cpp @@ -375,15 +375,21 @@ nest::iaf_psc_exp_ps::update( const Time& origin, { // time is measured backward: inverse order in difference const double ministep = last_offset - ev_offset; + assert( ministep >= 0 ); - propagate_( ministep ); - - // check for threshold crossing during ministep - // this must be done before adding the input, since - // interpolation requires continuity - if ( S_.y2_ >= P_.U_th_ ) + // dt == 0 may occur if two spikes arrive simultaneously; + // no propagation in that case; see #368 + if ( ministep > 0 ) { - emit_spike_( origin, lag, V_.h_ms_ - last_offset, ministep ); + propagate_( ministep ); + + // check for threshold crossing during ministep + // this must be done before adding the input, since + // interpolation requires continuity + if ( S_.y2_ >= P_.U_th_ ) + { + emit_spike_( origin, lag, V_.h_ms_ - last_offset, ministep ); + } } // handle event @@ -479,10 +485,9 @@ nest::iaf_psc_exp_ps::handle( DataLoggingRequest& e ) void nest::iaf_psc_exp_ps::propagate_( const double dt ) { - if ( dt == 0 ) - { - return; // if two input spikes arrived simultaneously (#368) - } + // dt == 0 may occur if two spikes arrive simultaneously; + // propagate_() shall not be called then; see #368. + assert( dt > 0 ); const double expm1_tau_ex = numerics::expm1( -dt / P_.tau_ex_ ); const double expm1_tau_in = numerics::expm1( -dt / P_.tau_in_ ); diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index ae0c3b0beb..5f8f250921 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -415,7 +415,10 @@ nest::iaf_psc_exp_ps_lossless::update( const Time& origin, // time is measured backward: inverse order in difference const double ministep = last_offset - ev_offset; assert( ministep >= 0.0 ); - if ( ministep > 0.0 ) + + // dt == 0 may occur if two spikes arrive simultaneously; + // no propagation in that case; see #368 + if ( ministep > 0 ) { propagate_( ministep ); @@ -524,6 +527,10 @@ nest::iaf_psc_exp_ps_lossless::handle( DataLoggingRequest& e ) void nest::iaf_psc_exp_ps_lossless::propagate_( const double dt ) { + // dt == 0 may occur if two spikes arrive simultaneously; + // propagate_() shall not be called then; see #368. + assert( dt > 0 ); + const double expm1_tau_ex = numerics::expm1( -dt / P_.tau_ex_ ); const double expm1_tau_in = numerics::expm1( -dt / P_.tau_in_ ); @@ -551,6 +558,10 @@ nest::iaf_psc_exp_ps_lossless::emit_spike_( const Time& origin, const double t0, const double dt ) { + // dt == 0 may occur if two spikes arrive simultaneously; + // emit_spike_() shall not be called then; see #368. + assert( dt > 0 ); + // we know that the potential is subthreshold at t0, super at t0+dt // compute spike time relative to beginning of step @@ -631,6 +642,10 @@ nest::iaf_psc_exp_ps_lossless::bisectioning_( const double dt ) const double nest::iaf_psc_exp_ps_lossless::is_spike_( const double dt ) { + // dt == 0 may occur if two spikes arrive simultaneously; + // is_spike_() shall not be called then; see #368. + assert( dt > 0 ); + const double I_0 = V_.I_syn_ex_before_ + V_.I_syn_in_before_; const double V_0 = V_.y2_before_; const double exp_tau_s = numerics::expm1( dt / P_.tau_ex_ ); From 9480e8eaf985abfeb923ea38bce432c79f7be512 Mon Sep 17 00:00:00 2001 From: ChristianKeup Date: Thu, 29 Mar 2018 17:02:54 +0200 Subject: [PATCH 39/48] Handle external dc current correctly in iaf_psc_exp_ps_lossless::is_spike_() --- precise/iaf_psc_exp_ps_lossless.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index 5f8f250921..fa82cc4704 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -652,14 +652,15 @@ nest::iaf_psc_exp_ps_lossless::is_spike_( const double dt ) const double exp_tau_m = numerics::expm1( dt / P_.tau_m_ ); const double exp_tau_m_s = numerics::expm1( dt / P_.tau_m_ - dt / P_.tau_ex_ ); + const double I_e = V_.y0_before_ + P_.I_e_; double g = - ( ( V_.a1_ * I_0 * exp_tau_m_s + exp_tau_m * ( V_.a3_ - P_.I_e_ * V_.a2_ ) + ( ( V_.a1_ * I_0 * exp_tau_m_s + exp_tau_m * ( V_.a3_ - I_e * V_.a2_ ) + V_.a3_ ) / V_.a4_ ); // no-spike, NS_1, (V <= f_h,I_e(I) and V < g_h,I_e(I)) if ( ( V_0 - <= ( ( ( I_0 + P_.I_e_ ) * ( V_.b1_ * exp_tau_m + V_.b2_ * exp_tau_s ) + <= ( ( ( I_0 + I_e ) * ( V_.b1_ * exp_tau_m + V_.b2_ * exp_tau_s ) + V_.b3_ * ( exp_tau_m - exp_tau_s ) ) / ( V_.b4_ * exp_tau_s ) ) ) and ( V_0 < g ) ) { @@ -672,9 +673,9 @@ nest::iaf_psc_exp_ps_lossless::is_spike_( const double dt ) return dt; } // no-spike, NS_2, V < b(I) - else if ( V_0 < ( V_.c1_ * P_.I_e_ + V_.c2_ * I_0 + else if ( V_0 < ( V_.c1_ * I_e + V_.c2_ * I_0 + V_.c3_ * std::pow( I_0, V_.c4_ ) - * std::pow( ( V_.c5_ - P_.I_e_ ), V_.c6_ ) ) ) + * std::pow( ( V_.c5_ - I_e ), V_.c6_ ) ) ) { return numerics::nan; } @@ -683,6 +684,6 @@ nest::iaf_psc_exp_ps_lossless::is_spike_( const double dt ) { return ( V_.a1_ / P_.tau_m_ * P_.tau_ex_ ) * std::log( V_.b1_ * I_0 - / ( V_.a2_ * P_.I_e_ - V_.a1_ * I_0 - V_.a4_ * V_0 ) ); + / ( V_.a2_ * I_e - V_.a1_ * I_0 - V_.a4_ * V_0 ) ); } } From f8a7a622afbaaf1599b9ad7bd7b7a38335bfe902 Mon Sep 17 00:00:00 2001 From: ChristianKeup Date: Fri, 30 Mar 2018 15:50:02 +0200 Subject: [PATCH 40/48] resolves confusion of g and f in spike detection of /iaf_psc_exp_ps_lossless --- precise/iaf_psc_exp_ps_lossless.cpp | 23 +++++++++++++++++------ precise/iaf_psc_exp_ps_lossless.h | 4 ++-- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index fa82cc4704..5cb5520d24 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -654,21 +654,32 @@ nest::iaf_psc_exp_ps_lossless::is_spike_( const double dt ) numerics::expm1( dt / P_.tau_m_ - dt / P_.tau_ex_ ); const double I_e = V_.y0_before_ + P_.I_e_; - double g = + /* Expressions for f and b below are rewritten but equivalent + to those given in Krishnan et al. 2018. + The expression for g given in the paper as eq.(49) is incorrect. + It can instead be constructed as a line through the points (see Fig.6): + (I_theta-I_e, V_th) and (i2, f(i2)) where i2=(I_theta-I_e)*exp(dt/tau_s). + + Note that there is a typo in Algorithm 1 and 2 of the paper: + g and f are interchanged. (compare to Fig.6) + */ + + double f = ( ( V_.a1_ * I_0 * exp_tau_m_s + exp_tau_m * ( V_.a3_ - I_e * V_.a2_ ) + V_.a3_ ) / V_.a4_ ); - // no-spike, NS_1, (V <= f_h,I_e(I) and V < g_h,I_e(I)) + + // no-spike, NS_1, (V <= g_h,I_e(I) and V < f_h,I_e(I)) if ( ( V_0 - <= ( ( ( I_0 + I_e ) * ( V_.b1_ * exp_tau_m + V_.b2_ * exp_tau_s ) + < ( ( ( I_0 + I_e ) * ( V_.b1_ * exp_tau_m + V_.b2_ * exp_tau_s ) + V_.b3_ * ( exp_tau_m - exp_tau_s ) ) - / ( V_.b4_ * exp_tau_s ) ) ) and ( V_0 < g ) ) + / ( V_.b4_ * exp_tau_s ) ) ) and ( V_0 <= f ) ) { return numerics::nan; } - // spike, S_1, V >= g_h,I_e(I) - else if ( V_0 >= g ) + // spike, S_1, V >= f_h,I_e(I) + else if ( V_0 >= f ) { return dt; } diff --git a/precise/iaf_psc_exp_ps_lossless.h b/precise/iaf_psc_exp_ps_lossless.h index d2a842d28d..a9e06d4337 100644 --- a/precise/iaf_psc_exp_ps_lossless.h +++ b/precise/iaf_psc_exp_ps_lossless.h @@ -224,8 +224,8 @@ class iaf_psc_exp_ps_lossless : public Archiving_Node * threshold line V < \theta, envelope, V > b(I_e) and line corresponding to * the final timestep * V > f(h, I) (or) linear approximation of the envelope, V < g(h, I_e). - * Propagate the neuron's state by dt. - * @returns time to emit spike. + * Note that in Algorithm 1 and 2 of [1], a typo interchanges g and f. + * @returns time interval in which threshold was crossed, or nan. */ double is_spike_( const double ); From 0b1ef91566ea4361924b7a62322a971eb75198d5 Mon Sep 17 00:00:00 2001 From: ChristianKeup Date: Fri, 30 Mar 2018 17:14:21 +0200 Subject: [PATCH 41/48] fixed test_iaf_psc_exp_ps_lossless and added test_iaf_psc_exp_ps --- testsuite/unittests/test_iaf_psc_exp_ps.sli | 176 ++++++++++++++++++ .../test_iaf_psc_exp_ps_lossless.sli | 37 ++-- 2 files changed, 199 insertions(+), 14 deletions(-) create mode 100644 testsuite/unittests/test_iaf_psc_exp_ps.sli diff --git a/testsuite/unittests/test_iaf_psc_exp_ps.sli b/testsuite/unittests/test_iaf_psc_exp_ps.sli new file mode 100644 index 0000000000..6a66e3e3e5 --- /dev/null +++ b/testsuite/unittests/test_iaf_psc_exp_ps.sli @@ -0,0 +1,176 @@ +/* + * test_iaf_psc_exp_ps.sli + * + * 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 . + * + */ + + /* BeginDocumentation + Name: testsuite::test_iaf_psc_exp_ps - sli script for overall test of iaf_psc_exp_ps model + + Synopsis: (test_iaf_psc_exp_ps) run -> compares response to current step with reference data + + Description: + test_iaf_psc_exp_ps.sli is an overall test of the iaf_psc_exp_ps model connected + to some useful devices. + + A DC current is injected into the neuron using a current generator + device. The membrane potential as well as the spiking activity are + recorded by corresponding devices. + + It can be observed how the current charges the membrane, a spike + is emitted, the neuron becomes absolute refractory, and finally + starts to recover. + + The timing of the various events on the simulation grid is of + particular interest and crucial for the consistency of the + simulation scheme. + + Although 0.1 cannot be represented in the IEEE double data type, it + is safe to simulate with a resolution (computation step size) of 0.1 + ms because by default nest is built with a timebase enabling exact + representation of 0.1 ms. + + The expected output is documented and briefly commented at the end of + the script. The textual output of the voltmeter documented in this file + can be regenerated by setting adding /to_screen true to the SetStatus + call of vm below. + + Other test programs discuss the various aspects of this script in detail, + see the SeeAlso key below. + + Author: Jeyashree Krishnan, 2017 + SeeAlso: iaf_psc_exp, testsuite::test_iaf_i0, testsuite::test_iaf_i0_refractory, testsuite::test_iaf_dc, testsuite::test_iaf_psc_exp_ps_lossless +*/ + + +(unittest) run +/unittest using + +0.1 /h Set + +ResetKernel + +0 << + /local_num_threads 1 + /resolution h + >> SetStatus + +/iaf_psc_exp_ps Create /neuron Set + +/dc_generator Create /dc_gen Set +dc_gen << /amplitude 1000. >> SetStatus + +/voltmeter Create /vm Set +vm << /withtime true /time_in_steps true /interval h >> SetStatus + +/spike_detector Create /sp_det Set +sp_det << /withtime true /withgid true /precise_times true >> SetStatus + + +dc_gen neuron 1.0 h Connect +vm neuron 1.0 h Connect +neuron sp_det 1.0 h Connect + +8 Simulate + +sp_det /events get /times get First stack % prints spike time + +{ % reference data + dup Transpose First /test_times Set % times of reference + + vm [/events [/times /V_m]] get cva % array of recorded data + 6 ToUnitTestPrecision % to precision of reference + Transpose dup == % all recorded tuples + {First test_times exch MemberQ } Select % those with reference + eq % compare +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Expected output of this program: +% +% The output send to std::cout is a superposition of the output of +% the voltmeter and the spike detector. Both, voltmeter and spike +% detector are connected to the same neuron. +% +% time (in steps) voltage (in mV) +[ +[ 1 -70 ] %<----- The earliest time dc_gen can be switched on. +[ 2 -70 ] %<----- The DC current arrives at the neuron, it is +[ 3 -69.602 ] %<- reflected in the neuron's state variable y0, +[ 4 -69.2079 ] % | (initial condition) but has not yet affected +[ 5 -68.8178 ] % | the membrane potential. +[ 6 -68.4316 ] % | +[ 7 -68.0492 ] % --- the effect of the DC current is visible in the +[ 8 -67.6706 ] % membrane potential +[ 9 -67.2958 ] % +[ 10 -66.9247 ] % +% +% ... +% +[ 45 -56.0204 ] % +[ 46 -55.7615 ] % +[ 47 -55.5051 ] % +[ 48 -55.2513 ] % +[ 49 -55.0001 ] % +[ 50 -70 ] % <---- The membrane potential crossed threshold in the +[ 51 -70 ] % step 4.9 ms -> 5.0 ms. The membrane potential is +[ 52 -70 ] % reset (no super-threshold values can be observed). +[ 53 -70 ] % The precise spike time is reported at 4.90004 ms. +[ 54 -70 ] % +[ 55 -70 ] % +[ 56 -70 ] % +[ 57 -70 ] % +[ 58 -70 ] % +[ 59 -70 ] % +[ 60 -70 ] % +[ 61 -70 ] % +[ 62 -70 ] % +[ 63 -70 ] % +[ 64 -70 ] % +[ 65 -70 ] % +[ 66 -70 ] % +[ 67 -70 ] % +[ 68 -70 ] % +[ 69 -70 ] % +[ 70 -69.6021 ] % <---- Since the neuron uses precise spike times that are not +[ 71 -69.2081 ] % locked to the grid, the refractory period ended after +[ 72 -68.818 ] % 2.0 ms during the timestep 6.9 ms -> 7.0 ms. The membrane +[ 73 -68.4317 ] % potential has already started to increase again. +[ 74 -68.0493 ] % +[ 75 -67.6707 ] % +[ 76 -67.2959 ] % +[ 77 -66.9248 ] % +[ 78 -66.5574 ] % +[ 79 -66.1936 ] % <-- +] % | +% | +% - The simulation was run for 8.0 ms. However, in the step +% 7.9 ms -> 8.0 ms the voltmeter necessarily receives the +% voltages that occurred at time 7.9 ms (delay h). This +% results in different end times of the recorded voltage +% traces at different resolutions. In the current +% simulation kernel there is no general cure for this +% problem. One workaround is to end the simulation script +% with "h Simulate", thereby making the script resolution +% dependent. +% + +exch assert_or_die + diff --git a/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli b/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli index 8373060075..eb98e77628 100644 --- a/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli +++ b/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli @@ -29,6 +29,13 @@ test_iaf_psc_exp_ps_lossless.sli is an overall test of the iaf_psc_exp_ps_lossless model connected to some useful devices. + The first part of this test is exactly the same as test_iaf_psc_exp_ps, + demonstrating the numerical equivalency of both models in usual conditions. + The only difference between the models, which is tested in the second part, + is the detection of double threshold crossings during a simulation step + (so the membrane potential is again below V_th at the end of the step) + by the lossless model. + A DC current is injected into the neuron using a current generator device. The membrane potential as well as the spiking activity are recorded by corresponding devices. @@ -55,7 +62,7 @@ see the SeeAlso key below. Author: Jeyashree Krishnan, 2017 - SeeAlso: iaf_psc_exp, testsuite::test_iaf_i0, testsuite::test_iaf_i0_refractory, testsuite::test_iaf_dc + SeeAlso: iaf_psc_exp, testsuite::test_iaf_i0, testsuite::test_iaf_i0_refractory, testsuite::test_iaf_dc, testsuite::iaf_psc_exp_ps */ @@ -80,7 +87,7 @@ dc_gen << /amplitude 1000. >> SetStatus vm << /withtime true /time_in_steps true /interval h >> SetStatus /spike_detector Create /sp_det Set -sp_det << /withtime true /withgid true /time_in_steps true >> SetStatus +sp_det << /withtime true /withgid true /precise_times true >> SetStatus dc_gen neuron 1.0 h Connect @@ -89,7 +96,7 @@ neuron sp_det 1.0 h Connect 8 Simulate - +sp_det /events get /times get First stack % prints spike time { % reference data dup Transpose First /test_times Set % times of reference @@ -132,7 +139,7 @@ neuron sp_det 1.0 h Connect [ 50 -70 ] % <---- The membrane potential crossed threshold in the [ 51 -70 ] % step 4.9 ms -> 5.0 ms. The membrane potential is [ 52 -70 ] % reset (no super-threshold values can be observed). -[ 53 -70 ] % The spike is reported at 5.0 ms +[ 53 -70 ] % The precise spike time is reported at 4.90004 ms. [ 54 -70 ] % [ 55 -70 ] % [ 56 -70 ] % @@ -149,16 +156,16 @@ neuron sp_det 1.0 h Connect [ 67 -70 ] % [ 68 -70 ] % [ 69 -70 ] % -[ 70 -70 ] % <---- The last point in time at which the membrane potential -[ 71 -69.602 ] % <- is clamped. The fact that the neuron is not refractory -[ 72 -69.2079 ] % | anymore is reflected in the state variable r==0. -[ 73 -68.8178 ] % | The neuron was refractory for 2.0 ms. -[ 74 -68.4316 ] % | -[ 75 -68.0492 ] % --- The membrane potential starts to increase -[ 76 -67.6706 ] % immediately afterwards and the neuron can generate -[ 77 -67.2958 ] % spikes again (at this resolution reported with time -[ 78 -66.9247 ] % stamp 7.1 ms on the grid) -[ 79 -66.5572 ] % <-- +[ 70 -69.6021 ] % <---- Since the neuron uses precise spike times that are not +[ 71 -69.2081 ] % locked to the grid, the refractory period ended after +[ 72 -68.818 ] % 2.0 ms during the timestep 6.9 ms -> 7.0 ms. The membrane +[ 73 -68.4317 ] % potential has already started to increase again. +[ 74 -68.0493 ] % +[ 75 -67.6707 ] % +[ 76 -67.2959 ] % +[ 77 -66.9248 ] % +[ 78 -66.5574 ] % +[ 79 -66.1936 ] % <-- ] % | % | % - The simulation was run for 8.0 ms. However, in the step @@ -174,3 +181,5 @@ neuron sp_det 1.0 h Connect exch assert_or_die +% TODO: second part testing for region of missed spikes + From e0cda000350e99a3ed70c88b920896a3ce944cc3 Mon Sep 17 00:00:00 2001 From: ChristianKeup Date: Thu, 5 Apr 2018 14:33:36 +0200 Subject: [PATCH 42/48] adressed formatting issues, comment --- precise/iaf_psc_exp_ps_lossless.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index 5cb5520d24..b8afd3d910 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -661,19 +661,20 @@ nest::iaf_psc_exp_ps_lossless::is_spike_( const double dt ) (I_theta-I_e, V_th) and (i2, f(i2)) where i2=(I_theta-I_e)*exp(dt/tau_s). Note that there is a typo in Algorithm 1 and 2 of the paper: - g and f are interchanged. (compare to Fig.6) + g and f are interchanged. (compare to Fig.6) */ - double f = + const double f = ( ( V_.a1_ * I_0 * exp_tau_m_s + exp_tau_m * ( V_.a3_ - I_e * V_.a2_ ) - + V_.a3_ ) / V_.a4_ ); + + V_.a3_ ) + / V_.a4_ ); // no-spike, NS_1, (V <= g_h,I_e(I) and V < f_h,I_e(I)) - if ( ( V_0 - < ( ( ( I_0 + I_e ) * ( V_.b1_ * exp_tau_m + V_.b2_ * exp_tau_s ) - + V_.b3_ * ( exp_tau_m - exp_tau_s ) ) - / ( V_.b4_ * exp_tau_s ) ) ) and ( V_0 <= f ) ) + if ( ( V_0 < ( ( ( I_0 + I_e ) * ( V_.b1_ * exp_tau_m + V_.b2_ * exp_tau_s ) + + V_.b3_ * ( exp_tau_m - exp_tau_s ) ) + / ( V_.b4_ * exp_tau_s ) ) ) + and ( V_0 <= f ) ) { return numerics::nan; } @@ -694,7 +695,7 @@ nest::iaf_psc_exp_ps_lossless::is_spike_( const double dt ) // missed spike detected, S_2 { return ( V_.a1_ / P_.tau_m_ * P_.tau_ex_ ) - * std::log( V_.b1_ * I_0 - / ( V_.a2_ * I_e - V_.a1_ * I_0 - V_.a4_ * V_0 ) ); + * std::log( + V_.b1_ * I_0 / ( V_.a2_ * I_e - V_.a1_ * I_0 - V_.a4_ * V_0 ) ); } } From d8cbb6488a1538236986e430ed42fcb7a66406ac Mon Sep 17 00:00:00 2001 From: ChristianKeup Date: Thu, 5 Apr 2018 14:37:54 +0200 Subject: [PATCH 43/48] extended test_iaf_psc_exp_ps_lossless.sli to check spike detection alg. --- .../test_iaf_psc_exp_ps_lossless.sli | 140 +++++++++++++++++- 1 file changed, 136 insertions(+), 4 deletions(-) diff --git a/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli b/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli index eb98e77628..baccde31c9 100644 --- a/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli +++ b/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli @@ -23,7 +23,7 @@ /* BeginDocumentation Name: testsuite::test_iaf_psc_exp_ps_lossless - sli script for overall test of iaf_psc_exp_ps_lossless model - Synopsis: (test_iaf_psc_exp_ps_lossless) run -> compares response to current step with reference data + Synopsis: (test_iaf_psc_exp_ps_lossless) run -> compares response to current step with reference data and tests lossless spike detection Description: test_iaf_psc_exp_ps_lossless.sli is an overall test of the iaf_psc_exp_ps_lossless model connected @@ -59,9 +59,36 @@ call of vm below. Other test programs discuss the various aspects of this script in detail, - see the SeeAlso key below. + see the SeeAlso key below. - Author: Jeyashree Krishnan, 2017 + + The second part tests wether the lossless spike detection algorithm [1] is + working correctly. + + The algorithm checks whether a spike is emitted on the basis of the neurons position + in state space. There are 4 regions in state space (see [1]): NS1, NS2, S1 and S2. + S1 corresponds to threshold crossings that would also be detected by the lossy + implementation /iaf_psc_exp_ps. S2 corresponds to crossings that would be missed. + The lossless model detects both. + + The test brings 3 neurons into the state space regions NS2, S1 and S2, + which is done by keeping their membrane potential close to threshold and then + sending a single spike to them, which, received via different synaptic weights, + sets the synaptic current such that the neurons are in the respective region. + The existence and precise times of the resulting spikes are compared to reference data. + + If you need to reproduce the reference data, ask the authors of [1] for the script + regions_algorithm.py which they used to generate Fig. 6. Here you can adjust the + parameters as whished and obtain the respective regions. + + + References: + [1] Krishnan J, Porta Mana P, Helias M, Diesmann M and Di Napoli E + (2018) Perfect Detection of Spikes in the Linear Sub-threshold + Dynamics of Point Neurons. Front. Neuroinform. 11:75. + doi: 10.3389/fninf.2017.00075 + + Author: Jeyashree Krishnan, 2017, and Christian Keup, 2018 SeeAlso: iaf_psc_exp, testsuite::test_iaf_i0, testsuite::test_iaf_i0_refractory, testsuite::test_iaf_dc, testsuite::iaf_psc_exp_ps */ @@ -181,5 +208,110 @@ sp_det /events get /times get First stack % prints spike time exch assert_or_die -% TODO: second part testing for region of missed spikes + + + +%%%%%%%%% +% +% Beginning of 2nd part. Testing the spike detection algorithm +% + +ResetKernel + +reset + +(unittest) run +/unittest using + +0 << + /local_num_threads 1 + /resolution 1. % low resolution is crucial. + >> SetStatus + +/iaf_psc_exp_ps_lossless << + /tau_m 100. + /tau_syn_ex 1. + /C_m 250. + /V_th -49. + >> SetDefaults + +% 3 neurons that will test the detection of different types of threshold crossing +/iaf_psc_exp_ps_lossless Create /nrn_nospike Set +/iaf_psc_exp_ps_lossless Create /nrn_missingspike Set +/iaf_psc_exp_ps_lossless Create /nrn_spike Set + + +%syn weights of trigger spike that will put the nrn in the different state space regions +55. /I_nospike Set +70. /I_missingspike Set +90. /I_spike Set + +%send one trigger spike to the nrns at specified time: + +/spike_generator << /precise_times true /spike_times [3.0] >> Create /sp_gen Set + +sp_gen nrn_nospike I_nospike 1. Connect +sp_gen nrn_missingspike I_missingspike 1. Connect +sp_gen nrn_spike I_spike 1. Connect + + +%external current to keep nrns close to threshold: + +/dc_generator << /amplitude 52.5 >> Create /dc_gen Set + +dc_gen nrn_nospike 1. 1. Connect +dc_gen nrn_missingspike 1. 1. Connect +dc_gen nrn_spike 1. 1. Connect + + +%read out spike response of nrns: + +/spike_detector << /precise_times true >> SetDefaults + +/spike_detector Create /det_nospike Set +nrn_nospike det_nospike Connect + +/spike_detector Create /det_missingspike Set +nrn_missingspike det_missingspike Connect + +/spike_detector Create /det_spike Set +nrn_spike det_spike Connect + + +2 Simulate + +% set nrns close to threshold +nrn_nospike << /V_m -49.001 >> SetStatus +nrn_missingspike << /V_m -49.001 >> SetStatus +nrn_spike << /V_m -49.001 >> SetStatus + +% swich off ext. current. This effect will reach the nrns at 3.0 due to syn delay, +% so that the external current will be zero when the trigger spike arrives at 4.0 . +dc_gen << /amplitude 0. >> SetStatus + +10 Simulate + + +% get spike times + +det_nospike /events get /times get cva % array of spike times (this one should be empty) + Total % sum of array elements. works also for empty array + 6 ToUnitTestPrecision + /time_nospike Set + +det_missingspike /events get /times get cva + Total + 6 ToUnitTestPrecision + /time_missingspike Set + +det_spike /events get /times get cva + Total + 6 ToUnitTestPrecision + /time_spike Set + + +{ time_nospike 0 eq } assert_or_die +{ time_missingspike 4.01442 eq } assert_or_die +{ time_spike 4.00659 eq } assert_or_die + From d5e66e57d42368d6cbbdaa8d0273df5608e0504c Mon Sep 17 00:00:00 2001 From: ChristianKeup Date: Thu, 5 Apr 2018 16:24:01 +0200 Subject: [PATCH 44/48] again adressing clang-format issues --- precise/iaf_psc_exp_ps_lossless.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index b8afd3d910..7c573db07b 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -666,15 +666,13 @@ nest::iaf_psc_exp_ps_lossless::is_spike_( const double dt ) const double f = ( ( V_.a1_ * I_0 * exp_tau_m_s + exp_tau_m * ( V_.a3_ - I_e * V_.a2_ ) - + V_.a3_ ) - / V_.a4_ ); + + V_.a3_ ) / V_.a4_ ); // no-spike, NS_1, (V <= g_h,I_e(I) and V < f_h,I_e(I)) if ( ( V_0 < ( ( ( I_0 + I_e ) * ( V_.b1_ * exp_tau_m + V_.b2_ * exp_tau_s ) + V_.b3_ * ( exp_tau_m - exp_tau_s ) ) - / ( V_.b4_ * exp_tau_s ) ) ) - and ( V_0 <= f ) ) + / ( V_.b4_ * exp_tau_s ) ) ) and ( V_0 <= f ) ) { return numerics::nan; } From ec7586e5bcd3e8d341ad222dbaf73100d9f2bf0e Mon Sep 17 00:00:00 2001 From: ChristianKeup Date: Fri, 6 Apr 2018 17:29:08 +0200 Subject: [PATCH 45/48] enforced tau_syn_ex == tau_syn_in for /iaf_psc_exp_ps_lossless --- precise/iaf_psc_exp_ps_lossless.cpp | 11 +++++++++++ precise/iaf_psc_exp_ps_lossless.h | 5 +++++ testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli | 1 + 3 files changed, 17 insertions(+) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index 7c573db07b..f254b25d70 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -182,6 +182,14 @@ nest::iaf_psc_exp_ps_lossless::Parameters_::set( const DictionaryDatum& d ) throw BadProperty( "Refractory time must not be negative." ); } + if ( tau_ex_ != tau_in_ ) + { + throw BadProperty( + "Exc. and inh. synapse time constants must be equal in the current implementation." + " If you need unequal time constants, use iaf_psc_exp_ps for now." + " See note in documentation, and github issue #921" ); + } + if ( tau_m_ <= 0 || tau_ex_ <= 0 || tau_in_ <= 0 ) { throw BadProperty( "All time constants must be strictly positive." ); @@ -646,6 +654,9 @@ nest::iaf_psc_exp_ps_lossless::is_spike_( const double dt ) // is_spike_() shall not be called then; see #368. assert( dt > 0 ); + // synapse time constants are assumed to be equal in this implementation + assert( P_.tau_ex_ == P_.tau_in_ ); + const double I_0 = V_.I_syn_ex_before_ + V_.I_syn_in_before_; const double V_0 = V_.y2_before_; const double exp_tau_s = numerics::expm1( dt / P_.tau_ex_ ); diff --git a/precise/iaf_psc_exp_ps_lossless.h b/precise/iaf_psc_exp_ps_lossless.h index a9e06d4337..661d13eb59 100644 --- a/precise/iaf_psc_exp_ps_lossless.h +++ b/precise/iaf_psc_exp_ps_lossless.h @@ -73,6 +73,11 @@ meets the threshold. V_min double - Absolute lower value for the membrane potential. V_reset double - Reset value for the membrane potential. +Note: In the current implementation, tau_syn_ex and tau_syn_in must be equal. + This is because the state space would be 3-dimensional otherwise, which + makes the detection of threshold crossing more difficult [1]. + Support for different time constants may be added in the future, see issue #921. + References: [1] Krishnan J, Porta Mana P, Helias M, Diesmann M and Di Napoli E (2018) Perfect Detection of Spikes in the Linear Sub-threshold diff --git a/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli b/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli index baccde31c9..882e6fc984 100644 --- a/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli +++ b/testsuite/unittests/test_iaf_psc_exp_ps_lossless.sli @@ -231,6 +231,7 @@ reset /iaf_psc_exp_ps_lossless << /tau_m 100. /tau_syn_ex 1. + /tau_syn_in 1. /C_m 250. /V_th -49. >> SetDefaults From 52a541b82a156305c1c097f565ec306cb830e8da Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Sat, 7 Apr 2018 15:12:19 +0200 Subject: [PATCH 46/48] Improved error message and fixed formatting. --- precise/iaf_psc_exp_ps_lossless.cpp | 6 +++--- precise/iaf_psc_exp_ps_lossless.h | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index f254b25d70..06acf53c0d 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -185,17 +185,17 @@ nest::iaf_psc_exp_ps_lossless::Parameters_::set( const DictionaryDatum& d ) if ( tau_ex_ != tau_in_ ) { throw BadProperty( - "Exc. and inh. synapse time constants must be equal in the current implementation." + "tau_syn_ex == tau_syn_in is required in the current implementation." " If you need unequal time constants, use iaf_psc_exp_ps for now." " See note in documentation, and github issue #921" ); } - if ( tau_m_ <= 0 || tau_ex_ <= 0 || tau_in_ <= 0 ) + if ( tau_m_ <= 0 or tau_ex_ <= 0 or tau_in_ <= 0 ) { throw BadProperty( "All time constants must be strictly positive." ); } - if ( tau_m_ == tau_ex_ || tau_m_ == tau_in_ ) + if ( tau_m_ == tau_ex_ or tau_m_ == tau_in_ ) { throw BadProperty( "Membrane and synapse time constant(s) must differ." diff --git a/precise/iaf_psc_exp_ps_lossless.h b/precise/iaf_psc_exp_ps_lossless.h index 661d13eb59..5616b5707c 100644 --- a/precise/iaf_psc_exp_ps_lossless.h +++ b/precise/iaf_psc_exp_ps_lossless.h @@ -76,7 +76,8 @@ meets the threshold. Note: In the current implementation, tau_syn_ex and tau_syn_in must be equal. This is because the state space would be 3-dimensional otherwise, which makes the detection of threshold crossing more difficult [1]. - Support for different time constants may be added in the future, see issue #921. + Support for different time constants may be added in the future, see issue + #921. References: [1] Krishnan J, Porta Mana P, Helias M, Diesmann M and Di Napoli E From 703d1389ac4b34b4a65429238c0df58d14990988 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Sat, 7 Apr 2018 15:13:07 +0200 Subject: [PATCH 47/48] Test ticket-686-... ignores iaf_ps_exp_ps_lossless until #921 is implemented. --- .../ticket-686-positive-parameters.sli | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/testsuite/regressiontests/ticket-686-positive-parameters.sli b/testsuite/regressiontests/ticket-686-positive-parameters.sli index 5765e179a5..abd865a865 100644 --- a/testsuite/regressiontests/ticket-686-positive-parameters.sli +++ b/testsuite/regressiontests/ticket-686-positive-parameters.sli @@ -153,21 +153,28 @@ skipped_models { modeldict exch undef } forall /model Set - % now test one at a time - model GetPositiveKeys { - - /key Set - - /nrn model Create def - /newval nrn key get 1.0 add def - nrn << >> dup key newval put SetStatus - nrn key get newval eq - - } Map - - true exch { and } Fold - dup not { (ERROR2: ) model cvs join == } if + model /iaf_psc_exp_ps_lossless neq + { + % now test one at a time + model GetPositiveKeys { + + /key Set + + /nrn model Create def + /newval nrn key get 1.0 add def + nrn << >> dup key newval put SetStatus + nrn key get newval eq + + } Map + true exch { and } Fold + dup not { (ERROR2: ) model cvs join == } if + } + { + true + } + ifelse + } Map % modeldict keys From 4a39c275525cdf9f251fb4cb29b83c90867cb3f6 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Mon, 9 Apr 2018 13:26:13 +0200 Subject: [PATCH 48/48] Update iaf_psc_exp_ps_lossless.cpp Fixed typo in comment. --- precise/iaf_psc_exp_ps_lossless.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/precise/iaf_psc_exp_ps_lossless.cpp b/precise/iaf_psc_exp_ps_lossless.cpp index 06acf53c0d..90558461ba 100644 --- a/precise/iaf_psc_exp_ps_lossless.cpp +++ b/precise/iaf_psc_exp_ps_lossless.cpp @@ -410,7 +410,7 @@ nest::iaf_psc_exp_ps_lossless::update( const Time& origin, } else { - // We only get here if there is at least on event, + // We only get here if there is at least one event, // which has been read above. We can therefore use // a do-while loop.