diff --git a/nestkernel/CMakeLists.txt b/nestkernel/CMakeLists.txt index 1ce5df272c..f2cc4deef7 100644 --- a/nestkernel/CMakeLists.txt +++ b/nestkernel/CMakeLists.txt @@ -113,6 +113,10 @@ set ( nestkernel_sources position.h spatial.h spatial.cpp stimulation_backend.h + vectorized_node.h vectorized_node.cpp + jit_node.h jit_node.cpp + structural_plasticity_vector.h structural_plasticity_vector.cpp + archiving_vector.h archiving_vector.cpp ) diff --git a/nestkernel/archiving_vector.cpp b/nestkernel/archiving_vector.cpp new file mode 100644 index 0000000000..e6def109b3 --- /dev/null +++ b/nestkernel/archiving_vector.cpp @@ -0,0 +1,283 @@ +/* + * archiving_vector.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 "archiving_vector.h" + +// Includes from nestkernel: +#include "kernel_manager.h" + +// Includes from sli: +#include "dictutils.h" +#include "structural_plasticity_node.h" + +namespace nest +{ + +nest::ArchivingVector::ArchivingVector() + : n_incoming_( 0 ) + , Kminus_( 0 ) + , Kminus_triplet_( 0 ) + , tau_minus_( 0 ) + , tau_minus_inv_( 0 ) + , tau_minus_triplet_( 0 ) + , tau_minus_triplet_inv_( 0 ) + , max_delay_( 0 ) + , trace_( 0 ) + , last_spike_( 0 ) + , history_( 0 ) +{ +} +// nest::ArchivingVector::ArchivingVector( const ArchivingVector& n ) : StructuralPlasticityVector(n) +// { +// throw std::runtime_error( "ArchivingVector shouldn't be cloned" ); +// } + +void +ArchivingVector::resize( index extended_space, index thread_id ) +{ + index total_space = size(); + + n_incoming_.resize( total_space, 0 ); + Kminus_triplet_.resize( total_space, 0.0 ); + Kminus_.resize( total_space, 0.0 ); + tau_minus_.resize( total_space, 20.0 ); + tau_minus_inv_.resize( total_space, 1 / 20.0 ); + tau_minus_triplet_.resize( total_space, 110.0 ); + tau_minus_triplet_inv_.resize( total_space, 1 / 110.0 ); + max_delay_.resize( total_space, 0.0 ); + trace_.resize( total_space, 0.0 ); + last_spike_.resize( total_space, -1.0 ); + history_.resize( total_space, std::deque< histentry >() ); + + + StructuralPlasticityVector::resize( extended_space, thread_id ); +} + +void +nest::ArchivingVector::register_stdp_connection( double t_first_read, double delay, index local_id ) +{ + for ( std::deque< histentry >::iterator runner = history_.at( local_id ).begin(); + runner != history_.at( local_id ).end() + and ( t_first_read - runner->t_ > -1.0 * kernel().connection_manager.get_stdp_eps() ); + ++runner ) + { + ( runner->access_counter_ )++; + } + + n_incoming_.at( local_id )++; + + max_delay_.at( local_id ) = std::max( delay, max_delay_.at( local_id ) ); +} +double +nest::ArchivingVector::get_K_value( double t, index local_id ) +{ + // case when the neuron has not yet spiked + if ( history_.at( local_id ).empty() ) + { + trace_.at( local_id ) = 0.; + return trace_.at( local_id ); + } + + // search for the latest post spike in the history buffer that came strictly + // before `t` + int i = history_.at( local_id ).size() - 1; + while ( i >= 0 ) + { + if ( t - history_.at( local_id )[ i ].t_ > kernel().connection_manager.get_stdp_eps() ) + { + trace_.at( local_id ) = ( history_.at( local_id )[ i ].Kminus_ + * std::exp( ( history_.at( local_id )[ i ].t_ - t ) * tau_minus_inv_.at( local_id ) ) ); + return trace_.at( local_id ); + } + --i; + } + + // this case occurs when the trace was requested at a time precisely at or + // before the first spike in the history + trace_.at( local_id ) = 0.; + return trace_.at( local_id ); +} +void +nest::ArchivingVector::get_K_values( double t, + double& K_value, + double& nearest_neighbor_K_value, + double& K_triplet_value, + index local_id ) +{ + // case when the neuron has not yet spiked + if ( history_.at( local_id ).empty() ) + { + K_triplet_value = Kminus_triplet_.at( local_id ); + nearest_neighbor_K_value = Kminus_.at( local_id ); + K_value = Kminus_.at( local_id ); + return; + } + + // search for the latest post spike in the history buffer that came strictly + // before `t` + int i = history_.at( local_id ).size() - 1; + while ( i >= 0 ) + { + if ( t - history_.at( local_id )[ i ].t_ > kernel().connection_manager.get_stdp_eps() ) + { + K_triplet_value = ( history_.at( local_id )[ i ].Kminus_triplet_ + * std::exp( ( history_.at( local_id )[ i ].t_ - t ) * tau_minus_triplet_inv_.at( local_id ) ) ); + K_value = ( history_.at( local_id )[ i ].Kminus_ + * std::exp( ( history_.at( local_id )[ i ].t_ - t ) * tau_minus_inv_.at( local_id ) ) ); + nearest_neighbor_K_value = std::exp( ( history_.at( local_id )[ i ].t_ - t ) * tau_minus_inv_.at( local_id ) ); + return; + } + --i; + } + + // this case occurs when the trace was requested at a time precisely at or + // before the first spike in the history + K_triplet_value = 0.0; + nearest_neighbor_K_value = 0.0; + K_value = 0.0; +} +void +nest::ArchivingVector::get_history( double t1, + double t2, + std::deque< histentry >::iterator* start, + std::deque< histentry >::iterator* finish, + index local_id ) +{ + *finish = history_.at( local_id ).end(); + if ( history_.at( local_id ).empty() ) + { + *start = *finish; + return; + } + std::deque< histentry >::reverse_iterator runner = history_.at( local_id ).rbegin(); + const double t2_lim = t2 + kernel().connection_manager.get_stdp_eps(); + const double t1_lim = t1 + kernel().connection_manager.get_stdp_eps(); + while ( runner != history_.at( local_id ).rend() and runner->t_ >= t2_lim ) + { + ++runner; + } + *finish = runner.base(); + while ( runner != history_.at( local_id ).rend() and runner->t_ >= t1_lim ) + { + runner->access_counter_++; + ++runner; + } + *start = runner.base(); +} + + +void +nest::ArchivingVector::set_spiketime( const Time& t_sp, index local_id, double offset ) +{ + StructuralPlasticityVector::set_spiketime( t_sp, local_id, offset ); + + const double t_sp_ms = t_sp.get_ms() - offset; + + if ( n_incoming_.at( local_id ) ) + { + // prune all spikes from history which are no longer needed + // only remove a spike if: + // - its access counter indicates it has been read out by all connected + // STDP synapses, and + // - there is another, later spike, that is strictly more than + // (max_delay_ + eps) away from the new spike (at t_sp_ms) + while ( history_.at( local_id ).size() > 1 ) + { + const double next_t_sp = history_.at( local_id )[ 1 ].t_; + if ( history_.at( local_id ).front().access_counter_ >= n_incoming_.at( local_id ) + and t_sp_ms - next_t_sp > max_delay_.at( local_id ) + kernel().connection_manager.get_stdp_eps() ) + { + history_.at( local_id ).pop_front(); + } + else + { + break; + } + } + // update spiking history + Kminus_.at( local_id ) = + Kminus_.at( local_id ) * std::exp( ( last_spike_.at( local_id ) - t_sp_ms ) * tau_minus_inv_.at( local_id ) ) + + 1.0; + Kminus_triplet_.at( local_id ) = Kminus_triplet_.at( local_id ) + * std::exp( ( last_spike_.at( local_id ) - t_sp_ms ) * tau_minus_triplet_inv_.at( local_id ) ) + + 1.0; + last_spike_.at( local_id ) = t_sp_ms; + history_.at( local_id ) + .push_back( histentry( last_spike_.at( local_id ), Kminus_.at( local_id ), Kminus_triplet_.at( local_id ), 0 ) ); + } + else + { + last_spike_.at( local_id ) = t_sp_ms; + } +} +void +nest::ArchivingVector::get_status( DictionaryDatum& d, index local_id ) const +{ + def< double >( d, names::t_spike, get_spiketime_ms( local_id ) ); + def< double >( d, names::tau_minus, tau_minus_.at( local_id ) ); + def< double >( d, names::tau_minus_triplet, tau_minus_triplet_.at( local_id ) ); + def< double >( d, names::post_trace, trace_.at( local_id ) ); +#ifdef DEBUG_ARCHIVER + def< int >( d, names::archiver_length, history_.at( local_id ).size() ); +#endif + + // add status dict items from the parent class + StructuralPlasticityVector::get_status( d, local_id ); +} +void +nest::ArchivingVector::set_status( const DictionaryDatum& d, index local_id ) +{ + // We need to preserve values in case invalid values are set + double new_tau_minus = tau_minus_.at( local_id ); + double new_tau_minus_triplet = tau_minus_triplet_.at( local_id ); + updateValue< double >( d, names::tau_minus, new_tau_minus ); + updateValue< double >( d, names::tau_minus_triplet, new_tau_minus_triplet ); + + if ( new_tau_minus <= 0.0 or new_tau_minus_triplet <= 0.0 ) + { + throw BadProperty( "All time constants must be strictly positive." ); + } + + StructuralPlasticityVector::set_status( d, local_id ); + + // do the actual update + tau_minus_.at( local_id ) = new_tau_minus; + tau_minus_triplet_.at( local_id ) = new_tau_minus_triplet; + tau_minus_inv_.at( local_id ) = 1. / new_tau_minus; + tau_minus_triplet_inv_.at( local_id ) = 1. / new_tau_minus_triplet; + + // check, if to clear spike history and K_minus + bool clear = false; + updateValue< bool >( d, names::clear, clear ); + if ( clear ) + { + clear_history( local_id ); + } +} +void +nest::ArchivingVector::clear_history( index local_id ) +{ + last_spike_.at( local_id ) = -1.0; + Kminus_.at( local_id ) = 0.0; + Kminus_triplet_.at( local_id ) = 0.0; + history_.at( local_id ).clear(); +} +} // end of namespace nest diff --git a/nestkernel/archiving_vector.h b/nestkernel/archiving_vector.h new file mode 100644 index 0000000000..9fac3f1152 --- /dev/null +++ b/nestkernel/archiving_vector.h @@ -0,0 +1,196 @@ +/* + * archiving_vector.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 ARCHIVING_VECTOR_H +#define ARCHIVING_VECTOR_H + +// C++ includes: +#include +#include + +// Includes from nestkernel: +#include "histentry.h" +#include "nest_time.h" +#include "nest_types.h" +#include "node.h" +#include "structural_plasticity_vector.h" + +// Includes from sli: +#include "dictdatum.h" + +#define DEBUG_ARCHIVER 1 + +namespace nest +{ +class ArchivingVector : public StructuralPlasticityVector +{ +public: + /** + * \fn ArchivingVector() + * Constructor. + */ + ArchivingVector(); + + /** + * \fn ArchivingNode() + * Copy Constructor. + */ + ArchivingVector( const ArchivingVector& ) = delete; + + ~ArchivingVector() + { + Kminus_.clear(); + Kminus_triplet_.clear(); + tau_minus_.clear(); + tau_minus_inv_.clear(); + tau_minus_triplet_.clear(); + tau_minus_triplet_inv_.clear(); + max_delay_.clear(); + trace_.clear(); + last_spike_.clear(); + history_.clear(); + } + /** + * \fn double get_K_value(long t) + * return the Kminus (synaptic trace) value at t (in ms). When the trace is + * requested at the exact same time that the neuron emits a spike, the trace + * value as it was just before the spike is returned. + */ + double get_K_value( double t, index local_id ); + + /** + * \fn void get_K_values( double t, + * double& Kminus, + * double& nearest_neighbor_Kminus, + * double& Kminus_triplet ) + * write the Kminus (eligibility trace for STDP), + * nearest_neighbour_Kminus (eligibility trace for nearest-neighbour STDP: + * like Kminus, but increased to 1, rather than by 1, on a spike + * occurrence), + * and Kminus_triplet + * values at t (in ms) to the provided locations. + * @throws UnexpectedEvent + */ + void + get_K_values( double t, double& Kminus, double& nearest_neighbor_Kminus, double& Kminus_triplet, index local_id ); + + /** + * \fn void get_K_values( double t, + * double& Kminus, + * double& Kminus_triplet ) + * The legacy version of the function, kept for compatibility + * after changing the function signature in PR #865. + * @throws UnexpectedEvent + */ + void + get_K_values( double t, double& Kminus, double& Kminus_triplet, index local_id ) + { + double nearest_neighbor_Kminus_to_discard; + get_K_values( t, Kminus, nearest_neighbor_Kminus_to_discard, Kminus_triplet, local_id ); + } + + /** + * \fn double get_K_triplet_value(std::deque::iterator &iter) + * return the triplet Kminus value for the associated iterator. + */ + double get_K_triplet_value( const std::deque< histentry >::iterator& iter, index local_id ); + + /** + * \fn void get_history(long t1, long t2, + * std::deque::iterator* start, + * std::deque::iterator* finish) + * return the spike times (in steps) of spikes which occurred in the range + * (t1,t2]. + */ + void get_history( double t1, + double t2, + std::deque< histentry >::iterator* start, + std::deque< histentry >::iterator* finish, + index local_id ); + + /** + * Register a new incoming STDP connection. + * + * t_first_read: The newly registered synapse will read the history entries + * with t > t_first_read. + */ + void register_stdp_connection( double t_first_read, double delay, index local_id ); + + void get_status( DictionaryDatum& d, index local_id ) const; + void set_status( const DictionaryDatum& d, index local_id ); + + void resize( index extended_space, index thread_id = 0 ); + +protected: + /** + * \fn void set_spiketime(Time const & t_sp, double offset) + * record spike history + */ + void set_spiketime( Time const&, index, double = 0.0 ); + + /** + * \fn double get_spiketime() + * return most recent spike time in ms + */ + inline double get_spiketime_ms( index local_id ) const; + + /** + * \fn void clear_history() + * clear spike history + */ + void clear_history( index local_id ); + + // number of incoming connections from stdp connectors. + // needed to determine, if every incoming connection has + // read the spikehistory for a given point in time + std::vector< size_t > n_incoming_; + +private: + // sum exp(-(t-ti)/tau_minus) + std::vector< double > Kminus_; + + // sum exp(-(t-ti)/tau_minus_triplet) + std::vector< double > Kminus_triplet_; + + std::vector< double > tau_minus_; + std::vector< double > tau_minus_inv_; + + // time constant for triplet low pass filtering of "post" spike train + std::vector< double > tau_minus_triplet_; + std::vector< double > tau_minus_triplet_inv_; + + std::vector< double > max_delay_; + std::vector< double > trace_; + + std::vector< double > last_spike_; + + // spiking history needed by stdp synapses + std::vector< std::deque< histentry > > history_; +}; +inline double +ArchivingVector::get_spiketime_ms( index local_id ) const +{ + return last_spike_.at( local_id ); +} + +} +#endif // ARCHIVING_VECTOR_H diff --git a/nestkernel/exceptions.cpp b/nestkernel/exceptions.cpp index b8f7456c73..ae4afb9deb 100644 --- a/nestkernel/exceptions.cpp +++ b/nestkernel/exceptions.cpp @@ -178,6 +178,13 @@ nest::UnknownPort::message() const } return out.str(); } +std::string +nest::VectorizedExpected::message() const +{ + std::ostringstream out; + out << "Instance must be derived from VectorizedExpected"; + return out.str(); +} std::string nest::IllegalConnection::message() const @@ -472,4 +479,4 @@ std::string nest::LayerNodeExpected::message() const { return std::string(); -} +} \ No newline at end of file diff --git a/nestkernel/exceptions.h b/nestkernel/exceptions.h index 752f4193bc..828f171889 100644 --- a/nestkernel/exceptions.h +++ b/nestkernel/exceptions.h @@ -414,6 +414,23 @@ class UnknownPort : public KernelException std::string message() const override; }; +class VectorizedExpected : public KernelException +{ + +public: + VectorizedExpected() + : KernelException( "VectorizedExpected" ) + { + } + + ~VectorizedExpected() throw() + { + } + + std::string message() const; +}; + + /** * To be thrown if a connection is not possible. * This exception is e.g. thrown if a connection was attempted with diff --git a/nestkernel/genericmodel.h b/nestkernel/genericmodel.h index dada55f7a8..770bd67d2e 100644 --- a/nestkernel/genericmodel.h +++ b/nestkernel/genericmodel.h @@ -94,6 +94,19 @@ class GenericModel : public Model void deprecation_warning( const std::string& ) override; + + std::shared_ptr< VectorizedNode > + get_container() override + { + return proto_.get_container(); + } + void + clone_container( std::shared_ptr< VectorizedNode > container ) override + { + proto_.clone_container( container ); + } + + private: void set_status_( DictionaryDatum ) override; DictionaryDatum get_status_() override; @@ -110,6 +123,7 @@ class GenericModel : public Model */ ElementT proto_; + /** * String containing deprecation information; empty if model not deprecated. */ diff --git a/nestkernel/jit_node.cpp b/nestkernel/jit_node.cpp new file mode 100644 index 0000000000..6a35d61bb9 --- /dev/null +++ b/nestkernel/jit_node.cpp @@ -0,0 +1,511 @@ +/* + * jit_node.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 "jit_node.h" + +// Includes from sli: +#include "dictdatum.h" +#include "dictutils.h" + + +// Includes from libnestutil: +#include "compose.hpp" +#include "event.h" + +namespace nest +{ + +JitNode::JitNode() + : local_id_( -1 ) +{ +} + +JitNode::~JitNode() +{ + container_->reset(); +} + +JitNode::JitNode( const JitNode& n ) + : Node( n ) + , local_id_( -1 ) + , container_( n.container_ ) +{ +} + + +std::map< std::string, const std::vector< double >& > +JitNode::get_recordables() const +{ + return container_->get_recordables(); +} +void +JitNode::reset_node() +{ + local_id_ = -1; + container_->reset(); +} +bool +JitNode::supports_urbanczik_archiving() const +{ + return false; +} +bool +JitNode::local_receiver() const +{ + return false; +} +bool +JitNode::one_node_per_process() const +{ + return false; +} +bool +JitNode::is_off_grid() const +{ + return false; +} +bool +JitNode::is_proxy() const +{ + return false; +} + +index +JitNode::get_node_id() const +{ + return container_->get_global_id( local_id_ ); +} + + +bool +JitNode::is_frozen() const +{ + return container_->is_frozen( local_id_ ); +} +bool +JitNode::node_uses_wfr() const +{ + return container_->node_uses_wfr( local_id_ ); +} +void +JitNode::set_node_uses_wfr( const bool value ) +{ + container_->set_node_uses_wfr( value, local_id_ ); +} +void +JitNode::init() +{ + container_->init( local_id_ ); +} +void +JitNode::pre_run_hook() +{ + container_->calibrate( local_id_ ); +} + +void +JitNode::calibrate_time( const TimeConverter& time_converter ) +{ + container_->calibrate_time( time_converter, local_id_ ); +} +void +JitNode::post_run_cleanup() +{ + container_->post_run_cleanup( local_id_ ); +} +void +JitNode::finalize() +{ + container_->finalize( local_id_ ); +} +void +JitNode::update( const Time& network_time, const long initial_step, const long post_final ) +{ + container_->update( network_time, initial_step, post_final, local_id_ ); +} +bool +JitNode::wfr_update( const Time& network_time, const long initial_step, const long post_final ) +{ + return container_->wfr_update( network_time, initial_step, post_final, local_id_ ); +} +void +JitNode::set_status( const DictionaryDatum& d ) +{ + container_->set_status( d, local_id_ ); +} +void +JitNode::get_status( DictionaryDatum& d ) const +{ + ( *d )[ Name( "is_vectorized" ) ] = true; + container_->get_status( d, local_id_ ); +} +port +JitNode::send_test_event( Node& receiving_node, rport receptor_type, synindex syn_id, bool dummy_target ) +{ + return container_->send_test_event( receiving_node, receptor_type, syn_id, dummy_target, local_id_ ); +} + +port +JitNode::handles_test_event( SpikeEvent& spike, rport receptor_type ) +{ + return container_->handles_test_event( spike, receptor_type, local_id_ ); +} +port +JitNode::handles_test_event( WeightRecorderEvent& weightRecorder, rport receptor_type ) +{ + return container_->handles_test_event( weightRecorder, receptor_type, local_id_ ); +} +port +JitNode::handles_test_event( RateEvent& rate, rport receptor_type ) +{ + return container_->handles_test_event( rate, receptor_type, local_id_ ); +} +port +JitNode::handles_test_event( DataLoggingRequest& dataLogging_request, rport receptor_type ) +{ + return container_->handles_test_event( dataLogging_request, receptor_type, local_id_ ); +} +port +JitNode::handles_test_event( CurrentEvent& current, rport receptor_type ) +{ + return container_->handles_test_event( current, receptor_type, local_id_ ); +} +port +JitNode::handles_test_event( ConductanceEvent& conductance, rport receptor_type ) +{ + return container_->handles_test_event( conductance, receptor_type, local_id_ ); +} +port +JitNode::handles_test_event( DoubleDataEvent& double_data, rport receptor_type ) +{ + return container_->handles_test_event( double_data, receptor_type, local_id_ ); +} +port +JitNode::handles_test_event( DSCurrentEvent& ds_current, rport receptor_type ) +{ + return container_->handles_test_event( ds_current, receptor_type, local_id_ ); +} +port +JitNode::handles_test_event( DSSpikeEvent& ds_spike, rport receptor_type ) +{ + return container_->handles_test_event( ds_spike, receptor_type, local_id_ ); +} +port +JitNode::handles_test_event( GapJunctionEvent& gap_junction, rport receptor_type ) +{ + return container_->handles_test_event( gap_junction, receptor_type, local_id_ ); +} + +port +JitNode::handles_test_event( InstantaneousRateConnectionEvent& instantaneous_rate_connection, rport receptor_type ) +{ + return container_->handles_test_event( instantaneous_rate_connection, receptor_type, local_id_ ); +} +port +JitNode::handles_test_event( DiffusionConnectionEvent& diffusion_connection, rport receptor_type ) +{ + return container_->handles_test_event( diffusion_connection, receptor_type, local_id_ ); +} +port +JitNode::handles_test_event( DelayedRateConnectionEvent& delayed_rate_connection, rport receptor_type ) +{ + return container_->handles_test_event( delayed_rate_connection, receptor_type, local_id_ ); +} +void +JitNode::sends_secondary_event( GapJunctionEvent& ge ) +{ + container_->sends_secondary_event( ge, local_id_ ); +} +void +JitNode::sends_secondary_event( InstantaneousRateConnectionEvent& re ) +{ + container_->sends_secondary_event( re, local_id_ ); +} +void +JitNode::sends_secondary_event( DelayedRateConnectionEvent& re ) +{ + container_->sends_secondary_event( re, local_id_ ); +} + +void +JitNode::sends_secondary_event( DiffusionConnectionEvent& de ) +{ + container_->sends_secondary_event( de, local_id_ ); +} +void +JitNode::register_stdp_connection( double a, double b ) +{ + container_->register_stdp_connection( a, b, local_id_ ); +} + +void +JitNode::handle( RateEvent& e ) +{ + container_->handle( e, local_id_ ); +} +void +JitNode::handle( SpikeEvent& e ) +{ + container_->handle( e, local_id_ ); +} +void +JitNode::handle( CurrentEvent& e ) +{ + container_->handle( e, local_id_ ); +} +void +JitNode::handle( DoubleDataEvent& e ) +{ + container_->handle( e, local_id_ ); +} +void +JitNode::handle( ConductanceEvent& e ) +{ + container_->handle( e, local_id_ ); +} +void +JitNode::handle( DataLoggingReply& e ) +{ + container_->handle( e, local_id_ ); +} +void +JitNode::handle( GapJunctionEvent& e ) +{ + container_->handle( e, local_id_ ); +} +void +JitNode::handle( DataLoggingRequest& e ) +{ + container_->handle( e, local_id_ ); +} +void +JitNode::handle( WeightRecorderEvent& e ) +{ + container_->handle( e, local_id_ ); +} +void +JitNode::handle( DiffusionConnectionEvent& e ) +{ + container_->handle( e, local_id_ ); +} +void +JitNode::handle( DelayedRateConnectionEvent& e ) +{ + container_->handle( e, local_id_ ); +} +void +JitNode::handle( InstantaneousRateConnectionEvent& e ) +{ + container_->handle( e, local_id_ ); +} + +double +JitNode::get_Ca_minus() const +{ + return container_->get_Ca_minus( local_id_ ); +} +double +JitNode::get_synaptic_elements( Name name ) const +{ + return container_->get_synaptic_elements( name, local_id_ ); +} + +int +JitNode::get_synaptic_elements_vacant( Name name ) const +{ + return container_->get_synaptic_elements_vacant( name, local_id_ ); +} +int +JitNode::get_synaptic_elements_connected( Name name ) const +{ + return container_->get_synaptic_elements_connected( name, local_id_ ); +} +std::map< Name, double > +JitNode::get_synaptic_elements() const +{ + return container_->get_synaptic_elements( local_id_ ); +} +void +JitNode::update_synaptic_elements( double value ) +{ + container_->update_synaptic_elements( value, local_id_ ); +} +void +JitNode::decay_synaptic_elements_vacant() +{ + container_->decay_synaptic_elements_vacant( local_id_ ); +} +void +JitNode::connect_synaptic_element( Name name, int number ) +{ + container_->connect_synaptic_element( name, number, local_id_ ); +} +double +JitNode::get_K_value( double t ) +{ + return container_->get_K_value( t, local_id_ ); +} +double +JitNode::get_LTD_value( double t ) +{ + return container_->get_LTD_value( t, local_id_ ); +} +void +JitNode::get_K_values( double t, double& Kminus, double& nearest_neighbor_Kminus, double& Kminus_triplet ) +{ + container_->get_K_values( t, Kminus, nearest_neighbor_Kminus, Kminus_triplet, local_id_ ); +} +void +JitNode::get_history( double t1, + double t2, + std::deque< histentry >::iterator* start, + std::deque< histentry >::iterator* finish ) +{ + container_->get_history( t1, t2, start, finish, local_id_ ); +} +void +JitNode::get_LTP_history( double t1, + double t2, + std::deque< histentry_extended >::iterator* start, + std::deque< histentry_extended >::iterator* finish ) +{ + container_->get_LTP_history( t1, t2, start, finish, local_id_ ); +} + +void +JitNode::get_urbanczik_history( double t1, + double t2, + std::deque< histentry_extended >::iterator* start, + std::deque< histentry_extended >::iterator* finish, + int value ) +{ + container_->get_urbanczik_history( t1, t2, start, finish, value, local_id_ ); +} +double +JitNode::get_C_m( int comp ) +{ + return container_->get_C_m( comp, local_id_ ); +} +double +JitNode::get_g_L( int comp ) +{ + return container_->get_g_L( comp, local_id_ ); +} +double +JitNode::get_tau_L( int comp ) +{ + return container_->get_tau_L( comp, local_id_ ); +} +double +JitNode::get_tau_s( int comp ) +{ + return container_->get_tau_s( comp, local_id_ ); +} +double +JitNode::get_tau_syn_ex( int comp ) +{ + return container_->get_tau_syn_ex( comp, local_id_ ); +} +double +JitNode::get_tau_syn_in( int comp ) +{ + return container_->get_tau_syn_in( comp, local_id_ ); +} +void +JitNode::event_hook( DSSpikeEvent& ds_spike ) +{ + container_->event_hook( ds_spike, local_id_ ); +} +void +JitNode::event_hook( DSCurrentEvent& ds_current ) +{ + container_->event_hook( ds_current, local_id_ ); +} +void +JitNode::set_initialized_() +{ + container_->set_initialized_( local_id_ ); +} +void +JitNode::set_frozen_( bool frozen ) +{ + container_->set_frozen_( frozen, local_id_ ); +} + + +void +JitNode::set_node_id_( index id ) +{ + container_->insert_global_id( id ); + local_id_ = container_->size() - 1; +} + +void +JitNode::clone_container( std::shared_ptr< VectorizedNode > container ) +{ + container_ = container->clone(); +} + +void +JitNode::set_container( std::shared_ptr< VectorizedNode > container ) +{ + container_ = container; +} + +SignalType +JitNode::sends_signal() const +{ + return container_->sends_signal( local_id_ ); +} +SignalType +JitNode::receives_signal() const +{ + return container_->receives_signal( local_id_ ); +} +void +JitNode::set_status_base( const DictionaryDatum& dict ) +{ + try + { + set_status( dict ); + } + catch ( BadProperty& e ) + { + throw BadProperty( + String::compose( "Setting status of a '%1' with node ID %2: %3", get_name(), get_node_id(), e.message() ) ); + } + bool value = container_->is_frozen( local_id_ ); + updateValue< bool >( dict, names::frozen, value ); + container_->set_frozen_( value, local_id_ ); +} +void +JitNode::init_state_() +{ + container_->init_state_( local_id_ ); +} + +void +JitNode::init_buffers_() +{ + container_->init_buffers_( local_id_ ); +} +} diff --git a/nestkernel/jit_node.h b/nestkernel/jit_node.h new file mode 100644 index 0000000000..d41c2e97cc --- /dev/null +++ b/nestkernel/jit_node.h @@ -0,0 +1,246 @@ +/* + * jit_node.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 JITNODE_H +#define JITNODE_H + +#include "node.h" +#include "vectorized_node.h" + + +namespace nest +{ + +class JitNode : public Node +{ +private: + index local_id_; + + +public: + std::shared_ptr< VectorizedNode > container_; + + JitNode(); + ~JitNode(); + + JitNode( JitNode const& ); + + void reset_node(); + + + std::shared_ptr< VectorizedNode > + get_container() + { + return container_; + } + + void clone_container( std::shared_ptr< VectorizedNode > container ); + + std::map< std::string, const std::vector< double >& > get_recordables() const; + + + index get_pos_in_thread() const; + + void set_pos_in_thread( index pos ); + + bool supports_urbanczik_archiving() const; + + bool local_receiver() const; + + bool one_node_per_process() const; + + bool is_off_grid() const; + + bool is_proxy() const; + + index get_node_id() const; + + index + get_node_local_id() const + { + return this->local_id_; + } + + bool is_frozen() const; + + bool node_uses_wfr() const; + + void set_node_uses_wfr( const bool ); + + void init(); + + void pre_run_hook(); + + void calibrate_time( const TimeConverter& ); + + void post_run_cleanup(); + + void finalize(); + + void update( Time const&, const long, const long ); + + bool wfr_update( Time const&, const long, const long ); + + void set_status( const DictionaryDatum& ); + + void get_status( DictionaryDatum& ) const; + + void set_container( std::shared_ptr< VectorizedNode > container ); + + port send_test_event( Node& receiving_node, rport receptor_type, synindex syn_id, bool dummy_target ); + + port handles_test_event( SpikeEvent&, rport receptor_type ); + + port handles_test_event( WeightRecorderEvent&, rport receptor_type ); + + port handles_test_event( RateEvent&, rport receptor_type ); + + port handles_test_event( DataLoggingRequest&, rport receptor_type ); + + port handles_test_event( CurrentEvent&, rport receptor_type ); + + port handles_test_event( ConductanceEvent&, rport receptor_type ); + + port handles_test_event( DoubleDataEvent&, rport receptor_type ); + + port handles_test_event( DSSpikeEvent&, rport receptor_type ); + + port handles_test_event( DSCurrentEvent&, rport receptor_type ); + + port handles_test_event( GapJunctionEvent&, rport receptor_type ); + + port handles_test_event( InstantaneousRateConnectionEvent&, rport receptor_type ); + + port handles_test_event( DiffusionConnectionEvent&, rport receptor_type ); + + port handles_test_event( DelayedRateConnectionEvent&, rport receptor_type ); + + void sends_secondary_event( GapJunctionEvent& ge ); + + void sends_secondary_event( InstantaneousRateConnectionEvent& re ); + + void sends_secondary_event( DelayedRateConnectionEvent& re ); + + void sends_secondary_event( DiffusionConnectionEvent& de ); + + void register_stdp_connection( double, double ); + + void handle( SpikeEvent& e ); + + void handle( WeightRecorderEvent& e ); + + void handle( RateEvent& e ); + + void handle( DataLoggingRequest& e ); + + void handle( DataLoggingReply& e ); + + void handle( CurrentEvent& e ); + + void handle( ConductanceEvent& e ); + + void handle( DoubleDataEvent& e ); + + void handle( GapJunctionEvent& e ); + + void handle( InstantaneousRateConnectionEvent& e ); + + void handle( DiffusionConnectionEvent& e ); + + void handle( DelayedRateConnectionEvent& e ); + + double get_Ca_minus() const; + + double get_synaptic_elements( Name ) const; + + int get_synaptic_elements_vacant( Name ) const; + + int get_synaptic_elements_connected( Name ) const; + + std::map< Name, double > get_synaptic_elements() const; + + void update_synaptic_elements( double ); + + void decay_synaptic_elements_vacant(); + + void connect_synaptic_element( Name, int ); + + double get_K_value( double t ); + + double get_LTD_value( double t ); + + void get_K_values( double t, double& Kminus, double& nearest_neighbor_Kminus, double& Kminus_triplet ); + + void get_history( double t1, + double t2, + std::deque< histentry >::iterator* start, + std::deque< histentry >::iterator* finish ); + + + void get_LTP_history( double t1, + double t2, + std::deque< histentry_extended >::iterator* start, + std::deque< histentry_extended >::iterator* finish ); + + void get_urbanczik_history( double t1, + double t2, + std::deque< histentry_extended >::iterator* start, + std::deque< histentry_extended >::iterator* finish, + int value ); + + double get_C_m( int comp ); + + double get_g_L( int comp ); + + double get_tau_L( int comp ); + + double get_tau_s( int comp ); + + double get_tau_syn_ex( int comp ); + + double get_tau_syn_in( int comp ); + + void event_hook( DSSpikeEvent& ); + + void event_hook( DSCurrentEvent& ); + + + SignalType sends_signal() const; + + SignalType receives_signal() const; + + void set_status_base( const DictionaryDatum& ); + +private: + void set_node_id_( index ); + +protected: + void init_state_(); + + void init_buffers_(); + + void set_initialized_(); + + void set_frozen_( bool frozen ); +}; +} +#endif // JITNODE_H diff --git a/nestkernel/model.cpp b/nestkernel/model.cpp index 0e52e2b3b4..56e7d0b889 100644 --- a/nestkernel/model.cpp +++ b/nestkernel/model.cpp @@ -42,9 +42,19 @@ Model::Model( const std::string& name ) : name_( name ) , type_id_( 0 ) , memory_() + , uses_vectors( false ) + , thread_to_node( kernel().vp_manager.get_num_threads() ) { } +Model::Model( const Model& m ) + : name_( m.name_ ) + , type_id_( m.type_id_ ) + , memory_( m.memory_ ) + , uses_vectors( m.uses_vectors ) + , thread_to_node( kernel().vp_manager.get_num_threads() ) +{ +} void Model::set_threads() { diff --git a/nestkernel/model.h b/nestkernel/model.h index a478660ecd..b52e1bf8d3 100644 --- a/nestkernel/model.h +++ b/nestkernel/model.h @@ -24,6 +24,8 @@ #define MODEL_H // C++ includes: +#include +#include #include #include #include @@ -33,10 +35,11 @@ // Includes from nestkernel: #include "node.h" - +#include "vectorized_node.h" // Includes from sli: #include "dictutils.h" + namespace nest { class TimeConverter; @@ -57,17 +60,13 @@ class Model { public: Model( const std::string& name ); - Model( const Model& m ) - : name_( m.name_ ) - , type_id_( m.type_id_ ) - , memory_( m.memory_ ) - { - } + Model( const Model& m ); virtual ~Model() { } + /** * Create clone with new name. */ @@ -89,6 +88,19 @@ class Model */ Node* create( thread t ); + virtual std::shared_ptr< VectorizedNode > + get_container() + { + assert( false ); + return 0; + } + + virtual void + clone_container( std::shared_ptr< VectorizedNode > ) + { + assert( false ); + } + /** * Deletes all nodes which belong to this model. */ @@ -201,6 +213,41 @@ class Model { return type_id_; } + bool + get_uses_vectors() const + { + return uses_vectors; + } + + void + set_uses_vecotrs( bool is_vectorized ) + { + uses_vectors = is_vectorized; + } + + void + add_thread_node_pair( thread t, index pos ) + { + thread_to_node.at( t ).push_back( pos ); + } + + bool + has_thread_assigned( thread t ) + { + + if ( thread_to_node.at( t ).size() > 0 ) + { + return true; + } + return false; + } + + index + get_node_pos_in_thread( thread t ) + { + return thread_to_node.at( t ).at( 0 ); + } + private: virtual void set_status_( DictionaryDatum ) = 0; @@ -238,6 +285,19 @@ class Model * Memory for all nodes sorted by threads. */ std::vector< std::vector< Node* > > memory_; + + /** + * Indicartor if the model supports the vectorization schema + * + */ + bool uses_vectors; + + /** + * Stores if the model has seen the thread before or not. + * Only needed when using the vectorization schema + * + */ + std::vector< std::vector< index > > thread_to_node; }; diff --git a/nestkernel/model_manager.cpp b/nestkernel/model_manager.cpp index d85340a19b..cc2c2dc720 100644 --- a/nestkernel/model_manager.cpp +++ b/nestkernel/model_manager.cpp @@ -92,7 +92,9 @@ ModelManager::initialize() // set the number of threads for the number of sli pools builtin_node_models_[ i ]->set_threads(); std::string name = builtin_node_models_[ i ]->get_name(); - node_models_.push_back( builtin_node_models_[ i ]->clone( name ) ); + Model* cloned_model = builtin_node_models_[ i ]->clone( name ); + cloned_model->set_uses_vecotrs( builtin_node_models_[ i ]->get_uses_vectors() ); + node_models_.push_back( cloned_model ); modeldict_->insert( name, i ); } @@ -222,10 +224,13 @@ ModelManager::register_node_model_( Model* model ) model->set_model_id( id ); model->set_type_id( id ); + builtin_node_models_.push_back( model ); Model* cloned_model = model->clone( name ); cloned_model->set_model_id( id ); + cloned_model->set_uses_vecotrs( model->get_uses_vectors() ); + node_models_.push_back( cloned_model ); modeldict_->insert( name, id ); @@ -246,6 +251,12 @@ ModelManager::copy_node_model_( index old_id, Name new_name ) old_model->deprecation_warning( "CopyModel" ); Model* new_model = old_model->clone( new_name.toString() ); + if ( old_model->get_uses_vectors() ) + { + new_model->clone_container( old_model->get_container() ); + new_model->set_uses_vecotrs( true ); + } + const index new_id = node_models_.size(); new_model->set_model_id( new_id ); diff --git a/nestkernel/model_manager.h b/nestkernel/model_manager.h index 0293170738..03985d2193 100644 --- a/nestkernel/model_manager.h +++ b/nestkernel/model_manager.h @@ -83,6 +83,10 @@ class ModelManager : public ManagerInterface const std::vector< ConnectorModel* >& get_connection_models( thread tid ); + + template < class ModelT > + index register_node_model_vectorized( const Name& name, std::string deprecation_info = std::string() ); + /** * Register a node-model prototype. * This function must be called exactly once for each model class to make diff --git a/nestkernel/model_manager_impl.h b/nestkernel/model_manager_impl.h index 235ec8b8f0..3e0073923c 100644 --- a/nestkernel/model_manager_impl.h +++ b/nestkernel/model_manager_impl.h @@ -31,6 +31,7 @@ // Includes from nestkernel: #include "connection_label.h" +#include "jit_node.h" #include "kernel_manager.h" #include "nest.h" #include "target_identifier.h" @@ -53,6 +54,27 @@ ModelManager::register_node_model( const Name& name, std::string deprecation_inf return register_node_model_( model ); } +template < class ModelT > +index +ModelManager::register_node_model_vectorized( const Name& name, std::string deprecation_info ) +{ + if ( modeldict_->known( name ) ) + { + std::string msg = String::compose( "A model called '%1' already exists. Please choose a different name!", name ); + throw NamingConflict( msg ); + } + + + Model* model = new GenericModel< JitNode >( name.toString(), deprecation_info ); + model->set_uses_vecotrs( true ); + + std::shared_ptr< ModelT > container = std::make_shared< ModelT >(); + const Node& wrapper = model->get_prototype(); + ( *( JitNode* ) &wrapper ).set_container( container ); + return register_node_model_( model ); +} + + template < template < typename targetidentifierT > class ConnectionT > void ModelManager::register_connection_model( const std::string& name, const RegisterConnectionModelFlags flags ) @@ -129,4 +151,4 @@ ModelManager::get_proxy_node( thread tid, index node_id ) } // namespace nest -#endif /* #ifndef MODEL_MANAGER_IMPL_H */ +#endif /* #ifndef MODEL_MANAGER_IMPL_H */ \ No newline at end of file diff --git a/nestkernel/node.cpp b/nestkernel/node.cpp index a387e2560c..1b2cd7f45d 100644 --- a/nestkernel/node.cpp +++ b/nestkernel/node.cpp @@ -103,7 +103,11 @@ void Node::set_initialized_() { } - +index +Node::get_node_id() const +{ + return node_id_; +} std::string Node::get_name() const { @@ -152,6 +156,7 @@ Node::get_status_base() ( *dict )[ names::model_id ] = get_model_id(); ( *dict )[ names::global_id ] = get_node_id(); ( *dict )[ names::vp ] = get_vp(); + ( *dict )[ Name( "is_vectorized" ) ] = false; ( *dict )[ names::element_type ] = LiteralDatum( get_element_type() ); // add information available only for local nodes @@ -491,4 +496,4 @@ Node::event_hook( DSCurrentEvent& e ) e.get_receiver().handle( e ); } -} // namespace +} // namespace \ No newline at end of file diff --git a/nestkernel/node.h b/nestkernel/node.h index 0b8faee901..be24c447a1 100644 --- a/nestkernel/node.h +++ b/nestkernel/node.h @@ -53,7 +53,7 @@ namespace nest class Model; class ArchivingNode; class TimeConverter; - +class VectorizedNode; /** * @defgroup user_interface Model developer interface. @@ -83,22 +83,22 @@ class TimeConverter; /** @BeginDocumentation - Name: Node - General properties of all nodes. - - Parameters: - frozen booltype - Whether the node is updated during simulation - global_id integertype - The node ID of the node (cf. local_id) - local booltype - Whether the node is available on the local process - model literaltype - The model type the node was created from - state integertype - The state of the node (see the help on elementstates - for details) - thread integertype - The id of the thread the node is assigned to (valid - locally) - vp integertype - The id of the virtual process the node is assigned - to (valid globally) - - SeeAlso: GetStatus, SetStatus, elementstates - */ + Name: Node - General properties of all nodes. + + Parameters: + frozen booltype - Whether the node is updated during simulation + global_id integertype - The node ID of the node (cf. local_id) + local booltype - Whether the node is available on the local process + model literaltype - The model type the node was created from + state integertype - The state of the node (see the help on elementstates + for details) + thread integertype - The id of the thread the node is assigned to (valid + locally) + vp integertype - The id of the virtual process the node is assigned + to (valid globally) + + SeeAlso: GetStatus, SetStatus, elementstates +*/ class Node { @@ -128,6 +128,25 @@ class Node return nullptr; } + virtual void + reset_node() + { + } + + + virtual std::shared_ptr< VectorizedNode > + get_container() + { + return 0; + } + + + virtual void + clone_container( std::shared_ptr< VectorizedNode > ) + { + } + + /** * Returns true if the node has proxies on remote threads. This is * used to discriminate between different types of nodes, when adding @@ -196,7 +215,27 @@ class Node * * The smallest valid node ID is 1. */ - index get_node_id() const; + virtual index get_node_id() const; + + virtual index + get_node_local_id() const + { + return get_node_id(); + } + + virtual std::map< std::string, const std::vector< double >& > + get_recordables() const + { + + return std::map< std::string, const std::vector< double >& >(); + }; + + virtual double + get_state_element( size_t ) const + { + assert( false ); + return -1; + } /** * Return lockpointer to the NodeCollection that created this node. @@ -224,7 +263,7 @@ class Node /** * Returns true if node is frozen, i.e., shall not be updated. */ - bool is_frozen() const; + virtual bool is_frozen() const; /** * Returns true if the node uses the waveform relaxation method @@ -245,7 +284,7 @@ class Node * called on a given node, otherwise it returns immediately. init() calls * virtual functions init_state_() and init_buffers_(). */ - void init(); + virtual void init(); /** * Re-calculate dependent parameters of the node. @@ -351,6 +390,19 @@ class Node */ virtual void get_status( DictionaryDatum& ) const = 0; + virtual void + set_container( std::shared_ptr< VectorizedNode > ) + { + } + + + virtual Node* + get_wrapper( index = -1 ) + { + return this; + } + + public: /** * @defgroup event_interface Communication. @@ -788,7 +840,7 @@ class Node * Forwards to set_status() of the derived class. * @internal */ - void set_status_base( const DictionaryDatum& ); + virtual void set_status_base( const DictionaryDatum& ); /** * Returns true if node is model prototype. @@ -827,7 +879,7 @@ class Node DeprecationWarning deprecation_warning; private: - void set_node_id_( index ); //!< Set global node id + virtual void set_node_id_( index ); //!< Set global node id void set_nc_( NodeCollectionPTR ); @@ -863,7 +915,7 @@ class Node Model& get_model_() const; //! Mark node as frozen. - void + virtual void set_frozen_( bool frozen ) { frozen_ = frozen; @@ -914,12 +966,14 @@ Node::is_frozen() const return frozen_; } + inline bool Node::node_uses_wfr() const { return node_uses_wfr_; } + inline bool Node::supports_urbanczik_archiving() const { @@ -968,11 +1022,6 @@ Node::get_element_type() const return names::neuron; } -inline index -Node::get_node_id() const -{ - return node_id_; -} inline NodeCollectionPTR Node::get_nc() const @@ -1057,5 +1106,4 @@ Node::get_thread_lid() const } } // namespace - #endif diff --git a/nestkernel/node_manager.cpp b/nestkernel/node_manager.cpp index b957ec47d7..b5191914ed 100644 --- a/nestkernel/node_manager.cpp +++ b/nestkernel/node_manager.cpp @@ -47,6 +47,7 @@ namespace nest NodeManager::NodeManager() : local_nodes_( 1 ) + , vectorized_nodes( 0 ) , wfr_nodes_vec_() , wfr_is_used_( false ) , wfr_network_size_( 0 ) // zero to force update @@ -69,6 +70,7 @@ NodeManager::initialize() // explicitly force construction of wfr_nodes_vec_ to ensure consistent state wfr_network_size_ = 0; local_nodes_.resize( kernel().vp_manager.get_num_threads() ); + vectorized_nodes.resize( kernel().vp_manager.get_num_threads() ); num_thread_local_devices_.resize( kernel().vp_manager.get_num_threads(), 0 ); ensure_valid_thread_local_ids(); @@ -112,7 +114,6 @@ NodeManager::add_node( index model_id, long n ) { throw BadProperty(); } - Model* model = kernel().model_manager.get_node_model( model_id ); assert( model ); model->deprecation_warning( "Create" ); @@ -138,7 +139,14 @@ NodeManager::add_node( index model_id, long n ) if ( model->has_proxies() ) { - add_neurons_( *model, min_node_id, max_node_id, nc_ptr ); + if ( model->get_uses_vectors() ) + { + add_vectorized_neurons_( model, min_node_id, max_node_id, nc_ptr ); + } + else + { + add_neurons_( *model, min_node_id, max_node_id, nc_ptr ); + } } else if ( not model->one_node_per_process() ) { @@ -182,6 +190,36 @@ NodeManager::add_node( index model_id, long n ) return nc_ptr; } +void +NodeManager::add_vectorized_neurons_( Model* model, index min_node_id, index max_node_id, NodeCollectionPTR nc_ptr ) +{ + const size_t num_vps = kernel().vp_manager.get_num_virtual_processes(); + + // just for the pipline to work, but will be deleted! + bool tmp = nc_ptr->valid(); + tmp = tmp and tmp; + +#pragma omp parallel + { + const size_t tid = kernel().vp_manager.get_thread_id(); + + const size_t vp = kernel().vp_manager.thread_to_vp( tid ); + const size_t min_node_id_vp = kernel().vp_manager.node_id_to_vp( min_node_id ); + + size_t start_id = min_node_id + ( vp - min_node_id_vp ) % num_vps; + + if ( start_id <= max_node_id ) + { + + std::shared_ptr< VectorizedNode > t_container = get_container( model, tid ); + size_t diff = max_node_id - start_id; + bool has_more_than_one = diff % num_vps == 0; + size_t number_of_elements_to_add = has_more_than_one ? diff / num_vps + 1 : 1; + + t_container->resize( number_of_elements_to_add, tid ); + } + } +} void NodeManager::add_neurons_( Model& model, index min_node_id, index max_node_id, NodeCollectionPTR nc_ptr ) @@ -192,10 +230,10 @@ NodeManager::add_neurons_( Model& model, index min_node_id, index max_node_id, N const size_t max_new_per_thread = static_cast< size_t >( std::ceil( static_cast< double >( max_node_id - min_node_id + 1 ) / num_vps ) ); + #pragma omp parallel { const index t = kernel().vp_manager.get_thread_id(); - try { model.reserve_additional( t, max_new_per_thread ); @@ -204,12 +242,12 @@ NodeManager::add_neurons_( Model& model, index min_node_id, index max_node_id, N // - node_id >= min_node_id const size_t vp = kernel().vp_manager.thread_to_vp( t ); const size_t min_node_id_vp = kernel().vp_manager.node_id_to_vp( min_node_id ); - size_t node_id = min_node_id + ( num_vps + vp - min_node_id_vp ) % num_vps; while ( node_id <= max_node_id ) { Node* node = model.create( t ); + node->set_node_id_( node_id ); node->set_nc_( nc_ptr ); node->set_model_id( model.get_model_id() ); @@ -220,6 +258,7 @@ NodeManager::add_neurons_( Model& model, index min_node_id, index max_node_id, N local_nodes_[ t ].add_local_node( *node ); node_id += num_vps; } + local_nodes_[ t ].set_max_node_id( max_node_id ); } catch ( std::exception& err ) @@ -421,8 +460,11 @@ NodeManager::get_num_thread_local_devices( thread t ) const Node* NodeManager::get_node_or_proxy( index node_id, thread t ) { + assert( 0 <= t and ( t == -1 or t < kernel().vp_manager.get_num_threads() ) ); - assert( 0 < node_id and node_id <= size() ); + // TODO: this assertion must consider dependency between nodes from different threads + // TODO: see node_manager:: add_neurons::set_max_node_id + // assert( 0 < node_id and node_id <= local_nodes_[ t ].size() ); Node* node = local_nodes_[ t ].get_node_by_node_id( node_id ); if ( not node ) @@ -573,6 +615,8 @@ NodeManager::destruct_nodes_() { delete node.get_node(); } + + vectorized_nodes[ tid ].clear(); local_nodes_[ tid ].clear(); } // omp parallel } @@ -670,15 +714,19 @@ NodeManager::prepare_nodes() void NodeManager::post_run_cleanup() { + std::cout << "cleaning starts" << std::endl; #pragma omp parallel { + index t = kernel().vp_manager.get_thread_id(); SparseNodeArray::const_iterator n; for ( n = local_nodes_[ t ].begin(); n != local_nodes_[ t ].end(); ++n ) { n->get_node()->post_run_cleanup(); } + } // omp parallel + std::cout << "cleaning ends" << std::endl; } /** @@ -765,4 +813,27 @@ void NodeManager::set_status( const DictionaryDatum& ) { } + + +std::shared_ptr< nest::VectorizedNode > +NodeManager::get_container( Model* model, size_t tid ) +{ + + index t_container_pos; + if ( model->has_thread_assigned( tid ) ) + { + t_container_pos = model->get_node_pos_in_thread( tid ); + } + else + { + std::shared_ptr< nest::VectorizedNode > t_container = model->get_container()->clone(); + t_container->set_thread( tid ); + vectorized_nodes.at( tid ).push_back( t_container ); + t_container_pos = vectorized_nodes.at( tid ).size() - 1; + model->add_thread_node_pair( tid, t_container_pos ); + } + return vectorized_nodes.at( tid ).at( t_container_pos ); +} + + } diff --git a/nestkernel/node_manager.h b/nestkernel/node_manager.h index 0ce68eb5a2..d243aab104 100644 --- a/nestkernel/node_manager.h +++ b/nestkernel/node_manager.h @@ -35,6 +35,7 @@ #include "nest_types.h" #include "node_collection.h" #include "sparse_node_array.h" +#include "vectorized_node.h" // Includes from sli: #include "arraydatum.h" @@ -266,6 +267,9 @@ class NodeManager : public ManagerInterface */ void add_neurons_( Model& model, index min_node_id, index max_node_id, NodeCollectionPTR nc_ptr ); + void add_vectorized_neurons_( Model* model, index min_node_id, index max_node_id, NodeCollectionPTR nc_ptr ); + + /** * Add device nodes. * @@ -290,11 +294,14 @@ class NodeManager : public ManagerInterface void add_music_nodes_( Model& model, index min_node_id, index max_node_id, NodeCollectionPTR nc_ptr ); private: + std::shared_ptr< nest::VectorizedNode > get_container( Model* model, size_t tid ); + /** * The network as sparse array of local nodes. One entry per thread, * which contains only the thread-local nodes. */ std::vector< SparseNodeArray > local_nodes_; + std::vector< std::vector< std::shared_ptr< VectorizedNode > > > vectorized_nodes; std::vector< std::vector< Node* > > wfr_nodes_vec_; //!< Nodelists for unfrozen nodes that //!< use the waveform relaxation method diff --git a/nestkernel/proxynode.cpp b/nestkernel/proxynode.cpp index 52c0ffed4e..bdd4f04eb0 100644 --- a/nestkernel/proxynode.cpp +++ b/nestkernel/proxynode.cpp @@ -85,6 +85,17 @@ proxynode::sends_signal() const return kernel().model_manager.get_node_model( get_model_id() )->sends_signal(); } +std::shared_ptr< VectorizedNode > +proxynode::get_container() +{ + return kernel().model_manager.get_node_model( get_model_id() )->get_container(); +} +void +proxynode::clone_container( std::shared_ptr< VectorizedNode > container ) +{ + kernel().model_manager.get_node_model( get_model_id() )->clone_container( container ); +} + void proxynode::get_status( DictionaryDatum& d ) const { diff --git a/nestkernel/proxynode.h b/nestkernel/proxynode.h index 915499eb68..c349d5fe09 100644 --- a/nestkernel/proxynode.h +++ b/nestkernel/proxynode.h @@ -94,6 +94,10 @@ class proxynode : public Node { } + std::shared_ptr< VectorizedNode > get_container() override; + + void clone_container( std::shared_ptr< VectorizedNode > container ) override; + void get_status( DictionaryDatum& ) const override; /** diff --git a/nestkernel/recordables_map.h b/nestkernel/recordables_map.h index a472ef8540..0d671b2ff2 100644 --- a/nestkernel/recordables_map.h +++ b/nestkernel/recordables_map.h @@ -139,16 +139,34 @@ class DataAccessFunctor // copying element in std::map when using libc++ under C++11. HostNode* parent_; size_t elem_; + const std::vector< double >* data_; + bool requires_context; public: DataAccessFunctor( HostNode& n, size_t elem ) : parent_( &n ) - , elem_( elem ) {}; + , elem_( elem ) + , data_( nullptr ) + , requires_context( false ) {}; + + DataAccessFunctor( const std::vector< double >& data ) + : parent_( nullptr ) + , elem_( 0 ) + , data_( &data ) + , requires_context( true ) {}; double - operator()() const + operator()( size_t pos = 0 ) const { - return parent_->get_state_element( elem_ ); + if ( requires_context ) + { + assert( data_ != nullptr ); + return data_->at( pos ); + } + else + { + return parent_->get_state_element( elem_ ); + } }; }; diff --git a/nestkernel/recording_backend_memory.cpp b/nestkernel/recording_backend_memory.cpp index 6ff762603f..a7b1909e63 100644 --- a/nestkernel/recording_backend_memory.cpp +++ b/nestkernel/recording_backend_memory.cpp @@ -108,7 +108,6 @@ nest::RecordingBackendMemory::write( const RecordingDevice& device, { thread t = device.get_thread(); index node_id = device.get_node_id(); - device_data_[ t ][ node_id ].push_back( event, double_values, long_values ); } @@ -192,6 +191,7 @@ nest::RecordingBackendMemory::DeviceData::push_back( const Event& event, const std::vector< double >& double_values, const std::vector< long >& long_values ) { + senders_.push_back( event.get_sender_node_id() ); if ( time_in_steps_ ) @@ -206,6 +206,7 @@ nest::RecordingBackendMemory::DeviceData::push_back( const Event& event, for ( size_t i = 0; i < double_values.size(); ++i ) { + double_values_[ i ].push_back( double_values[ i ] ); } for ( size_t i = 0; i < long_values.size(); ++i ) diff --git a/nestkernel/simulation_manager.cpp b/nestkernel/simulation_manager.cpp index 53d07b7a0c..256b4e1835 100644 --- a/nestkernel/simulation_manager.cpp +++ b/nestkernel/simulation_manager.cpp @@ -545,6 +545,7 @@ nest::SimulationManager::run( Time const& t ) kernel().random_manager.check_rng_synchrony(); kernel().io_manager.pre_run_hook(); + if ( not prepared_ ) { std::string msg = "Run called without calling Prepare."; @@ -591,6 +592,7 @@ nest::SimulationManager::run( Time const& t ) call_update_(); kernel().io_manager.post_run_hook(); + kernel().random_manager.check_rng_synchrony(); sw_simulate_.stop(); @@ -658,7 +660,10 @@ nest::SimulationManager::call_update_() simulating_ = true; simulated_ = true; + std::cout << "update start\n"; update_(); + std::cout << "update end\n"; + simulating_ = false; @@ -815,6 +820,7 @@ nest::SimulationManager::update_() // from postsynaptic data update_connection_infrastructure( tid ); + } // of structural plasticity if ( from_step_ == 0 ) @@ -977,6 +983,7 @@ nest::SimulationManager::update_() { if ( kernel().connection_manager.has_primary_connections() ) { + kernel().event_delivery_manager.gather_spike_data( tid ); } if ( kernel().connection_manager.secondary_connections_exist() ) diff --git a/nestkernel/structural_plasticity_vector.cpp b/nestkernel/structural_plasticity_vector.cpp new file mode 100644 index 0000000000..2eb670708c --- /dev/null +++ b/nestkernel/structural_plasticity_vector.cpp @@ -0,0 +1,261 @@ +/* + * structural_plasticity_vector.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 "structural_plasticity_vector.h" + +// Includes from nestkernel: +#include "kernel_manager.h" +#include "synaptic_element.h" + +// Includes from sli: +#include "dictutils.h" + + +namespace nest +{ +nest::StructuralPlasticityVector::StructuralPlasticityVector() + : Ca_t_( 0 ) + , Ca_minus_( 0 ) + , tau_Ca_( 0 ) + , beta_Ca_( 0 ) + , synaptic_elements_map_( 0 ) +{ +} + + +void +StructuralPlasticityVector::resize( index extended_space, index thread_id ) +{ + index total_space = size(); + + Ca_minus_.resize( total_space, 0 ); + Ca_t_.resize( total_space, 0 ); + tau_Ca_.resize( total_space, 10000.0 ); + beta_Ca_.resize( total_space, 0.001 ); + synaptic_elements_map_.resize( total_space, std::map< Name, SynapticElement >() ); + + VectorizedNode::resize( extended_space, thread_id ); +} +void +nest::StructuralPlasticityVector::get_status( DictionaryDatum& d, index local_id ) const +{ + DictionaryDatum synaptic_elements_d; + DictionaryDatum synaptic_element_d; + + def< double >( d, names::Ca, Ca_minus_.at( local_id ) ); + def< double >( d, names::tau_Ca, tau_Ca_.at( local_id ) ); + def< double >( d, names::beta_Ca, beta_Ca_.at( local_id ) ); + + synaptic_elements_d = DictionaryDatum( new Dictionary ); + def< DictionaryDatum >( d, names::synaptic_elements, synaptic_elements_d ); + for ( std::map< Name, SynapticElement >::const_iterator it = synaptic_elements_map_.at( local_id ).begin(); + it != synaptic_elements_map_.at( local_id ).end(); + ++it ) + { + synaptic_element_d = DictionaryDatum( new Dictionary ); + def< DictionaryDatum >( synaptic_elements_d, it->first, synaptic_element_d ); + it->second.get( synaptic_element_d ); + } +} +void +nest::StructuralPlasticityVector::set_status( const DictionaryDatum& d, index local_id ) +{ + // We need to preserve values in case invalid values are set + double new_tau_Ca = tau_Ca_.at( local_id ); + double new_beta_Ca = beta_Ca_.at( local_id ); + updateValue< double >( d, names::tau_Ca, new_tau_Ca ); + updateValue< double >( d, names::beta_Ca, new_beta_Ca ); + + if ( new_tau_Ca <= 0.0 ) + { + throw BadProperty( "All time constants must be strictly positive." ); + } + tau_Ca_.at( local_id ) = new_tau_Ca; + + if ( new_beta_Ca <= 0.0 ) + { + throw BadProperty( + "For Ca to function as an integrator of the electrical activity, beta_ca " + "needs to be greater than 0." ); + } + beta_Ca_.at( local_id ) = new_beta_Ca; + + // check, if to clear spike history and K_minus + bool clear = false; + updateValue< bool >( d, names::clear, clear ); + if ( clear ) + { + clear_history( local_id ); + } + + if ( d->known( names::synaptic_elements_param ) ) + { + const DictionaryDatum synaptic_elements_dict = getValue< DictionaryDatum >( d, names::synaptic_elements_param ); + + for ( std::map< Name, SynapticElement >::iterator it = synaptic_elements_map_.at( local_id ).begin(); + it != synaptic_elements_map_.at( local_id ).end(); + ++it ) + { + if ( synaptic_elements_dict->known( it->first ) ) + { + const DictionaryDatum synaptic_elements_a = getValue< DictionaryDatum >( synaptic_elements_dict, it->first ); + it->second.set( synaptic_elements_a ); + } + } + } + if ( not d->known( names::synaptic_elements ) ) + { + return; + } + // we replace the existing synaptic_elements_map_ by the new one + DictionaryDatum synaptic_elements_d; + std::pair< std::map< Name, SynapticElement >::iterator, bool > insert_result; + + synaptic_elements_map_.at( local_id ) = std::map< Name, SynapticElement >(); + synaptic_elements_d = getValue< DictionaryDatum >( d, names::synaptic_elements ); + + for ( Dictionary::const_iterator i = synaptic_elements_d->begin(); i != synaptic_elements_d->end(); ++i ) + { + insert_result = + synaptic_elements_map_.at( local_id ).insert( std::pair< Name, SynapticElement >( i->first, SynapticElement() ) ); + ( insert_result.first->second ).set( getValue< DictionaryDatum >( synaptic_elements_d, i->first ) ); + } +} +void +nest::StructuralPlasticityVector::clear_history( index local_id ) +{ + Ca_minus_.at( local_id ) = 0.0; + Ca_t_.at( local_id ) = 0.0; +} + +double +nest::StructuralPlasticityVector::get_synaptic_elements( Name n, index local_id ) const +{ + std::map< Name, SynapticElement >::const_iterator se_it; + se_it = synaptic_elements_map_.at( local_id ).find( n ); + double z_value; + + if ( se_it != synaptic_elements_map_.at( local_id ).end() ) + { + z_value = ( se_it->second ).get_z(); + if ( ( se_it->second ).continuous() ) + { + return z_value; + } + else + { + return std::floor( z_value ); + } + } + else + { + return 0.0; + } +} +int +nest::StructuralPlasticityVector::get_synaptic_elements_vacant( Name n, index local_id ) const +{ + std::map< Name, SynapticElement >::const_iterator se_it; + se_it = synaptic_elements_map_.at( local_id ).find( n ); + + if ( se_it != synaptic_elements_map_.at( local_id ).end() ) + { + return se_it->second.get_z_vacant(); + } + else + { + return 0; + } +} +int +nest::StructuralPlasticityVector::get_synaptic_elements_connected( Name n, index local_id ) const +{ + std::map< Name, SynapticElement >::const_iterator se_it; + se_it = synaptic_elements_map_.at( local_id ).find( n ); + + if ( se_it != synaptic_elements_map_.at( local_id ).end() ) + { + return se_it->second.get_z_connected(); + } + else + { + return 0; + } +} +std::map< Name, double > +nest::StructuralPlasticityVector::get_synaptic_elements( index local_id ) const +{ + std::map< Name, double > n_map; + + for ( std::map< Name, SynapticElement >::const_iterator it = synaptic_elements_map_.at( local_id ).begin(); + it != synaptic_elements_map_.at( local_id ).end(); + ++it ) + { + n_map.insert( std::pair< Name, double >( it->first, get_synaptic_elements( it->first, local_id ) ) ); + } + return n_map; +} +void +nest::StructuralPlasticityVector::update_synaptic_elements( double t, index local_id ) +{ + assert( t >= Ca_t_.at( local_id ) ); + + for ( std::map< Name, SynapticElement >::iterator it = synaptic_elements_map_.at( local_id ).begin(); + it != synaptic_elements_map_.at( local_id ).end(); + ++it ) + { + it->second.update( t, Ca_t_.at( local_id ), Ca_minus_.at( local_id ), tau_Ca_.at( local_id ) ); + } + // Update calcium concentration + Ca_minus_.at( local_id ) = + Ca_minus_.at( local_id ) * std::exp( ( Ca_t_.at( local_id ) - t ) / tau_Ca_.at( local_id ) ); + Ca_t_.at( local_id ) = t; +} +void +nest::StructuralPlasticityVector::decay_synaptic_elements_vacant( index local_id ) +{ + for ( std::map< Name, SynapticElement >::iterator it = synaptic_elements_map_.at( local_id ).begin(); + it != synaptic_elements_map_.at( local_id ).end(); + ++it ) + { + it->second.decay_z_vacant(); + } +} +void +nest::StructuralPlasticityVector::connect_synaptic_element( Name name, int n, index local_id ) +{ + std::map< Name, SynapticElement >::iterator se_it; + se_it = synaptic_elements_map_.at( local_id ).find( name ); + + if ( se_it != synaptic_elements_map_.at( local_id ).end() ) + { + se_it->second.connect( n ); + } +} +void +nest::StructuralPlasticityVector::set_spiketime( const Time& t_sp, index local_id, double offset ) +{ + const double t_sp_ms = t_sp.get_ms() - offset; + update_synaptic_elements( t_sp_ms, local_id ); + Ca_minus_.at( local_id ) += beta_Ca_.at( local_id ); +} +} diff --git a/nestkernel/structural_plasticity_vector.h b/nestkernel/structural_plasticity_vector.h new file mode 100644 index 0000000000..1883a9db5f --- /dev/null +++ b/nestkernel/structural_plasticity_vector.h @@ -0,0 +1,126 @@ +/* + * structural_plasticity_vector.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 STRUCTURAL_PLASTICITY_VECTOR_H +#define STRUCTURAL_PLASTICITY_VECTOR_H + +#include "vectorized_node.h" +namespace nest +{ +class StructuralPlasticityVector : public VectorizedNode +{ +public: + /** + * \fn StructuralPlasticityVector() + * Constructor. + */ + StructuralPlasticityVector(); + + /** + * \fn StructuralPlasticityNode() + * Copy Constructor. + */ + StructuralPlasticityVector( const StructuralPlasticityVector& ) = delete; + + ~StructuralPlasticityVector() + { + beta_Ca_.clear(); + tau_Ca_.clear(); + Ca_minus_.clear(); + Ca_t_.clear(); + synaptic_elements_map_.clear(); + } + + double get_synaptic_elements( Name n, index local_id ) const; + + int get_synaptic_elements_vacant( Name n, index local_id ) const; + + int get_synaptic_elements_connected( Name n, index local_id ) const; + + void resize( index extended_space, index thread_id = 0 ); + + + std::map< Name, double > get_synaptic_elements( index local_id ) const; + + void update_synaptic_elements( double t, index local_id ); + + void decay_synaptic_elements_vacant( index local_id ); + + void connect_synaptic_element( Name name, int n, index local_id ); + + void get_status( DictionaryDatum& d, index local_id ) const; + + void set_status( const DictionaryDatum& d, index local_id ); + + double get_tau_Ca( index local_id ) const; + + double get_Ca_minus( index local_id ) const; + +protected: + void clear_history( index local_id ); + void set_spiketime( Time const& t_sp, index, double offset = 0.0 ); + + +private: + /** + * Time of the last update of the Calcium concentration in ms + */ + std::vector< double > Ca_t_; + + /** + * Value of the calcium concentration [Ca2+] at Ca_t_. + * + * Intracellular calcium concentration has a linear factor to mean + * electrical activity of 10^2, this means, for example, that a [Ca2+] of + * 0.2 is equivalent to a mean activity of 20 Hz. + */ + std::vector< double > Ca_minus_; + + /** + * Time constant for exponential decay of the intracellular calcium + * concentration + */ + std::vector< double > tau_Ca_; + + /** + * Increase in calcium concentration [Ca2+] for each spike of the neuron + */ + std::vector< double > beta_Ca_; + + /** + * Map of the synaptic elements + */ + std::vector< std::map< Name, SynapticElement > > synaptic_elements_map_; +}; +inline double +StructuralPlasticityVector::get_tau_Ca( index local_id ) const +{ + return tau_Ca_.at( local_id ); +} + +inline double +StructuralPlasticityVector::get_Ca_minus( index local_id ) const +{ + return Ca_minus_.at( local_id ); +} +} +#endif // STRUCTURAL_PLASTICITY_VECTOR_H diff --git a/nestkernel/universal_data_logger.h b/nestkernel/universal_data_logger.h index 6480fa8981..e6c50ca58f 100644 --- a/nestkernel/universal_data_logger.h +++ b/nestkernel/universal_data_logger.h @@ -117,6 +117,8 @@ class UniversalDataLogger */ UniversalDataLogger( HostNode& ); + UniversalDataLogger( UniversalDataLogger&& ) = default; + /** * Notify data logger that the node is recorded from. * @@ -375,6 +377,9 @@ class DynamicUniversalDataLogger */ DynamicUniversalDataLogger( HostNode& ); + DynamicUniversalDataLogger( DynamicUniversalDataLogger&& ) = default; + + /** * Notify data logger that the node is recorded from. * @@ -401,6 +406,7 @@ class DynamicUniversalDataLogger */ void handle( const DataLoggingRequest& ); + /** * Record data using predefined access functions. * This function should be called once per time step at the end of the diff --git a/nestkernel/universal_data_logger_impl.h b/nestkernel/universal_data_logger_impl.h index a26b78cf1e..2fd8698375 100644 --- a/nestkernel/universal_data_logger_impl.h +++ b/nestkernel/universal_data_logger_impl.h @@ -141,7 +141,7 @@ nest::DynamicUniversalDataLogger< HostNode >::DataLogger_::init() template < typename HostNode > void -nest::DynamicUniversalDataLogger< HostNode >::DataLogger_::record_data( const HostNode&, long step ) +nest::DynamicUniversalDataLogger< HostNode >::DataLogger_::record_data( const HostNode& hostnode, long step ) { if ( num_vars_ < 1 or step < next_rec_step_ ) { @@ -170,7 +170,8 @@ nest::DynamicUniversalDataLogger< HostNode >::DataLogger_::record_data( const Ho // obtain data through access functions, calling via pointer-to-member for ( size_t j = 0; j < num_vars_; ++j ) { - dest.data[ j ] = ( *( node_access_[ j ] ) )(); + const typename DynamicRecordablesMap< HostNode >::DataAccessFct* access_func = node_access_[ j ]; + dest.data[ j ] = ( *( access_func ) )( hostnode.get_node_local_id() ); } next_rec_step_ += rec_int_steps_; diff --git a/nestkernel/vectorized_node.cpp b/nestkernel/vectorized_node.cpp new file mode 100644 index 0000000000..263601e220 --- /dev/null +++ b/nestkernel/vectorized_node.cpp @@ -0,0 +1,379 @@ +/* + * vectorized_node.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 "vectorized_node.h" + +// Includes from libnestutil: +#include "compose.hpp" + +// Includes from nestkernel: +#include "exceptions.h" +#include "kernel_manager.h" + + +// Includes from sli: +#include "arraydatum.h" +#include "dictutils.h" +#include "namedatum.h" + +namespace nest +{ +VectorizedNode::VectorizedNode() + : node_uses_wfr_( 0 ) + , frozen_( 0 ) + , initialized_( 0 ) + , global_ids( 0 ) + , thread( -1 ) +{ +} + +void +VectorizedNode::reset() +{ + node_uses_wfr_.clear(); + frozen_.clear(); + initialized_.clear(); + global_ids.clear(); + thread = -1; +} +bool +VectorizedNode::wfr_update( const Time&, const long, const long, index ) +{ + throw UnexpectedEvent( "Waveform relaxation not supported." ); +} +void +VectorizedNode::set_frozen_( bool frozen, index local_id ) +{ + frozen_.at( local_id ) = frozen; +} +port +VectorizedNode::send_test_event( Node&, rport, synindex, bool, index ) +{ + throw IllegalConnection( + "Source node does not send output.\n" + " Note that recorders must be connected as Connect(neuron, recorder)." ); +} +void +VectorizedNode::register_stdp_connection( double, double, index ) +{ + throw IllegalConnection( "The target node does not support STDP synapses." ); +} +void +VectorizedNode::handle( SpikeEvent&, index ) +{ + throw UnexpectedEvent( "The target node does not handle spike input." ); +} +void +VectorizedNode::handle( WeightRecorderEvent&, index ) +{ + throw UnexpectedEvent( "The target node does not handle weight recorder events." ); +} +port +VectorizedNode::handles_test_event( WeightRecorderEvent&, rport, index ) +{ + throw IllegalConnection( "The target node or synapse model does not support weight recorder events." ); +} + +port +VectorizedNode::handles_test_event( SpikeEvent&, rport, index ) +{ + throw IllegalConnection( + "The target node or synapse model does not support spike input.\n" + " Note that volt/multimeters must be connected as Connect(meter, neuron)." ); +} +void +VectorizedNode::handle( RateEvent&, index ) +{ + throw UnexpectedEvent( "The target node does not handle rate input." ); +} +port +VectorizedNode::handles_test_event( RateEvent&, rport, index ) +{ + throw IllegalConnection( "The target node or synapse model does not support rate input." ); +} +void +VectorizedNode::handle( CurrentEvent&, index ) +{ + throw UnexpectedEvent( "The target node does not handle current input." ); +} +port +VectorizedNode::handles_test_event( CurrentEvent&, rport, index ) +{ + throw IllegalConnection( "The target node or synapse model does not support current input." ); +} +void +VectorizedNode::handle( DataLoggingRequest&, index ) +{ + throw UnexpectedEvent( "The target node does not handle data logging requests." ); +} +port +VectorizedNode::handles_test_event( DataLoggingRequest&, rport, index ) +{ + throw IllegalConnection( "The target node or synapse model does not support data logging requests." ); +} +void +VectorizedNode::handle( DataLoggingReply&, index ) +{ + throw UnexpectedEvent(); +} + +void +VectorizedNode::handle( ConductanceEvent&, index ) +{ + throw UnexpectedEvent( "The target node does not handle conductance input." ); +} + +port +VectorizedNode::handles_test_event( ConductanceEvent&, rport, index ) +{ + throw IllegalConnection( "The target node or synapse model does not support conductance input." ); +} + +void +VectorizedNode::handle( DoubleDataEvent&, index ) +{ + throw UnexpectedEvent(); +} + +port +VectorizedNode::handles_test_event( DoubleDataEvent&, rport, index ) +{ + throw IllegalConnection( "The target node or synapse model does not support double data event." ); +} + +port +VectorizedNode::handles_test_event( DSSpikeEvent&, rport, index ) +{ + throw IllegalConnection( "The target node or synapse model does not support spike input." ); +} + +port +VectorizedNode::handles_test_event( DSCurrentEvent&, rport, index ) +{ + throw IllegalConnection( "The target node or synapse model does not support DS current input." ); +} + +void +VectorizedNode::handle( GapJunctionEvent&, index ) +{ + throw UnexpectedEvent( "The target node does not handle gap junction input." ); +} + +port +VectorizedNode::handles_test_event( GapJunctionEvent&, rport, index ) +{ + throw IllegalConnection( "The target node or synapse model does not support gap junction input." ); + return invalid_port; +} + +void +VectorizedNode::sends_secondary_event( GapJunctionEvent&, index ) +{ + throw IllegalConnection( "The source node does not support gap junction output." ); +} + +void +VectorizedNode::handle( InstantaneousRateConnectionEvent&, index ) +{ + throw UnexpectedEvent( "The target node does not handle instantaneous rate input." ); +} + +port +VectorizedNode::handles_test_event( InstantaneousRateConnectionEvent&, rport, index ) +{ + throw IllegalConnection( "The target node or synapse model does not support instantaneous rate input." ); + return invalid_port; +} + +void +VectorizedNode::sends_secondary_event( InstantaneousRateConnectionEvent&, index ) +{ + throw IllegalConnection( "The source node does not support instantaneous rate output." ); +} + +void +VectorizedNode::sends_secondary_event( DiffusionConnectionEvent&, index ) +{ + throw IllegalConnection( "The source node does not support instantaneous rate output." ); +} + + +void +VectorizedNode::handle( DiffusionConnectionEvent&, index ) +{ + throw UnexpectedEvent( "The target node does not handle diffusion input." ); +} + +port +VectorizedNode::handles_test_event( DiffusionConnectionEvent&, rport, index ) +{ + throw IllegalConnection( "The target node or synapse model does not support diffusion input." ); +} + + +void +VectorizedNode::handle( DelayedRateConnectionEvent&, index ) +{ + throw UnexpectedEvent( "The target node does not handle delayed rate input." ); +} + +port +VectorizedNode::handles_test_event( DelayedRateConnectionEvent&, rport, index ) +{ + throw IllegalConnection( "The target node or synapse model does not support delayed rate input." ); +} + +void +VectorizedNode::sends_secondary_event( DelayedRateConnectionEvent&, index ) +{ + throw IllegalConnection( "The source node does not support delayed rate output." ); +} +double +VectorizedNode::get_K_value( double, index ) +{ + throw UnexpectedEvent(); +} +double +VectorizedNode::get_LTD_value( double, index ) +{ + throw UnexpectedEvent(); +} +void +VectorizedNode::get_K_values( double, double&, double&, double&, index ) +{ + throw UnexpectedEvent(); +} +void +VectorizedNode::get_history( double, + double, + std::deque< histentry >::iterator*, + std::deque< histentry >::iterator*, + index ) +{ + throw UnexpectedEvent(); +} +void +VectorizedNode::get_LTP_history( double, + double, + std::deque< histentry_extended >::iterator*, + std::deque< histentry_extended >::iterator*, + index ) +{ + throw UnexpectedEvent(); +} +void +VectorizedNode::get_urbanczik_history( double, + double, + std::deque< histentry_extended >::iterator*, + std::deque< histentry_extended >::iterator*, + int, + index ) +{ + throw UnexpectedEvent(); +} +double +VectorizedNode::get_C_m( int, index ) +{ + throw UnexpectedEvent(); +} +double +VectorizedNode::get_g_L( int, index ) +{ + throw UnexpectedEvent(); +} + +double +VectorizedNode::get_tau_Ca( index ) const +{ + throw UnexpectedEvent(); +} +double +VectorizedNode::get_tau_L( int, index ) +{ + throw UnexpectedEvent(); +} +double +VectorizedNode::get_tau_s( int, index ) +{ + throw UnexpectedEvent(); +} +double +VectorizedNode::get_tau_syn_ex( int, index ) +{ + throw UnexpectedEvent(); +} +double +VectorizedNode::get_tau_syn_in( int, index ) +{ + throw UnexpectedEvent(); +} +void +VectorizedNode::event_hook( DSSpikeEvent& e, index ) +{ + e.get_receiver().handle( e ); +} +void +VectorizedNode::event_hook( DSCurrentEvent& e, index ) +{ + e.get_receiver().handle( e ); +} +index +VectorizedNode::get_global_id( index local_id ) const +{ + return global_ids.at( local_id ); +} +void +VectorizedNode::insert_global_id( index id ) +{ + global_ids.push_back( id ); +} +index +VectorizedNode::size() const +{ + return global_ids.size(); +} + +void +VectorizedNode::set_initialized_( index ) +{ + // does nothing the base implementation +} + +void +VectorizedNode::resize( index, index ) +{ + // index current_size = global_ids.size(); + index total_space = global_ids.size(); + + node_uses_wfr_.resize( total_space, false ); + frozen_.resize( total_space, false ); + initialized_.resize( total_space, false ); +} + +Node* +VectorizedNode::get_wrapper( index node_id, index ) const +{ + nest::index global_id = get_global_id( node_id ); + Node* node = kernel().node_manager.get_node_or_proxy( global_id, thread ); + return node; +} +} diff --git a/nestkernel/vectorized_node.h b/nestkernel/vectorized_node.h new file mode 100644 index 0000000000..704ea7b222 --- /dev/null +++ b/nestkernel/vectorized_node.h @@ -0,0 +1,667 @@ +/* + * vectorized_node.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 VECTORIZED_NODE_H +#define VECTORIZED_NODE_H + + +// C++ includes: +#include "map" +#include "vector" +#include +#include + +// Includes from nestkernel: +#include "jit_node.h" +#include "nest_time.h" +#include "nest_types.h" +#include "node.h" +#include "synaptic_element.h" + + +// Includes from sli: +#include "dictdatum.h" +namespace nest +{ +class JitNode; +class TimeConverter; +class VectorizedNode +{ + friend class JitNode; + +public: + VectorizedNode(); + + // VectorizedNode( const VectorizedNode* ); + + virtual std::shared_ptr< VectorizedNode > + clone() const + { + return 0; + }; + + virtual ~VectorizedNode() + { + node_uses_wfr_.clear(); + frozen_.clear(); + initialized_.clear(); + global_ids.clear(); + } + + index + get_thread() + { + return thread; + } + + void + set_thread( index t ) + { + thread = t; + } + virtual std::map< std::string, const std::vector< double >& > + + get_recordables() const + { + return std::map< std::string, const std::vector< double >& >(); + } + + void reset(); + /** + * Returns true if node is frozen, i.e., shall not be updated. + */ + bool is_frozen( index ) const; + + /** + * Returns true if the node uses the waveform relaxation method + */ + bool node_uses_wfr( index ) const; + + index size() const; + /** + * Sets node_uses_wfr_ member variable + * (to be able to set it to "true" for any class derived from Node) + */ + void set_node_uses_wfr( const bool, index ); + + index get_global_id( index ) const; + + void insert_global_id( index ); + + void + set_wrapper( Node* wrapper ) + { + wrapper_ = wrapper; + } + + Node* get_wrapper( index = -1, nest::index = 0 ) const; + /** + * Initialize node prior to first simulation after node has been created. + * + * init() allows the node to configure internal data structures prior to + * being simulated. The method has an effect only the first time it is + * called on a given node, otherwise it returns immediately. init() calls + * virtual functions init_state_() and init_buffers_(). + */ + void init( index ); + + /** + * Re-calculate dependent parameters of the node. + * This function is called each time a simulation is begun/resumed. + * It must re-calculate all internal Variables of the node required + * for spike handling or updating the node. + * + */ + virtual void calibrate( index ) = 0; + + /** + * Re-calculate time-based properties of the node. + * This function is called after a change in resolution. + */ + virtual void + calibrate_time( const TimeConverter&, index ) + { + } + + /** + * Cleanup node after Run. Override this function if a node needs to + * "wrap up" things after a call to Run, i.e., before + * SimulationManager::run() returns. Typical use-cases are devices + * that need to flush buffers. + */ + virtual void + post_run_cleanup( index ) + { + } + /** + * Finalize node. + * Override this function if a node needs to "wrap up" things after a + * full simulation, i.e., a cycle of Prepare, Run, Cleanup. Typical + * use-cases are devices that need to close files. + */ + virtual void + finalize( index ) + { + } + + /** + * Bring the node from state $t$ to $t+n*dt$. + * + * n->update(T, from, to) performs the update steps beginning + * at T+from .. T+to-1, ie, emitting events with time stamps + * T+from+1 .. T+to. + * + * @param Time network time at beginning of time slice. + * @param long initial step inside time slice + * @param long post-final step inside time slice + * + */ + virtual void update( Time const&, const long, const long, index ) = 0; + + /** + * Bring the node from state $t$ to $t+n*dt$, sends SecondaryEvents + * (e.g. GapJunctionEvent) and resets state variables to values at $t$. + * + * n->wfr_update(T, from, to) performs the update steps beginning + * at T+from .. T+to-1. + * + * Does not emit spikes, does not log state variables. + * + * throws UnexpectedEvent if not reimplemented in derived class + * + * @param Time network time at beginning of time slice. + * @param long initial step inside time slice + * @param long post-final step inside time slice + * + */ + virtual bool wfr_update( Time const&, const long, const long, index ); + + +public: + /** + * @defgroup event_interface Communication. + * Functions and infrastructure, responsible for communication + * between Nodes. + * + * Nodes communicate by sending an receiving events. The + * communication interface consists of two parts: + * -# Functions to handle incoming events. + * -# Functions to check if a connection between nodes is possible. + * + * @see Event + */ + + /** + * Send an event to the receiving_node passed as an argument. + * This is required during the connection handshaking to test, + * if the receiving_node can handle the event type and receptor_type sent + * by the source node. + * + * If dummy_target is true, this indicates that receiving_node is derived from + * ConnTestDummyNodeBase and used in the first call to send_test_event(). + * This can be ignored in most cases, but Nodes sending DS*Events to their + * own event hooks and then *Events to their proper targets must send + * DS*Events when called with the dummy target, and *Events when called with + * the real target, see #478. + */ + virtual port send_test_event( Node& receiving_node, rport receptor_type, synindex syn_id, bool dummy_target, index ); + + /** + * Check if the node can handle a particular event and receptor type. + * This function is called upon connection setup by send_test_event(). + * + * handles_test_event() function is used to verify that the receiver + * can handle the event. It can also be used by the receiver to + * return information to the sender in form of the returned port. + * The default implementation throws an IllegalConnection + * exception. Any node class should define handles_test_event() + * functions for all those event types it can handle. + * + * See Kunkel et al, Front Neuroinform 8:78 (2014), Sec 3. + * + * @note The semantics of all other handles_test_event() functions is + * identical. + * @ingroup event_interface + * @throws IllegalConnection + */ + virtual port handles_test_event( SpikeEvent&, rport receptor_type, index ); + virtual port handles_test_event( WeightRecorderEvent&, rport receptor_type, index ); + virtual port handles_test_event( RateEvent&, rport receptor_type, index ); + virtual port handles_test_event( DataLoggingRequest&, rport receptor_type, index ); + virtual port handles_test_event( CurrentEvent&, rport receptor_type, index ); + virtual port handles_test_event( ConductanceEvent&, rport receptor_type, index ); + virtual port handles_test_event( DoubleDataEvent&, rport receptor_type, index ); + virtual port handles_test_event( DSSpikeEvent&, rport receptor_type, index ); + virtual port handles_test_event( DSCurrentEvent&, rport receptor_type, index ); + virtual port handles_test_event( GapJunctionEvent&, rport receptor_type, index ); + virtual port handles_test_event( InstantaneousRateConnectionEvent&, rport receptor_type, index ); + virtual port handles_test_event( DiffusionConnectionEvent&, rport receptor_type, index ); + virtual port handles_test_event( DelayedRateConnectionEvent&, rport receptor_type, index ); + + /** + * Required to check, if source neuron may send a SecondaryEvent. + * This base class implementation throws IllegalConnection + * and needs to be overwritten in the derived class. + * @ingroup event_interface + * @throws IllegalConnection + */ + virtual void sends_secondary_event( GapJunctionEvent&, index ); + + /** + * Required to check, if source neuron may send a SecondaryEvent. + * This base class implementation throws IllegalConnection + * and needs to be overwritten in the derived class. + * @ingroup event_interface + * @throws IllegalConnection + */ + virtual void sends_secondary_event( InstantaneousRateConnectionEvent&, index ); + + /** + * Required to check, if source neuron may send a SecondaryEvent. + * This base class implementation throws IllegalConnection + * and needs to be overwritten in the derived class. + * @ingroup event_interface + * @throws IllegalConnection + */ + virtual void sends_secondary_event( DiffusionConnectionEvent&, index ); + + /** + * Required to check, if source neuron may send a SecondaryEvent. + * This base class implementation throws IllegalConnection + * and needs to be overwritten in the derived class. + * @ingroup event_interface + * @throws IllegalConnection + */ + virtual void sends_secondary_event( DelayedRateConnectionEvent&, index ); + + /** + * Register a STDP connection + * + * @throws IllegalConnection + * + */ + virtual void register_stdp_connection( double, double, index ); + + /** + * Handle incoming spike events. + * @param thrd Id of the calling thread. + * @param e Event object. + * + * This handler has to be implemented if a Node should + * accept spike events. + * @see class SpikeEvent + * @ingroup event_interface + */ + virtual void handle( SpikeEvent&, index ); + + /** + * Handle incoming weight recording events. + * @param thrd Id of the calling thread. + * @param e Event object. + * + * This handler has to be implemented if a Node should + * accept weight recording events. + * @see class WeightRecordingEvent + * @ingroup event_interface + */ + virtual void handle( WeightRecorderEvent&, index ); + + /** + * Handler for rate events. + * @see handle(SpikeEvent&) + * @ingroup event_interface + * @throws UnexpectedEvent + */ + virtual void handle( RateEvent&, index ); + + /** + * Handler for universal data logging request. + * @see handle(SpikeEvent&) + * @ingroup event_interface + * @throws UnexpectedEvent + */ + virtual void handle( DataLoggingRequest&, index ); + + /** + * Handler for universal data logging request. + * @see handle(SpikeEvent&) + * @ingroup event_interface + * @throws UnexpectedEvent + * @note There is no connect_sender() for DataLoggingReply, since + * this event is only used as "back channel" for DataLoggingRequest. + */ + virtual void handle( DataLoggingReply&, index ); + + /** + * Handler for current events. + * @see handle(thread, SpikeEvent&) + * @ingroup event_interface + * @throws UnexpectedEvent + */ + virtual void handle( CurrentEvent&, index ); + + /** + * Handler for conductance events. + * @see handle(thread, SpikeEvent&) + * @ingroup event_interface + * @throws UnexpectedEvent + */ + virtual void handle( ConductanceEvent&, index ); + + /** + * Handler for DoubleData events. + * @see handle(thread, SpikeEvent&) + * @ingroup event_interface + * @throws UnexpectedEvent + */ + virtual void handle( DoubleDataEvent&, index ); + + /** + * Handler for gap junction events. + * @see handle(thread, GapJunctionEvent&) + * @ingroup event_interface + * @throws UnexpectedEvent + */ + virtual void handle( GapJunctionEvent&, index ); + + /** + * Handler for rate neuron events. + * @see handle(thread, InstantaneousRateConnectionEvent&) + * @ingroup event_interface + * @throws UnexpectedEvent + */ + virtual void handle( InstantaneousRateConnectionEvent&, index ); + + /** + * Handler for rate neuron events. + * @see handle(thread, InstantaneousRateConnectionEvent&) + * @ingroup event_interface + * @throws UnexpectedEvent + */ + virtual void handle( DiffusionConnectionEvent&, index ); + + /** + * Handler for delay rate neuron events. + * @see handle(thread, DelayedRateConnectionEvent&) + * @ingroup event_interface + * @throws UnexpectedEvent + */ + virtual void handle( DelayedRateConnectionEvent&, index ); + + /** + * @defgroup SP_functions Structural Plasticity in NEST. + * Functions related to accessibility and setup of variables required for + * the implementation of a model of Structural Plasticity in NEST. + * + */ + + + virtual double + get_Ca_minus( index ) const + { + return 0.0; + } + std::string + get_name() const + { + return "Ayssar_testing_concept"; + } + + + /** + * return the Kminus value at t (in ms). + * @throws UnexpectedEvent + */ + virtual double get_K_value( double t, index ); + + virtual double get_LTD_value( double t, index ); + + /** + * write the Kminus, nearest_neighbor_Kminus, and Kminus_triplet + * values at t (in ms) to the provided locations. + * @throws UnexpectedEvent + */ + virtual void get_K_values( double t, double& Kminus, double& nearest_neighbor_Kminus, double& Kminus_triplet, index ); + + virtual void get_history( double t1, + double t2, + std::deque< histentry >::iterator* start, + std::deque< histentry >::iterator* finish, + index ); + + virtual void get_LTP_history( double t1, + double t2, + std::deque< histentry_extended >::iterator* start, + std::deque< histentry_extended >::iterator* finish, + index ); + + virtual void get_urbanczik_history( double t1, + double t2, + std::deque< histentry_extended >::iterator* start, + std::deque< histentry_extended >::iterator* finish, + int, + index ); + + // make neuron parameters accessible in Urbanczik synapse + virtual double get_C_m( int comp, index ); + virtual double get_g_L( int comp, index ); + virtual double get_tau_L( int comp, index ); + virtual double get_tau_s( int comp, index ); + virtual double get_tau_syn_ex( int comp, index ); + virtual double get_tau_syn_in( int comp, index ); + + + /** + * Modify Event object parameters during event delivery. + * Some Nodes want to perform a function on an event for each + * of their targets. An example is the poisson_generator which + * needs to draw a random number for each target. The DSSpikeEvent, + * DirectSendingSpikeEvent, calls sender->event_hook(thread, *this) + * in its operator() function instead of calling target->handle(). + * The default implementation of Node::event_hook() just calls + * target->handle(DSSpikeEvent&). Any reimplementation must also + * execute this call. Otherwise the event will not be delivered. + * If needed, target->handle(DSSpikeEvent) may be called more than + * once. + */ + virtual void event_hook( DSSpikeEvent&, index ); + + virtual void event_hook( DSCurrentEvent&, index ); + + + /** + * @returns type of signal this node produces + * used in check_connection to only connect neurons which send / receive + * compatible information + */ + virtual SignalType + sends_signal( index ) const + { + return SPIKE; + } + + /** + * @returns type of signal this node consumes + * used in check_connection to only connect neurons which send / receive + * compatible information + */ + virtual SignalType + receives_signal( index ) const + { + return SPIKE; + } + + /** + * \fn double get_synaptic_elements(Name n) + * get the number of synaptic element for the current Node + * the number of synaptic elements is a double value but the number of + * actual vacant and connected elements is an integer truncated from this + * value + * @param local_id position of node in the vector + */ + virtual double + get_synaptic_elements( Name, index ) const + { + return 0.0; + }; + + /** + * \fn int get_synaptic_elements_vacant(Name n) + * Get the number of synaptic elements of type n which are available + * for new synapse creation + * @param local_id position of node in the vector + */ + virtual int + get_synaptic_elements_vacant( Name, index ) const + { + return 0; + } + + /** + * \fn int get_synaptic_elements_connected(Name n) + * get the number of synaptic element of type n which are currently + * connected + * @param local_id position of node in the vector + */ + virtual int + get_synaptic_elements_connected( Name, index ) const + { + return 0; + } + + /** + * \fn std::map get_synaptic_elements() + * get the number of all synaptic elements for the current Node + * @param local_id position of node in the vector + */ + virtual std::map< Name, double > + get_synaptic_elements( index ) const + { + return std::map< Name, double >(); + } + + /** + * \fn void update_synaptic_elements() + * Change the number of synaptic elements in the node depending on the + * dynamics described by the corresponding growth curve + * @param local_id position of node in the vector + */ + virtual void update_synaptic_elements( double, index ) {}; + + /** + * \fn void decay_synaptic_elements_vacant() + * Delete a certain portion of the vacant synaptic elements which are not + * in use + * @param local_id position of node in the vector + */ + virtual void decay_synaptic_elements_vacant( index ) {}; + + /** + * \fn void connect_synaptic_element() + * Change the number of connected synaptic elements by n + * @param local_id position of node in the vector + */ + virtual void connect_synaptic_element( Name, int, index ) {}; + + virtual void get_status( DictionaryDatum&, index ) const {}; + virtual void set_status( const DictionaryDatum&, index ) {}; + virtual void resize( index, index = 0 ); + + /** + * retrieve the current value of tau_Ca which defines the exponential decay + * constant of the intracellular calcium concentration + * @param local_id position of node in the vector + */ + virtual double get_tau_Ca( index ) const = 0; + +protected: + /** + * Configure state variables depending on runtime information. + * + * Overload this method if the node needs to adapt state variables prior to + * first simulation to runtime information, e.g., the number of incoming + * connections. + */ + virtual void init_state_( index ) {}; + + virtual void set_frozen_( bool, index ); + + /** + * Configure persistent internal data structures. + * + * Let node configure persistent internal data structures, such as input + * buffers or ODE solvers, to runtime information prior to first simulation. + */ + virtual void init_buffers_( index ) {}; + + virtual void set_initialized_( index ); + /** + * \fn void set_spiketime(Time const & t_sp, double offset) + * record spike history + */ + virtual void set_spiketime( Time const&, index = -1, double = 0.0 ) {}; + + /** + * \fn void clear_history() + * clear spike history + */ + virtual void clear_history( index ) = 0; + + +private: + std::vector< bool > node_uses_wfr_; + std::vector< bool > frozen_; + std::vector< bool > initialized_; + std::vector< index > global_ids; + index thread; + Node* wrapper_; +}; +inline bool +VectorizedNode::is_frozen( index local_id ) const +{ + return frozen_.at( local_id ); +} +inline bool +VectorizedNode::node_uses_wfr( index local_id ) const +{ + return node_uses_wfr_.at( local_id ); +} +inline void +VectorizedNode::set_node_uses_wfr( const bool value, index local_id ) +{ + node_uses_wfr_.at( local_id ) = value; +} +inline void +VectorizedNode::init( index local_id ) +{ + if ( initialized_.at( local_id ) ) + { + return; + } + init_state_( local_id ); + init_buffers_( local_id ); + + initialized_.at( local_id ) = true; +} +} +#endif // VECTORIZED_NODE_H diff --git a/pynest/nest/ll_api.py b/pynest/nest/ll_api.py index 32362b330b..ab555cdcf1 100644 --- a/pynest/nest/ll_api.py +++ b/pynest/nest/ll_api.py @@ -278,6 +278,7 @@ class KernelAttribute: """ Descriptor that dispatches attribute access to the nest kernel. """ + def __init__(self, typehint, description, readonly=False, default=None, localonly=False): self._readonly = readonly self._localonly = localonly diff --git a/testsuite/do_tests.sh b/testsuite/do_tests.sh index 27cc6df655..1aaf197558 100755 --- a/testsuite/do_tests.sh +++ b/testsuite/do_tests.sh @@ -177,6 +177,13 @@ echo NEST_VERSION="$(sli -c "statusdict/version :: =only")" echo " NEST executable .... $NEST (version $NEST_VERSION)" echo " PREFIX ............. $PREFIX" + + +if test -n "${MUSIC}"; then + MUSIC_VERSION="$("${MUSIC}" --version | head -n1 | cut -d' ' -f2)" + echo " MUSIC executable ... $MUSIC (version $MUSIC_VERSION)" +fi + if test -n "${PYTHON}"; then PYTHON_VERSION="$("${PYTHON}" --version | cut -d' ' -f2)" echo " Python executable .. $PYTHON (version $PYTHON_VERSION)" @@ -191,10 +198,7 @@ if test "${HAVE_MPI}" = "true"; then else echo " Running MPI tests .. no (compiled without MPI support)" fi -if test -n "${MUSIC}"; then - MUSIC_VERSION="$("${MUSIC}" --version | head -n1 | cut -d' ' -f2)" - echo " MUSIC executable ... $MUSIC (version $MUSIC_VERSION)" -fi + echo " TEST_BASEDIR ....... $TEST_BASEDIR" echo " REPORTDIR .......... $REPORTDIR" echo " PATH ............... `print_paths ${PATH}`"