From 546a01f5caed8443befa238db85d9a3fc0ea4fe6 Mon Sep 17 00:00:00 2001 From: ackurth Date: Fri, 22 Sep 2023 18:09:08 +0200 Subject: [PATCH 01/52] First changes for Poisson connections --- nestkernel/connection_creator.h | 14 ++- nestkernel/connection_creator_impl.h | 131 +++++++++++++++++++++++++++ nestkernel/nest_names.cpp | 1 + nestkernel/nest_names.h | 1 + nestkernel/random_generators.h | 8 ++ 5 files changed, 154 insertions(+), 1 deletion(-) diff --git a/nestkernel/connection_creator.h b/nestkernel/connection_creator.h index cf8120c2f1..85eeaff336 100644 --- a/nestkernel/connection_creator.h +++ b/nestkernel/connection_creator.h @@ -68,6 +68,7 @@ class ConnectionCreator { Pairwise_bernoulli_on_source, Pairwise_bernoulli_on_target, + Pairwise_poisson, Fixed_indegree, Fixed_outdegree }; @@ -143,6 +144,13 @@ class ConnectionCreator const Position< D >& tgt_pos, size_t tgt_thread, const Layer< D >& source ); + + void connect_to_target_poisson_( Iterator from, + Iterator to, + Node* tgt_ptr, + const Position< D >& tgt_pos, + size_t tgt_thread, + const Layer< D >& source ); template < int D > void pairwise_bernoulli_on_source_( Layer< D >& source, @@ -155,7 +163,11 @@ class ConnectionCreator NodeCollectionPTR source_nc, Layer< D >& target, NodeCollectionPTR target_nc ); - + + template < int D > + void + pairwise_poisson_( Layer< D >& source, NodeCollectionPTR source_nc, Layer< D >& target, NodeCollectionPTR target_nc ); + template < int D > void fixed_indegree_( Layer< D >& source, NodeCollectionPTR source_nc, Layer< D >& target, NodeCollectionPTR target_nc ); diff --git a/nestkernel/connection_creator_impl.h b/nestkernel/connection_creator_impl.h index 21c1f2e573..d3b04819b3 100644 --- a/nestkernel/connection_creator_impl.h +++ b/nestkernel/connection_creator_impl.h @@ -62,6 +62,11 @@ ConnectionCreator::connect( Layer< D >& source, pairwise_bernoulli_on_target_( source, source_nc, target, target_nc ); break; + + case Pairwise_poisson: + + pairwise_poisson_( source, source_nc, target, target_nc ); + break; default: throw BadProperty( "Unknown connection type." ); @@ -110,6 +115,56 @@ ConnectionCreator::connect_to_target_( Iterator from, } } +//template < typename Iterator, int D > +//void +//ConnectionCreator::connect_to_target_poisson_( Iterator from, +// Iterator to, +// Node* tgt_ptr, +// const Position< D >& tgt_pos, +// size_t tgt_thread, +// const Layer< D >& source ) +//{ +// RngPtr rng = get_vp_specific_rng( tgt_thread ); +// +// // We create a source pos vector here that can be updated with the +// // source position. This is done to avoid creating and destroying +// // unnecessarily many vectors. +// std::vector< double > source_pos( D ); +// const std::vector< double > target_pos = tgt_pos.get_vector(); +// +// // Declare number of connections variable +// unsigned long num_conns; +// +// const bool without_kernel = not kernel_.get(); +// for ( Iterator iter = from; iter != to; ++iter ) +// { +// if ( not allow_autapses_ and ( iter->second == tgt_ptr->get_node_id() ) ) +// { +// continue; +// } +// iter->first.get_vector( source_pos ); +// +// num_conns = rng->prand( ); +// //if ( without_kernel or rng->drand() < kernel_->value( rng, source_pos, target_pos, source, tgt_ptr ) ) +// if ( without_kernel) +// { +// for ( unsigned long conn_counter = 1: conn_counter <= num_conns; ++conn_counter) +// { +// for ( size_t indx = 0; indx < synapse_model_.size(); ++indx ) +// { +// kernel().connection_manager.connect( iter->second, +// tgt_ptr, +// tgt_thread, +// synapse_model_[ indx ], +// param_dicts_[ indx ][ tgt_thread ], +// delay_[ indx ]->value( rng, source_pos, target_pos, source, tgt_ptr ), +// weight_[ indx ]->value( rng, source_pos, target_pos, source, tgt_ptr ) ); +// } +// } +// } +// } +//} + template < int D > ConnectionCreator::PoolWrapper_< D >::PoolWrapper_() : masked_layer_( 0 ) @@ -337,6 +392,82 @@ ConnectionCreator::pairwise_bernoulli_on_target_( Layer< D >& source, } } + +template < int D > +void +ConnectionCreator::pairwise_poisson_( Layer< D >& source, + NodeCollectionPTR source_nc, + Layer< D >& target, + NodeCollectionPTR target_nc ) +{ + // Connect using pairwise Bernoulli drawing source nodes (target driven) + // For each local target node: + // 1. Apply Mask to source layer + // 2. For each source node: Compute probability, draw random number, make + // connection conditionally + + // retrieve global positions, either for masked or unmasked pool + PoolWrapper_< D > pool; + if ( mask_.get() ) // MaskedLayer will be freed by PoolWrapper d'tor + { + pool.define( new MaskedLayer< D >( source, mask_, allow_oversized_, source_nc ) ); + } + else + { + pool.define( source.get_global_positions_vector( source_nc ) ); + } + + std::vector< std::shared_ptr< WrappedThreadException > > exceptions_raised_( kernel().vp_manager.get_num_threads() ); + +// sharing specs on next line commented out because gcc 4.2 cannot handle them +#pragma omp parallel // default(none) shared(source, target, masked_layer, + // target_begin, target_end) + { + const int thread_id = kernel().vp_manager.get_thread_id(); + try + { + NodeCollection::const_iterator target_begin = target_nc->begin(); + NodeCollection::const_iterator target_end = target_nc->end(); + + for ( NodeCollection::const_iterator tgt_it = target_begin; tgt_it < target_end; ++tgt_it ) + { + Node* const tgt = kernel().node_manager.get_node_or_proxy( ( *tgt_it ).node_id, thread_id ); + + if ( not tgt->is_proxy() ) + { + const Position< D > target_pos = target.get_position( ( *tgt_it ).lid ); + + if ( mask_.get() ) + { + connect_to_target_poisson_( + pool.masked_begin( target_pos ), pool.masked_end(), tgt, target_pos, thread_id, source ); + } + else + { + connect_to_target_poisson_( pool.begin(), pool.end(), tgt, target_pos, thread_id, source ); + } + } + } // for target_begin + } + catch ( std::exception& err ) + { + // We must create a new exception here, err's lifetime ends at + // the end of the catch block. + exceptions_raised_.at( thread_id ) = + std::shared_ptr< WrappedThreadException >( new WrappedThreadException( err ) ); + } + } // omp parallel + // check if any exceptions have been raised + for ( size_t thr = 0; thr < kernel().vp_manager.get_num_threads(); ++thr ) + { + if ( exceptions_raised_.at( thr ).get() ) + { + throw WrappedThreadException( *( exceptions_raised_.at( thr ) ) ); + } + } +} + + template < int D > void ConnectionCreator::fixed_indegree_( Layer< D >& source, diff --git a/nestkernel/nest_names.cpp b/nestkernel/nest_names.cpp index d24e1f1968..35a29b0b60 100644 --- a/nestkernel/nest_names.cpp +++ b/nestkernel/nest_names.cpp @@ -351,6 +351,7 @@ const Name params( "params" ); const Name parent_idx( "parent_idx" ); const Name phase( "phase" ); const Name phi_max( "phi_max" ); +const Name pairwise_poisson( "pairwise_poisson" ); const Name polar_angle( "polar_angle" ); const Name polar_axis( "polar_axis" ); const Name port( "port" ); diff --git a/nestkernel/nest_names.h b/nestkernel/nest_names.h index c2469d8f62..fb0bfdbc57 100644 --- a/nestkernel/nest_names.h +++ b/nestkernel/nest_names.h @@ -377,6 +377,7 @@ extern const Name params; extern const Name parent_idx; extern const Name phase; extern const Name phi_max; +extern const Name pairwise_poisson; extern const Name polar_angle; extern const Name polar_axis; extern const Name port; diff --git a/nestkernel/random_generators.h b/nestkernel/random_generators.h index f2e98f32bf..11185af657 100644 --- a/nestkernel/random_generators.h +++ b/nestkernel/random_generators.h @@ -265,11 +265,19 @@ class RandomGenerator final : public BaseRandomGenerator std::uniform_int_distribution< unsigned long >::param_type param( 0, N - 1 ); return uniform_ulong_dist_( rng_, param ); } + + inline unsigned long + prand( double lam ) override + { + std::poisson_distribution< unsigned long >::param_type param( lam ); + return poisson_ulong_dist_( rng_, param ); + } private: RandomEngineT rng_; //!< Wrapped RNG engine. std::uniform_int_distribution< unsigned long > uniform_ulong_dist_; std::uniform_real_distribution<> uniform_double_dist_0_1_; + std::poisson_distribution< unsigned long > poisson_ulong_dist_; }; /** From 85d88b716ae483053fbaeac0e12af22ffe5eb55f Mon Sep 17 00:00:00 2001 From: ackurth Date: Sun, 24 Sep 2023 23:09:15 +0200 Subject: [PATCH 02/52] First working version for pairwise Poisson connections --- nestkernel/conn_builder.cpp | 124 +++++++++++++++++++ nestkernel/conn_builder.h | 16 +++ nestkernel/connection_creator.h | 1 + nestkernel/connection_creator_impl.h | 98 +++++++-------- nestkernel/nestmodule.cpp | 1 + nestkernel/random_generators.h | 7 ++ pynest/nest/lib/hl_api_connection_helpers.py | 8 +- pynest/nest/lib/hl_api_connections.py | 1 + 8 files changed, 205 insertions(+), 51 deletions(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index a50c4f996e..840ecc01ab 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -1585,6 +1585,130 @@ nest::BernoulliBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, } +nest::PoissonBuilder::PoissonBuilder( NodeCollectionPTR sources, + NodeCollectionPTR targets, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : ConnBuilder( sources, targets, conn_spec, syn_specs ) +{ + ParameterDatum* pd = dynamic_cast< ParameterDatum* >( ( *conn_spec )[ names::p ].datum() ); + if ( pd ) + { + p_ = *pd; + // TODO: Checks of parameter range + } + else + { + // Assume p is a scalar + const double value = ( *conn_spec )[ names::p]; + if ( value < 0 ) + { + throw BadProperty( "Connection parameter 0 <= lam required." ); + } + if ( not allow_multapses_ ) + { + throw BadProperty( "Multapses must be allowed for this connection rule." ); + } + p_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); + } +} + +void +nest::PoissonBuilder::connect_() +{ +#pragma omp parallel + { + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); + + try + { + RngPtr rng = get_vp_specific_rng( tid ); + + if ( loop_over_targets_() ) + { + NodeCollection::const_iterator target_it = targets_->begin(); + for ( ; target_it < targets_->end(); ++target_it ) + { + const size_t tnode_id = ( *target_it ).node_id; + Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); + if ( target->is_proxy() ) + { + // skip array parameters handled in other virtual processes + skip_conn_parameter_( tid ); + continue; + } + + inner_connect_( tid, rng, target, tnode_id ); + } + } + + else + { + const SparseNodeArray& local_nodes = kernel().node_manager.get_local_nodes( tid ); + SparseNodeArray::const_iterator n; + for ( n = local_nodes.begin(); n != local_nodes.end(); ++n ) + { + const size_t tnode_id = n->get_node_id(); + + // Is the local node in the targets list? + if ( targets_->get_lid( tnode_id ) < 0 ) + { + continue; + } + + inner_connect_( tid, rng, n->get_node(), tnode_id ); + } + } + } + catch ( std::exception& err ) + { + // We must create a new exception here, err's lifetime ends at + // the end of the catch block. + exceptions_raised_.at( tid ) = std::shared_ptr< WrappedThreadException >( new WrappedThreadException( err ) ); + } + } // of omp parallel +} + +void +nest::PoissonBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, size_t tnode_id ) +{ + const size_t target_thread = target->get_thread(); + + unsigned long num_conns; + + // check whether the target is on our thread + if ( static_cast< size_t >( tid ) != target_thread ) + { + return; + } + + // It is not possible to disable multapses with the PoissonBuilder, already checked + + NodeCollection::const_iterator source_it = sources_->begin(); + for ( ; source_it < sources_->end(); ++source_it ) + { + const size_t snode_id = ( *source_it ).node_id; + + if ( not allow_autapses_ and snode_id == tnode_id ) + { + continue; + } + + // Sample to number of connections that are to be established + num_conns = rng->prand( p_->value( rng, target ) ); + + if ( num_conns == 0) + { + continue; + } + for ( unsigned long conn_counter = 1; conn_counter <= num_conns; ++conn_counter) + { + single_connect_( snode_id, *target, target_thread, rng ); + } + } +} + nest::SymmetricBernoulliBuilder::SymmetricBernoulliBuilder( NodeCollectionPTR sources, NodeCollectionPTR targets, const DictionaryDatum& conn_spec, diff --git a/nestkernel/conn_builder.h b/nestkernel/conn_builder.h index 53e0f7f04b..d88f928da9 100644 --- a/nestkernel/conn_builder.h +++ b/nestkernel/conn_builder.h @@ -447,6 +447,22 @@ class BernoulliBuilder : public ConnBuilder ParameterDatum p_; //!< connection probability }; +class PoissonBuilder : public ConnBuilder +{ +public: + PoissonBuilder( NodeCollectionPTR, + NodeCollectionPTR, + const DictionaryDatum&, + const std::vector< DictionaryDatum >& ); + +protected: + void connect_() override; + +private: + void inner_connect_( const int, RngPtr, Node*, size_t ); + ParameterDatum p_; //!< connection probability +}; + class SymmetricBernoulliBuilder : public ConnBuilder { public: diff --git a/nestkernel/connection_creator.h b/nestkernel/connection_creator.h index 85eeaff336..7510eda3e5 100644 --- a/nestkernel/connection_creator.h +++ b/nestkernel/connection_creator.h @@ -145,6 +145,7 @@ class ConnectionCreator size_t tgt_thread, const Layer< D >& source ); + template < typename Iterator, int D > void connect_to_target_poisson_( Iterator from, Iterator to, Node* tgt_ptr, diff --git a/nestkernel/connection_creator_impl.h b/nestkernel/connection_creator_impl.h index d3b04819b3..a324ceda8b 100644 --- a/nestkernel/connection_creator_impl.h +++ b/nestkernel/connection_creator_impl.h @@ -115,55 +115,55 @@ ConnectionCreator::connect_to_target_( Iterator from, } } -//template < typename Iterator, int D > -//void -//ConnectionCreator::connect_to_target_poisson_( Iterator from, -// Iterator to, -// Node* tgt_ptr, -// const Position< D >& tgt_pos, -// size_t tgt_thread, -// const Layer< D >& source ) -//{ -// RngPtr rng = get_vp_specific_rng( tgt_thread ); -// -// // We create a source pos vector here that can be updated with the -// // source position. This is done to avoid creating and destroying -// // unnecessarily many vectors. -// std::vector< double > source_pos( D ); -// const std::vector< double > target_pos = tgt_pos.get_vector(); -// -// // Declare number of connections variable -// unsigned long num_conns; -// -// const bool without_kernel = not kernel_.get(); -// for ( Iterator iter = from; iter != to; ++iter ) -// { -// if ( not allow_autapses_ and ( iter->second == tgt_ptr->get_node_id() ) ) -// { -// continue; -// } -// iter->first.get_vector( source_pos ); -// -// num_conns = rng->prand( ); -// //if ( without_kernel or rng->drand() < kernel_->value( rng, source_pos, target_pos, source, tgt_ptr ) ) -// if ( without_kernel) -// { -// for ( unsigned long conn_counter = 1: conn_counter <= num_conns; ++conn_counter) -// { -// for ( size_t indx = 0; indx < synapse_model_.size(); ++indx ) -// { -// kernel().connection_manager.connect( iter->second, -// tgt_ptr, -// tgt_thread, -// synapse_model_[ indx ], -// param_dicts_[ indx ][ tgt_thread ], -// delay_[ indx ]->value( rng, source_pos, target_pos, source, tgt_ptr ), -// weight_[ indx ]->value( rng, source_pos, target_pos, source, tgt_ptr ) ); -// } -// } -// } -// } -//} +template < typename Iterator, int D > +void +ConnectionCreator::connect_to_target_poisson_( Iterator from, + Iterator to, + Node* tgt_ptr, + const Position< D >& tgt_pos, + size_t tgt_thread, + const Layer< D >& source ) +{ + RngPtr rng = get_vp_specific_rng( tgt_thread ); + + // We create a source pos vector here that can be updated with the + // source position. This is done to avoid creating and destroying + // unnecessarily many vectors. + std::vector< double > source_pos( D ); + const std::vector< double > target_pos = tgt_pos.get_vector(); + + // Declare number of connections variable + unsigned long num_conns; + + const bool without_kernel = not kernel_.get(); + for ( Iterator iter = from; iter != to; ++iter ) + { + if ( not allow_autapses_ and ( iter->second == tgt_ptr->get_node_id() ) ) + { + continue; + } + iter->first.get_vector( source_pos ); + + // Sample number of connections that are to be established + num_conns = rng->prand( kernel_->value( rng, source_pos, target_pos, source, tgt_ptr ) ); + if ( without_kernel) + { + for ( unsigned long conn_counter = 1; conn_counter <= num_conns; ++conn_counter) + { + for ( size_t indx = 0; indx < synapse_model_.size(); ++indx ) + { + kernel().connection_manager.connect( iter->second, + tgt_ptr, + tgt_thread, + synapse_model_[ indx ], + param_dicts_[ indx ][ tgt_thread ], + delay_[ indx ]->value( rng, source_pos, target_pos, source, tgt_ptr ), + weight_[ indx ]->value( rng, source_pos, target_pos, source, tgt_ptr ) ); + } + } + } + } +} template < int D > ConnectionCreator::PoolWrapper_< D >::PoolWrapper_() diff --git a/nestkernel/nestmodule.cpp b/nestkernel/nestmodule.cpp index 079f178430..8d8cd5ce97 100644 --- a/nestkernel/nestmodule.cpp +++ b/nestkernel/nestmodule.cpp @@ -2164,6 +2164,7 @@ NestModule::init( SLIInterpreter* i ) kernel().connection_manager.register_conn_builder< FixedInDegreeBuilder >( "fixed_indegree" ); kernel().connection_manager.register_conn_builder< FixedOutDegreeBuilder >( "fixed_outdegree" ); kernel().connection_manager.register_conn_builder< BernoulliBuilder >( "pairwise_bernoulli" ); + kernel().connection_manager.register_conn_builder< PoissonBuilder >( "pairwise_poisson" ); kernel().connection_manager.register_conn_builder< SymmetricBernoulliBuilder >( "symmetric_pairwise_bernoulli" ); kernel().connection_manager.register_conn_builder< FixedTotalNumberBuilder >( "fixed_total_number" ); #ifdef HAVE_LIBNEUROSIM diff --git a/nestkernel/random_generators.h b/nestkernel/random_generators.h index 11185af657..daf55498df 100644 --- a/nestkernel/random_generators.h +++ b/nestkernel/random_generators.h @@ -115,6 +115,13 @@ class BaseRandomGenerator * @param N Maximum value that can be drawn. */ virtual unsigned long ulrand( unsigned long N ) = 0; + + /** + * @brief Uses the wrapped RNG engine to draw an unsigned long from a Poisson distribution with mean lam. + * + * @param lam Mean value of Poisson distribution. + */ + virtual unsigned long prand( double lam ) = 0; }; /** diff --git a/pynest/nest/lib/hl_api_connection_helpers.py b/pynest/nest/lib/hl_api_connection_helpers.py index 2cceb09533..3098af4cf7 100644 --- a/pynest/nest/lib/hl_api_connection_helpers.py +++ b/pynest/nest/lib/hl_api_connection_helpers.py @@ -213,6 +213,10 @@ def _process_spatial_projections(conn_spec, syn_spec): projections["connection_type"] = "pairwise_bernoulli_on_target" if "use_on_source" in projections: projections.pop("use_on_source") + elif conn_spec["rule"] == "pairwise_poisson": + if "use_on_source" in conn_spec: + raise ValueError("'use_on_source' can only be set when using pairwise_bernoulli") + projections["connection_type"] = "pairwise_poisson" else: raise kernel.NESTError( "When using kernel or mask, the only possible connection rules are " @@ -230,8 +234,8 @@ def _connect_layers_needed(conn_spec, syn_spec): if isinstance(item, Parameter) and item.is_spatial(): return True # We must use ConnectLayers in some additional cases. - rule_is_bernoulli = "pairwise_bernoulli" in str(conn_spec["rule"]) - if "mask" in conn_spec or ("p" in conn_spec and not rule_is_bernoulli) or "use_on_source" in conn_spec: + rule_is_bernoulli_poisson = str(conn_spec["rule"]) in ["pairwise_bernoulli", "pairwise_poisson"] + if "mask" in conn_spec or ("p" in conn_spec and not rule_is_bernoulli_poisson) or "use_on_source" in conn_spec: return True # If a syn_spec entry is based on spatial properties, we must use ConnectLayers. if isinstance(syn_spec, dict): diff --git a/pynest/nest/lib/hl_api_connections.py b/pynest/nest/lib/hl_api_connections.py index 5c1673cf74..9853ded83a 100644 --- a/pynest/nest/lib/hl_api_connections.py +++ b/pynest/nest/lib/hl_api_connections.py @@ -165,6 +165,7 @@ def Connect(pre, post, conn_spec=None, syn_spec=None, return_synapsecollection=F - 'fixed_outdegree', 'outdegree' - 'fixed_total_number', 'N' - 'pairwise_bernoulli', 'p' + - 'pairwise_poisson', 'p' - 'symmetric_pairwise_bernoulli', 'p' See :ref:`conn_rules` for more details, including example usage. From b24fd9408430a4cea5ac298ae154f343f704e826 Mon Sep 17 00:00:00 2001 From: ackurth Date: Mon, 25 Sep 2023 11:41:45 +0200 Subject: [PATCH 03/52] pairwise poisson works --- nestkernel/conn_builder.cpp | 12 ++++++------ nestkernel/conn_builder.h | 2 +- nestkernel/connection_creator.cpp | 12 ++++++++++++ nestkernel/connection_creator_impl.h | 2 +- nestkernel/nest_names.cpp | 1 + nestkernel/nest_names.h | 1 + pynest/nest/lib/hl_api_connection_helpers.py | 10 ++++++++-- pynest/nest/lib/hl_api_connections.py | 2 +- 8 files changed, 31 insertions(+), 11 deletions(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index 840ecc01ab..16fdcbf142 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -1591,16 +1591,16 @@ nest::PoissonBuilder::PoissonBuilder( NodeCollectionPTR sources, const std::vector< DictionaryDatum >& syn_specs ) : ConnBuilder( sources, targets, conn_spec, syn_specs ) { - ParameterDatum* pd = dynamic_cast< ParameterDatum* >( ( *conn_spec )[ names::p ].datum() ); + ParameterDatum* pd = dynamic_cast< ParameterDatum* >( ( *conn_spec )[ names::lam ].datum() ); if ( pd ) { - p_ = *pd; + lam_ = *pd; // TODO: Checks of parameter range } else { - // Assume p is a scalar - const double value = ( *conn_spec )[ names::p]; + // Assume lam is a scalar + const double value = ( *conn_spec )[ names::lam]; if ( value < 0 ) { throw BadProperty( "Connection parameter 0 <= lam required." ); @@ -1609,7 +1609,7 @@ nest::PoissonBuilder::PoissonBuilder( NodeCollectionPTR sources, { throw BadProperty( "Multapses must be allowed for this connection rule." ); } - p_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); + lam_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); } } @@ -1696,7 +1696,7 @@ nest::PoissonBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, s } // Sample to number of connections that are to be established - num_conns = rng->prand( p_->value( rng, target ) ); + num_conns = rng->prand( lam_->value( rng, target ) ); if ( num_conns == 0) { diff --git a/nestkernel/conn_builder.h b/nestkernel/conn_builder.h index d88f928da9..ad0f4c899e 100644 --- a/nestkernel/conn_builder.h +++ b/nestkernel/conn_builder.h @@ -460,7 +460,7 @@ class PoissonBuilder : public ConnBuilder private: void inner_connect_( const int, RngPtr, Node*, size_t ); - ParameterDatum p_; //!< connection probability + ParameterDatum lam_; //!< Mean number of connections }; class SymmetricBernoulliBuilder : public ConnBuilder diff --git a/nestkernel/connection_creator.cpp b/nestkernel/connection_creator.cpp index 51e52b0e0c..024e959163 100644 --- a/nestkernel/connection_creator.cpp +++ b/nestkernel/connection_creator.cpp @@ -135,6 +135,18 @@ ConnectionCreator::ConnectionCreator( DictionaryDatum dict ) type_ = Pairwise_bernoulli_on_source; } } + else if ( connection_type == names::pairwise_poisson ) + { + + if ( dict->known( names::number_of_connections ) ) + { + type_ = Fixed_indegree; + } + else + { + type_ = Pairwise_poisson; + } + } else if ( connection_type == names::pairwise_bernoulli_on_target ) { diff --git a/nestkernel/connection_creator_impl.h b/nestkernel/connection_creator_impl.h index a324ceda8b..03855f5261 100644 --- a/nestkernel/connection_creator_impl.h +++ b/nestkernel/connection_creator_impl.h @@ -146,7 +146,7 @@ ConnectionCreator::connect_to_target_poisson_( Iterator from, // Sample number of connections that are to be established num_conns = rng->prand( kernel_->value( rng, source_pos, target_pos, source, tgt_ptr ) ); - if ( without_kernel) + if ( without_kernel or num_conns) { for ( unsigned long conn_counter = 1; conn_counter <= num_conns; ++conn_counter) { diff --git a/nestkernel/nest_names.cpp b/nestkernel/nest_names.cpp index 35a29b0b60..78098deecf 100644 --- a/nestkernel/nest_names.cpp +++ b/nestkernel/nest_names.cpp @@ -268,6 +268,7 @@ const Name keep_source_table( "keep_source_table" ); const Name kernel( "kernel" ); const Name label( "label" ); +const Name lam( "lam" ); const Name lambda( "lambda" ); const Name lambda_0( "lambda_0" ); const Name len_kernel( "len_kernel" ); diff --git a/nestkernel/nest_names.h b/nestkernel/nest_names.h index fb0bfdbc57..4e4f3db2c2 100644 --- a/nestkernel/nest_names.h +++ b/nestkernel/nest_names.h @@ -294,6 +294,7 @@ extern const Name keep_source_table; extern const Name kernel; extern const Name label; +extern const Name lam; extern const Name lambda; extern const Name lambda_0; extern const Name len_kernel; diff --git a/pynest/nest/lib/hl_api_connection_helpers.py b/pynest/nest/lib/hl_api_connection_helpers.py index 3098af4cf7..82355d8711 100644 --- a/pynest/nest/lib/hl_api_connection_helpers.py +++ b/pynest/nest/lib/hl_api_connection_helpers.py @@ -169,6 +169,7 @@ def _process_spatial_projections(conn_spec, syn_spec): "indegree", "outdegree", "p", + "lam", "use_on_source", "allow_oversized_mask", ] @@ -181,6 +182,8 @@ def _process_spatial_projections(conn_spec, syn_spec): projections.update(conn_spec) if "p" in conn_spec: projections["kernel"] = projections.pop("p") + elif "lam" in conn_spec: + projections["kernel"] = projections.pop("lam") if syn_spec is not None: if isinstance(syn_spec, CollocatedSynapses): for syn_list in syn_spec.syn_specs: @@ -234,8 +237,11 @@ def _connect_layers_needed(conn_spec, syn_spec): if isinstance(item, Parameter) and item.is_spatial(): return True # We must use ConnectLayers in some additional cases. - rule_is_bernoulli_poisson = str(conn_spec["rule"]) in ["pairwise_bernoulli", "pairwise_poisson"] - if "mask" in conn_spec or ("p" in conn_spec and not rule_is_bernoulli_poisson) or "use_on_source" in conn_spec: + rule_is_bernoulli= "pairwise_bernoulli" in str(conn_spec["rule"]) + if "mask" in conn_spec or ("p" in conn_spec and not rule_is_bernoulli) or "use_on_source" in conn_spec: + return True + rule_is_poisson= "pairwise_poisson" in str(conn_spec["rule"]) + if "mask" in conn_spec or ("lam" in conn_spec and not rule_is_poisson) or "use_on_source" in conn_spec: return True # If a syn_spec entry is based on spatial properties, we must use ConnectLayers. if isinstance(syn_spec, dict): diff --git a/pynest/nest/lib/hl_api_connections.py b/pynest/nest/lib/hl_api_connections.py index 9853ded83a..b8ff81f47b 100644 --- a/pynest/nest/lib/hl_api_connections.py +++ b/pynest/nest/lib/hl_api_connections.py @@ -165,8 +165,8 @@ def Connect(pre, post, conn_spec=None, syn_spec=None, return_synapsecollection=F - 'fixed_outdegree', 'outdegree' - 'fixed_total_number', 'N' - 'pairwise_bernoulli', 'p' - - 'pairwise_poisson', 'p' - 'symmetric_pairwise_bernoulli', 'p' + - 'pairwise_poisson', 'lam' See :ref:`conn_rules` for more details, including example usage. From 892a63532de3a63724391d3beb220ca52a953137 Mon Sep 17 00:00:00 2001 From: ackurth Date: Mon, 25 Sep 2023 15:04:30 +0200 Subject: [PATCH 04/52] Add tests --- testsuite/pytests/connect_test_base.py | 9 ++ .../pytests/test_connect_pairwise_poisson.py | 98 +++++++++++++++++++ .../test_spatial/test_connect_layers.py | 83 +++++++++++++++- 3 files changed, 185 insertions(+), 5 deletions(-) create mode 100644 testsuite/pytests/test_connect_pairwise_poisson.py diff --git a/testsuite/pytests/connect_test_base.py b/testsuite/pytests/connect_test_base.py index 7c25a7b1c6..3b14e27b51 100644 --- a/testsuite/pytests/connect_test_base.py +++ b/testsuite/pytests/connect_test_base.py @@ -553,6 +553,15 @@ def get_expected_degrees_bernoulli(p, fan, len_source_pop, len_target_pop): # adapted from Masterthesis, Daniel Hjertholm +def get_expected_degrees_poisson(lam, fan, len_source_pop, len_target_pop): + expected_indegree = lam * len_source_pop + expected_outdegree = lam * len_target_pop + if fan == "in": + return expected_indegree + elif fan == "out": + return expected_outdegree + + def reset_seed(seed, nr_threads): """ Reset the simulator and seed the PRNGs. diff --git a/testsuite/pytests/test_connect_pairwise_poisson.py b/testsuite/pytests/test_connect_pairwise_poisson.py new file mode 100644 index 0000000000..8c63fab989 --- /dev/null +++ b/testsuite/pytests/test_connect_pairwise_poisson.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +# +# test_connect_pairwise_bernoulli.py +# +# This file is part of NEST. +# +# Copyright (C) 2004 The NEST Initiative +# +# NEST is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# NEST is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with NEST. If not, see . + + +import unittest + +import connect_test_base +import nest +import numpy as np +import scipy.stats + +HAVE_OPENMP = nest.ll_api.sli_func("is_threaded") + +@unittest.skipIf(not HAVE_OPENMP, "NEST was compiled without multi-threading") +@nest.ll_api.check_stack +class TestPairwisePoisson(connect_test_base.ConnectTestBase): + # specify connection pattern and specific params + rule = "pairwise_poisson" + lam = 0.5 + conn_dict = {"rule": rule, "lam": lam} + # sizes of source-, target-population and connection probability for + # statistical test + N_s = 50 + N_t = 50 + # Critical values and number of iterations of two level test + stat_dict = {"alpha2": 0.8, "n_runs": 20} + + def testErrorMessages(self): + got_error = False + conn_params = self.conn_dict.copy() + conn_params["allow_multapses"] = False + try: + self.setUpNetwork(conn_params) + except nest.kernel.NESTError: + got_error = True + self.assertTrue(got_error) + + def testExpInOutdegree(self): + connect_test_base.reset_seed(1, self.nr_threads) + self.setUpNetwork(conn_dict=self.conn_dict, N1=self.N_s, N2=self.N_t) + for fan in ["in", "out"]: + expected = connect_test_base.get_expected_degrees_poisson(self.lam, fan, self.N_s, self.N_t) + + mean_degrees = connect_test_base.get_degrees(fan, self.pop1, self.pop2).mean() + + np.testing.assert_allclose(expected, mean_degrees, atol=2) + + def testStatistics(self): + multapses = [] + + for i in range(self.stat_dict["n_runs"]): + connect_test_base.reset_seed(i + 1, self.nr_threads) + self.setUpNetwork(conn_dict=self.conn_dict, N1=1, N2=1) + multapses.append(connect_test_base.get_degrees("out", self.pop1, self.pop2)) + + # Create x values for binning multapses + x = np.arange(0, 10, 1) - 0.5 + multapse_distribution, auto_bins = np.histogram(multapses, bins=x, density=True) + bins = (auto_bins[:-1] + auto_bins[1:]) / 2 + + expected_distribution = scipy.stats.poisson.pmf(bins, self.lam) + _, p = connect_test_base.chi_squared_check(multapse_distribution, + expected_distribution, + self.rule) + + self.assertTrue(p > self.stat_dict["alpha2"]) + + +def suite(): + suite = unittest.TestLoader().loadTestsFromTestCase(TestPairwisePoisson) + return suite + + +def run(): + runner = unittest.TextTestRunner(verbosity=2) + runner.run(suite()) + + +if __name__ == '__main__': + unittest.main() diff --git a/testsuite/pytests/test_spatial/test_connect_layers.py b/testsuite/pytests/test_spatial/test_connect_layers.py index d76da7dcdb..8e39ff30a3 100644 --- a/testsuite/pytests/test_spatial/test_connect_layers.py +++ b/testsuite/pytests/test_spatial/test_connect_layers.py @@ -45,14 +45,17 @@ def setUp(self): nest.rng_seed = 123 self.layer = nest.Create("iaf_psc_alpha", positions=nest.spatial.grid(self.dim, extent=self.extent)) - def _check_connections(self, conn_spec, expected_num_connections): + def _check_connections(self, conn_spec, expected_num_connections, return_conns=False): """Helper function which asserts that connecting with the specified conn_spec gives the expected number of connections.""" nest.Connect(self.layer, self.layer, conn_spec) conns = nest.GetConnections() - self.assertEqual(len(conns), expected_num_connections) + if return_conns: + return conns + else: + self.assertEqual(len(conns), expected_num_connections) - def _check_connections_statistical(self, conn_spec, p, num_pairs): + def _check_connections_statistical_bernoulli(self, conn_spec, p, num_pairs): """Helper function which asserts that the number of connections created are based on a bernoulli distribution. The connection function is iterated N times, then the distribution of number of created connections are tested against a bernoulli distribution using a Kolmogorov-Smirnov test. This is done ks_N times, to get statistical @@ -84,6 +87,38 @@ def _check_connections_statistical(self, conn_spec, p, num_pairs): self.assertGreater(mean_p_val, p_val_lim) self.assertLess(mean_ks_stat, ks_stat_lim) + def _check_connections_statistical_poisson(self, conn_spec, lam, num_pairs): + """Helper function which asserts that the number of connections created are based on a poisson distribution. + The connection function is iterated N times, then the distribution of number of created connections are tested + against a bernoulli distribution using a Kolmogorov-Smirnov test. This is done ks_N times, to get statistical + values. The mean of the KS tests is then compared to the limits. If either of the values are below the specified + limits, the test fails.""" + self.assertEqual(conn_spec["rule"], "pairwise_poisson") + N = 100 # Number of samples per KS test + ks_N = 5 # Number of KS tests to perform. + p_val_lim = 0.1 # Limit for the p value of the KS test + ks_stat_lim = 0.2 # Limit for the KS statistic + + p_vals = np.zeros(ks_N) + ks_stats = np.zeros(ks_N) + + for ks_i in range(ks_N): + n_conns = np.zeros(N) + ref = np.zeros(N) + for i in range(N): + nest.Connect(self.layer, self.layer, conn_spec) + n_conns[i] = nest.num_connections - np.sum(n_conns) + ref[i] = np.sum(scipy.stats.poisson.rvs(lam, size=num_pairs)) + ks_stats[ks_i], p_vals[ks_i] = scipy.stats.ks_2samp(n_conns, ref) + print(f"ks_stat={ks_stats[ks_i]}, p_val={p_vals[ks_i]}") + + mean_p_val = np.mean(p_vals) + mean_ks_stat = np.mean(ks_stats) + print(f"mean_ks_stat={mean_ks_stat}, mean_p_val={mean_p_val}") + + self.assertGreater(mean_p_val, p_val_lim) + self.assertLess(mean_ks_stat, ks_stat_lim) + def _assert_connect_layers_autapses(self, autapses, expected_num_autapses): """Helper function which asserts that connecting with or without allowing autapses gives the expected number of autapses.""" @@ -164,6 +199,12 @@ def test_connect_layers_bernoulli_source(self): conn_spec = {"rule": "pairwise_bernoulli", "p": 1.0, "use_on_source": True} self._check_connections(conn_spec, 400) + def test_connect_layers_poisson(self): + """Connecting layers with pairwise_poisson.""" + conn_spec = {"rule": "pairwise_poisson", "lam": 0.5} + conns = self._check_connections(conn_spec, None, return_conns=True) + np.testing.assert_allclose(200, len(conns), atol=5) + def test_connect_layers_indegree_mask(self): """Connecting layers with fixed_indegree and mask.""" conn_spec = { @@ -237,6 +278,16 @@ def test_connect_layers_bernoulli_mask(self): } self._check_connections(conn_spec, 108) + def test_connect_layers_poisson_mask(self): + """Connecting layers with pairwise_poisson and mask""" + conn_spec = { + "rule": "pairwise_poisson", + "lam": 0.5, + "mask": {"rectangular": {"lower_left": [-5.0, -5.0], "upper_right": [0.1, 0.1]}}, + } + conns = self._check_connections(conn_spec, None, return_conns=True) + np.testing.assert_allclose(54, len(conns), atol=5) + @unittest.skipIf(not HAVE_SCIPY, "SciPy package is not available") def test_connect_layers_bernoulli_kernel_mask(self): """Connecting layers with pairwise_bernoulli, kernel and mask""" @@ -246,7 +297,18 @@ def test_connect_layers_bernoulli_kernel_mask(self): "p": p, "mask": {"rectangular": {"lower_left": [-5.0, -5.0], "upper_right": [0.1, 0.1]}}, } - self._check_connections_statistical(conn_spec, p, 108) + self._check_connections_statistical_bernoulli(conn_spec, p, 108) + + @unittest.skipIf(not HAVE_SCIPY, "SciPy package is not available") + def test_connect_layers_poisson_kernel_mask(self): + """Connecting layers with pairwise_poisson, kernel and mask""" + lam = 0.5 + conn_spec = { + "rule": "pairwise_poisson", + "lam": lam, + "mask": {"rectangular": {"lower_left": [-5.0, -5.0], "upper_right": [0.1, 0.1]}}, + } + self._check_connections_statistical_poisson(conn_spec, lam, 108) @unittest.skipIf(not HAVE_SCIPY, "SciPy package is not available") def test_connect_layers_bernoulli_kernel_mask_source(self): @@ -260,7 +322,7 @@ def test_connect_layers_bernoulli_kernel_mask_source(self): "mask": {"rectangular": {"lower_left": [-5.0, -5.0], "upper_right": [0.1, 0.1]}}, "use_on_source": True, } - self._check_connections_statistical(conn_spec, p, 108) + self._check_connections_statistical_bernoulli(conn_spec, p, 108) def test_connect_nonlayers_mask(self): """Throw when connecting non-layer NodeCollections with mask.""" @@ -273,6 +335,17 @@ def test_connect_nonlayers_mask(self): with self.assertRaises(TypeError): nest.Connect(neurons, neurons, conn_spec) + def test_connect_nonlayers_mask(self): + """Throw when connecting non-layer NodeCollections with mask.""" + neurons = nest.Create("iaf_psc_alpha", 20) + conn_spec = { + "rule": "pairwise_poisson", + "p": 1.0, + "mask": {"rectangular": {"lower_left": [-5.0, -5.0], "upper_right": [0.1, 0.1]}}, + } + with self.assertRaises(TypeError): + nest.Connect(neurons, neurons, conn_spec) + def test_connect_nonlayers_kernel(self): """Throw when connecting non-layer NodeCollections with kernel.""" neurons = nest.Create("iaf_psc_alpha", 20) From 9f5071c16e954da44580479968ea5d5f326da11f Mon Sep 17 00:00:00 2001 From: ackurth Date: Mon, 25 Sep 2023 15:31:41 +0200 Subject: [PATCH 05/52] Correct pytest --- testsuite/pytests/test_connect_pairwise_poisson.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/testsuite/pytests/test_connect_pairwise_poisson.py b/testsuite/pytests/test_connect_pairwise_poisson.py index 8c63fab989..02d34538ee 100644 --- a/testsuite/pytests/test_connect_pairwise_poisson.py +++ b/testsuite/pytests/test_connect_pairwise_poisson.py @@ -23,6 +23,7 @@ import unittest import connect_test_base +from connect_test_base import get_connectivity_matrix import nest import numpy as np import scipy.stats @@ -53,6 +54,18 @@ def testErrorMessages(self): got_error = True self.assertTrue(got_error) + def testAutapsesTrue(self): + conn_params = self.conn_dict.copy() + + # test that autapses exist + conn_params["allow_autapses"] = True + pop1 = nest.Create("iaf_psc_alpha", self.N1) + nest.Connect(pop1, pop1, conn_params) + # make sure all connections do exist + M = connect_test_base.get_connectivity_matrix(pop1, pop1) + np.testing.assert_allclose(np.diag(M).sum(), self.N1 * self.lam, atol=2) + + def testExpInOutdegree(self): connect_test_base.reset_seed(1, self.nr_threads) self.setUpNetwork(conn_dict=self.conn_dict, N1=self.N_s, N2=self.N_t) From 41b24ae40dec0ac6f301f1aa1a92950f48181bf5 Mon Sep 17 00:00:00 2001 From: ackurth Date: Wed, 27 Sep 2023 09:19:23 +0200 Subject: [PATCH 06/52] Black python files --- pynest/nest/lib/hl_api_connection_helpers.py | 4 ++-- testsuite/pytests/connect_test_base.py | 2 +- .../pytests/test_connect_pairwise_poisson.py | 16 +++++++--------- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/pynest/nest/lib/hl_api_connection_helpers.py b/pynest/nest/lib/hl_api_connection_helpers.py index 82355d8711..c768497d14 100644 --- a/pynest/nest/lib/hl_api_connection_helpers.py +++ b/pynest/nest/lib/hl_api_connection_helpers.py @@ -237,10 +237,10 @@ def _connect_layers_needed(conn_spec, syn_spec): if isinstance(item, Parameter) and item.is_spatial(): return True # We must use ConnectLayers in some additional cases. - rule_is_bernoulli= "pairwise_bernoulli" in str(conn_spec["rule"]) + rule_is_bernoulli = "pairwise_bernoulli" in str(conn_spec["rule"]) if "mask" in conn_spec or ("p" in conn_spec and not rule_is_bernoulli) or "use_on_source" in conn_spec: return True - rule_is_poisson= "pairwise_poisson" in str(conn_spec["rule"]) + rule_is_poisson = "pairwise_poisson" in str(conn_spec["rule"]) if "mask" in conn_spec or ("lam" in conn_spec and not rule_is_poisson) or "use_on_source" in conn_spec: return True # If a syn_spec entry is based on spatial properties, we must use ConnectLayers. diff --git a/testsuite/pytests/connect_test_base.py b/testsuite/pytests/connect_test_base.py index 3b14e27b51..0a05186c27 100644 --- a/testsuite/pytests/connect_test_base.py +++ b/testsuite/pytests/connect_test_base.py @@ -554,7 +554,7 @@ def get_expected_degrees_bernoulli(p, fan, len_source_pop, len_target_pop): def get_expected_degrees_poisson(lam, fan, len_source_pop, len_target_pop): - expected_indegree = lam * len_source_pop + expected_indegree = lam * len_source_pop expected_outdegree = lam * len_target_pop if fan == "in": return expected_indegree diff --git a/testsuite/pytests/test_connect_pairwise_poisson.py b/testsuite/pytests/test_connect_pairwise_poisson.py index 02d34538ee..b1839098c9 100644 --- a/testsuite/pytests/test_connect_pairwise_poisson.py +++ b/testsuite/pytests/test_connect_pairwise_poisson.py @@ -30,6 +30,7 @@ HAVE_OPENMP = nest.ll_api.sli_func("is_threaded") + @unittest.skipIf(not HAVE_OPENMP, "NEST was compiled without multi-threading") @nest.ll_api.check_stack class TestPairwisePoisson(connect_test_base.ConnectTestBase): @@ -43,7 +44,7 @@ class TestPairwisePoisson(connect_test_base.ConnectTestBase): N_t = 50 # Critical values and number of iterations of two level test stat_dict = {"alpha2": 0.8, "n_runs": 20} - + def testErrorMessages(self): got_error = False conn_params = self.conn_dict.copy() @@ -53,7 +54,7 @@ def testErrorMessages(self): except nest.kernel.NESTError: got_error = True self.assertTrue(got_error) - + def testAutapsesTrue(self): conn_params = self.conn_dict.copy() @@ -65,7 +66,6 @@ def testAutapsesTrue(self): M = connect_test_base.get_connectivity_matrix(pop1, pop1) np.testing.assert_allclose(np.diag(M).sum(), self.N1 * self.lam, atol=2) - def testExpInOutdegree(self): connect_test_base.reset_seed(1, self.nr_threads) self.setUpNetwork(conn_dict=self.conn_dict, N1=self.N_s, N2=self.N_t) @@ -88,15 +88,13 @@ def testStatistics(self): x = np.arange(0, 10, 1) - 0.5 multapse_distribution, auto_bins = np.histogram(multapses, bins=x, density=True) bins = (auto_bins[:-1] + auto_bins[1:]) / 2 - + expected_distribution = scipy.stats.poisson.pmf(bins, self.lam) - _, p = connect_test_base.chi_squared_check(multapse_distribution, - expected_distribution, - self.rule) + _, p = connect_test_base.chi_squared_check(multapse_distribution, expected_distribution, self.rule) self.assertTrue(p > self.stat_dict["alpha2"]) - + def suite(): suite = unittest.TestLoader().loadTestsFromTestCase(TestPairwisePoisson) return suite @@ -107,5 +105,5 @@ def run(): runner.run(suite()) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() From ed1b18b60d04d58b6be904d9131b57090b9913a8 Mon Sep 17 00:00:00 2001 From: ackurth Date: Wed, 27 Sep 2023 09:23:21 +0200 Subject: [PATCH 07/52] Format cpp files --- nestkernel/conn_builder.cpp | 6 +++--- nestkernel/conn_builder.h | 5 +---- nestkernel/connection_creator.h | 6 +++--- nestkernel/connection_creator_impl.h | 26 +++++++++++++------------- nestkernel/random_generators.h | 2 +- 5 files changed, 21 insertions(+), 24 deletions(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index 16fdcbf142..43639f06cc 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -1600,7 +1600,7 @@ nest::PoissonBuilder::PoissonBuilder( NodeCollectionPTR sources, else { // Assume lam is a scalar - const double value = ( *conn_spec )[ names::lam]; + const double value = ( *conn_spec )[ names::lam ]; if ( value < 0 ) { throw BadProperty( "Connection parameter 0 <= lam required." ); @@ -1698,11 +1698,11 @@ nest::PoissonBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, s // Sample to number of connections that are to be established num_conns = rng->prand( lam_->value( rng, target ) ); - if ( num_conns == 0) + if ( num_conns == 0 ) { continue; } - for ( unsigned long conn_counter = 1; conn_counter <= num_conns; ++conn_counter) + for ( unsigned long conn_counter = 1; conn_counter <= num_conns; ++conn_counter ) { single_connect_( snode_id, *target, target_thread, rng ); } diff --git a/nestkernel/conn_builder.h b/nestkernel/conn_builder.h index ad0f4c899e..40feaa3461 100644 --- a/nestkernel/conn_builder.h +++ b/nestkernel/conn_builder.h @@ -450,10 +450,7 @@ class BernoulliBuilder : public ConnBuilder class PoissonBuilder : public ConnBuilder { public: - PoissonBuilder( NodeCollectionPTR, - NodeCollectionPTR, - const DictionaryDatum&, - const std::vector< DictionaryDatum >& ); + PoissonBuilder( NodeCollectionPTR, NodeCollectionPTR, const DictionaryDatum&, const std::vector< DictionaryDatum >& ); protected: void connect_() override; diff --git a/nestkernel/connection_creator.h b/nestkernel/connection_creator.h index 7510eda3e5..4e2debfe91 100644 --- a/nestkernel/connection_creator.h +++ b/nestkernel/connection_creator.h @@ -144,7 +144,7 @@ class ConnectionCreator const Position< D >& tgt_pos, size_t tgt_thread, const Layer< D >& source ); - + template < typename Iterator, int D > void connect_to_target_poisson_( Iterator from, Iterator to, @@ -164,11 +164,11 @@ class ConnectionCreator NodeCollectionPTR source_nc, Layer< D >& target, NodeCollectionPTR target_nc ); - + template < int D > void pairwise_poisson_( Layer< D >& source, NodeCollectionPTR source_nc, Layer< D >& target, NodeCollectionPTR target_nc ); - + template < int D > void fixed_indegree_( Layer< D >& source, NodeCollectionPTR source_nc, Layer< D >& target, NodeCollectionPTR target_nc ); diff --git a/nestkernel/connection_creator_impl.h b/nestkernel/connection_creator_impl.h index 03855f5261..143292b858 100644 --- a/nestkernel/connection_creator_impl.h +++ b/nestkernel/connection_creator_impl.h @@ -62,7 +62,7 @@ ConnectionCreator::connect( Layer< D >& source, pairwise_bernoulli_on_target_( source, source_nc, target, target_nc ); break; - + case Pairwise_poisson: pairwise_poisson_( source, source_nc, target, target_nc ); @@ -146,20 +146,20 @@ ConnectionCreator::connect_to_target_poisson_( Iterator from, // Sample number of connections that are to be established num_conns = rng->prand( kernel_->value( rng, source_pos, target_pos, source, tgt_ptr ) ); - if ( without_kernel or num_conns) + if ( without_kernel or num_conns ) { - for ( unsigned long conn_counter = 1; conn_counter <= num_conns; ++conn_counter) + for ( unsigned long conn_counter = 1; conn_counter <= num_conns; ++conn_counter ) { - for ( size_t indx = 0; indx < synapse_model_.size(); ++indx ) - { - kernel().connection_manager.connect( iter->second, - tgt_ptr, - tgt_thread, - synapse_model_[ indx ], - param_dicts_[ indx ][ tgt_thread ], - delay_[ indx ]->value( rng, source_pos, target_pos, source, tgt_ptr ), - weight_[ indx ]->value( rng, source_pos, target_pos, source, tgt_ptr ) ); - } + for ( size_t indx = 0; indx < synapse_model_.size(); ++indx ) + { + kernel().connection_manager.connect( iter->second, + tgt_ptr, + tgt_thread, + synapse_model_[ indx ], + param_dicts_[ indx ][ tgt_thread ], + delay_[ indx ]->value( rng, source_pos, target_pos, source, tgt_ptr ), + weight_[ indx ]->value( rng, source_pos, target_pos, source, tgt_ptr ) ); + } } } } diff --git a/nestkernel/random_generators.h b/nestkernel/random_generators.h index daf55498df..45e090a0ae 100644 --- a/nestkernel/random_generators.h +++ b/nestkernel/random_generators.h @@ -272,7 +272,7 @@ class RandomGenerator final : public BaseRandomGenerator std::uniform_int_distribution< unsigned long >::param_type param( 0, N - 1 ); return uniform_ulong_dist_( rng_, param ); } - + inline unsigned long prand( double lam ) override { From 09d7da67e9f67bd2684afb6996ecca0afafeaae7 Mon Sep 17 00:00:00 2001 From: ackurth Date: Wed, 27 Sep 2023 09:55:04 +0200 Subject: [PATCH 08/52] Correct header in test_connect_pairwise_poisson --- testsuite/pytests/test_connect_pairwise_poisson.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testsuite/pytests/test_connect_pairwise_poisson.py b/testsuite/pytests/test_connect_pairwise_poisson.py index b1839098c9..1714dd4cce 100644 --- a/testsuite/pytests/test_connect_pairwise_poisson.py +++ b/testsuite/pytests/test_connect_pairwise_poisson.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# test_connect_pairwise_bernoulli.py +# test_connect_pairwise_poisson.py # # This file is part of NEST. # From 6b97257b4528b55b167d560c19b9ff44f09ad2d7 Mon Sep 17 00:00:00 2001 From: ackurth Date: Wed, 27 Sep 2023 10:48:39 +0200 Subject: [PATCH 09/52] Change name of lam parameter --- nestkernel/conn_builder.cpp | 14 +++++++------- nestkernel/conn_builder.h | 2 +- nestkernel/nest_names.cpp | 2 +- nestkernel/nest_names.h | 2 +- pynest/nest/lib/hl_api_connection_helpers.py | 8 ++++---- pynest/nest/lib/hl_api_connections.py | 2 +- testsuite/pytests/connect_test_base.py | 6 +++--- testsuite/pytests/test_connect_pairwise_poisson.py | 12 +++++++----- .../pytests/test_spatial/test_connect_layers.py | 14 +++++++------- 9 files changed, 32 insertions(+), 30 deletions(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index 43639f06cc..05662fc202 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -1591,25 +1591,25 @@ nest::PoissonBuilder::PoissonBuilder( NodeCollectionPTR sources, const std::vector< DictionaryDatum >& syn_specs ) : ConnBuilder( sources, targets, conn_spec, syn_specs ) { - ParameterDatum* pd = dynamic_cast< ParameterDatum* >( ( *conn_spec )[ names::lam ].datum() ); + ParameterDatum* pd = dynamic_cast< ParameterDatum* >( ( *conn_spec )[ names::pairwise_avg_num_conns ].datum() ); if ( pd ) { - lam_ = *pd; + pairwise_avg_num_conns_ = *pd; // TODO: Checks of parameter range } else { - // Assume lam is a scalar - const double value = ( *conn_spec )[ names::lam ]; + // Assume pairwise_avg_num_conns is a scalar + const double value = ( *conn_spec )[ names::pairwise_avg_num_conns ]; if ( value < 0 ) { - throw BadProperty( "Connection parameter 0 <= lam required." ); + throw BadProperty( "Connection parameter 0 <= pairwise_avg_num_conns required." ); } if ( not allow_multapses_ ) { throw BadProperty( "Multapses must be allowed for this connection rule." ); } - lam_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); + pairwise_avg_num_conns_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); } } @@ -1696,7 +1696,7 @@ nest::PoissonBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, s } // Sample to number of connections that are to be established - num_conns = rng->prand( lam_->value( rng, target ) ); + num_conns = rng->prand( pairwise_avg_num_conns_->value( rng, target ) ); if ( num_conns == 0 ) { diff --git a/nestkernel/conn_builder.h b/nestkernel/conn_builder.h index 40feaa3461..84f775adff 100644 --- a/nestkernel/conn_builder.h +++ b/nestkernel/conn_builder.h @@ -457,7 +457,7 @@ class PoissonBuilder : public ConnBuilder private: void inner_connect_( const int, RngPtr, Node*, size_t ); - ParameterDatum lam_; //!< Mean number of connections + ParameterDatum pairwise_avg_num_conns_; //!< Mean number of connections }; class SymmetricBernoulliBuilder : public ConnBuilder diff --git a/nestkernel/nest_names.cpp b/nestkernel/nest_names.cpp index 78098deecf..4827b4e8d9 100644 --- a/nestkernel/nest_names.cpp +++ b/nestkernel/nest_names.cpp @@ -268,7 +268,6 @@ const Name keep_source_table( "keep_source_table" ); const Name kernel( "kernel" ); const Name label( "label" ); -const Name lam( "lam" ); const Name lambda( "lambda" ); const Name lambda_0( "lambda_0" ); const Name len_kernel( "len_kernel" ); @@ -348,6 +347,7 @@ const Name p_copy( "p_copy" ); const Name p_transmit( "p_transmit" ); const Name pairwise_bernoulli_on_source( "pairwise_bernoulli_on_source" ); const Name pairwise_bernoulli_on_target( "pairwise_bernoulli_on_target" ); +const Name pairwise_avg_num_conns( "pairwise_avg_num_conns" ); const Name params( "params" ); const Name parent_idx( "parent_idx" ); const Name phase( "phase" ); diff --git a/nestkernel/nest_names.h b/nestkernel/nest_names.h index 4e4f3db2c2..6b43f4b516 100644 --- a/nestkernel/nest_names.h +++ b/nestkernel/nest_names.h @@ -294,7 +294,6 @@ extern const Name keep_source_table; extern const Name kernel; extern const Name label; -extern const Name lam; extern const Name lambda; extern const Name lambda_0; extern const Name len_kernel; @@ -374,6 +373,7 @@ extern const Name p_copy; extern const Name p_transmit; extern const Name pairwise_bernoulli_on_source; extern const Name pairwise_bernoulli_on_target; +extern const Name pairwise_avg_num_conns; extern const Name params; extern const Name parent_idx; extern const Name phase; diff --git a/pynest/nest/lib/hl_api_connection_helpers.py b/pynest/nest/lib/hl_api_connection_helpers.py index c768497d14..7d36027c09 100644 --- a/pynest/nest/lib/hl_api_connection_helpers.py +++ b/pynest/nest/lib/hl_api_connection_helpers.py @@ -169,7 +169,7 @@ def _process_spatial_projections(conn_spec, syn_spec): "indegree", "outdegree", "p", - "lam", + "pairwise_avg_num_conns", "use_on_source", "allow_oversized_mask", ] @@ -182,8 +182,8 @@ def _process_spatial_projections(conn_spec, syn_spec): projections.update(conn_spec) if "p" in conn_spec: projections["kernel"] = projections.pop("p") - elif "lam" in conn_spec: - projections["kernel"] = projections.pop("lam") + elif "pairwise_avg_num_conns" in conn_spec: + projections["kernel"] = projections.pop("pairwise_avg_num_conns") if syn_spec is not None: if isinstance(syn_spec, CollocatedSynapses): for syn_list in syn_spec.syn_specs: @@ -241,7 +241,7 @@ def _connect_layers_needed(conn_spec, syn_spec): if "mask" in conn_spec or ("p" in conn_spec and not rule_is_bernoulli) or "use_on_source" in conn_spec: return True rule_is_poisson = "pairwise_poisson" in str(conn_spec["rule"]) - if "mask" in conn_spec or ("lam" in conn_spec and not rule_is_poisson) or "use_on_source" in conn_spec: + if "mask" in conn_spec or ("pairwise_avg_num_conns" in conn_spec and not rule_is_poisson) or "use_on_source" in conn_spec: return True # If a syn_spec entry is based on spatial properties, we must use ConnectLayers. if isinstance(syn_spec, dict): diff --git a/pynest/nest/lib/hl_api_connections.py b/pynest/nest/lib/hl_api_connections.py index b8ff81f47b..1b524cb751 100644 --- a/pynest/nest/lib/hl_api_connections.py +++ b/pynest/nest/lib/hl_api_connections.py @@ -166,7 +166,7 @@ def Connect(pre, post, conn_spec=None, syn_spec=None, return_synapsecollection=F - 'fixed_total_number', 'N' - 'pairwise_bernoulli', 'p' - 'symmetric_pairwise_bernoulli', 'p' - - 'pairwise_poisson', 'lam' + - 'pairwise_poisson', 'pairwise_avg_num_conns' See :ref:`conn_rules` for more details, including example usage. diff --git a/testsuite/pytests/connect_test_base.py b/testsuite/pytests/connect_test_base.py index 0a05186c27..8cda8d403d 100644 --- a/testsuite/pytests/connect_test_base.py +++ b/testsuite/pytests/connect_test_base.py @@ -553,9 +553,9 @@ def get_expected_degrees_bernoulli(p, fan, len_source_pop, len_target_pop): # adapted from Masterthesis, Daniel Hjertholm -def get_expected_degrees_poisson(lam, fan, len_source_pop, len_target_pop): - expected_indegree = lam * len_source_pop - expected_outdegree = lam * len_target_pop +def get_expected_degrees_poisson(pairwise_avg_num_conns, fan, len_source_pop, len_target_pop): + expected_indegree = pairwise_avg_num_conns * len_source_pop + expected_outdegree = pairwise_avg_num_conns * len_target_pop if fan == "in": return expected_indegree elif fan == "out": diff --git a/testsuite/pytests/test_connect_pairwise_poisson.py b/testsuite/pytests/test_connect_pairwise_poisson.py index 1714dd4cce..6948c83c50 100644 --- a/testsuite/pytests/test_connect_pairwise_poisson.py +++ b/testsuite/pytests/test_connect_pairwise_poisson.py @@ -36,8 +36,8 @@ class TestPairwisePoisson(connect_test_base.ConnectTestBase): # specify connection pattern and specific params rule = "pairwise_poisson" - lam = 0.5 - conn_dict = {"rule": rule, "lam": lam} + pairwise_avg_num_conns = 0.5 + conn_dict = {"rule": rule, "pairwise_avg_num_conns": pairwise_avg_num_conns} # sizes of source-, target-population and connection probability for # statistical test N_s = 50 @@ -64,13 +64,15 @@ def testAutapsesTrue(self): nest.Connect(pop1, pop1, conn_params) # make sure all connections do exist M = connect_test_base.get_connectivity_matrix(pop1, pop1) - np.testing.assert_allclose(np.diag(M).sum(), self.N1 * self.lam, atol=2) + np.testing.assert_allclose(np.diag(M).sum(), self.N1 * self.pairwise_avg_num_conns, atol=2) def testExpInOutdegree(self): connect_test_base.reset_seed(1, self.nr_threads) self.setUpNetwork(conn_dict=self.conn_dict, N1=self.N_s, N2=self.N_t) for fan in ["in", "out"]: - expected = connect_test_base.get_expected_degrees_poisson(self.lam, fan, self.N_s, self.N_t) + expected = connect_test_base.get_expected_degrees_poisson( + self.pairwise_avg_num_conns, fan, self.N_s, self.N_t + ) mean_degrees = connect_test_base.get_degrees(fan, self.pop1, self.pop2).mean() @@ -89,7 +91,7 @@ def testStatistics(self): multapse_distribution, auto_bins = np.histogram(multapses, bins=x, density=True) bins = (auto_bins[:-1] + auto_bins[1:]) / 2 - expected_distribution = scipy.stats.poisson.pmf(bins, self.lam) + expected_distribution = scipy.stats.poisson.pmf(bins, self.pairwise_avg_num_conns) _, p = connect_test_base.chi_squared_check(multapse_distribution, expected_distribution, self.rule) self.assertTrue(p > self.stat_dict["alpha2"]) diff --git a/testsuite/pytests/test_spatial/test_connect_layers.py b/testsuite/pytests/test_spatial/test_connect_layers.py index 8e39ff30a3..d639e519ba 100644 --- a/testsuite/pytests/test_spatial/test_connect_layers.py +++ b/testsuite/pytests/test_spatial/test_connect_layers.py @@ -87,7 +87,7 @@ def _check_connections_statistical_bernoulli(self, conn_spec, p, num_pairs): self.assertGreater(mean_p_val, p_val_lim) self.assertLess(mean_ks_stat, ks_stat_lim) - def _check_connections_statistical_poisson(self, conn_spec, lam, num_pairs): + def _check_connections_statistical_poisson(self, conn_spec, pairwise_avg_num_conns, num_pairs): """Helper function which asserts that the number of connections created are based on a poisson distribution. The connection function is iterated N times, then the distribution of number of created connections are tested against a bernoulli distribution using a Kolmogorov-Smirnov test. This is done ks_N times, to get statistical @@ -108,7 +108,7 @@ def _check_connections_statistical_poisson(self, conn_spec, lam, num_pairs): for i in range(N): nest.Connect(self.layer, self.layer, conn_spec) n_conns[i] = nest.num_connections - np.sum(n_conns) - ref[i] = np.sum(scipy.stats.poisson.rvs(lam, size=num_pairs)) + ref[i] = np.sum(scipy.stats.poisson.rvs(pairwise_avg_num_conns, size=num_pairs)) ks_stats[ks_i], p_vals[ks_i] = scipy.stats.ks_2samp(n_conns, ref) print(f"ks_stat={ks_stats[ks_i]}, p_val={p_vals[ks_i]}") @@ -201,7 +201,7 @@ def test_connect_layers_bernoulli_source(self): def test_connect_layers_poisson(self): """Connecting layers with pairwise_poisson.""" - conn_spec = {"rule": "pairwise_poisson", "lam": 0.5} + conn_spec = {"rule": "pairwise_poisson", "pairwise_avg_num_conns": 0.5} conns = self._check_connections(conn_spec, None, return_conns=True) np.testing.assert_allclose(200, len(conns), atol=5) @@ -282,7 +282,7 @@ def test_connect_layers_poisson_mask(self): """Connecting layers with pairwise_poisson and mask""" conn_spec = { "rule": "pairwise_poisson", - "lam": 0.5, + "pairwise_avg_num_conns": 0.5, "mask": {"rectangular": {"lower_left": [-5.0, -5.0], "upper_right": [0.1, 0.1]}}, } conns = self._check_connections(conn_spec, None, return_conns=True) @@ -302,13 +302,13 @@ def test_connect_layers_bernoulli_kernel_mask(self): @unittest.skipIf(not HAVE_SCIPY, "SciPy package is not available") def test_connect_layers_poisson_kernel_mask(self): """Connecting layers with pairwise_poisson, kernel and mask""" - lam = 0.5 + pairwise_avg_num_conns = 0.5 conn_spec = { "rule": "pairwise_poisson", - "lam": lam, + "pairwise_avg_num_conns": pairwise_avg_num_conns, "mask": {"rectangular": {"lower_left": [-5.0, -5.0], "upper_right": [0.1, 0.1]}}, } - self._check_connections_statistical_poisson(conn_spec, lam, 108) + self._check_connections_statistical_poisson(conn_spec, pairwise_avg_num_conns, 108) @unittest.skipIf(not HAVE_SCIPY, "SciPy package is not available") def test_connect_layers_bernoulli_kernel_mask_source(self): From 1e4bcd058783183b5f155b968ab72847d950245c Mon Sep 17 00:00:00 2001 From: ackurth Date: Wed, 27 Sep 2023 11:32:25 +0200 Subject: [PATCH 10/52] Add documentation --- .../spatially_structured_networks.rst | 30 ++++++++++++++++--- .../synapses/connection_management.rst | 20 +++++++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/doc/htmldoc/networks/spatially_structured_networks.rst b/doc/htmldoc/networks/spatially_structured_networks.rst index 2148e063c5..80fd33e34d 100644 --- a/doc/htmldoc/networks/spatially_structured_networks.rst +++ b/doc/htmldoc/networks/spatially_structured_networks.rst @@ -471,6 +471,8 @@ Pool +================================+==============+==============+ | ``rule='pairwise_bernoulli'`` | source layer | target layer | +--------------------------------+--------------+--------------+ + | ``rule='pairwise_poisson'`` | source layer | target layer | + +--------------------------------+--------------+--------------+ | ``rule='fixed_outdegree'`` | source layer | target layer | +--------------------------------+--------------+--------------+ | ``rule='pairwise_bernoulli'`` | target layer | source layer | @@ -500,6 +502,13 @@ Connection probability or ``p`` The default probability is :math:`1`, i.e., connections are created with certainty. See section :ref:`sec_conn_kernels` for details. +Pairwise average number of connections or ``pairwise_avg_num_conns`` + The *pairwise average number of connections* between a driver and a pool node. + It is used in the ``pairwise_poisson`` connection rule and determines + the mean value of the Poisson distribution from which the number of + connections between the nodes is sampled. See section + :ref:`sec_conn_kernels` for details. + Autapse An *autapse* is a synapse (connection) from a node onto itself. Autapses are permitted by default, but can be disabled by adding @@ -832,10 +841,14 @@ Probabilistic connection rules Many neuronal network models employ probabilistic connection rules. NEST supports probabilistic connections through the -``pairwise_bernoulli`` connection rule. The probability can then be a constant, -depend on the position of the source or the target neuron, or on the -distance between a driver and a pool node to a connection probability. To -create dependencies on neuron positions, NEST parameters objects are used. +``pairwise_bernoulli`` and the ``pairwise_poisson`` connection rule. +For the ``pairwise_bernoulli`` rule, the probability can then be a +constant, depend on the position of the source or the target neuron, +or on the distance between a driver and a pool node to a connection probability. +For the ``pairwise_poisson`` rule, the pairwise average number of connections +again can bdepend on the position of the source or the target neuron, +or on the distance between a driver and a pool node to a connection probability. +To create dependencies on neuron positions, NEST parameters objects are used. NEST then generates a connection according to this probability. Probabilistic connections between layers can be generated in two different @@ -849,6 +862,15 @@ Free probabilistic connections using ``pairwise_bernoulli`` driver-pool pair is inspected exactly once* and that there will be *at most one connection between each driver-pool pair*. +Free probabilistic connections using ``pairwise_poisson`` + In this case, :py:func:`.Connect` considers each driver node :math:`D` in turn. + For each :math:`D`, it evaluates the parameter value for each pool node + :math:`P` within the mask and creates a number of connections that are sampled + from a Poisson distribution with mean of the parameter value. This means in + particular that *each possible driver-pool pair is inspected exactly once* + and that *more than one connection between each driver-pool pair is possible*. + Additionally, the parameter may be larger than :math:`1`. + Prescribed number of connections can be obtained by using ``fixed_indegree`` or ``fixed_outdegree`` connection rule, and specifying the number of connections to create diff --git a/doc/htmldoc/synapses/connection_management.rst b/doc/htmldoc/synapses/connection_management.rst index 88263bb3e4..2a3000bf96 100644 --- a/doc/htmldoc/synapses/connection_management.rst +++ b/doc/htmldoc/synapses/connection_management.rst @@ -256,6 +256,26 @@ must be ``True``. 'allow_autapses': False, 'make_symmetric': True} nest.Connect(A, B, conn_spec_dict) +pairwise poisson +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For each possible pair of nodes from ``A`` and ``B``, a number of +connections is created following a Poisson distribution with mean +``pairwise_avg_num_conns``. This means that even for a small +average number of connections between single neurons in ``A`` and +``B`` multiple connections are possible. Thus, for this rule +``allow_multapses`` cannot be ``False``. +The ``pairwise_avg_num_conns`` can be greater than one. + +.. code-block:: python + + n, m, p_avg_num_conns = 10, 12, 0.2 + A = nest.Create('iaf_psc_alpha', n) + B = nest.Create('iaf_psc_alpha', m) + conn_spec_dict = {'rule': 'pairwise_poisson', + 'pairwise_avg_num_conns': p_avg_num_conns} + nest.Connect(A, B, conn_spec_dict) + .. _synapse_spec: Synapse Specification From 3134ee85104b92e1af65306c31324b152471d0db Mon Sep 17 00:00:00 2001 From: ackurth Date: Wed, 27 Sep 2023 11:34:52 +0200 Subject: [PATCH 11/52] Run isort --- testsuite/pytests/test_connect_pairwise_poisson.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testsuite/pytests/test_connect_pairwise_poisson.py b/testsuite/pytests/test_connect_pairwise_poisson.py index 6948c83c50..f7a2c693ca 100644 --- a/testsuite/pytests/test_connect_pairwise_poisson.py +++ b/testsuite/pytests/test_connect_pairwise_poisson.py @@ -23,10 +23,10 @@ import unittest import connect_test_base -from connect_test_base import get_connectivity_matrix import nest import numpy as np import scipy.stats +from connect_test_base import get_connectivity_matrix HAVE_OPENMP = nest.ll_api.sli_func("is_threaded") From 22726ee4c7ada02d301c8fddf0879f8dea19613f Mon Sep 17 00:00:00 2001 From: ackurth Date: Wed, 27 Sep 2023 11:37:47 +0200 Subject: [PATCH 12/52] Run black --- pynest/nest/lib/hl_api_connection_helpers.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pynest/nest/lib/hl_api_connection_helpers.py b/pynest/nest/lib/hl_api_connection_helpers.py index 7d36027c09..4811930d78 100644 --- a/pynest/nest/lib/hl_api_connection_helpers.py +++ b/pynest/nest/lib/hl_api_connection_helpers.py @@ -241,7 +241,11 @@ def _connect_layers_needed(conn_spec, syn_spec): if "mask" in conn_spec or ("p" in conn_spec and not rule_is_bernoulli) or "use_on_source" in conn_spec: return True rule_is_poisson = "pairwise_poisson" in str(conn_spec["rule"]) - if "mask" in conn_spec or ("pairwise_avg_num_conns" in conn_spec and not rule_is_poisson) or "use_on_source" in conn_spec: + if ( + "mask" in conn_spec + or ("pairwise_avg_num_conns" in conn_spec and not rule_is_poisson) + or "use_on_source" in conn_spec + ): return True # If a syn_spec entry is based on spatial properties, we must use ConnectLayers. if isinstance(syn_spec, dict): From e36ac72fc636585c342fd668b343b8dac3a1415c Mon Sep 17 00:00:00 2001 From: ackurth Date: Wed, 27 Sep 2023 11:40:23 +0200 Subject: [PATCH 13/52] Correct flak8 error --- testsuite/pytests/test_spatial/test_connect_layers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testsuite/pytests/test_spatial/test_connect_layers.py b/testsuite/pytests/test_spatial/test_connect_layers.py index d639e519ba..cfb81ef2d0 100644 --- a/testsuite/pytests/test_spatial/test_connect_layers.py +++ b/testsuite/pytests/test_spatial/test_connect_layers.py @@ -324,7 +324,7 @@ def test_connect_layers_bernoulli_kernel_mask_source(self): } self._check_connections_statistical_bernoulli(conn_spec, p, 108) - def test_connect_nonlayers_mask(self): + def test_connect_nonlayers_mask_bernoulli(self): """Throw when connecting non-layer NodeCollections with mask.""" neurons = nest.Create("iaf_psc_alpha", 20) conn_spec = { @@ -335,7 +335,7 @@ def test_connect_nonlayers_mask(self): with self.assertRaises(TypeError): nest.Connect(neurons, neurons, conn_spec) - def test_connect_nonlayers_mask(self): + def test_connect_nonlayers_mask_poisson(self): """Throw when connecting non-layer NodeCollections with mask.""" neurons = nest.Create("iaf_psc_alpha", 20) conn_spec = { From 39f5b6aee2af054949fbc1ecd316d0629a75b168 Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Fri, 29 Sep 2023 09:18:44 +0200 Subject: [PATCH 14/52] Update doc/htmldoc/networks/spatially_structured_networks.rst Co-authored-by: Nicolai Haug <39106781+nicolossus@users.noreply.github.com> --- doc/htmldoc/networks/spatially_structured_networks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/htmldoc/networks/spatially_structured_networks.rst b/doc/htmldoc/networks/spatially_structured_networks.rst index 80fd33e34d..063ab1e1d9 100644 --- a/doc/htmldoc/networks/spatially_structured_networks.rst +++ b/doc/htmldoc/networks/spatially_structured_networks.rst @@ -846,7 +846,7 @@ For the ``pairwise_bernoulli`` rule, the probability can then be a constant, depend on the position of the source or the target neuron, or on the distance between a driver and a pool node to a connection probability. For the ``pairwise_poisson`` rule, the pairwise average number of connections -again can bdepend on the position of the source or the target neuron, +again can depend on the position of the source or the target neuron, or on the distance between a driver and a pool node to a connection probability. To create dependencies on neuron positions, NEST parameters objects are used. NEST then generates a connection according to this probability. From f99864fffb7c83451b9546aa489a01aa663ba14d Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Fri, 29 Sep 2023 09:18:53 +0200 Subject: [PATCH 15/52] Update doc/htmldoc/networks/spatially_structured_networks.rst Co-authored-by: Nicolai Haug <39106781+nicolossus@users.noreply.github.com> --- doc/htmldoc/networks/spatially_structured_networks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/htmldoc/networks/spatially_structured_networks.rst b/doc/htmldoc/networks/spatially_structured_networks.rst index 063ab1e1d9..dffbfcabfe 100644 --- a/doc/htmldoc/networks/spatially_structured_networks.rst +++ b/doc/htmldoc/networks/spatially_structured_networks.rst @@ -848,7 +848,7 @@ or on the distance between a driver and a pool node to a connection probability. For the ``pairwise_poisson`` rule, the pairwise average number of connections again can depend on the position of the source or the target neuron, or on the distance between a driver and a pool node to a connection probability. -To create dependencies on neuron positions, NEST parameters objects are used. +To create dependencies on neuron positions, NEST :py:class:`.Parameter` objects are used. NEST then generates a connection according to this probability. Probabilistic connections between layers can be generated in two different From a0c253819731a8be7fbc7f3a32376911499ebff8 Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Fri, 29 Sep 2023 09:19:02 +0200 Subject: [PATCH 16/52] Update nestkernel/connection_creator_impl.h Co-authored-by: Nicolai Haug <39106781+nicolossus@users.noreply.github.com> --- nestkernel/connection_creator_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nestkernel/connection_creator_impl.h b/nestkernel/connection_creator_impl.h index 143292b858..8741a04956 100644 --- a/nestkernel/connection_creator_impl.h +++ b/nestkernel/connection_creator_impl.h @@ -400,7 +400,7 @@ ConnectionCreator::pairwise_poisson_( Layer< D >& source, Layer< D >& target, NodeCollectionPTR target_nc ) { - // Connect using pairwise Bernoulli drawing source nodes (target driven) + // Connect using pairwise Poisson drawing source nodes (target driven) // For each local target node: // 1. Apply Mask to source layer // 2. For each source node: Compute probability, draw random number, make From 4a9011d9514b0510ea505bb55427c774fdec4c6d Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Fri, 29 Sep 2023 09:19:12 +0200 Subject: [PATCH 17/52] Update nestkernel/random_generators.h Co-authored-by: Nicolai Haug <39106781+nicolossus@users.noreply.github.com> --- nestkernel/random_generators.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nestkernel/random_generators.h b/nestkernel/random_generators.h index 45e090a0ae..d0cd4138e2 100644 --- a/nestkernel/random_generators.h +++ b/nestkernel/random_generators.h @@ -117,7 +117,7 @@ class BaseRandomGenerator virtual unsigned long ulrand( unsigned long N ) = 0; /** - * @brief Uses the wrapped RNG engine to draw an unsigned long from a Poisson distribution with mean lam. + * @brief Uses the wrapped RNG engine to draw an unsigned long from a Poisson distribution with mean mu. * * @param lam Mean value of Poisson distribution. */ From fa705c0a37ef2a2ed9e30e6564bf595328881f2b Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Fri, 29 Sep 2023 09:19:21 +0200 Subject: [PATCH 18/52] Update pynest/nest/lib/hl_api_connection_helpers.py Co-authored-by: Nicolai Haug <39106781+nicolossus@users.noreply.github.com> --- pynest/nest/lib/hl_api_connection_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynest/nest/lib/hl_api_connection_helpers.py b/pynest/nest/lib/hl_api_connection_helpers.py index 4811930d78..9a1dafc61c 100644 --- a/pynest/nest/lib/hl_api_connection_helpers.py +++ b/pynest/nest/lib/hl_api_connection_helpers.py @@ -218,7 +218,7 @@ def _process_spatial_projections(conn_spec, syn_spec): projections.pop("use_on_source") elif conn_spec["rule"] == "pairwise_poisson": if "use_on_source" in conn_spec: - raise ValueError("'use_on_source' can only be set when using pairwise_bernoulli") + raise ValueError("'use_on_source' can only be set when using 'pairwise_bernoulli'.") projections["connection_type"] = "pairwise_poisson" else: raise kernel.NESTError( From 80ecefeb06aa3567a27772fc6b3f1a0884f8eea8 Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Fri, 29 Sep 2023 18:24:22 +0200 Subject: [PATCH 19/52] Update nestkernel/random_generators.h Co-authored-by: Nicolai Haug <39106781+nicolossus@users.noreply.github.com> --- nestkernel/random_generators.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nestkernel/random_generators.h b/nestkernel/random_generators.h index d0cd4138e2..97c1ea0d1e 100644 --- a/nestkernel/random_generators.h +++ b/nestkernel/random_generators.h @@ -119,7 +119,7 @@ class BaseRandomGenerator /** * @brief Uses the wrapped RNG engine to draw an unsigned long from a Poisson distribution with mean mu. * - * @param lam Mean value of Poisson distribution. + * @param mu Mean value of Poisson distribution. */ virtual unsigned long prand( double lam ) = 0; }; From 9f07c07b89672dfcac4ed305b6dccdd1fcaf9989 Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Fri, 29 Sep 2023 18:24:42 +0200 Subject: [PATCH 20/52] Update nestkernel/random_generators.h Co-authored-by: Nicolai Haug <39106781+nicolossus@users.noreply.github.com> --- nestkernel/random_generators.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nestkernel/random_generators.h b/nestkernel/random_generators.h index 97c1ea0d1e..e4d01166d7 100644 --- a/nestkernel/random_generators.h +++ b/nestkernel/random_generators.h @@ -121,7 +121,7 @@ class BaseRandomGenerator * * @param mu Mean value of Poisson distribution. */ - virtual unsigned long prand( double lam ) = 0; + virtual unsigned long prand( double mu ) = 0; }; /** From 6d898cf9bdf65cdd2c90c348886002245a905916 Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Fri, 29 Sep 2023 18:24:56 +0200 Subject: [PATCH 21/52] Update nestkernel/random_generators.h Co-authored-by: Nicolai Haug <39106781+nicolossus@users.noreply.github.com> --- nestkernel/random_generators.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nestkernel/random_generators.h b/nestkernel/random_generators.h index e4d01166d7..b32d4e7e6f 100644 --- a/nestkernel/random_generators.h +++ b/nestkernel/random_generators.h @@ -274,9 +274,9 @@ class RandomGenerator final : public BaseRandomGenerator } inline unsigned long - prand( double lam ) override + prand( double mu ) override { - std::poisson_distribution< unsigned long >::param_type param( lam ); + std::poisson_distribution< unsigned long >::param_type param( mu ); return poisson_ulong_dist_( rng_, param ); } From 8a7a66a77a91d80d3314e024afd59f336649488c Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Tue, 28 Nov 2023 11:38:22 +0100 Subject: [PATCH 22/52] Update doc/htmldoc/networks/spatially_structured_networks.rst Co-authored-by: Hans Ekkehard Plesser --- doc/htmldoc/networks/spatially_structured_networks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/htmldoc/networks/spatially_structured_networks.rst b/doc/htmldoc/networks/spatially_structured_networks.rst index dffbfcabfe..f2388769b3 100644 --- a/doc/htmldoc/networks/spatially_structured_networks.rst +++ b/doc/htmldoc/networks/spatially_structured_networks.rst @@ -866,7 +866,7 @@ Free probabilistic connections using ``pairwise_poisson`` In this case, :py:func:`.Connect` considers each driver node :math:`D` in turn. For each :math:`D`, it evaluates the parameter value for each pool node :math:`P` within the mask and creates a number of connections that are sampled - from a Poisson distribution with mean of the parameter value. This means in + from a Poisson distribution with mean of the parameter value. This means in particular that *each possible driver-pool pair is inspected exactly once* and that *more than one connection between each driver-pool pair is possible*. Additionally, the parameter may be larger than :math:`1`. From 37d42676fa9f4f7d78ef040c111cacb93ca94b8e Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Tue, 28 Nov 2023 11:38:34 +0100 Subject: [PATCH 23/52] Update doc/htmldoc/synapses/connection_management.rst Co-authored-by: Hans Ekkehard Plesser --- doc/htmldoc/synapses/connection_management.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/htmldoc/synapses/connection_management.rst b/doc/htmldoc/synapses/connection_management.rst index 2a3000bf96..0e6dc1333c 100644 --- a/doc/htmldoc/synapses/connection_management.rst +++ b/doc/htmldoc/synapses/connection_management.rst @@ -257,7 +257,7 @@ must be ``True``. nest.Connect(A, B, conn_spec_dict) pairwise poisson -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~ For each possible pair of nodes from ``A`` and ``B``, a number of connections is created following a Poisson distribution with mean From ad2f754a970b8763fa005523d486aad1c4809240 Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Tue, 28 Nov 2023 11:38:49 +0100 Subject: [PATCH 24/52] Update nestkernel/connection_creator.cpp Co-authored-by: Hans Ekkehard Plesser --- nestkernel/connection_creator.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/nestkernel/connection_creator.cpp b/nestkernel/connection_creator.cpp index 024e959163..4d1319bc88 100644 --- a/nestkernel/connection_creator.cpp +++ b/nestkernel/connection_creator.cpp @@ -137,7 +137,6 @@ ConnectionCreator::ConnectionCreator( DictionaryDatum dict ) } else if ( connection_type == names::pairwise_poisson ) { - if ( dict->known( names::number_of_connections ) ) { type_ = Fixed_indegree; From d6507779c239b05615c98984a229734e4bdacbec Mon Sep 17 00:00:00 2001 From: ackurth Date: Tue, 28 Nov 2023 15:27:56 +0100 Subject: [PATCH 25/52] Formatting --- nestkernel/conn_builder.h | 1 + 1 file changed, 1 insertion(+) diff --git a/nestkernel/conn_builder.h b/nestkernel/conn_builder.h index a9eef62918..0f7b850be8 100644 --- a/nestkernel/conn_builder.h +++ b/nestkernel/conn_builder.h @@ -458,6 +458,7 @@ class PoissonBuilder : public ConnBuilder { public: PoissonBuilder( NodeCollectionPTR, NodeCollectionPTR, const DictionaryDatum&, const std::vector< DictionaryDatum >& ); + protected: void connect_() override; From e2604821bd7cc3f436c1c7b0810da3246e0e4486 Mon Sep 17 00:00:00 2001 From: ackurth Date: Mon, 4 Dec 2023 11:16:52 +0100 Subject: [PATCH 26/52] Connection parameters table reordering --- doc/htmldoc/networks/spatially_structured_networks.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/htmldoc/networks/spatially_structured_networks.rst b/doc/htmldoc/networks/spatially_structured_networks.rst index f2388769b3..21fc65b5ee 100644 --- a/doc/htmldoc/networks/spatially_structured_networks.rst +++ b/doc/htmldoc/networks/spatially_structured_networks.rst @@ -469,16 +469,16 @@ Pool +--------------------------------+--------------+--------------+ | Connection parameters | Driver | Pool | +================================+==============+==============+ - | ``rule='pairwise_bernoulli'`` | source layer | target layer | - +--------------------------------+--------------+--------------+ - | ``rule='pairwise_poisson'`` | source layer | target layer | + | ``rule='fixed_indegree'`` | target layer | source layer | +--------------------------------+--------------+--------------+ | ``rule='fixed_outdegree'`` | source layer | target layer | +--------------------------------+--------------+--------------+ + | ``rule='pairwise_bernoulli'`` | source layer | target layer | + +--------------------------------+--------------+--------------+ | ``rule='pairwise_bernoulli'`` | target layer | source layer | | and ``use_on_source=True`` | | | +--------------------------------+--------------+--------------+ - | ``rule='fixed_indegree'`` | target layer | source layer | + | ``rule='pairwise_poisson'`` | source layer | target layer | +--------------------------------+--------------+--------------+ Displacement From 8d12b035cf4201e59bfa3a99bd29727e723dce0b Mon Sep 17 00:00:00 2001 From: ackurth Date: Mon, 4 Dec 2023 11:18:17 +0100 Subject: [PATCH 27/52] Remove again --- doc/htmldoc/networks/spatially_structured_networks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/htmldoc/networks/spatially_structured_networks.rst b/doc/htmldoc/networks/spatially_structured_networks.rst index 21fc65b5ee..dd5b15d367 100644 --- a/doc/htmldoc/networks/spatially_structured_networks.rst +++ b/doc/htmldoc/networks/spatially_structured_networks.rst @@ -846,7 +846,7 @@ For the ``pairwise_bernoulli`` rule, the probability can then be a constant, depend on the position of the source or the target neuron, or on the distance between a driver and a pool node to a connection probability. For the ``pairwise_poisson`` rule, the pairwise average number of connections -again can depend on the position of the source or the target neuron, +can depend on the position of the source or the target neuron, or on the distance between a driver and a pool node to a connection probability. To create dependencies on neuron positions, NEST :py:class:`.Parameter` objects are used. NEST then generates a connection according to this probability. From 29f6d751013b363b90ed241b363c7205d37cfb60 Mon Sep 17 00:00:00 2001 From: ackurth Date: Mon, 4 Dec 2023 16:21:47 +0100 Subject: [PATCH 28/52] Change random number generation --- nestkernel/conn_builder.cpp | 6 ++-- nestkernel/connection_creator_impl.h | 6 +++- nestkernel/random_generators.h | 46 ++++++++++------------------ 3 files changed, 25 insertions(+), 33 deletions(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index 723b4fe9a2..9378f7d7fa 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -1685,8 +1685,9 @@ nest::PoissonBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, s return; } - // It is not possible to disable multapses with the PoissonBuilder, already checked + poisson_distribution poi_dist; + // It is not possible to disable multapses with the PoissonBuilder, already checked NodeCollection::const_iterator source_it = sources_->begin(); for ( ; source_it < sources_->end(); ++source_it ) { @@ -1698,7 +1699,8 @@ nest::PoissonBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, s } // Sample to number of connections that are to be established - num_conns = rng->prand( pairwise_avg_num_conns_->value( rng, target ) ); + poisson_distribution::param_type param( pairwise_avg_num_conns_->value( rng, target ) ); + num_conns = poi_dist( rng, param ); if ( num_conns == 0 ) { diff --git a/nestkernel/connection_creator_impl.h b/nestkernel/connection_creator_impl.h index 8741a04956..c56f9bfef9 100644 --- a/nestkernel/connection_creator_impl.h +++ b/nestkernel/connection_creator_impl.h @@ -132,6 +132,9 @@ ConnectionCreator::connect_to_target_poisson_( Iterator from, std::vector< double > source_pos( D ); const std::vector< double > target_pos = tgt_pos.get_vector(); + + poisson_distribution poi_dist; + // Declare number of connections variable unsigned long num_conns; @@ -145,7 +148,8 @@ ConnectionCreator::connect_to_target_poisson_( Iterator from, iter->first.get_vector( source_pos ); // Sample number of connections that are to be established - num_conns = rng->prand( kernel_->value( rng, source_pos, target_pos, source, tgt_ptr ) ); + poisson_distribution::param_type param( kernel_->value( rng, source_pos, target_pos, source, tgt_ptr ) ); + num_conns = poi_dist( rng, param ); if ( without_kernel or num_conns ) { for ( unsigned long conn_counter = 1; conn_counter <= num_conns; ++conn_counter ) diff --git a/nestkernel/random_generators.h b/nestkernel/random_generators.h index acd9e3c060..15f7278cde 100644 --- a/nestkernel/random_generators.h +++ b/nestkernel/random_generators.h @@ -51,13 +51,13 @@ using RngPtr = BaseRandomGenerator*; using uniform_int_distribution = RandomDistribution< std::uniform_int_distribution< unsigned long > >; using uniform_real_distribution = RandomDistribution< std::uniform_real_distribution<> >; -using poisson_distribution = RandomDistribution< std::poisson_distribution< unsigned long > >; using normal_distribution = RandomDistribution< std::normal_distribution<> >; using lognormal_distribution = RandomDistribution< std::lognormal_distribution<> >; using binomial_distribution = RandomDistribution< std::binomial_distribution< unsigned long > >; using gamma_distribution = RandomDistribution< std::gamma_distribution<> >; using exponential_distribution = RandomDistribution< std::exponential_distribution<> >; using discrete_distribution = RandomDistribution< std::discrete_distribution< unsigned long > >; +using poisson_distribution = RandomDistribution< std::poisson_distribution< unsigned long > >; /** @@ -79,13 +79,13 @@ class BaseRandomGenerator */ virtual unsigned long operator()( std::uniform_int_distribution< unsigned long >& d ) = 0; virtual double operator()( std::uniform_real_distribution<>& d ) = 0; - virtual unsigned long operator()( std::poisson_distribution< unsigned long >& d ) = 0; virtual double operator()( std::normal_distribution<>& d ) = 0; virtual double operator()( std::lognormal_distribution<>& d ) = 0; virtual unsigned long operator()( std::binomial_distribution< unsigned long >& d ) = 0; virtual double operator()( std::gamma_distribution<>& d ) = 0; virtual double operator()( std::exponential_distribution<>& d ) = 0; virtual unsigned long operator()( std::discrete_distribution< unsigned long >& d ) = 0; + virtual unsigned long operator()( std::poisson_distribution< unsigned long >& d ) = 0; /** * @brief Calls the provided distribution with the wrapped RNG engine, using provided distribution parameters. @@ -98,8 +98,6 @@ class BaseRandomGenerator virtual unsigned long operator()( std::uniform_int_distribution< unsigned long >& d, std::uniform_int_distribution< unsigned long >::param_type& p ) = 0; virtual double operator()( std::uniform_real_distribution<>& d, std::uniform_real_distribution<>::param_type& p ) = 0; - virtual unsigned long operator()( std::poisson_distribution< unsigned long >& d, - std::poisson_distribution< unsigned long >::param_type& p ) = 0; virtual double operator()( std::normal_distribution<>& d, std::normal_distribution<>::param_type& p ) = 0; virtual double operator()( std::lognormal_distribution<>& d, std::lognormal_distribution<>::param_type& p ) = 0; virtual unsigned long operator()( std::binomial_distribution< unsigned long >& d, @@ -108,6 +106,8 @@ class BaseRandomGenerator virtual double operator()( std::exponential_distribution<>& d, std::exponential_distribution<>::param_type& p ) = 0; virtual unsigned long operator()( std::discrete_distribution< unsigned long >& d, std::discrete_distribution< unsigned long >::param_type& p ) = 0; + virtual unsigned long operator()( std::poisson_distribution< unsigned long >& d, + std::poisson_distribution< unsigned long >::param_type& p ) = 0; /** * @brief Uses the wrapped RNG engine to draw a double from a uniform distribution in the range [0, 1). @@ -121,12 +121,6 @@ class BaseRandomGenerator */ virtual unsigned long ulrand( unsigned long N ) = 0; - /** - * @brief Uses the wrapped RNG engine to draw an unsigned long from a Poisson distribution with mean mu. - * - * @param mu Mean value of Poisson distribution. - */ - virtual unsigned long prand( double mu ) = 0; /** * @brief Wrap std::sample for selection from NodeCollection * @@ -174,12 +168,6 @@ class RandomGenerator final : public BaseRandomGenerator return d( rng_ ); } - inline unsigned long - operator()( std::poisson_distribution< unsigned long >& d ) override - { - return d( rng_ ); - } - inline double operator()( std::normal_distribution<>& d ) override { @@ -215,6 +203,11 @@ class RandomGenerator final : public BaseRandomGenerator { return d( rng_ ); } + inline unsigned long + operator()( std::poisson_distribution< unsigned long >& d ) override + { + return d( rng_ ); + } inline unsigned long operator()( std::uniform_int_distribution< unsigned long >& d, @@ -229,13 +222,6 @@ class RandomGenerator final : public BaseRandomGenerator return d( rng_, p ); } - inline unsigned long - operator()( std::poisson_distribution< unsigned long >& d, - std::poisson_distribution< unsigned long >::param_type& p ) override - { - return d( rng_, p ); - } - inline double operator()( std::normal_distribution<>& d, std::normal_distribution<>::param_type& p ) override { @@ -273,6 +259,13 @@ class RandomGenerator final : public BaseRandomGenerator { return d( rng_, p ); } + + inline unsigned long + operator()( std::poisson_distribution< unsigned long >& d, + std::poisson_distribution< unsigned long >::param_type& p ) override + { + return d( rng_, p ); + } inline double drand() override @@ -287,12 +280,6 @@ class RandomGenerator final : public BaseRandomGenerator return uniform_ulong_dist_( rng_, param ); } - inline unsigned long - prand( double mu ) override - { - std::poisson_distribution< unsigned long >::param_type param( mu ); - return poisson_ulong_dist_( rng_, param ); - } inline void sample( NodeCollection::const_iterator first, NodeCollection::const_iterator last, @@ -306,7 +293,6 @@ class RandomGenerator final : public BaseRandomGenerator RandomEngineT rng_; //!< Wrapped RNG engine. std::uniform_int_distribution< unsigned long > uniform_ulong_dist_; std::uniform_real_distribution<> uniform_double_dist_0_1_; - std::poisson_distribution< unsigned long > poisson_ulong_dist_; }; /** From 4861e9189e8ebcd4696767fe08119141153ed31f Mon Sep 17 00:00:00 2001 From: ackurth Date: Mon, 4 Dec 2023 16:24:17 +0100 Subject: [PATCH 29/52] Fix format --- nestkernel/random_generators.h | 1 + 1 file changed, 1 insertion(+) diff --git a/nestkernel/random_generators.h b/nestkernel/random_generators.h index 15f7278cde..a71108c897 100644 --- a/nestkernel/random_generators.h +++ b/nestkernel/random_generators.h @@ -203,6 +203,7 @@ class RandomGenerator final : public BaseRandomGenerator { return d( rng_ ); } + inline unsigned long operator()( std::poisson_distribution< unsigned long >& d ) override { From 65f2a91a5f2431677bb65d0d44dbb5d0d4facec3 Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Mon, 4 Dec 2023 16:26:47 +0100 Subject: [PATCH 30/52] Update pynest/nest/lib/hl_api_connection_helpers.py Co-authored-by: Hans Ekkehard Plesser --- pynest/nest/lib/hl_api_connection_helpers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pynest/nest/lib/hl_api_connection_helpers.py b/pynest/nest/lib/hl_api_connection_helpers.py index 9a1dafc61c..9a417638a0 100644 --- a/pynest/nest/lib/hl_api_connection_helpers.py +++ b/pynest/nest/lib/hl_api_connection_helpers.py @@ -244,7 +244,6 @@ def _connect_layers_needed(conn_spec, syn_spec): if ( "mask" in conn_spec or ("pairwise_avg_num_conns" in conn_spec and not rule_is_poisson) - or "use_on_source" in conn_spec ): return True # If a syn_spec entry is based on spatial properties, we must use ConnectLayers. From c0065ff8c17dc81c1fc237f3a16c4f5510f53d52 Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Mon, 4 Dec 2023 16:39:17 +0100 Subject: [PATCH 31/52] Update nestkernel/connection_creator_impl.h Co-authored-by: Hans Ekkehard Plesser --- nestkernel/connection_creator_impl.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/nestkernel/connection_creator_impl.h b/nestkernel/connection_creator_impl.h index c56f9bfef9..53ab167a77 100644 --- a/nestkernel/connection_creator_impl.h +++ b/nestkernel/connection_creator_impl.h @@ -135,8 +135,6 @@ ConnectionCreator::connect_to_target_poisson_( Iterator from, poisson_distribution poi_dist; - // Declare number of connections variable - unsigned long num_conns; const bool without_kernel = not kernel_.get(); for ( Iterator iter = from; iter != to; ++iter ) From 2b867868f4f3bbfb66c954fd6d5d5c7b547e027f Mon Sep 17 00:00:00 2001 From: ackurth Date: Mon, 4 Dec 2023 16:41:10 +0100 Subject: [PATCH 32/52] Manually declaring type --- nestkernel/connection_creator_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nestkernel/connection_creator_impl.h b/nestkernel/connection_creator_impl.h index 53ab167a77..0baf03639d 100644 --- a/nestkernel/connection_creator_impl.h +++ b/nestkernel/connection_creator_impl.h @@ -147,7 +147,7 @@ ConnectionCreator::connect_to_target_poisson_( Iterator from, // Sample number of connections that are to be established poisson_distribution::param_type param( kernel_->value( rng, source_pos, target_pos, source, tgt_ptr ) ); - num_conns = poi_dist( rng, param ); + const unsigned long num_conns = poi_dist( rng, param ); if ( without_kernel or num_conns ) { for ( unsigned long conn_counter = 1; conn_counter <= num_conns; ++conn_counter ) From 9af0d30ec25d3d223a5283a4c638e54b25fbaebc Mon Sep 17 00:00:00 2001 From: ackurth Date: Thu, 7 Dec 2023 18:31:52 +0100 Subject: [PATCH 33/52] Fix bug, stick to C++ conventions. --- nestkernel/connection_creator_impl.h | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/nestkernel/connection_creator_impl.h b/nestkernel/connection_creator_impl.h index 0baf03639d..b321640210 100644 --- a/nestkernel/connection_creator_impl.h +++ b/nestkernel/connection_creator_impl.h @@ -148,9 +148,9 @@ ConnectionCreator::connect_to_target_poisson_( Iterator from, // Sample number of connections that are to be established poisson_distribution::param_type param( kernel_->value( rng, source_pos, target_pos, source, tgt_ptr ) ); const unsigned long num_conns = poi_dist( rng, param ); - if ( without_kernel or num_conns ) + if ( num_conns ) { - for ( unsigned long conn_counter = 1; conn_counter <= num_conns; ++conn_counter ) + for ( unsigned long conn_counter = 0; conn_counter < num_conns; ++conn_counter ) { for ( size_t indx = 0; indx < synapse_model_.size(); ++indx ) { @@ -164,6 +164,19 @@ ConnectionCreator::connect_to_target_poisson_( Iterator from, } } } + else if ( without_kernel ) + { + for ( size_t indx = 0; indx < synapse_model_.size(); ++indx ) + { + kernel().connection_manager.connect( iter->second, + tgt_ptr, + tgt_thread, + synapse_model_[ indx ], + param_dicts_[ indx ][ tgt_thread ], + delay_[ indx ]->value( rng, source_pos, target_pos, source, tgt_ptr ), + weight_[ indx ]->value( rng, source_pos, target_pos, source, tgt_ptr ) ); + } + } } } From 1920cdc163f953f6bb4e44f980bb1dcfd62e507d Mon Sep 17 00:00:00 2001 From: ackurth Date: Thu, 7 Dec 2023 18:41:55 +0100 Subject: [PATCH 34/52] Remove sharing info for parallel sections --- nestkernel/connection_creator_impl.h | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/nestkernel/connection_creator_impl.h b/nestkernel/connection_creator_impl.h index b321640210..caa9db25fb 100644 --- a/nestkernel/connection_creator_impl.h +++ b/nestkernel/connection_creator_impl.h @@ -271,9 +271,7 @@ ConnectionCreator::pairwise_bernoulli_on_source_( Layer< D >& source, std::vector< std::shared_ptr< WrappedThreadException > > exceptions_raised_( kernel().vp_manager.get_num_threads() ); -// sharing specs on next line commented out because gcc 4.2 cannot handle them -#pragma omp parallel // default(none) shared(source, target, masked_layer, - // target_begin, target_end) +#pragma omp parallel { const int thread_id = kernel().vp_manager.get_thread_id(); try @@ -357,9 +355,7 @@ ConnectionCreator::pairwise_bernoulli_on_target_( Layer< D >& source, throw IllegalConnection( "Spatial Connect with pairwise_bernoulli to devices is not possible." ); } -// sharing specs on next line commented out because gcc 4.2 cannot handle them -#pragma omp parallel // default(none) shared(source, target, masked_layer, - // target_begin, target_end) +#pragma omp parallel { const int thread_id = kernel().vp_manager.get_thread_id(); try @@ -434,9 +430,7 @@ ConnectionCreator::pairwise_poisson_( Layer< D >& source, std::vector< std::shared_ptr< WrappedThreadException > > exceptions_raised_( kernel().vp_manager.get_num_threads() ); -// sharing specs on next line commented out because gcc 4.2 cannot handle them -#pragma omp parallel // default(none) shared(source, target, masked_layer, - // target_begin, target_end) +#pragma omp parallel { const int thread_id = kernel().vp_manager.get_thread_id(); try From c0a04a1a8b471731387263da6ed4517e210a1824 Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Thu, 7 Dec 2023 18:44:09 +0100 Subject: [PATCH 35/52] Update nestkernel/conn_builder.cpp Co-authored-by: Hans Ekkehard Plesser --- nestkernel/conn_builder.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index 9378f7d7fa..af6c6ac0bc 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -1598,7 +1598,6 @@ nest::PoissonBuilder::PoissonBuilder( NodeCollectionPTR sources, if ( pd ) { pairwise_avg_num_conns_ = *pd; - // TODO: Checks of parameter range } else { From 9d46d7043ae6e82e2244240dedb99d5413fe2011 Mon Sep 17 00:00:00 2001 From: ackurth Date: Fri, 8 Dec 2023 09:47:19 +0100 Subject: [PATCH 36/52] Remove outdated ToDo comment --- nestkernel/conn_builder.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index 9378f7d7fa..af6c6ac0bc 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -1598,7 +1598,6 @@ nest::PoissonBuilder::PoissonBuilder( NodeCollectionPTR sources, if ( pd ) { pairwise_avg_num_conns_ = *pd; - // TODO: Checks of parameter range } else { From a86780f6fc1a0723601a09b0a104afddd7e6195e Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Fri, 8 Dec 2023 09:55:03 +0100 Subject: [PATCH 37/52] Update nestkernel/conn_builder.cpp Co-authored-by: Hans Ekkehard Plesser --- nestkernel/conn_builder.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index af6c6ac0bc..ee8d6524aa 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -1701,11 +1701,7 @@ nest::PoissonBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, s poisson_distribution::param_type param( pairwise_avg_num_conns_->value( rng, target ) ); num_conns = poi_dist( rng, param ); - if ( num_conns == 0 ) - { - continue; - } - for ( unsigned long conn_counter = 1; conn_counter <= num_conns; ++conn_counter ) + for ( size_t n= 0 ; n < num_conns ; ++n ) { single_connect_( snode_id, *target, target_thread, rng ); } From 32f2d1b69dbbf45f0972e3c5b76ed5d2b002a2ee Mon Sep 17 00:00:00 2001 From: ackurth Date: Fri, 8 Dec 2023 10:01:30 +0100 Subject: [PATCH 38/52] Black formatting --- pynest/nest/lib/hl_api_connection_helpers.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pynest/nest/lib/hl_api_connection_helpers.py b/pynest/nest/lib/hl_api_connection_helpers.py index 9a417638a0..3f1e68851f 100644 --- a/pynest/nest/lib/hl_api_connection_helpers.py +++ b/pynest/nest/lib/hl_api_connection_helpers.py @@ -241,10 +241,7 @@ def _connect_layers_needed(conn_spec, syn_spec): if "mask" in conn_spec or ("p" in conn_spec and not rule_is_bernoulli) or "use_on_source" in conn_spec: return True rule_is_poisson = "pairwise_poisson" in str(conn_spec["rule"]) - if ( - "mask" in conn_spec - or ("pairwise_avg_num_conns" in conn_spec and not rule_is_poisson) - ): + if "mask" in conn_spec or ("pairwise_avg_num_conns" in conn_spec and not rule_is_poisson): return True # If a syn_spec entry is based on spatial properties, we must use ConnectLayers. if isinstance(syn_spec, dict): From b8f3bffd282a446d361381d1919b8a53d8cd798c Mon Sep 17 00:00:00 2001 From: ackurth Date: Sat, 9 Dec 2023 18:33:07 +0100 Subject: [PATCH 39/52] Formatting --- nestkernel/random_generators.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nestkernel/random_generators.h b/nestkernel/random_generators.h index a71108c897..b8b470e256 100644 --- a/nestkernel/random_generators.h +++ b/nestkernel/random_generators.h @@ -260,7 +260,7 @@ class RandomGenerator final : public BaseRandomGenerator { return d( rng_, p ); } - + inline unsigned long operator()( std::poisson_distribution< unsigned long >& d, std::poisson_distribution< unsigned long >::param_type& p ) override From 238ff2e3a728a5ab8395bcd517c6d9f59d46071f Mon Sep 17 00:00:00 2001 From: ackurth Date: Sat, 9 Dec 2023 18:34:04 +0100 Subject: [PATCH 40/52] Remove , simply connection routine for poisson connections --- nestkernel/connection_creator_impl.h | 30 +++++++--------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/nestkernel/connection_creator_impl.h b/nestkernel/connection_creator_impl.h index caa9db25fb..b59a8b5dd9 100644 --- a/nestkernel/connection_creator_impl.h +++ b/nestkernel/connection_creator_impl.h @@ -65,6 +65,8 @@ ConnectionCreator::connect( Layer< D >& source, case Pairwise_poisson: + if ( not kernel_ ) throw BadProperty( "pairwise_poisson requires setting a kernel" ); + pairwise_poisson_( source, source_nc, target, target_nc ); break; @@ -90,7 +92,6 @@ ConnectionCreator::connect_to_target_( Iterator from, std::vector< double > source_pos( D ); const std::vector< double > target_pos = tgt_pos.get_vector(); - const bool without_kernel = not kernel_.get(); for ( Iterator iter = from; iter != to; ++iter ) { if ( not allow_autapses_ and ( iter->second == tgt_ptr->get_node_id() ) ) @@ -99,7 +100,7 @@ ConnectionCreator::connect_to_target_( Iterator from, } iter->first.get_vector( source_pos ); - if ( without_kernel or rng->drand() < kernel_->value( rng, source_pos, target_pos, source, tgt_ptr ) ) + if ( not kernel_ or rng->drand() < kernel_->value( rng, source_pos, target_pos, source, tgt_ptr ) ) { for ( size_t indx = 0; indx < synapse_model_.size(); ++indx ) { @@ -136,7 +137,6 @@ ConnectionCreator::connect_to_target_poisson_( Iterator from, poisson_distribution poi_dist; - const bool without_kernel = not kernel_.get(); for ( Iterator iter = from; iter != to; ++iter ) { if ( not allow_autapses_ and ( iter->second == tgt_ptr->get_node_id() ) ) @@ -148,23 +148,7 @@ ConnectionCreator::connect_to_target_poisson_( Iterator from, // Sample number of connections that are to be established poisson_distribution::param_type param( kernel_->value( rng, source_pos, target_pos, source, tgt_ptr ) ); const unsigned long num_conns = poi_dist( rng, param ); - if ( num_conns ) - { - for ( unsigned long conn_counter = 0; conn_counter < num_conns; ++conn_counter ) - { - for ( size_t indx = 0; indx < synapse_model_.size(); ++indx ) - { - kernel().connection_manager.connect( iter->second, - tgt_ptr, - tgt_thread, - synapse_model_[ indx ], - param_dicts_[ indx ][ tgt_thread ], - delay_[ indx ]->value( rng, source_pos, target_pos, source, tgt_ptr ), - weight_[ indx ]->value( rng, source_pos, target_pos, source, tgt_ptr ) ); - } - } - } - else if ( without_kernel ) + for ( unsigned long conn_counter = 0; conn_counter < num_conns; ++conn_counter ) { for ( size_t indx = 0; indx < synapse_model_.size(); ++indx ) { @@ -544,7 +528,7 @@ ConnectionCreator::fixed_indegree_( Layer< D >& source, // If there is no kernel, we can just draw uniform random numbers, // but with a kernel we have to set up a probability distribution // function using a discrete_distribution. - if ( kernel_.get() ) + if ( kernel_ ) { std::vector< double > probabilities; @@ -677,7 +661,7 @@ ConnectionCreator::fixed_indegree_( Layer< D >& source, // If there is no kernel, we can just draw uniform random numbers, // but with a kernel we have to set up a probability distribution // function using a discrete_distribution. - if ( kernel_.get() ) + if ( kernel_ ) { std::vector< double > probabilities; @@ -836,7 +820,7 @@ ConnectionCreator::fixed_outdegree_( Layer< D >& source, std::copy( masked_target.begin( source_pos ), masked_target_end, target_pos_node_id_pairs.begin() ); probabilities.reserve( target_pos_node_id_pairs.size() ); - if ( kernel_.get() ) + if ( kernel_ ) { for ( const auto& target_pos_node_id_pair : target_pos_node_id_pairs ) { From 95f7f89cc001232d81593ac43c9321f199c4fa74 Mon Sep 17 00:00:00 2001 From: ackurth Date: Sat, 9 Dec 2023 19:06:52 +0100 Subject: [PATCH 41/52] Fix formating --- nestkernel/conn_builder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index ee8d6524aa..d83c5db627 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -1701,7 +1701,7 @@ nest::PoissonBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, s poisson_distribution::param_type param( pairwise_avg_num_conns_->value( rng, target ) ); num_conns = poi_dist( rng, param ); - for ( size_t n= 0 ; n < num_conns ; ++n ) + for ( size_t n = 0; n < num_conns; ++n ) { single_connect_( snode_id, *target, target_thread, rng ); } From 9d9ed89975f27719ad579b2d3fb8cb1a3c173b2e Mon Sep 17 00:00:00 2001 From: ackurth Date: Sat, 9 Dec 2023 19:07:41 +0100 Subject: [PATCH 42/52] Remove unecessary check --- nestkernel/connection_creator_impl.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/nestkernel/connection_creator_impl.h b/nestkernel/connection_creator_impl.h index b59a8b5dd9..dfa2f456dd 100644 --- a/nestkernel/connection_creator_impl.h +++ b/nestkernel/connection_creator_impl.h @@ -65,8 +65,6 @@ ConnectionCreator::connect( Layer< D >& source, case Pairwise_poisson: - if ( not kernel_ ) throw BadProperty( "pairwise_poisson requires setting a kernel" ); - pairwise_poisson_( source, source_nc, target, target_nc ); break; From a77da2693df57b2b916853fc969bac2e9bf4c90c Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Tue, 2 Jan 2024 19:27:40 +0100 Subject: [PATCH 43/52] Update nestkernel/conn_builder.cpp Co-authored-by: Hans Ekkehard Plesser --- nestkernel/conn_builder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index d83c5db627..2bd0806669 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -1636,7 +1636,7 @@ nest::PoissonBuilder::connect_() Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); if ( target->is_proxy() ) { - // skip array parameters handled in other virtual processes + // skip parameters handled in other virtual processes skip_conn_parameter_( tid ); continue; } From 79a0eb23eb5fb9949faec36145a2c72c3fe8e752 Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Tue, 2 Jan 2024 19:28:11 +0100 Subject: [PATCH 44/52] Update nestkernel/conn_builder.cpp Co-authored-by: Hans Ekkehard Plesser --- nestkernel/conn_builder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index 2bd0806669..3d63c39c00 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -1605,7 +1605,7 @@ nest::PoissonBuilder::PoissonBuilder( NodeCollectionPTR sources, const double value = ( *conn_spec )[ names::pairwise_avg_num_conns ]; if ( value < 0 ) { - throw BadProperty( "Connection parameter 0 <= pairwise_avg_num_conns required." ); + throw BadProperty( "Connection parameter 0 ≤ pairwise_avg_num_conns required." ); } if ( not allow_multapses_ ) { From 2af3b80587a90e92ac46d40d15cef1215fafc491 Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Tue, 2 Jan 2024 19:28:34 +0100 Subject: [PATCH 45/52] Update nestkernel/connection_creator_impl.h Co-authored-by: Hans Ekkehard Plesser --- nestkernel/connection_creator_impl.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/nestkernel/connection_creator_impl.h b/nestkernel/connection_creator_impl.h index dfa2f456dd..83a89a2413 100644 --- a/nestkernel/connection_creator_impl.h +++ b/nestkernel/connection_creator_impl.h @@ -131,10 +131,8 @@ ConnectionCreator::connect_to_target_poisson_( Iterator from, std::vector< double > source_pos( D ); const std::vector< double > target_pos = tgt_pos.get_vector(); - poisson_distribution poi_dist; - for ( Iterator iter = from; iter != to; ++iter ) { if ( not allow_autapses_ and ( iter->second == tgt_ptr->get_node_id() ) ) From 4cce885a7311e1412986aac0c9bc185ca51b0837 Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Tue, 2 Jan 2024 19:28:52 +0100 Subject: [PATCH 46/52] Update doc/htmldoc/synapses/connection_management.rst Co-authored-by: Hans Ekkehard Plesser --- doc/htmldoc/synapses/connection_management.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/htmldoc/synapses/connection_management.rst b/doc/htmldoc/synapses/connection_management.rst index 4cc6b43793..3ddffcae14 100644 --- a/doc/htmldoc/synapses/connection_management.rst +++ b/doc/htmldoc/synapses/connection_management.rst @@ -257,7 +257,7 @@ must be ``True``. nest.Connect(A, B, conn_spec_dict) pairwise poisson -~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~ For each possible pair of nodes from ``A`` and ``B``, a number of connections is created following a Poisson distribution with mean From 58a5a8ddc09fd03fd7ba1c2dd5ea0ad627adf596 Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Tue, 2 Jan 2024 19:30:46 +0100 Subject: [PATCH 47/52] Update nestkernel/conn_builder.h Co-authored-by: Hans Ekkehard Plesser --- nestkernel/conn_builder.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/nestkernel/conn_builder.h b/nestkernel/conn_builder.h index 0f7b850be8..c5edfd2ccb 100644 --- a/nestkernel/conn_builder.h +++ b/nestkernel/conn_builder.h @@ -527,8 +527,6 @@ class TripartiteBernoulliWithPoolBuilder : public ConnBuilder void connect_() override; private: - void inner_connect_( const int, RngPtr, Node*, size_t ); - ParameterDatum pairwise_avg_num_conns_; //!< Mean number of connections //! Provide index of first third-party node to be assigned to pool for given target node size_t get_first_pool_index_( const size_t target_index ) const; From 686699ff0c456343c1b1f39c35b8f9b5c967c9ab Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Tue, 2 Jan 2024 19:31:00 +0100 Subject: [PATCH 48/52] Update nestkernel/conn_builder.cpp Co-authored-by: Hans Ekkehard Plesser --- nestkernel/conn_builder.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index 3d63c39c00..006b2ffc45 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -1644,7 +1644,6 @@ nest::PoissonBuilder::connect_() inner_connect_( tid, rng, target, tnode_id ); } } - else { const SparseNodeArray& local_nodes = kernel().node_manager.get_local_nodes( tid ); From 0054a98256159614c0925676b3869808251054c0 Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Tue, 2 Jan 2024 19:32:11 +0100 Subject: [PATCH 49/52] Update nestkernel/conn_builder.cpp Co-authored-by: Hans Ekkehard Plesser --- nestkernel/conn_builder.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index 006b2ffc45..89bfb9c395 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -1675,8 +1675,6 @@ nest::PoissonBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, s { const size_t target_thread = target->get_thread(); - unsigned long num_conns; - // check whether the target is on our thread if ( static_cast< size_t >( tid ) != target_thread ) { @@ -1698,7 +1696,7 @@ nest::PoissonBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, s // Sample to number of connections that are to be established poisson_distribution::param_type param( pairwise_avg_num_conns_->value( rng, target ) ); - num_conns = poi_dist( rng, param ); + const size_t num_conns = poi_dist( rng, param ); for ( size_t n = 0; n < num_conns; ++n ) { From 6ac755d0d827fdf1e374e77dc03a6d5812e3b2d9 Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Tue, 2 Jan 2024 19:32:33 +0100 Subject: [PATCH 50/52] Update nestkernel/connection_creator_impl.h Co-authored-by: Hans Ekkehard Plesser --- nestkernel/connection_creator_impl.h | 1 - 1 file changed, 1 deletion(-) diff --git a/nestkernel/connection_creator_impl.h b/nestkernel/connection_creator_impl.h index 83a89a2413..7b28a80757 100644 --- a/nestkernel/connection_creator_impl.h +++ b/nestkernel/connection_creator_impl.h @@ -130,7 +130,6 @@ ConnectionCreator::connect_to_target_poisson_( Iterator from, // unnecessarily many vectors. std::vector< double > source_pos( D ); const std::vector< double > target_pos = tgt_pos.get_vector(); - poisson_distribution poi_dist; for ( Iterator iter = from; iter != to; ++iter ) From 5f2ccfe8e8a1c33537c6cf3d4c2ee2d9923de0de Mon Sep 17 00:00:00 2001 From: Anno Christopher Kurth <44397333+ackurth@users.noreply.github.com> Date: Tue, 2 Jan 2024 19:32:44 +0100 Subject: [PATCH 51/52] Update testsuite/pytests/test_spatial/test_connect_layers.py Co-authored-by: Hans Ekkehard Plesser --- testsuite/pytests/test_spatial/test_connect_layers.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/testsuite/pytests/test_spatial/test_connect_layers.py b/testsuite/pytests/test_spatial/test_connect_layers.py index cfb81ef2d0..219844d829 100644 --- a/testsuite/pytests/test_spatial/test_connect_layers.py +++ b/testsuite/pytests/test_spatial/test_connect_layers.py @@ -278,16 +278,6 @@ def test_connect_layers_bernoulli_mask(self): } self._check_connections(conn_spec, 108) - def test_connect_layers_poisson_mask(self): - """Connecting layers with pairwise_poisson and mask""" - conn_spec = { - "rule": "pairwise_poisson", - "pairwise_avg_num_conns": 0.5, - "mask": {"rectangular": {"lower_left": [-5.0, -5.0], "upper_right": [0.1, 0.1]}}, - } - conns = self._check_connections(conn_spec, None, return_conns=True) - np.testing.assert_allclose(54, len(conns), atol=5) - @unittest.skipIf(not HAVE_SCIPY, "SciPy package is not available") def test_connect_layers_bernoulli_kernel_mask(self): """Connecting layers with pairwise_bernoulli, kernel and mask""" From 995f22d40bbbd7d5ae454dfc50a1f248a7c5a341 Mon Sep 17 00:00:00 2001 From: ackurth Date: Tue, 2 Jan 2024 19:46:05 +0100 Subject: [PATCH 52/52] Remove case distinction when chosing connection types --- nestkernel/connection_creator.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/nestkernel/connection_creator.cpp b/nestkernel/connection_creator.cpp index 4d1319bc88..21b94e3153 100644 --- a/nestkernel/connection_creator.cpp +++ b/nestkernel/connection_creator.cpp @@ -137,14 +137,7 @@ ConnectionCreator::ConnectionCreator( DictionaryDatum dict ) } else if ( connection_type == names::pairwise_poisson ) { - if ( dict->known( names::number_of_connections ) ) - { - type_ = Fixed_indegree; - } - else - { - type_ = Pairwise_poisson; - } + type_ = Pairwise_poisson; } else if ( connection_type == names::pairwise_bernoulli_on_target ) {