From 72c7b88aa242f749db8bd9705458c8e7ad3d31a4 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Thu, 22 Feb 2024 15:59:05 +0100 Subject: [PATCH 01/86] Experimental work on new tripartite implementation; does not yet compile --- nestkernel/conn_builder.cpp | 2520 +++++++++++++++-------------- nestkernel/conn_builder.h | 235 ++- nestkernel/conn_builder_factory.h | 16 +- nestkernel/conn_builder_impl.h | 2 +- nestkernel/connection_manager.cpp | 53 +- nestkernel/connection_manager.h | 4 +- nestkernel/sp_manager.cpp | 2 +- 7 files changed, 1524 insertions(+), 1308 deletions(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index a67b0b95a5..0066a35b93 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -42,12 +42,88 @@ // Includes from C++: #include -nest::ConnBuilder::ConnBuilder( NodeCollectionPTR sources, + +nest::ConnBuilder::ConnBuilder( const std::string& primary_rule, + NodeCollectionPTR sources, + NodeCollectionPTR targets, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : third_out_builder_( nullptr ) + , + , third_in_builder_() + , primary_builder_( get_conn_builder( rule_name, + sources, + targets, + third_out_builder_, + conn_spec, + const_cast< std::map< Name, std::vector< DictionaryDatum > >& >( syn_specs )[ names::primary ] ) ) +{ +} + +nest::ConnBuilder::ConnBuilder( const std::string& primary_rule, + const std::string& third_rule, + NodeCollectionPTR sources, + NodeCollectionPTR targets, + NodeCollectionPTR third, + const DictionaryDatum& conn_spec, + const DictionaryDatum& third_conn_spec, + const std::map< Name, std::vector< DictionaryDatum > >& syn_specs ) + : third_out_builder_( get_third_conn_builder( third_rule, + targets, + third, + third_conn_spec, + // const_cast here seems required, clang complains otherwise; try to clean up when Datums disappear + const_cast< std::map< Name, std::vector< DictionaryDatum > >& >( syn_specs )[ names::third_out ] ) ) + , third_in_builder_( sources, + third, + *third_out_builder_, + third_conn_spec, + const_cast< std::map< Name, std::vector< DictionaryDatum > >& >( syn_specs )[ names::third_in ] ) + , primary_builder_( get_conn_builder( rule_name, + sources, + targets, + third_out_builder_, + conn_spec, + const_cast< std::map< Name, std::vector< DictionaryDatum > >& >( syn_specs )[ names::primary ] ) ) +{ +} + +nest::ConnBuilder::~ConnBuilder() +{ + delete primary_builder_; // fully constructed CB has one + + if ( third_out_builder_ ) + { + delete third_out_builder_; + } +} + +void +nest::ConnBuilder::connect() +{ + primary_builder_->connect(); // triggers third_out_builder_ + third_in_builder_.connect(); +} + +void +nest::ConnBuilder::disconnect() +{ + if ( third_out_builder_ ) + { + throw KernelException( "Disconnect is not supported for connections with third factor." ); + } + primary_builder_->disconnect(); +} + + +nest::BipartiteConnBuilder::BipartiteConnBuilder( NodeCollectionPTR sources, NodeCollectionPTR targets, const DictionaryDatum& conn_spec, + ThirdOutBuilder* third_out_, const std::vector< DictionaryDatum >& syn_specs ) : sources_( sources ) , targets_( targets ) + , third_out_( third_out ) , allow_autapses_( true ) , allow_multapses_( true ) , make_symmetric_( false ) @@ -121,7 +197,7 @@ nest::ConnBuilder::ConnBuilder( NodeCollectionPTR sources, } } -nest::ConnBuilder::~ConnBuilder() +nest::BipartiteConnBuilder::~BipartiteConnBuilder() { for ( auto weight : weights_ ) { @@ -143,7 +219,10 @@ nest::ConnBuilder::~ConnBuilder() } bool -nest::ConnBuilder::change_connected_synaptic_elements( size_t snode_id, size_t tnode_id, const size_t tid, int update ) +nest::BipartiteConnBuilder::change_connected_synaptic_elements( size_t snode_id, + size_t tnode_id, + const size_t tid, + int update ) { int local = true; @@ -186,7 +265,7 @@ nest::ConnBuilder::change_connected_synaptic_elements( size_t snode_id, size_t t } void -nest::ConnBuilder::connect() +nest::BipartiteConnBuilder::connect() { // We test here, and not in the ConnBuilder constructor, so the derived // classes are fully constructed when the test is executed @@ -252,7 +331,7 @@ nest::ConnBuilder::connect() } void -nest::ConnBuilder::disconnect() +nest::BipartiteConnBuilder::disconnect() { if ( use_structural_plasticity_ ) { @@ -274,7 +353,7 @@ nest::ConnBuilder::disconnect() } void -nest::ConnBuilder::update_param_dict_( size_t snode_id, +nest::BipartiteConnBuilder::update_param_dict_( size_t snode_id, Node& target, size_t target_thread, RngPtr rng, @@ -302,7 +381,7 @@ nest::ConnBuilder::update_param_dict_( size_t snode_id, } void -nest::ConnBuilder::single_connect_( size_t snode_id, Node& target, size_t target_thread, RngPtr rng ) +nest::BipartiteConnBuilder::single_connect_( size_t snode_id, Node& target, size_t target_thread, RngPtr rng ) { if ( this->requires_proxies() and not target.has_proxies() ) { @@ -353,10 +432,16 @@ nest::ConnBuilder::single_connect_( size_t snode_id, Node& target, size_t target weight ); } } + + // We connect third-party only once per source-target pair, not per collocated synapse type + if ( third_out_ ) + { + third_out_->third_connect( snode_id, target ); + } } void -nest::ConnBuilder::set_synaptic_element_names( const std::string& pre_name, const std::string& post_name ) +nest::BipartiteConnBuilder::set_synaptic_element_names( const std::string& pre_name, const std::string& post_name ) { if ( pre_name.empty() or post_name.empty() ) { @@ -370,7 +455,7 @@ nest::ConnBuilder::set_synaptic_element_names( const std::string& pre_name, cons } bool -nest::ConnBuilder::all_parameters_scalar_() const +nest::BipartiteConnBuilder::all_parameters_scalar_() const { bool all_scalar = true; @@ -402,14 +487,14 @@ nest::ConnBuilder::all_parameters_scalar_() const } bool -nest::ConnBuilder::loop_over_targets_() const +nest::BipartiteConnBuilder::loop_over_targets_() const { return targets_->size() < kernel().node_manager.size() or not targets_->is_range() or parameters_requiring_skipping_.size() > 0; } void -nest::ConnBuilder::set_synapse_model_( DictionaryDatum syn_params, size_t synapse_indx ) +nest::BipartiteConnBuilder::set_synapse_model_( DictionaryDatum syn_params, size_t synapse_indx ) { if ( not syn_params->known( names::synapse_model ) ) { @@ -427,7 +512,7 @@ nest::ConnBuilder::set_synapse_model_( DictionaryDatum syn_params, size_t synaps } void -nest::ConnBuilder::set_default_weight_or_delay_( DictionaryDatum syn_params, size_t synapse_indx ) +nest::BipartiteConnBuilder::set_default_weight_or_delay_( DictionaryDatum syn_params, size_t synapse_indx ) { DictionaryDatum syn_defaults = kernel().model_manager.get_connector_defaults( synapse_model_id_[ synapse_indx ] ); @@ -463,7 +548,9 @@ nest::ConnBuilder::set_default_weight_or_delay_( DictionaryDatum syn_params, siz } void -nest::ConnBuilder::set_synapse_params( DictionaryDatum syn_defaults, DictionaryDatum syn_params, size_t synapse_indx ) +nest::BipartiteConnBuilder::set_synapse_params( DictionaryDatum syn_defaults, + DictionaryDatum syn_params, + size_t synapse_indx ) { for ( Dictionary::const_iterator default_it = syn_defaults->begin(); default_it != syn_defaults->end(); ++default_it ) { @@ -502,7 +589,7 @@ nest::ConnBuilder::set_synapse_params( DictionaryDatum syn_defaults, DictionaryD } void -nest::ConnBuilder::set_structural_plasticity_parameters( std::vector< DictionaryDatum > syn_specs ) +nest::BipartiteConnBuilder::set_structural_plasticity_parameters( std::vector< DictionaryDatum > syn_specs ) { bool have_structural_plasticity_parameters = false; for ( auto& syn_spec : syn_specs ) @@ -535,7 +622,7 @@ nest::ConnBuilder::set_structural_plasticity_parameters( std::vector< Dictionary } void -nest::ConnBuilder::reset_weights_() +nest::BipartiteConnBuilder::reset_weights_() { for ( auto weight : weights_ ) { @@ -547,7 +634,7 @@ nest::ConnBuilder::reset_weights_() } void -nest::ConnBuilder::reset_delays_() +nest::BipartiteConnBuilder::reset_delays_() { for ( auto delay : delays_ ) { @@ -558,740 +645,582 @@ nest::ConnBuilder::reset_delays_() } } -nest::OneToOneBuilder::OneToOneBuilder( const NodeCollectionPTR sources, +nest::ThirdInBuilder::ThirdInBuilder( const NodeCollectionPTR sources, + const NodeCollectionPTR third, + BipartiteConnBuilder* third_out, + const DictionaryDatum& third_conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : BipartiteConnBuilder( sources, third, nullptr, third_conn_spec, syn_specs ) + , third_out_( third_out ) +{ +} + +void +nest::ThirdInBuilder::connect_() +{ + std::cout << "not implemented yet" << std::endl; +} + +nest::ThirdOutBuilder::ThirdOutBuilder( const NodeCollectionPTR third, const NodeCollectionPTR targets, - const DictionaryDatum& conn_spec, + const DictionaryDatum& third_conn_spec, const std::vector< DictionaryDatum >& syn_specs ) - : ConnBuilder( sources, targets, conn_spec, syn_specs ) + : BipartiteConnBuilder( third, targets, nullptr, third_conn_spec, syn_specs ) { - // make sure that target and source population have the same size - if ( sources_->size() != targets_->size() ) - { - throw DimensionMismatch( "Source and Target population must be of the same size." ); - } } void -nest::OneToOneBuilder::connect_() +nest::ThirdOutBuilder::connect() { + assert( false ); // should never be called +} -#pragma omp parallel +nest::ThirdBernoulliWithPoolBuilder::ThirdBernoulliWithPoolBuilder( const NodeCollectionPTR third, + const NodeCollectionPTR targets, + const DictionaryDatum& third_conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : ThirdOutBuilder( third, targets, nullptr, third_conn_spec, syn_specs ) + , p_( 1.0 ) + , random_pool_( true ) + , pool_size_( third->size() ) + , targets_per_third_( targets->size() / third->size() ) + , + , previous_target_( kernel().vp_manager.get_num_threads(), nullptr ) +{ + updateValue< double >( conn_spec, names::p, p_ ); + updateValue< long >( conn_spec, names::pool_size, pool_size_ ); + std::string pool_type; + if ( updateValue< std::string >( conn_spec, names::pool_type, pool_type ) ) { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); - - try + if ( pool_type == "random" ) { - RngPtr rng = get_vp_specific_rng( tid ); - - if ( loop_over_targets_() ) - { - // A more efficient way of doing this might be to use NodeCollection's local_begin(). For this to work we would - // need to change some of the logic, sources and targets might not be on the same process etc., so therefore - // we are not doing it at the moment. This also applies to other ConnBuilders below. - NodeCollection::const_iterator target_it = targets_->begin(); - NodeCollection::const_iterator source_it = sources_->begin(); - for ( ; target_it < targets_->end(); ++target_it, ++source_it ) - { - assert( source_it < sources_->end() ); - - const size_t snode_id = ( *source_it ).node_id; - const size_t tnode_id = ( *target_it ).node_id; - - if ( snode_id == tnode_id and not allow_autapses_ ) - { - continue; - } + random_pool_ = true; + } + else if ( pool_type == "block" ) + { + random_pool_ = false; + } + else + { + throw BadProperty( "pool_type must be 'random' or 'block'" ); + } + } - 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; - } + if ( p_ < 0 or 1 < p_ ) + { + throw BadProperty( "Conditional probability of third-factor connection 0 ≤ p_third_if_primary ≤ 1 required" ); + } - single_connect_( snode_id, *target, tid, rng ); - } - } - 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 ) - { - Node* target = n->get_node(); + if ( pool_size_ < 1 or third->size() < pool_size_ ) + { + throw BadProperty( "Pool size 1 ≤ pool_size ≤ size of third-factor population required" ); + } - const size_t tnode_id = n->get_node_id(); - const long lid = targets_->get_lid( tnode_id ); - if ( lid < 0 ) // Is local node in target list? - { - continue; - } + if ( not( random_pool_ or ( targets->size() * pool_size_ == third->size() ) + or ( pool_size_ == 1 and targets->size() % third->size() == 0 ) ) ) + { + throw BadProperty( + "The sizes of target and third-factor populations and the chosen pool size do not fit." + " If pool_size == 1, the target population size must be a multiple of the third-factor" + " population size. For pool_size > 1, size(targets) * pool_size == size(third factor)" + " is required. For all other cases, use random pools." ); + } - // one-to-one, thus we can use target idx for source as well - const size_t snode_id = ( *sources_ )[ lid ]; - if ( not allow_autapses_ and snode_id == tnode_id ) - { - // no skipping required / possible, - // as we iterate only over local nodes - continue; - } - single_connect_( snode_id, *target, tid, rng ); - } - } - } - 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 ) ); - } +#pragma omp parallel + { + const size_t thrd = kernel().vp_manager.get_thread_id(); + pool_[ thrd ] = new BlockVector< NodeIDTriple >(); + source_third_gids_[ thrd ] = new BlockVector< SourceThirdInfo_ >(); + source_third_counts_[ thrd ] = new std::vector< size_t >( KernelManager().mpi_manager.get_num_processes(), 0 ); } } -void -nest::OneToOneBuilder::disconnect_() +void nest::ThirdBernoulliWithPoolBuilder::third_connect( < #size_t source_gid # >, < #Node & target # > ) { + // We assume target is on this thread + const size_t tid = kernel().vp_manager.get_thread_id(); + RngPtr rng = get_vp_specific_rng( tid ); -#pragma omp parallel + + // conditionally connect third factor + if ( not( rng->drand() < p_ ) ) { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); + return; + } - try + // step 2, build pool if new target + if ( &target != previous_target_[ tid ] ) + { + pool_[ tid ]->reserve( pool_size_ ); + if ( random_pool_ ) { - NodeCollection::const_iterator target_it = targets_->begin(); - NodeCollection::const_iterator source_it = sources_->begin(); - for ( ; target_it < targets_->end(); ++target_it, ++source_it ) - { - assert( source_it < sources_->end() ); + rng->sample( third_->begin(), third_->end(), std::back_inserter( *pool_[ tid ] ), pool_size_ ); + } + else + { + std::copy_n( + third_->begin() + get_first_pool_index_( target.lid ), pool_size_, std::back_inserter( *pool[ tid ] ) ); + } - const size_t tnode_id = ( *target_it ).node_id; - const size_t snode_id = ( *source_it ).node_id; + // select third-factor neuron randomly from pool for this target + const auto third_index = pool_size_ == 1 ? 0 : rng->ulrand( pool_size_ ); + const auto third_node_id = ( *pool_[ tid ] )[ third_index ].node_id; + const auto third_node_rank = kernel().mpi_manager.get_process_id_of_vp( kernel().vp_manager.node_id_to_vp( third_node_id ); - // check whether the target is on this mpi machine - if ( not kernel().node_manager.is_local_node_id( tnode_id ) ) - { - // Disconnecting: no parameter skipping required - continue; - } + single_connect_( third_node_id, target, tid, rng ); - Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); - const size_t target_thread = target->get_thread(); + source_third_gids_[ tid ]->emplace_back( { snode_id, third_node_id, third_node_rank } ); + ++((*source_third_counts_[tid])[third_node_rank]); + } - // check whether the target is a proxy - if ( target->is_proxy() ) - { - // Disconnecting: no parameter skipping required - continue; - } - single_disconnect_( snode_id, *target, target_thread ); - } - } - catch ( std::exception& err ) + + size_t nest::ThirdBernoulliWithPoolBuilder::get_first_pool_index_( const size_t target_index ) const + { + if ( pool_size_ > 1 ) { - // 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 ) ); + return target_index * pool_size_; } - } -} -void -nest::OneToOneBuilder::sp_connect_() -{ + return target_index / targets_per_third_; // intentional integer division + } -#pragma omp parallel + void nest::TripartiteBernoulliWithPoolBuilder::connect_() { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); - - try +#pragma omp parallel { - RngPtr rng = get_vp_specific_rng( tid ); + const size_t tid = kernel().vp_manager.get_thread_id(); - NodeCollection::const_iterator target_it = targets_->begin(); - NodeCollection::const_iterator source_it = sources_->begin(); - for ( ; target_it < targets_->end(); ++target_it, ++source_it ) + try { - assert( source_it < sources_->end() ); + /* Random number generators: + * - Use RNG generating same number sequence on all threads to decide which connections to create + * - Use per-thread random number generator to randomize connection properties + */ + RngPtr synced_rng = get_vp_synced_rng( tid ); + RngPtr rng = get_vp_specific_rng( tid ); - const size_t snode_id = ( *source_it ).node_id; - const size_t tnode_id = ( *target_it ).node_id; + binomial_distribution bino_dist; + binomial_distribution::param_type bino_param( sources_->size(), p_primary_ ); - if ( snode_id == tnode_id and not allow_autapses_ ) + // Iterate through target neurons. For each, three steps are done: + // 1. draw indegree 2. select astrocyte pool 3. make connections + for ( const auto& target : *targets_ ) { - continue; - } + const size_t tnode_id = target.node_id; + Node* target_node = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); + const bool local_target = not target_node->is_proxy(); - if ( not change_connected_synaptic_elements( snode_id, tnode_id, tid, 1 ) ) - { - skip_conn_parameter_( tid ); - continue; - } - Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); - const size_t target_thread = target->get_thread(); + // step 1, draw indegree for this target + const auto indegree = bino_dist( synced_rng, bino_param ); + if ( indegree == 0 ) + { + continue; // no connections for this target + } - single_connect_( snode_id, *target, target_thread, rng ); + + // step 3, iterate through indegree to make connections for this target + // - by construction, we cannot get multapses + // - if the target is also among sources, it can be drawn at most once; + // we ignore it then connecting if no autapses are wanted + std::vector< NodeIDTriple > sources_to_connect_; + sources_to_connect_.reserve( indegree ); + synced_rng->sample( sources_->begin(), sources_->end(), std::back_inserter( sources_to_connect_ ), indegree ); + + for ( const auto source : sources_to_connect_ ) + { + const auto snode_id = source.node_id; + if ( not allow_autapses_ and snode_id == tnode_id ) + { + continue; + } + + if ( local_target ) + { + // plain connect now with thread-local rng for randomized parameters + single_connect_( snode_id, *target_node, tid, rng ); + } + + // conditionally connect third factor + if ( not( synced_rng->drand() < p_third_if_primary_ ) ) + { + continue; + } + + // select third-factor neuron randomly from pool for this target + const auto third_index = pool_size_ == 1 ? 0 : synced_rng->ulrand( pool_size_ ); + const auto third_node_id = pool[ third_index ].node_id; + Node* third_node = kernel().node_manager.get_node_or_proxy( third_node_id, tid ); + const bool local_third_node = not third_node->is_proxy(); + + if ( local_third_node ) + { + // route via auxiliary builder who handles parameters + third_in_builder_.single_connect( snode_id, *third_node, tid, rng ); + } + + // connection third-factor node to target if local + if ( local_target ) + { + // route via auxiliary builder who handles parameters + third_out_builder_.single_connect( third_node_id, *target_node, tid, rng ); + } + } + } + } + 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 ) ); } } - catch ( std::exception& err ) + } + + + nest::OneToOneBuilder::OneToOneBuilder( const NodeCollectionPTR sources, + const NodeCollectionPTR targets, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : BipartiteConnBuilder( sources, targets, conn_spec, syn_specs ) + { + // make sure that target and source population have the same size + if ( sources_->size() != targets_->size() ) { - // 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 ) ); + throw DimensionMismatch( "Source and Target population must be of the same size." ); } } -} - -void -nest::OneToOneBuilder::sp_disconnect_() -{ -#pragma omp parallel + void nest::OneToOneBuilder::connect_() { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); - try +#pragma omp parallel { - NodeCollection::const_iterator target_it = targets_->begin(); - NodeCollection::const_iterator source_it = sources_->begin(); - for ( ; target_it < targets_->end(); ++target_it, ++source_it ) - { - assert( source_it < sources_->end() ); + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); - const size_t snode_id = ( *source_it ).node_id; - const size_t tnode_id = ( *target_it ).node_id; + try + { + RngPtr rng = get_vp_specific_rng( tid ); - if ( not change_connected_synaptic_elements( snode_id, tnode_id, tid, -1 ) ) + if ( loop_over_targets_() ) { - continue; - } + // A more efficient way of doing this might be to use NodeCollection's local_begin(). For this to work we + // would need to change some of the logic, sources and targets might not be on the same process etc., so + // therefore we are not doing it at the moment. This also applies to other ConnBuilders below. + NodeCollection::const_iterator target_it = targets_->begin(); + NodeCollection::const_iterator source_it = sources_->begin(); + for ( ; target_it < targets_->end(); ++target_it, ++source_it ) + { + assert( source_it < sources_->end() ); + + const size_t snode_id = ( *source_it ).node_id; + const size_t tnode_id = ( *target_it ).node_id; - Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); - const size_t target_thread = target->get_thread(); + if ( snode_id == tnode_id and not allow_autapses_ ) + { + continue; + } - single_disconnect_( snode_id, *target, target_thread ); + 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; + } + + single_connect_( snode_id, *target, tid, rng ); + } + } + 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 ) + { + Node* target = n->get_node(); + + const size_t tnode_id = n->get_node_id(); + const long lid = targets_->get_lid( tnode_id ); + if ( lid < 0 ) // Is local node in target list? + { + continue; + } + + // one-to-one, thus we can use target idx for source as well + const size_t snode_id = ( *sources_ )[ lid ]; + if ( not allow_autapses_ and snode_id == tnode_id ) + { + // no skipping required / possible, + // as we iterate only over local nodes + continue; + } + single_connect_( snode_id, *target, tid, rng ); + } + } + } + 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 ) ); } - } - 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 ) ); } } -} - -void -nest::AllToAllBuilder::connect_() -{ -#pragma omp parallel + void nest::OneToOneBuilder::disconnect_() { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); - try +#pragma omp parallel { - RngPtr rng = get_vp_specific_rng( tid ); + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); - if ( loop_over_targets_() ) + try { NodeCollection::const_iterator target_it = targets_->begin(); - for ( ; target_it < targets_->end(); ++target_it ) + NodeCollection::const_iterator source_it = sources_->begin(); + for ( ; target_it < targets_->end(); ++target_it, ++source_it ) { + assert( source_it < sources_->end() ); + 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() ) + const size_t snode_id = ( *source_it ).node_id; + + // check whether the target is on this mpi machine + if ( not kernel().node_manager.is_local_node_id( tnode_id ) ) { - skip_conn_parameter_( tid, sources_->size() ); + // Disconnecting: no parameter skipping required continue; } - inner_connect_( tid, rng, target, tnode_id, true ); - } - } - 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(); + Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); + const size_t target_thread = target->get_thread(); - // Is the local node in the targets list? - if ( targets_->get_lid( tnode_id ) < 0 ) + // check whether the target is a proxy + if ( target->is_proxy() ) { + // Disconnecting: no parameter skipping required continue; } - - inner_connect_( tid, rng, n->get_node(), tnode_id, false ); + single_disconnect_( snode_id, *target, target_thread ); } } - } - 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 ) ); - } - } -} - -void -nest::AllToAllBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, size_t tnode_id, bool skip ) -{ - const size_t target_thread = target->get_thread(); - - // check whether the target is on our thread - if ( static_cast< size_t >( tid ) != target_thread ) - { - if ( skip ) - { - skip_conn_parameter_( tid, sources_->size() ); - } - return; - } - - 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 ) - { - if ( skip ) + catch ( std::exception& err ) { - skip_conn_parameter_( target_thread ); + // 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 ) ); } - continue; } - - single_connect_( snode_id, *target, target_thread, rng ); } -} -void -nest::AllToAllBuilder::sp_connect_() -{ -#pragma omp parallel + void nest::OneToOneBuilder::sp_connect_() { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); - try + +#pragma omp parallel { - RngPtr rng = get_vp_specific_rng( tid ); + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); - NodeCollection::const_iterator target_it = targets_->begin(); - for ( ; target_it < targets_->end(); ++target_it ) + try { - const size_t tnode_id = ( *target_it ).node_id; + RngPtr rng = get_vp_specific_rng( tid ); + NodeCollection::const_iterator target_it = targets_->begin(); NodeCollection::const_iterator source_it = sources_->begin(); - for ( ; source_it < sources_->end(); ++source_it ) + for ( ; target_it < targets_->end(); ++target_it, ++source_it ) { + assert( source_it < sources_->end() ); + const size_t snode_id = ( *source_it ).node_id; + const size_t tnode_id = ( *target_it ).node_id; - if ( not allow_autapses_ and snode_id == tnode_id ) + if ( snode_id == tnode_id and not allow_autapses_ ) { - skip_conn_parameter_( tid ); continue; } + if ( not change_connected_synaptic_elements( snode_id, tnode_id, tid, 1 ) ) { - skip_conn_parameter_( tid, sources_->size() ); + skip_conn_parameter_( tid ); continue; } Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); const size_t target_thread = target->get_thread(); + single_connect_( snode_id, *target, target_thread, rng ); } } - } - 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 ) ); - } - } -} - -void -nest::AllToAllBuilder::disconnect_() -{ - -#pragma omp parallel - { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); - - try - { - NodeCollection::const_iterator target_it = targets_->begin(); - for ( ; target_it < targets_->end(); ++target_it ) + catch ( std::exception& err ) { - const size_t tnode_id = ( *target_it ).node_id; - - // check whether the target is on this mpi machine - if ( not kernel().node_manager.is_local_node_id( tnode_id ) ) - { - // Disconnecting: no parameter skipping required - continue; - } - - Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); - const size_t target_thread = target->get_thread(); - - // check whether the target is a proxy - if ( target->is_proxy() ) - { - // Disconnecting: no parameter skipping required - continue; - } - - NodeCollection::const_iterator source_it = sources_->begin(); - for ( ; source_it < sources_->end(); ++source_it ) - { - const size_t snode_id = ( *source_it ).node_id; - single_disconnect_( snode_id, *target, target_thread ); - } + // 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 ) ); } } - 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 ) ); - } } -} -void -nest::AllToAllBuilder::sp_disconnect_() -{ -#pragma omp parallel + void nest::OneToOneBuilder::sp_disconnect_() { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); - try +#pragma omp parallel { - NodeCollection::const_iterator target_it = targets_->begin(); - for ( ; target_it < targets_->end(); ++target_it ) - { - const size_t tnode_id = ( *target_it ).node_id; + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); + try + { + NodeCollection::const_iterator target_it = targets_->begin(); NodeCollection::const_iterator source_it = sources_->begin(); - for ( ; source_it < sources_->end(); ++source_it ) + for ( ; target_it < targets_->end(); ++target_it, ++source_it ) { + assert( source_it < sources_->end() ); + const size_t snode_id = ( *source_it ).node_id; + const size_t tnode_id = ( *target_it ).node_id; if ( not change_connected_synaptic_elements( snode_id, tnode_id, tid, -1 ) ) { - // Disconnecting: no parameter skipping required continue; } + Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); const size_t target_thread = target->get_thread(); + single_disconnect_( snode_id, *target, target_thread ); } } - } - 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 ) ); - } - } -} - -nest::FixedInDegreeBuilder::FixedInDegreeBuilder( NodeCollectionPTR sources, - NodeCollectionPTR targets, - const DictionaryDatum& conn_spec, - const std::vector< DictionaryDatum >& syn_specs ) - : ConnBuilder( sources, targets, conn_spec, syn_specs ) -{ - // check for potential errors - long n_sources = static_cast< long >( sources_->size() ); - if ( n_sources == 0 ) - { - throw BadProperty( "Source array must not be empty." ); - } - ParameterDatum* pd = dynamic_cast< ParameterDatum* >( ( *conn_spec )[ names::indegree ].datum() ); - if ( pd ) - { - indegree_ = *pd; - // TODO: Checks of parameter range - } - else - { - // Assume indegree is a scalar - const long value = ( *conn_spec )[ names::indegree ]; - indegree_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); - - // verify that indegree is not larger than source population if multapses are disabled - if ( not allow_multapses_ ) - { - if ( value > n_sources ) - { - throw BadProperty( "Indegree cannot be larger than population size." ); - } - else if ( value == n_sources and not allow_autapses_ ) - { - LOG( M_WARNING, - "FixedInDegreeBuilder::connect", - "Multapses and autapses prohibited. When the sources and the targets " - "have a non-empty intersection, the connect algorithm will enter an infinite loop." ); - return; - } - - if ( value > 0.9 * n_sources ) + catch ( std::exception& err ) { - LOG( M_WARNING, - "FixedInDegreeBuilder::connect", - "Multapses are prohibited and you request more than 90% connectivity. Expect long connecting times!" ); + // 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 ) ); } - } // if (not allow_multapses_ ) - - if ( value < 0 ) - { - throw BadProperty( "Indegree cannot be less than zero." ); } } -} - -void -nest::FixedInDegreeBuilder::connect_() -{ -#pragma omp parallel + void nest::AllToAllBuilder::connect_() { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); - try +#pragma omp parallel { - RngPtr rng = get_vp_specific_rng( tid ); + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); - if ( loop_over_targets_() ) + try { - 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 ); + RngPtr rng = get_vp_specific_rng( tid ); - const long indegree_value = std::round( indegree_->value( rng, target ) ); - if ( target->is_proxy() ) + if ( loop_over_targets_() ) + { + NodeCollection::const_iterator target_it = targets_->begin(); + for ( ; target_it < targets_->end(); ++target_it ) { - // skip array parameters handled in other virtual processes - skip_conn_parameter_( tid, indegree_value ); - continue; + 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_conn_parameter_( tid, sources_->size() ); + continue; + } + + inner_connect_( tid, rng, target, tnode_id, true ); } - - inner_connect_( tid, rng, target, tnode_id, true, indegree_value ); } - } - 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 ) + else { - const size_t tnode_id = n->get_node_id(); - - // Is the local node in the targets list? - if ( targets_->get_lid( tnode_id ) < 0 ) + 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 ) { - continue; - } - auto source = n->get_node(); - const long indegree_value = std::round( indegree_->value( rng, source ) ); + const size_t tnode_id = n->get_node_id(); - inner_connect_( tid, rng, source, tnode_id, false, indegree_value ); + // 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, false ); + } } } + 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 ) ); + } } - 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 ) ); - } - } -} - -void -nest::FixedInDegreeBuilder::inner_connect_( const int tid, - RngPtr rng, - Node* target, - size_t tnode_id, - bool skip, - long indegree_value ) -{ - const size_t target_thread = target->get_thread(); - - // check whether the target is on our thread - if ( static_cast< size_t >( tid ) != target_thread ) - { - // skip array parameters handled in other virtual processes - if ( skip ) - { - skip_conn_parameter_( tid, indegree_value ); - } - return; - } - - std::set< long > ch_ids; - long n_rnd = sources_->size(); - - for ( long j = 0; j < indegree_value; ++j ) - { - unsigned long s_id; - size_t snode_id; - bool skip_autapse = false; - bool skip_multapse = false; - - do - { - s_id = rng->ulrand( n_rnd ); - snode_id = ( *sources_ )[ s_id ]; - skip_autapse = not allow_autapses_ and snode_id == tnode_id; - skip_multapse = not allow_multapses_ and ch_ids.find( s_id ) != ch_ids.end(); - } while ( skip_autapse or skip_multapse ); - - if ( not allow_multapses_ ) - { - ch_ids.insert( s_id ); - } - - single_connect_( snode_id, *target, target_thread, rng ); } -} -nest::FixedOutDegreeBuilder::FixedOutDegreeBuilder( NodeCollectionPTR sources, - NodeCollectionPTR targets, - const DictionaryDatum& conn_spec, - const std::vector< DictionaryDatum >& syn_specs ) - : ConnBuilder( sources, targets, conn_spec, syn_specs ) -{ - // check for potential errors - long n_targets = static_cast< long >( targets_->size() ); - if ( n_targets == 0 ) - { - throw BadProperty( "Target array must not be empty." ); - } - ParameterDatum* pd = dynamic_cast< ParameterDatum* >( ( *conn_spec )[ names::outdegree ].datum() ); - if ( pd ) + void nest::AllToAllBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, size_t tnode_id, bool skip ) { - outdegree_ = *pd; - // TODO: Checks of parameter range - } - else - { - // Assume outdegree is a scalar - const long value = ( *conn_spec )[ names::outdegree ]; - - outdegree_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); + const size_t target_thread = target->get_thread(); - // verify that outdegree is not larger than target population if multapses - // are disabled - if ( not allow_multapses_ ) + // check whether the target is on our thread + if ( static_cast< size_t >( tid ) != target_thread ) { - if ( value > n_targets ) - { - throw BadProperty( "Outdegree cannot be larger than population size." ); - } - else if ( value == n_targets and not allow_autapses_ ) - { - LOG( M_WARNING, - "FixedOutDegreeBuilder::connect", - "Multapses and autapses prohibited. When the sources and the targets " - "have a non-empty intersection, the connect algorithm will enter an infinite loop." ); - return; - } - - if ( value > 0.9 * n_targets ) + if ( skip ) { - LOG( M_WARNING, - "FixedOutDegreeBuilder::connect", - "Multapses are prohibited and you request more than 90% connectivity. Expect long connecting times!" ); + skip_conn_parameter_( tid, sources_->size() ); } + return; } - if ( value < 0 ) - { - throw BadProperty( "Outdegree cannot be less than zero." ); - } - } -} - -void -nest::FixedOutDegreeBuilder::connect_() -{ - // get global rng that is tested for synchronization for all threads - RngPtr grng = get_rank_synced_rng(); - - NodeCollection::const_iterator source_it = sources_->begin(); - for ( ; source_it < sources_->end(); ++source_it ) - { - const size_t snode_id = ( *source_it ).node_id; - - std::set< long > ch_ids; - std::vector< size_t > tgt_ids_; - const long n_rnd = targets_->size(); - - Node* source_node = kernel().node_manager.get_node_or_proxy( snode_id ); - const long outdegree_value = std::round( outdegree_->value( grng, source_node ) ); - for ( long j = 0; j < outdegree_value; ++j ) + NodeCollection::const_iterator source_it = sources_->begin(); + for ( ; source_it < sources_->end(); ++source_it ) { - unsigned long t_id; - size_t tnode_id; - bool skip_autapse = false; - bool skip_multapse = false; - - do - { - t_id = grng->ulrand( n_rnd ); - tnode_id = ( *targets_ )[ t_id ]; - skip_autapse = not allow_autapses_ and tnode_id == snode_id; - skip_multapse = not allow_multapses_ and ch_ids.find( t_id ) != ch_ids.end(); - } while ( skip_autapse or skip_multapse ); + const size_t snode_id = ( *source_it ).node_id; - if ( not allow_multapses_ ) + if ( not allow_autapses_ and snode_id == tnode_id ) { - ch_ids.insert( t_id ); + if ( skip ) + { + skip_conn_parameter_( target_thread ); + } + continue; } - tgt_ids_.push_back( tnode_id ); + single_connect_( snode_id, *target, target_thread, rng ); } + } + void nest::AllToAllBuilder::sp_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 ); - std::vector< size_t >::const_iterator tnode_id_it = tgt_ids_.begin(); - for ( ; tnode_id_it != tgt_ids_.end(); ++tnode_id_it ) + NodeCollection::const_iterator target_it = targets_->begin(); + for ( ; target_it < targets_->end(); ++target_it ) { - Node* const target = kernel().node_manager.get_node_or_proxy( *tnode_id_it, tid ); - if ( target->is_proxy() ) + const size_t tnode_id = ( *target_it ).node_id; + + NodeCollection::const_iterator source_it = sources_->begin(); + for ( ; source_it < sources_->end(); ++source_it ) { - // skip array parameters handled in other virtual processes - skip_conn_parameter_( tid ); - continue; + const size_t snode_id = ( *source_it ).node_id; + + if ( not allow_autapses_ and snode_id == tnode_id ) + { + skip_conn_parameter_( tid ); + continue; + } + if ( not change_connected_synaptic_elements( snode_id, tnode_id, tid, 1 ) ) + { + skip_conn_parameter_( tid, sources_->size() ); + continue; + } + Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); + const size_t target_thread = target->get_thread(); + single_connect_( snode_id, *target, target_thread, rng ); } - - single_connect_( snode_id, *target, tid, rng ); } } catch ( std::exception& err ) @@ -1302,839 +1231,1014 @@ nest::FixedOutDegreeBuilder::connect_() } } } -} - -nest::FixedTotalNumberBuilder::FixedTotalNumberBuilder( NodeCollectionPTR sources, - NodeCollectionPTR targets, - const DictionaryDatum& conn_spec, - const std::vector< DictionaryDatum >& syn_specs ) - : ConnBuilder( sources, targets, conn_spec, syn_specs ) - , N_( ( *conn_spec )[ names::N ] ) -{ - - // check for potential errors - - // verify that total number of connections is not larger than - // N_sources*N_targets - if ( not allow_multapses_ ) - { - if ( ( N_ > static_cast< long >( sources_->size() * targets_->size() ) ) ) - { - throw BadProperty( "Total number of connections cannot exceed product of source and target population sizes." ); - } - } - if ( N_ < 0 ) + void nest::AllToAllBuilder::disconnect_() { - throw BadProperty( "Total number of connections cannot be negative." ); - } - // for now multapses cannot be forbidden - // TODO: Implement option for multapses_ = False, where already existing - // connections are stored in - // a bitmap - if ( not allow_multapses_ ) - { - throw NotImplemented( "Connect doesn't support the suppression of multapses in the FixedTotalNumber connector." ); - } -} +#pragma omp parallel + { + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); -void -nest::FixedTotalNumberBuilder::connect_() -{ - const int M = kernel().vp_manager.get_num_virtual_processes(); - const long size_sources = sources_->size(); - const long size_targets = targets_->size(); + try + { + NodeCollection::const_iterator target_it = targets_->begin(); + for ( ; target_it < targets_->end(); ++target_it ) + { + const size_t tnode_id = ( *target_it ).node_id; + + // check whether the target is on this mpi machine + if ( not kernel().node_manager.is_local_node_id( tnode_id ) ) + { + // Disconnecting: no parameter skipping required + continue; + } - // drawing connection ids + Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); + const size_t target_thread = target->get_thread(); - // Compute the distribution of targets over processes using the modulo - // function - std::vector< size_t > number_of_targets_on_vp( M, 0 ); - std::vector< size_t > local_targets; - local_targets.reserve( size_targets / kernel().mpi_manager.get_num_processes() ); - for ( size_t t = 0; t < targets_->size(); t++ ) - { - int vp = kernel().vp_manager.node_id_to_vp( ( *targets_ )[ t ] ); - ++number_of_targets_on_vp[ vp ]; - if ( kernel().vp_manager.is_local_vp( vp ) ) - { - local_targets.push_back( ( *targets_ )[ t ] ); + // check whether the target is a proxy + if ( target->is_proxy() ) + { + // Disconnecting: no parameter skipping required + continue; + } + + NodeCollection::const_iterator source_it = sources_->begin(); + for ( ; source_it < sources_->end(); ++source_it ) + { + const size_t snode_id = ( *source_it ).node_id; + single_disconnect_( snode_id, *target, target_thread ); + } + } + } + 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 ) ); + } } } - // We use the multinomial distribution to determine the number of - // connections that will be made on one virtual process, i.e. we - // partition the set of edges into n_vps subsets. The number of - // edges on one virtual process is binomially distributed with - // the boundary condition that the sum of all edges over virtual - // processes is the total number of edges. - // To obtain the num_conns_on_vp we adapt the gsl - // implementation of the multinomial distribution. - - // K from gsl is equivalent to M = n_vps - // N is already taken from stack - // p[] is targets_on_vp - std::vector< long > num_conns_on_vp( M, 0 ); // corresponds to n[] + void nest::AllToAllBuilder::sp_disconnect_() + { +#pragma omp parallel + { + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); - // calculate exact multinomial distribution - // get global rng that is tested for synchronization for all threads - RngPtr grng = get_rank_synced_rng(); + try + { + NodeCollection::const_iterator target_it = targets_->begin(); + for ( ; target_it < targets_->end(); ++target_it ) + { + const size_t tnode_id = ( *target_it ).node_id; - // begin code adapted from gsl 1.8 // - double sum_dist = 0.0; // corresponds to sum_p - // norm is equivalent to size_targets - unsigned int sum_partitions = 0; // corresponds to sum_n + 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 change_connected_synaptic_elements( snode_id, tnode_id, tid, -1 ) ) + { + // Disconnecting: no parameter skipping required + continue; + } + Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); + const size_t target_thread = target->get_thread(); + single_disconnect_( snode_id, *target, target_thread ); + } + } + } + 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 ) ); + } + } + } - binomial_distribution bino_dist; - for ( int k = 0; k < M; k++ ) + nest::FixedInDegreeBuilder::FixedInDegreeBuilder( NodeCollectionPTR sources, + NodeCollectionPTR targets, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : BipartiteConnBuilder( sources, targets, conn_spec, syn_specs ) { - // If we have distributed all connections on the previous processes we exit the loop. It is important to - // have this check here, as N - sum_partition is set as n value for GSL, and this must be larger than 0. - if ( N_ == sum_partitions ) + // check for potential errors + long n_sources = static_cast< long >( sources_->size() ); + if ( n_sources == 0 ) { - break; + throw BadProperty( "Source array must not be empty." ); } - if ( number_of_targets_on_vp[ k ] > 0 ) + ParameterDatum* pd = dynamic_cast< ParameterDatum* >( ( *conn_spec )[ names::indegree ].datum() ); + if ( pd ) { - double num_local_targets = static_cast< double >( number_of_targets_on_vp[ k ] ); - double p_local = num_local_targets / ( size_targets - sum_dist ); - - binomial_distribution::param_type param( N_ - sum_partitions, p_local ); - num_conns_on_vp[ k ] = bino_dist( grng, param ); + indegree_ = *pd; + // TODO: Checks of parameter range } + else + { + // Assume indegree is a scalar + const long value = ( *conn_spec )[ names::indegree ]; + indegree_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); - sum_dist += static_cast< double >( number_of_targets_on_vp[ k ] ); - sum_partitions += static_cast< unsigned int >( num_conns_on_vp[ k ] ); - } + // verify that indegree is not larger than source population if multapses are disabled + if ( not allow_multapses_ ) + { + if ( value > n_sources ) + { + throw BadProperty( "Indegree cannot be larger than population size." ); + } + else if ( value == n_sources and not allow_autapses_ ) + { + LOG( M_WARNING, + "FixedInDegreeBuilder::connect", + "Multapses and autapses prohibited. When the sources and the targets " + "have a non-empty intersection, the connect algorithm will enter an infinite loop." ); + return; + } - // end code adapted from gsl 1.8 + if ( value > 0.9 * n_sources ) + { + LOG( M_WARNING, + "FixedInDegreeBuilder::connect", + "Multapses are prohibited and you request more than 90% connectivity. Expect long connecting times!" ); + } + } // if (not allow_multapses_ ) -#pragma omp parallel + if ( value < 0 ) + { + throw BadProperty( "Indegree cannot be less than zero." ); + } + } + } + + void nest::FixedInDegreeBuilder::connect_() { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); - try +#pragma omp parallel { - const size_t vp_id = kernel().vp_manager.thread_to_vp( tid ); + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); - if ( kernel().vp_manager.is_local_vp( vp_id ) ) + try { RngPtr rng = get_vp_specific_rng( tid ); - // gather local target node IDs - std::vector< size_t > thread_local_targets; - thread_local_targets.reserve( number_of_targets_on_vp[ vp_id ] ); - - std::vector< size_t >::const_iterator tnode_id_it = local_targets.begin(); - for ( ; tnode_id_it != local_targets.end(); ++tnode_id_it ) + if ( loop_over_targets_() ) { - if ( kernel().vp_manager.node_id_to_vp( *tnode_id_it ) == vp_id ) + NodeCollection::const_iterator target_it = targets_->begin(); + for ( ; target_it < targets_->end(); ++target_it ) { - thread_local_targets.push_back( *tnode_id_it ); + const size_t tnode_id = ( *target_it ).node_id; + Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); + + const long indegree_value = std::round( indegree_->value( rng, target ) ); + if ( target->is_proxy() ) + { + // skip array parameters handled in other virtual processes + skip_conn_parameter_( tid, indegree_value ); + continue; + } + + inner_connect_( tid, rng, target, tnode_id, true, indegree_value ); } } - - assert( thread_local_targets.size() == number_of_targets_on_vp[ vp_id ] ); - - while ( num_conns_on_vp[ vp_id ] > 0 ) + 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(); - // draw random numbers for source node from all source neurons - const long s_index = rng->ulrand( size_sources ); - // draw random numbers for target node from - // targets_on_vp on this virtual process - const long t_index = rng->ulrand( thread_local_targets.size() ); - // map random number of source node to node ID corresponding to - // the source_adr vector - const long snode_id = ( *sources_ )[ s_index ]; - // map random number of target node to node ID using the - // targets_on_vp vector - const long tnode_id = thread_local_targets[ t_index ]; + // Is the local node in the targets list? + if ( targets_->get_lid( tnode_id ) < 0 ) + { + continue; + } + auto source = n->get_node(); + const long indegree_value = std::round( indegree_->value( rng, source ) ); - Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); - const size_t target_thread = target->get_thread(); - - if ( allow_autapses_ or snode_id != tnode_id ) - { - single_connect_( snode_id, *target, target_thread, rng ); - num_conns_on_vp[ vp_id ]--; + inner_connect_( tid, rng, source, tnode_id, false, indegree_value ); } } } + 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 ) ); + } } - catch ( std::exception& err ) + } + + void nest::FixedInDegreeBuilder::inner_connect_( + const int tid, RngPtr rng, Node* target, size_t tnode_id, bool skip, long indegree_value ) + { + const size_t target_thread = target->get_thread(); + + // check whether the target is on our thread + if ( static_cast< size_t >( tid ) != target_thread ) { - // 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 ) ); + // skip array parameters handled in other virtual processes + if ( skip ) + { + skip_conn_parameter_( tid, indegree_value ); + } + return; } - } -} + std::set< long > ch_ids; + long n_rnd = sources_->size(); -nest::BernoulliBuilder::BernoulliBuilder( 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 + for ( long j = 0; j < indegree_value; ++j ) + { + unsigned long s_id; + size_t snode_id; + bool skip_autapse = false; + bool skip_multapse = false; + + do + { + s_id = rng->ulrand( n_rnd ); + snode_id = ( *sources_ )[ s_id ]; + skip_autapse = not allow_autapses_ and snode_id == tnode_id; + skip_multapse = not allow_multapses_ and ch_ids.find( s_id ) != ch_ids.end(); + } while ( skip_autapse or skip_multapse ); + + if ( not allow_multapses_ ) + { + ch_ids.insert( s_id ); + } + + single_connect_( snode_id, *target, target_thread, rng ); + } } - else + + nest::FixedOutDegreeBuilder::FixedOutDegreeBuilder( NodeCollectionPTR sources, + NodeCollectionPTR targets, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : BipartiteConnBuilder( sources, targets, conn_spec, syn_specs ) { - // Assume p is a scalar - const double value = ( *conn_spec )[ names::p ]; - if ( value < 0 or 1 < value ) + // check for potential errors + long n_targets = static_cast< long >( targets_->size() ); + if ( n_targets == 0 ) { - throw BadProperty( "Connection probability 0 <= p <= 1 required." ); + throw BadProperty( "Target array must not be empty." ); } - p_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); - } -} + ParameterDatum* pd = dynamic_cast< ParameterDatum* >( ( *conn_spec )[ names::outdegree ].datum() ); + if ( pd ) + { + outdegree_ = *pd; + // TODO: Checks of parameter range + } + else + { + // Assume outdegree is a scalar + const long value = ( *conn_spec )[ names::outdegree ]; + outdegree_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); -void -nest::BernoulliBuilder::connect_() -{ -#pragma omp parallel + // verify that outdegree is not larger than target population if multapses + // are disabled + if ( not allow_multapses_ ) + { + if ( value > n_targets ) + { + throw BadProperty( "Outdegree cannot be larger than population size." ); + } + else if ( value == n_targets and not allow_autapses_ ) + { + LOG( M_WARNING, + "FixedOutDegreeBuilder::connect", + "Multapses and autapses prohibited. When the sources and the targets " + "have a non-empty intersection, the connect algorithm will enter an infinite loop." ); + return; + } + + if ( value > 0.9 * n_targets ) + { + LOG( M_WARNING, + "FixedOutDegreeBuilder::connect", + "Multapses are prohibited and you request more than 90% connectivity. Expect long connecting times!" ); + } + } + + if ( value < 0 ) + { + throw BadProperty( "Outdegree cannot be less than zero." ); + } + } + } + + void nest::FixedOutDegreeBuilder::connect_() { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); + // get global rng that is tested for synchronization for all threads + RngPtr grng = get_rank_synced_rng(); - try + NodeCollection::const_iterator source_it = sources_->begin(); + for ( ; source_it < sources_->end(); ++source_it ) { - RngPtr rng = get_vp_specific_rng( tid ); + const size_t snode_id = ( *source_it ).node_id; - if ( loop_over_targets_() ) + std::set< long > ch_ids; + std::vector< size_t > tgt_ids_; + const long n_rnd = targets_->size(); + + Node* source_node = kernel().node_manager.get_node_or_proxy( snode_id ); + const long outdegree_value = std::round( outdegree_->value( grng, source_node ) ); + for ( long j = 0; j < outdegree_value; ++j ) { - NodeCollection::const_iterator target_it = targets_->begin(); - for ( ; target_it < targets_->end(); ++target_it ) + unsigned long t_id; + size_t tnode_id; + bool skip_autapse = false; + bool skip_multapse = false; + + do { - 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; - } + t_id = grng->ulrand( n_rnd ); + tnode_id = ( *targets_ )[ t_id ]; + skip_autapse = not allow_autapses_ and tnode_id == snode_id; + skip_multapse = not allow_multapses_ and ch_ids.find( t_id ) != ch_ids.end(); + } while ( skip_autapse or skip_multapse ); - inner_connect_( tid, rng, target, tnode_id ); + if ( not allow_multapses_ ) + { + ch_ids.insert( t_id ); } + + tgt_ids_.push_back( tnode_id ); } - else +#pragma omp parallel { - 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 ) + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); + + try { - const size_t tnode_id = n->get_node_id(); + RngPtr rng = get_vp_specific_rng( tid ); - // Is the local node in the targets list? - if ( targets_->get_lid( tnode_id ) < 0 ) + std::vector< size_t >::const_iterator tnode_id_it = tgt_ids_.begin(); + for ( ; tnode_id_it != tgt_ids_.end(); ++tnode_id_it ) { - continue; + Node* const target = kernel().node_manager.get_node_or_proxy( *tnode_id_it, tid ); + if ( target->is_proxy() ) + { + // skip array parameters handled in other virtual processes + skip_conn_parameter_( tid ); + continue; + } + + single_connect_( snode_id, *target, tid, rng ); } - - 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 ) ); } } } - 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::BernoulliBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, size_t tnode_id ) -{ - const size_t target_thread = target->get_thread(); - - // check whether the target is on our thread - if ( static_cast< size_t >( tid ) != target_thread ) - { - return; } - // It is not possible to create multapses with this type of BernoulliBuilder, - // hence leave out corresponding checks. - - NodeCollection::const_iterator source_it = sources_->begin(); - for ( ; source_it < sources_->end(); ++source_it ) + nest::FixedTotalNumberBuilder::FixedTotalNumberBuilder( NodeCollectionPTR sources, + NodeCollectionPTR targets, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : BipartiteConnBuilder( sources, targets, conn_spec, syn_specs ) + , N_( ( *conn_spec )[ names::N ] ) { - const size_t snode_id = ( *source_it ).node_id; - if ( not allow_autapses_ and snode_id == tnode_id ) + // check for potential errors + + // verify that total number of connections is not larger than + // N_sources*N_targets + if ( not allow_multapses_ ) { - continue; + if ( ( N_ > static_cast< long >( sources_->size() * targets_->size() ) ) ) + { + throw BadProperty( "Total number of connections cannot exceed product of source and target population sizes." ); + } } - if ( rng->drand() >= p_->value( rng, target ) ) + + if ( N_ < 0 ) { - continue; + throw BadProperty( "Total number of connections cannot be negative." ); } - single_connect_( snode_id, *target, target_thread, rng ); + // for now multapses cannot be forbidden + // TODO: Implement option for multapses_ = False, where already existing + // connections are stored in + // a bitmap + if ( not allow_multapses_ ) + { + throw NotImplemented( "Connect doesn't support the suppression of multapses in the FixedTotalNumber connector." ); + } } -} - -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::pairwise_avg_num_conns ].datum() ); - if ( pd ) - { - pairwise_avg_num_conns_ = *pd; - } - else + void nest::FixedTotalNumberBuilder::connect_() { - // Assume pairwise_avg_num_conns is a scalar - const double value = ( *conn_spec )[ names::pairwise_avg_num_conns ]; - if ( value < 0 ) + const int M = kernel().vp_manager.get_num_virtual_processes(); + const long size_sources = sources_->size(); + const long size_targets = targets_->size(); + + // drawing connection ids + + // Compute the distribution of targets over processes using the modulo + // function + std::vector< size_t > number_of_targets_on_vp( M, 0 ); + std::vector< size_t > local_targets; + local_targets.reserve( size_targets / kernel().mpi_manager.get_num_processes() ); + for ( size_t t = 0; t < targets_->size(); t++ ) { - throw BadProperty( "Connection parameter 0 ≤ pairwise_avg_num_conns required." ); + int vp = kernel().vp_manager.node_id_to_vp( ( *targets_ )[ t ] ); + ++number_of_targets_on_vp[ vp ]; + if ( kernel().vp_manager.is_local_vp( vp ) ) + { + local_targets.push_back( ( *targets_ )[ t ] ); + } } - if ( not allow_multapses_ ) + + // We use the multinomial distribution to determine the number of + // connections that will be made on one virtual process, i.e. we + // partition the set of edges into n_vps subsets. The number of + // edges on one virtual process is binomially distributed with + // the boundary condition that the sum of all edges over virtual + // processes is the total number of edges. + // To obtain the num_conns_on_vp we adapt the gsl + // implementation of the multinomial distribution. + + // K from gsl is equivalent to M = n_vps + // N is already taken from stack + // p[] is targets_on_vp + std::vector< long > num_conns_on_vp( M, 0 ); // corresponds to n[] + + // calculate exact multinomial distribution + // get global rng that is tested for synchronization for all threads + RngPtr grng = get_rank_synced_rng(); + + // begin code adapted from gsl 1.8 // + double sum_dist = 0.0; // corresponds to sum_p + // norm is equivalent to size_targets + unsigned int sum_partitions = 0; // corresponds to sum_n + + binomial_distribution bino_dist; + for ( int k = 0; k < M; k++ ) { - throw BadProperty( "Multapses must be allowed for this connection rule." ); + // If we have distributed all connections on the previous processes we exit the loop. It is important to + // have this check here, as N - sum_partition is set as n value for GSL, and this must be larger than 0. + if ( N_ == sum_partitions ) + { + break; + } + if ( number_of_targets_on_vp[ k ] > 0 ) + { + double num_local_targets = static_cast< double >( number_of_targets_on_vp[ k ] ); + double p_local = num_local_targets / ( size_targets - sum_dist ); + + binomial_distribution::param_type param( N_ - sum_partitions, p_local ); + num_conns_on_vp[ k ] = bino_dist( grng, param ); + } + + sum_dist += static_cast< double >( number_of_targets_on_vp[ k ] ); + sum_partitions += static_cast< unsigned int >( num_conns_on_vp[ k ] ); } - pairwise_avg_num_conns_ = 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(); + // end code adapted from gsl 1.8 - try +#pragma omp parallel { - RngPtr rng = get_vp_specific_rng( tid ); + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); - if ( loop_over_targets_() ) + try { - NodeCollection::const_iterator target_it = targets_->begin(); - for ( ; target_it < targets_->end(); ++target_it ) + const size_t vp_id = kernel().vp_manager.thread_to_vp( tid ); + + if ( kernel().vp_manager.is_local_vp( vp_id ) ) { - 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() ) + RngPtr rng = get_vp_specific_rng( tid ); + + // gather local target node IDs + std::vector< size_t > thread_local_targets; + thread_local_targets.reserve( number_of_targets_on_vp[ vp_id ] ); + + std::vector< size_t >::const_iterator tnode_id_it = local_targets.begin(); + for ( ; tnode_id_it != local_targets.end(); ++tnode_id_it ) { - // skip parameters handled in other virtual processes - skip_conn_parameter_( tid ); - continue; + if ( kernel().vp_manager.node_id_to_vp( *tnode_id_it ) == vp_id ) + { + thread_local_targets.push_back( *tnode_id_it ); + } } - 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(); + assert( thread_local_targets.size() == number_of_targets_on_vp[ vp_id ] ); - // Is the local node in the targets list? - if ( targets_->get_lid( tnode_id ) < 0 ) + while ( num_conns_on_vp[ vp_id ] > 0 ) { - continue; + + // draw random numbers for source node from all source neurons + const long s_index = rng->ulrand( size_sources ); + // draw random numbers for target node from + // targets_on_vp on this virtual process + const long t_index = rng->ulrand( thread_local_targets.size() ); + // map random number of source node to node ID corresponding to + // the source_adr vector + const long snode_id = ( *sources_ )[ s_index ]; + // map random number of target node to node ID using the + // targets_on_vp vector + const long tnode_id = thread_local_targets[ t_index ]; + + Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); + const size_t target_thread = target->get_thread(); + + if ( allow_autapses_ or snode_id != tnode_id ) + { + single_connect_( snode_id, *target, target_thread, rng ); + num_conns_on_vp[ vp_id ]--; + } } - 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 ) ); + } } - catch ( std::exception& err ) + } + + + nest::BernoulliBuilder::BernoulliBuilder( NodeCollectionPTR sources, + NodeCollectionPTR targets, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : BipartiteConnBuilder( sources, targets, conn_spec, syn_specs ) + { + ParameterDatum* pd = dynamic_cast< ParameterDatum* >( ( *conn_spec )[ names::p ].datum() ); + if ( pd ) { - // 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 ) ); + p_ = *pd; + // TODO: Checks of parameter range } - } // of omp parallel -} + else + { + // Assume p is a scalar + const double value = ( *conn_spec )[ names::p ]; + if ( value < 0 or 1 < value ) + { + throw BadProperty( "Connection probability 0 <= p <= 1 required." ); + } + p_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); + } + } -void -nest::PoissonBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, size_t tnode_id ) -{ - const size_t target_thread = target->get_thread(); - // check whether the target is on our thread - if ( static_cast< size_t >( tid ) != target_thread ) + void nest::BernoulliBuilder::connect_() { - return; +#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 } - 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 ) + void nest::BernoulliBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, size_t tnode_id ) { - const size_t snode_id = ( *source_it ).node_id; + const size_t target_thread = target->get_thread(); - if ( not allow_autapses_ and snode_id == tnode_id ) + // check whether the target is on our thread + if ( static_cast< size_t >( tid ) != target_thread ) { - continue; + return; } - // Sample to number of connections that are to be established - poisson_distribution::param_type param( pairwise_avg_num_conns_->value( rng, target ) ); - const size_t num_conns = poi_dist( rng, param ); + // It is not possible to create multapses with this type of BernoulliBuilder, + // hence leave out corresponding checks. - for ( size_t n = 0; n < num_conns; ++n ) + 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; + } + if ( rng->drand() >= p_->value( rng, target ) ) + { + continue; + } + single_connect_( snode_id, *target, target_thread, rng ); } } -} - - -nest::AuxiliaryBuilder::AuxiliaryBuilder( NodeCollectionPTR sources, - NodeCollectionPTR targets, - const DictionaryDatum& conn_spec, - const std::vector< DictionaryDatum >& syn_spec ) - : ConnBuilder( sources, targets, conn_spec, syn_spec ) -{ -} - -void -nest::AuxiliaryBuilder::single_connect( size_t snode_id, Node& tgt, size_t tid, RngPtr rng ) -{ - single_connect_( snode_id, tgt, tid, rng ); -} -nest::TripartiteBernoulliWithPoolBuilder::TripartiteBernoulliWithPoolBuilder( NodeCollectionPTR sources, - NodeCollectionPTR targets, - NodeCollectionPTR third, - const DictionaryDatum& conn_spec, - const std::map< Name, std::vector< DictionaryDatum > >& syn_specs ) - : ConnBuilder( sources, - targets, - conn_spec, - // const_cast here seems required, clang complains otherwise; try to clean up when Datums disappear - const_cast< std::map< Name, std::vector< DictionaryDatum > >& >( syn_specs )[ names::primary ] ) - , third_( third ) - , third_in_builder_( sources, - third, - conn_spec, - const_cast< std::map< Name, std::vector< DictionaryDatum > >& >( syn_specs )[ names::third_in ] ) - , third_out_builder_( third, - targets, - conn_spec, - const_cast< std::map< Name, std::vector< DictionaryDatum > >& >( syn_specs )[ names::third_out ] ) - , p_primary_( 1.0 ) - , p_third_if_primary_( 1.0 ) - , random_pool_( true ) - , pool_size_( third->size() ) - , targets_per_third_( targets->size() / third->size() ) -{ - updateValue< double >( conn_spec, names::p_primary, p_primary_ ); - updateValue< double >( conn_spec, names::p_third_if_primary, p_third_if_primary_ ); - updateValue< long >( conn_spec, names::pool_size, pool_size_ ); - std::string pool_type; - if ( updateValue< std::string >( conn_spec, names::pool_type, pool_type ) ) + nest::PoissonBuilder::PoissonBuilder( NodeCollectionPTR sources, + NodeCollectionPTR targets, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : BipartiteConnBuilder( sources, targets, conn_spec, syn_specs ) { - if ( pool_type == "random" ) + ParameterDatum* pd = dynamic_cast< ParameterDatum* >( ( *conn_spec )[ names::pairwise_avg_num_conns ].datum() ); + if ( pd ) { - random_pool_ = true; - } - else if ( pool_type == "block" ) - { - random_pool_ = false; + pairwise_avg_num_conns_ = *pd; } else { - throw BadProperty( "pool_type must be 'random' or 'block'" ); + // 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 ≤ pairwise_avg_num_conns required." ); + } + if ( not allow_multapses_ ) + { + throw BadProperty( "Multapses must be allowed for this connection rule." ); + } + pairwise_avg_num_conns_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); } } - if ( p_primary_ < 0 or 1 < p_primary_ ) - { - throw BadProperty( "Probability of primary connection 0 ≤ p_primary ≤ 1 required" ); - } - - if ( p_third_if_primary_ < 0 or 1 < p_third_if_primary_ ) - { - throw BadProperty( "Conditional probability of third-factor connection 0 ≤ p_third_if_primary ≤ 1 required" ); - } - - if ( pool_size_ < 1 or third->size() < pool_size_ ) - { - throw BadProperty( "Pool size 1 ≤ pool_size ≤ size of third-factor population required" ); - } - - if ( not( random_pool_ or ( targets->size() * pool_size_ == third->size() ) - or ( pool_size_ == 1 and targets->size() % third->size() == 0 ) ) ) - { - throw BadProperty( - "The sizes of target and third-factor populations and the chosen pool size do not fit." - " If pool_size == 1, the target population size must be a multiple of the third-factor" - " population size. For pool_size > 1, size(targets) * pool_size == size(third factor)" - " is required. For all other cases, use random pools." ); - } -} - -size_t -nest::TripartiteBernoulliWithPoolBuilder::get_first_pool_index_( const size_t target_index ) const -{ - if ( pool_size_ > 1 ) + void nest::PoissonBuilder::connect_() { - return target_index * pool_size_; - } - - return target_index / targets_per_third_; // intentional integer division -} - -void -nest::TripartiteBernoulliWithPoolBuilder::connect_() -{ #pragma omp parallel - { - const size_t tid = kernel().vp_manager.get_thread_id(); - - try { - /* Random number generators: - * - Use RNG generating same number sequence on all threads to decide which connections to create - * - Use per-thread random number generator to randomize connection properties - */ - RngPtr synced_rng = get_vp_synced_rng( tid ); - RngPtr rng = get_vp_specific_rng( tid ); - - binomial_distribution bino_dist; - binomial_distribution::param_type bino_param( sources_->size(), p_primary_ ); + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); - // Iterate through target neurons. For each, three steps are done: - // 1. draw indegree 2. select astrocyte pool 3. make connections - for ( const auto& target : *targets_ ) + try { - const size_t tnode_id = target.node_id; - Node* target_node = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); - const bool local_target = not target_node->is_proxy(); - - // step 1, draw indegree for this target - const auto indegree = bino_dist( synced_rng, bino_param ); - if ( indegree == 0 ) - { - continue; // no connections for this target - } + RngPtr rng = get_vp_specific_rng( tid ); - // step 2, build pool for target - std::vector< NodeIDTriple > pool; - pool.reserve( pool_size_ ); - if ( random_pool_ ) + if ( loop_over_targets_() ) { - synced_rng->sample( third_->begin(), third_->end(), std::back_inserter( pool ), pool_size_ ); + 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 parameters handled in other virtual processes + skip_conn_parameter_( tid ); + continue; + } + + inner_connect_( tid, rng, target, tnode_id ); + } } else { - std::copy_n( third_->begin() + get_first_pool_index_( target.lid ), pool_size_, std::back_inserter( pool ) ); - } - - // step 3, iterate through indegree to make connections for this target - // - by construction, we cannot get multapses - // - if the target is also among sources, it can be drawn at most once; - // we ignore it then connecting if no autapses are wanted - std::vector< NodeIDTriple > sources_to_connect_; - sources_to_connect_.reserve( indegree ); - synced_rng->sample( sources_->begin(), sources_->end(), std::back_inserter( sources_to_connect_ ), indegree ); - - for ( const auto source : sources_to_connect_ ) - { - const auto snode_id = source.node_id; - if ( not allow_autapses_ and snode_id == tnode_id ) - { - continue; - } - - if ( local_target ) + 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 ) { - // plain connect now with thread-local rng for randomized parameters - single_connect_( snode_id, *target_node, tid, rng ); + 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 + } - // conditionally connect third factor - if ( not( synced_rng->drand() < p_third_if_primary_ ) ) - { - continue; - } + void nest::PoissonBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, size_t tnode_id ) + { + const size_t target_thread = target->get_thread(); - // select third-factor neuron randomly from pool for this target - const auto third_index = pool_size_ == 1 ? 0 : synced_rng->ulrand( pool_size_ ); - const auto third_node_id = pool[ third_index ].node_id; - Node* third_node = kernel().node_manager.get_node_or_proxy( third_node_id, tid ); - const bool local_third_node = not third_node->is_proxy(); + // check whether the target is on our thread + if ( static_cast< size_t >( tid ) != target_thread ) + { + return; + } - if ( local_third_node ) - { - // route via auxiliary builder who handles parameters - third_in_builder_.single_connect( snode_id, *third_node, tid, rng ); - } + poisson_distribution poi_dist; - // connection third-factor node to target if local - if ( local_target ) - { - // route via auxiliary builder who handles parameters - third_out_builder_.single_connect( third_node_id, *target_node, tid, rng ); - } - } - } - } - catch ( std::exception& err ) + // 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 ) { - // 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 ) ); - } - } -} + const size_t snode_id = ( *source_it ).node_id; + if ( not allow_autapses_ and snode_id == tnode_id ) + { + continue; + } -nest::SymmetricBernoulliBuilder::SymmetricBernoulliBuilder( NodeCollectionPTR sources, - NodeCollectionPTR targets, - const DictionaryDatum& conn_spec, - const std::vector< DictionaryDatum >& syn_specs ) - : ConnBuilder( sources, targets, conn_spec, syn_specs ) - , p_( ( *conn_spec )[ names::p ] ) -{ - // This connector takes care of symmetric connections on its own - creates_symmetric_connections_ = true; + // Sample to number of connections that are to be established + poisson_distribution::param_type param( pairwise_avg_num_conns_->value( rng, target ) ); + const size_t num_conns = poi_dist( rng, param ); - if ( p_ < 0 or 1 <= p_ ) - { - throw BadProperty( "Connection probability 0 <= p < 1 required." ); + for ( size_t n = 0; n < num_conns; ++n ) + { + single_connect_( snode_id, *target, target_thread, rng ); + } + } } - if ( not allow_multapses_ ) - { - throw BadProperty( "Multapses must be enabled." ); - } - if ( allow_autapses_ ) + nest::AuxiliaryBuilder::AuxiliaryBuilder( NodeCollectionPTR sources, + NodeCollectionPTR targets, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_spec ) + : BipartiteConnBuilder( sources, targets, conn_spec, syn_spec ) { - throw BadProperty( "Autapses must be disabled." ); } - if ( not make_symmetric_ ) + void nest::AuxiliaryBuilder::single_connect( size_t snode_id, Node & tgt, size_t tid, RngPtr rng ) { - throw BadProperty( "Symmetric connections must be enabled." ); + single_connect_( snode_id, tgt, tid, rng ); } -} -void -nest::SymmetricBernoulliBuilder::connect_() -{ -#pragma omp parallel + nest::SymmetricBernoulliBuilder::SymmetricBernoulliBuilder( NodeCollectionPTR sources, + NodeCollectionPTR targets, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : BipartiteConnBuilder( sources, targets, conn_spec, syn_specs ) + , p_( ( *conn_spec )[ names::p ] ) { - const size_t tid = kernel().vp_manager.get_thread_id(); + // This connector takes care of symmetric connections on its own + creates_symmetric_connections_ = true; - // Use RNG generating same number sequence on all threads - RngPtr synced_rng = get_vp_synced_rng( tid ); + if ( p_ < 0 or 1 <= p_ ) + { + throw BadProperty( "Connection probability 0 <= p < 1 required." ); + } - try + if ( not allow_multapses_ ) { - binomial_distribution bino_dist; - binomial_distribution::param_type param( sources_->size(), p_ ); + throw BadProperty( "Multapses must be enabled." ); + } - unsigned long indegree; - size_t snode_id; - std::set< size_t > previous_snode_ids; - Node* target; - size_t target_thread; - Node* source; - size_t source_thread; + if ( allow_autapses_ ) + { + throw BadProperty( "Autapses must be disabled." ); + } - for ( NodeCollection::const_iterator tnode_id = targets_->begin(); tnode_id != targets_->end(); ++tnode_id ) - { - // sample indegree according to truncated Binomial distribution - indegree = sources_->size(); - while ( indegree >= sources_->size() ) - { - indegree = bino_dist( synced_rng, param ); - } - assert( indegree < sources_->size() ); + if ( not make_symmetric_ ) + { + throw BadProperty( "Symmetric connections must be enabled." ); + } + } - target = kernel().node_manager.get_node_or_proxy( ( *tnode_id ).node_id, tid ); - target_thread = tid; - // check whether the target is on this thread - if ( target->is_proxy() ) - { - target_thread = invalid_thread; - } + void nest::SymmetricBernoulliBuilder::connect_() + { +#pragma omp parallel + { + const size_t tid = kernel().vp_manager.get_thread_id(); - previous_snode_ids.clear(); + // Use RNG generating same number sequence on all threads + RngPtr synced_rng = get_vp_synced_rng( tid ); - // choose indegree number of sources randomly from all sources - size_t i = 0; - while ( i < indegree ) + try + { + binomial_distribution bino_dist; + binomial_distribution::param_type param( sources_->size(), p_ ); + + unsigned long indegree; + size_t snode_id; + std::set< size_t > previous_snode_ids; + Node* target; + size_t target_thread; + Node* source; + size_t source_thread; + + for ( NodeCollection::const_iterator tnode_id = targets_->begin(); tnode_id != targets_->end(); ++tnode_id ) { - snode_id = ( *sources_ )[ synced_rng->ulrand( sources_->size() ) ]; - - // Avoid autapses and multapses. Due to symmetric connectivity, - // multapses might exist if the target neuron with node ID snode_id draws the - // source with node ID tnode_id while choosing sources itself. - if ( snode_id == ( *tnode_id ).node_id or previous_snode_ids.find( snode_id ) != previous_snode_ids.end() ) + // sample indegree according to truncated Binomial distribution + indegree = sources_->size(); + while ( indegree >= sources_->size() ) { - continue; + indegree = bino_dist( synced_rng, param ); } - previous_snode_ids.insert( snode_id ); + assert( indegree < sources_->size() ); - source = kernel().node_manager.get_node_or_proxy( snode_id, tid ); - source_thread = tid; + target = kernel().node_manager.get_node_or_proxy( ( *tnode_id ).node_id, tid ); + target_thread = tid; - if ( source->is_proxy() ) + // check whether the target is on this thread + if ( target->is_proxy() ) { - source_thread = invalid_thread; + target_thread = invalid_thread; } - // if target is local: connect - if ( target_thread == tid ) - { - assert( target ); - single_connect_( snode_id, *target, target_thread, synced_rng ); - } + previous_snode_ids.clear(); - // if source is local: connect - if ( source_thread == tid ) + // choose indegree number of sources randomly from all sources + size_t i = 0; + while ( i < indegree ) { - assert( source ); - single_connect_( ( *tnode_id ).node_id, *source, source_thread, synced_rng ); + snode_id = ( *sources_ )[ synced_rng->ulrand( sources_->size() ) ]; + + // Avoid autapses and multapses. Due to symmetric connectivity, + // multapses might exist if the target neuron with node ID snode_id draws the + // source with node ID tnode_id while choosing sources itself. + if ( snode_id == ( *tnode_id ).node_id or previous_snode_ids.find( snode_id ) != previous_snode_ids.end() ) + { + continue; + } + previous_snode_ids.insert( snode_id ); + + source = kernel().node_manager.get_node_or_proxy( snode_id, tid ); + source_thread = tid; + + if ( source->is_proxy() ) + { + source_thread = invalid_thread; + } + + // if target is local: connect + if ( target_thread == tid ) + { + assert( target ); + single_connect_( snode_id, *target, target_thread, synced_rng ); + } + + // if source is local: connect + if ( source_thread == tid ) + { + assert( source ); + single_connect_( ( *tnode_id ).node_id, *source, source_thread, synced_rng ); + } + + ++i; } - - ++i; } } - } - 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 ) ); + 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 ) ); + } } } -} -nest::SPBuilder::SPBuilder( NodeCollectionPTR sources, - NodeCollectionPTR targets, - const DictionaryDatum& conn_spec, - const std::vector< DictionaryDatum >& syn_specs ) - : ConnBuilder( sources, targets, conn_spec, syn_specs ) -{ - // Check that both pre and postsynaptic element are provided - if ( not use_structural_plasticity_ ) + nest::SPBuilder::SPBuilder( NodeCollectionPTR sources, + NodeCollectionPTR targets, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : BipartiteConnBuilder( sources, targets, conn_spec, syn_specs ) { - throw BadProperty( "pre_synaptic_element and/or post_synaptic_elements is missing." ); + // Check that both pre and postsynaptic element are provided + if ( not use_structural_plasticity_ ) + { + throw BadProperty( "pre_synaptic_element and/or post_synaptic_elements is missing." ); + } } -} -void -nest::SPBuilder::update_delay( long& d ) const -{ - if ( get_default_delay() ) + void nest::SPBuilder::update_delay( long& d ) const { - DictionaryDatum syn_defaults = kernel().model_manager.get_connector_defaults( get_synapse_model() ); - const double delay = getValue< double >( syn_defaults, "delay" ); - d = Time( Time::ms( delay ) ).get_steps(); + if ( get_default_delay() ) + { + DictionaryDatum syn_defaults = kernel().model_manager.get_connector_defaults( get_synapse_model() ); + const double delay = getValue< double >( syn_defaults, "delay" ); + d = Time( Time::ms( delay ) ).get_steps(); + } } -} - -void -nest::SPBuilder::sp_connect( const std::vector< size_t >& sources, const std::vector< size_t >& targets ) -{ - connect_( sources, targets ); - // check if any exceptions have been raised - for ( size_t tid = 0; tid < kernel().vp_manager.get_num_threads(); ++tid ) + void nest::SPBuilder::sp_connect( const std::vector< size_t >& sources, const std::vector< size_t >& targets ) { - if ( exceptions_raised_.at( tid ).get() ) + connect_( sources, targets ); + + // check if any exceptions have been raised + for ( size_t tid = 0; tid < kernel().vp_manager.get_num_threads(); ++tid ) { - throw WrappedThreadException( *( exceptions_raised_.at( tid ) ) ); + if ( exceptions_raised_.at( tid ).get() ) + { + throw WrappedThreadException( *( exceptions_raised_.at( tid ) ) ); + } } } -} - -void -nest::SPBuilder::connect_() -{ - throw NotImplemented( "Connection without structural plasticity is not possible for this connection builder." ); -} -/** - * In charge of dynamically creating the new synapses - */ -void -nest::SPBuilder::connect_( NodeCollectionPTR, NodeCollectionPTR ) -{ - throw NotImplemented( "Connection without structural plasticity is not possible for this connection builder." ); -} + void nest::SPBuilder::connect_() + { + throw NotImplemented( "Connection without structural plasticity is not possible for this connection builder." ); + } -void -nest::SPBuilder::connect_( const std::vector< size_t >& sources, const std::vector< size_t >& targets ) -{ - // Code copied and adapted from OneToOneBuilder::connect_() - // make sure that target and source population have the same size - if ( sources.size() != targets.size() ) + /** + * In charge of dynamically creating the new synapses + */ + void nest::SPBuilder::connect_( NodeCollectionPTR, NodeCollectionPTR ) { - throw DimensionMismatch( "Source and target population must be of the same size." ); + throw NotImplemented( "Connection without structural plasticity is not possible for this connection builder." ); } -#pragma omp parallel + void nest::SPBuilder::connect_( const std::vector< size_t >& sources, const std::vector< size_t >& targets ) { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); + // Code copied and adapted from OneToOneBuilder::connect_() + // make sure that target and source population have the same size + if ( sources.size() != targets.size() ) + { + throw DimensionMismatch( "Source and target population must be of the same size." ); + } - try +#pragma omp parallel { - RngPtr rng = get_vp_specific_rng( tid ); + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); - std::vector< size_t >::const_iterator tnode_id_it = targets.begin(); - std::vector< size_t >::const_iterator snode_id_it = sources.begin(); - for ( ; tnode_id_it != targets.end(); ++tnode_id_it, ++snode_id_it ) + try { - assert( snode_id_it != sources.end() ); + RngPtr rng = get_vp_specific_rng( tid ); - if ( *snode_id_it == *tnode_id_it and not allow_autapses_ ) + std::vector< size_t >::const_iterator tnode_id_it = targets.begin(); + std::vector< size_t >::const_iterator snode_id_it = sources.begin(); + for ( ; tnode_id_it != targets.end(); ++tnode_id_it, ++snode_id_it ) { - continue; - } + assert( snode_id_it != sources.end() ); - if ( not change_connected_synaptic_elements( *snode_id_it, *tnode_id_it, tid, 1 ) ) - { - skip_conn_parameter_( tid ); - continue; - } - Node* const target = kernel().node_manager.get_node_or_proxy( *tnode_id_it, tid ); + if ( *snode_id_it == *tnode_id_it and not allow_autapses_ ) + { + continue; + } - single_connect_( *snode_id_it, *target, tid, rng ); + if ( not change_connected_synaptic_elements( *snode_id_it, *tnode_id_it, tid, 1 ) ) + { + skip_conn_parameter_( tid ); + continue; + } + Node* const target = kernel().node_manager.get_node_or_proxy( *tnode_id_it, tid ); + + single_connect_( *snode_id_it, *target, tid, rng ); + } + } + 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 ) ); } - } - 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 ) ); } } -} diff --git a/nestkernel/conn_builder.h b/nestkernel/conn_builder.h index c5edfd2ccb..41777d4688 100644 --- a/nestkernel/conn_builder.h +++ b/nestkernel/conn_builder.h @@ -35,6 +35,9 @@ #include #include +// Includes from libnestutil +#include "block_vector.h" + // Includes from nestkernel: #include "conn_parameter.h" #include "nest_time.h" @@ -50,9 +53,13 @@ namespace nest class Node; class ConnParameter; class SparseNodeArray; +class BipartiteConnBuilder; +class ThirdInBuilder; +class ThirdOutBuilder; + /** - * Abstract base class for ConnBuilders. + * Abstract base class for Bipartite ConnBuilders which form the components of ConnBuilder. * * The base class extracts and holds parameters and provides * the connect interface. Derived classes implement the connect @@ -60,8 +67,7 @@ class SparseNodeArray; * * @note Naming classes *Builder to avoid name confusion with Connector classes. */ - -class ConnBuilder +class BipartiteConnBuilder { public: //! Connect with or without structural plasticity @@ -70,18 +76,12 @@ class ConnBuilder //! Delete synapses with or without structural plasticity virtual void disconnect(); - ConnBuilder( NodeCollectionPTR sources, + BipartiteConnBuilder( NodeCollectionPTR sources, NodeCollectionPTR targets, + ThirdOutBuilder* third_out, const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_specs ); - virtual ~ConnBuilder(); - - /** - * Mark ConnBuilder subclasses as building tripartite rules or not. - * - * @note This flag is required for template specialisation of ConnBuilderFactory's. - */ - static constexpr bool is_tripartite = false; + virtual ~BipartiteConnBuilder(); size_t get_synapse_model() const @@ -207,6 +207,8 @@ class ConnBuilder NodeCollectionPTR sources_; NodeCollectionPTR targets_; + ThirdOutBuilder* third_out_; + bool allow_autapses_; bool allow_multapses_; bool make_symmetric_; @@ -291,7 +293,140 @@ class ConnBuilder void reset_delays_(); }; -class OneToOneBuilder : public ConnBuilder +// not expected to be subclassed further +class ThirdInBuilder : public BipartiteConnBuilder +{ +public: + ThirdInBuilder( NodeCollectionPTR, + NodeCollectionPTR, + BipartiteConnBuilder*, + const DictionaryDatum&, // only for compatibility with BCB + const std::vector< DictionaryDatum >& ); + +private: + void connect_() override; + + ThirdOutBuilder* third_out_; +}; + +// to be subclassed further +class ThirdOutBuilder : public BipartiteConnBuilder +{ +public: + ThirdOutBuilder( NodeCollectionPTR, + NodeCollectionPTR, + const DictionaryDatum&, // only for compatibility with BCB + const std::vector< DictionaryDatum >& ); + + void connect() override; + + virtual void third_connect( size_t source_gid, Node& target ) = 0; +}; + + +/** + * Class representing a connection builder which may be bi- or tripartite. + * + * A ConnBuilder alwyas has a primary BipartiteConnBuilder. It additionally can have a pair of third_in and third_out + * Bipartite builders, where the third_in builder must perform one-to-one connections on given source-third pairs. + */ +class ConnBuilder +{ +public: + /** + * Constructor for bipartite connection + * + * @param primary_rule Name of conn rule for primary connection + * @param sources Source population for primary connection + * @param targets Target population for primary connection + * @param conn_spec Connection specification dictionary for tripartite bernoulli rule + * @param syn_spec Dictionary of synapse specification + */ + ConnBuilder( const std::string& primary_rule, + NodeCollectionPTR sources, + NodeCollectionPTR targets, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_specs ); + + /** + * Constructor for tripartite connection + * + * @param primary_rule Name of conn rule for primary connection + * @param third_rule Name of conn rule for third-factor connection + * @param sources Source population for primary connection + * @param targets Target population for primary connection + * @param third Third-party population + * @param conn_spec Connection specification dictionary for tripartite bernoulli rule + * @param syn_specs Dictionary of synapse specifications for the three connections that may be created. Allowed keys + * are `"primary"`, `"third_in"`, `"third_out"` + */ + ConnBuilder( const std::string& primary_rule, + const std::string& third_rule, + NodeCollectionPTR sources, + NodeCollectionPTR targets, + NodeCollectionPTR third, + const DictionaryDatum& conn_spec, + const std::map< Name, std::vector< DictionaryDatum > >& syn_specs ); + + ~ConnBuilder(); + + //! Connect with or without structural plasticity + void connect(); + + //! Delete synapses with or without structural plasticity + void disconnect(); + +private: + // order of declarations based on dependencies + ThirdOutBuilder* third_out_builder_; + ThirdInBuilder third_in_builder_; + BipartiteConnBuilder* primary_builder_; +}; + + +class ThirdBernoulliWithPoolBuilder : public ThirdOutBuilder +{ +public: + ThirdBernoulliWithPoolBuilder( NodeCollectionPTR, + NodeCollectionPTR, + const DictionaryDatum&, // only for compatibility with BCB + const std::vector< DictionaryDatum >& ); + + void third_connect( size_t source_gid, Node& target ) override; + +private: + size_t get_first_pool_index_( const size_t target_index ) const; + + double p_; + bool random_pool_; + size_t pool_size_; + size_t targets_per_third_; + std::vector< Node* > previous_target_; // TODO: cache thrashing possibility + std::vector< std::vector< NodeIDTriple >* > pool_; // outer: threads + + struct SourceThirdInfo_ + { + SourceThirdInfo_( size_t src, size_t trd, size_t rank ) + : source_gid( src ) + , third_gid( trd ) + , third_rank( rank ) + { + } + + size_t source_gid; + size_t third_gid; + size_t third_rank; + }; + + //! source-thirdparty GID pairs to be communicated; one per thread + std::vector< BlockVector< SourceThirdInfo_ >* > source_third_gids_; + + //! number of source-third pairs to send. Outer dimension writing thread, inner dimension rank to send to + std::vector< std::vector< size_t >* > source_third_counts_; +}; + + +class OneToOneBuilder : public BipartiteConnBuilder { public: OneToOneBuilder( NodeCollectionPTR sources, @@ -338,14 +473,14 @@ class OneToOneBuilder : public ConnBuilder void sp_disconnect_() override; }; -class AllToAllBuilder : public ConnBuilder +class AllToAllBuilder : public BipartiteConnBuilder { public: AllToAllBuilder( NodeCollectionPTR sources, NodeCollectionPTR targets, const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_specs ) - : ConnBuilder( sources, targets, conn_spec, syn_specs ) + : BipartiteConnBuilder( sources, targets, conn_spec, syn_specs ) { } @@ -392,7 +527,7 @@ class AllToAllBuilder : public ConnBuilder }; -class FixedInDegreeBuilder : public ConnBuilder +class FixedInDegreeBuilder : public BipartiteConnBuilder { public: FixedInDegreeBuilder( NodeCollectionPTR, @@ -408,7 +543,7 @@ class FixedInDegreeBuilder : public ConnBuilder ParameterDatum indegree_; }; -class FixedOutDegreeBuilder : public ConnBuilder +class FixedOutDegreeBuilder : public BipartiteConnBuilder { public: FixedOutDegreeBuilder( NodeCollectionPTR, @@ -423,7 +558,7 @@ class FixedOutDegreeBuilder : public ConnBuilder ParameterDatum outdegree_; }; -class FixedTotalNumberBuilder : public ConnBuilder +class FixedTotalNumberBuilder : public BipartiteConnBuilder { public: FixedTotalNumberBuilder( NodeCollectionPTR, @@ -438,7 +573,7 @@ class FixedTotalNumberBuilder : public ConnBuilder long N_; }; -class BernoulliBuilder : public ConnBuilder +class BernoulliBuilder : public BipartiteConnBuilder { public: BernoulliBuilder( NodeCollectionPTR, @@ -454,7 +589,7 @@ class BernoulliBuilder : public ConnBuilder ParameterDatum p_; //!< connection probability }; -class PoissonBuilder : public ConnBuilder +class PoissonBuilder : public BipartiteConnBuilder { public: PoissonBuilder( NodeCollectionPTR, NodeCollectionPTR, const DictionaryDatum&, const std::vector< DictionaryDatum >& ); @@ -474,7 +609,7 @@ class PoissonBuilder : public ConnBuilder * it maintains an AuxiliaryBuilder which handles the parameterization of the corresponding * third-party connection. */ -class AuxiliaryBuilder : public ConnBuilder +class AuxiliaryBuilder : public BipartiteConnBuilder { public: AuxiliaryBuilder( NodeCollectionPTR, @@ -494,55 +629,7 @@ class AuxiliaryBuilder : public ConnBuilder } }; -/** - * Class representing tripartite Bernoulli connector - * - * For each source-target pair, a Bernoulli trial is performed. If a primary connection is created, a third-factor - * connection is created conditionally on a second Bernoulli trial. The third-party neuron to be connected is - * chosen from a pool, which can either be set up in blocks or randomized. The third-party neuron receives - * input from the source neuron and provides output to the target neuron of the primary connection. - */ -class TripartiteBernoulliWithPoolBuilder : public ConnBuilder -{ -public: - /** - * Constructor - * - * @param sources Source population for primary connection - * @param targets Target population for primary connection - * @param third Third-party population - * @param conn_spec Connection specification dictionary for tripartite bernoulli rule - * @param syn_specs Dictionary of synapse specifications for the three connections that may be created. Allowed keys - * are `"primary"`, `"third_in"`, `"third_out"` - */ - TripartiteBernoulliWithPoolBuilder( NodeCollectionPTR sources, - NodeCollectionPTR targets, - NodeCollectionPTR third, - const DictionaryDatum& conn_spec, - const std::map< Name, std::vector< DictionaryDatum > >& syn_specs ); - - static constexpr bool is_tripartite = true; - -protected: - void connect_() override; - -private: - //! 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; - - NodeCollectionPTR third_; - - AuxiliaryBuilder third_in_builder_; - AuxiliaryBuilder third_out_builder_; - - double p_primary_; //!< connection probability for pre-post connections - double p_third_if_primary_; //!< probability of third-factor connection if primary connection created - bool random_pool_; //!< if true, select astrocyte pool at random - size_t pool_size_; //!< size of third-factor pool - size_t targets_per_third_; //!< target nodes per third-factor node -}; - -class SymmetricBernoulliBuilder : public ConnBuilder +class SymmetricBernoulliBuilder : public BipartiteConnBuilder { public: SymmetricBernoulliBuilder( NodeCollectionPTR, @@ -563,7 +650,7 @@ class SymmetricBernoulliBuilder : public ConnBuilder double p_; //!< connection probability }; -class SPBuilder : public ConnBuilder +class SPBuilder : public BipartiteConnBuilder { public: /** @@ -620,7 +707,7 @@ class SPBuilder : public ConnBuilder //! The name of the SPBuilder; used to identify its properties in the structural_plasticity_synapses kernel attributes std::string name_; - using ConnBuilder::connect_; + using BipartiteConnBuilder::connect_; void connect_() override; void connect_( NodeCollectionPTR sources, NodeCollectionPTR targets ); @@ -634,7 +721,7 @@ class SPBuilder : public ConnBuilder }; inline void -ConnBuilder::register_parameters_requiring_skipping_( ConnParameter& param ) +BipartiteConnBuilder::register_parameters_requiring_skipping_( ConnParameter& param ) { if ( param.is_array() ) { @@ -643,7 +730,7 @@ ConnBuilder::register_parameters_requiring_skipping_( ConnParameter& param ) } inline void -ConnBuilder::skip_conn_parameter_( size_t target_thread, size_t n_skip ) +BipartiteConnBuilder::skip_conn_parameter_( size_t target_thread, size_t n_skip ) { for ( std::vector< ConnParameter* >::iterator it = parameters_requiring_skipping_.begin(); it != parameters_requiring_skipping_.end(); diff --git a/nestkernel/conn_builder_factory.h b/nestkernel/conn_builder_factory.h index a9937611a5..a4303453d4 100644 --- a/nestkernel/conn_builder_factory.h +++ b/nestkernel/conn_builder_factory.h @@ -53,7 +53,7 @@ class GenericConnBuilderFactory /** * Factory method for builders for bipartite connection rules (the default). */ - virtual ConnBuilder* create( NodeCollectionPTR, + virtual BipartiteConnBuilder* create( NodeCollectionPTR, NodeCollectionPTR, const DictionaryDatum&, const std::vector< DictionaryDatum >& ) const = 0; @@ -61,7 +61,7 @@ class GenericConnBuilderFactory /** * Factory method for builders for tripartite connection rules. */ - virtual ConnBuilder* create( NodeCollectionPTR, + virtual BipartiteConnBuilder* create( NodeCollectionPTR, NodeCollectionPTR, NodeCollectionPTR, const DictionaryDatum&, @@ -80,7 +80,7 @@ template < typename ConnBuilderType, bool is_tripartite = ConnBuilderType::is_tr class ConnBuilderFactory : public GenericConnBuilderFactory { public: - ConnBuilder* + BipartiteConnBuilder* create( NodeCollectionPTR sources, NodeCollectionPTR targets, const DictionaryDatum& conn_spec, @@ -90,7 +90,7 @@ class ConnBuilderFactory : public GenericConnBuilderFactory } //! create tripartite builder - ConnBuilder* + BipartiteConnBuilder* create( NodeCollectionPTR sources, NodeCollectionPTR targets, NodeCollectionPTR third, @@ -106,7 +106,7 @@ class ConnBuilderFactory : public GenericConnBuilderFactory template < typename ConnBuilderType > class ConnBuilderFactory< ConnBuilderType, false > : public GenericConnBuilderFactory { - ConnBuilder* + BipartiteConnBuilder* create( NodeCollectionPTR sources, NodeCollectionPTR targets, const DictionaryDatum& conn_spec, @@ -115,7 +115,7 @@ class ConnBuilderFactory< ConnBuilderType, false > : public GenericConnBuilderFa return new ConnBuilderType( sources, targets, conn_spec, syn_specs ); } - ConnBuilder* + BipartiteConnBuilder* create( NodeCollectionPTR sources, NodeCollectionPTR targets, NodeCollectionPTR third, @@ -131,7 +131,7 @@ class ConnBuilderFactory< ConnBuilderType, false > : public GenericConnBuilderFa template < typename ConnBuilderType > class ConnBuilderFactory< ConnBuilderType, true > : public GenericConnBuilderFactory { - ConnBuilder* + BipartiteConnBuilder* create( NodeCollectionPTR sources, NodeCollectionPTR targets, const DictionaryDatum& conn_spec, @@ -141,7 +141,7 @@ class ConnBuilderFactory< ConnBuilderType, true > : public GenericConnBuilderFac String::compose( "Connection rule %1 only supports tripartite connections.", ( *conn_spec )[ names::rule ] ) ); } - ConnBuilder* + BipartiteConnBuilder* create( NodeCollectionPTR sources, NodeCollectionPTR targets, NodeCollectionPTR third, diff --git a/nestkernel/conn_builder_impl.h b/nestkernel/conn_builder_impl.h index 3c9a390ac6..385bb12ec8 100644 --- a/nestkernel/conn_builder_impl.h +++ b/nestkernel/conn_builder_impl.h @@ -33,7 +33,7 @@ namespace nest { inline void -ConnBuilder::single_disconnect_( size_t snode_id, Node& target, size_t target_thread ) +BipartiteConnBuilder::single_disconnect_( size_t snode_id, Node& target, size_t target_thread ) { // index tnode_id = target.get_node_id(); // This is the most simple case in which only the synapse_model_ has been diff --git a/nestkernel/connection_manager.cpp b/nestkernel/connection_manager.cpp index 7345f07193..a22ec86550 100644 --- a/nestkernel/connection_manager.cpp +++ b/nestkernel/connection_manager.cpp @@ -349,7 +349,7 @@ nest::ConnectionManager::get_user_set_delay_extrema() const return user_set_delay_extrema; } -nest::ConnBuilder* +nest::BipartiteConnBuilder* nest::ConnectionManager::get_conn_builder( const std::string& name, NodeCollectionPTR sources, NodeCollectionPTR targets, @@ -357,12 +357,12 @@ nest::ConnectionManager::get_conn_builder( const std::string& name, const std::vector< DictionaryDatum >& syn_specs ) { const size_t rule_id = connruledict_->lookup( name ); - ConnBuilder* cb = connbuilder_factories_.at( rule_id )->create( sources, targets, conn_spec, syn_specs ); + BipartiteConnBuilder* cb = connbuilder_factories_.at( rule_id )->create( sources, targets, conn_spec, syn_specs ); assert( cb ); return cb; } -nest::ConnBuilder* +nest::BipartiteConnBuilder* nest::ConnectionManager::get_conn_builder( const std::string& name, NodeCollectionPTR sources, NodeCollectionPTR targets, @@ -370,8 +370,34 @@ nest::ConnectionManager::get_conn_builder( const std::string& name, const DictionaryDatum& conn_spec, const std::map< Name, std::vector< DictionaryDatum > >& syn_specs ) { + if ( not connruledict_->known( name ) ) + { + throw BadProperty( String::compose( "Unknown connection rule: %1", name ) ); + } + + const size_t rule_id = connruledict_->lookup( name ); + BipartiteConnBuilder* cb = + connbuilder_factories_.at( rule_id )->create( sources, targets, third, conn_spec, syn_specs ); + assert( cb ); + return cb; +} + +nest::BipartiteConnBuilder* +nest::ConnectionManager::get_third_conn_builder( const std::string& name, + NodeCollectionPTR sources, + NodeCollectionPTR targets, + NodeCollectionPTR third, + const DictionaryDatum& conn_spec, + const std::map< Name, std::vector< DictionaryDatum > >& syn_specs ) +{ + if ( not thirdconnruledict_->known( name ) ) + { + throw BadProperty( String::compose( "Unknown third-factor connection rule: %1", name ) ); + } + const size_t rule_id = connruledict_->lookup( name ); - ConnBuilder* cb = connbuilder_factories_.at( rule_id )->create( sources, targets, third, conn_spec, syn_specs ); + BipartiteConnBuilder* cb = + connbuilder_factories_.at( rule_id )->create( sources, targets, third, conn_spec, syn_specs ); assert( cb ); return cb; } @@ -418,7 +444,7 @@ nest::ConnectionManager::connect( NodeCollectionPTR sources, throw BadProperty( String::compose( "Unknown connection rule: %1", rule_name ) ); } - ConnBuilder* cb = get_conn_builder( rule_name, sources, targets, conn_spec, syn_specs ); + ConnBuilder cb( sources, targets, conn_spec, syn_specs ); // at this point, all entries in conn_spec and syn_spec have been checked ALL_ENTRIES_ACCESSED( *conn_spec, "Connect", "Unread dictionary entries in conn_spec: " ); @@ -430,8 +456,7 @@ nest::ConnectionManager::connect( NodeCollectionPTR sources, // Set flag before calling cb->connect() in case exception is thrown after some connections have been created. set_connections_have_changed(); - cb->connect(); - delete cb; + cb.connect(); } @@ -804,14 +829,15 @@ nest::ConnectionManager::connect_tripartite( NodeCollectionPTR sources, { throw BadProperty( "The connection specification must contain a connection rule." ); } - const std::string rule_name = static_cast< const std::string >( ( *conn_spec )[ names::rule ] ); - - if ( not connruledict_->known( rule_name ) ) + if ( not third_conn_spec->known( names::rule ) ) { - throw BadProperty( String::compose( "Unknown connection rule: %1", rule_name ) ); + throw BadProperty( "The third-factor connection specification must contain a connection rule." ); } - ConnBuilder* cb = get_conn_builder( rule_name, sources, targets, third, conn_spec, syn_specs ); + const std::string primary_rule = static_cast< const std::string >( ( *conn_spec )[ names::rule ] ); + const std::string third_rule = static_cast< const std::string >( ( *conn_spec )[ names::third_rule ] ); + + ConnBuilder cb( primary_rule, third_rule, sources, targets, third, conn_spec, syn_specs ); // at this point, all entries in conn_spec and syn_spec have been checked ALL_ENTRIES_ACCESSED( *conn_spec, "Connect", "Unread dictionary entries in conn_spec: " ); @@ -826,8 +852,7 @@ nest::ConnectionManager::connect_tripartite( NodeCollectionPTR sources, // Set flag before calling cb->connect() in case exception is thrown after some connections have been created. set_connections_have_changed(); - cb->connect(); - delete cb; + cb.connect(); } diff --git a/nestkernel/connection_manager.h b/nestkernel/connection_manager.h index 315e2a1305..e3e2fc41e8 100644 --- a/nestkernel/connection_manager.h +++ b/nestkernel/connection_manager.h @@ -95,14 +95,14 @@ class ConnectionManager : public ManagerInterface void register_conn_builder( const std::string& name ); //! Obtain builder for bipartite connections - ConnBuilder* get_conn_builder( const std::string& name, + BipartiteConnBuilder* get_conn_builder( const std::string& name, NodeCollectionPTR sources, NodeCollectionPTR targets, const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_specs ); //! Obtain builder for tripartite connections - ConnBuilder* get_conn_builder( const std::string& name, + BipartiteConnBuilder* get_conn_builder( const std::string& name, NodeCollectionPTR sources, NodeCollectionPTR targets, NodeCollectionPTR third, diff --git a/nestkernel/sp_manager.cpp b/nestkernel/sp_manager.cpp index 345ac6b9c2..9c205c39ac 100644 --- a/nestkernel/sp_manager.cpp +++ b/nestkernel/sp_manager.cpp @@ -237,7 +237,7 @@ SPManager::disconnect( NodeCollectionPTR sources, } } - ConnBuilder* cb = nullptr; + BipartiteConnBuilder* cb = nullptr; conn_spec->clear_access_flags(); syn_spec->clear_access_flags(); From 4f34121c941236e27ff1db25b4e0db302ed2553a Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Fri, 23 Feb 2024 12:21:40 +0100 Subject: [PATCH 02/86] Code now compiles, not complete --- nestkernel/conn_builder.cpp | 2183 +++++++++++++------------- nestkernel/conn_builder.h | 34 +- nestkernel/conn_builder_factory.h | 86 +- nestkernel/connection_manager.cpp | 52 +- nestkernel/connection_manager.h | 21 +- nestkernel/connection_manager_impl.h | 12 + nestkernel/nest.cpp | 4 +- nestkernel/nest.h | 1 + nestkernel/nestmodule.cpp | 22 +- nestkernel/nestmodule.h | 2 +- nestkernel/sp_manager.cpp | 7 + 11 files changed, 1186 insertions(+), 1238 deletions(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index 0066a35b93..cb46685068 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -28,6 +28,7 @@ // Includes from nestkernel: #include "conn_builder_impl.h" #include "conn_parameter.h" +#include "connection_manager.h" #include "exceptions.h" #include "kernel_manager.h" #include "nest_names.h" @@ -49,14 +50,13 @@ nest::ConnBuilder::ConnBuilder( const std::string& primary_rule, const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_specs ) : third_out_builder_( nullptr ) - , - , third_in_builder_() - , primary_builder_( get_conn_builder( rule_name, + , third_in_builder_( nullptr ) + , primary_builder_( kernel().connection_manager.get_conn_builder( primary_rule, sources, targets, third_out_builder_, conn_spec, - const_cast< std::map< Name, std::vector< DictionaryDatum > >& >( syn_specs )[ names::primary ] ) ) + syn_specs ) ) { } @@ -68,18 +68,18 @@ nest::ConnBuilder::ConnBuilder( const std::string& primary_rule, const DictionaryDatum& conn_spec, const DictionaryDatum& third_conn_spec, const std::map< Name, std::vector< DictionaryDatum > >& syn_specs ) - : third_out_builder_( get_third_conn_builder( third_rule, + : third_out_builder_( kernel().connection_manager.get_third_conn_builder( third_rule, targets, third, third_conn_spec, // const_cast here seems required, clang complains otherwise; try to clean up when Datums disappear const_cast< std::map< Name, std::vector< DictionaryDatum > >& >( syn_specs )[ names::third_out ] ) ) - , third_in_builder_( sources, + , third_in_builder_( new ThirdInBuilder( sources, third, - *third_out_builder_, + third_out_builder_, third_conn_spec, - const_cast< std::map< Name, std::vector< DictionaryDatum > >& >( syn_specs )[ names::third_in ] ) - , primary_builder_( get_conn_builder( rule_name, + const_cast< std::map< Name, std::vector< DictionaryDatum > >& >( syn_specs )[ names::third_in ] ) ) + , primary_builder_( kernel().connection_manager.get_conn_builder( primary_rule, sources, targets, third_out_builder_, @@ -102,7 +102,7 @@ void nest::ConnBuilder::connect() { primary_builder_->connect(); // triggers third_out_builder_ - third_in_builder_.connect(); + third_in_builder_->connect(); } void @@ -118,8 +118,8 @@ nest::ConnBuilder::disconnect() nest::BipartiteConnBuilder::BipartiteConnBuilder( NodeCollectionPTR sources, NodeCollectionPTR targets, + ThirdOutBuilder* third_out, const DictionaryDatum& conn_spec, - ThirdOutBuilder* third_out_, const std::vector< DictionaryDatum >& syn_specs ) : sources_( sources ) , targets_( targets ) @@ -645,9 +645,9 @@ nest::BipartiteConnBuilder::reset_delays_() } } -nest::ThirdInBuilder::ThirdInBuilder( const NodeCollectionPTR sources, - const NodeCollectionPTR third, - BipartiteConnBuilder* third_out, +nest::ThirdInBuilder::ThirdInBuilder( NodeCollectionPTR sources, + NodeCollectionPTR third, + ThirdOutBuilder* third_out, const DictionaryDatum& third_conn_spec, const std::vector< DictionaryDatum >& syn_specs ) : BipartiteConnBuilder( sources, third, nullptr, third_conn_spec, syn_specs ) @@ -677,14 +677,13 @@ nest::ThirdOutBuilder::connect() nest::ThirdBernoulliWithPoolBuilder::ThirdBernoulliWithPoolBuilder( const NodeCollectionPTR third, const NodeCollectionPTR targets, - const DictionaryDatum& third_conn_spec, + const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_specs ) - : ThirdOutBuilder( third, targets, nullptr, third_conn_spec, syn_specs ) + : ThirdOutBuilder( third, targets, conn_spec, syn_specs ) , p_( 1.0 ) , random_pool_( true ) , pool_size_( third->size() ) , targets_per_third_( targets->size() / third->size() ) - , , previous_target_( kernel().vp_manager.get_num_threads(), nullptr ) { updateValue< double >( conn_spec, names::p, p_ ); @@ -729,13 +728,14 @@ nest::ThirdBernoulliWithPoolBuilder::ThirdBernoulliWithPoolBuilder( const NodeCo #pragma omp parallel { const size_t thrd = kernel().vp_manager.get_thread_id(); - pool_[ thrd ] = new BlockVector< NodeIDTriple >(); + pool_[ thrd ] = new std::vector< NodeIDTriple >(); source_third_gids_[ thrd ] = new BlockVector< SourceThirdInfo_ >(); - source_third_counts_[ thrd ] = new std::vector< size_t >( KernelManager().mpi_manager.get_num_processes(), 0 ); + source_third_counts_[ thrd ] = new std::vector< size_t >( kernel().mpi_manager.get_num_processes(), 0 ); } } -void nest::ThirdBernoulliWithPoolBuilder::third_connect( < #size_t source_gid # >, < #Node & target # > ) +void +nest::ThirdBernoulliWithPoolBuilder::third_connect( size_t primary_source_id, Node& primary_target ) { // We assume target is on this thread const size_t tid = kernel().vp_manager.get_thread_id(); @@ -749,1262 +749,1201 @@ void nest::ThirdBernoulliWithPoolBuilder::third_connect( < #size_t source_gid # } // step 2, build pool if new target - if ( &target != previous_target_[ tid ] ) + if ( &primary_target != previous_target_[ tid ] ) { pool_[ tid ]->reserve( pool_size_ ); if ( random_pool_ ) { - rng->sample( third_->begin(), third_->end(), std::back_inserter( *pool_[ tid ] ), pool_size_ ); + rng->sample( sources_->begin(), sources_->end(), std::back_inserter( *pool_[ tid ] ), pool_size_ ); } else { - std::copy_n( - third_->begin() + get_first_pool_index_( target.lid ), pool_size_, std::back_inserter( *pool[ tid ] ) ); + std::copy_n( sources_->begin() + get_first_pool_index_( primary_target.get_thread_lid() ), + pool_size_, + std::back_inserter( *( pool_[ tid ] ) ) ); } + } - // select third-factor neuron randomly from pool for this target - const auto third_index = pool_size_ == 1 ? 0 : rng->ulrand( pool_size_ ); - const auto third_node_id = ( *pool_[ tid ] )[ third_index ].node_id; - const auto third_node_rank = kernel().mpi_manager.get_process_id_of_vp( kernel().vp_manager.node_id_to_vp( third_node_id ); + // select third-factor neuron randomly from pool for this target + const auto third_index = pool_size_ == 1 ? 0 : rng->ulrand( pool_size_ ); + const auto third_node_id = ( *pool_[ tid ] )[ third_index ].node_id; + const auto third_node_rank = + kernel().mpi_manager.get_process_id_of_vp( kernel().vp_manager.node_id_to_vp( third_node_id ) ); - single_connect_( third_node_id, target, tid, rng ); + single_connect_( third_node_id, primary_target, tid, rng ); - source_third_gids_[ tid ]->emplace_back( { snode_id, third_node_id, third_node_rank } ); - ++((*source_third_counts_[tid])[third_node_rank]); - } + source_third_gids_[ tid ]->push_back( { primary_source_id, third_node_id, third_node_rank } ); + ++( ( *source_third_counts_[ tid ] )[ third_node_rank ] ); +} - size_t nest::ThirdBernoulliWithPoolBuilder::get_first_pool_index_( const size_t target_index ) const +size_t +nest::ThirdBernoulliWithPoolBuilder::get_first_pool_index_( const size_t target_index ) const +{ + if ( pool_size_ > 1 ) { - if ( pool_size_ > 1 ) - { - return target_index * pool_size_; - } - - return target_index / targets_per_third_; // intentional integer division + return target_index * pool_size_; } - void nest::TripartiteBernoulliWithPoolBuilder::connect_() + return target_index / targets_per_third_; // intentional integer division +} + + +nest::OneToOneBuilder::OneToOneBuilder( const NodeCollectionPTR sources, + const NodeCollectionPTR targets, + ThirdOutBuilder* third_out, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : BipartiteConnBuilder( sources, targets, third_out, conn_spec, syn_specs ) +{ + // make sure that target and source population have the same size + if ( sources_->size() != targets_->size() ) { + throw DimensionMismatch( "Source and Target population must be of the same size." ); + } +} + +void +nest::OneToOneBuilder::connect_() +{ + #pragma omp parallel + { + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); + + try { - const size_t tid = kernel().vp_manager.get_thread_id(); + RngPtr rng = get_vp_specific_rng( tid ); - try + if ( loop_over_targets_() ) { - /* Random number generators: - * - Use RNG generating same number sequence on all threads to decide which connections to create - * - Use per-thread random number generator to randomize connection properties - */ - RngPtr synced_rng = get_vp_synced_rng( tid ); - RngPtr rng = get_vp_specific_rng( tid ); + // A more efficient way of doing this might be to use NodeCollection's local_begin(). For this to work we + // would need to change some of the logic, sources and targets might not be on the same process etc., so + // therefore we are not doing it at the moment. This also applies to other ConnBuilders below. + NodeCollection::const_iterator target_it = targets_->begin(); + NodeCollection::const_iterator source_it = sources_->begin(); + for ( ; target_it < targets_->end(); ++target_it, ++source_it ) + { + assert( source_it < sources_->end() ); - binomial_distribution bino_dist; - binomial_distribution::param_type bino_param( sources_->size(), p_primary_ ); + const size_t snode_id = ( *source_it ).node_id; + const size_t tnode_id = ( *target_it ).node_id; - // Iterate through target neurons. For each, three steps are done: - // 1. draw indegree 2. select astrocyte pool 3. make connections - for ( const auto& target : *targets_ ) - { - const size_t tnode_id = target.node_id; - Node* target_node = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); - const bool local_target = not target_node->is_proxy(); + if ( snode_id == tnode_id and not allow_autapses_ ) + { + continue; + } - // step 1, draw indegree for this target - const auto indegree = bino_dist( synced_rng, bino_param ); - if ( indegree == 0 ) + Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); + if ( target->is_proxy() ) { - continue; // no connections for this target + // skip array parameters handled in other virtual processes + skip_conn_parameter_( tid ); + continue; } + single_connect_( snode_id, *target, tid, rng ); + } + } + 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 ) + { + Node* target = n->get_node(); - // step 3, iterate through indegree to make connections for this target - // - by construction, we cannot get multapses - // - if the target is also among sources, it can be drawn at most once; - // we ignore it then connecting if no autapses are wanted - std::vector< NodeIDTriple > sources_to_connect_; - sources_to_connect_.reserve( indegree ); - synced_rng->sample( sources_->begin(), sources_->end(), std::back_inserter( sources_to_connect_ ), indegree ); + const size_t tnode_id = n->get_node_id(); + const long lid = targets_->get_lid( tnode_id ); + if ( lid < 0 ) // Is local node in target list? + { + continue; + } - for ( const auto source : sources_to_connect_ ) + // one-to-one, thus we can use target idx for source as well + const size_t snode_id = ( *sources_ )[ lid ]; + if ( not allow_autapses_ and snode_id == tnode_id ) { - const auto snode_id = source.node_id; - if ( not allow_autapses_ and snode_id == tnode_id ) - { - continue; - } - - if ( local_target ) - { - // plain connect now with thread-local rng for randomized parameters - single_connect_( snode_id, *target_node, tid, rng ); - } - - // conditionally connect third factor - if ( not( synced_rng->drand() < p_third_if_primary_ ) ) - { - continue; - } - - // select third-factor neuron randomly from pool for this target - const auto third_index = pool_size_ == 1 ? 0 : synced_rng->ulrand( pool_size_ ); - const auto third_node_id = pool[ third_index ].node_id; - Node* third_node = kernel().node_manager.get_node_or_proxy( third_node_id, tid ); - const bool local_third_node = not third_node->is_proxy(); - - if ( local_third_node ) - { - // route via auxiliary builder who handles parameters - third_in_builder_.single_connect( snode_id, *third_node, tid, rng ); - } - - // connection third-factor node to target if local - if ( local_target ) - { - // route via auxiliary builder who handles parameters - third_out_builder_.single_connect( third_node_id, *target_node, tid, rng ); - } + // no skipping required / possible, + // as we iterate only over local nodes + continue; } + single_connect_( snode_id, *target, tid, rng ); } } - 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 ) ); - } + } + 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 ) ); } } +} +void +nest::OneToOneBuilder::disconnect_() +{ - nest::OneToOneBuilder::OneToOneBuilder( const NodeCollectionPTR sources, - const NodeCollectionPTR targets, - const DictionaryDatum& conn_spec, - const std::vector< DictionaryDatum >& syn_specs ) - : BipartiteConnBuilder( sources, targets, conn_spec, syn_specs ) +#pragma omp parallel { - // make sure that target and source population have the same size - if ( sources_->size() != targets_->size() ) + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); + + try + { + NodeCollection::const_iterator target_it = targets_->begin(); + NodeCollection::const_iterator source_it = sources_->begin(); + for ( ; target_it < targets_->end(); ++target_it, ++source_it ) + { + assert( source_it < sources_->end() ); + + const size_t tnode_id = ( *target_it ).node_id; + const size_t snode_id = ( *source_it ).node_id; + + // check whether the target is on this mpi machine + if ( not kernel().node_manager.is_local_node_id( tnode_id ) ) + { + // Disconnecting: no parameter skipping required + continue; + } + + Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); + const size_t target_thread = target->get_thread(); + + // check whether the target is a proxy + if ( target->is_proxy() ) + { + // Disconnecting: no parameter skipping required + continue; + } + single_disconnect_( snode_id, *target, target_thread ); + } + } + catch ( std::exception& err ) { - throw DimensionMismatch( "Source and Target population must be of the same size." ); + // 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 ) ); } } +} - void nest::OneToOneBuilder::connect_() - { +void +nest::OneToOneBuilder::sp_connect_() +{ #pragma omp parallel + { + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); + + try { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); + RngPtr rng = get_vp_specific_rng( tid ); - try + NodeCollection::const_iterator target_it = targets_->begin(); + NodeCollection::const_iterator source_it = sources_->begin(); + for ( ; target_it < targets_->end(); ++target_it, ++source_it ) { - RngPtr rng = get_vp_specific_rng( tid ); + assert( source_it < sources_->end() ); - if ( loop_over_targets_() ) + const size_t snode_id = ( *source_it ).node_id; + const size_t tnode_id = ( *target_it ).node_id; + + if ( snode_id == tnode_id and not allow_autapses_ ) { - // A more efficient way of doing this might be to use NodeCollection's local_begin(). For this to work we - // would need to change some of the logic, sources and targets might not be on the same process etc., so - // therefore we are not doing it at the moment. This also applies to other ConnBuilders below. - NodeCollection::const_iterator target_it = targets_->begin(); - NodeCollection::const_iterator source_it = sources_->begin(); - for ( ; target_it < targets_->end(); ++target_it, ++source_it ) - { - assert( source_it < sources_->end() ); + continue; + } + + if ( not change_connected_synaptic_elements( snode_id, tnode_id, tid, 1 ) ) + { + skip_conn_parameter_( tid ); + continue; + } + Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); + const size_t target_thread = target->get_thread(); - const size_t snode_id = ( *source_it ).node_id; - const size_t tnode_id = ( *target_it ).node_id; + single_connect_( snode_id, *target, target_thread, rng ); + } + } + 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 ) ); + } + } +} - if ( snode_id == tnode_id and not allow_autapses_ ) - { - continue; - } +void +nest::OneToOneBuilder::sp_disconnect_() +{ - 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; - } +#pragma omp parallel + { + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); - single_connect_( snode_id, *target, tid, rng ); - } - } - else + try + { + NodeCollection::const_iterator target_it = targets_->begin(); + NodeCollection::const_iterator source_it = sources_->begin(); + for ( ; target_it < targets_->end(); ++target_it, ++source_it ) + { + assert( source_it < sources_->end() ); + + const size_t snode_id = ( *source_it ).node_id; + const size_t tnode_id = ( *target_it ).node_id; + + if ( not change_connected_synaptic_elements( snode_id, tnode_id, tid, -1 ) ) { - 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 ) - { - Node* target = n->get_node(); - - const size_t tnode_id = n->get_node_id(); - const long lid = targets_->get_lid( tnode_id ); - if ( lid < 0 ) // Is local node in target list? - { - continue; - } - - // one-to-one, thus we can use target idx for source as well - const size_t snode_id = ( *sources_ )[ lid ]; - if ( not allow_autapses_ and snode_id == tnode_id ) - { - // no skipping required / possible, - // as we iterate only over local nodes - continue; - } - single_connect_( snode_id, *target, tid, rng ); - } + continue; } + + Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); + const size_t target_thread = target->get_thread(); + + single_disconnect_( snode_id, *target, target_thread ); } - 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 ) ); - } + } + 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 ) ); } } +} - void nest::OneToOneBuilder::disconnect_() - { +void +nest::AllToAllBuilder::connect_() +{ #pragma omp parallel + { + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); + + try { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); + RngPtr rng = get_vp_specific_rng( tid ); - try + if ( loop_over_targets_() ) { NodeCollection::const_iterator target_it = targets_->begin(); - NodeCollection::const_iterator source_it = sources_->begin(); - for ( ; target_it < targets_->end(); ++target_it, ++source_it ) + for ( ; target_it < targets_->end(); ++target_it ) { - assert( source_it < sources_->end() ); - const size_t tnode_id = ( *target_it ).node_id; - const size_t snode_id = ( *source_it ).node_id; - - // check whether the target is on this mpi machine - if ( not kernel().node_manager.is_local_node_id( tnode_id ) ) + Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); + if ( target->is_proxy() ) { - // Disconnecting: no parameter skipping required + skip_conn_parameter_( tid, sources_->size() ); continue; } - Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); - const size_t target_thread = target->get_thread(); + inner_connect_( tid, rng, target, tnode_id, true ); + } + } + 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(); - // check whether the target is a proxy - if ( target->is_proxy() ) + // Is the local node in the targets list? + if ( targets_->get_lid( tnode_id ) < 0 ) { - // Disconnecting: no parameter skipping required continue; } - single_disconnect_( snode_id, *target, target_thread ); + + inner_connect_( tid, rng, n->get_node(), tnode_id, false ); } } - 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 ) ); - } } + 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 ) ); + } + } +} + +void +nest::AllToAllBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, size_t tnode_id, bool skip ) +{ + const size_t target_thread = target->get_thread(); + + // check whether the target is on our thread + if ( static_cast< size_t >( tid ) != target_thread ) + { + if ( skip ) + { + skip_conn_parameter_( tid, sources_->size() ); + } + return; } - void nest::OneToOneBuilder::sp_connect_() + 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 ) + { + if ( skip ) + { + skip_conn_parameter_( target_thread ); + } + continue; + } + + single_connect_( snode_id, *target, target_thread, rng ); + } +} +void +nest::AllToAllBuilder::sp_connect_() +{ #pragma omp parallel + { + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); + try { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); + RngPtr rng = get_vp_specific_rng( tid ); - try + NodeCollection::const_iterator target_it = targets_->begin(); + for ( ; target_it < targets_->end(); ++target_it ) { - RngPtr rng = get_vp_specific_rng( tid ); + const size_t tnode_id = ( *target_it ).node_id; - NodeCollection::const_iterator target_it = targets_->begin(); NodeCollection::const_iterator source_it = sources_->begin(); - for ( ; target_it < targets_->end(); ++target_it, ++source_it ) + for ( ; source_it < sources_->end(); ++source_it ) { - assert( source_it < sources_->end() ); - const size_t snode_id = ( *source_it ).node_id; - const size_t tnode_id = ( *target_it ).node_id; - if ( snode_id == tnode_id and not allow_autapses_ ) + if ( not allow_autapses_ and snode_id == tnode_id ) { + skip_conn_parameter_( tid ); continue; } - if ( not change_connected_synaptic_elements( snode_id, tnode_id, tid, 1 ) ) { - skip_conn_parameter_( tid ); + skip_conn_parameter_( tid, sources_->size() ); continue; } Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); const size_t target_thread = target->get_thread(); - single_connect_( snode_id, *target, target_thread, rng ); } } - catch ( std::exception& err ) + } + 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 ) ); + } + } +} + +void +nest::AllToAllBuilder::disconnect_() +{ + +#pragma omp parallel + { + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); + + try + { + NodeCollection::const_iterator target_it = targets_->begin(); + for ( ; target_it < targets_->end(); ++target_it ) { - // 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 ) ); + const size_t tnode_id = ( *target_it ).node_id; + + // check whether the target is on this mpi machine + if ( not kernel().node_manager.is_local_node_id( tnode_id ) ) + { + // Disconnecting: no parameter skipping required + continue; + } + + Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); + const size_t target_thread = target->get_thread(); + + // check whether the target is a proxy + if ( target->is_proxy() ) + { + // Disconnecting: no parameter skipping required + continue; + } + + NodeCollection::const_iterator source_it = sources_->begin(); + for ( ; source_it < sources_->end(); ++source_it ) + { + const size_t snode_id = ( *source_it ).node_id; + single_disconnect_( snode_id, *target, target_thread ); + } } } + 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 ) ); + } } +} - void nest::OneToOneBuilder::sp_disconnect_() +void +nest::AllToAllBuilder::sp_disconnect_() +{ +#pragma omp parallel { + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); -#pragma omp parallel + try { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); - - try + NodeCollection::const_iterator target_it = targets_->begin(); + for ( ; target_it < targets_->end(); ++target_it ) { - NodeCollection::const_iterator target_it = targets_->begin(); + const size_t tnode_id = ( *target_it ).node_id; + NodeCollection::const_iterator source_it = sources_->begin(); - for ( ; target_it < targets_->end(); ++target_it, ++source_it ) + for ( ; source_it < sources_->end(); ++source_it ) { - assert( source_it < sources_->end() ); - const size_t snode_id = ( *source_it ).node_id; - const size_t tnode_id = ( *target_it ).node_id; if ( not change_connected_synaptic_elements( snode_id, tnode_id, tid, -1 ) ) { + // Disconnecting: no parameter skipping required continue; } - Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); const size_t target_thread = target->get_thread(); - single_disconnect_( snode_id, *target, target_thread ); } } - 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 ) ); - } + } + 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 ) ); } } +} - void nest::AllToAllBuilder::connect_() +nest::FixedInDegreeBuilder::FixedInDegreeBuilder( NodeCollectionPTR sources, + NodeCollectionPTR targets, + ThirdOutBuilder* third_out, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : BipartiteConnBuilder( sources, targets, third_out, conn_spec, syn_specs ) +{ + // check for potential errors + long n_sources = static_cast< long >( sources_->size() ); + if ( n_sources == 0 ) + { + throw BadProperty( "Source array must not be empty." ); + } + ParameterDatum* pd = dynamic_cast< ParameterDatum* >( ( *conn_spec )[ names::indegree ].datum() ); + if ( pd ) + { + indegree_ = *pd; + // TODO: Checks of parameter range + } + else { + // Assume indegree is a scalar + const long value = ( *conn_spec )[ names::indegree ]; + indegree_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); -#pragma omp parallel + // verify that indegree is not larger than source population if multapses are disabled + if ( not allow_multapses_ ) { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); - - try + if ( value > n_sources ) { - 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_conn_parameter_( tid, sources_->size() ); - continue; - } - - inner_connect_( tid, rng, target, tnode_id, true ); - } - } - 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, false ); - } - } + throw BadProperty( "Indegree cannot be larger than population size." ); } - catch ( std::exception& err ) + else if ( value == n_sources and not allow_autapses_ ) { - // 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 ) ); + LOG( M_WARNING, + "FixedInDegreeBuilder::connect", + "Multapses and autapses prohibited. When the sources and the targets " + "have a non-empty intersection, the connect algorithm will enter an infinite loop." ); + return; } - } - } - - void nest::AllToAllBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, size_t tnode_id, bool skip ) - { - const size_t target_thread = target->get_thread(); - // check whether the target is on our thread - if ( static_cast< size_t >( tid ) != target_thread ) - { - if ( skip ) + if ( value > 0.9 * n_sources ) { - skip_conn_parameter_( tid, sources_->size() ); + LOG( M_WARNING, + "FixedInDegreeBuilder::connect", + "Multapses are prohibited and you request more than 90% connectivity. Expect long connecting times!" ); } - return; - } + } // if (not allow_multapses_ ) - NodeCollection::const_iterator source_it = sources_->begin(); - for ( ; source_it < sources_->end(); ++source_it ) + if ( value < 0 ) { - const size_t snode_id = ( *source_it ).node_id; - - if ( not allow_autapses_ and snode_id == tnode_id ) - { - if ( skip ) - { - skip_conn_parameter_( target_thread ); - } - continue; - } - - single_connect_( snode_id, *target, target_thread, rng ); + throw BadProperty( "Indegree cannot be less than zero." ); } } +} - void nest::AllToAllBuilder::sp_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 ); - - NodeCollection::const_iterator target_it = targets_->begin(); - for ( ; target_it < targets_->end(); ++target_it ) - { - const size_t tnode_id = ( *target_it ).node_id; - - 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 ) - { - skip_conn_parameter_( tid ); - continue; - } - if ( not change_connected_synaptic_elements( snode_id, tnode_id, tid, 1 ) ) - { - skip_conn_parameter_( tid, sources_->size() ); - continue; - } - Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); - const size_t target_thread = target->get_thread(); - single_connect_( snode_id, *target, target_thread, rng ); - } - } - } - 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 ) ); - } - } - } +void +nest::FixedInDegreeBuilder::connect_() +{ - void nest::AllToAllBuilder::disconnect_() +#pragma omp parallel { + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); -#pragma omp parallel + try { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); + RngPtr rng = get_vp_specific_rng( tid ); - try + 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; - - // check whether the target is on this mpi machine - if ( not kernel().node_manager.is_local_node_id( tnode_id ) ) - { - // Disconnecting: no parameter skipping required - continue; - } - Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); - const size_t target_thread = target->get_thread(); - // check whether the target is a proxy + const long indegree_value = std::round( indegree_->value( rng, target ) ); if ( target->is_proxy() ) { - // Disconnecting: no parameter skipping required + // skip array parameters handled in other virtual processes + skip_conn_parameter_( tid, indegree_value ); continue; } - NodeCollection::const_iterator source_it = sources_->begin(); - for ( ; source_it < sources_->end(); ++source_it ) - { - const size_t snode_id = ( *source_it ).node_id; - single_disconnect_( snode_id, *target, target_thread ); - } + inner_connect_( tid, rng, target, tnode_id, true, indegree_value ); } } - 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 ) ); - } - } - } - - void nest::AllToAllBuilder::sp_disconnect_() - { -#pragma omp parallel - { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); - - try + else { - NodeCollection::const_iterator target_it = targets_->begin(); - for ( ; target_it < targets_->end(); ++target_it ) + 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 = ( *target_it ).node_id; + const size_t tnode_id = n->get_node_id(); - NodeCollection::const_iterator source_it = sources_->begin(); - for ( ; source_it < sources_->end(); ++source_it ) + // Is the local node in the targets list? + if ( targets_->get_lid( tnode_id ) < 0 ) { - const size_t snode_id = ( *source_it ).node_id; - - if ( not change_connected_synaptic_elements( snode_id, tnode_id, tid, -1 ) ) - { - // Disconnecting: no parameter skipping required - continue; - } - Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); - const size_t target_thread = target->get_thread(); - single_disconnect_( snode_id, *target, target_thread ); + continue; } + auto source = n->get_node(); + const long indegree_value = std::round( indegree_->value( rng, source ) ); + + inner_connect_( tid, rng, source, tnode_id, false, indegree_value ); } } - 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 ) ); - } + } + 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 ) ); } } +} - nest::FixedInDegreeBuilder::FixedInDegreeBuilder( NodeCollectionPTR sources, - NodeCollectionPTR targets, - const DictionaryDatum& conn_spec, - const std::vector< DictionaryDatum >& syn_specs ) - : BipartiteConnBuilder( sources, targets, conn_spec, syn_specs ) +void +nest::FixedInDegreeBuilder::inner_connect_( const int tid, + RngPtr rng, + Node* target, + size_t tnode_id, + bool skip, + long indegree_value ) +{ + const size_t target_thread = target->get_thread(); + + // check whether the target is on our thread + if ( static_cast< size_t >( tid ) != target_thread ) { - // check for potential errors - long n_sources = static_cast< long >( sources_->size() ); - if ( n_sources == 0 ) + // skip array parameters handled in other virtual processes + if ( skip ) { - throw BadProperty( "Source array must not be empty." ); + skip_conn_parameter_( tid, indegree_value ); } - ParameterDatum* pd = dynamic_cast< ParameterDatum* >( ( *conn_spec )[ names::indegree ].datum() ); - if ( pd ) - { - indegree_ = *pd; - // TODO: Checks of parameter range - } - else - { - // Assume indegree is a scalar - const long value = ( *conn_spec )[ names::indegree ]; - indegree_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); + return; + } - // verify that indegree is not larger than source population if multapses are disabled - if ( not allow_multapses_ ) - { - if ( value > n_sources ) - { - throw BadProperty( "Indegree cannot be larger than population size." ); - } - else if ( value == n_sources and not allow_autapses_ ) - { - LOG( M_WARNING, - "FixedInDegreeBuilder::connect", - "Multapses and autapses prohibited. When the sources and the targets " - "have a non-empty intersection, the connect algorithm will enter an infinite loop." ); - return; - } + std::set< long > ch_ids; + long n_rnd = sources_->size(); - if ( value > 0.9 * n_sources ) - { - LOG( M_WARNING, - "FixedInDegreeBuilder::connect", - "Multapses are prohibited and you request more than 90% connectivity. Expect long connecting times!" ); - } - } // if (not allow_multapses_ ) + for ( long j = 0; j < indegree_value; ++j ) + { + unsigned long s_id; + size_t snode_id; + bool skip_autapse = false; + bool skip_multapse = false; - if ( value < 0 ) - { - throw BadProperty( "Indegree cannot be less than zero." ); - } + do + { + s_id = rng->ulrand( n_rnd ); + snode_id = ( *sources_ )[ s_id ]; + skip_autapse = not allow_autapses_ and snode_id == tnode_id; + skip_multapse = not allow_multapses_ and ch_ids.find( s_id ) != ch_ids.end(); + } while ( skip_autapse or skip_multapse ); + + if ( not allow_multapses_ ) + { + ch_ids.insert( s_id ); } + + single_connect_( snode_id, *target, target_thread, rng ); } +} - void nest::FixedInDegreeBuilder::connect_() +nest::FixedOutDegreeBuilder::FixedOutDegreeBuilder( NodeCollectionPTR sources, + NodeCollectionPTR targets, + ThirdOutBuilder* third_out, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : BipartiteConnBuilder( sources, targets, third_out, conn_spec, syn_specs ) +{ + // check for potential errors + long n_targets = static_cast< long >( targets_->size() ); + if ( n_targets == 0 ) { + throw BadProperty( "Target array must not be empty." ); + } + ParameterDatum* pd = dynamic_cast< ParameterDatum* >( ( *conn_spec )[ names::outdegree ].datum() ); + if ( pd ) + { + outdegree_ = *pd; + // TODO: Checks of parameter range + } + else + { + // Assume outdegree is a scalar + const long value = ( *conn_spec )[ names::outdegree ]; -#pragma omp parallel - { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); + outdegree_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); - try + // verify that outdegree is not larger than target population if multapses + // are disabled + if ( not allow_multapses_ ) + { + if ( value > n_targets ) { - 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 ); - - const long indegree_value = std::round( indegree_->value( rng, target ) ); - if ( target->is_proxy() ) - { - // skip array parameters handled in other virtual processes - skip_conn_parameter_( tid, indegree_value ); - continue; - } - - inner_connect_( tid, rng, target, tnode_id, true, indegree_value ); - } - } - 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; - } - auto source = n->get_node(); - const long indegree_value = std::round( indegree_->value( rng, source ) ); - - inner_connect_( tid, rng, source, tnode_id, false, indegree_value ); - } - } + throw BadProperty( "Outdegree cannot be larger than population size." ); } - catch ( std::exception& err ) + else if ( value == n_targets and not allow_autapses_ ) { - // 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 ) ); + LOG( M_WARNING, + "FixedOutDegreeBuilder::connect", + "Multapses and autapses prohibited. When the sources and the targets " + "have a non-empty intersection, the connect algorithm will enter an infinite loop." ); + return; } - } - } - void nest::FixedInDegreeBuilder::inner_connect_( - const int tid, RngPtr rng, Node* target, size_t tnode_id, bool skip, long indegree_value ) - { - const size_t target_thread = target->get_thread(); - - // check whether the target is on our thread - if ( static_cast< size_t >( tid ) != target_thread ) - { - // skip array parameters handled in other virtual processes - if ( skip ) + if ( value > 0.9 * n_targets ) { - skip_conn_parameter_( tid, indegree_value ); + LOG( M_WARNING, + "FixedOutDegreeBuilder::connect", + "Multapses are prohibited and you request more than 90% connectivity. Expect long connecting times!" ); } - return; } + if ( value < 0 ) + { + throw BadProperty( "Outdegree cannot be less than zero." ); + } + } +} + +void +nest::FixedOutDegreeBuilder::connect_() +{ + // get global rng that is tested for synchronization for all threads + RngPtr grng = get_rank_synced_rng(); + + NodeCollection::const_iterator source_it = sources_->begin(); + for ( ; source_it < sources_->end(); ++source_it ) + { + const size_t snode_id = ( *source_it ).node_id; + std::set< long > ch_ids; - long n_rnd = sources_->size(); + std::vector< size_t > tgt_ids_; + const long n_rnd = targets_->size(); - for ( long j = 0; j < indegree_value; ++j ) + Node* source_node = kernel().node_manager.get_node_or_proxy( snode_id ); + const long outdegree_value = std::round( outdegree_->value( grng, source_node ) ); + for ( long j = 0; j < outdegree_value; ++j ) { - unsigned long s_id; - size_t snode_id; + unsigned long t_id; + size_t tnode_id; bool skip_autapse = false; bool skip_multapse = false; do { - s_id = rng->ulrand( n_rnd ); - snode_id = ( *sources_ )[ s_id ]; - skip_autapse = not allow_autapses_ and snode_id == tnode_id; - skip_multapse = not allow_multapses_ and ch_ids.find( s_id ) != ch_ids.end(); + t_id = grng->ulrand( n_rnd ); + tnode_id = ( *targets_ )[ t_id ]; + skip_autapse = not allow_autapses_ and tnode_id == snode_id; + skip_multapse = not allow_multapses_ and ch_ids.find( t_id ) != ch_ids.end(); } while ( skip_autapse or skip_multapse ); if ( not allow_multapses_ ) { - ch_ids.insert( s_id ); + ch_ids.insert( t_id ); } - single_connect_( snode_id, *target, target_thread, rng ); + tgt_ids_.push_back( tnode_id ); } - } - nest::FixedOutDegreeBuilder::FixedOutDegreeBuilder( NodeCollectionPTR sources, - NodeCollectionPTR targets, - const DictionaryDatum& conn_spec, - const std::vector< DictionaryDatum >& syn_specs ) - : BipartiteConnBuilder( sources, targets, conn_spec, syn_specs ) - { - // check for potential errors - long n_targets = static_cast< long >( targets_->size() ); - if ( n_targets == 0 ) - { - throw BadProperty( "Target array must not be empty." ); - } - ParameterDatum* pd = dynamic_cast< ParameterDatum* >( ( *conn_spec )[ names::outdegree ].datum() ); - if ( pd ) - { - outdegree_ = *pd; - // TODO: Checks of parameter range - } - else - { - // Assume outdegree is a scalar - const long value = ( *conn_spec )[ names::outdegree ]; - - outdegree_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); - - // verify that outdegree is not larger than target population if multapses - // are disabled - if ( not allow_multapses_ ) - { - if ( value > n_targets ) - { - throw BadProperty( "Outdegree cannot be larger than population size." ); - } - else if ( value == n_targets and not allow_autapses_ ) - { - LOG( M_WARNING, - "FixedOutDegreeBuilder::connect", - "Multapses and autapses prohibited. When the sources and the targets " - "have a non-empty intersection, the connect algorithm will enter an infinite loop." ); - return; - } - - if ( value > 0.9 * n_targets ) - { - LOG( M_WARNING, - "FixedOutDegreeBuilder::connect", - "Multapses are prohibited and you request more than 90% connectivity. Expect long connecting times!" ); - } - } - - if ( value < 0 ) - { - throw BadProperty( "Outdegree cannot be less than zero." ); - } - } - } - - void nest::FixedOutDegreeBuilder::connect_() - { - // get global rng that is tested for synchronization for all threads - RngPtr grng = get_rank_synced_rng(); - - NodeCollection::const_iterator source_it = sources_->begin(); - for ( ; source_it < sources_->end(); ++source_it ) +#pragma omp parallel { - const size_t snode_id = ( *source_it ).node_id; - - std::set< long > ch_ids; - std::vector< size_t > tgt_ids_; - const long n_rnd = targets_->size(); + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); - Node* source_node = kernel().node_manager.get_node_or_proxy( snode_id ); - const long outdegree_value = std::round( outdegree_->value( grng, source_node ) ); - for ( long j = 0; j < outdegree_value; ++j ) + try { - unsigned long t_id; - size_t tnode_id; - bool skip_autapse = false; - bool skip_multapse = false; + RngPtr rng = get_vp_specific_rng( tid ); - do + std::vector< size_t >::const_iterator tnode_id_it = tgt_ids_.begin(); + for ( ; tnode_id_it != tgt_ids_.end(); ++tnode_id_it ) { - t_id = grng->ulrand( n_rnd ); - tnode_id = ( *targets_ )[ t_id ]; - skip_autapse = not allow_autapses_ and tnode_id == snode_id; - skip_multapse = not allow_multapses_ and ch_ids.find( t_id ) != ch_ids.end(); - } while ( skip_autapse or skip_multapse ); + Node* const target = kernel().node_manager.get_node_or_proxy( *tnode_id_it, tid ); + if ( target->is_proxy() ) + { + // skip array parameters handled in other virtual processes + skip_conn_parameter_( tid ); + continue; + } - if ( not allow_multapses_ ) - { - ch_ids.insert( t_id ); + single_connect_( snode_id, *target, tid, rng ); } - - tgt_ids_.push_back( tnode_id ); } - -#pragma omp parallel + catch ( std::exception& err ) { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); - - try - { - RngPtr rng = get_vp_specific_rng( tid ); - - std::vector< size_t >::const_iterator tnode_id_it = tgt_ids_.begin(); - for ( ; tnode_id_it != tgt_ids_.end(); ++tnode_id_it ) - { - Node* const target = kernel().node_manager.get_node_or_proxy( *tnode_id_it, tid ); - if ( target->is_proxy() ) - { - // skip array parameters handled in other virtual processes - skip_conn_parameter_( tid ); - continue; - } - - single_connect_( snode_id, *target, tid, rng ); - } - } - 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 ) ); - } + // 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 ) ); } } } +} - nest::FixedTotalNumberBuilder::FixedTotalNumberBuilder( NodeCollectionPTR sources, - NodeCollectionPTR targets, - const DictionaryDatum& conn_spec, - const std::vector< DictionaryDatum >& syn_specs ) - : BipartiteConnBuilder( sources, targets, conn_spec, syn_specs ) - , N_( ( *conn_spec )[ names::N ] ) - { +nest::FixedTotalNumberBuilder::FixedTotalNumberBuilder( NodeCollectionPTR sources, + NodeCollectionPTR targets, + ThirdOutBuilder* third_out, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : BipartiteConnBuilder( sources, targets, third_out, conn_spec, syn_specs ) + , N_( ( *conn_spec )[ names::N ] ) +{ - // check for potential errors + // check for potential errors - // verify that total number of connections is not larger than - // N_sources*N_targets - if ( not allow_multapses_ ) - { - if ( ( N_ > static_cast< long >( sources_->size() * targets_->size() ) ) ) - { - throw BadProperty( "Total number of connections cannot exceed product of source and target population sizes." ); - } - } - - if ( N_ < 0 ) + // verify that total number of connections is not larger than + // N_sources*N_targets + if ( not allow_multapses_ ) + { + if ( ( N_ > static_cast< long >( sources_->size() * targets_->size() ) ) ) { - throw BadProperty( "Total number of connections cannot be negative." ); + throw BadProperty( "Total number of connections cannot exceed product of source and target population sizes." ); } + } - // for now multapses cannot be forbidden - // TODO: Implement option for multapses_ = False, where already existing - // connections are stored in - // a bitmap - if ( not allow_multapses_ ) - { - throw NotImplemented( "Connect doesn't support the suppression of multapses in the FixedTotalNumber connector." ); - } + if ( N_ < 0 ) + { + throw BadProperty( "Total number of connections cannot be negative." ); } - void nest::FixedTotalNumberBuilder::connect_() + // for now multapses cannot be forbidden + // TODO: Implement option for multapses_ = False, where already existing + // connections are stored in + // a bitmap + if ( not allow_multapses_ ) { - const int M = kernel().vp_manager.get_num_virtual_processes(); - const long size_sources = sources_->size(); - const long size_targets = targets_->size(); + throw NotImplemented( "Connect doesn't support the suppression of multapses in the FixedTotalNumber connector." ); + } +} - // drawing connection ids +void +nest::FixedTotalNumberBuilder::connect_() +{ + const int M = kernel().vp_manager.get_num_virtual_processes(); + const long size_sources = sources_->size(); + const long size_targets = targets_->size(); + + // drawing connection ids - // Compute the distribution of targets over processes using the modulo - // function - std::vector< size_t > number_of_targets_on_vp( M, 0 ); - std::vector< size_t > local_targets; - local_targets.reserve( size_targets / kernel().mpi_manager.get_num_processes() ); - for ( size_t t = 0; t < targets_->size(); t++ ) + // Compute the distribution of targets over processes using the modulo + // function + std::vector< size_t > number_of_targets_on_vp( M, 0 ); + std::vector< size_t > local_targets; + local_targets.reserve( size_targets / kernel().mpi_manager.get_num_processes() ); + for ( size_t t = 0; t < targets_->size(); t++ ) + { + int vp = kernel().vp_manager.node_id_to_vp( ( *targets_ )[ t ] ); + ++number_of_targets_on_vp[ vp ]; + if ( kernel().vp_manager.is_local_vp( vp ) ) { - int vp = kernel().vp_manager.node_id_to_vp( ( *targets_ )[ t ] ); - ++number_of_targets_on_vp[ vp ]; - if ( kernel().vp_manager.is_local_vp( vp ) ) - { - local_targets.push_back( ( *targets_ )[ t ] ); - } + local_targets.push_back( ( *targets_ )[ t ] ); } + } - // We use the multinomial distribution to determine the number of - // connections that will be made on one virtual process, i.e. we - // partition the set of edges into n_vps subsets. The number of - // edges on one virtual process is binomially distributed with - // the boundary condition that the sum of all edges over virtual - // processes is the total number of edges. - // To obtain the num_conns_on_vp we adapt the gsl - // implementation of the multinomial distribution. + // We use the multinomial distribution to determine the number of + // connections that will be made on one virtual process, i.e. we + // partition the set of edges into n_vps subsets. The number of + // edges on one virtual process is binomially distributed with + // the boundary condition that the sum of all edges over virtual + // processes is the total number of edges. + // To obtain the num_conns_on_vp we adapt the gsl + // implementation of the multinomial distribution. - // K from gsl is equivalent to M = n_vps - // N is already taken from stack - // p[] is targets_on_vp - std::vector< long > num_conns_on_vp( M, 0 ); // corresponds to n[] + // K from gsl is equivalent to M = n_vps + // N is already taken from stack + // p[] is targets_on_vp + std::vector< long > num_conns_on_vp( M, 0 ); // corresponds to n[] - // calculate exact multinomial distribution - // get global rng that is tested for synchronization for all threads - RngPtr grng = get_rank_synced_rng(); + // calculate exact multinomial distribution + // get global rng that is tested for synchronization for all threads + RngPtr grng = get_rank_synced_rng(); - // begin code adapted from gsl 1.8 // - double sum_dist = 0.0; // corresponds to sum_p - // norm is equivalent to size_targets - unsigned int sum_partitions = 0; // corresponds to sum_n + // begin code adapted from gsl 1.8 // + double sum_dist = 0.0; // corresponds to sum_p + // norm is equivalent to size_targets + unsigned int sum_partitions = 0; // corresponds to sum_n - binomial_distribution bino_dist; - for ( int k = 0; k < M; k++ ) + binomial_distribution bino_dist; + for ( int k = 0; k < M; k++ ) + { + // If we have distributed all connections on the previous processes we exit the loop. It is important to + // have this check here, as N - sum_partition is set as n value for GSL, and this must be larger than 0. + if ( N_ == sum_partitions ) { - // If we have distributed all connections on the previous processes we exit the loop. It is important to - // have this check here, as N - sum_partition is set as n value for GSL, and this must be larger than 0. - if ( N_ == sum_partitions ) - { - break; - } - if ( number_of_targets_on_vp[ k ] > 0 ) - { - double num_local_targets = static_cast< double >( number_of_targets_on_vp[ k ] ); - double p_local = num_local_targets / ( size_targets - sum_dist ); - - binomial_distribution::param_type param( N_ - sum_partitions, p_local ); - num_conns_on_vp[ k ] = bino_dist( grng, param ); - } + break; + } + if ( number_of_targets_on_vp[ k ] > 0 ) + { + double num_local_targets = static_cast< double >( number_of_targets_on_vp[ k ] ); + double p_local = num_local_targets / ( size_targets - sum_dist ); - sum_dist += static_cast< double >( number_of_targets_on_vp[ k ] ); - sum_partitions += static_cast< unsigned int >( num_conns_on_vp[ k ] ); + binomial_distribution::param_type param( N_ - sum_partitions, p_local ); + num_conns_on_vp[ k ] = bino_dist( grng, param ); } - // end code adapted from gsl 1.8 + sum_dist += static_cast< double >( number_of_targets_on_vp[ k ] ); + sum_partitions += static_cast< unsigned int >( num_conns_on_vp[ k ] ); + } + + // end code adapted from gsl 1.8 #pragma omp parallel + { + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); + + try { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); + const size_t vp_id = kernel().vp_manager.thread_to_vp( tid ); - try + if ( kernel().vp_manager.is_local_vp( vp_id ) ) { - const size_t vp_id = kernel().vp_manager.thread_to_vp( tid ); - - if ( kernel().vp_manager.is_local_vp( vp_id ) ) - { - RngPtr rng = get_vp_specific_rng( tid ); + RngPtr rng = get_vp_specific_rng( tid ); - // gather local target node IDs - std::vector< size_t > thread_local_targets; - thread_local_targets.reserve( number_of_targets_on_vp[ vp_id ] ); + // gather local target node IDs + std::vector< size_t > thread_local_targets; + thread_local_targets.reserve( number_of_targets_on_vp[ vp_id ] ); - std::vector< size_t >::const_iterator tnode_id_it = local_targets.begin(); - for ( ; tnode_id_it != local_targets.end(); ++tnode_id_it ) + std::vector< size_t >::const_iterator tnode_id_it = local_targets.begin(); + for ( ; tnode_id_it != local_targets.end(); ++tnode_id_it ) + { + if ( kernel().vp_manager.node_id_to_vp( *tnode_id_it ) == vp_id ) { - if ( kernel().vp_manager.node_id_to_vp( *tnode_id_it ) == vp_id ) - { - thread_local_targets.push_back( *tnode_id_it ); - } + thread_local_targets.push_back( *tnode_id_it ); } + } - assert( thread_local_targets.size() == number_of_targets_on_vp[ vp_id ] ); + assert( thread_local_targets.size() == number_of_targets_on_vp[ vp_id ] ); - while ( num_conns_on_vp[ vp_id ] > 0 ) - { + while ( num_conns_on_vp[ vp_id ] > 0 ) + { + + // draw random numbers for source node from all source neurons + const long s_index = rng->ulrand( size_sources ); + // draw random numbers for target node from + // targets_on_vp on this virtual process + const long t_index = rng->ulrand( thread_local_targets.size() ); + // map random number of source node to node ID corresponding to + // the source_adr vector + const long snode_id = ( *sources_ )[ s_index ]; + // map random number of target node to node ID using the + // targets_on_vp vector + const long tnode_id = thread_local_targets[ t_index ]; + + Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); + const size_t target_thread = target->get_thread(); - // draw random numbers for source node from all source neurons - const long s_index = rng->ulrand( size_sources ); - // draw random numbers for target node from - // targets_on_vp on this virtual process - const long t_index = rng->ulrand( thread_local_targets.size() ); - // map random number of source node to node ID corresponding to - // the source_adr vector - const long snode_id = ( *sources_ )[ s_index ]; - // map random number of target node to node ID using the - // targets_on_vp vector - const long tnode_id = thread_local_targets[ t_index ]; - - Node* const target = kernel().node_manager.get_node_or_proxy( tnode_id, tid ); - const size_t target_thread = target->get_thread(); - - if ( allow_autapses_ or snode_id != tnode_id ) - { - single_connect_( snode_id, *target, target_thread, rng ); - num_conns_on_vp[ vp_id ]--; - } + if ( allow_autapses_ or snode_id != tnode_id ) + { + single_connect_( snode_id, *target, target_thread, rng ); + num_conns_on_vp[ vp_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 ) ); - } + } + 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 ) ); } } +} - nest::BernoulliBuilder::BernoulliBuilder( NodeCollectionPTR sources, - NodeCollectionPTR targets, - const DictionaryDatum& conn_spec, - const std::vector< DictionaryDatum >& syn_specs ) - : BipartiteConnBuilder( sources, targets, conn_spec, syn_specs ) +nest::BernoulliBuilder::BernoulliBuilder( NodeCollectionPTR sources, + NodeCollectionPTR targets, + ThirdOutBuilder* third_out, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : BipartiteConnBuilder( sources, targets, third_out, conn_spec, syn_specs ) +{ + ParameterDatum* pd = dynamic_cast< ParameterDatum* >( ( *conn_spec )[ names::p ].datum() ); + if ( pd ) + { + p_ = *pd; + // TODO: Checks of parameter range + } + else { - 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 or 1 < value ) { - // Assume p is a scalar - const double value = ( *conn_spec )[ names::p ]; - if ( value < 0 or 1 < value ) - { - throw BadProperty( "Connection probability 0 <= p <= 1 required." ); - } - p_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); + throw BadProperty( "Connection probability 0 <= p <= 1 required." ); } + p_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); } +} - void nest::BernoulliBuilder::connect_() - { +void +nest::BernoulliBuilder::connect_() +{ #pragma omp parallel + { + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); + + try { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); + RngPtr rng = get_vp_specific_rng( tid ); - try + if ( loop_over_targets_() ) { - 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 ) { - 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() ) { - 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 ); + // skip array parameters handled in other virtual processes + skip_conn_parameter_( tid ); + continue; } + + inner_connect_( tid, rng, target, tnode_id ); } + } - else + 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 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; - } + const size_t tnode_id = n->get_node_id(); - inner_connect_( tid, rng, n->get_node(), tnode_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 + } + 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::BernoulliBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, size_t tnode_id ) +{ + const size_t target_thread = target->get_thread(); + + // check whether the target is on our thread + if ( static_cast< size_t >( tid ) != target_thread ) + { + return; } - void nest::BernoulliBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, size_t tnode_id ) + // It is not possible to create multapses with this type of BernoulliBuilder, + // hence leave out corresponding checks. + + NodeCollection::const_iterator source_it = sources_->begin(); + for ( ; source_it < sources_->end(); ++source_it ) { - const size_t target_thread = target->get_thread(); + const size_t snode_id = ( *source_it ).node_id; - // check whether the target is on our thread - if ( static_cast< size_t >( tid ) != target_thread ) + if ( not allow_autapses_ and snode_id == tnode_id ) { - return; + continue; } - - // It is not possible to create multapses with this type of BernoulliBuilder, - // hence leave out corresponding checks. - - NodeCollection::const_iterator source_it = sources_->begin(); - for ( ; source_it < sources_->end(); ++source_it ) + if ( rng->drand() >= p_->value( rng, target ) ) { - const size_t snode_id = ( *source_it ).node_id; - - if ( not allow_autapses_ and snode_id == tnode_id ) - { - continue; - } - if ( rng->drand() >= p_->value( rng, target ) ) - { - continue; - } - - single_connect_( snode_id, *target, target_thread, rng ); + continue; } + + single_connect_( snode_id, *target, target_thread, rng ); } +} - nest::PoissonBuilder::PoissonBuilder( NodeCollectionPTR sources, - NodeCollectionPTR targets, - const DictionaryDatum& conn_spec, - const std::vector< DictionaryDatum >& syn_specs ) - : BipartiteConnBuilder( sources, targets, conn_spec, syn_specs ) +nest::PoissonBuilder::PoissonBuilder( NodeCollectionPTR sources, + NodeCollectionPTR targets, + ThirdOutBuilder* third_out, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : BipartiteConnBuilder( sources, targets, third_out, conn_spec, syn_specs ) +{ + ParameterDatum* pd = dynamic_cast< ParameterDatum* >( ( *conn_spec )[ names::pairwise_avg_num_conns ].datum() ); + if ( pd ) + { + pairwise_avg_num_conns_ = *pd; + } + else { - ParameterDatum* pd = dynamic_cast< ParameterDatum* >( ( *conn_spec )[ names::pairwise_avg_num_conns ].datum() ); - if ( pd ) + // Assume pairwise_avg_num_conns is a scalar + const double value = ( *conn_spec )[ names::pairwise_avg_num_conns ]; + if ( value < 0 ) { - pairwise_avg_num_conns_ = *pd; + throw BadProperty( "Connection parameter 0 ≤ pairwise_avg_num_conns required." ); } - else + if ( not allow_multapses_ ) { - // 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 ≤ pairwise_avg_num_conns required." ); - } - if ( not allow_multapses_ ) - { - throw BadProperty( "Multapses must be allowed for this connection rule." ); - } - pairwise_avg_num_conns_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); + throw BadProperty( "Multapses must be allowed for this connection rule." ); } + pairwise_avg_num_conns_ = std::shared_ptr< Parameter >( new ConstantParameter( value ) ); } +} - void nest::PoissonBuilder::connect_() - { +void +nest::PoissonBuilder::connect_() +{ #pragma omp parallel + { + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); + + try { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); + RngPtr rng = get_vp_specific_rng( tid ); - try + if ( loop_over_targets_() ) { - 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 ) { - 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() ) { - 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 parameters handled in other virtual processes - skip_conn_parameter_( tid ); - continue; - } - - inner_connect_( tid, rng, target, tnode_id ); + // skip parameters handled in other virtual processes + skip_conn_parameter_( tid ); + continue; } + + inner_connect_( tid, rng, target, tnode_id ); } - else + } + 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 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 ) { - 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 ); + 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 + } + 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(); + + // check whether the target is on our thread + if ( static_cast< size_t >( tid ) != target_thread ) + { + return; } - void nest::PoissonBuilder::inner_connect_( const int tid, RngPtr rng, Node* target, size_t tnode_id ) + 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 ) { - const size_t target_thread = target->get_thread(); + const size_t snode_id = ( *source_it ).node_id; - // check whether the target is on our thread - if ( static_cast< size_t >( tid ) != target_thread ) + if ( not allow_autapses_ and snode_id == tnode_id ) { - return; + continue; } - poisson_distribution poi_dist; + // Sample to number of connections that are to be established + poisson_distribution::param_type param( pairwise_avg_num_conns_->value( rng, target ) ); + const size_t num_conns = poi_dist( rng, param ); - // 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 ) + for ( size_t n = 0; n < num_conns; ++n ) { - 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 - poisson_distribution::param_type param( pairwise_avg_num_conns_->value( rng, target ) ); - const size_t num_conns = poi_dist( rng, param ); - - for ( size_t n = 0; n < num_conns; ++n ) - { - single_connect_( snode_id, *target, target_thread, rng ); - } + single_connect_( snode_id, *target, target_thread, rng ); } } +} - +/* nest::AuxiliaryBuilder::AuxiliaryBuilder( NodeCollectionPTR sources, NodeCollectionPTR targets, const DictionaryDatum& conn_spec, @@ -2017,228 +1956,236 @@ void nest::ThirdBernoulliWithPoolBuilder::third_connect( < #size_t source_gid # { single_connect_( snode_id, tgt, tid, rng ); } +*/ +nest::SymmetricBernoulliBuilder::SymmetricBernoulliBuilder( NodeCollectionPTR sources, + NodeCollectionPTR targets, + ThirdOutBuilder* third_out, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : BipartiteConnBuilder( sources, targets, third_out, conn_spec, syn_specs ) + , p_( ( *conn_spec )[ names::p ] ) +{ + // This connector takes care of symmetric connections on its own + creates_symmetric_connections_ = true; - nest::SymmetricBernoulliBuilder::SymmetricBernoulliBuilder( NodeCollectionPTR sources, - NodeCollectionPTR targets, - const DictionaryDatum& conn_spec, - const std::vector< DictionaryDatum >& syn_specs ) - : BipartiteConnBuilder( sources, targets, conn_spec, syn_specs ) - , p_( ( *conn_spec )[ names::p ] ) + if ( p_ < 0 or 1 <= p_ ) { - // This connector takes care of symmetric connections on its own - creates_symmetric_connections_ = true; - - if ( p_ < 0 or 1 <= p_ ) - { - throw BadProperty( "Connection probability 0 <= p < 1 required." ); - } + throw BadProperty( "Connection probability 0 <= p < 1 required." ); + } - if ( not allow_multapses_ ) - { - throw BadProperty( "Multapses must be enabled." ); - } + if ( not allow_multapses_ ) + { + throw BadProperty( "Multapses must be enabled." ); + } - if ( allow_autapses_ ) - { - throw BadProperty( "Autapses must be disabled." ); - } + if ( allow_autapses_ ) + { + throw BadProperty( "Autapses must be disabled." ); + } - if ( not make_symmetric_ ) - { - throw BadProperty( "Symmetric connections must be enabled." ); - } + if ( not make_symmetric_ ) + { + throw BadProperty( "Symmetric connections must be enabled." ); } +} - void nest::SymmetricBernoulliBuilder::connect_() - { +void +nest::SymmetricBernoulliBuilder::connect_() +{ #pragma omp parallel + { + const size_t tid = kernel().vp_manager.get_thread_id(); + + // Use RNG generating same number sequence on all threads + RngPtr synced_rng = get_vp_synced_rng( tid ); + + try { - const size_t tid = kernel().vp_manager.get_thread_id(); + binomial_distribution bino_dist; + binomial_distribution::param_type param( sources_->size(), p_ ); - // Use RNG generating same number sequence on all threads - RngPtr synced_rng = get_vp_synced_rng( tid ); + unsigned long indegree; + size_t snode_id; + std::set< size_t > previous_snode_ids; + Node* target; + size_t target_thread; + Node* source; + size_t source_thread; - try + for ( NodeCollection::const_iterator tnode_id = targets_->begin(); tnode_id != targets_->end(); ++tnode_id ) { - binomial_distribution bino_dist; - binomial_distribution::param_type param( sources_->size(), p_ ); - - unsigned long indegree; - size_t snode_id; - std::set< size_t > previous_snode_ids; - Node* target; - size_t target_thread; - Node* source; - size_t source_thread; - - for ( NodeCollection::const_iterator tnode_id = targets_->begin(); tnode_id != targets_->end(); ++tnode_id ) + // sample indegree according to truncated Binomial distribution + indegree = sources_->size(); + while ( indegree >= sources_->size() ) + { + indegree = bino_dist( synced_rng, param ); + } + assert( indegree < sources_->size() ); + + target = kernel().node_manager.get_node_or_proxy( ( *tnode_id ).node_id, tid ); + target_thread = tid; + + // check whether the target is on this thread + if ( target->is_proxy() ) + { + target_thread = invalid_thread; + } + + previous_snode_ids.clear(); + + // choose indegree number of sources randomly from all sources + size_t i = 0; + while ( i < indegree ) { - // sample indegree according to truncated Binomial distribution - indegree = sources_->size(); - while ( indegree >= sources_->size() ) + snode_id = ( *sources_ )[ synced_rng->ulrand( sources_->size() ) ]; + + // Avoid autapses and multapses. Due to symmetric connectivity, + // multapses might exist if the target neuron with node ID snode_id draws the + // source with node ID tnode_id while choosing sources itself. + if ( snode_id == ( *tnode_id ).node_id or previous_snode_ids.find( snode_id ) != previous_snode_ids.end() ) { - indegree = bino_dist( synced_rng, param ); + continue; } - assert( indegree < sources_->size() ); + previous_snode_ids.insert( snode_id ); - target = kernel().node_manager.get_node_or_proxy( ( *tnode_id ).node_id, tid ); - target_thread = tid; + source = kernel().node_manager.get_node_or_proxy( snode_id, tid ); + source_thread = tid; - // check whether the target is on this thread - if ( target->is_proxy() ) + if ( source->is_proxy() ) { - target_thread = invalid_thread; + source_thread = invalid_thread; } - previous_snode_ids.clear(); + // if target is local: connect + if ( target_thread == tid ) + { + assert( target ); + single_connect_( snode_id, *target, target_thread, synced_rng ); + } - // choose indegree number of sources randomly from all sources - size_t i = 0; - while ( i < indegree ) + // if source is local: connect + if ( source_thread == tid ) { - snode_id = ( *sources_ )[ synced_rng->ulrand( sources_->size() ) ]; - - // Avoid autapses and multapses. Due to symmetric connectivity, - // multapses might exist if the target neuron with node ID snode_id draws the - // source with node ID tnode_id while choosing sources itself. - if ( snode_id == ( *tnode_id ).node_id or previous_snode_ids.find( snode_id ) != previous_snode_ids.end() ) - { - continue; - } - previous_snode_ids.insert( snode_id ); - - source = kernel().node_manager.get_node_or_proxy( snode_id, tid ); - source_thread = tid; - - if ( source->is_proxy() ) - { - source_thread = invalid_thread; - } - - // if target is local: connect - if ( target_thread == tid ) - { - assert( target ); - single_connect_( snode_id, *target, target_thread, synced_rng ); - } - - // if source is local: connect - if ( source_thread == tid ) - { - assert( source ); - single_connect_( ( *tnode_id ).node_id, *source, source_thread, synced_rng ); - } - - ++i; + assert( source ); + single_connect_( ( *tnode_id ).node_id, *source, source_thread, synced_rng ); } + + ++i; } } - 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 ) ); - } + } + 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 ) ); } } +} - nest::SPBuilder::SPBuilder( NodeCollectionPTR sources, - NodeCollectionPTR targets, - const DictionaryDatum& conn_spec, - const std::vector< DictionaryDatum >& syn_specs ) - : BipartiteConnBuilder( sources, targets, conn_spec, syn_specs ) +nest::SPBuilder::SPBuilder( NodeCollectionPTR sources, + NodeCollectionPTR targets, + ThirdOutBuilder* third_out, + const DictionaryDatum& conn_spec, + const std::vector< DictionaryDatum >& syn_specs ) + : BipartiteConnBuilder( sources, targets, third_out, conn_spec, syn_specs ) +{ + // Check that both pre and postsynaptic element are provided + if ( not use_structural_plasticity_ ) { - // Check that both pre and postsynaptic element are provided - if ( not use_structural_plasticity_ ) - { - throw BadProperty( "pre_synaptic_element and/or post_synaptic_elements is missing." ); - } + throw BadProperty( "pre_synaptic_element and/or post_synaptic_elements is missing." ); } +} - void nest::SPBuilder::update_delay( long& d ) const +void +nest::SPBuilder::update_delay( long& d ) const +{ + if ( get_default_delay() ) { - if ( get_default_delay() ) - { - DictionaryDatum syn_defaults = kernel().model_manager.get_connector_defaults( get_synapse_model() ); - const double delay = getValue< double >( syn_defaults, "delay" ); - d = Time( Time::ms( delay ) ).get_steps(); - } + DictionaryDatum syn_defaults = kernel().model_manager.get_connector_defaults( get_synapse_model() ); + const double delay = getValue< double >( syn_defaults, "delay" ); + d = Time( Time::ms( delay ) ).get_steps(); } +} - void nest::SPBuilder::sp_connect( const std::vector< size_t >& sources, const std::vector< size_t >& targets ) - { - connect_( sources, targets ); +void +nest::SPBuilder::sp_connect( const std::vector< size_t >& sources, const std::vector< size_t >& targets ) +{ + connect_( sources, targets ); - // check if any exceptions have been raised - for ( size_t tid = 0; tid < kernel().vp_manager.get_num_threads(); ++tid ) + // check if any exceptions have been raised + for ( size_t tid = 0; tid < kernel().vp_manager.get_num_threads(); ++tid ) + { + if ( exceptions_raised_.at( tid ).get() ) { - if ( exceptions_raised_.at( tid ).get() ) - { - throw WrappedThreadException( *( exceptions_raised_.at( tid ) ) ); - } + throw WrappedThreadException( *( exceptions_raised_.at( tid ) ) ); } } +} - void nest::SPBuilder::connect_() - { - throw NotImplemented( "Connection without structural plasticity is not possible for this connection builder." ); - } +void +nest::SPBuilder::connect_() +{ + throw NotImplemented( "Connection without structural plasticity is not possible for this connection builder." ); +} + +/** + * In charge of dynamically creating the new synapses + */ +void +nest::SPBuilder::connect_( NodeCollectionPTR, NodeCollectionPTR ) +{ + throw NotImplemented( "Connection without structural plasticity is not possible for this connection builder." ); +} - /** - * In charge of dynamically creating the new synapses - */ - void nest::SPBuilder::connect_( NodeCollectionPTR, NodeCollectionPTR ) +void +nest::SPBuilder::connect_( const std::vector< size_t >& sources, const std::vector< size_t >& targets ) +{ + // Code copied and adapted from OneToOneBuilder::connect_() + // make sure that target and source population have the same size + if ( sources.size() != targets.size() ) { - throw NotImplemented( "Connection without structural plasticity is not possible for this connection builder." ); + throw DimensionMismatch( "Source and target population must be of the same size." ); } - void nest::SPBuilder::connect_( const std::vector< size_t >& sources, const std::vector< size_t >& targets ) +#pragma omp parallel { - // Code copied and adapted from OneToOneBuilder::connect_() - // make sure that target and source population have the same size - if ( sources.size() != targets.size() ) - { - throw DimensionMismatch( "Source and target population must be of the same size." ); - } + // get thread id + const size_t tid = kernel().vp_manager.get_thread_id(); -#pragma omp parallel + try { - // get thread id - const size_t tid = kernel().vp_manager.get_thread_id(); + RngPtr rng = get_vp_specific_rng( tid ); - try + std::vector< size_t >::const_iterator tnode_id_it = targets.begin(); + std::vector< size_t >::const_iterator snode_id_it = sources.begin(); + for ( ; tnode_id_it != targets.end(); ++tnode_id_it, ++snode_id_it ) { - RngPtr rng = get_vp_specific_rng( tid ); + assert( snode_id_it != sources.end() ); - std::vector< size_t >::const_iterator tnode_id_it = targets.begin(); - std::vector< size_t >::const_iterator snode_id_it = sources.begin(); - for ( ; tnode_id_it != targets.end(); ++tnode_id_it, ++snode_id_it ) + if ( *snode_id_it == *tnode_id_it and not allow_autapses_ ) { - assert( snode_id_it != sources.end() ); - - if ( *snode_id_it == *tnode_id_it and not allow_autapses_ ) - { - continue; - } - - if ( not change_connected_synaptic_elements( *snode_id_it, *tnode_id_it, tid, 1 ) ) - { - skip_conn_parameter_( tid ); - continue; - } - Node* const target = kernel().node_manager.get_node_or_proxy( *tnode_id_it, tid ); + continue; + } - single_connect_( *snode_id_it, *target, tid, rng ); + if ( not change_connected_synaptic_elements( *snode_id_it, *tnode_id_it, tid, 1 ) ) + { + skip_conn_parameter_( tid ); + continue; } + Node* const target = kernel().node_manager.get_node_or_proxy( *tnode_id_it, tid ); + + single_connect_( *snode_id_it, *target, tid, rng ); } - 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 ) ); - } + } + 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 ) ); } } +} diff --git a/nestkernel/conn_builder.h b/nestkernel/conn_builder.h index 41777d4688..62e194be1d 100644 --- a/nestkernel/conn_builder.h +++ b/nestkernel/conn_builder.h @@ -299,7 +299,7 @@ class ThirdInBuilder : public BipartiteConnBuilder public: ThirdInBuilder( NodeCollectionPTR, NodeCollectionPTR, - BipartiteConnBuilder*, + ThirdOutBuilder*, const DictionaryDatum&, // only for compatibility with BCB const std::vector< DictionaryDatum >& ); @@ -366,6 +366,7 @@ class ConnBuilder NodeCollectionPTR targets, NodeCollectionPTR third, const DictionaryDatum& conn_spec, + const DictionaryDatum& third_conn_spec, const std::map< Name, std::vector< DictionaryDatum > >& syn_specs ); ~ConnBuilder(); @@ -379,7 +380,7 @@ class ConnBuilder private: // order of declarations based on dependencies ThirdOutBuilder* third_out_builder_; - ThirdInBuilder third_in_builder_; + ThirdInBuilder* third_in_builder_; BipartiteConnBuilder* primary_builder_; }; @@ -395,6 +396,11 @@ class ThirdBernoulliWithPoolBuilder : public ThirdOutBuilder void third_connect( size_t source_gid, Node& target ) override; private: + void + connect_() override + { + assert( false ); + } size_t get_first_pool_index_( const size_t target_index ) const; double p_; @@ -406,6 +412,12 @@ class ThirdBernoulliWithPoolBuilder : public ThirdOutBuilder struct SourceThirdInfo_ { + SourceThirdInfo_() + : source_gid( 0 ) + , third_gid( 0 ) + , third_rank( 0 ) + { + } SourceThirdInfo_( size_t src, size_t trd, size_t rank ) : source_gid( src ) , third_gid( trd ) @@ -431,6 +443,7 @@ class OneToOneBuilder : public BipartiteConnBuilder public: OneToOneBuilder( NodeCollectionPTR sources, NodeCollectionPTR targets, + ThirdOutBuilder* third_out, const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_specs ); @@ -478,9 +491,10 @@ class AllToAllBuilder : public BipartiteConnBuilder public: AllToAllBuilder( NodeCollectionPTR sources, NodeCollectionPTR targets, + ThirdOutBuilder* third_out, const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_specs ) - : BipartiteConnBuilder( sources, targets, conn_spec, syn_specs ) + : BipartiteConnBuilder( sources, targets, third_out, conn_spec, syn_specs ) { } @@ -532,6 +546,7 @@ class FixedInDegreeBuilder : public BipartiteConnBuilder public: FixedInDegreeBuilder( NodeCollectionPTR, NodeCollectionPTR, + ThirdOutBuilder* third_out, const DictionaryDatum&, const std::vector< DictionaryDatum >& ); @@ -548,6 +563,7 @@ class FixedOutDegreeBuilder : public BipartiteConnBuilder public: FixedOutDegreeBuilder( NodeCollectionPTR, NodeCollectionPTR, + ThirdOutBuilder* third_out, const DictionaryDatum&, const std::vector< DictionaryDatum >& ); @@ -563,6 +579,7 @@ class FixedTotalNumberBuilder : public BipartiteConnBuilder public: FixedTotalNumberBuilder( NodeCollectionPTR, NodeCollectionPTR, + ThirdOutBuilder* third_out, const DictionaryDatum&, const std::vector< DictionaryDatum >& ); @@ -578,6 +595,7 @@ class BernoulliBuilder : public BipartiteConnBuilder public: BernoulliBuilder( NodeCollectionPTR, NodeCollectionPTR, + ThirdOutBuilder* third_out, const DictionaryDatum&, const std::vector< DictionaryDatum >& ); @@ -592,7 +610,11 @@ class BernoulliBuilder : public BipartiteConnBuilder class PoissonBuilder : public BipartiteConnBuilder { public: - PoissonBuilder( NodeCollectionPTR, NodeCollectionPTR, const DictionaryDatum&, const std::vector< DictionaryDatum >& ); + PoissonBuilder( NodeCollectionPTR, + NodeCollectionPTR, + ThirdOutBuilder* third_out, + const DictionaryDatum&, + const std::vector< DictionaryDatum >& ); protected: void connect_() override; @@ -609,6 +631,7 @@ class PoissonBuilder : public BipartiteConnBuilder * it maintains an AuxiliaryBuilder which handles the parameterization of the corresponding * third-party connection. */ +/* class AuxiliaryBuilder : public BipartiteConnBuilder { public: @@ -628,12 +651,14 @@ class AuxiliaryBuilder : public BipartiteConnBuilder assert( false ); } }; +*/ class SymmetricBernoulliBuilder : public BipartiteConnBuilder { public: SymmetricBernoulliBuilder( NodeCollectionPTR, NodeCollectionPTR, + ThirdOutBuilder* third_out, const DictionaryDatum&, const std::vector< DictionaryDatum >& ); @@ -664,6 +689,7 @@ class SPBuilder : public BipartiteConnBuilder */ SPBuilder( NodeCollectionPTR sources, NodeCollectionPTR targets, + ThirdOutBuilder* third_out, const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_spec ); diff --git a/nestkernel/conn_builder_factory.h b/nestkernel/conn_builder_factory.h index a4303453d4..8603190c6e 100644 --- a/nestkernel/conn_builder_factory.h +++ b/nestkernel/conn_builder_factory.h @@ -55,103 +55,63 @@ class GenericConnBuilderFactory */ virtual BipartiteConnBuilder* create( NodeCollectionPTR, NodeCollectionPTR, + ThirdOutBuilder*, const DictionaryDatum&, const std::vector< DictionaryDatum >& ) const = 0; - - /** - * Factory method for builders for tripartite connection rules. - */ - virtual BipartiteConnBuilder* create( NodeCollectionPTR, - NodeCollectionPTR, - NodeCollectionPTR, - const DictionaryDatum&, - const std::map< Name, std::vector< DictionaryDatum > >& ) const = 0; }; /** * Factory class for ConnBuilders - * - * This template class provides an interface with bipartite and tripartite `create()` methods. - * Implementation is delegated to explicit template specialisations below, which only implement - * the `create()` method with the proper arity depending on the `is_tripartite` flag of - * the pertaining conn builder. */ -template < typename ConnBuilderType, bool is_tripartite = ConnBuilderType::is_tripartite > +template < typename ConnBuilderType > class ConnBuilderFactory : public GenericConnBuilderFactory { public: BipartiteConnBuilder* create( NodeCollectionPTR sources, NodeCollectionPTR targets, + ThirdOutBuilder* third_out, const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_specs ) const override { - assert( false ); // only specialisations should be called - } - - //! create tripartite builder - BipartiteConnBuilder* - create( NodeCollectionPTR sources, - NodeCollectionPTR targets, - NodeCollectionPTR third, - const DictionaryDatum& conn_spec, - const std::map< Name, std::vector< DictionaryDatum > >& syn_specs ) const override - { - assert( false ); // only specialisations should be called + return new ConnBuilderType( sources, targets, third_out, conn_spec, syn_specs ); } }; - -// Specialisation for bipartite ConnBuilders -template < typename ConnBuilderType > -class ConnBuilderFactory< ConnBuilderType, false > : public GenericConnBuilderFactory +class GenericThirdConnBuilderFactory { - BipartiteConnBuilder* - create( NodeCollectionPTR sources, - NodeCollectionPTR targets, - const DictionaryDatum& conn_spec, - const std::vector< DictionaryDatum >& syn_specs ) const override +public: + virtual ~GenericThirdConnBuilderFactory() { - return new ConnBuilderType( sources, targets, conn_spec, syn_specs ); } - BipartiteConnBuilder* - create( NodeCollectionPTR sources, - NodeCollectionPTR targets, - NodeCollectionPTR third, - const DictionaryDatum& conn_spec, - const std::map< Name, std::vector< DictionaryDatum > >& syn_specs ) const override - { - throw IllegalConnection( String::compose( - "Connection rule '%1' does not support tripartite connections.", ( *conn_spec )[ names::rule ] ) ); - } + /** + * Factory method for builders for bipartite connection rules (the default). + */ + virtual ThirdOutBuilder* create( NodeCollectionPTR, + NodeCollectionPTR, + const DictionaryDatum&, + const std::vector< DictionaryDatum >& ) const = 0; }; -// Specialisation for tripartite ConnBuilders -template < typename ConnBuilderType > -class ConnBuilderFactory< ConnBuilderType, true > : public GenericConnBuilderFactory +/** + * Factory class for Third-factor ConnBuilders + */ +template < typename ThirdConnBuilderType > +class ThirdConnBuilderFactory : public GenericThirdConnBuilderFactory { - BipartiteConnBuilder* +public: + ThirdOutBuilder* create( NodeCollectionPTR sources, NodeCollectionPTR targets, const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_specs ) const override { - throw BadProperty( - String::compose( "Connection rule %1 only supports tripartite connections.", ( *conn_spec )[ names::rule ] ) ); - } - - BipartiteConnBuilder* - create( NodeCollectionPTR sources, - NodeCollectionPTR targets, - NodeCollectionPTR third, - const DictionaryDatum& conn_spec, - const std::map< Name, std::vector< DictionaryDatum > >& syn_specs ) const override - { - return new ConnBuilderType( sources, targets, third, conn_spec, syn_specs ); + return new ThirdConnBuilderType( sources, targets, conn_spec, syn_specs ); } }; + } // namespace nest #endif diff --git a/nestkernel/connection_manager.cpp b/nestkernel/connection_manager.cpp index a22ec86550..b87165ac16 100644 --- a/nestkernel/connection_manager.cpp +++ b/nestkernel/connection_manager.cpp @@ -65,6 +65,8 @@ nest::ConnectionManager::ConnectionManager() : connruledict_( new Dictionary() ) , connbuilder_factories_() + , thirdconnruledict_( new Dictionary() ) + , thirdconnbuilder_factories_() , min_delay_( 1 ) , max_delay_( 1 ) , keep_source_table_( true ) @@ -353,51 +355,26 @@ nest::BipartiteConnBuilder* nest::ConnectionManager::get_conn_builder( const std::string& name, NodeCollectionPTR sources, NodeCollectionPTR targets, + ThirdOutBuilder* third_out, const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_specs ) { - const size_t rule_id = connruledict_->lookup( name ); - BipartiteConnBuilder* cb = connbuilder_factories_.at( rule_id )->create( sources, targets, conn_spec, syn_specs ); - assert( cb ); - return cb; -} - -nest::BipartiteConnBuilder* -nest::ConnectionManager::get_conn_builder( const std::string& name, - NodeCollectionPTR sources, - NodeCollectionPTR targets, - NodeCollectionPTR third, - const DictionaryDatum& conn_spec, - const std::map< Name, std::vector< DictionaryDatum > >& syn_specs ) -{ - if ( not connruledict_->known( name ) ) - { - throw BadProperty( String::compose( "Unknown connection rule: %1", name ) ); - } - const size_t rule_id = connruledict_->lookup( name ); BipartiteConnBuilder* cb = - connbuilder_factories_.at( rule_id )->create( sources, targets, third, conn_spec, syn_specs ); + connbuilder_factories_.at( rule_id )->create( sources, targets, third_out, conn_spec, syn_specs ); assert( cb ); return cb; } -nest::BipartiteConnBuilder* +nest::ThirdOutBuilder* nest::ConnectionManager::get_third_conn_builder( const std::string& name, NodeCollectionPTR sources, NodeCollectionPTR targets, - NodeCollectionPTR third, const DictionaryDatum& conn_spec, - const std::map< Name, std::vector< DictionaryDatum > >& syn_specs ) + const std::vector< DictionaryDatum >& syn_specs ) { - if ( not thirdconnruledict_->known( name ) ) - { - throw BadProperty( String::compose( "Unknown third-factor connection rule: %1", name ) ); - } - - const size_t rule_id = connruledict_->lookup( name ); - BipartiteConnBuilder* cb = - connbuilder_factories_.at( rule_id )->create( sources, targets, third, conn_spec, syn_specs ); + const size_t rule_id = thirdconnruledict_->lookup( name ); + ThirdOutBuilder* cb = thirdconnbuilder_factories_.at( rule_id )->create( sources, targets, conn_spec, syn_specs ); assert( cb ); return cb; } @@ -437,14 +414,14 @@ nest::ConnectionManager::connect( NodeCollectionPTR sources, { throw BadProperty( "The connection specification must contain a connection rule." ); } - const std::string rule_name = static_cast< const std::string >( ( *conn_spec )[ names::rule ] ); + const std::string rule = static_cast< const std::string >( ( *conn_spec )[ names::rule ] ); - if ( not connruledict_->known( rule_name ) ) + if ( not connruledict_->known( rule ) ) { - throw BadProperty( String::compose( "Unknown connection rule: %1", rule_name ) ); + throw BadProperty( String::compose( "Unknown connection rule: %1", rule ) ); } - ConnBuilder cb( sources, targets, conn_spec, syn_specs ); + ConnBuilder cb( rule, sources, targets, conn_spec, syn_specs ); // at this point, all entries in conn_spec and syn_spec have been checked ALL_ENTRIES_ACCESSED( *conn_spec, "Connect", "Unread dictionary entries in conn_spec: " ); @@ -801,6 +778,7 @@ nest::ConnectionManager::connect_tripartite( NodeCollectionPTR sources, NodeCollectionPTR targets, NodeCollectionPTR third, const DictionaryDatum& conn_spec, + const DictionaryDatum& third_conn_spec, const std::map< Name, std::vector< DictionaryDatum > >& syn_specs ) { if ( sources->empty() ) @@ -835,9 +813,9 @@ nest::ConnectionManager::connect_tripartite( NodeCollectionPTR sources, } const std::string primary_rule = static_cast< const std::string >( ( *conn_spec )[ names::rule ] ); - const std::string third_rule = static_cast< const std::string >( ( *conn_spec )[ names::third_rule ] ); + const std::string third_rule = static_cast< const std::string >( ( *conn_spec )[ names::rule ] ); - ConnBuilder cb( primary_rule, third_rule, sources, targets, third, conn_spec, syn_specs ); + ConnBuilder cb( primary_rule, third_rule, sources, targets, third, conn_spec, third_conn_spec, syn_specs ); // at this point, all entries in conn_spec and syn_spec have been checked ALL_ENTRIES_ACCESSED( *conn_spec, "Connect", "Unread dictionary entries in conn_spec: " ); diff --git a/nestkernel/connection_manager.h b/nestkernel/connection_manager.h index e3e2fc41e8..37bffa8455 100644 --- a/nestkernel/connection_manager.h +++ b/nestkernel/connection_manager.h @@ -53,6 +53,7 @@ namespace nest { class GenericConnBuilderFactory; +class GenericThirdConnBuilderFactory; class spikecounter; class Node; class Event; @@ -94,20 +95,26 @@ class ConnectionManager : public ManagerInterface template < typename ConnBuilder > void register_conn_builder( const std::string& name ); + /** + * Add a connectivity rule, i.e. the respective ConnBuilderFactory. + */ + template < typename ThirdConnBuilder > + void register_third_conn_builder( const std::string& name ); + //! Obtain builder for bipartite connections BipartiteConnBuilder* get_conn_builder( const std::string& name, NodeCollectionPTR sources, NodeCollectionPTR targets, + ThirdOutBuilder* third_out, const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_specs ); - //! Obtain builder for tripartite connections - BipartiteConnBuilder* get_conn_builder( const std::string& name, + //! Obtain builder for bipartite connections + ThirdOutBuilder* get_third_conn_builder( const std::string& name, NodeCollectionPTR sources, NodeCollectionPTR targets, - NodeCollectionPTR third, const DictionaryDatum& conn_spec, - const std::map< Name, std::vector< DictionaryDatum > >& syn_specs ); + const std::vector< DictionaryDatum >& syn_specs ); /** * Create connections. @@ -192,6 +199,7 @@ class ConnectionManager : public ManagerInterface NodeCollectionPTR targets, NodeCollectionPTR third, const DictionaryDatum& connectivity, + const DictionaryDatum& third_connectivity, const std::map< Name, std::vector< DictionaryDatum > >& synapse_specs ); size_t find_connection( const size_t tid, const synindex syn_id, const size_t snode_id, const size_t tnode_id ); @@ -640,6 +648,11 @@ class ConnectionManager : public ManagerInterface //! ConnBuilder factories, indexed by connruledict_ elements. std::vector< GenericConnBuilderFactory* > connbuilder_factories_; + DictionaryDatum thirdconnruledict_; //!< Dictionary for third-factor connection rules. + + //! Third-factor ConnBuilder factories, indexed by thirdconnruledict_ elements. + std::vector< GenericThirdConnBuilderFactory* > thirdconnbuilder_factories_; + long min_delay_; //!< Value of the smallest delay in the network. long max_delay_; //!< Value of the largest delay in the network in steps. diff --git a/nestkernel/connection_manager_impl.h b/nestkernel/connection_manager_impl.h index 3563059a1b..7141ee3ec0 100644 --- a/nestkernel/connection_manager_impl.h +++ b/nestkernel/connection_manager_impl.h @@ -50,6 +50,18 @@ ConnectionManager::register_conn_builder( const std::string& name ) connruledict_->insert( name, id ); } +template < typename ThirdConnBuilder > +void +ConnectionManager::register_third_conn_builder( const std::string& name ) +{ + assert( not thirdconnruledict_->known( name ) ); + GenericThirdConnBuilderFactory* cb = new ThirdConnBuilderFactory< ThirdConnBuilder >(); + assert( cb ); + const int id = thirdconnbuilder_factories_.size(); + thirdconnbuilder_factories_.push_back( cb ); + thirdconnruledict_->insert( name, id ); +} + inline void ConnectionManager::send_to_devices( const size_t tid, const size_t source_node_id, Event& e ) { diff --git a/nestkernel/nest.cpp b/nestkernel/nest.cpp index 00e0e78fc8..262f7b62f2 100644 --- a/nestkernel/nest.cpp +++ b/nestkernel/nest.cpp @@ -192,9 +192,11 @@ connect_tripartite( NodeCollectionPTR sources, NodeCollectionPTR targets, NodeCollectionPTR third, const DictionaryDatum& connectivity, + const DictionaryDatum& third_connectivity, const std::map< Name, std::vector< DictionaryDatum > >& synapse_specs ) { - kernel().connection_manager.connect_tripartite( sources, targets, third, connectivity, synapse_specs ); + kernel().connection_manager.connect_tripartite( + sources, targets, third, connectivity, third_connectivity, synapse_specs ); } void diff --git a/nestkernel/nest.h b/nestkernel/nest.h index 2ecfece57b..fec9e9b830 100644 --- a/nestkernel/nest.h +++ b/nestkernel/nest.h @@ -102,6 +102,7 @@ void connect_tripartite( NodeCollectionPTR sources, NodeCollectionPTR targets, NodeCollectionPTR third, const DictionaryDatum& connectivity, + const DictionaryDatum& third_connectivity, const std::map< Name, std::vector< DictionaryDatum > >& synapse_specs ); /** diff --git a/nestkernel/nestmodule.cpp b/nestkernel/nestmodule.cpp index f6f206a5d8..bc4a5feb65 100644 --- a/nestkernel/nestmodule.cpp +++ b/nestkernel/nestmodule.cpp @@ -773,12 +773,13 @@ NestModule::ConnectTripartite_g_g_g_D_DFunction::execute( SLIInterpreter* i ) co { kernel().connection_manager.sw_construction_connect.start(); - i->assert_stack_load( 5 ); + i->assert_stack_load( 6 ); - NodeCollectionDatum sources = getValue< NodeCollectionDatum >( i->OStack.pick( 4 ) ); - NodeCollectionDatum targets = getValue< NodeCollectionDatum >( i->OStack.pick( 3 ) ); - NodeCollectionDatum third = getValue< NodeCollectionDatum >( i->OStack.pick( 2 ) ); - DictionaryDatum connectivity = getValue< DictionaryDatum >( i->OStack.pick( 1 ) ); + NodeCollectionDatum sources = getValue< NodeCollectionDatum >( i->OStack.pick( 5 ) ); + NodeCollectionDatum targets = getValue< NodeCollectionDatum >( i->OStack.pick( 4 ) ); + NodeCollectionDatum third = getValue< NodeCollectionDatum >( i->OStack.pick( 3 ) ); + DictionaryDatum connectivity = getValue< DictionaryDatum >( i->OStack.pick( 2 ) ); + DictionaryDatum third_connectivity = getValue< DictionaryDatum >( i->OStack.pick( 1 ) ); DictionaryDatum synapse_specs_dict = getValue< DictionaryDatum >( i->OStack.pick( 0 ) ); std::map< Name, std::vector< DictionaryDatum > > synapse_specs { @@ -796,9 +797,9 @@ NestModule::ConnectTripartite_g_g_g_D_DFunction::execute( SLIInterpreter* i ) co } // dictionary access checking is handled by connect - connect_tripartite( sources, targets, third, connectivity, synapse_specs ); + connect_tripartite( sources, targets, third, connectivity, third_connectivity, synapse_specs ); - i->OStack.pop( 5 ); + i->OStack.pop( 6 ); i->EStack.pop(); kernel().connection_manager.sw_construction_connect.stop(); @@ -2121,7 +2122,7 @@ NestModule::init( SLIInterpreter* i ) i->createcommand( "Connect_g_g_D_D", &connect_g_g_D_Dfunction ); i->createcommand( "Connect_g_g_D_a", &connect_g_g_D_afunction ); i->createcommand( "ConnectSonata_D", &ConnectSonata_D_Function ); - i->createcommand( "ConnectTripartite_g_g_g_D_D", &connect_tripartite_g_g_g_D_Dfunction ); + i->createcommand( "ConnectTripartite_g_g_g_D_D_D", &connect_tripartite_g_g_g_D_D_Dfunction ); i->createcommand( "ResetKernel", &resetkernelfunction ); @@ -2204,10 +2205,11 @@ NestModule::init( SLIInterpreter* i ) 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< TripartiteBernoulliWithPoolBuilder >( - "tripartite_bernoulli_with_pool" ); kernel().connection_manager.register_conn_builder< SymmetricBernoulliBuilder >( "symmetric_pairwise_bernoulli" ); kernel().connection_manager.register_conn_builder< FixedTotalNumberBuilder >( "fixed_total_number" ); + kernel().connection_manager.register_third_conn_builder< ThirdBernoulliWithPoolBuilder >( + "third_factor_bernoulli_with_pool" ); + #ifdef HAVE_LIBNEUROSIM kernel().connection_manager.register_conn_builder< ConnectionGeneratorBuilder >( "conngen" ); #endif diff --git a/nestkernel/nestmodule.h b/nestkernel/nestmodule.h index c028d2f17c..7a32eebda8 100644 --- a/nestkernel/nestmodule.h +++ b/nestkernel/nestmodule.h @@ -549,7 +549,7 @@ class NestModule : public SLIModule { public: void execute( SLIInterpreter* ) const override; - } connect_tripartite_g_g_g_D_Dfunction; + } connect_tripartite_g_g_g_D_D_Dfunction; class ResetKernelFunction : public SLIFunction { diff --git a/nestkernel/sp_manager.cpp b/nestkernel/sp_manager.cpp index 9c205c39ac..3dd7544368 100644 --- a/nestkernel/sp_manager.cpp +++ b/nestkernel/sp_manager.cpp @@ -139,6 +139,8 @@ SPManager::set_status( const DictionaryDatum& d ) } // We use a ConnBuilder with dummy values to check the synapse parameters + assert( false ); + /* SPBuilder* conn_builder = new SPBuilder( sources, targets, conn_spec, { syn_spec } ); conn_builder->set_name( i->first.toString() ); @@ -151,6 +153,7 @@ SPManager::set_status( const DictionaryDatum& d ) "specify the min and max delay in the kernel parameters." ); } sp_conn_builders_.push_back( conn_builder ); + */ } } @@ -260,15 +263,19 @@ SPManager::disconnect( NodeCollectionPTR sources, std::string synModel = getValue< std::string >( syn_spec, names::synapse_model ); if ( ( *i )->get_synapse_model() == kernel().model_manager.get_synapse_model_id( synModel ) ) { + /* cb = kernel().connection_manager.get_conn_builder( rule_name, sources, targets, conn_spec, { syn_spec } ); cb->set_synaptic_element_names( ( *i )->get_pre_synaptic_element_name(), ( *i )->get_post_synaptic_element_name() ); + */ } } } else { + /* cb = kernel().connection_manager.get_conn_builder( rule_name, sources, targets, conn_spec, { syn_spec } ); + */ } assert( cb ); From 3f6f7e90f6c2d12b14a56b49fc2a549903b6592c Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Fri, 23 Feb 2024 13:17:58 +0100 Subject: [PATCH 03/86] All tests not related to disconnect, structural plasticity or tripartite connectivity pass --- nestkernel/conn_builder.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index cb46685068..7117efd262 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -102,7 +102,10 @@ void nest::ConnBuilder::connect() { primary_builder_->connect(); // triggers third_out_builder_ - third_in_builder_->connect(); + if ( third_in_builder_ ) + { + third_in_builder_->connect(); + } } void From 0dab76e9e80611de7fbe15a78687dfd80a5f23a7 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Fri, 23 Feb 2024 15:10:32 +0100 Subject: [PATCH 04/86] First minimal example works --- nestkernel/conn_builder.cpp | 97 +++++++++++++++---- nestkernel/conn_builder.h | 69 +++++++------ nestkernel/conn_builder_factory.h | 4 +- nestkernel/connection_manager.cpp | 6 +- nestkernel/connection_manager.h | 1 + nestkernel/nestmodule.cpp | 2 +- nestkernel/nestmodule.h | 2 +- .../astrocytes/astrocyte_small_network.py | 9 +- pynest/nest/lib/hl_api_connections.py | 9 +- 9 files changed, 138 insertions(+), 61 deletions(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index 7117efd262..c8e2da7413 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -49,8 +49,8 @@ nest::ConnBuilder::ConnBuilder( const std::string& primary_rule, NodeCollectionPTR targets, const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_specs ) - : third_out_builder_( nullptr ) - , third_in_builder_( nullptr ) + : third_in_builder_( nullptr ) + , third_out_builder_( nullptr ) , primary_builder_( kernel().connection_manager.get_conn_builder( primary_rule, sources, targets, @@ -68,17 +68,17 @@ nest::ConnBuilder::ConnBuilder( const std::string& primary_rule, const DictionaryDatum& conn_spec, const DictionaryDatum& third_conn_spec, const std::map< Name, std::vector< DictionaryDatum > >& syn_specs ) - : third_out_builder_( kernel().connection_manager.get_third_conn_builder( third_rule, - targets, + : third_in_builder_( new ThirdInBuilder( sources, third, third_conn_spec, - // const_cast here seems required, clang complains otherwise; try to clean up when Datums disappear - const_cast< std::map< Name, std::vector< DictionaryDatum > >& >( syn_specs )[ names::third_out ] ) ) - , third_in_builder_( new ThirdInBuilder( sources, + const_cast< std::map< Name, std::vector< DictionaryDatum > >& >( syn_specs )[ names::third_in ] ) ) + , third_out_builder_( kernel().connection_manager.get_third_conn_builder( third_rule, third, - third_out_builder_, + targets, + third_in_builder_, third_conn_spec, - const_cast< std::map< Name, std::vector< DictionaryDatum > >& >( syn_specs )[ names::third_in ] ) ) + // const_cast here seems required, clang complains otherwise; try to clean up when Datums disappear + const_cast< std::map< Name, std::vector< DictionaryDatum > >& >( syn_specs )[ names::third_out ] ) ) , primary_builder_( kernel().connection_manager.get_conn_builder( primary_rule, sources, targets, @@ -92,6 +92,11 @@ nest::ConnBuilder::~ConnBuilder() { delete primary_builder_; // fully constructed CB has one + if ( third_in_builder_ ) + { + delete third_in_builder_; + } + if ( third_out_builder_ ) { delete third_out_builder_; @@ -650,25 +655,68 @@ nest::BipartiteConnBuilder::reset_delays_() nest::ThirdInBuilder::ThirdInBuilder( NodeCollectionPTR sources, NodeCollectionPTR third, - ThirdOutBuilder* third_out, const DictionaryDatum& third_conn_spec, const std::vector< DictionaryDatum >& syn_specs ) : BipartiteConnBuilder( sources, third, nullptr, third_conn_spec, syn_specs ) - , third_out_( third_out ) + , source_third_gids_( kernel().vp_manager.get_num_threads(), nullptr ) + , source_third_counts_( kernel().vp_manager.get_num_threads(), nullptr ) +{ +#pragma omp parallel + { + const size_t thrd = kernel().vp_manager.get_thread_id(); + source_third_gids_[ thrd ] = new BlockVector< SourceThirdInfo_ >(); + source_third_counts_[ thrd ] = new std::vector< size_t >( kernel().mpi_manager.get_num_processes(), 0 ); + } +} + +nest::ThirdInBuilder::~ThirdInBuilder() { +#pragma omp parallel + { + const size_t thrd = kernel().vp_manager.get_thread_id(); + delete source_third_gids_[ thrd ]; + delete source_third_counts_[ thrd ]; + } +} + +void +nest::ThirdInBuilder::register_connection( size_t primary_source_id, size_t third_node_id ) +{ + const size_t tid = kernel().vp_manager.get_thread_id(); + const auto third_node_rank = + kernel().mpi_manager.get_process_id_of_vp( kernel().vp_manager.node_id_to_vp( third_node_id ) ); + source_third_gids_[ tid ]->push_back( { primary_source_id, third_node_id, third_node_rank } ); + ++( ( *source_third_counts_[ tid ] )[ third_node_rank ] ); } void nest::ThirdInBuilder::connect_() { - std::cout << "not implemented yet" << std::endl; + kernel().vp_manager.assert_single_threaded(); + +#pragma omp parallel + { + const size_t tid = kernel().vp_manager.get_thread_id(); + RngPtr rng = kernel().random_manager.get_vp_specific_rng( tid ); + + for ( auto& stg : *source_third_gids_[ tid ] ) + { + if ( not kernel().vp_manager.is_node_id_vp_local( stg.third_gid ) ) + { + continue; + } + single_connect_( stg.source_gid, *kernel().node_manager.get_node_or_proxy( stg.third_gid, tid ), tid, rng ); + } + } } nest::ThirdOutBuilder::ThirdOutBuilder( const NodeCollectionPTR third, const NodeCollectionPTR targets, + ThirdInBuilder* third_in, const DictionaryDatum& third_conn_spec, const std::vector< DictionaryDatum >& syn_specs ) : BipartiteConnBuilder( third, targets, nullptr, third_conn_spec, syn_specs ) + , third_in_( third_in ) { } @@ -678,16 +726,19 @@ nest::ThirdOutBuilder::connect() assert( false ); // should never be called } + nest::ThirdBernoulliWithPoolBuilder::ThirdBernoulliWithPoolBuilder( const NodeCollectionPTR third, const NodeCollectionPTR targets, + ThirdInBuilder* third_in, const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_specs ) - : ThirdOutBuilder( third, targets, conn_spec, syn_specs ) + : ThirdOutBuilder( third, targets, third_in, conn_spec, syn_specs ) , p_( 1.0 ) , random_pool_( true ) , pool_size_( third->size() ) , targets_per_third_( targets->size() / third->size() ) , previous_target_( kernel().vp_manager.get_num_threads(), nullptr ) + , pool_( kernel().vp_manager.get_num_threads(), nullptr ) { updateValue< double >( conn_spec, names::p, p_ ); updateValue< long >( conn_spec, names::pool_size, pool_size_ ); @@ -732,8 +783,16 @@ nest::ThirdBernoulliWithPoolBuilder::ThirdBernoulliWithPoolBuilder( const NodeCo { const size_t thrd = kernel().vp_manager.get_thread_id(); pool_[ thrd ] = new std::vector< NodeIDTriple >(); - source_third_gids_[ thrd ] = new BlockVector< SourceThirdInfo_ >(); - source_third_counts_[ thrd ] = new std::vector< size_t >( kernel().mpi_manager.get_num_processes(), 0 ); + pool_[ thrd ]->reserve( pool_size_ ); + } +} + +nest::ThirdBernoulliWithPoolBuilder::~ThirdBernoulliWithPoolBuilder() +{ +#pragma omp parallel + { + const size_t thrd = kernel().vp_manager.get_thread_id(); + delete pool_[ thrd ]; } } @@ -744,7 +803,6 @@ nest::ThirdBernoulliWithPoolBuilder::third_connect( size_t primary_source_id, No const size_t tid = kernel().vp_manager.get_thread_id(); RngPtr rng = get_vp_specific_rng( tid ); - // conditionally connect third factor if ( not( rng->drand() < p_ ) ) { @@ -754,7 +812,7 @@ nest::ThirdBernoulliWithPoolBuilder::third_connect( size_t primary_source_id, No // step 2, build pool if new target if ( &primary_target != previous_target_[ tid ] ) { - pool_[ tid ]->reserve( pool_size_ ); + pool_[ tid ]->clear(); if ( random_pool_ ) { rng->sample( sources_->begin(), sources_->end(), std::back_inserter( *pool_[ tid ] ), pool_size_ ); @@ -770,13 +828,10 @@ nest::ThirdBernoulliWithPoolBuilder::third_connect( size_t primary_source_id, No // select third-factor neuron randomly from pool for this target const auto third_index = pool_size_ == 1 ? 0 : rng->ulrand( pool_size_ ); const auto third_node_id = ( *pool_[ tid ] )[ third_index ].node_id; - const auto third_node_rank = - kernel().mpi_manager.get_process_id_of_vp( kernel().vp_manager.node_id_to_vp( third_node_id ) ); single_connect_( third_node_id, primary_target, tid, rng ); - source_third_gids_[ tid ]->push_back( { primary_source_id, third_node_id, third_node_rank } ); - ++( ( *source_third_counts_[ tid ] )[ third_node_rank ] ); + third_in_->register_connection( primary_source_id, third_node_id ); } diff --git a/nestkernel/conn_builder.h b/nestkernel/conn_builder.h index 62e194be1d..87fbfb1ada 100644 --- a/nestkernel/conn_builder.h +++ b/nestkernel/conn_builder.h @@ -299,14 +299,45 @@ class ThirdInBuilder : public BipartiteConnBuilder public: ThirdInBuilder( NodeCollectionPTR, NodeCollectionPTR, - ThirdOutBuilder*, const DictionaryDatum&, // only for compatibility with BCB const std::vector< DictionaryDatum >& ); + ~ThirdInBuilder(); + + void register_connection( size_t primary_source_id, size_t primary_target_id ); private: void connect_() override; - ThirdOutBuilder* third_out_; + /** + * Provide information on connections from primary source to third-factor population. + * + * Data is written by ThirdOutBuilder and communicated and processed by ThirdInBuilder. + */ + struct SourceThirdInfo_ + { + SourceThirdInfo_() + : source_gid( 0 ) + , third_gid( 0 ) + , third_rank( 0 ) + { + } + SourceThirdInfo_( size_t src, size_t trd, size_t rank ) + : source_gid( src ) + , third_gid( trd ) + , third_rank( rank ) + { + } + + size_t source_gid; + size_t third_gid; + size_t third_rank; + }; + + //! source-thirdparty GID pairs to be communicated; one per thread + std::vector< BlockVector< SourceThirdInfo_ >* > source_third_gids_; + + //! number of source-third pairs to send. Outer dimension writing thread, inner dimension rank to send to + std::vector< std::vector< size_t >* > source_third_counts_; }; // to be subclassed further @@ -315,12 +346,16 @@ class ThirdOutBuilder : public BipartiteConnBuilder public: ThirdOutBuilder( NodeCollectionPTR, NodeCollectionPTR, + ThirdInBuilder*, const DictionaryDatum&, // only for compatibility with BCB const std::vector< DictionaryDatum >& ); void connect() override; virtual void third_connect( size_t source_gid, Node& target ) = 0; + +protected: + ThirdInBuilder* third_in_; }; @@ -379,8 +414,8 @@ class ConnBuilder private: // order of declarations based on dependencies - ThirdOutBuilder* third_out_builder_; ThirdInBuilder* third_in_builder_; + ThirdOutBuilder* third_out_builder_; BipartiteConnBuilder* primary_builder_; }; @@ -390,8 +425,10 @@ class ThirdBernoulliWithPoolBuilder : public ThirdOutBuilder public: ThirdBernoulliWithPoolBuilder( NodeCollectionPTR, NodeCollectionPTR, + ThirdInBuilder* third_in, const DictionaryDatum&, // only for compatibility with BCB const std::vector< DictionaryDatum >& ); + ~ThirdBernoulliWithPoolBuilder(); void third_connect( size_t source_gid, Node& target ) override; @@ -409,32 +446,6 @@ class ThirdBernoulliWithPoolBuilder : public ThirdOutBuilder size_t targets_per_third_; std::vector< Node* > previous_target_; // TODO: cache thrashing possibility std::vector< std::vector< NodeIDTriple >* > pool_; // outer: threads - - struct SourceThirdInfo_ - { - SourceThirdInfo_() - : source_gid( 0 ) - , third_gid( 0 ) - , third_rank( 0 ) - { - } - SourceThirdInfo_( size_t src, size_t trd, size_t rank ) - : source_gid( src ) - , third_gid( trd ) - , third_rank( rank ) - { - } - - size_t source_gid; - size_t third_gid; - size_t third_rank; - }; - - //! source-thirdparty GID pairs to be communicated; one per thread - std::vector< BlockVector< SourceThirdInfo_ >* > source_third_gids_; - - //! number of source-third pairs to send. Outer dimension writing thread, inner dimension rank to send to - std::vector< std::vector< size_t >* > source_third_counts_; }; diff --git a/nestkernel/conn_builder_factory.h b/nestkernel/conn_builder_factory.h index 8603190c6e..584acc8fd2 100644 --- a/nestkernel/conn_builder_factory.h +++ b/nestkernel/conn_builder_factory.h @@ -90,6 +90,7 @@ class GenericThirdConnBuilderFactory */ virtual ThirdOutBuilder* create( NodeCollectionPTR, NodeCollectionPTR, + ThirdInBuilder*, const DictionaryDatum&, const std::vector< DictionaryDatum >& ) const = 0; }; @@ -104,10 +105,11 @@ class ThirdConnBuilderFactory : public GenericThirdConnBuilderFactory ThirdOutBuilder* create( NodeCollectionPTR sources, NodeCollectionPTR targets, + ThirdInBuilder* third_in, const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_specs ) const override { - return new ThirdConnBuilderType( sources, targets, conn_spec, syn_specs ); + return new ThirdConnBuilderType( sources, targets, third_in, conn_spec, syn_specs ); } }; diff --git a/nestkernel/connection_manager.cpp b/nestkernel/connection_manager.cpp index b87165ac16..5eaa951ee9 100644 --- a/nestkernel/connection_manager.cpp +++ b/nestkernel/connection_manager.cpp @@ -370,11 +370,13 @@ nest::ThirdOutBuilder* nest::ConnectionManager::get_third_conn_builder( const std::string& name, NodeCollectionPTR sources, NodeCollectionPTR targets, + ThirdInBuilder* third_in, const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_specs ) { const size_t rule_id = thirdconnruledict_->lookup( name ); - ThirdOutBuilder* cb = thirdconnbuilder_factories_.at( rule_id )->create( sources, targets, conn_spec, syn_specs ); + ThirdOutBuilder* cb = + thirdconnbuilder_factories_.at( rule_id )->create( sources, targets, third_in, conn_spec, syn_specs ); assert( cb ); return cb; } @@ -813,7 +815,7 @@ nest::ConnectionManager::connect_tripartite( NodeCollectionPTR sources, } const std::string primary_rule = static_cast< const std::string >( ( *conn_spec )[ names::rule ] ); - const std::string third_rule = static_cast< const std::string >( ( *conn_spec )[ names::rule ] ); + const std::string third_rule = static_cast< const std::string >( ( *third_conn_spec )[ names::rule ] ); ConnBuilder cb( primary_rule, third_rule, sources, targets, third, conn_spec, third_conn_spec, syn_specs ); diff --git a/nestkernel/connection_manager.h b/nestkernel/connection_manager.h index 37bffa8455..431ab7d1d3 100644 --- a/nestkernel/connection_manager.h +++ b/nestkernel/connection_manager.h @@ -113,6 +113,7 @@ class ConnectionManager : public ManagerInterface ThirdOutBuilder* get_third_conn_builder( const std::string& name, NodeCollectionPTR sources, NodeCollectionPTR targets, + ThirdInBuilder* third_in, const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_specs ); diff --git a/nestkernel/nestmodule.cpp b/nestkernel/nestmodule.cpp index bc4a5feb65..de1b48ff08 100644 --- a/nestkernel/nestmodule.cpp +++ b/nestkernel/nestmodule.cpp @@ -769,7 +769,7 @@ NestModule::Connect_g_g_D_aFunction::execute( SLIInterpreter* i ) const void -NestModule::ConnectTripartite_g_g_g_D_DFunction::execute( SLIInterpreter* i ) const +NestModule::ConnectTripartite_g_g_g_D_D_DFunction::execute( SLIInterpreter* i ) const { kernel().connection_manager.sw_construction_connect.start(); diff --git a/nestkernel/nestmodule.h b/nestkernel/nestmodule.h index 7a32eebda8..8c34c5092f 100644 --- a/nestkernel/nestmodule.h +++ b/nestkernel/nestmodule.h @@ -545,7 +545,7 @@ class NestModule : public SLIModule void execute( SLIInterpreter* ) const; } ConnectSonata_D_Function; - class ConnectTripartite_g_g_g_D_DFunction : public SLIFunction + class ConnectTripartite_g_g_g_D_D_DFunction : public SLIFunction { public: void execute( SLIInterpreter* ) const override; diff --git a/pynest/examples/astrocytes/astrocyte_small_network.py b/pynest/examples/astrocytes/astrocyte_small_network.py index 9411b78d07..36ae5f1af7 100644 --- a/pynest/examples/astrocytes/astrocyte_small_network.py +++ b/pynest/examples/astrocytes/astrocyte_small_network.py @@ -399,9 +399,12 @@ def plot_dynamics(astro_data, neuron_data, start): post_neurons, astrocytes, conn_spec={ - "rule": "tripartite_bernoulli_with_pool", - "p_primary": p_primary, - "p_third_if_primary": p_third_if_primary, + "rule": "bernoulli", + "p": p_primary, + }, + third_factor_conn_spec={ + "rule": "third_factor_bernoulli_with_pool", + "p": p_third_if_primary, "pool_size": pool_size, "pool_type": pool_type, }, diff --git a/pynest/nest/lib/hl_api_connections.py b/pynest/nest/lib/hl_api_connections.py index b977c3f23b..e6b43b440f 100644 --- a/pynest/nest/lib/hl_api_connections.py +++ b/pynest/nest/lib/hl_api_connections.py @@ -296,7 +296,7 @@ def Connect(pre, post, conn_spec=None, syn_spec=None, return_synapsecollection=F @check_stack -def TripartiteConnect(pre, post, third, conn_spec, syn_specs=None): +def TripartiteConnect(pre, post, third, conn_spec, third_factor_conn_spec, syn_specs=None): """ Connect `pre` nodes to `post` nodes and a `third`-factor nodes. @@ -317,7 +317,9 @@ def TripartiteConnect(pre, post, third, conn_spec, syn_specs=None): third : NodeCollection Third population to include in connection conn_spec : dict - Specifies connection rule, which must support tripartite connections, see below + Specifies connection rule for primary connection + third_factor_conn_spec: dict + Specifies third-factor connection rule syn_spec : dict, optional Specifies synapse models to be used, see below @@ -403,8 +405,9 @@ def TripartiteConnect(pre, post, third, conn_spec, syn_specs=None): sps(post) sps(third) sps(conn_spec) + sps(third_factor_conn_spec) sps(syn_specs) - sr("ConnectTripartite_g_g_g_D_D") + sr("ConnectTripartite_g_g_g_D_D_D") @check_stack From 4e1d0d9a9cf6c0839274ebf44e6e5e16797bcab7 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Sun, 25 Feb 2024 17:37:33 +0100 Subject: [PATCH 05/86] Version working for single MPI process, random pool, no disconnect/structural plast --- nestkernel/conn_builder.cpp | 17 +- nestkernel/connection_manager.cpp | 10 + .../examples/astrocytes/astrocyte_brunel.py | 22 +- .../astrocytes/astrocyte_brunel_fix_in.py | 389 ++++++++++++++++++ .../astrocytes/astrocyte_small_network.py | 4 +- 5 files changed, 429 insertions(+), 13 deletions(-) create mode 100644 pynest/examples/astrocytes/astrocyte_brunel_fix_in.py diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index c8e2da7413..efd0421784 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -699,13 +699,17 @@ nest::ThirdInBuilder::connect_() const size_t tid = kernel().vp_manager.get_thread_id(); RngPtr rng = kernel().random_manager.get_vp_specific_rng( tid ); - for ( auto& stg : *source_third_gids_[ tid ] ) + for ( auto& conn_pairs_per_thread : source_third_gids_ ) { - if ( not kernel().vp_manager.is_node_id_vp_local( stg.third_gid ) ) + for ( auto& conn_pair : *conn_pairs_per_thread ) { - continue; + if ( not kernel().vp_manager.is_node_id_vp_local( conn_pair.third_gid ) ) + { + continue; + } + single_connect_( + conn_pair.source_gid, *kernel().node_manager.get_node_or_proxy( conn_pair.third_gid, tid ), tid, rng ); } - single_connect_( stg.source_gid, *kernel().node_manager.get_node_or_proxy( stg.third_gid, tid ), tid, rng ); } } } @@ -819,10 +823,13 @@ nest::ThirdBernoulliWithPoolBuilder::third_connect( size_t primary_source_id, No } else { - std::copy_n( sources_->begin() + get_first_pool_index_( primary_target.get_thread_lid() ), + throw NotImplemented( + "block pools are currently not implemented because we do not have access to pos of target in targets" ); + std::copy_n( sources_->begin() + get_first_pool_index_( 0 /* pos ot target in targets node collection */ ), pool_size_, std::back_inserter( *( pool_[ tid ] ) ) ); } + previous_target_[ tid ] = &primary_target; } // select third-factor neuron randomly from pool for this target diff --git a/nestkernel/connection_manager.cpp b/nestkernel/connection_manager.cpp index 5eaa951ee9..6bcbb080fa 100644 --- a/nestkernel/connection_manager.cpp +++ b/nestkernel/connection_manager.cpp @@ -359,6 +359,11 @@ nest::ConnectionManager::get_conn_builder( const std::string& name, const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_specs ) { + if ( not connruledict_->known( name ) ) + { + throw IllegalConnection( String::compose( "Unknown connection rule '%1'.", name ) ); + } + const size_t rule_id = connruledict_->lookup( name ); BipartiteConnBuilder* cb = connbuilder_factories_.at( rule_id )->create( sources, targets, third_out, conn_spec, syn_specs ); @@ -374,6 +379,11 @@ nest::ConnectionManager::get_third_conn_builder( const std::string& name, const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_specs ) { + if ( not thirdconnruledict_->known( name ) ) + { + throw IllegalConnection( String::compose( "Unknown third-factor connection rule '%1'.", name ) ); + } + const size_t rule_id = thirdconnruledict_->lookup( name ); ThirdOutBuilder* cb = thirdconnbuilder_factories_.at( rule_id )->create( sources, targets, third_in, conn_spec, syn_specs ); diff --git a/pynest/examples/astrocytes/astrocyte_brunel.py b/pynest/examples/astrocytes/astrocyte_brunel.py index 78669886cc..435d1ac173 100644 --- a/pynest/examples/astrocytes/astrocyte_brunel.py +++ b/pynest/examples/astrocytes/astrocyte_brunel.py @@ -78,7 +78,7 @@ "N_rec_spk": 100, # number of neurons to record from with spike recorder "N_rec_mm": 50, # number of nodes (neurons, astrocytes) to record from with multimeter "n_threads": 4, # number of threads for NEST - "seed": 100, # seed for the random module + "seed": 1010, # seed for the random module } ############################################################################### @@ -192,10 +192,10 @@ def connect_astro_network(nodes_ex, nodes_in, nodes_astro, nodes_noise, scale=1. print("Connecting neurons and astrocytes ...") # excitatory connections are paired with astrocytes # conn_spec and syn_spec according to the "tripartite_bernoulli_with_pool" rule - conn_params_e = { - "rule": "tripartite_bernoulli_with_pool", - "p_primary": network_params["p_primary"] / scale, - "p_third_if_primary": network_params[ + conn_params_e = {"rule": "pairwise_bernoulli", "p": network_params["p_primary"] / scale} + conn_params_astro = { + "rule": "third_factor_bernoulli_with_pool", + "p": network_params[ "p_third_if_primary" ], # "p_third_if_primary" is scaled along with "p_primary", so no further scaling is required "pool_size": network_params["pool_size"], @@ -216,7 +216,14 @@ def connect_astro_network(nodes_ex, nodes_in, nodes_astro, nodes_noise, scale=1. }, "third_out": {"synapse_model": "sic_connection", "weight": syn_params["w_a2n"]}, } - nest.TripartiteConnect(nodes_ex, nodes_ex + nodes_in, nodes_astro, conn_spec=conn_params_e, syn_specs=syn_params_e) + nest.TripartiteConnect( + nodes_ex, + nodes_ex + nodes_in, + nodes_astro, + conn_spec=conn_params_e, + third_factor_conn_spec=conn_params_astro, + syn_specs=syn_params_e, + ) # inhibitory connections are not paired with astrocytes conn_params_i = {"rule": "pairwise_bernoulli", "p": network_params["p_primary"] / scale} syn_params_i = { @@ -374,3 +381,6 @@ def run_simulation(): # Run simulation. run_simulation() + +print(nest.num_connections) +print(nest.GetKernelStatus()["time_construction_connect"]) diff --git a/pynest/examples/astrocytes/astrocyte_brunel_fix_in.py b/pynest/examples/astrocytes/astrocyte_brunel_fix_in.py new file mode 100644 index 0000000000..cb2dd4bcef --- /dev/null +++ b/pynest/examples/astrocytes/astrocyte_brunel_fix_in.py @@ -0,0 +1,389 @@ +# -*- coding: utf-8 -*- +# +# astrocyte_brunel.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 . + +""" +Random balanced network with astrocytes +--------------------------------------- + +This script simulates a random balanced network with excitatory and inhibitory +neurons and astrocytes. The astrocytes are modeled with ``astrocyte_lr_1994``, +implemented according to [1]_, [2]_, and [3]_. The neurons are modeled with +``aeif_cond_alpha_astro``, an adaptive exponential integrate-and-fire neuron +supporting neuron-astrocyte interactions. + +The simulation results show how astrocytes affect neuronal excitability. The +astrocytic dynamics, the slow inward current in the neurons induced by the +astrocytes, and the raster plot of neuronal firings are shown in the created +figures. + +References +~~~~~~~~~~ + +.. [1] Li, Y. X., & Rinzel, J. (1994). Equations for InsP3 receptor-mediated + [Ca2+]i oscillations derived from a detailed kinetic model: a + Hodgkin-Huxley like formalism. Journal of theoretical Biology, 166(4), + 461-473. DOI: https://doi.org/10.1006/jtbi.1994.1041 + +.. [2] De Young, G. W., & Keizer, J. (1992). A single-pool inositol + 1,4,5-trisphosphate-receptor-based model for agonist-stimulated + oscillations in Ca2+ concentration. Proceedings of the National Academy + of Sciences, 89(20), 9895-9899. DOI: + https://doi.org/10.1073/pnas.89.20.9895 + +.. [3] Nadkarni, S., & Jung, P. (2003). Spontaneous oscillations of dressed + neurons: a new mechanism for epilepsy?. Physical review letters, 91(26), + 268101. DOI: https://doi.org/10.1103/PhysRevLett.91.268101 + +See Also +~~~~~~~~ + +:doc:`astrocyte_small_network` + +""" + +############################################################################### +# Import all necessary modules for simulation and plotting. + +import random + +import matplotlib.pyplot as plt +import nest +import numpy as np + +############################################################################### +# Set simulation parameters. + +sim_params = { + "dt": 0.1, # simulation resolution in ms + "pre_sim_time": 100.0, # pre-simulation time in ms (data not recorded) + "sim_time": 1000.0, # simulation time in ms + "N_rec_spk": 100, # number of neurons to record from with spike recorder + "N_rec_mm": 50, # number of nodes (neurons, astrocytes) to record from with multimeter + "n_threads": 4, # number of threads for NEST + "seed": 100, # seed for the random module +} + +############################################################################### +# Set network parameters. + +network_params = { + "N_ex": 8000, # number of excitatory neurons + "N_in": 2000, # number of inhibitory neurons + "N_astro": 10000, # number of astrocytes + "CE": 800, + "CI": 200, + "p_third_if_primary": 0.5, # probability of each created neuron-neuron connection to be paired with one astrocyte + "pool_size": 10, # astrocyte pool size for each target neuron + "pool_type": "random", # astrocyte pool will be chosen randomly for each target neuron + "poisson_rate": 2000, # Poisson input rate for neurons +} + +syn_params = { + "w_a2n": 0.01, # weight of astrocyte-to-neuron connection + "w_e": 1.0, # weight of excitatory connection in nS + "w_i": -4.0, # weight of inhibitory connection in nS + "d_e": 2.0, # delay of excitatory connection in ms + "d_i": 1.0, # delay of inhibitory connection in ms +} + +############################################################################### +# Set astrocyte parameters. + +astrocyte_model = "astrocyte_lr_1994" +astrocyte_params = { + "IP3": 0.4, # IP3 initial value in µM + "delta_IP3": 0.5, # Parameter determining the increase in astrocytic IP3 concentration induced by synaptic input + "tau_IP3": 2.0, # Time constant of the exponential decay of astrocytic IP3 +} + +############################################################################### +# Set neuron parameters. + +neuron_model = "aeif_cond_alpha_astro" +tau_syn_ex = 2.0 +tau_syn_in = 4.0 + +neuron_params_ex = { + "tau_syn_ex": tau_syn_ex, # excitatory synaptic time constant in ms + "tau_syn_in": tau_syn_in, # inhibitory synaptic time constant in ms +} + +neuron_params_in = { + "tau_syn_ex": tau_syn_ex, # excitatory synaptic time constant in ms + "tau_syn_in": tau_syn_in, # inhibitory synaptic time constant in ms +} + +############################################################################### +# This function creates the nodes and build the network. The astrocytes only +# respond to excitatory synaptic inputs; therefore, only the excitatory +# neuron-neuron connections are paired with the astrocytes. The +# ``TripartiteConnect()`` function and the ``tripartite_bernoulli_with_pool`` rule +# are used to create the connectivity of the network. + + +def create_astro_network(scale=1.0): + """Create nodes for a neuron-astrocyte network. + + Nodes in a neuron-astrocyte network are created according to the give scale + of the model. The nodes created include excitatory and inhibitory neruons, + astrocytes, and a Poisson generator. + + Parameters + --------- + scale + Scale of the model. + + Return values + ------------- + Created nodes and Poisson generator. + + """ + print("Creating nodes ...") + assert scale >= 1.0, "scale must be >= 1.0" + nodes_ex = nest.Create(neuron_model, int(network_params["N_ex"] * scale), params=neuron_params_ex) + nodes_in = nest.Create(neuron_model, int(network_params["N_in"] * scale), params=neuron_params_in) + nodes_astro = nest.Create(astrocyte_model, int(network_params["N_astro"] * scale), params=astrocyte_params) + nodes_noise = nest.Create("poisson_generator", params={"rate": network_params["poisson_rate"]}) + return nodes_ex, nodes_in, nodes_astro, nodes_noise + + +def connect_astro_network(nodes_ex, nodes_in, nodes_astro, nodes_noise, scale=1.0): + """Connect the nodes in a neuron-astrocyte network. + + Nodes in a neuron-astrocyte network are connected. The connection + probability between neurons is divided by a the given scale to preserve + the expected number of connections for each node. The astrocytes are paired + with excitatory connections only. + + Parameters + --------- + nodes_ex + Nodes of excitatory neurons. + nodes_in + Nodes of inhibitory neurons. + nodes_astro + Nodes of astrocytes. + node_noise + Poisson generator. + scale + Scale of the model. + + """ + print("Connecting Poisson generator ...") + assert scale >= 1.0, "scale must be >= 1.0" + nest.Connect(nodes_noise, nodes_ex + nodes_in, syn_spec={"weight": syn_params["w_e"]}) + print("Connecting neurons and astrocytes ...") + # excitatory connections are paired with astrocytes + # conn_spec and syn_spec according to the "tripartite_bernoulli_with_pool" rule + conn_params_e = {"rule": "fixed_indegree", "indegree": network_params["CE"]} + conn_params_astro = { + "rule": "third_factor_bernoulli_with_pool", + "p": network_params["p_third_if_primary"], + "pool_size": network_params["pool_size"], + "pool_type": network_params["pool_type"], + } + syn_params_e = { + "primary": { + "synapse_model": "tsodyks_synapse", + "weight": syn_params["w_e"], + "tau_psc": tau_syn_ex, + "delay": syn_params["d_e"], + }, + "third_in": { + "synapse_model": "tsodyks_synapse", + "weight": syn_params["w_e"], + "tau_psc": tau_syn_ex, + "delay": syn_params["d_e"], + }, + "third_out": {"synapse_model": "sic_connection", "weight": syn_params["w_a2n"]}, + } + nest.TripartiteConnect( + nodes_ex, + nodes_ex + nodes_in, + nodes_astro, + conn_spec=conn_params_e, + third_factor_conn_spec=conn_params_astro, + syn_specs=syn_params_e, + ) + # nest.Connect(nodes_ex, nodes_ex + nodes_in, + # conn_spec=conn_params_e, + # syn_spec=syn_params_e["primary"]) + + # inhibitory connections are not paired with astrocytes + conn_params_i = {"rule": "fixed_indegree", "indegree": network_params["CI"]} + syn_params_i = { + "synapse_model": "tsodyks_synapse", + "weight": syn_params["w_i"], + "tau_psc": tau_syn_in, + "delay": syn_params["d_i"], + } + nest.Connect(nodes_in, nodes_ex + nodes_in, conn_params_i, syn_params_i) + + +############################################################################### +# This function plots the dynamics in the astrocytes and their resultant output +# to the neurons. The IP3 and calcium in the astrocytes and the SIC in neurons +# are plotted. Means and standard deviations across sampled nodes are indicated +# by lines and shaded areas, respectively. + + +def plot_dynamics(astro_data, neuron_data, start): + """Plot the dynamics in neurons and astrocytes. + + The dynamics in the given neuron and astrocyte nodes are plotted. The + dynamics in clude IP3 and calcium in the astrocytes, and the SIC input to + the neurons. + + Parameters + --------- + astro_data + Data of IP3 and calcium dynamics in the astrocytes. + neuron_data + Data of SIC input to the neurons. + start + Start time of the plotted dynamics. + + """ + print("Plotting dynamics ...") + # astrocyte data + astro_mask = astro_data["times"] > start + astro_ip3 = astro_data["IP3"][astro_mask] + astro_cal = astro_data["Ca_astro"][astro_mask] + astro_times = astro_data["times"][astro_mask] + astro_times_set = list(set(astro_times)) + ip3_means = np.array([np.mean(astro_ip3[astro_times == t]) for t in astro_times_set]) + ip3_sds = np.array([np.std(astro_ip3[astro_times == t]) for t in astro_times_set]) + cal_means = np.array([np.mean(astro_cal[astro_times == t]) for t in astro_times_set]) + cal_sds = np.array([np.std(astro_cal[astro_times == t]) for t in astro_times_set]) + # neuron data + neuron_mask = neuron_data["times"] > start + neuron_sic = neuron_data["I_SIC"][neuron_mask] + neuron_times = neuron_data["times"][neuron_mask] + neuron_times_set = list(set(neuron_times)) + sic_means = np.array([np.mean(neuron_sic[neuron_times == t]) for t in neuron_times_set]) + sic_sds = np.array([np.std(neuron_sic[neuron_times == t]) for t in neuron_times_set]) + # set plots + fig, axes = plt.subplots(2, 1, sharex=True) + color_ip3 = "tab:blue" + color_cal = "tab:green" + color_sic = "tab:purple" + # astrocyte plot + axes[0].set_title(f"{r'IP$_{3}$'} and {r'Ca$^{2+}$'} in astrocytes (n={len(set(astro_data['senders']))})") + axes[0].set_ylabel(r"IP$_{3}$ ($\mu$M)") + axes[0].tick_params(axis="y", labelcolor=color_ip3) + axes[0].fill_between( + astro_times_set, ip3_means + ip3_sds, ip3_means - ip3_sds, alpha=0.3, linewidth=0.0, color=color_ip3 + ) + axes[0].plot(astro_times_set, ip3_means, linewidth=2, color=color_ip3) + ax = axes[0].twinx() + ax.set_ylabel(r"Ca$^{2+}$ ($\mu$M)") + ax.tick_params(axis="y", labelcolor=color_cal) + ax.fill_between( + astro_times_set, cal_means + cal_sds, cal_means - cal_sds, alpha=0.3, linewidth=0.0, color=color_cal + ) + ax.plot(astro_times_set, cal_means, linewidth=2, color=color_cal) + # neuron plot + axes[1].set_title(f"SIC in neurons (n={len(set(neuron_data['senders']))})") + axes[1].set_ylabel("SIC (pA)") + axes[1].set_xlabel("Time (ms)") + axes[1].fill_between( + neuron_times_set, sic_means + sic_sds, sic_means - sic_sds, alpha=0.3, linewidth=0.0, color=color_sic + ) + axes[1].plot(neuron_times_set, sic_means, linewidth=2, color=color_sic) + + +############################################################################### +# This is the main function for simulation. The network is created and the +# neurons and astrocytes are randomly chosen for recording. After simulation, +# recorded data of neurons and astrocytes are plotted. + + +def run_simulation(): + """Run simulation of a neuron-astrocyte network.""" + # NEST configuration + nest.ResetKernel() + nest.resolution = sim_params["dt"] + nest.local_num_threads = sim_params["n_threads"] + nest.print_time = True + nest.overwrite_files = True + + # use random seed for reproducible sampling + random.seed(sim_params["seed"]) + + # simulation settings + pre_sim_time = sim_params["pre_sim_time"] + sim_time = sim_params["sim_time"] + + # create and connect nodes + exc, inh, astro, noise = create_astro_network() + connect_astro_network(exc, inh, astro, noise) + + # create and connect recorders (multimeter default resolution = 1 ms) + sr_neuron = nest.Create("spike_recorder") + mm_neuron = nest.Create("multimeter", params={"record_from": ["I_SIC"]}) + mm_astro = nest.Create("multimeter", params={"record_from": ["IP3", "Ca_astro"]}) + + # select nodes randomly and connect them with recorders + print("Connecting recorders ...") + neuron_list = (exc + inh).tolist() + astro_list = astro.tolist() + n_neuron_rec_spk = min(len(neuron_list), sim_params["N_rec_spk"]) + n_neuron_rec_mm = min(len(neuron_list), sim_params["N_rec_mm"]) + n_astro_rec = min(len(astro), sim_params["N_rec_mm"]) + neuron_list_for_sr = neuron_list[: min(len(neuron_list), n_neuron_rec_spk)] + neuron_list_for_mm = sorted(random.sample(neuron_list, n_neuron_rec_mm)) + astro_list_for_mm = sorted(random.sample(astro_list, n_astro_rec)) + nest.Connect(neuron_list_for_sr, sr_neuron) + nest.Connect(mm_neuron, neuron_list_for_mm) + nest.Connect(mm_astro, astro_list_for_mm) + + # run pre-simulation + print("Running pre-simulation ...") + nest.Simulate(pre_sim_time) + + # run simulation + print("Running simulation ...") + nest.Simulate(sim_time) + + # read out recordings + neuron_spikes = sr_neuron.events + neuron_data = mm_neuron.events + astro_data = mm_astro.events + + # make raster plot + nest.raster_plot.from_device( + sr_neuron, hist=True, title=f"Raster plot of neuron {neuron_list_for_sr[0]} to {neuron_list_for_sr[-1]}" + ) + + # plot dynamics in astrocytes and neurons + plot_dynamics(astro_data, neuron_data, 0.0) + + # show plots + plt.show() + + +############################################################################### +# Run simulation. + +run_simulation() + +print(nest.num_connections) +print(nest.GetKernelStatus()["time_construction_connect"]) diff --git a/pynest/examples/astrocytes/astrocyte_small_network.py b/pynest/examples/astrocytes/astrocyte_small_network.py index 36ae5f1af7..60a8d21b04 100644 --- a/pynest/examples/astrocytes/astrocyte_small_network.py +++ b/pynest/examples/astrocytes/astrocyte_small_network.py @@ -149,7 +149,7 @@ p_primary = 1.0 # connection probability between neurons p_third_if_primary = 1.0 # probability of each created neuron-neuron connection to be paired with one astrocyte pool_size = 1 # astrocyte pool size for each target neuron -pool_type = "block" # the way to determine the astrocyte pool for each target neuron +pool_type = "random" # the way to determine the astrocyte pool for each target neuron ############################################################################### # Set astrocyte parameters. @@ -399,7 +399,7 @@ def plot_dynamics(astro_data, neuron_data, start): post_neurons, astrocytes, conn_spec={ - "rule": "bernoulli", + "rule": "pairwise_bernoulli", "p": p_primary, }, third_factor_conn_spec={ From 7376c6bcaaf180f51e40c3d553b7c2a09de90588 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Mon, 26 Feb 2024 09:48:52 +0100 Subject: [PATCH 06/86] Adapted tripartite tests to new syntax --- .../test_connect_tripartite_bernoulli.py | 33 +++++++++++++------ testsuite/pytests/test_tripartite_connect.py | 32 ++++++++++++++---- 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/testsuite/pytests/test_connect_tripartite_bernoulli.py b/testsuite/pytests/test_connect_tripartite_bernoulli.py index 04ee1f003e..50a8d5dcb4 100644 --- a/testsuite/pytests/test_connect_tripartite_bernoulli.py +++ b/testsuite/pytests/test_connect_tripartite_bernoulli.py @@ -33,16 +33,20 @@ haveMPI4Py = False -def setup_network(conn_dict, syn_dict, N1, N2, primary_model="aeif_cond_alpha_astro", third_model="astrocyte_lr_1994"): +def setup_network( + conn_dict, third_conn_dict, syn_dict, N1, N2, primary_model="aeif_cond_alpha_astro", third_model="astrocyte_lr_1994" +): """Setup the network for the statistical test. A three-population network is created for a statictical test of the - "tripartite_bernoulli_with_pool" rule. + "third_factor_bernoulli_with_pool" rule. Parameters --------- conn_dict - Dictionary for the connectivity specifications (conn_spec). + Dictionary for the primary connectivity specifications (conn_spec). + third_conn_dict + Dictionary for the third-factor connectivity specifications (third_factor_conn_spec). syn_dict Dictionary for the synapse specifications (syn_spec). N1 @@ -62,7 +66,9 @@ def setup_network(conn_dict, syn_dict, N1, N2, primary_model="aeif_cond_alpha_as pop1 = nest.Create(primary_model, N1) pop2 = nest.Create(primary_model, N2) pop3 = nest.Create(third_model, N2) - nest.TripartiteConnect(pop1, pop2, pop3, conn_spec=conn_dict, syn_specs=syn_dict) + nest.TripartiteConnect( + pop1, pop2, pop3, conn_spec=conn_dict, third_factor_conn_spec=third_conn_dict, syn_specs=syn_dict + ) return pop1, pop2, pop3 @@ -313,7 +319,7 @@ def mpi_assert(data_original, data_test): def test_statistics(p_primary): """ A test for the parameters "p_primary" and "pool_size" in the - "tripartite_bernoulli_with_pool" rule. + "third_factor_bernoulli_with_pool" rule. Parameters --------- @@ -325,7 +331,8 @@ def test_statistics(p_primary): N1 = 50 N2 = 50 pool_size = 5 - conn_dict = {"rule": "tripartite_bernoulli_with_pool", "p_primary": p_primary, "pool_size": pool_size} + conn_dict = {"rule": "pairwise_bernoulli", "p": p_primary} + third_conn_dict = {"rule": "third_factor_bernoulli_with_pool", "pool_size": pool_size} # set test parameters stat_dict = {"alpha2": 0.05, "n_runs": 20} @@ -338,7 +345,7 @@ def test_statistics(p_primary): # 1. p_primary yields the correct indegree and outdegree # 2. pool_size limits the number of third-population nodes connected to each target nodes for fan in ["in", "out"]: - expected = get_expected_degrees_bernoulli(conn_dict["p_primary"], fan, N1, N2) + expected = get_expected_degrees_bernoulli(conn_dict["p"], fan, N1, N2) pvalues = [] n_third_nodes = [] for i in range(stat_dict["n_runs"]): @@ -346,7 +353,9 @@ def test_statistics(p_primary): nest.ResetKernel() nest.local_num_threads = nr_threads nest.rng_seed = i + 1 - pop1, pop2, pop3 = setup_network(conn_dict, {"third_out": {"synapse_model": "sic_connection"}}, N1, N2) + pop1, pop2, pop3 = setup_network( + conn_dict, third_conn_dict, {"third_out": {"synapse_model": "sic_connection"}}, N1, N2 + ) # get indegree or outdegree degrees = get_degrees(fan, pop1, pop2) # gather data from MPI processes @@ -385,10 +394,13 @@ def test_autapses_true(autapses): # set network and connectivity parameters N = 50 conn_dict = { - "rule": "tripartite_bernoulli_with_pool", - "p_primary": 1.0, + "rule": "pairwise_bernoulli", + "p": 1.0, "allow_autapses": autapses, } + third_conn_dict = { + "rule": "third_factor_bernoulli_with_pool", + } # set NEST verbosity nest.set_verbosity("M_FATAL") @@ -401,6 +413,7 @@ def test_autapses_true(autapses): pop_primay, pop_third, conn_spec=conn_dict, + third_factor_conn_spec=third_conn_dict, syn_specs={"third_out": {"synapse_model": "sic_connection"}}, ) diff --git a/testsuite/pytests/test_tripartite_connect.py b/testsuite/pytests/test_tripartite_connect.py index 115c128ecd..0dc7cd81b7 100644 --- a/testsuite/pytests/test_tripartite_connect.py +++ b/testsuite/pytests/test_tripartite_connect.py @@ -45,7 +45,11 @@ def test_connect_all(): third = nest.Create("parrot_neuron", n_third) nest.TripartiteConnect( - pre, post, third, {"rule": "tripartite_bernoulli_with_pool", "p_primary": 1.0, "p_third_if_primary": 1} + pre, + post, + third, + {"rule": "pairwise_bernoulli", "p": 1.0}, + {"rule": "third_factor_bernoulli_with_pool", "p": 1.0}, ) n_primary = n_pre * n_post @@ -64,7 +68,8 @@ def test_connect_astro(): pre, post, third, - {"rule": "tripartite_bernoulli_with_pool", "p_primary": 1.0, "p_third_if_primary": 1}, + {"rule": "pairwise_bernoulli", "p": 1.0}, + {"rule": "third_factor_bernoulli_with_pool", "p": 1.0}, {"third_out": {"synapse_model": "sic_connection"}}, ) @@ -81,7 +86,11 @@ def test_explicit_random_pool(): third = nest.Create("parrot_neuron", n_third) nest.TripartiteConnect( - pre, post, third, {"rule": "tripartite_bernoulli_with_pool", "pool_type": "random", "pool_size": 2} + pre, + post, + third, + {"rule": "pairwise_bernoulli", "p": 1.0}, + {"rule": "third_factor_bernoulli_with_pool", "p": 1.0, "pool_type": "random", "pool_size": 2}, ) n_primary = n_pre * n_post @@ -97,7 +106,11 @@ def test_block_pool_single(): third = nest.Create("parrot_neuron", n_third) nest.TripartiteConnect( - pre, post, third, {"rule": "tripartite_bernoulli_with_pool", "pool_type": "block", "pool_size": 1} + pre, + post, + third, + {"rule": "pairwise_bernoulli", "p": 1.0}, + {"rule": "third_factor_bernoulli_with_pool", "p": 1.0, "pool_type": "block", "pool_size": 1}, ) n_primary = n_pre * n_post @@ -113,7 +126,11 @@ def test_block_pool_wide(): third = nest.Create("parrot_neuron", n_third) nest.TripartiteConnect( - pre, post, third, {"rule": "tripartite_bernoulli_with_pool", "pool_type": "block", "pool_size": 4} + pre, + post, + third, + {"rule": "pairwise_bernoulli", "p": 1.0}, + {"rule": "third_factor_bernoulli_with_pool", "p": 1.0, "pool_type": "block", "pool_size": 4}, ) n_primary = n_pre * n_post @@ -129,7 +146,7 @@ def test_bipartitet_raises(): third = nest.Create("parrot_neuron", n_third) with pytest.raises(nest.kernel.NESTErrors.IllegalConnection): - nest.TripartiteConnect(pre, post, third, {"rule": "one_to_one"}) + nest.TripartiteConnect(pre, post, third, {"rule": "one_to_one"}, {"rule": "one_to_one"}) def test_connect_complex_synspecs(): @@ -142,7 +159,8 @@ def test_connect_complex_synspecs(): pre, post, third, - {"rule": "tripartite_bernoulli_with_pool", "p_primary": 1.0, "p_third_if_primary": 1}, + {"rule": "pairwise_bernoulli", "p": 1.0}, + {"rule": "third_factor_bernoulli_with_pool", "p": 1.0}, { "primary": nest.CollocatedSynapses( {"synapse_model": "stdp_synapse", "weight": 2.0}, {"synapse_model": "tsodyks_synapse", "delay": 3.0} From dce1ef1aaf30859051f570d90c6922dfec6c4dfe Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Mon, 26 Feb 2024 10:20:00 +0100 Subject: [PATCH 07/86] Structural plasticity and disconnect work --- nestkernel/sp_manager.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/nestkernel/sp_manager.cpp b/nestkernel/sp_manager.cpp index 3dd7544368..1015e67649 100644 --- a/nestkernel/sp_manager.cpp +++ b/nestkernel/sp_manager.cpp @@ -139,9 +139,7 @@ SPManager::set_status( const DictionaryDatum& d ) } // We use a ConnBuilder with dummy values to check the synapse parameters - assert( false ); - /* - SPBuilder* conn_builder = new SPBuilder( sources, targets, conn_spec, { syn_spec } ); + SPBuilder* conn_builder = new SPBuilder( sources, targets, /* third_out */ nullptr, conn_spec, { syn_spec } ); conn_builder->set_name( i->first.toString() ); // check that the user defined the min and max delay properly, if the @@ -153,7 +151,6 @@ SPManager::set_status( const DictionaryDatum& d ) "specify the min and max delay in the kernel parameters." ); } sp_conn_builders_.push_back( conn_builder ); - */ } } @@ -263,19 +260,25 @@ SPManager::disconnect( NodeCollectionPTR sources, std::string synModel = getValue< std::string >( syn_spec, names::synapse_model ); if ( ( *i )->get_synapse_model() == kernel().model_manager.get_synapse_model_id( synModel ) ) { - /* - cb = kernel().connection_manager.get_conn_builder( rule_name, sources, targets, conn_spec, { syn_spec } ); + cb = kernel().connection_manager.get_conn_builder( rule_name, + sources, + targets, + /* third_out */ nullptr, + conn_spec, + { syn_spec } ); cb->set_synaptic_element_names( ( *i )->get_pre_synaptic_element_name(), ( *i )->get_post_synaptic_element_name() ); - */ } } } else { - /* - cb = kernel().connection_manager.get_conn_builder( rule_name, sources, targets, conn_spec, { syn_spec } ); - */ + cb = kernel().connection_manager.get_conn_builder( rule_name, + sources, + targets, + /* third_out */ nullptr, + conn_spec, + { syn_spec } ); } assert( cb ); From 240f5538acc7d95861cc8443e7608a5388dbc354 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Mon, 26 Feb 2024 18:32:25 +0100 Subject: [PATCH 08/86] Block pools now supported again --- nestkernel/conn_builder.cpp | 31 ++++++++++++++++++++++++++++--- nestkernel/node.cpp | 2 ++ nestkernel/node.h | 35 +++++++++++++++++++++++++++++++++++ nestkernel/node_manager.h | 2 +- 4 files changed, 66 insertions(+), 4 deletions(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index efd0421784..3b478f8055 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -789,6 +789,22 @@ nest::ThirdBernoulliWithPoolBuilder::ThirdBernoulliWithPoolBuilder( const NodeCo pool_[ thrd ] = new std::vector< NodeIDTriple >(); pool_[ thrd ]->reserve( pool_size_ ); } + + if ( not random_pool_ ) + { + // We cannot do this parallel with targets->local_begin() since we need to + // count over all elements of the node collection which might be a complex + // composition of slices with non-trivial mapping between elements and vps. + size_t idx = 0; + for ( auto tgt_it = targets_->begin(); tgt_it != targets_->end(); ++tgt_it ) + { + Node* const tgt = kernel().node_manager.get_node_or_proxy( ( *tgt_it ).node_id ); + if ( not tgt->is_proxy() ) + { + tgt->set_tmp_nc_index( idx++ ); // must be postfix + } + } + } } nest::ThirdBernoulliWithPoolBuilder::~ThirdBernoulliWithPoolBuilder() @@ -797,6 +813,17 @@ nest::ThirdBernoulliWithPoolBuilder::~ThirdBernoulliWithPoolBuilder() { const size_t thrd = kernel().vp_manager.get_thread_id(); delete pool_[ thrd ]; + + if ( not random_pool_ ) + { + // Here we can work in parallel since we just reset to invalid_index + for ( auto tgt_it = targets_->local_begin(); tgt_it != targets_->end(); ++tgt_it ) + { + Node* const tgt = kernel().node_manager.get_node_or_proxy( ( *tgt_it ).node_id, thrd ); + assert( not tgt->is_proxy() ); + tgt->set_tmp_nc_index( invalid_index ); + } + } } } @@ -823,9 +850,7 @@ nest::ThirdBernoulliWithPoolBuilder::third_connect( size_t primary_source_id, No } else { - throw NotImplemented( - "block pools are currently not implemented because we do not have access to pos of target in targets" ); - std::copy_n( sources_->begin() + get_first_pool_index_( 0 /* pos ot target in targets node collection */ ), + std::copy_n( sources_->begin() + get_first_pool_index_( primary_target.get_tmp_nc_index() ), pool_size_, std::back_inserter( *( pool_[ tid ] ) ) ); } diff --git a/nestkernel/node.cpp b/nestkernel/node.cpp index d490e40330..544341b6e4 100644 --- a/nestkernel/node.cpp +++ b/nestkernel/node.cpp @@ -48,6 +48,7 @@ Node::Node() , frozen_( false ) , initialized_( false ) , node_uses_wfr_( false ) + , tmp_nc_index_( invalid_index ) { } @@ -62,6 +63,7 @@ Node::Node( const Node& n ) // copy must always initialized its own buffers , initialized_( false ) , node_uses_wfr_( n.node_uses_wfr_ ) + , tmp_nc_index_( invalid_index ) { } diff --git a/nestkernel/node.h b/nestkernel/node.h index 737ffe4f1f..36bf7e1ec2 100644 --- a/nestkernel/node.h +++ b/nestkernel/node.h @@ -880,6 +880,17 @@ class Node */ DeprecationWarning deprecation_warning; + /** + * This is only to be used to get the index in the NC to the ThirdOutBuilder + */ + void set_tmp_nc_index( size_t index ); + + /** + * Return index in NC and invalidate entry to avoid multiple reads. Only to be called by ThirdOutBuilder + */ + size_t get_tmp_nc_index() const; + + private: void set_node_id_( size_t ); //!< Set global node id @@ -957,6 +968,15 @@ class Node bool frozen_; //!< node shall not be updated if true bool initialized_; //!< state and buffers have been initialized bool node_uses_wfr_; //!< node uses waveform relaxation method + + /** + * Store index in NodeCollection. + * + * @note This is only here so that the primary connection builder can inform the ThirdOutBuilder + * about the index of the target neuron in the targets node collection. This is required for block-based + * builders. + */ + size_t tmp_nc_index_; }; inline bool @@ -1096,6 +1116,21 @@ Node::get_thread_lid() const return thread_lid_; } +inline void +Node::set_tmp_nc_index( size_t index ) +{ + tmp_nc_index_ = index; +} + +inline size_t +Node::get_tmp_nc_index() const +{ + assert( tmp_nc_index_ != invalid_index ); + + return tmp_nc_index_; +} + + } // namespace #endif diff --git a/nestkernel/node_manager.h b/nestkernel/node_manager.h index def97d9e60..86044911b3 100644 --- a/nestkernel/node_manager.h +++ b/nestkernel/node_manager.h @@ -139,7 +139,7 @@ class NodeManager : public ManagerInterface * * The function expects that * the given node ID and thread are valid. If they are not, an assertion - * will fail. In case the given Node does not exist on the fiven + * will fail. In case the given Node does not exist on the given * thread, a proxy is returned instead. * * @param node_id index of the Node From 9adaf6f939eb6297510b6fdaf579bb9805ae1ba0 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Mon, 26 Feb 2024 20:53:36 +0100 Subject: [PATCH 09/86] Add MPI support for new tripartite scheme --- nestkernel/conn_builder.cpp | 64 ++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index 3b478f8055..3b6a3533e2 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -694,21 +694,69 @@ nest::ThirdInBuilder::connect_() { kernel().vp_manager.assert_single_threaded(); + // count up how many source-third pairs we need to send to each rank + const size_t num_ranks = kernel().mpi_manager.get_num_processes(); + std::vector< size_t > source_third_per_rank( num_ranks, 0 ); + for ( auto stcp : source_third_counts_ ) + { + const auto& stc = *stcp; + for ( size_t rank = 0; rank < stc.size(); ++rank ) + { + source_third_per_rank[ rank ] += stc[ rank ]; + } + } + + // now find global maximum; for simplicity, we will use this to configure buffers + std::vector< long > max_stc( num_ranks ); // MPIManager does not support size_t + max_stc[ kernel().mpi_manager.get_rank() ] = + *std::max_element( source_third_per_rank.begin(), source_third_per_rank.end() ); + kernel().mpi_manager.communicate( max_stc ); + const size_t global_max_stc = *std::max_element( max_stc.begin(), max_stc.end() ); + const size_t slots_per_rank = 2 * global_max_stc; + + // create and fill send buffer, entries per pair + std::vector< size_t > send_stg( num_ranks * slots_per_rank, 0 ); + std::vector< size_t > rank_idx( num_ranks, 0 ); + + for ( auto stgp : source_third_gids_ ) + { + for ( auto& stg : *stgp ) + { + const auto ix = stg.third_rank * slots_per_rank + rank_idx[ stg.third_rank ]; + send_stg[ ix ] = stg.third_gid; // write third gid first because we need to look at it first below + send_stg[ ix + 1 ] = stg.source_gid; + rank_idx[ stg.third_rank ] += 2; + } + } + + std::vector< size_t > recv_stg( num_ranks * slots_per_rank, 0 ); + + const size_t send_recv_count = sizeof( size_t ) / sizeof( unsigned int ) * slots_per_rank; + + // force to master thread for compatibility with MPI standard +#pragma omp master + { + kernel().mpi_manager.communicate_Alltoall( send_stg, recv_stg, send_recv_count ); + } + + // Now recv_stg contains all source-third pairs where third is on current rank + // Create connections in parallel + #pragma omp parallel { const size_t tid = kernel().vp_manager.get_thread_id(); RngPtr rng = kernel().random_manager.get_vp_specific_rng( tid ); - for ( auto& conn_pairs_per_thread : source_third_gids_ ) + for ( size_t idx = 0; idx < recv_stg.size(); idx += 2 ) { - for ( auto& conn_pair : *conn_pairs_per_thread ) + // TODO: Once third_gid == 0, we are done for data for this rank and could jump + // to beginning of section for next rank + const auto third_gid = recv_stg[ idx ]; + if ( third_gid > 0 and kernel().vp_manager.is_node_id_vp_local( third_gid ) ) { - if ( not kernel().vp_manager.is_node_id_vp_local( conn_pair.third_gid ) ) - { - continue; - } - single_connect_( - conn_pair.source_gid, *kernel().node_manager.get_node_or_proxy( conn_pair.third_gid, tid ), tid, rng ); + const auto source_gid = recv_stg[ idx + 1 ]; + assert( source_gid > 0 ); + single_connect_( source_gid, *kernel().node_manager.get_node_or_proxy( third_gid, tid ), tid, rng ); } } } From a4037651a54a9e1f1b3d839b5934de93ede4e69a Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Mon, 26 Feb 2024 21:11:48 +0100 Subject: [PATCH 10/86] Fix formatting and pylint problems; improve example names --- nestkernel/nest_names.cpp | 2 -- nestkernel/nest_names.h | 2 -- ...yte_brunel.py => astrocyte_brunel_bernoulli.py} | 14 +++++++------- ...ix_in.py => astrocyte_brunel_fixed_indegree.py} | 8 ++++---- pynest/examples/pong/pong.py | 5 +---- testsuite/pytests/test_jonke_synapse.py | 3 +-- testsuite/pytests/test_stdp_nn_synapses.py | 6 ++---- 7 files changed, 15 insertions(+), 25 deletions(-) rename pynest/examples/astrocytes/{astrocyte_brunel.py => astrocyte_brunel_bernoulli.py} (98%) rename pynest/examples/astrocytes/{astrocyte_brunel_fix_in.py => astrocyte_brunel_fixed_indegree.py} (99%) diff --git a/nestkernel/nest_names.cpp b/nestkernel/nest_names.cpp index b0364f2584..5dfd99249d 100644 --- a/nestkernel/nest_names.cpp +++ b/nestkernel/nest_names.cpp @@ -345,8 +345,6 @@ const Name overwrite_files( "overwrite_files" ); const Name P( "P" ); const Name p( "p" ); const Name p_copy( "p_copy" ); -const Name p_primary( "p_primary" ); -const Name p_third_if_primary( "p_third_if_primary" ); 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" ); diff --git a/nestkernel/nest_names.h b/nestkernel/nest_names.h index 81b31a6056..18d207f4ca 100644 --- a/nestkernel/nest_names.h +++ b/nestkernel/nest_names.h @@ -371,8 +371,6 @@ extern const Name overwrite_files; extern const Name P; extern const Name p; extern const Name p_copy; -extern const Name p_primary; -extern const Name p_third_if_primary; extern const Name p_transmit; extern const Name pairwise_bernoulli_on_source; extern const Name pairwise_bernoulli_on_target; diff --git a/pynest/examples/astrocytes/astrocyte_brunel.py b/pynest/examples/astrocytes/astrocyte_brunel_bernoulli.py similarity index 98% rename from pynest/examples/astrocytes/astrocyte_brunel.py rename to pynest/examples/astrocytes/astrocyte_brunel_bernoulli.py index 435d1ac173..98b2c3eec6 100644 --- a/pynest/examples/astrocytes/astrocyte_brunel.py +++ b/pynest/examples/astrocytes/astrocyte_brunel_bernoulli.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# astrocyte_brunel.py +# astrocyte_brunel_bernoulli.py # # This file is part of NEST. # @@ -34,6 +34,9 @@ astrocytes, and the raster plot of neuronal firings are shown in the created figures. +In this version of the model, primary connections between populations are +created with the pairwise bernoulli rule. + References ~~~~~~~~~~ @@ -77,8 +80,8 @@ "sim_time": 1000.0, # simulation time in ms "N_rec_spk": 100, # number of neurons to record from with spike recorder "N_rec_mm": 50, # number of nodes (neurons, astrocytes) to record from with multimeter - "n_threads": 4, # number of threads for NEST - "seed": 1010, # seed for the random module + "n_vp": 4, # number of VPs for NEST + "seed": 100, # seed for the random module } ############################################################################### @@ -318,7 +321,7 @@ def run_simulation(): # NEST configuration nest.ResetKernel() nest.resolution = sim_params["dt"] - nest.local_num_threads = sim_params["n_threads"] + nest.total_num_virtual_procs = sim_params["n_vp"] nest.print_time = True nest.overwrite_files = True @@ -381,6 +384,3 @@ def run_simulation(): # Run simulation. run_simulation() - -print(nest.num_connections) -print(nest.GetKernelStatus()["time_construction_connect"]) diff --git a/pynest/examples/astrocytes/astrocyte_brunel_fix_in.py b/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py similarity index 99% rename from pynest/examples/astrocytes/astrocyte_brunel_fix_in.py rename to pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py index cb2dd4bcef..fb19ed20e4 100644 --- a/pynest/examples/astrocytes/astrocyte_brunel_fix_in.py +++ b/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# astrocyte_brunel.py +# astrocyte_brunel_fixed_indegree.py # # This file is part of NEST. # @@ -34,6 +34,9 @@ astrocytes, and the raster plot of neuronal firings are shown in the created figures. +In this version of the model, primary connections between populations are +created with the fixed-indegree rule. + References ~~~~~~~~~~ @@ -384,6 +387,3 @@ def run_simulation(): # Run simulation. run_simulation() - -print(nest.num_connections) -print(nest.GetKernelStatus()["time_construction_connect"]) diff --git a/pynest/examples/pong/pong.py b/pynest/examples/pong/pong.py index 3f0590ce5d..72e4a37214 100644 --- a/pynest/examples/pong/pong.py +++ b/pynest/examples/pong/pong.py @@ -193,10 +193,7 @@ def propagate_ball_and_paddles(self): """Updates ball and paddle coordinates based on direction and velocity.""" for paddle in [self.r_paddle, self.l_paddle]: paddle.y_pos += paddle.direction * paddle.velocity - if paddle.y_pos < 0: - paddle.y_pos = 0 - if paddle.y_pos > self.y_length: - paddle.y_pos = self.y_length + paddle.y_pos = min(max(0, paddle.y_pos), self.y_length) paddle.update_cell() self.ball.y_pos += self.ball.velocity * self.ball.direction[1] self.ball.x_pos += self.ball.velocity * self.ball.direction[0] diff --git a/testsuite/pytests/test_jonke_synapse.py b/testsuite/pytests/test_jonke_synapse.py index 06a330d324..2b96fc7871 100644 --- a/testsuite/pytests/test_jonke_synapse.py +++ b/testsuite/pytests/test_jonke_synapse.py @@ -225,8 +225,7 @@ def facilitate(self, _delta_t, weight, Kplus): * Kplus * np.exp(_delta_t / self.synapse_constants["tau_plus"]) ) - if weight > self.synapse_constants["Wmax"]: - weight = self.synapse_constants["Wmax"] + weight = min(weight, self.synapse_constants["Wmax"]) return weight def depress(self, _delta_t, weight, Kminus): diff --git a/testsuite/pytests/test_stdp_nn_synapses.py b/testsuite/pytests/test_stdp_nn_synapses.py index 6beb1a55e4..8313bf6ac3 100644 --- a/testsuite/pytests/test_stdp_nn_synapses.py +++ b/testsuite/pytests/test_stdp_nn_synapses.py @@ -260,8 +260,7 @@ def facilitate(self, _delta_t, w): * ((1 - w / self.synapse_parameters["Wmax"]) ** self.synapse_parameters["mu_plus"]) * exp(-1 * _delta_t / self.synapse_parameters["tau_plus"]) ) - if w > self.synapse_parameters["Wmax"]: - w = self.synapse_parameters["Wmax"] + w = min(w, self.synapse_parameters["Wmax"]) return w def depress(self, _delta_t, w): @@ -273,8 +272,7 @@ def depress(self, _delta_t, w): * ((w / self.synapse_parameters["Wmax"]) ** self.synapse_parameters["mu_minus"]) * exp(_delta_t / self.neuron_parameters["tau_minus"]) ) - if w < 0: - w = 0 + w = max(0, w) return w def test_nn_symm_synapse(self): From a16cbea52e850bbe413226c7f5f614757bedf688 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Mon, 26 Feb 2024 23:02:50 +0100 Subject: [PATCH 11/86] Fix conngen builder --- nestkernel/conn_builder_conngen.cpp | 5 ++++- nestkernel/conn_builder_conngen.h | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/nestkernel/conn_builder_conngen.cpp b/nestkernel/conn_builder_conngen.cpp index f003512198..7c0547ab34 100644 --- a/nestkernel/conn_builder_conngen.cpp +++ b/nestkernel/conn_builder_conngen.cpp @@ -35,12 +35,15 @@ namespace nest ConnectionGeneratorBuilder::ConnectionGeneratorBuilder( NodeCollectionPTR sources, NodeCollectionPTR targets, + ThirdOutBuilder* third_out, const DictionaryDatum& conn_spec, const std::vector< DictionaryDatum >& syn_specs ) - : ConnBuilder( sources, targets, conn_spec, syn_specs ) + : BipartiteConnBuilder( sources, targets, third_out, conn_spec, syn_specs ) , cg_( ConnectionGeneratorDatum() ) , params_map_() { + assert( third_out == nullptr ); + updateValue< ConnectionGeneratorDatum >( conn_spec, "cg", cg_ ); if ( cg_->arity() != 0 ) { diff --git a/nestkernel/conn_builder_conngen.h b/nestkernel/conn_builder_conngen.h index 05d05eb9bf..87c8445846 100644 --- a/nestkernel/conn_builder_conngen.h +++ b/nestkernel/conn_builder_conngen.h @@ -69,7 +69,7 @@ namespace nest * interface MPI aware and communicating the masks during connection * setup. */ -class ConnectionGeneratorBuilder : public ConnBuilder +class ConnectionGeneratorBuilder : public BipartiteConnBuilder { typedef std::vector< ConnectionGenerator::ClosedInterval > RangeSet; typedef ConnectionGenerator::ClosedInterval Range; @@ -77,6 +77,7 @@ class ConnectionGeneratorBuilder : public ConnBuilder public: ConnectionGeneratorBuilder( NodeCollectionPTR, NodeCollectionPTR, + ThirdOutBuilder*, const DictionaryDatum&, const std::vector< DictionaryDatum >& ); From a883317d56e849c266fa4ccb0a504d9dae1ab165 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Tue, 27 Feb 2024 11:28:36 +0100 Subject: [PATCH 12/86] Prohibit 'make_symmetric' in for tripartite connectivity and extend tests --- nestkernel/conn_builder.cpp | 5 +++ testsuite/pytests/test_tripartite_connect.py | 44 +++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index 3b6a3533e2..a9fe4e22d5 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -148,6 +148,11 @@ nest::BipartiteConnBuilder::BipartiteConnBuilder( NodeCollectionPTR sources, updateValue< bool >( conn_spec, names::allow_multapses, allow_multapses_ ); updateValue< bool >( conn_spec, names::make_symmetric, make_symmetric_ ); + if ( make_symmetric_ and third_out_ ) + { + throw BadProperty( "Third-factor connectivity cannot be used with 'make_symmetric == True'." ); + } + // Synapse-specific parameters that should be skipped when we set default synapse parameters skip_syn_params_ = { names::weight, names::delay, names::min_delay, names::max_delay, names::num_connections, names::synapse_model diff --git a/testsuite/pytests/test_tripartite_connect.py b/testsuite/pytests/test_tripartite_connect.py index 0dc7cd81b7..ce8d540902 100644 --- a/testsuite/pytests/test_tripartite_connect.py +++ b/testsuite/pytests/test_tripartite_connect.py @@ -139,7 +139,7 @@ def test_block_pool_wide(): assert len(nest.GetConnections(third, post)) == n_primary -def test_bipartitet_raises(): +def test_tripartite_raises(): n_pre, n_post, n_third = 4, 2, 8 pre = nest.Create("parrot_neuron", n_pre) post = nest.Create("parrot_neuron", n_post) @@ -149,6 +149,48 @@ def test_bipartitet_raises(): nest.TripartiteConnect(pre, post, third, {"rule": "one_to_one"}, {"rule": "one_to_one"}) +def test_tripartite_rejects_make_symmetric(): + n_pre, n_post, n_third = 4, 4, 8 + pre = nest.Create("parrot_neuron", n_pre) + post = nest.Create("parrot_neuron", n_post) + third = nest.Create("parrot_neuron", n_third) + + with pytest.raises(nest.kernel.NESTErrors.BadProperty): + nest.TripartiteConnect( + pre, + post, + third, + {"rule": "one_to_one", "make_symmetric": True}, + {"rule": "third_factor_bernoulli_with_pool"}, + ) + + +@pytest.mark.skipif_missing_threads +@pytest.mark.parametrize( + "connspec, num_conns_expected", + [ + ({"rule": "one_to_one"}, 6), + ({"rule": "all_to_all"}, 36), + ({"rule": "fixed_indegree", "indegree": 3}, 18), + ({"rule": "fixed_outdegree", "outdegree": 3}, 18), + ({"rule": "fixed_total_number", "N": 23}, 23), + ({"rule": "pairwise_bernoulli", "p": 1}, 36), + ], +) +def test_third_works_for_all_primary_rules(connspec, num_conns_expected): + nest.local_num_threads = 4 + n = 6 # if this is changed, number of expected connection must be adjusted above + pre = nest.Create("parrot_neuron", n) + post = nest.Create("parrot_neuron", n) + third = nest.Create("parrot_neuron", 5) + + nest.TripartiteConnect(pre, post, third, connspec, {"rule": "third_factor_bernoulli_with_pool", "p": 1.0}) + + assert len(nest.GetConnections(pre, post)) == num_conns_expected + assert len(nest.GetConnections(pre, third)) == num_conns_expected + assert len(nest.GetConnections(third, post)) == num_conns_expected + + def test_connect_complex_synspecs(): n_pre, n_post, n_third = 4, 2, 3 pre = nest.Create("parrot_neuron", n_pre) From 6fd399517f888529932288c05a530a6bf8bf3f13 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Mon, 6 May 2024 15:14:01 +0200 Subject: [PATCH 13/86] Unregister third-factor conn factories on ResetKernel --- nestkernel/connection_manager.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/nestkernel/connection_manager.cpp b/nestkernel/connection_manager.cpp index 4e29ed35eb..1d47802ff9 100644 --- a/nestkernel/connection_manager.cpp +++ b/nestkernel/connection_manager.cpp @@ -173,6 +173,13 @@ nest::ConnectionManager::finalize( const bool adjust_number_of_threads_or_rng_on } connbuilder_factories_.clear(); connruledict_->clear(); + + for ( auto tcbf : thirdconnbuilder_factories_ ) + { + delete tcbf; + } + thirdconnbuilder_factories_.clear(); + thirdconnruledict_->clear(); } } From 1eaa37ab8ab3b0eaac73086b3d6b2a2e65689327 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Thu, 20 Jun 2024 22:30:15 +0200 Subject: [PATCH 14/86] Remove superfluous checks --- nestkernel/conn_builder.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index e31ea5af89..b1f7d28988 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -90,17 +90,9 @@ nest::ConnBuilder::ConnBuilder( const std::string& primary_rule, nest::ConnBuilder::~ConnBuilder() { - delete primary_builder_; // fully constructed CB has one - - if ( third_in_builder_ ) - { - delete third_in_builder_; - } - - if ( third_out_builder_ ) - { - delete third_out_builder_; - } + delete primary_builder_; + delete third_in_builder_; + delete third_out_builder_; } void From 95299f05e14fac91c428a348a0c115d861fc034f Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Thu, 20 Jun 2024 22:41:40 +0200 Subject: [PATCH 15/86] Make ThirdInBuilder final and improve class doc --- nestkernel/conn_builder.h | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/nestkernel/conn_builder.h b/nestkernel/conn_builder.h index 87fbfb1ada..c49126dc33 100644 --- a/nestkernel/conn_builder.h +++ b/nestkernel/conn_builder.h @@ -293,13 +293,21 @@ class BipartiteConnBuilder void reset_delays_(); }; -// not expected to be subclassed further -class ThirdInBuilder : public BipartiteConnBuilder + +/** + * Builder creating "thírd in" connections based on data from "third out" builder. + * + * This builder creates the actual connections from primary sources to third-factor nodes + * based on the source-third lists generated by the third-out builder. + * + * The class is final because there is no freedom of choice of connection rule at this stage. + */ +class ThirdInBuilder final : public BipartiteConnBuilder { public: ThirdInBuilder( NodeCollectionPTR, NodeCollectionPTR, - const DictionaryDatum&, // only for compatibility with BCB + const DictionaryDatum&, // only for compatibility with BipartiteConnBuilder const std::vector< DictionaryDatum >& ); ~ThirdInBuilder(); @@ -340,14 +348,23 @@ class ThirdInBuilder : public BipartiteConnBuilder std::vector< std::vector< size_t >* > source_third_counts_; }; -// to be subclassed further + +/** + * Builder for connections from third-factor nodes to primary target populations. + * + * This builder creates connections from third-factor nodes to primary targets based on the + * third-factor connection rule. It also registers source-third-factor pairs with the + * corresponding third-in ConnBuilder for later instantiation. + * + * This class needs to be subclassed for each third-factor connection rule. + */ class ThirdOutBuilder : public BipartiteConnBuilder { public: ThirdOutBuilder( NodeCollectionPTR, NodeCollectionPTR, ThirdInBuilder*, - const DictionaryDatum&, // only for compatibility with BCB + const DictionaryDatum&, // only for compatibility with BipartiteConnBuilder const std::vector< DictionaryDatum >& ); void connect() override; From 9a862e691c98a776dcf1df099d60a207ac99b13a Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Thu, 20 Jun 2024 22:42:25 +0200 Subject: [PATCH 16/86] Fix typo. Co-authored-by: clinssen --- nestkernel/conn_builder.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nestkernel/conn_builder.h b/nestkernel/conn_builder.h index 87fbfb1ada..c0646a5d9e 100644 --- a/nestkernel/conn_builder.h +++ b/nestkernel/conn_builder.h @@ -362,7 +362,7 @@ class ThirdOutBuilder : public BipartiteConnBuilder /** * Class representing a connection builder which may be bi- or tripartite. * - * A ConnBuilder alwyas has a primary BipartiteConnBuilder. It additionally can have a pair of third_in and third_out + * A ConnBuilder always has a primary BipartiteConnBuilder. It additionally can have a pair of third_in and third_out * Bipartite builders, where the third_in builder must perform one-to-one connections on given source-third pairs. */ class ConnBuilder From a8a937326c37d717ca9fc589f7b7ddaf88fcdc55 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Thu, 20 Jun 2024 22:57:19 +0200 Subject: [PATCH 17/86] Add comment --- nestkernel/conn_builder.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nestkernel/conn_builder.h b/nestkernel/conn_builder.h index c49126dc33..3549c07058 100644 --- a/nestkernel/conn_builder.h +++ b/nestkernel/conn_builder.h @@ -207,7 +207,7 @@ class BipartiteConnBuilder NodeCollectionPTR sources_; NodeCollectionPTR targets_; - ThirdOutBuilder* third_out_; + ThirdOutBuilder* third_out_; //!< to be triggered when primary connection is created bool allow_autapses_; bool allow_multapses_; From 4dce0b461038fe5c5c609ee818ca66be628a849a Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Wed, 26 Jun 2024 16:39:51 +0200 Subject: [PATCH 18/86] Fix links to examples --- doc/htmldoc/examples/index.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/htmldoc/examples/index.rst b/doc/htmldoc/examples/index.rst index 5ef22afc0a..9dafbbf628 100644 --- a/doc/htmldoc/examples/index.rst +++ b/doc/htmldoc/examples/index.rst @@ -60,7 +60,8 @@ PyNEST examples * :doc:`../auto_examples/astrocytes/astrocyte_single` * :doc:`../auto_examples/astrocytes/astrocyte_interaction` * :doc:`../auto_examples/astrocytes/astrocyte_small_network` - * :doc:`../auto_examples/astrocytes/astrocyte_brunel` + * :doc:`../auto_examples/astrocytes/astrocyte_brunel_bernoulli` + * :doc:`../auto_examples/astrocytes/astrocyte_brunel_fixed_indegree` .. grid:: 1 1 2 3 From 986ffd51aa9f276c8a0e854aaf3e609f115cd4f8 Mon Sep 17 00:00:00 2001 From: Mahesh Madhav Date: Mon, 5 Aug 2024 21:55:12 +0000 Subject: [PATCH 19/86] Fix memory safety issue in slistartup.cc Also fixes the memory leak in parser.h --- sli/parser.h | 3 ++- sli/slistartup.cc | 22 ++++++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/sli/parser.h b/sli/parser.h index dc5d67aae9..75b5009128 100644 --- a/sli/parser.h +++ b/sli/parser.h @@ -38,7 +38,7 @@ class Parser { - Scanner* s; + Scanner* s = nullptr; Token arraytoken; Token proctoken; @@ -61,6 +61,7 @@ class Parser public: Parser(); Parser( std::istream& ); + ~Parser() { if (s) delete s; } bool operator()( Token& ); bool diff --git a/sli/slistartup.cc b/sli/slistartup.cc index 61dcd706cb..982a0692e9 100644 --- a/sli/slistartup.cc +++ b/sli/slistartup.cc @@ -214,53 +214,55 @@ SLIStartup::SLIStartup( int argc, char** argv ) StringDatum* sd = new StringDatum( argv[ i ] ); args_array.push_back( Token( sd ) ); - if ( *sd == "-d" or *sd == "--debug" ) + std::string myarg = argv[i]; + + if ( myarg == "-d" or myarg == "--debug" ) { debug_ = true; verbosity_ = SLIInterpreter::M_ALL; // make the interpreter verbose. continue; } - if ( *sd == "--verbosity=ALL" ) + if ( myarg == "--verbosity=ALL" ) { verbosity_ = SLIInterpreter::M_ALL; continue; } - if ( *sd == "--verbosity=DEBUG" ) + if ( myarg == "--verbosity=DEBUG" ) { verbosity_ = SLIInterpreter::M_DEBUG; continue; } - if ( *sd == "--verbosity=STATUS" ) + if ( myarg == "--verbosity=STATUS" ) { verbosity_ = SLIInterpreter::M_STATUS; continue; } - if ( *sd == "--verbosity=INFO" ) + if ( myarg == "--verbosity=INFO" ) { verbosity_ = SLIInterpreter::M_INFO; continue; } - if ( *sd == "--verbosity=DEPRECATED" ) + if ( myarg == "--verbosity=DEPRECATED" ) { verbosity_ = SLIInterpreter::M_DEPRECATED; continue; } - if ( *sd == "--verbosity=WARNING" ) + if ( myarg == "--verbosity=WARNING" ) { verbosity_ = SLIInterpreter::M_WARNING; continue; } - if ( *sd == "--verbosity=ERROR" ) + if ( myarg == "--verbosity=ERROR" ) { verbosity_ = SLIInterpreter::M_ERROR; continue; } - if ( *sd == "--verbosity=FATAL" ) + if ( myarg == "--verbosity=FATAL" ) { verbosity_ = SLIInterpreter::M_FATAL; continue; } - if ( *sd == "--verbosity=QUIET" ) + if ( myarg == "--verbosity=QUIET" ) { verbosity_ = SLIInterpreter::M_QUIET; continue; From bd85376777f1d72c6c06f714ad6725414c41a479 Mon Sep 17 00:00:00 2001 From: Jessica Mitchell Date: Wed, 7 Aug 2024 09:43:43 +0200 Subject: [PATCH 20/86] update gap junctions docs --- .../simulations_with_gap_junctions.rst | 68 ++++++++++++------- 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/doc/htmldoc/synapses/simulations_with_gap_junctions.rst b/doc/htmldoc/synapses/simulations_with_gap_junctions.rst index 0558abadde..8ef65332fc 100644 --- a/doc/htmldoc/synapses/simulations_with_gap_junctions.rst +++ b/doc/htmldoc/synapses/simulations_with_gap_junctions.rst @@ -3,14 +3,6 @@ Simulations with gap junctions ============================== -**Note:** This documentation describes the usage of gap junctions in -NEST 2.12. A documentation for NEST 2.10 can be found in `Hahne et al. -2016 `__. -It is however recommended to use NEST 2.12 (or later), due to several -improvements in terms of usability. - -Introduction ------------- Simulations with gap junctions are supported by the Hodgkin-Huxley neuron model ``hh_psc_alpha_gap``. The synapse model to create a @@ -29,9 +21,11 @@ possibility to create both connections with a single call to a = nest.Create('hh_psc_alpha_gap') b = nest.Create('hh_psc_alpha_gap') + gap_weight = 0.5 + syn_dict = {'synapse_model':'gap_junction', 'weight': gap_weight} + conn_dict = {'rule': 'one_to_one', 'make_symmetric': True} # Create gap junction between neurons a and b - nest.Connect(a, b, {'rule': 'one_to_one', 'make_symmetric': True}, - {'model': 'gap_junction', 'weight': 0.5}) + nest.Connect(a, b, conn_dict, syn_dict) In this case the reverse connection is created internally. In order to prevent the creation of incomplete or non-symmetrical gap junctions the @@ -61,25 +55,29 @@ level with e.g. the ``random`` module of the Python Standard Library: # total number of gap junctions n_gap_junction = 3000 + gap_weight = 0.5 n = nest.Create('hh_psc_alpha_gap', n_neuron) + n_list = n.tolist() random.seed(0) - # draw n_gap_junction pairs of random samples from the list of all - # neurons and reshaped data into two corresponding lists of neurons - m = np.transpose( - [random.sample(n, 2) for _ in range(n_gap_junction)]) + # draw n_gap_junction pairs of random samples + connections = np.random.choice(n_list, [n_gap_junction, 2]) + + for source_node_id, target_node_id in connections: + nest.Connect( + nest.NodeCollection([source_node_id]), + nest.NodeCollection([target_node_id]), + {"rule": "one_to_one", "make_symmetric": True}, + {"synapse_model": "gap_junction", "weight": gap_weight}, + ) - # connect obtained lists of neurons both ways - nest.Connect(m[0], m[1], - {'rule': 'one_to_one', 'make_symmetric': True}, - {'model': 'gap_junction', 'weight': 0.5}) As each gap junction contributes to the total number of gap-junction connections of two neurons, it is hardly possible to create networks with a fixed number of gap junctions per neuron. With the above script it is however possible to control the approximate number of gap -junctions per neuron. E.g. if one desires ``gap_per_neuron = 60`` the +junctions per neuron. For example, if one desires ``gap_per_neuron = 60`` the total number of gap junctions should be chosen as ``n_gap_junction = n_neuron * gap_per_neuron / 2``. @@ -92,18 +90,16 @@ total number of gap junctions should be chosen as full set of random numbers and temporarily represent the total connectivity in variable ``m``. Therefore it is advisable to use the internal random connection rules of NEST for the creation of connections - whenever possible. For more details see `Hahne et al. - 2016 `__. + whenever possible. For more details see Hahne et al. [1]_ Adjust settings of iterative solution scheme -------------------------------------------- -For simulations with gap junctions NEST uses an iterative solution +For simulations with gap junctions, NEST uses an iterative solution scheme based on a numerical method called Jacobi waveform relaxation. The default settings of the iterative method are based on numerical -results, benchmarks and previous experience with gap-junction -simulations (see `Hahne et al. -2015 `__) +results, benchmarks, and previous experience with gap-junction +simulations (see Hahne et al. [2]_) and should only be changed with proper knowledge of the method. In general the following parameters can be set via kernel parameters: @@ -116,4 +112,24 @@ general the following parameters can be set via kernel parameters: nest.wfr_interpolation_order = 3 For a detailed description of the parameters and their function see -(`Hahne et al. 2016 `__, Table 2). +[3]_, Table 2. + + + +References +---------- + +.. [1] Hahne J, et al. 2016. Including Gap Junctions into Distributed Neuronal Network Simulations. + In: Amunts K, Grandinetti L, Lippert T, Petkov N. (eds) Brain-Inspired Computing. + BrainComp 2015. Lecture Notes in Computer Science(), vol 10087. Springer, Cham. + https://doi.org/10.1007/978-3-319-50862-7_4 + +.. [2] Hahne J, Helias M, Kunkel S, Igarashi J, Bolten M, Frommer A, Diesmann M 2015. + A unified framework for spiking and gap-junction interactions in distributed neuronal network simulations. + Frontiers in Neuroinformatics. 9 + https://www.frontiersin.org/journals/neuroinformatics/articles/10.3389/fninf.2015.00022 + +.. [3] Hahne J, Dahmen D , Schuecker J, Frommer A, Bolten M, Helias M, Diesmann M. 2017. + Integration of Continuous-Time Dynamics in a Spiking Neural Network Simulator. + Frontiers in Neuroinformatics. 11. + https://www.frontiersin.org/journals/neuroinformatics/articles/10.3389/fninf.2017.00034 From 44c73e62f884d03552f8a8b7d93e89051bc2135f Mon Sep 17 00:00:00 2001 From: Jessica Mitchell Date: Wed, 7 Aug 2024 12:42:44 +0200 Subject: [PATCH 21/86] add example links --- doc/htmldoc/synapses/simulations_with_gap_junctions.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/htmldoc/synapses/simulations_with_gap_junctions.rst b/doc/htmldoc/synapses/simulations_with_gap_junctions.rst index 8ef65332fc..8bdd933215 100644 --- a/doc/htmldoc/synapses/simulations_with_gap_junctions.rst +++ b/doc/htmldoc/synapses/simulations_with_gap_junctions.rst @@ -114,7 +114,10 @@ general the following parameters can be set via kernel parameters: For a detailed description of the parameters and their function see [3]_, Table 2. +.. seealso:: + * :doc:`/auto_examples/gap_junctions_inhibitory_network` + * :doc:`/auto_examples/gap_junctions_two_neurons` References ---------- From 0b7926c96b0d9efa790a2433a47c8aa659fffead Mon Sep 17 00:00:00 2001 From: Jessica Mitchell Date: Thu, 8 Aug 2024 09:59:43 +0200 Subject: [PATCH 22/86] update mailing list --- .../guidelines/mailing_list_guidelines.rst | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/doc/htmldoc/developer_space/guidelines/mailing_list_guidelines.rst b/doc/htmldoc/developer_space/guidelines/mailing_list_guidelines.rst index afadaadff8..d499ba0c8f 100644 --- a/doc/htmldoc/developer_space/guidelines/mailing_list_guidelines.rst +++ b/doc/htmldoc/developer_space/guidelines/mailing_list_guidelines.rst @@ -8,10 +8,15 @@ Guidelines for contributing to the mailing list To ensure community members provide accurate and quick responses to questions on the mailing list, please follow the guidelines below: +#. Check the `Mailing List Archive `_ and `GitHub issues `_ to see if your + question has already been addressed. + + You can also look at our :ref:`troubleshooting page ` for installation issues. #. Write an informative subject to your email regarding your question or problem. -#. **DO NOT** copy and paste lengthy log or error output without context. +#. **DO NOT** copy and paste lengthy log or error output *without context*. Remember you are addressing human beings, and we do not have time to read through pages of logs, especially if we don't understand what you want. @@ -25,7 +30,7 @@ please follow the guidelines below: * the steps you took that lead to the problem. * the specific error messages you get. - * relevant system and version information (e.g., Ubuntu 18.04/ NEST 2.20 installed using Conda). + * relevant system and version information (e.g., Ubuntu 22.04/ NEST 3.5 installed using the Conda package). #. Keep topics separate. @@ -36,7 +41,7 @@ please follow the guidelines below: .. grid:: 2 .. grid-item-card:: |mailicon| Access the mailing list - :link-type: url + :link-type: url :link: https://www.nest-initiative.org/mailinglist/ From 92ff702ecc1c5adc11fe8371b9ab2dc9cc8a5422 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Thu, 8 Aug 2024 11:04:58 +0200 Subject: [PATCH 23/86] Ensure script also works for networks with single neuron per layer. Co-authored-by: IiroAhokainen <59561196+IiroAhokainen@users.noreply.github.com> --- .../examples/astrocytes/astrocyte_small_network.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/pynest/examples/astrocytes/astrocyte_small_network.py b/pynest/examples/astrocytes/astrocyte_small_network.py index 60a8d21b04..d823615557 100644 --- a/pynest/examples/astrocytes/astrocyte_small_network.py +++ b/pynest/examples/astrocytes/astrocyte_small_network.py @@ -132,6 +132,8 @@ ############################################################################### # Import all necessary modules. +from collections.abc import Iterable + import matplotlib.pyplot as plt import nest import numpy as np @@ -195,10 +197,17 @@ def plot_connections(conn_n2n, conn_n2a, conn_a2n, pre_id_list, post_id_list, as """ print("Plotting connections ...") + # helper to ensure data can be converted to np.array + def _ensure_iterable(data): + if isinstance(data, Iterable): + return data + else: + return [data] + # helper function to create lists of connection positions def get_conn_positions(dict_in, source_center, target_center): - source_list = np.array(dict_in["source"]) - source_center - target_list = np.array(dict_in["target"]) - target_center + source_list = np.array(_ensure_iterable(dict_in["source"])) - source_center + target_list = np.array(_ensure_iterable(dict_in["target"])) - target_center return source_list.tolist(), target_list.tolist() # prepare data (lists of node positions, list of connection positions) From a47c9bcf8516cbc89e1bba6a37d411f6c42ea912 Mon Sep 17 00:00:00 2001 From: Jessica Mitchell Date: Thu, 8 Aug 2024 11:09:18 +0200 Subject: [PATCH 24/86] move images back to pynest/examples, update links --- .../EI_clustered_network_schematic.png | Bin pynest/examples/EI_clustered_network/README.rst | 2 +- pynest/examples/Potjans_2014/README.rst | 6 +++--- .../Potjans_2014}/potjans_2014_box_plot.png | Bin .../Potjans_2014}/potjans_2014_microcircuit.png | Bin .../Potjans_2014}/potjans_2014_raster_plot.png | Bin pynest/examples/eprop_plasticity/README.rst | 2 +- ...pervised_classification_evidence-accumulation.py | 2 +- .../eprop_supervised_regression_handwriting.py | 12 +++++++++++- .../eprop_supervised_regression_infinite-loop.py | 2 +- .../eprop_supervised_regression_sine-waves.py | 2 +- pynest/examples/hpc_benchmark.py | 2 +- 12 files changed, 20 insertions(+), 10 deletions(-) rename {doc/htmldoc/static/img/pynest => pynest/examples/EI_clustered_network}/EI_clustered_network_schematic.png (100%) rename {doc/htmldoc/static/img => pynest/examples/Potjans_2014}/potjans_2014_box_plot.png (100%) rename {doc/htmldoc/static/img => pynest/examples/Potjans_2014}/potjans_2014_microcircuit.png (100%) rename {doc/htmldoc/static/img => pynest/examples/Potjans_2014}/potjans_2014_raster_plot.png (100%) diff --git a/doc/htmldoc/static/img/pynest/EI_clustered_network_schematic.png b/pynest/examples/EI_clustered_network/EI_clustered_network_schematic.png similarity index 100% rename from doc/htmldoc/static/img/pynest/EI_clustered_network_schematic.png rename to pynest/examples/EI_clustered_network/EI_clustered_network_schematic.png diff --git a/pynest/examples/EI_clustered_network/README.rst b/pynest/examples/EI_clustered_network/README.rst index 75d3961457..45dd99e717 100644 --- a/pynest/examples/EI_clustered_network/README.rst +++ b/pynest/examples/EI_clustered_network/README.rst @@ -3,7 +3,7 @@ EI-clustered circuit model This is PyNEST implementation of the EI-clustered circuit model described by Rostami et al. [1]_. -.. figure:: /static/img/pynest/EI_clustered_network_schematic.png +.. figure:: EI_clustered_network_schematic.png :alt: EI-clustered circuit model. Schematic of the EI-clustered circuit model. The network consists of `n_clusters` with one excitatory and one inhibitory population each. diff --git a/pynest/examples/Potjans_2014/README.rst b/pynest/examples/Potjans_2014/README.rst index d8ec2c5539..1c6ad7be4c 100644 --- a/pynest/examples/Potjans_2014/README.rst +++ b/pynest/examples/Potjans_2014/README.rst @@ -9,15 +9,15 @@ The network model represents four layers of cortex, L2/3, L4, L5, and L6, each c .. grid-item:: - .. image:: ../../static/img/potjans_2014_microcircuit.png + .. image:: potjans_2014_microcircuit.png .. grid-item:: - .. image:: ../../static/img/potjans_2014_raster_plot.png + .. image:: potjans_2014_raster_plot.png .. grid-item:: - .. image:: ../../static/img/potjans_2014_box_plot.png + .. image:: potjans_2014_box_plot.png diff --git a/doc/htmldoc/static/img/potjans_2014_box_plot.png b/pynest/examples/Potjans_2014/potjans_2014_box_plot.png similarity index 100% rename from doc/htmldoc/static/img/potjans_2014_box_plot.png rename to pynest/examples/Potjans_2014/potjans_2014_box_plot.png diff --git a/doc/htmldoc/static/img/potjans_2014_microcircuit.png b/pynest/examples/Potjans_2014/potjans_2014_microcircuit.png similarity index 100% rename from doc/htmldoc/static/img/potjans_2014_microcircuit.png rename to pynest/examples/Potjans_2014/potjans_2014_microcircuit.png diff --git a/doc/htmldoc/static/img/potjans_2014_raster_plot.png b/pynest/examples/Potjans_2014/potjans_2014_raster_plot.png similarity index 100% rename from doc/htmldoc/static/img/potjans_2014_raster_plot.png rename to pynest/examples/Potjans_2014/potjans_2014_raster_plot.png diff --git a/pynest/examples/eprop_plasticity/README.rst b/pynest/examples/eprop_plasticity/README.rst index 99c49c1e94..6a23010f0c 100644 --- a/pynest/examples/eprop_plasticity/README.rst +++ b/pynest/examples/eprop_plasticity/README.rst @@ -2,7 +2,7 @@ E-prop plasticity examples ========================== -.. image:: ../../../../pynest/examples/eprop_plasticity/eprop_supervised_regression_schematic_sine-waves.png +.. image:: eprop_supervised_regression_schematic_sine-waves.png Eligibility propagation (e-prop) [1]_ is a three-factor learning rule for spiking neural networks that approximates backpropagation through time. The original TensorFlow implementation of e-prop diff --git a/pynest/examples/eprop_plasticity/eprop_supervised_classification_evidence-accumulation.py b/pynest/examples/eprop_plasticity/eprop_supervised_classification_evidence-accumulation.py index f0facff028..a82a018e93 100644 --- a/pynest/examples/eprop_plasticity/eprop_supervised_classification_evidence-accumulation.py +++ b/pynest/examples/eprop_plasticity/eprop_supervised_classification_evidence-accumulation.py @@ -40,7 +40,7 @@ infer the underlying rationale of the task. Here, the solution is to turn to the side in which more cues were presented. -.. image:: ../../../../pynest/examples/eprop_plasticity/eprop_supervised_classification_schematic_evidence-accumulation.png +.. image:: eprop_supervised_classification_schematic_evidence-accumulation.png :width: 70 % :alt: See Figure 1 below. :align: center diff --git a/pynest/examples/eprop_plasticity/eprop_supervised_regression_handwriting.py b/pynest/examples/eprop_plasticity/eprop_supervised_regression_handwriting.py index 6c2f5aa46c..381d878848 100644 --- a/pynest/examples/eprop_plasticity/eprop_supervised_regression_handwriting.py +++ b/pynest/examples/eprop_plasticity/eprop_supervised_regression_handwriting.py @@ -39,7 +39,7 @@ learns to reproduce with its overall spiking activity a two-dimensional, roughly one-second-long target signal which encode the x and y coordinates of the handwritten word "chaos". -.. image:: ../../../../pynest/examples/eprop_plasticity/eprop_supervised_regression_schematic_handwriting.png +.. image:: eprop_supervised_regression_schematic_handwriting.png :width: 70 % :alt: See Figure 1 below. :align: center @@ -57,6 +57,16 @@ Charl Linssen, inspired by activities and feedback received at the CapoCaccia Workshop toward Neuromorphic Intelligence 2023. + +Get the chaos_handwriting.txt file: + +.. grid:: + + .. grid-item-card:: + :columns: 3 + + :download:`chaos_handwriting.txt` + References ~~~~~~~~~~ diff --git a/pynest/examples/eprop_plasticity/eprop_supervised_regression_infinite-loop.py b/pynest/examples/eprop_plasticity/eprop_supervised_regression_infinite-loop.py index 1ed14fd4a6..9cd9b07eb6 100644 --- a/pynest/examples/eprop_plasticity/eprop_supervised_regression_infinite-loop.py +++ b/pynest/examples/eprop_plasticity/eprop_supervised_regression_infinite-loop.py @@ -39,7 +39,7 @@ learns to reproduce with its overall spiking activity a two-dimensional, roughly two-second-long target signal which encode the x and y coordinates of an infinite-loop. -.. image:: ../../../../pynest/examples/eprop_plasticity/eprop_supervised_regression_schematic_infinite-loop.png +.. image:: eprop_supervised_regression_schematic_infinite-loop.png :width: 70 % :alt: See Figure 1 below. :align: center diff --git a/pynest/examples/eprop_plasticity/eprop_supervised_regression_sine-waves.py b/pynest/examples/eprop_plasticity/eprop_supervised_regression_sine-waves.py index 0f848a1390..c74a69cf36 100644 --- a/pynest/examples/eprop_plasticity/eprop_supervised_regression_sine-waves.py +++ b/pynest/examples/eprop_plasticity/eprop_supervised_regression_sine-waves.py @@ -38,7 +38,7 @@ network learns to reproduce with its overall spiking activity a one-dimensional, one-second-long target signal which is a superposition of four sine waves of different amplitudes, phases, and periods. -.. image:: ../../../../pynest/examples/eprop_plasticity/eprop_supervised_regression_schematic_sine-waves.png +.. image:: eprop_supervised_regression_schematic_sine-waves.png :width: 70 % :alt: See Figure 1 below. :align: center diff --git a/pynest/examples/hpc_benchmark.py b/pynest/examples/hpc_benchmark.py index 32acf595a3..c59f16edbc 100644 --- a/pynest/examples/hpc_benchmark.py +++ b/pynest/examples/hpc_benchmark.py @@ -37,7 +37,7 @@ A note on connectivity ~~~~~~~~~~~~~~~~~~~~~~ -.. image:: ../examples/hpc_benchmark_connectivity.svg +.. image:: hpc_benchmark_connectivity.svg :width: 50 % :alt: HPC Benchmark network architecture :align: right From 9df59008b626f990c1daadb3d6d436e8d45ebcfd Mon Sep 17 00:00:00 2001 From: Jessica Mitchell Date: Thu, 8 Aug 2024 11:09:47 +0200 Subject: [PATCH 25/86] remove copy function and use the sphinx gallery parameter to copy various file types --- doc/htmldoc/conf.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/doc/htmldoc/conf.py b/doc/htmldoc/conf.py index 2206227a47..911e5f4cbb 100644 --- a/doc/htmldoc/conf.py +++ b/doc/htmldoc/conf.py @@ -80,6 +80,7 @@ "gallery_dirs": "auto_examples", "plot_gallery": "False", "download_all_examples": False, + "copyfile_regex": r".*\.rst|.*\.png|.*\.svg|Snakefile|.*\.txt", } # General information about the project. @@ -260,14 +261,6 @@ ] -def copy_example_file(src): - copyfile(os.path.join(pynest_dir, src), Path("examples") / Path(src).parts[-1]) - - -# -- Copy documentation for Microcircuit Model ---------------------------- -copy_example_file("examples/hpc_benchmark_connectivity.svg") - - def patch_documentation(patch_url): """Apply a hot-fix patch to the documentation before building it. From c98c79c2870fc8da1bd3214bd9eec2f900a53ad6 Mon Sep 17 00:00:00 2001 From: Jessica Mitchell Date: Thu, 8 Aug 2024 11:16:58 +0200 Subject: [PATCH 26/86] update links in examples, add hpc svg --- doc/htmldoc/examples/index.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/htmldoc/examples/index.rst b/doc/htmldoc/examples/index.rst index 2f22cb80ab..c5fc79b5e3 100644 --- a/doc/htmldoc/examples/index.rst +++ b/doc/htmldoc/examples/index.rst @@ -76,14 +76,14 @@ PyNEST examples .. grid-item-card:: Cortical microcircuit (Potjans) - :img-top: ../static/img/potjans_2014_raster_plot.png + :img-top: ../auto_examples/Potjans_2014/potjans_2014_raster_plot.png * :doc:`../auto_examples/Potjans_2014/index` .. grid-item-card:: EI clustered network (Rostami et al) - :img-top: ../static/img/pynest/EI_clustered_network_schematic.png + :img-top: ../auto_examples/EI_clustered_network/EI_clustered_network_schematic.png :doc:`../auto_examples/EI_clustered_network/index` @@ -256,7 +256,7 @@ PyNEST examples * :doc:`../auto_examples/sonata_example/sonata_network` .. grid-item-card:: HPC benchmark - :img-top: ../static/img/nest_logo-faded.png + :img-top: ../auto_examples/hpc_benchmark_connectivity.svg * :doc:`../auto_examples/hpc_benchmark` From 725725d35b9b82ad0685e6a1f0451ce938f7bb84 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Thu, 8 Aug 2024 11:17:44 +0200 Subject: [PATCH 27/86] Updated example documentation to new tripartite scheme --- .../astrocytes/astrocyte_small_network.py | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/pynest/examples/astrocytes/astrocyte_small_network.py b/pynest/examples/astrocytes/astrocyte_small_network.py index d823615557..00aecd5e47 100644 --- a/pynest/examples/astrocytes/astrocyte_small_network.py +++ b/pynest/examples/astrocytes/astrocyte_small_network.py @@ -29,15 +29,17 @@ neurons are modeled with ``aeif_cond_alpha_astro``, an adaptive exponential integrate-and-fire neuron supporting neuron-astrocyte interactions. -The network is created with the ``TripartiteConnect()`` function and the -``tripartite_bernoulli_with_pool`` rule (see :ref:`tripartite_connectivity` for -detailed descriptions). This rule creates a tripartite Bernoulli connectivity -with the following principles: - -1. For each pair of neurons, a Bernoulli trial with a probability ``p_primary`` +Neurons are connected with each other and with astrocytes using the ``TripartiteConnect()`` +function. Neuron populations are connected using NEST's standard ``pairwise_bernoulli`` +connection and connections with astrocytes are added according to the +``third_factor_bernoulli_with_pool`` rule (see :ref:`tripartite_connectivity` for detailed +descriptions). This creates a tripartite Bernoulli connectivity with the following +principles: + +1. For each pair of neurons, a Bernoulli trial with probability ``p_primary`` determines if a ``tsodyks_synapse`` will be created between them. -2. For each neuron-neuron connection created, a Bernoulli trial with a +2. For each neuron-neuron connection created, a Bernoulli trial with probability ``p_third_if_primary`` determines if it will be paired with one astrocyte. The selection of this particular astrocyte is confined by ``pool_size`` and ``pool_type`` (see below). @@ -49,11 +51,13 @@ The available connectivity parameters are as follows: -* ``conn_spec`` parameters +* ``conn_spec``: any NEST one-directional connection rule + +* ``third_factor_conn_spec`` - * ``p_primary``: Connection probability between neurons. + * ``rule``: a third-factor connectivity rule - * ``p_third_if_primary``: Probability of each created neuron-neuron connection to be + * ``p``: Probability of each created neuron-neuron connection to be paired with one astrocyte. * ``pool_size``: The size of astrocyte pool for each target neuron. The @@ -77,17 +81,19 @@ * ``third_out``: ``syn_spec`` specifications for the connections from astrocytes to neurons. -In this script, the network is created with the ``pool_type`` being ``"block"``. -``p_primary`` and ``p_third_if_primary`` are both set to one to include as many -connections as possible. One of the created figures shows the connections between -neurons and astrocytes as a result (note that multiple connections may exist -between a pair of nodes; this is not obvious in the figure since connections -drawn later cover previous ones). It can be seen from the figure that ``"block"`` -results in astrocytes being connected to postsynaptic neurons in non-overlapping -blocks. The ``pool_size`` should be compatible with this arrangement; in the case -here, a ``pool_size`` of one is required. Users can try different parameters -(e.g. ``p_primary`` = 0.5 and ``p_third_if_primary`` = 0.5) to see changes in -connections. +In this script, the network is created with the ``pool_type`` being +``"block"``. Probabilities for the primary and the third-factor +connections are both set to 1 to include as many connections as +possible. One of the created figures shows the connections between +neurons and astrocytes as a result (note that multiple connections may +exist between a pair of nodes; this is not obvious in the figure since +connections drawn later cover previous ones). It can be seen from the +figure that ``"block"`` results in astrocytes being connected to +postsynaptic neurons in non-overlapping blocks. The ``pool_size`` +should be compatible with this arrangement; in the case here, a +``pool_size`` of 1 is required. Users can try different parameters +(e.g. ``conn_spec["p"]`` = 0.5 and ``third_factor_conn_spec["p"]`` = +0.5) to see changes in connections. With the created network, neuron-astrocyte interactions can be observed. The presynaptic spikes induce the generation of IP3, which then changes the calcium @@ -101,7 +107,7 @@ total number of astrocytes. See :ref:`tripartite_connectivity` for more details about the -``TripartiteConnect()`` function and the ``tripartite_bernoulli_with_pool`` +``TripartiteConnect()`` function and the ``third_factor_bernoulli_with_pool`` rule. References From a49f97bcf9fd7af55c244251c84b04936fb3185e Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Thu, 8 Aug 2024 11:25:54 +0200 Subject: [PATCH 28/86] Update pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py Co-authored-by: IiroAhokainen <59561196+IiroAhokainen@users.noreply.github.com> --- pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py b/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py index fb19ed20e4..c0fd16fda7 100644 --- a/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py +++ b/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py @@ -172,7 +172,7 @@ def connect_astro_network(nodes_ex, nodes_in, nodes_astro, nodes_noise, scale=1. """Connect the nodes in a neuron-astrocyte network. Nodes in a neuron-astrocyte network are connected. The connection - probability between neurons is divided by a the given scale to preserve + probability between neurons is divided by the given scale to preserve the expected number of connections for each node. The astrocytes are paired with excitatory connections only. From 39d86d76f4314ecaef7533e90fcf42c26722ca30 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Thu, 8 Aug 2024 11:34:09 +0200 Subject: [PATCH 29/86] Update pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py Co-authored-by: IiroAhokainen <59561196+IiroAhokainen@users.noreply.github.com> --- pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py b/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py index c0fd16fda7..f6b9a540c7 100644 --- a/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py +++ b/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py @@ -252,7 +252,7 @@ def plot_dynamics(astro_data, neuron_data, start): """Plot the dynamics in neurons and astrocytes. The dynamics in the given neuron and astrocyte nodes are plotted. The - dynamics in clude IP3 and calcium in the astrocytes, and the SIC input to + dynamics include IP3 and calcium in the astrocytes, and the SIC input to the neurons. Parameters From 11e67baae8724ef47d79e400a7195b5f6fa63fb4 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Thu, 8 Aug 2024 11:35:00 +0200 Subject: [PATCH 30/86] Update pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py Co-authored-by: IiroAhokainen <59561196+IiroAhokainen@users.noreply.github.com> --- pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py b/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py index f6b9a540c7..9c12932db5 100644 --- a/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py +++ b/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py @@ -253,7 +253,7 @@ def plot_dynamics(astro_data, neuron_data, start): The dynamics in the given neuron and astrocyte nodes are plotted. The dynamics include IP3 and calcium in the astrocytes, and the SIC input to - the neurons. + the postsynaptic neurons. Parameters --------- From c67b1ad81cec67c886d42118011b210bd403304a Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Thu, 8 Aug 2024 11:44:58 +0200 Subject: [PATCH 31/86] Merging suggestions from Github. --- .../astrocytes/astrocyte_brunel_bernoulli.py | 9 +++--- .../astrocyte_brunel_fixed_indegree.py | 29 +++++++------------ 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/pynest/examples/astrocytes/astrocyte_brunel_bernoulli.py b/pynest/examples/astrocytes/astrocyte_brunel_bernoulli.py index 98b2c3eec6..74673697e5 100644 --- a/pynest/examples/astrocytes/astrocyte_brunel_bernoulli.py +++ b/pynest/examples/astrocytes/astrocyte_brunel_bernoulli.py @@ -30,9 +30,9 @@ supporting neuron-astrocyte interactions. The simulation results show how astrocytes affect neuronal excitability. The -astrocytic dynamics, the slow inward current in the neurons induced by the -astrocytes, and the raster plot of neuronal firings are shown in the created -figures. +figures displayed at the end of the simulation show the astrocytic dynamics, +the slow inward current induced by the astrocytes in the postsynaptic neurons, +and a raster plot of neuronal firings, respectively. In this version of the model, primary connections between populations are created with the pairwise bernoulli rule. @@ -171,7 +171,7 @@ def connect_astro_network(nodes_ex, nodes_in, nodes_astro, nodes_noise, scale=1. """Connect the nodes in a neuron-astrocyte network. Nodes in a neuron-astrocyte network are connected. The connection - probability between neurons is divided by a the given scale to preserve + probability between neurons is divided by the given scale to preserve the expected number of connections for each node. The astrocytes are paired with excitatory connections only. @@ -364,7 +364,6 @@ def run_simulation(): nest.Simulate(sim_time) # read out recordings - neuron_spikes = sr_neuron.events neuron_data = mm_neuron.events astro_data = mm_astro.events diff --git a/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py b/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py index fb19ed20e4..8ecce5e385 100644 --- a/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py +++ b/pynest/examples/astrocytes/astrocyte_brunel_fixed_indegree.py @@ -30,9 +30,9 @@ supporting neuron-astrocyte interactions. The simulation results show how astrocytes affect neuronal excitability. The -astrocytic dynamics, the slow inward current in the neurons induced by the -astrocytes, and the raster plot of neuronal firings are shown in the created -figures. +figures displayed at the end of the simulation show the astrocytic dynamics, +the slow inward current induced by the astrocytes in the postsynaptic neurons, +and a raster plot of neuronal firings, respectively. In this version of the model, primary connections between populations are created with the fixed-indegree rule. @@ -91,8 +91,8 @@ "N_ex": 8000, # number of excitatory neurons "N_in": 2000, # number of inhibitory neurons "N_astro": 10000, # number of astrocytes - "CE": 800, - "CI": 200, + "CE": 800, # number of incoming excitatory connections per neuron + "CI": 200, # number of incoming inhbitory connections per neuron "p_third_if_primary": 0.5, # probability of each created neuron-neuron connection to be paired with one astrocyte "pool_size": 10, # astrocyte pool size for each target neuron "pool_type": "random", # astrocyte pool will be chosen randomly for each target neuron @@ -146,7 +146,7 @@ def create_astro_network(scale=1.0): """Create nodes for a neuron-astrocyte network. Nodes in a neuron-astrocyte network are created according to the give scale - of the model. The nodes created include excitatory and inhibitory neruons, + of the model. The nodes created include excitatory and inhibitory neurons, astrocytes, and a Poisson generator. Parameters @@ -168,13 +168,13 @@ def create_astro_network(scale=1.0): return nodes_ex, nodes_in, nodes_astro, nodes_noise -def connect_astro_network(nodes_ex, nodes_in, nodes_astro, nodes_noise, scale=1.0): +def connect_astro_network(nodes_ex, nodes_in, nodes_astro, nodes_noise): """Connect the nodes in a neuron-astrocyte network. - Nodes in a neuron-astrocyte network are connected. The connection - probability between neurons is divided by a the given scale to preserve - the expected number of connections for each node. The astrocytes are paired - with excitatory connections only. + Nodes in a neuron-astrocyte network are connected. The indegree of neurons + is not changed with network scale to preserve the expected number of connections + for each node (consistent with the corresponding bernoulli example). + The astrocytes are paired with excitatory connections only. Parameters --------- @@ -186,12 +186,9 @@ def connect_astro_network(nodes_ex, nodes_in, nodes_astro, nodes_noise, scale=1. Nodes of astrocytes. node_noise Poisson generator. - scale - Scale of the model. """ print("Connecting Poisson generator ...") - assert scale >= 1.0, "scale must be >= 1.0" nest.Connect(nodes_noise, nodes_ex + nodes_in, syn_spec={"weight": syn_params["w_e"]}) print("Connecting neurons and astrocytes ...") # excitatory connections are paired with astrocytes @@ -226,9 +223,6 @@ def connect_astro_network(nodes_ex, nodes_in, nodes_astro, nodes_noise, scale=1. third_factor_conn_spec=conn_params_astro, syn_specs=syn_params_e, ) - # nest.Connect(nodes_ex, nodes_ex + nodes_in, - # conn_spec=conn_params_e, - # syn_spec=syn_params_e["primary"]) # inhibitory connections are not paired with astrocytes conn_params_i = {"rule": "fixed_indegree", "indegree": network_params["CI"]} @@ -367,7 +361,6 @@ def run_simulation(): nest.Simulate(sim_time) # read out recordings - neuron_spikes = sr_neuron.events neuron_data = mm_neuron.events astro_data = mm_astro.events From 0290888502757f6b93c22789c30211e7422e8c6f Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Thu, 8 Aug 2024 12:05:16 +0200 Subject: [PATCH 32/86] Update connectivity_concepts documentation to new tripartite scheme --- .../synapses/connectivity_concepts.rst | 61 ++++++++----------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/doc/htmldoc/synapses/connectivity_concepts.rst b/doc/htmldoc/synapses/connectivity_concepts.rst index 962428ac73..9221ea93d0 100644 --- a/doc/htmldoc/synapses/connectivity_concepts.rst +++ b/doc/htmldoc/synapses/connectivity_concepts.rst @@ -500,18 +500,25 @@ As multapses are per default allowed and possible with this rule, you can disall .. _tripartite_connectivity: -Tripartite Bernoulli with pool ------------------------------- +Third-factor Bernoulli with pool +-------------------------------- For each possible pair of nodes from a source ``NodeCollection`` (e.g., a neuron population ``S``) and a target ``NodeCollection`` (e.g., a neuron population ``T``), a connection is -created with probability ``p_primary``, and these connections are -called 'primary' connections. For each primary connection, a +created according to the ``conn_spec`` parameter passed to +``TripariteConnect``; any one-directional connection specification can +be used. The connections created between ``S`` and ``T`` are the +'primary' connections. + +For each primary connection, a third-party connection pair involving a node from a third ``NodeCollection`` -(e.g., an astrocyte population ``A``) is created with the conditional probability -``p_third_if_primary``. This connection pair includes a connection +(e.g., an astrocyte population ``A``) is created according to the +``third_factor_conn_spec`` provide. This connection pair includes a connection from the ``S`` node to the ``A`` node, and a connection from the ``A`` node to the -``T`` node. The ``A`` node to connect to is chosen +``T`` node. + +At present, ``third_factor_bernoulli_with_pool`` is the only connection rule +available for third-factor connectivity. It chooses the ``A`` node to connect at random from a pool, a subset of the nodes in ``A``. By default, this pool is all of ``A``. @@ -548,13 +555,14 @@ up to two randomly selected nodes in ``A`` (given ``pool_size == 2``). S = nest.Create('aeif_cond_alpha_astro', N_S) T = nest.Create('aeif_cond_alpha_astro', N_T) A = nest.Create('astrocyte_lr_1994', N_A) - conn_spec = {'rule': 'tripartite_bernoulli_with_pool', - 'p_primary': p_primary, - 'p_third_if_primary': p_third_if_primary, - 'pool_type': pool_type, - 'pool_size': pool_size} + conn_spec = {'rule': 'pairwise_bernoulli', + 'p': p_primary} + third_factor_conn_spec = {'rule': 'third_factor_bernoulli_with_pool', + 'p': p_third_if_primary, + 'pool_type': pool_type, + 'pool_size': pool_size} syn_specs = {'third_out': 'sic_connection'} - nest.TripartiteConnect(S, T, A, conn_spec, syn_specs) + nest.TripartiteConnect(S, T, A, conn_spec, third_factor_conn_spec, syn_specs) (B) In the first example of ``'block'`` pool type, let ``N_T/N_A`` = 2, @@ -562,20 +570,12 @@ then each node in ``T`` can be connected with one node in ``A`` (``pool_size == 1`` is required because ``N_A < N_T``), and each node in ``A`` can be connected with up to two nodes in ``T``. +The code for this example is identical to the code for example (A), +except for the choice of pool type and size: + .. code-block:: python - N_S, N_T, N_A, p_primary, p_third_if_primary = 6, 6, 3, 0.2, 1.0 pool_type, pool_size = 'block', 1 - S = nest.Create('aeif_cond_alpha_astro', N_S) - T = nest.Create('aeif_cond_alpha_astro', N_T) - A = nest.Create('astrocyte_lr_1994', N_A) - conn_spec = {'rule': 'tripartite_bernoulli_with_pool', - 'p_primary': p_primary, - 'p_third_if_primary': p_third_if_primary, - 'pool_type': pool_type, - 'pool_size': pool_size} - syn_specs = {'third_out': 'sic_connection'} - nest.TripartiteConnect(S, T, A, conn_spec, syn_specs) (C) In the second example of ``'block'`` pool type, let ``N_A/N_T`` = 2, then each node in @@ -583,20 +583,13 @@ of ``'block'`` pool type, let ``N_A/N_T`` = 2, then each node in required because ``N_A/N_T`` = 2), and each node in ``A`` can be connected to one node in ``T``. +In this example, we have different values for ``N_T`` and ``N_A`` than +in examples (A) and (B), and a different pool size than in example (B): + .. code-block:: python N_S, N_T, N_A, p_primary, p_third_if_primary = 6, 3, 6, 0.2, 1.0 pool_type, pool_size = 'block', 2 - S = nest.Create('aeif_cond_alpha_astro', N_S) - T = nest.Create('aeif_cond_alpha_astro', N_T) - A = nest.Create('astrocyte_lr_1994', N_A) - conn_spec = {'rule': 'tripartite_bernoulli_with_pool', - 'p_primary': p_primary, - 'p_third_if_primary': p_third_if_primary, - 'pool_type': pool_type, - 'pool_size': pool_size} - syn_specs = {'third_out': 'sic_connection'} - nest.TripartiteConnect(S, T, A, conn_spec, syn_specs) References From 7f5c12e55077590fe4c953dbd5761a6a19e12d85 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Thu, 8 Aug 2024 12:19:58 +0200 Subject: [PATCH 33/86] Fix typos --- doc/htmldoc/synapses/connectivity_concepts.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/htmldoc/synapses/connectivity_concepts.rst b/doc/htmldoc/synapses/connectivity_concepts.rst index 9221ea93d0..ead396e2d2 100644 --- a/doc/htmldoc/synapses/connectivity_concepts.rst +++ b/doc/htmldoc/synapses/connectivity_concepts.rst @@ -508,12 +508,12 @@ and a target ``NodeCollection`` (e.g., a neuron population ``T``), a connection created according to the ``conn_spec`` parameter passed to ``TripariteConnect``; any one-directional connection specification can be used. The connections created between ``S`` and ``T`` are the -'primary' connections. +*primary* connections. For each primary connection, a third-party connection pair involving a node from a third ``NodeCollection`` (e.g., an astrocyte population ``A``) is created according to the -``third_factor_conn_spec`` provide. This connection pair includes a connection +``third_factor_conn_spec`` provided. This connection pair includes a connection from the ``S`` node to the ``A`` node, and a connection from the ``A`` node to the ``T`` node. From e06cbf613c173cdf2a488d8f6f26b6b7a141dcca Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Thu, 8 Aug 2024 14:16:04 +0200 Subject: [PATCH 34/86] Update Boost detection to CMake's CONFIG scheme --- cmake/ProcessOptions.cmake | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cmake/ProcessOptions.cmake b/cmake/ProcessOptions.cmake index b4abab8237..09498146d1 100644 --- a/cmake/ProcessOptions.cmake +++ b/cmake/ProcessOptions.cmake @@ -294,11 +294,9 @@ function( NEST_PROCESS_WITH_GSL ) set( GSL_ROOT "${with-gsl}" ) endif () - find_package( GSL ) + find_package( GSL 1.11 ) - # only allow GSL 1.11 and later - if ( GSL_FOUND AND ( "${GSL_VERSION}" VERSION_GREATER "1.11" - OR "${GSL_VERSION}" VERSION_EQUAL "1.11" )) + if ( GSL_FOUND ) set( HAVE_GSL ON PARENT_SCOPE ) # export found variables to parent scope @@ -550,7 +548,8 @@ function( NEST_PROCESS_WITH_BOOST ) set(Boost_USE_RELEASE_LIBS ON) # only find release libs # Needs Boost version >=1.62.0 to use Boost sorting, JUNIT logging # Require Boost version >=1.69.0 due to change in Boost sort - find_package( Boost 1.69.0 ) + # Require Boost version >=1.70.0 due to change in package finding + find_package( Boost 1.70 CONFIG ) if ( Boost_FOUND ) # export found variables to parent scope set( HAVE_BOOST ON PARENT_SCOPE ) From d857959f03e80f4e54a38f7da546966ab7605b6c Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Thu, 8 Aug 2024 14:16:29 +0200 Subject: [PATCH 35/86] Issue warning if NEST is built without Boost --- cmake/ConfigureSummary.cmake | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cmake/ConfigureSummary.cmake b/cmake/ConfigureSummary.cmake index 0e3eba6208..ea9610aa9a 100644 --- a/cmake/ConfigureSummary.cmake +++ b/cmake/ConfigureSummary.cmake @@ -261,6 +261,18 @@ function( NEST_PRINT_CONFIG_SUMMARY ) message( "" ) endif () + if ( NOT HAVE_BOOST ) + message( "" ) + message( "ATTENTION!" ) + message( "You are about to compile NEST without the Boost Library or" ) + message( "your Boost Library is too old (before v1.70). This means" ) + message( "that a few neuron models will not be available and that" ) + message( "overall performance may be reduced." ) + message( "" ) + message( "--------------------------------------------------------------------------------" ) + message( "" ) + endif () + message( "You can now build and install NEST with" ) message( " make" ) message( " make install" ) From 5fabcba1c9b46c91de338ce407ccad6acff02a88 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Thu, 8 Aug 2024 14:25:22 +0200 Subject: [PATCH 36/86] Hike version number after release of 3.8 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index f3dc78103b..6e0bc89667 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.7.0-post0.dev0 +3.8.0-post0.dev0 From 6a819fe8a466a3bd3230e01fa44718418be38343 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Fri, 9 Aug 2024 11:06:48 +0200 Subject: [PATCH 37/86] Stop timer for gather secondary correctly --- nestkernel/simulation_manager.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/nestkernel/simulation_manager.cpp b/nestkernel/simulation_manager.cpp index bad0b83e7c..9e0da05602 100644 --- a/nestkernel/simulation_manager.cpp +++ b/nestkernel/simulation_manager.cpp @@ -1108,13 +1108,12 @@ nest::SimulationManager::update_() sw_gather_secondary_data_.start(); #endif kernel().event_delivery_manager.gather_secondary_events( true ); - } #ifdef TIMER_DETAILED - sw_gather_secondary_data_.stop(); + sw_gather_secondary_data_.stop(); #endif + } } - advance_time_(); if ( print_time_ ) From 275618089304898517bd40fd33ec477d9300352c Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Mon, 12 Aug 2024 16:34:34 +0200 Subject: [PATCH 38/86] Revised pool caching to support connection rules with arbitrary target order --- nestkernel/conn_builder.cpp | 24 +++++++++++++----------- nestkernel/conn_builder.h | 6 ++++-- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index b1f7d28988..0d893da53c 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -786,8 +786,7 @@ nest::ThirdBernoulliWithPoolBuilder::ThirdBernoulliWithPoolBuilder( const NodeCo , random_pool_( true ) , pool_size_( third->size() ) , targets_per_third_( targets->size() / third->size() ) - , previous_target_( kernel().vp_manager.get_num_threads(), nullptr ) - , pool_( kernel().vp_manager.get_num_threads(), nullptr ) + , pools_( kernel().vp_manager.get_num_threads(), nullptr ) { updateValue< double >( conn_spec, names::p, p_ ); updateValue< long >( conn_spec, names::pool_size, pool_size_ ); @@ -831,8 +830,7 @@ nest::ThirdBernoulliWithPoolBuilder::ThirdBernoulliWithPoolBuilder( const NodeCo #pragma omp parallel { const size_t thrd = kernel().vp_manager.get_thread_id(); - pool_[ thrd ] = new std::vector< NodeIDTriple >(); - pool_[ thrd ]->reserve( pool_size_ ); + pools_[ thrd ] = new TgtPoolMap_(); } if ( not random_pool_ ) @@ -857,7 +855,7 @@ nest::ThirdBernoulliWithPoolBuilder::~ThirdBernoulliWithPoolBuilder() #pragma omp parallel { const size_t thrd = kernel().vp_manager.get_thread_id(); - delete pool_[ thrd ]; + delete pools_[ thrd ]; if ( not random_pool_ ) { @@ -886,25 +884,29 @@ nest::ThirdBernoulliWithPoolBuilder::third_connect( size_t primary_source_id, No } // step 2, build pool if new target - if ( &primary_target != previous_target_[ tid ] ) + const size_t tgt_gid = primary_target.get_node_id(); + auto pool_it = pools_[ tid ]->find( tgt_gid ); + if ( pool_it == pools_[ tid ]->end() ) { - pool_[ tid ]->clear(); + const auto [ new_pool_it, emplace_ok ] = pools_[ tid ]->emplace( tgt_gid, PoolType_() ); + assert( emplace_ok ); + if ( random_pool_ ) { - rng->sample( sources_->begin(), sources_->end(), std::back_inserter( *pool_[ tid ] ), pool_size_ ); + rng->sample( sources_->begin(), sources_->end(), std::back_inserter( new_pool_it->second ), pool_size_ ); } else { std::copy_n( sources_->begin() + get_first_pool_index_( primary_target.get_tmp_nc_index() ), pool_size_, - std::back_inserter( *( pool_[ tid ] ) ) ); + std::back_inserter( new_pool_it->second ) ); } - previous_target_[ tid ] = &primary_target; + pool_it = new_pool_it; } // select third-factor neuron randomly from pool for this target const auto third_index = pool_size_ == 1 ? 0 : rng->ulrand( pool_size_ ); - const auto third_node_id = ( *pool_[ tid ] )[ third_index ].node_id; + const auto third_node_id = ( pool_it->second )[ third_index ].node_id; single_connect_( third_node_id, primary_target, tid, rng ); diff --git a/nestkernel/conn_builder.h b/nestkernel/conn_builder.h index f2cf0846ea..743cbb3569 100644 --- a/nestkernel/conn_builder.h +++ b/nestkernel/conn_builder.h @@ -461,8 +461,10 @@ class ThirdBernoulliWithPoolBuilder : public ThirdOutBuilder bool random_pool_; size_t pool_size_; size_t targets_per_third_; - std::vector< Node* > previous_target_; // TODO: cache thrashing possibility - std::vector< std::vector< NodeIDTriple >* > pool_; // outer: threads + + typedef std::vector< NodeIDTriple > PoolType_; + typedef std::map< size_t, PoolType_ > TgtPoolMap_; + std::vector< TgtPoolMap_* > pools_; // outer: threads }; From a8c30d2ed48d386bc6314fe85521bbd1e9aac36a Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Wed, 14 Aug 2024 22:28:32 +0200 Subject: [PATCH 39/86] Add detailed timers for third_inner connect --- nestkernel/conn_builder.cpp | 28 +++++++++++++++++++++++++++- nestkernel/connection_manager.cpp | 15 +++++++++++++++ nestkernel/connection_manager.h | 5 +++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/nestkernel/conn_builder.cpp b/nestkernel/conn_builder.cpp index 0d893da53c..7bb9d8d9f2 100644 --- a/nestkernel/conn_builder.cpp +++ b/nestkernel/conn_builder.cpp @@ -691,6 +691,10 @@ nest::ThirdInBuilder::connect_() { kernel().vp_manager.assert_single_threaded(); +#ifdef TIMER_DETAILED + kernel().connection_manager.sw_construction_connect_third_inner_count.start(); +#endif + // count up how many source-third pairs we need to send to each rank const size_t num_ranks = kernel().mpi_manager.get_num_processes(); std::vector< size_t > source_third_per_rank( num_ranks, 0 ); @@ -703,6 +707,11 @@ nest::ThirdInBuilder::connect_() } } +#ifdef TIMER_DETAILED + kernel().connection_manager.sw_construction_connect_third_inner_count.stop(); + kernel().connection_manager.sw_construction_connect_third_inner_max.start(); +#endif + // now find global maximum; for simplicity, we will use this to configure buffers std::vector< long > max_stc( num_ranks ); // MPIManager does not support size_t max_stc[ kernel().mpi_manager.get_rank() ] = @@ -711,6 +720,11 @@ nest::ThirdInBuilder::connect_() const size_t global_max_stc = *std::max_element( max_stc.begin(), max_stc.end() ); const size_t slots_per_rank = 2 * global_max_stc; +#ifdef TIMER_DETAILED + kernel().connection_manager.sw_construction_connect_third_inner_max.stop(); + kernel().connection_manager.sw_construction_connect_third_inner_fill.start(); +#endif + // create and fill send buffer, entries per pair std::vector< size_t > send_stg( num_ranks * slots_per_rank, 0 ); std::vector< size_t > rank_idx( num_ranks, 0 ); @@ -727,15 +741,24 @@ nest::ThirdInBuilder::connect_() } std::vector< size_t > recv_stg( num_ranks * slots_per_rank, 0 ); - const size_t send_recv_count = sizeof( size_t ) / sizeof( unsigned int ) * slots_per_rank; +#ifdef TIMER_DETAILED + kernel().connection_manager.sw_construction_connect_third_inner_fill.stop(); + kernel().connection_manager.sw_construction_connect_third_inner_communicate.start(); +#endif + // force to master thread for compatibility with MPI standard #pragma omp master { kernel().mpi_manager.communicate_Alltoall( send_stg, recv_stg, send_recv_count ); } +#ifdef TIMER_DETAILED + kernel().connection_manager.sw_construction_connect_third_inner_communicate.stop(); + kernel().connection_manager.sw_construction_connect_third_inner_connect.start(); +#endif + // Now recv_stg contains all source-third pairs where third is on current rank // Create connections in parallel @@ -757,6 +780,9 @@ nest::ThirdInBuilder::connect_() } } } +#ifdef TIMER_DETAILED + kernel().connection_manager.sw_construction_connect_third_inner_connect.stop(); +#endif } nest::ThirdOutBuilder::ThirdOutBuilder( const NodeCollectionPTR third, diff --git a/nestkernel/connection_manager.cpp b/nestkernel/connection_manager.cpp index 1d47802ff9..3ee4aeac00 100644 --- a/nestkernel/connection_manager.cpp +++ b/nestkernel/connection_manager.cpp @@ -120,6 +120,11 @@ nest::ConnectionManager::initialize( const bool adjust_number_of_threads_or_rng_ stdp_eps_ = 1.0e-6; min_delay_ = max_delay_ = 1; sw_construction_connect.reset(); + sw_construction_connect_third_inner_count.reset(); + sw_construction_connect_third_inner_max.reset(); + sw_construction_connect_third_inner_fill.reset(); + sw_construction_connect_third_inner_communicate.reset(); + sw_construction_connect_third_inner_connect.reset(); } const size_t num_threads = kernel().vp_manager.get_num_threads(); @@ -227,6 +232,16 @@ nest::ConnectionManager::get_status( DictionaryDatum& dict ) def< bool >( dict, names::use_compressed_spikes, use_compressed_spikes_ ); def< double >( dict, names::time_construction_connect, sw_construction_connect.elapsed() ); + def< double >( + dict, "time_construction_connect_third_inner_count", sw_construction_connect_third_inner_count.elapsed() ); + def< double >( dict, "time_construction_connect_third_inner_max", sw_construction_connect_third_inner_max.elapsed() ); + def< double >( + dict, "time_construction_connect_third_inner_fill", sw_construction_connect_third_inner_fill.elapsed() ); + def< double >( dict, + "time_construction_connect_third_inner_communicate", + sw_construction_connect_third_inner_communicate.elapsed() ); + def< double >( + dict, "time_construction_connect_third_inner_connect", sw_construction_connect_third_inner_connect.elapsed() ); ArrayDatum connection_rules; for ( auto const& element : *connruledict_ ) diff --git a/nestkernel/connection_manager.h b/nestkernel/connection_manager.h index 431ab7d1d3..dc437e5147 100644 --- a/nestkernel/connection_manager.h +++ b/nestkernel/connection_manager.h @@ -462,6 +462,11 @@ class ConnectionManager : public ManagerInterface // public stop watch for benchmarking purposes // start and stop in high-level connect functions in nestmodule.cpp and nest.cpp Stopwatch sw_construction_connect; + Stopwatch sw_construction_connect_third_inner_count; + Stopwatch sw_construction_connect_third_inner_max; + Stopwatch sw_construction_connect_third_inner_fill; + Stopwatch sw_construction_connect_third_inner_communicate; + Stopwatch sw_construction_connect_third_inner_connect; const std::vector< SpikeData >& get_compressed_spike_data( const synindex syn_id, const size_t idx ); From 137b1cdce597a7b675a4048640723602aa1f50e6 Mon Sep 17 00:00:00 2001 From: Mahesh Madhav Date: Wed, 14 Aug 2024 22:44:40 +0000 Subject: [PATCH 40/86] Fix pedantic error for ISO C++ error: ISO C++ requires the name after '::~' to be found in the same scope as the name before '::~' [-Werror,-Wdtor-name] --- 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 7aa224347f..e094ab59d5 100644 --- a/nestkernel/connection_creator_impl.h +++ b/nestkernel/connection_creator_impl.h @@ -167,7 +167,7 @@ ConnectionCreator::PoolWrapper_< D >::PoolWrapper_() } template < int D > -ConnectionCreator::PoolWrapper_< D >::~PoolWrapper_() +ConnectionCreator::PoolWrapper_< D >::~PoolWrapper_< D >() { if ( masked_layer_ ) { From 8f92cdebc63b5775b8e791b7dc8bdd8d909b1554 Mon Sep 17 00:00:00 2001 From: Mahesh Madhav <67384846+heshpdx@users.noreply.github.com> Date: Thu, 15 Aug 2024 11:36:35 -0700 Subject: [PATCH 41/86] Update sli/parser.h Co-authored-by: Hans Ekkehard Plesser --- sli/parser.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sli/parser.h b/sli/parser.h index 75b5009128..4bd1aff33a 100644 --- a/sli/parser.h +++ b/sli/parser.h @@ -61,7 +61,7 @@ class Parser public: Parser(); Parser( std::istream& ); - ~Parser() { if (s) delete s; } + ~Parser() { delete s; } bool operator()( Token& ); bool From 3ee5e11d764d20bdad45be8fae2425bdaeb3b88d Mon Sep 17 00:00:00 2001 From: Mahesh Madhav <67384846+heshpdx@users.noreply.github.com> Date: Thu, 15 Aug 2024 11:36:41 -0700 Subject: [PATCH 42/86] Update sli/slistartup.cc Co-authored-by: Hans Ekkehard Plesser --- sli/slistartup.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sli/slistartup.cc b/sli/slistartup.cc index 982a0692e9..a090c701f9 100644 --- a/sli/slistartup.cc +++ b/sli/slistartup.cc @@ -214,7 +214,7 @@ SLIStartup::SLIStartup( int argc, char** argv ) StringDatum* sd = new StringDatum( argv[ i ] ); args_array.push_back( Token( sd ) ); - std::string myarg = argv[i]; + const std::string myarg = argv[ i ]; if ( myarg == "-d" or myarg == "--debug" ) { From 1f39884ea018cc4cacd20e494410425d0301e3d7 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Sat, 17 Aug 2024 14:24:55 +0200 Subject: [PATCH 43/86] Add stopwatch for SyncProcesses --- nestkernel/mpi_manager.cpp | 11 +++++++++++ nestkernel/mpi_manager.h | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/nestkernel/mpi_manager.cpp b/nestkernel/mpi_manager.cpp index b7a348322e..8ca8d3ff8a 100644 --- a/nestkernel/mpi_manager.cpp +++ b/nestkernel/mpi_manager.cpp @@ -185,6 +185,8 @@ nest::MPIManager::initialize( const bool adjust_number_of_threads_or_rng_only ) return; } + sw_synchronize_.reset(); + #ifndef HAVE_MPI char* pmix_rank_set = std::getenv( "PMIX_RANK" ); // set by OpenMPI's launcher char* pmi_rank_set = std::getenv( "PMI_RANK" ); // set by MPICH's launcher @@ -260,6 +262,7 @@ nest::MPIManager::get_status( DictionaryDatum& dict ) def< size_t >( dict, names::max_buffer_size_target_data, max_buffer_size_target_data_ ); def< double >( dict, names::growth_factor_buffer_spike_data, growth_factor_buffer_spike_data_ ); def< double >( dict, names::growth_factor_buffer_target_data, growth_factor_buffer_target_data_ ); + def< double >( dict, "time_synchronize", sw_synchronize_.elapsed() ); } #ifdef HAVE_MPI @@ -798,7 +801,15 @@ nest::MPIManager::communicate_recv_counts_secondary_events() void nest::MPIManager::synchronize() { +#ifdef TIMER_DETAILED + sw_synchronize_.start(); +#endif + MPI_Barrier( comm ); + +#ifdef TIMER_DETAILED + sw_synchronize_.stop(); +#endif } diff --git a/nestkernel/mpi_manager.h b/nestkernel/mpi_manager.h index 58d7d30423..faaf16a796 100644 --- a/nestkernel/mpi_manager.h +++ b/nestkernel/mpi_manager.h @@ -42,6 +42,7 @@ // Includes from libnestutil: #include "manager_interface.h" +#include "stopwatch.h" // Includes from nestkernel: #include "nest_types.h" @@ -348,6 +349,9 @@ class MPIManager : public ManagerInterface //! Offset in the MPI send buffer (in ints) from which elements send to each rank will be read std::vector< int > send_displacements_secondary_events_in_int_per_rank_; + //! Stopwatch for calls to synchronize() + Stopwatch sw_synchronize_; + #ifdef HAVE_MPI std::vector< int > comm_step_; From d45c5e408d52496e192b5321d9ec7d96d296f3c2 Mon Sep 17 00:00:00 2001 From: Hans Ekkehard Plesser Date: Mon, 19 Aug 2024 11:15:19 +0200 Subject: [PATCH 44/86] Update sli/parser.h --- sli/parser.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sli/parser.h b/sli/parser.h index 4bd1aff33a..79f70bcba4 100644 --- a/sli/parser.h +++ b/sli/parser.h @@ -61,7 +61,10 @@ class Parser public: Parser(); Parser( std::istream& ); - ~Parser() { delete s; } + ~Parser() + { + delete s; + } bool operator()( Token& ); bool From 08fb4738a2e8dac45e4989375a8982b860f1066a Mon Sep 17 00:00:00 2001 From: Jessica Mitchell Date: Tue, 20 Aug 2024 10:11:45 +0200 Subject: [PATCH 45/86] fix typo --- models/multimeter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/multimeter.h b/models/multimeter.h index a6c106d74e..181e9ff2e8 100644 --- a/models/multimeter.h +++ b/models/multimeter.h @@ -76,7 +76,7 @@ using the ``multimeter`` parameter ``interval``. The default value of :: - nest.SetStatus(mm, 'interval': 0.1}) + nest.SetStatus(mm, {'interval': 0.1}) The recording interval must be greater than or equal to the :ref:`simulation resolution `, which defaults From b24dfa0aada5472f13bbc3e6cd3a8cd6584b147b Mon Sep 17 00:00:00 2001 From: Jessica Mitchell Date: Wed, 21 Aug 2024 10:45:43 +0200 Subject: [PATCH 46/86] add jquery link --- doc/htmldoc/templates/layout.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/htmldoc/templates/layout.html b/doc/htmldoc/templates/layout.html index 4805cc5cd2..8c940cc447 100644 --- a/doc/htmldoc/templates/layout.html +++ b/doc/htmldoc/templates/layout.html @@ -7,6 +7,7 @@ + {{ super() }} {% endblock %} From 7a25498bdf8dd6aa149f872be9ae51d2db31338a Mon Sep 17 00:00:00 2001 From: Jessica Mitchell Date: Wed, 21 Aug 2024 10:46:11 +0200 Subject: [PATCH 47/86] remove js from directory --- doc/htmldoc/static/js/jquery-3.6.0.min.js | 3 - doc/htmldoc/static/js/jquery-ui.min.js | 13 --- doc/htmldoc/static/js/particles-app.js | 119 ---------------------- doc/htmldoc/static/js/tsparticles.min.js | 30 ------ 4 files changed, 165 deletions(-) delete mode 100644 doc/htmldoc/static/js/jquery-3.6.0.min.js delete mode 100644 doc/htmldoc/static/js/jquery-ui.min.js delete mode 100644 doc/htmldoc/static/js/particles-app.js delete mode 100644 doc/htmldoc/static/js/tsparticles.min.js diff --git a/doc/htmldoc/static/js/jquery-3.6.0.min.js b/doc/htmldoc/static/js/jquery-3.6.0.min.js deleted file mode 100644 index 65d6e24d25..0000000000 --- a/doc/htmldoc/static/js/jquery-3.6.0.min.js +++ /dev/null @@ -1,3 +0,0 @@ -/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0"))}function n(e){var i="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return e.on("mouseout",i,function(){t(this).removeClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&t(this).removeClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&t(this).removeClass("ui-datepicker-next-hover")}).on("mouseover",i,o)}function o(){t.datepicker._isDisabledDatepicker(m.inline?m.dpDiv.parent()[0]:m.input[0])||(t(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),t(this).addClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&t(this).addClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&t(this).addClass("ui-datepicker-next-hover"))}function a(e,i){t.extend(e,i);for(var s in i)null==i[s]&&(e[s]=i[s]);return e}function r(t){return function(){var e=this.element.val();t.apply(this,arguments),this._refresh(),e!==this.element.val()&&this._trigger("change")}}t.ui=t.ui||{},t.ui.version="1.12.1";var h=0,l=Array.prototype.slice;t.cleanData=function(e){return function(i){var s,n,o;for(o=0;null!=(n=i[o]);o++)try{s=t._data(n,"events"),s&&s.remove&&t(n).triggerHandler("remove")}catch(a){}e(i)}}(t.cleanData),t.widget=function(e,i,s){var n,o,a,r={},h=e.split(".")[0];e=e.split(".")[1];var l=h+"-"+e;return s||(s=i,i=t.Widget),t.isArray(s)&&(s=t.extend.apply(null,[{}].concat(s))),t.expr[":"][l.toLowerCase()]=function(e){return!!t.data(e,l)},t[h]=t[h]||{},n=t[h][e],o=t[h][e]=function(t,e){return this._createWidget?(arguments.length&&this._createWidget(t,e),void 0):new o(t,e)},t.extend(o,n,{version:s.version,_proto:t.extend({},s),_childConstructors:[]}),a=new i,a.options=t.widget.extend({},a.options),t.each(s,function(e,s){return t.isFunction(s)?(r[e]=function(){function t(){return i.prototype[e].apply(this,arguments)}function n(t){return i.prototype[e].apply(this,t)}return function(){var e,i=this._super,o=this._superApply;return this._super=t,this._superApply=n,e=s.apply(this,arguments),this._super=i,this._superApply=o,e}}(),void 0):(r[e]=s,void 0)}),o.prototype=t.widget.extend(a,{widgetEventPrefix:n?a.widgetEventPrefix||e:e},r,{constructor:o,namespace:h,widgetName:e,widgetFullName:l}),n?(t.each(n._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete n._childConstructors):i._childConstructors.push(o),t.widget.bridge(e,o),o},t.widget.extend=function(e){for(var i,s,n=l.call(arguments,1),o=0,a=n.length;a>o;o++)for(i in n[o])s=n[o][i],n[o].hasOwnProperty(i)&&void 0!==s&&(e[i]=t.isPlainObject(s)?t.isPlainObject(e[i])?t.widget.extend({},e[i],s):t.widget.extend({},s):s);return e},t.widget.bridge=function(e,i){var s=i.prototype.widgetFullName||e;t.fn[e]=function(n){var o="string"==typeof n,a=l.call(arguments,1),r=this;return o?this.length||"instance"!==n?this.each(function(){var i,o=t.data(this,s);return"instance"===n?(r=o,!1):o?t.isFunction(o[n])&&"_"!==n.charAt(0)?(i=o[n].apply(o,a),i!==o&&void 0!==i?(r=i&&i.jquery?r.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+n+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; "+"attempted to call method '"+n+"'")}):r=void 0:(a.length&&(n=t.widget.extend.apply(null,[n].concat(a))),this.each(function(){var e=t.data(this,s);e?(e.option(n||{}),e._init&&e._init()):t.data(this,s,new i(n,this))})),r}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
",options:{classes:{},disabled:!1,create:null},_createWidget:function(e,i){i=t(i||this.defaultElement||this)[0],this.element=t(i),this.uuid=h++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},i!==this&&(t.data(i,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===i&&this.destroy()}}),this.document=t(i.style?i.ownerDocument:i.document||i),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),e),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,o,a=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(a={},s=e.split("."),e=s.shift(),s.length){for(n=a[e]=t.widget.extend({},this.options[e]),o=0;s.length-1>o;o++)n[s[o]]=n[s[o]]||{},n=n[s[o]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];a[e]=i}return this._setOptions(a),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,n;for(i in e)n=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&n&&n.length&&(s=t(n.get()),this._removeClass(n,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,o){var a,r;for(r=0;i.length>r;r++)a=n.classesElementLookup[i[r]]||t(),a=e.add?t(t.unique(a.get().concat(e.element.get()))):t(a.not(e.element).get()),n.classesElementLookup[i[r]]=a,s.push(i[r]),o&&e.classes[i[r]]&&s.push(e.classes[i[r]])}var s=[],n=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),this._on(e.element,{remove:"_untrackClassesElement"}),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_untrackClassesElement:function(e){var i=this;t.each(i.classesElementLookup,function(s,n){-1!==t.inArray(e.target,n)&&(i.classesElementLookup[s]=t(n.not(e.target).get()))})},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var n="string"==typeof t||null===t,o={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s};return o.element.toggleClass(this._classes(o),s),this},_on:function(e,i,s){var n,o=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,a){function r(){return e||o.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof a?o[a]:a).apply(o,arguments):void 0}"string"!=typeof a&&(r.guid=a.guid=a.guid||r.guid||t.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+o.eventNamespace,c=h[2];c?n.on(l,c,r):i.on(l,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}}),t.widget,function(){function e(t,e,i){return[parseFloat(t[0])*(u.test(t[0])?e/100:1),parseFloat(t[1])*(u.test(t[1])?i/100:1)]}function i(e,i){return parseInt(t.css(e,i),10)||0}function s(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}var n,o=Math.max,a=Math.abs,r=/left|center|right/,h=/top|center|bottom/,l=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,u=/%$/,d=t.fn.position;t.position={scrollbarWidth:function(){if(void 0!==n)return n;var e,i,s=t("
"),o=s.children()[0];return t("body").append(s),e=o.offsetWidth,s.css("overflow","scroll"),i=o.offsetWidth,e===i&&(i=s[0].clientWidth),s.remove(),n=e-i},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.widthi?"left":e>0?"right":"center",vertical:0>r?"top":s>0?"bottom":"middle"};l>p&&p>a(e+i)&&(u.horizontal="center"),c>f&&f>a(s+r)&&(u.vertical="middle"),u.important=o(a(e),a(i))>o(a(s),a(r))?"horizontal":"vertical",n.using.call(this,t,u)}),h.offset(t.extend(D,{using:r}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=t.left-e.collisionPosition.marginLeft,h=n-r,l=r+e.collisionWidth-a-n;e.collisionWidth>a?h>0&&0>=l?(i=t.left+h+e.collisionWidth-a-n,t.left+=h-i):t.left=l>0&&0>=h?n:h>l?n+a-e.collisionWidth:n:h>0?t.left+=h:l>0?t.left-=l:t.left=o(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,a=e.within.height,r=t.top-e.collisionPosition.marginTop,h=n-r,l=r+e.collisionHeight-a-n;e.collisionHeight>a?h>0&&0>=l?(i=t.top+h+e.collisionHeight-a-n,t.top+=h-i):t.top=l>0&&0>=h?n:h>l?n+a-e.collisionHeight:n:h>0?t.top+=h:l>0?t.top-=l:t.top=o(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,o=n.offset.left+n.scrollLeft,r=n.width,h=n.isWindow?n.scrollLeft:n.offset.left,l=t.left-e.collisionPosition.marginLeft,c=l-h,u=l+e.collisionWidth-r-h,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-r-o,(0>i||a(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-h,(s>0||u>a(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,o=n.offset.top+n.scrollTop,r=n.height,h=n.isWindow?n.scrollTop:n.offset.top,l=t.top-e.collisionPosition.marginTop,c=l-h,u=l+e.collisionHeight-r-h,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,g=-2*e.offset[1];0>c?(s=t.top+p+f+g+e.collisionHeight-r-o,(0>s||a(c)>s)&&(t.top+=p+f+g)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+g-h,(i>0||u>a(i))&&(t.top+=p+f+g))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}}}(),t.ui.position,t.extend(t.expr[":"],{data:t.expr.createPseudo?t.expr.createPseudo(function(e){return function(i){return!!t.data(i,e)}}):function(e,i,s){return!!t.data(e,s[3])}}),t.fn.extend({disableSelection:function(){var t="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.on(t+".ui-disableSelection",function(t){t.preventDefault()})}}(),enableSelection:function(){return this.off(".ui-disableSelection")}});var c="ui-effects-",u="ui-effects-style",d="ui-effects-animated",p=t;t.effects={effect:{}},function(t,e){function i(t,e,i){var s=u[e.type]||{};return null==t?i||!e.def?null:e.def:(t=s.floor?~~t:parseFloat(t),isNaN(t)?e.def:s.mod?(t+s.mod)%s.mod:0>t?0:t>s.max?s.max:t)}function s(i){var s=l(),n=s._rgba=[];return i=i.toLowerCase(),f(h,function(t,o){var a,r=o.re.exec(i),h=r&&o.parse(r),l=o.space||"rgba";return h?(a=s[l](h),s[c[l].cache]=a[c[l].cache],n=s._rgba=a._rgba,!1):e}),n.length?("0,0,0,0"===n.join()&&t.extend(n,o.transparent),s):o[i]}function n(t,e,i){return i=(i+1)%1,1>6*i?t+6*(e-t)*i:1>2*i?e:2>3*i?t+6*(e-t)*(2/3-i):t}var o,a="backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",r=/^([\-+])=\s*(\d+\.?\d*)/,h=[{re:/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(t){return[t[1],t[2],t[3],t[4]]}},{re:/rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(t){return[2.55*t[1],2.55*t[2],2.55*t[3],t[4]]}},{re:/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,parse:function(t){return[parseInt(t[1],16),parseInt(t[2],16),parseInt(t[3],16)]}},{re:/#([a-f0-9])([a-f0-9])([a-f0-9])/,parse:function(t){return[parseInt(t[1]+t[1],16),parseInt(t[2]+t[2],16),parseInt(t[3]+t[3],16)]}},{re:/hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,space:"hsla",parse:function(t){return[t[1],t[2]/100,t[3]/100,t[4]]}}],l=t.Color=function(e,i,s,n){return new t.Color.fn.parse(e,i,s,n)},c={rgba:{props:{red:{idx:0,type:"byte"},green:{idx:1,type:"byte"},blue:{idx:2,type:"byte"}}},hsla:{props:{hue:{idx:0,type:"degrees"},saturation:{idx:1,type:"percent"},lightness:{idx:2,type:"percent"}}}},u={"byte":{floor:!0,max:255},percent:{max:1},degrees:{mod:360,floor:!0}},d=l.support={},p=t("

")[0],f=t.each;p.style.cssText="background-color:rgba(1,1,1,.5)",d.rgba=p.style.backgroundColor.indexOf("rgba")>-1,f(c,function(t,e){e.cache="_"+t,e.props.alpha={idx:3,type:"percent",def:1}}),l.fn=t.extend(l.prototype,{parse:function(n,a,r,h){if(n===e)return this._rgba=[null,null,null,null],this;(n.jquery||n.nodeType)&&(n=t(n).css(a),a=e);var u=this,d=t.type(n),p=this._rgba=[];return a!==e&&(n=[n,a,r,h],d="array"),"string"===d?this.parse(s(n)||o._default):"array"===d?(f(c.rgba.props,function(t,e){p[e.idx]=i(n[e.idx],e)}),this):"object"===d?(n instanceof l?f(c,function(t,e){n[e.cache]&&(u[e.cache]=n[e.cache].slice())}):f(c,function(e,s){var o=s.cache;f(s.props,function(t,e){if(!u[o]&&s.to){if("alpha"===t||null==n[t])return;u[o]=s.to(u._rgba)}u[o][e.idx]=i(n[t],e,!0)}),u[o]&&0>t.inArray(null,u[o].slice(0,3))&&(u[o][3]=1,s.from&&(u._rgba=s.from(u[o])))}),this):e},is:function(t){var i=l(t),s=!0,n=this;return f(c,function(t,o){var a,r=i[o.cache];return r&&(a=n[o.cache]||o.to&&o.to(n._rgba)||[],f(o.props,function(t,i){return null!=r[i.idx]?s=r[i.idx]===a[i.idx]:e})),s}),s},_space:function(){var t=[],e=this;return f(c,function(i,s){e[s.cache]&&t.push(i)}),t.pop()},transition:function(t,e){var s=l(t),n=s._space(),o=c[n],a=0===this.alpha()?l("transparent"):this,r=a[o.cache]||o.to(a._rgba),h=r.slice();return s=s[o.cache],f(o.props,function(t,n){var o=n.idx,a=r[o],l=s[o],c=u[n.type]||{};null!==l&&(null===a?h[o]=l:(c.mod&&(l-a>c.mod/2?a+=c.mod:a-l>c.mod/2&&(a-=c.mod)),h[o]=i((l-a)*e+a,n)))}),this[n](h)},blend:function(e){if(1===this._rgba[3])return this;var i=this._rgba.slice(),s=i.pop(),n=l(e)._rgba;return l(t.map(i,function(t,e){return(1-s)*n[e]+s*t}))},toRgbaString:function(){var e="rgba(",i=t.map(this._rgba,function(t,e){return null==t?e>2?1:0:t});return 1===i[3]&&(i.pop(),e="rgb("),e+i.join()+")"},toHslaString:function(){var e="hsla(",i=t.map(this.hsla(),function(t,e){return null==t&&(t=e>2?1:0),e&&3>e&&(t=Math.round(100*t)+"%"),t});return 1===i[3]&&(i.pop(),e="hsl("),e+i.join()+")"},toHexString:function(e){var i=this._rgba.slice(),s=i.pop();return e&&i.push(~~(255*s)),"#"+t.map(i,function(t){return t=(t||0).toString(16),1===t.length?"0"+t:t}).join("")},toString:function(){return 0===this._rgba[3]?"transparent":this.toRgbaString()}}),l.fn.parse.prototype=l.fn,c.hsla.to=function(t){if(null==t[0]||null==t[1]||null==t[2])return[null,null,null,t[3]];var e,i,s=t[0]/255,n=t[1]/255,o=t[2]/255,a=t[3],r=Math.max(s,n,o),h=Math.min(s,n,o),l=r-h,c=r+h,u=.5*c;return e=h===r?0:s===r?60*(n-o)/l+360:n===r?60*(o-s)/l+120:60*(s-n)/l+240,i=0===l?0:.5>=u?l/c:l/(2-c),[Math.round(e)%360,i,u,null==a?1:a]},c.hsla.from=function(t){if(null==t[0]||null==t[1]||null==t[2])return[null,null,null,t[3]];var e=t[0]/360,i=t[1],s=t[2],o=t[3],a=.5>=s?s*(1+i):s+i-s*i,r=2*s-a;return[Math.round(255*n(r,a,e+1/3)),Math.round(255*n(r,a,e)),Math.round(255*n(r,a,e-1/3)),o]},f(c,function(s,n){var o=n.props,a=n.cache,h=n.to,c=n.from;l.fn[s]=function(s){if(h&&!this[a]&&(this[a]=h(this._rgba)),s===e)return this[a].slice();var n,r=t.type(s),u="array"===r||"object"===r?s:arguments,d=this[a].slice();return f(o,function(t,e){var s=u["object"===r?t:e.idx];null==s&&(s=d[e.idx]),d[e.idx]=i(s,e)}),c?(n=l(c(d)),n[a]=d,n):l(d)},f(o,function(e,i){l.fn[e]||(l.fn[e]=function(n){var o,a=t.type(n),h="alpha"===e?this._hsla?"hsla":"rgba":s,l=this[h](),c=l[i.idx];return"undefined"===a?c:("function"===a&&(n=n.call(this,c),a=t.type(n)),null==n&&i.empty?this:("string"===a&&(o=r.exec(n),o&&(n=c+parseFloat(o[2])*("+"===o[1]?1:-1))),l[i.idx]=n,this[h](l)))})})}),l.hook=function(e){var i=e.split(" ");f(i,function(e,i){t.cssHooks[i]={set:function(e,n){var o,a,r="";if("transparent"!==n&&("string"!==t.type(n)||(o=s(n)))){if(n=l(o||n),!d.rgba&&1!==n._rgba[3]){for(a="backgroundColor"===i?e.parentNode:e;(""===r||"transparent"===r)&&a&&a.style;)try{r=t.css(a,"backgroundColor"),a=a.parentNode}catch(h){}n=n.blend(r&&"transparent"!==r?r:"_default")}n=n.toRgbaString()}try{e.style[i]=n}catch(h){}}},t.fx.step[i]=function(e){e.colorInit||(e.start=l(e.elem,i),e.end=l(e.end),e.colorInit=!0),t.cssHooks[i].set(e.elem,e.start.transition(e.end,e.pos))}})},l.hook(a),t.cssHooks.borderColor={expand:function(t){var e={};return f(["Top","Right","Bottom","Left"],function(i,s){e["border"+s+"Color"]=t}),e}},o=t.Color.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00",transparent:[null,null,null,0],_default:"#ffffff"}}(p),function(){function e(e){var i,s,n=e.ownerDocument.defaultView?e.ownerDocument.defaultView.getComputedStyle(e,null):e.currentStyle,o={};if(n&&n.length&&n[0]&&n[n[0]])for(s=n.length;s--;)i=n[s],"string"==typeof n[i]&&(o[t.camelCase(i)]=n[i]);else for(i in n)"string"==typeof n[i]&&(o[i]=n[i]);return o}function i(e,i){var s,o,a={};for(s in i)o=i[s],e[s]!==o&&(n[s]||(t.fx.step[s]||!isNaN(parseFloat(o)))&&(a[s]=o));return a}var s=["add","remove","toggle"],n={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};t.each(["borderLeftStyle","borderRightStyle","borderBottomStyle","borderTopStyle"],function(e,i){t.fx.step[i]=function(t){("none"!==t.end&&!t.setAttr||1===t.pos&&!t.setAttr)&&(p.style(t.elem,i,t.end),t.setAttr=!0)}}),t.fn.addBack||(t.fn.addBack=function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}),t.effects.animateClass=function(n,o,a,r){var h=t.speed(o,a,r);return this.queue(function(){var o,a=t(this),r=a.attr("class")||"",l=h.children?a.find("*").addBack():a;l=l.map(function(){var i=t(this);return{el:i,start:e(this)}}),o=function(){t.each(s,function(t,e){n[e]&&a[e+"Class"](n[e])})},o(),l=l.map(function(){return this.end=e(this.el[0]),this.diff=i(this.start,this.end),this}),a.attr("class",r),l=l.map(function(){var e=this,i=t.Deferred(),s=t.extend({},h,{queue:!1,complete:function(){i.resolve(e)}});return this.el.animate(this.diff,s),i.promise()}),t.when.apply(t,l.get()).done(function(){o(),t.each(arguments,function(){var e=this.el;t.each(this.diff,function(t){e.css(t,"")})}),h.complete.call(a[0])})})},t.fn.extend({addClass:function(e){return function(i,s,n,o){return s?t.effects.animateClass.call(this,{add:i},s,n,o):e.apply(this,arguments)}}(t.fn.addClass),removeClass:function(e){return function(i,s,n,o){return arguments.length>1?t.effects.animateClass.call(this,{remove:i},s,n,o):e.apply(this,arguments)}}(t.fn.removeClass),toggleClass:function(e){return function(i,s,n,o,a){return"boolean"==typeof s||void 0===s?n?t.effects.animateClass.call(this,s?{add:i}:{remove:i},n,o,a):e.apply(this,arguments):t.effects.animateClass.call(this,{toggle:i},s,n,o)}}(t.fn.toggleClass),switchClass:function(e,i,s,n,o){return t.effects.animateClass.call(this,{add:i,remove:e},s,n,o)}})}(),function(){function e(e,i,s,n){return t.isPlainObject(e)&&(i=e,e=e.effect),e={effect:e},null==i&&(i={}),t.isFunction(i)&&(n=i,s=null,i={}),("number"==typeof i||t.fx.speeds[i])&&(n=s,s=i,i={}),t.isFunction(s)&&(n=s,s=null),i&&t.extend(e,i),s=s||i.duration,e.duration=t.fx.off?0:"number"==typeof s?s:s in t.fx.speeds?t.fx.speeds[s]:t.fx.speeds._default,e.complete=n||i.complete,e}function i(e){return!e||"number"==typeof e||t.fx.speeds[e]?!0:"string"!=typeof e||t.effects.effect[e]?t.isFunction(e)?!0:"object"!=typeof e||e.effect?!1:!0:!0}function s(t,e){var i=e.outerWidth(),s=e.outerHeight(),n=/^rect\((-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto)\)$/,o=n.exec(t)||["",0,i,s,0];return{top:parseFloat(o[1])||0,right:"auto"===o[2]?i:parseFloat(o[2]),bottom:"auto"===o[3]?s:parseFloat(o[3]),left:parseFloat(o[4])||0}}t.expr&&t.expr.filters&&t.expr.filters.animated&&(t.expr.filters.animated=function(e){return function(i){return!!t(i).data(d)||e(i)}}(t.expr.filters.animated)),t.uiBackCompat!==!1&&t.extend(t.effects,{save:function(t,e){for(var i=0,s=e.length;s>i;i++)null!==e[i]&&t.data(c+e[i],t[0].style[e[i]])},restore:function(t,e){for(var i,s=0,n=e.length;n>s;s++)null!==e[s]&&(i=t.data(c+e[s]),t.css(e[s],i))},setMode:function(t,e){return"toggle"===e&&(e=t.is(":hidden")?"show":"hide"),e},createWrapper:function(e){if(e.parent().is(".ui-effects-wrapper"))return e.parent();var i={width:e.outerWidth(!0),height:e.outerHeight(!0),"float":e.css("float")},s=t("

").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),n={width:e.width(),height:e.height()},o=document.activeElement;try{o.id}catch(a){o=document.body}return e.wrap(s),(e[0]===o||t.contains(e[0],o))&&t(o).trigger("focus"),s=e.parent(),"static"===e.css("position")?(s.css({position:"relative"}),e.css({position:"relative"})):(t.extend(i,{position:e.css("position"),zIndex:e.css("z-index")}),t.each(["top","left","bottom","right"],function(t,s){i[s]=e.css(s),isNaN(parseInt(i[s],10))&&(i[s]="auto")}),e.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),e.css(n),s.css(i).show()},removeWrapper:function(e){var i=document.activeElement;return e.parent().is(".ui-effects-wrapper")&&(e.parent().replaceWith(e),(e[0]===i||t.contains(e[0],i))&&t(i).trigger("focus")),e}}),t.extend(t.effects,{version:"1.12.1",define:function(e,i,s){return s||(s=i,i="effect"),t.effects.effect[e]=s,t.effects.effect[e].mode=i,s},scaledDimensions:function(t,e,i){if(0===e)return{height:0,width:0,outerHeight:0,outerWidth:0};var s="horizontal"!==i?(e||100)/100:1,n="vertical"!==i?(e||100)/100:1;return{height:t.height()*n,width:t.width()*s,outerHeight:t.outerHeight()*n,outerWidth:t.outerWidth()*s}},clipToBox:function(t){return{width:t.clip.right-t.clip.left,height:t.clip.bottom-t.clip.top,left:t.clip.left,top:t.clip.top}},unshift:function(t,e,i){var s=t.queue();e>1&&s.splice.apply(s,[1,0].concat(s.splice(e,i))),t.dequeue()},saveStyle:function(t){t.data(u,t[0].style.cssText)},restoreStyle:function(t){t[0].style.cssText=t.data(u)||"",t.removeData(u)},mode:function(t,e){var i=t.is(":hidden");return"toggle"===e&&(e=i?"show":"hide"),(i?"hide"===e:"show"===e)&&(e="none"),e},getBaseline:function(t,e){var i,s;switch(t[0]){case"top":i=0;break;case"middle":i=.5;break;case"bottom":i=1;break;default:i=t[0]/e.height}switch(t[1]){case"left":s=0;break;case"center":s=.5;break;case"right":s=1;break;default:s=t[1]/e.width}return{x:s,y:i}},createPlaceholder:function(e){var i,s=e.css("position"),n=e.position();return e.css({marginTop:e.css("marginTop"),marginBottom:e.css("marginBottom"),marginLeft:e.css("marginLeft"),marginRight:e.css("marginRight")}).outerWidth(e.outerWidth()).outerHeight(e.outerHeight()),/^(static|relative)/.test(s)&&(s="absolute",i=t("<"+e[0].nodeName+">").insertAfter(e).css({display:/^(inline|ruby)/.test(e.css("display"))?"inline-block":"block",visibility:"hidden",marginTop:e.css("marginTop"),marginBottom:e.css("marginBottom"),marginLeft:e.css("marginLeft"),marginRight:e.css("marginRight"),"float":e.css("float")}).outerWidth(e.outerWidth()).outerHeight(e.outerHeight()).addClass("ui-effects-placeholder"),e.data(c+"placeholder",i)),e.css({position:s,left:n.left,top:n.top}),i},removePlaceholder:function(t){var e=c+"placeholder",i=t.data(e);i&&(i.remove(),t.removeData(e))},cleanUp:function(e){t.effects.restoreStyle(e),t.effects.removePlaceholder(e)},setTransition:function(e,i,s,n){return n=n||{},t.each(i,function(t,i){var o=e.cssUnit(i);o[0]>0&&(n[i]=o[0]*s+o[1])}),n}}),t.fn.extend({effect:function(){function i(e){function i(){r.removeData(d),t.effects.cleanUp(r),"hide"===s.mode&&r.hide(),a()}function a(){t.isFunction(h)&&h.call(r[0]),t.isFunction(e)&&e()}var r=t(this);s.mode=c.shift(),t.uiBackCompat===!1||o?"none"===s.mode?(r[l](),a()):n.call(r[0],s,i):(r.is(":hidden")?"hide"===l:"show"===l)?(r[l](),a()):n.call(r[0],s,a)}var s=e.apply(this,arguments),n=t.effects.effect[s.effect],o=n.mode,a=s.queue,r=a||"fx",h=s.complete,l=s.mode,c=[],u=function(e){var i=t(this),s=t.effects.mode(i,l)||o;i.data(d,!0),c.push(s),o&&("show"===s||s===o&&"hide"===s)&&i.show(),o&&"none"===s||t.effects.saveStyle(i),t.isFunction(e)&&e()};return t.fx.off||!n?l?this[l](s.duration,h):this.each(function(){h&&h.call(this)}):a===!1?this.each(u).each(i):this.queue(r,u).queue(r,i)},show:function(t){return function(s){if(i(s))return t.apply(this,arguments);var n=e.apply(this,arguments);return n.mode="show",this.effect.call(this,n) -}}(t.fn.show),hide:function(t){return function(s){if(i(s))return t.apply(this,arguments);var n=e.apply(this,arguments);return n.mode="hide",this.effect.call(this,n)}}(t.fn.hide),toggle:function(t){return function(s){if(i(s)||"boolean"==typeof s)return t.apply(this,arguments);var n=e.apply(this,arguments);return n.mode="toggle",this.effect.call(this,n)}}(t.fn.toggle),cssUnit:function(e){var i=this.css(e),s=[];return t.each(["em","px","%","pt"],function(t,e){i.indexOf(e)>0&&(s=[parseFloat(i),e])}),s},cssClip:function(t){return t?this.css("clip","rect("+t.top+"px "+t.right+"px "+t.bottom+"px "+t.left+"px)"):s(this.css("clip"),this)},transfer:function(e,i){var s=t(this),n=t(e.to),o="fixed"===n.css("position"),a=t("body"),r=o?a.scrollTop():0,h=o?a.scrollLeft():0,l=n.offset(),c={top:l.top-r,left:l.left-h,height:n.innerHeight(),width:n.innerWidth()},u=s.offset(),d=t("
").appendTo("body").addClass(e.className).css({top:u.top-r,left:u.left-h,height:s.innerHeight(),width:s.innerWidth(),position:o?"fixed":"absolute"}).animate(c,e.duration,e.easing,function(){d.remove(),t.isFunction(i)&&i()})}}),t.fx.step.clip=function(e){e.clipInit||(e.start=t(e.elem).cssClip(),"string"==typeof e.end&&(e.end=s(e.end,e.elem)),e.clipInit=!0),t(e.elem).cssClip({top:e.pos*(e.end.top-e.start.top)+e.start.top,right:e.pos*(e.end.right-e.start.right)+e.start.right,bottom:e.pos*(e.end.bottom-e.start.bottom)+e.start.bottom,left:e.pos*(e.end.left-e.start.left)+e.start.left})}}(),function(){var e={};t.each(["Quad","Cubic","Quart","Quint","Expo"],function(t,i){e[i]=function(e){return Math.pow(e,t+2)}}),t.extend(e,{Sine:function(t){return 1-Math.cos(t*Math.PI/2)},Circ:function(t){return 1-Math.sqrt(1-t*t)},Elastic:function(t){return 0===t||1===t?t:-Math.pow(2,8*(t-1))*Math.sin((80*(t-1)-7.5)*Math.PI/15)},Back:function(t){return t*t*(3*t-2)},Bounce:function(t){for(var e,i=4;((e=Math.pow(2,--i))-1)/11>t;);return 1/Math.pow(4,3-i)-7.5625*Math.pow((3*e-2)/22-t,2)}}),t.each(e,function(e,i){t.easing["easeIn"+e]=i,t.easing["easeOut"+e]=function(t){return 1-i(1-t)},t.easing["easeInOut"+e]=function(t){return.5>t?i(2*t)/2:1-i(-2*t+2)/2}})}();var f=t.effects;t.effects.define("blind","hide",function(e,i){var s={up:["bottom","top"],vertical:["bottom","top"],down:["top","bottom"],left:["right","left"],horizontal:["right","left"],right:["left","right"]},n=t(this),o=e.direction||"up",a=n.cssClip(),r={clip:t.extend({},a)},h=t.effects.createPlaceholder(n);r.clip[s[o][0]]=r.clip[s[o][1]],"show"===e.mode&&(n.cssClip(r.clip),h&&h.css(t.effects.clipToBox(r)),r.clip=a),h&&h.animate(t.effects.clipToBox(r),e.duration,e.easing),n.animate(r,{queue:!1,duration:e.duration,easing:e.easing,complete:i})}),t.effects.define("bounce",function(e,i){var s,n,o,a=t(this),r=e.mode,h="hide"===r,l="show"===r,c=e.direction||"up",u=e.distance,d=e.times||5,p=2*d+(l||h?1:0),f=e.duration/p,g=e.easing,m="up"===c||"down"===c?"top":"left",_="up"===c||"left"===c,v=0,b=a.queue().length;for(t.effects.createPlaceholder(a),o=a.css(m),u||(u=a["top"===m?"outerHeight":"outerWidth"]()/3),l&&(n={opacity:1},n[m]=o,a.css("opacity",0).css(m,_?2*-u:2*u).animate(n,f,g)),h&&(u/=Math.pow(2,d-1)),n={},n[m]=o;d>v;v++)s={},s[m]=(_?"-=":"+=")+u,a.animate(s,f,g).animate(n,f,g),u=h?2*u:u/2;h&&(s={opacity:0},s[m]=(_?"-=":"+=")+u,a.animate(s,f,g)),a.queue(i),t.effects.unshift(a,b,p+1)}),t.effects.define("clip","hide",function(e,i){var s,n={},o=t(this),a=e.direction||"vertical",r="both"===a,h=r||"horizontal"===a,l=r||"vertical"===a;s=o.cssClip(),n.clip={top:l?(s.bottom-s.top)/2:s.top,right:h?(s.right-s.left)/2:s.right,bottom:l?(s.bottom-s.top)/2:s.bottom,left:h?(s.right-s.left)/2:s.left},t.effects.createPlaceholder(o),"show"===e.mode&&(o.cssClip(n.clip),n.clip=s),o.animate(n,{queue:!1,duration:e.duration,easing:e.easing,complete:i})}),t.effects.define("drop","hide",function(e,i){var s,n=t(this),o=e.mode,a="show"===o,r=e.direction||"left",h="up"===r||"down"===r?"top":"left",l="up"===r||"left"===r?"-=":"+=",c="+="===l?"-=":"+=",u={opacity:0};t.effects.createPlaceholder(n),s=e.distance||n["top"===h?"outerHeight":"outerWidth"](!0)/2,u[h]=l+s,a&&(n.css(u),u[h]=c+s,u.opacity=1),n.animate(u,{queue:!1,duration:e.duration,easing:e.easing,complete:i})}),t.effects.define("explode","hide",function(e,i){function s(){b.push(this),b.length===u*d&&n()}function n(){p.css({visibility:"visible"}),t(b).remove(),i()}var o,a,r,h,l,c,u=e.pieces?Math.round(Math.sqrt(e.pieces)):3,d=u,p=t(this),f=e.mode,g="show"===f,m=p.show().css("visibility","hidden").offset(),_=Math.ceil(p.outerWidth()/d),v=Math.ceil(p.outerHeight()/u),b=[];for(o=0;u>o;o++)for(h=m.top+o*v,c=o-(u-1)/2,a=0;d>a;a++)r=m.left+a*_,l=a-(d-1)/2,p.clone().appendTo("body").wrap("
").css({position:"absolute",visibility:"visible",left:-a*_,top:-o*v}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:_,height:v,left:r+(g?l*_:0),top:h+(g?c*v:0),opacity:g?0:1}).animate({left:r+(g?0:l*_),top:h+(g?0:c*v),opacity:g?1:0},e.duration||500,e.easing,s)}),t.effects.define("fade","toggle",function(e,i){var s="show"===e.mode;t(this).css("opacity",s?0:1).animate({opacity:s?1:0},{queue:!1,duration:e.duration,easing:e.easing,complete:i})}),t.effects.define("fold","hide",function(e,i){var s=t(this),n=e.mode,o="show"===n,a="hide"===n,r=e.size||15,h=/([0-9]+)%/.exec(r),l=!!e.horizFirst,c=l?["right","bottom"]:["bottom","right"],u=e.duration/2,d=t.effects.createPlaceholder(s),p=s.cssClip(),f={clip:t.extend({},p)},g={clip:t.extend({},p)},m=[p[c[0]],p[c[1]]],_=s.queue().length;h&&(r=parseInt(h[1],10)/100*m[a?0:1]),f.clip[c[0]]=r,g.clip[c[0]]=r,g.clip[c[1]]=0,o&&(s.cssClip(g.clip),d&&d.css(t.effects.clipToBox(g)),g.clip=p),s.queue(function(i){d&&d.animate(t.effects.clipToBox(f),u,e.easing).animate(t.effects.clipToBox(g),u,e.easing),i()}).animate(f,u,e.easing).animate(g,u,e.easing).queue(i),t.effects.unshift(s,_,4)}),t.effects.define("highlight","show",function(e,i){var s=t(this),n={backgroundColor:s.css("backgroundColor")};"hide"===e.mode&&(n.opacity=0),t.effects.saveStyle(s),s.css({backgroundImage:"none",backgroundColor:e.color||"#ffff99"}).animate(n,{queue:!1,duration:e.duration,easing:e.easing,complete:i})}),t.effects.define("size",function(e,i){var s,n,o,a=t(this),r=["fontSize"],h=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],l=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],c=e.mode,u="effect"!==c,d=e.scale||"both",p=e.origin||["middle","center"],f=a.css("position"),g=a.position(),m=t.effects.scaledDimensions(a),_=e.from||m,v=e.to||t.effects.scaledDimensions(a,0);t.effects.createPlaceholder(a),"show"===c&&(o=_,_=v,v=o),n={from:{y:_.height/m.height,x:_.width/m.width},to:{y:v.height/m.height,x:v.width/m.width}},("box"===d||"both"===d)&&(n.from.y!==n.to.y&&(_=t.effects.setTransition(a,h,n.from.y,_),v=t.effects.setTransition(a,h,n.to.y,v)),n.from.x!==n.to.x&&(_=t.effects.setTransition(a,l,n.from.x,_),v=t.effects.setTransition(a,l,n.to.x,v))),("content"===d||"both"===d)&&n.from.y!==n.to.y&&(_=t.effects.setTransition(a,r,n.from.y,_),v=t.effects.setTransition(a,r,n.to.y,v)),p&&(s=t.effects.getBaseline(p,m),_.top=(m.outerHeight-_.outerHeight)*s.y+g.top,_.left=(m.outerWidth-_.outerWidth)*s.x+g.left,v.top=(m.outerHeight-v.outerHeight)*s.y+g.top,v.left=(m.outerWidth-v.outerWidth)*s.x+g.left),a.css(_),("content"===d||"both"===d)&&(h=h.concat(["marginTop","marginBottom"]).concat(r),l=l.concat(["marginLeft","marginRight"]),a.find("*[width]").each(function(){var i=t(this),s=t.effects.scaledDimensions(i),o={height:s.height*n.from.y,width:s.width*n.from.x,outerHeight:s.outerHeight*n.from.y,outerWidth:s.outerWidth*n.from.x},a={height:s.height*n.to.y,width:s.width*n.to.x,outerHeight:s.height*n.to.y,outerWidth:s.width*n.to.x};n.from.y!==n.to.y&&(o=t.effects.setTransition(i,h,n.from.y,o),a=t.effects.setTransition(i,h,n.to.y,a)),n.from.x!==n.to.x&&(o=t.effects.setTransition(i,l,n.from.x,o),a=t.effects.setTransition(i,l,n.to.x,a)),u&&t.effects.saveStyle(i),i.css(o),i.animate(a,e.duration,e.easing,function(){u&&t.effects.restoreStyle(i)})})),a.animate(v,{queue:!1,duration:e.duration,easing:e.easing,complete:function(){var e=a.offset();0===v.opacity&&a.css("opacity",_.opacity),u||(a.css("position","static"===f?"relative":f).offset(e),t.effects.saveStyle(a)),i()}})}),t.effects.define("scale",function(e,i){var s=t(this),n=e.mode,o=parseInt(e.percent,10)||(0===parseInt(e.percent,10)?0:"effect"!==n?0:100),a=t.extend(!0,{from:t.effects.scaledDimensions(s),to:t.effects.scaledDimensions(s,o,e.direction||"both"),origin:e.origin||["middle","center"]},e);e.fade&&(a.from.opacity=1,a.to.opacity=0),t.effects.effect.size.call(this,a,i)}),t.effects.define("puff","hide",function(e,i){var s=t.extend(!0,{},e,{fade:!0,percent:parseInt(e.percent,10)||150});t.effects.effect.scale.call(this,s,i)}),t.effects.define("pulsate","show",function(e,i){var s=t(this),n=e.mode,o="show"===n,a="hide"===n,r=o||a,h=2*(e.times||5)+(r?1:0),l=e.duration/h,c=0,u=1,d=s.queue().length;for((o||!s.is(":visible"))&&(s.css("opacity",0).show(),c=1);h>u;u++)s.animate({opacity:c},l,e.easing),c=1-c;s.animate({opacity:c},l,e.easing),s.queue(i),t.effects.unshift(s,d,h+1)}),t.effects.define("shake",function(e,i){var s=1,n=t(this),o=e.direction||"left",a=e.distance||20,r=e.times||3,h=2*r+1,l=Math.round(e.duration/h),c="up"===o||"down"===o?"top":"left",u="up"===o||"left"===o,d={},p={},f={},g=n.queue().length;for(t.effects.createPlaceholder(n),d[c]=(u?"-=":"+=")+a,p[c]=(u?"+=":"-=")+2*a,f[c]=(u?"-=":"+=")+2*a,n.animate(d,l,e.easing);r>s;s++)n.animate(p,l,e.easing).animate(f,l,e.easing);n.animate(p,l,e.easing).animate(d,l/2,e.easing).queue(i),t.effects.unshift(n,g,h+1)}),t.effects.define("slide","show",function(e,i){var s,n,o=t(this),a={up:["bottom","top"],down:["top","bottom"],left:["right","left"],right:["left","right"]},r=e.mode,h=e.direction||"left",l="up"===h||"down"===h?"top":"left",c="up"===h||"left"===h,u=e.distance||o["top"===l?"outerHeight":"outerWidth"](!0),d={};t.effects.createPlaceholder(o),s=o.cssClip(),n=o.position()[l],d[l]=(c?-1:1)*u+n,d.clip=o.cssClip(),d.clip[a[h][1]]=d.clip[a[h][0]],"show"===r&&(o.cssClip(d.clip),o.css(l,d[l]),d.clip=s,d[l]=n),o.animate(d,{queue:!1,duration:e.duration,easing:e.easing,complete:i})});var f;t.uiBackCompat!==!1&&(f=t.effects.define("transfer",function(e,i){t(this).transfer(e,i)})),t.ui.focusable=function(i,s){var n,o,a,r,h,l=i.nodeName.toLowerCase();return"area"===l?(n=i.parentNode,o=n.name,i.href&&o&&"map"===n.nodeName.toLowerCase()?(a=t("img[usemap='#"+o+"']"),a.length>0&&a.is(":visible")):!1):(/^(input|select|textarea|button|object)$/.test(l)?(r=!i.disabled,r&&(h=t(i).closest("fieldset")[0],h&&(r=!h.disabled))):r="a"===l?i.href||s:s,r&&t(i).is(":visible")&&e(t(i)))},t.extend(t.expr[":"],{focusable:function(e){return t.ui.focusable(e,null!=t.attr(e,"tabindex"))}}),t.ui.focusable,t.fn.form=function(){return"string"==typeof this[0].form?this.closest("form"):t(this[0].form)},t.ui.formResetMixin={_formResetHandler:function(){var e=t(this);setTimeout(function(){var i=e.data("ui-form-reset-instances");t.each(i,function(){this.refresh()})})},_bindFormResetHandler:function(){if(this.form=this.element.form(),this.form.length){var t=this.form.data("ui-form-reset-instances")||[];t.length||this.form.on("reset.ui-form-reset",this._formResetHandler),t.push(this),this.form.data("ui-form-reset-instances",t)}},_unbindFormResetHandler:function(){if(this.form.length){var e=this.form.data("ui-form-reset-instances");e.splice(t.inArray(this,e),1),e.length?this.form.data("ui-form-reset-instances",e):this.form.removeData("ui-form-reset-instances").off("reset.ui-form-reset")}}},"1.7"===t.fn.jquery.substring(0,3)&&(t.each(["Width","Height"],function(e,i){function s(e,i,s,o){return t.each(n,function(){i-=parseFloat(t.css(e,"padding"+this))||0,s&&(i-=parseFloat(t.css(e,"border"+this+"Width"))||0),o&&(i-=parseFloat(t.css(e,"margin"+this))||0)}),i}var n="Width"===i?["Left","Right"]:["Top","Bottom"],o=i.toLowerCase(),a={innerWidth:t.fn.innerWidth,innerHeight:t.fn.innerHeight,outerWidth:t.fn.outerWidth,outerHeight:t.fn.outerHeight};t.fn["inner"+i]=function(e){return void 0===e?a["inner"+i].call(this):this.each(function(){t(this).css(o,s(this,e)+"px")})},t.fn["outer"+i]=function(e,n){return"number"!=typeof e?a["outer"+i].call(this,e):this.each(function(){t(this).css(o,s(this,e,!0,n)+"px")})}}),t.fn.addBack=function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}),t.ui.keyCode={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38},t.ui.escapeSelector=function(){var t=/([!"#$%&'()*+,.\/:;<=>?@[\]^`{|}~])/g;return function(e){return e.replace(t,"\\$1")}}(),t.fn.labels=function(){var e,i,s,n,o;return this[0].labels&&this[0].labels.length?this.pushStack(this[0].labels):(n=this.eq(0).parents("label"),s=this.attr("id"),s&&(e=this.eq(0).parents().last(),o=e.add(e.length?e.siblings():this.siblings()),i="label[for='"+t.ui.escapeSelector(s)+"']",n=n.add(o.find(i).addBack(i))),this.pushStack(n))},t.fn.scrollParent=function(e){var i=this.css("position"),s="absolute"===i,n=e?/(auto|scroll|hidden)/:/(auto|scroll)/,o=this.parents().filter(function(){var e=t(this);return s&&"static"===e.css("position")?!1:n.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==i&&o.length?o:t(this[0].ownerDocument||document)},t.extend(t.expr[":"],{tabbable:function(e){var i=t.attr(e,"tabindex"),s=null!=i;return(!s||i>=0)&&t.ui.focusable(e,s)}}),t.fn.extend({uniqueId:function(){var t=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++t)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&t(this).removeAttr("id")})}}),t.widget("ui.accordion",{version:"1.12.1",options:{active:0,animate:{},classes:{"ui-accordion-header":"ui-corner-top","ui-accordion-header-collapsed":"ui-corner-all","ui-accordion-content":"ui-corner-bottom"},collapsible:!1,event:"click",header:"> li > :first-child, > :not(li):even",heightStyle:"auto",icons:{activeHeader:"ui-icon-triangle-1-s",header:"ui-icon-triangle-1-e"},activate:null,beforeActivate:null},hideProps:{borderTopWidth:"hide",borderBottomWidth:"hide",paddingTop:"hide",paddingBottom:"hide",height:"hide"},showProps:{borderTopWidth:"show",borderBottomWidth:"show",paddingTop:"show",paddingBottom:"show",height:"show"},_create:function(){var e=this.options;this.prevShow=this.prevHide=t(),this._addClass("ui-accordion","ui-widget ui-helper-reset"),this.element.attr("role","tablist"),e.collapsible||e.active!==!1&&null!=e.active||(e.active=0),this._processPanels(),0>e.active&&(e.active+=this.headers.length),this._refresh()},_getCreateEventData:function(){return{header:this.active,panel:this.active.length?this.active.next():t()}},_createIcons:function(){var e,i,s=this.options.icons;s&&(e=t(""),this._addClass(e,"ui-accordion-header-icon","ui-icon "+s.header),e.prependTo(this.headers),i=this.active.children(".ui-accordion-header-icon"),this._removeClass(i,s.header)._addClass(i,null,s.activeHeader)._addClass(this.headers,"ui-accordion-icons"))},_destroyIcons:function(){this._removeClass(this.headers,"ui-accordion-icons"),this.headers.children(".ui-accordion-header-icon").remove()},_destroy:function(){var t;this.element.removeAttr("role"),this.headers.removeAttr("role aria-expanded aria-selected aria-controls tabIndex").removeUniqueId(),this._destroyIcons(),t=this.headers.next().css("display","").removeAttr("role aria-hidden aria-labelledby").removeUniqueId(),"content"!==this.options.heightStyle&&t.css("height","")},_setOption:function(t,e){return"active"===t?(this._activate(e),void 0):("event"===t&&(this.options.event&&this._off(this.headers,this.options.event),this._setupEvents(e)),this._super(t,e),"collapsible"!==t||e||this.options.active!==!1||this._activate(0),"icons"===t&&(this._destroyIcons(),e&&this._createIcons()),void 0)},_setOptionDisabled:function(t){this._super(t),this.element.attr("aria-disabled",t),this._toggleClass(null,"ui-state-disabled",!!t),this._toggleClass(this.headers.add(this.headers.next()),null,"ui-state-disabled",!!t)},_keydown:function(e){if(!e.altKey&&!e.ctrlKey){var i=t.ui.keyCode,s=this.headers.length,n=this.headers.index(e.target),o=!1;switch(e.keyCode){case i.RIGHT:case i.DOWN:o=this.headers[(n+1)%s];break;case i.LEFT:case i.UP:o=this.headers[(n-1+s)%s];break;case i.SPACE:case i.ENTER:this._eventHandler(e);break;case i.HOME:o=this.headers[0];break;case i.END:o=this.headers[s-1]}o&&(t(e.target).attr("tabIndex",-1),t(o).attr("tabIndex",0),t(o).trigger("focus"),e.preventDefault())}},_panelKeyDown:function(e){e.keyCode===t.ui.keyCode.UP&&e.ctrlKey&&t(e.currentTarget).prev().trigger("focus")},refresh:function(){var e=this.options;this._processPanels(),e.active===!1&&e.collapsible===!0||!this.headers.length?(e.active=!1,this.active=t()):e.active===!1?this._activate(0):this.active.length&&!t.contains(this.element[0],this.active[0])?this.headers.length===this.headers.find(".ui-state-disabled").length?(e.active=!1,this.active=t()):this._activate(Math.max(0,e.active-1)):e.active=this.headers.index(this.active),this._destroyIcons(),this._refresh()},_processPanels:function(){var t=this.headers,e=this.panels;this.headers=this.element.find(this.options.header),this._addClass(this.headers,"ui-accordion-header ui-accordion-header-collapsed","ui-state-default"),this.panels=this.headers.next().filter(":not(.ui-accordion-content-active)").hide(),this._addClass(this.panels,"ui-accordion-content","ui-helper-reset ui-widget-content"),e&&(this._off(t.not(this.headers)),this._off(e.not(this.panels)))},_refresh:function(){var e,i=this.options,s=i.heightStyle,n=this.element.parent();this.active=this._findActive(i.active),this._addClass(this.active,"ui-accordion-header-active","ui-state-active")._removeClass(this.active,"ui-accordion-header-collapsed"),this._addClass(this.active.next(),"ui-accordion-content-active"),this.active.next().show(),this.headers.attr("role","tab").each(function(){var e=t(this),i=e.uniqueId().attr("id"),s=e.next(),n=s.uniqueId().attr("id");e.attr("aria-controls",n),s.attr("aria-labelledby",i)}).next().attr("role","tabpanel"),this.headers.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}).next().attr({"aria-hidden":"true"}).hide(),this.active.length?this.active.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}).next().attr({"aria-hidden":"false"}):this.headers.eq(0).attr("tabIndex",0),this._createIcons(),this._setupEvents(i.event),"fill"===s?(e=n.height(),this.element.siblings(":visible").each(function(){var i=t(this),s=i.css("position");"absolute"!==s&&"fixed"!==s&&(e-=i.outerHeight(!0))}),this.headers.each(function(){e-=t(this).outerHeight(!0)}),this.headers.next().each(function(){t(this).height(Math.max(0,e-t(this).innerHeight()+t(this).height()))}).css("overflow","auto")):"auto"===s&&(e=0,this.headers.next().each(function(){var i=t(this).is(":visible");i||t(this).show(),e=Math.max(e,t(this).css("height","").height()),i||t(this).hide()}).height(e))},_activate:function(e){var i=this._findActive(e)[0];i!==this.active[0]&&(i=i||this.active[0],this._eventHandler({target:i,currentTarget:i,preventDefault:t.noop}))},_findActive:function(e){return"number"==typeof e?this.headers.eq(e):t()},_setupEvents:function(e){var i={keydown:"_keydown"};e&&t.each(e.split(" "),function(t,e){i[e]="_eventHandler"}),this._off(this.headers.add(this.headers.next())),this._on(this.headers,i),this._on(this.headers.next(),{keydown:"_panelKeyDown"}),this._hoverable(this.headers),this._focusable(this.headers)},_eventHandler:function(e){var i,s,n=this.options,o=this.active,a=t(e.currentTarget),r=a[0]===o[0],h=r&&n.collapsible,l=h?t():a.next(),c=o.next(),u={oldHeader:o,oldPanel:c,newHeader:h?t():a,newPanel:l};e.preventDefault(),r&&!n.collapsible||this._trigger("beforeActivate",e,u)===!1||(n.active=h?!1:this.headers.index(a),this.active=r?t():a,this._toggle(u),this._removeClass(o,"ui-accordion-header-active","ui-state-active"),n.icons&&(i=o.children(".ui-accordion-header-icon"),this._removeClass(i,null,n.icons.activeHeader)._addClass(i,null,n.icons.header)),r||(this._removeClass(a,"ui-accordion-header-collapsed")._addClass(a,"ui-accordion-header-active","ui-state-active"),n.icons&&(s=a.children(".ui-accordion-header-icon"),this._removeClass(s,null,n.icons.header)._addClass(s,null,n.icons.activeHeader)),this._addClass(a.next(),"ui-accordion-content-active")))},_toggle:function(e){var i=e.newPanel,s=this.prevShow.length?this.prevShow:e.oldPanel;this.prevShow.add(this.prevHide).stop(!0,!0),this.prevShow=i,this.prevHide=s,this.options.animate?this._animate(i,s,e):(s.hide(),i.show(),this._toggleComplete(e)),s.attr({"aria-hidden":"true"}),s.prev().attr({"aria-selected":"false","aria-expanded":"false"}),i.length&&s.length?s.prev().attr({tabIndex:-1,"aria-expanded":"false"}):i.length&&this.headers.filter(function(){return 0===parseInt(t(this).attr("tabIndex"),10)}).attr("tabIndex",-1),i.attr("aria-hidden","false").prev().attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0})},_animate:function(t,e,i){var s,n,o,a=this,r=0,h=t.css("box-sizing"),l=t.length&&(!e.length||t.index()",delay:300,options:{icons:{submenu:"ui-icon-caret-1-e"},items:"> *",menus:"ul",position:{my:"left top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.element.uniqueId().attr({role:this.options.role,tabIndex:0}),this._addClass("ui-menu","ui-widget ui-widget-content"),this._on({"mousedown .ui-menu-item":function(t){t.preventDefault()},"click .ui-menu-item":function(e){var i=t(e.target),s=t(t.ui.safeActiveElement(this.document[0]));!this.mouseHandled&&i.not(".ui-state-disabled").length&&(this.select(e),e.isPropagationStopped()||(this.mouseHandled=!0),i.has(".ui-menu").length?this.expand(e):!this.element.is(":focus")&&s.closest(".ui-menu").length&&(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(e){if(!this.previousFilter){var i=t(e.target).closest(".ui-menu-item"),s=t(e.currentTarget);i[0]===s[0]&&(this._removeClass(s.siblings().children(".ui-state-active"),null,"ui-state-active"),this.focus(e,s))}},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(t,e){var i=this.active||this.element.find(this.options.items).eq(0);e||this.focus(t,i)},blur:function(e){this._delay(function(){var i=!t.contains(this.element[0],t.ui.safeActiveElement(this.document[0]));i&&this.collapseAll(e)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(t){this._closeOnDocumentClick(t)&&this.collapseAll(t),this.mouseHandled=!1}})},_destroy:function(){var e=this.element.find(".ui-menu-item").removeAttr("role aria-disabled"),i=e.children(".ui-menu-item-wrapper").removeUniqueId().removeAttr("tabIndex role aria-haspopup");this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeAttr("role aria-labelledby aria-expanded aria-hidden aria-disabled tabIndex").removeUniqueId().show(),i.children().each(function(){var e=t(this);e.data("ui-menu-submenu-caret")&&e.remove()})},_keydown:function(e){var i,s,n,o,a=!0;switch(e.keyCode){case t.ui.keyCode.PAGE_UP:this.previousPage(e);break;case t.ui.keyCode.PAGE_DOWN:this.nextPage(e);break;case t.ui.keyCode.HOME:this._move("first","first",e);break;case t.ui.keyCode.END:this._move("last","last",e);break;case t.ui.keyCode.UP:this.previous(e);break;case t.ui.keyCode.DOWN:this.next(e);break;case t.ui.keyCode.LEFT:this.collapse(e);break;case t.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(e);break;case t.ui.keyCode.ENTER:case t.ui.keyCode.SPACE:this._activate(e);break;case t.ui.keyCode.ESCAPE:this.collapse(e);break;default:a=!1,s=this.previousFilter||"",o=!1,n=e.keyCode>=96&&105>=e.keyCode?""+(e.keyCode-96):String.fromCharCode(e.keyCode),clearTimeout(this.filterTimer),n===s?o=!0:n=s+n,i=this._filterMenuItems(n),i=o&&-1!==i.index(this.active.next())?this.active.nextAll(".ui-menu-item"):i,i.length||(n=String.fromCharCode(e.keyCode),i=this._filterMenuItems(n)),i.length?(this.focus(e,i),this.previousFilter=n,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter}a&&e.preventDefault()},_activate:function(t){this.active&&!this.active.is(".ui-state-disabled")&&(this.active.children("[aria-haspopup='true']").length?this.expand(t):this.select(t))},refresh:function(){var e,i,s,n,o,a=this,r=this.options.icons.submenu,h=this.element.find(this.options.menus);this._toggleClass("ui-menu-icons",null,!!this.element.find(".ui-icon").length),s=h.filter(":not(.ui-menu)").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var e=t(this),i=e.prev(),s=t("").data("ui-menu-submenu-caret",!0);a._addClass(s,"ui-menu-icon","ui-icon "+r),i.attr("aria-haspopup","true").prepend(s),e.attr("aria-labelledby",i.attr("id"))}),this._addClass(s,"ui-menu","ui-widget ui-widget-content ui-front"),e=h.add(this.element),i=e.find(this.options.items),i.not(".ui-menu-item").each(function(){var e=t(this);a._isDivider(e)&&a._addClass(e,"ui-menu-divider","ui-widget-content")}),n=i.not(".ui-menu-item, .ui-menu-divider"),o=n.children().not(".ui-menu").uniqueId().attr({tabIndex:-1,role:this._itemRole()}),this._addClass(n,"ui-menu-item")._addClass(o,"ui-menu-item-wrapper"),i.filter(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!t.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(t,e){if("icons"===t){var i=this.element.find(".ui-menu-icon");this._removeClass(i,null,this.options.icons.submenu)._addClass(i,null,e.submenu)}this._super(t,e)},_setOptionDisabled:function(t){this._super(t),this.element.attr("aria-disabled",t+""),this._toggleClass(null,"ui-state-disabled",!!t)},focus:function(t,e){var i,s,n;this.blur(t,t&&"focus"===t.type),this._scrollIntoView(e),this.active=e.first(),s=this.active.children(".ui-menu-item-wrapper"),this._addClass(s,null,"ui-state-active"),this.options.role&&this.element.attr("aria-activedescendant",s.attr("id")),n=this.active.parent().closest(".ui-menu-item").children(".ui-menu-item-wrapper"),this._addClass(n,null,"ui-state-active"),t&&"keydown"===t.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),i=e.children(".ui-menu"),i.length&&t&&/^mouse/.test(t.type)&&this._startOpening(i),this.activeMenu=e.parent(),this._trigger("focus",t,{item:e})},_scrollIntoView:function(e){var i,s,n,o,a,r;this._hasScroll()&&(i=parseFloat(t.css(this.activeMenu[0],"borderTopWidth"))||0,s=parseFloat(t.css(this.activeMenu[0],"paddingTop"))||0,n=e.offset().top-this.activeMenu.offset().top-i-s,o=this.activeMenu.scrollTop(),a=this.activeMenu.height(),r=e.outerHeight(),0>n?this.activeMenu.scrollTop(o+n):n+r>a&&this.activeMenu.scrollTop(o+n-a+r))},blur:function(t,e){e||clearTimeout(this.timer),this.active&&(this._removeClass(this.active.children(".ui-menu-item-wrapper"),null,"ui-state-active"),this._trigger("blur",t,{item:this.active}),this.active=null)},_startOpening:function(t){clearTimeout(this.timer),"true"===t.attr("aria-hidden")&&(this.timer=this._delay(function(){this._close(),this._open(t)},this.delay))},_open:function(e){var i=t.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(e.parents(".ui-menu")).hide().attr("aria-hidden","true"),e.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(i)},collapseAll:function(e,i){clearTimeout(this.timer),this.timer=this._delay(function(){var s=i?this.element:t(e&&e.target).closest(this.element.find(".ui-menu"));s.length||(s=this.element),this._close(s),this.blur(e),this._removeClass(s.find(".ui-state-active"),null,"ui-state-active"),this.activeMenu=s},this.delay)},_close:function(t){t||(t=this.active?this.active.parent():this.element),t.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false")},_closeOnDocumentClick:function(e){return!t(e.target).closest(".ui-menu").length},_isDivider:function(t){return!/[^\-\u2014\u2013\s]/.test(t.text())},collapse:function(t){var e=this.active&&this.active.parent().closest(".ui-menu-item",this.element);e&&e.length&&(this._close(),this.focus(t,e))},expand:function(t){var e=this.active&&this.active.children(".ui-menu ").find(this.options.items).first();e&&e.length&&(this._open(e.parent()),this._delay(function(){this.focus(t,e)}))},next:function(t){this._move("next","first",t)},previous:function(t){this._move("prev","last",t)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(t,e,i){var s;this.active&&(s="first"===t||"last"===t?this.active["first"===t?"prevAll":"nextAll"](".ui-menu-item").eq(-1):this.active[t+"All"](".ui-menu-item").eq(0)),s&&s.length&&this.active||(s=this.activeMenu.find(this.options.items)[e]()),this.focus(i,s)},nextPage:function(e){var i,s,n;return this.active?(this.isLastItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return i=t(this),0>i.offset().top-s-n}),this.focus(e,i)):this.focus(e,this.activeMenu.find(this.options.items)[this.active?"last":"first"]())),void 0):(this.next(e),void 0)},previousPage:function(e){var i,s,n;return this.active?(this.isFirstItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return i=t(this),i.offset().top-s+n>0}),this.focus(e,i)):this.focus(e,this.activeMenu.find(this.options.items).first())),void 0):(this.next(e),void 0)},_hasScroll:function(){return this.element.outerHeight()",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},requestIndex:0,pending:0,_create:function(){var e,i,s,n=this.element[0].nodeName.toLowerCase(),o="textarea"===n,a="input"===n; -this.isMultiLine=o||!a&&this._isContentEditable(this.element),this.valueMethod=this.element[o||a?"val":"text"],this.isNewMenu=!0,this._addClass("ui-autocomplete-input"),this.element.attr("autocomplete","off"),this._on(this.element,{keydown:function(n){if(this.element.prop("readOnly"))return e=!0,s=!0,i=!0,void 0;e=!1,s=!1,i=!1;var o=t.ui.keyCode;switch(n.keyCode){case o.PAGE_UP:e=!0,this._move("previousPage",n);break;case o.PAGE_DOWN:e=!0,this._move("nextPage",n);break;case o.UP:e=!0,this._keyEvent("previous",n);break;case o.DOWN:e=!0,this._keyEvent("next",n);break;case o.ENTER:this.menu.active&&(e=!0,n.preventDefault(),this.menu.select(n));break;case o.TAB:this.menu.active&&this.menu.select(n);break;case o.ESCAPE:this.menu.element.is(":visible")&&(this.isMultiLine||this._value(this.term),this.close(n),n.preventDefault());break;default:i=!0,this._searchTimeout(n)}},keypress:function(s){if(e)return e=!1,(!this.isMultiLine||this.menu.element.is(":visible"))&&s.preventDefault(),void 0;if(!i){var n=t.ui.keyCode;switch(s.keyCode){case n.PAGE_UP:this._move("previousPage",s);break;case n.PAGE_DOWN:this._move("nextPage",s);break;case n.UP:this._keyEvent("previous",s);break;case n.DOWN:this._keyEvent("next",s)}}},input:function(t){return s?(s=!1,t.preventDefault(),void 0):(this._searchTimeout(t),void 0)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(t){return this.cancelBlur?(delete this.cancelBlur,void 0):(clearTimeout(this.searching),this.close(t),this._change(t),void 0)}}),this._initSource(),this.menu=t("